• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Stellarium / stellarium / 5926409083

21 Aug 2023 12:45PM UTC coverage: 11.905% (+0.01%) from 11.891%
5926409083

Pull #3373

github

gzotti
Reduce verbosity a bit.
Pull Request #3373: Fix: unambiguous comet names

264 of 264 new or added lines in 9 files covered. (100.0%)

14845 of 124700 relevant lines covered (11.9%)

23289.81 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/plugins/SolarSystemEditor/src/gui/MpcImportWindow.cpp
1
/*
2
 * Solar System editor plug-in for Stellarium
3
 *
4
 * Copyright (C) 2010 Bogdan Marinov
5
 *
6
 * This program is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU General Public License
8
 * as published by the Free Software Foundation; either version 2
9
 * of the License, or (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA  02110-1335, USA.
19
 */
20

21
#include "SolarSystemEditor.hpp"
22

23
#include "MpcImportWindow.hpp"
24
#include "ui_mpcImportWindow.h"
25

26
#include "StelApp.hpp"
27
#include "StelFileMgr.hpp"
28
#include "StelJsonParser.hpp"
29
#include "StelModuleMgr.hpp"
30
#include "StelTranslator.hpp"
31
#include "SolarSystem.hpp"
32
#include "StelProgressController.hpp"
33
#include "SearchDialog.hpp"
34
#include "StelUtils.hpp"
35

36
#include <QGuiApplication>
37
#include <QClipboard>
38
#include <QDesktopServices>
39
#include <QFileDialog>
40
#include <QSortFilterProxyModel>
41
#include <QHash>
42
#include <QList>
43
#include <QNetworkAccessManager>
44
#include <QNetworkRequest>
45
#include <QNetworkReply>
46
#include <QStandardItemModel>
47
#include <QString>
48
#include <QTemporaryFile>
49
#include <QTimer>
50
#include <QUrl>
51
#include <QUrlQuery>
52
#include <QDir>
53
#include <QRegularExpression>
54
#include <stdexcept>
55

56
MpcImportWindow::MpcImportWindow()
×
57
        : StelDialog("SolarSystemEditorMPCimport")
58
        , filterProxyModel(nullptr)
×
59
        , importType(ImportType())
×
60
        , downloadReply(nullptr)
×
61
        , queryReply(nullptr)
×
62
        , downloadProgressBar(nullptr)
×
63
        , queryProgressBar(nullptr)
×
64
        , countdown(0)
×
65
{
66
        ui = new Ui_mpcImportWindow();
×
67
        ssoManager = GETSTELMODULE(SolarSystemEditor);
×
68

69
        //networkManager = StelApp::getInstance().getNetworkAccessManager();
70
        networkManager = new QNetworkAccessManager(this);
×
71

72
        countdownTimer = new QTimer(this);
×
73

74
        QHash<QString,QString> asteroidBookmarks;
×
75
        QHash<QString,QString> cometBookmarks;
×
76
        bookmarks.insert(MpcComets, cometBookmarks);
×
77
        bookmarks.insert(MpcMinorPlanets, asteroidBookmarks);
×
78

79
        candidateObjectsModel = new QStandardItemModel(this);
×
80
}
×
81

82
MpcImportWindow::~MpcImportWindow()
×
83
{
84
        delete ui;
×
85
        delete countdownTimer;
×
86
        candidateObjectsModel->clear();
×
87
        delete candidateObjectsModel;
×
88
        if (downloadReply)
×
89
                downloadReply->deleteLater();
×
90
        if (queryReply)
×
91
                queryReply->deleteLater();
×
92
        if (downloadProgressBar)
×
93
                StelApp::getInstance().removeProgressBar(downloadProgressBar);
×
94
        if (queryProgressBar)
×
95
                StelApp::getInstance().removeProgressBar(queryProgressBar);
×
96
}
×
97

98
void MpcImportWindow::createDialogContent()
×
99
{
100
        ui->setupUi(dialog);
×
101

102
        //Signals
103
        connect(&StelApp::getInstance(), SIGNAL(languageChanged()), this, SLOT(retranslate()));
×
104
        connect(ui->closeStelWindow,     SIGNAL(clicked()),         this, SLOT(close()));
×
105
        connect(ui->TitleBar,            SIGNAL(movedTo(QPoint)),   this, SLOT(handleMovedTo(QPoint)));
×
106

107
        connect(ui->pushButtonAcquire,       SIGNAL(clicked()), this, SLOT(acquireObjectData()));
×
108
        connect(ui->pushButtonAbortDownload, SIGNAL(clicked()), this, SLOT(abortDownload()));
×
109
        connect(ui->pushButtonAdd,           SIGNAL(clicked()), this, SLOT(addObjects()));
×
110
        connect(ui->pushButtonDiscard,       SIGNAL(clicked()), this, SLOT(discardObjects()));
×
111

112
        connect(ui->pushButtonBrowse,        SIGNAL(clicked()), this, SLOT(selectFile()));
×
113
        connect(ui->comboBoxBookmarks,       SIGNAL(currentIndexChanged(int)),this, SLOT(bookmarkSelected(int)));
×
114

115
        connect(ui->radioButtonFile,         SIGNAL(toggled(bool)), ui->frameFile, SLOT(setVisible(bool)));
×
116
        connect(ui->radioButtonURL,          SIGNAL(toggled(bool)), ui->frameURL,  SLOT(setVisible(bool)));
×
117

118
        connect(ui->radioButtonAsteroids,    SIGNAL(toggled(bool)), this, SLOT(switchImportType(bool)));
×
119
        connect(ui->radioButtonComets,       SIGNAL(toggled(bool)), this, SLOT(switchImportType(bool)));
×
120

121
        connect(ui->pushButtonMarkAll,       SIGNAL(clicked()), this, SLOT(markAll()));
×
122
        connect(ui->pushButtonMarkNone,      SIGNAL(clicked()), this, SLOT(unmarkAll()));
×
123

124
        connect(ui->pushButtonSendQuery,     SIGNAL(clicked()),           this, SLOT(sendQuery()));
×
125
        connect(ui->lineEditQuery,           SIGNAL(returnPressed()),     this, SLOT(sendQuery()));
×
126
        connect(ui->pushButtonAbortQuery,    SIGNAL(clicked()),           this, SLOT(abortQuery()));
×
127
        connect(ui->lineEditQuery,           SIGNAL(textEdited(QString)), this, SLOT(resetNotFound()));
×
128
        //connect(ui->lineEditQuery,         SIGNAL(editingFinished()),   this, SLOT(sendQuery()));
129
        connect(countdownTimer,              SIGNAL(timeout()),           this, SLOT(updateCountdown()));
×
130

131
        filterProxyModel = new QSortFilterProxyModel(this);
×
132
        filterProxyModel->setSourceModel(candidateObjectsModel);
×
133
        filterProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
×
134
        ui->listViewObjects->setModel(filterProxyModel);
×
135
        connect(ui->lineEditSearch, SIGNAL(textChanged(const QString&)),
×
136
                filterProxyModel, SLOT(setFilterFixedString(const QString&)));
×
137

138
        loadBookmarks();
×
139
        updateTexts();
×
140

141
        resetCountdown();
×
142
        resetDialog();
×
143
}
×
144

