wiview.cpp 35.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/***************************************************************************
                          wiview.cpp  -  K Desktop Planetarium
                             -------------------
    begin                : 2012/26/05
    copyright            : (C) 2012 by Samikshan Bairagya
    email                : samikshan@gmail.com
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/
17 18 19 20
#include <QtQuick/QQuickView>
#include <QtQuick/QQuickItem>
#include <QStandardPaths>
#include <QGraphicsObject>
21
#include "wiview.h"
22
#include "skymap.h"
23
#include "dialogs/detaildialog.h"
24
#include <klocalizedcontext.h>
25
#include "kspaths.h"
26
#include "starobject.h"
27 28 29 30
#include "skymap.h"
#include "Options.h"
#include "wiequipsettings.h"
#include "skymapcomposite.h"
31
#include <QtConcurrent>
32

33

34 35 36 37 38 39 40
#ifdef HAVE_INDI
#include <basedevice.h>
#include "indi/indilistener.h"
#include "indi/indistd.h"
#include "indi/driverinfo.h"
#endif

41

42
WIView::WIView(QWidget * parent) : QWidget(parent), m_CurrentObjectListName(-1)
43
{
44

45 46 47 48 49
    //These settings are like this just to get it started.
    int bortle = Options::bortleClass();
    int aperture = 100;
    ObsConditions::Equipment equip = ObsConditions::Telescope;
    ObsConditions::TelescopeType telType = ObsConditions::Reflector;
50

51
    m_Obs = new ObsConditions(bortle, aperture, equip, telType);
52

53
    m_ModManager = new ModelManager(m_Obs);
54

55
    m_BaseView = new QQuickView();
56

57
    ///To use i18n() instead of qsTr() in qml/wiview.qml for translation
58
    //KDeclarative kd;
59
    // kd.setDeclarativeEngine(m_BaseView->engine());
60 61
    //kd.initialize();
    //kd.setupBindings();
62

63
    m_Ctxt = m_BaseView->rootContext();
64

65 66
    m_Ctxt->setContextProperty("soListModel", m_ModManager->getTempModel()); // This is to avoid an error saying it doesn't exist.

67 68 69
    ///Use instead of KDeclarative
    m_Ctxt->setContextObject(new KLocalizedContext(m_BaseView));

Jasem Mutlaq's avatar
Jasem Mutlaq committed
70 71
    QString WI_Location;
    #if defined(Q_OS_OSX)
72 73 74
        WI_Location = QCoreApplication::applicationDirPath()+"/../Resources/data/tools/whatsinteresting/qml/wiview.qml";
        if(!QFileInfo(WI_Location).exists())
            WI_Location = KSPaths::locate(QStandardPaths::AppDataLocation, "tools/whatsinteresting/qml/wiview.qml");
Jasem Mutlaq's avatar
Jasem Mutlaq committed
75 76
    #elif defined(Q_OS_WIN)
           WI_Location = KSPaths::locate(QStandardPaths::GenericDataLocation, "tools/whatsinteresting/qml/wiview.qml");
77 78 79 80 81
    #else
        WI_Location = KSPaths::locate(QStandardPaths::AppDataLocation, "tools/whatsinteresting/qml/wiview.qml");
    #endif

    m_BaseView->setSource(QUrl::fromLocalFile(WI_Location));
82

83
    m_BaseObj = m_BaseView->rootObject();
84

85 86
    m_ProgressBar = m_BaseObj->findChild<QQuickItem *>("progressBar");

87 88
    m_loadingMessage = m_BaseObj->findChild<QQuickItem *>("loadingMessage");

89
    m_CategoryTitle = m_BaseObj->findChild<QQuickItem *>(QString("categoryTitle"));
90

91
    m_ViewsRowObj = m_BaseObj->findChild<QQuickItem *>(QString("viewsRowObj"));
92 93
    connect(m_ViewsRowObj, SIGNAL(categorySelected(QString)), this, SLOT(onCategorySelected(QString)));
    connect(m_ViewsRowObj, SIGNAL(inspectSkyObject(QString)), this, SLOT(inspectSkyObject(QString)));
94

95
    m_SoListObj = m_BaseObj->findChild<QQuickItem *>("soListObj");
96
    connect(m_SoListObj, SIGNAL(soListItemClicked(int)), this, SLOT(onSoListItemClicked(int)));
97

98
    m_DetailsViewObj = m_BaseObj->findChild<QQuickItem *>("detailsViewObj");
99

100 101 102
    descTextObj = m_DetailsViewObj->findChild<QObject *>("descTextObj");
    infoBoxText = m_DetailsViewObj->findChild<QObject *>("infoBoxText");

103 104 105
    m_skyObjView = m_BaseObj->findChild<QQuickItem *>("skyObjView");
    m_ContainerObj = m_BaseObj->findChild<QQuickItem *>("containerObj");

106
    m_NextObj = m_BaseObj->findChild<QQuickItem *>("nextObj");
107
    connect(m_NextObj, SIGNAL(nextObjClicked()), this, SLOT(onNextObjClicked()));
108
    m_PrevObj = m_BaseObj->findChild<QQuickItem *>("prevObj");
109
    connect(m_PrevObj, SIGNAL(prevObjClicked()), this, SLOT(onPrevObjClicked()));
110

111 112
    m_CenterButtonObj = m_BaseObj->findChild<QQuickItem *>("centerButtonObj");
    connect(m_CenterButtonObj, SIGNAL(centerButtonClicked()), this, SLOT(onCenterButtonClicked()));
113

114 115 116
    autoCenterCheckbox = m_DetailsViewObj->findChild<QObject *>("autoCenterCheckbox");
    autoTrackCheckbox = m_DetailsViewObj->findChild<QObject *>("autoTrackCheckbox");

117 118 119
    m_SlewTelescopeButtonObj = m_BaseObj->findChild<QQuickItem *>("slewTelescopeButtonObj");
    connect(m_SlewTelescopeButtonObj, SIGNAL(slewTelescopeButtonClicked()), this, SLOT(onSlewTelescopeButtonClicked()));

120
    m_DetailsButtonObj = m_BaseObj->findChild<QQuickItem *>("detailsButtonObj");
121 122
    connect(m_DetailsButtonObj, SIGNAL(detailsButtonClicked()), this, SLOT(onDetailsButtonClicked()));

123
    QObject * settingsIconObj = m_BaseObj->findChild<QQuickItem *>("settingsIconObj");
124 125
    connect(settingsIconObj, SIGNAL(settingsIconClicked()), this, SLOT(onSettingsIconClicked()));

126 127 128
    inspectIconObj = m_BaseObj->findChild<QQuickItem *>("inspectIconObj");
    connect(inspectIconObj, SIGNAL(inspectIconClicked(bool)), this, SLOT(onInspectIconClicked(bool)));

129 130 131 132 133 134
    visibleIconObj = m_BaseObj->findChild<QQuickItem *>("visibleIconObj");
    connect(visibleIconObj, SIGNAL(visibleIconClicked(bool)), this, SLOT(onVisibleIconClicked(bool)));

    favoriteIconObj = m_BaseObj->findChild<QQuickItem *>("favoriteIconObj");
    connect(favoriteIconObj, SIGNAL(favoriteIconClicked(bool)), this, SLOT(onFavoriteIconClicked(bool)));

135
    QObject * reloadIconObj = m_BaseObj->findChild<QQuickItem *>("reloadIconObj");
136 137
    connect(reloadIconObj, SIGNAL(reloadIconClicked()), this, SLOT(onReloadIconClicked()));

138
    QObject * downloadIconObj = m_BaseObj->findChild<QQuickItem *>("downloadIconObj");
139
    connect(downloadIconObj, SIGNAL(downloadIconClicked()), this, SLOT(onUpdateIconClicked()));
140

141
    m_BaseView->setResizeMode(QQuickView::SizeRootObjectToView);
142
    m_BaseView->show();
143

144
    connect(KStars::Instance()->map(),SIGNAL(objectClicked(SkyObject*)),this, SLOT(inspectSkyObjectOnClick(SkyObject*)));
145 146

    manager = new QNetworkAccessManager();
147 148 149

    setProgressBarVisible(true);
    connect(m_ModManager, SIGNAL(loadProgressUpdated(double)),this,SLOT(updateProgress(double)));
150
    connect(m_ModManager, SIGNAL(modelUpdated()),this,SLOT(refreshListView()));
151 152
    m_ViewsRowObj->setProperty("enabled",false);

153 154
    inspectOnClick = false;

155 156
    nightVision = m_BaseObj->findChild<QObject *>("nightVision");
    if(Options::darkAppColors())
157
        nightVision->setProperty("state","active");
158

159 160
}

Samikshan Bairagya's avatar
Samikshan Bairagya committed
161 162
WIView::~WIView()
{
163
    delete m_ModManager;
164
    delete m_CurSoItem;
165
    delete manager;
Samikshan Bairagya's avatar
Samikshan Bairagya committed
166
}
167

168 169 170 171 172 173 174 175 176
void WIView::setNightVisionOn(bool on){
    if(on)
        nightVision->setProperty("state","active");
    else
        nightVision->setProperty("state","");
    if(m_CurSoItem)
        loadDetailsView(m_CurSoItem, m_CurIndex);
}

177 178 179 180 181 182
void WIView::setProgressBarVisible(bool visible){
    m_ProgressBar->setProperty("visible", visible);
}

void WIView::updateProgress(double value){
    m_ProgressBar->setProperty("value", value);
183

184 185 186
    if(value==1){
        setProgressBarVisible(false);
        m_ViewsRowObj->setProperty("enabled",true);
187 188 189 190
        m_loadingMessage->setProperty("state", "");
    } else{
        setProgressBarVisible(true);
        m_loadingMessage->setProperty("state", "loading");
191 192 193
    }
}

194 195
void WIView::updateObservingConditions(){
    int bortle = Options::bortleClass();
196

197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217
    /**
    NOTE This part of the code dealing with equipment type is presently not required
    as WI does not differentiate between Telescope and Binoculars. It only needs the
     aperture of the equipment whichever available. However this is kept as a part of
    the code as support to be utilised in the future.
    **/
   ObsConditions::Equipment equip = ObsConditions::None;

   if (Options::telescopeCheck() && Options::binocularsCheck())
       equip = ObsConditions::Both;
   else if (Options::telescopeCheck())
       equip = ObsConditions::Telescope;
   else if (Options::binocularsCheck())
       equip = ObsConditions::Binoculars;

   ObsConditions::TelescopeType telType;

    if(KStars::Instance()->getWIEquipSettings())
        telType = (equip == ObsConditions::Telescope) ? KStars::Instance()->getWIEquipSettings()->getTelType() : ObsConditions::Invalid;
    else
        telType = ObsConditions::Invalid;
