From a482a74e1a88b2eefee0cd8a223ffb6e0efba056 Mon Sep 17 00:00:00 2001 From: James Turner Date: Wed, 15 Jun 2016 22:28:27 +0100 Subject: [PATCH] Allow scenery installation from the launcher. Replaces FGadmin functionality. --- src/GUI/CMakeLists.txt | 6 +- src/GUI/InstallSceneryDialog.cxx | 273 +++++++++++++++++++++++++++++++ src/GUI/InstallSceneryDialog.hxx | 70 ++++++++ src/GUI/InstallSceneryDialog.ui | 178 ++++++++++++++++++++ src/GUI/PathsDialog.cxx | 25 +++ src/GUI/PathsDialog.hxx | 2 + src/GUI/PathsDialog.ui | 24 +++ utils/TerraSync/CMakeLists.txt | 12 +- 8 files changed, 583 insertions(+), 7 deletions(-) create mode 100644 src/GUI/InstallSceneryDialog.cxx create mode 100644 src/GUI/InstallSceneryDialog.hxx create mode 100644 src/GUI/InstallSceneryDialog.ui diff --git a/src/GUI/CMakeLists.txt b/src/GUI/CMakeLists.txt index 02b0bafcd..c9afa8e12 100644 --- a/src/GUI/CMakeLists.txt +++ b/src/GUI/CMakeLists.txt @@ -76,7 +76,9 @@ if (HAVE_QT) AddCatalogDialog.ui PathsDialog.ui LocationWidget.ui - NoOfficialHangar.ui) + NoOfficialHangar.ui + InstallSceneryDialog.ui + ) qt5_add_resources(qrc_sources resources.qrc) @@ -109,6 +111,8 @@ if (HAVE_QT) QtMessageBox.hxx QtFileDialog.cxx QtFileDialog.hxx + InstallSceneryDialog.hxx + InstallSceneryDialog.cxx ${uic_sources} ${qrc_sources}) diff --git a/src/GUI/InstallSceneryDialog.cxx b/src/GUI/InstallSceneryDialog.cxx new file mode 100644 index 000000000..53ab348a0 --- /dev/null +++ b/src/GUI/InstallSceneryDialog.cxx @@ -0,0 +1,273 @@ +// InstallSceneryDialog.cxx - part of GUI launcher using Qt5 +// +// Written by James Turner, started June 2016. +// +// Copyright (C) 2016 James Turner +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#include "InstallSceneryDialog.hxx" +#include "ui_InstallSceneryDialog.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include
+#include
+#include + +#include + +class InstallSceneryThread : public QThread +{ + Q_OBJECT +public: + InstallSceneryThread(QString extractDir, QStringList files) : + m_extractDir(extractDir), + m_remainingPaths(files), + m_error(false), + m_totalBytes(0), + m_bytesRead(0) + { + + } + + virtual void run() + { + // pre-check each file + Q_FOREACH (QString path, m_remainingPaths) { + QFileInfo finfo(path); + QString baseName = finfo.baseName(); + QRegularExpression re("[e|w]\\d{2}0[n|s]\\d0", QRegularExpression::CaseInsensitiveOption); + Q_ASSERT(re.isValid()); + if (!re.match(baseName).hasMatch()) { + emit extractionError(path,tr("scenery archive name is not correct.")); + m_error = true; + return; + } + + QFile f(path); + f.open(QIODevice::ReadOnly); + QByteArray firstData = f.read(8192); + + if (!simgear::TarExtractor::isTarData((uint8_t*) firstData.data(), firstData.count())) { + emit extractionError(path,tr("file does not appear to be a scenery archive.")); + m_error = true; + return; + } + + m_totalBytes += f.size(); + } + + while (!m_remainingPaths.isEmpty() && !m_error) { + extractNextArchive(); + } + } + +signals: + void extractionError(QString file, QString msg); + + void progress(int percent); +private: + void extractNextArchive() + { + SGPath root(m_extractDir.toStdString()); + m_untar.reset(new simgear::TarExtractor(root)); + + QString path = m_remainingPaths.front(); + m_remainingPaths.pop_front(); + QFileInfo finfo(path); + + QFile f(path); + f.open(QIODevice::ReadOnly); + Q_ASSERT(f.isOpen()); + + while (!f.atEnd()) { + QByteArray bytes = f.read(4 * 1024 * 1024); + m_untar->extractBytes(bytes.constData(), bytes.size()); + m_bytesRead += bytes.size(); + + if (m_untar->hasError()) { + break; + } + + emit progress((m_bytesRead * 100) / m_totalBytes); + } + + if (m_untar->hasError() || !m_untar->isAtEndOfArchive()) { + emit extractionError(path, tr("unarchiving failed")); + m_error = true; + // try to clean up? + } + } + + QString m_extractDir; + QStringList m_remainingPaths; + std::auto_ptr m_untar; + bool m_error; + quint64 m_totalBytes; + quint64 m_bytesRead; +}; + +InstallSceneryDialog::InstallSceneryDialog(QWidget *parent, QString downloadDir) : + QDialog(parent, Qt::Dialog + | Qt::CustomizeWindowHint + | Qt::WindowTitleHint + | Qt::WindowSystemMenuHint + | Qt::WindowContextHelpButtonHint + | Qt::MSWindowsFixedSizeDialogHint), + m_state(STATE_START), + m_downloadDir(downloadDir), + ui(new Ui::InstallSceneryDialog) +{ + ui->setupUi(this); + if (m_downloadDir.isEmpty()) { + m_downloadDir = QString::fromStdString(flightgear::defaultDownloadDir()); + } + + QString baseIntroString = ui->introText->text(); + ui->introText->setText(baseIntroString.arg(m_downloadDir)); + updateUi(); +} + +InstallSceneryDialog::~InstallSceneryDialog() +{ + delete ui; +} + +void InstallSceneryDialog::updateUi() +{ + QPushButton* b = ui->buttonBox->button(QDialogButtonBox::Ok); + QPushButton* cancel = ui->buttonBox->button(QDialogButtonBox::Cancel); + + switch (m_state) { + case STATE_START: + b->setText(tr("Next")); + // b->setEnabled(m_catalogUrl.isValid() && !m_catalogUrl.isRelative()); + break; + + case STATE_EXTRACTING: + b->setEnabled(false); + cancel->setEnabled(false); + ui->stack->setCurrentIndex(1); + break; + + case STATE_EXTRACT_FAILED: + b->setEnabled(false); + cancel->setEnabled(true); + ui->stack->setCurrentIndex(2); + break; + + case STATE_FINISHED: + b->setEnabled(true); + cancel->setEnabled(false); + b->setText(tr("Okay")); + ui->stack->setCurrentIndex(2); + QString basicDesc = ui->resultsSummaryLabel->text(); + + ui->resultsSummaryLabel->setText(basicDesc.arg(sceneryPath())); + break; + } +} + + +void InstallSceneryDialog::accept() +{ + switch (m_state) { + case STATE_START: + pickFiles(); + break; + + case STATE_EXTRACTING: + case STATE_EXTRACT_FAILED: + // can't happen, button is disabled + break; + + case STATE_FINISHED: + // check if download path is in scenery list, add if not + QDialog::accept(); + break; + } +} + +void InstallSceneryDialog::reject() +{ + + QDialog::reject(); +} + +void InstallSceneryDialog::pickFiles() +{ + QStringList downloads = QStandardPaths::standardLocations(QStandardPaths::DownloadLocation); + QStringList files = QFileDialog::getOpenFileNames(this, "Choose scenery to install", + downloads.first(), "*.tar *.gz *.tgz"); + if (!files.isEmpty()) { + QDir d(m_downloadDir); + if (!d.exists("Scenery")) { + d.mkdir("Scenery"); + } + + m_state = STATE_EXTRACTING; + m_thread.reset(new InstallSceneryThread(d.filePath("Scenery"), files)); + // connect up some signals + connect(m_thread.data(), &QThread::finished, this, &InstallSceneryDialog::onThreadFinished); + connect(m_thread.data(), &InstallSceneryThread::extractionError, + this, &InstallSceneryDialog::onExtractError); + connect(m_thread.data(), &InstallSceneryThread::progress, + this, &InstallSceneryDialog::onExtractProgress); + updateUi(); + m_thread->start(); + } else { + // user cancelled file dialog, cancel us as well + QDialog::reject(); + } +} + +void InstallSceneryDialog::onThreadFinished() +{ + m_state = STATE_FINISHED; + updateUi(); +} + +void InstallSceneryDialog::onExtractError(QString file, QString msg) +{ + ui->resultsSummaryLabel->setText(tr("Problems occured extracting the archive '%1': %2").arg(file).arg(msg)); + m_state = STATE_EXTRACT_FAILED; + updateUi(); +} + +void InstallSceneryDialog::onExtractProgress(int percent) +{ + ui->progressBar->setValue(percent); +} + +QString InstallSceneryDialog::sceneryPath() +{ + if (m_state == STATE_FINISHED) { + QDir d(m_downloadDir); + return d.filePath("Scenery"); + } + + return QString(); +} + +#include "InstallSceneryDialog.moc" diff --git a/src/GUI/InstallSceneryDialog.hxx b/src/GUI/InstallSceneryDialog.hxx new file mode 100644 index 000000000..e6ffcad2d --- /dev/null +++ b/src/GUI/InstallSceneryDialog.hxx @@ -0,0 +1,70 @@ +// InstallSceneryDialog.hxx - part of GUI launcher using Qt5 +// +// Written by James Turner, started June 2016. +// +// Copyright (C) 2016 James Turner +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifndef FG_GUI_INSTALL_SCENERY_DIALOG_HXX +#define FG_GUI_INSTALL_SCENERY_DIALOG_HXX + +#include +#include + +namespace Ui { + class InstallSceneryDialog; +} + +class InstallSceneryThread; + +class InstallSceneryDialog : public QDialog +{ + Q_OBJECT + +public: + explicit InstallSceneryDialog(QWidget *parent, QString downloadDir); + ~InstallSceneryDialog(); + + QString sceneryPath(); +private slots: + virtual void reject(); + virtual void accept(); + +private: + void updateUi(); + + void pickFiles(); + + void onThreadFinished(); + void onExtractError(QString file, QString msg); + void onExtractProgress(int percent); + + enum State { + STATE_START = 0, // awaiting user input on first screen + STATE_EXTRACTING = 1, // in-progress, showing progress page + STATE_FINISHED = 2, // catalog added ok, showing summary page + STATE_EXTRACT_FAILED = 3 + }; + + State m_state; + QString m_downloadDir; + + Ui::InstallSceneryDialog *ui; + + QScopedPointer m_thread; +}; + +#endif // FG_GUI_INSTALL_SCENERY_DIALOG_HXX diff --git a/src/GUI/InstallSceneryDialog.ui b/src/GUI/InstallSceneryDialog.ui new file mode 100644 index 000000000..6a95b6a49 --- /dev/null +++ b/src/GUI/InstallSceneryDialog.ui @@ -0,0 +1,178 @@ + + + InstallSceneryDialog + + + + 0 + 0 + 528 + 311 + + + + Install scenery + + + + + + 0 + + + + + + + <html><head/><body><p>The easiest way to automatically install scenery is to use TerraSync. If you prefer to download and install scenery manually, you can use this dialog to extract and install the files in the correct place. (<a href="http://ns334561.ip-5-196-65.eu/~fgscenery/WS2.0/scenery-2.0.1.html"><span style=" text-decoration: underline; color:#0000ff;">Click here to download scenery</span></a>)</p><p>Files will be extracted and installed to a 'Scenery' folder inside your chosen downloads location (currently %1), after which you can delete the archives if you wish.</p><p>To begin, click 'Next' and select one more more downloaded scenery archives, which have names such as 'w010n40.tar.gz'</p></body></html> + + + true + + + true + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + 0 + 0 + + + + Please wait, verifying and extracting scenery... + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + TextLabel + + + + + + + 100 + + + -1 + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + Successfully installed the scenery files to '%1'. This location will be added to the list of additional sceneries to be used. + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + true + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + InstallSceneryDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + InstallSceneryDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/GUI/PathsDialog.cxx b/src/GUI/PathsDialog.cxx index 10854f9ed..d02294deb 100644 --- a/src/GUI/PathsDialog.cxx +++ b/src/GUI/PathsDialog.cxx @@ -11,6 +11,7 @@ #include "AddCatalogDialog.hxx" #include "AircraftModel.hxx" #include "QtLauncher_private.hxx" +#include "InstallSceneryDialog.hxx" #include
#include
@@ -60,6 +61,8 @@ AddOnsPage::AddOnsPage(QWidget *parent, simgear::pkg::RootRef root) : connect(m_ui->changeDataDir, &QPushButton::clicked, this, &AddOnsPage::onChangeDataDir); + connect(m_ui->installSceneryButton, &QPushButton::clicked, + this, &AddOnsPage::onInstallScenery); QSettings settings; @@ -180,6 +183,17 @@ void AddOnsPage::saveSceneryPaths() emit sceneryPathsChanged(); } +bool AddOnsPage::haveSceneryPath(QString path) const +{ + for (int i=0; isceneryPathsList->count(); ++i) { + if (m_ui->sceneryPathsList->item(i)->text() == path) { + return true; + } + } + + return false; +} + void AddOnsPage::onAddCatalog() { QScopedPointer dlg(new AddCatalogDialog(this, m_packageRoot)); @@ -310,6 +324,17 @@ void AddOnsPage::onChangeDataDir() QtLauncher::restartTheApp(QStringList()); } +void AddOnsPage::onInstallScenery() +{ + InstallSceneryDialog dlg(this, m_downloadDir); + if (dlg.exec() == QDialog::Accepted) { + if (!haveSceneryPath(dlg.sceneryPath())) { + m_ui->sceneryPathsList->addItem(dlg.sceneryPath()); + saveSceneryPaths(); + } + } +} + void AddOnsPage::updateUi() { QString s = m_downloadDir; diff --git a/src/GUI/PathsDialog.hxx b/src/GUI/PathsDialog.hxx index 749bb9318..3fcaed321 100644 --- a/src/GUI/PathsDialog.hxx +++ b/src/GUI/PathsDialog.hxx @@ -41,12 +41,14 @@ private slots: void onClearDownloadDir(); void onChangeDataDir(); + void onInstallScenery(); private: void updateUi(); void setDownloadDir(); void saveAircraftPaths(); void saveSceneryPaths(); + bool haveSceneryPath(QString path) const; Ui::AddOnsPage* m_ui; CatalogListModel* m_catalogsModel; diff --git a/src/GUI/PathsDialog.ui b/src/GUI/PathsDialog.ui index 070c1370a..89cd1e42f 100644 --- a/src/GUI/PathsDialog.ui +++ b/src/GUI/PathsDialog.ui @@ -106,6 +106,30 @@ + + + + + + Install downloaded scenery... + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + diff --git a/utils/TerraSync/CMakeLists.txt b/utils/TerraSync/CMakeLists.txt index cc27ec82b..f97996a9a 100644 --- a/utils/TerraSync/CMakeLists.txt +++ b/utils/TerraSync/CMakeLists.txt @@ -1,9 +1,9 @@ -add_executable(terrasync terrasync.cxx) +#add_executable(terrasync terrasync.cxx) -target_link_libraries(terrasync - ${SIMGEAR_CORE_LIBRARIES} - ${SIMGEAR_CORE_LIBRARY_DEPENDENCIES} -) +#target_link_libraries(terrasync +# ${SIMGEAR_CORE_LIBRARIES} +# ${SIMGEAR_CORE_LIBRARY_DEPENDENCIES} +#) -install(TARGETS terrasync RUNTIME DESTINATION bin) +#install(TARGETS terrasync RUNTIME DESTINATION bin) -- 2.39.5