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
72#if defined(Q_OS_OSX)
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
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,
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,
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,
130
131 QObject *settingsIconObj = m_BaseObj->findChild<QQuickItem *>("settingsIconObj");
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");
150
151 QObject *downloadIconObj = m_BaseObj->findChild<QQuickItem *>("downloadIconObj");
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 QtConcurrent::run(m_ModManager.get(), &ModelManager::loadCatalog, model);
272 return;
273 }
274
275 updateModel(*m_Obs);
276}
277
279{
280 SkyObjItem *soitem = m_ModManager->returnModel(m_CurrentObjectListName)->getSkyObjItem(index);
281 if (soitem)
282 loadDetailsView(soitem, index);
283}
284
286{
287 if (!m_CurrentObjectListName.isEmpty())
288 {
289 int modelSize = m_ModManager->returnModel(m_CurrentObjectListName)->rowCount();
290 if (modelSize > 0)
291 {
292 SkyObjItem *nextItem =
293 m_ModManager->returnModel(m_CurrentObjectListName)->getSkyObjItem((m_CurIndex + 1) % modelSize);
294 loadDetailsView(nextItem, (m_CurIndex + 1) % modelSize);
295 }
296 }
297}
298
300{
301 if (!m_CurrentObjectListName.isEmpty())
302 {
303 int modelSize = m_ModManager->returnModel(m_CurrentObjectListName)->rowCount();
304 if (modelSize > 0)
305 {
307 m_ModManager->returnModel(m_CurrentObjectListName)->getSkyObjItem((m_CurIndex - 1 + modelSize) % modelSize);
308 loadDetailsView(prevItem, (m_CurIndex - 1 + modelSize) % modelSize);
309 }
310 }
311}
312
314{
315 ///Center map on selected sky-object
316 SkyObject *so = m_CurSoItem->getSkyObject();
318
319 if (so)
320 {
321 kstars->map()->setFocusPoint(so);
322 kstars->map()->setFocusObject(so);
323 kstars->map()->setDestination(*kstars->map()->focusPoint());
324 Options::setIsTracking(autoTrackCheckbox->property("checked") == true);
325 }
326}
327
329{
331 KMessageBox::warningContinueCancel(nullptr, "Are you sure you want your telescope to slew to this object?",
333 "continue_wi_slew_warning"))
334 {
335#ifdef HAVE_INDI
336
337 if (INDIListener::Instance()->size() == 0)
338 {
339 KSNotification::sorry(i18n("No connected mounts found."));
340 return;
341 }
342
343 for (auto &oneDevice : INDIListener::devices())
344 {
345 if (!(oneDevice->getDriverInterface() & INDI::BaseDevice::TELESCOPE_INTERFACE))
346 continue;
347
348 if (oneDevice->isConnected() == false)
349 {
350 KSNotification::error(i18n("Mount %1 is offline. Please connect and retry again.", oneDevice->getDeviceName()));
351 return;
352 }
353
354 auto mount = oneDevice->getMount();
355 if (!mount)
356 continue;
357 mount->Slew(m_CurSoItem->getSkyObject());
358
359 /// Slew map to selected sky-object
361 return;
362 }
363
364 KSNotification::sorry(i18n("No connected mounts found."));
365
366#endif
367 }
368}
369
371{
372 ///Code taken from WUTDialog::slotDetails()
374 SkyObject *so = m_CurSoItem->getSkyObject();
375 if (so)
376 {
377 DetailDialog *detail = new DetailDialog(so, kstars->data()->lt(), kstars->data()->geo(), kstars);
378 detail->exec();
379 delete detail;
380 }
381}
382
384{
386 kstars->showWISettingsUI();
387}
388
390{
391 if (!m_CurrentObjectListName.isEmpty())
392 {
393 updateModel(*m_Obs);
394 m_CurIndex = m_ModManager->returnModel(m_CurrentObjectListName)->getSkyObjIndex(m_CurSoItem);
395 }
396 loadDetailsView(m_CurSoItem, m_CurIndex);
397}
398
399void WIView::onVisibleIconClicked(bool visible)
400{
401 m_ModManager->setShowOnlyVisibleObjects(visible);
403}
404
405void WIView::onFavoriteIconClicked(bool favorites)
406{
407 m_ModManager->setShowOnlyFavoriteObjects(favorites);
409}
410
411void WIView::onUpdateIconClicked()
412{
414 QPushButton *currentObject = mbox.addButton("Current Object", QMessageBox::AcceptRole);
415 QPushButton *missingObjects = nullptr;
416 QPushButton *allObjects = nullptr;
417
418 mbox.setText("Please choose which object(s) to try to update with Wikipedia data.");
419 if (!m_CurrentObjectListName.isEmpty())
420 {
421 missingObjects = mbox.addButton("Objects with no data", QMessageBox::AcceptRole);
422 allObjects = mbox.addButton("Entire List", QMessageBox::AcceptRole);
423 }
424 QPushButton *cancel = mbox.addButton("Cancel", QMessageBox::AcceptRole);
425 mbox.setDefaultButton(cancel);
426
427 mbox.exec();
428 if (mbox.clickedButton() == currentObject)
429 {
430 if (m_CurSoItem != nullptr)
431 {
432 tryToUpdateWikipediaInfo(m_CurSoItem, getWikipediaName(m_CurSoItem));
433 }
434 }
435 else if (mbox.clickedButton() == allObjects || mbox.clickedButton() == missingObjects)
436 {
437 SkyObjListModel *model = m_ModManager->returnModel(m_CurrentObjectListName);
438 if (model->rowCount() > 0)
439 {
440 tryToUpdateWikipediaInfoInModel(mbox.clickedButton() == missingObjects);
441 }
442 else
443 {
444 qDebug() << Q_FUNC_INFO << "No Objects in List!";
445 }
446 }
447}
448
449void WIView::refreshListView()
450{
451 m_Ctxt->setContextProperty("soListModel", nullptr);
452 if (!m_CurrentObjectListName.isEmpty())
453 m_Ctxt->setContextProperty("soListModel", m_ModManager->returnModel(m_CurrentObjectListName));
454 if (m_CurIndex == -2)
456 if (m_CurIndex != -1)
457 m_SoListObj->setProperty("currentIndex", m_CurIndex);
458}
459
461{
462 if (!m_CurrentObjectListName.isEmpty())
463 {
464 m_Obs = &obs;
465 m_ModManager->updateModel(m_Obs, m_CurrentObjectListName);
466 }
467}
468
469void WIView::inspectSkyObject(const QString &name)
470{
471 if (!name.isEmpty() && name != "star")
472 {
473 SkyObject *obj = KStarsData::Instance()->skyComposite()->findByName(name);
474
475 if (obj)
476 inspectSkyObject(obj);
477 }
478}
479
480void WIView::inspectSkyObjectOnClick(SkyObject *obj)
481{
482 if (inspectOnClick && KStars::Instance()->isWIVisible())
483 inspectSkyObject(obj);
484}
485
486void WIView::inspectSkyObject(SkyObject *obj)
487{
488 if (!obj)
489 return;
490
491 if (obj->name() != "star")
492 {
493 m_CurrentObjectListName = "";
494 trackedItem.reset(new SkyObjItem(obj));
495 loadDetailsView(trackedItem.get(), -1);
496 m_BaseObj->setProperty("state", "singleItemSelected");
497 m_CategoryTitle->setProperty("text", "Selected Object");
498 }
499}
500
502{
503 if (soitem == nullptr)
504 return;
505
506 int modelSize = -1;
507
508 if (index != -1)
509 modelSize = m_ModManager->returnModel(m_CurrentObjectListName)->rowCount();
510
511 if (soitem != m_CurSoItem)
512 m_CurSoItem = soitem;
513
514 m_CurIndex = index;
515 if (modelSize <= 1)
516 {
517 m_NextObj->setProperty("visible", "false");
518 m_PrevObj->setProperty("visible", "false");
519 }
520 else
521 {
522 SkyObjItem *nextItem =
523 m_ModManager->returnModel(m_CurrentObjectListName)->getSkyObjItem((m_CurIndex + 1) % modelSize);
525 m_ModManager->returnModel(m_CurrentObjectListName)->getSkyObjItem((m_CurIndex - 1 + modelSize) % modelSize);
526
527 m_NextObj->setProperty("visible", "true");
528 m_PrevObj->setProperty("visible", "true");
529 QObject *nextTextObj = m_NextObj->findChild<QObject *>("nextTextObj");
530
531 nextTextObj->setProperty("text", nextItem->getName());
532 QObject *prevTextObj = m_PrevObj->findChild<QObject *>("prevTextObj");
533
534 prevTextObj->setProperty("text", prevItem->getName());
535 }
536
537 QObject *sonameObj = m_DetailsViewObj->findChild<QObject *>("sonameObj");
538 QObject *posTextObj = m_DetailsViewObj->findChild<QObject *>("posTextObj");
539 QObject *detailImage = m_DetailsViewObj->findChild<QObject *>("detailImage");
540 QObject *detailsTextObj = m_DetailsViewObj->findChild<QObject *>("detailsTextObj");
541
542 sonameObj->setProperty("text", soitem->getDescName());
543 posTextObj->setProperty("text", soitem->getPosition());
544 detailImage->setProperty("refreshableSource", soitem->getImageURL(false));
545
546 loadObjectDescription(soitem);
547
548 infoBoxText->setProperty(
549 "text",
550 "<BR><BR>No Wikipedia information. <BR> Please try to download it using the orange download button below.");
551 loadObjectInfoBox(soitem);
552
553 QString summary = soitem->getSummary(false);
554
556 if (soitem->getType() == SkyObjItem::Constellation)
557 magText = xi18n("Magnitude: --");
558 else
559 magText = xi18n("Magnitude: %1", QLocale().toString(soitem->getMagnitude(), 'f', 2));
560
561 QString sbText = xi18n("Surface Brightness: %1", soitem->getSurfaceBrightness());
562
563 QString sizeText = xi18n("Size: %1", soitem->getSize());
564
565 QString details = summary + "<BR>" + sbText + "<BR>" + magText + "<BR>" + sizeText;
566 detailsTextObj->setProperty("text", details);
567
568 if (autoCenterCheckbox->property("checked") == true)
569 {
571 }
572
573 if (m_CurIndex != -1)
574 m_SoListObj->setProperty("currentIndex", m_CurIndex);
575}
576
577QString WIView::getWikipediaName(SkyObjItem *soitem)
578{
579 if (!soitem)
580 return "";
581
582 QString name;
583
584 if (soitem->getName().toLower().startsWith(QLatin1String("m ")))
585 name = soitem->getName().replace("M ", "Messier_").remove(' ');
586 else if (soitem->getName().toLower().startsWith(QLatin1String("ngc")))
587 name = soitem->getName().toLower().replace("ngc", "NGC_").remove(' ');
588 else if (soitem->getName().toLower().startsWith(QLatin1String("ic")))
589 name = soitem->getName().toLower().replace("ic", "IC_").remove(' ');
590 else if (soitem->getType() == SkyObjItem::Constellation)
591 {
592 QStringList words = soitem->getName().split(' ');
593
594 for (int i = 0; i < words.size(); i++)
595 {
596 QString temp = words.at(i).toLower();
597 temp[0] = temp[0].toUpper();
598 words.replace(i, temp);
599 }
600 name = words.join("_") + "_(constellation)";
601 if (name.contains("Serpens"))
602 name = "Serpens_(constellation)";
603 }
604 else if (soitem->getTypeName() == i18n("Asteroid"))
605 name = soitem->getName().remove(' ') + "_(asteroid)";
606 else if (soitem->getTypeName() == i18n("Comet"))
607 name = soitem->getLongName();
608 else if (soitem->getType() == SkyObjItem::Planet && soitem->getName() != i18n("Sun") && soitem->getName() != i18n("Moon"))
609 name = soitem->getName().remove(' ') + "_(planet)";
610 else if (soitem->getType() == SkyObjItem::Star)
611 {
612 StarObject *star = dynamic_cast<StarObject *>(soitem->getSkyObject());
613
614 // The greek name seems to give the most consistent search results for opensearch.
615 name = star->gname(false).replace(' ', '_');
616 if (name.isEmpty())
617 name = soitem->getName().replace(' ', '_') + "_(star)";
618 name.remove('[').remove(']');
619 }
620 else
621 name = soitem->getName().remove(' ');
622
623 return name;
624}
625
626void WIView::updateWikipediaDescription(SkyObjItem *soitem)
627{
628 if (!soitem)
629 return;
630
631 QString name = getWikipediaName(soitem);
632
633 QUrl url("https://en.wikipedia.org/w/api.php?format=xml&action=query&prop=extracts&exintro&explaintext&redirects=1&titles=" + name);
634
635 QNetworkReply *response = manager->get(QNetworkRequest(url));
636 QTimer::singleShot(30000, response, [response] //Shut it down after 30 sec.
637 {
638 response->abort();
639 response->deleteLater();
640 qDebug() << Q_FUNC_INFO << "Wikipedia Download Timed out.";
641 });
643 {
644 response->deleteLater();
645 if (response->error() != QNetworkReply::NoError)
646 return;
647 QString contentType = response->header(QNetworkRequest::ContentTypeHeader).toString();
648 if (!contentType.contains("charset=utf-8"))
649 {
650 qWarning() << "Content charsets other than utf-8 are not implemented yet.";
651 return;
652 }
653 QString result = QString::fromUtf8(response->readAll());
654 int leftPos = result.indexOf("<extract xml:space=\"preserve\">") + 30;
655 if (leftPos < 30)
656 return;
657 int rightPos = result.indexOf("</extract>") - leftPos;
658
660 "\n<p style=text-align:right>Source: (<a href='" + QString("https://en.wikipedia.org/wiki/") + name + "'>" +
661 "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.
662 QString html = "<HTML>" + result.mid(leftPos, rightPos) + srchtml + "</HTML>";
663
664 saveObjectInfoBoxText(soitem, "description", html);
665
666 //TODO is this explicitly needed now with themes?
667#if 0
668 QString color = (Options::darkAppColors()) ? "red" : "white";
669 QString linkColor = (Options::darkAppColors()) ? "red" : "yellow";
670 html = "<HTML><HEAD><style type=text/css>body {color:" + color +
671 ";} a {text-decoration: none;color:" + linkColor + ";}</style></HEAD><BODY>" + html + "</BODY></HTML>";
672#endif
673
674 if (soitem == m_CurSoItem)
675 descTextObj->setProperty("text", html);
676 refreshListView();
677 });
678}
679
680void WIView::loadObjectDescription(SkyObjItem *soitem)
681{
682 QFile file;
683 QString fname = "description-" + soitem->getName().toLower().remove(' ') + ".html";
684 //determine filename in local user KDE directory tree.
685 file.setFileName(QDir(KSPaths::writableLocation(QStandardPaths::AppLocalDataLocation)).filePath("descriptions/" + fname));
686
687 if (file.exists())
688 {
689 if (file.open(QIODevice::ReadOnly))
690 {
691 QTextStream in(&file);
692 bool isDarkTheme = (Options::currentTheme() == "Night Vision");
693 QString color = (isDarkTheme) ? "red" : "white";
694 QString linkColor = (isDarkTheme) ? "red" : "yellow";
695
696 QString line = "<HTML><HEAD><style type=text/css>body {color:" + color +
697 ";} a {text-decoration: none;color:" + linkColor + ";}</style></HEAD><BODY><BR>" +
698 in.readAll() + "</BODY></HTML>";
699 descTextObj->setProperty("text", line);
700 file.close();
701 }
702 }
703 else
704 {
705 descTextObj->setProperty("text", soitem->getTypeName());
706 }
707}
708
709void WIView::loadObjectInfoBox(SkyObjItem *soitem)
710{
711 if (!soitem)
712 return;
713 QFile file;
714 QString fname = "infoText-" + soitem->getName().toLower().remove(' ') + ".html";
715 //determine filename in local user KDE directory tree.
716 file.setFileName(QDir(KSPaths::writableLocation(QStandardPaths::AppLocalDataLocation)).filePath("descriptions/" + fname));
717
718 if (file.exists())
719 {
720 if (file.open(QIODevice::ReadOnly))
721 {
722 QTextStream in(&file);
724 while (!in.atEnd())
725 {
726 infoBoxHTML = in.readAll();
730 "descriptions/wikiImage-" + soitem->getName().toLower().remove(' ') + ".png"))
731 .url();
732 if (!wikiImageName.isEmpty())
733 {
734 int captionEnd = infoBoxHTML.indexOf(
735 "</caption>"); //Start looking for the image AFTER the caption. Planets have images in their caption.
736 if (captionEnd == -1)
737 captionEnd = 0;
738 int leftImg = infoBoxHTML.indexOf("src=\"", captionEnd) + 5;
739 int rightImg = infoBoxHTML.indexOf("\"", leftImg) - leftImg;
742 }
743 bool isDarkTheme = (Options::currentTheme() == "Night Vision");
744 QString color = (isDarkTheme) ? "red" : "white";
745 QString linkColor = (isDarkTheme) ? "red" : "yellow";
746 if (isDarkTheme)
747 infoBoxHTML.replace("color: white", "color: " + color);
748 infoBoxHTML = "<HTML><HEAD><style type=text/css>body {color:" + color +
749 ";} a {text-decoration: none;color:" + linkColor + ";}</style></HEAD><BODY>" +
750 infoBoxHTML + "</BODY></HTML>";
751
752 infoBoxText->setProperty("text", infoBoxHTML);
753 }
754 file.close();
755 }
756 }
757}
758
759void WIView::tryToUpdateWikipediaInfoInModel(bool onlyMissing)
760{
761 SkyObjListModel *model = m_ModManager->returnModel(m_CurrentObjectListName);
762 int objectNum = model->rowCount();
763 for (int i = 0; i < objectNum; i++)
764 {
765 SkyObjItem *soitem = model->getSkyObjItem(i);
766 QFile file;
767 QString fname = "infoText-" + soitem->getName().toLower().remove(' ') + ".html";
768 //determine filename in local user KDE directory tree.
769 file.setFileName(QDir(KSPaths::writableLocation(QStandardPaths::AppLocalDataLocation)).filePath("descriptions/" + fname));
770
771 if (file.exists() && onlyMissing)
772 continue;
773
774 tryToUpdateWikipediaInfo(soitem, getWikipediaName(soitem));
775 }
776}
777
778void WIView::tryToUpdateWikipediaInfo(SkyObjItem *soitem, QString name)
779{
780 if (name.isEmpty() || !soitem)
781 return;
782
783 QUrl url("https://en.wikipedia.org/w/index.php?action=render&title=" + name + "&redirects");
784 QNetworkReply *response = manager->get(QNetworkRequest(url));
785
786 QTimer::singleShot(30000, response, [response] //Shut it down after 30 sec.
787 {
788 response->abort();
789 response->deleteLater();
790 qDebug() << Q_FUNC_INFO << "Wikipedia Download Timed out.";
791 });
793 {
794 response->deleteLater();
796 {
797 QString html = "<BR>Sorry, No Wikipedia article with this object name seems to exist. It is possible that "
798 "one does exist but does not match the namimg scheme.";
799 saveObjectInfoBoxText(soitem, "infoText", html);
800 infoBoxText->setProperty("text", html);
801 return;
802 }
804 return;
805 QString result = QString::fromUtf8(response->readAll());
806 int leftPos = result.indexOf("<table class=\"infobox");
807 int rightPos = result.indexOf("</table>", leftPos) - leftPos;
808
809 if (leftPos == -1)
810 {
811 //No InfoBox is Found
812 if (soitem->getType() == SkyObjItem::Star &&
813 name != soitem->getName().replace(' ', '_')) //For stars, the regular name rather than gname
814 {
815 tryToUpdateWikipediaInfo(soitem, soitem->getName().replace(' ', '_'));
816 return;
817 }
818 QString html = "<BR>Sorry, no Information Box in the object's Wikipedia article was found.";
819 saveObjectInfoBoxText(soitem, "infoText", html);
820 infoBoxText->setProperty("text", html);
821 return;
822 }
823
824 updateWikipediaDescription(soitem);
825
827
828 //This if statement should correct for a situation like for the planets where there is a single internal table inside the infoText Box.
829 if (infoText.indexOf("<table", leftPos + 6) != -1)
830 {
831 rightPos = result.indexOf("</table>", leftPos + rightPos + 6) - leftPos;
832 infoText = result.mid(leftPos, rightPos);
833 }
834
835 //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.
836 infoText.replace("background: #", "color:black;background: #")
837 .replace("background-color: #", "color:black;background: #")
838 .replace("background:#", "color:black;background:#")
839 .replace("background-color:#", "color:black;background:#")
840 .replace("background: pink", "color:black;background: pink");
841 infoText.replace("//", "http://"); //This is to fix links on wikipedia which are missing http from the url
842 infoText.replace("https:http:", "https:")
843 .replace("http:http:", "http:"); //Just in case it was done to an actual complete url
844
845 //This section is intended to remove links from the object name header at the top. The links break up the header.
846 int thLeft = infoText.indexOf("<th ");
847 if (thLeft != -1)
848 {
849 int thRight = infoText.indexOf("</th>", thLeft);
850 int firstA = infoText.indexOf("<a ", thLeft);
851 if (firstA != -1 && firstA < thRight)
852 {
853 int rightA = infoText.indexOf(">", firstA) - firstA + 1;
854 infoText.remove(firstA, rightA);
855 int endA = infoText.indexOf("</a>", firstA);
856 infoText.remove(endA, 4);
857 }
858 }
859
860 int annotationLeft = infoText.indexOf("<annotation");
861 int annotationRight = infoText.indexOf("</annotation>", annotationLeft) + 13 - annotationLeft;
863 annotationRight); //This removes the annotation that does not render correctly for some DSOs.
864
865 int mathLeft = infoText.indexOf("<img src=\"https://wikimedia.org/api/rest_v1/media/math");
866 int mathRight = infoText.indexOf(">", mathLeft) + 1 - mathLeft;
867 infoText.remove(mathLeft, mathRight); //This removes an image that doesn't render properly for some DSOs.
868
869 infoText.replace("style=\"width:22em\"", "style=\"width:100%;background-color: black;color: white;\"");
870 infoText = infoText + "<BR>(Source: <a href='" + "https://en.wikipedia.org/w/index.php?title=" + name +
871 "&redirects" + "'>Wikipedia</a>)";
872 saveInfoURL(soitem, "https://en.wikipedia.org/w/index.php?title=" + name + "&redirects");
873
874 int captionEnd = infoText.indexOf(
875 "</caption>"); //Start looking for the image AFTER the caption. Planets have images in their caption.
876 if (captionEnd == -1)
877 captionEnd = 0;
878 int leftImg = infoText.indexOf("src=\"", captionEnd) + 5;
879 if (leftImg > captionEnd + 5)
880 {
881 int rightImg = infoText.indexOf("\"", leftImg) - leftImg;
883 imgURL.replace(
884 "http://upload.wikimedia.org",
885 "https://upload.wikimedia.org"); //Although they will display, the images apparently don't download properly unless they are https.
886 saveImageURL(soitem, imgURL);
887 downloadWikipediaImage(soitem, imgURL);
888 }
889
890 QString html = "<CENTER>" + infoText + "</table></CENTER>";
891
892 saveObjectInfoBoxText(soitem, "infoText", html);
893 bool isDarkTheme = (Options::currentTheme() == "Night Vision");
894 QString color = (isDarkTheme) ? "red" : "white";
895 QString linkColor = (isDarkTheme) ? "red" : "yellow";
896 if (isDarkTheme)
897 html.replace("color: white", "color: " + color);
898 html = "<HTML><HEAD><style type=text/css>body {color:" + color +
899 ";} a {text-decoration: none;color:" + linkColor + ";}</style></HEAD><BODY>" + html + "</BODY></HTML>";
900 if (soitem == m_CurSoItem)
901 infoBoxText->setProperty("text", html);
902 });
903}
904
905void WIView::saveObjectInfoBoxText(SkyObjItem *soitem, QString type, QString text)
906{
907 QFile file;
908 QString fname = type + '-' + soitem->getName().toLower().remove(' ') + ".html";
909
910 QDir filePath(KSPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + "/descriptions");
911 filePath.mkpath(".");
912
913 //determine filename in local user KDE directory tree.
914 file.setFileName(filePath.filePath(fname));
915
916 if (file.open(QIODevice::WriteOnly) == false)
917 {
918 qDebug() << Q_FUNC_INFO << "Image text cannot be saved for later. file save error";
919 return;
920 }
921 else
922 {
923 QTextStream stream(&file);
924 stream << text;
925 file.close();
926 }
927}
928
929void WIView::saveImageURL(SkyObjItem *soitem, QString imageURL)
930{
931 QFile file;
932 //determine filename in local user KDE directory tree.
933 file.setFileName(QDir(KSPaths::writableLocation(QStandardPaths::AppLocalDataLocation)).filePath("image_url.dat"));
934 QString entry = soitem->getName() + ':' + "Show Wikipedia Image" + ':' + imageURL;
935
936 if (file.open(QIODevice::ReadOnly))
937 {
938 QTextStream in(&file);
939 QString line;
940 while (!in.atEnd())
941 {
942 line = in.readLine();
943 if (line == entry)
944 {
945 file.close();
946 return;
947 }
948 }
949 file.close();
950 }
951
953 {
954 qDebug() << Q_FUNC_INFO << "Image URL cannot be saved for later. image_url.dat error";
955 return;
956 }
957 else
958 {
959 QTextStream stream(&file);
960 stream << entry << '\n';
961 file.close();
962 }
963}
964
965void WIView::saveInfoURL(SkyObjItem *soitem, QString infoURL)
966{
967 QFile file;
968 //determine filename in local user KDE directory tree.
969 file.setFileName(QDir(KSPaths::writableLocation(QStandardPaths::AppLocalDataLocation)).filePath("info_url.dat"));
970 QString entry = soitem->getName() + ':' + "Wikipedia Page" + ':' + infoURL;
971
972 if (file.open(QIODevice::ReadOnly))
973 {
974 QTextStream in(&file);
975 QString line;
976 while (!in.atEnd())
977 {
978 line = in.readLine();
979 if (line == entry)
980 {
981 file.close();
982 return;
983 }
984 }
985 file.close();
986 }
987
989 {
990 qDebug() << Q_FUNC_INFO << "Info URL cannot be saved for later. info_url.dat error";
991 return;
992 }
993 else
994 {
995 QTextStream stream(&file);
996 stream << entry << '\n';
997 file.close();
998 }
999}
1000
1001void WIView::downloadWikipediaImage(SkyObjItem *soitem, QString imageURL)
1002{
1003 QString fname = "wikiImage-" + soitem->getName().toLower().remove(' ') + ".png";
1004
1005 QDir filePath(KSPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + "/descriptions");
1006 filePath.mkpath(".");
1007
1008 QString fileN = filePath.filePath(fname);
1009
1011 QTimer::singleShot(60000, response, [response] //Shut it down after 60 sec.
1012 {
1013 response->abort();
1014 response->deleteLater();
1015 qDebug() << Q_FUNC_INFO << "Image Download Timed out.";
1016 });
1018 {
1019 response->deleteLater();
1020 if (response->error() != QNetworkReply::NoError)
1021 return;
1022 QImage *image = new QImage();
1023 QByteArray responseData = response->readAll();
1024 if (image->loadFromData(responseData))
1025 {
1026 image->save(fileN);
1027 refreshListView(); //This is to update the images displayed with the new image.
1028 }
1029 else
1030 qDebug() << Q_FUNC_INFO << "image not downloaded";
1031 });
1032}
DetailDialog is a window showing detailed information for a selected object.
This is the main window for KStars.
Definition kstars.h:91
static KStars * Instance()
Definition kstars.h:123
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)
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 getName() const
Get name of sky-object associated with the SkyObjItem.
Definition skyobjitem.h:69
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.
void loadDetailsView(SkyObjItem *soitem, int index)
Load details-view for selected sky-object.
Definition wiview.cpp:501
void onSettingsIconClicked()
public slot - Open WI settings dialog.
Definition wiview.cpp:383
void onReloadIconClicked()
public slot - Reload list of visible sky-objects.
Definition wiview.cpp:389
void onSoListItemClicked(int index)
public slot - Act upon signal emitted when an item is selected from list of sky-objects.
Definition wiview.cpp:278
void onDetailsButtonClicked()
public slot - Open Details Dialog to show more details for current sky-object.
Definition wiview.cpp:370
void onNextObjClicked()
public slot - Show details-view for next sky-object from list of current sky-objects's category.
Definition wiview.cpp:285
void onCenterButtonClicked()
public slot - Slew map to current sky-object in the details view.
Definition wiview.cpp:313
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:460
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:328
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:299
QString xi18n(const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
Type type(const QSqlDatabase &db)
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)
KGuiItem cont()
KGuiItem cancel()
QString name(StandardShortcut id)
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
const_reference at(qsizetype i) const const
void replace(qsizetype i, parameter_type value)
qsizetype size() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
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)
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
void setFlags(Qt::WindowFlags flags)
void show()
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:19:04 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.