1
0
Fork 0

Prompt the user when the default hangar is missing.

In the Qt launcher, when the default hangar isn’t found, show
a hint in the aircraft list.
This commit is contained in:
James Turner 2016-04-15 17:06:53 +01:00
parent c15e4753ac
commit 6095646a62
9 changed files with 256 additions and 36 deletions

View file

@ -43,7 +43,21 @@ AircraftItemDelegate::AircraftItemDelegate(QListView* view) :
void AircraftItemDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, void AircraftItemDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option,
const QModelIndex & index) const const QModelIndex & index) const
{ {
painter->setRenderHint(QPainter::Antialiasing); QRect contentRect = option.rect.adjusted(MARGIN, MARGIN, -MARGIN, -MARGIN);
QVariant v = index.data(AircraftPackageStatusRole);
AircraftItemStatus status = static_cast<AircraftItemStatus>(v.toInt());
if (status == NoOfficialCatalogMessage) {
painter->setPen(QColor(0x7f, 0x7f, 0x7f));
painter->setBrush(Qt::NoBrush);
// draw bottom dividing line
painter->drawLine(contentRect.left(), contentRect.bottom() + MARGIN,
contentRect.right(), contentRect.bottom() + MARGIN);
return;
}
// selection feedback rendering // selection feedback rendering
if (option.state & QStyle::State_Selected) { if (option.state & QStyle::State_Selected) {
QLinearGradient grad(option.rect.topLeft(), option.rect.bottomLeft()); QLinearGradient grad(option.rect.topLeft(), option.rect.bottomLeft());
@ -57,7 +71,6 @@ void AircraftItemDelegate::paint(QPainter * painter, const QStyleOptionViewItem
painter->drawLine(option.rect.topLeft(), option.rect.topRight()); painter->drawLine(option.rect.topLeft(), option.rect.topRight());
} }
QRect contentRect = option.rect.adjusted(MARGIN, MARGIN, -MARGIN, -MARGIN);
QPixmap thumbnail = index.data(Qt::DecorationRole).value<QPixmap>(); QPixmap thumbnail = index.data(Qt::DecorationRole).value<QPixmap>();
quint32 yPos = contentRect.center().y() - (thumbnail.height() / 2); quint32 yPos = contentRect.center().y() - (thumbnail.height() / 2);
@ -130,6 +143,8 @@ void AircraftItemDelegate::paint(QPainter * painter, const QStyleOptionViewItem
r.moveLeft(r.right()); r.moveLeft(r.right());
r.setHeight(qMax(24, smallMetrics.height() + MARGIN)); r.setHeight(qMax(24, smallMetrics.height() + MARGIN));
painter->setRenderHint(QPainter::Antialiasing, true);
if (index.data(AircraftHasRatingsRole).toBool()) { if (index.data(AircraftHasRatingsRole).toBool()) {
drawRating(painter, "Flight model:", r, index.data(AircraftRatingRole).toInt()); drawRating(painter, "Flight model:", r, index.data(AircraftRatingRole).toInt());
r.moveTop(r.bottom()); r.moveTop(r.bottom());
@ -142,8 +157,6 @@ void AircraftItemDelegate::paint(QPainter * painter, const QStyleOptionViewItem
drawRating(painter, "Exterior:", r, index.data(AircraftRatingRole + 3).toInt()); drawRating(painter, "Exterior:", r, index.data(AircraftRatingRole + 3).toInt());
} }
QVariant v = index.data(AircraftPackageStatusRole);
AircraftItemStatus status = static_cast<AircraftItemStatus>(v.toInt());
double downloadFraction = 0.0; double downloadFraction = 0.0;
if (status != PackageInstalled) { if (status != PackageInstalled) {
@ -205,10 +218,22 @@ void AircraftItemDelegate::paint(QPainter * painter, const QStyleOptionViewItem
painter->setPen(Qt::black); painter->setPen(Qt::black);
painter->drawText(infoTextRect, Qt::AlignLeft | Qt::AlignVCenter, infoText); painter->drawText(infoTextRect, Qt::AlignLeft | Qt::AlignVCenter, infoText);
} // of update / install / download status } // of update / install / download status
painter->setRenderHint(QPainter::Antialiasing, false);
} }
QSize AircraftItemDelegate::sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index) const QSize AircraftItemDelegate::sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index) const
{ {
QVariant v = index.data(AircraftPackageStatusRole);
AircraftItemStatus status = static_cast<AircraftItemStatus>(v.toInt());
if (status == NoOfficialCatalogMessage) {
QSize r = option.rect.size();
r.setHeight(100);
return r;
}
QRect contentRect = option.rect.adjusted(MARGIN, MARGIN, -MARGIN, -MARGIN); QRect contentRect = option.rect.adjusted(MARGIN, MARGIN, -MARGIN, -MARGIN);
QSize thumbnailSize = index.data(AircraftThumbnailSizeRole).toSize(); QSize thumbnailSize = index.data(AircraftThumbnailSizeRole).toSize();

View file

@ -434,7 +434,8 @@ private:
AircraftItemModel::AircraftItemModel(QObject* pr ) : AircraftItemModel::AircraftItemModel(QObject* pr ) :
QAbstractListModel(pr), QAbstractListModel(pr),
m_scanThread(NULL) m_scanThread(NULL),
m_showOfficialHangarMessage(false)
{ {
} }
@ -465,14 +466,48 @@ void AircraftItemModel::setPaths(QStringList paths)
m_paths = paths; m_paths = paths;
} }
void AircraftItemModel::setOfficialHangarMessageVisible(bool vis)
{
if (m_showOfficialHangarMessage == vis) {
return;
}
m_showOfficialHangarMessage = vis;
if (vis) {
beginInsertRows(QModelIndex(), 0, 0);
m_items.prepend(AircraftItemPtr(new AircraftItem));
m_activeVariant.prepend(0);
endInsertRows();
} else {
beginRemoveRows(QModelIndex(), 0, 0);
m_items.removeAt(0);
m_activeVariant.removeAt(0);
endRemoveRows();
}
}
QModelIndex AircraftItemModel::officialHangarMessageIndex() const
{
if (!m_showOfficialHangarMessage) {
return QModelIndex();
}
return index(0);
}
void AircraftItemModel::scanDirs() void AircraftItemModel::scanDirs()
{ {
abandonCurrentScan(); abandonCurrentScan();
beginResetModel(); int firstRow = (m_showOfficialHangarMessage ? 1 : 0);
m_items.clear(); int numToRemove = m_items.size() - firstRow;
m_activeVariant.clear(); int lastRow = firstRow + numToRemove - 1;
endResetModel();
beginRemoveRows(QModelIndex(), firstRow, lastRow);
m_items.remove(firstRow, numToRemove);
m_activeVariant.remove(firstRow, numToRemove);
endRemoveRows();
QStringList dirs = m_paths; QStringList dirs = m_paths;
@ -504,10 +539,34 @@ void AircraftItemModel::abandonCurrentScan()
void AircraftItemModel::refreshPackages() void AircraftItemModel::refreshPackages()
{ {
beginResetModel(); simgear::pkg::PackageList newPkgs = m_packageRoot->allPackages();
m_packages = m_packageRoot->allPackages(); int firstRow = m_items.size();
m_packageVariant.resize(m_packages.size()); int newSize = newPkgs.size();
endResetModel();
if (m_packages.size() != newPkgs.size()) {
int oldSize = m_packages.size();
if (newSize > oldSize) {
// growing
int firstNewRow = firstRow + oldSize;
int lastNewRow = firstRow + newSize - 1;
beginInsertRows(QModelIndex(), firstNewRow, lastNewRow);
m_packages = newPkgs;
m_packageVariant.resize(newSize);
endInsertRows();
} else {
// shrinking
int firstOldRow = firstRow + newSize;
int lastOldRow = firstRow + oldSize - 1;
beginRemoveRows(QModelIndex(), firstOldRow, lastOldRow);
m_packages = newPkgs;
m_packageVariant.resize(newSize);
endRemoveRows();
}
} else {
m_packages = newPkgs;
}
emit dataChanged(index(firstRow), index(firstRow + newSize - 1));
} }
int AircraftItemModel::rowCount(const QModelIndex& parent) const int AircraftItemModel::rowCount(const QModelIndex& parent) const
@ -517,8 +576,19 @@ int AircraftItemModel::rowCount(const QModelIndex& parent) const
QVariant AircraftItemModel::data(const QModelIndex& index, int role) const QVariant AircraftItemModel::data(const QModelIndex& index, int role) const
{ {
if (index.row() >= m_items.size()) { int row = index.row();
quint32 packageIndex = index.row() - m_items.size(); if (m_showOfficialHangarMessage) {
if (row == 0) {
if (role == AircraftPackageStatusRole) {
return NoOfficialCatalogMessage;
}
return QVariant();
}
}
if (row >= m_items.size()) {
quint32 packageIndex = row - m_items.size();
if (role == AircraftVariantRole) { if (role == AircraftVariantRole) {
return m_packageVariant.at(packageIndex); return m_packageVariant.at(packageIndex);
@ -537,11 +607,11 @@ QVariant AircraftItemModel::data(const QModelIndex& index, int role) const
return dataFromPackage(pkg, variantIndex, role); return dataFromPackage(pkg, variantIndex, role);
} else { } else {
if (role == AircraftVariantRole) { if (role == AircraftVariantRole) {
return m_activeVariant.at(index.row()); return m_activeVariant.at(row);
} }
quint32 variantIndex = m_activeVariant.at(index.row()); quint32 variantIndex = m_activeVariant.at(row);
const AircraftItemPtr item(m_items.at(index.row())); const AircraftItemPtr item(m_items.at(row));
return dataFromItem(item, variantIndex, role); return dataFromItem(item, variantIndex, role);
} }
} }
@ -761,6 +831,10 @@ bool AircraftItemModel::setData(const QModelIndex &index, const QVariant &value,
QModelIndex AircraftItemModel::indexOfAircraftURI(QUrl uri) const QModelIndex AircraftItemModel::indexOfAircraftURI(QUrl uri) const
{ {
if (uri.isEmpty()) {
return QModelIndex();
}
if (uri.isLocalFile()) { if (uri.isLocalFile()) {
QString path = uri.toLocalFile(); QString path = uri.toLocalFile();
for (int row=0; row <m_items.size(); ++row) { for (int row=0; row <m_items.size(); ++row) {
@ -778,11 +852,13 @@ QModelIndex AircraftItemModel::indexOfAircraftURI(QUrl uri) const
} }
} else if (uri.scheme() == "package") { } else if (uri.scheme() == "package") {
QString ident = uri.path(); QString ident = uri.path();
int rowOffset = m_items.size();
PackageRef pkg = m_packageRoot->getPackageById(ident.toStdString()); PackageRef pkg = m_packageRoot->getPackageById(ident.toStdString());
if (pkg) { if (pkg) {
for (size_t i=0; i < m_packages.size(); ++i) { for (size_t i=0; i < m_packages.size(); ++i) {
if (m_packages[i] == pkg) { if (m_packages[i] == pkg) {
return index(m_items.size() + i); return index(rowOffset + i);
} }
} // of linear package scan } // of linear package scan
} }
@ -820,8 +896,6 @@ void AircraftItemModel::onScanFinished()
void AircraftItemModel::installFailed(QModelIndex index, simgear::pkg::Delegate::StatusCode reason) void AircraftItemModel::installFailed(QModelIndex index, simgear::pkg::Delegate::StatusCode reason)
{ {
Q_ASSERT(index.row() >= m_items.size());
QString msg; QString msg;
switch (reason) { switch (reason) {
case Delegate::FAIL_CHECKSUM: case Delegate::FAIL_CHECKSUM:

View file

@ -94,7 +94,8 @@ enum AircraftItemStatus {
PackageInstalled, PackageInstalled,
PackageUpdateAvailable, PackageUpdateAvailable,
PackageQueued, PackageQueued,
PackageDownloading PackageDownloading,
NoOfficialCatalogMessage
}; };
class AircraftItemModel : public QAbstractListModel class AircraftItemModel : public QAbstractListModel
@ -130,6 +131,14 @@ public:
*/ */
bool isIndexRunnable(const QModelIndex& index) const; bool isIndexRunnable(const QModelIndex& index) const;
/**
* should we show the prompt about the official hangar not being installed
* or not?
*/
void setOfficialHangarMessageVisible(bool vis);
QModelIndex officialHangarMessageIndex() const;
/** /**
* @helper to determine if a particular path is likely to contain * @helper to determine if a particular path is likely to contain
* aircraft or not. Checks for -set.xml files one level down in the tree. * aircraft or not. Checks for -set.xml files one level down in the tree.
@ -157,7 +166,7 @@ private:
quint32 variantIndex, int role) const; quint32 variantIndex, int role) const;
QVariant packageThumbnail(simgear::pkg::PackageRef p, int index, bool download = true) const; QVariant packageThumbnail(simgear::pkg::PackageRef p, int index, bool download = true) const;
void abandonCurrentScan(); void abandonCurrentScan();
void refreshPackages(); void refreshPackages();
@ -168,7 +177,8 @@ private:
AircraftScanThread* m_scanThread; AircraftScanThread* m_scanThread;
QVector<AircraftItemPtr> m_items; QVector<AircraftItemPtr> m_items;
PackageDelegate* m_delegate; PackageDelegate* m_delegate;
bool m_showOfficialHangarMessage;
QVector<quint32> m_activeVariant; QVector<quint32> m_activeVariant;
QVector<quint32> m_packageVariant; QVector<quint32> m_packageVariant;

View file

@ -75,7 +75,8 @@ if (HAVE_QT)
SetupRootDialog.ui SetupRootDialog.ui
AddCatalogDialog.ui AddCatalogDialog.ui
PathsDialog.ui PathsDialog.ui
LocationWidget.ui) LocationWidget.ui
NoOfficialHanagar.ui)
qt5_add_resources(qrc_sources resources.qrc) qt5_add_resources(qrc_sources resources.qrc)

View file

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>NoOfficialHangarMessage</class>
<widget class="QWidget" name="NoOfficialHangarMessage">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>607</width>
<height>134</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<property name="autoFillBackground">
<bool>false</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The official FlightGear aircraft hangar is not selected, so only the default aircraft will be available. You can add the official hangar by &lt;a href=&quot;action:add-official&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;clicking here&lt;/span&gt;&lt;/a&gt;, or go to the 'add-ons' page to add other hangars or aircraft folders.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;action:hide&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Click here&lt;/span&gt;&lt;/a&gt; to permanently hide this message.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View file

@ -190,20 +190,25 @@ void AddOnsPage::onAddCatalog()
} }
void AddOnsPage::onAddDefaultCatalog() void AddOnsPage::onAddDefaultCatalog()
{
addDefaultCatalog(this);
m_catalogsModel->refresh();
updateUi();
}
void AddOnsPage::addDefaultCatalog(QWidget* pr)
{ {
// check it's not a duplicate somehow // check it's not a duplicate somehow
FGHTTPClient* http = globals->get_subsystem<FGHTTPClient>(); FGHTTPClient* http = globals->get_subsystem<FGHTTPClient>();
if (http->isDefaultCatalogInstalled()) if (http->isDefaultCatalogInstalled())
return; return;
QScopedPointer<AddCatalogDialog> dlg(new AddCatalogDialog(this, m_packageRoot)); QScopedPointer<AddCatalogDialog> dlg(new AddCatalogDialog(pr, globals->packageRoot()));
QUrl url(QString::fromStdString(http->getDefaultCatalogUrl())); QUrl url(QString::fromStdString(http->getDefaultCatalogUrl()));
dlg->setUrlAndDownload(url); dlg->setUrlAndDownload(url);
dlg->exec(); dlg->exec();
if (dlg->result() == QDialog::Accepted) {
m_catalogsModel->refresh();
updateUi();
}
} }
void AddOnsPage::onRemoveCatalog() void AddOnsPage::onRemoveCatalog()

View file

@ -20,6 +20,8 @@ public:
explicit AddOnsPage(QWidget *parent, simgear::pkg::RootRef root); explicit AddOnsPage(QWidget *parent, simgear::pkg::RootRef root);
~AddOnsPage(); ~AddOnsPage();
static void addDefaultCatalog(QWidget* pr);
signals: signals:
void downloadDirChanged(); void downloadDirChanged();
void sceneryPathsChanged(); void sceneryPathsChanged();

View file

@ -59,6 +59,8 @@
#include <simgear/package/Install.hxx> #include <simgear/package/Install.hxx>
#include "ui_Launcher.h" #include "ui_Launcher.h"
#include "ui_NoOfficialHangar.h"
#include "EditRatingsFilterDialog.hxx" #include "EditRatingsFilterDialog.hxx"
#include "AircraftItemDelegate.hxx" #include "AircraftItemDelegate.hxx"
#include "AircraftModel.hxx" #include "AircraftModel.hxx"
@ -293,12 +295,18 @@ public slots:
protected: protected:
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
{ {
QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent);
QVariant v = index.data(AircraftPackageStatusRole);
AircraftItemStatus status = static_cast<AircraftItemStatus>(v.toInt());
if (status == NoOfficialCatalogMessage) {
return true;
}
if (!QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent)) { if (!QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent)) {
return false; return false;
} }
if (m_onlyShowInstalled) { if (m_onlyShowInstalled) {
QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent);
QVariant v = index.data(AircraftPackageStatusRole); QVariant v = index.data(AircraftPackageStatusRole);
AircraftItemStatus status = static_cast<AircraftItemStatus>(v.toInt()); AircraftItemStatus status = static_cast<AircraftItemStatus>(v.toInt());
if (status == PackageNotInstalled) { if (status == PackageNotInstalled) {
@ -307,7 +315,6 @@ protected:
} }
if (!m_onlyShowInstalled && m_ratingsFilter) { if (!m_onlyShowInstalled && m_ratingsFilter) {
QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent);
for (int i=0; i<4; ++i) { for (int i=0; i<4; ++i) {
if (m_ratings[i] > index.data(AircraftRatingRole + i).toInt()) { if (m_ratings[i] > index.data(AircraftRatingRole + i).toInt()) {
return false; return false;
@ -324,6 +331,26 @@ private:
int m_ratings[4]; int m_ratings[4];
}; };
class NoOfficialHangarMessage : public QWidget
{
Q_OBJECT
public:
NoOfficialHangarMessage() :
m_ui(new Ui::NoOfficialHangarMessage)
{
m_ui->setupUi(this);
// proxy this signal upwards
connect(m_ui->label, &QLabel::linkActivated,
this, &NoOfficialHangarMessage::linkActivated);
}
Q_SIGNALS:
void linkActivated(QUrl link);
private:
Ui::NoOfficialHangarMessage* m_ui;
};
static void initQtResources() static void initQtResources()
{ {
Q_INIT_RESOURCE(resources); Q_INIT_RESOURCE(resources);
@ -593,6 +620,7 @@ QtLauncher::QtLauncher() :
m_aircraftModel->setPackageRoot(globals->packageRoot()); m_aircraftModel->setPackageRoot(globals->packageRoot());
m_aircraftModel->scanDirs(); m_aircraftModel->scanDirs();
checkOfficialCatalogMessage();
restoreSettings(); restoreSettings();
} }
@ -1163,6 +1191,7 @@ void QtLauncher::onSubsytemIdleTimeout()
void QtLauncher::onDownloadDirChanged() void QtLauncher::onDownloadDirChanged()
{ {
// replace existing package root // replace existing package root
globals->get_subsystem<FGHTTPClient>()->shutdown(); globals->get_subsystem<FGHTTPClient>()->shutdown();
globals->setPackageRoot(simgear::pkg::RootRef()); globals->setPackageRoot(simgear::pkg::RootRef());
@ -1172,16 +1201,50 @@ void QtLauncher::onDownloadDirChanged()
globals->get_subsystem<FGHTTPClient>()->init(); globals->get_subsystem<FGHTTPClient>()->init();
// re-scan the aircraft list
QSettings settings; QSettings settings;
// re-scan the aircraft list
m_aircraftModel->setPackageRoot(globals->packageRoot()); m_aircraftModel->setPackageRoot(globals->packageRoot());
m_aircraftModel->setPaths(settings.value("aircraft-paths").toStringList()); m_aircraftModel->setPaths(settings.value("aircraft-paths").toStringList());
m_aircraftModel->scanDirs(); m_aircraftModel->scanDirs();
checkOfficialCatalogMessage();
// re-set scenery dirs // re-set scenery dirs
setSceneryPaths(); setSceneryPaths();
} }
void QtLauncher::checkOfficialCatalogMessage()
{
QSettings settings;
bool showOfficialCatalogMesssage = !globals->get_subsystem<FGHTTPClient>()->isDefaultCatalogInstalled();
if (settings.value("hide-official-catalog-message").toBool()) {
showOfficialCatalogMesssage = false;
}
m_aircraftModel->setOfficialHangarMessageVisible(showOfficialCatalogMesssage);
if (showOfficialCatalogMesssage) {
NoOfficialHangarMessage* messageWidget = new NoOfficialHangarMessage;
connect(messageWidget, &NoOfficialHangarMessage::linkActivated,
this, &QtLauncher::onOfficialCatalogMessageLink);
QModelIndex index = m_aircraftProxy->mapFromSource(m_aircraftModel->officialHangarMessageIndex());
m_ui->aircraftList->setIndexWidget(index, messageWidget);
}
}
void QtLauncher::onOfficialCatalogMessageLink(QUrl link)
{
QString s = link.toString();
if (s == "action:hide") {
QSettings settings;
settings.setValue("hide-official-catalog-message", true);
} else if (s == "action:add-official") {
AddOnsPage::addDefaultCatalog(this);
}
checkOfficialCatalogMessage();
}
simgear::pkg::PackageRef QtLauncher::packageForAircraftURI(QUrl uri) const simgear::pkg::PackageRef QtLauncher::packageForAircraftURI(QUrl uri) const
{ {
if (uri.scheme() != "package") { if (uri.scheme() != "package") {

View file

@ -115,6 +115,9 @@ private:
simgear::pkg::PackageRef packageForAircraftURI(QUrl uri) const; simgear::pkg::PackageRef packageForAircraftURI(QUrl uri) const;
void checkOfficialCatalogMessage();
void onOfficialCatalogMessageLink(QUrl link);
// need to wait after a model reset before restoring selection and // need to wait after a model reset before restoring selection and
// scrolling, to give the view time it seems. // scrolling, to give the view time it seems.
void delayedAircraftModelReset(); void delayedAircraftModelReset();