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

Stellarium / stellarium / 17068063291

19 Aug 2025 11:22AM UTC coverage: 11.766%. Remained the same
17068063291

push

github

alex-w
Reformatting

14706 of 124990 relevant lines covered (11.77%)

18303.49 hits per line

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

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

19
#include "TextUserInterface.hpp"
20
#include "TuiNode.hpp"
21
#include "TuiNodeActivate.hpp"
22
#include "TuiNodeBool.hpp"
23
#include "TuiNodeInt.hpp"
24
#include "TuiNodeDouble.hpp"
25
#include "TuiNodeFloat.hpp"
26
#include "TuiNodeDateTime.hpp"
27
#include "TuiNodeColor.hpp"
28
#include "TuiNodeEnum.hpp"
29

30
#include "StelProjector.hpp"
31
#include "StelPainter.hpp"
32
#include "StelApp.hpp"
33
#include "StelCore.hpp"
34
#include "StelLocaleMgr.hpp"
35
#include "StelModuleMgr.hpp"
36
#include "StarMgr.hpp"
37
#include "StelMovementMgr.hpp"
38
#include "StelObjectMgr.hpp"
39
#include "StelSkyDrawer.hpp"
40
#include "ConstellationMgr.hpp"
41
#include "NebulaMgr.hpp"
42
#include "SolarSystem.hpp"
43
#include "LandscapeMgr.hpp"
44
#include "GridLinesMgr.hpp"
45
#include "MilkyWay.hpp"
46
#include "ZodiacalLight.hpp"
47
#include "StelLocation.hpp"
48
#include "StelMainView.hpp"
49
#include "StelSkyCultureMgr.hpp"
50
#include "StelFileMgr.hpp"
51
#ifdef ENABLE_SCRIPTING
52
#include "StelScriptMgr.hpp"
53
#endif
54
#include "StelGui.hpp"
55
#include "StelGuiItems.hpp"// Funny thing to include in a TEXT user interface...
56

57
#include <QKeyEvent>
58
#include <QDebug>
59
#include <QLabel>
60
#include <QTime>
61
#include <QProcess>
62
#include <QDir>
63
#include <QSettings>
64

65

66
/*************************************************************************
67
 Utility functions
68
*************************************************************************/
69
QString colToConf(const Vec3f& c)
×
70
{
71
        return QString("%1,%2,%3").arg(c[0],2,'f',2).arg(c[1],2,'f',2).arg(c[2],2,'f',2);
×
72
}
73

74
/*************************************************************************
75
 This method is the one called automatically by the StelModuleMgr just 
76
 after loading the dynamic library
77
*************************************************************************/
78
StelModule* TextUserInterfaceStelPluginInterface::getStelModule() const
×
79
{
80
        return new TextUserInterface();
×
81
}
82

83
StelPluginInfo TextUserInterfaceStelPluginInterface::getPluginInfo() const
×
84
{
85
        StelPluginInfo info;
×
86
        info.id = "TextUserInterface";
×
87
        info.displayedName = N_("Text User Interface");
×
88
        info.authors = "Matthew Gates";
×
89
        info.contact = STELLARIUM_DEV_URL;
×
90
        info.description = N_("Plugin implementation of 0.9.x series Text User Interface (TUI), used in planetarium systems");
×
91
        info.version = TUI_PLUGIN_VERSION;
×
92
        info.license = TUI_PLUGIN_LICENSE;
×
93
        return info;
×
94
}
×
95

96

97
/*************************************************************************
98
 Constructor
99
*************************************************************************/
100
TextUserInterface::TextUserInterface()
×
101
        : dummyDialog(this)
×
102
        , tuiActive(false)
×
103
        , tuiDateTime(false)
×
104
        , tuiObjInfo(false)
×
105
        , tuiGravityUi(false)
×
106
        , currentNode(Q_NULLPTR)
×
107
{
108
        setObjectName("TextUserInterface");        
×
109
}
×
110

111
/*************************************************************************
112
 Destructor
113
*************************************************************************/
114
TextUserInterface::~TextUserInterface()
×
115
{        
116
}
×
117

118
/*************************************************************************
119
 Reimplementation of the getCallOrder method
120
*************************************************************************/
121
double TextUserInterface::getCallOrder(StelModuleActionName actionName) const
×
122
{
123
        if (actionName==StelModule::ActionDraw)
×
124
                return StelApp::getInstance().getModuleMgr().getModule("LandscapeMgr")->getCallOrder(actionName)+10.;
×
125
        if (actionName==StelModule::ActionHandleKeys)
×
126
                return -1;
×
127
        return 0;
×
128
}
129

130

