File indexing completed on 2024-03-24 15:15:32

0001 /*
0002     SPDX-FileCopyrightText: 2002 Jason Harris and Jasem Mutlaq <kstars@30doradus.org>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "detaildialog.h"
0008 
0009 #include "config-kstars.h"
0010 
0011 #include "addlinkdialog.h"
0012 #include "kspaths.h"
0013 #include "ksnotification.h"
0014 #include "kstarsdata.h"
0015 #include "observinglist.h"
0016 #include "skymap.h"
0017 #include "skyobjectuserdata.h"
0018 #include "thumbnailpicker.h"
0019 #include "skycomponents/constellationboundarylines.h"
0020 #include "skycomponents/skymapcomposite.h"
0021 #include "catalogobject.h"
0022 #include "skyobjects/ksasteroid.h"
0023 #include "skyobjects/kscomet.h"
0024 #include "skyobjects/ksmoon.h"
0025 #include "skyobjects/starobject.h"
0026 #include "skyobjects/supernova.h"
0027 #include "catalogsdb.h"
0028 #include "Options.h"
0029 
0030 #ifdef HAVE_INDI
0031 #include "indi/indilistener.h"
0032 #include "indi/indimount.h"
0033 #endif
0034 
0035 #include <QDesktopServices>
0036 #include <QDir>
0037 #include <QDirIterator>
0038 
0039 DetailDialog::DetailDialog(SkyObject *o, const KStarsDateTime &ut, GeoLocation *geo,
0040                            QWidget *parent)
0041     : KPageDialog(parent), selectedObject(o), Data(nullptr), DataComet(nullptr),
0042       Pos(nullptr), Links(nullptr), Adv(nullptr),
0043       Log(nullptr), m_user_data{ KStarsData::Instance()->getUserData(o->name()) }
0044 {
0045 #ifdef Q_OS_OSX
0046     setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint);
0047 #endif
0048     setFaceType(Tabbed);
0049     setBackgroundRole(QPalette::Base);
0050 
0051     titlePalette = palette();
0052     titlePalette.setColor(backgroundRole(), palette().color(QPalette::Active, QPalette::Highlight));
0053     titlePalette.setColor(foregroundRole(), palette().color(QPalette::Active, QPalette::HighlightedText));
0054 
0055     //Create thumbnail image
0056     Thumbnail.reset(new QPixmap(200, 200));
0057 
0058     setWindowTitle(i18nc("@title:window", "Object Details"));
0059 
0060     // JM 2016-11-22: Do we really need a close button?
0061     //setStandardButtons(QDialogButtonBox::Close);
0062     setStandardButtons(QDialogButtonBox::NoButton);
0063 
0064     createGeneralTab();
0065     createPositionTab(ut, geo);
0066     createLinksTab();
0067     createAdvancedTab();
0068     createLogTab();
0069 }
0070 
0071 void DetailDialog::createGeneralTab()
0072 {
0073     Data = new DataWidget(this);
0074     addPage(Data, i18n("General"));
0075 
0076     Data->Names->setPalette(titlePalette);
0077 
0078     //Connections
0079     connect(Data->ObsListButton, SIGNAL(clicked()), this, SLOT(addToObservingList()));
0080     connect(Data->CenterButton, SIGNAL(clicked()), this, SLOT(centerMap()));
0081 #ifdef HAVE_INDI
0082     connect(Data->ScopeButton, SIGNAL(clicked()), this, SLOT(centerTelescope()));
0083 #else
0084     Data->ScopeButton->setEnabled(false);
0085 #endif
0086     connect(Data->Image, SIGNAL(clicked()), this, SLOT(updateThumbnail()));
0087 
0088     // Stuff that should be visible only for specific types of objects
0089     Data->IllumLabel->setVisible(false); // Only shown for the moon
0090     Data->Illumination->setVisible(false);
0091 
0092     Data->BVIndex->setVisible(false); // Only shown for stars
0093     Data->BVLabel->setVisible(false);
0094 
0095     Data->CatalogLabel->setVisible(false);
0096 
0097     //Show object thumbnail image
0098     showThumbnail();
0099 
0100     //Fill in the data fields
0101     //Contents depend on type of object
0102     QString objecttyp;
0103 
0104     switch (selectedObject->type())
0105     {
0106         case SkyObject::STAR:
0107         {
0108             StarObject *s = (StarObject *)selectedObject;
0109 
0110             if (s->getHDIndex())
0111             {
0112                 Data->Names->setText(
0113                     QString("%1, HD %2").arg(s->longname()).arg(s->getHDIndex()));
0114             }
0115             else
0116             {
0117                 Data->Names->setText(s->longname());
0118             }
0119 
0120             objecttyp = i18n("%1 star", s->sptype());
0121             Data->Magnitude->setText(
0122                 i18nc("number in magnitudes", "%1 mag",
0123                       QLocale().toString(s->mag(), 'f', 2))); //show to hundredth place
0124 
0125             Data->BVLabel->setVisible(true);
0126             Data->BVIndex->setVisible(true);
0127             if (s->getBVIndex() < 30.)
0128             {
0129                 Data->BVIndex->setText(QString::number(s->getBVIndex(), 'f', 2));
0130             }
0131 
0132             //The thumbnail image is empty, and isn't clickable for stars
0133             //Also, don't show the border around the Image QFrame.
0134             Data->Image->setFrameStyle(QFrame::NoFrame);
0135             disconnect(Data->Image, SIGNAL(clicked()), this, SLOT(updateThumbnail()));
0136 
0137             //distance
0138             if (s->distance() > 2000. || s->distance() < 0.) // parallax < 0.5 mas
0139             {
0140                 Data->Distance->setText(
0141                     QString(i18nc("larger than 2000 parsecs", "> 2000 pc")));
0142             }
0143             else if (s->distance() > 50.) //show to nearest integer
0144             {
0145                 Data->Distance->setText(i18nc("number in parsecs", "%1 pc",
0146                                               QLocale().toString(s->distance(), 'f', 0)));
0147             }
0148             else if (s->distance() > 10.0) //show to tenths place
0149             {
0150                 Data->Distance->setText(i18nc("number in parsecs", "%1 pc",
0151                                               QLocale().toString(s->distance(), 'f', 1)));
0152             }
0153             else //show to hundredths place
0154             {
0155                 Data->Distance->setText(i18nc("number in parsecs", "%1 pc",
0156                                               QLocale().toString(s->distance(), 'f', 2)));
0157             }
0158 
0159             //Note multiplicity/variability in angular size label
0160             Data->AngSizeLabel->setText(QString());
0161             Data->AngSize->setText(QString());
0162             Data->AngSizeLabel->setFont(Data->AngSize->font());
0163             if (s->isMultiple() && s->isVariable())
0164             {
0165                 Data->AngSizeLabel->setText(
0166                     i18nc("the star is a multiple star", "multiple") + ',');
0167                 Data->AngSize->setText(i18nc("the star is a variable star", "variable"));
0168             }
0169             else if (s->isMultiple())
0170             {
0171                 Data->AngSizeLabel->setText(
0172                     i18nc("the star is a multiple star", "multiple"));
0173             }
0174             else if (s->isVariable())
0175             {
0176                 Data->AngSizeLabel->setText(
0177                     i18nc("the star is a variable star", "variable"));
0178             }
0179 
0180             // Add a label to indicate proper motion
0181             double pmRA = s->pmRA(), pmDec = s->pmDec();
0182             if (std::isfinite(pmRA) && std::isfinite(pmDec) && (pmRA != 0.0 || pmDec != 0.0))
0183             {
0184                 // we have data : abuse the illumination label to show it!
0185                 Data->IllumLabel->setText(i18nc("Proper motion of a star", "Proper Motion:"));
0186                 Data->Illumination->setText(
0187                     i18nc(
0188                         "The first arg is proper motion in right ascension and the second in the declination. The unit stands for milliarcsecond per year",
0189                         "%1 %2 mas/yr",
0190                         QLocale().toString(pmRA, 'f', (pmRA >= 100.0 ? 1 :  2)),
0191                         QLocale().toString(pmDec, 'f', (pmDec >= 100.0 ? 1 : 2))
0192                     ));
0193                 Data->IllumLabel->setVisible(true);
0194                 Data->Illumination->setVisible(true);
0195             }
0196 
0197             break; //end of stars case
0198         }
0199         case SkyObject::ASTEROID: //[fall through to planets]
0200         case SkyObject::COMET:    //[fall through to planets]
0201         case SkyObject::MOON:     //[fall through to planets]
0202         case SkyObject::PLANET:
0203         {
0204             KSPlanetBase *ps = dynamic_cast<KSPlanetBase *>(selectedObject);
0205 
0206             if (!ps)
0207                 break;
0208 
0209             Data->Names->setText(ps->longname());
0210 
0211             //Type is "G5 star" for Sun
0212             if (ps->name() == i18n("Sun"))
0213             {
0214                 objecttyp = i18n("G5 star");
0215             }
0216             else if (ps->name() == i18n("Moon"))
0217             {
0218                 objecttyp = ps->translatedName();
0219             }
0220             else if (ps->name() == i18nc("Asteroid name (optional)", "Pluto") ||
0221                      ps->name() == i18nc("Asteroid name (optional)", "Ceres") ||
0222                      ps->name() == i18nc("Asteroid name (optional)", "Eris"))
0223             {
0224                 objecttyp = i18n("Dwarf planet");
0225             }
0226             else
0227             {
0228                 objecttyp = ps->typeName();
0229             }
0230 
0231             //The moon displays illumination fraction and updateMag is called to calculate moon's current magnitude
0232             if (selectedObject->name() == i18n("Moon"))
0233             {
0234                 Data->IllumLabel->setVisible(true);
0235                 Data->Illumination->setVisible(true);
0236                 Data->Illumination->setText(QString("%1 %").arg(QLocale().toString(
0237                                                 ((KSMoon *)selectedObject)->illum() * 100., 'f', 0)));
0238                 ((KSMoon *)selectedObject)->updateMag();
0239             }
0240 
0241             // JM: Shouldn't we use the calculated magnitude? Disabling the following
0242             /*
0243             if(selectedObject->type() == SkyObject::COMET){
0244                 Data->Magnitude->setText(i18nc("number in magnitudes", "%1 mag",
0245                                          QLocale().toString( ((KSComet *)selectedObject)->getTotalMagnitudeParameter(), 'f', 2)));  //show to hundredth place
0246 
0247             }
0248             else{*/
0249             Data->Magnitude->setText(
0250                 i18nc("number in magnitudes", "%1 mag",
0251                       QLocale().toString(ps->mag(), 'f', 2))); //show to hundredth place
0252             //}
0253 
0254             //Distance from Earth.  The moon requires a unit conversion
0255             if (ps->name() == i18n("Moon"))
0256             {
0257                 Data->Distance->setText(
0258                     i18nc("distance in kilometers", "%1 km",
0259                           QLocale().toString(ps->rearth() * AU_KM, 'f', 2)));
0260             }
0261             else
0262             {
0263                 Data->Distance->setText(i18nc("distance in Astronomical Units", "%1 AU",
0264                                               QLocale().toString(ps->rearth(), 'f', 3)));
0265             }
0266 
0267             //Angular size; moon and sun in arcmin, others in arcsec
0268             if (ps->angSize())
0269             {
0270                 if (ps->name() == i18n("Sun") || ps->name() == i18n("Moon"))
0271                 {
0272                     Data->AngSize->setText(i18nc(
0273                                                "angular size in arcminutes", "%1 arcmin",
0274                                                QLocale().toString(
0275                                                    ps->angSize(), 'f',
0276                                                    1))); // Needn't be a plural form because sun / moon will never contract to 1 arcminute
0277                 }
0278                 else
0279                 {
0280                     Data->AngSize->setText(
0281                         i18nc("angular size in arcseconds", "%1 arcsec",
0282                               QLocale().toString(ps->angSize() * 60.0, 'f', 1)));
0283                 }
0284             }
0285             else
0286             {
0287                 Data->AngSize->setText("--");
0288             }
0289 
0290             break; //end of planets/comets/asteroids case
0291         }
0292         case SkyObject::SUPERNOVA:
0293         {
0294             Supernova *sup = dynamic_cast<Supernova *>(selectedObject);
0295 
0296             objecttyp = i18n("Supernova");
0297             Data->Names->setText(sup->name());
0298             if (sup->mag() < 99)
0299                 Data->Magnitude->setText(i18nc("number in magnitudes", "%1 mag",
0300                                                QLocale().toString(sup->mag(), 'f', 2)));
0301             else
0302                 Data->Magnitude->setText("--");
0303 
0304             Data->DistanceLabel->setVisible(false);
0305             Data->Distance->setVisible(false);
0306 
0307             Data->AngSizeLabel->setVisible(false);
0308             Data->AngSize->setVisible(false);
0309 
0310             QLabel *discoveryDateLabel = new QLabel(i18n("Discovery Date:"), this);
0311             QLabel *discoveryDate      = new QLabel(sup->getDate(), this);
0312             Data->dataGridLayout->addWidget(discoveryDateLabel, 1, 0);
0313             Data->dataGridLayout->addWidget(discoveryDate, 1, 1);
0314 
0315             QLabel *typeLabel = new QLabel(i18n("Type:"), this);
0316             QLabel *type      = new QLabel(sup->getType(), this);
0317             Data->dataGridLayout->addWidget(typeLabel, 2, 0);
0318             Data->dataGridLayout->addWidget(type, 2, 1);
0319 
0320             QLabel *hostGalaxyLabel = new QLabel(i18n("Host Galaxy:"), this);
0321             QLabel *hostGalaxy      = new QLabel(
0322                 sup->getHostGalaxy().isEmpty() ? "--" : sup->getHostGalaxy(), this);
0323             Data->dataGridLayout->addWidget(hostGalaxyLabel, 3, 0);
0324             Data->dataGridLayout->addWidget(hostGalaxy, 3, 1);
0325 
0326             QLabel *redShiftLabel = new QLabel(i18n("Red Shift:"), this);
0327             QLabel *redShift      = new QLabel(
0328                 (sup->getRedShift() < 99) ? QString::number(sup->getRedShift(), 'f', 2) :
0329                 QString("--"),
0330                 this);
0331             Data->dataGridLayout->addWidget(redShiftLabel, 4, 0);
0332             Data->dataGridLayout->addWidget(redShift, 4, 1);
0333 
0334             QLabel *urlLabel = new QLabel(i18n("URL:"), this);
0335             QLabel *url      = new QLabel(QString("<a href=\"%1\">%1</a>").arg(sup->url()), this);
0336             url->setTextFormat(Qt::RichText);
0337             url->setTextInteractionFlags(Qt::TextBrowserInteraction);
0338             url->setOpenExternalLinks(true);
0339             Data->dataGridLayout->addWidget(urlLabel, 5, 0);
0340             Data->dataGridLayout->addWidget(url, 5, 1);
0341 
0342             break;
0343         }
0344         default: //deep-sky objects
0345         {
0346             CatalogObject *dso = dynamic_cast<CatalogObject *>(selectedObject);
0347 
0348             if (!dso)
0349                 break;
0350 
0351             //Show all names recorded for the object
0352             QStringList nameList;
0353             if (!dso->longname().isEmpty() && dso->longname() != dso->name())
0354             {
0355                 nameList.append(dso->translatedLongName());
0356             }
0357 
0358             nameList.append(dso->translatedName());
0359 
0360             if (!dso->translatedName2().isEmpty())
0361             {
0362                 nameList.append(dso->translatedName2());
0363             }
0364 
0365             Data->Names->setText(nameList.join(","));
0366 
0367             objecttyp = dso->typeName();
0368 
0369             if (dso->type() == SkyObject::RADIO_SOURCE)
0370             {
0371                 Data->MagLabel->setText(
0372                     i18nc("integrated flux at a frequency", "Flux(%1):", 1));
0373                 Data->Magnitude->setText(i18nc("integrated flux value", "%1 %2",
0374                                                QLocale().toString(dso->flux(), 'f', 1),
0375                                                "obs")); //show to tenths place
0376             }
0377             else if (std::isnan(dso->mag()))
0378             {
0379                 Data->Magnitude->setText("--");
0380             }
0381             else
0382             {
0383                 Data->Magnitude->setText(
0384                     i18nc("number in magnitudes", "%1 mag",
0385                           QLocale().toString(dso->mag(), 'f', 1))); //show to tenths place
0386             }
0387 
0388             //No distances at this point...
0389             Data->Distance->setText("--");
0390 
0391             Data->CatalogLabel->setVisible(true);
0392             Data->Catalog->setText(dso->getCatalog().name);
0393 
0394             //Only show decimal place for small angular sizes
0395             if (dso->a() > 10.0)
0396             {
0397                 Data->AngSize->setText(i18nc("angular size in arcminutes", "%1 arcmin",
0398                                              QLocale().toString(dso->a(), 'f', 0)));
0399             }
0400             else if (dso->a())
0401             {
0402                 Data->AngSize->setText(i18nc("angular size in arcminutes", "%1 arcmin",
0403                                              QLocale().toString(dso->a(), 'f', 1)));
0404             }
0405             else
0406             {
0407                 Data->AngSize->setText("--");
0408             }
0409 
0410             break;
0411         }
0412     }
0413 
0414     // Add specifics data
0415     switch (selectedObject->type())
0416     {
0417         case SkyObject::ASTEROID:
0418         {
0419             KSAsteroid *ast = dynamic_cast<KSAsteroid *>(selectedObject);
0420             // Show same specifics data as comets
0421             DataComet = new DataCometWidget(this);
0422             Data->IncludeData->layout()->addWidget(DataComet);
0423 
0424             // Perihelion
0425             DataComet->Perihelion->setText(i18nc("Distance in astronomical units",
0426                                                  "%1 AU",
0427                                                  QString::number(ast->getPerihelion())));
0428             // Earth MOID
0429             if (ast->getEarthMOID() == 0)
0430                 DataComet->EarthMOID->setText("--");
0431             else
0432                 DataComet->EarthMOID->setText(
0433                     i18nc("Distance in astronomical units", "%1 AU",
0434                           QString::number(ast->getEarthMOID())));
0435             // Orbit ID
0436             DataComet->OrbitID->setText(ast->getOrbitID());
0437             // Orbit Class
0438             DataComet->OrbitClass->setText(ast->getOrbitClass());
0439             // NEO
0440             if (ast->isNEO())
0441                 DataComet->NEO->setText(i18n("Yes"));
0442             else
0443                 DataComet->NEO->setText(i18n("No"));
0444             // Albedo
0445             if (ast->getAlbedo() == 0.0)
0446                 DataComet->Albedo->setText("--");
0447             else
0448                 DataComet->Albedo->setText(QString::number(ast->getAlbedo()));
0449             // Diameter
0450             if (ast->getDiameter() == 0.0)
0451                 DataComet->Diameter->setText("--");
0452             else
0453                 DataComet->Diameter->setText(i18nc("Diameter in kilometers", "%1 km",
0454                                                    QString::number(ast->getDiameter())));
0455             // Dimensions
0456             if (ast->getDimensions().isEmpty())
0457                 DataComet->Dimensions->setText("--");
0458             else
0459                 DataComet->Dimensions->setText(
0460                     i18nc("Dimension in kilometers", "%1 km", ast->getDimensions()));
0461             // Rotation period
0462             if (ast->getRotationPeriod() == 0.0)
0463                 DataComet->Rotation->setText("--");
0464             else
0465                 DataComet->Rotation->setText(
0466                     i18nc("Rotation period in hours", "%1 h",
0467                           QString::number(ast->getRotationPeriod())));
0468             // Period
0469             if (ast->getPeriod() == 0.0)
0470                 DataComet->Period->setText("--");
0471             else
0472                 DataComet->Period->setText(i18nc("Orbit period in years", "%1 y",
0473                                                  QString::number(ast->getPeriod())));
0474             break;
0475         }
0476         case SkyObject::COMET:
0477         {
0478             KSComet *com = dynamic_cast<KSComet *>(selectedObject);
0479             DataComet    = new DataCometWidget(this);
0480             Data->IncludeData->layout()->addWidget(DataComet);
0481 
0482             // Perihelion
0483             DataComet->Perihelion->setText(i18nc("Distance in astronomical units",
0484                                                  "%1 AU",
0485                                                  QString::number(com->getPerihelion())));
0486             // Earth MOID
0487             if (com->getEarthMOID() == 0)
0488                 DataComet->EarthMOID->setText("--");
0489             else
0490                 DataComet->EarthMOID->setText(
0491                     i18nc("Distance in astronomical units", "%1 AU",
0492                           QString::number(com->getEarthMOID())));
0493             // Orbit ID
0494             DataComet->OrbitID->setText(com->getOrbitID());
0495             // Orbit Class
0496             DataComet->OrbitClass->setText(com->getOrbitClass());
0497             // NEO
0498             if (com->isNEO())
0499                 DataComet->NEO->setText(i18n("Yes"));
0500             else
0501                 DataComet->NEO->setText(i18n("No"));
0502             // Albedo
0503             if (com->getAlbedo() == 0.0)
0504                 DataComet->Albedo->setText("--");
0505             else
0506                 DataComet->Albedo->setText(QString::number(com->getAlbedo()));
0507             // Diameter
0508             if (com->getDiameter() == 0.0)
0509                 DataComet->Diameter->setText("--");
0510             else
0511                 DataComet->Diameter->setText(i18nc("Diameter in kilometers", "%1 km",
0512                                                    QString::number(com->getDiameter())));
0513             // Dimensions
0514             if (com->getDimensions().isEmpty())
0515                 DataComet->Dimensions->setText("--");
0516             else
0517                 DataComet->Dimensions->setText(
0518                     i18nc("Dimension in kilometers", "%1 km", com->getDimensions()));
0519             // Rotation period
0520             if (com->getRotationPeriod() == 0.0)
0521                 DataComet->Rotation->setText("--");
0522             else
0523                 DataComet->Rotation->setText(
0524                     i18nc("Rotation period in hours", "%1 h",
0525                           QString::number(com->getRotationPeriod())));
0526             // Period
0527             if (com->getPeriod() == 0.0)
0528                 DataComet->Period->setText("--");
0529             else
0530                 DataComet->Period->setText(i18nc("Orbit period in years", "%1 y",
0531                                                  QString::number(com->getPeriod())));
0532             break;
0533         }
0534     }
0535 
0536     //Common to all types:
0537     QString cname = KStarsData::Instance()
0538                     ->skyComposite()
0539                     ->constellationBoundary()
0540                     ->constellationName(selectedObject);
0541     if (selectedObject->type() != SkyObject::CONSTELLATION)
0542     {
0543         cname = i18nc(
0544                     "%1 type of sky object (planet, asteroid etc), %2 name of a constellation",
0545                     "%1 in %2", objecttyp, cname);
0546     }
0547     Data->ObjectTypeInConstellation->setText(cname);
0548 }
0549 
0550 void DetailDialog::createPositionTab(const KStarsDateTime &ut, GeoLocation *geo)
0551 {
0552     Pos = new PositionWidget(this);
0553     addPage(Pos, i18n("Position"));
0554 
0555     Pos->CoordTitle->setPalette(titlePalette);
0556     Pos->RSTTitle->setPalette(titlePalette);
0557     KStarsData *data = KStarsData::Instance();
0558 
0559     //Coordinates Section:
0560     //Don't use KLocale::formatNumber() for the epoch string,
0561     //because we don't want a thousands-place separator!
0562     selectedObject->updateCoords(data->updateNum(), true, data->geo()->lat(), data->lst(), false);
0563     QString sEpoch = QString::number(KStarsDateTime::jdToEpoch(selectedObject->getLastPrecessJD()), 'f', 1);
0564     //Replace the decimal point with localized decimal symbol
0565     sEpoch.replace('.', QLocale().decimalPoint()); // Is this necessary? -- asimha Oct 2016
0566 
0567     /*qDebug() << Q_FUNC_INFO << (selectedObject->deprecess(data->updateNum())).ra0().toHMSString()
0568              << (selectedObject->deprecess(data->updateNum())).dec0().toDMSString();*/
0569     //qDebug() << Q_FUNC_INFO << selectedObject->ra().toHMSString() << selectedObject->dec().toDMSString();
0570     Pos->RALabel->setText(i18n("RA (%1):", sEpoch));
0571     Pos->DecLabel->setText(i18n("DE (%1):", sEpoch));
0572     Pos->RA->setText(selectedObject->ra().toHMSString(false, true));
0573     Pos->Dec->setText(selectedObject->dec().toDMSString(false, false, true));
0574 
0575     selectedObject->EquatorialToHorizontal(data->lst(), data->geo()->lat());
0576 
0577     Pos->Az->setText(selectedObject->az().toDMSString());
0578     dms a;
0579 
0580     if (Options::useAltAz())
0581         a = selectedObject->alt();
0582     else
0583         a = selectedObject->altRefracted();
0584     Pos->Alt->setText(a.toDMSString());
0585 
0586     // Display the RA0 and Dec0 for objects that are outside the solar system
0587     // 2017-09-10 JM: Exception added for asteroids and comets since we have J2000 for them.
0588     // Maybe others?
0589     Pos->RA0->setText(selectedObject->ra0().toHMSString(false, true));
0590     Pos->Dec0->setText(selectedObject->dec0().toDMSString(false, false, true));
0591 #if 0
0592     if (!selectedObject->isSolarSystem() || selectedObject->type() == SkyObject::COMET
0593             || selectedObject->type() == SkyObject::ASTEROID)
0594     {
0595         Pos->RA0->setText(selectedObject->ra0().toHMSString());
0596         Pos->Dec0->setText(selectedObject->dec0().toDMSString());
0597     }
0598     else
0599     {
0600         Pos->RA0->setText("--");
0601         Pos->Dec0->setText("--");
0602     }
0603 #endif
0604 
0605     //Hour Angle can be negative, but dms HMS expressions cannot.
0606     //Here's a kludgy workaround:
0607     dms lst = geo->GSTtoLST(ut.gst());
0608     dms ha(lst.Degrees() - selectedObject->ra().Degrees());
0609     QChar sgn('+');
0610     if (ha.Hours() > 12.0)
0611     {
0612         ha.setH(24.0 - ha.Hours());
0613         sgn = '-';
0614     }
0615     Pos->HA->setText(QString("%1%2").arg(sgn).arg(ha.toHMSString()));
0616 
0617     //Airmass is approximated as the secant of the zenith distance,
0618     //equivalent to 1./sin(Alt).  Beware of Inf at Alt=0!
0619     if (selectedObject->alt().Degrees() > 0.0)
0620         Pos->Airmass->setText(QLocale().toString(selectedObject->airmass(), 'f', 2));
0621     else
0622         Pos->Airmass->setText("--");
0623 
0624     //Rise/Set/Transit Section:
0625 
0626     //Prepare time/position variables
0627     QTime rt = selectedObject->riseSetTime(ut, geo, true);   //true = use rise time
0628     dms raz  = selectedObject->riseSetTimeAz(ut, geo, true); //true = use rise time
0629 
0630     //If transit time is before rise time, use transit time for tomorrow
0631     QTime tt = selectedObject->transitTime(ut, geo);
0632     dms talt = selectedObject->transitAltitude(ut, geo);
0633     if (tt < rt)
0634     {
0635         tt   = selectedObject->transitTime(ut.addDays(1), geo);
0636         talt = selectedObject->transitAltitude(ut.addDays(1), geo);
0637     }
0638 
0639     // JM 2021.09.14: Set time is already taken care of
0640     QTime st = selectedObject->riseSetTime(ut, geo, false);   //false = use set time
0641     dms saz  = selectedObject->riseSetTimeAz(ut, geo, false); //false = use set time
0642     //    if (st < rt)
0643     //    {
0644     //        st  = selectedObject->riseSetTime(ut.addDays(1), geo, false);   //false = use set time
0645     //        saz = selectedObject->riseSetTimeAz(ut.addDays(1), geo, false); //false = use set time
0646     //    }
0647 
0648     if (rt.isValid())
0649     {
0650         Pos->TimeRise->setText(QString::asprintf("%02d:%02d", rt.hour(), rt.minute()));
0651         Pos->TimeSet->setText(QString::asprintf("%02d:%02d", st.hour(), st.minute()));
0652         Pos->AzRise->setText(raz.toDMSString());
0653         Pos->AzSet->setText(saz.toDMSString());
0654     }
0655     else
0656     {
0657         if (selectedObject->alt().Degrees() > 0.0)
0658         {
0659             Pos->TimeRise->setText(i18n("Circumpolar"));
0660             Pos->TimeSet->setText(i18n("Circumpolar"));
0661         }
0662         else
0663         {
0664             Pos->TimeRise->setText(i18n("Never rises"));
0665             Pos->TimeSet->setText(i18n("Never rises"));
0666         }
0667 
0668         Pos->AzRise->setText(i18nc("Not Applicable", "N/A"));
0669         Pos->AzSet->setText(i18nc("Not Applicable", "N/A"));
0670     }
0671 
0672     Pos->TimeTransit->setText(QString::asprintf("%02d:%02d", tt.hour(), tt.minute()));
0673     Pos->AltTransit->setText(talt.toDMSString());
0674 
0675     // Restore the position and other time-dependent parameters
0676     selectedObject->recomputeCoords(ut, geo);
0677 }
0678 
0679 void DetailDialog::createLinksTab()
0680 {
0681     // don't create a link tab for an unnamed star
0682     if (selectedObject->name() == QString("star"))
0683         return;
0684 
0685     Links = new LinksWidget(this);
0686     addPage(Links, i18n("Links"));
0687 
0688     Links->InfoTitle->setPalette(titlePalette);
0689     Links->ImagesTitle->setPalette(titlePalette);
0690 
0691     for (const auto &link : m_user_data.websites())
0692         Links->InfoTitleList->addItem(i18nc("Image/info menu item (should be translated)",
0693                                             link.title.toLocal8Bit()));
0694 
0695     //Links->InfoTitleList->setCurrentRow(0);
0696 
0697     for (const auto &link : m_user_data.images())
0698         Links->ImageTitleList->addItem(i18nc(
0699                                            "Image/info menu item (should be translated)", link.title.toLocal8Bit()));
0700 
0701     // Signals/Slots
0702     connect(Links->ViewButton, SIGNAL(clicked()), this, SLOT(viewLink()));
0703     connect(Links->AddLinkButton, SIGNAL(clicked()), this, SLOT(addLink()));
0704     connect(Links->EditLinkButton, SIGNAL(clicked()), this, SLOT(editLinkDialog()));
0705     connect(Links->RemoveLinkButton, SIGNAL(clicked()), this, SLOT(removeLinkDialog()));
0706 
0707     // When an item is selected in info list, selected items are cleared image list.
0708     connect(Links->InfoTitleList, SIGNAL(currentItemChanged(QListWidgetItem*, QListWidgetItem*)), this,
0709             SLOT(setCurrentLink(QListWidgetItem*)));
0710     connect(Links->InfoTitleList, SIGNAL(currentItemChanged(QListWidgetItem*, QListWidgetItem*)),
0711             Links->ImageTitleList, SLOT(clearSelection()));
0712 
0713     // vice versa
0714     connect(Links->ImageTitleList, SIGNAL(currentItemChanged(QListWidgetItem*, QListWidgetItem*)), this,
0715             SLOT(setCurrentLink(QListWidgetItem*)));
0716     connect(Links->ImageTitleList, SIGNAL(currentItemChanged(QListWidgetItem*, QListWidgetItem*)),
0717             Links->InfoTitleList, SLOT(clearSelection()));
0718 
0719     connect(Links->InfoTitleList, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(viewLink()));
0720     connect(Links->ImageTitleList, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(viewLink()));
0721 
0722     connect(Links->InfoTitleList, SIGNAL(itemSelectionChanged()), this, SLOT(updateButtons()));
0723     connect(Links->ImageTitleList, SIGNAL(itemSelectionChanged()), this, SLOT(updateButtons()));
0724 
0725     updateLists();
0726 }
0727 
0728 void DetailDialog::addLink()
0729 {
0730     if (!selectedObject)
0731         return;
0732     QPointer<AddLinkDialog> adialog = new AddLinkDialog(this, selectedObject->name());
0733     QString entry;
0734     QFile file;
0735 
0736     if (adialog->exec() == QDialog::Accepted)
0737     {
0738         const auto &success = KStarsData::Instance()->addToUserData(
0739                                   selectedObject->name(),
0740                                   SkyObjectUserdata::LinkData
0741         {
0742             adialog->desc(), QUrl(adialog->url()),
0743             (adialog->isImageLink()) ? SkyObjectUserdata::LinkData::Type::image :
0744             SkyObjectUserdata::LinkData::Type::website });
0745 
0746         if (!success.first)
0747         {
0748             KSNotification::sorry(success.second, i18n("Could not add the link."));
0749         }
0750     }
0751 
0752     updateLists();
0753     delete adialog;
0754 }
0755 
0756 void DetailDialog::createAdvancedTab()
0757 {
0758     // Don't create an adv tab for an unnamed star or if advinterface file failed loading
0759     // We also don't need adv dialog for solar system objects.
0760     if (selectedObject->name() == QString("star") || KStarsData::Instance()->avdTree().isEmpty() ||
0761             selectedObject->type() == SkyObject::PLANET || selectedObject->type() == SkyObject::COMET ||
0762             selectedObject->type() == SkyObject::ASTEROID)
0763         return;
0764 
0765     Adv = new DatabaseWidget(this);
0766     addPage(Adv, i18n("Advanced"));
0767 
0768     connect(Adv->ADVTree, SIGNAL(itemDoubleClicked(QTreeWidgetItem*, int)), this, SLOT(viewADVData()));
0769 
0770     populateADVTree();
0771 }
0772 
0773 void DetailDialog::createLogTab()
0774 {
0775     //Don't create a log tab for an unnamed star
0776     if (selectedObject->name() == QString("star"))
0777         return;
0778 
0779     // Log Tab
0780     Log = new LogWidget(this);
0781     addPage(Log, i18n("Log"));
0782 
0783     Log->LogTitle->setPalette(titlePalette);
0784 
0785     const auto &log = m_user_data.userLog;
0786 
0787     if (log.isEmpty())
0788         Log->UserLog->setText(i18n("Record here observation logs and/or data on %1.",
0789                                    selectedObject->translatedName()));
0790     else
0791         Log->UserLog->setText(log);
0792 
0793     //Automatically save the log contents when the widget loses focus
0794     connect(Log->UserLog, SIGNAL(focusOut()), this, SLOT(saveLogData()));
0795 }
0796 
0797 void DetailDialog::setCurrentLink(QListWidgetItem *it)
0798 {
0799     m_CurrentLink = it;
0800 }
0801 
0802 void DetailDialog::viewLink()
0803 {
0804     QString URL;
0805 
0806     if (m_CurrentLink == nullptr)
0807         return;
0808 
0809     if (m_CurrentLink->listWidget() == Links->InfoTitleList)
0810     {
0811         URL = m_user_data.websites()
0812               .at(Links->InfoTitleList->row(m_CurrentLink))
0813               .url.toString();
0814     }
0815     else if (m_CurrentLink->listWidget() == Links->ImageTitleList)
0816     {
0817         URL = m_user_data.images()
0818               .at(Links->ImageTitleList->row(m_CurrentLink))
0819               .url.toString();
0820     }
0821 
0822     if (!URL.isEmpty())
0823         QDesktopServices::openUrl(QUrl(URL));
0824 }
0825 
0826 void DetailDialog::updateLists()
0827 {
0828     Links->InfoTitleList->clear();
0829     Links->ImageTitleList->clear();
0830 
0831     for (const auto &element : m_user_data.websites())
0832         Links->InfoTitleList->addItem(element.title);
0833 
0834     for (const auto &element : m_user_data.images())
0835         Links->ImageTitleList->addItem(element.title);
0836 
0837     updateButtons();
0838 }
0839 
0840 void DetailDialog::updateButtons()
0841 {
0842     bool anyLink = false;
0843     if (!Links->InfoTitleList->selectedItems().isEmpty() || !Links->ImageTitleList->selectedItems().isEmpty())
0844         anyLink = true;
0845 
0846     // Buttons could be disabled if lists are initially empty, we enable and disable them here
0847     // depending on the current status of the list.
0848     Links->ViewButton->setEnabled(anyLink);
0849     Links->EditLinkButton->setEnabled(anyLink);
0850     Links->RemoveLinkButton->setEnabled(anyLink);
0851 }
0852 
0853 void DetailDialog::editLinkDialog()
0854 {
0855     SkyObjectUserdata::Type type = SkyObjectUserdata::Type::image;
0856     int row                      = 0;
0857 
0858     if (m_CurrentLink == nullptr)
0859         return;
0860 
0861     QDialog editDialog(this);
0862     editDialog.setWindowTitle(i18nc("@title:window", "Edit Link"));
0863 
0864     QVBoxLayout *mainLayout = new QVBoxLayout;
0865 
0866     QFrame editFrame(&editDialog);
0867 
0868     mainLayout->addWidget(&editFrame);
0869 
0870     QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
0871     mainLayout->addWidget(buttonBox);
0872     connect(buttonBox, SIGNAL(accepted()), &editDialog, SLOT(accept()));
0873     connect(buttonBox, SIGNAL(rejected()), &editDialog, SLOT(reject()));
0874 
0875     editDialog.setLayout(mainLayout);
0876 
0877     if (m_CurrentLink->listWidget() == Links->InfoTitleList)
0878     {
0879         row  = Links->InfoTitleList->row(m_CurrentLink);
0880         type = SkyObjectUserdata::Type::website;
0881     }
0882     else if (m_CurrentLink->listWidget() == Links->ImageTitleList)
0883     {
0884         row  = Links->ImageTitleList->row(m_CurrentLink);
0885         type = SkyObjectUserdata::Type::image;
0886     }
0887     else
0888         return;
0889 
0890     const auto &currentItem = m_user_data.links.at(type).at(row);
0891 
0892     QLineEdit editNameField(&editFrame);
0893     editNameField.setObjectName("nameedit");
0894     editNameField.home(false);
0895     editNameField.setText(currentItem.title);
0896     QLabel editLinkURL(i18n("URL:"), &editFrame);
0897     QLineEdit editLinkField(&editFrame);
0898     editLinkField.setObjectName("urledit");
0899     editLinkField.home(false);
0900     editLinkField.setText(currentItem.url.toString());
0901     QVBoxLayout vlay(&editFrame);
0902     vlay.setObjectName("vlay");
0903     QHBoxLayout editLinkLayout(&editFrame);
0904     editLinkLayout.setObjectName("editlinklayout");
0905     editLinkLayout.addWidget(&editLinkURL);
0906     editLinkLayout.addWidget(&editLinkField);
0907     vlay.addWidget(&editNameField);
0908     vlay.addLayout(&editLinkLayout);
0909 
0910     bool go(true);
0911     // If user presses cancel then skip the action
0912     if (editDialog.exec() != QDialog::Accepted)
0913         go = false;
0914 
0915     // If nothing changed, skip th action
0916     if (editLinkField.text() == currentItem.url.toString() &&
0917             editNameField.text() == currentItem.title)
0918         go = false;
0919 
0920     if (go)
0921     {
0922         const auto &success = KStarsData::Instance()->editUserData(
0923                                   selectedObject->name(), row,
0924         { editNameField.text(), QUrl{ editLinkField.text() }, type });
0925 
0926         if (!success.first)
0927             KSNotification::sorry(success.second, i18n("Could not edit the entry."));
0928 
0929         // Set focus to the same item again
0930         if (type == SkyObjectUserdata::Type::website)
0931             Links->InfoTitleList->setCurrentRow(row);
0932         else
0933             Links->ImageTitleList->setCurrentRow(row);
0934     }
0935 
0936     updateLists();
0937 }
0938 
0939 void DetailDialog::removeLinkDialog()
0940 {
0941     int row = 0;
0942     if (m_CurrentLink == nullptr)
0943         return;
0944 
0945     SkyObjectUserdata::Type type;
0946 
0947     if (m_CurrentLink->listWidget() == Links->InfoTitleList)
0948     {
0949         row  = Links->InfoTitleList->row(m_CurrentLink);
0950         type = SkyObjectUserdata::Type::website;
0951     }
0952     else if (m_CurrentLink->listWidget() == Links->ImageTitleList)
0953     {
0954         row  = Links->ImageTitleList->row(m_CurrentLink);
0955         type = SkyObjectUserdata::Type::image;
0956     }
0957     else
0958         return;
0959 
0960     if (KMessageBox::warningContinueCancel(
0961                 nullptr,
0962                 i18n("Are you sure you want to remove the %1 link?", m_CurrentLink->text()),
0963                 i18n("Delete Confirmation"),
0964                 KStandardGuiItem::del()) != KMessageBox::Continue)
0965         return;
0966 
0967     const auto &success =
0968         KStarsData::Instance()->deleteUserData(selectedObject->name(), row, type);
0969 
0970     if (!success.first)
0971         KSNotification::sorry(success.second, i18n("Could not delete the entry."));
0972     else
0973         updateLists();
0974 }
0975 
0976 void DetailDialog::populateADVTree()
0977 {
0978     QTreeWidgetItem *parent = nullptr;
0979     QTreeWidgetItem *temp   = nullptr;
0980 
0981     // We populate the tree iteratively, keeping track of parents as we go
0982     // This solution is more efficient than the previous recursion algorithm.
0983     foreach (ADVTreeData *item, KStarsData::Instance()->avdTree())
0984     {
0985         switch (item->Type)
0986         {
0987             // Top Level
0988             case 0:
0989                 temp = new QTreeWidgetItem(parent, QStringList(i18nc("Advanced URLs: description or category",
0990                                            item->Name.toLocal8Bit().data())));
0991                 if (parent == nullptr)
0992                     Adv->ADVTree->addTopLevelItem(temp);
0993                 parent = temp;
0994 
0995                 break;
0996 
0997             // End of top level
0998             case 1:
0999                 if (parent != nullptr)
1000                     parent = parent->parent();
1001                 break;
1002 
1003             // Leaf
1004             case 2:
1005                 new QTreeWidgetItem(parent, QStringList(i18nc("Advanced URLs: description or category", item->Name.toLocal8Bit().data())));
1006                 break;
1007         }
1008     }
1009 }
1010 
1011 void DetailDialog::viewADVData()
1012 {
1013     QString link;
1014     QTreeWidgetItem *current = Adv->ADVTree->currentItem();
1015 
1016     //If the item has children or is invalid, do nothing
1017     if (!current || current->childCount() > 0)
1018         return;
1019 
1020     foreach (ADVTreeData *item, KStarsData::Instance()->avdTree())
1021     {
1022         if (item->Name == current->text(0))
1023         {
1024             link = item->Link;
1025             link = parseADVData(link);
1026             QDesktopServices::openUrl(QUrl(link));
1027             return;
1028         }
1029     }
1030 }
1031 
1032 QString DetailDialog::parseADVData(const QString &inlink)
1033 {
1034     QString link = inlink;
1035     QString subLink;
1036     int index;
1037 
1038     if ((index = link.indexOf("KSOBJ")) != -1)
1039     {
1040         link.remove(index, 5);
1041         link = link.insert(index, selectedObject->name());
1042     }
1043 
1044     if ((index = link.indexOf("KSRA")) != -1)
1045     {
1046         link.remove(index, 4);
1047         subLink = QString::asprintf("%02d%02d%02d", selectedObject->ra0().hour(), selectedObject->ra0().minute(),
1048                                     selectedObject->ra0().second());
1049         subLink = subLink.insert(2, "%20");
1050         subLink = subLink.insert(7, "%20");
1051 
1052         link = link.insert(index, subLink);
1053     }
1054     if ((index = link.indexOf("KSDEC")) != -1)
1055     {
1056         link.remove(index, 5);
1057         if (selectedObject->dec().degree() < 0)
1058         {
1059             subLink = QString::asprintf("%03d%02d%02d", selectedObject->dec0().degree(), selectedObject->dec0().arcmin(),
1060                                         selectedObject->dec0().arcsec());
1061             subLink = subLink.insert(3, "%20");
1062             subLink = subLink.insert(8, "%20");
1063         }
1064         else
1065         {
1066             subLink = QString::asprintf("%02d%02d%02d", selectedObject->dec0().degree(), selectedObject->dec0().arcmin(),
1067                                         selectedObject->dec0().arcsec());
1068             subLink = subLink.insert(0, "%2B");
1069             subLink = subLink.insert(5, "%20");
1070             subLink = subLink.insert(10, "%20");
1071         }
1072         link = link.insert(index, subLink);
1073     }
1074 
1075     return link;
1076 }
1077 
1078 void DetailDialog::saveLogData()
1079 {
1080     const auto &success = KStarsData::Instance()->updateUserLog(
1081                               selectedObject->name(), Log->UserLog->toPlainText());
1082 
1083     if (!success.first)
1084         KSNotification::sorry(success.second, i18n("Could not update the user log."));
1085 }
1086 
1087 void DetailDialog::addToObservingList()
1088 {
1089     KStarsData::Instance()->observingList()->slotAddObject(selectedObject);
1090 }
1091 
1092 void DetailDialog::centerMap()
1093 {
1094     SkyMap::Instance()->setClickedObject(selectedObject);
1095     SkyMap::Instance()->slotCenter();
1096 }
1097 
1098 void DetailDialog::centerTelescope()
1099 {
1100 #ifdef HAVE_INDI
1101 
1102     if (INDIListener::Instance()->size() == 0)
1103     {
1104         KSNotification::sorry(i18n("No connected mounts found."));
1105         return;
1106     }
1107 
1108     for (auto &oneDevice : INDIListener::devices())
1109     {
1110         if (!(oneDevice->getDriverInterface() & INDI::BaseDevice::TELESCOPE_INTERFACE))
1111             continue;
1112 
1113         if (oneDevice->isConnected() == false)
1114         {
1115             KSNotification::error(i18n("Mount %1 is offline. Please connect and retry again.", oneDevice->getDeviceName()));
1116             return;
1117         }
1118 
1119         auto mount = oneDevice->getMount();
1120         if (!mount)
1121             continue;
1122 
1123         // Display Sun warning on slew
1124         if (selectedObject && selectedObject->name() == i18n("Sun"))
1125         {
1126             if (KMessageBox::warningContinueCancel(nullptr,
1127                                                    i18n("Danger! Viewing the Sun without adequate solar filters is dangerous and will result in permanent eye damage!"))
1128                     == KMessageBox::Cancel)
1129                 return;
1130         }
1131 
1132         mount->Slew(selectedObject);
1133         return;
1134     }
1135 
1136     KSNotification::sorry(i18n("No connected mounts found."));
1137 
1138 #endif
1139 }
1140 
1141 void DetailDialog::showThumbnail()
1142 {
1143     //No image if object is a star
1144     if (selectedObject->type() == SkyObject::STAR ||
1145             selectedObject->type() == SkyObject::CATALOG_STAR)
1146     {
1147         Thumbnail->scaled(Data->Image->width(), Data->Image->height());
1148         Thumbnail->fill(Data->DataFrame->palette().color(QPalette::Window));
1149         Data->Image->setPixmap(*Thumbnail);
1150         return;
1151     }
1152 
1153     //Try to load the object's image from disk
1154     //If no image found, load "no image" image
1155 
1156     const auto &base = KSPaths::writableLocation(QStandardPaths::AppLocalDataLocation);
1157     QDirIterator search(base, QStringList() << "thumb*", QDir::Dirs);
1158 
1159     bool found = false;
1160     while (search.hasNext())
1161     {
1162         const auto &path =
1163             QDir(search.next())
1164             .absoluteFilePath(
1165                 "thumb-" + selectedObject->name().toLower().remove(' ').remove('/') +
1166                 ".png");
1167 
1168         const QFile file{ path };
1169         if (file.exists())
1170         {
1171             Thumbnail->load(path, "PNG");
1172             found = true;
1173         }
1174     }
1175 
1176     if (!found)
1177         Thumbnail->load(":/images/noimage.png");
1178 
1179     *Thumbnail = Thumbnail->scaled(Data->Image->width(), Data->Image->height(),
1180                                    Qt::KeepAspectRatio, Qt::FastTransformation);
1181 
1182     Data->Image->setPixmap(*Thumbnail);
1183 }
1184 
1185 void DetailDialog::updateThumbnail()
1186 {
1187     QPointer<ThumbnailPicker> tp = new ThumbnailPicker(selectedObject, *Thumbnail, this);
1188 
1189     if (tp->exec() == QDialog::Accepted)
1190     {
1191         QDir(KSPaths::writableLocation(QStandardPaths::AppLocalDataLocation)).mkpath("thumbnails");
1192 
1193         QString const fname =
1194             QDir(KSPaths::writableLocation(QStandardPaths::AppLocalDataLocation))
1195             .filePath("thumb-" + selectedObject->name().toLower().remove(' ').remove('/') + ".png");
1196 
1197         Data->Image->setPixmap(*(tp->image()));
1198 
1199         //If a real image was set, save it.
1200         //If the image was unset, delete the old image on disk.
1201         if (tp->imageFound())
1202         {
1203 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
1204             bool rc = Data->Image->pixmap()->save(fname, "PNG");
1205             if (rc == false)
1206             {
1207                 KSNotification::error(i18n("Unable to save image to %1", fname),
1208                                       i18n("Save Thumbnail"));
1209             }
1210             else
1211                 *Thumbnail = *(Data->Image->pixmap());
1212 #else
1213             bool rc = Data->Image->pixmap(Qt::ReturnByValue).save(fname, "PNG");
1214             if (rc == false)
1215             {
1216                 KSNotification::error(i18n("Unable to save image to %1", fname),
1217                                       i18n("Save Thumbnail"));
1218             }
1219             else
1220                 *Thumbnail = (Data->Image->pixmap(Qt::ReturnByValue));
1221 #endif
1222         }
1223         else
1224         {
1225             QFile f;
1226             f.setFileName(fname);
1227             f.remove();
1228         }
1229     }
1230     delete tp;
1231 }
1232 
1233 DataWidget::DataWidget(QWidget *p) : QFrame(p)
1234 {
1235     setupUi(this);
1236     DataFrame->setBackgroundRole(QPalette::Base);
1237 }
1238 
1239 DataCometWidget::DataCometWidget(QWidget *p) : QFrame(p)
1240 {
1241     setupUi(this);
1242 }
1243 
1244 PositionWidget::PositionWidget(QWidget *p) : QFrame(p)
1245 {
1246     setupUi(this);
1247     CoordFrame->setBackgroundRole(QPalette::Base);
1248     RSTFrame->setBackgroundRole(QPalette::Base);
1249 }
1250 
1251 LinksWidget::LinksWidget(QWidget *p) : QFrame(p)
1252 {
1253     setupUi(this);
1254 }
1255 
1256 DatabaseWidget::DatabaseWidget(QWidget *p) : QFrame(p)
1257 {
1258     setupUi(this);
1259 }
1260 
1261 LogWidget::LogWidget(QWidget *p) : QFrame(p)
1262 {
1263     setupUi(this);
1264 }