145
void MpcImportWindow::updateTexts()
×
146
{
147
        QString linkText("<a href=\"https://www.minorplanetcenter.net/iau/MPEph/MPEph.html\">Minor Planet &amp; Comet Ephemeris Service</a>");
×
148
        // TRANSLATORS: A link showing the text "Minor Planet & Comet Ephemeris Service" is inserted.
149
        QString queryString(q_("Query the MPC's %1:"));
×
150
        ui->labelQueryLink->setText(QString(queryString).arg(linkText));
×
151
        
152
        QString firstLine(q_("Only one result will be returned if the query is successful."));
×
153
        QString secondLine(q_("Both comets and asteroids can be identified with their number, name (in English) or IAU designation."));
×
154
        QString cPrefix("<b>C/</b>");
×
155
        QString pPrefix("<b>P/</b>");
×
156
        QString cometQuery("<tt>C/Halley</tt>");
×
157
        QString cometName("1P/Halley");
×
158
        QString asteroidQuery("<tt>Halley</tt>");
×
159
        QString asteroidName("(2688) Halley");
×
160
        QString nameWarning(q_("Comet <i>names</i> need to be prefixed with %1 or %2. If more than one comet matches a name, only the first result will be returned. For example, searching for \"%3\" will return %4, Halley's Comet, but a search for \"%5\" will return the asteroid %6."));
×
161
        QString thirdLine = QString(nameWarning).arg(cPrefix, pPrefix, cometQuery, cometName, asteroidQuery, asteroidName);
×
162
        ui->labelQueryInstructions->setText(QString("%1<br/>%2<br/>%3").arg(firstLine, secondLine, thirdLine));
×
163
}
×
164

165
void MpcImportWindow::resetDialog()
×
166
{
167
        ui->stackedWidget->setCurrentIndex(0);
×
168

169
        //ui->tabWidget->setCurrentIndex(0);
170
        ui->groupBoxType->setVisible(true);
×
171
        ui->radioButtonAsteroids->setChecked(true);
×
172

173
        ui->radioButtonURL->setChecked(true);
×
174
        ui->frameFile->setVisible(false);
×
175

176
        ui->lineEditFilePath->clear();
×
177
        ui->lineEditQuery->clear();
×
178
        ui->lineEditURL->setText("https://");
×
179
        ui->checkBoxAddBookmark->setChecked(false);
×
180
        ui->frameBookmarkTitle->setVisible(false);
×
181
        ui->comboBoxBookmarks->setCurrentIndex(0);
×
182

183
        ui->radioButtonUpdate->setChecked(true);
×
184
        ui->checkBoxOnlyOrbitalElements->setChecked(true);
×
185

186
        //TODO: Is this the right place?
187
        ui->pushButtonAbortQuery->setVisible(false);
×
188
        ui->pushButtonAbortDownload->setVisible(false);
×
189

190
        //Resetting the dialog should not reset the timer
191
        //resetCountdown();
192
        resetNotFound();
×
193
        enableInterface(true);
×
194
}
×
195

196
void MpcImportWindow::populateBookmarksList()
×
197
{
198
        ui->comboBoxBookmarks->clear();
×
199
        ui->comboBoxBookmarks->addItem(q_("Select bookmark..."));
×
200
        QStringList bookmarkTitles(bookmarks.value(importType).keys());
×
201
        bookmarkTitles.sort();
×
202
        ui->comboBoxBookmarks->addItems(bookmarkTitles);
×
203
}
×
204

205
void MpcImportWindow::retranslate()
×
206
{
207
        if (dialog)
×
208
        {
209
                ui->retranslateUi(dialog);
×
210
                updateTexts();
×
211
        }
212
}
×
213

214
void MpcImportWindow::acquireObjectData()
×
215
{
216
        if (ui->radioButtonFile->isChecked())
×
217
        {
218
                QString filePath = ui->lineEditFilePath->text();
×
219
                if (filePath.isEmpty())
×
220
                        return;
×
221

222
                QList<SsoElements> objects = readElementsFromFile(importType, filePath);
×
223
                if (objects.isEmpty())
×
224
                        return;
×
225

226
                //Temporary, until the slot/socket mechanism is ready
227
                populateCandidateObjects(objects);
×
228
                ui->stackedWidget->setCurrentIndex(1);
×
229
        }
×
230
        else if (ui->radioButtonURL->isChecked())
×
231
        {
232
                QString url = ui->lineEditURL->text();
×
233
                if (url.isEmpty())
×
234
                        return;
×
235
                startDownload(url);
×
236
        }
×
237
        //close();
238
}
239