Rafal Kulaga's avatar
Rafal Kulaga committed
218

219
   int aperture=100;
220

221 222 223
   //This doesn't work correctly, FIXME!!
   // if(KStars::Instance()->getWIEquipSettings())
   //    aperture = KStars::Instance()->getWIEquipSettings()->getAperture();
224

225

226 227 228 229 230 231 232 233 234
   if (!m_Obs)
       m_Obs = new ObsConditions(bortle, aperture, equip, telType);
   else
       m_Obs->setObsConditions(bortle, aperture, equip, telType);
}

void WIView::onCategorySelected(QString model)
{
    m_CurrentObjectListName = model;
235 236
    m_Ctxt->setContextProperty("soListModel", m_ModManager->returnModel(m_CurrentObjectListName));
    m_CurIndex = -2;
237 238 239 240
    if(!m_ModManager->showOnlyVisibleObjects())
        visibleIconObj->setProperty("state","unchecked");
    if(!m_ModManager->showOnlyFavoriteObjects())
        favoriteIconObj->setProperty("state","unchecked");
241 242 243 244 245 246 247 248 249 250 251 252 253
    if(model=="ngc"&&(!m_ModManager->isNGCLoaded())){
        QtConcurrent::run(m_ModManager,&ModelManager::loadNGCCatalog);
        return;
    }
    if(model=="ic"&&(!m_ModManager->isICLoaded())){
        QtConcurrent::run(m_ModManager,&ModelManager::loadICCatalog);
        return;
    }
    if(model=="sharpless"&&(!m_ModManager->isSharplessLoaded())){
        QtConcurrent::run(m_ModManager,&ModelManager::loadSharplessCatalog);
        return;
    }
    updateModel(m_Obs);
254 255 256 257 258 259 260
}

