1 /*
2  * Copyright (C) 2012 Alexander Wolf
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA  02110-1335, USA.
17  */
18 
19 #include "StelProjector.hpp"
20 #include "StelPainter.hpp"
21 #include "StelApp.hpp"
22 #include "StelCore.hpp"
23 #include "StelGui.hpp"
24 #include "StelGuiItems.hpp"
25 #include "StelLocaleMgr.hpp"
26 #include "StelModuleMgr.hpp"
27 #include "StelObjectMgr.hpp"
28 #include "StelTextureMgr.hpp"
29 #include "StelJsonParser.hpp"
30 #include "StelFileMgr.hpp"
31 #include "StelUtils.hpp"
32 #include "StelTranslator.hpp"
33 #include "LabelMgr.hpp"
34 #include "Pulsar.hpp"
35 #include "Pulsars.hpp"
36 #include "PulsarsDialog.hpp"
37 #include "StelProgressController.hpp"
38 
39 #include <QNetworkAccessManager>
40 #include <QNetworkReply>
41 #include <QKeyEvent>
42 #include <QDebug>
43 #include <QFileInfo>
44 #include <QFile>
45 #include <QTimer>
46 #include <QVariantMap>
47 #include <QVariant>
48 #include <QList>
49 #include <QSharedPointer>
50 #include <QStringList>
51 #include <QDir>
52 #include <QSettings>
53 #include <stdexcept>
54 
55 #define CATALOG_FORMAT_VERSION 2 /* Version of format of catalog */
56 
57 /*
58  This method is the one called automatically by the StelModuleMgr just
59  after loading the dynamic library
60 */
getStelModule() const61 StelModule* PulsarsStelPluginInterface::getStelModule() const
62 {
63 	return new Pulsars();
64 }
65 
getPluginInfo() const66 StelPluginInfo PulsarsStelPluginInterface::getPluginInfo() const
67 {
68 	Q_INIT_RESOURCE(Pulsars);
69 
70 	StelPluginInfo info;
71 	info.id = "Pulsars";
72 	info.displayedName = N_("Pulsars");
73 	info.authors = "Alexander Wolf";
74 	info.contact = STELLARIUM_DEV_URL;
75 	info.description = N_("This plugin plots the position of various pulsars, with object information about each one.");
76 	info.version = PULSARS_PLUGIN_VERSION;
77 	info.license = PULSARS_PLUGIN_LICENSE;
78 	return info;
79 }
80 
81 /*
82  Constructor
83 */
Pulsars()84 Pulsars::Pulsars()
85 	: PsrCount(0)
86 	, updateState(CompleteNoUpdates)
87 	, networkManager(Q_NULLPTR)
88 	, downloadReply(Q_NULLPTR)
89 	, updateTimer(Q_NULLPTR)
90 	, updatesEnabled(false)
91 	, updateFrequencyDays(0)
92 	, enableAtStartup(false)
93 	, flagShowPulsars(false)
94 	, flagShowPulsarsButton(false)
95 	, OnIcon(Q_NULLPTR)
96 	, OffIcon(Q_NULLPTR)
97 	, GlowIcon(Q_NULLPTR)
98 	, toolbarButton(Q_NULLPTR)
99 	, progressBar(Q_NULLPTR)
100 {
101 	setObjectName("Pulsars");
102 	configDialog = new PulsarsDialog();
103 	conf = StelApp::getInstance().getSettings();
104 	setFontSize(StelApp::getInstance().getScreenFontSize());
105 	connect(&StelApp::getInstance(), SIGNAL(screenFontSizeChanged(int)), this, SLOT(setFontSize(int)));
106 }
107 
108 /*
109  Destructor
110 */
~Pulsars()111 Pulsars::~Pulsars()
112 {
113 	delete configDialog;
114 
115 	if (GlowIcon)
116 		delete GlowIcon;
117 	if (OnIcon)
118 		delete OnIcon;
119 	if (OffIcon)
120 		delete OffIcon;
121 }
122 
deinit()123 void Pulsars::deinit()
124 {
125 	psr.clear();
126 	Pulsar::markerTexture.clear();
127 	texPointer.clear();
128 }
129 
130 /*
131  Reimplementation of the getCallOrder method
132 */
getCallOrder(StelModuleActionName actionName) const133 double Pulsars::getCallOrder(StelModuleActionName actionName) const
134 {
135 	if (actionName==StelModule::ActionDraw)
136 		return StelApp::getInstance().getModuleMgr().getModule("ConstellationMgr")->getCallOrder(actionName)+10.;
137 	return 0;
138 }
139 
140 
141 /*
142  Init our module
143 */
init()144 void Pulsars::init()
145 {
146 	upgradeConfigIni();
147 
148 	try
149 	{
150 		StelFileMgr::makeSureDirExistsAndIsWritable(StelFileMgr::getUserDir()+"/modules/Pulsars");
151 
152 		// If no settings in the main config file, create with defaults
153 		if (!conf->childGroups().contains("Pulsars"))
154 		{
155 			qDebug() << "[Pulsars] No Pulsars section exists in main config file - creating with defaults";
156 			restoreDefaultConfigIni();
157 		}
158 
159 		// populate settings from main config file.
160 		readSettingsFromConfig();
161 
162 		jsonCatalogPath = StelFileMgr::findFile("modules/Pulsars", static_cast<StelFileMgr::Flags>(StelFileMgr::Directory|StelFileMgr::Writable)) + "/pulsars.json";
163 		if (jsonCatalogPath.isEmpty())
164 			return;
165 
166 		texPointer = StelApp::getInstance().getTextureManager().createTexture(StelFileMgr::getInstallationDir()+"/textures/pointeur2.png");
167 		Pulsar::markerTexture = StelApp::getInstance().getTextureManager().createTexture(":/Pulsars/pulsar.png");
168 
169 		// key bindings and other actions
170 		addAction("actionShow_Pulsars", N_("Pulsars"), N_("Show pulsars"), "pulsarsVisible", "Ctrl+Alt+P");
171 		addAction("actionShow_Pulsars_dialog", N_("Pulsars"), N_("Show settings dialog"), configDialog, "visible", ""); // Allow assign shortkey
172 
173 		GlowIcon = new QPixmap(":/graphicGui/miscGlow32x32.png");
174 		OnIcon = new QPixmap(":/Pulsars/btPulsars-on.png");
175 		OffIcon = new QPixmap(":/Pulsars/btPulsars-off.png");
176 
177 		setFlagShowPulsars(getEnableAtStartup());
178 		setFlagShowPulsarsButton(flagShowPulsarsButton);
179 	}
180 	catch (std::runtime_error &e)
181 	{
182 		qWarning() << "[Pulsars] init error:" << e.what();
183 		return;
184 	}
185 
186 	// If the json file does not already exist, create it from the resource in the Qt resource
187 	if(QFileInfo(jsonCatalogPath).exists())
188 	{
189 		if (!checkJsonFileFormat() || getJsonFileFormatVersion()<CATALOG_FORMAT_VERSION)
190 		{
191 			restoreDefaultJsonFile();
192 		}
193 	}
194 	else
195 	{
196 		qDebug() << "[Pulsars] pulsars.json does not exist - copying default file to" << QDir::toNativeSeparators(jsonCatalogPath);
197 		restoreDefaultJsonFile();
198 	}
199 
200 	qDebug() << "[Pulsars] Loading catalog file:" << QDir::toNativeSeparators(jsonCatalogPath);
201 
202 	readJsonFile();
203 
204 	// Set up download manager and the update schedule
205 	networkManager = StelApp::getInstance().getNetworkAccessManager();
206 	updateState = CompleteNoUpdates;
207 	updateTimer = new QTimer(this);
208 	updateTimer->setSingleShot(false);   // recurring check for update
209 	updateTimer->setInterval(13000);     // check once every 13 seconds to see if it is time for an update
210 	connect(updateTimer, SIGNAL(timeout()), this, SLOT(checkForUpdate()));
211 	updateTimer->start();
212 
213 	connect(this, SIGNAL(jsonUpdateComplete(void)), this, SLOT(reloadCatalog()));
214 	connect(StelApp::getInstance().getCore(), SIGNAL(configurationDataSaved()), this, SLOT(saveSettings()));
215 
216 	GETSTELMODULE(StelObjectMgr)->registerStelObjectMgr(this);
217 }
218 
219 /*
220  Draw our module.
221 */
draw(StelCore * core)222 void Pulsars::draw(StelCore* core)
223 {
224 	if (!flagShowPulsars)
225 		return;
226 
227 	StelProjectorP prj = core->getProjection(StelCore::FrameJ2000);
228 	StelPainter painter(prj);
229 	painter.setFont(font);
230 
231 	for (const auto& pulsar : qAsConst(psr))
232 	{
233 		if (pulsar && pulsar->initialized)
234 			pulsar->draw(core, &painter);
235 	}
236 
237 	if (GETSTELMODULE(StelObjectMgr)->getFlagSelectedObjectPointer())
238 		drawPointer(core, painter);
239 }
240 
drawPointer(StelCore * core,StelPainter & painter)241 void Pulsars::drawPointer(StelCore* core, StelPainter& painter)
242 {
243 	const StelProjectorP prj = core->getProjection(StelCore::FrameJ2000);
244 
245 	const QList<StelObjectP> newSelected = GETSTELMODULE(StelObjectMgr)->getSelectedObject("Pulsar");
246 	if (!newSelected.empty())
247 	{
248 		const StelObjectP obj = newSelected[0];
249 		Vec3d pos=obj->getJ2000EquatorialPos(core);
250 
251 		Vec3f screenpos;
252 		// Compute 2D pos and return if outside screen
253 		if (!painter.getProjector()->project(pos.toVec3f(), screenpos))
254 			return;
255 
256 		painter.setColor(obj->getInfoColor());
257 		texPointer->bind();
258 		painter.setBlending(true);
259 		painter.drawSprite2dMode(screenpos[0], screenpos[1], 13.f, StelApp::getInstance().getTotalRunTime()*40.);
260 	}
261 }
262 
searchAround(const Vec3d & av,double limitFov,const StelCore * core) const263 QList<StelObjectP> Pulsars::searchAround(const Vec3d& av, double limitFov, const StelCore* core) const
264 {
265 	QList<StelObjectP> result;
266 
267 	if (!flagShowPulsars)
268 		return result;
269 
270 	Vec3d v(av);
271 	v.normalize();
272 	const double cosLimFov = cos(limitFov * M_PI/180.);
273 	Vec3d equPos;
274 
275 	for (const auto& pulsar : psr)
276 	{
277 		if (pulsar->initialized)
278 		{
279 			equPos = pulsar->getJ2000EquatorialPos(core);
280 			equPos.normalize();
281 			if (equPos.dot(v) >= cosLimFov)
282 			{
283 				result.append(qSharedPointerCast<StelObject>(pulsar));
284 			}
285 		}
286 	}
287 
288 	return result;
289 }
290 
searchByName(const QString & englishName) const291 StelObjectP Pulsars::searchByName(const QString& englishName) const
292 {
293 	if (!flagShowPulsars)
294 		return Q_NULLPTR;
295 
296 	for (const auto& pulsar : psr)
297 	{
298 		if (pulsar->getEnglishName().toUpper() == englishName.toUpper() || pulsar->getDesignation().toUpper() == englishName.toUpper())
299 			return qSharedPointerCast<StelObject>(pulsar);
300 	}
301 
302 	return Q_NULLPTR;
303 }
304 
searchByNameI18n(const QString & nameI18n) const305 StelObjectP Pulsars::searchByNameI18n(const QString& nameI18n) const
306 {
307 	if (!flagShowPulsars)
308 		return Q_NULLPTR;
309 
310 	for (const auto& pulsar : psr)
311 	{
312 		if (pulsar->getNameI18n().toUpper() == nameI18n.toUpper() || pulsar->getDesignation().toUpper() == nameI18n.toUpper())
313 			return qSharedPointerCast<StelObject>(pulsar);
314 	}
315 
316 	return Q_NULLPTR;
317 }
318 
listMatchingObjects(const QString & objPrefix,int maxNbItem,bool useStartOfWords) const319 QStringList Pulsars::listMatchingObjects(const QString& objPrefix, int maxNbItem, bool useStartOfWords) const
320 {
321 	QStringList result;
322 	if (flagShowPulsars && maxNbItem>0)
323 	{
324 		QStringList names;
325 		for (const auto& pulsar : psr)
326 		{
327 			if (!pulsar->getNameI18n().isEmpty())
328 				names << pulsar->getNameI18n();
329 			if (!pulsar->getEnglishName().isEmpty())
330 				names << pulsar->getEnglishName();
331 			names << pulsar->getDesignation();
332 		}
333 
334 		QString fullMatch = "";
335 		for (const auto& name : qAsConst(names))
336 		{
337 			if (!matchObjectName(name, objPrefix, useStartOfWords))
338 				continue;
339 
340 			if (name==objPrefix)
341 				fullMatch = name;
342 			else
343 				result.append(name);
344 
345 			if (result.size() >= maxNbItem)
346 				break;
347 		}
348 
349 		result.sort();
350 		if (!fullMatch.isEmpty())
351 			result.prepend(fullMatch);
352 	}
353 	return result;
354 }
355 
listAllObjects(bool inEnglish) const356 QStringList Pulsars::listAllObjects(bool inEnglish) const
357 {
358 	QStringList result;
359 	if (!flagShowPulsars)
360 		return result;
361 
362 	if (inEnglish)
363 	{
364 		for (const auto& pulsar : psr)
365 		{
366 			if (!pulsar->getEnglishName().isEmpty())
367 				result << pulsar->getEnglishName();
368 			result << pulsar->getDesignation();
369 		}
370 	}
371 	else
372 	{
373 		for (const auto& pulsar : psr)
374 		{
375 			if (!pulsar->getNameI18n().isEmpty())
376 				result << pulsar->getNameI18n();
377 			result << pulsar->getDesignation();
378 		}
379 	}
380 	return result;
381 }
382 
383 /*
384   Replace the JSON file with the default from the compiled-in resource
385 */
restoreDefaultJsonFile(void)386 void Pulsars::restoreDefaultJsonFile(void)
387 {
388 	if (QFileInfo(jsonCatalogPath).exists())
389 		backupJsonFile(true);
390 
391 	QFile src(":/Pulsars/pulsars.json");
392 	if (!src.copy(jsonCatalogPath))
393 	{
394 		qWarning() << "[Pulsars] Cannot copy JSON resource to" + QDir::toNativeSeparators(jsonCatalogPath);
395 	}
396 	else
397 	{
398 		qDebug() << "[Pulsars] Copied default pulsars.json to" << QDir::toNativeSeparators(jsonCatalogPath);
399 		// The resource is read only, and the new file inherits this...  make sure the new file
400 		// is writable by the Stellarium process so that updates can be done.
401 		QFile dest(jsonCatalogPath);
402 		dest.setPermissions(dest.permissions() | QFile::WriteOwner);
403 
404 		// Make sure that in the case where an online update has previously been done, but
405 		// the json file has been manually removed, that an update is schreduled in a timely
406 		// manner
407 		conf->remove("Pulsars/last_update");
408 		lastUpdate = QDateTime::fromString("2012-05-24T12:00:00", Qt::ISODate);
409 	}
410 }
411 
412 /*
413   Creates a backup of the pulsars.json file called pulsars.json.old
414 */
backupJsonFile(bool deleteOriginal)415 bool Pulsars::backupJsonFile(bool deleteOriginal)
416 {
417 	QFile old(jsonCatalogPath);
418 	if (!old.exists())
419 	{
420 		qWarning() << "[Pulsars] No file to backup";
421 		return false;
422 	}
423 
424 	QString backupPath = jsonCatalogPath + ".old";
425 	if (QFileInfo(backupPath).exists())
426 		QFile(backupPath).remove();
427 
428 	if (old.copy(backupPath))
429 	{
430 		if (deleteOriginal)
431 		{
432 			if (!old.remove())
433 			{
434 				qWarning() << "[Pulsars] WARNING - could not remove old pulsars.json file";
435 				return false;
436 			}
437 		}
438 	}
439 	else
440 	{
441 		qWarning() << "[Pulsars] WARNING - failed to copy pulsars.json to pulsars.json.old";
442 		return false;
443 	}
444 
445 	return true;
446 }
447 
448 /*
449   Read the JSON file and create list of pulsars.
450 */
readJsonFile(void)451 void Pulsars::readJsonFile(void)
452 {
453 	setPSRMap(loadPSRMap());
454 }
455 
456 /*
457   Parse JSON file and load pulsars to map
458 */
loadPSRMap(QString path)459 QVariantMap Pulsars::loadPSRMap(QString path)
460 {
461 	if (path.isEmpty())
462 	    path = jsonCatalogPath;
463 
464 	QVariantMap map;
465 	QFile jsonFile(path);
466 	if (!jsonFile.open(QIODevice::ReadOnly))
467 		qWarning() << "[Pulsars] Cannot open" << QDir::toNativeSeparators(path);
468 	else
469 	{
470 		try
471 		{
472 			map = StelJsonParser::parse(jsonFile.readAll()).toMap();
473 			jsonFile.close();
474 		}
475 		catch (std::runtime_error &e)
476 		{
477 			qDebug() << "[Pulsars] File format is wrong! Error: " << e.what();
478 			return QVariantMap();
479 		}
480 	}
481 	return map;
482 }
483 
484 /*
485   Set items for list of struct from data map
486 */
setPSRMap(const QVariantMap & map)487 void Pulsars::setPSRMap(const QVariantMap& map)
488 {
489 	psr.clear();
490 	PsrCount = 0;
491 	QVariantMap psrMap = map.value("pulsars").toMap();
492 	for (auto psrKey : psrMap.keys())
493 	{
494 		QVariantMap psrData = psrMap.value(psrKey).toMap();
495 		psrData["designation"] = psrKey;
496 
497 		PsrCount++;
498 
499 		PulsarP pulsar(new Pulsar(psrData));
500 		if (pulsar->initialized)
501 			psr.append(pulsar);
502 	}
503 }
504 
getJsonFileFormatVersion(void)505 int Pulsars::getJsonFileFormatVersion(void)
506 {
507 	int jsonVersion = -1;
508 	QFile jsonPSRCatalogFile(jsonCatalogPath);
509 	if (!jsonPSRCatalogFile.open(QIODevice::ReadOnly))
510 	{
511 		qWarning() << "[Pulsars] Cannot open" << QDir::toNativeSeparators(jsonCatalogPath);
512 		return jsonVersion;
513 	}
514 
515 	QVariantMap map;
516 	try
517 	{
518 		map = StelJsonParser::parse(&jsonPSRCatalogFile).toMap();
519 		jsonPSRCatalogFile.close();
520 	}
521 	catch (std::runtime_error &e)
522 	{
523 		qDebug() << "[Pulsars] File format is wrong! Error: " << e.what();
524 		return jsonVersion;
525 	}
526 	if (map.contains("version"))
527 	{
528 		jsonVersion = map.value("version").toInt();
529 	}
530 	qDebug() << "[Pulsars] Version of the format of the catalog:" << jsonVersion;
531 	return jsonVersion;
532 }
533 
checkJsonFileFormat()534 bool Pulsars::checkJsonFileFormat()
535 {
536 	QFile jsonPSRCatalogFile(jsonCatalogPath);
537 	if (!jsonPSRCatalogFile.open(QIODevice::ReadOnly))
538 	{
539 		qWarning() << "[Pulsars] Cannot open" << QDir::toNativeSeparators(jsonCatalogPath);
540 		return false;
541 	}
542 
543 	QVariantMap map;
544 	try
545 	{
546 		map = StelJsonParser::parse(&jsonPSRCatalogFile).toMap();
547 		jsonPSRCatalogFile.close();
548 	}
549 	catch (std::runtime_error& e)
550 	{
551 		qDebug() << "[Pulsars] File format is wrong! Error:" << e.what();
552 		return false;
553 	}
554 
555 	return true;
556 }
557 
getByID(const QString & id) const558 PulsarP Pulsars::getByID(const QString& id) const
559 {
560 	for (const auto& pulsar : psr)
561 	{
562 		if (pulsar->initialized && pulsar->designation == id)
563 			return pulsar;
564 	}
565 	return PulsarP();
566 }
567 
configureGui(bool show)568 bool Pulsars::configureGui(bool show)
569 {
570 	if (show)
571 		configDialog->setVisible(true);
572 	return true;
573 }
574 
restoreDefaults(void)575 void Pulsars::restoreDefaults(void)
576 {
577 	restoreDefaultConfigIni();
578 	restoreDefaultJsonFile();
579 	readJsonFile();
580 	readSettingsFromConfig();
581 }
582 
restoreDefaultConfigIni(void)583 void Pulsars::restoreDefaultConfigIni(void)
584 {
585 	conf->beginGroup("Pulsars");
586 
587 	// delete all existing Pulsars settings...
588 	conf->remove("");
589 
590 	conf->setValue("distribution_enabled", false);
591 	conf->setValue("enable_at_startup", false);
592 	conf->setValue("updates_enabled", true);
593 	conf->setValue("url", "https://stellarium.org/json/pulsars.json");
594 	conf->setValue("update_frequency_days", 100);
595 	conf->setValue("flag_show_pulsars_button", true);
596 	conf->setValue("marker_color", "0.4,0.5,1.0");
597 	conf->setValue("glitch_color", "0.2,0.3,1.0");
598 	conf->setValue("use_separate_colors", false);
599 	conf->setValue("filter_enabled", false);
600 	conf->setValue("filter_value", "150.00");
601 	conf->endGroup();
602 }
603 
readSettingsFromConfig(void)604 void Pulsars::readSettingsFromConfig(void)
605 {
606 	conf->beginGroup("Pulsars");
607 
608 	updateUrl = conf->value("url", "https://stellarium.org/json/pulsars.json").toString();
609 	updateFrequencyDays = conf->value("update_frequency_days", 100).toInt();
610 	lastUpdate = QDateTime::fromString(conf->value("last_update", "2012-05-24T12:00:00").toString(), Qt::ISODate);
611 	updatesEnabled = conf->value("updates_enabled", true).toBool();
612 	setDisplayMode(conf->value("distribution_enabled", false).toBool());
613 	setGlitchFlag(conf->value("use_separate_colors", false).toBool());
614 	setFilteredMode(conf->value("filter_enabled", false).toBool());
615 	setFilterValue(conf->value("filter_value", 150.f).toFloat());
616 	setMarkerColor(Vec3f(conf->value("marker_color", "0.4,0.5,1.0").toString()));
617 	setGlitchColor(Vec3f(conf->value("glitch_color", "0.2,0.3,1.0").toString()));
618 	enableAtStartup = conf->value("enable_at_startup", false).toBool();
619 	flagShowPulsarsButton = conf->value("flag_show_pulsars_button", true).toBool();
620 
621 	conf->endGroup();
622 }
623 
saveSettingsToConfig(void)624 void Pulsars::saveSettingsToConfig(void)
625 {
626 	conf->beginGroup("Pulsars");
627 
628 	conf->setValue("url", updateUrl);
629 	conf->setValue("update_frequency_days", updateFrequencyDays);
630 	conf->setValue("updates_enabled", updatesEnabled);
631 	conf->setValue("distribution_enabled", getDisplayMode());
632 	conf->setValue("use_separate_colors", getGlitchFlag());
633 	conf->setValue("filter_enabled", getFilteredMode());
634 	conf->setValue("filter_value", QString::number(getFilterValue(), 'f', 2));
635 	conf->setValue("enable_at_startup", enableAtStartup);
636 	conf->setValue("flag_show_pulsars_button", flagShowPulsarsButton);
637 	conf->setValue("marker_color", getMarkerColor().toStr());
638 	conf->setValue("glitch_color", getGlitchColor().toStr());
639 
640 	conf->endGroup();
641 }
642 
getSecondsToUpdate(void)643 int Pulsars::getSecondsToUpdate(void)
644 {
645 	QDateTime nextUpdate = lastUpdate.addSecs(updateFrequencyDays * 3600 * 24);
646 	return static_cast<int>(QDateTime::currentDateTime().secsTo(nextUpdate));
647 }
648 
checkForUpdate(void)649 void Pulsars::checkForUpdate(void)
650 {
651 	if (updatesEnabled && lastUpdate.addSecs(updateFrequencyDays * 3600 * 24) <= QDateTime::currentDateTime() && networkManager->networkAccessible()==QNetworkAccessManager::Accessible)
652 		updateJSON();
653 }
654 
updateJSON(void)655 void Pulsars::updateJSON(void)
656 {
657 	if (updateState==Pulsars::Updating)
658 	{
659 		qWarning() << "[Pulsars] Already updating...  will not start again current update is complete.";
660 		return;
661 	}
662 
663 	qDebug() << "[Pulsars] Updating pulsars catalog...";
664 	startDownload(updateUrl);
665 }
666 
deleteDownloadProgressBar()667 void Pulsars::deleteDownloadProgressBar()
668 {
669 	disconnect(this, SLOT(updateDownloadProgress(qint64,qint64)));
670 
671 	if (progressBar)
672 	{
673 		StelApp::getInstance().removeProgressBar(progressBar);
674 		progressBar = Q_NULLPTR;
675 	}
676 }
677 
startDownload(QString urlString)678 void Pulsars::startDownload(QString urlString)
679 {
680 	QUrl url(urlString);
681 	if (!url.isValid() || url.isRelative() || !url.scheme().startsWith("http", Qt::CaseInsensitive))
682 	{
683 		qWarning() << "[Pulsars] Invalid URL:" << urlString;
684 		return;
685 	}
686 
687 	if (progressBar == Q_NULLPTR)
688 		progressBar = StelApp::getInstance().addProgressBar();
689 	progressBar->setValue(0);
690 	progressBar->setRange(0, 0);
691 
692 	connect(networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(downloadComplete(QNetworkReply*)));
693 	QNetworkRequest request;
694 	request.setUrl(QUrl(updateUrl));
695 	request.setRawHeader("User-Agent", StelUtils::getUserAgentString().toUtf8());
696 	request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
697 	downloadReply = networkManager->get(request);
698 	connect(downloadReply, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(updateDownloadProgress(qint64,qint64)));
699 
700 	updateState = Pulsars::Updating;
701 	emit(updateStateChanged(updateState));
702 }
703 
updateDownloadProgress(qint64 bytesReceived,qint64 bytesTotal)704 void Pulsars::updateDownloadProgress(qint64 bytesReceived, qint64 bytesTotal)
705 {
706 	if (progressBar == Q_NULLPTR)
707 		return;
708 
709 	int currentValue = 0;
710 	int endValue = 0;
711 
712 	if (bytesTotal > -1 && bytesReceived <= bytesTotal)
713 	{
714 		//Round to the greatest possible derived unit
715 		while (bytesTotal > 1024)
716 		{
717 			bytesReceived = static_cast<qint64>(std::floor(bytesReceived / 1024.));
718 			bytesTotal    = static_cast<qint64>(std::floor(bytesTotal / 1024.));
719 		}
720 		currentValue = static_cast<int>(bytesReceived);
721 		endValue = static_cast<int>(bytesTotal);
722 	}
723 
724 	progressBar->setValue(currentValue);
725 	progressBar->setRange(0, endValue);
726 }
727 
downloadComplete(QNetworkReply * reply)728 void Pulsars::downloadComplete(QNetworkReply *reply)
729 {
730 	if (reply == Q_NULLPTR)
731 		return;
732 
733 	disconnect(networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(downloadComplete(QNetworkReply*)));
734 	deleteDownloadProgressBar();
735 
736 	if (reply->error() || reply->bytesAvailable()==0)
737 	{
738 		qWarning() << "[Pulsars] Download error: While trying to access"
739 			   << reply->url().toString()
740 			   << "the following error occured:"
741 			   << reply->errorString();
742 
743 		reply->deleteLater();
744 		downloadReply = Q_NULLPTR;
745 		return;
746 	}
747 
748 	// download completed successfully.
749 	try
750 	{
751 		QString jsonFilePath = StelFileMgr::findFile("modules/Pulsars", StelFileMgr::Flags(StelFileMgr::Writable|StelFileMgr::Directory)) + "/pulsars.json";
752 		QFile jsonFile(jsonFilePath);
753 		if (jsonFile.exists())
754 			jsonFile.remove();
755 
756 		if (jsonFile.open(QIODevice::WriteOnly | QIODevice::Text))
757 		{
758 			jsonFile.write(reply->readAll());
759 			jsonFile.close();
760 		}
761 
762 		updateState = Pulsars::CompleteUpdates;
763 
764 		lastUpdate = QDateTime::currentDateTime();
765 		conf->setValue("Pulsars/last_update", lastUpdate.toString(Qt::ISODate));
766 	}
767 	catch (std::runtime_error &e)
768 	{
769 		qWarning() << "[Pulsars] Cannot write JSON data to file:" << e.what();
770 		updateState = Pulsars::DownloadError;
771 	}
772 
773 	emit(updateStateChanged(updateState));
774 	emit(jsonUpdateComplete());
775 
776 	reply->deleteLater();
777 	downloadReply = Q_NULLPTR;
778 
779 	readJsonFile();
780 }
781 
782 
displayMessage(const QString & message,const QString hexColor)783 void Pulsars::displayMessage(const QString& message, const QString hexColor)
784 {
785 	messageIDs << GETSTELMODULE(LabelMgr)->labelScreen(message, 30, 30 + (20*messageIDs.count()), true, 16, hexColor, false, 9000);
786 }
787 
upgradeConfigIni(void)788 void Pulsars::upgradeConfigIni(void)
789 {
790 	// Upgrade settings for Pulsars plugin
791 	if (conf->contains("Pulsars/flag_show_pulsars"))
792 	{
793 		bool b = conf->value("Pulsars/flag_show_pulsars", false).toBool();
794 		if (!conf->contains("Pulsars/enable_at_startup"))
795 			conf->setValue("Pulsars/enable_at_startup", b);
796 		conf->remove("Pulsars/flag_show_pulsars");
797 	}
798 }
799 
800 // Define whether the button toggling pulsars should be visible
setFlagShowPulsarsButton(bool b)801 void Pulsars::setFlagShowPulsarsButton(bool b)
802 {
803 	StelGui* gui = dynamic_cast<StelGui*>(StelApp::getInstance().getGui());
804 	if (gui!=Q_NULLPTR)
805 	{
806 		if (b==true) {
807 			if (toolbarButton==Q_NULLPTR) {
808 				// Create the pulsars button
809 				toolbarButton = new StelButton(Q_NULLPTR, *OnIcon, *OffIcon, *GlowIcon, "actionShow_Pulsars", false, "actionShow_Pulsars_dialog");
810 			}
811 			gui->getButtonBar()->addButton(toolbarButton, "065-pluginsGroup");
812 		} else {
813 			gui->getButtonBar()->hideButton("actionShow_Pulsars");
814 		}
815 	}
816 	flagShowPulsarsButton = b;
817 }
818 
getDisplayMode() const819 bool Pulsars::getDisplayMode() const
820 {
821 	return Pulsar::distributionMode;
822 }
823 
setDisplayMode(bool b)824 void Pulsars::setDisplayMode(bool b)
825 {
826 	Pulsar::distributionMode=b;
827 }
828 
getGlitchFlag() const829 bool Pulsars::getGlitchFlag() const
830 {
831 	return Pulsar::glitchFlag;
832 }
833 
setGlitchFlag(bool b)834 void Pulsars::setGlitchFlag(bool b)
835 {
836 	Pulsar::glitchFlag=b;
837 }
838 
getFilteredMode() const839 bool Pulsars::getFilteredMode() const
840 {
841 	return Pulsar::filteredMode;
842 }
843 
setFilteredMode(bool b)844 void Pulsars::setFilteredMode(bool b)
845 {
846 	Pulsar::filteredMode=b;
847 }
848 
getFilterValue() const849 float Pulsars::getFilterValue() const
850 {
851 	return Pulsar::filterValue;
852 }
853 
setFilterValue(float v)854 void Pulsars::setFilterValue(float v)
855 {
856 	Pulsar::filterValue=v;
857 }
858 
getMarkerColor() const859 Vec3f Pulsars::getMarkerColor() const
860 {
861 	return Pulsar::markerColor;
862 }
863 
setMarkerColor(const Vec3f & c)864 void Pulsars::setMarkerColor(const Vec3f &c)
865 {
866 	Pulsar::markerColor = c;
867 	emit markerColorChanged(c);
868 }
869 
getGlitchColor() const870 Vec3f Pulsars::getGlitchColor() const
871 {
872 	return Pulsar::glitchColor;
873 }
874 
setGlitchColor(const Vec3f & c)875 void Pulsars::setGlitchColor(const Vec3f &c)
876 {
877 	Pulsar::glitchColor = c;
878 	emit glitchColorChanged(c);
879 }
880 
reloadCatalog(void)881 void Pulsars::reloadCatalog(void)
882 {
883 	bool hasSelection = false;
884 	StelObjectMgr* objMgr = GETSTELMODULE(StelObjectMgr);
885 	// Whether any pulsar are selected? Save the current selection...
886 	const QList<StelObjectP> selectedObject = objMgr->getSelectedObject("Pulsar");
887 	if (!selectedObject.isEmpty())
888 	{
889 		// ... unselect current pulsar.
890 		hasSelection = true;
891 		objMgr->unSelect();
892 	}
893 
894 	readJsonFile();
895 
896 	if (hasSelection)
897 	{
898 		// Restore selection...
899 		StelObjectP obj = selectedObject[0];
900 		objMgr->findAndSelect(obj->getEnglishName(), obj->getType());
901 	}
902 }
903 
setFlagShowPulsars(bool b)904 void Pulsars::setFlagShowPulsars(bool b)
905 {
906 	if (b!=flagShowPulsars)
907 	{
908 		flagShowPulsars=b;
909 		emit flagPulsarsVisibilityChanged(b);
910 		emit StelApp::getInstance().getCore()->updateSearchLists();
911 	}
912 }
913