131
/*************************************************************************
132
 Init our module
133
*************************************************************************/
134
void TextUserInterface::init()
×
135
{
136
        //Reusing translations: The translators will have to do less work if the
137
        //strings used here match strings used elsewhere. Do not change strings
138
        //unless you have a good reason. --BM
139
        
140
        StelCore* core = StelApp::getInstance().getCore();
×
141
        // Main config.
142
        loadConfiguration();
×
143
        //Reusing strings from the location dialog
144
        TuiNode* m1 = new TuiNode(N_("Location"));
×
145
        m1->setParent(this);
×
146
        TuiNode* m1_1 = new TuiNodeDouble(N_("Latitude:"),
147
                                          this, SLOT(setLatitude(double)),
148
                                          getLatitude(), -90, 90, 0.5, m1);
×
149
        TuiNode* m1_2 = new TuiNodeDouble(N_("Longitude:"),
150
                                          this, SLOT(setLongitude(double)),
151
                                          getLongitude(), -180, 180, 0.5, m1, m1_1);
×
152
        TuiNode* m1_3 = new TuiNodeInt(N_("Altitude:"),
153
                                       this, SLOT(setAltitude(int)),
154
                                       core->getCurrentLocation().altitude,
×
155
                                       -450, 200000, 100, m1, m1_2);
×
156
        
157
        // TODO: Now new Solar System bodies can be added at runtime, so the list
158
        // needs to be populated every time this happens. --BM
159
        SolarSystem* solarSystem = GETSTELMODULE(SolarSystem);
×
160
        TuiNode* m1_4 = new TuiNodeEnum(N_("Solar System body"),
161
                                        this, SLOT(setHomePlanet(QString)),
162
                                        solarSystem->getAllPlanetEnglishNames(),
×
163
                                        core->getCurrentLocation().planetName,
×
164
                                        m1, m1_3);
×
165
        m1_1->setNextNode(m1_2);
×
166
        m1_2->setNextNode(m1_3);
×
167
        m1_3->setNextNode(m1_4);
×
168
        m1_4->setNextNode(m1_1);
×
169
        m1_1->loopToTheLast();
×
170
        m1->setChildNode(m1_1);
×
171

172
        TuiNode* m2 = new TuiNode(N_("Date and Time"), Q_NULLPTR, m1);
×
173
        m2->setParent(this);
×
174
        m1->setNextNode(m2);
×
175
        TuiNode* m2_1 = new TuiNodeDateTime(N_("Current date/time"),
176
                                            core,
177
                                            SLOT(setJD(double)),
178
                                            core->getJD(),
179
                                            m2);
×
180
        TuiNode* m2_2 = new TuiNode(N_("Set time zone"), m2, m2_1); // GZ This is inactive(?)
×
181
        TuiNode* m2_3 = new TuiNode(N_("Day keys"), m2, m2_2);      // GZ This is inactive(?)
×
182
        TuiNode* m2_4 = new TuiNodeDateTime(N_("Startup date/time preset"),
183
                                            core,
184
                                            SLOT(setPresetSkyTime(double)), 
185
                                            core->getPresetSkyTime(),
186
                                            m2, m2_3);
×
187
        QStringList startupModes;
×
188
        // TRANSLATORS: The current system time is used at startup
189
        startupModes << N_("system");
×
190
        // TRANSLATORS: A pre-set time is used at startup
191
        startupModes << N_("preset");
×
192
        TuiNode* m2_5 = new TuiNodeEnum(N_("Startup date and time"),
193
                                        this, SLOT(setStartupDateMode(QString)),
194
                                        startupModes,
195
                                                                        core->getStartupTimeMode(),
×
196
                                        m2, m2_4);
×
197
        StelLocaleMgr& localeMgr = StelApp::getInstance().getLocaleMgr();
×
198
        QStringList dateFormats;
×
199
        dateFormats << "system_default" << N_("mmddyyyy") << N_("ddmmyyyy") << N_("yyyymmdd");
×
200
        TuiNode* m2_6 = new TuiNodeEnum(N_("Date display format"), //Used in Time Zone plugin
201
                                        this, SLOT(setDateFormat(QString)),
202
                                        dateFormats,
203
                                        localeMgr.getDateFormatStr(),
×
204
                                        m2, m2_5);
×
205
        QStringList timeFormats;
×
206
        timeFormats << "system_default";
×
207
        // TRANSLATORS: 12-hour time format
208
        timeFormats << N_("12h");
×
209
        // TRANSLATORS: 24-hour time format
210
        timeFormats << N_("24h");
×
211
        TuiNode* m2_7 = new TuiNodeEnum(N_("Time display format"), //Used in Time Zone plugin
212
                                        this, SLOT(setTimeFormat(QString)),
213
                                        timeFormats,
214
                                        localeMgr.getTimeFormatStr(),
×
215
                                        m2, m2_6);
×
216
        m2_1->setNextNode(m2_2);
×
217
        m2_2->setNextNode(m2_3);
×
218
        m2_3->setNextNode(m2_4);
×
219
        m2_4->setNextNode(m2_5);
×
220
        m2_5->setNextNode(m2_6);
×
221
        m2_6->setNextNode(m2_7);
×
222
        m2_7->setNextNode(m2_1);
×
223
        m2_1->loopToTheLast();
×
224
        m2->setChildNode(m2_1);
×
225

226
        TuiNode* m3 = new TuiNode(N_("General"), Q_NULLPTR, m2);
×
227
        m3->setParent(this);
×
228
        m2->setNextNode(m3);
×
229
        StelSkyCultureMgr& skyCultureMgr = StelApp::getInstance().getSkyCultureMgr();
×
230
        TuiNode* m3_1 = new TuiNodeEnum(N_("Sky Culture"),
231
                                        this, 
232
                                        SLOT(setSkyCulture(QString)), 
233
                                        skyCultureMgr.getSkyCultureListI18(),
×
234
                                        skyCultureMgr.getCurrentSkyCultureNameI18(),
×
235
                                        m3);
×
236
        TuiNode* m3_2 = new TuiNodeEnum(N_("App Language"),
237
                                        this,
238
                                        SLOT(setAppLanguage(QString)),
239
                                        StelTranslator::globalTranslator->getAvailableLanguagesNamesNative(StelFileMgr::getLocaleDir()),
×
240
                                        StelTranslator::iso639_1CodeToNativeName(localeMgr.getAppLanguage()),
×
241
                                        m3, m3_1);
×
242
        m3_1->setNextNode(m3_2);
×
243
        m3_2->setNextNode(m3_1);
×
244
        m3_1->loopToTheLast();
×
245
        m3->setChildNode(m3_1);
×
246

247
        TuiNode* m4 = new TuiNode(N_("Stars"), Q_NULLPTR, m3);
×
248
        m4->setParent(this);
×
249
        m3->setNextNode(m4);
×
250
        StarMgr* starMgr = GETSTELMODULE(StarMgr);
×
251
        TuiNode* m4_1 = new TuiNodeBool(N_("Show stars"),
252
                                        starMgr, SLOT(setFlagStars(bool)), 
253
                                        starMgr->getFlagStars(), m4);
×
254
        StelSkyDrawer* skyDrawer = core->getSkyDrawer();
×
255
        TuiNode* m4_2 = new TuiNodeDouble(N_("Relative scale:"),
256
                                          skyDrawer,
257
                                          SLOT(setRelativeStarScale(double)),
258
                                          skyDrawer->getRelativeStarScale(),
259
                                          0.0, 5., 0.15,
260
                                          m4, m4_1);
×
261
        TuiNode* m4_3 = new TuiNodeDouble(N_("Absolute scale:"),
262
                                          skyDrawer,
263
                                          SLOT(setAbsoluteStarScale(double)),
264
                                          skyDrawer->getAbsoluteStarScale(),
265
                                          0.0, 10., 0.15,
266
                                          m4, m4_2);
×
267
        TuiNode* m4_4 = new TuiNodeDouble(N_("Twinkle:"),
268
                                          skyDrawer, SLOT(setTwinkleAmount(double)),
269
                                          skyDrawer->getTwinkleAmount(),
270
                                          0.0, 1.5, 0.1,
271
                                          m4, m4_3);
×
272
        TuiNode* m4_5 = new TuiNodeDouble(N_("Mag limit:"),
273
                                          skyDrawer, SLOT(setCustomStarMagnitudeLimit(double)),
274
                                          skyDrawer->getCustomStarMagnitudeLimit(),
275
                                          0.0, 20.0, 0.1,
276
                                          m4, m4_4);
×
277
        TuiNode* m4_6 = new TuiNodeBool(N_("Use mag limit"),
278
                                        skyDrawer, SLOT(setFlagStarMagnitudeLimit(bool)),
279
                                        skyDrawer->getFlagStarMagnitudeLimit(), m4, m4_5);
×
280
        TuiNode* m4_7 = new TuiNodeBool(N_("Spiky stars"),
281
                                        skyDrawer, SLOT(setFlagStarSpiky(bool)),
282
                                        skyDrawer->getFlagStarSpiky(), m4, m4_6);
×
283
        TuiNode* m4_8 = new TuiNodeDouble(N_("Labels and Markers"),
284
                                          starMgr, SLOT(setLabelsAmount(double)),
285
                                          starMgr->getLabelsAmount(),
286
                                          0.0, 19.0, 0.2,
287
                                          m4, m4_7);
×
288
        TuiNode* m4_9 = new TuiNodeBool(N_("Show additional star names"),
289
                                        starMgr, SLOT(setFlagAdditionalNames(bool)),
290
                                        starMgr->getFlagAdditionalNames(), m4, m4_8);
×
291
        TuiNode* m4_10 = new TuiNodeBool(N_("Use designations for screen labels"),
292
                                        starMgr, SLOT(setDesignationUsage(bool)),
293
                                        starMgr->getDesignationUsage(), m4, m4_9);
×
294

295
        m4_1->setNextNode(m4_2);
×
296
        m4_2->setNextNode(m4_3);
×
297
        m4_3->setNextNode(m4_4);
×
298
        m4_4->setNextNode(m4_5);
×
299
        m4_5->setNextNode(m4_6);
×
300
        m4_6->setNextNode(m4_7);
×
301
        m4_7->setNextNode(m4_8);
×
302
        m4_8->setNextNode(m4_9);
×
303
        m4_9->setNextNode(m4_10);
×
304
        m4_10->setNextNode(m4_1);
×
305
        m4_1->loopToTheLast();
×
306
        m4->setChildNode(m4_1);
×
307

308
        TuiNode* m5 = new TuiNode(N_("Colors"), Q_NULLPTR, m4);
×
309
        m5->setParent(this);
×
310
        m4->setNextNode(m5);
×
311
        ConstellationMgr* constellationMgr = GETSTELMODULE(ConstellationMgr);
×
312
        TuiNode* m5_1 = new TuiNodeColor(N_("Constellation lines"),
313
                                         constellationMgr,
314
                                         SLOT(setLinesColor(Vec3f)),
315
                                         constellationMgr->getLinesColor(), 
316
                                         m5);
×
317
        TuiNode* m5_2 = new TuiNodeColor(N_("Constellation labels"),
318
                                         constellationMgr,
319
                                         SLOT(setLabelsColor(Vec3f)),
320
                                         constellationMgr->getLabelsColor(), 
321
                                         m5, m5_1);
×
322
//        TuiNode* m5_3 = new TuiNode(N_("Constellation art"), m5, m5_2); // dysfunctional duplicate dummy
323
        // TRANSLATORS: Refers to constellation art
324
        TuiNode* m5_3 = new TuiNodeFloat(N_("Art brightness:"),
325
                                          constellationMgr,
326
                                          SLOT(setArtIntensity(float)),
327
                                          constellationMgr->getArtIntensity(),
328
                                          0.0, 1.0, 0.05f,
329
                                          m5, m5_2);
×
330
        TuiNode* m5_4 = new TuiNodeColor(N_("Constellation boundaries"),
331
                                         constellationMgr,
332
                                         SLOT(setBoundariesColor(Vec3f)),
333
                                         constellationMgr->getBoundariesColor(), 
334
                                         m5, m5_3);
×
335
        LandscapeMgr* landscapeMgr = GETSTELMODULE(LandscapeMgr);
×
336
        TuiNode* m5_5 = new TuiNodeColor(N_("Cardinal points"),
337
                                         landscapeMgr,
338
                                         SLOT(setColorCardinalPoints(Vec3f)),
339
                                         landscapeMgr->getColorCardinalPoints(), 
340
                                         m5, m5_4);
×
341
        TuiNode* m5_6 = new TuiNodeColor(N_("Planet labels"),
342
                                         solarSystem, SLOT(setLabelsColor(Vec3f)),
343
                                         solarSystem->getLabelsColor(), 
344
                                         m5, m5_5);
×
345
        TuiNode* m5_7 = new TuiNodeColor(N_("Planet orbits"),
346
                                         solarSystem, SLOT(setOrbitsColor(Vec3f)),
347
                                         solarSystem->getOrbitsColor(), 
348
                                         m5, m5_6);
×
349
        TuiNode* m5_8 = new TuiNodeColor(N_("Planet trails"),
350
                                         solarSystem, SLOT(setTrailsColor(Vec3f)),
351
                                         solarSystem->getTrailsColor(), 
352
                                         m5, m5_7);
×
353
        GridLinesMgr* gridLinesMgr = GETSTELMODULE(GridLinesMgr);
×
354
        TuiNode* m5_9 = new TuiNodeColor(N_("Meridian line"),
355
                                         gridLinesMgr,
356
                                         SLOT(setColorMeridianLine(Vec3f)),
357
                                         gridLinesMgr->getColorMeridianLine(), 
358
                                         m5, m5_8);
×
359
        TuiNode* m5_10 = new TuiNodeColor(N_("Azimuthal grid"),
360
                                         gridLinesMgr,
361
                                         SLOT(setColorAzimuthalGrid(Vec3f)),
362
                                         gridLinesMgr->getColorAzimuthalGrid(), 
363
                                         m5, m5_9);
×
364
        TuiNode* m5_11 = new TuiNodeColor(N_("Equatorial grid"),
365
                                         gridLinesMgr,
366
                                         SLOT(setColorEquatorGrid(Vec3f)),
367
                                         gridLinesMgr->getColorEquatorGrid(), 
368
                                         m5, m5_10);
×
369
        TuiNode* m5_12 = new TuiNodeColor(N_("Equatorial J2000 grid"),
370
                                         gridLinesMgr,
371
                                         SLOT(setColorEquatorJ2000Grid(Vec3f)),
372
                                         gridLinesMgr->getColorEquatorJ2000Grid(), 
373
                                         m5, m5_11);
×
374
        TuiNode* m5_13 = new TuiNodeColor(N_("Equator line"),
375
                                         gridLinesMgr,
376
                                         SLOT(setColorEquatorLine(Vec3f)),
377
                                         gridLinesMgr->getColorEquatorLine(), 
378
                                         m5, m5_12);
×
379
        TuiNode* m5_14 = new TuiNodeColor(N_("Ecliptic line"),
380
                                         gridLinesMgr,
381
                                         SLOT(setColorEclipticLine(Vec3f)),
382
                                         gridLinesMgr->getColorEclipticLine(), 
383
                                         m5, m5_13);
×
384
        TuiNode* m5_15 = new TuiNodeColor(N_("Ecliptic line (J2000)"),
385
                                         gridLinesMgr,
386
                                         SLOT(setColorEclipticJ2000Line(Vec3f)),
387
                                         gridLinesMgr->getColorEclipticJ2000Line(),
388
                                         m5, m5_14);
×
389
// TODO: Add all other lines/grids, sort and label in a consistent manner, and put DSO stuff behind. Update Guide.
390
        NebulaMgr* nebulaMgr = GETSTELMODULE(NebulaMgr);
×
391
        TuiNode* m5_16 = new TuiNodeColor(N_("Nebula names"),
392
                                         nebulaMgr, SLOT(setLabelsColor(Vec3f)),
393
                                         nebulaMgr->getLabelsColor(), 
394
                                         m5, m5_15);
×
395
        TuiNode* m5_17 = new TuiNodeColor(N_("Nebula hints"),
396
                                          nebulaMgr, SLOT(setCirclesColor(Vec3f)),
397
                                          nebulaMgr->getCirclesColor(), 
398
                                          m5, m5_16);
×
399
        TuiNode* m5_18 = new TuiNodeColor(N_("Galaxy hints"),
400
                                          nebulaMgr, SLOT(setGalaxyColor(Vec3f)),
401
                                          nebulaMgr->getGalaxyColor(),
402
                                          m5, m5_17);
×
403
        TuiNode* m5_19 = new TuiNodeColor(N_("Dark nebulae hints"),
404
                                          nebulaMgr, SLOT(setDarkNebulaColor(Vec3f)),
405
                                          nebulaMgr->getDarkNebulaColor(),
406
                                          m5, m5_18);
×
407
        TuiNode* m5_20 = new TuiNodeColor(N_("Clusters hints"),
408
                                          nebulaMgr, SLOT(setClusterColor(Vec3f)),
409
                                          nebulaMgr->getClusterColor(),
410
                                          m5, m5_19);
×
411
        TuiNode* m5_21 = new TuiNodeColor(N_("Horizon line"),
412
                                         gridLinesMgr,
413
                                         SLOT(setColorHorizonLine(Vec3f)),
414
                                         gridLinesMgr->getColorHorizonLine(),
415
                                         m5, m5_20);
×
416
        TuiNode* m5_22 = new TuiNodeColor(N_("Galactic grid"),
417
                                         gridLinesMgr,
418
                                         SLOT(setColorGalacticGrid(Vec3f)),
419
                                         gridLinesMgr->getColorGalacticGrid(),
420
                                         m5, m5_21);
×
421
        TuiNode* m5_23 = new TuiNodeColor(N_("Galactic equator line"),
422
                                         gridLinesMgr,
423
                                         SLOT(setColorGalacticEquatorLine(Vec3f)),
424
                                         gridLinesMgr->getColorGalacticEquatorLine(),
425
                                         m5, m5_22);
×
426
        TuiNode* m5_24 = new TuiNodeColor(N_("Opposition/conjunction longitude line"),
427
                                         gridLinesMgr,
428
                                         SLOT(setColorLongitudeLine(Vec3f)),
429
                                         gridLinesMgr->getColorLongitudeLine(),
430
                                         m5, m5_23);
×
431
        StelMainView *mainView=&StelMainView::getInstance();
×
432
        TuiNode* m5_25 = new TuiNodeColor(N_("Sky Background (default: black)"),
433
                                         mainView,
434
                                         SLOT(setSkyBackgroundColor(Vec3f)),
435
                                         mainView->getSkyBackgroundColor(),
436
                                         m5, m5_24);
×
437
        m5_1->setNextNode(m5_2);
×
438
        m5_2->setNextNode(m5_3);
×
439
        m5_3->setNextNode(m5_4);
×
440
        m5_4->setNextNode(m5_5);
×
441
        m5_5->setNextNode(m5_6);
×
442
        m5_6->setNextNode(m5_7);
×
443
        m5_7->setNextNode(m5_8);
×
444
        m5_8->setNextNode(m5_9);
×
445
        m5_9->setNextNode(m5_10);
×
446
        m5_10->setNextNode(m5_11);
×
447
        m5_11->setNextNode(m5_12);
×
448
        m5_12->setNextNode(m5_13);
×
449
        m5_13->setNextNode(m5_14);
×
450
        m5_14->setNextNode(m5_15);
×
451
        m5_15->setNextNode(m5_16);
×
452
        m5_16->setNextNode(m5_17);
×
453
        m5_17->setNextNode(m5_18);
×
454
        m5_18->setNextNode(m5_19);
×
455
        m5_19->setNextNode(m5_20);
×
456
        m5_20->setNextNode(m5_21);
×
457
        m5_21->setNextNode(m5_22);
×
458
        m5_22->setNextNode(m5_23);
×
459
        m5_23->setNextNode(m5_24);
×
460
        m5_24->setNextNode(m5_25);        
×
461
        m5_25->setNextNode(m5_1);
×
462
        m5_1->loopToTheLast();
×
463
        m5->setChildNode(m5_1);
×
464

465
        TuiNode* m6 = new TuiNode(N_("Effects"), Q_NULLPTR, m5);
×
466
        m6->setParent(this);
×
467
        m5->setNextNode(m6);
×
468
        TuiNode* m6_1 = new TuiNodeInt(N_("Light pollution:"),
469
                                       this,
470
                                       SLOT(setLightPollutionLevel(int)),
471
                                       skyDrawer->getBortleScaleIndex(), 1, 9, 1,
472
                                       m6);
×
473
        TuiNode* m6_2 = new TuiNodeEnum(N_("Landscape"),
474
                                        landscapeMgr,
475
                                        SLOT(setCurrentLandscapeName(QString)),
476
                                        landscapeMgr->getAllLandscapeNames(),
×
477
                                        landscapeMgr->getCurrentLandscapeName(),
×
478
                                        m6, m6_1);
×
479
        TuiNode* m6_3 = new TuiNodeBool(N_("Setting landscape sets location"),
480
                                        landscapeMgr,
481
                                        SLOT(setFlagLandscapeSetsLocation(bool)),
482
                                        landscapeMgr->getFlagLandscapeSetsLocation(),
×
483
                                        m6, m6_2);
×
484
        StelMovementMgr* movementMgr = GETSTELMODULE(StelMovementMgr);
×
485
        TuiNode* m6_4 = new TuiNodeBool(N_("Auto zoom out returns to initial direction of view"),
486
                                        movementMgr,
487
                                        SLOT(setFlagAutoZoomOutResetsDirection(bool)), 
488
                                        movementMgr->getFlagAutoZoomOutResetsDirection(), 
×
489
                                        m6, m6_3);
×
490
        TuiNode* m6_5 = new TuiNodeFloat(N_("Zoom duration:"),
491
                                         movementMgr,
492
                                         SLOT(setAutoMoveDuration(float)),
493
                                         movementMgr->getAutoMoveDuration(),
494
                                         0, 20.0, 0.1f,
495
                                         m6, m6_4);
×
496
        TuiNode* m6_6 = new TuiNodeDouble(N_("Milky Way intensity:"),
497
                                         GETSTELMODULE(MilkyWay),
×
498
                                         SLOT(setIntensity(double)),
499
                                         GETSTELMODULE(MilkyWay)->getIntensity(),
×
500
                                         0, 10.0, 0.1, 
501
                                         m6, m6_5);
×
502
        TuiNode* m6_7 = new TuiNodeDouble(N_("Milky Way saturation:"),
503
                                         GETSTELMODULE(MilkyWay),
×
504
                                         SLOT(setSaturation(double)),
505
                                         GETSTELMODULE(MilkyWay)->getSaturation(),
×
506
                                         0, 1.0, 0.1,
507
                                         m6, m6_6);
×
508
        TuiNode* m6_8 = new TuiNodeDouble(N_("Zodiacal light intensity:"),
509
                                         GETSTELMODULE(ZodiacalLight),
×
510
                                         SLOT(setIntensity(double)),
511
                                         GETSTELMODULE(ZodiacalLight)->getIntensity(),
×
512
                                         0, 10.0, 0.1,
513
                                         m6, m6_7);
×
514
        m6_1->setNextNode(m6_2);
×
515
        m6_2->setNextNode(m6_3);
×
516
        m6_3->setNextNode(m6_4);
×
517
        m6_4->setNextNode(m6_5);
×
518
        m6_5->setNextNode(m6_6);
×
519
        m6_6->setNextNode(m6_7);
×
520
        m6_7->setNextNode(m6_8);
×
521
        m6_8->setNextNode(m6_1);
×
522
        m6_1->loopToTheLast();
×
523
        m6->setChildNode(m6_1);
×
524

525
        #ifdef ENABLE_SCRIPTING
526
        TuiNode* m7 = new TuiNode(N_("Scripts"), Q_NULLPTR, m6);
×
527
        m7->setParent(this);
×
528
        m6->setNextNode(m7);        
×
529
        StelScriptMgr& scriptMgr = StelApp::getInstance().getScriptMgr();
×
530
        TuiNode* m7_1 = new TuiNodeEnum(N_("Run local script"),
531
                                        &scriptMgr,
532
                                        SLOT(runScript(QString)),
533
                                        scriptMgr.getScriptList(),
×
534
                                        "",
535
                                        m7);
×
536
        TuiNode* m7_2 = new TuiNodeActivate(N_("Stop running script"),
537
                                            &scriptMgr, SLOT(stopScript()),
538
                                            m7, m7_1);
×
539
//        TuiNode* m7_3 = new TuiNode(N_("CD/DVD script"), m7, m7_2); // Dead node.
540
        m7_1->setNextNode(m7_2);
×
541
        m7_2->setNextNode(m7_1);
×
542
//        m7_3->setNextNode(m7_1);
543
        m7_1->loopToTheLast();
×
544
        m7->setChildNode(m7_1);
×
545

546

547
        TuiNode* m8 = new TuiNode(N_("Administration"), Q_NULLPTR, m7);
×
548
        m8->setParent(this);
×
549
        m7->setNextNode(m8);
×
550
        #else
551
        TuiNode* m8 = new TuiNode(N_("Administration"), Q_NULLPTR, m6);
552
        m8->setParent(this);
553
        m6->setNextNode(m8);
554
        #endif
555
        m8->setNextNode(m1);
×
556
        m1->loopToTheLast();
×
557
        TuiNode* m8_1 = new TuiNode(N_("Load default configuration"), m8);
×
558
        TuiNode* m8_2 = new TuiNodeActivate(N_("Save current configuration"),
559
                                            this, SLOT(saveDefaultSettings()),
560
                                            m8, m8_1);
×
561
        TuiNode* m8_3 = new TuiNodeActivate(N_("Shut down"), this, SLOT(shutDown()), 
562
                                            m8, m8_2);
×
563
        m8_1->setNextNode(m8_2);
×
564
        m8_2->setNextNode(m8_3);
×
565
        m8_3->setNextNode(m8_1);
×
566
        m8_1->loopToTheLast();
×
567
        m8->setChildNode(m8_1);
×
568

569

570
        currentNode = m1;
×
571

572
        addAction("actionShow_TUI_dateTime",   N_("Text User Interface"), N_("Toggle TUI date&time"),   this, "tuiDateTime", ""); // Recommend "Ctrl+Alt+T", but conflicts with Equation of Time.
×
573
        addAction("actionShow_TUI_objectInfo", N_("Text User Interface"), N_("Toggle TUI object info"), this, "tuiObjInfo",  ""); // Recommend  "Ctrl+Alt+Shift+T"
×
574
}
×
575