void WIView::onSoListItemClicked(int index)
{
    SkyObjItem * soitem = m_ModManager->returnModel(m_CurrentObjectListName)->getSkyObjItem(index);
    if(soitem)
        loadDetailsView(soitem, index);
261 262
}

263
void WIView::onNextObjClicked()
264
{
265 266 267 268 269
    if( m_CurrentObjectListName!=""){
        int modelSize = m_ModManager->returnModel(m_CurrentObjectListName)->rowCount();
        SkyObjItem * nextItem = m_ModManager->returnModel(m_CurrentObjectListName)->getSkyObjItem((m_CurIndex+1)%modelSize);
        loadDetailsView(nextItem, (m_CurIndex+1)%modelSize);
    }
Samikshan Bairagya's avatar
Samikshan Bairagya committed
270
}
271 272 273

void WIView::onPrevObjClicked()
{
274 275 276 277 278
    if( m_CurrentObjectListName!=""){
        int modelSize = m_ModManager->returnModel(m_CurrentObjectListName)->rowCount();
        SkyObjItem * prevItem = m_ModManager->returnModel(m_CurrentObjectListName)->getSkyObjItem((m_CurIndex-1+modelSize)%modelSize);
        loadDetailsView(prevItem, (m_CurIndex-1+modelSize)%modelSize);
    }
279
}
280

281
void WIView::onCenterButtonClicked()
282
{
283
    ///Center map on selected sky-object
284 285
    SkyObject * so = m_CurSoItem->getSkyObject();
    KStars * kstars = KStars::Instance();
286
    if (so)
287
    {
288 289 290
        kstars->map()->setFocusPoint(so);
        kstars->map()->setFocusObject(so);
        kstars->map()->setDestination(*kstars->map()->focusPoint());
291
        Options::setIsTracking(autoTrackCheckbox->property("checked")==true);
292 293
    }
}
294

295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332
void WIView::onSlewTelescopeButtonClicked()
{

    if(KMessageBox::Continue==KMessageBox::warningContinueCancel(NULL, "Are you sure you want your telescope to slew to this object?",
            i18n("Continue Slew"),  KStandardGuiItem::cont(), KStandardGuiItem::cancel(), "continue_wi_slew_warning"))
    {

#ifdef HAVE_INDI

    if (INDIListener::Instance()->size() == 0)
    {
        KMessageBox::sorry(0, i18n("KStars did not find any active telescopes."));
        return;
    }

    foreach(ISD::GDInterface * gd, INDIListener::Instance()->getDevices())
    {
        INDI::BaseDevice * bd = gd->getBaseDevice();

        if (gd->getType() != KSTARS_TELESCOPE)
            continue;

        if (bd == NULL)
            continue;

        if (bd->isConnected() == false)
        {
            KMessageBox::error(0, i18n("Telescope %1 is offline. Please connect and retry again.", gd->getDeviceName()));
            return;
        }


        ISD::GDSetCommand SlewCMD(INDI_SWITCH, "ON_COORD_SET", "TRACK", ISS_ON, this);

        gd->setProperty(&SlewCMD);
        gd->runCommand(INDI_SEND_COORDS, m_CurSoItem->getSkyObject());

        ///Slew map to selected sky-object
333
        onCenterButtonClicked();
334 335 336 337 338 339 340 341 342 343 344 345 346

        return;

    }

    KMessageBox::sorry(0, i18n("KStars did not find any active telescopes."));

#endif
    }


}