240
void MpcImportWindow::addObjects()
×
241
{
242
        disconnect(ssoManager, SIGNAL(solarSystemChanged()), this, SLOT(resetDialog()));
×
243

244
        QList<QString> checkedObjectsNames;
×
245

246
        // Collect names of marked objects
247
        //TODO: Something smarter?
248
        for (int row = 0; row < candidateObjectsModel->rowCount(); row++)
×
249
        {
250
                QStandardItem * item = candidateObjectsModel->item(row);
×
251
                if (item->checkState() == Qt::Checked)
×
252
                {
253
                        checkedObjectsNames.append(item->text());
×
254
                        if (row==0)
×
255
                                SearchDialog::extSearchText = item->text();
×
256
                }
257
        }
258
        //qDebug() << "Checked:" << checkedObjectsNames;
259

260
        // collect from candidatesForAddition all candidates that were selected by the user into `approvedForAddition` ...
261
        QList<SsoElements> approvedForAddition;
×
262
        for (int i = 0; i < candidatesForAddition.count(); i++)
×
263
        {
264
                auto candidate = candidatesForAddition.at(i);
×
265
                QString name = candidate.value("name").toString();
×
266
                if (checkedObjectsNames.contains(name))
×
267
                        approvedForAddition.append(candidate);
×
268
        }
×
269

270
        //qDebug() << "Approved for addition:" << approvedForAddition;
271

272
        // collect all new (!!!) candidates that were selected by the user into `approvedForUpdate`
273
        // if the user opted to overwrite, those candidates are added to `approvedForAddition` instead
274
        bool overwrite = ui->radioButtonOverwrite->isChecked();
×
275
        QList<SsoElements> approvedForUpdate;
×
276
        for (int j = 0; j < candidatesForUpdate.count(); j++)
×
277
        {
278
                auto candidate = candidatesForUpdate.at(j);
×
279
                QString name = candidate.value("name").toString();
×
280
                if (checkedObjectsNames.contains(name))
×
281
                {
282
                        // XXX: odd... if "overwrite" is false, data is overwritten anyway.
283
                        if (overwrite)
×
284
                        {
285
                                approvedForAddition.append(candidate);
×
286
                        }
287
                        else
288
                        {
289
                                approvedForUpdate.append(candidate);
×
290
                        }
291
                }
292
        }
×
293

294
        //qDebug() << "Approved for updates:" << approvedForUpdate;
295

296
        // append *** + update *** the approvedForAddition candidates to custom solar system config
297
        ssoManager->appendToSolarSystemConfigurationFile(approvedForAddition);
×
298

299
        // if instead "update existing objects" was selected, update existing candidates from `approvedForUpdate` in custom solar system config
300
        // update name, MPC number, orbital elements
301
        // if the user asked more to update, include type (asteroid, comet, plutino, cubewano, ...) and magnitude parameters
302
        bool update = ui->radioButtonUpdate->isChecked();
×
303
        // ASSERT(update != overwrite); // because of radiobutton behaviour. TODO this UI is not very clear anyway.
304
        if (update) 
×
305
        {
306
                SolarSystemEditor::UpdateFlags flags(SolarSystemEditor::UpdateNameAndNumber | SolarSystemEditor::UpdateOrbitalElements);
×
307
                bool onlyorbital = ui->checkBoxOnlyOrbitalElements->isChecked();
×
308
                if (!onlyorbital)
×
309
                {
310
                        flags |= SolarSystemEditor::UpdateType;
×
311
                        flags |= SolarSystemEditor::UpdateMagnitudeParameters;
×
312
                }
313

314
                ssoManager->updateSolarSystemConfigurationFile(approvedForUpdate, flags);
×
315
        }
316

317
        //Refresh the Solar System
318
        GETSTELMODULE(SolarSystem)->reloadPlanets();
×
319

320
        resetDialog();
×
321
        emit objectsImported();
×
322
}
×
323

324
void MpcImportWindow::discardObjects()
×
325
{
326
        resetDialog();
×
327
}
×
328

329
void MpcImportWindow::pasteClipboardURL()
×
330
{
331
        ui->lineEditURL->setText(QGuiApplication::clipboard()->text());
×
332
}
×
333

334
void MpcImportWindow::selectFile()
×
335
{
336
        QString filter = q_("Plain Text File");
×
337
        filter.append(" (*.txt);;");
×
338
        filter.append(q_("All Files"));
×
339
        filter.append(" (*.*)");
×
340
        QString filePath = QFileDialog::getOpenFileName(nullptr, q_("Select a file"), QDir::homePath(), filter);
×
341
        ui->lineEditFilePath->setText(filePath);
×
342
}
×
343

344
void MpcImportWindow::bookmarkSelected(int bookmarkIndex)
×
345
{
346
        const QString bookmarkTitle=ui->comboBoxBookmarks->itemText(bookmarkIndex);
×
347
        if (bookmarkTitle.isEmpty() || bookmarks.value(importType).value(bookmarkTitle).isEmpty())
×
348
        {
349
                ui->lineEditURL->clear();
×
350
                return;
×
351
        }
352
        QString bookmarkUrl = bookmarks.value(importType).value(bookmarkTitle);
×
353
        ui->lineEditURL->setText(bookmarkUrl);
×
354
}
×
355

356
void MpcImportWindow::populateCandidateObjects(QList<SsoElements> objects)
×
357
{
358
        candidatesForAddition.clear();        // new objects
×
359
        candidatesForUpdate.clear();        // existing objects
×
360

361
        //Get a list of the current objects
362
        //QHash<QString,QString> defaultSsoIdentifiers = ssoManager->getDefaultSsoIdentifiers();
363
        QHash<QString,QString> loadedSsoIdentifiers = ssoManager->listAllLoadedSsoIdentifiers();
×
364

365
        //Separate the objects into visual (internally unsorted, anyone?) groups in the list
366

367
        //int newDefaultSsoIndex = 0;
368
        int newLoadedSsoIndex = 0; // existing objects
×
369
        int newNovelSsoIndex = 0; // new objects
×
370

371
        int insertionIndex = 0; // index of object to be inserted next
×
372

373
        QStandardItemModel * model = candidateObjectsModel;
×
374
        model->clear();
×
375
        model->setColumnCount(1);
×
376

377
        for (auto object : objects)
×
378
        {
379
                QString name = object.value("name").toString();
×
380
                if (name.isEmpty())
×
381
                        continue;
×
382

383
                QString group = object.value("section_name").toString();
×
384
                if (group.isEmpty())
×
385
                        continue;
×
386

387
                //Prevent name conflicts between asteroids and moons
388
                if (loadedSsoIdentifiers.contains(name))
×
389
                {
390
                        if (loadedSsoIdentifiers.value(name) != group)
×
391
                        {
392
                                name.append('*');
×
393
                                object.insert("name", name);
×
394
                        }
395
                }
396

397
                QStandardItem * item = new QStandardItem();
×
398
                item->setText(name);
×
399
                item->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled);
×
400
                item->setCheckState(Qt::Unchecked);
×
401

402
//                if (defaultSsoIdentifiers.contains(name))
403
//                {
404
//                        //Duplicate of a default solar system object
405
//                        QFont itemFont(item->font());
406
//                        itemFont.setBold(true);
407
//                        item->setFont(itemFont);
408

409
//                        candidatesForUpdate.append(object);
410

411
//                        insertionIndex = newDefaultSsoIndex;
412
//                        newDefaultSsoIndex++;
413
//                        newLoadedSsoIndex++;
414
//                        newNovelSsoIndex++;
415
//                }
416
//                else
417

418
                // identify existing (in italic) and new objects
419
                if (loadedSsoIdentifiers.contains(name))
×
420
                {
421
                        //Duplicate of another existing object
422
                        QFont itemFont(item->font());
×
423
                        itemFont.setItalic(true);
×
424
                        item->setFont(itemFont);
×
425

426
                        candidatesForUpdate.append(object);
×
427

428
                        insertionIndex = newLoadedSsoIndex;
×
429
                        newLoadedSsoIndex++;
×
430
                        newNovelSsoIndex++;
×
431
                }
×
432
                else
433
                {
434
                        candidatesForAddition.append(object);
×
435

436
                        insertionIndex = newNovelSsoIndex;
×
437
                        newNovelSsoIndex++;
×
438
                }
439

440
                model->insertRow(insertionIndex, item);
×
441
        }