576
/*************************************************************************
577
 Load settings from configuration file.
578
*************************************************************************/
579
void TextUserInterface::loadConfiguration(void)
×
580
{
581
        QSettings* conf = StelApp::getInstance().getSettings();
×
582
        Q_ASSERT(conf);
×
583

584
        font.setPixelSize(conf->value("tui/tui_font_size", 15).toInt());
×
585
        setTuiDateTime(conf->value("tui/flag_show_tui_datetime", false).toBool());
×
586
        setTuiObjInfo(conf->value("tui/flag_show_tui_short_obj_info", false).toBool());
×
587
        setTuiGravityUi(conf->value("tui/flag_show_gravity_ui", false).toBool());
×
588
        color = Vec3f(conf->value("tui/tui_font_color", "0.3,1,0.3").toString());
×
589
        StelCore *core=StelApp::getInstance().getCore();
×
590
        connect(core, SIGNAL(flagGravityLabelsChanged(bool)), this, SLOT(setTuiGravityUi(bool)));
×
591
}
×
592

593
/*************************************************************************
594
 Draw our module.
595
*************************************************************************/
596
void TextUserInterface::draw(StelCore* core)
×
597
{
598
        if (!tuiActive && !tuiDateTime && !tuiObjInfo)
×
599
                return;
×
600

601
        int x = 0, y = 0;
×
602
        int xVc = 0, yVc = 0;
×
603
        int pixOffset = 15;
×
604
        int fovOffsetX = 0, fovOffsetY=0;
×
605
        bool fovMaskDisk = false;
×
606

607
        StelGui* gui = dynamic_cast<StelGui*>(StelApp::getInstance().getGui());
×
608
        if (gui!=Q_NULLPTR)
×
609
        {
610
                if (gui->getVisible())
×
611
                {
612
                        QGraphicsItem* bottomBar = dynamic_cast<QGraphicsItem*>(gui->getButtonBar());
×
613
                        LeftStelBar* sideBar = gui->getWindowsButtonBar();
×
614
                        x = (sideBar) ? qRound(sideBar->boundingRectNoHelpLabel().right()) : 50;
×
615
                        y = (bottomBar) ? qRound(bottomBar->boundingRect().height()) : 50;
×
616
                }
617
        }
618

619
        // Alternate x,y for Disk viewport
620
        if (core->getProjection(StelCore::FrameJ2000)->getMaskType() == StelProjector::MaskDisk)
×
621
        {
622
                fovMaskDisk = true;
×
623
                StelProjector::StelProjectorParams projParams = core->getCurrentStelProjectorParams();
×
624
                xVc = qRound(projParams.viewportCenter[0]);
×
625
                yVc = qRound(projParams.viewportCenter[1]);
×
626
                fovOffsetX = qRound(projParams.viewportFovDiameter*std::sin(20.))/2;
×
627
                fovOffsetY = qRound(projParams.viewportFovDiameter*std::cos(20.))/2;
×
628
        }
629
        else 
630
        {
631
                xVc = core->getProjection(StelCore::FrameJ2000)->getViewportWidth()/2;
×
632
        }
633

634
        if (tuiActive)
×
635
        {
636
                int text_x = x + pixOffset, text_y = y + pixOffset;
×
637
                if (fovMaskDisk) {
×
638
                        text_x = xVc - fovOffsetX + pixOffset;
×
639
                        text_y = yVc - fovOffsetY + pixOffset;
×
640
                }
641
                        
642
                QString tuiText = q_("[no TUI node]");
×
643
                if (currentNode!=Q_NULLPTR) {
×
644
                        tuiText = currentNode->getDisplayText();
×
645
                }
646

647
                StelPainter painter(core->getProjection(StelCore::FrameJ2000));
×
648
                painter.setFont(font);
×
649
                painter.setColor(color[0],color[1],color[2]);
×
650
                painter.drawText(text_x, text_y, tuiText, 0, 0, 0, !tuiGravityUi);
×
651
        }
×
652

653
        if (tuiDateTime) 
×
654
        {
655
                double jd = core->getJD();
×
656
                int text_x = x + xVc*2/3, text_y = y + pixOffset;
×
657
                const double utcOffsetHrs=core->getUTCOffset(jd);
×
658

659
                QString newDate = StelApp::getInstance().getLocaleMgr().getPrintableDateLocal(jd, utcOffsetHrs) + "   "
×
660
                       +StelApp::getInstance().getLocaleMgr().getPrintableTimeLocal(jd, utcOffsetHrs);
×
661
                 
662
                if (fovMaskDisk) {
×
663
                        text_x = xVc + fovOffsetY - pixOffset;
×
664
                        text_y = yVc - fovOffsetX + pixOffset;
×
665
                }
666

667
                StelPainter painter(core->getProjection(StelCore::FrameAltAz));
×
668
                painter.setFont(font);
×
669
                painter.setColor(color[0],color[1],color[2]);
×
670
                painter.drawText(text_x, text_y, newDate, 0, 0, 0, !tuiGravityUi);
×
671
        }
×
672

673
        if (tuiObjInfo) 
×
674
        {
675
                QString objInfo = ""; 
×
676
                StelObject::InfoStringGroup tuiInfo(StelObject::Name|StelObject::CatalogNumber
×
677
                                |StelObject::Distance|StelObject::PlainText);
678
                int text_x = x + xVc*4/3, text_y = y + pixOffset; 
×
679

680
                QList<StelObjectP> selectedObj = GETSTELMODULE(StelObjectMgr)->getSelectedObject();
×
681
                if (selectedObj.isEmpty()) {
×
682
                        objInfo = "";        
×
683
                } else {
684
                        objInfo = selectedObj[0]->getInfoString(core, tuiInfo);
×
685
                        objInfo.replace("\n"," ");
×
686
                        objInfo.replace("Distance:"," ");
×
687
                        objInfo.replace("Light Years","ly");
×
688
                }
689

690
                if (fovMaskDisk) {
×
691
                        text_x = xVc + fovOffsetX - pixOffset;
×
692
                        text_y = yVc + fovOffsetY - pixOffset;
×
693
                }
694

695
                StelPainter painter(core->getProjection(StelCore::FrameJ2000));
×
696
                painter.setFont(font);
×
697
                painter.setColor(color[0],color[1],color[2]);
×
698
                painter.drawText(text_x, text_y, objInfo, 0, 0, 0, !tuiGravityUi);
×
699
        }
×
700
}
701

