From 6095646a6217486ef4525599575bc44372323a84 Mon Sep 17 00:00:00 2001 From: James Turner Date: Fri, 15 Apr 2016 17:06:53 +0100 Subject: [PATCH] Prompt the user when the default hangar is missing. MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit In the Qt launcher, when the default hangar isn’t found, show a hint in the aircraft list. --- src/GUI/AircraftItemDelegate.cxx | 33 ++++++++-- src/GUI/AircraftModel.cxx | 108 ++++++++++++++++++++++++++----- src/GUI/AircraftModel.hxx | 16 ++++- src/GUI/CMakeLists.txt | 3 +- src/GUI/NoOfficialHangar.ui | 37 +++++++++++ src/GUI/PathsDialog.cxx | 21 +++--- src/GUI/PathsDialog.hxx | 2 + src/GUI/QtLauncher.cxx | 69 +++++++++++++++++++- src/GUI/QtLauncher_private.hxx | 3 + 9 files changed, 256 insertions(+), 36 deletions(-) create mode 100644 src/GUI/NoOfficialHangar.ui diff --git a/src/GUI/AircraftItemDelegate.cxx b/src/GUI/AircraftItemDelegate.cxx index 0e293bc19..6945645b1 100644 --- a/src/GUI/AircraftItemDelegate.cxx +++ b/src/GUI/AircraftItemDelegate.cxx @@ -43,7 +43,21 @@ AircraftItemDelegate::AircraftItemDelegate(QListView* view) : void AircraftItemDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, 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(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 if (option.state & QStyle::State_Selected) { 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()); } - QRect contentRect = option.rect.adjusted(MARGIN, MARGIN, -MARGIN, -MARGIN); QPixmap thumbnail = index.data(Qt::DecorationRole).value(); quint32 yPos = contentRect.center().y() - (thumbnail.height() / 2); @@ -130,6 +143,8 @@ void AircraftItemDelegate::paint(QPainter * painter, const QStyleOptionViewItem r.moveLeft(r.right()); r.setHeight(qMax(24, smallMetrics.height() + MARGIN)); + painter->setRenderHint(QPainter::Antialiasing, true); + if (index.data(AircraftHasRatingsRole).toBool()) { drawRating(painter, "Flight model:", r, index.data(AircraftRatingRole).toInt()); r.moveTop(r.bottom()); @@ -142,8 +157,6 @@ void AircraftItemDelegate::paint(QPainter * painter, const QStyleOptionViewItem drawRating(painter, "Exterior:", r, index.data(AircraftRatingRole + 3).toInt()); } - QVariant v = index.data(AircraftPackageStatusRole); - AircraftItemStatus status = static_cast(v.toInt()); double downloadFraction = 0.0; if (status != PackageInstalled) { @@ -205,10 +218,22 @@ void AircraftItemDelegate::paint(QPainter * painter, const QStyleOptionViewItem painter->setPen(Qt::black); painter->drawText(infoTextRect, Qt::AlignLeft | Qt::AlignVCenter, infoText); } // of update / install / download status + + painter->setRenderHint(QPainter::Antialiasing, false); + } QSize AircraftItemDelegate::sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index) const { + QVariant v = index.data(AircraftPackageStatusRole); + AircraftItemStatus status = static_cast(v.toInt()); + + if (status == NoOfficialCatalogMessage) { + QSize r = option.rect.size(); + r.setHeight(100); + return r; + } + QRect contentRect = option.rect.adjusted(MARGIN, MARGIN, -MARGIN, -MARGIN); QSize thumbnailSize = index.data(AircraftThumbnailSizeRole).toSize(); diff --git a/src/GUI/AircraftModel.cxx b/src/GUI/AircraftModel.cxx index 92934790f..4c0b59886 100644 --- a/src/GUI/AircraftModel.cxx +++ b/src/GUI/AircraftModel.cxx @@ -434,7 +434,8 @@ private: AircraftItemModel::AircraftItemModel(QObject* pr ) : QAbstractListModel(pr), - m_scanThread(NULL) + m_scanThread(NULL), + m_showOfficialHangarMessage(false) { } @@ -465,14 +466,48 @@ void AircraftItemModel::setPaths(QStringList 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() { abandonCurrentScan(); - beginResetModel(); - m_items.clear(); - m_activeVariant.clear(); - endResetModel(); + int firstRow = (m_showOfficialHangarMessage ? 1 : 0); + int numToRemove = m_items.size() - firstRow; + int lastRow = firstRow + numToRemove - 1; + + beginRemoveRows(QModelIndex(), firstRow, lastRow); + m_items.remove(firstRow, numToRemove); + m_activeVariant.remove(firstRow, numToRemove); + endRemoveRows(); QStringList dirs = m_paths; @@ -504,10 +539,34 @@ void AircraftItemModel::abandonCurrentScan() void AircraftItemModel::refreshPackages() { - beginResetModel(); - m_packages = m_packageRoot->allPackages(); - m_packageVariant.resize(m_packages.size()); - endResetModel(); + simgear::pkg::PackageList newPkgs = m_packageRoot->allPackages(); + int firstRow = m_items.size(); + int newSize = newPkgs.size(); + + 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 @@ -517,8 +576,19 @@ int AircraftItemModel::rowCount(const QModelIndex& parent) const QVariant AircraftItemModel::data(const QModelIndex& index, int role) const { - if (index.row() >= m_items.size()) { - quint32 packageIndex = index.row() - m_items.size(); + int row = index.row(); + 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) { return m_packageVariant.at(packageIndex); @@ -537,11 +607,11 @@ QVariant AircraftItemModel::data(const QModelIndex& index, int role) const return dataFromPackage(pkg, variantIndex, role); } else { if (role == AircraftVariantRole) { - return m_activeVariant.at(index.row()); + return m_activeVariant.at(row); } - quint32 variantIndex = m_activeVariant.at(index.row()); - const AircraftItemPtr item(m_items.at(index.row())); + quint32 variantIndex = m_activeVariant.at(row); + const AircraftItemPtr item(m_items.at(row)); return dataFromItem(item, variantIndex, role); } } @@ -761,6 +831,10 @@ bool AircraftItemModel::setData(const QModelIndex &index, const QVariant &value, QModelIndex AircraftItemModel::indexOfAircraftURI(QUrl uri) const { + if (uri.isEmpty()) { + return QModelIndex(); + } + if (uri.isLocalFile()) { QString path = uri.toLocalFile(); for (int row=0; row getPackageById(ident.toStdString()); if (pkg) { for (size_t i=0; i < m_packages.size(); ++i) { if (m_packages[i] == pkg) { - return index(m_items.size() + i); + return index(rowOffset + i); } } // of linear package scan } @@ -820,8 +896,6 @@ void AircraftItemModel::onScanFinished() 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: diff --git a/src/GUI/AircraftModel.hxx b/src/GUI/AircraftModel.hxx index 90c243ceb..96ea5aab6 100644 --- a/src/GUI/AircraftModel.hxx +++ b/src/GUI/AircraftModel.hxx @@ -94,7 +94,8 @@ enum AircraftItemStatus { PackageInstalled, PackageUpdateAvailable, PackageQueued, - PackageDownloading + PackageDownloading, + NoOfficialCatalogMessage }; class AircraftItemModel : public QAbstractListModel @@ -130,6 +131,14 @@ public: */ 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 * aircraft or not. Checks for -set.xml files one level down in the tree. @@ -157,7 +166,7 @@ private: quint32 variantIndex, int role) const; QVariant packageThumbnail(simgear::pkg::PackageRef p, int index, bool download = true) const; - + void abandonCurrentScan(); void refreshPackages(); @@ -168,7 +177,8 @@ private: AircraftScanThread* m_scanThread; QVector m_items; PackageDelegate* m_delegate; - + bool m_showOfficialHangarMessage; + QVector m_activeVariant; QVector m_packageVariant; diff --git a/src/GUI/CMakeLists.txt b/src/GUI/CMakeLists.txt index 18d42495d..226e08b8f 100644 --- a/src/GUI/CMakeLists.txt +++ b/src/GUI/CMakeLists.txt @@ -75,7 +75,8 @@ if (HAVE_QT) SetupRootDialog.ui AddCatalogDialog.ui PathsDialog.ui - LocationWidget.ui) + LocationWidget.ui + NoOfficialHanagar.ui) qt5_add_resources(qrc_sources resources.qrc) diff --git a/src/GUI/NoOfficialHangar.ui b/src/GUI/NoOfficialHangar.ui new file mode 100644 index 000000000..d5bd60f08 --- /dev/null +++ b/src/GUI/NoOfficialHangar.ui @@ -0,0 +1,37 @@ + + + NoOfficialHangarMessage + + + + 0 + 0 + 607 + 134 + + + + Form + + + false + + + + + + <html><head/><body><p>The official FlightGear aircraft hangar is not selected, so only the default aircraft will be available. You can add the official hangar by <a href="action:add-official"><span style=" text-decoration: underline; color:#0000ff;">clicking here</span></a>, or go to the 'add-ons' page to add other hangars or aircraft folders.</p><p><a href="action:hide"><span style=" text-decoration: underline; color:#0000ff;">Click here</span></a> to permanently hide this message.</p></body></html> + + + Qt::RichText + + + true + + + + + + + + diff --git a/src/GUI/PathsDialog.cxx b/src/GUI/PathsDialog.cxx index 9ded69d40..4dcb6a042 100644 --- a/src/GUI/PathsDialog.cxx +++ b/src/GUI/PathsDialog.cxx @@ -190,20 +190,25 @@ void AddOnsPage::onAddCatalog() } void AddOnsPage::onAddDefaultCatalog() +{ + addDefaultCatalog(this); + + m_catalogsModel->refresh(); + updateUi(); +} + +void AddOnsPage::addDefaultCatalog(QWidget* pr) { // check it's not a duplicate somehow FGHTTPClient* http = globals->get_subsystem(); if (http->isDefaultCatalogInstalled()) return; - QScopedPointer dlg(new AddCatalogDialog(this, m_packageRoot)); - QUrl url(QString::fromStdString(http->getDefaultCatalogUrl())); - dlg->setUrlAndDownload(url); - dlg->exec(); - if (dlg->result() == QDialog::Accepted) { - m_catalogsModel->refresh(); - updateUi(); - } + QScopedPointer dlg(new AddCatalogDialog(pr, globals->packageRoot())); + QUrl url(QString::fromStdString(http->getDefaultCatalogUrl())); + dlg->setUrlAndDownload(url); + dlg->exec(); + } void AddOnsPage::onRemoveCatalog() diff --git a/src/GUI/PathsDialog.hxx b/src/GUI/PathsDialog.hxx index b6303421e..749bb9318 100644 --- a/src/GUI/PathsDialog.hxx +++ b/src/GUI/PathsDialog.hxx @@ -20,6 +20,8 @@ public: explicit AddOnsPage(QWidget *parent, simgear::pkg::RootRef root); ~AddOnsPage(); + static void addDefaultCatalog(QWidget* pr); + signals: void downloadDirChanged(); void sceneryPathsChanged(); diff --git a/src/GUI/QtLauncher.cxx b/src/GUI/QtLauncher.cxx index 59da6532b..c82e7e215 100644 --- a/src/GUI/QtLauncher.cxx +++ b/src/GUI/QtLauncher.cxx @@ -59,6 +59,8 @@ #include #include "ui_Launcher.h" +#include "ui_NoOfficialHangar.h" + #include "EditRatingsFilterDialog.hxx" #include "AircraftItemDelegate.hxx" #include "AircraftModel.hxx" @@ -293,12 +295,18 @@ public slots: protected: bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const { + QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent); + QVariant v = index.data(AircraftPackageStatusRole); + AircraftItemStatus status = static_cast(v.toInt()); + if (status == NoOfficialCatalogMessage) { + return true; + } + if (!QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent)) { return false; } if (m_onlyShowInstalled) { - QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent); QVariant v = index.data(AircraftPackageStatusRole); AircraftItemStatus status = static_cast(v.toInt()); if (status == PackageNotInstalled) { @@ -307,7 +315,6 @@ protected: } if (!m_onlyShowInstalled && m_ratingsFilter) { - QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent); for (int i=0; i<4; ++i) { if (m_ratings[i] > index.data(AircraftRatingRole + i).toInt()) { return false; @@ -324,6 +331,26 @@ private: 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() { Q_INIT_RESOURCE(resources); @@ -593,6 +620,7 @@ QtLauncher::QtLauncher() : m_aircraftModel->setPackageRoot(globals->packageRoot()); m_aircraftModel->scanDirs(); + checkOfficialCatalogMessage(); restoreSettings(); } @@ -1163,6 +1191,7 @@ void QtLauncher::onSubsytemIdleTimeout() void QtLauncher::onDownloadDirChanged() { + // replace existing package root globals->get_subsystem()->shutdown(); globals->setPackageRoot(simgear::pkg::RootRef()); @@ -1172,16 +1201,50 @@ void QtLauncher::onDownloadDirChanged() globals->get_subsystem()->init(); - // re-scan the aircraft list QSettings settings; + // re-scan the aircraft list m_aircraftModel->setPackageRoot(globals->packageRoot()); m_aircraftModel->setPaths(settings.value("aircraft-paths").toStringList()); m_aircraftModel->scanDirs(); + checkOfficialCatalogMessage(); + // re-set scenery dirs setSceneryPaths(); } +void QtLauncher::checkOfficialCatalogMessage() +{ + QSettings settings; + bool showOfficialCatalogMesssage = !globals->get_subsystem()->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 { if (uri.scheme() != "package") { diff --git a/src/GUI/QtLauncher_private.hxx b/src/GUI/QtLauncher_private.hxx index 58dc053fe..e53c7ba19 100644 --- a/src/GUI/QtLauncher_private.hxx +++ b/src/GUI/QtLauncher_private.hxx @@ -115,6 +115,9 @@ private: simgear::pkg::PackageRef packageForAircraftURI(QUrl uri) const; + void checkOfficialCatalogMessage(); + void onOfficialCatalogMessageLink(QUrl link); + // need to wait after a model reset before restoring selection and // scrolling, to give the view time it seems. void delayedAircraftModelReset(); -- 2.39.5