Kstars

wiview.cpp
1/*
2 SPDX-FileCopyrightText: 2012 Samikshan Bairagya <samikshan@gmail.com>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5*/
6
7#include "wiview.h"
8
9#include "kspaths.h"
10#include "kstars.h"
11#include "ksnotification.h"
12#include "modelmanager.h"
13#include "obsconditions.h"
14#include "Options.h"
15#include "skymap.h"
16#include "skymapcomposite.h"
17#include "skyobjitem.h"
18#include "skyobjlistmodel.h"
19#include "starobject.h"
20#include "wiequipsettings.h"
21#include "dialogs/detaildialog.h"
22
23#include <klocalizedcontext.h>
24
25#include <QGraphicsObject>
26#include <QNetworkAccessManager>
27#include <QNetworkReply>
28#include <QQmlContext>
29#include <QQuickItem>
30#include <QQuickView>
31#include <QStandardPaths>
32#include <QtConcurrent>
33
34#ifdef HAVE_INDI
35#include <basedevice.h>
36#include "indi/indilistener.h"
37#include "indi/indimount.h"
38#endif
39
40WIView::WIView(QWidget *parent) : QWidget(parent)
41{
42 //These settings are like this just to get it started.
43 int bortle = Options::bortleClass();
44 int aperture = 100;
45 ObsConditions::Equipment equip = ObsConditions::Telescope;
46 ObsConditions::TelescopeType telType = ObsConditions::Reflector;
47
48 m_Obs = new ObsConditions(bortle, aperture, equip, telType);
49
50 m_ModManager.reset(new ModelManager(m_Obs));
51
52 m_BaseView = new QQuickView();
53
54 ///To use i18n() instead of qsTr() in qml/wiview.qml for translation
55 //KDeclarative kd;
56 // kd.setDeclarativeEngine(m_BaseView->engine());
57 //kd.initialize();
58 //kd.setupBindings();
59
60 m_Ctxt = m_BaseView->rootContext();
61
62 m_Ctxt->setContextProperty(
63 "soListModel",
64 m_ModManager
65 ->getTempModel()); // This is to avoid an error saying it doesn't exist.
66
67 ///Use instead of KDeclarative
68 m_Ctxt->setContextObject(new KLocalizedContext(m_BaseView));
69
70#if 0
71 QString WI_Location;
72#if defined(Q_OS_MACOS)
73 WI_Location = QCoreApplication::applicationDirPath() + "/../Resources/kstars/tools/whatsinteresting/qml/wiview.qml";
74 if (!QFileInfo(WI_Location).exists())
75 WI_Location = KSPaths::locate(QStandardPaths::AppLocalDataLocation, "tools/whatsinteresting/qml/wiview.qml");
76#elif defined(Q_OS_WIN)
77 WI_Location = KSPaths::locate(QStandardPaths::GenericDataLocation, "tools/whatsinteresting/qml/wiview.qml");
78#else
79 WI_Location = KSPaths::locate(QStandardPaths::AppLocalDataLocation, "tools/whatsinteresting/qml/wiview.qml");
80#endif
81
82 m_BaseView->setSource(QUrl::fromLocalFile(WI_Location));
83#endif
84
85 m_BaseView->setSource(QUrl("qrc:/qml/whatisinteresting/wiview.qml"));
86
87 m_BaseObj = m_BaseView->rootObject();
88
89 m_ProgressBar = m_BaseObj->findChild<QQuickItem *>("progressBar");
90
91 m_loadingMessage = m_BaseObj->findChild<QQuickItem *>("loadingMessage");
92
93 m_CategoryTitle = m_BaseObj->findChild<QQuickItem *>(QString("categoryTitle"));
94
95 m_ViewsRowObj = m_BaseObj->findChild<QQuickItem *>(QString("viewsRowObj"));
96 connect(m_ViewsRowObj, SIGNAL(categorySelected(QString)), this,
98 connect(m_ViewsRowObj, SIGNAL(inspectSkyObject(QString)), this,
99 SLOT(inspectSkyObject(QString)));
100
101 m_SoListObj = m_BaseObj->findChild<QQuickItem *>("soListObj");
102 connect(m_SoListObj, SIGNAL(soListItemClicked(int)), this,
103 SLOT(onSoListItemClicked(int)));
104
105 m_DetailsViewObj = m_BaseObj->findChild<QQuickItem *>("detailsViewObj");
106
107 descTextObj = m_DetailsViewObj->findChild<QObject *>("descTextObj");
108 infoBoxText = m_DetailsViewObj->findChild<QObject *>("infoBoxText");
109
110 m_NextObj = m_BaseObj->findChild<QQuickItem *>("nextObj");
111 connect(m_NextObj, SIGNAL(nextObjClicked()), this, SLOT(onNextObjClicked()));
112 m_PrevObj = m_BaseObj->findChild<QQuickItem *>("prevObj");
113 connect(m_PrevObj, SIGNAL(prevObjClicked()), this, SLOT(onPrevObjClicked()));
114
115 m_CenterButtonObj = m_BaseObj->findChild<QQuickItem *>("centerButtonObj");
116 connect(m_CenterButtonObj, SIGNAL(centerButtonClicked()), this,
117 SLOT(onCenterButtonClicked()));
118
119 autoCenterCheckbox = m_DetailsViewObj->findChild<QObject *>("autoCenterCheckbox");
120 autoTrackCheckbox = m_DetailsViewObj->findChild<QObject *>("autoTrackCheckbox");
121
122 m_SlewTelescopeButtonObj =
123 m_BaseObj->findChild<QQuickItem *>("slewTelescopeButtonObj");
124 connect(m_SlewTelescopeButtonObj, SIGNAL(slewTelescopeButtonClicked()), this,
126
127 m_DetailsButtonObj = m_BaseObj->findChild<QQuickItem *>("detailsButtonObj");
128 connect(m_DetailsButtonObj, SIGNAL(detailsButtonClicked()), this,
129 SLOT(onDetailsButtonClicked()));
130
131 QObject *settingsIconObj = m_BaseObj->findChild<QQuickItem *>("settingsIconObj");
132 connect(settingsIconObj, SIGNAL(settingsIconClicked()), this,
133 SLOT(onSettingsIconClicked()));
134
135 inspectIconObj = m_BaseObj->findChild<QQuickItem *>("inspectIconObj");
136 connect(inspectIconObj, SIGNAL(inspectIconClicked(bool)), this,
137 SLOT(onInspectIconClicked(bool)));
138
139 visibleIconObj = m_BaseObj->findChild<QQuickItem *>("visibleIconObj");
140 connect(visibleIconObj, SIGNAL(visibleIconClicked(bool)), this,
141 SLOT(onVisibleIconClicked(bool)));
142
143 favoriteIconObj = m_BaseObj->findChild<QQuickItem *>("favoriteIconObj");
144 connect(favoriteIconObj, SIGNAL(favoriteIconClicked(bool)), this,
145 SLOT(onFavoriteIconClicked(bool)));
146
147 QObject *reloadIconObj = m_BaseObj->findChild<QQuickItem *>("reloadIconObj");
148 connect(reloadIconObj, SIGNAL(reloadIconClicked()), this,
149 SLOT(onReloadIconClicked()));
150
151 QObject *downloadIconObj = m_BaseObj->findChild<QQuickItem *>("downloadIconObj");
152 connect(downloadIconObj, SIGNAL(downloadIconClicked()), this,
153 SLOT(onUpdateIconClicked()));
154
156 m_BaseView->show();
157
158 // Fix some weird issue with what's interesting panel view under Windows
159 // In Qt 5.9 it content is messed up and there is no way to close the panel
160#ifdef Q_OS_WIN
162#endif
163
164 connect(KStars::Instance()->map(), SIGNAL(objectClicked(SkyObject *)), this,
165 SLOT(inspectSkyObjectOnClick(SkyObject *)));
166
167 manager.reset(new QNetworkAccessManager());
168
169 setProgressBarVisible(true);
170 connect(m_ModManager.get(), SIGNAL(loadProgressUpdated(double)), this,
171 SLOT(updateProgress(double)));
172 connect(m_ModManager.get(), SIGNAL(modelUpdated()), this, SLOT(refreshListView()));
173 m_ViewsRowObj->setProperty("enabled", false);
174
175 inspectOnClick = false;
176
177 nightVision = m_BaseObj->findChild<QObject *>("nightVision");
178 //if (Options::darkAppColors())
179 // nightVision->setProperty("state", "active");
180}
181
182void WIView::setNightVisionOn(bool on)
183{
184 if (on)
185 nightVision->setProperty("state", "active");
186 else
187 nightVision->setProperty("state", "");
188
189 if (m_CurSoItem != nullptr)
190 loadDetailsView(m_CurSoItem, m_CurIndex);
191}
192
193void WIView::setProgressBarVisible(bool visible)
194{
195 m_ProgressBar->setProperty("visible", visible);
196}
197
198void WIView::updateProgress(double value)
199{
200 m_ProgressBar->setProperty("value", value);
201
202 if (value == 1)
203 {
204 setProgressBarVisible(false);
205 m_ViewsRowObj->setProperty("enabled", true);
206 m_loadingMessage->setProperty("state", "");
207 }
208 else
209 {
210 setProgressBarVisible(true);
211 m_loadingMessage->setProperty("state", "loading");
212 }
213}
214
216{
217 int bortle = Options::bortleClass();
218
219 /**
220 NOTE This part of the code dealing with equipment type is presently not required
221 as WI does not differentiate between Telescope and Binoculars. It only needs the
222 aperture of the equipment whichever available. However this is kept as a part of
223 the code as support to be utilised in the future.
224 **/
225 ObsConditions::Equipment equip = ObsConditions::None;
226
227 if (Options::telescopeCheck() && Options::binocularsCheck())
228 equip = ObsConditions::Both;
229 else if (Options::telescopeCheck())
230 equip = ObsConditions::Telescope;
231 else if (Options::binocularsCheck())
232 equip = ObsConditions::Binoculars;
233
235
236 if (KStars::Instance()->getWIEquipSettings())
237 telType = (equip == ObsConditions::Telescope) ? KStars::Instance()->getWIEquipSettings()->getTelType() :
238 ObsConditions::Invalid;
239 else
240 telType = ObsConditions::Invalid;
241
242 int aperture = 100;
243
244 //This doesn't work correctly, FIXME!!
245 // if(KStars::Instance()->getWIEquipSettings())
246 // aperture = KStars::Instance()->getWIEquipSettings()->getAperture();
247
248 if (!m_Obs)
249 m_Obs = new ObsConditions(bortle, aperture, equip, telType);
250 else
251 m_Obs->setObsConditions(bortle, aperture, equip, telType);
252}
253
255{
256 m_CurrentObjectListName = model;
257 m_Ctxt->setContextProperty("soListModel",
258 m_ModManager->returnModel(m_CurrentObjectListName));
259 m_CurIndex = -2;
260 if (!m_ModManager->showOnlyVisibleObjects())
261 visibleIconObj->setProperty("state", "unchecked");
262 if (!m_ModManager->showOnlyFavoriteObjects())
263 favoriteIconObj->setProperty("state", "unchecked");
264
265 if ((QStringList() << "ngc"
266 << "ic"
267 << "messier"
268 << "sharpless")
269 .contains(model))
270 {
271#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
272 QtConcurrent::run(&ModelManager::loadCatalog, m_ModManager.get(), model);
273#else
274 QtConcurrent::run(m_ModManager.get(), &ModelManager::loadCatalog, model);
275#endif
276 return;
277 }
278
279 updateModel(*m_Obs);
280}
281
283{
284 SkyObjItem *soitem = m_ModManager->returnModel(m_CurrentObjectListName)->getSkyObjItem(index);
285 if (soitem)
286 loadDetailsView(soitem, index);
287}
288
290{
291 if (!m_CurrentObjectListName.isEmpty())
292 {
293 int modelSize = m_ModManager->returnModel(m_CurrentObjectListName)->rowCount();
294 if (modelSize > 0)
295 {
296 SkyObjItem *nextItem =
297 m_ModManager->returnModel(m_CurrentObjectListName)->getSkyObjItem((m_CurIndex + 1) % modelSize);
298 loadDetailsView(nextItem, (m_CurIndex + 1) % modelSize);
299 }
300 }
301}
302
304{
305 if (!m_CurrentObjectListName.isEmpty())
306 {
307 int modelSize = m_ModManager->returnModel(m_CurrentObjectListName)->rowCount();
308 if (modelSize > 0)
309 {
310 SkyObjItem *prevItem =
311 m_ModManager->returnModel(m_CurrentObjectListName)->getSkyObjItem((m_CurIndex - 1 + modelSize) % modelSize);
312 loadDetailsView(prevItem, (m_CurIndex - 1 + modelSize) % modelSize);
313 }
314 }
315}
316
318{
319 ///Center map on selected sky-object
320 SkyObject *so = m_CurSoItem->getSkyObject();
321 KStars *kstars = KStars::Instance();
322
323 if (so)
324 {
325 kstars->map()->setFocusPoint(so);
326 kstars->map()->setFocusObject(so);
327 kstars->map()->setDestination(*kstars->map()->focusPoint());
328 Options::setIsTracking(autoTrackCheckbox->property("checked") == true);
329 }
330}
331
333{
335 KMessageBox::warningContinueCancel(nullptr, "Are you sure you want your telescope to slew to this object?",
337 "continue_wi_slew_warning"))
338 {
339#ifdef HAVE_INDI
340
341 if (INDIListener::Instance()->size() == 0)
342 {
343 KSNotification::sorry(i18n("No connected mounts found."));
344 return;
345 }
346
347 for (auto &oneDevice : INDIListener::devices())
348 {
349 if (!(oneDevice->getDriverInterface() & INDI::BaseDevice::TELESCOPE_INTERFACE))
350 continue;
351
352 if (oneDevice->isConnected() == false)
353 {
354 KSNotification::error(i18n("Mount %1 is offline. Please connect and retry again.", oneDevice->getDeviceName()));
355 return;
356 }
357
358 auto mount = oneDevice->getMount();
359 if (!mount)
360 continue;
361 mount->Slew(m_CurSoItem->getSkyObject());
362
363 /// Slew map to selected sky-object
365 return;
366 }
367
368 KSNotification::sorry(i18n("No connected mounts found."));
369
370#endif
371 }
372}
373
375{
376 ///Code taken from WUTDialog::slotDetails()
377 KStars *kstars = KStars::Instance();
378 SkyObject *so = m_CurSoItem->getSkyObject();
379 if (so)
380 {
381 DetailDialog *detail = new DetailDialog(so, kstars->data()->lt(), kstars->data()->geo(), kstars);
382 detail->exec();
383 delete detail;
384 }
385}
386
388{
389 KStars *kstars = KStars::Instance();
390 kstars->showWISettingsUI();
391}
392
394{
395 if (!m_CurrentObjectListName.isEmpty())
396 {
397 updateModel(*m_Obs);
398 m_CurIndex = m_ModManager->returnModel(m_CurrentObjectListName)->getSkyObjIndex(m_CurSoItem);
399 }
400 loadDetailsView(m_CurSoItem, m_CurIndex);
401}
402
403void WIView::onVisibleIconClicked(bool visible)
404{
405 m_ModManager->setShowOnlyVisibleObjects(visible);
407}
408
409void WIView::onFavoriteIconClicked(bool favorites)
410{
411 m_ModManager->setShowOnlyFavoriteObjects(favorites);
413}
414
415void WIView::onUpdateIconClicked()
416{
417 QMessageBox mbox;
418 QPushButton *currentObject = mbox.addButton("Current Object", QMessageBox::AcceptRole);
419 QPushButton *missingObjects = nullptr;
420 QPushButton *allObjects = nullptr;
421
422 mbox.setText("Please choose which object(s) to try to update with Wikipedia data.");
423 if (!m_CurrentObjectListName.isEmpty())
424 {
425 missingObjects = mbox.addButton("Objects with no data", QMessageBox::AcceptRole);
426 allObjects = mbox.addButton("Entire List", QMessageBox::AcceptRole);
427 }
429 mbox.setDefaultButton(cancel);
430
431 mbox.exec();
432 if (mbox.clickedButton() == currentObject)
433 {
434 if (m_CurSoItem != nullptr)
435 {
436 tryToUpdateWikipediaInfo(m_CurSoItem, getWikipediaName(m_CurSoItem));
437 }
438 }
439 else if (mbox.clickedButton() == allObjects || mbox.clickedButton() == missingObjects)
440 {
441 SkyObjListModel *model = m_ModManager->returnModel(m_CurrentObjectListName);
442 if (model->rowCount() > 0)
443 {
444 tryToUpdateWikipediaInfoInModel(mbox.clickedButton() == missingObjects);
445 }
446 else
447 {
448 qDebug() << Q_FUNC_INFO << "No Objects in List!";
449 }
450 }
451}
452
453void WIView::refreshListView()
454{
455 m_Ctxt->setContextProperty("soListModel", nullptr);
456 if (!m_CurrentObjectListName.isEmpty())
457 m_Ctxt->setContextProperty("soListModel", m_ModManager->returnModel(m_CurrentObjectListName));
458 if (m_CurIndex == -2)
460 if (m_CurIndex != -1)
461 m_SoListObj->setProperty("currentIndex", m_CurIndex);
462}
463
465{
466 if (!m_CurrentObjectListName.isEmpty())
467 {
468 m_Obs = &obs;
469 m_ModManager->updateModel(m_Obs, m_CurrentObjectListName);
470 }
471}
472
473void WIView::inspectSkyObject(const QString &name)
474{
475 if (!name.isEmpty() && name != "star")
476 {
477 SkyObject *obj = KStarsData::Instance()->skyComposite()->findByName(name);
478
479 if (obj)
480 inspectSkyObject(obj);
481 }
482}
483
484void WIView::inspectSkyObjectOnClick(SkyObject *obj)
485{
486 if (inspectOnClick && KStars::Instance()->isWIVisible())
487 inspectSkyObject(obj);
488}
489
490void WIView::inspectSkyObject(SkyObject *obj)
491{
492 if (!obj)
493 return;
494
495 if (obj->name() != "star")
496 {
497 m_CurrentObjectListName = "";
498 trackedItem.reset(new SkyObjItem(obj));
499 loadDetailsView(trackedItem.get(), -1);
500 m_BaseObj->setProperty("state", "singleItemSelected");
501 m_CategoryTitle->setProperty("text", "Selected Object");
502 }
503}
504
505void WIView::loadDetailsView(SkyObjItem *soitem, int index)
506{
507 if (soitem == nullptr)
508 return;
509
510 int modelSize = -1;
511
512 if (index != -1)
513 modelSize = m_ModManager->returnModel(m_CurrentObjectListName)->rowCount();
514
515 if (soitem != m_CurSoItem)
516 m_CurSoItem = soitem;
517
518 m_CurIndex = index;
519 if (modelSize <= 1)
520 {
521 m_NextObj->setProperty("visible", "false");
522 m_PrevObj->setProperty("visible", "false");
523 }
524 else
525 {
526 SkyObjItem *nextItem =
527 m_ModManager->returnModel(m_CurrentObjectListName)->getSkyObjItem((m_CurIndex + 1) % modelSize);
528 SkyObjItem *prevItem =
529 m_ModManager->returnModel(m_CurrentObjectListName)->getSkyObjItem((m_CurIndex - 1 + modelSize) % modelSize);
530
531 m_NextObj->setProperty("visible", "true");
532 m_PrevObj->setProperty("visible", "true");
533 QObject *nextTextObj = m_NextObj->findChild<QObject *>("nextTextObj");
534
535 nextTextObj->setProperty("text", nextItem->getName());
536 QObject *prevTextObj = m_PrevObj->findChild<QObject *>("prevTextObj");
537
538 prevTextObj->setProperty("text", prevItem->getName());
539 }
540
541 QObject *sonameObj = m_DetailsViewObj->findChild<QObject *>("sonameObj");
542 QObject *posTextObj = m_DetailsViewObj->findChild<QObject *>("posTextObj");
543 QObject *detailImage = m_DetailsViewObj->findChild<QObject *>("detailImage");
544 QObject *detailsTextObj = m_DetailsViewObj->findChild<QObject *>("detailsTextObj");
545
546 sonameObj->setProperty("text", soitem->getDescName());
547 posTextObj->setProperty("text", soitem->getPosition());
548 detailImage->setProperty("refreshableSource", soitem->getImageURL(false));
549
550 loadObjectDescription(soitem);
551
552 infoBoxText->setProperty(
553 "text",
554 "<BR><BR>No Wikipedia information. <BR> Please try to download it using the orange download button below.");
555 loadObjectInfoBox(soitem);
556
557 QString summary = soitem->getSummary(false);
558
559 QString magText;
560 if (soitem->getType() == SkyObjItem::Constellation)
561 magText = xi18n("Magnitude: --");
562 else
563 magText = xi18n("Magnitude: %1", QLocale().toString(soitem->getMagnitude(), 'f', 2));
564
565 QString sbText = xi18n("Surface Brightness: %1", soitem->getSurfaceBrightness());
566
567 QString sizeText = xi18n("Size: %1", soitem->getSize());
568
569 QString details = summary + "<BR>" + sbText + "<BR>" + magText + "<BR>" + sizeText;
570 detailsTextObj->setProperty("text", details);
571
572 if (autoCenterCheckbox->property("checked") == true)
573 {
574 QTimer::singleShot(500, this, SLOT(onCenterButtonClicked()));
575 }
576
577 if (m_CurIndex != -1)
578 m_SoListObj->setProperty("currentIndex", m_CurIndex);
579}
580
581QString WIView::getWikipediaName(SkyObjItem *soitem)
582{
583 if (!soitem)
584 return "";
585
586 QString name;
587
588 if (soitem->getName().toLower().startsWith(QLatin1String("m ")))
589 name = soitem->getName().replace("M ", "Messier_").remove(' ');
590 else if (soitem->getName().toLower().startsWith(QLatin1String("ngc")))
591 name = soitem->getName().toLower().replace("ngc", "NGC_").remove(' ');
592 else if (soitem->getName().toLower().startsWith(QLatin1String("ic")))
593 name = soitem->getName().toLower().replace("ic", "IC_").remove(' ');
594 else if (soitem->getType() == SkyObjItem::Constellation)
595 {
596 QStringList words = soitem->getName().split(' ');
597
598 for (int i = 0; i < words.size(); i++)
599 {
600 QString temp = words.at(i).toLower();
601 temp[0] = temp[0].toUpper();
602 words.replace(i, temp);
603 }
604 name = words.join("_") + "_(constellation)";
605 if (name.contains("Serpens"))
606 name = "Serpens_(constellation)";
607 }
608 else if (soitem->getTypeName() == i18n("Asteroid"))
609 name = soitem->getName().remove(' ') + "_(asteroid)";
610 else if (soitem->getTypeName() == i18n("Comet"))
611 name = soitem->getLongName();
612 else if (soitem->getType() == SkyObjItem::Planet && soitem->getName() != i18n("Sun") && soitem->getName() != i18n("Moon"))
613 name = soitem->getName().remove(' ') + "_(planet)";
614 else if (soitem->getType() == SkyObjItem::Star)
615 {
616 StarObject *star = dynamic_cast<StarObject *>(soitem->getSkyObject());
617
618 // The greek name seems to give the most consistent search results for opensearch.
619 name = star->gname(false).replace(' ', '_');
620 if (name.isEmpty())
621 name = soitem->getName().replace(' ', '_') + "_(star)";
622 name.remove('[').remove(']');
623 }
624 else
625 name = soitem->getName().remove(' ');
626
627 return name;
628}
629
630void WIView::updateWikipediaDescription(SkyObjItem *soitem)
631{
632 if (!soitem)
633 return;
634
635 QString name = getWikipediaName(soitem);
636
637 QUrl url("https://en.wikipedia.org/w/api.php?format=xml&action=query&prop=extracts&exintro&explaintext&redirects=1&titles=" + name);
638
639 QNetworkReply *response = manager->get(QNetworkRequest(url));
640 QTimer::singleShot(30000, response, [response] //Shut it down after 30 sec.
641 {
642 response->abort();
643 response->deleteLater();
644 qDebug() << Q_FUNC_INFO << "Wikipedia Download Timed out.";
645 });
646 connect(response, &QNetworkReply::finished, this, [soitem, this, response, name]
647 {
648 response->deleteLater();
649 if (response->error() != QNetworkReply::NoError)
650 return;
652 if (!contentType.contains("charset=utf-8"))
653 {
654 qWarning() << "Content charsets other than utf-8 are not implemented yet.";
655 return;
656 }
657 QString result = QString::fromUtf8(response->readAll());
658 int leftPos = result.indexOf("<extract xml:space=\"preserve\">") + 30;
659 if (leftPos < 30)
660 return;
661 int rightPos = result.indexOf("</extract>") - leftPos;
662
663 QString srchtml =
664 "\n<p style=text-align:right>Source: (<a href='" + QString("https://en.wikipedia.org/wiki/") + name + "'>" +
665 "Wikipedia</a>)"; //Note the \n is so that the description is put on another line in the file. Doesn't affect the display but allows the source to be loaded in the details but not the list.
666 QString html = "<HTML>" + result.mid(leftPos, rightPos) + srchtml + "</HTML>";
667
668 saveObjectInfoBoxText(soitem, "description", html);
669
670 //TODO is this explicitly needed now with themes?
671#if 0
672 QString color = (Options::darkAppColors()) ? "red" : "white";
673 QString linkColor = (Options::darkAppColors()) ? "red" : "yellow";
674 html = "<HTML><HEAD><style type=text/css>body {color:" + color +
675 ";} a {text-decoration: none;color:" + linkColor + ";}</style></HEAD><BODY>" + html + "</BODY></HTML>";
676#endif
677
678 if (soitem == m_CurSoItem)
679 descTextObj->setProperty("text", html);
680 refreshListView();
681 });
682}
683
684void WIView::loadObjectDescription(SkyObjItem *soitem)
685{
686 QFile file;
687 QString fname = "description-" + soitem->getName().toLower().remove(' ') + ".html";
688 //determine filename in local user KDE directory tree.
689 file.setFileName(QDir(KSPaths::writableLocation(QStandardPaths::AppLocalDataLocation)).filePath("descriptions/" + fname));
690
691 if (file.exists())
692 {
693 if (file.open(QIODevice::ReadOnly))
694 {
695 QTextStream in(&file);
696 bool isDarkTheme = (Options::currentTheme() == "Night Vision");
697 QString color = (isDarkTheme) ? "red" : "white";
698 QString linkColor = (isDarkTheme) ? "red" : "yellow";
699
700 QString line = "<HTML><HEAD><style type=text/css>body {color:" + color +
701 ";} a {text-decoration: none;color:" + linkColor + ";}</style></HEAD><BODY><BR>" +
702 in.readAll() + "</BODY></HTML>";
703 descTextObj->setProperty("text", line);
704 file.close();
705 }
706 }
707 else
708 {
709 descTextObj->setProperty("text", soitem->getTypeName());
710 }
711}
712
713void WIView::loadObjectInfoBox(SkyObjItem *soitem)
714{
715 if (!soitem)
716 return;
717 QFile file;
718 QString fname = "infoText-" + soitem->getName().toLower().remove(' ') + ".html";
719 //determine filename in local user KDE directory tree.
720 file.setFileName(QDir(KSPaths::writableLocation(QStandardPaths::AppLocalDataLocation)).filePath("descriptions/" + fname));
721
722 if (file.exists())
723 {
724 if (file.open(QIODevice::ReadOnly))
725 {
726 QTextStream in(&file);
727 QString infoBoxHTML;
728 while (!in.atEnd())
729 {
730 infoBoxHTML = in.readAll();
731 QString wikiImageName =
734 "descriptions/wikiImage-" + soitem->getName().toLower().remove(' ') + ".png"))
735 .url();
736 if (!wikiImageName.isEmpty())
737 {
738 int captionEnd = infoBoxHTML.indexOf(
739 "</caption>"); //Start looking for the image AFTER the caption. Planets have images in their caption.
740 if (captionEnd == -1)
741 captionEnd = 0;
742 int leftImg = infoBoxHTML.indexOf("src=\"", captionEnd) + 5;
743 int rightImg = infoBoxHTML.indexOf("\"", leftImg) - leftImg;
744 QString imgURL = infoBoxHTML.mid(leftImg, rightImg);
745 infoBoxHTML.replace(imgURL, wikiImageName);
746 }
747 bool isDarkTheme = (Options::currentTheme() == "Night Vision");
748 QString color = (isDarkTheme) ? "red" : "white";
749 QString linkColor = (isDarkTheme) ? "red" : "yellow";
750 if (isDarkTheme)
751 infoBoxHTML.replace("color: white", "color: " + color);
752 infoBoxHTML = "<HTML><HEAD><style type=text/css>body {color:" + color +
753 ";} a {text-decoration: none;color:" + linkColor + ";}</style></HEAD><BODY>" +
754 infoBoxHTML + "</BODY></HTML>";
755
756 infoBoxText->setProperty("text", infoBoxHTML);
757 }
758 file.close();
759 }
760 }
761}
762
763void WIView::tryToUpdateWikipediaInfoInModel(bool onlyMissing)
764{
765 SkyObjListModel *model = m_ModManager->returnModel(m_CurrentObjectListName);
766 int objectNum = model->rowCount();
767 for (int i = 0; i < objectNum; i++)
768 {
769 SkyObjItem *soitem = model->getSkyObjItem(i);
770 QFile file;
771 QString fname = "infoText-" + soitem->getName().toLower().remove(' ') + ".html";
772 //determine filename in local user KDE directory tree.
773 file.setFileName(QDir(KSPaths::writableLocation(QStandardPaths::AppLocalDataLocation)).filePath("descriptions/" + fname));
774
775 if (file.exists() && onlyMissing)
776 continue;
777
778 tryToUpdateWikipediaInfo(soitem, getWikipediaName(soitem));
779 }
780}
781
782void WIView::tryToUpdateWikipediaInfo(SkyObjItem *soitem, QString name)
783{
784 if (name.isEmpty() || !soitem)
785 return;
786
787 QUrl url("https://en.wikipedia.org/w/index.php?action=render&title=" + name + "&redirects");
788 QNetworkReply *response = manager->get(QNetworkRequest(url));
789
790 QTimer::singleShot(30000, response, [response] //Shut it down after 30 sec.
791 {
792 response->abort();
793 response->deleteLater();
794 qDebug() << Q_FUNC_INFO << "Wikipedia Download Timed out.";
795 });
796 connect(response, &QNetworkReply::finished, this, [name, response, soitem, this]
797 {
798 response->deleteLater();
800 {
801 QString html = "<BR>Sorry, No Wikipedia article with this object name seems to exist. It is possible that "
802 "one does exist but does not match the namimg scheme.";
803 saveObjectInfoBoxText(soitem, "infoText", html);
804 infoBoxText->setProperty("text", html);
805 return;
806 }
807 if (response->error() != QNetworkReply::NoError)
808 return;
809 QString result = QString::fromUtf8(response->readAll());
810 int leftPos = result.indexOf("<table class=\"infobox");
811 int rightPos = result.indexOf("</table>", leftPos) - leftPos;
812
813 if (leftPos == -1)
814 {
815 //No InfoBox is Found
816 if (soitem->getType() == SkyObjItem::Star &&
817 name != soitem->getName().replace(' ', '_')) //For stars, the regular name rather than gname
818 {
819 tryToUpdateWikipediaInfo(soitem, soitem->getName().replace(' ', '_'));
820 return;
821 }
822 QString html = "<BR>Sorry, no Information Box in the object's Wikipedia article was found.";
823 saveObjectInfoBoxText(soitem, "infoText", html);
824 infoBoxText->setProperty("text", html);
825 return;
826 }
827
828 updateWikipediaDescription(soitem);
829
830 QString infoText = result.mid(leftPos, rightPos);
831
832 //This if statement should correct for a situation like for the planets where there is a single internal table inside the infoText Box.
833 if (infoText.indexOf("<table", leftPos + 6) != -1)
834 {
835 rightPos = result.indexOf("</table>", leftPos + rightPos + 6) - leftPos;
836 infoText = result.mid(leftPos, rightPos);
837 }
838
839 //This next section is for the headers in the colored boxes. It turns them black instead of white because they are more visible that way.
840 infoText.replace("background: #", "color:black;background: #")
841 .replace("background-color: #", "color:black;background: #")
842 .replace("background:#", "color:black;background:#")
843 .replace("background-color:#", "color:black;background:#")
844 .replace("background: pink", "color:black;background: pink");
845 infoText.replace("//", "http://"); //This is to fix links on wikipedia which are missing http from the url
846 infoText.replace("https:http:", "https:")
847 .replace("http:http:", "http:"); //Just in case it was done to an actual complete url
848
849 //This section is intended to remove links from the object name header at the top. The links break up the header.
850 int thLeft = infoText.indexOf("<th ");
851 if (thLeft != -1)
852 {
853 int thRight = infoText.indexOf("</th>", thLeft);
854 int firstA = infoText.indexOf("<a ", thLeft);
855 if (firstA != -1 && firstA < thRight)
856 {
857 int rightA = infoText.indexOf(">", firstA) - firstA + 1;
858 infoText.remove(firstA, rightA);
859 int endA = infoText.indexOf("</a>", firstA);
860 infoText.remove(endA, 4);
861 }
862 }
863
864 int annotationLeft = infoText.indexOf("<annotation");
865 int annotationRight = infoText.indexOf("</annotation>", annotationLeft) + 13 - annotationLeft;
866 infoText.remove(annotationLeft,
867 annotationRight); //This removes the annotation that does not render correctly for some DSOs.
868
869 int mathLeft = infoText.indexOf("<img src=\"https://wikimedia.org/api/rest_v1/media/math");
870 int mathRight = infoText.indexOf(">", mathLeft) + 1 - mathLeft;
871 infoText.remove(mathLeft, mathRight); //This removes an image that doesn't render properly for some DSOs.
872
873 infoText.replace("style=\"width:22em\"", "style=\"width:100%;background-color: black;color: white;\"");
874 infoText = infoText + "<BR>(Source: <a href='" + "https://en.wikipedia.org/w/index.php?title=" + name +
875 "&redirects" + "'>Wikipedia</a>)";
876 saveInfoURL(soitem, "https://en.wikipedia.org/w/index.php?title=" + name + "&redirects");
877
878 int captionEnd = infoText.indexOf(
879 "</caption>"); //Start looking for the image AFTER the caption. Planets have images in their caption.
880 if (captionEnd == -1)
881 captionEnd = 0;
882 int leftImg = infoText.indexOf("src=\"", captionEnd) + 5;
883 if (leftImg > captionEnd + 5)
884 {
885 int rightImg = infoText.indexOf("\"", leftImg) - leftImg;
886 QString imgURL = infoText.mid(leftImg, rightImg);
887 imgURL.replace(
888 "http://upload.wikimedia.org",
889 "https://upload.wikimedia.org"); //Although they will display, the images apparently don't download properly unless they are https.
890 saveImageURL(soitem, imgURL);
891 downloadWikipediaImage(soitem, imgURL);
892 }
893
894 QString html = "<CENTER>" + infoText + "</table></CENTER>";
895
896 saveObjectInfoBoxText(soitem, "infoText", html);
897 bool isDarkTheme = (Options::currentTheme() == "Night Vision");
898 QString color = (isDarkTheme) ? "red" : "white";
899 QString linkColor = (isDarkTheme) ? "red" : "yellow";
900 if (isDarkTheme)
901 html.replace("color: white", "color: " + color);
902 html = "<HTML><HEAD><style type=text/css>body {color:" + color +
903 ";} a {text-decoration: none;color:" + linkColor + ";}</style></HEAD><BODY>" + html + "</BODY></HTML>";
904 if (soitem == m_CurSoItem)
905 infoBoxText->setProperty("text", html);
906 });
907}
908
909void WIView::saveObjectInfoBoxText(SkyObjItem *soitem, QString type, QString text)
910{
911 QFile file;
912 QString fname = type + '-' + soitem->getName().toLower().remove(' ') + ".html";
913
914 QDir filePath(KSPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + "/descriptions");
915 filePath.mkpath(".");
916
917 //determine filename in local user KDE directory tree.
918 file.setFileName(filePath.filePath(fname));
919
920 if (file.open(QIODevice::WriteOnly) == false)
921 {
922 qDebug() << Q_FUNC_INFO << "Image text cannot be saved for later. file save error";
923 return;
924 }
925 else
926 {
927 QTextStream stream(&file);
928 stream << text;
929 file.close();
930 }
931}
932
933void WIView::saveImageURL(SkyObjItem *soitem, QString imageURL)
934{
935 QFile file;
936 //determine filename in local user KDE directory tree.
937 file.setFileName(QDir(KSPaths::writableLocation(QStandardPaths::AppLocalDataLocation)).filePath("image_url.dat"));
938 QString entry = soitem->getName() + ':' + "Show Wikipedia Image" + ':' + imageURL;
939
940 if (file.open(QIODevice::ReadOnly))
941 {
942 QTextStream in(&file);
943 QString line;
944 while (!in.atEnd())
945 {
946 line = in.readLine();
947 if (line == entry)
948 {
949 file.close();
950 return;
951 }
952 }
953 file.close();
954 }
955
957 {
958 qDebug() << Q_FUNC_INFO << "Image URL cannot be saved for later. image_url.dat error";
959 return;
960 }
961 else
962 {
963 QTextStream stream(&file);
964 stream << entry << '\n';
965 file.close();
966 }
967}
968
969void WIView::saveInfoURL(SkyObjItem *soitem, QString infoURL)
970{
971 QFile file;
972 //determine filename in local user KDE directory tree.
973 file.setFileName(QDir(KSPaths::writableLocation(QStandardPaths::AppLocalDataLocation)).filePath("info_url.dat"));
974 QString entry = soitem->getName() + ':' + "Wikipedia Page" + ':' + infoURL;
975
976 if (file.open(QIODevice::ReadOnly))
977 {
978 QTextStream in(&file);
979 QString line;
980 while (!in.atEnd())
981 {
982 line = in.readLine();
983 if (line == entry)
984 {
985 file.close();
986 return;
987 }
988 }
989 file.close();
990 }
991
993 {
994 qDebug() << Q_FUNC_INFO << "Info URL cannot be saved for later. info_url.dat error";
995 return;
996 }
997 else
998 {
999 QTextStream stream(&file);
1000 stream << entry << '\n';
1001 file.close();
1002 }
1003}
1004
1005void WIView::downloadWikipediaImage(SkyObjItem *soitem, QString imageURL)
1006{
1007 QString fname = "wikiImage-" + soitem->getName().toLower().remove(' ') + ".png";
1008
1009 QDir filePath(KSPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + "/descriptions");
1010 filePath.mkpath(".");
1011
1012 QString fileN = filePath.filePath(fname);
1013
1014 QNetworkReply *response = manager->get(QNetworkRequest(QUrl(imageURL)));
1015 QTimer::singleShot(60000, response, [response] //Shut it down after 60 sec.
1016 {
1017 response->abort();
1018 response->deleteLater();
1019 qDebug() << Q_FUNC_INFO << "Image Download Timed out.";
1020 });
1021 connect(response, &QNetworkReply::finished, this, [fileN, response, this]
1022 {
1023 response->deleteLater();
1024 if (response->error() != QNetworkReply::NoError)
1025 return;
1026 QImage *image = new QImage();
1027 QByteArray responseData = response->readAll();
1028 if (image->loadFromData(responseData))
1029 {
1030 image->save(fileN);
1031 refreshListView(); //This is to update the images displayed with the new image.
1032 }
1033 else
1034 qDebug() << Q_FUNC_INFO << "image not downloaded";
1035 });
1036}
DetailDialog is a window showing detailed information for a selected object.
const KStarsDateTime & lt() const
Definition kstarsdata.h:151
GeoLocation * geo()
Definition kstarsdata.h:230
SkyMapComposite * skyComposite()
Definition kstarsdata.h:166
This is the main window for KStars.
Definition kstars.h:89
SkyMap * map() const
Definition kstars.h:139
static KStars * Instance()
Definition kstars.h:121
KStarsData * data() const
Definition kstars.h:133
Manages models for QML listviews of different types of sky-objects.
void loadCatalog(const QString &name)
Load objects from the dso db for the catalog with name can be used to retreive the object lists later...
This class deals with the observing conditions of the night sky.
void setObsConditions(int bortle, double aperture, Equipment equip, TelescopeType telType)
Set new observing conditions.
Equipment
Equipment available to the user.
TelescopeType
Telescope Type (Reflector/Refractor)
SkyObject * findByName(const QString &name, bool exact=true) override
Search the children of this SkyMapComposite for a SkyObject whose name matches the argument.
SkyPoint * focusPoint()
retrieve the FocusPoint position.
Definition skymap.h:149
void setDestination(const SkyPoint &f)
sets the destination point of the sky map.
Definition skymap.cpp:980
void setFocusObject(SkyObject *o)
Set the FocusObject pointer to the argument.
Definition skymap.cpp:371
void setFocusPoint(SkyPoint *f)
set the FocusPoint; the position that is to be the next Destination.
Definition skymap.h:204
Represents an item in the list of interesting sky-objects.
Definition skyobjitem.h:21
SkyObject * getSkyObject()
Get sky-object associated with the SkyObjItem.
Definition skyobjitem.h:123
QString getSurfaceBrightness() const
Get surface-brightness of sky-object associated with the SkyObjItem as a QString to be displayed on t...
int getType() const
Get category of sky-object associated with the SkyObjItem as an integer.
Definition skyobjitem.h:99
QString getSummary(bool includeDescription) const
Get Summary Description for the SkyObjItem.
QString getTypeName() const
Get category of sky-object associated with the SkyObjItem as a QString.
Definition skyobjitem.h:93
QString getName() const
Get name of sky-object associated with the SkyObjItem.
Definition skyobjitem.h:69
QString getSize() const
Get size of sky-object associated with the SkyObjItem as a QString to be displayed on the details-vie...
float getMagnitude() const
Get magnitude of sky-object associated with the SkyObjItem.
QString getLongName() const
Get longname of sky-object associated with the SkyObjItem.
Definition skyobjitem.h:87
QString getDescName() const
Get longname of sky-object associated with the SkyObjItem.
Definition skyobjitem.h:75
QString getPosition() const
Get current position of sky-object associated with the SkyObjItem.
Definition skyobjitem.h:105
Represents a model for the list of interesting sky-objects to be displayed in the QML interface.
int rowCount(const QModelIndex &parent=QModelIndex()) const override
Overridden method from QAbstractItemModel.
SkyObjItem * getSkyObjItem(int index)
Get sky-object item referred to by index.
Provides all necessary information about an object in the sky: its coordinates, name(s),...
Definition skyobject.h:42
virtual QString name(void) const
Definition skyobject.h:145
This is a subclass of SkyObject.
Definition starobject.h:33
QString gname(bool useGreekChars=true) const
Returns the genetive name of the star.
ObsConditions::TelescopeType getTelType()
Inline method to return telescope type.
void loadDetailsView(SkyObjItem *soitem, int index)
Load details-view for selected sky-object.
Definition wiview.cpp:505
void onSettingsIconClicked()
public slot - Open WI settings dialog.
Definition wiview.cpp:387
void onReloadIconClicked()
public slot - Reload list of visible sky-objects.
Definition wiview.cpp:393
void onSoListItemClicked(int index)
public slot - Act upon signal emitted when an item is selected from list of sky-objects.
Definition wiview.cpp:282
void onDetailsButtonClicked()
public slot - Open Details Dialog to show more details for current sky-object.
Definition wiview.cpp:374
void onNextObjClicked()
public slot - Show details-view for next sky-object from list of current sky-objects's category.
Definition wiview.cpp:289
void onCenterButtonClicked()
public slot - Slew map to current sky-object in the details view.
Definition wiview.cpp:317
void onCategorySelected(QString model)
public slot - Act upon signal emitted when category of sky-object is selected from category selection...
Definition wiview.cpp:254
void updateModel(ObsConditions &obs)
Updates sky-object list models.
Definition wiview.cpp:464
WIView(QWidget *parent=nullptr)
Constructor - Store QML components as QObject pointers.
Definition wiview.cpp:40
void onSlewTelescopeButtonClicked()
public slot - Slew map to current sky-object in the details view.
Definition wiview.cpp:332
void updateObservingConditions()
Definition wiview.cpp:215
void onPrevObjClicked()
public slot - Show details-view for previous sky-object from list of current sky-objects's category.
Definition wiview.cpp:303
QString xi18n(const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
QString name(GameStandardAction id)
ButtonCode warningContinueCancel(QWidget *parent, const QString &text, const QString &title=QString(), const KGuiItem &buttonContinue=KStandardGuiItem::cont(), const KGuiItem &buttonCancel=KStandardGuiItem::cancel(), const QString &dontAskAgainName=QString(), Options options=Notify)
VehicleSection::Type type(QStringView coachNumber, QStringView coachClassification)
KGuiItem cont()
KGuiItem cancel()
QString applicationDirPath()
virtual int exec()
bool exists(const QString &fileName)
bool open(FILE *fh, OpenMode mode, FileHandleFlags handleFlags)
void setFileName(const QString &name)
virtual void close() override
bool loadFromData(QByteArrayView data, const char *format)
bool save(QIODevice *device, const char *format, int quality) const const
QByteArray readAll()
const_reference at(qsizetype i) const const
void replace(qsizetype i, parameter_type value)
qsizetype size() const const
QPushButton * addButton(StandardButton button)
QAbstractButton * clickedButton() const const
virtual int exec() override
void setDefaultButton(QPushButton *button)
void setText(const QString &text)
virtual void abort()=0
NetworkError error() const const
QVariant header(QNetworkRequest::KnownHeaders header) const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void deleteLater()
T findChild(const QString &name, Qt::FindChildOptions options) const const
QVariant property(const char *name) const const
bool setProperty(const char *name, QVariant &&value)
void setContextObject(QObject *object)
void setContextProperty(const QString &name, QObject *value)
void setResizeMode(ResizeMode)
QQmlContext * rootContext() const const
QQuickItem * rootObject() const const
void setSource(const QUrl &url)
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
QString fromUtf8(QByteArrayView str)
qsizetype indexOf(QChar ch, qsizetype from, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
QString mid(qsizetype position, qsizetype n) const const
QString & remove(QChar ch, Qt::CaseSensitivity cs)
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
QStringList split(QChar sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
QString toLower() const const
QString toUpper() const const
QString join(QChar separator) const const
WindowCloseButtonHint
QFuture< T > run(Function function,...)
QUrl fromLocalFile(const QString &localFile)
QString url(FormattingOptions options) const const
QString toString() const const
void setFlags(Qt::WindowFlags flags)
void show()
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 4 2024 16:38:44 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.