347 348 349
void WIView::onDetailsButtonClicked()
{
    ///Code taken from WUTDialog::slotDetails()
350 351
    KStars * kstars = KStars::Instance();
    SkyObject * so = m_CurSoItem->getSkyObject();
352 353 354 355 356
    if(so){
        DetailDialog * detail = new DetailDialog(so, kstars->data()->lt(), kstars->data()->geo(), kstars);
        detail->exec();
        delete detail;
    }
357
}
358 359 360

void WIView::onSettingsIconClicked()
{
361
    KStars * kstars = KStars::Instance();
362
    kstars->showWISettingsUI();
363
}
364

365 366
void WIView::onReloadIconClicked()
{
367 368 369

    if(m_CurrentObjectListName!=""){
        updateModel(m_Obs);
370
        m_CurIndex=m_ModManager->returnModel(m_CurrentObjectListName)->getSkyObjIndex(m_CurSoItem);
371
    }
372 373 374 375 376 377 378 379 380 381 382 383 384 385 386
    loadDetailsView(m_CurSoItem, m_CurIndex);
}

void WIView::onVisibleIconClicked(bool visible)
{
    m_ModManager->setShowOnlyVisibleObjects(visible);
    onReloadIconClicked();
}

void WIView::onFavoriteIconClicked(bool favorites)
{
    m_ModManager->setShowOnlyFavoriteObjects(favorites);
    onReloadIconClicked();
}

387 388 389 390
void WIView::onUpdateIconClicked(){
    QMessageBox mbox;
    mbox.setText( "Please choose which object(s) to try to update with Wikipedia data." );
    QPushButton * currentObject = mbox.addButton("Current Object" , QMessageBox::AcceptRole);
391 392 393 394 395 396
    QPushButton * missingObjects;
    QPushButton * allObjects;
    if(m_CurrentObjectListName!=""){
        missingObjects = mbox.addButton("Objects with no data" , QMessageBox::AcceptRole);
        allObjects = mbox.addButton("Entire List" , QMessageBox::AcceptRole);
    }
397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418
    QPushButton * cancel = mbox.addButton( "Cancel", QMessageBox::AcceptRole );
    mbox.setDefaultButton(cancel);

    mbox.exec();
    if(mbox.clickedButton()==currentObject)
    {
        if(m_CurSoItem)
        {
            tryToUpdateWikipediaInfo(m_CurSoItem, getWikipediaName(m_CurSoItem));
        }
    }
    else if(mbox.clickedButton()==allObjects||mbox.clickedButton()==missingObjects)
    {
        SkyObjListModel *model = m_ModManager->returnModel(m_CurrentObjectListName);
        if(model->rowCount() > 0)
        {
            tryToUpdateWikipediaInfoInModel(mbox.clickedButton()==missingObjects);
        }
        else
        {
            qDebug()<<"No Objects in List!";
        }
419
    }
420 421
}

422 423 424 425
void WIView::refreshListView(){
    m_Ctxt->setContextProperty("soListModel",0);
    if (m_CurrentObjectListName !="")
        m_Ctxt->setContextProperty("soListModel", m_ModManager->returnModel(m_CurrentObjectListName));
426 427
    if(m_CurIndex==-2)
        onSoListItemClicked(0);
428 429 430 431
    if(m_CurIndex!=-1)
        m_SoListObj->setProperty("currentIndex", m_CurIndex);
}

432
void WIView::updateModel(ObsConditions * obs)
433
{
434 435 436 437
    if(m_CurrentObjectListName!=""){
        m_Obs = obs;
        m_ModManager->updateModel(m_Obs,m_CurrentObjectListName);
    }
438 439 440
}

void WIView::inspectSkyObject(QString name){
441
    if(name != "" && name != "star"){
442 443 444
        SkyObject *obj=KStarsData::Instance()->skyComposite()->findByName(name);
        if(obj)
            inspectSkyObject(obj);
445
    }
446 447
}

448 449 450 451 452 453
void WIView::inspectSkyObjectOnClick(SkyObject *obj){
    if(inspectOnClick&&KStars::Instance()->isWIVisible()){
        inspectSkyObject(obj);
    }
}

454 455 456
void WIView::inspectSkyObject(SkyObject *obj){
    if(obj){
        if(obj->name()!="star"){
457
            m_CurrentObjectListName="";
458 459 460 461 462
            loadDetailsView(new SkyObjItem(obj),-1);
            m_BaseObj->setProperty("state", "singleItemSelected");
            m_CategoryTitle->setProperty("text", "Selected Object");
        }
    }
463 464
}