×
442

443
        //Scroll to the first items
444
        ui->listViewObjects->scrollToTop();
×
445
}
×
446

447
void MpcImportWindow::enableInterface(bool enable)
×
448
{
449
        ui->groupBoxType->setVisible(enable);
×
450

451
        ui->frameFile->setEnabled(enable);
×
452
        ui->frameURL->setEnabled(enable);
×
453

454
        ui->radioButtonFile->setEnabled(enable);
×
455
        ui->radioButtonURL->setEnabled(enable);
×
456

457
        ui->pushButtonAcquire->setEnabled(enable);
×
458
}
×
459

460
SsoElements MpcImportWindow::readElementsFromString(QString elements)
×
461
{
462
        Q_ASSERT(ssoManager);
×
463

464
        switch (importType)
×
465
        {
466
                case MpcComets:
×
467
                        return ssoManager->readMpcOneLineCometElements(elements);
×
468
                case MpcMinorPlanets:
×
469
                default:
470
                        return ssoManager->readMpcOneLineMinorPlanetElements(elements);
×
471
        }
472
}
473

474
QList<SsoElements> MpcImportWindow::readElementsFromFile(ImportType type, QString filePath)
×
475
{
476
        Q_ASSERT(ssoManager);
×
477

478
        switch (type)
×
479
        {
480
                case MpcComets:
×
481
                        return ssoManager->readMpcOneLineCometElementsFromFile(filePath);
×
482
                case MpcMinorPlanets:
×
483
                default:
484
                        return ssoManager->readMpcOneLineMinorPlanetElementsFromFile(filePath);
×
485
        }
486
}
487

488
void MpcImportWindow::switchImportType(bool)
×
489
{
490
        if (ui->radioButtonAsteroids->isChecked())
×
491
        {
492
                importType = MpcMinorPlanets;
×
493
        }
494
        else
495
        {
496
                importType = MpcComets;
×
497
        }
498

499
        populateBookmarksList();
×
500

501
        //Clear the fields
502
        //ui->lineEditSingle->clear();
503
        ui->lineEditFilePath->clear();
×
504
        ui->lineEditURL->clear();
×
505

506
        //If one of the options is selected, show the rest of the dialog
507
        ui->groupBoxSource->setVisible(true);
×
508
}
×
509

510
void MpcImportWindow::setCheckState(Qt::CheckState state)
×
511
{
512
        int rowCount = filterProxyModel->rowCount();
×
513
        if (rowCount < 1)
×
514
                return;
×
515

516
        for (int row = 0; row < rowCount; row++)
×
517
        {
518
                QModelIndex proxyIndex = filterProxyModel->index(row, 0);
×
519
                QModelIndex index = filterProxyModel->mapToSource(proxyIndex);
×
520
                QStandardItem * item = candidateObjectsModel->itemFromIndex(index);
×
521
                if (item)
×
522
                        item->setCheckState(state);
×
523
        }
524
}
525

526
void MpcImportWindow::markAll()
×
527
{
528
        setCheckState(Qt::Checked);
×
529
}
×
530

531
void MpcImportWindow::unmarkAll()
×
532
{
533
        setCheckState(Qt::Unchecked);
×
534
}
×
535

536
void MpcImportWindow::updateDownloadProgress(qint64 bytesReceived, qint64 bytesTotal)
×
537
{
538
        if (downloadProgressBar == nullptr)
×
539
                return;
×
540

541
        int currentValue = 0;
×
542
        int endValue = 0;
×
543

544
        if (bytesTotal > -1 && bytesReceived <= bytesTotal)
×
545
        {
546
                //Round to the greatest possible derived unit
547
                while (bytesTotal > 1024)
×
548
                {
549
                        bytesReceived >>= 10;
×
550
                        bytesTotal    >>= 10;
×
551
                }
552
                currentValue = static_cast<int>(bytesReceived);
×
553
                endValue = static_cast<int>(bytesTotal);
×
554
        }
555

556
        downloadProgressBar->setValue(currentValue);
×
557
        downloadProgressBar->setRange(0, endValue);
×
558
}
559

560
void MpcImportWindow::updateQueryProgress(qint64, qint64)
×
561
{
562
        if (queryProgressBar == nullptr)
×
563
                return;
×
564

565
        //Just show activity
566
        queryProgressBar->setValue(0);
×
567
        queryProgressBar->setRange(0, 0);
×
568
}
569

570
void MpcImportWindow::startDownload(QString urlString)
×
571
{
572
        if (downloadReply)
×
573
        {
574
                //There's already an operation in progress?
575
                //TODO
576
                return;
×
577
        }
578

579
        QUrl url(urlString);
×
580
        if (!url.isValid())
×
581
        {
582
                qWarning() << "Invalid URL:" << urlString;
×
583
                return;
×
584
        }
585
        //qDebug() << url.toString();
586

587
        //TODO: Interface changes!
588

589
        downloadProgressBar = StelApp::getInstance().addProgressBar();
×
590
        downloadProgressBar->setValue(0);
×
591
        downloadProgressBar->setRange(0, 0);
×
592

593
        //TODO: Better handling of the interface
594
        //dialog->setVisible(false);
595
        enableInterface(false);
×
596
        ui->pushButtonAbortDownload->setVisible(true);
×
597

598
        connect(networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(downloadComplete(QNetworkReply*)));
×
599
        QNetworkRequest request;
×
600
        request.setUrl(QUrl(url));
×
601
        request.setRawHeader("User-Agent", StelUtils::getUserAgentString().toUtf8());
×
602
#if (QT_VERSION<QT_VERSION_CHECK(6,0,0))
603
        request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, true);
