1 /*
2  * Stellarium
3  * Copyright (C) 2017 Alexander Wolf
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA  02110-1335, USA.
18  */
19 
20 
21 #include "AsterismMgr.hpp"
22 #include "Asterism.hpp"
23 #include "StarMgr.hpp"
24 #include "StelApp.hpp"
25 #include "StelProjector.hpp"
26 #include "StelObjectMgr.hpp"
27 #include "StelLocaleMgr.hpp"
28 #include "StelSkyCultureMgr.hpp"
29 #include "StelModuleMgr.hpp"
30 #include "StelMovementMgr.hpp"
31 #include "StelFileMgr.hpp"
32 #include "StelCore.hpp"
33 #include "StelPainter.hpp"
34 #include "StelSkyDrawer.hpp"
35 #include "SolarSystem.hpp"
36 
37 #include <vector>
38 #include <QDebug>
39 #include <QFile>
40 #include <QSettings>
41 #include <QRegularExpression>
42 #include <QString>
43 #include <QStringList>
44 #include <QDir>
45 
46 using namespace std;
47 
48 // constructor which loads all data from appropriate files
AsterismMgr(StarMgr * _hip_stars)49 AsterismMgr::AsterismMgr(StarMgr *_hip_stars)
50 	: hipStarMgr(_hip_stars)
51 	, linesDisplayed(false)
52 	, rayHelpersDisplayed(false)
53 	, namesDisplayed(false)
54 	, hasAsterism(false)
55 	, isolateAsterismSelected(false)
56 	, asterismLineThickness(1)
57 	, rayHelperThickness(1)
58 {
59 	setObjectName("AsterismMgr");
60 	Q_ASSERT(hipStarMgr);
61 }
62 
~AsterismMgr()63 AsterismMgr::~AsterismMgr()
64 {
65 	for (auto* asterism : asterisms)
66 	{
67 		delete asterism;
68 	}
69 }
70 
init()71 void AsterismMgr::init()
72 {
73 	QSettings* conf = StelApp::getInstance().getSettings();
74 	Q_ASSERT(conf);
75 
76 	lastLoadedSkyCulture = "dummy";
77 	asterFont.setPixelSize(conf->value("viewing/asterism_font_size", 14).toInt());
78 	setFlagLines(conf->value("viewing/flag_asterism_drawing").toBool());
79 	setFlagRayHelpers(conf->value("viewing/flag_rayhelper_drawing").toBool());
80 	setFlagLabels(conf->value("viewing/flag_asterism_name").toBool());
81 	setAsterismLineThickness(conf->value("viewing/asterism_line_thickness", 1).toInt());
82 	setRayHelperThickness(conf->value("viewing/rayhelper_line_thickness", 1).toInt());
83 
84 	// Load colors from config file
85 	QString defaultColor = conf->value("color/default_color").toString();
86 	setLinesColor(Vec3f(conf->value("color/asterism_lines_color", defaultColor).toString()));
87 	setLabelsColor(Vec3f(conf->value("color/asterism_names_color", defaultColor).toString()));
88 	setRayHelpersColor(Vec3f(conf->value("color/rayhelper_lines_color", defaultColor).toString()));
89 
90 	StelObjectMgr *objectManager = GETSTELMODULE(StelObjectMgr);
91 	objectManager->registerStelObjectMgr(this);
92 	connect(objectManager, SIGNAL(selectedObjectChanged(StelModule::StelModuleSelectAction)),
93 			this, SLOT(selectedObjectChange(StelModule::StelModuleSelectAction)));
94 	StelApp *app = &StelApp::getInstance();
95 	connect(app, SIGNAL(languageChanged()), this, SLOT(updateI18n()));
96 	connect(&app->getSkyCultureMgr(), SIGNAL(currentSkyCultureChanged(QString)), this, SLOT(updateSkyCulture(const QString&)));
97 
98 	QString displayGroup = N_("Display Options");
99 	addAction("actionShow_Asterism_Lines", displayGroup, N_("Asterism lines"), "linesDisplayed", "Alt+A");
100 	addAction("actionShow_Asterism_Labels", displayGroup, N_("Asterism labels"), "namesDisplayed", "Alt+V");
101 	addAction("actionShow_Asterism_Isolated", displayGroup, N_("Toggle single asterism selection mode"), "switchSelectionMode()");
102 	addAction("actionShow_Ray_Helpers", displayGroup, N_("Ray helpers"), "rayHelpersDisplayed", "Alt+R");
103 }
104 
105 /*************************************************************************
106  Reimplementation of the getCallOrder method
107 *************************************************************************/
getCallOrder(StelModuleActionName actionName) const108 double AsterismMgr::getCallOrder(StelModuleActionName actionName) const
109 {
110 	if (actionName==StelModule::ActionDraw)
111 		return StelApp::getInstance().getModuleMgr().getModule("GridLinesMgr")->getCallOrder(actionName)+11;
112 	return 0;
113 }
114 
updateSkyCulture(const QString & skyCultureDir)115 void AsterismMgr::updateSkyCulture(const QString& skyCultureDir)
116 {
117 	currentSkyCultureID = skyCultureDir;
118 
119 	StelObjectMgr* objMgr = GETSTELMODULE(StelObjectMgr);
120 	const QList<StelObjectP> selectedObject = objMgr->getSelectedObject("Asterism");
121 	if (!selectedObject.isEmpty()) // Unselect asterism
122 		objMgr->unSelect();
123 
124 	// Check if the sky culture changed since last load, if not don't load anything
125 	if (lastLoadedSkyCulture == skyCultureDir)
126 		return;
127 
128 	QString fic = StelFileMgr::findFile("skycultures/"+skyCultureDir+"/asterism_lines.fab");
129 	if (fic.isEmpty())
130 	{
131 		hasAsterism = false;
132 		qWarning() << "No asterisms for skyculture" << currentSkyCultureID;
133 	}
134 	else
135 	{
136 		hasAsterism = true;
137 		loadLines(fic);
138 	}
139 
140 	// load asterism names
141 	fic = StelFileMgr::findFile("skycultures/" + skyCultureDir + "/asterism_names.eng.fab");
142 	if (!fic.isEmpty())
143 		loadNames(fic);
144 
145 	// Translate asterism names for the new sky culture
146 	updateI18n();
147 
148 	lastLoadedSkyCulture = skyCultureDir;
149 }
150 
setLinesColor(const Vec3f & color)151 void AsterismMgr::setLinesColor(const Vec3f& color)
152 {
153 	if (color != Asterism::lineColor)
154 	{
155 		Asterism::lineColor = color;
156 		emit linesColorChanged(color);
157 	}
158 }
159 
getLinesColor() const160 Vec3f AsterismMgr::getLinesColor() const
161 {
162 	return Asterism::lineColor;
163 }
164 
setRayHelpersColor(const Vec3f & color)165 void AsterismMgr::setRayHelpersColor(const Vec3f& color)
166 {
167 	if (color != Asterism::rayHelperColor)
168 	{
169 		Asterism::rayHelperColor = color;
170 		emit rayHelpersColorChanged(color);
171 	}
172 }
173 
getRayHelpersColor() const174 Vec3f AsterismMgr::getRayHelpersColor() const
175 {
176 	return Asterism::rayHelperColor;
177 }
178 
179 
setLabelsColor(const Vec3f & color)180 void AsterismMgr::setLabelsColor(const Vec3f& color)
181 {
182 	if (Asterism::labelColor != color)
183 	{
184 		Asterism::labelColor = color;
185 		emit namesColorChanged(color);
186 	}
187 }
188 
getLabelsColor() const189 Vec3f AsterismMgr::getLabelsColor() const
190 {
191 	return Asterism::labelColor;
192 }
193 
setFontSize(const float newFontSize)194 void AsterismMgr::setFontSize(const float newFontSize)
195 {
196 	if ((static_cast<float>(asterFont.pixelSize()) - newFontSize) != 0.0f)
197 	{
198 		asterFont.setPixelSize(static_cast<int>(newFontSize));
199 		emit fontSizeChanged(newFontSize);
200 	}
201 }
202 
getFontSize() const203 float AsterismMgr::getFontSize() const
204 {
205 	return asterFont.pixelSize();
206 }
207 
setAsterismLineThickness(const int thickness)208 void AsterismMgr::setAsterismLineThickness(const int thickness)
209 {
210 	if(thickness!=asterismLineThickness)
211 	{
212 		asterismLineThickness = thickness;
213 		if (asterismLineThickness<=0) // The line can not be negative or zero thickness
214 			asterismLineThickness = 1;
215 
216 		emit asterismLineThicknessChanged(thickness);
217 	}
218 }
219 
setRayHelperThickness(const int thickness)220 void AsterismMgr::setRayHelperThickness(const int thickness)
221 {
222 	if(thickness!=rayHelperThickness)
223 	{
224 		rayHelperThickness = thickness;
225 		if (rayHelperThickness<=0) // The line can not be negative or zero thickness
226 			rayHelperThickness = 1;
227 
228 		emit rayHelperThicknessChanged(thickness);
229 	}
230 }
231 
loadLines(const QString & fileName)232 void AsterismMgr::loadLines(const QString &fileName)
233 {
234 	QFile in(fileName);
235 	if (!in.open(QIODevice::ReadOnly | QIODevice::Text))
236 	{
237 		qWarning() << "Can't open asterism data file" << QDir::toNativeSeparators(fileName)  << "for culture" << currentSkyCultureID;
238 		Q_ASSERT(0);
239 	}
240 
241 	int totalRecords=0;
242 	QString record;
243 	QRegularExpression commentRx("^(\\s*#.*|\\s*)$");
244 	while (!in.atEnd())
245 	{
246 		record = QString::fromUtf8(in.readLine());
247 		if (!commentRx.match(record).hasMatch())
248 			totalRecords++;
249 	}
250 	in.seek(0);
251 
252 	// delete existing data, if any
253 	for (auto* asterism : asterisms)
254 		delete asterism;
255 
256 	asterisms.clear();
257 	Asterism *aster = Q_NULLPTR;
258 
259 	// read the file of line patterns, adding a record per non-comment line
260 	int currentLineNumber = 0;	// line in file
261 	int readOk = 0;			// count of records processed OK
262 	while (!in.atEnd())
263 	{
264 		record = QString::fromUtf8(in.readLine());
265 		currentLineNumber++;
266 		if (commentRx.match(record).hasMatch())
267 			continue;
268 
269 		aster = new Asterism;
270 		if(aster->read(record, hipStarMgr))
271 		{
272 			aster->setFlagLines(linesDisplayed);
273 			aster->setFlagLabels(namesDisplayed);
274 			aster->setFlagRayHelpers(rayHelpersDisplayed);
275 			asterisms.push_back(aster);
276 			++readOk;
277 		}
278 		else
279 		{
280 			qWarning() << "ERROR reading asterism lines record at line " << currentLineNumber << "for culture" << currentSkyCultureID;
281 			delete aster;
282 		}
283 	}
284 	in.close();
285 	qDebug() << "Loaded" << readOk << "/" << totalRecords << "asterism records successfully for culture" << currentSkyCultureID;
286 
287 	// Set current states
288 	setFlagLines(linesDisplayed);
289 	setFlagLabels(namesDisplayed);
290 	setFlagRayHelpers(rayHelpersDisplayed);
291 }
292 
draw(StelCore * core)293 void AsterismMgr::draw(StelCore* core)
294 {
295 	const StelProjectorP prj = core->getProjection(StelCore::FrameJ2000);
296 	StelPainter sPainter(prj);
297 	sPainter.setFont(asterFont);
298 	drawLines(sPainter, core);
299 	drawRayHelpers(sPainter, core);
300 	drawNames(sPainter);
301 }
302 
303 // Draw asterisms lines
drawLines(StelPainter & sPainter,const StelCore * core) const304 void AsterismMgr::drawLines(StelPainter& sPainter, const StelCore* core) const
305 {
306 	if (!hasAsterism)
307 		return;
308 
309 	const float ppx = static_cast<float>(sPainter.getProjector()->getDevicePixelsPerPixel());
310 	sPainter.setBlending(true);
311 	if (asterismLineThickness>1 || ppx>1.f)
312 		sPainter.setLineWidth(asterismLineThickness*ppx); // set line thickness
313 	sPainter.setLineSmooth(true);
314 
315 	const SphericalCap& viewportHalfspace = sPainter.getProjector()->getBoundingCap();
316 	for (auto* asterism : asterisms)
317 	{
318 		if (asterism->isAsterism())
319 			asterism->drawOptim(sPainter, core, viewportHalfspace);
320 	}
321 	if (asterismLineThickness>1 || ppx>1.f)
322 		sPainter.setLineWidth(1); // restore line thickness
323 	sPainter.setLineSmooth(false);
324 }
325 
326 // Draw asterisms lines
drawRayHelpers(StelPainter & sPainter,const StelCore * core) const327 void AsterismMgr::drawRayHelpers(StelPainter& sPainter, const StelCore* core) const
328 {
329 	if (!hasAsterism)
330 		return;
331 
332 	const float ppx = static_cast<float>(sPainter.getProjector()->getDevicePixelsPerPixel());
333 	sPainter.setBlending(true);
334 	if (rayHelperThickness>1 || ppx>1.f)
335 		sPainter.setLineWidth(rayHelperThickness*ppx); // set line thickness
336 	sPainter.setLineSmooth(true);
337 
338 	const SphericalCap& viewportHalfspace = sPainter.getProjector()->getBoundingCap();
339 	for (auto* asterism : asterisms)
340 	{
341 		if (!asterism->isAsterism())
342 			asterism->drawOptim(sPainter, core, viewportHalfspace);
343 	}
344 	if (rayHelperThickness>1 || ppx>1.f)
345 		sPainter.setLineWidth(1); // restore line thickness
346 	sPainter.setLineSmooth(false);
347 }
348 
349 // Draw the names of all the asterisms
drawNames(StelPainter & sPainter) const350 void AsterismMgr::drawNames(StelPainter& sPainter) const
351 {
352 	if (!hasAsterism)
353 		return;
354 
355 	StelCore *core=StelApp::getInstance().getCore();
356 	Vec3d vel=core->getCurrentPlanet()->getHeliocentricEclipticVelocity();
357 	vel=StelCore::matVsop87ToJ2000*vel;
358 	vel*=core->getAberrationFactor() * (AU/(86400.0*SPEED_OF_LIGHT));
359 
360 	sPainter.setBlending(true);
361 	for (auto* asterism : asterisms)
362 	{
363 		if (!asterism->flagAsterism) continue;
364 		// Check if in the field of view
365 		Vec3d XYZname=asterism->XYZname;
366 		if (core->getUseAberration())
367 		{
368 			XYZname.normalize();
369 			XYZname+=vel;
370 			XYZname.normalize();
371 		}
372 
373 		if (sPainter.getProjector()->projectCheck(XYZname, asterism->XYname))
374 			asterism->drawName(sPainter);
375 	}
376 }
377 
findFromAbbreviation(const QString & abbreviation) const378 Asterism* AsterismMgr::findFromAbbreviation(const QString& abbreviation) const
379 {
380 	for (auto* asterism : asterisms)
381 	{
382 		if (asterism->abbreviation.compare(abbreviation, Qt::CaseInsensitive) == 0)
383 			return asterism;
384 	}
385 	return Q_NULLPTR;
386 }
387 
388 
389 // Can't find asterism from a position because it's not well localized
searchAround(const Vec3d &,double,const StelCore *) const390 QList<StelObjectP> AsterismMgr::searchAround(const Vec3d&, double, const StelCore*) const
391 {
392 	return QList<StelObjectP>();
393 }
394 
loadNames(const QString & namesFile)395 void AsterismMgr::loadNames(const QString& namesFile)
396 {
397 	// Asterism not loaded yet
398 	if (asterisms.empty()) return;
399 
400 	// clear previous names
401 	for (auto* asterism : asterisms)
402 	{
403 		asterism->englishName.clear();
404 	}
405 
406 	// Open file
407 	QFile commonNameFile(namesFile);
408 	if (!commonNameFile.open(QIODevice::ReadOnly | QIODevice::Text))
409 	{
410 		qDebug() << "Cannot open file" << QDir::toNativeSeparators(namesFile);
411 		return;
412 	}
413 
414 	// Now parse the file
415 	// lines to ignore which start with a # or are empty
416 	QRegularExpression commentRx("^(\\s*#.*|\\s*)$");
417 	QRegularExpression recRx("^\\s*(\\w+)\\s+_[(]\"(.*)\"[)]\\s*([\\,\\d\\s]*)\\n");
418 	QRegularExpression ctxRx("(.*)\",\\s*\"(.*)");
419 
420 	// keep track of how many records we processed.
421 	int totalRecords=0;
422 	int readOk=0;
423 	int lineNumber=0;
424 	while (!commonNameFile.atEnd())
425 	{
426 		QString record = QString::fromUtf8(commonNameFile.readLine());
427 		lineNumber++;
428 
429 		// Skip comments
430 		if (commentRx.match(record).hasMatch())
431 			continue;
432 
433 		totalRecords++;
434 
435 		QRegularExpressionMatch recMatch=recRx.match(record);
436 		if (!recMatch.hasMatch())
437 		{
438 			qWarning() << "ERROR - cannot parse record at line" << lineNumber << "in asterism names file" << QDir::toNativeSeparators(namesFile) << ":" << record;
439 		}
440 		else
441 		{
442 			QString shortName = recMatch.captured(1);
443 			Asterism *aster = findFromAbbreviation(shortName);
444 			// If the asterism exists, set the English name
445 			if (aster != Q_NULLPTR)
446 			{
447 				QString ctxt = recMatch.captured(2);
448 				QRegularExpressionMatch ctxMatch=ctxRx.match(ctxt);
449 				if (ctxMatch.hasMatch())
450 				{
451 					aster->englishName = ctxMatch.captured(1);
452 					aster->context = ctxMatch.captured(2);
453 				}
454 				else
455 				{
456 					aster->englishName = ctxt;
457 					aster->context = "";
458 				}
459 				readOk++;
460 			}
461 			else
462 			{
463 				qWarning() << "WARNING - asterism abbreviation" << shortName << "not found when loading asterism names";
464 			}
465 		}
466 	}
467 	commonNameFile.close();
468 	qDebug() << "Loaded" << readOk << "/" << totalRecords << "asterism names";
469 }
470 
updateI18n()471 void AsterismMgr::updateI18n()
472 {
473 	const StelTranslator& trans = StelApp::getInstance().getLocaleMgr().getSkyTranslator();
474 	for (auto* asterism : asterisms)
475 	{
476 		asterism->nameI18 = trans.qtranslate(asterism->englishName, asterism->context);
477 	}
478 }
479 
480 // update faders
update(double deltaTime)481 void AsterismMgr::update(double deltaTime)
482 {
483 	const int delta = static_cast<int>(deltaTime*1000);
484 	for (auto* asterism : asterisms)
485 	{
486 		asterism->update(delta);
487 	}
488 }
489 
setFlagLines(const bool displayed)490 void AsterismMgr::setFlagLines(const bool displayed)
491 {
492 	if(linesDisplayed != displayed)
493 	{
494 		linesDisplayed = displayed;
495 		if (!selected.empty() && getFlagIsolateAsterismSelected())
496 		{
497 			for (auto* asterism : selected)
498 				asterism->setFlagLines(linesDisplayed);
499 		}
500 		else
501 		{
502 			for (auto* asterism : asterisms)
503 				asterism->setFlagLines(linesDisplayed);
504 		}
505 		emit linesDisplayedChanged(displayed);
506 	}
507 }
508 
getFlagLines(void) const509 bool AsterismMgr::getFlagLines(void) const
510 {
511 	return linesDisplayed;
512 }
513 
setFlagRayHelpers(const bool displayed)514 void AsterismMgr::setFlagRayHelpers(const bool displayed)
515 {
516 	if(rayHelpersDisplayed != displayed)
517 	{
518 		rayHelpersDisplayed = displayed;
519 		for (auto* asterism : asterisms)
520 		{
521 			asterism->setFlagRayHelpers(rayHelpersDisplayed);
522 		}
523 		emit rayHelpersDisplayedChanged(displayed);
524 	}
525 }
526 
getFlagRayHelpers(void) const527 bool AsterismMgr::getFlagRayHelpers(void) const
528 {
529 	return rayHelpersDisplayed;
530 }
531 
setFlagLabels(const bool displayed)532 void AsterismMgr::setFlagLabels(const bool displayed)
533 {
534 	if (namesDisplayed != displayed)
535 	{
536 		namesDisplayed = displayed;
537 		if (!selected.empty() && getFlagIsolateAsterismSelected())
538 		{
539 			for (auto* asterism : selected)
540 				asterism->setFlagLabels(namesDisplayed);
541 		}
542 		else
543 		{
544 			for (auto* asterism : asterisms)
545 				asterism->setFlagLabels(namesDisplayed);
546 		}
547 		emit namesDisplayedChanged(displayed);
548 	}
549 }
550 
getFlagLabels(void) const551 bool AsterismMgr::getFlagLabels(void) const
552 {
553 	return namesDisplayed;
554 }
555 
searchByNameI18n(const QString & nameI18n) const556 StelObjectP AsterismMgr::searchByNameI18n(const QString& nameI18n) const
557 {
558 	QString objw = nameI18n.toUpper();
559 
560 	for (auto* asterism : asterisms)
561 	{
562 		QString objwcap = asterism->nameI18.toUpper();
563 		if (objwcap == objw) return asterism;
564 	}
565 	return Q_NULLPTR;
566 }
567 
searchByName(const QString & name) const568 StelObjectP AsterismMgr::searchByName(const QString& name) const
569 {
570 	QString objw = name.toUpper();
571 	for (auto* asterism : asterisms)
572 	{
573 		QString objwcap = asterism->englishName.toUpper();
574 		if (objwcap == objw) return asterism;
575 
576 		objwcap = asterism->abbreviation.toUpper();
577 		if (objwcap == objw) return asterism;
578 	}
579 	return Q_NULLPTR;
580 }
581 
listAllObjects(bool inEnglish) const582 QStringList AsterismMgr::listAllObjects(bool inEnglish) const
583 {
584 	QStringList result;
585 	if (inEnglish)
586 	{
587 		for (auto* asterism : asterisms)
588 		{
589 			if (asterism->isAsterism())
590 				result << asterism->getEnglishName();
591 		}
592 	}
593 	else
594 	{
595 		for (auto* asterism : asterisms)
596 		{
597 			if (asterism->isAsterism())
598 				result << asterism->getNameI18n();
599 		}
600 	}
601 	return result;
602 }
603 
searchByID(const QString & id) const604 StelObjectP AsterismMgr::searchByID(const QString &id) const
605 {
606 	for (auto* asterism : asterisms)
607 	{
608 		if (asterism->getID() == id) return asterism;
609 	}
610 	return Q_NULLPTR;
611 }
612 
getStelObjectType() const613 QString AsterismMgr::getStelObjectType() const
614 {
615 	return Asterism::ASTERISM_TYPE;
616 }
617 
setFlagIsolateAsterismSelected(const bool isolate)618 void AsterismMgr::setFlagIsolateAsterismSelected(const bool isolate)
619 {
620 	if (isolateAsterismSelected != isolate)
621 	{
622 		isolateAsterismSelected = isolate;
623 
624 		// when turning off isolated selection mode, clear existing isolated selections.
625 		if (!isolateAsterismSelected)
626 		{
627 			for (auto* asterism : asterisms)
628 			{
629 				asterism->setFlagLines(getFlagLines());
630 				asterism->setFlagLabels(getFlagLabels());
631 			}
632 		}
633 		emit isolateAsterismSelectedChanged(isolate);
634 	}
635 }
636 
getFlagIsolateAsterismSelected(void) const637 bool AsterismMgr::getFlagIsolateAsterismSelected(void) const
638 {
639 	return isolateAsterismSelected;
640 }
641 
setSelectedAsterism(Asterism * a)642 void AsterismMgr::setSelectedAsterism(Asterism *a)
643 {
644 	// update states for other asterisms to fade them out
645 	if (a != Q_NULLPTR)
646 	{
647 		selected.push_back(a);
648 
649 		if (getFlagIsolateAsterismSelected())
650 		{
651 			// Propagate current settings to newly selected asterism
652 			a->setFlagLines(getFlagLines());
653 			a->setFlagLabels(getFlagLabels());
654 
655 			for (auto* asterism : asterisms)
656 			{
657 				bool match = false;
658 				for (auto* selected_asterisms : selected)
659 				{
660 					if (asterism == selected_asterisms)
661 					{
662 						match=true; // this is a selected asterism
663 						break;
664 					}
665 				}
666 
667 				if(!match)
668 				{
669 					// Not selected asterism
670 					asterism->setFlagLines(false);
671 					asterism->setFlagLabels(false);
672 				}
673 			}
674 		}
675 		else
676 		{
677 			for (auto* asterism : asterisms)
678 			{
679 				asterism->setFlagLines(false);
680 				asterism->setFlagLabels(false);
681 			}
682 
683 			// Propagate current settings to newly selected asterism
684 			a->setFlagLines(getFlagLines());
685 			a->setFlagLabels(getFlagLabels());
686 		}
687 	}
688 	else
689 	{
690 		if (selected.empty()) return;
691 
692 		// Otherwise apply standard flags to all asterisms
693 		for (auto* asterism : asterisms)
694 		{
695 			asterism->setFlagLines(getFlagLines());
696 			asterism->setFlagLabels(getFlagLabels());
697 		}
698 
699 		// And remove all selections
700 		selected.clear();
701 	}
702 }
703 
704 //! Remove a asterism from the selected asterism list
unsetSelectedAsterism(Asterism * a)705 void AsterismMgr::unsetSelectedAsterism(Asterism *a)
706 {
707 	if (a != Q_NULLPTR)
708 	{
709 		for (auto iter = selected.begin(); iter != selected.end();)
710 		{
711 			if( (*iter)->getEnglishName().toLower() == a->getEnglishName().toLower() )
712 				iter = selected.erase(iter);
713 			else
714 				++iter;
715 		}
716 
717 		// If no longer any selection, restore all flags on all asterisms
718 		if (selected.empty())
719 		{
720 			// Otherwise apply standard flags to all asterisms
721 			for (auto* asterism : asterisms)
722 			{
723 				asterism->setFlagLines(getFlagLines());
724 				asterism->setFlagLabels(getFlagLabels());
725 			}
726 		}
727 		else if(isolateAsterismSelected)
728 		{
729 			// No longer selected asterism
730 			a->setFlagLines(false);
731 			a->setFlagLabels(false);
732 		}
733 	}
734 }
735 
selectAsterism(const QString & englishName)736 void AsterismMgr::selectAsterism(const QString &englishName)
737 {
738 	if (!getFlagIsolateAsterismSelected())
739 		setFlagIsolateAsterismSelected(true); // Enable isolated selection
740 
741 	bool found = false;
742 	for (auto* asterism : asterisms)
743 	{
744 		if (asterism->getEnglishName().toLower()==englishName.toLower())
745 		{
746 			setSelectedAsterism(asterism);
747 			found = true;
748 			break;
749 		}
750 	}
751 	if (!found)
752 		qDebug() << "The asterism" << englishName << "is not found";
753 }
754 
deselectAsterism(const QString & englishName)755 void AsterismMgr::deselectAsterism(const QString &englishName)
756 {
757 	if (!getFlagIsolateAsterismSelected())
758 		setFlagIsolateAsterismSelected(true); // Enable isolated selection
759 
760 	bool found = false;
761 	for (auto* asterism : asterisms)
762 	{
763 		if (asterism->getEnglishName().toLower()==englishName.toLower())
764 		{
765 			unsetSelectedAsterism(asterism);
766 			found = true;
767 			break;
768 		}
769 	}
770 
771 	if (selected.size()==0 && found)
772 	{
773 		// Let's remove the selection for all asterisms if the list of selected asterisms is empty
774 		for (auto* asterism : asterisms)
775 		{
776 			asterism->setFlagLines(false);
777 			asterism->setFlagLabels(false);
778 		}
779 	}
780 
781 	if (!found)
782 		qDebug() << "The asterism" << englishName << "is not found";
783 }
784 
deselectAsterisms(void)785 void AsterismMgr::deselectAsterisms(void)
786 {
787 	StelObjectMgr* omgr = GETSTELMODULE(StelObjectMgr);
788 	Q_ASSERT(omgr);
789 	if (getFlagIsolateAsterismSelected())
790 	{
791 		// The list of selected asterisms is empty, but...
792 		if (selected.size()==0)
793 		{
794 			// ...let's unselect all asterisms for guarantee
795 			for (auto* asterism : asterisms)
796 			{
797 				asterism->setFlagLines(false);
798 				asterism->setFlagLabels(false);
799 			}
800 		}
801 
802 		// If any asterism is selected at the moment, then let's do not touch to it!
803 		if (omgr->getWasSelected() && selected.size()>0)
804 			selected.pop_back();
805 
806 		// Let's hide all previously selected asterisms
807 		for (auto* asterism : selected)
808 		{
809 			asterism->setFlagLines(false);
810 			asterism->setFlagLabels(false);
811 		}
812 	}
813 	else
814 	{
815 		const QList<StelObjectP> newSelectedConst = omgr->getSelectedObject("Asterism");
816 		if (!newSelectedConst.empty())
817 			omgr->unSelect();
818 	}
819 	selected.clear();
820 }
821 
selectAllAsterisms()822 void AsterismMgr::selectAllAsterisms()
823 {
824 	for (auto* asterism : asterisms)
825 		setSelectedAsterism(asterism);
826 }
827 
selectedObjectChange(StelModule::StelModuleSelectAction action)828 void AsterismMgr::selectedObjectChange(StelModule::StelModuleSelectAction action)
829 {
830 	StelObjectMgr* omgr = GETSTELMODULE(StelObjectMgr);
831 	Q_ASSERT(omgr);
832 	const QList<StelObjectP> newSelected = omgr->getSelectedObject();
833 	if (newSelected.empty())
834 		return;
835 
836 	const QList<StelObjectP> newSelectedAsterisms = omgr->getSelectedObject("Asterism");
837 	if (!newSelectedAsterisms.empty())
838 	{
839 		// If removing this selection
840 		if(action == StelModule::RemoveFromSelection)
841 			unsetSelectedAsterism(static_cast<Asterism *>(newSelectedAsterisms[0].data()));
842 		else // Add asterism to selected list (do not select a star, just the constellation)
843 			setSelectedAsterism(static_cast<Asterism *>(newSelectedAsterisms[0].data()));
844 	}
845 }
846 
switchSelectionMode()847 void AsterismMgr::switchSelectionMode()
848 {
849 	bool state = getFlagIsolateAsterismSelected();
850 	setFlagIsolateAsterismSelected(!state);
851 	if (!state)
852 		deselectAsterisms();
853 }
854