465
void WIView::loadDetailsView(SkyObjItem * soitem, int index)
466
{
467 468
    if(!soitem)
        return;
469 470
    m_CurSoItem = soitem;
    m_CurIndex = index;
471 472 473 474 475
    int modelSize;
    if(index==-1)
        modelSize=-1;
    else
        modelSize = m_ModManager->returnModel(m_CurrentObjectListName)->rowCount();
476

Vijay Dhameliya's avatar
Vijay Dhameliya committed
477 478 479 480 481 482 483
    if(modelSize <= 1)
    {
        m_NextObj->setProperty("visible", "false");
        m_PrevObj->setProperty("visible", "false");
    }
    else
    {
484 485
        SkyObjItem * nextItem = m_ModManager->returnModel(m_CurrentObjectListName)->getSkyObjItem((m_CurIndex+1)%modelSize);
        SkyObjItem * prevItem = m_ModManager->returnModel(m_CurrentObjectListName)->getSkyObjItem((m_CurIndex-1+modelSize)%modelSize);
Vijay Dhameliya's avatar
Vijay Dhameliya committed
486 487
        m_NextObj->setProperty("visible", "true");
        m_PrevObj->setProperty("visible", "true");
488
        QObject * nextTextObj = m_NextObj->findChild<QObject *>("nextTextObj");
Vijay Dhameliya's avatar
Vijay Dhameliya committed
489
        nextTextObj->setProperty("text", nextItem->getName());
490
        QObject * prevTextObj = m_PrevObj->findChild<QObject *>("prevTextObj");
Vijay Dhameliya's avatar
Vijay Dhameliya committed
491 492
        prevTextObj->setProperty("text", prevItem->getName());
    }
493

494 495
    QObject * sonameObj = m_DetailsViewObj->findChild<QObject *>("sonameObj");
    QObject * posTextObj = m_DetailsViewObj->findChild<QObject *>("posTextObj");
496
    QObject * detailImage = m_DetailsViewObj->findChild<QObject *>("detailImage");
497
    QObject * detailsTextObj = m_DetailsViewObj->findChild<QObject *>("detailsTextObj");
498

499

500
    sonameObj->setProperty("text", soitem->getDescName());
501
    posTextObj->setProperty("text", soitem->getPosition());
502
    detailImage->setProperty("refreshableSource", soitem->getImageURL(false));
503

504 505
    loadObjectDescription(soitem);

506
    infoBoxText->setProperty("text", "<BR><BR>No Wikipedia information. <BR>  Please try to download it using the orange download button below.");
507
    loadObjectInfoBox(soitem);
508

509
    QString summary=soitem->getSummary(false);
510

511 512
    QString magText;
    if (soitem->getType() == SkyObjItem::Constellation)
Jasem Mutlaq's avatar
Jasem Mutlaq committed
513
        magText = xi18n("Magnitude:  --");
514
    else
515
        magText = xi18n("Magnitude: %1", QLocale().toString(soitem->getMagnitude(),'f', 2));
516

Jasem Mutlaq's avatar
Jasem Mutlaq committed
517
    QString sbText = xi18n("Surface Brightness: %1", soitem->getSurfaceBrightness());
518

Jasem Mutlaq's avatar
Jasem Mutlaq committed
519
    QString sizeText = xi18n("Size: %1", soitem->getSize());
520

521
    QString details = summary + "<BR>" + sbText + "<BR>" + magText + "<BR>" + sizeText;
522 523
    detailsTextObj->setProperty("text", details);

524
    if(autoCenterCheckbox->property("checked")==true){
525
        QTimer::singleShot(500, this, SLOT(onCenterButtonClicked()));
526 527
    }

528 529 530
    if(m_CurIndex!=-1)
        m_SoListObj->setProperty("currentIndex", m_CurIndex);

531 532
}

533
QString WIView::getWikipediaName(SkyObjItem *soitem){
534 535
    if(!soitem)
        return "";
536 537 538 539 540 541 542 543 544 545 546 547 548 549 550
    QString name;
    if(soitem->getName().toLower().startsWith("m "))
        name = soitem->getName().replace("M ","Messier_").remove( ' ' );
    else if(soitem->getName().toLower().startsWith("ngc"))
        name = soitem->getName().toLower().replace("ngc","NGC_").remove( ' ' );
    else if(soitem->getName().toLower().startsWith("ic"))
        name = soitem->getName().toLower().replace("ic","IC_").remove( ' ' );
    else if(soitem->getType() == SkyObjItem::Constellation){
        QStringList words = soitem->getName().split(" ");
        for(int i=0;i<words.size();i++){
            QString temp=words.at(i).toLower();
            temp[0]=temp[0].toUpper();
            words.replace(i,temp);
        }
        name = words.join("_")+ "_(constellation)";
551 552
        if(name.contains("Serpens"))
            name = "Serpens_(constellation)";
553 554 555 556 557 558 559
    }
    else if(soitem->getTypeName() == "Asteroid")
        name = soitem->getName().remove( ' ' ) + "_(asteroid)";
    else if(soitem->getTypeName() == "Comet")
        name = soitem->getLongName();
    else if(soitem->getType() == SkyObjItem::Planet&&soitem->getName()!="Sun"&&soitem->getName()!="Moon")
        name = soitem->getName().remove( ' ' ) + "_(planet)";
560 561 562 563 564 565 566 567 568
    else if(soitem->getType() == SkyObjItem::Star)
    {
        StarObject *star=dynamic_cast<StarObject*>(soitem->getSkyObject());
        name = star->gname(false).replace( " ", "_" ); //the greek name seems to give the most consistent search results for opensearch.
        if(name=="")
            name = soitem->getName().replace( " ", "_" ) + "_(star)";
        name.remove("[").remove("]");
    }
    else
569 570 571
        name = soitem->getName().remove( ' ' );
    return name;
}
572