702
void TextUserInterface::handleKeys(QKeyEvent* event)
×
703
{
704
        if (currentNode == Q_NULLPTR)
×
705
        {
706
                qWarning() << "No current node in TextUserInterface plugin";
×
707
                event->setAccepted(false);
×
708
                return;
×
709
        }
710

711
        if (event->type()==QEvent::KeyPress && (event->modifiers() & Qt::AltModifier) &&  event->key()==Qt::Key_T)
×
712
        {
713
                tuiActive = ! tuiActive;
×
714
                dummyDialog.close();
×
715
                event->setAccepted(true);
×
716
                return;
×
717
        }
718

719
        if (!tuiActive)
×
720
        {
721
                event->setAccepted(false);
×
722
                return;
×
723
        }
724

725
        if (event->type()==QEvent::KeyPress)
×
726
        {
727
                TuiNodeResponse response = currentNode->handleKey(event->key());
×
728
                if (response.accepted)
×
729
                {
730
                        currentNode = response.newNode;
×
731
                        event->setAccepted(true);
×
732
                }
733
                else
734
                {
735
                        event->setAccepted(false);
×
736
                }
737
                return;
×
738
        }
739
}
740

741
void TextUserInterface::setHomePlanet(const QString &planetName)
×
742
{
743
        StelCore* core = StelApp::getInstance().getCore();
×
744
        if (core->getCurrentLocation().planetName != planetName)
×
745
        {
746
                StelLocation newLocation = core->getCurrentLocation();
×
747
                newLocation.planetName = planetName;
×
748
                core->moveObserverTo(newLocation);
×
749
        }
×
750
}
×
751

752
void TextUserInterface::setAltitude(int altitude)
×
753
{
754
        StelCore* core = StelApp::getInstance().getCore();
×
755
        if (core->getCurrentLocation().altitude != altitude)
×
756
        {
757
                StelLocation newLocation = core->getCurrentLocation();
×
758
                newLocation.altitude = altitude;
×
759
                core->moveObserverTo(newLocation, 0.0, 0.0);
×
760
        }
×
761
}
×
762

763
void TextUserInterface::setLatitude(double latitude)
×
764
{
765
        StelCore* core = StelApp::getInstance().getCore();
×
766
        if (core->getCurrentLocation().getLatitude() != latitude)
×
767
        {
768
                StelLocation newLocation = core->getCurrentLocation();
×
769
                newLocation.setLatitude(static_cast<float>(latitude));
×
770
                core->moveObserverTo(newLocation, 0.0, 0.0);
×
771
        }
×
772
}
×
773

774
void TextUserInterface::setLongitude(double longitude)
×
775
{
776
        StelCore* core = StelApp::getInstance().getCore();
×
777
        if (core->getCurrentLocation().getLongitude() != longitude)
×
778
        {
779
                StelLocation newLocation = core->getCurrentLocation();
×
780
                newLocation.setLongitude(static_cast<float>(longitude));
×
781
                core->moveObserverTo(newLocation, 0.0, 0.0);
×
782
        }
×
783
}
×
784

