File indexing completed on 2024-04-14 14:08:59

0001 /*
0002     SPDX-FileCopyrightText: 2004 Jason Harris <kstars@30doradus.org>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "kswizard.h"
0008 
0009 #include "geolocation.h"
0010 #include "kspaths.h"
0011 #include "kstars.h"
0012 #include "kstarsdata.h"
0013 #include "ksnotification.h"
0014 #include "ksutils.h"
0015 #include "Options.h"
0016 #include "widgets/dmsbox.h"
0017 
0018 #include <kns3/downloaddialog.h>
0019 
0020 #include <QDesktopServices>
0021 #include <QFile>
0022 #include <QLineEdit>
0023 #include <QPixmap>
0024 #include <QPushButton>
0025 #include <QStackedWidget>
0026 #include <QStandardPaths>
0027 
0028 namespace
0029 {
0030 bool hasPrefix(QString str, QString prefix)
0031 {
0032     if (prefix.isEmpty())
0033         return true;
0034     return str.startsWith(prefix, Qt::CaseInsensitive);
0035 }
0036 }
0037 
0038 WizWelcomeUI::WizWelcomeUI(QWidget *parent) : QFrame(parent)
0039 {
0040     setupUi(this);
0041 }
0042 
0043 WizLocationUI::WizLocationUI(QWidget *parent) : QFrame(parent)
0044 {
0045     setupUi(this);
0046 }
0047 
0048 WizDownloadUI::WizDownloadUI(QWidget *parent) : QFrame(parent)
0049 {
0050     setupUi(this);
0051 }
0052 
0053 #ifdef Q_OS_OSX
0054 WizDataUI::WizDataUI(QWidget *parent) : QFrame(parent)
0055 {
0056     setupUi(this);
0057 }
0058 #endif
0059 
0060 KSWizard::KSWizard(QWidget *parent) : QDialog(parent)
0061 {
0062     wizardStack = new QStackedWidget(this);
0063     adjustSize();
0064 
0065     setWindowTitle(i18nc("@title:window", "Startup Wizard"));
0066 
0067     QVBoxLayout *mainLayout = new QVBoxLayout;
0068     mainLayout->addWidget(wizardStack);
0069     setLayout(mainLayout);
0070 
0071     buttonBox = new QDialogButtonBox(Qt::Horizontal);
0072     nextB     = new QPushButton(i18n("&Next >"));
0073     nextB->setDefault(true);
0074     backB = new QPushButton(i18n("< &Back"));
0075     backB->setEnabled(false);
0076     completeB = new QPushButton(i18n("Done"));
0077 
0078     buttonBox->addButton(backB, QDialogButtonBox::ActionRole);
0079     buttonBox->addButton(nextB, QDialogButtonBox::ActionRole);
0080     buttonBox->addButton(completeB, QDialogButtonBox::AcceptRole);
0081     completeB->setVisible(false);
0082 
0083     mainLayout->addWidget(buttonBox);
0084 
0085     welcome = new WizWelcomeUI(wizardStack);
0086 #ifdef Q_OS_OSX
0087     data       = new WizDataUI(wizardStack);
0088 #endif
0089     location                = new WizLocationUI(wizardStack);
0090     WizDownloadUI *download = new WizDownloadUI(wizardStack);
0091 
0092     wizardStack->addWidget(welcome);
0093 #ifdef Q_OS_OSX
0094     wizardStack->addWidget(data);
0095 #endif
0096     wizardStack->addWidget(location);
0097     wizardStack->addWidget(download);
0098     wizardStack->setCurrentWidget(welcome);
0099 
0100     //Load images into banner frames.
0101     QPixmap im;
0102     if (im.load(KSPaths::locate(QStandardPaths::AppLocalDataLocation, "wzstars.png")))
0103         welcome->Banner->setPixmap(im);
0104     else if (im.load(QDir(QCoreApplication::applicationDirPath() + "/../Resources/kstars").absolutePath() +
0105                      "/wzstars.png"))
0106         welcome->Banner->setPixmap(im);
0107     if (im.load(KSPaths::locate(QStandardPaths::AppLocalDataLocation, "wzgeo.png")))
0108         location->Banner->setPixmap(im);
0109     else if (im.load(QDir(QCoreApplication::applicationDirPath() + "/../Resources/kstars").absolutePath() + "/wzgeo.png"))
0110         location->Banner->setPixmap(im);
0111     if (im.load(KSPaths::locate(QStandardPaths::AppLocalDataLocation, "wzdownload.png")))
0112         download->Banner->setPixmap(im);
0113     else if (im.load(QDir(QCoreApplication::applicationDirPath() + "/../Resources/kstars").absolutePath() +
0114                      "/wzdownload.png"))
0115         download->Banner->setPixmap(im);
0116 
0117 #ifdef Q_OS_OSX
0118     if (im.load(KSPaths::locate(QStandardPaths::AppLocalDataLocation, "wzdownload.png")))
0119         data->Banner->setPixmap(im);
0120     else if (im.load(QDir(QCoreApplication::applicationDirPath() + "/../Resources/kstars").absolutePath() +
0121                      "/wzdownload.png"))
0122         data->Banner->setPixmap(im);
0123 
0124     data->dataPath->setText(
0125         QStandardPaths::locate(QStandardPaths::GenericDataLocation, QString(), QStandardPaths::LocateDirectory) +
0126         "kstars");
0127     slotUpdateDataButtons();
0128 
0129     connect(data->copyKStarsData, SIGNAL(clicked()), this, SLOT(slotOpenOrCopyKStarsDataDirectory()));
0130     connect(data->installGSC, SIGNAL(clicked()), this, SLOT(slotInstallGSC()));
0131 
0132     gscMonitor = new QProgressIndicator(data);
0133     data->GSCLayout->addWidget(gscMonitor);
0134     data->downloadProgress->setValue(0);
0135 
0136     data->downloadProgress->setEnabled(false);
0137     data->downloadProgress->setVisible(false);
0138 
0139     data->gscInstallCancel->setVisible(false);
0140     data->gscInstallCancel->setEnabled(false);
0141 
0142 #endif
0143 
0144     //connect signals/slots
0145     connect(nextB, SIGNAL(clicked()), this, SLOT(slotNextPage()));
0146     connect(backB, SIGNAL(clicked()), this, SLOT(slotPrevPage()));
0147     connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
0148     connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
0149 
0150     connect(location->CityListBox, SIGNAL(itemSelectionChanged()), this, SLOT(slotChangeCity()));
0151     connect(location->CityFilter, SIGNAL(textChanged(QString)), this, SLOT(slotFilterCities()));
0152     connect(location->ProvinceFilter, SIGNAL(textChanged(QString)), this, SLOT(slotFilterCities()));
0153     connect(location->CountryFilter, SIGNAL(textChanged(QString)), this, SLOT(slotFilterCities()));
0154     connect(download->DownloadButton, SIGNAL(clicked()), this, SLOT(slotDownload()));
0155 
0156     //Initialize Geographic Location page
0157     if (KStars::Instance())
0158         initGeoPage();
0159 }
0160 
0161 void KSWizard::setButtonsEnabled()
0162 {
0163     nextB->setEnabled(wizardStack->currentIndex() < wizardStack->count() - 1);
0164     backB->setEnabled(wizardStack->currentIndex() > 0);
0165     completeB->setVisible(wizardStack->currentIndex() == wizardStack->count() - 1);
0166 
0167 #ifdef Q_OS_OSX
0168     if ((wizardStack->currentWidget() == data) && (!dataDirExists()))
0169     {
0170         nextB->setEnabled(false);
0171     }
0172 #endif
0173 }
0174 
0175 void KSWizard::slotNextPage()
0176 {
0177     wizardStack->setCurrentIndex(wizardStack->currentIndex() + 1);
0178     setButtonsEnabled();
0179 }
0180 
0181 void KSWizard::slotPrevPage()
0182 {
0183     wizardStack->setCurrentIndex(wizardStack->currentIndex() - 1);
0184     setButtonsEnabled();
0185 }
0186 
0187 void KSWizard::initGeoPage()
0188 {
0189     KStarsData *data = KStarsData::Instance();
0190     location->LongBox->setReadOnly(true);
0191     location->LatBox->setReadOnly(true);
0192 
0193     //Populate the CityListBox
0194     //flag the ID of the current City
0195     foreach (GeoLocation *loc, data->getGeoList())
0196     {
0197         location->CityListBox->addItem(loc->fullName());
0198         filteredCityList.append(loc);
0199         if (loc->fullName() == data->geo()->fullName())
0200         {
0201             Geo = loc;
0202         }
0203     }
0204 
0205     //Sort alphabetically
0206     location->CityListBox->sortItems();
0207     //preset to current city
0208     QList<QListWidgetItem*> locations = location->CityListBox->findItems(QString(data->geo()->fullName()), Qt::MatchExactly);
0209     if (locations.isEmpty() == false)
0210         location->CityListBox->setCurrentItem(locations[0]);
0211 }
0212 
0213 void KSWizard::slotChangeCity()
0214 {
0215     if (location->CityListBox->currentItem())
0216     {
0217         for (auto &city : filteredCityList)
0218         {
0219             if (city->fullName() == location->CityListBox->currentItem()->text())
0220             {
0221                 Geo = city;
0222                 break;
0223             }
0224         }
0225         location->LongBox->show(Geo->lng());
0226         location->LatBox->show(Geo->lat());
0227     }
0228 }
0229 
0230 void KSWizard::slotFilterCities()
0231 {
0232     location->CityListBox->clear();
0233     //Do NOT delete members of filteredCityList!
0234     filteredCityList.clear();
0235 
0236     foreach (GeoLocation *loc, KStarsData::Instance()->getGeoList())
0237     {
0238         if (hasPrefix(loc->translatedName(), location->CityFilter->text()) &&
0239                 hasPrefix(loc->translatedCountry(), location->CountryFilter->text()) &&
0240                 hasPrefix(loc->translatedProvince(), location->ProvinceFilter->text()))
0241         {
0242             location->CityListBox->addItem(loc->fullName());
0243             filteredCityList.append(loc);
0244         }
0245     }
0246     location->CityListBox->sortItems();
0247 
0248     if (location->CityListBox->count() > 0) // set first item in list as selected
0249         location->CityListBox->setCurrentItem(location->CityListBox->item(0));
0250 }
0251 
0252 void KSWizard::slotDownload()
0253 {
0254     KNS3::DownloadDialog dlg;
0255     dlg.exec();
0256 }
0257 
0258 void KSWizard::slotOpenOrCopyKStarsDataDirectory()
0259 {
0260 #ifdef Q_OS_OSX
0261     QString dataLocation =
0262         QStandardPaths::locate(QStandardPaths::GenericDataLocation, "kstars", QStandardPaths::LocateDirectory);
0263     if (dataLocation.isEmpty())
0264     {
0265         QDir dataSourceDir = QDir(QCoreApplication::applicationDirPath() + "/../Resources/kstars").absolutePath();
0266         if (! dataSourceDir.exists()) //If there is no default data directory in the app bundle
0267         {
0268             KSNotification::sorry(i18n("There was no default data directory found in the app bundle."));
0269             return;
0270         }
0271         QDir writableDir(KSPaths::writableLocation(QStandardPaths::AppLocalDataLocation));
0272         writableDir.mkpath(".");
0273         dataLocation =
0274             QStandardPaths::locate(QStandardPaths::GenericDataLocation, "kstars", QStandardPaths::LocateDirectory);
0275         if (dataLocation.isEmpty()) //If there *still* is not a kstars data directory
0276         {
0277             KSNotification::sorry(i18n("There was a problem creating the data directory ~/Library/Application Support/."));
0278             return;
0279         }
0280         KSUtils::copyRecursively(dataSourceDir.absolutePath(), dataLocation);
0281         //This will update the next, ok, and copy kstars dir buttons.
0282         slotUpdateDataButtons();
0283     }
0284     else
0285     {
0286         QUrl path = QUrl::fromLocalFile(
0287                         QStandardPaths::locate(QStandardPaths::GenericDataLocation, "kstars", QStandardPaths::LocateDirectory));
0288         QDesktopServices::openUrl(path);
0289     }
0290 
0291 #endif
0292 }
0293 
0294 void KSWizard::slotInstallGSC()
0295 {
0296 #ifdef Q_OS_OSX
0297 
0298     QNetworkAccessManager *manager = new QNetworkAccessManager();
0299 
0300     QString location =
0301         QStandardPaths::locate(QStandardPaths::GenericDataLocation, "kstars", QStandardPaths::LocateDirectory);
0302     gscZipPath            = location + "/gsc.zip";
0303 
0304     data->downloadProgress->setVisible(true);
0305     data->downloadProgress->setEnabled(true);
0306 
0307     data->gscInstallCancel->setVisible(true);
0308     data->gscInstallCancel->setEnabled(true);
0309 
0310     QString gscURL = "http://www.indilib.org/jdownloads/Mac/gsc.zip";
0311 
0312     QNetworkReply *response = manager->get(QNetworkRequest(QUrl(gscURL)));
0313 
0314     QMetaObject::Connection *cancelConnection = new QMetaObject::Connection();
0315     QMetaObject::Connection *replyConnection = new QMetaObject::Connection();
0316     QMetaObject::Connection *percentConnection = new QMetaObject::Connection();
0317 
0318     *percentConnection = connect(response, &QNetworkReply::downloadProgress,
0319                                  [ = ](qint64 bytesReceived, qint64 bytesTotal)
0320     {
0321         data->downloadProgress->setValue(bytesReceived);
0322         data->downloadProgress->setMaximum(bytesTotal);
0323     });
0324 
0325     *cancelConnection = connect(data->gscInstallCancel, &QPushButton::clicked,
0326                                 [ = ]()
0327     {
0328         qDebug() << Q_FUNC_INFO << "Download Cancelled.";
0329 
0330         if(cancelConnection)
0331             disconnect(*cancelConnection);
0332         if(replyConnection)
0333             disconnect(*replyConnection);
0334 
0335         if(response)
0336         {
0337             response->abort();
0338             response->deleteLater();
0339         }
0340 
0341         data->downloadProgress->setVisible(false);
0342         data->downloadProgress->setEnabled(false);
0343 
0344         data->gscInstallCancel->setVisible(false);
0345         data->gscInstallCancel->setEnabled(false);
0346 
0347         if(manager)
0348             manager->deleteLater();
0349 
0350     });
0351 
0352     *replyConnection = connect(response, &QNetworkReply::finished, this,
0353                                [ = ]()
0354     {
0355         if(response)
0356         {
0357 
0358             if(cancelConnection)
0359                 disconnect(*cancelConnection);
0360             if(replyConnection)
0361                 disconnect(*replyConnection);
0362 
0363             data->downloadProgress->setVisible(false);
0364             data->downloadProgress->setEnabled(false);
0365 
0366             data->gscInstallCancel->setVisible(false);
0367             data->gscInstallCancel->setEnabled(false);
0368 
0369 
0370             response->deleteLater();
0371             if(manager)
0372                 manager->deleteLater();
0373             if (response->error() != QNetworkReply::NoError)
0374                 return;
0375 
0376             QByteArray responseData = response->readAll();
0377 
0378             QFile file(gscZipPath);
0379             if (QFileInfo(QFileInfo(file).path()).isWritable())
0380             {
0381                 if (!file.open(QIODevice::WriteOnly))
0382                 {
0383                     KSNotification::error( i18n("File write error."));
0384                     return;
0385                 }
0386                 else
0387                 {
0388                     file.write(responseData.data(), responseData.size());
0389                     file.close();
0390                     slotExtractGSC();
0391                 }
0392             }
0393             else
0394             {
0395                 KSNotification::error( i18n("Data folder permissions error."));
0396             }
0397         }
0398     });
0399 
0400 #endif
0401 }
0402 
0403 void KSWizard::slotExtractGSC()
0404 {
0405 #ifdef Q_OS_OSX
0406     QString location =
0407         QStandardPaths::locate(QStandardPaths::GenericDataLocation, "kstars", QStandardPaths::LocateDirectory);
0408     QProcess *gscExtractor = new QProcess();
0409     connect(gscExtractor, SIGNAL(finished(int)), this, SLOT(slotGSCInstallerFinished()));
0410     connect(gscExtractor, SIGNAL(finished(int)), this, SLOT(gscExtractor.deleteLater()));
0411     gscExtractor->setWorkingDirectory(location);
0412     gscExtractor->start("unzip", QStringList() << "-ao"
0413                         << "gsc.zip");
0414     gscMonitor->startAnimation();
0415 #endif
0416 }
0417 
0418 void KSWizard::slotGSCInstallerFinished()
0419 {
0420 #ifdef Q_OS_OSX
0421     if (downloadMonitor)
0422     {
0423         downloadMonitor->stop();
0424         delete downloadMonitor;
0425     }
0426     data->downloadProgress->setEnabled(false);
0427     data->downloadProgress->setValue(0);
0428     data->downloadProgress->setVisible(false);
0429     gscMonitor->stopAnimation();
0430     slotUpdateDataButtons();
0431     if (QFile(gscZipPath).exists())
0432         QFile(gscZipPath).remove();
0433 #endif
0434 }
0435 
0436 #ifdef Q_OS_OSX
0437 bool KSWizard::dataDirExists()
0438 {
0439     QString dataLocation =
0440         QStandardPaths::locate(QStandardPaths::GenericDataLocation, "kstars", QStandardPaths::LocateDirectory);
0441     return !dataLocation.isEmpty();
0442 }
0443 
0444 bool KSWizard::GSCExists()
0445 {
0446     QString GSCLocation =
0447         QStandardPaths::locate(QStandardPaths::GenericDataLocation, "kstars/gsc", QStandardPaths::LocateDirectory);
0448     return !GSCLocation.isEmpty();
0449 }
0450 
0451 #endif
0452 
0453 void KSWizard::slotUpdateDataButtons()
0454 {
0455 #ifdef Q_OS_OSX
0456     data->dataDirFound->setChecked(dataDirExists());
0457     if (dataDirExists())
0458     {
0459         data->copyKStarsData->setText("Open KStars Data Directory");
0460         data->foundFeedback1->setText("The KStars Data Directory called kstars is located at:");
0461         data->foundFeedback2->setText("Your data directory was found.  If you have any problems with it, you can "
0462                                       "always delete this data directory and KStars will give you a new data "
0463                                       "directory.  You can click this button to open the data directory, just be "
0464                                       "careful not to delete any important files.");
0465     }
0466     else
0467     {
0468         data->foundFeedback1->setText("The KStars Data Directory called kstars should be located at:");
0469         data->foundFeedback2->setText("<html><head/><body><p>Your data directory was not found. You can click the "
0470                                       "button below to copy a default KStars data directory to the correct location, "
0471                                       "or if you have a KStars directory already some place else, you can exit KStars "
0472                                       "and copy it to that location yourself.</p></body></html>");
0473     }
0474     bool ifGSCExists = GSCExists();
0475     data->GSCFound->setChecked(ifGSCExists);
0476     data->installGSC->setDisabled(ifGSCExists || !dataDirExists());
0477     if (ifGSCExists)
0478         data->GSCFeedback->setText("GSC was found on your system.  To use it, just take an image in the CCD simulator. "
0479                                    "To uninstall, just delete the gsc folder from your data directory above.");
0480     setButtonsEnabled();
0481 #endif
0482 }