573
void WIView::updateWikipediaDescription(SkyObjItem * soitem)
574
{
575 576
    if(!soitem)
        return;
577
    QString name=getWikipediaName(soitem);
578 579 580 581

    QUrl url("https://en.wikipedia.org/w/api.php?action=opensearch&search=" + name + "&format=xml");

    QNetworkReply * response = manager->get(QNetworkRequest(url));
582 583 584 585 586 587
    QTimer::singleShot(30000, response, [response]
    { //Shut it down after 30 sec.
        response->abort();
        response->deleteLater();
        qDebug()<<"Wikipedia Download Timed out.";
    });
588
    connect(response, &QNetworkReply::finished, this, [soitem, this, response]{
589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605
      response->deleteLater();
      if (response->error() != QNetworkReply::NoError) return;
      QString contentType =
        response->header(QNetworkRequest::ContentTypeHeader).toString();
      if (!contentType.contains("charset=utf-8")) {
        qWarning() << "Content charsets other than utf-8 are not implemented yet.";
        return;
      }
    QString result = QString::fromUtf8(response->readAll());
    int leftPos=result.indexOf("<Description")+34;
    if(leftPos<34)
        return;
    int rightPos=result.indexOf("</Description>")-leftPos;

    int leftURL=result.indexOf("<Url xml:space=\"preserve\">")+26;
    int rightURL=result.indexOf("</Url>")-leftURL;

606 607
    QString srchtml="\n<p style=text-align:right>Source: (<a href='" + result.mid(leftURL,rightURL) + "'>" + "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.
    QString html="<HTML>" + result.mid(leftPos,rightPos) + srchtml + "</HTML>";
608

609
      saveObjectInfoBoxText( soitem, "description", html);
610 611 612 613 614

      QString color = (Options::darkAppColors()) ? "red" : "white";
      QString linkColor = (Options::darkAppColors()) ? "red" : "yellow";
      html = "<HTML><HEAD><style type=text/css>body {color:" + color + ";} a {text-decoration: none;color:" + linkColor + ";}</style></HEAD><BODY>" + html + "</BODY></HTML>";

615 616 617
      if(soitem==m_CurSoItem)
        descTextObj->setProperty("text", html);
      refreshListView();
618
    });
619 620
}

621
void WIView::loadObjectDescription(SkyObjItem * soitem){
622 623 624 625 626 627
    QFile file;
    QString fname = "description-" + soitem->getName().toLower().remove( ' ' ) + ".html";
    file.setFileName( KSPaths::writableLocation(QStandardPaths::GenericDataLocation) + "descriptions/" + fname ) ; //determine filename in local user KDE directory tree.

    if(file.exists()){

628
        if(file.open(QIODevice::ReadOnly))
629
        {
630
            QTextStream in(&file);
631 632
            QString color = (Options::darkAppColors()) ? "red" : "white";
            QString linkColor = (Options::darkAppColors()) ? "red" : "yellow";
633
            QString line = "<HTML><HEAD><style type=text/css>body {color:" + color + ";} a {text-decoration: none;color:" + linkColor + ";}</style></HEAD><BODY><BR>" + in.readAll() + "</BODY></HTML>";
634
            descTextObj->setProperty("text", line);
635
            file.close();
636
        }
637 638
    } else {
        descTextObj->setProperty("text", soitem->getTypeName());
639 640 641
    }
}

642
void WIView::loadObjectInfoBox(SkyObjItem * soitem)
643
{
644 645
    if(!soitem)
        return;
646 647 648 649 650 651 652 653 654
    QFile file;
    QString fname = "infoText-" + soitem->getName().toLower().remove( ' ' ) + ".html";
    file.setFileName( KSPaths::writableLocation(QStandardPaths::GenericDataLocation) + "descriptions/" + fname ) ; //determine filename in local user KDE directory tree.

    if(file.exists())
    {
        if(file.open(QIODevice::ReadOnly))
        {
            QTextStream in(&file);
655
            QString infoBoxHTML;
656 657
            while ( !in.atEnd() )
            {
658
                infoBoxHTML = in.readAll();
659
                QString wikiImageName=QUrl::fromLocalFile(KSPaths::locate(QStandardPaths::GenericDataLocation, "descriptions/wikiImage-" + soitem->getName().toLower().remove( ' ' ) + ".png" )).url();
660 661 662 663 664 665 666 667 668
                if(wikiImageName!=""){
                    int captionEnd = infoBoxHTML.indexOf("</caption>"); //Start looking for the image AFTER the caption.  Planets have images in their caption.
                    if(captionEnd == -1)
                        captionEnd = 0;
                    int leftImg=infoBoxHTML.indexOf("src=\"", captionEnd)+5;
                    int rightImg=infoBoxHTML.indexOf("\"",leftImg)-leftImg;
                    QString imgURL=infoBoxHTML.mid(leftImg,rightImg);
                    infoBoxHTML.replace(imgURL,wikiImageName);
                }
669 670 671 672 673 674
                QString color = (Options::darkAppColors()) ? "red" : "white";
                QString linkColor = (Options::darkAppColors()) ? "red" : "yellow";
                if(Options::darkAppColors())
                    infoBoxHTML.replace("color: white","color: " + color);
                infoBoxHTML = "<HTML><HEAD><style type=text/css>body {color:" + color + ";} a {text-decoration: none;color:" + linkColor + ";}</style></HEAD><BODY>" + infoBoxHTML + "</BODY></HTML>";

675
                infoBoxText->setProperty("text", infoBoxHTML);
676 677 678 679
            }
            file.close();
        }
    }
680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695
}