×
604
#endif
605
        downloadReply = networkManager->get(request);
×
606
        connect(downloadReply, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(updateDownloadProgress(qint64,qint64)));
×
607
}
×
608

609
void MpcImportWindow::abortDownload()
×
610
{
611
        if (downloadReply == nullptr || downloadReply->isFinished())
×
612
                return;
×
613

614
        qDebug() << "Aborting download...";
×
615

616
        disconnect(networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(downloadComplete(QNetworkReply*)));
×
617
        deleteDownloadProgressBar();
×
618

619
        downloadReply->abort();
×
620
        downloadReply->deleteLater();
×
621
        downloadReply = nullptr;
×
622

623
        enableInterface(true);
×
624
        ui->pushButtonAbortDownload->setVisible(false);
×
625
}
626

627
void MpcImportWindow::downloadComplete(QNetworkReply *reply)
×
628
{
629
        disconnect(networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(downloadComplete(QNetworkReply*)));
×
630
        deleteDownloadProgressBar();
×
631
        ui->pushButtonAbortDownload->setVisible(false);
×
632

633
        /*
634
        qDebug() << "reply->isOpen():" << reply->isOpen()
635
                << "reply->isReadable():" << reply->isReadable()
636
                << "reply->isFinished():" << reply->isFinished();
637
        */
638

639
        if(reply->error() || reply->bytesAvailable()==0)
×
640
        {
641
                qWarning() << "Download error: While downloading"
×
642
                           << reply->url().toString()
×
643
                                   << "the following error occurred:"
×
644
                                   << reply->errorString();
×
645
                enableInterface(true);
×
646
                reply->deleteLater();
×
647
                downloadReply = nullptr;
×
648
                return;
×
649
        }
650

651
        QList<SsoElements> objects;
×
652
        QTemporaryFile file;
×
653
        if (file.open())
×
654
        {
655
                file.write(reply->readAll());
×
656
                file.close();
×
657
                objects = readElementsFromFile(importType, file.fileName());
×
658
        }
659
        else
660
        {
661
                qWarning() << "Unable to open a temporary file. Aborting operation.";
×
662
        }
663

664
        if (objects.isEmpty())
×
665
        {
666
                qWarning() << "No objects found in the file downloaded from"
×
667
                           << reply->url().toString();
×
668
        }
669
        else
670
        {
671
                //The request has been successful: add the URL to bookmarks?
672
                if (ui->checkBoxAddBookmark->isChecked())
×
673
                {
674
                        QString url = reply->url().toString();
×
675
                        QString title = ui->lineEditBookmarkTitle->text().trimmed();
×
676
                        //If no title has been entered, use the URL as a title
677
                        if (title.isEmpty())
×
678
                                title = url;
×
679
                        if (!bookmarks.value(importType).values().contains(url))
×
680
                        {
681
                                bookmarks[importType].insert(title, url);
×
682
                                populateBookmarksList();
×
683
                                saveBookmarks();
×
684
                        }
685
                }
×
686
        }
687

688
        reply->deleteLater();
×
689
        downloadReply = nullptr;
×
690

691
        //Temporary, until the slot/socket mechanism is ready
692
        populateCandidateObjects(objects);
×
693
        ui->stackedWidget->setCurrentIndex(1);
×
694
        //As this window is persistent, if the Solar System is changed
695
        //while there is a list, it should be reset.
696
        connect(ssoManager, SIGNAL(solarSystemChanged()), this, SLOT(resetDialog()));
×
697
}
×
698

699
void MpcImportWindow::deleteDownloadProgressBar()
×
700
{
701
        disconnect(this, SLOT(updateDownloadProgress(qint64,qint64)));
×
702

703
        if (downloadProgressBar)
×
704
        {
705
                StelApp::getInstance().removeProgressBar(downloadProgressBar);
×
706
                downloadProgressBar = nullptr;
×
707
        }
708
}
×
709

710
void MpcImportWindow::sendQuery()
×
711
{
712
        if (queryReply != nullptr)
×
713
                return;
×
714

715
        query = ui->lineEditQuery->text().trimmed();
×
716
        if (query.isEmpty())
×
717
                return;
×
718

719
        //Progress bar
720
        queryProgressBar = StelApp::getInstance().addProgressBar();
×
721
        queryProgressBar->setValue(0);
×
722
        queryProgressBar->setRange(0, 0);
×
723
        queryProgressBar->setFormat(q_("Searching..."));
×
724

725
        //TODO: Better handling of the interface
726
        enableInterface(false);
×
727
        ui->labelQueryMessage->setVisible(false);
×
728

729
        startCountdown();
×
730
        ui->pushButtonAbortQuery->setVisible(true);
×
731

732
        //sendQueryToUrl(QUrl("http://stellarium.org/mpc-mpeph"));
733
        //sendQueryToUrl(QUrl("http://scully.cfa.harvard.edu/cgi-bin/mpeph2.cgi"));
734
        // MPC requirements now :(
735
        sendQueryToUrl(QUrl("https://www.minorplanetcenter.net/cgi-bin/mpeph2.cgi"));
×
736
}
737

738
void MpcImportWindow::sendQueryToUrl(QUrl url)
×
739
{
740
        QUrlQuery q(url);
×
741
        q.addQueryItem("ty","e");//Type: ephemerides
×
742
        q.addQueryItem("TextArea", query);//Object name query
×
743
        q.addQueryItem("e", "-1");//Elements format: MPC 1-line
×
744
        //Switch to MPC 1-line format --AW
745
        //XEphem's format is used instead because it doesn't truncate object names.
746
        //q.addQueryItem("e", "3");//Elements format: XEphem
747
        //Yes, all of the rest are necessary
748
        q.addQueryItem("d","");
×
749
        q.addQueryItem("l","");
×
750
        q.addQueryItem("i","");
×
751
        q.addQueryItem("u","d");
×
752
        q.addQueryItem("uto", "0");
×
753
        q.addQueryItem("c", "");
×
754
        q.addQueryItem("long", "");
×
755
        q.addQueryItem("lat", "");
×
756
        q.addQueryItem("alt", "");
×
757
        q.addQueryItem("raty", "a");
×
758
        q.addQueryItem("s", "t");
×
759
        q.addQueryItem("m", "m");
×
760
        q.addQueryItem("adir", "S");
×
761
        q.addQueryItem("oed", "");
×
762
        q.addQueryItem("resoc", "");
×
763
        q.addQueryItem("tit", "");
×
764
        q.addQueryItem("bu", "");
×
765
        q.addQueryItem("ch", "c");
×
766
        q.addQueryItem("ce", "f");
×
767
        q.addQueryItem("js", "f");
×
768
        url.setQuery(q);
×
769

770
        QNetworkRequest request;
×
771
        request.setUrl(QUrl(url));
×
772
        request.setRawHeader("User-Agent", StelUtils::getUserAgentString().toUtf8());
×
773
        request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); //Is this really necessary?
