1 /*
2  * Stellarium
3  * Copyright (C) 2002 Fabien Chereau
4  * Copyright (C) 2011 Alexander Wolf
5  * Copyright (C) 2015 Georg Zotti
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA  02110-1335, USA.
20  */
21 
22 // class used to manage groups of Nebulas
23 
24 #include "StelApp.hpp"
25 #include "NebulaList.hpp"
26 #include "NebulaMgr.hpp"
27 #include "Nebula.hpp"
28 #include "StelTexture.hpp"
29 #include "StelUtils.hpp"
30 #include "StelSkyDrawer.hpp"
31 #include "StelTranslator.hpp"
32 #include "StelTextureMgr.hpp"
33 #include "StelObjectMgr.hpp"
34 #include "StelLocaleMgr.hpp"
35 #include "StelSkyCultureMgr.hpp"
36 #include "StelFileMgr.hpp"
37 #include "StelModuleMgr.hpp"
38 #include "StelCore.hpp"
39 #include "StelSkyImageTile.hpp"
40 #include "StelPainter.hpp"
41 #include "RefractionExtinction.hpp"
42 #include "StelActionMgr.hpp"
43 
44 #include <algorithm>
45 #include <vector>
46 #include <QDebug>
47 #include <QFile>
48 #include <QSettings>
49 #include <QString>
50 #include <QStringList>
51 #include <QRegularExpression>
52 #include <QDir>
53 #include <QMessageBox>
54 
55 // Define version of valid Stellarium DSO Catalog
56 // This number must be incremented each time the content or file format of the stars catalogs change
57 static const QString StellariumDSOCatalogVersion = "3.13";
58 
setLabelsColor(const Vec3f & c)59 void NebulaMgr::setLabelsColor(const Vec3f& c) {Nebula::labelColor = c; emit labelsColorChanged(c);}
getLabelsColor(void) const60 const Vec3f NebulaMgr::getLabelsColor(void) const {return Nebula::labelColor;}
setCirclesColor(const Vec3f & c)61 void NebulaMgr::setCirclesColor(const Vec3f& c) {Nebula::hintColorMap.insert(Nebula::NebUnknown, c); emit circlesColorChanged(c); }
getCirclesColor(void) const62 const Vec3f NebulaMgr::getCirclesColor(void) const {return Nebula::hintColorMap.value(Nebula::NebUnknown);}
setRegionsColor(const Vec3f & c)63 void NebulaMgr::setRegionsColor(const Vec3f& c) {Nebula::hintColorMap.insert(Nebula::NebRegion, c); emit regionsColorChanged(c); }
getRegionsColor(void) const64 const Vec3f NebulaMgr::getRegionsColor(void) const {return Nebula::hintColorMap.value(Nebula::NebRegion);}
setGalaxyColor(const Vec3f & c)65 void NebulaMgr::setGalaxyColor(const Vec3f& c) {Nebula::hintColorMap.insert(Nebula::NebGx, c); Nebula::hintColorMap.insert(Nebula::NebPartOfGx, c); emit galaxiesColorChanged(c); }
getGalaxyColor(void) const66 const Vec3f NebulaMgr::getGalaxyColor(void) const {return Nebula::hintColorMap.value(Nebula::NebGx);}
setRadioGalaxyColor(const Vec3f & c)67 void NebulaMgr::setRadioGalaxyColor(const Vec3f& c) {Nebula::hintColorMap.insert(Nebula::NebRGx, c); emit radioGalaxiesColorChanged(c); }
getRadioGalaxyColor(void) const68 const Vec3f NebulaMgr::getRadioGalaxyColor(void) const {return Nebula::hintColorMap.value(Nebula::NebRGx);}
setActiveGalaxyColor(const Vec3f & c)69 void NebulaMgr::setActiveGalaxyColor(const Vec3f& c) {Nebula::hintColorMap.insert(Nebula::NebAGx, c); emit activeGalaxiesColorChanged(c); }
getActiveGalaxyColor(void) const70 const Vec3f NebulaMgr::getActiveGalaxyColor(void) const {return Nebula::hintColorMap.value(Nebula::NebAGx);}
setInteractingGalaxyColor(const Vec3f & c)71 void NebulaMgr::setInteractingGalaxyColor(const Vec3f& c) {Nebula::hintColorMap.insert(Nebula::NebIGx, c); emit interactingGalaxiesColorChanged(c); }
getInteractingGalaxyColor(void) const72 const Vec3f NebulaMgr::getInteractingGalaxyColor(void) const {return Nebula::hintColorMap.value(Nebula::NebIGx);}
setQuasarColor(const Vec3f & c)73 void NebulaMgr::setQuasarColor(const Vec3f& c) {Nebula::hintColorMap.insert(Nebula::NebQSO, c); emit quasarsColorChanged(c); }
getQuasarColor(void) const74 const Vec3f NebulaMgr::getQuasarColor(void) const {return Nebula::hintColorMap.value(Nebula::NebQSO);}
setNebulaColor(const Vec3f & c)75 void NebulaMgr::setNebulaColor(const Vec3f& c) {Nebula::hintColorMap.insert(Nebula::NebN, c); emit nebulaeColorChanged(c); }
getNebulaColor(void) const76 const Vec3f NebulaMgr::getNebulaColor(void) const {return Nebula::hintColorMap.value(Nebula::NebN);}
setPlanetaryNebulaColor(const Vec3f & c)77 void NebulaMgr::setPlanetaryNebulaColor(const Vec3f& c) {Nebula::hintColorMap.insert(Nebula::NebPn, c); emit planetaryNebulaeColorChanged(c);}
getPlanetaryNebulaColor(void) const78 const Vec3f NebulaMgr::getPlanetaryNebulaColor(void) const {return Nebula::hintColorMap.value(Nebula::NebPn);}
setReflectionNebulaColor(const Vec3f & c)79 void NebulaMgr::setReflectionNebulaColor(const Vec3f& c) {Nebula::hintColorMap.insert(Nebula::NebRn, c); emit reflectionNebulaeColorChanged(c);}
getReflectionNebulaColor(void) const80 const Vec3f NebulaMgr::getReflectionNebulaColor(void) const {return Nebula::hintColorMap.value(Nebula::NebRn);}
setBipolarNebulaColor(const Vec3f & c)81 void NebulaMgr::setBipolarNebulaColor(const Vec3f& c) {Nebula::hintColorMap.insert(Nebula::NebBn, c); emit bipolarNebulaeColorChanged(c);}
getBipolarNebulaColor(void) const82 const Vec3f NebulaMgr::getBipolarNebulaColor(void) const {return Nebula::hintColorMap.value(Nebula::NebBn);}
setEmissionNebulaColor(const Vec3f & c)83 void NebulaMgr::setEmissionNebulaColor(const Vec3f& c) {Nebula::hintColorMap.insert(Nebula::NebEn, c); emit emissionNebulaeColorChanged(c);}
getEmissionNebulaColor(void) const84 const Vec3f NebulaMgr::getEmissionNebulaColor(void) const {return Nebula::hintColorMap.value(Nebula::NebEn);}
setDarkNebulaColor(const Vec3f & c)85 void NebulaMgr::setDarkNebulaColor(const Vec3f& c) {Nebula::hintColorMap.insert(Nebula::NebDn, c); emit darkNebulaeColorChanged(c);}
getDarkNebulaColor(void) const86 const Vec3f NebulaMgr::getDarkNebulaColor(void) const {return Nebula::hintColorMap.value(Nebula::NebDn);}
setHydrogenRegionColor(const Vec3f & c)87 void NebulaMgr::setHydrogenRegionColor(const Vec3f& c) {Nebula::hintColorMap.insert(Nebula::NebHII, c); emit hydrogenRegionsColorChanged(c);}
getHydrogenRegionColor(void) const88 const Vec3f NebulaMgr::getHydrogenRegionColor(void) const {return Nebula::hintColorMap.value(Nebula::NebHII);}
setSupernovaRemnantColor(const Vec3f & c)89 void NebulaMgr::setSupernovaRemnantColor(const Vec3f& c) {Nebula::hintColorMap.insert(Nebula::NebSNR, c); emit supernovaRemnantsColorChanged(c);}
getSupernovaRemnantColor(void) const90 const Vec3f NebulaMgr::getSupernovaRemnantColor(void) const {return Nebula::hintColorMap.value(Nebula::NebSNR);}
setSupernovaCandidateColor(const Vec3f & c)91 void NebulaMgr::setSupernovaCandidateColor(const Vec3f& c) {Nebula::hintColorMap.insert(Nebula::NebSNC, c); emit supernovaCandidatesColorChanged(c);}
getSupernovaCandidateColor(void) const92 const Vec3f NebulaMgr::getSupernovaCandidateColor(void) const {return Nebula::hintColorMap.value(Nebula::NebSNC);}
setSupernovaRemnantCandidateColor(const Vec3f & c)93 void NebulaMgr::setSupernovaRemnantCandidateColor(const Vec3f& c) {Nebula::hintColorMap.insert(Nebula::NebSNRC, c); emit supernovaRemnantCandidatesColorChanged(c);}
getSupernovaRemnantCandidateColor(void) const94 const Vec3f NebulaMgr::getSupernovaRemnantCandidateColor(void) const {return Nebula::hintColorMap.value(Nebula::NebSNRC);}
setInterstellarMatterColor(const Vec3f & c)95 void NebulaMgr::setInterstellarMatterColor(const Vec3f& c) {Nebula::hintColorMap.insert(Nebula::NebISM, c); emit interstellarMatterColorChanged(c);}
getInterstellarMatterColor(void) const96 const Vec3f NebulaMgr::getInterstellarMatterColor(void) const {return Nebula::hintColorMap.value(Nebula::NebISM);}
setClusterWithNebulosityColor(const Vec3f & c)97 void NebulaMgr::setClusterWithNebulosityColor(const Vec3f& c) {Nebula::hintColorMap.insert(Nebula::NebCn, c); emit clusterWithNebulosityColorChanged(c);}
getClusterWithNebulosityColor(void) const98 const Vec3f NebulaMgr::getClusterWithNebulosityColor(void) const {return Nebula::hintColorMap.value(Nebula::NebCn);}
setClusterColor(const Vec3f & c)99 void NebulaMgr::setClusterColor(const Vec3f& c) {Nebula::hintColorMap.insert(Nebula::NebCl, c); emit clustersColorChanged(c);}
getClusterColor(void) const100 const Vec3f NebulaMgr::getClusterColor(void) const {return Nebula::hintColorMap.value(Nebula::NebCl);}
setOpenClusterColor(const Vec3f & c)101 void NebulaMgr::setOpenClusterColor(const Vec3f& c) {Nebula::hintColorMap.insert(Nebula::NebOc, c); emit openClustersColorChanged(c);}
getOpenClusterColor(void) const102 const Vec3f NebulaMgr::getOpenClusterColor(void) const {return Nebula::hintColorMap.value(Nebula::NebOc);}
setGlobularClusterColor(const Vec3f & c)103 void NebulaMgr::setGlobularClusterColor(const Vec3f& c) {Nebula::hintColorMap.insert(Nebula::NebGc, c); emit globularClustersColorChanged(c);}
getGlobularClusterColor(void) const104 const Vec3f NebulaMgr::getGlobularClusterColor(void) const {return Nebula::hintColorMap.value(Nebula::NebGc);}
setStellarAssociationColor(const Vec3f & c)105 void NebulaMgr::setStellarAssociationColor(const Vec3f& c) {Nebula::hintColorMap.insert(Nebula::NebSA, c); emit stellarAssociationsColorChanged(c);}
getStellarAssociationColor(void) const106 const Vec3f NebulaMgr::getStellarAssociationColor(void) const {return Nebula::hintColorMap.value(Nebula::NebSA);}
setStarCloudColor(const Vec3f & c)107 void NebulaMgr::setStarCloudColor(const Vec3f& c) {Nebula::hintColorMap.insert(Nebula::NebSC, c); emit starCloudsColorChanged(c);}
getStarCloudColor(void) const108 const Vec3f NebulaMgr::getStarCloudColor(void) const {return Nebula::hintColorMap.value(Nebula::NebSC);}
setEmissionObjectColor(const Vec3f & c)109 void NebulaMgr::setEmissionObjectColor(const Vec3f& c) {Nebula::hintColorMap.insert(Nebula::NebEMO, c); emit emissionObjectsColorChanged(c);}
getEmissionObjectColor(void) const110 const Vec3f NebulaMgr::getEmissionObjectColor(void) const {return Nebula::hintColorMap.value(Nebula::NebEMO);}
setBlLacObjectColor(const Vec3f & c)111 void NebulaMgr::setBlLacObjectColor(const Vec3f& c) {Nebula::hintColorMap.insert(Nebula::NebBLL, c); emit blLacObjectsColorChanged(c);}
getBlLacObjectColor(void) const112 const Vec3f NebulaMgr::getBlLacObjectColor(void) const {return Nebula::hintColorMap.value(Nebula::NebBLL);}
setBlazarColor(const Vec3f & c)113 void NebulaMgr::setBlazarColor(const Vec3f& c) {Nebula::hintColorMap.insert(Nebula::NebBLA, c); emit blazarsColorChanged(c);}
getBlazarColor(void) const114 const Vec3f NebulaMgr::getBlazarColor(void) const {return Nebula::hintColorMap.value(Nebula::NebBLA);}
setMolecularCloudColor(const Vec3f & c)115 void NebulaMgr::setMolecularCloudColor(const Vec3f& c) {Nebula::hintColorMap.insert(Nebula::NebMolCld, c); emit molecularCloudsColorChanged(c);}
getMolecularCloudColor(void) const116 const Vec3f NebulaMgr::getMolecularCloudColor(void) const {return Nebula::hintColorMap.value(Nebula::NebMolCld);}
setYoungStellarObjectColor(const Vec3f & c)117 void NebulaMgr::setYoungStellarObjectColor(const Vec3f& c) {Nebula::hintColorMap.insert(Nebula::NebYSO, c); emit youngStellarObjectsColorChanged(c);}
getYoungStellarObjectColor(void) const118 const Vec3f NebulaMgr::getYoungStellarObjectColor(void) const {return Nebula::hintColorMap.value(Nebula::NebYSO);}
setPossibleQuasarColor(const Vec3f & c)119 void NebulaMgr::setPossibleQuasarColor(const Vec3f& c) {Nebula::hintColorMap.insert(Nebula::NebPossQSO, c); emit possibleQuasarsColorChanged(c);}
getPossibleQuasarColor(void) const120 const Vec3f NebulaMgr::getPossibleQuasarColor(void) const {return Nebula::hintColorMap.value(Nebula::NebPossQSO);}
setPossiblePlanetaryNebulaColor(const Vec3f & c)121 void NebulaMgr::setPossiblePlanetaryNebulaColor(const Vec3f& c) {Nebula::hintColorMap.insert(Nebula::NebPossPN, c); emit possiblePlanetaryNebulaeColorChanged(c);}
getPossiblePlanetaryNebulaColor(void) const122 const Vec3f NebulaMgr::getPossiblePlanetaryNebulaColor(void) const {return Nebula::hintColorMap.value(Nebula::NebPossPN);}
setProtoplanetaryNebulaColor(const Vec3f & c)123 void NebulaMgr::setProtoplanetaryNebulaColor(const Vec3f& c) {Nebula::hintColorMap.insert(Nebula::NebPPN, c); emit protoplanetaryNebulaeColorChanged(c);}
getProtoplanetaryNebulaColor(void) const124 const Vec3f NebulaMgr::getProtoplanetaryNebulaColor(void) const {return Nebula::hintColorMap.value(Nebula::NebPPN);}
setStarColor(const Vec3f & c)125 void NebulaMgr::setStarColor(const Vec3f& c) {Nebula::hintColorMap.insert(Nebula::NebStar, c); emit starsColorChanged(c);}
getStarColor(void) const126 const Vec3f NebulaMgr::getStarColor(void) const {return Nebula::hintColorMap.value(Nebula::NebStar);}
setSymbioticStarColor(const Vec3f & c)127 void NebulaMgr::setSymbioticStarColor(const Vec3f& c) {Nebula::hintColorMap.insert(Nebula::NebSymbioticStar, c); emit symbioticStarsColorChanged(c);}
getSymbioticStarColor(void) const128 const Vec3f NebulaMgr::getSymbioticStarColor(void) const {return Nebula::hintColorMap.value(Nebula::NebSymbioticStar);}
setEmissionLineStarColor(const Vec3f & c)129 void NebulaMgr::setEmissionLineStarColor(const Vec3f& c) {Nebula::hintColorMap.insert(Nebula::NebEmissionLineStar, c); emit emissionLineStarsColorChanged(c);}
getEmissionLineStarColor(void) const130 const Vec3f NebulaMgr::getEmissionLineStarColor(void) const {return Nebula::hintColorMap.value(Nebula::NebEmissionLineStar);}
setGalaxyClusterColor(const Vec3f & c)131 void NebulaMgr::setGalaxyClusterColor(const Vec3f& c) {Nebula::hintColorMap.insert(Nebula::NebGxCl, c); emit galaxyClustersColorChanged(c);}
getGalaxyClusterColor(void) const132 const Vec3f NebulaMgr::getGalaxyClusterColor(void) const {return Nebula::hintColorMap.value(Nebula::NebGxCl);}
setHintsProportional(const bool proportional)133 void NebulaMgr::setHintsProportional(const bool proportional) {if(Nebula::drawHintProportional!=proportional){ Nebula::drawHintProportional=proportional; emit hintsProportionalChanged(proportional);}}
getHintsProportional(void) const134 bool NebulaMgr::getHintsProportional(void) const {return Nebula::drawHintProportional;}
setDesignationUsage(const bool flag)135 void NebulaMgr::setDesignationUsage(const bool flag) {if(Nebula::designationUsage!=flag){ Nebula::designationUsage=flag; emit designationUsageChanged(flag);}}
getDesignationUsage(void) const136 bool NebulaMgr::getDesignationUsage(void) const {return Nebula::designationUsage; }
setFlagOutlines(const bool flag)137 void NebulaMgr::setFlagOutlines(const bool flag) {if(Nebula::flagUseOutlines!=flag){ Nebula::flagUseOutlines=flag; emit flagOutlinesDisplayedChanged(flag);}}
getFlagOutlines(void) const138 bool NebulaMgr::getFlagOutlines(void) const {return Nebula::flagUseOutlines;}
setFlagAdditionalNames(const bool flag)139 void NebulaMgr::setFlagAdditionalNames(const bool flag) {if(Nebula::flagShowAdditionalNames!=flag){ Nebula::flagShowAdditionalNames=flag; emit flagAdditionalNamesDisplayedChanged(flag);}}
getFlagAdditionalNames(void) const140 bool NebulaMgr::getFlagAdditionalNames(void) const {return Nebula::flagShowAdditionalNames;}
141 
NebulaMgr(void)142 NebulaMgr::NebulaMgr(void) : StelObjectModule()
143 	, nebGrid(200)
144 	, hintsAmount(0)
145 	, labelsAmount(0)
146 	, flagConverter(false)
147 	, flagDecimalCoordinates(true)
148 {
149 	setObjectName("NebulaMgr");
150 }
151 
~NebulaMgr()152 NebulaMgr::~NebulaMgr()
153 {
154 	Nebula::texCircle = StelTextureSP();
155 	Nebula::texCircleLarge = StelTextureSP();
156 	Nebula::texRegion = StelTextureSP();
157 	Nebula::texGalaxy = StelTextureSP();
158 	Nebula::texGalaxyLarge = StelTextureSP();
159 	Nebula::texOpenCluster = StelTextureSP();
160 	Nebula::texOpenClusterLarge = StelTextureSP();
161 	Nebula::texOpenClusterXLarge = StelTextureSP();
162 	Nebula::texGlobularCluster = StelTextureSP();
163 	Nebula::texGlobularClusterLarge = StelTextureSP();
164 	Nebula::texPlanetaryNebula = StelTextureSP();
165 	Nebula::texDiffuseNebula = StelTextureSP();
166 	Nebula::texDiffuseNebulaLarge = StelTextureSP();
167 	Nebula::texDiffuseNebulaXLarge = StelTextureSP();
168 	Nebula::texDarkNebula = StelTextureSP();
169 	Nebula::texDarkNebulaLarge = StelTextureSP();
170 	Nebula::texOpenClusterWithNebulosity = StelTextureSP();
171 	Nebula::texOpenClusterWithNebulosityLarge = StelTextureSP();
172 }
173 
174 /*************************************************************************
175  Reimplementation of the getCallOrder method
176 *************************************************************************/
getCallOrder(StelModuleActionName actionName) const177 double NebulaMgr::getCallOrder(StelModuleActionName actionName) const
178 {
179 	if (actionName==StelModule::ActionDraw)
180 		return StelApp::getInstance().getModuleMgr().getModule("MilkyWay")->getCallOrder(actionName)+10;
181 	return 0;
182 }
183 
184 // read from stream
init()185 void NebulaMgr::init()
186 {
187 	QSettings* conf = StelApp::getInstance().getSettings();
188 	Q_ASSERT(conf);
189 
190 	Nebula::buildTypeStringMap();
191 	nebulaFont.setPixelSize(StelApp::getInstance().getScreenFontSize());
192 	connect(&StelApp::getInstance(), SIGNAL(screenFontSizeChanged(int)), SLOT(setFontSizeFromApp(int)));
193 	// Load circle texture
194 	Nebula::texCircle		= StelApp::getInstance().getTextureManager().createTexture(StelFileMgr::getInstallationDir()+"/textures/neb.png");
195 	// Load circle texture for large DSO
196 	Nebula::texCircleLarge	= StelApp::getInstance().getTextureManager().createTexture(StelFileMgr::getInstallationDir()+"/textures/neb_lrg.png");
197 	// Load dashed shape texture
198 	Nebula::texRegion		= StelApp::getInstance().getTextureManager().createTexture(StelFileMgr::getInstallationDir()+"/textures/neb_reg.png");
199 	// Load ellipse texture
200 	Nebula::texGalaxy		= StelApp::getInstance().getTextureManager().createTexture(StelFileMgr::getInstallationDir()+"/textures/neb_gal.png");
201 	// Load ellipse texture for large galaxies
202 	Nebula::texGalaxyLarge		= StelApp::getInstance().getTextureManager().createTexture(StelFileMgr::getInstallationDir()+"/textures/neb_gal_lrg.png");
203 	// Load open cluster marker texture
204 	Nebula::texOpenCluster		= StelApp::getInstance().getTextureManager().createTexture(StelFileMgr::getInstallationDir()+"/textures/neb_ocl.png");
205 	// Load open cluster marker texture for large objects
206 	Nebula::texOpenClusterLarge	= StelApp::getInstance().getTextureManager().createTexture(StelFileMgr::getInstallationDir()+"/textures/neb_ocl_lrg.png");
207 	// Load open cluster marker texture for extra large objects
208 	Nebula::texOpenClusterXLarge	= StelApp::getInstance().getTextureManager().createTexture(StelFileMgr::getInstallationDir()+"/textures/neb_ocl_xlrg.png");
209 	// Load globular cluster marker texture
210 	Nebula::texGlobularCluster	= StelApp::getInstance().getTextureManager().createTexture(StelFileMgr::getInstallationDir()+"/textures/neb_gcl.png");
211 	// Load globular cluster marker texture for large GCls
212 	Nebula::texGlobularClusterLarge	= StelApp::getInstance().getTextureManager().createTexture(StelFileMgr::getInstallationDir()+"/textures/neb_gcl_lrg.png");
213 	// Load planetary nebula marker texture
214 	Nebula::texPlanetaryNebula	= StelApp::getInstance().getTextureManager().createTexture(StelFileMgr::getInstallationDir()+"/textures/neb_pnb.png");
215 	// Load diffuse nebula marker texture
216 	Nebula::texDiffuseNebula	= StelApp::getInstance().getTextureManager().createTexture(StelFileMgr::getInstallationDir()+"/textures/neb_dif.png");
217 	// Load diffuse nebula marker texture for large DSO
218 	Nebula::texDiffuseNebulaLarge	= StelApp::getInstance().getTextureManager().createTexture(StelFileMgr::getInstallationDir()+"/textures/neb_dif_lrg.png");
219 	// Load diffuse nebula marker texture for extra large DSO
220 	Nebula::texDiffuseNebulaXLarge	= StelApp::getInstance().getTextureManager().createTexture(StelFileMgr::getInstallationDir()+"/textures/neb_dif_xlrg.png");
221 	// Load dark nebula marker texture
222 	Nebula::texDarkNebula		= StelApp::getInstance().getTextureManager().createTexture(StelFileMgr::getInstallationDir()+"/textures/neb_drk.png");
223 	// Load dark nebula marker texture for large DSO
224 	Nebula::texDarkNebulaLarge	= StelApp::getInstance().getTextureManager().createTexture(StelFileMgr::getInstallationDir()+"/textures/neb_drk_lrg.png");
225 	// Load Ocl/Nebula marker texture
226 	Nebula::texOpenClusterWithNebulosity = StelApp::getInstance().getTextureManager().createTexture(StelFileMgr::getInstallationDir()+"/textures/neb_ocln.png");
227 	// Load Ocl/Nebula marker texture for large objects
228 	Nebula::texOpenClusterWithNebulosityLarge = StelApp::getInstance().getTextureManager().createTexture(StelFileMgr::getInstallationDir()+"/textures/neb_ocln_lrg.png");
229 	// Load pointer texture
230 	texPointer = StelApp::getInstance().getTextureManager().createTexture(StelFileMgr::getInstallationDir()+"/textures/pointeur5.png");
231 
232 	setFlagShow(conf->value("astro/flag_nebula",true).toBool());
233 	setFlagHints(conf->value("astro/flag_nebula_name",false).toBool());
234 	setHintsAmount(conf->value("astro/nebula_hints_amount", 3.0).toDouble());
235 	setLabelsAmount(conf->value("astro/nebula_labels_amount", 3.0).toDouble());
236 	setHintsProportional(conf->value("astro/flag_nebula_hints_proportional", false).toBool());
237 	setFlagOutlines(conf->value("astro/flag_dso_outlines_usage", false).toBool());
238 	setFlagAdditionalNames(conf->value("astro/flag_dso_additional_names",true).toBool());
239 	setDesignationUsage(conf->value("astro/flag_dso_designation_usage", false).toBool());
240 	setFlagSurfaceBrightnessUsage(conf->value("astro/flag_surface_brightness_usage", false).toBool());
241 	setFlagSurfaceBrightnessArcsecUsage(conf->value("gui/flag_surface_brightness_arcsec", false).toBool());
242 	setFlagSurfaceBrightnessShortNotationUsage(conf->value("gui/flag_surface_brightness_short", false).toBool());
243 
244 	setFlagSizeLimitsUsage(conf->value("astro/flag_size_limits_usage", false).toBool());
245 	setMinSizeLimit(conf->value("astro/size_limit_min", 1.0).toDouble());
246 	setMaxSizeLimit(conf->value("astro/size_limit_max", 600.0).toDouble());
247 
248 	// Load colors from config file
249 	// Upgrade config keys
250 	if (conf->contains("color/nebula_label_color"))
251 	{
252 		conf->setValue("color/dso_label_color", conf->value("color/nebula_label_color", "0.4,0.3,0.5").toString());
253 		conf->remove("color/nebula_label_color");
254 	}
255 	if (conf->contains("color/nebula_circle_color"))
256 	{
257 		conf->setValue("color/dso_circle_color", conf->value("color/nebula_circle_color", "0.8,0.8,0.1").toString());
258 		conf->remove("color/nebula_circle_color");
259 	}
260 	if (conf->contains("color/nebula_galaxy_color"))
261 	{
262 		conf->setValue("color/dso_galaxy_color", conf->value("color/nebula_galaxy_color", "1.0,0.2,0.2").toString());
263 		conf->remove("color/nebula_galaxy_color");
264 	}
265 	if (conf->contains("color/nebula_radioglx_color"))
266 	{
267 		conf->setValue("color/dso_radio_galaxy_color", conf->value("color/nebula_radioglx_color", "0.3,0.3,0.3").toString());
268 		conf->remove("color/nebula_radioglx_color");
269 	}
270 	if (conf->contains("color/nebula_activeglx_color"))
271 	{
272 		conf->setValue("color/dso_active_galaxy_color", conf->value("color/nebula_activeglx_color", "1.0,0.5,0.2").toString());
273 		conf->remove("color/nebula_activeglx_color");
274 	}
275 	if (conf->contains("color/nebula_intglx_color"))
276 	{
277 		conf->setValue("color/dso_interacting_galaxy_color", conf->value("color/nebula_intglx_color", "0.2,0.5,1.0").toString());
278 		conf->remove("color/nebula_intglx_color");
279 	}
280 	if (conf->contains("color/nebula_brightneb_color"))
281 	{
282 		conf->setValue("color/dso_nebula_color", conf->value("color/nebula_brightneb_color", "0.1,1.0,0.1").toString());
283 		conf->remove("color/nebula_brightneb_color");
284 	}
285 	if (conf->contains("color/nebula_darkneb_color"))
286 	{
287 		conf->setValue("color/dso_dark_nebula_color", conf->value("color/nebula_darkneb_color", "0.3,0.3,0.3").toString());
288 		conf->remove("color/nebula_darkneb_color");
289 	}
290 	if (conf->contains("color/nebula_hregion_color"))
291 	{
292 		conf->setValue("color/dso_hydrogen_region_color", conf->value("color/nebula_hregion_color", "0.1,1.0,0.1").toString());
293 		conf->remove("color/nebula_hregion_color");
294 	}
295 	if (conf->contains("color/nebula_snr_color"))
296 	{
297 		conf->setValue("color/dso_supernova_remnant_color", conf->value("color/nebula_snr_color", "0.1,1.0,0.1").toString());
298 		conf->remove("color/nebula_snr_color");
299 	}
300 	if (conf->contains("color/nebula_cluster_color"))
301 	{
302 		conf->setValue("color/dso_cluster_color", conf->value("color/nebula_cluster_color", "0.8,0.8,0.1").toString());
303 		conf->remove("color/nebula_cluster_color");
304 	}
305 
306 	// Set colors for markers
307 	setLabelsColor(Vec3f(conf->value("color/dso_label_color", "0.2,0.6,0.7").toString()));
308 	setCirclesColor(Vec3f(conf->value("color/dso_circle_color", "1.0,0.7,0.2").toString()));
309 	setRegionsColor(Vec3f(conf->value("color/dso_region_color", "0.7,0.7,0.2").toString()));
310 
311 	QString defaultGalaxyColor = conf->value("color/dso_galaxy_color", "1.0,0.2,0.2").toString();
312 	setGalaxyColor(           Vec3f(defaultGalaxyColor));
313 	setRadioGalaxyColor(      Vec3f(conf->value("color/dso_radio_galaxy_color", "0.3,0.3,0.3").toString()));
314 	setActiveGalaxyColor(     Vec3f(conf->value("color/dso_active_galaxy_color", "1.0,0.5,0.2").toString()));
315 	setInteractingGalaxyColor(Vec3f(conf->value("color/dso_interacting_galaxy_color", "0.2,0.5,1.0").toString()));
316 	setGalaxyClusterColor(    Vec3f(conf->value("color/dso_galaxy_cluster_color", "0.2,0.8,1.0").toString()));
317 	setQuasarColor(           Vec3f(conf->value("color/dso_quasar_color", defaultGalaxyColor).toString()));
318 	setPossibleQuasarColor(   Vec3f(conf->value("color/dso_possible_quasar_color", defaultGalaxyColor).toString()));
319 	setBlLacObjectColor(      Vec3f(conf->value("color/dso_bl_lac_color", defaultGalaxyColor).toString()));
320 	setBlazarColor(           Vec3f(conf->value("color/dso_blazar_color", defaultGalaxyColor).toString()));
321 
322 	QString defaultNebulaColor = conf->value("color/dso_nebula_color", "0.1,1.0,0.1").toString();
323 	setNebulaColor(                   Vec3f(defaultNebulaColor));
324 	setPlanetaryNebulaColor(          Vec3f(conf->value("color/dso_planetary_nebula_color", defaultNebulaColor).toString()));
325 	setReflectionNebulaColor(         Vec3f(conf->value("color/dso_reflection_nebula_color", defaultNebulaColor).toString()));
326 	setBipolarNebulaColor(            Vec3f(conf->value("color/dso_bipolar_nebula_color", defaultNebulaColor).toString()));
327 	setEmissionNebulaColor(           Vec3f(conf->value("color/dso_emission_nebula_color", defaultNebulaColor).toString()));
328 	setDarkNebulaColor(               Vec3f(conf->value("color/dso_dark_nebula_color", "0.3,0.3,0.3").toString()));
329 	setHydrogenRegionColor(           Vec3f(conf->value("color/dso_hydrogen_region_color", defaultNebulaColor).toString()));
330 	setSupernovaRemnantColor(         Vec3f(conf->value("color/dso_supernova_remnant_color", defaultNebulaColor).toString()));
331 	setSupernovaCandidateColor(       Vec3f(conf->value("color/dso_supernova_candidate_color", defaultNebulaColor).toString()));
332 	setSupernovaRemnantCandidateColor(Vec3f(conf->value("color/dso_supernova_remnant_cand_color", defaultNebulaColor).toString()));
333 	setInterstellarMatterColor(       Vec3f(conf->value("color/dso_interstellar_matter_color", defaultNebulaColor).toString()));
334 	setClusterWithNebulosityColor(    Vec3f(conf->value("color/dso_cluster_with_nebulosity_color", defaultNebulaColor).toString()));
335 	setMolecularCloudColor(           Vec3f(conf->value("color/dso_molecular_cloud_color", defaultNebulaColor).toString()));
336 	setPossiblePlanetaryNebulaColor(  Vec3f(conf->value("color/dso_possible_planetary_nebula_color", defaultNebulaColor).toString()));
337 	setProtoplanetaryNebulaColor(     Vec3f(conf->value("color/dso_protoplanetary_nebula_color", defaultNebulaColor).toString()));
338 
339 	QString defaultClusterColor = conf->value("color/dso_cluster_color", "1.0,1.0,0.1").toString();
340 	setClusterColor(           Vec3f(defaultClusterColor));
341 	setOpenClusterColor(       Vec3f(conf->value("color/dso_open_cluster_color", defaultClusterColor).toString()));
342 	setGlobularClusterColor(   Vec3f(conf->value("color/dso_globular_cluster_color", defaultClusterColor).toString()));
343 	setStellarAssociationColor(Vec3f(conf->value("color/dso_stellar_association_color", defaultClusterColor).toString()));
344 	setStarCloudColor(         Vec3f(conf->value("color/dso_star_cloud_color", defaultClusterColor).toString()));
345 
346 	QString defaultStellarColor = conf->value("color/dso_star_color", "1.0,0.7,0.2").toString();
347 	setStarColor(              Vec3f(defaultStellarColor));
348 	setSymbioticStarColor(     Vec3f(conf->value("color/dso_symbiotic_star_color", defaultStellarColor).toString()));
349 	setEmissionLineStarColor(  Vec3f(conf->value("color/dso_emission_star_color", defaultStellarColor).toString()));
350 	setEmissionObjectColor(    Vec3f(conf->value("color/dso_emission_object_color", defaultStellarColor).toString()));
351 	setYoungStellarObjectColor(Vec3f(conf->value("color/dso_young_stellar_object_color", defaultStellarColor).toString()));
352 
353 	// for DSO convertor (for developers!)
354 	flagConverter = conf->value("devel/convert_dso_catalog", false).toBool();
355 	flagDecimalCoordinates = conf->value("devel/convert_dso_decimal_coord", true).toBool();
356 
357 	setFlagUseTypeFilters(conf->value("astro/flag_use_type_filter", false).toBool());
358 
359 	Nebula::CatalogGroup catalogFilters = Nebula::CatalogGroup(Q_NULLPTR);
360 
361 	conf->beginGroup("dso_catalog_filters");
362 	if (conf->value("flag_show_ngc", true).toBool())
363 		catalogFilters	|= Nebula::CatNGC;
364 	if (conf->value("flag_show_ic", true).toBool())
365 		catalogFilters	|= Nebula::CatIC;
366 	if (conf->value("flag_show_m", true).toBool())
367 		catalogFilters	|= Nebula::CatM;
368 	if (conf->value("flag_show_c", false).toBool())
369 		catalogFilters	|= Nebula::CatC;
370 	if (conf->value("flag_show_b", false).toBool())
371 		catalogFilters	|= Nebula::CatB;
372 	if (conf->value("flag_show_sh2", false).toBool())
373 		catalogFilters	|= Nebula::CatSh2;
374 	if (conf->value("flag_show_vdb", false).toBool())
375 		catalogFilters	|= Nebula::CatVdB;
376 	if (conf->value("flag_show_lbn", false).toBool())
377 		catalogFilters	|= Nebula::CatLBN;
378 	if (conf->value("flag_show_ldn", false).toBool())
379 		catalogFilters	|= Nebula::CatLDN;
380 	if (conf->value("flag_show_rcw", false).toBool())
381 		catalogFilters	|= Nebula::CatRCW;
382 	if (conf->value("flag_show_cr", false).toBool())
383 		catalogFilters	|= Nebula::CatCr;
384 	if (conf->value("flag_show_mel", false).toBool())
385 		catalogFilters	|= Nebula::CatMel;
386 	if (conf->value("flag_show_pgc", false).toBool())
387 		catalogFilters	|= Nebula::CatPGC;
388 	if (conf->value("flag_show_ced", false).toBool())
389 		catalogFilters	|= Nebula::CatCed;
390 	if (conf->value("flag_show_ugc", false).toBool())
391 		catalogFilters	|= Nebula::CatUGC;
392 	if (conf->value("flag_show_arp", false).toBool())
393 		catalogFilters	|= Nebula::CatArp;
394 	if (conf->value("flag_show_vv", false).toBool())
395 		catalogFilters	|= Nebula::CatVV;
396 	if (conf->value("flag_show_pk", false).toBool())
397 		catalogFilters	|= Nebula::CatPK;
398 	if (conf->value("flag_show_png", false).toBool())
399 		catalogFilters	|= Nebula::CatPNG;
400 	if (conf->value("flag_show_snrg", false).toBool())
401 		catalogFilters	|= Nebula::CatSNRG;
402 	if (conf->value("flag_show_aco", false).toBool())
403 		catalogFilters	|= Nebula::CatACO;
404 	if (conf->value("flag_show_hcg", false).toBool())
405 		catalogFilters	|= Nebula::CatHCG;
406 	if (conf->value("flag_show_eso", false).toBool())
407 		catalogFilters	|= Nebula::CatESO;
408 	if (conf->value("flag_show_vdbh", false).toBool())
409 		catalogFilters	|= Nebula::CatVdBH;
410 	if (conf->value("flag_show_dwb", false).toBool())
411 		catalogFilters	|= Nebula::CatDWB;
412 	if (conf->value("flag_show_tr", false).toBool())
413 		catalogFilters	|= Nebula::CatTr;
414 	if (conf->value("flag_show_st", false).toBool())
415 		catalogFilters	|= Nebula::CatSt;
416 	if (conf->value("flag_show_ru", false).toBool())
417 		catalogFilters	|= Nebula::CatRu;
418 	if (conf->value("flag_show_vdbha", false).toBool())
419 		catalogFilters	|= Nebula::CatVdBHa;
420 	if (conf->value("flag_show_other", true).toBool())
421 		catalogFilters	|= Nebula::CatOther;
422 	conf->endGroup();
423 
424 	// NB: nebula set loaded inside setter of catalog filter
425 	setCatalogFilters(catalogFilters);
426 
427 	Nebula::TypeGroup typeFilters = Nebula::TypeGroup(Q_NULLPTR);
428 
429 	conf->beginGroup("dso_type_filters");
430 	if (conf->value("flag_show_galaxies", true).toBool())
431 		typeFilters	|= Nebula::TypeGalaxies;
432 	if (conf->value("flag_show_active_galaxies", true).toBool())
433 		typeFilters	|= Nebula::TypeActiveGalaxies;
434 	if (conf->value("flag_show_interacting_galaxies", true).toBool())
435 		typeFilters	|= Nebula::TypeInteractingGalaxies;
436 	if (conf->value("flag_show_open_clusters", true).toBool())
437 		typeFilters	|= Nebula::TypeOpenStarClusters;
438 	if (conf->value("flag_show_globular_clusters", true).toBool())
439 		typeFilters	|= Nebula::TypeGlobularStarClusters;
440 	if (conf->value("flag_show_bright_nebulae", true).toBool())
441 		typeFilters	|= Nebula::TypeBrightNebulae;
442 	if (conf->value("flag_show_dark_nebulae", true).toBool())
443 		typeFilters	|= Nebula::TypeDarkNebulae;
444 	if (conf->value("flag_show_planetary_nebulae", true).toBool())
445 		typeFilters	|= Nebula::TypePlanetaryNebulae;
446 	if (conf->value("flag_show_hydrogen_regions", true).toBool())
447 		typeFilters	|= Nebula::TypeHydrogenRegions;
448 	if (conf->value("flag_show_supernova_remnants", true).toBool())
449 		typeFilters	|= Nebula::TypeSupernovaRemnants;
450 	if (conf->value("flag_show_galaxy_clusters", true).toBool())
451 		typeFilters	|= Nebula::TypeGalaxyClusters;
452 	if (conf->value("flag_show_other", true).toBool())
453 		typeFilters	|= Nebula::TypeOther;
454 	conf->endGroup();
455 
456 	setTypeFilters(typeFilters);
457 
458 	// TODO: mechanism to specify which sets get loaded at start time.
459 	// candidate methods:
460 	// 1. config file option (list of sets to load at startup)
461 	// 2. load all
462 	// 3. flag in nebula_textures.fab (yuk)
463 	// 4. info.ini file in each set containing a "load at startup" item
464 	// For now (0.9.0), just load the default set
465 	loadNebulaSet("default");
466 
467 	updateI18n();
468 
469 	StelApp *app = &StelApp::getInstance();
470 	connect(app, SIGNAL(languageChanged()), this, SLOT(updateI18n()));
471 	connect(&app->getSkyCultureMgr(), SIGNAL(currentSkyCultureChanged(QString)), this, SLOT(updateSkyCulture(const QString&)));
472 	GETSTELMODULE(StelObjectMgr)->registerStelObjectMgr(this);
473 
474 	addAction("actionShow_Nebulas", N_("Display Options"), N_("Deep-sky objects"), "flagHintDisplayed", "D", "N");
475 	addAction("actionSet_Nebula_TypeFilterUsage", N_("Display Options"), N_("Toggle DSO type filter"), "flagTypeFiltersUsage");
476 }
477 
478 struct DrawNebulaFuncObject
479 {
DrawNebulaFuncObjectDrawNebulaFuncObject480 	DrawNebulaFuncObject(float amaxMagHints, float amaxMagLabels, StelPainter* p, StelCore* aCore, bool acheckMaxMagHints)
481 		: maxMagHints(amaxMagHints)
482 		, maxMagLabels(amaxMagLabels)
483 		, sPainter(p)
484 		, core(aCore)
485 		, checkMaxMagHints(acheckMaxMagHints)
486 	{
487 		angularSizeLimit = 5.f/sPainter->getProjector()->getPixelPerRadAtCenter()*M_180_PIf;
488 	}
operator ()DrawNebulaFuncObject489 	void operator()(StelRegionObject* obj)
490 	{
491 		if (checkMaxMagHints)
492 			return;
493 
494 		Nebula* n = static_cast<Nebula*>(obj);
495 		float mag = n->vMag;
496 		if (mag>90.f)
497 			mag = n->bMag;
498 
499 		StelSkyDrawer *drawer = core->getSkyDrawer();
500 		// filter out DSOs which are too dim to be seen (e.g. for bino observers)
501 		if ((drawer->getFlagNebulaMagnitudeLimit()) && (mag > static_cast<float>(drawer->getCustomNebulaMagnitudeLimit())))
502 			return;
503 
504 		if (!n->objectInDisplayedCatalog())
505 			return;
506 
507 		if (!n->objectInAllowedSizeRangeLimits())
508 			return;
509 
510 		if (n->majorAxisSize>angularSizeLimit || n->majorAxisSize==0.f || mag <= maxMagHints)
511 		{
512 			sPainter->getProjector()->project(n->getJ2000EquatorialPos(core),n->XY);
513 			n->drawLabel(*sPainter, maxMagLabels);
514 			n->drawHints(*sPainter, maxMagHints, core);
515 			n->drawOutlines(*sPainter, maxMagHints);
516 		}
517 	}
518 	float maxMagHints;
519 	float maxMagLabels;
520 	StelPainter* sPainter;
521 	StelCore* core;
522 	float angularSizeLimit;
523 	bool checkMaxMagHints;
524 };
525 
setCatalogFilters(Nebula::CatalogGroup cflags)526 void NebulaMgr::setCatalogFilters(Nebula::CatalogGroup cflags)
527 {
528 	if(static_cast<int>(cflags) != static_cast<int>(Nebula::catalogFilters))
529 	{
530 		Nebula::catalogFilters = cflags;
531 		emit catalogFiltersChanged(cflags);
532 	}
533 }
534 
setTypeFilters(Nebula::TypeGroup tflags)535 void NebulaMgr::setTypeFilters(Nebula::TypeGroup tflags)
536 {
537 	if(static_cast<int>(tflags) != static_cast<int>(Nebula::typeFilters))
538 	{
539 		Nebula::typeFilters = tflags;
540 		emit typeFiltersChanged(tflags);
541 	}
542 }
543 
setFlagSurfaceBrightnessUsage(const bool usage)544 void NebulaMgr::setFlagSurfaceBrightnessUsage(const bool usage)
545 {
546 	if (usage!=Nebula::surfaceBrightnessUsage)
547 	{
548 		Nebula::surfaceBrightnessUsage=usage;
549 		emit flagSurfaceBrightnessUsageChanged(usage);
550 	}
551 }
552 
getFlagSurfaceBrightnessUsage(void) const553 bool NebulaMgr::getFlagSurfaceBrightnessUsage(void) const
554 {
555 	return Nebula::surfaceBrightnessUsage;
556 }
557 
setFlagSurfaceBrightnessArcsecUsage(const bool usage)558 void NebulaMgr::setFlagSurfaceBrightnessArcsecUsage(const bool usage)
559 {
560 	if (usage!=Nebula::flagUseArcsecSurfaceBrightness)
561 	{
562 		Nebula::flagUseArcsecSurfaceBrightness=usage;
563 		emit flagSurfaceBrightnessArcsecUsageChanged(usage);
564 	}
565 }
566 
getFlagSurfaceBrightnessArcsecUsage(void) const567 bool NebulaMgr::getFlagSurfaceBrightnessArcsecUsage(void) const
568 {
569 	return Nebula::flagUseArcsecSurfaceBrightness;
570 }
571 
setFlagSurfaceBrightnessShortNotationUsage(const bool usage)572 void NebulaMgr::setFlagSurfaceBrightnessShortNotationUsage(const bool usage)
573 {
574 	if (usage!=Nebula::flagUseShortNotationSurfaceBrightness)
575 	{
576 		Nebula::flagUseShortNotationSurfaceBrightness=usage;
577 		emit flagSurfaceBrightnessShortNotationUsageChanged(usage);
578 	}
579 }
580 
getFlagSurfaceBrightnessShortNotationUsage(void) const581 bool NebulaMgr::getFlagSurfaceBrightnessShortNotationUsage(void) const
582 {
583 	return Nebula::flagUseShortNotationSurfaceBrightness;
584 }
585 
setFlagSizeLimitsUsage(const bool usage)586 void NebulaMgr::setFlagSizeLimitsUsage(const bool usage)
587 {
588 	if (usage!=Nebula::flagUseSizeLimits)
589 	{
590 		Nebula::flagUseSizeLimits=usage;
591 		emit flagSizeLimitsUsageChanged(usage);
592 	}
593 }
594 
getFlagSizeLimitsUsage(void) const595 bool NebulaMgr::getFlagSizeLimitsUsage(void) const
596 {
597 	return Nebula::flagUseSizeLimits;
598 }
599 
setFlagUseTypeFilters(const bool b)600 void NebulaMgr::setFlagUseTypeFilters(const bool b)
601 {
602 	if (Nebula::flagUseTypeFilters!=b)
603 	{
604 		Nebula::flagUseTypeFilters=b;
605 		emit flagUseTypeFiltersChanged(b);
606 	}
607 }
608 
getFlagUseTypeFilters(void) const609 bool NebulaMgr::getFlagUseTypeFilters(void) const
610 {
611 	return Nebula::flagUseTypeFilters;
612 }
613 
setLabelsAmount(double a)614 void NebulaMgr::setLabelsAmount(double a)
615 {
616 	if((a-labelsAmount) != 0.)
617 	{
618 		labelsAmount=a;
619 		emit labelsAmountChanged(a);
620 	}
621 }
622 
getLabelsAmount(void) const623 double NebulaMgr::getLabelsAmount(void) const
624 {
625 	return labelsAmount;
626 }
627 
setHintsAmount(double f)628 void NebulaMgr::setHintsAmount(double f)
629 {
630 	if((hintsAmount-f) != 0.)
631 	{
632 		hintsAmount = f;
633 		emit hintsAmountChanged(f);
634 	}
635 }
636 
getHintsAmount(void) const637 double NebulaMgr::getHintsAmount(void) const
638 {
639 	return hintsAmount;
640 }
641 
setMinSizeLimit(double s)642 void NebulaMgr::setMinSizeLimit(double s)
643 {
644 	if((Nebula::minSizeLimit-s) != 0.)
645 	{
646 		Nebula::minSizeLimit = s;
647 		emit minSizeLimitChanged(s);
648 	}
649 }
650 
getMinSizeLimit() const651 double NebulaMgr::getMinSizeLimit() const
652 {
653 	return Nebula::minSizeLimit;
654 }
655 
setMaxSizeLimit(double s)656 void NebulaMgr::setMaxSizeLimit(double s)
657 {
658 	if((Nebula::maxSizeLimit-s) != 0.)
659 	{
660 		Nebula::maxSizeLimit = s;
661 		emit maxSizeLimitChanged(s);
662 	}
663 }
664 
getMaxSizeLimit() const665 double NebulaMgr::getMaxSizeLimit() const
666 {
667 	return Nebula::maxSizeLimit;
668 }
669 
computeMaxMagHint(const StelSkyDrawer * skyDrawer) const670 float NebulaMgr::computeMaxMagHint(const StelSkyDrawer* skyDrawer) const
671 {
672 	return skyDrawer->getLimitMagnitude()*1.2f-2.f+static_cast<float>(hintsAmount *1.)-2.f;
673 }
674 
675 // Draw all the Nebulae
draw(StelCore * core)676 void NebulaMgr::draw(StelCore* core)
677 {
678 	const StelProjectorP prj = core->getProjection(StelCore::FrameJ2000);
679 	StelPainter sPainter(prj);
680 
681 	StelSkyDrawer* skyDrawer = core->getSkyDrawer();
682 
683 	Nebula::hintsBrightness = hintsFader.getInterstate()*flagShow.getInterstate();
684 
685 	sPainter.setBlending(true, GL_ONE, GL_ONE);
686 
687 	// Use a 4 degree margin (esp. for wide outlines)
688 	const float margin = 4.f*M_PI_180f*prj->getPixelPerRadAtCenter();
689 	const SphericalRegionP& p = prj->getViewportConvexPolygon(margin, margin);
690 
691 	// Print all the nebulae of all the selected zones
692 	float maxMagHints  = computeMaxMagHint(skyDrawer);
693 	float maxMagLabels = skyDrawer->getLimitMagnitude()-2.f+static_cast<float>(labelsAmount*1.2)-2.f;
694 	sPainter.setFont(nebulaFont);
695 	DrawNebulaFuncObject func(maxMagHints, maxMagLabels, &sPainter, core, hintsFader.getInterstate()<=0.f);
696 	nebGrid.processIntersectingPointInRegions(p.data(), func);
697 
698 	if (GETSTELMODULE(StelObjectMgr)->getFlagSelectedObjectPointer())
699 		drawPointer(core, sPainter);
700 }
701 
drawPointer(const StelCore * core,StelPainter & sPainter)702 void NebulaMgr::drawPointer(const StelCore* core, StelPainter& sPainter)
703 {
704 	const StelProjectorP prj = core->getProjection(StelCore::FrameJ2000);
705 
706 	const QList<StelObjectP> newSelected = GETSTELMODULE(StelObjectMgr)->getSelectedObject("Nebula");
707 	if (!newSelected.empty())
708 	{
709 		const StelObjectP obj = newSelected[0];
710 		Vec3d pos=obj->getJ2000EquatorialPos(core);
711 
712 		// Compute 2D pos and return if outside screen
713 		if (!prj->projectInPlace(pos)) return;
714 		sPainter.setColor(0.4f,0.5f,0.8f);
715 
716 		texPointer->bind();
717 
718 		sPainter.setBlending(true);
719 
720 		// Size on screen
721 		float size = static_cast<float>(obj->getAngularSize(core))*M_PI_180f*prj->getPixelPerRadAtCenter();
722 		if (size>120.f) // avoid oversized marker
723 			size = 120.f;
724 
725 		if (Nebula::drawHintProportional)
726 			size*=1.2f;
727 		size+=20.f + 10.f*std::sin(3.f * static_cast<float>(StelApp::getInstance().getAnimationTime()));
728 		sPainter.drawSprite2dMode(static_cast<float>(pos[0])-size*0.5f, static_cast<float>(pos[1])-size*0.5f, 10, 90);
729 		sPainter.drawSprite2dMode(static_cast<float>(pos[0])-size*0.5f, static_cast<float>(pos[1])+size*0.5f, 10, 0);
730 		sPainter.drawSprite2dMode(static_cast<float>(pos[0])+size*0.5f, static_cast<float>(pos[1])+size*0.5f, 10, -90);
731 		sPainter.drawSprite2dMode(static_cast<float>(pos[0])+size*0.5f, static_cast<float>(pos[1])-size*0.5f, 10, -180);
732 	}
733 }
734 
735 // Search by name
search(const QString & name)736 NebulaP NebulaMgr::search(const QString& name)
737 {
738 	QString uname = name.toUpper();
739 
740 	for (const auto& n : dsoArray)
741 	{
742 		QString testName = n->getEnglishName().toUpper();
743 		if (testName==uname) return n;
744 	}
745 
746 	return searchByDesignation(uname);
747 }
748 
loadNebulaSet(const QString & setName)749 void NebulaMgr::loadNebulaSet(const QString& setName)
750 {
751 	QString srcCatalogPath		= StelFileMgr::findFile("nebulae/" + setName + "/catalog.txt");
752 	QString dsoCatalogPath	= StelFileMgr::findFile("nebulae/" + setName + "/catalog-" + StellariumDSOCatalogVersion + ".dat");
753 	if (dsoCatalogPath.isEmpty()) // Extended edition is not exist, let's try find standard edition
754 		dsoCatalogPath		= StelFileMgr::findFile("nebulae/" + setName + "/catalog.dat");
755 	QString dsoOutlinesPath	= StelFileMgr::findFile("nebulae/" + setName + "/outlines.dat");
756 
757 	dsoArray.clear();
758 	dsoIndex.clear();
759 	nebGrid.clear();
760 
761 	if (flagConverter)
762 	{
763 		if (!srcCatalogPath.isEmpty())
764 			convertDSOCatalog(srcCatalogPath, StelFileMgr::findFile("nebulae/" + setName + "/catalog.pack", StelFileMgr::New), flagDecimalCoordinates);
765 		else
766 			qWarning() << "ERROR convert catalogue, because source data set does not exist for " << setName;
767 	}
768 
769 	if (dsoCatalogPath.isEmpty())
770 	{
771 		qWarning() << "ERROR while loading deep-sky catalog data set " << setName;
772 		return;
773 	}
774 
775 	loadDSOCatalog(dsoCatalogPath);
776 
777 	if (!dsoOutlinesPath.isEmpty())
778 		loadDSOOutlines(dsoOutlinesPath);
779 }
780 
781 // Look for a nebula by XYZ coords
search(const Vec3d & apos)782 NebulaP NebulaMgr::search(const Vec3d& apos)
783 {
784 	Vec3d pos(apos);
785 	pos.normalize();
786 	NebulaP plusProche;
787 	double anglePlusProche=0.0;
788 	for (const auto& n : dsoArray)
789 	{
790 		if (n->XYZ*pos>anglePlusProche)
791 		{
792 			anglePlusProche=n->XYZ*pos;
793 			plusProche=n;
794 		}
795 	}
796 	if (anglePlusProche>0.999) // object within ~2.5 degrees
797 	{
798 		return plusProche;
799 	}
800 	else return NebulaP();
801 }
802 
803 
searchAround(const Vec3d & av,double limitFov,const StelCore *) const804 QList<StelObjectP> NebulaMgr::searchAround(const Vec3d& av, double limitFov, const StelCore*) const
805 {
806 	QList<StelObjectP> result;
807 	if (!getFlagShow())
808 		return result;
809 
810 	Vec3d v(av);
811 	v.normalize();
812 	const double cosLimFov = cos(limitFov * M_PI/180.);
813 	Vec3d equPos;
814 	for (const auto& n : dsoArray)
815 	{
816 		equPos = n->XYZ;
817 		equPos.normalize();
818 		if (equPos*v >= cosLimFov)
819 		{
820 			result.push_back(qSharedPointerCast<StelObject>(n));
821 		}
822 	}
823 	return result;
824 }
825 
searchDSO(unsigned int DSO) const826 NebulaP NebulaMgr::searchDSO(unsigned int DSO) const
827 {
828 	if (dsoIndex.contains(DSO))
829 		return dsoIndex[DSO];
830 	return NebulaP();
831 }
832 
833 
searchM(unsigned int M) const834 NebulaP NebulaMgr::searchM(unsigned int M) const
835 {
836 	for (const auto& n : dsoArray)
837 		if (n->M_nb == M)
838 			return n;
839 	return NebulaP();
840 }
841 
searchNGC(unsigned int NGC) const842 NebulaP NebulaMgr::searchNGC(unsigned int NGC) const
843 {
844 	for (const auto& n : dsoArray)
845 		if (n->NGC_nb == NGC)
846 			return n;
847 	return NebulaP();
848 }
849 
searchIC(unsigned int IC) const850 NebulaP NebulaMgr::searchIC(unsigned int IC) const
851 {
852 	for (const auto& n : dsoArray)
853 		if (n->IC_nb == IC)
854 			return n;
855 	return NebulaP();
856 }
857 
searchC(unsigned int C) const858 NebulaP NebulaMgr::searchC(unsigned int C) const
859 {
860 	for (const auto& n : dsoArray)
861 		if (n->C_nb == C)
862 			return n;
863 	return NebulaP();
864 }
865 
searchB(unsigned int B) const866 NebulaP NebulaMgr::searchB(unsigned int B) const
867 {
868 	for (const auto& n : dsoArray)
869 		if (n->B_nb == B)
870 			return n;
871 	return NebulaP();
872 }
873 
searchSh2(unsigned int Sh2) const874 NebulaP NebulaMgr::searchSh2(unsigned int Sh2) const
875 {
876 	for (const auto& n : dsoArray)
877 		if (n->Sh2_nb == Sh2)
878 			return n;
879 	return NebulaP();
880 }
881 
searchVdB(unsigned int VdB) const882 NebulaP NebulaMgr::searchVdB(unsigned int VdB) const
883 {
884 	for (const auto& n : dsoArray)
885 		if (n->VdB_nb == VdB)
886 			return n;
887 	return NebulaP();
888 }
889 
searchVdBHa(unsigned int VdBHa) const890 NebulaP NebulaMgr::searchVdBHa(unsigned int VdBHa) const
891 {
892 	for (const auto& n : dsoArray)
893 		if (n->VdBHa_nb == VdBHa)
894 			return n;
895 	return NebulaP();
896 }
897 
searchRCW(unsigned int RCW) const898 NebulaP NebulaMgr::searchRCW(unsigned int RCW) const
899 {
900 	for (const auto& n : dsoArray)
901 		if (n->RCW_nb == RCW)
902 			return n;
903 	return NebulaP();
904 }
905 
searchLDN(unsigned int LDN) const906 NebulaP NebulaMgr::searchLDN(unsigned int LDN) const
907 {
908 	for (const auto& n : dsoArray)
909 		if (n->LDN_nb == LDN)
910 			return n;
911 	return NebulaP();
912 }
913 
searchLBN(unsigned int LBN) const914 NebulaP NebulaMgr::searchLBN(unsigned int LBN) const
915 {
916 	for (const auto& n : dsoArray)
917 		if (n->LBN_nb == LBN)
918 			return n;
919 	return NebulaP();
920 }
921 
searchCr(unsigned int Cr) const922 NebulaP NebulaMgr::searchCr(unsigned int Cr) const
923 {
924 	for (const auto& n : dsoArray)
925 		if (n->Cr_nb == Cr)
926 			return n;
927 	return NebulaP();
928 }
929 
searchMel(unsigned int Mel) const930 NebulaP NebulaMgr::searchMel(unsigned int Mel) const
931 {
932 	for (const auto& n : dsoArray)
933 		if (n->Mel_nb == Mel)
934 			return n;
935 	return NebulaP();
936 }
937 
searchPGC(unsigned int PGC) const938 NebulaP NebulaMgr::searchPGC(unsigned int PGC) const
939 {
940 	for (const auto& n : dsoArray)
941 		if (n->PGC_nb == PGC)
942 			return n;
943 	return NebulaP();
944 }
945 
searchUGC(unsigned int UGC) const946 NebulaP NebulaMgr::searchUGC(unsigned int UGC) const
947 {
948 	for (const auto& n : dsoArray)
949 		if (n->UGC_nb == UGC)
950 			return n;
951 	return NebulaP();
952 }
953 
searchCed(QString Ced) const954 NebulaP NebulaMgr::searchCed(QString Ced) const
955 {
956 	for (const auto& n : dsoArray)
957 		if (n->Ced_nb.trimmed().toUpper() == Ced.trimmed().toUpper())
958 			return n;
959 	return NebulaP();
960 }
961 
searchArp(unsigned int Arp) const962 NebulaP NebulaMgr::searchArp(unsigned int Arp) const
963 {
964 	for (const auto& n : dsoArray)
965 		if (n->Arp_nb == Arp)
966 			return n;
967 	return NebulaP();
968 }
969 
searchVV(unsigned int VV) const970 NebulaP NebulaMgr::searchVV(unsigned int VV) const
971 {
972 	for (const auto& n : dsoArray)
973 		if (n->VV_nb == VV)
974 			return n;
975 	return NebulaP();
976 }
977 
searchPK(QString PK) const978 NebulaP NebulaMgr::searchPK(QString PK) const
979 {
980 	for (const auto& n : dsoArray)
981 		if (n->PK_nb.trimmed().toUpper() == PK.trimmed().toUpper())
982 			return n;
983 	return NebulaP();
984 }
985 
searchPNG(QString PNG) const986 NebulaP NebulaMgr::searchPNG(QString PNG) const
987 {
988 	for (const auto& n : dsoArray)
989 		if (n->PNG_nb.trimmed().toUpper() == PNG.trimmed().toUpper())
990 			return n;
991 	return NebulaP();
992 }
993 
searchSNRG(QString SNRG) const994 NebulaP NebulaMgr::searchSNRG(QString SNRG) const
995 {
996 	for (const auto& n : dsoArray)
997 		if (n->SNRG_nb.trimmed().toUpper() == SNRG.trimmed().toUpper())
998 			return n;
999 	return NebulaP();
1000 }
1001 
searchACO(QString ACO) const1002 NebulaP NebulaMgr::searchACO(QString ACO) const
1003 {
1004 	for (const auto& n : dsoArray)
1005 		if (n->ACO_nb.trimmed().toUpper() == ACO.trimmed().toUpper())
1006 			return n;
1007 	return NebulaP();
1008 }
1009 
searchHCG(QString HCG) const1010 NebulaP NebulaMgr::searchHCG(QString HCG) const
1011 {
1012 	for (const auto& n : dsoArray)
1013 		if (n->HCG_nb.trimmed().toUpper() == HCG.trimmed().toUpper())
1014 			return n;
1015 	return NebulaP();
1016 }
1017 
searchESO(QString ESO) const1018 NebulaP NebulaMgr::searchESO(QString ESO) const
1019 {
1020 	for (const auto& n : dsoArray)
1021 		if (n->ESO_nb.trimmed().toUpper() == ESO.trimmed().toUpper())
1022 			return n;
1023 	return NebulaP();
1024 }
1025 
searchVdBH(QString VdBH) const1026 NebulaP NebulaMgr::searchVdBH(QString VdBH) const
1027 {
1028 	for (const auto& n : dsoArray)
1029 		if (n->VdBH_nb.trimmed().toUpper() == VdBH.trimmed().toUpper())
1030 			return n;
1031 	return NebulaP();
1032 }
1033 
searchDWB(unsigned int DWB) const1034 NebulaP NebulaMgr::searchDWB(unsigned int DWB) const
1035 {
1036 	for (const auto& n : dsoArray)
1037 		if (n->DWB_nb == DWB)
1038 			return n;
1039 	return NebulaP();
1040 }
1041 
searchTr(unsigned int Tr) const1042 NebulaP NebulaMgr::searchTr(unsigned int Tr) const
1043 {
1044 	for (const auto& n : dsoArray)
1045 		if (n->Tr_nb == Tr)
1046 			return n;
1047 	return NebulaP();
1048 }
1049 
searchSt(unsigned int St) const1050 NebulaP NebulaMgr::searchSt(unsigned int St) const
1051 {
1052 	for (const auto& n : dsoArray)
1053 		if (n->St_nb == St)
1054 			return n;
1055 	return NebulaP();
1056 }
1057 
searchRu(unsigned int Ru) const1058 NebulaP NebulaMgr::searchRu(unsigned int Ru) const
1059 {
1060 	for (const auto& n : dsoArray)
1061 		if (n->Ru_nb == Ru)
1062 			return n;
1063 	return NebulaP();
1064 }
1065 
getLatestSelectedDSODesignation() const1066 QString NebulaMgr::getLatestSelectedDSODesignation() const
1067 {
1068 	QString result = "";
1069 
1070 	const QList<StelObjectP> selected = GETSTELMODULE(StelObjectMgr)->getSelectedObject("Nebula");
1071 	if (!selected.empty())
1072 	{
1073 		for (const auto& n : dsoArray)
1074 			if (n==selected[0])
1075 				result = n->getDSODesignation(); // Get designation for latest selected DSO
1076 	}
1077 
1078 	return result;
1079 }
1080 
getLatestSelectedDSODesignationWIC() const1081 QString NebulaMgr::getLatestSelectedDSODesignationWIC() const
1082 {
1083 	QString result = "";
1084 
1085 	const QList<StelObjectP> selected = GETSTELMODULE(StelObjectMgr)->getSelectedObject("Nebula");
1086 	if (!selected.empty())
1087 	{
1088 		for (const auto& n : dsoArray)
1089 			if (n==selected[0])
1090 				result = n->getDSODesignationWIC(); // Get designation for latest selected DSO
1091 	}
1092 
1093 	return result;
1094 }
1095 
convertDSOCatalog(const QString & in,const QString & out,bool decimal=false)1096 void NebulaMgr::convertDSOCatalog(const QString &in, const QString &out, bool decimal=false)
1097 {
1098 	QFile dsoIn(in);
1099 	if (!dsoIn.open(QIODevice::ReadOnly | QIODevice::Text))
1100 		return;
1101 
1102 	QFile dsoOut(out);
1103 	if (!dsoOut.open(QIODevice::WriteOnly))
1104 	{
1105 		qDebug() << "Error converting DSO data! Cannot open file" << QDir::toNativeSeparators(out);
1106 		return;
1107 	}
1108 
1109 	int totalRecords=0;
1110 	QString record;
1111 	while (!dsoIn.atEnd())
1112 	{
1113 		dsoIn.readLine();
1114 		++totalRecords;
1115 	}
1116 
1117 	// rewind the file to the start
1118 	dsoIn.seek(0);
1119 
1120 	QDataStream dsoOutStream(&dsoOut);
1121 	dsoOutStream.setVersion(QDataStream::Qt_5_2);
1122 
1123 	int	id, orientationAngle, NGC, IC, M, C, B, Sh2, VdB, RCW, LDN, LBN, Cr, Mel, PGC, UGC, Arp, VV, DWB, Tr, St, Ru, VdBHa;
1124 	float	raRad, decRad, bMag, vMag, majorAxisSize, minorAxisSize, dist, distErr, z, zErr, plx, plxErr;
1125 	QString oType, mType, Ced, PK, PNG, SNRG, ACO, HCG, ESO, VdBH, ra, dec;
1126 	Nebula::NebulaType nType;
1127 
1128 	int readOk = 0;				// how many records weree rad without problems
1129 	while (!dsoIn.atEnd())
1130 	{
1131 		record = QString::fromUtf8(dsoIn.readLine());
1132 
1133 		QRegularExpression version("ersion\\s+([\\d\\.]+)\\s+(\\w+)");
1134 		QRegularExpressionMatch versionMatch;
1135 		int vp = record.indexOf(version, 0, &versionMatch);
1136 		if (vp!=-1) // Version of catalog, a first line!
1137 			dsoOutStream << versionMatch.captured(1).trimmed() << versionMatch.captured(2).trimmed();
1138 
1139 		// skip comments
1140 		if (record.startsWith("//") || record.startsWith("#"))
1141 		{
1142 			--totalRecords;
1143 			continue;
1144 		}
1145 
1146 		if (!record.isEmpty())
1147 		{
1148 			#if (QT_VERSION>=QT_VERSION_CHECK(5, 14, 0))
1149 			QStringList list=record.split("\t", Qt::KeepEmptyParts);
1150 			#else
1151 			QStringList list=record.split("\t", QString::KeepEmptyParts);
1152 			#endif
1153 
1154 			id				= list.at(0).toInt();		// ID (inner identification number)
1155 			ra				= list.at(1).trimmed();
1156 			dec				= list.at(2).trimmed();
1157 			bMag			= list.at(3).toFloat();		// B magnitude
1158 			vMag			= list.at(4).toFloat();		// V magnitude
1159 			oType			= list.at(5).trimmed();		// Object type
1160 			mType			= list.at(6).trimmed();		// Morphological type of object
1161 			majorAxisSize		= list.at(7).toFloat();		// major axis size (arcmin)
1162 			minorAxisSize		= list.at(8).toFloat();		// minor axis size (arcmin)
1163 			orientationAngle	= list.at(9).toInt();		// orientation angle (degrees)
1164 			z				= list.at(10).toFloat();	// redshift
1165 			zErr				= list.at(11).toFloat();	// error of redshift
1166 			plx				= list.at(12).toFloat();	// parallax (mas)
1167 			plxErr			= list.at(13).toFloat();	// error of parallax (mas)
1168 			dist				= list.at(14).toFloat();	// distance (Mpc for galaxies, kpc for other objects)
1169 			distErr			= list.at(15).toFloat();	// distance error (Mpc for galaxies, kpc for other objects)
1170 			// -----------------------------------------------
1171 			// cross-identification data
1172 			// -----------------------------------------------
1173 			NGC				= list.at(16).toInt();		// NGC number
1174 			IC				= list.at(17).toInt();		// IC number
1175 			M				= list.at(18).toInt();		// M number
1176 			C				= list.at(19).toInt();		// C number
1177 			B				= list.at(20).toInt();		// B number
1178 			Sh2				= list.at(21).toInt();		// Sh2 number
1179 			VdB				= list.at(22).toInt();		// VdB number
1180 			RCW				= list.at(23).toInt();		// RCW number
1181 			LDN				= list.at(24).toInt();		// LDN number
1182 			LBN				= list.at(25).toInt();		// LBN number
1183 			Cr				= list.at(26).toInt();		// Cr number (alias: Col)
1184 			Mel				= list.at(27).toInt();		// Mel number
1185 			PGC				= list.at(28).toInt();		// PGC number (subset)
1186 			UGC				= list.at(29).toInt();		// UGC number (subset)
1187 			Ced				= list.at(30).trimmed();	// Ced number
1188 			Arp				= list.at(31).toInt();		// Arp number
1189 			VV				= list.at(32).toInt();		// VV number
1190 			PK				= list.at(33).trimmed();	// PK number
1191 			PNG				= list.at(34).trimmed();	// PN G number
1192 			SNRG			= list.at(35).trimmed();	// SNR G number
1193 			ACO				= list.at(36).trimmed();	// ACO number
1194 			HCG				= list.at(37).trimmed();	// HCG number
1195 			ESO				= list.at(38).trimmed();	// ESO number
1196 			VdBH			= list.at(39).trimmed();	// VdBH number
1197 			DWB				= list.at(40).toInt();		// DWB number
1198 			Tr				= list.at(41).toInt();		// Tr number
1199 			St				= list.at(42).toInt();		// St number
1200 			Ru				= list.at(43).toInt();		// Ru number
1201 			VdBHa			= list.at(44).toInt();		// VdB-Ha number
1202 
1203 			if (decimal)
1204 			{
1205 				// Convert from deg to rad
1206 				raRad	= ra.toFloat() *M_PI_180f;
1207 				decRad	= dec.toFloat()*M_PI_180f;
1208 			}
1209 			else
1210 			{
1211 				QStringList raLst;
1212 				if (ra.contains(":"))
1213 					raLst	= ra.split(":");
1214 				else
1215 					raLst	= ra.split(" ");
1216 
1217 				QStringList decLst;
1218 				if (dec.contains(":"))
1219 					decLst = dec.split(":");
1220 				else
1221 					decLst = dec.split(" ");
1222 
1223 				raRad	= raLst.at(0).toFloat() + raLst.at(1).toFloat()/60.f + raLst.at(2).toFloat()/3600.f;
1224 				decRad	= qAbs(decLst.at(0).toFloat()) + decLst.at(1).toFloat()/60.f + decLst.at(2).toFloat()/3600.f;
1225 				if (dec.startsWith("-")) decRad *= -1.f;
1226 
1227 				raRad  *= M_PIf/12.f;	// Convert from hours to rad
1228 				decRad *= M_PIf/180.f;    // Convert from deg to rad
1229 			}
1230 
1231 			majorAxisSize /= 60.f;	// Convert from arcmin to degrees
1232 			minorAxisSize /= 60.f;	// Convert from arcmin to degrees
1233 
1234 			// Warning: Hyades and LMC has visual magnitude less than 1.0 (0.5^m and 0.9^m)
1235 			if (bMag <= 0.f) bMag = 99.f;
1236 			if (vMag <= 0.f) vMag = 99.f;
1237 			// TODO: The map could be sorted by probability (number of total objects). More common objects at start...
1238 			static const QMap<QString, Nebula::NebulaType> oTypesMap = {
1239 				{ "G"   , Nebula::NebGx  },
1240 				{ "GX"  , Nebula::NebGx  },
1241 				{ "GC"  , Nebula::NebGc  },
1242 				{ "OC"  , Nebula::NebOc  },
1243 				{ "NB"  , Nebula::NebN   },
1244 				{ "PN"  , Nebula::NebPn  },
1245 				{ "DN"  , Nebula::NebDn  },
1246 				{ "RN"  , Nebula::NebRn  },
1247 				{ "C+N" , Nebula::NebCn  },
1248 				{ "RNE" , Nebula::NebRn  },
1249 				{ "HII" , Nebula::NebHII },
1250 				{ "SNR" , Nebula::NebSNR },
1251 				{ "BN"  , Nebula::NebBn  },
1252 				{ "EN"  , Nebula::NebEn  },
1253 				{ "SA"  , Nebula::NebSA  },
1254 				{ "SC"  , Nebula::NebSC  },
1255 				{ "CL"  , Nebula::NebCl  },
1256 				{ "IG"  , Nebula::NebIGx },
1257 				{ "RG"  , Nebula::NebRGx },
1258 				{ "AGX" , Nebula::NebAGx },
1259 				{ "QSO" , Nebula::NebQSO },
1260 				{ "ISM" , Nebula::NebISM },
1261 				{ "EMO" , Nebula::NebEMO },
1262 				{ "GNE" , Nebula::NebHII },
1263 				{ "RAD" , Nebula::NebISM },
1264 				{ "LIN" , Nebula::NebAGx },// LINER-type active galaxies
1265 				{ "BLL" , Nebula::NebBLL },
1266 				{ "BLA" , Nebula::NebBLA },
1267 				{ "MOC" , Nebula::NebMolCld },
1268 				{ "YSO" , Nebula::NebYSO },
1269 				{ "Q?"  , Nebula::NebPossQSO },
1270 				{ "PN?" , Nebula::NebPossPN },
1271 				{ "*"   , Nebula::NebStar},
1272 				{ "SFR" , Nebula::NebMolCld },
1273 				{ "IR"  , Nebula::NebDn  },
1274 				{ "**"  , Nebula::NebStar},
1275 				{ "MUL" , Nebula::NebStar},
1276 				{ "PPN" , Nebula::NebPPN },
1277 				{ "GIG" , Nebula::NebIGx },
1278 				{ "OPC" , Nebula::NebOc  },
1279 				{ "MGR" , Nebula::NebSA  },
1280 				{ "IG2" , Nebula::NebIGx },
1281 				{ "IG3" , Nebula::NebIGx },
1282 				{ "SY*" , Nebula::NebSymbioticStar},
1283 				{ "PA*" , Nebula::NebPPN },
1284 				{ "CV*" , Nebula::NebStar},
1285 				{ "Y*?" , Nebula::NebYSO },
1286 				{ "CGB" , Nebula::NebISM },
1287 				{ "SNRG", Nebula::NebSNR },
1288 				{ "Y*O" , Nebula::NebYSO },
1289 				{ "SR*" , Nebula::NebStar},
1290 				{ "EM*" , Nebula::NebEmissionLineStar },
1291 				{ "AB*" , Nebula::NebStar },
1292 				{ "MI*" , Nebula::NebStar },
1293 				{ "MI?" , Nebula::NebStar },
1294 				{ "TT*" , Nebula::NebStar },
1295 				{ "WR*" , Nebula::NebStar },
1296 				{ "C*"  , Nebula::NebEmissionLineStar },
1297 				{ "WD*" , Nebula::NebStar },
1298 				{ "EL*" , Nebula::NebStar },
1299 				{ "NL*" , Nebula::NebStar },
1300 				{ "NO*" , Nebula::NebStar },
1301 				{ "HS*" , Nebula::NebStar },
1302 				{ "LP*" , Nebula::NebStar },
1303 				{ "OH*" , Nebula::NebStar },
1304 				{ "S?R" , Nebula::NebStar },
1305 				{ "IR*" , Nebula::NebStar },
1306 				{ "POC" , Nebula::NebMolCld },
1307 				{ "PNB" , Nebula::NebPn   },
1308 				{ "GXCL", Nebula::NebGxCl },
1309 				{ "AL*" , Nebula::NebStar },
1310 				{ "PR*" , Nebula::NebStar },
1311 				{ "RS*" , Nebula::NebStar },
1312 				{ "S*B" , Nebula::NebStar },
1313 				{ "SN?" , Nebula::NebSNC  },
1314 				{ "SR?" , Nebula::NebSNRC },
1315 				{ "DNE" , Nebula::NebDn   },
1316 				{ "RG*" , Nebula::NebStar },
1317 				{ "PSR" , Nebula::NebSNR  },
1318 				{ "HH"  , Nebula::NebISM  },
1319 				{ "V*"  , Nebula::NebStar },
1320 				{ "*IN"  , Nebula::NebCn },
1321 				{ "SN*"  , Nebula::NebStar },
1322 				{ "PA?" , Nebula::NebPPN  },
1323 				{ "BUB" , Nebula::NebISM  },
1324 				{ "CLG" , Nebula::NebGxCl },
1325 				{ "POG" , Nebula::NebPartOfGx },
1326 				{ "CGG" , Nebula::NebGxCl },
1327 				{ "SCG" , Nebula::NebGxCl },
1328 				{ "REG" , Nebula::NebRegion },
1329 				{ "?" , Nebula::NebUnknown }
1330 			};
1331 
1332 			nType=oTypesMap.value(oType.toUpper(), Nebula::NebUnknown);
1333 			if (nType == Nebula::NebUnknown)
1334 				qDebug() << "Record with ID" << id <<"has unknown type of object:" << oType;
1335 
1336 			++readOk;
1337 
1338 			dsoOutStream << id << raRad << decRad << bMag << vMag << static_cast<unsigned int>(nType) << mType << majorAxisSize << minorAxisSize
1339 				     << orientationAngle << z << zErr << plx << plxErr << dist  << distErr << NGC << IC << M << C
1340 				     << B << Sh2 << VdB << RCW  << LDN << LBN << Cr << Mel << PGC << UGC << Ced << Arp << VV << PK
1341 				     << PNG << SNRG << ACO << HCG << ESO << VdBH << DWB << Tr << St << Ru << VdBHa;
1342 		}
1343 	}
1344 	dsoIn.close();
1345 	dsoOut.flush();
1346 	dsoOut.close();
1347 	qDebug() << "Converted" << readOk << "/" << totalRecords << "DSO records";
1348 	qDebug() << "[...] Please use 'gzip -nc catalog.pack > catalog.dat' to pack the catalog.";
1349 }
1350 
loadDSOCatalog(const QString & filename)1351 bool NebulaMgr::loadDSOCatalog(const QString &filename)
1352 {
1353 	QFile in(filename);
1354 	if (!in.open(QIODevice::ReadOnly))
1355 		return false;
1356 
1357 	qDebug() << "Loading DSO data ...";
1358 
1359 	// Let's begin use gzipped data
1360 	QDataStream ins(StelUtils::uncompress(in.readAll()));
1361 	ins.setVersion(QDataStream::Qt_5_2);
1362 
1363 	QString version = "", edition= "";
1364 	int totalRecords=0;
1365 	while (!ins.atEnd())
1366 	{
1367 		if (totalRecords==0) // Read the version of catalog
1368 		{
1369 			ins >> version >> edition;
1370 			if (version.isEmpty())
1371 				version = "3.1"; // The first version of extended edition of the catalog
1372 			if (edition.isEmpty())
1373 				edition = "unknown";
1374 			qDebug() << "[...]" << QString("Stellarium DSO Catalog, version %1 (%2 edition)").arg(version).arg(edition);
1375 			if (StelUtils::compareVersions(version, StellariumDSOCatalogVersion)!=0)
1376 			{
1377 				++totalRecords;
1378 				qDebug() << "WARNING: Mismatch of DSO catalog version (" << version << ")! The expected version is" << StellariumDSOCatalogVersion;
1379 				qDebug() << "         See section 5.5 of the User Guide and install the right version of the catalog!";
1380 				QMessageBox::warning(Q_NULLPTR, q_("Attention!"), QString("%1. %2: %3 - %4: %5. %6").arg(q_("DSO catalog version mismatch"),  q_("Found"), version, q_("Expected"), StellariumDSOCatalogVersion, q_("See Logfile for instructions.")), QMessageBox::Ok);
1381 				break;
1382 			}
1383 		}
1384 		else
1385 		{
1386 			// Create a new Nebula record
1387 			NebulaP e = NebulaP(new Nebula);
1388 			e->readDSO(ins);
1389 
1390 			dsoArray.append(e);
1391 			nebGrid.insert(qSharedPointerCast<StelRegionObject>(e));
1392 			if (e->DSO_nb!=0)
1393 				dsoIndex.insert(e->DSO_nb, e);
1394 		}
1395 		++totalRecords;
1396 	}
1397 	in.close();
1398 	qDebug() << "Loaded" << --totalRecords << "DSO records";
1399 	return true;
1400 }
1401 
loadDSONames(const QString & filename)1402 bool NebulaMgr::loadDSONames(const QString &filename)
1403 {
1404 	qDebug() << "Loading DSO name data ...";
1405 	QFile dsoNameFile(filename);
1406 	if (!dsoNameFile.open(QIODevice::ReadOnly | QIODevice::Text))
1407 	{
1408 		qWarning() << "DSO name data file" << QDir::toNativeSeparators(filename) << "not found.";
1409 		return false;
1410 	}
1411 
1412 	// Read the names of the deep-sky objects
1413 	QString name, record, ref, cdes;
1414 	QStringList nodata;
1415 	nodata.clear();
1416 	int totalRecords=0;
1417 	int lineNumber=0;
1418 	int readOk=0;
1419 	unsigned int nb;
1420 	NebulaP e;
1421 	QRegularExpression commentRx("^(\\s*#.*|\\s*)$");
1422 	while (!dsoNameFile.atEnd())
1423 	{
1424 		record = QString::fromUtf8(dsoNameFile.readLine());
1425 		lineNumber++;
1426 		if (commentRx.match(record).hasMatch())
1427 			continue;
1428 
1429 		totalRecords++;
1430 
1431 		// bytes 1 - 5, designator for catalogue (prefix)
1432 		ref  = record.left(5).trimmed();
1433 		// bytes 6 -20, identificator for object in the catalog
1434 		cdes = record.mid(5, 15).trimmed().toUpper();
1435 		// bytes 21-80, proper name of the object (translatable)
1436 		name = record.mid(21).trimmed(); // Let gets the name with trimmed whitespaces
1437 
1438 		nb = cdes.toUInt();
1439 
1440 		static const QStringList catalogs = {
1441 			"IC",    "M",   "C",  "CR",  "MEL",   "B", "SH2", "VDB", "RCW",  "LDN",
1442 			"LBN", "NGC", "PGC", "UGC",  "CED", "ARP",  "VV",  "PK", "PNG", "SNRG",
1443 			"ACO", "HCG", "ESO", "VDBH", "DWB", "TR", "ST", "RU", "DBHA"};
1444 
1445 		switch (catalogs.indexOf(ref.toUpper()))
1446 		{
1447 			case 0:
1448 				e = searchIC(nb);
1449 				break;
1450 			case 1:
1451 				e = searchM(nb);
1452 				break;
1453 			case 2:
1454 				e = searchC(nb);
1455 				break;
1456 			case 3:
1457 				e = searchCr(nb);
1458 				break;
1459 			case 4:
1460 				e = searchMel(nb);
1461 				break;
1462 			case 5:
1463 				e = searchB(nb);
1464 				break;
1465 			case 6:
1466 				e = searchSh2(nb);
1467 				break;
1468 			case 7:
1469 				e = searchVdB(nb);
1470 				break;
1471 			case 8:
1472 				e = searchRCW(nb);
1473 				break;
1474 			case 9:
1475 				e = searchLDN(nb);
1476 				break;
1477 			case 10:
1478 				e = searchLBN(nb);
1479 				break;
1480 			case 11:
1481 				e = searchNGC(nb);
1482 				break;
1483 			case 12:
1484 				e = searchPGC(nb);
1485 				break;
1486 			case 13:
1487 				e = searchUGC(nb);
1488 				break;
1489 			case 14:
1490 				e = searchCed(cdes);
1491 				break;
1492 			case 15:
1493 				e = searchArp(nb);
1494 				break;
1495 			case 16:
1496 				e = searchVV(nb);
1497 				break;
1498 			case 17:
1499 				e = searchPK(cdes);
1500 				break;
1501 			case 18:
1502 				e = searchPNG(cdes);
1503 				break;
1504 			case 19:
1505 				e = searchSNRG(cdes);
1506 				break;
1507 			case 20:
1508 				e = searchACO(cdes);
1509 				break;
1510 			case 21:
1511 				e = searchHCG(cdes);
1512 				break;
1513 			case 22:
1514 				e = searchESO(cdes);
1515 				break;
1516 			case 23:
1517 				e = searchVdBH(cdes);
1518 				break;
1519 			case 24:
1520 				e = searchDWB(nb);
1521 				break;
1522 			case 25:
1523 				e = searchTr(nb);
1524 				break;
1525 			case 26:
1526 				e = searchSt(nb);
1527 				break;
1528 			case 27:
1529 				e = searchRu(nb);
1530 				break;
1531 			case 28:
1532 				e = searchVdBHa(nb);
1533 				break;
1534 			default:
1535 				e = searchDSO(nb);
1536 				break;
1537 		}
1538 
1539 		if (!e.isNull())
1540 		{
1541 			QRegularExpression transRx("_[(]\"(.*)\"[)](\\s*#.*)?"); // optional comments after name.
1542 			QRegularExpressionMatch transMatch=transRx.match(name);
1543 			if (transMatch.hasMatch())
1544 			{
1545 				QString propName = transMatch.captured(1).trimmed();
1546 				QString currName = e->getEnglishName();
1547 				if (currName.isEmpty())
1548 					e->setProperName(propName);
1549 				else if (currName!=propName)
1550 					e->addNameAlias(propName);
1551 			}
1552 			readOk++;
1553 		}
1554 		else
1555 			nodata.append(QString("%1 %2").arg(ref.trimmed(), cdes.trimmed()));
1556 	}
1557 	dsoNameFile.close();
1558 	qDebug() << "Loaded" << readOk << "/" << totalRecords << "DSO name records successfully";
1559 
1560 	int err = nodata.size();
1561 	if (err>0)
1562 		qDebug() << "WARNING - No position data for" << err << "objects:" << nodata.join(", ");
1563 
1564 	return true;
1565 }
1566 
loadDSOOutlines(const QString & filename)1567 bool NebulaMgr::loadDSOOutlines(const QString &filename)
1568 {
1569 	qDebug() << "Loading DSO outline data ...";
1570 	QFile dsoOutlineFile(filename);
1571 	if (!dsoOutlineFile.open(QIODevice::ReadOnly | QIODevice::Text))
1572 	{
1573 		qWarning() << "DSO outline data file" << QDir::toNativeSeparators(filename) << "not found.";
1574 		return false;
1575 	}
1576 
1577 	double RA, DE;
1578 	int i, readOk = 0;
1579 	Vec3d XYZ;
1580 	std::vector<Vec3d> *points = Q_NULLPTR;
1581 	typedef QPair<double, double> coords;
1582 	coords point, fpoint;
1583 	QList<coords> outline;
1584 	QString record, command, dso;
1585 	NebulaP e;
1586 	// Read the outlines data of the DSO
1587 	QRegularExpression commentRx("^(\\s*#.*|\\s*)$");
1588 	while (!dsoOutlineFile.atEnd())
1589 	{
1590 		record = QString::fromUtf8(dsoOutlineFile.readLine());
1591 		if (commentRx.match(record).hasMatch())
1592 			continue;
1593 
1594 		// bytes 1 - 8, RA
1595 		RA = record.left(8).toDouble();
1596 		// bytes 9 -18, DE
1597 		DE = record.mid(9, 10).toDouble();
1598 		// bytes 19-25, command
1599 		command = record.mid(19, 7).trimmed();
1600 		// bytes 26, designation of DSO
1601 		dso = record.mid(26).trimmed();
1602 
1603 		RA*=M_PI/12.;     // Convert from hours to rad
1604 		DE*=M_PI/180.;    // Convert from deg to rad
1605 
1606 		if (command.contains("start", Qt::CaseInsensitive))
1607 		{
1608 			outline.clear();
1609 			e = search(dso);
1610 			if (e.isNull()) // maybe this is inner number of DSO
1611 				e = searchDSO(dso.toUInt());
1612 
1613 			point.first  = RA;
1614 			point.second = DE;
1615 			outline.append(point);
1616 			fpoint = point;
1617 		}
1618 
1619 		if (command.contains("vertex", Qt::CaseInsensitive))
1620 		{
1621 			point.first  = RA;
1622 			point.second = DE;
1623 			outline.append(point);
1624 		}
1625 
1626 		if (command.contains("end", Qt::CaseInsensitive))
1627 		{
1628 			point.first  = RA;
1629 			point.second = DE;
1630 			outline.append(point);
1631 			outline.append(fpoint);
1632 
1633 			if (!e.isNull())
1634 			{
1635 				points = new std::vector<Vec3d>;
1636 				for (i = 0; i < outline.size(); i++)
1637 				{
1638 					// Calc the Cartesian coord with RA and DE
1639 					point = outline.at(i);
1640 					StelUtils::spheToRect(point.first, point.second, XYZ);
1641 					points->push_back(XYZ);
1642 				}
1643 
1644 				e->outlineSegments.push_back(points);
1645 			}
1646 			readOk++;
1647 		}
1648 	}
1649 	dsoOutlineFile.close();
1650 	qDebug() << "Loaded" << readOk << "DSO outline records successfully";
1651 	return true;
1652 }
1653 
updateSkyCulture(const QString & skyCultureDir)1654 void NebulaMgr::updateSkyCulture(const QString& skyCultureDir)
1655 {
1656 	QString namesFile = StelFileMgr::findFile("skycultures/" + skyCultureDir + "/dso_names.fab");
1657 
1658 	for (const auto& n : qAsConst(dsoArray))
1659 		n->removeAllNames();
1660 
1661 	if (namesFile.isEmpty())
1662 	{
1663 		QString setName = "default";
1664 		QString dsoNamesPath = StelFileMgr::findFile("nebulae/" + setName + "/names.dat");
1665 		if (dsoNamesPath.isEmpty())
1666 		{
1667 			qWarning() << "ERROR while loading deep-sky names data set " << setName;
1668 			return;
1669 		}
1670 		loadDSONames(dsoNamesPath);
1671 	}
1672 	else
1673 	{
1674 		// Open file
1675 		QFile dsoNamesFile(namesFile);
1676 		if (!dsoNamesFile.open(QIODevice::ReadOnly | QIODevice::Text))
1677 		{
1678 			qDebug() << "Cannot open file" << QDir::toNativeSeparators(namesFile);
1679 			return;
1680 		}
1681 
1682 		// Now parse the file
1683 		// lines to ignore which start with a # or are empty
1684 		QRegularExpression commentRx("^(\\s*#.*|\\s*)$");
1685 
1686 		// lines which look like records - we use the RE to extract the fields
1687 		// which will be available in recMatch.capturedTexts()
1688 		QRegularExpression recRx("^\\s*([\\w\\s]+)\\s*\\|[_]*[(]\"(.*)\"[)]\\s*([\\,\\d\\s]*)\\n");
1689 
1690 		QString record, dsoId, nativeName;
1691 		int totalRecords=0;
1692 		int readOk=0;
1693 		int lineNumber=0;
1694 		while (!dsoNamesFile.atEnd())
1695 		{
1696 			record = QString::fromUtf8(dsoNamesFile.readLine());
1697 			lineNumber++;
1698 
1699 			// Skip comments
1700 			if (commentRx.match(record).hasMatch())
1701 				continue;
1702 
1703 			totalRecords++;
1704 
1705 			QRegularExpressionMatch recMatch=recRx.match(record);
1706 			if (!recMatch.hasMatch())
1707 			{
1708 				qWarning() << "ERROR - cannot parse record at line" << lineNumber << "in native deep-sky object names file" << QDir::toNativeSeparators(namesFile);
1709 			}
1710 			else
1711 			{
1712 				dsoId = recMatch.captured(1).trimmed();
1713 				nativeName = recMatch.captured(2).trimmed(); // Use translatable text
1714 				NebulaP e = search(dsoId);
1715 				QString currentName = e->getEnglishName();
1716 				if (currentName.isEmpty()) // Set native name of DSO
1717 					e->setProperName(nativeName);
1718 				else if (currentName!=nativeName) // Add traditional (well-known?) name of DSO as alias
1719 					e->addNameAlias(nativeName);
1720 				readOk++;
1721 			}
1722 		}
1723 		dsoNamesFile.close();
1724 		qDebug() << "Loaded" << readOk << "/" << totalRecords << "native names of deep-sky objects";
1725 	}
1726 
1727 	updateI18n();
1728 }
1729 
updateI18n()1730 void NebulaMgr::updateI18n()
1731 {
1732 	Nebula::buildTypeStringMap();
1733 	const StelTranslator& trans = StelApp::getInstance().getLocaleMgr().getSkyTranslator();
1734 	for (const auto& n : dsoArray)
1735 		n->translateName(trans);
1736 }
1737 
1738 
1739 //! Return the matching Nebula object's pointer if exists or an "empty" StelObjectP
searchByNameI18n(const QString & nameI18n) const1740 StelObjectP NebulaMgr::searchByNameI18n(const QString& nameI18n) const
1741 {
1742 	QString objw = nameI18n.toUpper();
1743 
1744 	// Search by common names
1745 	for (const auto& n : dsoArray)
1746 	{
1747 		QString objwcap = n->nameI18.toUpper();
1748 		if (objwcap==objw)
1749 			return qSharedPointerCast<StelObject>(n);
1750 	}
1751 
1752 	// Search by aliases of common names
1753 	for (const auto& n : dsoArray)
1754 	{
1755 		for (auto objwcapa : n->nameI18Aliases)
1756 		{
1757 			if (objwcapa.toUpper()==objw)
1758 				return qSharedPointerCast<StelObject>(n);
1759 		}
1760 	}
1761 
1762 	// Search by designation
1763 	NebulaP n = searchByDesignation(objw);
1764 	return qSharedPointerCast<StelObject>(n);
1765 }
1766 
1767 
1768 //! Return the matching Nebula object's pointer if exists or an "empty" StelObjectP
searchByName(const QString & name) const1769 StelObjectP NebulaMgr::searchByName(const QString& name) const
1770 {
1771 	QString objw = name.toUpper();
1772 
1773 	// Search by common names
1774 	for (const auto& n : dsoArray)
1775 	{
1776 		QString objwcap = n->englishName.toUpper();
1777 		if (objwcap==objw)
1778 			return qSharedPointerCast<StelObject>(n);
1779 	}
1780 
1781 	if (getFlagAdditionalNames())
1782 	{
1783 		// Search by aliases of common names
1784 		for (const auto& n : dsoArray)
1785 		{
1786 			for (auto objwcapa : n->englishAliases)
1787 			{
1788 				if (objwcapa.toUpper()==objw)
1789 					return qSharedPointerCast<StelObject>(n);
1790 			}
1791 		}
1792 	}
1793 
1794 	// Search by designation
1795 	NebulaP n = searchByDesignation(objw);
1796 	return qSharedPointerCast<StelObject>(n);
1797 }
1798 
1799 //! Return the matching Nebula object's pointer if exists or Q_NULLPTR
1800 //! TODO Decide whether empty StelObjectP or Q_NULLPTR is the better return type and select the same for both.
searchByDesignation(const QString & designation) const1801 NebulaP NebulaMgr::searchByDesignation(const QString &designation) const
1802 {
1803 	NebulaP n;
1804 	QString uname = designation.toUpper();
1805 	// If no match found, try search by catalog reference
1806 	static QRegularExpression catNumRx("^(M|NGC|IC|C|B|VDB|RCW|LDN|LBN|CR|MEL|PGC|UGC|ARP|VV|DWB|TR|TRUMPLER|ST|STOCK|RU|RUPRECHT|VDB-HA)\\s*(\\d+)$");
1807 	QRegularExpressionMatch catNumMatch=catNumRx.match(uname);
1808 	if (catNumMatch.hasMatch())
1809 	{
1810 		QString cat = catNumMatch.captured(1);
1811 		unsigned int num = catNumMatch.captured(2).toUInt();
1812 		if (cat == "M") n = searchM(num);
1813 		if (cat == "NGC") n = searchNGC(num);
1814 		if (cat == "IC") n = searchIC(num);
1815 		if (cat == "C") n = searchC(num);
1816 		if (cat == "B") n = searchB(num);
1817 		if (cat == "VDB-HA") n = searchVdBHa(num);
1818 		if (cat == "VDB") n = searchVdB(num);
1819 		if (cat == "RCW") n = searchRCW(num);
1820 		if (cat == "LDN") n = searchLDN(num);
1821 		if (cat == "LBN") n = searchLBN(num);
1822 		if (cat == "CR") n = searchCr(num);
1823 		if (cat == "MEL") n = searchMel(num);
1824 		if (cat == "PGC") n = searchPGC(num);
1825 		if (cat == "UGC") n = searchUGC(num);
1826 		if (cat == "ARP") n = searchArp(num);
1827 		if (cat == "VV") n = searchVV(num);
1828 		if (cat == "DWB") n = searchDWB(num);
1829 		if (cat == "TR" || cat == "TRUMPLER") n = searchTr(num);
1830 		if (cat == "ST" || cat == "STOCK") n = searchSt(num);
1831 		if (cat == "RU" || cat == "RUPRECHT") n = searchRu(num);
1832 	}
1833 	static QRegularExpression dCatNumRx("^(SH)\\s*\\d-\\s*(\\d+)$");
1834 	QRegularExpressionMatch dCatNumMatch=dCatNumRx.match(uname);
1835 	if (dCatNumMatch.hasMatch())
1836 	{
1837 		QString dcat = dCatNumMatch.captured(1);
1838 		unsigned int dnum = dCatNumMatch.captured(2).toUInt();
1839 
1840 		if (dcat == "SH") n = searchSh2(dnum);
1841 	}
1842 	static QRegularExpression sCatNumRx("^(CED|PK|ACO|ABELL|HCG|ESO|VDBH)\\s*(.+)$");
1843 	QRegularExpressionMatch sCatNumMatch=sCatNumRx.match(uname);
1844 	if (sCatNumMatch.hasMatch())
1845 	{
1846 		QString cat = sCatNumMatch.captured(1);
1847 		QString num = sCatNumMatch.captured(2).trimmed();
1848 
1849 		if (cat == "CED") n = searchCed(num);
1850 		if (cat == "PK") n = searchPK(num);
1851 		if (cat == "ACO" || cat == "ABELL") n = searchACO(num);
1852 		if (cat == "HCG") n = searchHCG(num);
1853 		if (cat == "ESO") n = searchESO(num);
1854 		if (cat == "VDBH") n = searchVdBH(num);
1855 	}
1856 	static QRegularExpression gCatNumRx("^(PN|SNR)\\s*G(.+)$");
1857 	QRegularExpressionMatch gCatNumMatch=gCatNumRx.match(uname);
1858 	if (gCatNumMatch.hasMatch())
1859 	{
1860 		QString cat = gCatNumMatch.captured(1);
1861 		QString num = gCatNumMatch.captured(2).trimmed();
1862 
1863 		if (cat == "PN") n = searchPNG(num);
1864 		if (cat == "SNR") n = searchSNRG(num);
1865 	}
1866 
1867 	return n;
1868 }
1869 
1870 //! Find and return the list of at most maxNbItem objects auto-completing the passed object name
listMatchingObjects(const QString & objPrefix,int maxNbItem,bool useStartOfWords) const1871 QStringList NebulaMgr::listMatchingObjects(const QString& objPrefix, int maxNbItem, bool useStartOfWords) const
1872 {
1873 	QStringList result;
1874 	if (maxNbItem <= 0)
1875 		return result;
1876 
1877 	QString objw = objPrefix.toUpper();
1878 
1879 	// Search by Messier objects number (possible formats are "M31" or "M 31")
1880 	if (objw.size()>=1 && objw.left(1)=="M" && objw.left(3)!="MEL")
1881 	{
1882 		for (const auto& n : dsoArray)
1883 		{
1884 			if (n->M_nb==0) continue;
1885 			QString constw = QString("M%1").arg(n->M_nb);
1886 			QString constws = constw.mid(0, objw.size());
1887 			if (constws.toUpper()==objw)
1888 			{
1889 				result << constws;
1890 				continue;	// Prevent adding both forms for name
1891 			}
1892 			constw = QString("M %1").arg(n->M_nb);
1893 			constws = constw.mid(0, objw.size());
1894 			if (constws.toUpper()==objw)
1895 				result << constw;
1896 		}
1897 	}
1898 
1899 	// Search by Melotte objects number (possible formats are "Mel31" or "Mel 31")
1900 	if (objw.size()>=1 && objw.left(3)=="MEL")
1901 	{
1902 		for (const auto& n : dsoArray)
1903 		{
1904 			if (n->Mel_nb==0) continue;
1905 			QString constw = QString("Mel%1").arg(n->Mel_nb);
1906 			QString constws = constw.mid(0, objw.size());
1907 			QString constws2 = QString("Melotte%1").arg(n->Mel_nb).mid(0, objw.size());
1908 			if (constws.toUpper()==objw || constws2.toUpper()==objw)
1909 			{
1910 				result << constws;
1911 				continue;	// Prevent adding both forms for name
1912 			}
1913 			constw = QString("Mel %1").arg(n->Mel_nb);
1914 			constws = constw.mid(0, objw.size());
1915 			constws2 = QString("Melotte %1").arg(n->Mel_nb).mid(0, objw.size());
1916 			if (constws.toUpper()==objw || constws2.toUpper()==objw)
1917 				result << constw;
1918 		}
1919 	}
1920 
1921 	// Search by IC objects number (possible formats are "IC466" or "IC 466")
1922 	if (objw.size()>=1 && objw.left(2)=="IC")
1923 	{
1924 		for (const auto& n : dsoArray)
1925 		{
1926 			if (n->IC_nb==0) continue;
1927 			QString constw = QString("IC%1").arg(n->IC_nb);
1928 			QString constws = constw.mid(0, objw.size());
1929 			if (constws.toUpper()==objw)
1930 			{
1931 				result << constws;
1932 				continue;	// Prevent adding both forms for name
1933 			}
1934 			constw = QString("IC %1").arg(n->IC_nb);
1935 			constws = constw.mid(0, objw.size());
1936 			if (constws.toUpper()==objw)
1937 				result << constw;
1938 		}
1939 	}
1940 
1941 	// Search by NGC numbers (possible formats are "NGC31" or "NGC 31")
1942 	for (const auto& n : dsoArray)
1943 	{
1944 		if (n->NGC_nb==0) continue;
1945 		QString constw = QString("NGC%1").arg(n->NGC_nb);
1946 		QString constws = constw.mid(0, objw.size());
1947 		if (constws.toUpper()==objw)
1948 		{
1949 			result << constws;
1950 			continue;
1951 		}
1952 		constw = QString("NGC %1").arg(n->NGC_nb);
1953 		constws = constw.mid(0, objw.size());
1954 		if (constws.toUpper()==objw)
1955 			result << constw;
1956 	}
1957 
1958 	// Search by PGC object numbers (possible formats are "PGC31" or "PGC 31")
1959 	if (objw.size()>=1 && objw.left(3)=="PGC")
1960 	{
1961 		for (const auto& n : dsoArray)
1962 		{
1963 			if (n->PGC_nb==0) continue;
1964 			QString constw = QString("PGC%1").arg(n->PGC_nb);
1965 			QString constws = constw.mid(0, objw.size());
1966 			if (constws.toUpper()==objw)
1967 			{
1968 				result << constws;	// Prevent adding both forms for name
1969 				continue;
1970 			}
1971 			constw = QString("PGC %1").arg(n->PGC_nb);
1972 			constws = constw.mid(0, objw.size());
1973 			if (constws.toUpper()==objw)
1974 				result << constw;
1975 		}
1976 	}
1977 
1978 	// Search by UGC object numbers (possible formats are "UGC31" or "UGC 31")
1979 	if (objw.size()>=1 && objw.left(3)=="UGC")
1980 	{
1981 		for (const auto& n : dsoArray)
1982 		{
1983 			if (n->UGC_nb==0) continue;
1984 			QString constw = QString("UGC%1").arg(n->UGC_nb);
1985 			QString constws = constw.mid(0, objw.size());
1986 			if (constws.toUpper()==objw)
1987 			{
1988 				result << constws;
1989 				continue;	// Prevent adding both forms for name
1990 			}
1991 			constw = QString("UGC %1").arg(n->UGC_nb);
1992 			constws = constw.mid(0, objw.size());
1993 			if (constws.toUpper()==objw)
1994 				result << constw;
1995 		}
1996 	}
1997 
1998 	// Search by Caldwell objects number (possible formats are "C31" or "C 31")
1999 	if (objw.size()>=1 && objw.left(1)=="C" && objw.left(2)!="CR" && objw.left(2)!="CE")
2000 	{
2001 		for (const auto& n : dsoArray)
2002 		{
2003 			if (n->C_nb==0) continue;
2004 			QString constw = QString("C%1").arg(n->C_nb);
2005 			QString constws = constw.mid(0, objw.size());
2006 			if (constws.toUpper()==objw)
2007 			{
2008 				result << constws;
2009 				continue;	// Prevent adding both forms for name
2010 			}
2011 			constw = QString("C %1").arg(n->C_nb);
2012 			constws = constw.mid(0, objw.size());
2013 			if (constws.toUpper()==objw)
2014 				result << constw;
2015 		}
2016 	}
2017 
2018 	// Search by Collinder objects number (possible formats are "Cr31" or "Cr 31")
2019 	if (objw.size()>=1 && (objw.left(2)=="CR" || objw.left(9)=="COLLINDER"))
2020 	{
2021 		for (const auto& n : dsoArray)
2022 		{
2023 			if (n->Cr_nb==0) continue;
2024 			QString constw = QString("Cr%1").arg(n->Cr_nb);
2025 			QString constws = constw.mid(0, objw.size());
2026 			QString constws2 = QString("Collinder%1").arg(n->Cr_nb).mid(0, objw.size());
2027 			if (constws.toUpper()==objw || constws2.toUpper()==objw)
2028 			{
2029 				result << constws;
2030 				continue;	// Prevent adding both forms for name
2031 			}
2032 			constw = QString("Cr %1").arg(n->Cr_nb);
2033 			constws = constw.mid(0, objw.size());
2034 			constws2 = QString("Collinder %1").arg(n->Cr_nb).mid(0, objw.size());
2035 			if (constws.toUpper()==objw || constws2.toUpper()==objw)
2036 				result << constw;
2037 		}
2038 	}
2039 
2040 	// Search by Ced objects number (possible formats are "Ced31" or "Ced 31")
2041 	if (objw.size()>=1 && objw.left(3)=="CED")
2042 	{
2043 		for (const auto& n : dsoArray)
2044 		{
2045 			if (n->Ced_nb.isEmpty()) continue;
2046 			QString constw = QString("Ced%1").arg(n->Ced_nb.trimmed());
2047 			QString constws = constw.mid(0, objw.size());
2048 			if (constws.toUpper()==objw)
2049 			{
2050 				result << constws;
2051 				continue;	// Prevent adding both forms for name
2052 			}
2053 			constw = QString("Ced %1").arg(n->Ced_nb.trimmed());
2054 			constws = constw.mid(0, objw.size());
2055 			if (constws.toUpper()==objw)
2056 				result << constw;
2057 		}
2058 	}
2059 
2060 	// Search by Barnard objects number (possible formats are "B31" or "B 31")
2061 	if (objw.size()>=1 && objw.left(1)=="B")
2062 	{
2063 		for (const auto& n : dsoArray)
2064 		{
2065 			if (n->B_nb==0) continue;
2066 			QString constw = QString("B%1").arg(n->B_nb);
2067 			QString constws = constw.mid(0, objw.size());
2068 			if (constws.toUpper()==objw)
2069 			{
2070 				result << constws;
2071 				continue;	// Prevent adding both forms for name
2072 			}
2073 			constw = QString("B %1").arg(n->B_nb);
2074 			constws = constw.mid(0, objw.size());
2075 			if (constws.toUpper()==objw)
2076 				result << constw;
2077 		}
2078 	}
2079 
2080 	// Search by Sharpless objects number (possible formats are "Sh2-31" or "Sh 2-31")
2081 	if (objw.size()>=1 && objw.left(2)=="SH")
2082 	{
2083 		for (const auto& n : dsoArray)
2084 		{
2085 			if (n->Sh2_nb==0) continue;
2086 			QString constw = QString("SH2-%1").arg(n->Sh2_nb);
2087 			QString constws = constw.mid(0, objw.size());
2088 			if (constws.toUpper()==objw)
2089 			{
2090 				result << constws;
2091 				continue;	// Prevent adding both forms for name
2092 			}
2093 			constw = QString("SH 2-%1").arg(n->Sh2_nb);
2094 			constws = constw.mid(0, objw.size());
2095 			if (constws.toUpper()==objw)
2096 				result << constw;
2097 		}
2098 	}
2099 
2100 	// Search by van den Bergh objects number (possible formats are "vdB31" or "vdB 31")
2101 	if (objw.size()>=1 && objw.left(3)=="VDB" && objw.left(6)!="VDB-HA" && objw.left(4)!="VDBH")
2102 	{
2103 		for (const auto& n : dsoArray)
2104 		{
2105 			if (n->VdB_nb==0) continue;
2106 			QString constw = QString("vdB%1").arg(n->VdB_nb);
2107 			QString constws = constw.mid(0, objw.size());
2108 			if (constws.toUpper()==objw)
2109 			{
2110 				result << constws;
2111 				continue;	// Prevent adding both forms for name
2112 			}
2113 			constw = QString("vdB %1").arg(n->VdB_nb);
2114 			constws = constw.mid(0, objw.size());
2115 			if (constws.toUpper()==objw)
2116 				result << constw;
2117 		}
2118 	}
2119 
2120 	// Search by RCW objects number (possible formats are "RCW31" or "RCW 31")
2121 	if (objw.size()>=1 && objw.left(3)=="RCW")
2122 	{
2123 		for (const auto& n : dsoArray)
2124 		{
2125 			if (n->RCW_nb==0) continue;
2126 			QString constw = QString("RCW%1").arg(n->RCW_nb);
2127 			QString constws = constw.mid(0, objw.size());
2128 			if (constws.toUpper()==objw)
2129 			{
2130 				result << constws;
2131 				continue;	// Prevent adding both forms for name
2132 			}
2133 			constw = QString("RCW %1").arg(n->RCW_nb);
2134 			constws = constw.mid(0, objw.size());
2135 			if (constws.toUpper()==objw)
2136 				result << constw;
2137 		}
2138 	}
2139 
2140 	// Search by LDN objects number (possible formats are "LDN31" or "LDN 31")
2141 	if (objw.size()>=1 && objw.left(3)=="LDN")
2142 	{
2143 		for (const auto& n : dsoArray)
2144 		{
2145 			if (n->LDN_nb==0) continue;
2146 			QString constw = QString("LDN%1").arg(n->LDN_nb);
2147 			QString constws = constw.mid(0, objw.size());
2148 			if (constws.toUpper()==objw)
2149 			{
2150 				result << constws;
2151 				continue;	// Prevent adding both forms for name
2152 			}
2153 			constw = QString("LDN %1").arg(n->LDN_nb);
2154 			constws = constw.mid(0, objw.size());
2155 			if (constws.toUpper()==objw)
2156 				result << constw;
2157 		}
2158 	}
2159 
2160 	// Search by LBN objects number (possible formats are "LBN31" or "LBN 31")
2161 	if (objw.size()>=1 && objw.left(3)=="LBN")
2162 	{
2163 		for (const auto& n : dsoArray)
2164 		{
2165 			if (n->LBN_nb==0) continue;
2166 			QString constw = QString("LBN%1").arg(n->LBN_nb);
2167 			QString constws = constw.mid(0, objw.size());
2168 			if (constws.toUpper()==objw)
2169 			{
2170 				result << constws;
2171 				continue;	// Prevent adding both forms for name
2172 			}
2173 			constw = QString("LBN %1").arg(n->LBN_nb);
2174 			constws = constw.mid(0, objw.size());
2175 			if (constws.toUpper()==objw)
2176 				result << constw;
2177 		}
2178 	}
2179 
2180 	// Search by Arp objects number
2181 	if (objw.size()>=1 && objw.left(3)=="ARP")
2182 	{
2183 		for (const auto& n : dsoArray)
2184 		{
2185 			if (n->Arp_nb==0) continue;
2186 			QString constw = QString("Arp%1").arg(n->Arp_nb);
2187 			QString constws = constw.mid(0, objw.size());
2188 			if (constws.toUpper()==objw)
2189 			{
2190 				result << constws;
2191 				continue;	// Prevent adding both forms for name
2192 			}
2193 			constw = QString("Arp %1").arg(n->Arp_nb);
2194 			constws = constw.mid(0, objw.size());
2195 			if (constws.toUpper()==objw)
2196 				result << constw;
2197 		}
2198 	}
2199 
2200 	// Search by VV objects number
2201 	if (objw.size()>=1 && objw.left(2)=="VV")
2202 	{
2203 		for (const auto& n : dsoArray)
2204 		{
2205 			if (n->VV_nb==0) continue;
2206 			QString constw = QString("VV%1").arg(n->VV_nb);
2207 			QString constws = constw.mid(0, objw.size());
2208 			if (constws.toUpper()==objw)
2209 			{
2210 				result << constws;
2211 				continue;	// Prevent adding both forms for name
2212 			}
2213 			constw = QString("VV %1").arg(n->VV_nb);
2214 			constws = constw.mid(0, objw.size());
2215 			if (constws.toUpper()==objw)
2216 				result << constw;
2217 		}
2218 	}
2219 
2220 	// Search by PK objects number
2221 	if (objw.size()>=1 && objw.left(2)=="PK")
2222 	{
2223 		for (const auto& n : dsoArray)
2224 		{
2225 			if (n->PK_nb.isEmpty()) continue;
2226 			QString constw = QString("PK%1").arg(n->PK_nb.trimmed());
2227 			QString constws = constw.mid(0, objw.size());
2228 			if (constws.toUpper()==objw)
2229 			{
2230 				result << constws;
2231 				continue;	// Prevent adding both forms for name
2232 			}
2233 			constw = QString("PK %1").arg(n->PK_nb.trimmed());
2234 			constws = constw.mid(0, objw.size());
2235 			if (constws.toUpper()==objw)
2236 				result << constw;
2237 		}
2238 	}
2239 
2240 	// Search by PN G objects number
2241 	if (objw.size()>=1 && objw.left(2)=="PN")
2242 	{
2243 		for (const auto& n : dsoArray)
2244 		{
2245 			if (n->PNG_nb.isEmpty()) continue;
2246 			QString constw = QString("PNG%1").arg(n->PNG_nb.trimmed());
2247 			QString constws = constw.mid(0, objw.size());
2248 			if (constws.toUpper()==objw)
2249 			{
2250 				result << constws;
2251 				continue;	// Prevent adding both forms for name
2252 			}
2253 			constw = QString("PN G%1").arg(n->PNG_nb.trimmed());
2254 			constws = constw.mid(0, objw.size());
2255 			if (constws.toUpper()==objw)
2256 				result << constw;
2257 		}
2258 	}
2259 
2260 	// Search by SNR G objects number
2261 	if (objw.size()>=1 && objw.left(3)=="SNR")
2262 	{
2263 		for (const auto& n : dsoArray)
2264 		{
2265 			if (n->SNRG_nb.isEmpty()) continue;
2266 			QString constw = QString("SNRG%1").arg(n->SNRG_nb.trimmed());
2267 			QString constws = constw.mid(0, objw.size());
2268 			if (constws.toUpper()==objw)
2269 			{
2270 				result << constws;
2271 				continue;	// Prevent adding both forms for name
2272 			}
2273 			constw = QString("SNR G%1").arg(n->SNRG_nb.trimmed());
2274 			constws = constw.mid(0, objw.size());
2275 			if (constws.toUpper()==objw)
2276 				result << constw;
2277 		}
2278 	}
2279 
2280 	// Search by ACO (Abell) objects number
2281 	if (objw.size()>=1 && (objw.left(5)=="ABELL" || objw.left(3)=="ACO"))
2282 	{
2283 		for (const auto& n : dsoArray)
2284 		{
2285 			if (n->ACO_nb.isEmpty()) continue;
2286 			QString constw = QString("Abell%1").arg(n->ACO_nb.trimmed());
2287 			QString constws = constw.mid(0, objw.size());
2288 			QString constws2 = QString("ACO%1").arg(n->ACO_nb.trimmed()).mid(0, objw.size());
2289 			if (constws.toUpper()==objw || constws2.toUpper()==objw)
2290 			{
2291 				result << constws;
2292 				continue;	// Prevent adding both forms for name
2293 			}
2294 			constw = QString("Abell %1").arg(n->ACO_nb.trimmed());
2295 			constws = constw.mid(0, objw.size());
2296 			constws2 = QString("ACO %1").arg(n->ACO_nb.trimmed()).mid(0, objw.size());
2297 			if (constws.toUpper()==objw || constws2.toUpper()==objw)
2298 				result << constw;
2299 		}
2300 	}
2301 
2302 	// Search by HCG objects number
2303 	if (objw.size()>=1 && objw.left(3)=="HCG")
2304 	{
2305 		for (const auto& n : dsoArray)
2306 		{
2307 			if (n->HCG_nb.isEmpty()) continue;
2308 			QString constw = QString("HCG%1").arg(n->HCG_nb.trimmed());
2309 			QString constws = constw.mid(0, objw.size());
2310 			if (constws.toUpper()==objw)
2311 			{
2312 				result << constws;
2313 				continue;	// Prevent adding both forms for name
2314 			}
2315 			constw = QString("HCG %1").arg(n->HCG_nb.trimmed());
2316 			constws = constw.mid(0, objw.size());
2317 			if (constws.toUpper()==objw)
2318 				result << constw;
2319 		}
2320 	}
2321 
2322 	// Search by ESO objects number
2323 	if (objw.size()>=1 && objw.left(3)=="ESO")
2324 	{
2325 		for (const auto& n : dsoArray)
2326 		{
2327 			if (n->ESO_nb.isEmpty()) continue;
2328 			QString constw = QString("ESO%1").arg(n->ESO_nb.trimmed());
2329 			QString constws = constw.mid(0, objw.size());
2330 			if (constws.toUpper()==objw)
2331 			{
2332 				result << constws;
2333 				continue;	// Prevent adding both forms for name
2334 			}
2335 			constw = QString("ESO %1").arg(n->ESO_nb.trimmed());
2336 			constws = constw.mid(0, objw.size());
2337 			if (constws.toUpper()==objw)
2338 				result << constw;
2339 		}
2340 	}
2341 
2342 	// Search by VdBH objects number
2343 	if (objw.size()>=1 && objw.left(4)=="VDBH")
2344 	{
2345 		for (const auto& n : dsoArray)
2346 		{
2347 			if (n->VdBH_nb.isEmpty()) continue;
2348 			QString constw = QString("vdBH%1").arg(n->VdBH_nb.trimmed());
2349 			QString constws = constw.mid(0, objw.size());
2350 			if (constws.toUpper()==objw)
2351 			{
2352 				result << constws;
2353 				continue;	// Prevent adding both forms for name
2354 			}
2355 			constw = QString("vdBH %1").arg(n->VdBH_nb.trimmed());
2356 			constws = constw.mid(0, objw.size());
2357 			if (constws.toUpper()==objw)
2358 				result << constw;
2359 		}
2360 	}
2361 
2362 	// Search by DWB objects number
2363 	if (objw.size()>=1 && objw.left(3)=="DWB")
2364 	{
2365 		for (const auto& n : dsoArray)
2366 		{
2367 			if (n->DWB_nb==0) continue;
2368 			QString constw = QString("DWB%1").arg(n->DWB_nb);
2369 			QString constws = constw.mid(0, objw.size());
2370 			if (constws.toUpper()==objw)
2371 			{
2372 				result << constws;
2373 				continue;	// Prevent adding both forms for name
2374 			}
2375 			constw = QString("DWB %1").arg(n->DWB_nb);
2376 			constws = constw.mid(0, objw.size());
2377 			if (constws.toUpper()==objw)
2378 				result << constw;
2379 		}
2380 	}
2381 
2382 	// Search by Tr (Trumpler) objects number
2383 	if (objw.size()>=1 && (objw.left(8)=="TRUMPLER" || objw.left(2)=="TR"))
2384 	{
2385 		for (const auto& n : dsoArray)
2386 		{
2387 			if (n->Tr_nb==0) continue;
2388 			QString constw = QString("Tr%1").arg(n->Tr_nb);
2389 			QString constws = constw.mid(0, objw.size());
2390 			QString constws2 = QString("Trumpler%1").arg(n->Tr_nb).mid(0, objw.size());
2391 			if (constws.toUpper()==objw || constws2.toUpper()==objw)
2392 			{
2393 				result << constws;
2394 				continue;	// Prevent adding both forms for name
2395 			}
2396 			constw = QString("Tr %1").arg(n->Tr_nb);
2397 			constws = constw.mid(0, objw.size());
2398 			constws2 = QString("Trumpler %1").arg(n->Tr_nb).mid(0, objw.size());
2399 			if (constws.toUpper()==objw || constws2.toUpper()==objw)
2400 				result << constw;
2401 		}
2402 	}
2403 
2404 	// Search by St (Stock) objects number
2405 	if (objw.size()>=1 && (objw.left(5)=="STOCK" || objw.left(2)=="ST"))
2406 	{
2407 		for (const auto& n : dsoArray)
2408 		{
2409 			if (n->St_nb==0) continue;
2410 			QString constw = QString("St%1").arg(n->St_nb);
2411 			QString constws = constw.mid(0, objw.size());
2412 			QString constws2 = QString("Stock%1").arg(n->St_nb).mid(0, objw.size());
2413 			if (constws.toUpper()==objw || constws2.toUpper()==objw)
2414 			{
2415 				result << constws;
2416 				continue;	// Prevent adding both forms for name
2417 			}
2418 			constw = QString("St %1").arg(n->St_nb);
2419 			constws = constw.mid(0, objw.size());
2420 			constws2 = QString("Stock %1").arg(n->St_nb).mid(0, objw.size());
2421 			if (constws.toUpper()==objw || constws2.toUpper()==objw)
2422 				result << constw;
2423 		}
2424 	}
2425 
2426 	// Search by Ru (Ruprecht) objects number
2427 	if (objw.size()>=1 && (objw.left(8)=="RUPRECHT" || objw.left(2)=="RU"))
2428 	{
2429 		for (const auto& n : dsoArray)
2430 		{
2431 			if (n->Ru_nb==0) continue;
2432 			QString constw = QString("Ru%1").arg(n->Ru_nb);
2433 			QString constws = constw.mid(0, objw.size());
2434 			QString constws2 = QString("Ruprecht%1").arg(n->Ru_nb).mid(0, objw.size());
2435 			if (constws.toUpper()==objw || constws2.toUpper()==objw)
2436 			{
2437 				result << constws;
2438 				continue;	// Prevent adding both forms for name
2439 			}
2440 			constw = QString("Ru %1").arg(n->Ru_nb);
2441 			constws = constw.mid(0, objw.size());
2442 			constws2 = QString("Ruprecht %1").arg(n->Ru_nb).mid(0, objw.size());
2443 			if (constws.toUpper()==objw || constws2.toUpper()==objw)
2444 				result << constw;
2445 		}
2446 	}
2447 
2448 	// Search by van den Bergh-Hagen Catalogue objects number
2449 	if (objw.size()>=1 && objw.left(6)=="VDB-HA")
2450 	{
2451 		for (const auto& n : dsoArray)
2452 		{
2453 			if (n->VdBHa_nb==0) continue;
2454 			QString constw = QString("vdB-Ha%1").arg(n->VdBHa_nb);
2455 			QString constws = constw.mid(0, objw.size());
2456 			if (constws.toUpper()==objw)
2457 			{
2458 				result << constws;
2459 				continue;	// Prevent adding both forms for name
2460 			}
2461 			constw = QString("vdB-Ha %1").arg(n->VdBHa_nb);
2462 			constws = constw.mid(0, objw.size());
2463 			if (constws.toUpper()==objw)
2464 				result << constw;
2465 		}
2466 	}
2467 
2468 	// Search by common names and aliases
2469 	QStringList names;
2470 	for (const auto& n : dsoArray)
2471 	{
2472 		names.append(n->nameI18);
2473 		names.append(n->englishName);
2474 		if (getFlagAdditionalNames())
2475 		{
2476 			QStringList nameList = n->nameI18Aliases;
2477 			for (const auto &name : nameList)
2478 				names.append(name);
2479 
2480 			nameList = n->englishAliases;
2481 			for (const auto &name : nameList)
2482 				names.append(name);
2483 		}
2484 	}
2485 
2486 	QString fullMatch = "";
2487 	for (const auto& name : qAsConst(names))
2488 	{
2489 		if (!matchObjectName(name, objPrefix, useStartOfWords))
2490 			continue;
2491 
2492 		if (name==objPrefix)
2493 			fullMatch = name;
2494 		else
2495 			result.append(name);
2496 	}
2497 
2498 	result.sort();
2499 	if (!fullMatch.isEmpty())
2500 		result.prepend(fullMatch);
2501 
2502 	if (result.size() > maxNbItem)
2503 		result.erase(result.begin() + maxNbItem, result.end());
2504 
2505 	return result;
2506 }
2507 
listAllObjects(bool inEnglish) const2508 QStringList NebulaMgr::listAllObjects(bool inEnglish) const
2509 {
2510 	QStringList result;
2511 	for (const auto& n : dsoArray)
2512 	{
2513 		if (!n->getEnglishName().isEmpty())
2514 		{
2515 			if (inEnglish)
2516 				result << n->getEnglishName();
2517 			else
2518 				result << n->getNameI18n();
2519 		}
2520 	}
2521 	return result;
2522 }
2523 
listAllObjectsByType(const QString & objType,bool inEnglish) const2524 QStringList NebulaMgr::listAllObjectsByType(const QString &objType, bool inEnglish) const
2525 {
2526 	QStringList result;
2527 	int type = objType.toInt();
2528 	switch (type)
2529 	{
2530 		case 0: // Bright galaxies?
2531 			for (const auto& n : dsoArray)
2532 			{
2533 				if (n->nType==type && qMin(n->vMag, n->bMag)<=10.f)
2534 				{
2535 					if (!n->getEnglishName().isEmpty())
2536 					{
2537 						if (inEnglish)
2538 							result << n->getEnglishName();
2539 						else
2540 							result << n->getNameI18n();
2541 					}
2542 					else
2543 						result << n->getDSODesignationWIC();
2544 				}
2545 			}
2546 			break;
2547 		case 100: // Messier Catalogue?
2548 			for (const auto& n : getDeepSkyObjectsByType(objType))
2549 				result << QString("M%1").arg(n->M_nb);
2550 			break;
2551 		case 101: // Caldwell Catalogue?
2552 			for (const auto& n : getDeepSkyObjectsByType(objType))
2553 				result << QString("C%1").arg(n->C_nb);
2554 			break;
2555 		case 102: // Barnard Catalogue?
2556 			for (const auto& n : getDeepSkyObjectsByType(objType))
2557 				result << QString("B %1").arg(n->B_nb);
2558 			break;
2559 		case 103: // Sharpless Catalogue?
2560 			for (const auto& n : getDeepSkyObjectsByType(objType))
2561 				result << QString("SH 2-%1").arg(n->Sh2_nb);
2562 			break;
2563 		case 104: // van den Bergh Catalogue
2564 			for (const auto& n : getDeepSkyObjectsByType(objType))
2565 				result << QString("vdB %1").arg(n->VdB_nb);
2566 			break;
2567 		case 105: // RCW Catalogue
2568 			for (const auto& n : getDeepSkyObjectsByType(objType))
2569 				result << QString("RCW %1").arg(n->RCW_nb);
2570 			break;
2571 		case 106: // Collinder Catalogue
2572 			for (const auto& n : getDeepSkyObjectsByType(objType))
2573 				result << QString("Cr %1").arg(n->Cr_nb);
2574 			break;
2575 		case 107: // Melotte Catalogue
2576 			for (const auto& n : getDeepSkyObjectsByType(objType))
2577 				result << QString("Mel %1").arg(n->Mel_nb);
2578 			break;
2579 		case 108: // New General Catalogue
2580 			for (const auto& n : getDeepSkyObjectsByType(objType))
2581 				result << QString("NGC %1").arg(n->NGC_nb);
2582 			break;
2583 		case 109: // Index Catalogue
2584 			for (const auto& n : getDeepSkyObjectsByType(objType))
2585 				result << QString("IC %1").arg(n->IC_nb);
2586 			break;
2587 		case 110: // Lynds' Catalogue of Bright Nebulae
2588 			for (const auto& n : getDeepSkyObjectsByType(objType))
2589 				result << QString("LBN %1").arg(n->LBN_nb);
2590 			break;
2591 		case 111: // Lynds' Catalogue of Dark Nebulae
2592 			for (const auto& n : getDeepSkyObjectsByType(objType))
2593 				result << QString("LDN %1").arg(n->LDN_nb);
2594 			break;
2595 		case 114: // Cederblad Catalog
2596 			for (const auto& n : getDeepSkyObjectsByType(objType))
2597 				result << QString("Ced %1").arg(n->Ced_nb);
2598 			break;
2599 		case 115: // Atlas of Peculiar Galaxies (Arp)
2600 			for (const auto& n : getDeepSkyObjectsByType(objType))
2601 				result << QString("Arp %1").arg(n->Arp_nb);
2602 			break;
2603 		case 116: // The Catalogue of Interacting Galaxies by Vorontsov-Velyaminov (VV)
2604 			for (const auto& n : getDeepSkyObjectsByType(objType))
2605 				result << QString("VV %1").arg(n->VV_nb);
2606 			break;
2607 		case 117: // Catalogue of Galactic Planetary Nebulae (PK)
2608 			for (const auto& n : getDeepSkyObjectsByType(objType))
2609 				result << QString("PK %1").arg(n->PK_nb);
2610 			break;
2611 		case 118: // Strasbourg-ESO Catalogue of Galactic Planetary Nebulae by Acker et. al. (PN G)
2612 			for (const auto& n : getDeepSkyObjectsByType(objType))
2613 				result << QString("PN G%1").arg(n->PNG_nb);
2614 			break;
2615 		case 119: // A catalogue of Galactic supernova remnants by Green (SNR G)
2616 			for (const auto& n : getDeepSkyObjectsByType(objType))
2617 				result << QString("SNR G%1").arg(n->SNRG_nb);
2618 			break;
2619 		case 120: // A Catalog of Rich Clusters of Galaxies by Abell et. al. (Abell (ACO))
2620 			for (const auto& n : getDeepSkyObjectsByType(objType))
2621 				result << QString("Abell %1").arg(n->ACO_nb);
2622 			break;
2623 		case 121: // Hickson Compact Group by Hickson et. al. (HCG)
2624 			for (const auto& n : getDeepSkyObjectsByType(objType))
2625 				result << QString("HCG %1").arg(n->HCG_nb);
2626 			break;
2627 		case 122: // ESO/Uppsala Survey of the ESO(B) Atlas (ESO)
2628 			for (const auto& n : getDeepSkyObjectsByType(objType))
2629 				result << QString("ESO %1").arg(n->ESO_nb);
2630 			break;
2631 		case 123: // Catalogue of southern stars embedded in nebulosity (vdBH)
2632 			for (const auto& n : getDeepSkyObjectsByType(objType))
2633 				result << QString("vdBH %1").arg(n->VdBH_nb);
2634 			break;
2635 		case 124: // Catalogue and distances of optically visible H II regions (DWB)
2636 			for (const auto& n : getDeepSkyObjectsByType(objType))
2637 				result << QString("DWB %1").arg(n->DWB_nb);
2638 			break;
2639 		case 125: // Trumpler Catalogue (Tr)
2640 			for (const auto& n : getDeepSkyObjectsByType(objType))
2641 				result << QString("Tr %1").arg(n->Tr_nb);
2642 			break;
2643 		case 126: // Stock Catalogue (St)
2644 			for (const auto& n : getDeepSkyObjectsByType(objType))
2645 				result << QString("St %1").arg(n->St_nb);
2646 			break;
2647 		case 127: // Ruprecht Catalogue (Ru)
2648 			for (const auto& n : getDeepSkyObjectsByType(objType))
2649 				result << QString("Ru %1").arg(n->Ru_nb);
2650 			break;
2651 		case 128: // van den Bergh-Hagen Catalogue (VdB-Ha)
2652 			for (const auto& n : getDeepSkyObjectsByType(objType))
2653 				result << QString("vdB-Ha %1").arg(n->VdBHa_nb);
2654 			break;
2655 		case 150: // Dwarf galaxies [see NebulaList.hpp]
2656 		{
2657 			for (unsigned int i = 0; i < sizeof(DWARF_GALAXIES) / sizeof(DWARF_GALAXIES[0]); i++)
2658 				result << QString("PGC %1").arg(DWARF_GALAXIES[i]);
2659 			break;
2660 		}
2661 		case 151: // Herschel 400 Catalogue [see NebulaList.hpp]
2662 		{
2663 			for (unsigned int i = 0; i < sizeof(H400_LIST) / sizeof(H400_LIST[0]); i++)
2664 				result << QString("NGC %1").arg(H400_LIST[i]);
2665 			break;
2666 		}
2667 		case 152: // Jack Bennett's deep sky catalogue [see NebulaList.hpp]
2668 		{
2669 			for (unsigned int i = 0; i < sizeof(BENNETT_LIST) / sizeof(BENNETT_LIST[0]); i++)
2670 				result << QString("NGC %1").arg(BENNETT_LIST[i]);
2671 			result << "Mel 105" << "IC 1459";
2672 			break;
2673 		}
2674 		case 153: // James Dunlop's deep sky catalogue [see NebulaList.hpp]
2675 		{
2676 			for (unsigned int i = 0; i < sizeof(DUNLOP_LIST) / sizeof(DUNLOP_LIST[0]); i++)
2677 				result << QString("NGC %1").arg(DUNLOP_LIST[i]);
2678 			break;
2679 		}
2680 		default:
2681 		{
2682 			for (const auto& n : dsoArray)
2683 			{
2684 				if (n->nType==type)
2685 				{
2686 					if (!n->getEnglishName().isEmpty())
2687 					{
2688 						if (inEnglish)
2689 							result << n->getEnglishName();
2690 						else
2691 							result << n->getNameI18n();
2692 					}
2693 					else
2694 						result << n->getDSODesignationWIC();
2695 				}
2696 			}
2697 			break;
2698 		}
2699 	}
2700 
2701 	result.removeDuplicates();
2702 	return result;
2703 }
2704 
getDeepSkyObjectsByType(const QString & objType) const2705 QList<NebulaP> NebulaMgr::getDeepSkyObjectsByType(const QString &objType) const
2706 {
2707 	QList<NebulaP> dso;
2708 	int type = objType.toInt();
2709 	switch (type)
2710 	{
2711 		case 100: // Messier Catalogue?
2712 			for (const auto& n : dsoArray)
2713 			{
2714 				if (n->M_nb>0)
2715 					dso.append(n);
2716 			}
2717 			break;
2718 		case 101: // Caldwell Catalogue?
2719 			for (const auto& n : dsoArray)
2720 			{
2721 				if (n->C_nb>0)
2722 					dso.append(n);
2723 			}
2724 			break;
2725 		case 102: // Barnard Catalogue?
2726 			for (const auto& n : dsoArray)
2727 			{
2728 				if (n->B_nb>0)
2729 					dso.append(n);
2730 			}
2731 			break;
2732 		case 103: // Sharpless Catalogue?
2733 			for (const auto& n : dsoArray)
2734 			{
2735 				if (n->Sh2_nb>0)
2736 					dso.append(n);
2737 			}
2738 			break;
2739 		case 104: // van den Bergh Catalogue
2740 			for (const auto& n : dsoArray)
2741 			{
2742 				if (n->VdB_nb>0)
2743 					dso.append(n);
2744 			}
2745 			break;
2746 		case 105: // RCW Catalogue
2747 			for (const auto& n : dsoArray)
2748 			{
2749 				if (n->RCW_nb>0)
2750 					dso.append(n);
2751 			}
2752 			break;
2753 		case 106: // Collinder Catalogue
2754 			for (const auto& n : dsoArray)
2755 			{
2756 				if (n->Cr_nb>0)
2757 					dso.append(n);
2758 			}
2759 			break;
2760 		case 107: // Melotte Catalogue
2761 			for (const auto& n : dsoArray)
2762 			{
2763 				if (n->Mel_nb>0)
2764 					dso.append(n);
2765 			}
2766 			break;
2767 		case 108: // New General Catalogue
2768 			for (const auto& n : dsoArray)
2769 			{
2770 				if (n->NGC_nb>0)
2771 					dso.append(n);
2772 			}
2773 			break;
2774 		case 109: // Index Catalogue
2775 			for (const auto& n : dsoArray)
2776 			{
2777 				if (n->IC_nb>0)
2778 					dso.append(n);
2779 			}
2780 			break;
2781 		case 110: // Lynds' Catalogue of Bright Nebulae
2782 			for (const auto& n : dsoArray)
2783 			{
2784 				if (n->LBN_nb>0)
2785 					dso.append(n);
2786 			}
2787 			break;
2788 		case 111: // Lynds' Catalogue of Dark Nebulae
2789 			for (const auto& n : dsoArray)
2790 			{
2791 				if (n->LDN_nb>0)
2792 					dso.append(n);
2793 			}
2794 			break;
2795 		case 112: // Principal Galaxy Catalog
2796 			for (const auto& n : dsoArray)
2797 			{
2798 				if (n->PGC_nb>0)
2799 					dso.append(n);
2800 			}
2801 			break;
2802 		case 113: // The Uppsala General Catalogue of Galaxies
2803 			for (const auto& n : dsoArray)
2804 			{
2805 				if (n->UGC_nb>0)
2806 					dso.append(n);
2807 			}
2808 			break;
2809 		case 114: // Cederblad Catalog
2810 			for (const auto& n : dsoArray)
2811 			{
2812 				if (!n->Ced_nb.isEmpty())
2813 					dso.append(n);
2814 			}
2815 			break;
2816 		case 115: // Atlas of Peculiar Galaxies (Arp)
2817 			for (const auto& n : dsoArray)
2818 			{
2819 				if (n->Arp_nb>0)
2820 					dso.append(n);
2821 			}
2822 			break;
2823 		case 116: // The Catalogue of Interacting Galaxies by Vorontsov-Velyaminov (VV)
2824 			for (const auto& n : dsoArray)
2825 			{
2826 				if (n->VV_nb>0)
2827 					dso.append(n);
2828 			}
2829 			break;
2830 		case 117: // Catalogue of Galactic Planetary Nebulae (PK)
2831 			for (const auto& n : dsoArray)
2832 			{
2833 				if (!n->PK_nb.isEmpty())
2834 					dso.append(n);
2835 			}
2836 			break;
2837 		case 118: // Strasbourg-ESO Catalogue of Galactic Planetary Nebulae by Acker et. al. (PN G)
2838 			for (const auto& n : dsoArray)
2839 			{
2840 				if (!n->PNG_nb.isEmpty())
2841 					dso.append(n);
2842 			}
2843 			break;
2844 		case 119: // A catalogue of Galactic supernova remnants by Green (SNR G)
2845 			for (const auto& n : dsoArray)
2846 			{
2847 				if (!n->SNRG_nb.isEmpty())
2848 					dso.append(n);
2849 			}
2850 			break;
2851 		case 120: // A Catalog of Rich Clusters of Galaxies by Abell et. al. (ACO)
2852 			for (const auto& n : dsoArray)
2853 			{
2854 				if (!n->ACO_nb.isEmpty())
2855 					dso.append(n);
2856 			}
2857 			break;
2858 		case 121: // Hickson Compact Group by Hickson et. al. (HCG)
2859 			for (const auto& n : dsoArray)
2860 			{
2861 				if (!n->HCG_nb.isEmpty())
2862 					dso.append(n);
2863 			}
2864 			break;
2865 		case 122: // ESO/Uppsala Survey of the ESO(B) Atlas (ESO)
2866 			for (const auto& n : dsoArray)
2867 			{
2868 				if (!n->ESO_nb.isEmpty())
2869 					dso.append(n);
2870 			}
2871 			break;
2872 		case 123: // Catalogue of southern stars embedded in nebulosity (VdBH)
2873 			for (const auto& n : dsoArray)
2874 			{
2875 				if (!n->VdBH_nb.isEmpty())
2876 					dso.append(n);
2877 			}
2878 			break;
2879 		case 124: // Catalogue and distances of optically visible H II regions (DWB)
2880 			for (const auto& n : dsoArray)
2881 			{
2882 				if (n->DWB_nb > 0)
2883 					dso.append(n);
2884 			}
2885 			break;
2886 		case 125: // Trumpler Catalogue (Tr)
2887 			for (const auto& n : dsoArray)
2888 			{
2889 				if (n->Tr_nb > 0)
2890 					dso.append(n);
2891 			}
2892 			break;
2893 		case 126: // Stock Catalogue (St)
2894 			for (const auto& n : dsoArray)
2895 			{
2896 				if (n->St_nb > 0)
2897 					dso.append(n);
2898 			}
2899 			break;
2900 		case 127: // Ruprecht Catalogue (Ru)
2901 			for (const auto& n : dsoArray)
2902 			{
2903 				if (n->Ru_nb > 0)
2904 					dso.append(n);
2905 			}
2906 			break;
2907 		case 128: // van den Bergh-Hagen Catalogue (VdB-Ha)
2908 			for (const auto& n : dsoArray)
2909 			{
2910 				if (n->VdBHa_nb > 0)
2911 					dso.append(n);
2912 			}
2913 			break;
2914 		case 150: // Dwarf galaxies [see NebulaList.hpp]
2915 		{
2916 			NebulaP ds;
2917 			for (unsigned int i = 0; i < sizeof(DWARF_GALAXIES) / sizeof(DWARF_GALAXIES[0]); i++)
2918 			{
2919 				ds = searchPGC(DWARF_GALAXIES[i]);
2920 				if (!ds.isNull())
2921 					dso.append(ds);
2922 			}
2923 			break;
2924 		}
2925 		case 151: // Herschel 400 Catalogue [see NebulaList.hpp]
2926 		{
2927 			NebulaP ds;
2928 			for (unsigned int i = 0; i < sizeof(H400_LIST) / sizeof(H400_LIST[0]); i++)
2929 			{
2930 				ds = searchNGC(H400_LIST[i]);
2931 				if (!ds.isNull())
2932 					dso.append(ds);
2933 			}
2934 			break;
2935 		}
2936 		case 152: // Jack Bennett's deep sky catalogue [see NebulaList.hpp]
2937 		{
2938 			NebulaP ds;
2939 			for (unsigned int i = 0; i < sizeof(BENNETT_LIST) / sizeof(BENNETT_LIST[0]); i++)
2940 			{
2941 				ds = searchNGC(BENNETT_LIST[i]);
2942 				if (!ds.isNull())
2943 					dso.append(ds);
2944 			}
2945 			ds = searchMel(105);
2946 			if (!ds.isNull())
2947 				dso.append(ds);
2948 			ds = searchIC(1459);
2949 			if (!ds.isNull())
2950 				dso.append(ds);
2951 			break;
2952 		}
2953 		case 153: // James Dunlop's deep sky catalogue [see NebulaList.hpp]
2954 		{
2955 			NebulaP ds;
2956 			for (unsigned int i = 0; i < sizeof(DUNLOP_LIST) / sizeof(DUNLOP_LIST[0]); i++)
2957 			{
2958 				ds = searchNGC(DUNLOP_LIST[i]);
2959 				if (!ds.isNull())
2960 					dso.append(ds);
2961 			}
2962 			break;
2963 		}
2964 		default:
2965 		{
2966 			for (const auto& n : dsoArray)
2967 			{
2968 				if (n->nType==type)
2969 					dso.append(n);
2970 			}
2971 			break;
2972 		}
2973 	}
2974 
2975 	return dso;
2976 }
2977