~ubuntu-branches/debian/jessie/stellarium/jessie

1.2.5 by Cédric Delfosse
Import upstream version 0.10.5
1
/*
2
 * Stellarium
3
 * Copyright (C) 2002 Fabien Chereau
1.2.6 by Cédric Delfosse
Import upstream version 0.11.0
4
 * Copyright (C) 2011 Alexander Wolf
1.2.5 by Cédric Delfosse
Import upstream version 0.10.5
5
 *
6
 * This program is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU General Public License
8
 * as published by the Free Software Foundation; either version 2
9
 * of the License, or (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, write to the Free Software
1.2.7 by Tomasz Buchert
Import upstream version 0.11.2
18
 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA  02110-1335, USA.
1.2.5 by Cédric Delfosse
Import upstream version 0.10.5
19
 */
20
21
// class used to manage groups of Nebulas
22
23
#include <algorithm>
24
#include <QDebug>
25
#include <QFile>
26
#include <QSettings>
27
#include <QString>
28
#include <QStringList>
29
#include <QRegExp>
30
31
#include "StelApp.hpp"
32
#include "NebulaMgr.hpp"
33
#include "Nebula.hpp"
34
#include "StelTexture.hpp"
1.2.6 by Cédric Delfosse
Import upstream version 0.11.0
35
1.2.5 by Cédric Delfosse
Import upstream version 0.10.5
36
#include "StelSkyDrawer.hpp"
37
#include "StelTranslator.hpp"
38
#include "StelTextureMgr.hpp"
39
#include "StelObjectMgr.hpp"
40
#include "StelLocaleMgr.hpp"
41
#include "StelSkyCultureMgr.hpp"
42
#include "StelFileMgr.hpp"
43
#include "StelModuleMgr.hpp"
44
#include "StelCore.hpp"
45
#include "StelSkyImageTile.hpp"
46
#include "StelPainter.hpp"
1.2.6 by Cédric Delfosse
Import upstream version 0.11.0
47
#include "RefractionExtinction.hpp"
1.2.5 by Cédric Delfosse
Import upstream version 0.10.5
48
49
void NebulaMgr::setLabelsColor(const Vec3f& c) {Nebula::labelColor = c;}
50
const Vec3f &NebulaMgr::getLabelsColor(void) const {return Nebula::labelColor;}
51
void NebulaMgr::setCirclesColor(const Vec3f& c) {Nebula::circleColor = c;}
52
const Vec3f &NebulaMgr::getCirclesColor(void) const {return Nebula::circleColor;}
53
void NebulaMgr::setCircleScale(float scale) {Nebula::circleScale = scale;}
54
float NebulaMgr::getCircleScale(void) const {return Nebula::circleScale;}
55
56
57
NebulaMgr::NebulaMgr(void) : nebGrid(200), displayNoTexture(false)
58
{
59
	setObjectName("NebulaMgr");
60
}
61
62
NebulaMgr::~NebulaMgr()
63
{
64
	Nebula::texCircle = StelTextureSP();
1.2.6 by Cédric Delfosse
Import upstream version 0.11.0
65
	Nebula::texOpenCluster = StelTextureSP();
66
	Nebula::texGlobularCluster = StelTextureSP();
67
	Nebula::texPlanetNebula = StelTextureSP();
1.2.5 by Cédric Delfosse
Import upstream version 0.10.5
68
}
69
70
/*************************************************************************
71
 Reimplementation of the getCallOrder method
72
*************************************************************************/
73
double NebulaMgr::getCallOrder(StelModuleActionName actionName) const
74
{
75
	if (actionName==StelModule::ActionDraw)
76
		return StelApp::getInstance().getModuleMgr().getModule("MilkyWay")->getCallOrder(actionName)+10;
77
	return 0;
78
}
79
80
// read from stream
81
void NebulaMgr::init()
82
{
83
	// TODO: mechanism to specify which sets get loaded at start time.
84
	// candidate methods:
85
	// 1. config file option (list of sets to load at startup)
86
	// 2. load all
87
	// 3. flag in nebula_textures.fab (yuk)
88
	// 4. info.ini file in each set containing a "load at startup" item
89
	// For now (0.9.0), just load the default set
90
	loadNebulaSet("default");
91
92
	QSettings* conf = StelApp::getInstance().getSettings();
93
	Q_ASSERT(conf);
94
1.2.7 by Tomasz Buchert
Import upstream version 0.11.2
95
	nebulaFont.setPixelSize(StelApp::getInstance().getSettings()->value("gui/base_font_size", 13).toInt());
1.2.5 by Cédric Delfosse
Import upstream version 0.10.5
96
	Nebula::texCircle = StelApp::getInstance().getTextureManager().createTexture("textures/neb.png");   // Load circle texture
1.2.6 by Cédric Delfosse
Import upstream version 0.11.0
97
	Nebula::texOpenCluster = StelApp::getInstance().getTextureManager().createTexture("textures/ocl.png");   // Load open clister marker texture
98
	Nebula::texGlobularCluster = StelApp::getInstance().getTextureManager().createTexture("textures/gcl.png");   // Load globular clister marker texture
99
	Nebula::texPlanetNebula = StelApp::getInstance().getTextureManager().createTexture("textures/pnb.png");   // Load planetary nebula marker texture
1.2.5 by Cédric Delfosse
Import upstream version 0.10.5
100
	texPointer = StelApp::getInstance().getTextureManager().createTexture("textures/pointeur5.png");   // Load pointer texture
101
102
	setFlagShow(conf->value("astro/flag_nebula",true).toBool());
103
	setFlagHints(conf->value("astro/flag_nebula_name",false).toBool());
104
	setHintsAmount(conf->value("astro/nebula_hints_amount", 3).toFloat());
105
	setLabelsAmount(conf->value("astro/nebula_labels_amount", 3).toFloat());
106
	setCircleScale(conf->value("astro/nebula_scale",1.0f).toFloat());
107
	setFlagDisplayNoTexture(conf->value("astro/flag_nebula_display_no_texture", false).toBool());
108
109
	updateI18n();
1.2.6 by Cédric Delfosse
Import upstream version 0.11.0
110
	
111
	StelApp *app = &StelApp::getInstance();
112
	connect(app, SIGNAL(languageChanged()), this, SLOT(updateI18n()));
113
	connect(app, SIGNAL(colorSchemeChanged(const QString&)), this, SLOT(setStelStyle(const QString&)));
1.2.5 by Cédric Delfosse
Import upstream version 0.10.5
114
	GETSTELMODULE(StelObjectMgr)->registerStelObjectMgr(this);
115
}
116
117
struct DrawNebulaFuncObject
118
{
1.2.6 by Cédric Delfosse
Import upstream version 0.11.0
119
	DrawNebulaFuncObject(float amaxMagHints, float amaxMagLabels, StelPainter* p, StelCore* aCore, bool acheckMaxMagHints) : maxMagHints(amaxMagHints), maxMagLabels(amaxMagLabels), sPainter(p), core(aCore), checkMaxMagHints(acheckMaxMagHints)
1.2.5 by Cédric Delfosse
Import upstream version 0.10.5
120
	{
121
		angularSizeLimit = 5.f/sPainter->getProjector()->getPixelPerRadAtCenter()*180.f/M_PI;
122
	}
123
	void operator()(StelRegionObjectP obj)
124
	{
125
		Nebula* n = obj.staticCast<Nebula>().data();
126
		if (n->angularSize>angularSizeLimit || (checkMaxMagHints && n->mag <= maxMagHints))
127
		{
1.2.6 by Cédric Delfosse
Import upstream version 0.11.0
128
			float refmag_add=0; // value to adjust hints visibility threshold.
1.2.5 by Cédric Delfosse
Import upstream version 0.10.5
129
			sPainter->getProjector()->project(n->XYZ,n->XY);
1.2.6 by Cédric Delfosse
Import upstream version 0.11.0
130
			n->drawLabel(*sPainter, maxMagLabels-refmag_add);
131
			n->drawHints(*sPainter, maxMagHints -refmag_add);
1.2.5 by Cédric Delfosse
Import upstream version 0.10.5
132
		}
133
	}
134
	float maxMagHints;
135
	float maxMagLabels;
136
	StelPainter* sPainter;
1.2.6 by Cédric Delfosse
Import upstream version 0.11.0
137
	StelCore* core;
1.2.5 by Cédric Delfosse
Import upstream version 0.10.5
138
	float angularSizeLimit;
139
	bool checkMaxMagHints;
140
};
141
142
// Draw all the Nebulae
143
void NebulaMgr::draw(StelCore* core)
144
{
145
	const StelProjectorP prj = core->getProjection(StelCore::FrameJ2000);
146
	StelPainter sPainter(prj);
147
148
	StelSkyDrawer* skyDrawer = core->getSkyDrawer();
149
150
	Nebula::hintsBrightness = hintsFader.getInterstate()*flagShow.getInterstate();
151
152
	sPainter.enableTexture2d(true);
153
	glEnable(GL_BLEND);
154
	glBlendFunc(GL_ONE, GL_ONE);
155
156
	// Use a 1 degree margin
157
	const double margin = 1.*M_PI/180.*prj->getPixelPerRadAtCenter();
158
	const SphericalRegionP& p = prj->getViewportConvexPolygon(margin, margin);
159
160
	// Print all the nebulae of all the selected zones
161
	float maxMagHints = skyDrawer->getLimitMagnitude()*1.2f-2.f+(hintsAmount*1.2f)-2.f;
162
	float maxMagLabels = skyDrawer->getLimitMagnitude()-2.f+(labelsAmount*1.2f)-2.f;
163
	sPainter.setFont(nebulaFont);
1.2.6 by Cédric Delfosse
Import upstream version 0.11.0
164
	DrawNebulaFuncObject func(maxMagHints, maxMagLabels, &sPainter, core, hintsFader.getInterstate()>0.0001);
1.2.5 by Cédric Delfosse
Import upstream version 0.10.5
165
	nebGrid.processIntersectingRegions(p, func);
166
167
	if (GETSTELMODULE(StelObjectMgr)->getFlagSelectedObjectPointer())
168
		drawPointer(core, sPainter);
169
}
170
171
void NebulaMgr::drawPointer(const StelCore* core, StelPainter& sPainter)
172
{
173
	const StelProjectorP prj = core->getProjection(StelCore::FrameJ2000);
174
175
	const QList<StelObjectP> newSelected = GETSTELMODULE(StelObjectMgr)->getSelectedObject("Nebula");
176
	if (!newSelected.empty())
177
	{
178
		const StelObjectP obj = newSelected[0];
1.2.6 by Cédric Delfosse
Import upstream version 0.11.0
179
		Vec3d pos=obj->getJ2000EquatorialPos(core);
1.2.5 by Cédric Delfosse
Import upstream version 0.10.5
180
181
		// Compute 2D pos and return if outside screen
182
		if (!prj->projectInPlace(pos)) return;
183
		sPainter.setColor(0.4f,0.5f,0.8f);
184
		texPointer->bind();
185
186
		sPainter.enableTexture2d(true);
187
		glEnable(GL_BLEND);
188
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Normal transparency mode
189
190
		// Size on screen
191
		float size = obj->getAngularSize(core)*M_PI/180.*prj->getPixelPerRadAtCenter();
192
193
		size+=20.f + 10.f*std::sin(2.f * StelApp::getInstance().getTotalRunTime());
194
		sPainter.drawSprite2dMode(pos[0]-size/2, pos[1]-size/2, 10, 90);
195
		sPainter.drawSprite2dMode(pos[0]-size/2, pos[1]+size/2, 10, 0);
196
		sPainter.drawSprite2dMode(pos[0]+size/2, pos[1]+size/2, 10, -90);
197
		sPainter.drawSprite2dMode(pos[0]+size/2, pos[1]-size/2, 10, -180);
198
	}
199
}
200
201
void NebulaMgr::setStelStyle(const QString& section)
202
{
203
	// Load colors from config file
204
	QSettings* conf = StelApp::getInstance().getSettings();
205
206
	QString defaultColor = conf->value(section+"/default_color").toString();
207
	setLabelsColor(StelUtils::strToVec3f(conf->value(section+"/nebula_label_color", defaultColor).toString()));
208
	setCirclesColor(StelUtils::strToVec3f(conf->value(section+"/nebula_circle_color", defaultColor).toString()));
209
}
210
211
// Search by name
212
NebulaP NebulaMgr::search(const QString& name)
213
{
214
	QString uname = name.toUpper();
215
216
	foreach (const NebulaP& n, nebArray)
217
	{
218
		QString testName = n->getEnglishName().toUpper();
219
		if (testName==uname) return n;
220
	}
221
222
	// If no match found, try search by catalog reference
223
	static QRegExp catNumRx("^(M|NGC|IC)\\s*(\\d+)$");
224
	if (catNumRx.exactMatch(uname))
225
	{
226
		QString cat = catNumRx.capturedTexts().at(1);
227
		int num = catNumRx.capturedTexts().at(2).toInt();
228
229
		if (cat == "M") return searchM(num);
230
		if (cat == "NGC") return searchNGC(num);
231
		if (cat == "IC") return searchIC(num);
232
	}
233
	return NebulaP();
234
}
235
236
void NebulaMgr::loadNebulaSet(const QString& setName)
237
{
238
	try
239
	{
240
		loadNGC(StelFileMgr::findFile("nebulae/" + setName + "/ngc2000.dat"));
241
		loadNGCNames(StelFileMgr::findFile("nebulae/" + setName + "/ngc2000names.dat"));
242
	}
243
	catch (std::runtime_error& e)
244
	{
245
		qWarning() << "ERROR while loading nebula data set " << setName << ": " << e.what();
246
	}
247
}
248
249
// Look for a nebulae by XYZ coords
250
NebulaP NebulaMgr::search(const Vec3d& apos)
251
{
252
	Vec3d pos = apos;
253
	pos.normalize();
254
	NebulaP plusProche;
255
	float anglePlusProche=0.;
256
	foreach (const NebulaP& n, nebArray)
257
	{
258
		if (n->XYZ*pos>anglePlusProche)
259
		{
260
			anglePlusProche=n->XYZ*pos;
261
			plusProche=n;
262
		}
263
	}
264
	if (anglePlusProche>0.999)
265
	{
266
		return plusProche;
267
	}
268
	else return NebulaP();
269
}
270
271
272
QList<StelObjectP> NebulaMgr::searchAround(const Vec3d& av, double limitFov, const StelCore*) const
273
{
274
	QList<StelObjectP> result;
275
	if (!getFlagShow())
276
		return result;
277
278
	Vec3d v(av);
279
	v.normalize();
280
	double cosLimFov = cos(limitFov * M_PI/180.);
281
	Vec3d equPos;
282
	foreach (const NebulaP& n, nebArray)
283
	{
284
		equPos = n->XYZ;
285
		equPos.normalize();
286
		if (equPos*v>=cosLimFov)
287
		{
288
			result.push_back(qSharedPointerCast<StelObject>(n));
289
		}
290
	}
291
	return result;
292
}
293
294
NebulaP NebulaMgr::searchM(unsigned int M)
295
{
296
	foreach (const NebulaP& n, nebArray)
297
		if (n->M_nb == M)
298
			return n;
299
	return NebulaP();
300
}
301
302
NebulaP NebulaMgr::searchNGC(unsigned int NGC)
303
{
304
	if (ngcIndex.contains(NGC))
305
		return ngcIndex[NGC];
306
	return NebulaP();
307
}
308
309
NebulaP NebulaMgr::searchIC(unsigned int IC)
310
{
311
	foreach (const NebulaP& n, nebArray)
312
		if (n->IC_nb == IC) return n;
313
	return NebulaP();
314
}
315
316
#if 0
317
// read from stream
318
bool NebulaMgr::loadNGCOld(const QString& catNGC)
319
{
320
	QFile in(catNGC);
321
	if (!in.open(QIODevice::ReadOnly | QIODevice::Text))
322
		return false;
323
324
	int totalRecords=0;
325
	QString record;
326
	while (!in.atEnd())
327
	{
328
		in.readLine();
329
		++totalRecords;
330
	}
331
332
	// rewind the file to the start
333
	in.seek(0);
334
335
	int currentLineNumber = 0;	// what input line we are on
336
	int currentRecordNumber = 0;	// what record number we are on
337
	int readOk = 0;			// how many records weree rad without problems
338
	while (!in.atEnd())
339
	{
340
		record = QString::fromUtf8(in.readLine());
341
		++currentLineNumber;
342
343
		// skip comments
344
		if (record.startsWith("//") || record.startsWith("#"))
345
			continue;
346
		++currentRecordNumber;
347
348
		// Create a new Nebula record
349
		NebulaP e = NebulaP(new Nebula);
350
		if (!e->readNGC((char*)record.toLocal8Bit().data())) // reading error
351
		{
352
			e.clear();
353
		}
354
		else
355
		{
356
			nebArray.append(e);
357
			nebGrid.insert(qSharedPointerCast<StelRegionObject>(e));
358
			if (e->NGC_nb!=0)
359
				ngcIndex.insert(e->NGC_nb, e);
360
			++readOk;
361
		}
362
	}
363
	in.close();
364
	qDebug() << "Loaded" << readOk << "/" << totalRecords << "NGC records";
365
	return true;
366
}
367
#endif
368
369
bool NebulaMgr::loadNGC(const QString& catNGC)
370
{
371
	QFile in(catNGC);
372
	if (!in.open(QIODevice::ReadOnly))
373
		return false;
374
	QDataStream ins(&in);
375
	ins.setVersion(QDataStream::Qt_4_5);
376
377
	int totalRecords=0;
378
	while (!ins.atEnd())
379
	{
380
		// Create a new Nebula record
381
		NebulaP e = NebulaP(new Nebula);
382
		e->readNGC(ins);
383
384
		nebArray.append(e);
385
		nebGrid.insert(qSharedPointerCast<StelRegionObject>(e));
386
		if (e->NGC_nb!=0)
387
			ngcIndex.insert(e->NGC_nb, e);
388
		++totalRecords;
389
	}
390
	in.close();
391
	qDebug() << "Loaded" << totalRecords << "NGC records";
392
	return true;
393
}
394
395
bool NebulaMgr::loadNGCNames(const QString& catNGCNames)
396
{
397
	qDebug() << "Loading NGC name data ...";
398
	QFile ngcNameFile(catNGCNames);
399
	if (!ngcNameFile.open(QIODevice::ReadOnly | QIODevice::Text))
400
	{
401
		qWarning() << "NGC name data file" << catNGCNames << "not found.";
402
		return false;
403
	}
404
405
	// Read the names of the NGC objects
406
	QString name, record;
407
	int totalRecords=0;
408
	int lineNumber=0;
409
	int readOk=0;
410
	int nb;
411
	NebulaP e;
412
	QRegExp commentRx("^(\\s*#.*|\\s*)$");
1.2.6 by Cédric Delfosse
Import upstream version 0.11.0
413
	QRegExp transRx("_[(]\"(.*)\"[)]");
1.2.5 by Cédric Delfosse
Import upstream version 0.10.5
414
	while (!ngcNameFile.atEnd())
415
	{
416
		record = QString::fromUtf8(ngcNameFile.readLine());
417
		lineNumber++;
418
		if (commentRx.exactMatch(record))
419
			continue;
420
421
		totalRecords++;
422
		nb = record.mid(38,4).toInt();
423
		if (record[37] == 'I')
424
		{
425
			e = searchIC(nb);
426
		}
427
		else
428
		{
429
			e = searchNGC(nb);
430
		}
431
432
		// get name, trimmed of whitespace
433
		name = record.left(36).trimmed();
434
435
		if (e)
436
		{
437
			// If the name is not a messier number perhaps one is already
438
			// defined for this object
439
			if (name.left(2).toUpper() != "M ")
440
			{
1.2.6 by Cédric Delfosse
Import upstream version 0.11.0
441
				if (transRx.exactMatch(name)) {
442
					e->englishName = transRx.capturedTexts().at(1).trimmed();
443
				}
444
				 else 
445
				{
446
					e->englishName = name;
447
				}
1.2.5 by Cédric Delfosse
Import upstream version 0.10.5
448
			}
449
			else
450
			{
451
				// If it's a messiernumber, we will call it a messier if there is no better name
452
				name = name.mid(2); // remove "M "
453
454
				// read the Messier number
455
				QTextStream istr(&name);
456
				int num;
457
				istr >> num;
458
				if (istr.status()!=QTextStream::Ok)
459
				{
460
					qWarning() << "cannot read Messier number at line" << lineNumber << "of" << catNGCNames;
461
					continue;
462
				}
463
464
				e->M_nb=(unsigned int)(num);
465
				e->englishName = QString("M%1").arg(num);
466
			}
467
468
			readOk++;
469
		}
470
		else
471
			qWarning() << "no position data for " << name << "at line" << lineNumber << "of" << catNGCNames;
472
	}
473
	ngcNameFile.close();
474
	qDebug() << "Loaded" << readOk << "/" << totalRecords << "NGC name records successfully";
475
476
	return true;
477
}
478
479
480
void NebulaMgr::updateI18n()
481
{
482
	StelTranslator trans = StelApp::getInstance().getLocaleMgr().getSkyTranslator();
483
	foreach (NebulaP n, nebArray)
1.2.6 by Cédric Delfosse
Import upstream version 0.11.0
484
			n->translateName(trans);
1.2.5 by Cédric Delfosse
Import upstream version 0.10.5
485
}
486
487
488
//! Return the matching Nebula object's pointer if exists or NULL
489
StelObjectP NebulaMgr::searchByNameI18n(const QString& nameI18n) const
490
{
491
	QString objw = nameI18n.toUpper();
492
493
	// Search by NGC numbers (possible formats are "NGC31" or "NGC 31")
494
	if (objw.mid(0, 3) == "NGC")
495
	{
496
		foreach (const NebulaP& n, nebArray)
497
		{
498
			if (QString("NGC%1").arg(n->NGC_nb) == objw || QString("NGC %1").arg(n->NGC_nb) == objw)
499
				return qSharedPointerCast<StelObject>(n);
500
		}
501
	}
502
503
	// Search by common names
504
	foreach (const NebulaP& n, nebArray)
505
	{
506
		QString objwcap = n->nameI18.toUpper();
507
		if (objwcap==objw)
508
			return qSharedPointerCast<StelObject>(n);
509
	}
510
511
	// Search by Messier numbers (possible formats are "M31" or "M 31")
512
	if (objw.mid(0, 1) == "M")
513
	{
514
		foreach (const NebulaP& n, nebArray)
515
		{
516
			if (QString("M%1").arg(n->M_nb) == objw || QString("M %1").arg(n->M_nb) == objw)
517
				return qSharedPointerCast<StelObject>(n);
518
		}
519
	}
520
521
	return StelObjectP();
522
}
523
524
525
//! Return the matching Nebula object's pointer if exists or NULL
526
//! TODO split common parts of this and I18 fn above into a separate fn.
527
StelObjectP NebulaMgr::searchByName(const QString& name) const
528
{
529
	QString objw = name.toUpper();
530
531
	// Search by NGC numbers (possible formats are "NGC31" or "NGC 31")
532
	if (objw.mid(0, 3) == "NGC")
533
	{
534
		foreach (const NebulaP& n, nebArray)
535
		{
536
			if (QString("NGC%1").arg(n->NGC_nb) == objw || QString("NGC %1").arg(n->NGC_nb) == objw)
537
				return qSharedPointerCast<StelObject>(n);
538
		}
539
	}
540
541
	// Search by common names
542
	foreach (const NebulaP& n, nebArray)
543
	{
544
		QString objwcap = n->englishName.toUpper();
545
		if (objwcap==objw)
546
			return qSharedPointerCast<StelObject>(n);
547
	}
548
549
	// Search by Messier numbers (possible formats are "M31" or "M 31")
550
	if (objw.mid(0, 1) == "M")
551
	{
552
		foreach (const NebulaP& n, nebArray)
553
		{
554
			if (QString("M%1").arg(n->M_nb) == objw || QString("M %1").arg(n->M_nb) == objw)
555
				return qSharedPointerCast<StelObject>(n);
556
		}
557
	}
558
559
	return NULL;
560
}
561
562
563
//! Find and return the list of at most maxNbItem objects auto-completing the passed object I18n name
564
QStringList NebulaMgr::listMatchingObjectsI18n(const QString& objPrefix, int maxNbItem) const
565
{
566
	QStringList result;
567
	if (maxNbItem==0) return result;
568
569
	QString objw = objPrefix.toUpper();
570
571
	// Search by messier objects number (possible formats are "M31" or "M 31")
572
	if (objw.size()>=1 && objw[0]=='M')
573
	{
574
		foreach (const NebulaP& n, nebArray)
575
		{
576
			if (n->M_nb==0) continue;
577
			QString constw = QString("M%1").arg(n->M_nb);
578
			QString constws = constw.mid(0, objw.size());
579
			if (constws==objw)
580
			{
581
				result << constw;
582
				continue;	// Prevent adding both forms for name
583
			}
584
			constw = QString("M %1").arg(n->M_nb);
585
			constws = constw.mid(0, objw.size());
586
			if (constws==objw)
587
				result << constw;
588
		}
589
	}
590
591
	// Search by NGC numbers (possible formats are "NGC31" or "NGC 31")
592
	foreach (const NebulaP& n, nebArray)
593
	{
594
		if (n->NGC_nb==0) continue;
595
		QString constw = QString("NGC%1").arg(n->NGC_nb);
596
		QString constws = constw.mid(0, objw.size());
597
		if (constws==objw)
598
		{
599
			result << constw;
600
			continue;
601
		}
602
		constw = QString("NGC %1").arg(n->NGC_nb);
603
		constws = constw.mid(0, objw.size());
604
		if (constws==objw)
605
			result << constw;
606
	}
607
608
	// Search by common names
609
	foreach (const NebulaP& n, nebArray)
610
	{
611
		QString constw = n->nameI18.mid(0, objw.size()).toUpper();
612
		if (constw==objw)
613
			result << n->nameI18;
614
	}
615
616
	result.sort();
617
	if (result.size()>maxNbItem) result.erase(result.begin()+maxNbItem, result.end());
618
619
	return result;
620
}
621