×
774
        request.setHeader(QNetworkRequest::ContentLengthHeader, url.query(QUrl::FullyEncoded).length());
×
775
#if (QT_VERSION<QT_VERSION_CHECK(6,0,0))
776
        request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, true);
×
777
#endif
778

779
        connect(networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(receiveQueryReply(QNetworkReply*)));
×
780
        queryReply = networkManager->post(request, url.query(QUrl::FullyEncoded).toUtf8());        
×
781
        connect(queryReply, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(updateQueryProgress(qint64,qint64)));
×
782
}
×
783

784
void MpcImportWindow::abortQuery()
×
785
{
786
        if (queryReply == nullptr)
×
787
                return;
×
788

789
        disconnect(networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(receiveQueryReply(QNetworkReply*)));
×
790
        deleteQueryProgressBar();
×
791

792
        queryReply->abort();
×
793
        queryReply->deleteLater();
×
794
        queryReply = nullptr;
×
795

796
        //resetCountdown();
797
        enableInterface(true);
×
798
        ui->pushButtonAbortQuery->setVisible(false);
×
799
}
800

801
void MpcImportWindow::receiveQueryReply(QNetworkReply *reply)
×
802
{
803
        if (reply == nullptr)
×
804
                return;
×
805

806
        disconnect(networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(receiveQueryReply(QNetworkReply*)));
×
807

808
        int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
×
809
        if (statusCode == 301 || statusCode == 302 || statusCode == 307)
×
810
        {
811
                QUrl rawUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
×
812
                QUrl redirectUrl(rawUrl.toString(QUrl::RemoveQuery));
×
813
                qDebug() << "The search query has been redirected to" << redirectUrl.toString();
×
814

815
                //TODO: Add counter and cycle check.
816

817
                reply->deleteLater();
×
818
                queryReply = nullptr;
×
819
                sendQueryToUrl(redirectUrl);
×
820
                return;
×
821
        }
×
822

823
        deleteQueryProgressBar();
×
824

825
        //Hide the abort button - a reply has been received
826
        ui->pushButtonAbortQuery->setVisible(false);
×
827

828
        if (reply->error() || reply->bytesAvailable()==0)
×
829
        {
830
                qWarning() << "Download error: While trying to access"
×
831
                           << reply->url().toString()
×
832
                           << "the following error occurred:"
×
833
                           << reply->errorString();
×
834
                ui->labelQueryMessage->setText(reply->errorString());//TODO: Decide if this is a good idea
×
835
                ui->labelQueryMessage->setVisible(true);
×
836
                enableInterface(true);
×
837

838
                reply->deleteLater();
×
839
                queryReply = nullptr;
×
840
                return;
×
841
        }
842

843
        QString contentType = reply->header(QNetworkRequest::ContentTypeHeader).toString();
×
844
        QString contentDisposition = reply->rawHeader(QByteArray("Content-disposition"));
×
845
        if (contentType == "text/ascii" &&
×
846
            contentDisposition == "attachment; filename=elements.txt")
×
847
        {
848
                readQueryReply(reply);
×
849
        }
850
        else
851
        {
852
                ui->labelQueryMessage->setText("Object not found.");
×
853
                ui->labelQueryMessage->setVisible(true);
×
854
                enableInterface(true);
×
855
        }
856

857
        reply->deleteLater();
×
858
        queryReply = nullptr;
×
859
}
×
860

861
void MpcImportWindow::readQueryReply(QNetworkReply * reply)
×
862
{
863
        Q_ASSERT(reply);
×
864

865
        QList<SsoElements> objects;
×
866
        QTemporaryFile file;
×
867
        if (file.open())
×
868
        {
869
                file.write(reply->readAll());                
×
870
                file.close();
×
871

872
                static const QRegularExpression cometIAUDesignation("[PCDXI]/");
×
873
                static const QRegularExpression cometDesignation("(\\d)+[PCDXI]/");
×
874
                QString queryData = ui->lineEditQuery->text().trimmed();
×
875

876
                if (queryData.indexOf(cometDesignation) == 0 || queryData.indexOf(cometIAUDesignation) == 0)
×
877
                        objects = readElementsFromFile(MpcComets, file.fileName());
×
878
                else
879
                        objects = readElementsFromFile(MpcMinorPlanets, file.fileName());
×
880

881
                /*
882
                //Try to read it as a comet first?
883
                objects = readElementsFromFile(MpcComets, file.fileName());
884
                if (objects.isEmpty())
885
                        objects = readElementsFromFile(MpcMinorPlanets, file.fileName());
886
                */
887
                //XEphem given wrong data for comets --AW
888
                //objects = ssoManager->readXEphemOneLineElementsFromFile(file.fileName());
889
        }
×
890
        else
891
        {
892
                qWarning() << "Unable to open a temporary file. Aborting operation.";
×
893
        }
894

895
        if (objects.isEmpty())
×
896
        {
897
                qWarning() << "No objects found in the file downloaded from"
×
898
                                   << reply->url().toString();
×
899
        }
900
        else
901
        {
902
                //The request has been successful: add the URL to bookmarks?
903
                if (ui->checkBoxAddBookmark->isChecked())
×
904
                {
905
                        QString url = reply->url().toString();
×
906
                        if (!bookmarks.value(importType).values().contains(url))
×
907
                        {
908
                                //Use the URL as a title for now
909
                                bookmarks[importType].insert(url, url);
×
910
                        }
911
                }
×
912

913
                //Temporary, until the slot/socket mechanism is ready
914
                populateCandidateObjects(objects);
×
915
                ui->stackedWidget->setCurrentIndex(1);
×
916
        }
917
}
×
918