785
double TextUserInterface::getLatitude(void)
×
786
{
787
        return static_cast<double>(StelApp::getInstance().getCore()->getCurrentLocation().getLatitude());
×
788
}
789

790
double TextUserInterface::getLongitude(void)
×
791
{
792
        return static_cast<double>(StelApp::getInstance().getCore()->getCurrentLocation().getLongitude());
×
793
}
794

795
void TextUserInterface::setStartupDateMode(const QString &mode)
×
796
{
797
        StelApp::getInstance().getCore()->setStartupTimeMode(mode);
×
798
}
×
799

800
void TextUserInterface::setDateFormat(const QString &format)
×
801
{
802
        StelApp::getInstance().getLocaleMgr().setDateFormatStr(format);
×
803
}
×
804

805
void TextUserInterface::setTimeFormat(const QString &format)
×
806
{
807
        StelApp::getInstance().getLocaleMgr().setTimeFormatStr(format);
×
808
}
×
809

810
void TextUserInterface::setSkyCulture(const QString &i18)
×
811
{
812
        StelApp::getInstance().getSkyCultureMgr().setCurrentSkyCultureNameI18(i18);
×
813
}
×
814

815
void TextUserInterface::setAppLanguage(const QString &lang)
×
816
{
817
        QString code = StelTranslator::nativeNameToIso639_1Code(lang);
×
818
        StelApp::getInstance().getLocaleMgr().setAppLanguage(code);
×
819
}
×
820