void WIView::tryToUpdateWikipediaInfoInModel(bool onlyMissing){
    SkyObjListModel *model = m_ModManager->returnModel(m_CurrentObjectListName);
    int objectNum=model->rowCount();
    for(int i = 0; i < objectNum; i++)
    {
        SkyObjItem * soitem = model->getSkyObjItem(i);
        QFile file;
        QString fname = "infoText-" + soitem->getName().toLower().remove( ' ' ) + ".html";
        file.setFileName( KSPaths::writableLocation(QStandardPaths::GenericDataLocation) + "descriptions/" + fname ) ; //determine filename in local user KDE directory tree.

        if(file.exists()&&onlyMissing)
            continue;

        tryToUpdateWikipediaInfo(soitem, getWikipediaName(soitem));
696 697 698
    }
}

699
void WIView::tryToUpdateWikipediaInfo(SkyObjItem * soitem, QString name)
700
{
701
    if(name==""||!soitem)
702
        return;
703 704 705
    QUrl url("https://en.wikipedia.org/w/index.php?action=render&title="+ name + "&redirects");

    QNetworkReply * response = manager->get(QNetworkRequest(url));
706 707 708 709 710 711
    QTimer::singleShot(30000, response, [response]
    { //Shut it down after 30 sec.
        response->abort();
        response->deleteLater();
        qDebug()<<"Wikipedia Download Timed out.";
    });
712
    connect(response, &QNetworkReply::finished, this, [name, response, soitem, this]{
713
        response->deleteLater();
714 715 716 717 718 719
        if (response->error()==QNetworkReply::ContentNotFoundError){
            QString html="<BR>Sorry, No Wikipedia article with this object name seems to exist.  It is possible that one does exist but does not match the namimg scheme.";
            saveObjectInfoBoxText( soitem, "infoText" , html);
            infoBoxText->setProperty("text", html);
            return;
        }
720 721 722 723
        if (response->error() != QNetworkReply::NoError) return;
        QString result = QString::fromUtf8(response->readAll());
        int leftPos=result.indexOf("<table class=\"infobox");
        int rightPos=result.indexOf("</table>", leftPos)-leftPos;
724

725 726 727 728 729 730
        if(leftPos==-1){ //No InfoBox is Found
            if(soitem->getType()==SkyObjItem::Star&&name!=soitem->getName().replace(" ", "_"))  //For stars, the regular name rather than gname
            {
                tryToUpdateWikipediaInfo(soitem, soitem->getName().replace(" ", "_"));
                return;
            }
731 732 733
            QString html="<BR>Sorry, no Information Box in the object's Wikipedia article was found.";
            saveObjectInfoBoxText( soitem, "infoText" , html);
            infoBoxText->setProperty("text", html);
734 735
            return;
        }
736

737 738
        updateWikipediaDescription(soitem);

739
        QString infoText=result.mid(leftPos,rightPos);
740

741 742 743 744 745
        //This if statement should correct for a situation like for the planets where there is a single internal table inside the infoText Box.
        if(infoText.indexOf("<table",leftPos+6)!=-1){
            rightPos=result.indexOf("</table>", leftPos+rightPos+6)-leftPos;
            infoText=result.mid(leftPos,rightPos);
        }
746

747
        //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.
748 749 750 751
        infoText.replace("background: #","color:black;background: #").replace("background-color: #","color:black;background: #").replace("background:#","color:black;background:#").replace("background-color:#","color:black;background:#").replace("background: pink","color:black;background: pink");
        infoText.replace("//","http://"); //This is to fix links on wikipedia which are missing http from the url
        infoText.replace("https:http:","https:").replace("http:http:","http:");//Just in case it was done to an actual complete url

752 753 754 755 756 757 758 759 760 761 762 763 764
        //This section is intended to remove links from the object name header at the top.  The links break up the header.
        int thLeft=infoText.indexOf("<th ");
        if(thLeft!=-1){
            int thRight=infoText.indexOf("</th>", thLeft);
            int firstA=infoText.indexOf("<a ", thLeft);
            if(firstA != -1 && firstA < thRight){
                int rightA =infoText.indexOf(">", firstA)-firstA+1;
                infoText.remove(firstA, rightA);
                int endA=infoText.indexOf("</a>", firstA);
                infoText.remove(endA,4);
            }
        }

765 766 767 768 769 770 771 772 773 774
        int annotationLeft=infoText.indexOf("<annotation");
        int annotationRight=infoText.indexOf("</annotation>" , annotationLeft) + 13 - annotationLeft;
        infoText.remove(annotationLeft,annotationRight); //This removes the annotation that does not render correctly for some DSOs.

        int mathLeft=infoText.indexOf("<img src=\"https://wikimedia.org/api/rest_v1/media/math");
        int mathRight=infoText.indexOf(">" , mathLeft) + 1 - mathLeft ;
        infoText.remove(mathLeft, mathRight);//This removes an image that doesn't render properly for some DSOs.

        infoText.replace("style=\"width:22em\"","style=\"width:100%;background-color: black;color: white;\"");
        infoText=infoText + "<BR>(Source: <a href='" + "https://en.wikipedia.org/w/index.php?title="+ name + "&redirects" + "'>Wikipedia</a>)";
775
        saveInfoURL(soitem, "https://en.wikipedia.org/w/index.php?title="+ name + "&redirects");
776 777 778 779 780

        int captionEnd = infoText.indexOf("</caption>"); //Start looking for the image AFTER the caption.  Planets have images in their caption.
        if(captionEnd == -1)
            captionEnd = 0;
        int leftImg=infoText.indexOf("src=\"", captionEnd)+5;
781 782 783 784 785 786 787
        if(leftImg > captionEnd + 5){
            int rightImg=infoText.indexOf("\"",leftImg)-leftImg;
            QString imgURL=infoText.mid(leftImg,rightImg);
            imgURL.replace("http://upload.wikimedia.org","https://upload.wikimedia.org"); //Although they will display, the images apparently don't download properly unless they are https.
            saveImageURL( soitem, imgURL);
            downloadWikipediaImage(soitem, imgURL);
        }
788

789 790 791 792 793 794 795 796
        QString html="<CENTER>" +  infoText + "</table></CENTER>";

        saveObjectInfoBoxText( soitem, "infoText", html);
        QString color = (Options::darkAppColors()) ? "red" : "white";
        QString linkColor = (Options::darkAppColors()) ? "red" : "yellow";
        if(Options::darkAppColors())
            html.replace("color: white","color: " + color);
        html = "<HTML><HEAD><style type=text/css>body {color:" + color + ";} a {text-decoration: none;color:" + linkColor + ";}</style></HEAD><BODY>" + html + "</BODY></HTML>";
797 798
        if(soitem==m_CurSoItem)
            infoBoxText->setProperty("text", html);
799 800 801
    });
}