919
void MpcImportWindow::deleteQueryProgressBar()
×
920
{
921
        disconnect(this, SLOT(updateQueryProgress(qint64,qint64)));
×
922
        if (queryProgressBar)
×
923
        {
924
                StelApp::getInstance().removeProgressBar(queryProgressBar);
×
925
                queryProgressBar = nullptr;
×
926
        }
927
}
×
928

929
void MpcImportWindow::startCountdown()
×
930
{
931
        if (!countdownTimer->isActive())
×
932
                countdownTimer->start(1000);//1 second
×
933

934
        //Disable the interface
935
        ui->lineEditQuery->setEnabled(false);
×
936
        ui->pushButtonSendQuery->setEnabled(false);
×
937
}
×
938

939
void MpcImportWindow::resetCountdown()
×
940
{
941
        //Stop the timer
942
        if (countdownTimer->isActive())
×
943
        {
944
                countdownTimer->stop();
×
945

946
                //If the query is still active, kill it
947
                if (queryReply != nullptr && queryReply->isRunning())
×
948
                {
949
                        abortQuery();
×
950
                        ui->labelQueryMessage->setText("The query timed out. You can try again, now or later.");
×
951
                        ui->labelQueryMessage->setVisible(true);
×
952
                }
953
        }
954

955
        //Reset the counter
956
        countdown = 60;
×
957

958
        //Enable the interface
959
        ui->lineEditQuery->setEnabled(true);
×
960
        ui->pushButtonSendQuery->setEnabled(true);
×
961
}
×
962

963
void MpcImportWindow::updateCountdown()
×
964
{
965
        --countdown;
×
966
        if (countdown < 0)
×
967
        {
968
                resetCountdown();
×
969
        }
970
        //If there has been an answer
971
        else if (countdown > 50 && queryReply == nullptr)
×
972
        {
973
                resetCountdown();
×
974
        }
975
}
×
976

977
void MpcImportWindow::resetNotFound()
×
978
{
979
        ui->labelQueryMessage->setVisible(false);
×
980
}
×
981

982
void MpcImportWindow::loadBookmarks()
×
983
{
984
        bookmarks[MpcComets].clear();
×
985
        bookmarks[MpcMinorPlanets].clear();
×
986

987
        QString bookmarksFilePath(StelFileMgr::getUserDir() + "/modules/SolarSystemEditor/bookmarks.json");
×
988
        bool outdated = false;
×
989
        if (StelFileMgr::isReadable(bookmarksFilePath))
×
990
        {
991
                QFile bookmarksFile(bookmarksFilePath);
×
992
                if (bookmarksFile.open(QFile::ReadOnly | QFile::Text))
×
993
                {
994
                        QVariantMap jsonRoot;
×
995
                        QString fileVersion = "0.0.0";
×
996
                        try
997
                        {
998
                                jsonRoot = StelJsonParser::parse(bookmarksFile.readAll()).toMap();
×
999
                                bookmarksFile.close();
×
1000

1001
                                fileVersion = jsonRoot.value("version").toString();
×
1002
                                if (fileVersion.isEmpty())
×
1003
                                        fileVersion = "0.0.0";
×
1004

1005
                                loadBookmarksGroup(jsonRoot.value("mpcMinorPlanets").toMap(), bookmarks[MpcMinorPlanets]);
×
1006
                                loadBookmarksGroup(jsonRoot.value("mpcComets").toMap(), bookmarks[MpcComets]);
×
1007
                        }
1008
                        catch (std::runtime_error &e)
×
1009
                        {
1010
                                qDebug() << "File format is wrong! Error: " << e.what();
×
1011
                                outdated = true;
×
1012
                        }
×
1013

1014
                        if (StelUtils::compareVersions(fileVersion, SOLARSYSTEMEDITOR_PLUGIN_VERSION)<0)
×
1015
                                outdated = true; // Oops... the list is outdated!
×
1016

1017
                        //If nothing was read, continue
1018
                        if (!bookmarks.value(MpcComets).isEmpty() && !bookmarks[MpcMinorPlanets].isEmpty() && !outdated)
×
1019
                                return;
×
1020
                }
×
1021
        }
×
1022

1023
        if (outdated)
×
1024
                qDebug() << "Bookmarks file is outdated! The list will be upgraded by hard-coded bookmarks.";
×
1025
        else
1026
                qDebug() << "Bookmarks file can't be read. Hard-coded bookmarks will be used.";
×
1027

1028
        // Remove outdated bookmarks for minor planets
1029
        QHashIterator<QString, QString> it(bookmarks[MpcMinorPlanets]);
×
1030
        while (it.hasNext())
×
1031
        {
1032
                it.next();
×
1033
                if (it.value().contains("dss.stellarium.org") || it.value().contains("2018/Soft00Bright.txt"))
×
1034
                        bookmarks[MpcMinorPlanets].remove(it.key());
×
1035
        }
1036

1037
        //Initialize with hard-coded values
1038
        // NOTE: this list is reordered anyway when loaded
1039

1040
        bookmarks[MpcMinorPlanets].insert("MPC's list of observable critical-list numbered minor planets",         "https://www.minorplanetcenter.net/iau/Ephemerides/CritList/Soft00CritList.txt");
×
1041
        bookmarks[MpcMinorPlanets].insert("MPC's list of observable distant minor planets",                         "https://www.minorplanetcenter.net/iau/Ephemerides/Distant/Soft00Distant.txt");
×
1042
        bookmarks[MpcMinorPlanets].insert("MPC's list of observable unusual minor planets",                         "https://www.minorplanetcenter.net/iau/Ephemerides/Unusual/Soft00Unusual.txt");
×
1043
        bookmarks[MpcMinorPlanets].insert("MPC's list of bright minor planets for 2023",                        "https://www.minorplanetcenter.net/iau/Ephemerides/Bright/2023/Soft00Bright.txt");
×
1044

1045
        bookmarks[MpcMinorPlanets].insert("MPCORB: near-Earth asteroids (NEAs)",                                 "https://www.minorplanetcenter.net/iau/MPCORB/NEA.txt");
×
1046
        bookmarks[MpcMinorPlanets].insert("MPCORB: potentially hazardous asteroids (PHAs)",                         "https://www.minorplanetcenter.net/iau/MPCORB/PHA.txt");
×
1047
        bookmarks[MpcMinorPlanets].insert("MPCORB: TNOs, Centaurs and SDOs",                                         "https://www.minorplanetcenter.net/iau/MPCORB/Distant.txt");
×
1048
        bookmarks[MpcMinorPlanets].insert("MPCORB: other unusual objects",                                         "https://www.minorplanetcenter.net/iau/MPCORB/Unusual.txt");
×
1049
        bookmarks[MpcMinorPlanets].insert("MPCORB: elements of NEAs for current epochs (today)",                 "https://www.minorplanetcenter.net/iau/MPCORB/NEAm00.txt");
×
1050
        bookmarks[MpcMinorPlanets].insert("MPCORB: orbits from the latest DOU MPEC",                                 "https://www.minorplanetcenter.net/iau/MPCORB/DAILY.DAT");
×
1051

1052
        bookmarks[MpcMinorPlanets].insert("MPCAT: Unusual minor planets (including NEOs)",                         "https://www.minorplanetcenter.net/iau/ECS/MPCAT/unusual.txt");
×
1053
        bookmarks[MpcMinorPlanets].insert("MPCAT: Distant minor planets (Centaurs and transneptunians)",         "https://www.minorplanetcenter.net/iau/ECS/MPCAT/distant.txt");
×
1054

1055
        const int start = 0;
×
1056
        const int finish = 61;
×
1057
        const int nsize = 6;
×
1058
        const QChar dash = QChar(0x2014);
×
1059

1060
        QString limits, idx, leftLimit, rightLimit;
×
1061

1062
        for (int i=start; i<=finish; i++)
×
1063
        {
1064
                leftLimit = (i==start) ? QString::number(1).rightJustified(nsize) : QString::number(i*10000).rightJustified(nsize);
×
1065
                rightLimit = (i==finish) ? "..." : QString::number(i*10000 + 9999).rightJustified(nsize);
×
1066
                limits = QString("%1%2%3").arg(leftLimit, dash, rightLimit);
×
1067
                idx = QString::number(i+1).rightJustified(2, '0');
×
1068
                bookmarks[MpcMinorPlanets].insert(
×
1069
                        QString("MPCAT: Numbered objects (%1)").arg(limits), 
×
1070
                        QString("http://dss.stellarium.org/MPC/mpn-%1.txt").arg(idx)
×
1071
                        );
1072
        }
1073

1074
        bookmarks[MpcComets].insert("MPC's list of observable comets",        "https://www.minorplanetcenter.net/iau/Ephemerides/Comets/Soft00Cmt.txt");
×
1075
        bookmarks[MpcComets].insert("MPCORB: comets",                        "https://www.minorplanetcenter.net/iau/MPCORB/CometEls.txt");
×
1076
        bookmarks[MpcComets].insert("MPCORB: all comets and A/-objects","https://www.minorplanetcenter.net/iau/MPCORB/AllCometEls.txt");
×
1077

1078
        bookmarks[MpcComets].insert("Gideon van Buitenen: comets",        "http://astro.vanbuitenen.nl/cometelements?format=mpc&mag=obs");
×
1079

1080
        //Try to save them to a file
1081
        saveBookmarks();
×
1082
}
×
1083

