FlightGear next
AircraftItemModel.cxx
Go to the documentation of this file.
1// AircraftModel.cxx - part of GUI launcher using Qt5
2//
3// Written by James Turner, started March 2015.
4//
5// Copyright (C) 2015 James Turner <zakalawe@mac.com>
6//
7// This program is free software; you can redistribute it and/or
8// modify it under the terms of the GNU General Public License as
9// published by the Free Software Foundation; either version 2 of the
10// License, or (at your option) any later version.
11//
12// This program is distributed in the hope that it will be useful, but
13// WITHOUT ANY WARRANTY; without even the implied warranty of
14// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15// General Public License for more details.
16//
17// You should have received a copy of the GNU General Public License
18// along with this program; if not, write to the Free Software
19// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20
21#include "config.h"
22
23#include "AircraftItemModel.hxx"
24
25#include <QSettings>
26#include <QDebug>
27#include <QSharedPointer>
28#include <QSettings>
29
30// Simgear
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>
37
38// FlightGear
40#include "QmlAircraftInfo.hxx"
41#include <Main/globals.hxx>
43
44using namespace simgear::pkg;
45
46bool isPackageFailure(Delegate::StatusCode status)
47{
48 switch (status) {
49 case Delegate::STATUS_SUCCESS:
50 case Delegate::STATUS_REFRESHED:
51 case Delegate::STATUS_IN_PROGRESS:
52 return false;
53
54 default:
55 return true;
56 }
57}
58
59class PackageDelegate : public simgear::pkg::Delegate
60{
61public:
63 m_model(model)
64 {
65 m_model->m_packageRoot->addDelegate(this);
66 }
67
69 {
70 m_model->m_packageRoot->removeDelegate(this);
71 }
72
73protected:
74 void catalogRefreshed(CatalogRef aCatalog, StatusCode aReason) override;
75 void startInstall(InstallRef aInstall) override
76 {
77 QModelIndex mi(indexForPackage(aInstall->package()));
78 m_model->dataChanged(mi, mi);
79 flightgear::addSentryBreadcrumb("Aircraft install started:" + aInstall->package()->qualifiedId(), "info");
80 }
81
82 void installProgress(InstallRef aInstall, unsigned int bytes, unsigned int total) override
83 {
84 Q_UNUSED(bytes)
85 Q_UNUSED(total)
86 QModelIndex mi(indexForPackage(aInstall->package()));
87 m_model->dataChanged(mi, mi);
88 }
89
90 void finishInstall(InstallRef aInstall, StatusCode aReason) override
91 {
92 QModelIndex mi(indexForPackage(aInstall->package()));
93 m_model->dataChanged(mi, mi);
94
95 if ((aReason != USER_CANCELLED) && (aReason != STATUS_SUCCESS)) {
96 flightgear::addSentryBreadcrumb("Aircraft install failed:" + aInstall->package()->qualifiedId(), "info");
97 m_model->installFailed(mi, aReason);
98 }
99
100 if (aReason == STATUS_SUCCESS) {
101 flightgear::addSentryBreadcrumb("Aircraft install succeeded:" + aInstall->package()->qualifiedId(), "info");
102 m_model->installSucceeded(mi);
103 }
104
105 m_model->installedAircraftCountChanged();
106 }
107
109 {
110 m_model->refreshPackages();
111 }
112
113 void installStatusChanged(InstallRef aInstall, StatusCode aReason) override
114 {
115 Q_UNUSED(aReason)
116 QModelIndex mi(indexForPackage(aInstall->package()));
117 m_model->dataChanged(mi, mi);
118 }
119
120 void finishUninstall(const PackageRef& pkg) override
121 {
122 QModelIndex mi(indexForPackage(pkg));
123 m_model->dataChanged(mi, mi);
124 m_model->installedAircraftCountChanged();
125 }
126
127private:
128 QModelIndex indexForPackage(const PackageRef& ref) const
129 {
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();
134 }
135
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);
138 }
139
140 AircraftItemModel* m_model;
141};
142
143void PackageDelegate::catalogRefreshed(CatalogRef aCatalog, StatusCode aReason)
144{
145 if (aReason == STATUS_IN_PROGRESS) {
146 // nothing to do
147 } else if ((aReason == STATUS_REFRESHED) || (aReason == STATUS_SUCCESS)) {
148 m_model->refreshPackages();
149 } else if (aReason == FAIL_VERSION) {
150 // silent about this
151 } else if (aCatalog) {
152 qWarning() << "failed refresh of"
153 << QString::fromStdString(aCatalog->url()) << ":" << aReason;
154 }
155}
156
162 QAbstractListModel(pr)
163{
164 auto cache = LocalAircraftCache::instance();
165 connect(cache, &LocalAircraftCache::scanStarted,
166 this, &AircraftItemModel::onScanStarted);
167 connect(cache, &LocalAircraftCache::addedItems,
168 this, &AircraftItemModel::onScanAddedItems);
169 connect(cache, &LocalAircraftCache::cleared,
170 this, &AircraftItemModel::onLocalCacheCleared);
171}
172
174{
175 delete m_delegate;
176}
177
178void AircraftItemModel::setPackageRoot(const simgear::pkg::RootRef& root)
179{
180 if (m_packageRoot) {
181 delete m_delegate;
182 m_delegate = nullptr;
183 }
184
185 m_packageRoot = root;
186
187 if (m_packageRoot) {
188 m_delegate = new PackageDelegate(this);
189 // packages may already be refreshed, so pull now
190 refreshPackages();
191 }
192}
193
194void AircraftItemModel::onScanStarted()
195{
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;
202 endRemoveRows();
203 }
204}
205
206void AircraftItemModel::refreshPackages()
207{
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;
212
213 if (m_packages.size() != newPkgs.size()) {
214 const int oldSize = static_cast<int>(m_packages.size());
215 if (newSize > oldSize) {
216 // growing
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);
222 endInsertRows();
223 } else {
224 // shrinking
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);
230 endRemoveRows();
231 }
232 } else {
233 m_packages = newPkgs;
234 }
235
236 emit dataChanged(index(firstRow), index(firstRow + newSize - 1));
237 emit contentsChanged();
238}
239
240int AircraftItemModel::rowCount(const QModelIndex&) const
241{
242 return m_cachedLocalAircraftCount + static_cast<int>(m_packages.size());
243}
244
245QVariant AircraftItemModel::data(const QModelIndex& index, int role) const
246{
247 int row = index.row();
248 if (role == AircraftVariantRole) {
249 return m_delegateStates.at(row).variant;
250 }
251
252 if (role == AircraftIsFavouriteRole) {
253 // recursive call here, hope that's okay
254 const auto uri = data(index, AircraftPrimaryURIRole).toUrl();
256 }
257
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();
262
263 if (role == AircraftInstallPercentRole) {
264 return ex.valid() ? ex->downloadedPercent() : 0;
265 } else if (role == AircraftInstallDownloadedSizeRole) {
266 return static_cast<quint64>(ex.valid() ? ex->downloadedBytes() : 0);
267 } else if (role == AircraftPackageRefRole ) {
268 return QVariant::fromValue(pkg);
269 }
270
271 return dataFromPackage(pkg, m_delegateStates.at(row), role);
272 } else {
273 const AircraftItemPtr item(LocalAircraftCache::instance()->itemAt(row));
274 return dataFromItem(item, m_delegateStates.at(row), role);
275 }
276}
277
278QVariant AircraftItemModel::dataFromItem(AircraftItemPtr item, const DelegateState& state, int role) const
279{
280 if (role == AircraftVariantCountRole) {
281 return item->variants.count();
282 }
283
284 if (role >= AircraftVariantDescriptionRole) {
285 int variantIndex = role - AircraftVariantDescriptionRole;
286 if (variantIndex == 0) {
287 return item->name();
288 }
289
290 Q_ASSERT(variantIndex < item->variants.size());
291 return item->variants.at(variantIndex)->name();
292 }
293
294 if (role == AircraftPrimaryURIRole) {
295 return QUrl::fromLocalFile(item->path);
296 }
297
298 if (state.variant) {
299 if (state.variant <= static_cast<quint32>(item->variants.count())) {
300 // show the selected variant
301 item = item->variants.at(state.variant - 1);
302 }
303 }
304
305 if (role == Qt::DisplayRole) {
306 if (item->name().isEmpty()) {
307 return tr("Missing description for: %1").arg(item->baseName());
308 }
309
310 return item->name();
311 } else if (role == AircraftPathRole) {
312 return item->path;
313 } else if (role == AircraftAuthorsRole) {
314 return item->authors;
315 } else if ((role >= AircraftRatingRole) && (role < AircraftVariantDescriptionRole)) {
316 return item->ratings[role - AircraftRatingRole];
317 } else if (role == AircraftPackageIdRole) {
318 // can we fake an ID? otherwise fall through to a null variant
319 } else if (role == AircraftPackageStatusRole) {
320 return LocalAircraftCache::PackageInstalled; // always the case
321 } else if (role == Qt::ToolTipRole) {
322 return item->path;
323 } else if (role == AircraftURIRole) {
324 return QUrl::fromLocalFile(item->path);
325 } else if (role == AircraftHasRatingsRole) {
326 bool have = false;
327 for (int i=0; i<4; ++i) {
328 have |= (item->ratings[i] > 0);
329 }
330 return have;
331 } else if (role == AircraftLongDescriptionRole) {
332 return item->description();
333 } else if (role == AircraftIsHelicopterRole) {
334 return item->usesHeliports;
335 } else if (role == AircraftIsSeaplaneRole) {
336 return item->usesSeaports;
337 } else if ((role == AircraftInstallDownloadedSizeRole) ||
338 (role == AircraftPackageSizeRole))
339 {
340 return 0;
341 } else if (role == AircraftStatusRole) {
342 return item->status(0 /* variant is always 0 */);
343 } else if (role == AircraftMinVersionRole) {
344 return item->minFGVersion;
345 }
346
347 return QVariant();
348}
349
350QVariant AircraftItemModel::dataFromPackage(const PackageRef& item, const DelegateState& state, int role) const
351{
352 if (role >= AircraftVariantDescriptionRole) {
353 const unsigned int variantIndex = static_cast<unsigned int>(role - AircraftVariantDescriptionRole);
354 QString desc = QString::fromStdString(item->nameForVariant(variantIndex));
355 if (desc.isEmpty()) {
356 desc = tr("Missing description for: %1").arg(QString::fromStdString(item->id()));
357 }
358 return desc;
359 }
360
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()));
365 }
366 return desc;
367 } else if (role == AircraftPathRole) {
368 InstallRef i = item->existingInstall();
369 if (i.valid()) {
370 return QString::fromStdString(i->primarySetPath().utf8Str());
371 }
372 } else if (role == AircraftPackageIdRole) {
373 return QString::fromStdString(item->variants()[state.variant]);
374 } else if (role == AircraftPackageStatusRole) {
375 InstallRef i = item->existingInstall();
376 if (i.valid()) {
377 if (i->isDownloading()) {
379 }
380 if (i->isQueued()) {
382 }
383 if (i->hasUpdate()) {
385 }
386
387 const auto status = i->status();
390
392 } else {
394 }
395 } else if (role == AircraftVariantCountRole) {
396 // this value wants the number of aditional variants, i.e not
397 // including the primary. Hence the -1 term.
398 return static_cast<quint32>(item->variants().size() - 1);
399 } else if (role == AircraftAuthorsRole) {
400 std::string authors = item->getLocalisedProp("author", state.variant);
401 if (!authors.empty()) {
402 return QString::fromStdString(authors);
403 }
404 } else if (role == AircraftLongDescriptionRole) {
405 std::string longDesc = item->getLocalisedProp("description", state.variant);
406 return QString::fromStdString(longDesc).simplified();
407 } else if (role == AircraftPackageSizeRole) {
408 return static_cast<int>(item->fileSizeBytes());
409 } else if (role == AircraftURIRole) {
410 return QUrl("package:" + QString::fromStdString(item->qualifiedVariantId(state.variant)));
411 } else if (role == AircraftPrimaryURIRole) {
412 return QUrl("package:" + QString::fromStdString(item->qualifiedId()));
413 } else if (role == AircraftHasRatingsRole) {
414 return item->properties()->hasChild("rating");
415 } else if ((role >= AircraftRatingRole) && (role < AircraftVariantDescriptionRole)) {
416 return packageRating(item, role - AircraftRatingRole);
417 } else if (role == AircraftStatusRole) {
419 } else if (role == AircraftMinVersionRole) {
420 const std::string v = item->properties()->getStringValue("minimum-fg-version");
421 if (!v.empty()) {
422 return QString::fromStdString(v);
423 }
424 } else if (role == AircraftIsHelicopterRole) {
425 return item->hasTag("helicopter");
426 } else if (role == AircraftIsSeaplaneRole) {
427 return item->hasTag("seaplane") || item->hasTag("floats");
428 }
429
430 return QVariant();
431}
432
433QVariant AircraftItemModel::packageRating(const PackageRef& p, int ratingIndex) const
434{
435 return LocalAircraftCache::ratingFromProperties(p->properties()->getChild("rating"), ratingIndex);
436}
437
438bool AircraftItemModel::setData(const QModelIndex &index, const QVariant &value, int role)
439 {
440 int row = index.row();
441 quint32 newValue = value.toUInt();
442
443 if (role == AircraftVariantRole) {
444 if (m_delegateStates[row].variant == newValue) {
445 return true;
446 }
447
448 m_delegateStates[row].variant = newValue;
449 emit dataChanged(index, index);
450 return true;
451 } else if (role == AircraftIsFavouriteRole) {
452 const auto uri = data(index, AircraftPrimaryURIRole).toUrl();
453 bool changed = FavouriteAircraftData::instance()->setFavourite(uri, value.toBool());
454 if (changed) {
455 emit dataChanged(index, index);
456 }
457 }
458
459 return false;
460}
461
462QHash<int, QByteArray> AircraftItemModel::roleNames() const
463{
464 QHash<int, QByteArray> result = QAbstractListModel::roleNames();
465
466 result[Qt::DisplayRole] = "title";
467 result[AircraftURIRole] = "uri";
468 result[AircraftPackageIdRole] = "package";
469 result[AircraftAuthorsRole] = "authors";
470 result[AircraftVariantCountRole] = "variantCount";
471 result[AircraftLongDescriptionRole] = "description";
472 result[AircraftPackageSizeRole] = "packageSizeBytes";
473 result[AircraftPackageStatusRole] = "packageStatus";
474
475 result[AircraftInstallDownloadedSizeRole] = "downloadedBytes";
476 result[AircraftVariantRole] = "activeVariant";
477 result[AircraftIsFavouriteRole] = "favourite";
478
479 result[AircraftStatusRole] = "aircraftStatus";
480 result[AircraftMinVersionRole] = "requiredFGVersion";
481
482 result[AircraftHasRatingsRole] = "hasRatings";
483 result[AircraftRatingRole] = "ratingFDM";
484 result[AircraftRatingRole + 1] = "ratingSystems";
485 result[AircraftRatingRole + 2] = "ratingCockpit";
486 result[AircraftRatingRole + 3] = "ratingExterior";
487
488 return result;
489}
490
491QModelIndex AircraftItemModel::indexOfAircraftURI(QUrl uri) const
492{
493 if (uri.isEmpty()) {
494 return QModelIndex();
495 }
496
497 if (uri.isLocalFile()) {
499 if (row >= 0) {
500 return index(row);
501 }
502 } else if (uri.scheme() == "package") {
503 QString ident = uri.path();
504 int rowOffset = m_cachedLocalAircraftCount;
505
506 PackageRef pkg = m_packageRoot->getPackageById(ident.toStdString());
507 if (pkg) {
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));
512 }
513 } // of linear package scan
514 }
515 } else if (uri.scheme() == "") {
516 // Empty URI scheme (no selection), nothing to do
517 } else {
518 qWarning() << "Unknown aircraft URI scheme" << uri << uri.scheme();
519 }
520
521 return QModelIndex();
522}
523
525{
526 if (uri.isEmpty()) {
527 return;
528 }
529
530 int variantIndex = 0;
531 QModelIndex modelIndex;
532
533 if (uri.isLocalFile()) {
535 if (row < 0) {
536 return;
537 }
538
539 modelIndex = index(row);
540 // now check if we are actually selecting a variant
542
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;
547 break;
548 }
549 }
550 } else if (uri.scheme() == "package") {
551 QString ident = uri.path();
552 int rowOffset = m_cachedLocalAircraftCount;
553
554 PackageRef pkg = m_packageRoot->getPackageById(ident.toStdString());
555 if (pkg) {
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());
560 break;
561 }
562 } // of linear package scan
563 }
564 } else {
565 qWarning() << "Unknown aircraft URI scheme" << uri << uri.scheme();
566 return;
567 }
568
569 if (modelIndex.isValid()) {
570 setData(modelIndex, variantIndex, AircraftVariantRole);
571 }
572}
573
575{
576 if (uri.isLocalFile()) {
578 if (!item) {
579 return {};
580 }
581
582 const QString path = uri.toLocalFile();
583 if (item->path == path) {
584 return item->name();
585 }
586
587 // check variants too
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();
592 }
593 }
594 } else if (uri.scheme() == "package") {
595 QString ident = uri.path();
596 PackageRef pkg = m_packageRoot->getPackageById(ident.toStdString());
597 if (pkg) {
598 const auto variantIndex = pkg->indexOfVariant(ident.toStdString());
599 return QString::fromStdString(pkg->nameForVariant(variantIndex));
600 }
601 } else {
602 qWarning() << "Unknown aircraft URI scheme" << uri << uri.scheme();
603 }
604
605 return {};
606}
607
609{
610 int c = m_cachedLocalAircraftCount;
611
612 for (const auto& cat : m_packageRoot->catalogs()) {
613 c += static_cast<int>(cat->installedPackages().size());
614 }
615
616 return c;
617}
618
619void AircraftItemModel::onScanAddedItems(int addedCount)
620{
621 Q_UNUSED(addedCount)
622 const auto items = LocalAircraftCache::instance()->allItems();
623 const int newItemCount = items.size() - m_cachedLocalAircraftCount;
624 const int firstRow = m_cachedLocalAircraftCount;
625 const int lastRow = firstRow + newItemCount - 1;
626
627 beginInsertRows(QModelIndex(), firstRow, lastRow);
628 m_delegateStates.insert(m_cachedLocalAircraftCount, newItemCount, {});
629 m_cachedLocalAircraftCount += newItemCount;
630 endInsertRows();
631 emit contentsChanged();
633}
634
635void AircraftItemModel::onLocalCacheCleared()
636{
637 if (m_cachedLocalAircraftCount > 0) {
638 const int firstRow = 0;
639 const int lastRow = m_cachedLocalAircraftCount - 1;
640
641 beginRemoveRows(QModelIndex(), firstRow, lastRow);
642 m_delegateStates.remove(0, m_cachedLocalAircraftCount);
643 m_cachedLocalAircraftCount = 0;
644 endRemoveRows();
645 }
646
648}
649
650void AircraftItemModel::installFailed(QModelIndex index, simgear::pkg::Delegate::StatusCode reason)
651{
652 QString msg;
653 switch (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:
665 default:
666 msg = tr("Unknown reason");
667 }
668
669 emit aircraftInstallFailed(index, msg);
670}
671
672void AircraftItemModel::installSucceeded(QModelIndex index)
673{
674 emit aircraftInstallCompleted(index);
675}
676
677bool AircraftItemModel::isIndexRunnable(const QModelIndex& index) const
678{
679 if (index.row() < m_cachedLocalAircraftCount) {
680 return true; // local file, always runnable
681 }
682
683 quint32 packageIndex = static_cast<quint32>(index.row() - m_cachedLocalAircraftCount);
684 const PackageRef& pkg(m_packages[packageIndex]);
685 InstallRef ex = pkg->existingInstall();
686 if (!ex.valid()) {
687 return false; // not installed
688 }
689
690 return !ex->isDownloading();
691}
692
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
#define p(x)
QSharedPointer< AircraftItem > AircraftItemPtr
#define i(x)
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...
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)
static int status
void addSentryBreadcrumb(const std::string &, const std::string &)