1 /*
2 SPDX-FileCopyrightText: 2002 Jason Harris <jharris@30doradus.org>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
7 #include "kstars.h"
8
9 #include "fov.h"
10 #include "kspaths.h"
11 #include "kstarsdata.h"
12 #include "kstars_debug.h"
13 #include "Options.h"
14 #include "skymap.h"
15 #include "texturemanager.h"
16 #include "projections/projector.h"
17 #include "skycomponents/skymapcomposite.h"
18 #include "skyobjects/ksplanetbase.h"
19 #include "widgets/timespinbox.h"
20 #include "widgets/timestepbox.h"
21 #include "widgets/timeunitbox.h"
22 #include "hips/hipsmanager.h"
23 #include "auxiliary/thememanager.h"
24
25 #ifdef HAVE_INDI
26 #include "indi/drivermanager.h"
27 #include "indi/guimanager.h"
28 #include "ekos/manager.h"
29 #endif
30
31 #include <KActionCollection>
32 #include <KActionMenu>
33 #include <KTipDialog>
34 #include <KToggleAction>
35 #include <KToolBar>
36
37 #include <QMenu>
38 #include <QStatusBar>
39
40 //This file contains functions that kstars calls at startup (except constructors).
41 //These functions are declared in kstars.h
42
43 namespace
44 {
45 // A lot of QAction is defined there. In order to decrease amount
46 // of boilerplate code a trick with << operator overloading is used.
47 // This makes code more concise and readable.
48 //
49 // When data type could not used directly. Either because of
50 // overloading rules or because one data type have different
51 // semantics its wrapped into struct.
52 //
53 // Downside is unfamiliar syntax and really unhelpful error
54 // messages due to general abuse of << overloading
55
56 // Set QAction text
operator <<(QAction * ka,QString text)57 QAction *operator<<(QAction *ka, QString text)
58 {
59 ka->setText(text);
60 return ka;
61 }
62 // Set icon for QAction
operator <<(QAction * ka,const QIcon & icon)63 QAction *operator<<(QAction *ka, const QIcon &icon)
64 {
65 ka->setIcon(icon);
66 return ka;
67 }
68 // Set keyboard shortcut
operator <<(QAction * ka,const QKeySequence sh)69 QAction *operator<<(QAction *ka, const QKeySequence sh)
70 {
71 KStars::Instance()->actionCollection()->setDefaultShortcut(ka, sh);
72 //ka->setShortcut(sh);
73 return ka;
74 }
75
76 // Add action to group. AddToGroup struct acts as newtype wrapper
77 // in order to allow overloading.
78 struct AddToGroup
79 {
80 QActionGroup *grp;
AddToGroup__anon49b0be1b0111::AddToGroup81 AddToGroup(QActionGroup *g) : grp(g) {}
82 };
operator <<(QAction * ka,AddToGroup g)83 QAction *operator<<(QAction *ka, AddToGroup g)
84 {
85 g.grp->addAction(ka);
86 return ka;
87 }
88
89 // Set checked property. Checked is newtype wrapper.
90 struct Checked
91 {
92 bool flag;
Checked__anon49b0be1b0111::Checked93 Checked(bool f) : flag(f) {}
94 };
operator <<(QAction * ka,Checked chk)95 QAction *operator<<(QAction *ka, Checked chk)
96 {
97 ka->setCheckable(true);
98 ka->setChecked(chk.flag);
99 return ka;
100 }
101
102 // Set tool tip. ToolTip is used as newtype wrapper.
103 struct ToolTip
104 {
105 QString tip;
ToolTip__anon49b0be1b0111::ToolTip106 ToolTip(QString msg) : tip(msg) {}
107 };
operator <<(QAction * ka,const ToolTip & tool)108 QAction *operator<<(QAction *ka, const ToolTip &tool)
109 {
110 ka->setToolTip(tool.tip);
111 return ka;
112 }
113
114 // Create new KToggleAction and connect slot to toggled(bool) signal
newToggleAction(KActionCollection * col,QString name,QString text,QObject * receiver,const char * member)115 QAction *newToggleAction(KActionCollection *col, QString name, QString text, QObject *receiver, const char *member)
116 {
117 QAction *ka = col->add<KToggleAction>(name) << text;
118 QObject::connect(ka, SIGNAL(toggled(bool)), receiver, member);
119 return ka;
120 }
121 }
122
123 // Resource file override - used by UI tests
124 QString KStars::m_KStarsUIResource = "kstarsui.rc";
setResourceFile(QString const rc)125 bool KStars::setResourceFile(QString const rc)
126 {
127 if (QFile(rc).exists())
128 {
129 m_KStarsUIResource = rc;
130 return true;
131 }
132 else return false;
133 }
134
135
initActions()136 void KStars::initActions()
137 {
138 // Check if we have this specific Breeze icon. If not, try to set the theme search path and if appropriate, the icon theme rcc file
139 // in each OS
140 if (!QIcon::hasThemeIcon(QLatin1String("kstars_flag")))
141 KSTheme::Manager::instance()->setIconTheme(KSTheme::Manager::BREEZE_DARK_THEME);
142
143 QAction *ka;
144
145 // ==== File menu ================
146 ka = new QAction(QIcon::fromTheme("favorites"), i18n("Download New Data..."), this);
147 connect(ka, &QAction::triggered, this, &KStars::slotDownload);
148 ka->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_N));
149 ka->setWhatsThis(i18n("Downloads new data"));
150 ka->setToolTip(ka->whatsThis());
151 ka->setStatusTip(ka->whatsThis());
152 actionCollection()->addAction(QStringLiteral("get_data"), ka);
153
154 #ifdef HAVE_CFITSIO
155 actionCollection()->addAction("open_file", this, SLOT(slotOpenFITS()))
156 << i18n("Open Image...") << QIcon::fromTheme("document-open")
157 << QKeySequence(Qt::CTRL + Qt::Key_O);
158 #endif
159 actionCollection()->addAction("export_image", this, SLOT(slotExportImage()))
160 << i18n("&Save Sky Image...")
161 << QIcon::fromTheme("document-export-image");
162
163 // 2017-09-17 Jasem: FIXME! Scripting does not work properly under non UNIX systems.
164 // It must be updated to use DBus session bus from Qt (like scheduler)
165 #ifndef Q_OS_WIN
166 actionCollection()->addAction("run_script", this, SLOT(slotRunScript()))
167 << i18n("&Run Script...") << QIcon::fromTheme("system-run")
168 << QKeySequence(Qt::CTRL + Qt::Key_R);
169 #endif
170 actionCollection()->addAction("printing_wizard", this, SLOT(slotPrintingWizard()))
171 << i18nc("start Printing Wizard", "Printing &Wizard...");
172 ka = actionCollection()->addAction(KStandardAction::Print, "print", this, SLOT(slotPrint()));
173 ka->setIcon(QIcon::fromTheme("document-print"));
174 //actionCollection()->addAction( KStandardAction::Quit, "quit", this, SLOT(close) );
175 ka = actionCollection()->addAction(KStandardAction::Quit, "quit", qApp, SLOT(closeAllWindows()));
176 ka->setIcon(QIcon::fromTheme("application-exit"));
177
178 // ==== Time Menu ================
179 actionCollection()->addAction("time_to_now", this, SLOT(slotSetTimeToNow()))
180 << i18n("Set Time to &Now") << QKeySequence(Qt::CTRL + Qt::Key_E)
181 << QIcon::fromTheme("clock");
182
183 actionCollection()->addAction("time_dialog", this, SLOT(slotSetTime()))
184 << i18nc("set Clock to New Time", "&Set Time...") << QKeySequence(Qt::CTRL + Qt::Key_S)
185 << QIcon::fromTheme("clock");
186
187 ka = actionCollection()->add<KToggleAction>("clock_startstop")
188 << i18n("Stop &Clock")
189 << QIcon::fromTheme("media-playback-pause");
190 if (!StartClockRunning)
191 ka->toggle();
192 QObject::connect(ka, SIGNAL(triggered()), this, SLOT(slotToggleTimer()));
193
194 // If we are started in --paused state make sure the icon reflects that now
195 if (StartClockRunning == false)
196 {
197 QAction *a = actionCollection()->action("clock_startstop");
198 if (a)
199 a->setIcon(QIcon::fromTheme("run-build-install-root"));
200 }
201
202 QObject::connect(data()->clock(), &SimClock::clockToggled, [ = ](bool toggled)
203 {
204 QAction *a = actionCollection()->action("clock_startstop");
205 if (a)
206 {
207 a->setChecked(toggled);
208 // Many users forget to unpause KStars, so we are using run-build-install-root icon which is red
209 // and stands out from the rest of the icons so users are aware when KStars is paused visually
210 a->setIcon(toggled ? QIcon::fromTheme("run-build-install-root") : QIcon::fromTheme("media-playback-pause"));
211 a->setToolTip(toggled ? i18n("Resume Clock") : i18n("Stop Clock"));
212 }
213 });
214 //UpdateTime() if clock is stopped (so hidden objects get drawn)
215 QObject::connect(data()->clock(), SIGNAL(clockToggled(bool)), this, SLOT(updateTime()));
216 actionCollection()->addAction("time_step_forward", this, SLOT(slotStepForward()))
217 << i18n("Advance One Step Forward in Time")
218 << QIcon::fromTheme("media-skip-forward")
219 << QKeySequence(Qt::Key_Greater);
220 actionCollection()->addAction("time_step_backward", this, SLOT(slotStepBackward()))
221 << i18n("Advance One Step Backward in Time")
222 << QIcon::fromTheme("media-skip-backward")
223 << QKeySequence(Qt::Key_Less);
224
225 // ==== Pointing Menu ================
226 actionCollection()->addAction("zenith", this, SLOT(slotPointFocus())) << i18n("&Zenith") << QKeySequence("Z");
227 actionCollection()->addAction("north", this, SLOT(slotPointFocus())) << i18n("&North") << QKeySequence("N");
228 actionCollection()->addAction("east", this, SLOT(slotPointFocus())) << i18n("&East") << QKeySequence("E");
229 actionCollection()->addAction("south", this, SLOT(slotPointFocus())) << i18n("&South") << QKeySequence("S");
230 actionCollection()->addAction("west", this, SLOT(slotPointFocus())) << i18n("&West") << QKeySequence("W");
231
232 actionCollection()->addAction("find_object", this, SLOT(slotFind()))
233 << i18n("&Find Object...") << QIcon::fromTheme("edit-find")
234 << QKeySequence(Qt::CTRL + Qt::Key_F);
235 actionCollection()->addAction("track_object", this, SLOT(slotTrack()))
236 << i18n("Engage &Tracking")
237 << QIcon::fromTheme("object-locked")
238 << QKeySequence(Qt::CTRL + Qt::Key_T);
239 actionCollection()->addAction("manual_focus", this, SLOT(slotManualFocus()))
240 << i18n("Set Coordinates &Manually...") << QKeySequence(Qt::CTRL + Qt::Key_M);
241
242 QAction *action;
243
244 // ==== View Menu ================
245 action = actionCollection()->addAction(KStandardAction::ZoomIn, "zoom_in", map(), SLOT(slotZoomIn()));
246 action->setIcon(QIcon::fromTheme("zoom-in"));
247
248 action = actionCollection()->addAction(KStandardAction::ZoomOut, "zoom_out", map(), SLOT(slotZoomOut()));
249 action->setIcon(QIcon::fromTheme("zoom-out"));
250
251 actionCollection()->addAction("zoom_default", map(), SLOT(slotZoomDefault()))
252 << i18n("&Default Zoom") << QIcon::fromTheme("zoom-fit-best")
253 << QKeySequence(Qt::CTRL + Qt::Key_Z);
254 actionCollection()->addAction("zoom_set", this, SLOT(slotSetZoom()))
255 << i18n("&Zoom to Angular Size...")
256 << QIcon::fromTheme("zoom-original")
257 << QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_Z);
258
259 action = actionCollection()->addAction(KStandardAction::FullScreen, this, SLOT(slotFullScreen()));
260 action->setIcon(QIcon::fromTheme("view-fullscreen"));
261
262 actionCollection()->addAction("coordsys", this, SLOT(slotCoordSys()))
263 << (Options::useAltAz() ? i18n("Switch to Star Globe View (Equatorial &Coordinates)") :
264 i18n("Switch to Horizonal View (Horizontal &Coordinates)"))
265 << QKeySequence("Space");
266
267 actionCollection()->addAction("toggle_terrain", this, SLOT(slotTerrain()))
268 << (Options::showTerrain() ? i18n("Hide Terrain") :
269 i18n("Show Terrain"))
270 << QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_T);
271
272 actionCollection()->addAction("project_lambert", this, SLOT(slotMapProjection()))
273 << i18n("&Lambert Azimuthal Equal-area") << QKeySequence("F5") << AddToGroup(projectionGroup)
274 << Checked(Options::projection() == Projector::Lambert);
275 actionCollection()->addAction("project_azequidistant", this, SLOT(slotMapProjection()))
276 << i18n("&Azimuthal Equidistant") << QKeySequence("F6") << AddToGroup(projectionGroup)
277 << Checked(Options::projection() == Projector::AzimuthalEquidistant);
278 actionCollection()->addAction("project_orthographic", this, SLOT(slotMapProjection()))
279 << i18n("&Orthographic") << QKeySequence("F7") << AddToGroup(projectionGroup)
280 << Checked(Options::projection() == Projector::Orthographic);
281 actionCollection()->addAction("project_equirectangular", this, SLOT(slotMapProjection()))
282 << i18n("&Equirectangular") << QKeySequence("F8") << AddToGroup(projectionGroup)
283 << Checked(Options::projection() == Projector::Equirectangular);
284 actionCollection()->addAction("project_stereographic", this, SLOT(slotMapProjection()))
285 << i18n("&Stereographic") << QKeySequence("F9") << AddToGroup(projectionGroup)
286 << Checked(Options::projection() == Projector::Stereographic);
287 actionCollection()->addAction("project_gnomonic", this, SLOT(slotMapProjection()))
288 << i18n("&Gnomonic") << QKeySequence("F10") << AddToGroup(projectionGroup)
289 << Checked(Options::projection() == Projector::Gnomonic);
290
291 //Settings Menu:
292 //Info Boxes option actions
293 QAction *kaBoxes = actionCollection()->add<KToggleAction>("show_boxes")
294 << i18nc("Show the information boxes", "Show &Info Boxes") << Checked(Options::showInfoBoxes());
295 connect(kaBoxes, SIGNAL(toggled(bool)), map(), SLOT(slotToggleInfoboxes(bool)));
296 kaBoxes->setChecked(Options::showInfoBoxes());
297
298 ka = actionCollection()->add<KToggleAction>("show_time_box")
299 << i18nc("Show time-related info box", "Show &Time Box");
300 connect(kaBoxes, SIGNAL(toggled(bool)), ka, SLOT(setEnabled(bool)));
301 connect(ka, SIGNAL(toggled(bool)), map(), SLOT(slotToggleTimeBox(bool)));
302 ka->setChecked(Options::showTimeBox());
303 ka->setEnabled(Options::showInfoBoxes());
304
305 ka = actionCollection()->add<KToggleAction>("show_focus_box")
306 << i18nc("Show focus-related info box", "Show &Focus Box");
307 connect(kaBoxes, SIGNAL(toggled(bool)), ka, SLOT(setEnabled(bool)));
308 connect(ka, SIGNAL(toggled(bool)), map(), SLOT(slotToggleFocusBox(bool)));
309 ka->setChecked(Options::showFocusBox());
310 ka->setEnabled(Options::showInfoBoxes());
311
312 ka = actionCollection()->add<KToggleAction>("show_location_box")
313 << i18nc("Show location-related info box", "Show &Location Box");
314 connect(kaBoxes, SIGNAL(toggled(bool)), ka, SLOT(setEnabled(bool)));
315 connect(ka, SIGNAL(toggled(bool)), map(), SLOT(slotToggleGeoBox(bool)));
316 ka->setChecked(Options::showGeoBox());
317 ka->setEnabled(Options::showInfoBoxes());
318
319 //Toolbar options
320 newToggleAction(actionCollection(), "show_mainToolBar", i18n("Show Main Toolbar"), toolBar("kstarsToolBar"),
321 SLOT(setVisible(bool)));
322 newToggleAction(actionCollection(), "show_viewToolBar", i18n("Show View Toolbar"), toolBar("viewToolBar"),
323 SLOT(setVisible(bool)));
324
325 //Statusbar view options
326 newToggleAction(actionCollection(), "show_statusBar", i18n("Show Statusbar"), this, SLOT(slotShowGUIItem(bool)));
327 newToggleAction(actionCollection(), "show_sbAzAlt", i18n("Show Az/Alt Field"), this, SLOT(slotShowGUIItem(bool)));
328 newToggleAction(actionCollection(), "show_sbRADec", i18n("Show RA/Dec Field"), this, SLOT(slotShowGUIItem(bool)));
329 newToggleAction(actionCollection(), "show_sbJ2000RADec", i18n("Show J2000.0 RA/Dec Field"), this,
330 SLOT(slotShowGUIItem(bool)));
331
332
333 populateThemes();
334
335 //Color scheme actions. These are added to the "colorschemes" KActionMenu.
336 colorActionMenu = actionCollection()->add<KActionMenu>("colorschemes");
337 colorActionMenu->setText(i18n("C&olor Schemes"));
338 addColorMenuItem(i18n("&Classic"), "cs_classic");
339 addColorMenuItem(i18n("&Star Chart"), "cs_chart");
340 addColorMenuItem(i18n("&Night Vision"), "cs_night");
341 addColorMenuItem(i18n("&Moonless Night"), "cs_moonless-night");
342
343 //Add any user-defined color schemes:
344 //determine filename in local user KDE directory tree.
345 QFile file(KSPaths::locate(QStandardPaths::AppDataLocation, "colors.dat"));
346 if (file.exists() && file.open(QIODevice::ReadOnly))
347 {
348 QTextStream stream(&file);
349 while (!stream.atEnd())
350 {
351 QString line = stream.readLine();
352 QString schemeName = line.left(line.indexOf(':'));
353 QString actionname = "cs_" + line.mid(line.indexOf(':') + 1, line.indexOf('.') - line.indexOf(':') - 1);
354 addColorMenuItem(i18n(schemeName.toLocal8Bit()), actionname.toLocal8Bit());
355 }
356 file.close();
357 }
358
359 //Add FOV Symbol actions
360 fovActionMenu = actionCollection()->add<KActionMenu>("fovsymbols");
361 fovActionMenu->setText(i18n("&FOV Symbols"));
362 fovActionMenu->setDelayed(false);
363 fovActionMenu->setIcon(QIcon::fromTheme("crosshairs"));
364 FOVManager::readFOVs();
365 repopulateFOV();
366
367 //Add HIPS Sources actions
368 hipsActionMenu = actionCollection()->add<KActionMenu>("hipssources");
369 hipsActionMenu->setText(i18n("HiPS All Sky Overlay"));
370 hipsActionMenu->setDelayed(false);
371 hipsActionMenu->setIcon(QIcon::fromTheme("view-preview"));
372 HIPSManager::Instance()->readSources();
373 repopulateHIPS();
374
375 actionCollection()->addAction("geolocation", this, SLOT(slotGeoLocator()))
376 << i18nc("Location on Earth", "&Geographic...")
377 << QIcon::fromTheme("kstars_xplanet")
378 << QKeySequence(Qt::CTRL + Qt::Key_G);
379
380 // Configure Notifications
381 #ifdef HAVE_NOTIFYCONFIG
382 KStandardAction::configureNotifications(this, SLOT(slotConfigureNotifications()), actionCollection());
383 #endif
384
385 // Prepare the options dialog early for modules to connect signals
386 prepareOps();
387
388 ka = actionCollection()->addAction(KStandardAction::Preferences, "configure", this, SLOT(slotViewOps()));
389 //I am not sure what icon preferences is supposed to be.
390 //ka->setIcon( QIcon::fromTheme(""));
391
392 actionCollection()->addAction("startwizard", this, SLOT(slotWizard()))
393 << i18n("Startup Wizard...")
394 << QIcon::fromTheme("tools-wizard");
395
396 // Manual data entry
397 actionCollection()->addAction("dso_catalog_gui", this, SLOT(slotDSOCatalogGUI()))
398 << i18n("Manage DSO Catalogs");
399
400 // Updates actions
401 actionCollection()->addAction("update_comets", this, SLOT(slotUpdateComets()))
402 << i18n("Update Comets Orbital Elements");
403 actionCollection()->addAction("update_asteroids", this, SLOT(slotUpdateAsteroids()))
404 << i18n("Update Asteroids Orbital Elements");
405 actionCollection()->addAction("update_supernovae", this, SLOT(slotUpdateSupernovae()))
406 << i18n("Update Recent Supernovae Data");
407 actionCollection()->addAction("update_satellites", this, SLOT(slotUpdateSatellites()))
408 << i18n("Update Satellites Orbital Elements");
409
410 //Tools Menu:
411 actionCollection()->addAction("astrocalculator", this, SLOT(slotCalculator()))
412 << i18n("Calculator")
413 << QIcon::fromTheme("accessories-calculator")
414 << QKeySequence(Qt::SHIFT + Qt::CTRL + Qt::Key_C);
415
416 /* FIXME Enable once port to KF5 is complete for moonphasetool
417 actionCollection()->addAction("moonphasetool", this, SLOT(slotMoonPhaseTool()) )
418 << i18n("Moon Phase Calendar");
419 */
420
421 actionCollection()->addAction("obslist", this, SLOT(slotObsList()))
422 << i18n("Observation Planner") << QKeySequence(Qt::CTRL + Qt::Key_L);
423
424 actionCollection()->addAction("altitude_vs_time", this, SLOT(slotAVT()))
425 << i18n("Altitude vs. Time") << QKeySequence(Qt::CTRL + Qt::Key_A);
426
427 actionCollection()->addAction("whats_up_tonight", this, SLOT(slotWUT()))
428 << i18n("What's up Tonight") << QKeySequence(Qt::CTRL + Qt::Key_U);
429
430 //FIXME Port to QML2
431 //#if 0
432 actionCollection()->addAction("whats_interesting", this, SLOT(slotToggleWIView()))
433 << i18n("What's Interesting...") << QKeySequence(Qt::CTRL + Qt::Key_W);
434 //#endif
435
436 actionCollection()->addAction("XPlanet", map(), SLOT(slotStartXplanetViewer()))
437 << i18n("XPlanet Solar System Simulator") << QKeySequence(Qt::CTRL + Qt::Key_X);
438
439 actionCollection()->addAction("skycalendar", this, SLOT(slotCalendar())) << i18n("Sky Calendar");
440
441 #ifdef HAVE_INDI
442 ka = actionCollection()->addAction("ekos", this, SLOT(slotEkos()))
443 << i18n("Ekos") << QKeySequence(Qt::CTRL + Qt::Key_K);
444 ka->setShortcutContext(Qt::ApplicationShortcut);
445 #endif
446
447 //FIXME: implement glossary
448 // ka = actionCollection()->addAction("glossary");
449 // ka->setText( i18n("Glossary...") );
450 // ka->setShortcuts( QKeySequence(Qt::CTRL+Qt::Key_K ) );
451 // connect( ka, SIGNAL(triggered()), this, SLOT(slotGlossary()) );
452
453 // 2017-09-17 Jasem: FIXME! Scripting does not work properly under non UNIX systems.
454 // It must be updated to use DBus session bus from Qt (like scheduler)
455 #ifndef Q_OS_WIN
456 actionCollection()->addAction("scriptbuilder", this, SLOT(slotScriptBuilder()))
457 << i18n("Script Builder") << QKeySequence(Qt::CTRL + Qt::Key_B);
458 #endif
459
460 actionCollection()->addAction("solarsystem", this, SLOT(slotSolarSystem()))
461 << i18n("Solar System") << QKeySequence(Qt::CTRL + Qt::Key_Y);
462
463 // Disabled until fixed later
464 actionCollection()->addAction("jmoontool", this, SLOT(slotJMoonTool()) )
465 << i18n("Jupiter's Moons")
466 << QKeySequence(Qt::CTRL + Qt::Key_J );
467
468 actionCollection()->addAction("flagmanager", this, SLOT(slotFlagManager())) << i18n("Flags");
469
470 actionCollection()->addAction("equipmentwriter", this, SLOT(slotEquipmentWriter()))
471 << i18n("List your &Equipment...") << QIcon::fromTheme("kstars") << QKeySequence(Qt::CTRL + Qt::Key_0);
472 actionCollection()->addAction("manageobserver", this, SLOT(slotObserverManager()))
473 << i18n("Manage Observer...") << QIcon::fromTheme("im-user") << QKeySequence(Qt::CTRL + Qt::Key_1);
474
475 //TODO only enable it when finished
476 actionCollection()->addAction("artificialhorizon", this, SLOT(slotHorizonManager()))
477 << i18n("Artificial Horizon...");
478
479 // ==== observation menu - execute ================
480 actionCollection()->addAction("execute", this, SLOT(slotExecute()))
481 << i18n("Execute the Session Plan...") << QKeySequence(Qt::CTRL + Qt::Key_2);
482
483 // ==== observation menu - polaris hour angle ================
484 actionCollection()->addAction("polaris_hour_angle", this, SLOT(slotPolarisHourAngle()))
485 << i18n("Polaris Hour Angle...");
486
487 // ==== devices Menu ================
488 #ifdef HAVE_INDI
489 #ifndef Q_OS_WIN
490 #if 0
491 actionCollection()->addAction("telescope_wizard", this, SLOT(slotTelescopeWizard()))
492 << i18n("Telescope Wizard...")
493 << QIcon::fromTheme("tools-wizard");
494 #endif
495 #endif
496 actionCollection()->addAction("device_manager", this, SLOT(slotINDIDriver()))
497 << i18n("Device Manager...")
498 << QIcon::fromTheme("network-server")
499 << QKeySequence(Qt::SHIFT + Qt::META + Qt::Key_D);
500 actionCollection()->addAction("custom_drivers", DriverManager::Instance(), SLOT(showCustomDrivers()))
501 << i18n("Custom Drivers...")
502 << QIcon::fromTheme("address-book-new");
503 ka = actionCollection()->addAction("indi_cpl", this, SLOT(slotINDIPanel()))
504 << i18n("INDI Control Panel...")
505 << QKeySequence(Qt::CTRL + Qt::Key_I);
506 ka->setShortcutContext(Qt::ApplicationShortcut);
507 ka->setEnabled(false);
508 #else
509 //FIXME need to disable/hide devices submenu in the tools menu. It is created from the kstarsui.rc file
510 //but I don't know how to hide/disable it yet. menuBar()->findChildren<QMenu *>() does not return any children that I can
511 //iterate over. Anyway to resolve this?
512 #endif
513
514 //Help Menu:
515 ka = actionCollection()->addAction(KStandardAction::TipofDay, "help_tipofday", this, SLOT(slotTipOfDay()));
516 ka->setWhatsThis(i18n("Displays the Tip of the Day"));
517 ka->setIcon(QIcon::fromTheme("help-hint"));
518 // KStandardAction::help(this, SLOT(appHelpActivated()), actionCollection(), "help_contents" );
519
520 //Add timestep widget for toolbar
521 m_TimeStepBox = new TimeStepBox(toolBar("kstarsToolBar"));
522 // Add a tool tip to TimeStep describing the weird nature of time steps
523 QString TSBToolTip = i18nc("Tooltip describing the nature of the time step control",
524 "Use this to set the rate at which time in the simulation flows.\nFor time step \'X\' "
525 "up to 10 minutes, time passes at the rate of \'X\' per second.\nFor time steps larger "
526 "than 10 minutes, frames are displayed at an interval of \'X\'.");
527 m_TimeStepBox->setToolTip(TSBToolTip);
528 m_TimeStepBox->tsbox()->setToolTip(TSBToolTip);
529 QWidgetAction *wa = new QWidgetAction(this);
530 wa->setDefaultWidget(m_TimeStepBox);
531
532 // Add actions for the timestep widget's functions
533 actionCollection()->addAction("timestep_control", wa) << i18n("Time step control");
534 const auto unitbox = m_TimeStepBox->unitbox();
535 ka = actionCollection()->addAction("timestep_increase_units", unitbox->increaseUnitsAction());
536 ka->setShortcut(QKeySequence(Qt::Key_Plus));
537 ka = actionCollection()->addAction("timestep_decrease_units", unitbox->decreaseUnitsAction());
538 ka->setShortcut(QKeySequence(Qt::Key_Underscore));
539
540 // ==== viewToolBar actions ================
541 actionCollection()->add<KToggleAction>("show_stars", this, SLOT(slotViewToolBar()))
542 << i18nc("Toggle Stars in the display", "Stars")
543 << QIcon::fromTheme("kstars_stars")
544 << ToolTip(i18n("Toggle stars"));
545 actionCollection()->add<KToggleAction>("show_deepsky", this, SLOT(slotViewToolBar()))
546 << i18nc("Toggle Deep Sky Objects in the display", "Deep Sky")
547 << QIcon::fromTheme("kstars_deepsky")
548 << ToolTip(i18n("Toggle deep sky objects"));
549 actionCollection()->add<KToggleAction>("show_planets", this, SLOT(slotViewToolBar()))
550 << i18nc("Toggle Solar System objects in the display", "Solar System")
551 << QIcon::fromTheme("kstars_planets")
552 << ToolTip(i18n("Toggle Solar system objects"));
553 actionCollection()->add<KToggleAction>("show_clines", this, SLOT(slotViewToolBar()))
554 << i18nc("Toggle Constellation Lines in the display", "Const. Lines")
555 << QIcon::fromTheme("kstars_clines")
556 << ToolTip(i18n("Toggle constellation lines"));
557 actionCollection()->add<KToggleAction>("show_cnames", this, SLOT(slotViewToolBar()))
558 << i18nc("Toggle Constellation Names in the display", "Const. Names")
559 << QIcon::fromTheme("kstars_cnames")
560 << ToolTip(i18n("Toggle constellation names"));
561 actionCollection()->add<KToggleAction>("show_cbounds", this, SLOT(slotViewToolBar()))
562 << i18nc("Toggle Constellation Boundaries in the display", "C. Boundaries")
563 << QIcon::fromTheme("kstars_cbound")
564 << ToolTip(i18n("Toggle constellation boundaries"));
565 actionCollection()->add<KToggleAction>("show_constellationart", this, SLOT(slotViewToolBar()))
566 << xi18nc("Toggle Constellation Art in the display", "C. Art (BETA)")
567 << QIcon::fromTheme("kstars_constellationart")
568 << ToolTip(xi18n("Toggle constellation art (BETA)"));
569 actionCollection()->add<KToggleAction>("show_mw", this, SLOT(slotViewToolBar()))
570 << i18nc("Toggle Milky Way in the display", "Milky Way")
571 << QIcon::fromTheme("kstars_mw")
572 << ToolTip(i18n("Toggle milky way"));
573 actionCollection()->add<KToggleAction>("show_equatorial_grid", this, SLOT(slotViewToolBar()))
574 << i18nc("Toggle Equatorial Coordinate Grid in the display", "Equatorial coord. grid")
575 << QIcon::fromTheme("kstars_grid")
576 << ToolTip(i18n("Toggle equatorial coordinate grid"));
577 actionCollection()->add<KToggleAction>("show_horizontal_grid", this, SLOT(slotViewToolBar()))
578 << i18nc("Toggle Horizontal Coordinate Grid in the display", "Horizontal coord. grid")
579 << QIcon::fromTheme("kstars_hgrid")
580 << ToolTip(i18n("Toggle horizontal coordinate grid"));
581 actionCollection()->add<KToggleAction>("show_horizon", this, SLOT(slotViewToolBar()))
582 << i18nc("Toggle the opaque fill of the ground polygon in the display", "Ground")
583 << QIcon::fromTheme("kstars_horizon")
584 << ToolTip(i18n("Toggle opaque ground"));
585 actionCollection()->add<KToggleAction>("show_flags", this, SLOT(slotViewToolBar()))
586 << i18nc("Toggle flags in the display", "Flags")
587 << QIcon::fromTheme("kstars_flag")
588 << ToolTip(i18n("Toggle flags"));
589 actionCollection()->add<KToggleAction>("show_satellites", this, SLOT(slotViewToolBar()))
590 << i18nc("Toggle satellites in the display", "Satellites")
591 << QIcon::fromTheme("kstars_satellites")
592 << ToolTip(i18n("Toggle satellites"));
593 actionCollection()->add<KToggleAction>("show_supernovae", this, SLOT(slotViewToolBar()))
594 << i18nc("Toggle supernovae in the display", "Supernovae")
595 << QIcon::fromTheme("kstars_supernovae")
596 << ToolTip(i18n("Toggle supernovae"));
597 actionCollection()->add<KToggleAction>("show_whatsinteresting", this, SLOT(slotToggleWIView()))
598 << i18nc("Toggle What's Interesting", "What's Interesting")
599 << QIcon::fromTheme("view-list-details")
600 << ToolTip(i18n("Toggle What's Interesting"));
601
602 #ifdef HAVE_INDI
603 // ==== INDIToolBar actions ================
604 actionCollection()->add<KToggleAction>("show_ekos", this, SLOT(slotINDIToolBar()))
605 << i18nc("Toggle Ekos in the display", "Ekos")
606 << QIcon::fromTheme("kstars_ekos")
607 << ToolTip(i18n("Toggle Ekos"));
608 ka = actionCollection()->add<KToggleAction>("show_control_panel", this, SLOT(slotINDIToolBar()))
609 << i18nc("Toggle the INDI Control Panel in the display", "INDI Control Panel")
610 << QIcon::fromTheme("kstars_indi")
611 << ToolTip(i18n("Toggle INDI Control Panel"));
612 ka->setEnabled(false);
613 ka = actionCollection()->add<KToggleAction>("show_fits_viewer", this, SLOT(slotINDIToolBar()))
614 << i18nc("Toggle the FITS Viewer in the display", "FITS Viewer")
615 << QIcon::fromTheme("kstars_fitsviewer")
616 << ToolTip(i18n("Toggle FITS Viewer"));
617 ka->setEnabled(false);
618
619 ka = actionCollection()->add<KToggleAction>("show_sensor_fov", this, SLOT(slotINDIToolBar()))
620 << i18nc("Toggle the sensor Field of View", "Sensor FOV")
621 << QIcon::fromTheme("archive-extract")
622 << ToolTip(i18n("Toggle Sensor FOV"));
623 ka->setEnabled(false);
624 ka->setChecked(Options::showSensorFOV());
625
626 ka = actionCollection()->add<KToggleAction>("show_mount_box", this, SLOT(slotINDIToolBar()))
627 << i18nc("Toggle the Mount Control Panel", "Mount Control")
628 << QIcon::fromTheme("draw-text")
629 << ToolTip(i18n("Toggle Mount Control Panel"));
630 telescopeGroup->addAction(ka);
631
632 ka = actionCollection()->add<KToggleAction>("lock_telescope", this, SLOT(slotINDIToolBar()))
633 << i18nc("Toggle the telescope center lock in display", "Center Telescope")
634 << QIcon::fromTheme("center_telescope", QIcon(":/icons/center_telescope.svg"))
635 << ToolTip(i18n("Toggle Lock Telescope Center"));
636 telescopeGroup->addAction(ka);
637
638 ka = actionCollection()->add<KToggleAction>("telescope_track", this, SLOT(slotINDITelescopeTrack()))
639 << i18n("Toggle Telescope Tracking")
640 << QIcon::fromTheme("object-locked");
641 telescopeGroup->addAction(ka);
642 ka = actionCollection()->addAction("telescope_slew", this, SLOT(slotINDITelescopeSlew()))
643 << i18n("Slew telescope to the focused object")
644 << QIcon::fromTheme("object-rotate-right");
645 telescopeGroup->addAction(ka);
646 ka = actionCollection()->addAction("telescope_sync", this, SLOT(slotINDITelescopeSync()))
647 << i18n("Sync telescope to the focused object")
648 << QIcon::fromTheme("media-record");
649 telescopeGroup->addAction(ka);
650 ka = actionCollection()->addAction("telescope_abort", this, SLOT(slotINDITelescopeAbort()))
651 << i18n("Abort telescope motions")
652 << QIcon::fromTheme("process-stop");
653 ka->setShortcutContext(Qt::ApplicationShortcut);
654 telescopeGroup->addAction(ka);
655 ka = actionCollection()->addAction("telescope_park", this, SLOT(slotINDITelescopePark()))
656 << i18n("Park telescope")
657 << QIcon::fromTheme("flag-red");
658 telescopeGroup->addAction(ka);
659 ka = actionCollection()->addAction("telescope_unpark", this, SLOT(slotINDITelescopeUnpark()))
660 << i18n("Unpark telescope")
661 << QIcon::fromTheme("flag-green");
662 ka->setShortcutContext(Qt::ApplicationShortcut);
663 telescopeGroup->addAction(ka);
664
665 actionCollection()->addAction("telescope_slew_mouse", this, SLOT(slotINDITelescopeSlewMousePointer()))
666 << i18n("Slew the telescope to the mouse pointer position");
667
668 actionCollection()->addAction("telescope_sync_mouse", this, SLOT(slotINDITelescopeSyncMousePointer()))
669 << i18n("Sync the telescope to the mouse pointer position");
670
671 // Disable all telescope actions by default
672 telescopeGroup->setEnabled(false);
673
674 // Dome Actions
675 ka = actionCollection()->addAction("dome_park", this, SLOT(slotINDIDomePark()))
676 << i18n("Park dome")
677 << QIcon::fromTheme("dome-park", QIcon(":/icons/dome-park.svg"));
678 domeGroup->addAction(ka);
679 ka = actionCollection()->addAction("dome_unpark", this, SLOT(slotINDIDomeUnpark()))
680 << i18n("Unpark dome")
681 << QIcon::fromTheme("dome-unpark", QIcon(":/icons/dome-unpark.svg"));
682 ka->setShortcutContext(Qt::ApplicationShortcut);
683 domeGroup->addAction(ka);
684
685 domeGroup->setEnabled(false);
686 #endif
687 }
688
repopulateFOV()689 void KStars::repopulateFOV()
690 {
691 // Read list of all FOVs
692 //qDeleteAll( data()->availFOVs );
693 data()->availFOVs = FOVManager::getFOVs();
694 data()->syncFOV();
695
696 // Iterate through FOVs
697 fovActionMenu->menu()->clear();
698 foreach (FOV *fov, data()->availFOVs)
699 {
700 KToggleAction *kta = actionCollection()->add<KToggleAction>(fov->name());
701 kta->setText(fov->name());
702 if (Options::fOVNames().contains(fov->name()))
703 {
704 kta->setChecked(true);
705 }
706
707 fovActionMenu->addAction(kta);
708 connect(kta, SIGNAL(toggled(bool)), this, SLOT(slotTargetSymbol(bool)));
709 }
710 // Add menu bottom
711 QAction *ka = actionCollection()->addAction("edit_fov", this, SLOT(slotFOVEdit())) << i18n("Edit FOV Symbols...");
712 fovActionMenu->addSeparator();
713 fovActionMenu->addAction(ka);
714 }
715
repopulateHIPS()716 void KStars::repopulateHIPS()
717 {
718 // Iterate through actions
719 hipsActionMenu->menu()->clear();
720 // Remove all actions
721 QList<QAction*> actions = hipsGroup->actions();
722
723 for (auto &action : actions)
724 hipsGroup->removeAction(action);
725
726 QAction *ka = actionCollection()->addAction(i18n("None"), this, SLOT(slotHIPSSource()))
727 << i18n("None") << AddToGroup(hipsGroup)
728 << Checked(Options::hIPSSource() == "None");
729
730 hipsActionMenu->addAction(ka);
731 hipsActionMenu->addSeparator();
732
733 for (QMap<QString, QString> source : HIPSManager::Instance()->getHIPSSources())
734 {
735 QString title = source.value("obs_title");
736
737 QAction *ka = actionCollection()->addAction(title, this, SLOT(slotHIPSSource()))
738 << title << AddToGroup(hipsGroup)
739 << Checked(Options::hIPSSource() == title);
740
741 hipsActionMenu->addAction(ka);
742 }
743
744 // Hips settings
745 ka = actionCollection()->addAction("hipssettings", HIPSManager::Instance(),
746 SLOT(showSettings())) << i18n("HiPS Settings...");
747 hipsActionMenu->addSeparator();
748 hipsActionMenu->addAction(ka);
749 }
750
initStatusBar()751 void KStars::initStatusBar()
752 {
753 statusBar()->showMessage(i18n(" Welcome to KStars "));
754
755 QString s = "000d 00m 00s, +00d 00\' 00\""; //only need this to set the width
756 if (Options::showAltAzField())
757 {
758 AltAzField.setText(s);
759 statusBar()->insertPermanentWidget(0, &AltAzField);
760 }
761
762 if (Options::showRADecField())
763 {
764 RADecField.setText(s);
765 statusBar()->insertPermanentWidget(1, &RADecField);
766 }
767
768 if (Options::showJ2000RADecField())
769 {
770 J2000RADecField.setText(s);
771 statusBar()->insertPermanentWidget(1, &J2000RADecField);
772 }
773
774 if (!Options::showStatusBar())
775 statusBar()->hide();
776 }
777
datainitFinished()778 void KStars::datainitFinished()
779 {
780 //Time-related connections
781 connect(data()->clock(), SIGNAL(timeAdvanced()), this, SLOT(updateTime()));
782 connect(data()->clock(), SIGNAL(timeChanged()), this, SLOT(updateTime()));
783
784 //Add GUI elements to main window
785 buildGUI();
786
787 connect(data()->clock(), SIGNAL(scaleChanged(float)), map(), SLOT(slotClockSlewing()));
788
789 connect(data(), SIGNAL(skyUpdate(bool)), map(), SLOT(forceUpdateNow()));
790 connect(m_TimeStepBox, SIGNAL(scaleChanged(float)), data(), SLOT(setTimeDirection(float)));
791 connect(m_TimeStepBox, SIGNAL(scaleChanged(float)), data()->clock(), SLOT(setClockScale(float)));
792 connect(m_TimeStepBox, SIGNAL(scaleChanged(float)), map(), SLOT(setFocus()));
793
794 //m_equipmentWriter = new EquipmentWriter();
795 //m_observerAdd = new ObserverAdd;
796
797 //Do not start the clock if "--paused" specified on the cmd line
798 if (StartClockRunning)
799 {
800 // The initial time is set when KStars is first executed
801 // but until all data is loaded, some time elapsed already so we need to synchronize if no Start Date string
802 // was supplied to KStars
803 if (StartDateString.isEmpty())
804 data()->changeDateTime(KStarsDateTime::currentDateTimeUtc());
805
806 data()->clock()->start();
807 }
808
809 // Connect cache function for Find dialog
810 connect(data(), SIGNAL(clearCache()), this, SLOT(clearCachedFindDialog()));
811
812 //Propagate config settings
813 applyConfig(false);
814
815 //show the window. must be before kswizard and messageboxes
816 show();
817
818 //Initialize focus
819 initFocus();
820
821 data()->setFullTimeUpdate();
822 updateTime();
823
824 // Initial State
825 qCDebug(KSTARS) << "Date/Time is:" << data()->clock()->utc().toString();
826 qCDebug(KSTARS) << "Location:" << data()->geo()->fullName();
827 qCDebug(KSTARS) << "TZ0:" << data()->geo()->TZ0() << "TZ:" << data()->geo()->TZ();
828
829 KSTheme::Manager::instance()->setCurrentTheme(Options::currentTheme());
830
831 //If this is the first startup, show the wizard
832 if (Options::runStartupWizard())
833 {
834 slotWizard();
835 }
836
837 //Show TotD
838 KTipDialog::showTip(this, "kstars/tips");
839
840 // Update comets and asteroids if enabled.
841 if (Options::orbitalElementsAutoUpdate())
842 {
843 slotUpdateComets(true);
844 slotUpdateAsteroids(true);
845 }
846
847 #ifdef HAVE_INDI
848 Ekos::Manager::Instance()->initialize();
849 #endif
850 }
851
initFocus()852 void KStars::initFocus()
853 {
854 //Case 1: tracking on an object
855 if (Options::isTracking() && Options::focusObject() != i18n("nothing"))
856 {
857 SkyObject *oFocus;
858 if (Options::focusObject() == i18n("star"))
859 {
860 SkyPoint p(Options::focusRA(), Options::focusDec());
861 double maxrad = 1.0;
862
863 oFocus = data()->skyComposite()->starNearest(&p, maxrad);
864 }
865 else
866 {
867 oFocus = data()->objectNamed(Options::focusObject());
868 }
869
870 if (oFocus)
871 {
872 map()->setFocusObject(oFocus);
873 map()->setClickedObject(oFocus);
874 map()->setFocusPoint(oFocus);
875 }
876 else
877 {
878 qWarning() << "Cannot center on " << Options::focusObject() << ": no object found.";
879 }
880
881 //Case 2: not tracking, and using Alt/Az coords. Set focus point using
882 //FocusRA as the Azimuth, and FocusDec as the Altitude
883 }
884 else if (!Options::isTracking() && Options::useAltAz())
885 {
886 SkyPoint pFocus;
887 pFocus.setAz(Options::focusRA());
888 pFocus.setAlt(Options::focusDec());
889 pFocus.HorizontalToEquatorial(data()->lst(), data()->geo()->lat());
890 map()->setFocusPoint(&pFocus);
891
892 //Default: set focus point using FocusRA as the RA and
893 //FocusDec as the Dec
894 }
895 else
896 {
897 SkyPoint pFocus(Options::focusRA(), Options::focusDec());
898 pFocus.EquatorialToHorizontal(data()->lst(), data()->geo()->lat());
899 map()->setFocusPoint(&pFocus);
900 }
901 data()->setSnapNextFocus();
902 map()->setDestination(*map()->focusPoint());
903 map()->setFocus(map()->destination());
904
905 map()->showFocusCoords();
906
907 //Check whether initial position is below the horizon.
908 if (Options::useAltAz() && Options::showGround() && map()->focus()->alt().Degrees() <= SkyPoint::altCrit)
909 {
910 QString caption = i18n("Initial Position is Below Horizon");
911 QString message =
912 i18n("The initial position is below the horizon.\nWould you like to reset to the default position?");
913 if (KMessageBox::warningYesNo(this, message, caption, KGuiItem(i18n("Reset Position")),
914 KGuiItem(i18n("Do Not Reset")), "dag_start_below_horiz") == KMessageBox::Yes)
915 {
916 map()->setClickedObject(nullptr);
917 map()->setFocusObject(nullptr);
918 Options::setIsTracking(false);
919
920 data()->setSnapNextFocus(true);
921
922 SkyPoint DefaultFocus;
923 DefaultFocus.setAz(180.0);
924 DefaultFocus.setAlt(45.0);
925 DefaultFocus.HorizontalToEquatorial(data()->lst(), data()->geo()->lat());
926 map()->setDestination(DefaultFocus);
927 }
928 }
929
930 //If there is a focusObject() and it is a SS body, add a temporary Trail
931 if (map()->focusObject() && map()->focusObject()->isSolarSystem() && Options::useAutoTrail())
932 {
933 ((KSPlanetBase *)map()->focusObject())->addToTrail();
934 data()->temporaryTrail = true;
935 }
936 }
937
buildGUI()938 void KStars::buildGUI()
939 {
940 //create the texture manager
941 TextureManager::Create();
942 //create the skymap
943 m_SkyMap = SkyMap::Create();
944 connect(m_SkyMap, SIGNAL(mousePointChanged(SkyPoint*)), SLOT(slotShowPositionBar(SkyPoint*)));
945 connect(m_SkyMap, SIGNAL(zoomChanged()), SLOT(slotZoomChanged()));
946 setCentralWidget(m_SkyMap);
947
948 //Initialize menus, toolbars, and statusbars
949 initStatusBar();
950 initActions();
951
952 // Setup GUI from the settings file
953 // UI tests provide the default settings file from the resources explicitly file to render UI properly
954 setupGUI(StandardWindowOptions(Default), m_KStarsUIResource);
955
956 //get focus of keyboard and mouse actions (for example zoom in with +)
957 map()->QWidget::setFocus();
958 resize(Options::windowWidth(), Options::windowHeight());
959
960 // check zoom in/out buttons
961 if (Options::zoomFactor() >= MAXZOOM)
962 actionCollection()->action("zoom_in")->setEnabled(false);
963 if (Options::zoomFactor() <= MINZOOM)
964 actionCollection()->action("zoom_out")->setEnabled(false);
965 }
966
populateThemes()967 void KStars::populateThemes()
968 {
969 KSTheme::Manager::instance()->setThemeMenuAction(new QMenu(i18n("&Themes"), this));
970 KSTheme::Manager::instance()->registerThemeActions(this);
971
972 connect(KSTheme::Manager::instance(), SIGNAL(signalThemeChanged()), this, SLOT(slotThemeChanged()));
973 }
974
slotThemeChanged()975 void KStars::slotThemeChanged()
976 {
977 Options::setCurrentTheme(KSTheme::Manager::instance()->currentThemeName());
978 }
979