1084
void MpcImportWindow::loadBookmarksGroup(QVariantMap source, Bookmarks & bookmarkGroup)
×
1085
{
1086
        if (source.isEmpty())
×
1087
                return;
×
1088

1089
        QMapIterator<QString, QVariant> it(source);
×
1090
        while (it.hasNext())
×
1091
        {
1092
                it.next();
×
1093
                QString url = it.value().toString();
×
1094
                if (!url.isEmpty())
×
1095
                        bookmarkGroup.insert(it.key(), url);
×
1096
        }
×
1097
}
×
1098

1099
void MpcImportWindow::saveBookmarks()
×
1100
{
1101
        try
1102
        {
1103
                StelFileMgr::makeSureDirExistsAndIsWritable(StelFileMgr::getUserDir() + "/modules/SolarSystemEditor");
×
1104

1105
                QVariantMap jsonRoot;
×
1106

1107
                QString bookmarksFilePath(StelFileMgr::getUserDir() + "/modules/SolarSystemEditor/bookmarks.json");
×
1108

1109
                //If the file exists, load it first
1110
                if (StelFileMgr::isReadable(bookmarksFilePath))
×
1111
                {
1112
                        QFile bookmarksFile(bookmarksFilePath);
×
1113
                        if (bookmarksFile.open(QFile::ReadOnly | QFile::Text))
×
1114
                        {
1115
                                jsonRoot = StelJsonParser::parse(bookmarksFile.readAll()).toMap();
×
1116
                                bookmarksFile.close();
×
1117
                        }
1118
                }
×
1119

1120
                QFile bookmarksFile(bookmarksFilePath);
×
1121
                if (bookmarksFile.open(QFile::WriteOnly | QFile::Truncate | QFile::Text))
×
1122
                {
1123
                        jsonRoot.insert("version", SOLARSYSTEMEDITOR_PLUGIN_VERSION);
×
1124

1125
                        QVariantMap minorPlanetsObject;
×
1126
                        saveBookmarksGroup(bookmarks[MpcMinorPlanets], minorPlanetsObject);
×
1127
                        //qDebug() << minorPlanetsObject.keys();
1128
                        jsonRoot.insert("mpcMinorPlanets", minorPlanetsObject);
×
1129

1130
                        QVariantMap cometsObject;
×
1131
                        saveBookmarksGroup(bookmarks[MpcComets], cometsObject);
×
1132
                        jsonRoot.insert("mpcComets", cometsObject);
×
1133

1134
                        StelJsonParser::write(jsonRoot, &bookmarksFile);
×
1135
                        bookmarksFile.close();
×
1136

1137
                        qDebug() << "Bookmarks file saved to" << QDir::toNativeSeparators(bookmarksFilePath);
×
1138
                        return;
×
1139
                }
×
1140
                else
1141
                {
1142
                        qDebug() << "Unable to write bookmarks file to" << QDir::toNativeSeparators(bookmarksFilePath);
×
1143
                }
1144
        }
×
1145
        catch (std::exception & e)
×
1146
        {
1147
                qDebug() << "Unable to save bookmarks file:" << e.what();
×
1148
        }
×
1149
}
1150

1151
void MpcImportWindow::saveBookmarksGroup(Bookmarks & bookmarkGroup, QVariantMap & output)
×
1152
{
1153
        QHashIterator<QString, QString>it(bookmarkGroup);
×
1154
        while (it.hasNext())
×
1155
        {
1156
                it.next();
×
1157
                output.insert(it.key(), it.value());
×
1158
        }
1159
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc