File indexing completed on 2024-04-28 15:11:25
0001 /* 0002 SPDX-FileCopyrightText: 2012 Samikshan Bairagya <samikshan@gmail.com> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "wiview.h" 0008 0009 #include "kspaths.h" 0010 #include "kstars.h" 0011 #include "ksnotification.h" 0012 #include "modelmanager.h" 0013 #include "obsconditions.h" 0014 #include "Options.h" 0015 #include "skymap.h" 0016 #include "skymapcomposite.h" 0017 #include "skyobjitem.h" 0018 #include "skyobjlistmodel.h" 0019 #include "starobject.h" 0020 #include "wiequipsettings.h" 0021 #include "dialogs/detaildialog.h" 0022 0023 #include <klocalizedcontext.h> 0024 0025 #include <QGraphicsObject> 0026 #include <QNetworkAccessManager> 0027 #include <QNetworkReply> 0028 #include <QQmlContext> 0029 #include <QQuickItem> 0030 #include <QQuickView> 0031 #include <QStandardPaths> 0032 #include <QtConcurrent> 0033 0034 #ifdef HAVE_INDI 0035 #include <basedevice.h> 0036 #include "indi/indilistener.h" 0037 #include "indi/indimount.h" 0038 #endif 0039 0040 WIView::WIView(QWidget *parent) : QWidget(parent) 0041 { 0042 //These settings are like this just to get it started. 0043 int bortle = Options::bortleClass(); 0044 int aperture = 100; 0045 ObsConditions::Equipment equip = ObsConditions::Telescope; 0046 ObsConditions::TelescopeType telType = ObsConditions::Reflector; 0047 0048 m_Obs = new ObsConditions(bortle, aperture, equip, telType); 0049 0050 m_ModManager.reset(new ModelManager(m_Obs)); 0051 0052 m_BaseView = new QQuickView(); 0053 0054 ///To use i18n() instead of qsTr() in qml/wiview.qml for translation 0055 //KDeclarative kd; 0056 // kd.setDeclarativeEngine(m_BaseView->engine()); 0057 //kd.initialize(); 0058 //kd.setupBindings(); 0059 0060 m_Ctxt = m_BaseView->rootContext(); 0061 0062 m_Ctxt->setContextProperty( 0063 "soListModel", 0064 m_ModManager 0065 ->getTempModel()); // This is to avoid an error saying it doesn't exist. 0066 0067 ///Use instead of KDeclarative 0068 m_Ctxt->setContextObject(new KLocalizedContext(m_BaseView)); 0069 0070 #if 0 0071 QString WI_Location; 0072 #if defined(Q_OS_OSX) 0073 WI_Location = QCoreApplication::applicationDirPath() + "/../Resources/kstars/tools/whatsinteresting/qml/wiview.qml"; 0074 if (!QFileInfo(WI_Location).exists()) 0075 WI_Location = KSPaths::locate(QStandardPaths::AppLocalDataLocation, "tools/whatsinteresting/qml/wiview.qml"); 0076 #elif defined(Q_OS_WIN) 0077 WI_Location = KSPaths::locate(QStandardPaths::GenericDataLocation, "tools/whatsinteresting/qml/wiview.qml"); 0078 #else 0079 WI_Location = KSPaths::locate(QStandardPaths::AppLocalDataLocation, "tools/whatsinteresting/qml/wiview.qml"); 0080 #endif 0081 0082 m_BaseView->setSource(QUrl::fromLocalFile(WI_Location)); 0083 #endif 0084 0085 m_BaseView->setSource(QUrl("qrc:/qml/whatisinteresting/wiview.qml")); 0086 0087 m_BaseObj = m_BaseView->rootObject(); 0088 0089 m_ProgressBar = m_BaseObj->findChild<QQuickItem *>("progressBar"); 0090 0091 m_loadingMessage = m_BaseObj->findChild<QQuickItem *>("loadingMessage"); 0092 0093 m_CategoryTitle = m_BaseObj->findChild<QQuickItem *>(QString("categoryTitle")); 0094 0095 m_ViewsRowObj = m_BaseObj->findChild<QQuickItem *>(QString("viewsRowObj")); 0096 connect(m_ViewsRowObj, SIGNAL(categorySelected(QString)), this, 0097 SLOT(onCategorySelected(QString))); 0098 connect(m_ViewsRowObj, SIGNAL(inspectSkyObject(QString)), this, 0099 SLOT(inspectSkyObject(QString))); 0100 0101 m_SoListObj = m_BaseObj->findChild<QQuickItem *>("soListObj"); 0102 connect(m_SoListObj, SIGNAL(soListItemClicked(int)), this, 0103 SLOT(onSoListItemClicked(int))); 0104 0105 m_DetailsViewObj = m_BaseObj->findChild<QQuickItem *>("detailsViewObj"); 0106 0107 descTextObj = m_DetailsViewObj->findChild<QObject *>("descTextObj"); 0108 infoBoxText = m_DetailsViewObj->findChild<QObject *>("infoBoxText"); 0109 0110 m_NextObj = m_BaseObj->findChild<QQuickItem *>("nextObj"); 0111 connect(m_NextObj, SIGNAL(nextObjClicked()), this, SLOT(onNextObjClicked())); 0112 m_PrevObj = m_BaseObj->findChild<QQuickItem *>("prevObj"); 0113 connect(m_PrevObj, SIGNAL(prevObjClicked()), this, SLOT(onPrevObjClicked())); 0114 0115 m_CenterButtonObj = m_BaseObj->findChild<QQuickItem *>("centerButtonObj"); 0116 connect(m_CenterButtonObj, SIGNAL(centerButtonClicked()), this, 0117 SLOT(onCenterButtonClicked())); 0118 0119 autoCenterCheckbox = m_DetailsViewObj->findChild<QObject *>("autoCenterCheckbox"); 0120 autoTrackCheckbox = m_DetailsViewObj->findChild<QObject *>("autoTrackCheckbox"); 0121 0122 m_SlewTelescopeButtonObj = 0123 m_BaseObj->findChild<QQuickItem *>("slewTelescopeButtonObj"); 0124 connect(m_SlewTelescopeButtonObj, SIGNAL(slewTelescopeButtonClicked()), this, 0125 SLOT(onSlewTelescopeButtonClicked())); 0126 0127 m_DetailsButtonObj = m_BaseObj->findChild<QQuickItem *>("detailsButtonObj"); 0128 connect(m_DetailsButtonObj, SIGNAL(detailsButtonClicked()), this, 0129 SLOT(onDetailsButtonClicked())); 0130 0131 QObject *settingsIconObj = m_BaseObj->findChild<QQuickItem *>("settingsIconObj"); 0132 connect(settingsIconObj, SIGNAL(settingsIconClicked()), this, 0133 SLOT(onSettingsIconClicked())); 0134 0135 inspectIconObj = m_BaseObj->findChild<QQuickItem *>("inspectIconObj"); 0136 connect(inspectIconObj, SIGNAL(inspectIconClicked(bool)), this, 0137 SLOT(onInspectIconClicked(bool))); 0138 0139 visibleIconObj = m_BaseObj->findChild<QQuickItem *>("visibleIconObj"); 0140 connect(visibleIconObj, SIGNAL(visibleIconClicked(bool)), this, 0141 SLOT(onVisibleIconClicked(bool))); 0142 0143 favoriteIconObj = m_BaseObj->findChild<QQuickItem *>("favoriteIconObj"); 0144 connect(favoriteIconObj, SIGNAL(favoriteIconClicked(bool)), this, 0145 SLOT(onFavoriteIconClicked(bool))); 0146 0147 QObject *reloadIconObj = m_BaseObj->findChild<QQuickItem *>("reloadIconObj"); 0148 connect(reloadIconObj, SIGNAL(reloadIconClicked()), this, 0149 SLOT(onReloadIconClicked())); 0150 0151 QObject *downloadIconObj = m_BaseObj->findChild<QQuickItem *>("downloadIconObj"); 0152 connect(downloadIconObj, SIGNAL(downloadIconClicked()), this, 0153 SLOT(onUpdateIconClicked())); 0154 0155 m_BaseView->setResizeMode(QQuickView::SizeRootObjectToView); 0156 m_BaseView->show(); 0157 0158 // Fix some weird issue with what's interesting panel view under Windows 0159 // In Qt 5.9 it content is messed up and there is no way to close the panel 0160 #ifdef Q_OS_WIN 0161 m_BaseView->setFlags(Qt::WindowCloseButtonHint); 0162 #endif 0163 0164 connect(KStars::Instance()->map(), SIGNAL(objectClicked(SkyObject *)), this, 0165 SLOT(inspectSkyObjectOnClick(SkyObject *))); 0166 0167 manager.reset(new QNetworkAccessManager()); 0168 0169 setProgressBarVisible(true); 0170 connect(m_ModManager.get(), SIGNAL(loadProgressUpdated(double)), this, 0171 SLOT(updateProgress(double))); 0172 connect(m_ModManager.get(), SIGNAL(modelUpdated()), this, SLOT(refreshListView())); 0173 m_ViewsRowObj->setProperty("enabled", false); 0174 0175 inspectOnClick = false; 0176 0177 nightVision = m_BaseObj->findChild<QObject *>("nightVision"); 0178 //if (Options::darkAppColors()) 0179 // nightVision->setProperty("state", "active"); 0180 } 0181 0182 void WIView::setNightVisionOn(bool on) 0183 { 0184 if (on) 0185 nightVision->setProperty("state", "active"); 0186 else 0187 nightVision->setProperty("state", ""); 0188 0189 if (m_CurSoItem != nullptr) 0190 loadDetailsView(m_CurSoItem, m_CurIndex); 0191 } 0192 0193 void WIView::setProgressBarVisible(bool visible) 0194 { 0195 m_ProgressBar->setProperty("visible", visible); 0196 } 0197 0198 void WIView::updateProgress(double value) 0199 { 0200 m_ProgressBar->setProperty("value", value); 0201 0202 if (value == 1) 0203 { 0204 setProgressBarVisible(false); 0205 m_ViewsRowObj->setProperty("enabled", true); 0206 m_loadingMessage->setProperty("state", ""); 0207 } 0208 else 0209 { 0210 setProgressBarVisible(true); 0211 m_loadingMessage->setProperty("state", "loading"); 0212 } 0213 } 0214 0215 void WIView::updateObservingConditions() 0216 { 0217 int bortle = Options::bortleClass(); 0218 0219 /** 0220 NOTE This part of the code dealing with equipment type is presently not required 0221 as WI does not differentiate between Telescope and Binoculars. It only needs the 0222 aperture of the equipment whichever available. However this is kept as a part of 0223 the code as support to be utilised in the future. 0224 **/ 0225 ObsConditions::Equipment equip = ObsConditions::None; 0226 0227 if (Options::telescopeCheck() && Options::binocularsCheck()) 0228 equip = ObsConditions::Both; 0229 else if (Options::telescopeCheck()) 0230 equip = ObsConditions::Telescope; 0231 else if (Options::binocularsCheck()) 0232 equip = ObsConditions::Binoculars; 0233 0234 ObsConditions::TelescopeType telType; 0235 0236 if (KStars::Instance()->getWIEquipSettings()) 0237 telType = (equip == ObsConditions::Telescope) ? KStars::Instance()->getWIEquipSettings()->getTelType() : 0238 ObsConditions::Invalid; 0239 else 0240 telType = ObsConditions::Invalid; 0241 0242 int aperture = 100; 0243 0244 //This doesn't work correctly, FIXME!! 0245 // if(KStars::Instance()->getWIEquipSettings()) 0246 // aperture = KStars::Instance()->getWIEquipSettings()->getAperture(); 0247 0248 if (!m_Obs) 0249 m_Obs = new ObsConditions(bortle, aperture, equip, telType); 0250 else 0251 m_Obs->setObsConditions(bortle, aperture, equip, telType); 0252 } 0253 0254 void WIView::onCategorySelected(QString model) 0255 { 0256 m_CurrentObjectListName = model; 0257 m_Ctxt->setContextProperty("soListModel", 0258 m_ModManager->returnModel(m_CurrentObjectListName)); 0259 m_CurIndex = -2; 0260 if (!m_ModManager->showOnlyVisibleObjects()) 0261 visibleIconObj->setProperty("state", "unchecked"); 0262 if (!m_ModManager->showOnlyFavoriteObjects()) 0263 favoriteIconObj->setProperty("state", "unchecked"); 0264 0265 if ((QStringList() << "ngc" 0266 << "ic" 0267 << "messier" 0268 << "sharpless") 0269 .contains(model)) 0270 { 0271 QtConcurrent::run(m_ModManager.get(), &ModelManager::loadCatalog, model); 0272 return; 0273 } 0274 0275 updateModel(*m_Obs); 0276 } 0277 0278 void WIView::onSoListItemClicked(int index) 0279 { 0280 SkyObjItem *soitem = m_ModManager->returnModel(m_CurrentObjectListName)->getSkyObjItem(index); 0281 if (soitem) 0282 loadDetailsView(soitem, index); 0283 } 0284 0285 void WIView::onNextObjClicked() 0286 { 0287 if (!m_CurrentObjectListName.isEmpty()) 0288 { 0289 int modelSize = m_ModManager->returnModel(m_CurrentObjectListName)->rowCount(); 0290 if (modelSize > 0) 0291 { 0292 SkyObjItem *nextItem = 0293 m_ModManager->returnModel(m_CurrentObjectListName)->getSkyObjItem((m_CurIndex + 1) % modelSize); 0294 loadDetailsView(nextItem, (m_CurIndex + 1) % modelSize); 0295 } 0296 } 0297 } 0298 0299 void WIView::onPrevObjClicked() 0300 { 0301 if (!m_CurrentObjectListName.isEmpty()) 0302 { 0303 int modelSize = m_ModManager->returnModel(m_CurrentObjectListName)->rowCount(); 0304 if (modelSize > 0) 0305 { 0306 SkyObjItem *prevItem = 0307 m_ModManager->returnModel(m_CurrentObjectListName)->getSkyObjItem((m_CurIndex - 1 + modelSize) % modelSize); 0308 loadDetailsView(prevItem, (m_CurIndex - 1 + modelSize) % modelSize); 0309 } 0310 } 0311 } 0312 0313 void WIView::onCenterButtonClicked() 0314 { 0315 ///Center map on selected sky-object 0316 SkyObject *so = m_CurSoItem->getSkyObject(); 0317 KStars *kstars = KStars::Instance(); 0318 0319 if (so) 0320 { 0321 kstars->map()->setFocusPoint(so); 0322 kstars->map()->setFocusObject(so); 0323 kstars->map()->setDestination(*kstars->map()->focusPoint()); 0324 Options::setIsTracking(autoTrackCheckbox->property("checked") == true); 0325 } 0326 } 0327 0328 void WIView::onSlewTelescopeButtonClicked() 0329 { 0330 if (KMessageBox::Continue == 0331 KMessageBox::warningContinueCancel(nullptr, "Are you sure you want your telescope to slew to this object?", 0332 i18n("Continue Slew"), KStandardGuiItem::cont(), KStandardGuiItem::cancel(), 0333 "continue_wi_slew_warning")) 0334 { 0335 #ifdef HAVE_INDI 0336 0337 if (INDIListener::Instance()->size() == 0) 0338 { 0339 KSNotification::sorry(i18n("No connected mounts found.")); 0340 return; 0341 } 0342 0343 for (auto &oneDevice : INDIListener::devices()) 0344 { 0345 if (!(oneDevice->getDriverInterface() & INDI::BaseDevice::TELESCOPE_INTERFACE)) 0346 continue; 0347 0348 if (oneDevice->isConnected() == false) 0349 { 0350 KSNotification::error(i18n("Mount %1 is offline. Please connect and retry again.", oneDevice->getDeviceName())); 0351 return; 0352 } 0353 0354 auto mount = oneDevice->getMount(); 0355 if (!mount) 0356 continue; 0357 mount->Slew(m_CurSoItem->getSkyObject()); 0358 0359 /// Slew map to selected sky-object 0360 onCenterButtonClicked(); 0361 return; 0362 } 0363 0364 KSNotification::sorry(i18n("No connected mounts found.")); 0365 0366 #endif 0367 } 0368 } 0369 0370 void WIView::onDetailsButtonClicked() 0371 { 0372 ///Code taken from WUTDialog::slotDetails() 0373 KStars *kstars = KStars::Instance(); 0374 SkyObject *so = m_CurSoItem->getSkyObject(); 0375 if (so) 0376 { 0377 DetailDialog *detail = new DetailDialog(so, kstars->data()->lt(), kstars->data()->geo(), kstars); 0378 detail->exec(); 0379 delete detail; 0380 } 0381 } 0382 0383 void WIView::onSettingsIconClicked() 0384 { 0385 KStars *kstars = KStars::Instance(); 0386 kstars->showWISettingsUI(); 0387 } 0388 0389 void WIView::onReloadIconClicked() 0390 { 0391 if (!m_CurrentObjectListName.isEmpty()) 0392 { 0393 updateModel(*m_Obs); 0394 m_CurIndex = m_ModManager->returnModel(m_CurrentObjectListName)->getSkyObjIndex(m_CurSoItem); 0395 } 0396 loadDetailsView(m_CurSoItem, m_CurIndex); 0397 } 0398 0399 void WIView::onVisibleIconClicked(bool visible) 0400 { 0401 m_ModManager->setShowOnlyVisibleObjects(visible); 0402 onReloadIconClicked(); 0403 } 0404 0405 void WIView::onFavoriteIconClicked(bool favorites) 0406 { 0407 m_ModManager->setShowOnlyFavoriteObjects(favorites); 0408 onReloadIconClicked(); 0409 } 0410 0411 void WIView::onUpdateIconClicked() 0412 { 0413 QMessageBox mbox; 0414 QPushButton *currentObject = mbox.addButton("Current Object", QMessageBox::AcceptRole); 0415 QPushButton *missingObjects = nullptr; 0416 QPushButton *allObjects = nullptr; 0417 0418 mbox.setText("Please choose which object(s) to try to update with Wikipedia data."); 0419 if (!m_CurrentObjectListName.isEmpty()) 0420 { 0421 missingObjects = mbox.addButton("Objects with no data", QMessageBox::AcceptRole); 0422 allObjects = mbox.addButton("Entire List", QMessageBox::AcceptRole); 0423 } 0424 QPushButton *cancel = mbox.addButton("Cancel", QMessageBox::AcceptRole); 0425 mbox.setDefaultButton(cancel); 0426 0427 mbox.exec(); 0428 if (mbox.clickedButton() == currentObject) 0429 { 0430 if (m_CurSoItem != nullptr) 0431 { 0432 tryToUpdateWikipediaInfo(m_CurSoItem, getWikipediaName(m_CurSoItem)); 0433 } 0434 } 0435 else if (mbox.clickedButton() == allObjects || mbox.clickedButton() == missingObjects) 0436 { 0437 SkyObjListModel *model = m_ModManager->returnModel(m_CurrentObjectListName); 0438 if (model->rowCount() > 0) 0439 { 0440 tryToUpdateWikipediaInfoInModel(mbox.clickedButton() == missingObjects); 0441 } 0442 else 0443 { 0444 qDebug() << Q_FUNC_INFO << "No Objects in List!"; 0445 } 0446 } 0447 } 0448 0449 void WIView::refreshListView() 0450 { 0451 m_Ctxt->setContextProperty("soListModel", nullptr); 0452 if (!m_CurrentObjectListName.isEmpty()) 0453 m_Ctxt->setContextProperty("soListModel", m_ModManager->returnModel(m_CurrentObjectListName)); 0454 if (m_CurIndex == -2) 0455 onSoListItemClicked(0); 0456 if (m_CurIndex != -1) 0457 m_SoListObj->setProperty("currentIndex", m_CurIndex); 0458 } 0459 0460 void WIView::updateModel(ObsConditions &obs) 0461 { 0462 if (!m_CurrentObjectListName.isEmpty()) 0463 { 0464 m_Obs = &obs; 0465 m_ModManager->updateModel(m_Obs, m_CurrentObjectListName); 0466 } 0467 } 0468 0469 void WIView::inspectSkyObject(const QString &name) 0470 { 0471 if (!name.isEmpty() && name != "star") 0472 { 0473 SkyObject *obj = KStarsData::Instance()->skyComposite()->findByName(name); 0474 0475 if (obj) 0476 inspectSkyObject(obj); 0477 } 0478 } 0479 0480 void WIView::inspectSkyObjectOnClick(SkyObject *obj) 0481 { 0482 if (inspectOnClick && KStars::Instance()->isWIVisible()) 0483 inspectSkyObject(obj); 0484 } 0485 0486 void WIView::inspectSkyObject(SkyObject *obj) 0487 { 0488 if (!obj) 0489 return; 0490 0491 if (obj->name() != "star") 0492 { 0493 m_CurrentObjectListName = ""; 0494 trackedItem.reset(new SkyObjItem(obj)); 0495 loadDetailsView(trackedItem.get(), -1); 0496 m_BaseObj->setProperty("state", "singleItemSelected"); 0497 m_CategoryTitle->setProperty("text", "Selected Object"); 0498 } 0499 } 0500 0501 void WIView::loadDetailsView(SkyObjItem *soitem, int index) 0502 { 0503 if (soitem == nullptr) 0504 return; 0505 0506 int modelSize = -1; 0507 0508 if (index != -1) 0509 modelSize = m_ModManager->returnModel(m_CurrentObjectListName)->rowCount(); 0510 0511 if (soitem != m_CurSoItem) 0512 m_CurSoItem = soitem; 0513 0514 m_CurIndex = index; 0515 if (modelSize <= 1) 0516 { 0517 m_NextObj->setProperty("visible", "false"); 0518 m_PrevObj->setProperty("visible", "false"); 0519 } 0520 else 0521 { 0522 SkyObjItem *nextItem = 0523 m_ModManager->returnModel(m_CurrentObjectListName)->getSkyObjItem((m_CurIndex + 1) % modelSize); 0524 SkyObjItem *prevItem = 0525 m_ModManager->returnModel(m_CurrentObjectListName)->getSkyObjItem((m_CurIndex - 1 + modelSize) % modelSize); 0526 0527 m_NextObj->setProperty("visible", "true"); 0528 m_PrevObj->setProperty("visible", "true"); 0529 QObject *nextTextObj = m_NextObj->findChild<QObject *>("nextTextObj"); 0530 0531 nextTextObj->setProperty("text", nextItem->getName()); 0532 QObject *prevTextObj = m_PrevObj->findChild<QObject *>("prevTextObj"); 0533 0534 prevTextObj->setProperty("text", prevItem->getName()); 0535 } 0536 0537 QObject *sonameObj = m_DetailsViewObj->findChild<QObject *>("sonameObj"); 0538 QObject *posTextObj = m_DetailsViewObj->findChild<QObject *>("posTextObj"); 0539 QObject *detailImage = m_DetailsViewObj->findChild<QObject *>("detailImage"); 0540 QObject *detailsTextObj = m_DetailsViewObj->findChild<QObject *>("detailsTextObj"); 0541 0542 sonameObj->setProperty("text", soitem->getDescName()); 0543 posTextObj->setProperty("text", soitem->getPosition()); 0544 detailImage->setProperty("refreshableSource", soitem->getImageURL(false)); 0545 0546 loadObjectDescription(soitem); 0547 0548 infoBoxText->setProperty( 0549 "text", 0550 "<BR><BR>No Wikipedia information. <BR> Please try to download it using the orange download button below."); 0551 loadObjectInfoBox(soitem); 0552 0553 QString summary = soitem->getSummary(false); 0554 0555 QString magText; 0556 if (soitem->getType() == SkyObjItem::Constellation) 0557 magText = xi18n("Magnitude: --"); 0558 else 0559 magText = xi18n("Magnitude: %1", QLocale().toString(soitem->getMagnitude(), 'f', 2)); 0560 0561 QString sbText = xi18n("Surface Brightness: %1", soitem->getSurfaceBrightness()); 0562 0563 QString sizeText = xi18n("Size: %1", soitem->getSize()); 0564 0565 QString details = summary + "<BR>" + sbText + "<BR>" + magText + "<BR>" + sizeText; 0566 detailsTextObj->setProperty("text", details); 0567 0568 if (autoCenterCheckbox->property("checked") == true) 0569 { 0570 QTimer::singleShot(500, this, SLOT(onCenterButtonClicked())); 0571 } 0572 0573 if (m_CurIndex != -1) 0574 m_SoListObj->setProperty("currentIndex", m_CurIndex); 0575 } 0576 0577 QString WIView::getWikipediaName(SkyObjItem *soitem) 0578 { 0579 if (!soitem) 0580 return ""; 0581 0582 QString name; 0583 0584 if (soitem->getName().toLower().startsWith(QLatin1String("m "))) 0585 name = soitem->getName().replace("M ", "Messier_").remove(' '); 0586 else if (soitem->getName().toLower().startsWith(QLatin1String("ngc"))) 0587 name = soitem->getName().toLower().replace("ngc", "NGC_").remove(' '); 0588 else if (soitem->getName().toLower().startsWith(QLatin1String("ic"))) 0589 name = soitem->getName().toLower().replace("ic", "IC_").remove(' '); 0590 else if (soitem->getType() == SkyObjItem::Constellation) 0591 { 0592 QStringList words = soitem->getName().split(' '); 0593 0594 for (int i = 0; i < words.size(); i++) 0595 { 0596 QString temp = words.at(i).toLower(); 0597 temp[0] = temp[0].toUpper(); 0598 words.replace(i, temp); 0599 } 0600 name = words.join("_") + "_(constellation)"; 0601 if (name.contains("Serpens")) 0602 name = "Serpens_(constellation)"; 0603 } 0604 else if (soitem->getTypeName() == i18n("Asteroid")) 0605 name = soitem->getName().remove(' ') + "_(asteroid)"; 0606 else if (soitem->getTypeName() == i18n("Comet")) 0607 name = soitem->getLongName(); 0608 else if (soitem->getType() == SkyObjItem::Planet && soitem->getName() != i18n("Sun") && soitem->getName() != i18n("Moon")) 0609 name = soitem->getName().remove(' ') + "_(planet)"; 0610 else if (soitem->getType() == SkyObjItem::Star) 0611 { 0612 StarObject *star = dynamic_cast<StarObject *>(soitem->getSkyObject()); 0613 0614 // The greek name seems to give the most consistent search results for opensearch. 0615 name = star->gname(false).replace(' ', '_'); 0616 if (name.isEmpty()) 0617 name = soitem->getName().replace(' ', '_') + "_(star)"; 0618 name.remove('[').remove(']'); 0619 } 0620 else 0621 name = soitem->getName().remove(' '); 0622 0623 return name; 0624 } 0625 0626 void WIView::updateWikipediaDescription(SkyObjItem *soitem) 0627 { 0628 if (!soitem) 0629 return; 0630 0631 QString name = getWikipediaName(soitem); 0632 0633 QUrl url("https://en.wikipedia.org/w/api.php?format=xml&action=query&prop=extracts&exintro&explaintext&redirects=1&titles=" + name); 0634 0635 QNetworkReply *response = manager->get(QNetworkRequest(url)); 0636 QTimer::singleShot(30000, response, [response] //Shut it down after 30 sec. 0637 { 0638 response->abort(); 0639 response->deleteLater(); 0640 qDebug() << Q_FUNC_INFO << "Wikipedia Download Timed out."; 0641 }); 0642 connect(response, &QNetworkReply::finished, this, [soitem, this, response, name] 0643 { 0644 response->deleteLater(); 0645 if (response->error() != QNetworkReply::NoError) 0646 return; 0647 QString contentType = response->header(QNetworkRequest::ContentTypeHeader).toString(); 0648 if (!contentType.contains("charset=utf-8")) 0649 { 0650 qWarning() << "Content charsets other than utf-8 are not implemented yet."; 0651 return; 0652 } 0653 QString result = QString::fromUtf8(response->readAll()); 0654 int leftPos = result.indexOf("<extract xml:space=\"preserve\">") + 30; 0655 if (leftPos < 30) 0656 return; 0657 int rightPos = result.indexOf("</extract>") - leftPos; 0658 0659 QString srchtml = 0660 "\n<p style=text-align:right>Source: (<a href='" + QString("https://en.wikipedia.org/wiki/") + name + "'>" + 0661 "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. 0662 QString html = "<HTML>" + result.mid(leftPos, rightPos) + srchtml + "</HTML>"; 0663 0664 saveObjectInfoBoxText(soitem, "description", html); 0665 0666 //TODO is this explicitly needed now with themes? 0667 #if 0 0668 QString color = (Options::darkAppColors()) ? "red" : "white"; 0669 QString linkColor = (Options::darkAppColors()) ? "red" : "yellow"; 0670 html = "<HTML><HEAD><style type=text/css>body {color:" + color + 0671 ";} a {text-decoration: none;color:" + linkColor + ";}</style></HEAD><BODY>" + html + "</BODY></HTML>"; 0672 #endif 0673 0674 if (soitem == m_CurSoItem) 0675 descTextObj->setProperty("text", html); 0676 refreshListView(); 0677 }); 0678 } 0679 0680 void WIView::loadObjectDescription(SkyObjItem *soitem) 0681 { 0682 QFile file; 0683 QString fname = "description-" + soitem->getName().toLower().remove(' ') + ".html"; 0684 //determine filename in local user KDE directory tree. 0685 file.setFileName(QDir(KSPaths::writableLocation(QStandardPaths::AppLocalDataLocation)).filePath("descriptions/" + fname)); 0686 0687 if (file.exists()) 0688 { 0689 if (file.open(QIODevice::ReadOnly)) 0690 { 0691 QTextStream in(&file); 0692 bool isDarkTheme = (Options::currentTheme() == "Night Vision"); 0693 QString color = (isDarkTheme) ? "red" : "white"; 0694 QString linkColor = (isDarkTheme) ? "red" : "yellow"; 0695 0696 QString line = "<HTML><HEAD><style type=text/css>body {color:" + color + 0697 ";} a {text-decoration: none;color:" + linkColor + ";}</style></HEAD><BODY><BR>" + 0698 in.readAll() + "</BODY></HTML>"; 0699 descTextObj->setProperty("text", line); 0700 file.close(); 0701 } 0702 } 0703 else 0704 { 0705 descTextObj->setProperty("text", soitem->getTypeName()); 0706 } 0707 } 0708 0709 void WIView::loadObjectInfoBox(SkyObjItem *soitem) 0710 { 0711 if (!soitem) 0712 return; 0713 QFile file; 0714 QString fname = "infoText-" + soitem->getName().toLower().remove(' ') + ".html"; 0715 //determine filename in local user KDE directory tree. 0716 file.setFileName(QDir(KSPaths::writableLocation(QStandardPaths::AppLocalDataLocation)).filePath("descriptions/" + fname)); 0717 0718 if (file.exists()) 0719 { 0720 if (file.open(QIODevice::ReadOnly)) 0721 { 0722 QTextStream in(&file); 0723 QString infoBoxHTML; 0724 while (!in.atEnd()) 0725 { 0726 infoBoxHTML = in.readAll(); 0727 QString wikiImageName = 0728 QUrl::fromLocalFile( 0729 KSPaths::locate(QStandardPaths::AppLocalDataLocation, 0730 "descriptions/wikiImage-" + soitem->getName().toLower().remove(' ') + ".png")) 0731 .url(); 0732 if (!wikiImageName.isEmpty()) 0733 { 0734 int captionEnd = infoBoxHTML.indexOf( 0735 "</caption>"); //Start looking for the image AFTER the caption. Planets have images in their caption. 0736 if (captionEnd == -1) 0737 captionEnd = 0; 0738 int leftImg = infoBoxHTML.indexOf("src=\"", captionEnd) + 5; 0739 int rightImg = infoBoxHTML.indexOf("\"", leftImg) - leftImg; 0740 QString imgURL = infoBoxHTML.mid(leftImg, rightImg); 0741 infoBoxHTML.replace(imgURL, wikiImageName); 0742 } 0743 bool isDarkTheme = (Options::currentTheme() == "Night Vision"); 0744 QString color = (isDarkTheme) ? "red" : "white"; 0745 QString linkColor = (isDarkTheme) ? "red" : "yellow"; 0746 if (isDarkTheme) 0747 infoBoxHTML.replace("color: white", "color: " + color); 0748 infoBoxHTML = "<HTML><HEAD><style type=text/css>body {color:" + color + 0749 ";} a {text-decoration: none;color:" + linkColor + ";}</style></HEAD><BODY>" + 0750 infoBoxHTML + "</BODY></HTML>"; 0751 0752 infoBoxText->setProperty("text", infoBoxHTML); 0753 } 0754 file.close(); 0755 } 0756 } 0757 } 0758 0759 void WIView::tryToUpdateWikipediaInfoInModel(bool onlyMissing) 0760 { 0761 SkyObjListModel *model = m_ModManager->returnModel(m_CurrentObjectListName); 0762 int objectNum = model->rowCount(); 0763 for (int i = 0; i < objectNum; i++) 0764 { 0765 SkyObjItem *soitem = model->getSkyObjItem(i); 0766 QFile file; 0767 QString fname = "infoText-" + soitem->getName().toLower().remove(' ') + ".html"; 0768 //determine filename in local user KDE directory tree. 0769 file.setFileName(QDir(KSPaths::writableLocation(QStandardPaths::AppLocalDataLocation)).filePath("descriptions/" + fname)); 0770 0771 if (file.exists() && onlyMissing) 0772 continue; 0773 0774 tryToUpdateWikipediaInfo(soitem, getWikipediaName(soitem)); 0775 } 0776 } 0777 0778 void WIView::tryToUpdateWikipediaInfo(SkyObjItem *soitem, QString name) 0779 { 0780 if (name.isEmpty() || !soitem) 0781 return; 0782 0783 QUrl url("https://en.wikipedia.org/w/index.php?action=render&title=" + name + "&redirects"); 0784 QNetworkReply *response = manager->get(QNetworkRequest(url)); 0785 0786 QTimer::singleShot(30000, response, [response] //Shut it down after 30 sec. 0787 { 0788 response->abort(); 0789 response->deleteLater(); 0790 qDebug() << Q_FUNC_INFO << "Wikipedia Download Timed out."; 0791 }); 0792 connect(response, &QNetworkReply::finished, this, [name, response, soitem, this] 0793 { 0794 response->deleteLater(); 0795 if (response->error() == QNetworkReply::ContentNotFoundError) 0796 { 0797 QString html = "<BR>Sorry, No Wikipedia article with this object name seems to exist. It is possible that " 0798 "one does exist but does not match the namimg scheme."; 0799 saveObjectInfoBoxText(soitem, "infoText", html); 0800 infoBoxText->setProperty("text", html); 0801 return; 0802 } 0803 if (response->error() != QNetworkReply::NoError) 0804 return; 0805 QString result = QString::fromUtf8(response->readAll()); 0806 int leftPos = result.indexOf("<table class=\"infobox"); 0807 int rightPos = result.indexOf("</table>", leftPos) - leftPos; 0808 0809 if (leftPos == -1) 0810 { 0811 //No InfoBox is Found 0812 if (soitem->getType() == SkyObjItem::Star && 0813 name != soitem->getName().replace(' ', '_')) //For stars, the regular name rather than gname 0814 { 0815 tryToUpdateWikipediaInfo(soitem, soitem->getName().replace(' ', '_')); 0816 return; 0817 } 0818 QString html = "<BR>Sorry, no Information Box in the object's Wikipedia article was found."; 0819 saveObjectInfoBoxText(soitem, "infoText", html); 0820 infoBoxText->setProperty("text", html); 0821 return; 0822 } 0823 0824 updateWikipediaDescription(soitem); 0825 0826 QString infoText = result.mid(leftPos, rightPos); 0827 0828 //This if statement should correct for a situation like for the planets where there is a single internal table inside the infoText Box. 0829 if (infoText.indexOf("<table", leftPos + 6) != -1) 0830 { 0831 rightPos = result.indexOf("</table>", leftPos + rightPos + 6) - leftPos; 0832 infoText = result.mid(leftPos, rightPos); 0833 } 0834 0835 //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. 0836 infoText.replace("background: #", "color:black;background: #") 0837 .replace("background-color: #", "color:black;background: #") 0838 .replace("background:#", "color:black;background:#") 0839 .replace("background-color:#", "color:black;background:#") 0840 .replace("background: pink", "color:black;background: pink"); 0841 infoText.replace("//", "http://"); //This is to fix links on wikipedia which are missing http from the url 0842 infoText.replace("https:http:", "https:") 0843 .replace("http:http:", "http:"); //Just in case it was done to an actual complete url 0844 0845 //This section is intended to remove links from the object name header at the top. The links break up the header. 0846 int thLeft = infoText.indexOf("<th "); 0847 if (thLeft != -1) 0848 { 0849 int thRight = infoText.indexOf("</th>", thLeft); 0850 int firstA = infoText.indexOf("<a ", thLeft); 0851 if (firstA != -1 && firstA < thRight) 0852 { 0853 int rightA = infoText.indexOf(">", firstA) - firstA + 1; 0854 infoText.remove(firstA, rightA); 0855 int endA = infoText.indexOf("</a>", firstA); 0856 infoText.remove(endA, 4); 0857 } 0858 } 0859 0860 int annotationLeft = infoText.indexOf("<annotation"); 0861 int annotationRight = infoText.indexOf("</annotation>", annotationLeft) + 13 - annotationLeft; 0862 infoText.remove(annotationLeft, 0863 annotationRight); //This removes the annotation that does not render correctly for some DSOs. 0864 0865 int mathLeft = infoText.indexOf("<img src=\"https://wikimedia.org/api/rest_v1/media/math"); 0866 int mathRight = infoText.indexOf(">", mathLeft) + 1 - mathLeft; 0867 infoText.remove(mathLeft, mathRight); //This removes an image that doesn't render properly for some DSOs. 0868 0869 infoText.replace("style=\"width:22em\"", "style=\"width:100%;background-color: black;color: white;\""); 0870 infoText = infoText + "<BR>(Source: <a href='" + "https://en.wikipedia.org/w/index.php?title=" + name + 0871 "&redirects" + "'>Wikipedia</a>)"; 0872 saveInfoURL(soitem, "https://en.wikipedia.org/w/index.php?title=" + name + "&redirects"); 0873 0874 int captionEnd = infoText.indexOf( 0875 "</caption>"); //Start looking for the image AFTER the caption. Planets have images in their caption. 0876 if (captionEnd == -1) 0877 captionEnd = 0; 0878 int leftImg = infoText.indexOf("src=\"", captionEnd) + 5; 0879 if (leftImg > captionEnd + 5) 0880 { 0881 int rightImg = infoText.indexOf("\"", leftImg) - leftImg; 0882 QString imgURL = infoText.mid(leftImg, rightImg); 0883 imgURL.replace( 0884 "http://upload.wikimedia.org", 0885 "https://upload.wikimedia.org"); //Although they will display, the images apparently don't download properly unless they are https. 0886 saveImageURL(soitem, imgURL); 0887 downloadWikipediaImage(soitem, imgURL); 0888 } 0889 0890 QString html = "<CENTER>" + infoText + "</table></CENTER>"; 0891 0892 saveObjectInfoBoxText(soitem, "infoText", html); 0893 bool isDarkTheme = (Options::currentTheme() == "Night Vision"); 0894 QString color = (isDarkTheme) ? "red" : "white"; 0895 QString linkColor = (isDarkTheme) ? "red" : "yellow"; 0896 if (isDarkTheme) 0897 html.replace("color: white", "color: " + color); 0898 html = "<HTML><HEAD><style type=text/css>body {color:" + color + 0899 ";} a {text-decoration: none;color:" + linkColor + ";}</style></HEAD><BODY>" + html + "</BODY></HTML>"; 0900 if (soitem == m_CurSoItem) 0901 infoBoxText->setProperty("text", html); 0902 }); 0903 } 0904 0905 void WIView::saveObjectInfoBoxText(SkyObjItem *soitem, QString type, QString text) 0906 { 0907 QFile file; 0908 QString fname = type + '-' + soitem->getName().toLower().remove(' ') + ".html"; 0909 0910 QDir filePath(KSPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + "/descriptions"); 0911 filePath.mkpath("."); 0912 0913 //determine filename in local user KDE directory tree. 0914 file.setFileName(filePath.filePath(fname)); 0915 0916 if (file.open(QIODevice::WriteOnly) == false) 0917 { 0918 qDebug() << Q_FUNC_INFO << "Image text cannot be saved for later. file save error"; 0919 return; 0920 } 0921 else 0922 { 0923 QTextStream stream(&file); 0924 stream << text; 0925 file.close(); 0926 } 0927 } 0928 0929 void WIView::saveImageURL(SkyObjItem *soitem, QString imageURL) 0930 { 0931 QFile file; 0932 //determine filename in local user KDE directory tree. 0933 file.setFileName(QDir(KSPaths::writableLocation(QStandardPaths::AppLocalDataLocation)).filePath("image_url.dat")); 0934 QString entry = soitem->getName() + ':' + "Show Wikipedia Image" + ':' + imageURL; 0935 0936 if (file.open(QIODevice::ReadOnly)) 0937 { 0938 QTextStream in(&file); 0939 QString line; 0940 while (!in.atEnd()) 0941 { 0942 line = in.readLine(); 0943 if (line == entry) 0944 { 0945 file.close(); 0946 return; 0947 } 0948 } 0949 file.close(); 0950 } 0951 0952 if (!file.open(QIODevice::ReadWrite | QIODevice::Append)) 0953 { 0954 qDebug() << Q_FUNC_INFO << "Image URL cannot be saved for later. image_url.dat error"; 0955 return; 0956 } 0957 else 0958 { 0959 QTextStream stream(&file); 0960 stream << entry << '\n'; 0961 file.close(); 0962 } 0963 } 0964 0965 void WIView::saveInfoURL(SkyObjItem *soitem, QString infoURL) 0966 { 0967 QFile file; 0968 //determine filename in local user KDE directory tree. 0969 file.setFileName(QDir(KSPaths::writableLocation(QStandardPaths::AppLocalDataLocation)).filePath("info_url.dat")); 0970 QString entry = soitem->getName() + ':' + "Wikipedia Page" + ':' + infoURL; 0971 0972 if (file.open(QIODevice::ReadOnly)) 0973 { 0974 QTextStream in(&file); 0975 QString line; 0976 while (!in.atEnd()) 0977 { 0978 line = in.readLine(); 0979 if (line == entry) 0980 { 0981 file.close(); 0982 return; 0983 } 0984 } 0985 file.close(); 0986 } 0987 0988 if (!file.open(QIODevice::ReadWrite | QIODevice::Append)) 0989 { 0990 qDebug() << Q_FUNC_INFO << "Info URL cannot be saved for later. info_url.dat error"; 0991 return; 0992 } 0993 else 0994 { 0995 QTextStream stream(&file); 0996 stream << entry << '\n'; 0997 file.close(); 0998 } 0999 } 1000 1001 void 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 1010 QNetworkReply *response = manager->get(QNetworkRequest(QUrl(imageURL))); 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 }); 1017 connect(response, &QNetworkReply::finished, this, [fileN, response, this] 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 }