821
void TextUserInterface::setLightPollutionLevel(const int level)
×
822
{
823
    const auto lum = StelCore::bortleScaleIndexToLuminance(level);
×
824
    const auto core = StelApp::getInstance().getCore();
×
825
    core->getSkyDrawer()->setLightPollutionLuminance(lum);
×
826
}
×
827

828
void TextUserInterface::saveDefaultSettings(void)
×
829
{
830
        QSettings* conf = StelApp::getInstance().getSettings();
×
831
        Q_ASSERT(conf);
×
832

833
        LandscapeMgr* lmgr = GETSTELMODULE(LandscapeMgr);
×
834
        Q_ASSERT(lmgr);
×
835
        SolarSystem* ssmgr = GETSTELMODULE(SolarSystem);
×
836
        Q_ASSERT(ssmgr);
×
837
        StelSkyDrawer* skyd = StelApp::getInstance().getCore()->getSkyDrawer();
×
838
        Q_ASSERT(skyd);
×
839
        ConstellationMgr* cmgr = GETSTELMODULE(ConstellationMgr);
×
840
        Q_ASSERT(cmgr);
×
841
        StarMgr* smgr = GETSTELMODULE(StarMgr);
×
842
        Q_ASSERT(smgr);
×
843
        NebulaMgr* nmgr = GETSTELMODULE(NebulaMgr);
×
844
        Q_ASSERT(nmgr);
×
845
        GridLinesMgr* glmgr = GETSTELMODULE(GridLinesMgr);
×
846
        Q_ASSERT(glmgr);
×
847
        StelMovementMgr* mvmgr = GETSTELMODULE(StelMovementMgr);
×
848
        Q_ASSERT(mvmgr);
×
849
        StelCore* core = StelApp::getInstance().getCore();
×
850
        Q_ASSERT(core);
×
851
        MilkyWay* milk = GETSTELMODULE(MilkyWay);
×
852
        Q_ASSERT(milk);
×
853
        const StelProjectorP proj = StelApp::getInstance().getCore()->getProjection(StelCore::FrameJ2000);
×
854
        Q_ASSERT(proj);
×
855
        StelLocaleMgr& lomgr = StelApp::getInstance().getLocaleMgr();
×
856

857
        // MENU ITEMS
858
        // sub-menu 1: location
859
        // TODO
860
        
861

862
        // sub-menu 2: date and time
863
        conf->setValue("navigation/preset_sky_time", core->getPresetSkyTime());
×
864
        conf->setValue("navigation/startup_time_mode", core->getStartupTimeMode());
×
865
        conf->setValue("navigation/startup_time_mode", core->getStartupTimeMode());
×
866
        conf->setValue("localization/time_display_format", lomgr.getTimeFormatStr());
×
867
        conf->setValue("localization/date_display_format", lomgr.getDateFormatStr());
×
868

869

870
        // sub-menu 3: general
871
        StelApp::getInstance().getSkyCultureMgr().setDefaultSkyCultureID(StelApp::getInstance().getSkyCultureMgr().getCurrentSkyCultureID());
×
872
        QString langName = lomgr.getAppLanguage();
×
873
        conf->setValue("localization/app_locale", StelTranslator::nativeNameToIso639_1Code(langName));
×
874
        langName = StelApp::getInstance().getLocaleMgr().getSkyLanguage();
×
875
        conf->setValue("localization/sky_locale", StelTranslator::nativeNameToIso639_1Code(langName));
×
876

877
        // sub-menu 4: stars
878
        conf->setValue("astro/flag_stars", smgr->getFlagStars());
×
879
        conf->setValue("stars/absolute_scale", skyd->getAbsoluteStarScale());
×
880
        conf->setValue("stars/relative_scale", skyd->getRelativeStarScale());
×
881
        conf->setValue("stars/flag_star_twinkle", skyd->getFlagTwinkle());
×
882

883
        // sub-menu 5: colors
884
        conf->setValue("color/const_lines_color", colToConf(cmgr->getLinesColor()));
×
885
         conf->setValue("color/const_names_color", colToConf(cmgr->getLabelsColor()));
×
886
        conf->setValue("color/const_boundary_color", colToConf(cmgr->getBoundariesColor()));
×
887
        conf->setValue("viewing/constellation_art_intensity", cmgr->getArtIntensity());
×
888
        conf->setValue("color/cardinal_color", colToConf(lmgr->getColorCardinalPoints()) );
×
889
        conf->setValue("color/planet_names_color", colToConf(ssmgr->getLabelsColor()));
×
890
        conf->setValue("color/planet_orbits_color", colToConf(ssmgr->getOrbitsColor()));
×
891
        conf->setValue("color/object_trails_color", colToConf(ssmgr->getTrailsColor()));
×
892
        conf->setValue("color/meridian_color", colToConf(glmgr->getColorMeridianLine()));
×
893
        conf->setValue("color/oc_longitude_color", colToConf(glmgr->getColorLongitudeLine()));
×
894
        conf->setValue("color/azimuthal_color", colToConf(glmgr->getColorAzimuthalGrid()));
×
895
        conf->setValue("color/equator_color", colToConf(glmgr->getColorEquatorGrid()));
×
896
        conf->setValue("color/equatorial_J2000_color", colToConf(glmgr->getColorEquatorJ2000Grid()));
×
897
        conf->setValue("color/equator_color", colToConf(glmgr->getColorEquatorLine()));
×
898
        conf->setValue("color/ecliptic_color", colToConf(glmgr->getColorEclipticLine()));
×
899
        conf->setValue("color/nebula_label_color", colToConf(nmgr->getLabelsColor()));
×
900
        conf->setValue("color/nebula_circle_color", colToConf(nmgr->getCirclesColor()));
×
901

902
        // sub-menu 6: effects
903
        conf->setValue("stars/init_bortle_scale", skyd->getBortleScaleIndex());
×
904
        lmgr->setDefaultLandscapeID(lmgr->getCurrentLandscapeID());
×
905
        conf->setValue("navigation/auto_zoom_out_resets_direction", mvmgr->getFlagAutoZoomOutResetsDirection());
×
906
        conf->setValue("astro/milky_way_intensity", milk->getIntensity());
×
907
        conf->setValue("navigation/auto_move_duration", mvmgr->getAutoMoveDuration());
×
908
        conf->setValue("landscape/flag_landscape_sets_location", lmgr->getFlagLandscapeSetsLocation());
×
909

910
        // GLOBAL DISPLAY SETTINGS
911
        // TODO 
912
        
913
        qDebug() << "TextUserInterface::saveDefaultSettings done";
×
914
}
×
915

916
void TextUserInterface::shutDown()
×
917
{
918
        QSettings* conf = StelApp::getInstance().getSettings();
×
919
        QString shutdownCmd = QDir::fromNativeSeparators(conf->value("tui/admin_shutdown_cmd", "").toString());
×
920
        int err; 
921
        if (!(err = QProcess::execute(shutdownCmd, {}))) {
×
922
                qDebug() << "[TextUserInterface] shutdown error, QProcess::execute():" << err;
×
923
        }
924
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc