Lots of work on aircraft package support
This commit is contained in:
parent
c2cbb36d16
commit
18a898f5f9
12 changed files with 621 additions and 137 deletions
|
@ -88,7 +88,7 @@ void AddCatalogDialog::updateUi()
|
|||
"%2 aircraft are included in this hangar.").arg(catDesc).arg(m_result->packages().size());
|
||||
ui->resultsSummaryLabel->setText(s);
|
||||
} else if (m_state == STATE_DOWNLOAD_FAILED) {
|
||||
Delegate::FailureCode code = m_result->status();
|
||||
Delegate::StatusCode code = m_result->status();
|
||||
qWarning() << Q_FUNC_INFO << "failed with code" << code;
|
||||
QString s;
|
||||
switch (code) {
|
||||
|
@ -98,7 +98,7 @@ void AddCatalogDialog::updateUi()
|
|||
break;
|
||||
|
||||
case Delegate::FAIL_VERSION:
|
||||
s = tr("The provided hangar is for a different version of FLightGear. "
|
||||
s = tr("The provided hangar is for a different version of FlightGear. "
|
||||
"(This is version %1)").arg(QString::fromUtf8(FLIGHTGEAR_VERSION));
|
||||
break;
|
||||
|
||||
|
@ -152,14 +152,14 @@ void AddCatalogDialog::reject()
|
|||
|
||||
void AddCatalogDialog::onCatalogStatusChanged(Catalog* cat)
|
||||
{
|
||||
Delegate::FailureCode s = cat->status();
|
||||
Delegate::StatusCode s = cat->status();
|
||||
qDebug() << Q_FUNC_INFO << "cat status:" << s;
|
||||
switch (s) {
|
||||
case Delegate::CATALOG_REFRESHED:
|
||||
case Delegate::STATUS_REFRESHED:
|
||||
m_state = STATE_FINISHED;
|
||||
break;
|
||||
|
||||
case Delegate::FAIL_IN_PROGRESS:
|
||||
case Delegate::STATUS_IN_PROGRESS:
|
||||
// don't jump to STATE_FINISHED
|
||||
return;
|
||||
|
||||
|
|
|
@ -143,29 +143,79 @@ void AircraftItemDelegate::paint(QPainter * painter, const QStyleOptionViewItem
|
|||
|
||||
QVariant v = index.data(AircraftPackageStatusRole);
|
||||
AircraftItemStatus status = static_cast<AircraftItemStatus>(v.toInt());
|
||||
// status = PackageNotInstalled;
|
||||
double downloadFraction = 0.0;
|
||||
|
||||
if (status != PackageInstalled) {
|
||||
QString buttonText, infoText;
|
||||
QColor buttonColor(27, 122, 211);
|
||||
|
||||
double sizeInMBytes = index.data(AircraftPackageSizeRole).toInt();
|
||||
sizeInMBytes /= 0x100000;
|
||||
|
||||
if (status == PackageDownloading) {
|
||||
buttonText = tr("Cancel");
|
||||
double downloadedMB = index.data(AircraftInstallDownloadedSizeRole).toInt();
|
||||
downloadedMB /= 0x100000;
|
||||
infoText = QStringLiteral("%1 MB of %2 MB").arg(downloadedMB, 0, 'f', 1).arg(sizeInMBytes, 0, 'f', 1);
|
||||
buttonColor = QColor(0xcf, 0xcf, 0xcf);
|
||||
downloadFraction = downloadedMB / sizeInMBytes;
|
||||
} else if (status == PackageQueued) {
|
||||
buttonText = tr("Cancel");
|
||||
infoText = tr("Waiting to download %1 MB").arg(sizeInMBytes, 0, 'f', 1);
|
||||
buttonColor = QColor(0xcf, 0xcf, 0xcf);
|
||||
} else {
|
||||
infoText = QStringLiteral("%1MB").arg(sizeInMBytes, 0, 'f', 1);
|
||||
if (status == PackageNotInstalled) {
|
||||
buttonText = "Install";
|
||||
} else if (status == PackageUpdateAvailable) {
|
||||
buttonText = "Update";
|
||||
}
|
||||
}
|
||||
|
||||
painter->setBrush(Qt::NoBrush);
|
||||
QRect buttonRect = packageButtonRect(option.rect, index);
|
||||
painter->setPen(Qt::NoPen);
|
||||
painter->setBrush(QColor(27, 122, 211));
|
||||
painter->setBrush(buttonColor);
|
||||
painter->drawRoundedRect(buttonRect, 5, 5);
|
||||
painter->setPen(Qt::white);
|
||||
painter->drawText(buttonRect, Qt::AlignCenter, buttonText);
|
||||
|
||||
QRect infoTextRect = buttonRect;
|
||||
infoTextRect.setLeft(buttonRect.right() + MARGIN);
|
||||
infoTextRect.setWidth(200);
|
||||
|
||||
if (status == PackageDownloading) {
|
||||
QRect progressRect = infoTextRect;
|
||||
progressRect.setHeight(6);
|
||||
painter->setPen(QPen(QColor(0xcf, 0xcf, 0xcf), 0));
|
||||
painter->setBrush(Qt::NoBrush);
|
||||
painter->drawRoundedRect(progressRect, 3, 3);
|
||||
infoTextRect.setTop(progressRect.bottom() + 1);
|
||||
|
||||
if (status == PackageNotInstalled) {
|
||||
painter->drawText(buttonRect, Qt::AlignCenter, "Install");
|
||||
} else if (status == PackageUpdateAvailable) {
|
||||
painter->drawText(buttonRect, Qt::AlignCenter, "Update");
|
||||
QRect progressBarRect = progressRect.marginsRemoved(QMargins(2, 2, 2, 2));
|
||||
|
||||
progressBarRect.setWidth(static_cast<int>(progressBarRect.width() * downloadFraction));
|
||||
|
||||
painter->setBrush(QColor(27, 122, 211));
|
||||
painter->setPen(Qt::NoPen);
|
||||
painter->drawRoundedRect(progressBarRect, 2, 2);
|
||||
}
|
||||
}
|
||||
|
||||
painter->setPen(Qt::black);
|
||||
painter->drawText(infoTextRect, Qt::AlignLeft | Qt::AlignVCenter, infoText);
|
||||
} // of update / install / download status
|
||||
}
|
||||
|
||||
QSize AircraftItemDelegate::sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index) const
|
||||
{
|
||||
QRect contentRect = option.rect.adjusted(MARGIN, MARGIN, -MARGIN, -MARGIN);
|
||||
QPixmap thumbnail = index.data(Qt::DecorationRole).value<QPixmap>();
|
||||
contentRect.setLeft(contentRect.left() + MARGIN + thumbnail.width());
|
||||
|
||||
const int THUMBNAIL_WIDTH = 172;
|
||||
// don't request the thumbnail here for remote sources. Assume the default
|
||||
//QPixmap thumbnail = index.data(Qt::DecorationRole).value<QPixmap>();
|
||||
//contentRect.setLeft(contentRect.left() + MARGIN + thumbnail.width());
|
||||
contentRect.setLeft(contentRect.left() + MARGIN + THUMBNAIL_WIDTH);
|
||||
|
||||
QFont f;
|
||||
f.setPointSize(18);
|
||||
QFontMetrics metrics(f);
|
||||
|
@ -209,10 +259,10 @@ bool AircraftItemDelegate::eventFilter( QObject*, QEvent* event )
|
|||
QModelIndex index = m_view->indexAt( me->pos() );
|
||||
int variantCount = index.data(AircraftVariantCountRole).toInt();
|
||||
int variantIndex = index.data(AircraftVariantRole).toInt();
|
||||
QRect vr = m_view->visualRect(index);
|
||||
|
||||
if ( (event->type() == QEvent::MouseButtonRelease) && (variantCount > 0) )
|
||||
{
|
||||
QRect vr = m_view->visualRect(index);
|
||||
QRect leftCycleRect = leftCycleArrowRect(vr, index),
|
||||
rightCycleRect = rightCycleArrowRect(vr, index);
|
||||
|
||||
|
@ -226,6 +276,22 @@ bool AircraftItemDelegate::eventFilter( QObject*, QEvent* event )
|
|||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if ((event->type() == QEvent::MouseButtonRelease) &&
|
||||
packageButtonRect(vr, index).contains(me->pos()))
|
||||
{
|
||||
QVariant v = index.data(AircraftPackageStatusRole);
|
||||
AircraftItemStatus status = static_cast<AircraftItemStatus>(v.toInt());
|
||||
if (status == PackageNotInstalled) {
|
||||
emit requestInstall(index);
|
||||
} else if ((status == PackageDownloading) || (status == PackageQueued)) {
|
||||
emit cancelDownload(index);
|
||||
} else if (status == PackageUpdateAvailable) {
|
||||
emit requestInstall(index);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
} else if ( event->type() == QEvent::MouseMove ) {
|
||||
QMouseEvent* me = static_cast< QMouseEvent* >( event );
|
||||
QModelIndex index = m_view->indexAt( me->pos() );
|
||||
|
@ -273,7 +339,8 @@ QRect AircraftItemDelegate::packageButtonRect(const QRect& visualRect, const QMo
|
|||
QPixmap thumbnail = index.data(Qt::DecorationRole).value<QPixmap>();
|
||||
contentRect.setLeft(contentRect.left() + MARGIN + thumbnail.width());
|
||||
|
||||
return QRect(contentRect.left() + ARROW_SIZE, contentRect.bottom() - 24, 60, BUTTON_HEIGHT);
|
||||
return QRect(contentRect.left() + ARROW_SIZE, contentRect.bottom() - 24,
|
||||
BUTTON_WIDTH, BUTTON_HEIGHT);
|
||||
}
|
||||
|
||||
void AircraftItemDelegate::drawRating(QPainter* painter, QString label, const QRect& box, int value) const
|
||||
|
|
|
@ -32,7 +32,8 @@ public:
|
|||
static const int MARGIN = 4;
|
||||
static const int ARROW_SIZE = 20;
|
||||
static const int BUTTON_HEIGHT = 24;
|
||||
|
||||
static const int BUTTON_WIDTH = 80;
|
||||
|
||||
AircraftItemDelegate(QListView* view);
|
||||
|
||||
virtual void paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const;
|
||||
|
@ -44,6 +45,9 @@ public:
|
|||
Q_SIGNALS:
|
||||
void variantChanged(const QModelIndex& index);
|
||||
|
||||
void requestInstall(const QModelIndex& index);
|
||||
|
||||
void cancelDownload(const QModelIndex& index);
|
||||
private:
|
||||
QRect leftCycleArrowRect(const QRect& visualRect, const QModelIndex& index) const;
|
||||
QRect rightCycleArrowRect(const QRect& visualRect, const QModelIndex& index) const;
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include <QDataStream>
|
||||
#include <QSettings>
|
||||
#include <QDebug>
|
||||
#include <QSharedPointer>
|
||||
|
||||
// Simgear
|
||||
#include <simgear/props/props_io.hxx>
|
||||
|
@ -39,6 +40,8 @@
|
|||
// FlightGear
|
||||
#include <Main/globals.hxx>
|
||||
|
||||
const int STANDARD_THUMBNAIL_HEIGHT = 128;
|
||||
|
||||
using namespace simgear::pkg;
|
||||
|
||||
AircraftItem::AircraftItem() :
|
||||
|
@ -123,8 +126,8 @@ QPixmap AircraftItem::thumbnail() const
|
|||
if (dir.exists("thumbnail.jpg")) {
|
||||
m_thumbnail.load(dir.filePath("thumbnail.jpg"));
|
||||
// resize to the standard size
|
||||
if (m_thumbnail.height() > 128) {
|
||||
m_thumbnail = m_thumbnail.scaledToHeight(128);
|
||||
if (m_thumbnail.height() > STANDARD_THUMBNAIL_HEIGHT) {
|
||||
m_thumbnail = m_thumbnail.scaledToHeight(STANDARD_THUMBNAIL_HEIGHT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -150,9 +153,9 @@ public:
|
|||
}
|
||||
|
||||
/** thread-safe access to items already scanned */
|
||||
QList<AircraftItem*> items()
|
||||
QVector<AircraftItemPtr> items()
|
||||
{
|
||||
QList<AircraftItem*> result;
|
||||
QVector<AircraftItemPtr> result;
|
||||
QMutexLocker g(&m_lock);
|
||||
result.swap(m_items);
|
||||
g.unlock();
|
||||
|
@ -196,13 +199,11 @@ private:
|
|||
}
|
||||
|
||||
for (int i=0; i<count; ++i) {
|
||||
AircraftItem* item = new AircraftItem;
|
||||
AircraftItemPtr item(new AircraftItem);
|
||||
item->fromDataStream(ds);
|
||||
|
||||
QFileInfo finfo(item->path);
|
||||
if (!finfo.exists() || (finfo.lastModified() != item->pathModTime)) {
|
||||
delete item;
|
||||
} else {
|
||||
if (finfo.exists() && (finfo.lastModified() == item->pathModTime)) {
|
||||
// corresponding -set.xml file still exists and is
|
||||
// unmodified
|
||||
m_cachedItems[item->path] = item;
|
||||
|
@ -220,7 +221,7 @@ private:
|
|||
quint32 count = m_nextCache.count();
|
||||
ds << CACHE_VERSION << count;
|
||||
|
||||
Q_FOREACH(AircraftItem* item, m_nextCache.values()) {
|
||||
Q_FOREACH(AircraftItemPtr item, m_nextCache.values()) {
|
||||
item->toDataStream(ds);
|
||||
}
|
||||
}
|
||||
|
@ -237,18 +238,18 @@ private:
|
|||
filters << "*-set.xml";
|
||||
Q_FOREACH(QFileInfo child, path.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot)) {
|
||||
QDir childDir(child.absoluteFilePath());
|
||||
QMap<QString, AircraftItem*> baseAircraft;
|
||||
QList<AircraftItem*> variants;
|
||||
QMap<QString, AircraftItemPtr> baseAircraft;
|
||||
QList<AircraftItemPtr> variants;
|
||||
|
||||
Q_FOREACH(QFileInfo xmlChild, childDir.entryInfoList(filters, QDir::Files)) {
|
||||
try {
|
||||
QString absolutePath = xmlChild.absoluteFilePath();
|
||||
AircraftItem* item = NULL;
|
||||
AircraftItemPtr item;
|
||||
|
||||
if (m_cachedItems.contains(absolutePath)) {
|
||||
item = m_cachedItems.value(absolutePath);
|
||||
} else {
|
||||
item = new AircraftItem(childDir, absolutePath);
|
||||
item = AircraftItemPtr(new AircraftItem(childDir, absolutePath));
|
||||
}
|
||||
|
||||
m_nextCache[absolutePath] = item;
|
||||
|
@ -272,10 +273,9 @@ private:
|
|||
} // of set.xml iteration
|
||||
|
||||
// bind variants to their principals
|
||||
Q_FOREACH(AircraftItem* item, variants) {
|
||||
Q_FOREACH(AircraftItemPtr item, variants) {
|
||||
if (!baseAircraft.contains(item->variantOf)) {
|
||||
qWarning() << "can't find principal aircraft " << item->variantOf << " for variant:" << item->path;
|
||||
delete item;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -285,7 +285,7 @@ private:
|
|||
// lock mutex while we modify the items array
|
||||
{
|
||||
QMutexLocker g(&m_lock);
|
||||
m_items.append(baseAircraft.values());
|
||||
m_items.append(baseAircraft.values().toVector());
|
||||
}
|
||||
|
||||
emit addedItems();
|
||||
|
@ -294,19 +294,122 @@ private:
|
|||
|
||||
QMutex m_lock;
|
||||
QStringList m_dirs;
|
||||
QList<AircraftItem*> m_items;
|
||||
QVector<AircraftItemPtr> m_items;
|
||||
|
||||
QMap<QString, AircraftItem* > m_cachedItems;
|
||||
QMap<QString, AircraftItem* > m_nextCache;
|
||||
QMap<QString, AircraftItemPtr > m_cachedItems;
|
||||
QMap<QString, AircraftItemPtr > m_nextCache;
|
||||
|
||||
bool m_done;
|
||||
};
|
||||
|
||||
class PackageDelegate : public simgear::pkg::Delegate
|
||||
{
|
||||
public:
|
||||
PackageDelegate(AircraftItemModel* model) :
|
||||
m_model(model)
|
||||
{
|
||||
m_model->m_packageRoot->addDelegate(this);
|
||||
}
|
||||
|
||||
~PackageDelegate()
|
||||
{
|
||||
m_model->m_packageRoot->removeDelegate(this);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void catalogRefreshed(CatalogRef aCatalog, StatusCode aReason)
|
||||
{
|
||||
if (aReason == STATUS_IN_PROGRESS) {
|
||||
qDebug() << "doing refresh of" << QString::fromStdString(aCatalog->url());
|
||||
} else if ((aReason == STATUS_REFRESHED) || (aReason == STATUS_SUCCESS)) {
|
||||
m_model->refreshPackages();
|
||||
} else {
|
||||
qWarning() << "failed refresh of "
|
||||
<< QString::fromStdString(aCatalog->url()) << ":" << aReason << endl;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void startInstall(InstallRef aInstall)
|
||||
{
|
||||
QModelIndex mi(indexForPackage(aInstall->package()));
|
||||
m_model->dataChanged(mi, mi);
|
||||
}
|
||||
|
||||
virtual void installProgress(InstallRef aInstall, unsigned int bytes, unsigned int total)
|
||||
{
|
||||
Q_UNUSED(bytes);
|
||||
Q_UNUSED(total);
|
||||
QModelIndex mi(indexForPackage(aInstall->package()));
|
||||
m_model->dataChanged(mi, mi);
|
||||
}
|
||||
|
||||
virtual void finishInstall(InstallRef aInstall, StatusCode aReason)
|
||||
{
|
||||
QModelIndex mi(indexForPackage(aInstall->package()));
|
||||
m_model->dataChanged(mi, mi);
|
||||
|
||||
if ((aReason != USER_CANCELLED) && (aReason != STATUS_SUCCESS)) {
|
||||
m_model->installFailed(mi, aReason);
|
||||
}
|
||||
|
||||
if (aReason == STATUS_SUCCESS) {
|
||||
m_model->installSucceeded(mi);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void dataForThumbnail(const std::string& aThumbnailUrl,
|
||||
size_t length, const uint8_t* bytes)
|
||||
{
|
||||
QImage img = QImage::fromData(QByteArray::fromRawData(reinterpret_cast<const char*>(bytes), length));
|
||||
if (img.isNull()) {
|
||||
qWarning() << "failed to load image data for URL:" <<
|
||||
QString::fromStdString(aThumbnailUrl);
|
||||
return;
|
||||
}
|
||||
|
||||
m_model->m_thumbnailPixmapCache.insert(QString::fromStdString(aThumbnailUrl),
|
||||
QPixmap::fromImage(img));
|
||||
|
||||
// notify any affected items. Linear scan here avoids another map/dict
|
||||
// structure.
|
||||
PackageList::const_iterator it;
|
||||
int i = 0;
|
||||
|
||||
for (it=m_model->m_packages.begin(); it != m_model->m_packages.end(); ++it, ++i) {
|
||||
const string_list& urls((*it)->thumbnailUrls());
|
||||
string_list::const_iterator cit = std::find(urls.begin(), urls.end(), aThumbnailUrl);
|
||||
if (cit != urls.end()) {
|
||||
QModelIndex mi(m_model->index(i + m_model->m_items.size()));
|
||||
m_model->dataChanged(mi, mi);
|
||||
}
|
||||
} // of packages iteration
|
||||
}
|
||||
|
||||
private:
|
||||
QModelIndex indexForPackage(const PackageRef& ref) const
|
||||
{
|
||||
PackageList::const_iterator it = std::find(m_model->m_packages.begin(),
|
||||
m_model->m_packages.end(),
|
||||
ref);
|
||||
if (it == m_model->m_packages.end()) {
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
size_t offset = it - m_model->m_packages.begin();
|
||||
return m_model->index(offset + m_model->m_items.size());
|
||||
}
|
||||
|
||||
AircraftItemModel* m_model;
|
||||
};
|
||||
|
||||
AircraftItemModel::AircraftItemModel(QObject* pr, simgear::pkg::RootRef& rootRef) :
|
||||
QAbstractListModel(pr),
|
||||
m_scanThread(NULL),
|
||||
m_packageRoot(rootRef)
|
||||
{
|
||||
new PackageDelegate(this);
|
||||
// packages may already be refreshed, so pull now
|
||||
refreshPackages();
|
||||
}
|
||||
|
||||
AircraftItemModel::~AircraftItemModel()
|
||||
|
@ -324,7 +427,6 @@ void AircraftItemModel::scanDirs()
|
|||
abandonCurrentScan();
|
||||
|
||||
beginResetModel();
|
||||
qDeleteAll(m_items);
|
||||
m_items.clear();
|
||||
m_activeVariant.clear();
|
||||
endResetModel();
|
||||
|
@ -345,7 +447,6 @@ void AircraftItemModel::scanDirs()
|
|||
connect(m_scanThread, &AircraftScanThread::addedItems,
|
||||
this, &AircraftItemModel::onScanResults);
|
||||
m_scanThread->start();
|
||||
|
||||
}
|
||||
|
||||
void AircraftItemModel::abandonCurrentScan()
|
||||
|
@ -358,18 +459,51 @@ void AircraftItemModel::abandonCurrentScan()
|
|||
}
|
||||
}
|
||||
|
||||
QVariant AircraftItemModel::data(const QModelIndex& index, int role) const
|
||||
void AircraftItemModel::refreshPackages()
|
||||
{
|
||||
if (role == AircraftVariantRole) {
|
||||
return m_activeVariant.at(index.row());
|
||||
}
|
||||
|
||||
const AircraftItem* item(m_items.at(index.row()));
|
||||
quint32 variantIndex = m_activeVariant.at(index.row());
|
||||
return dataFromItem(item, variantIndex, role);
|
||||
beginResetModel();
|
||||
m_packages = m_packageRoot->allPackages();
|
||||
m_packageVariant.resize(m_packages.size());
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
QVariant AircraftItemModel::dataFromItem(const AircraftItem* item, quint32 variantIndex, int role) const
|
||||
int AircraftItemModel::rowCount(const QModelIndex& parent) const
|
||||
{
|
||||
return m_items.size() + m_packages.size();
|
||||
}
|
||||
|
||||
QVariant AircraftItemModel::data(const QModelIndex& index, int role) const
|
||||
{
|
||||
if (index.row() >= m_items.size()) {
|
||||
quint32 packageIndex = index.row() - m_items.size();
|
||||
|
||||
if (role == AircraftVariantRole) {
|
||||
return m_packageVariant.at(packageIndex);
|
||||
}
|
||||
|
||||
const PackageRef& pkg(m_packages[packageIndex]);
|
||||
InstallRef ex = pkg->existingInstall();
|
||||
|
||||
if (role == AircraftInstallPercentRole) {
|
||||
return ex.valid() ? ex->downloadedPercent() : 0;
|
||||
} else if (role == AircraftInstallDownloadedSizeRole) {
|
||||
return static_cast<quint64>(ex.valid() ? ex->downloadedBytes() : 0);
|
||||
}
|
||||
|
||||
quint32 variantIndex = m_packageVariant.at(packageIndex);
|
||||
return dataFromPackage(pkg, variantIndex, role);
|
||||
} else {
|
||||
if (role == AircraftVariantRole) {
|
||||
return m_activeVariant.at(index.row());
|
||||
}
|
||||
|
||||
quint32 variantIndex = m_activeVariant.at(index.row());
|
||||
const AircraftItemPtr item(m_items.at(index.row()));
|
||||
return dataFromItem(item, variantIndex, role);
|
||||
}
|
||||
}
|
||||
|
||||
QVariant AircraftItemModel::dataFromItem(AircraftItemPtr item, quint32 variantIndex, int role) const
|
||||
{
|
||||
if (role == AircraftVariantCountRole) {
|
||||
return item->variants.count();
|
||||
|
@ -410,6 +544,8 @@ QVariant AircraftItemModel::dataFromItem(const AircraftItem* item, quint32 varia
|
|||
return PackageInstalled; // always the case
|
||||
} else if (role == Qt::ToolTipRole) {
|
||||
return item->path;
|
||||
} else if (role == AircraftURIRole) {
|
||||
return QUrl::fromLocalFile(item->path);
|
||||
} else if (role == AircraftHasRatingsRole) {
|
||||
bool have = false;
|
||||
for (int i=0; i<4; ++i) {
|
||||
|
@ -430,19 +566,28 @@ QVariant AircraftItemModel::dataFromItem(const AircraftItem* item, quint32 varia
|
|||
|
||||
QVariant AircraftItemModel::dataFromPackage(const PackageRef& item, quint32 variantIndex, int role) const
|
||||
{
|
||||
if (role == Qt::DecorationRole) {
|
||||
role = AircraftThumbnailRole; // use first thumbnail
|
||||
}
|
||||
|
||||
if (role == Qt::DisplayRole) {
|
||||
return QString::fromStdString(item->name());
|
||||
} else if (role == AircraftPathRole) {
|
||||
// can we return the theoretical path?
|
||||
InstallRef i = item->existingInstall();
|
||||
if (i.valid()) {
|
||||
return QString::fromStdString(i->primarySetPath().str());
|
||||
}
|
||||
} else if (role == AircraftPackageIdRole) {
|
||||
return QString::fromStdString(item->id());
|
||||
} else if (role == AircraftPackageStatusRole) {
|
||||
bool installed = item->isInstalled();
|
||||
if (installed) {
|
||||
InstallRef i = item->existingInstall();
|
||||
InstallRef i = item->existingInstall();
|
||||
if (i.valid()) {
|
||||
if (i->isDownloading()) {
|
||||
return PackageDownloading;
|
||||
}
|
||||
if (i->isQueued()) {
|
||||
return PackageQueued;
|
||||
}
|
||||
if (i->hasUpdate()) {
|
||||
return PackageUpdateAvailable;
|
||||
}
|
||||
|
@ -451,13 +596,72 @@ QVariant AircraftItemModel::dataFromPackage(const PackageRef& item, quint32 vari
|
|||
} else {
|
||||
return PackageNotInstalled;
|
||||
}
|
||||
} else if (role >= AircraftThumbnailRole) {
|
||||
return packageThumbnail(item , role - AircraftThumbnailRole);
|
||||
} else if (role == AircraftAuthorsRole) {
|
||||
SGPropertyNode* authors = item->properties()->getChild("author");
|
||||
if (authors) {
|
||||
return QString::fromStdString(authors->getStringValue());
|
||||
}
|
||||
} else if (role == AircraftLongDescriptionRole) {
|
||||
return QString::fromStdString(item->description());
|
||||
} else if (role == AircraftPackageSizeRole) {
|
||||
return static_cast<int>(item->fileSizeBytes());
|
||||
} else if (role == AircraftURIRole) {
|
||||
return QUrl("package:" + QString::fromStdString(item->qualifiedId()));
|
||||
} else if (role == AircraftHasRatingsRole) {
|
||||
return item->properties()->hasChild("rating");
|
||||
} else if ((role >= AircraftRatingRole) && (role < AircraftVariantDescriptionRole)) {
|
||||
int ratingIndex = role - AircraftRatingRole;
|
||||
SGPropertyNode* ratings = item->properties()->getChild("rating");
|
||||
if (!ratings) {
|
||||
return QVariant();
|
||||
}
|
||||
return ratings->getChild(ratingIndex)->getIntValue();
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QVariant AircraftItemModel::packageThumbnail(PackageRef p, int index) const
|
||||
{
|
||||
const string_list& thumbnails(p->thumbnailUrls());
|
||||
if (index >= thumbnails.size()) {
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
std::string thumbnailUrl = thumbnails.at(index);
|
||||
QString urlQString(QString::fromStdString(thumbnailUrl));
|
||||
if (m_thumbnailPixmapCache.contains(urlQString)) {
|
||||
// cache hit, easy
|
||||
return m_thumbnailPixmapCache.value(urlQString);
|
||||
}
|
||||
|
||||
// check the on-disk store. This relies on the order of thumbnails in the
|
||||
// results of thumbnailUrls and thumbnails corresponding
|
||||
InstallRef ex = p->existingInstall();
|
||||
if (ex.valid()) {
|
||||
const string_list& thumbNames(p->thumbnails());
|
||||
if (!thumbNames.empty()) {
|
||||
SGPath path(ex->path());
|
||||
path.append(p->thumbnails()[index]);
|
||||
if (path.exists()) {
|
||||
QPixmap pix;
|
||||
pix.load(QString::fromStdString(path.str()));
|
||||
// resize to the standard size
|
||||
if (pix.height() > STANDARD_THUMBNAIL_HEIGHT) {
|
||||
pix = pix.scaledToHeight(STANDARD_THUMBNAIL_HEIGHT);
|
||||
}
|
||||
m_thumbnailPixmapCache[urlQString] = pix;
|
||||
return pix;
|
||||
}
|
||||
} // of have thumbnail file names
|
||||
} // of have existing install
|
||||
|
||||
m_packageRoot->requestThumbnailData(thumbnailUrl);
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
bool AircraftItemModel::setData(const QModelIndex &index, const QVariant &value, int role)
|
||||
{
|
||||
if (role == AircraftVariantRole) {
|
||||
|
@ -469,21 +673,36 @@ bool AircraftItemModel::setData(const QModelIndex &index, const QVariant &value,
|
|||
return false;
|
||||
}
|
||||
|
||||
QModelIndex AircraftItemModel::indexOfAircraftPath(QString path) const
|
||||
QModelIndex AircraftItemModel::indexOfAircraftURI(QUrl uri) const
|
||||
{
|
||||
for (int row=0; row <m_items.size(); ++row) {
|
||||
const AircraftItem* item(m_items.at(row));
|
||||
if (item->path == path) {
|
||||
return index(row);
|
||||
if (uri.isLocalFile()) {
|
||||
QString path = uri.toLocalFile();
|
||||
for (int row=0; row <m_items.size(); ++row) {
|
||||
const AircraftItemPtr item(m_items.at(row));
|
||||
if (item->path == path) {
|
||||
return index(row);
|
||||
}
|
||||
}
|
||||
} else if (uri.scheme() == "package") {
|
||||
QString ident = uri.path();
|
||||
PackageRef pkg = m_packageRoot->getPackageById(ident.toStdString());
|
||||
if (pkg) {
|
||||
for (int i=0; i < m_packages.size(); ++i) {
|
||||
if (m_packages[i] == pkg) {
|
||||
return index(m_items.size() + i);
|
||||
}
|
||||
} // of linear package scan
|
||||
}
|
||||
} else {
|
||||
qWarning() << "Unknown aircraft URI scheme" << uri << uri.scheme();
|
||||
}
|
||||
|
||||
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
void AircraftItemModel::onScanResults()
|
||||
{
|
||||
QList<AircraftItem*> newItems = m_scanThread->items();
|
||||
QVector<AircraftItemPtr> newItems = m_scanThread->items();
|
||||
if (newItems.isEmpty())
|
||||
return;
|
||||
|
||||
|
@ -505,4 +724,51 @@ void AircraftItemModel::onScanFinished()
|
|||
m_scanThread = NULL;
|
||||
}
|
||||
|
||||
void AircraftItemModel::installFailed(QModelIndex index, simgear::pkg::Delegate::StatusCode reason)
|
||||
{
|
||||
Q_ASSERT(index.row() >= m_items.size());
|
||||
|
||||
QString msg;
|
||||
switch (reason) {
|
||||
case Delegate::FAIL_CHECKSUM:
|
||||
msg = tr("Invalid package checksum"); break;
|
||||
case Delegate::FAIL_DOWNLOAD:
|
||||
msg = tr("Download failed"); break;
|
||||
case Delegate::FAIL_EXTRACT:
|
||||
msg = tr("Package could not be extracted"); break;
|
||||
case Delegate::FAIL_FILESYSTEM:
|
||||
msg = tr("A local file-system error occurred"); break;
|
||||
case Delegate::FAIL_UNKNOWN:
|
||||
default:
|
||||
msg = tr("Unknown reason");
|
||||
}
|
||||
|
||||
quint32 packageIndex = index.row() - m_items.size();
|
||||
const PackageRef& pkg(m_packages[packageIndex]);
|
||||
QString packageName = QString::fromStdString(pkg->description());
|
||||
emit aircraftInstallFailed(index, tr("Failed installation of package '%1': %2").arg(packageName).arg(msg));
|
||||
}
|
||||
|
||||
void AircraftItemModel::installSucceeded(QModelIndex index)
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO << index;
|
||||
emit aircraftInstallCompleted(index);
|
||||
}
|
||||
|
||||
bool AircraftItemModel::isIndexRunnable(const QModelIndex& index) const
|
||||
{
|
||||
if (index.row() < m_items.size()) {
|
||||
return true; // local file, always runnable
|
||||
}
|
||||
|
||||
quint32 packageIndex = index.row() - m_items.size();
|
||||
const PackageRef& pkg(m_packages[packageIndex]);
|
||||
InstallRef ex = pkg->existingInstall();
|
||||
if (!ex.valid()) {
|
||||
return false; // not installed
|
||||
}
|
||||
|
||||
return !ex->isDownloading();
|
||||
}
|
||||
|
||||
#include "AircraftModel.moc"
|
||||
|
|
|
@ -26,6 +26,8 @@
|
|||
#include <QDir>
|
||||
#include <QPixmap>
|
||||
#include <QStringList>
|
||||
#include <QSharedPointer>
|
||||
#include <QUrl>
|
||||
|
||||
#include <simgear/package/Root.hxx>
|
||||
|
||||
|
@ -39,6 +41,10 @@ const int AircraftPackageStatusRole = Qt::UserRole + 7;
|
|||
const int AircraftPackageProgressRole = Qt::UserRole + 8;
|
||||
const int AircraftLongDescriptionRole = Qt::UserRole + 9;
|
||||
const int AircraftHasRatingsRole = Qt::UserRole + 10;
|
||||
const int AircraftInstallPercentRole = Qt::UserRole + 11;
|
||||
const int AircraftPackageSizeRole = Qt::UserRole + 12;
|
||||
const int AircraftInstallDownloadedSizeRole = Qt::UserRole + 13;
|
||||
const int AircraftURIRole = Qt::UserRole + 14;
|
||||
|
||||
const int AircraftRatingRole = Qt::UserRole + 100;
|
||||
const int AircraftVariantDescriptionRole = Qt::UserRole + 200;
|
||||
|
@ -47,6 +53,9 @@ const int AircraftThumbnailRole = Qt::UserRole + 300;
|
|||
class AircraftScanThread;
|
||||
class QDataStream;
|
||||
|
||||
struct AircraftItem;
|
||||
typedef QSharedPointer<AircraftItem> AircraftItemPtr;
|
||||
|
||||
struct AircraftItem
|
||||
{
|
||||
AircraftItem();
|
||||
|
@ -70,7 +79,7 @@ struct AircraftItem
|
|||
QString variantOf;
|
||||
QDateTime pathModTime;
|
||||
|
||||
QList<AircraftItem*> variants;
|
||||
QList<AircraftItemPtr> variants;
|
||||
private:
|
||||
mutable QPixmap m_thumbnail;
|
||||
};
|
||||
|
@ -80,6 +89,7 @@ enum AircraftItemStatus {
|
|||
PackageNotInstalled,
|
||||
PackageInstalled,
|
||||
PackageUpdateAvailable,
|
||||
PackageQueued,
|
||||
PackageDownloading
|
||||
};
|
||||
|
||||
|
@ -95,11 +105,8 @@ public:
|
|||
|
||||
void scanDirs();
|
||||
|
||||
virtual int rowCount(const QModelIndex& parent) const
|
||||
{
|
||||
return m_items.size();
|
||||
}
|
||||
|
||||
virtual int rowCount(const QModelIndex& parent) const;
|
||||
|
||||
virtual QVariant data(const QModelIndex& index, int role) const;
|
||||
|
||||
virtual bool setData(const QModelIndex &index, const QVariant &value, int role);
|
||||
|
@ -108,26 +115,53 @@ public:
|
|||
* given a -set.xml path, return the corresponding model index, if one
|
||||
* exists.
|
||||
*/
|
||||
QModelIndex indexOfAircraftPath(QString path) const;
|
||||
// QModelIndex indexOfAircraftPath(QString path) const;
|
||||
|
||||
QModelIndex indexOfAircraftURI(QUrl uri) const;
|
||||
|
||||
/**
|
||||
* return if a given aircraft is ready to be run, or not. Aircraft which
|
||||
* are not installed, or are downloading, are not runnable.
|
||||
*/
|
||||
bool isIndexRunnable(const QModelIndex& index) const;
|
||||
|
||||
signals:
|
||||
void aircraftInstallFailed(QModelIndex index, QString errorMessage);
|
||||
|
||||
void aircraftInstallCompleted(QModelIndex index);
|
||||
|
||||
private slots:
|
||||
void onScanResults();
|
||||
|
||||
void onScanFinished();
|
||||
|
||||
private:
|
||||
QVariant dataFromItem(const AircraftItem* item, quint32 variantIndex, int role) const;
|
||||
friend class PackageDelegate;
|
||||
|
||||
QVariant dataFromItem(AircraftItemPtr item, quint32 variantIndex, int role) const;
|
||||
|
||||
QVariant dataFromPackage(const simgear::pkg::PackageRef& item,
|
||||
quint32 variantIndex, int role) const;
|
||||
|
||||
QVariant packageThumbnail(simgear::pkg::PackageRef p, int index) const;
|
||||
|
||||
void abandonCurrentScan();
|
||||
|
||||
void refreshPackages();
|
||||
|
||||
void installSucceeded(QModelIndex index);
|
||||
void installFailed(QModelIndex index, simgear::pkg::Delegate::StatusCode reason);
|
||||
|
||||
QStringList m_paths;
|
||||
AircraftScanThread* m_scanThread;
|
||||
QList<AircraftItem*> m_items;
|
||||
QList<quint32> m_activeVariant;
|
||||
QVector<AircraftItemPtr> m_items;
|
||||
|
||||
QVector<quint32> m_activeVariant;
|
||||
QVector<quint32> m_packageVariant;
|
||||
|
||||
simgear::pkg::RootRef m_packageRoot;
|
||||
simgear::pkg::PackageList m_packages;
|
||||
|
||||
mutable QHash<QString, QPixmap> m_thumbnailPixmapCache;
|
||||
};
|
||||
|
||||
#endif // of FG_GUI_AIRCRAFT_MODEL
|
||||
|
|
|
@ -59,7 +59,7 @@ QVariant CatalogListModel::data(const QModelIndex& index, int role) const
|
|||
simgear::pkg::CatalogRef cat = m_packageRoot->catalogs().at(index.row());
|
||||
|
||||
if (role == Qt::DisplayRole) {
|
||||
return QString::fromStdString(cat->description());
|
||||
return QString::fromStdString(cat->description()).trimmed();
|
||||
} else if (role == Qt::ToolTipRole) {
|
||||
return QString::fromStdString(cat->url());
|
||||
} else if (role == CatalogUrlRole) {
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include <QSettings>
|
||||
#include <QFileDialog>
|
||||
#include <QMessageBox>
|
||||
|
||||
#include "CatalogListModel.hxx"
|
||||
#include "AddCatalogDialog.hxx"
|
||||
|
@ -128,7 +129,17 @@ void PathsDialog::onAddCatalog()
|
|||
|
||||
void PathsDialog::onRemoveCatalog()
|
||||
{
|
||||
|
||||
QModelIndex mi = m_ui->catalogsList->currentIndex();
|
||||
if (mi.isValid()) {
|
||||
QMessageBox mb;
|
||||
mb.setText(QStringLiteral("Remove aircraft hangar '%1'?").arg(mi.data(Qt::DisplayRole).toString()));
|
||||
mb.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
|
||||
mb.setDefaultButton(QMessageBox::No);
|
||||
mb.exec();
|
||||
|
||||
QString pkgId = mi.data(CatalogIdRole).toString();
|
||||
m_packageRoot->removeCatalogById(pkgId.toStdString());
|
||||
}
|
||||
}
|
||||
|
||||
void PathsDialog::onChangeDownloadDir()
|
||||
|
|
|
@ -48,6 +48,9 @@
|
|||
#include <simgear/structure/exception.hxx>
|
||||
#include <simgear/structure/subsystem_mgr.hxx>
|
||||
#include <simgear/misc/sg_path.hxx>
|
||||
#include <simgear/package/Catalog.hxx>
|
||||
#include <simgear/package/Package.hxx>
|
||||
#include <simgear/package/Install.hxx>
|
||||
|
||||
#include "ui_Launcher.h"
|
||||
#include "EditRatingsFilterDialog.hxx"
|
||||
|
@ -65,6 +68,7 @@
|
|||
#include <Network/HTTPClient.hxx>
|
||||
|
||||
using namespace flightgear;
|
||||
using namespace simgear::pkg;
|
||||
|
||||
const int MAX_RECENT_AIRPORTS = 32;
|
||||
const int MAX_RECENT_AIRCRAFT = 20;
|
||||
|
@ -457,8 +461,6 @@ QtLauncher::QtLauncher() :
|
|||
connect(m_ui->aircraftHistory, &QPushButton::clicked,
|
||||
this, &QtLauncher::onPopupAircraftHistory);
|
||||
|
||||
restoreSettings();
|
||||
|
||||
QAction* qa = new QAction(this);
|
||||
qa->setShortcut(QKeySequence("Ctrl+Q"));
|
||||
connect(qa, &QAction::triggered, this, &QtLauncher::onQuit);
|
||||
|
@ -495,7 +497,7 @@ QtLauncher::QtLauncher() :
|
|||
updateSettingsSummary();
|
||||
|
||||
fgInitPackageRoot();
|
||||
simgear::pkg::RootRef r(globals->packageRoot());
|
||||
RootRef r(globals->packageRoot());
|
||||
|
||||
FGHTTPClient* http = new FGHTTPClient;
|
||||
globals->add_subsystem("http", http);
|
||||
|
@ -521,10 +523,21 @@ QtLauncher::QtLauncher() :
|
|||
this, &QtLauncher::onAircraftSelected);
|
||||
connect(delegate, &AircraftItemDelegate::variantChanged,
|
||||
this, &QtLauncher::onAircraftSelected);
|
||||
connect(delegate, &AircraftItemDelegate::requestInstall,
|
||||
this, &QtLauncher::onRequestPackageInstall);
|
||||
connect(delegate, &AircraftItemDelegate::cancelDownload,
|
||||
this, &QtLauncher::onCancelDownload);
|
||||
|
||||
connect(m_aircraftModel, &AircraftItemModel::aircraftInstallCompleted,
|
||||
this, &QtLauncher::onAircraftInstalledCompleted);
|
||||
connect(m_aircraftModel, &AircraftItemModel::aircraftInstallFailed,
|
||||
this, &QtLauncher::onAircraftInstallFailed);
|
||||
|
||||
connect(m_ui->pathsButton, &QPushButton::clicked,
|
||||
this, &QtLauncher::onEditPaths);
|
||||
|
||||
restoreSettings();
|
||||
|
||||
QSettings settings;
|
||||
m_aircraftModel->setPaths(settings.value("aircraft-paths").toStringList());
|
||||
m_aircraftModel->scanDirs();
|
||||
|
@ -566,6 +579,7 @@ void QtLauncher::initApp(int& argc, char** argv)
|
|||
|
||||
bool QtLauncher::runLauncherDialog()
|
||||
{
|
||||
sglog().setLogLevels( SG_ALL, SG_INFO );
|
||||
Q_INIT_RESOURCE(resources);
|
||||
|
||||
// startup the nav-cache now. This pre-empts normal startup of
|
||||
|
@ -598,7 +612,7 @@ void QtLauncher::restoreSettings()
|
|||
m_ui->seasonCombo->setCurrentIndex(settings.value("season", 0).toInt());
|
||||
|
||||
// full paths to -set.xml files
|
||||
m_recentAircraft = settings.value("recent-aircraft").toStringList();
|
||||
m_recentAircraft = QUrl::fromStringList(settings.value("recent-aircraft").toStringList());
|
||||
|
||||
if (!m_recentAircraft.empty()) {
|
||||
m_selectedAircraft = m_recentAircraft.front();
|
||||
|
@ -638,7 +652,7 @@ void QtLauncher::saveSettings()
|
|||
settings.setValue("enable-realwx", m_ui->fetchRealWxrCheckbox->isChecked());
|
||||
settings.setValue("start-paused", m_ui->startPausedCheck->isChecked());
|
||||
settings.setValue("ratings-filter", m_ui->ratingsFilterCheck->isChecked());
|
||||
settings.setValue("recent-aircraft", m_recentAircraft);
|
||||
settings.setValue("recent-aircraft", QUrl::toStringList(m_recentAircraft));
|
||||
settings.setValue("recent-airports", m_recentAirports);
|
||||
settings.setValue("timeofday", m_ui->timeOfDayCombo->currentIndex());
|
||||
settings.setValue("season", m_ui->seasonCombo->currentIndex());
|
||||
|
@ -679,13 +693,22 @@ void QtLauncher::onRun()
|
|||
|
||||
// aircraft
|
||||
if (!m_selectedAircraft.isEmpty()) {
|
||||
QFileInfo setFileInfo(m_selectedAircraft);
|
||||
opt->addOption("aircraft-dir", setFileInfo.dir().absolutePath().toStdString());
|
||||
QString setFile = setFileInfo.fileName();
|
||||
Q_ASSERT(setFile.endsWith("-set.xml"));
|
||||
setFile.truncate(setFile.count() - 8); // drop the '-set.xml' portion
|
||||
opt->addOption("aircraft", setFile.toStdString());
|
||||
|
||||
if (m_selectedAircraft.isLocalFile()) {
|
||||
QFileInfo setFileInfo(m_selectedAircraft.toLocalFile());
|
||||
opt->addOption("aircraft-dir", setFileInfo.dir().absolutePath().toStdString());
|
||||
QString setFile = setFileInfo.fileName();
|
||||
Q_ASSERT(setFile.endsWith("-set.xml"));
|
||||
setFile.truncate(setFile.count() - 8); // drop the '-set.xml' portion
|
||||
opt->addOption("aircraft", setFile.toStdString());
|
||||
} else if (m_selectedAircraft.scheme() == "package") {
|
||||
PackageRef pkg = packageForAircraftURI(m_selectedAircraft);
|
||||
// no need to set aircraft-dir, handled by the corresponding code
|
||||
// in fgInitAircraft
|
||||
opt->addOption("aircraft", pkg->qualifiedId());
|
||||
} else {
|
||||
qWarning() << "unsupported aircraft launch URL" << m_selectedAircraft;
|
||||
}
|
||||
|
||||
// manage aircraft history
|
||||
if (m_recentAircraft.contains(m_selectedAircraft))
|
||||
m_recentAircraft.removeOne(m_selectedAircraft);
|
||||
|
@ -902,6 +925,29 @@ void QtLauncher::onToggleTerrasync(bool enabled)
|
|||
} // of is enabled
|
||||
}
|
||||
|
||||
void QtLauncher::onAircraftInstalledCompleted(QModelIndex index)
|
||||
{
|
||||
qDebug() << Q_FUNC_INFO;
|
||||
QUrl u = index.data(AircraftURIRole).toUrl();
|
||||
if (u == m_selectedAircraft) {
|
||||
// potentially enable the run button now!
|
||||
updateSelectedAircraft();
|
||||
qDebug() << "updating selected aircraft" << index.data();
|
||||
}
|
||||
}
|
||||
|
||||
void QtLauncher::onAircraftInstallFailed(QModelIndex index, QString errorMessage)
|
||||
{
|
||||
qWarning() << Q_FUNC_INFO << index.data(AircraftURIRole) << errorMessage;
|
||||
|
||||
QMessageBox msg;
|
||||
msg.setWindowTitle(tr("Aircraft insallation failed"));
|
||||
msg.setText(tr("An error occurred installing the aircraft %1: %2").
|
||||
arg(index.data(Qt::DisplayRole).toString()).arg(errorMessage));
|
||||
msg.addButton(QMessageBox::Ok);
|
||||
msg.exec();
|
||||
}
|
||||
|
||||
void QtLauncher::updateAirportDescription()
|
||||
{
|
||||
if (!m_selectedAirport) {
|
||||
|
@ -944,20 +990,42 @@ void QtLauncher::onAirportChoiceSelected(const QModelIndex& index)
|
|||
|
||||
void QtLauncher::onAircraftSelected(const QModelIndex& index)
|
||||
{
|
||||
m_selectedAircraft = index.data(AircraftPathRole).toString();
|
||||
m_selectedAircraft = index.data(AircraftURIRole).toUrl();
|
||||
updateSelectedAircraft();
|
||||
}
|
||||
|
||||
void QtLauncher::onRequestPackageInstall(const QModelIndex& index)
|
||||
{
|
||||
QString pkg = index.data(AircraftPackageIdRole).toString();
|
||||
qDebug() << "request install of" << pkg;
|
||||
simgear::pkg::PackageRef pref = globals->packageRoot()->getPackageById(pkg.toStdString());
|
||||
pref->install();
|
||||
}
|
||||
|
||||
void QtLauncher::onCancelDownload(const QModelIndex& index)
|
||||
{
|
||||
QString pkg = index.data(AircraftPackageIdRole).toString();
|
||||
qDebug() << "cancel download of" << pkg;
|
||||
simgear::pkg::PackageRef pref = globals->packageRoot()->getPackageById(pkg.toStdString());
|
||||
simgear::pkg::InstallRef i = pref->existingInstall();
|
||||
i->cancelDownload();
|
||||
}
|
||||
|
||||
void QtLauncher::updateSelectedAircraft()
|
||||
{
|
||||
try {
|
||||
QFileInfo info(m_selectedAircraft);
|
||||
AircraftItem item(info.dir(), m_selectedAircraft);
|
||||
m_ui->thumbnail->setPixmap(item.thumbnail());
|
||||
m_ui->aircraftDescription->setText(item.description);
|
||||
} catch (sg_exception& e) {
|
||||
QModelIndex index = m_aircraftModel->indexOfAircraftURI(m_selectedAircraft);
|
||||
if (index.isValid()) {
|
||||
QPixmap pm = index.data(Qt::DecorationRole).value<QPixmap>();
|
||||
m_ui->thumbnail->setPixmap(pm);
|
||||
m_ui->aircraftDescription->setText(index.data(Qt::DisplayRole).toString());
|
||||
|
||||
int status = index.data(AircraftPackageStatusRole).toInt();
|
||||
bool canRun = (status == PackageInstalled);
|
||||
m_ui->runButton->setEnabled(canRun);
|
||||
} else {
|
||||
m_ui->thumbnail->setPixmap(QPixmap());
|
||||
m_ui->aircraftDescription->setText("");
|
||||
m_ui->runButton->setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -985,16 +1053,16 @@ void QtLauncher::onPopupAirportHistory()
|
|||
}
|
||||
}
|
||||
|
||||
QModelIndex QtLauncher::proxyIndexForAircraftPath(QString path) const
|
||||
QModelIndex QtLauncher::proxyIndexForAircraftURI(QUrl uri) const
|
||||
{
|
||||
return m_aircraftProxy->mapFromSource(sourceIndexForAircraftPath(path));
|
||||
return m_aircraftProxy->mapFromSource(sourceIndexForAircraftURI(uri));
|
||||
}
|
||||
|
||||
QModelIndex QtLauncher::sourceIndexForAircraftPath(QString path) const
|
||||
QModelIndex QtLauncher::sourceIndexForAircraftURI(QUrl uri) const
|
||||
{
|
||||
AircraftItemModel* sourceModel = qobject_cast<AircraftItemModel*>(m_aircraftProxy->sourceModel());
|
||||
Q_ASSERT(sourceModel);
|
||||
return sourceModel->indexOfAircraftPath(path);
|
||||
return sourceModel->indexOfAircraftURI(uri);
|
||||
}
|
||||
|
||||
void QtLauncher::onPopupAircraftHistory()
|
||||
|
@ -1004,21 +1072,21 @@ void QtLauncher::onPopupAircraftHistory()
|
|||
}
|
||||
|
||||
QMenu m;
|
||||
Q_FOREACH(QString path, m_recentAircraft) {
|
||||
QModelIndex index = sourceIndexForAircraftPath(path);
|
||||
Q_FOREACH(QUrl uri, m_recentAircraft) {
|
||||
QModelIndex index = sourceIndexForAircraftURI(uri);
|
||||
if (!index.isValid()) {
|
||||
// not scanned yet
|
||||
continue;
|
||||
}
|
||||
QAction* act = m.addAction(index.data(Qt::DisplayRole).toString());
|
||||
act->setData(path);
|
||||
act->setData(uri);
|
||||
}
|
||||
|
||||
QPoint popupPos = m_ui->aircraftHistory->mapToGlobal(m_ui->aircraftHistory->rect().bottomLeft());
|
||||
QAction* triggered = m.exec(popupPos);
|
||||
if (triggered) {
|
||||
m_selectedAircraft = triggered->data().toString();
|
||||
QModelIndex index = proxyIndexForAircraftPath(m_selectedAircraft);
|
||||
m_selectedAircraft = triggered->data().toUrl();
|
||||
QModelIndex index = proxyIndexForAircraftURI(m_selectedAircraft);
|
||||
m_ui->aircraftList->selectionModel()->setCurrentIndex(index,
|
||||
QItemSelectionModel::ClearAndSelect);
|
||||
m_ui->aircraftFilter->clear();
|
||||
|
@ -1129,5 +1197,17 @@ void QtLauncher::onEditPaths()
|
|||
}
|
||||
}
|
||||
|
||||
simgear::pkg::PackageRef QtLauncher::packageForAircraftURI(QUrl uri) const
|
||||
{
|
||||
if (uri.scheme() != "package") {
|
||||
qWarning() << "invalid URL scheme:" << uri;
|
||||
return simgear::pkg::PackageRef();
|
||||
}
|
||||
|
||||
QString ident = uri.path();
|
||||
qDebug() << Q_FUNC_INFO << uri << ident;
|
||||
return globals->packageRoot()->getPackageById(ident.toStdString());
|
||||
}
|
||||
|
||||
#include "QtLauncher.moc"
|
||||
|
||||
|
|
|
@ -26,8 +26,12 @@
|
|||
#include <QStringList>
|
||||
#include <QModelIndex>
|
||||
#include <QTimer>
|
||||
#include <QUrl>
|
||||
|
||||
|
||||
#include <Airports/airport.hxx>
|
||||
#include <simgear/package/Package.hxx>
|
||||
#include <simgear/package/Catalog.hxx>
|
||||
|
||||
namespace Ui
|
||||
{
|
||||
|
@ -62,7 +66,9 @@ private slots:
|
|||
|
||||
void onAirportChoiceSelected(const QModelIndex& index);
|
||||
void onAircraftSelected(const QModelIndex& index);
|
||||
|
||||
void onRequestPackageInstall(const QModelIndex& index);
|
||||
void onCancelDownload(const QModelIndex& index);
|
||||
|
||||
void onPopupAirportHistory();
|
||||
void onPopupAircraftHistory();
|
||||
|
||||
|
@ -81,6 +87,9 @@ private slots:
|
|||
void onEditPaths();
|
||||
|
||||
void onAirportDiagramClicked(FGRunwayRef rwy);
|
||||
|
||||
void onAircraftInstalledCompleted(QModelIndex index);
|
||||
void onAircraftInstallFailed(QModelIndex index, QString errorMessage);
|
||||
private:
|
||||
void setAirport(FGAirportRef ref);
|
||||
void updateSelectedAircraft();
|
||||
|
@ -88,20 +97,22 @@ private:
|
|||
void restoreSettings();
|
||||
void saveSettings();
|
||||
|
||||
QModelIndex proxyIndexForAircraftPath(QString path) const;
|
||||
QModelIndex sourceIndexForAircraftPath(QString path) const;
|
||||
QModelIndex proxyIndexForAircraftURI(QUrl uri) const;
|
||||
QModelIndex sourceIndexForAircraftURI(QUrl uri) const;
|
||||
|
||||
void setEnableDisableOptionFromCheckbox(QCheckBox* cbox, QString name) const;
|
||||
|
||||
simgear::pkg::PackageRef packageForAircraftURI(QUrl uri) const;
|
||||
|
||||
QScopedPointer<Ui::Launcher> m_ui;
|
||||
AirportSearchModel* m_airportsModel;
|
||||
AircraftProxyModel* m_aircraftProxy;
|
||||
AircraftItemModel* m_aircraftModel;
|
||||
FGAirportRef m_selectedAirport;
|
||||
|
||||
QString m_selectedAircraft;
|
||||
QStringList m_recentAircraft,
|
||||
m_recentAirports;
|
||||
QUrl m_selectedAircraft;
|
||||
QList<QUrl> m_recentAircraft;
|
||||
QStringList m_recentAirports;
|
||||
QTimer* m_subsystemIdleTimer;
|
||||
|
||||
int m_ratingFilters[4];
|
||||
|
|
|
@ -546,11 +546,9 @@ int fgInitAircraft(bool reinit)
|
|||
// code in FindAndCacheAircraft works as normal
|
||||
// note since we may be using a variant, we can't use the package ID
|
||||
size_t lastDot = aircraftId.rfind('.');
|
||||
if (lastDot != std::string::npos) {
|
||||
aircraftId = aircraftId.substr(lastDot + 1);
|
||||
aircraftProp->setStringValue(aircraftId);
|
||||
|
||||
}
|
||||
assert(lastDot != std::string::npos);
|
||||
aircraftId = aircraftId.substr(lastDot + 1);
|
||||
aircraftProp->setStringValue(aircraftId);
|
||||
// run the traditional-code path below
|
||||
} else {
|
||||
#if 0
|
||||
|
|
|
@ -47,9 +47,8 @@ typedef nasal::Ghost<pkg::PackageRef> NasalPackage;
|
|||
typedef nasal::Ghost<pkg::CatalogRef> NasalCatalog;
|
||||
typedef nasal::Ghost<pkg::InstallRef> NasalInstall;
|
||||
|
||||
namespace {
|
||||
|
||||
class FGDelegate : public pkg::Delegate
|
||||
|
||||
class FGHTTPClient::FGDelegate : public pkg::Delegate
|
||||
{
|
||||
public:
|
||||
virtual void refreshComplete()
|
||||
|
@ -67,48 +66,52 @@ public:
|
|||
r->scheduleToUpdate((*it)->install());
|
||||
}
|
||||
}
|
||||
|
||||
virtual void failedRefresh(pkg::Catalog* aCat, FailureCode aReason)
|
||||
|
||||
virtual void catalogRefreshed(pkg::CatalogRef aCat, StatusCode aReason)
|
||||
{
|
||||
if (aCat.ptr() == NULL) {
|
||||
SG_LOG(SG_IO, SG_INFO, "refresh of all catalogs done");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (aReason) {
|
||||
case pkg::Delegate::FAIL_SUCCESS:
|
||||
SG_LOG(SG_IO, SG_WARN, "refresh of Catalog done");
|
||||
case pkg::Delegate::STATUS_SUCCESS:
|
||||
case pkg::Delegate::STATUS_REFRESHED:
|
||||
SG_LOG(SG_IO, SG_INFO, "refresh of Catalog done:" << aCat->url());
|
||||
break;
|
||||
|
||||
|
||||
case pkg::Delegate::STATUS_IN_PROGRESS:
|
||||
SG_LOG(SG_IO, SG_INFO, "refresh of Catalog started:" << aCat->url());
|
||||
break;
|
||||
|
||||
default:
|
||||
SG_LOG(SG_IO, SG_WARN, "refresh of Catalog " << aCat->url() << " failed:" << aReason);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void startInstall(pkg::Install* aInstall)
|
||||
virtual void startInstall(pkg::InstallRef aInstall)
|
||||
{
|
||||
SG_LOG(SG_IO, SG_INFO, "beginning install of:" << aInstall->package()->id()
|
||||
<< " to local path:" << aInstall->path());
|
||||
|
||||
}
|
||||
|
||||
virtual void installProgress(pkg::Install* aInstall, unsigned int aBytes, unsigned int aTotal)
|
||||
virtual void installProgress(pkg::InstallRef aInstall, unsigned int aBytes, unsigned int aTotal)
|
||||
{
|
||||
SG_LOG(SG_IO, SG_INFO, "installing:" << aInstall->package()->id() << ":"
|
||||
<< aBytes << " of " << aTotal);
|
||||
}
|
||||
|
||||
virtual void finishInstall(pkg::Install* aInstall)
|
||||
virtual void finishInstall(pkg::InstallRef aInstall, StatusCode aReason)
|
||||
{
|
||||
if (aReason == STATUS_SUCCESS) {
|
||||
SG_LOG(SG_IO, SG_INFO, "finished install of:" << aInstall->package()->id()
|
||||
<< " to local path:" << aInstall->path());
|
||||
} else {
|
||||
SG_LOG(SG_IO, SG_WARN, "install failed of:" << aInstall->package()->id()
|
||||
<< " to local path:" << aInstall->path());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
virtual void failedInstall(pkg::Install* aInstall, FailureCode aReason)
|
||||
{
|
||||
SG_LOG(SG_IO, SG_WARN, "install failed of:" << aInstall->package()->id()
|
||||
<< " to local path:" << aInstall->path());
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // of anonymous namespace
|
||||
}; // of FGHTTPClient::FGDelegate
|
||||
|
||||
FGHTTPClient::FGHTTPClient() :
|
||||
_inited(false)
|
||||
|
@ -142,11 +145,12 @@ void FGHTTPClient::init()
|
|||
// package system needs access to the HTTP engine too
|
||||
packageRoot->setHTTPClient(_http.get());
|
||||
|
||||
packageRoot->setDelegate(new FGDelegate);
|
||||
_packageDelegate.reset(new FGDelegate);
|
||||
packageRoot->addDelegate(_packageDelegate.get());
|
||||
|
||||
const char * defaultCatalogId = fgGetString("/sim/package-system/default-catalog/id", "org.flightgear.default" );
|
||||
const char * defaultCatalogId = fgGetString("/sim/package-system/default-catalog/id", "org.flightgear.official" );
|
||||
const char * defaultCatalogUrl = fgGetString("/sim/package-system/default-catalog/url",
|
||||
"http://fgfs.goneabitbursar.com/pkg/" FLIGHTGEAR_VERSION "/default-catalog.xml");
|
||||
"http://fgfs.goneabitbursar.com/pkg/" FLIGHTGEAR_VERSION "/catalog.xml");
|
||||
// setup default catalog if not present
|
||||
pkg::Catalog* defaultCatalog = packageRoot->getCatalogById( defaultCatalogId );
|
||||
if (!defaultCatalog) {
|
||||
|
@ -299,7 +303,13 @@ void FGHTTPClient::postinit()
|
|||
|
||||
void FGHTTPClient::shutdown()
|
||||
{
|
||||
_http.reset();
|
||||
pkg::Root* packageRoot = globals->packageRoot();
|
||||
if (packageRoot && _packageDelegate.get()) {
|
||||
packageRoot->removeDelegate(_packageDelegate.get());
|
||||
}
|
||||
|
||||
_packageDelegate.reset();
|
||||
_http.reset();
|
||||
}
|
||||
|
||||
void FGHTTPClient::update(double)
|
||||
|
|
|
@ -42,8 +42,11 @@ public:
|
|||
virtual void update(double);
|
||||
|
||||
private:
|
||||
class FGDelegate;
|
||||
|
||||
bool _inited;
|
||||
std::auto_ptr<simgear::HTTP::Client> _http;
|
||||
std::auto_ptr<FGDelegate> _packageDelegate;
|
||||
};
|
||||
|
||||
#endif // FG_HTTP_CLIENT_HXX
|
||||
|
|
Loading…
Add table
Reference in a new issue