802
void WIView::saveObjectInfoBoxText(SkyObjItem * soitem, QString type, QString text){
803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858

    QFile file;
    QString fname = type + "-" + soitem->getName().toLower().remove( ' ' ) + ".html";

    QDir writableDir;
    QString filePath=KSPaths::writableLocation(QStandardPaths::GenericDataLocation) + "descriptions";
    writableDir.mkpath(filePath);

    file.setFileName( filePath + "/" + fname ) ; //determine filename in local user KDE directory tree.

    if (file.open(QIODevice::WriteOnly) == false)
    {
        qDebug()<<"Image text cannot be saved for later.  file save error";
        return;
    }
    else
    {
        QTextStream stream( &file );
        stream << text;
        file.close();
    }
}

void WIView::saveImageURL(SkyObjItem * soitem, QString imageURL){

    QFile file;
    file.setFileName( KSPaths::writableLocation(QStandardPaths::GenericDataLocation) + "image_url.dat" ) ; //determine filename in local user KDE directory tree.
    QString entry = soitem->getName() + ':' + "Show Wikipedia Image" + ':' + imageURL;

    if(file.open(QIODevice::ReadOnly)){
        QTextStream in(&file);
        QString line;
        while ( !in.atEnd() )
        {
            line = in.readLine();
            if (line==entry){
                file.close();
                return;
            }
        }
        file.close();
    }

    if ( !file.open( QIODevice::ReadWrite | QIODevice::Append ) )
    {
        qDebug()<<"Image URL cannot be saved for later.  image_url.dat error";
        return;
    }
    else
    {
        QTextStream stream( &file );
        stream << entry << endl;
        file.close();
    }
}

859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890
void WIView::saveInfoURL(SkyObjItem * soitem, QString infoURL){

    QFile file;
    file.setFileName( KSPaths::writableLocation(QStandardPaths::GenericDataLocation) + "info_url.dat" ) ; //determine filename in local user KDE directory tree.
    QString entry = soitem->getName() + ':' + "Wikipedia Page" + ':' + infoURL;

    if(file.open(QIODevice::ReadOnly)){
        QTextStream in(&file);
        QString line;
        while ( !in.atEnd() )
        {
            line = in.readLine();
            if (line==entry){
                file.close();
                return;
            }
        }
        file.close();
    }

    if ( !file.open( QIODevice::ReadWrite | QIODevice::Append ) )
    {
        qDebug()<<"Info URL cannot be saved for later.  info_url.dat error";
        return;
    }
    else
    {
        QTextStream stream( &file );
        stream << entry << endl;
        file.close();
    }
}
891 892 893 894 895 896 897 898 899 900 901

void WIView::downloadWikipediaImage(SkyObjItem * soitem, QString imageURL)
{
    QString fname = "wikiImage-" + soitem->getName().toLower().remove( ' ' ) + ".png";

    QDir writableDir;
    QString filePath=KSPaths::writableLocation(QStandardPaths::GenericDataLocation) + "descriptions";
    writableDir.mkpath(filePath);

    QString fileN =filePath + "/" + fname;

902 903 904 905 906 907 908 909
    QNetworkReply * response = manager->get(QNetworkRequest(QUrl(imageURL)));
    QTimer::singleShot(60000, response, [response]
    { //Shut it down after 60 sec.
        response->abort();
        response->deleteLater();
        qDebug()<<"Image Download Timed out.";
    });
    connect(response, &QNetworkReply::finished, this, [fileN, response, this]{
910 911 912 913
      response->deleteLater();
      if (response->error() != QNetworkReply::NoError) return;
        QImage* image = new QImage();
        QByteArray responseData=response->readAll();
914 915
        if(image->loadFromData(responseData)){
            image->save(fileN);
916
            refreshListView(); //This is to update the images displayed with the new image.
917 918 919
        }
        else
            qDebug()<<"image not downloaded";
920 921 922
    });


923
}
924