File indexing completed on 2024-03-24 15:17:22

0001 /*
0002     SPDX-FileCopyrightText: 2016 Artem Fedoskin <afedoskin3@gmail.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "detaildialoglite.h"
0008 
0009 #include "constellationboundarylines.h"
0010 #include "deepskyobject.h"
0011 #include "kspaths.h"
0012 #include "ksutils.h"
0013 #include "Options.h"
0014 #include "skymapcomposite.h"
0015 #include "skymaplite.h"
0016 #include "starobject.h"
0017 #include "kstarslite/skyobjectlite.h"
0018 #include "skyobjects/ksasteroid.h"
0019 #include "skyobjects/kscomet.h"
0020 #include "skyobjects/ksmoon.h"
0021 #include "skyobjects/ksplanetbase.h"
0022 #include "skyobjects/supernova.h"
0023 
0024 #include <QDesktopServices>
0025 #include <QTemporaryFile>
0026 
0027 DetailDialogLite::DetailDialogLite()
0028 {
0029     setProperty("isLinksOn", true);
0030     setProperty("isLogOn", true);
0031 }
0032 
0033 void DetailDialogLite::initialize()
0034 {
0035     connect(SkyMapLite::Instance(), SIGNAL(objectLiteChanged()), this, SLOT(createGeneralTab()));
0036     connect(SkyMapLite::Instance(), SIGNAL(objectLiteChanged()), this, SLOT(createPositionTab()));
0037     connect(SkyMapLite::Instance(), SIGNAL(objectLiteChanged()), this, SLOT(createLogTab()));
0038     connect(SkyMapLite::Instance(), SIGNAL(objectLiteChanged()), this, SLOT(createLinksTab()));
0039 }
0040 
0041 void DetailDialogLite::createGeneralTab()
0042 {
0043     SkyObject *selectedObject = SkyMapLite::Instance()->getClickedObjectLite()->getObject();
0044 
0045     // Stuff that should be visible only for specific types of objects
0046     setProperty("illumination", ""); // Only shown for the moon
0047     setProperty("BVindex", "");      // Only shown for stars
0048     setupThumbnail();
0049 
0050     //Fill in the data fields
0051     //Contents depend on type of object
0052     QString objecttyp, str;
0053 
0054     switch (selectedObject->type())
0055     {
0056         case SkyObject::STAR:
0057         {
0058             StarObject *s = (StarObject *)selectedObject;
0059 
0060             if (s->getHDIndex())
0061             {
0062                 setProperty("name", (QString("%1, HD %2").arg(s->longname()).arg(s->getHDIndex())));
0063             }
0064             else
0065             {
0066                 setProperty("name", s->longname());
0067             }
0068 
0069             objecttyp = s->sptype() + ' ' + i18n("star");
0070             setProperty("magnitude", i18nc("number in magnitudes", "%1 mag",
0071                                            QLocale().toString(s->mag(), 'f', 2))); //show to hundredth place
0072 
0073             if (s->getBVIndex() < 30.)
0074             {
0075                 setProperty("BVindex", QString::number(s->getBVIndex(), 'f', 2));
0076             }
0077 
0078             //distance
0079             if (s->distance() > 2000. || s->distance() < 0.) // parallax < 0.5 mas
0080             {
0081                 setProperty("distance", (QString(i18nc("larger than 2000 parsecs", "> 2000 pc"))));
0082             }
0083             else if (s->distance() > 50.) //show to nearest integer
0084             {
0085                 setProperty("distance",
0086                             (i18nc("number in parsecs", "%1 pc", QLocale().toString(s->distance(), 'f', 0))));
0087             }
0088             else if (s->distance() > 10.0) //show to tenths place
0089             {
0090                 setProperty("distance",
0091                             (i18nc("number in parsecs", "%1 pc", QLocale().toString(s->distance(), 'f', 1))));
0092             }
0093             else //show to hundredths place
0094             {
0095                 setProperty("distance",
0096                             (i18nc("number in parsecs", "%1 pc", QLocale().toString(s->distance(), 'f', 2))));
0097             }
0098 
0099             //Note multiplicity/variability in angular size label
0100             setProperty("angSize", QString());
0101             if (s->isMultiple() && s->isVariable())
0102             {
0103                 QString multiple = QString(i18nc("the star is a multiple star", "multiple") + ',');
0104                 setProperty("angSize", QString(multiple + '\n' + (i18nc("the star is a variable star", "variable"))));
0105             }
0106             else if (s->isMultiple())
0107             {
0108                 setProperty("angSize", i18nc("the star is a multiple star", "multiple"));
0109             }
0110             else if (s->isVariable())
0111             {
0112                 setProperty("angSize", (i18nc("the star is a variable star", "variable")));
0113             }
0114 
0115             break; //end of stars case
0116         }
0117         case SkyObject::ASTEROID: //[fall through to planets]
0118         case SkyObject::COMET:    //[fall through to planets]
0119         case SkyObject::MOON:     //[fall through to planets]
0120         case SkyObject::PLANET:
0121         {
0122             KSPlanetBase *ps = (KSPlanetBase *)selectedObject;
0123 
0124             setProperty("name", ps->longname());
0125 
0126             //Type is "G5 star" for Sun
0127             if (ps->name() == "Sun")
0128             {
0129                 objecttyp = i18n("G5 star");
0130             }
0131             else if (ps->name() == "Moon")
0132             {
0133                 objecttyp = ps->translatedName();
0134             }
0135             else if (ps->name() == i18n("Pluto") || ps->name() == "Ceres" ||
0136                      ps->name() == "Eris") // TODO: Check if Ceres / Eris have translations and i18n() them
0137             {
0138                 objecttyp = i18n("Dwarf planet");
0139             }
0140             else
0141             {
0142                 objecttyp = ps->typeName();
0143             }
0144 
0145             //The moon displays illumination fraction and updateMag is called to calculate moon's current magnitude
0146             if (selectedObject->name() == "Moon")
0147             {
0148                 setProperty(
0149                     "illumination",
0150                     (QString("%1 %").arg(QLocale().toString(((KSMoon *)selectedObject)->illum() * 100., 'f', 0))));
0151                 ((KSMoon *)selectedObject)->updateMag();
0152             }
0153 
0154             // JM: Shouldn't we use the calculated magnitude? Disabling the following
0155             /*
0156                     if(selectedObject->type() == SkyObject::COMET){
0157                         Data->Magnitude->setText(i18nc("number in magnitudes", "%1 mag",
0158                                                  QLocale().toString( ((KSComet *)selectedObject)->getTotalMagnitudeParameter(), 'f', 2)));  //show to hundredth place
0159 
0160                     }
0161                     else{*/
0162             setProperty("magnitude", (i18nc("number in magnitudes", "%1 mag",
0163                                             QLocale().toString(ps->mag(), 'f', 2)))); //show to hundredth place
0164             //}
0165 
0166             //Distance from Earth.  The moon requires a unit conversion
0167             if (ps->name() == "Moon")
0168             {
0169                 setProperty("distance", (i18nc("distance in kilometers", "%1 km",
0170                                                QLocale().toString(ps->rearth() * AU_KM, 'f', 2))));
0171             }
0172             else
0173             {
0174                 setProperty("distance", (i18nc("distance in Astronomical Units", "%1 AU",
0175                                                QLocale().toString(ps->rearth(), 'f', 3))));
0176             }
0177 
0178             //Angular size; moon and sun in arcmin, others in arcsec
0179             if (ps->angSize())
0180             {
0181                 if (ps->name() == "Sun" || ps->name() == "Moon")
0182                 {
0183                     setProperty(
0184                         "angSize",
0185                         (i18nc(
0186                             "angular size in arcminutes", "%1 arcmin",
0187                             QLocale().toString(
0188                                 ps->angSize(), 'f',
0189                                 1)))); // Needn't be a plural form because sun / moon will never contract to 1 arcminute
0190                 }
0191                 else
0192                 {
0193                     setProperty("angSize", i18nc("angular size in arcseconds", "%1 arcsec",
0194                                                  QLocale().toString(ps->angSize() * 60.0, 'f', 1)));
0195                 }
0196             }
0197             else
0198             {
0199                 setProperty("angSize", "--");
0200             }
0201 
0202             break; //end of planets/comets/asteroids case
0203         }
0204         case SkyObject::SUPERNOVA:
0205         {
0206             Supernova *sup = (Supernova *)selectedObject;
0207 
0208             objecttyp = i18n("Supernova");
0209             setProperty("name", sup->name());
0210             setProperty("magnitude", (i18nc("number in magnitudes", "%1 mag", QLocale().toString(sup->mag(), 'f', 2))));
0211             setProperty("distance", "---");
0212             break;
0213         }
0214         default: //deep-sky objects
0215         {
0216             DeepSkyObject *dso = (DeepSkyObject *)selectedObject;
0217 
0218             //Show all names recorded for the object
0219             QStringList nameList;
0220             if (!dso->longname().isEmpty() && dso->longname() != dso->name())
0221             {
0222                 nameList.append(dso->translatedLongName());
0223                 nameList.append(dso->translatedName());
0224             }
0225             else
0226             {
0227                 nameList.append(dso->translatedName());
0228             }
0229 
0230             if (!dso->translatedName2().isEmpty())
0231             {
0232                 nameList.append(dso->translatedName2());
0233             }
0234 
0235             if (dso->ugc() != 0)
0236             {
0237                 nameList.append(QString("UGC %1").arg(dso->ugc()));
0238             }
0239 
0240             if (dso->pgc() != 0)
0241             {
0242                 nameList.append(QString("PGC %1").arg(dso->pgc()));
0243             }
0244 
0245             setProperty("name", nameList.join(","));
0246 
0247             objecttyp = dso->typeName();
0248 
0249             if (dso->type() == SkyObject::RADIO_SOURCE)
0250             {
0251                 //ta->MagLabel->setText(i18nc("integrated flux at a frequency", "Flux(%1):", dso->customCatalog()->fluxFrequency()));
0252                 //Data->Magnitude->setText(i18nc("integrated flux value", "%1 %2",
0253                 //                             QLocale().toString(dso->flux(), 'f', 1), dso->customCatalog()->fluxUnit()));  //show to tenths place
0254             }
0255             else if (dso->mag() > 90.0)
0256             {
0257                 setProperty("magnitude", "--");
0258             }
0259             else
0260             {
0261                 setProperty("magnitude", i18nc("number in magnitudes", "%1 mag",
0262                                                QLocale().toString(dso->mag(), 'f', 1))); //show to tenths place
0263             }
0264 
0265             //No distances at this point...
0266             setProperty("distance", "--");
0267 
0268             //Only show decimal place for small angular sizes
0269             if (dso->a() > 10.0)
0270             {
0271                 setProperty("angSize",
0272                             i18nc("angular size in arcminutes", "%1 arcmin", QLocale().toString(dso->a(), 'f', 0)));
0273             }
0274             else if (dso->a())
0275             {
0276                 setProperty("angSize",
0277                             i18nc("angular size in arcminutes", "%1 arcmin", QLocale().toString(dso->a(), 'f', 1)));
0278             }
0279             else
0280             {
0281                 setProperty("angSize", "--");
0282             }
0283 
0284             break;
0285         }
0286     }
0287 
0288     //Reset advanced properties
0289     setProperty("perihilion", "");
0290     setProperty("orbitID", "");
0291     setProperty("NEO", "");
0292     setProperty("diameter", "");
0293     setProperty("rotation", "");
0294     setProperty("earthMOID", "");
0295     setProperty("orbitClass", "");
0296     setProperty("albedo", "");
0297     setProperty("dimensions", "");
0298     setProperty("period", "");
0299 
0300     // Add specifics data
0301     switch (selectedObject->type())
0302     {
0303         case SkyObject::ASTEROID:
0304         {
0305             KSAsteroid *ast = (KSAsteroid *)selectedObject;
0306 
0307             // Perihelion
0308             str.setNum(ast->getPerihelion());
0309             setProperty("perihelion", QString(str + " AU"));
0310             // Earth MOID
0311             if (ast->getEarthMOID() == 0)
0312                 str = "";
0313             else
0314                 str.setNum(ast->getEarthMOID()).append(" AU");
0315             setProperty("earthMOID", str);
0316             // Orbit ID
0317             setProperty("orbitID", ast->getOrbitID());
0318             // Orbit Class
0319             setProperty("orbitClass", ast->getOrbitClass());
0320             // NEO
0321             if (ast->isNEO())
0322                 setProperty("NEO", "Yes");
0323             else
0324                 setProperty("NEO", "No");
0325             // Albedo
0326             if (ast->getAlbedo() == 0.0)
0327                 str = "";
0328             else
0329                 str.setNum(ast->getAlbedo());
0330             setProperty("albedo", str);
0331             // Diameter
0332             if (ast->getDiameter() == 0.0)
0333                 str = "";
0334             else
0335                 str.setNum(ast->getDiameter()).append(" km");
0336             setProperty("diameter", str);
0337             // Dimensions
0338             if (ast->getDimensions().isEmpty())
0339                 setProperty("dimensions", "");
0340             else
0341                 setProperty("dimensions", QString(ast->getDimensions() + " km"));
0342             // Rotation period
0343             if (ast->getRotationPeriod() == 0.0)
0344                 str = "";
0345             else
0346                 str.setNum(ast->getRotationPeriod()).append(" h");
0347             setProperty("rotation", str);
0348             // Period
0349             if (ast->getPeriod() == 0.0)
0350                 str = "";
0351             else
0352                 str.setNum(ast->getPeriod()).append(" y");
0353             setProperty("period", str);
0354 
0355             break;
0356         }
0357         case SkyObject::COMET:
0358         {
0359             KSComet *com = (KSComet *)selectedObject;
0360 
0361             // Perihelion
0362             str.setNum(com->getPerihelion());
0363             setProperty("perihelion", QString(str + " AU"));
0364             // Earth MOID
0365             if (com->getEarthMOID() == 0)
0366                 str = "";
0367             else
0368                 str.setNum(com->getEarthMOID()).append(" AU");
0369             setProperty("earthMOID", str);
0370             // Orbit ID
0371             setProperty("orbitID", com->getOrbitID());
0372             // Orbit Class
0373             setProperty("orbitClass", com->getOrbitClass());
0374             // NEO
0375             if (com->isNEO())
0376                 setProperty("NEO", "Yes");
0377             else
0378                 setProperty("NEO", "No");
0379             // Albedo
0380             if (com->getAlbedo() == 0.0)
0381                 str = "";
0382             else
0383                 str.setNum(com->getAlbedo());
0384             setProperty("albedo", str);
0385             // Diameter
0386             if (com->getDiameter() == 0.0)
0387                 str = "";
0388             else
0389                 str.setNum(com->getDiameter()).append(" km");
0390             setProperty("diameter", str);
0391             // Dimensions
0392             if (com->getDimensions().isEmpty())
0393                 setProperty("dimensions", "");
0394             else
0395                 setProperty("dimensions", QString(com->getDimensions() + " km"));
0396             // Rotation period
0397             if (com->getRotationPeriod() == 0.0)
0398                 str = "";
0399             else
0400                 str.setNum(com->getRotationPeriod()).append(" h");
0401             setProperty("rotation", str);
0402             // Period
0403             if (com->getPeriod() == 0.0)
0404                 str = "";
0405             else
0406                 str.setNum(com->getPeriod()).append(" y");
0407             setProperty("period", str);
0408 
0409             break;
0410         }
0411     }
0412 
0413     //Common to all types:
0414     QString cname = KStarsData::Instance()->skyComposite()->constellationBoundary()->constellationName(selectedObject);
0415     if (selectedObject->type() != SkyObject::CONSTELLATION)
0416     {
0417         cname = i18nc("%1 type of sky object (planet, asteroid etc), %2 name of a constellation", "%1 in %2", objecttyp,
0418                       cname);
0419     }
0420     setProperty("typeInConstellation", cname);
0421 }
0422 
0423 void DetailDialogLite::createPositionTab()
0424 {
0425     KStarsData *data  = KStarsData::Instance();
0426     KStarsDateTime ut = data->ut();
0427     GeoLocation *geo  = data->geo();
0428 
0429     SkyObject *selectedObject = SkyMapLite::Instance()->getClickedObjectLite()->getObject();
0430 
0431     //Coordinates Section:
0432     //Don't use KLocale::formatNumber() for the epoch string,
0433     //because we don't want a thousands-place separator!
0434     QString sEpoch = QString::number(ut.epoch(), 'f', 1);
0435     //Replace the decimal point with localized decimal symbol
0436     sEpoch.replace('.', QLocale().decimalPoint());
0437 
0438     qDebug() << (selectedObject->deprecess(data->updateNum(), 2451545.0l)).ra0().toHMSString()
0439              << (selectedObject->deprecess(data->updateNum(), 2451545.0l)).dec0().toDMSString() << endl;
0440     //qDebug() << selectedObject->ra().toHMSString() << selectedObject->dec().toDMSString() << endl;
0441     setProperty("RALabel", i18n("RA (%1):", sEpoch));
0442     setProperty("decLabel", i18n("Dec (%1):", sEpoch));
0443     setProperty("RA", selectedObject->ra().toHMSString());
0444     setProperty("dec", selectedObject->dec().toDMSString());
0445 
0446     selectedObject->EquatorialToHorizontal(data->lst(), data->geo()->lat());
0447 
0448     setProperty("az", selectedObject->az().toDMSString());
0449     dms a;
0450 
0451     if (Options::useAltAz())
0452         a = selectedObject->alt();
0453     else
0454         a = selectedObject->altRefracted();
0455     setProperty("alt", a.toDMSString());
0456 
0457     // Display the RA0 and Dec0 for objects that are outside the solar system
0458     if (!selectedObject->isSolarSystem())
0459     {
0460         setProperty("RA0", selectedObject->ra0().toHMSString());
0461         setProperty("dec0", selectedObject->dec0().toDMSString());
0462     }
0463     else
0464     {
0465         setProperty("RA0", "--");
0466         setProperty("dec0", "--");
0467     }
0468 
0469     //Hour Angle can be negative, but dms HMS expressions cannot.
0470     //Here's a kludgy workaround:
0471     dms lst = geo->GSTtoLST(ut.gst());
0472     dms ha(lst.Degrees() - selectedObject->ra().Degrees());
0473     QChar sgn('+');
0474     if (ha.Hours() > 12.0)
0475     {
0476         ha.setH(24.0 - ha.Hours());
0477         sgn = '-';
0478     }
0479     setProperty("HA", QString("%1%2").arg(sgn).arg(ha.toHMSString()));
0480 
0481     //Airmass is approximated as the secant of the zenith distance,
0482     //equivalent to 1./sin(Alt).  Beware of Inf at Alt=0!
0483     if (selectedObject->alt().Degrees() > 0.0)
0484         setProperty("airmass", QLocale().toString(selectedObject->airmass(), 'f', 2));
0485     else
0486         setProperty("airmass", "--");
0487 
0488     //Rise/Set/Transit Section:
0489 
0490     //Prepare time/position variables
0491     QTime rt = selectedObject->riseSetTime(ut, geo, true);   //true = use rise time
0492     dms raz  = selectedObject->riseSetTimeAz(ut, geo, true); //true = use rise time
0493 
0494     //If transit time is before rise time, use transit time for tomorrow
0495     QTime tt = selectedObject->transitTime(ut, geo);
0496     dms talt = selectedObject->transitAltitude(ut, geo);
0497     if (tt < rt)
0498     {
0499         tt   = selectedObject->transitTime(ut.addDays(1), geo);
0500         talt = selectedObject->transitAltitude(ut.addDays(1), geo);
0501     }
0502 
0503     //If set time is before rise time, use set time for tomorrow
0504     QTime st = selectedObject->riseSetTime(ut, geo, false);   //false = use set time
0505     dms saz  = selectedObject->riseSetTimeAz(ut, geo, false); //false = use set time
0506     if (st < rt)
0507     {
0508         st  = selectedObject->riseSetTime(ut.addDays(1), geo, false);   //false = use set time
0509         saz = selectedObject->riseSetTimeAz(ut.addDays(1), geo, false); //false = use set time
0510     }
0511 
0512     if (rt.isValid())
0513     {
0514         setProperty("timeRise", QString().sprintf("%02d:%02d", rt.hour(), rt.minute()));
0515         setProperty("timeSet", QString().sprintf("%02d:%02d", st.hour(), st.minute()));
0516         setProperty("azRise", raz.toDMSString());
0517         setProperty("azSet", saz.toDMSString());
0518     }
0519     else
0520     {
0521         if (selectedObject->alt().Degrees() > 0.0)
0522         {
0523             setProperty("timeRise", i18n("Circumpolar"));
0524             setProperty("timeSet", i18n("Circumpolar"));
0525         }
0526         else
0527         {
0528             setProperty("timeRise", i18n("Never rises"));
0529             setProperty("timeSet", i18n("Never rises"));
0530         }
0531 
0532         setProperty("azRise", i18nc("Not Applicable", "N/A"));
0533         setProperty("azSet", i18nc("Not Applicable", "N/A"));
0534     }
0535 
0536     setProperty("timeTransit", QString().sprintf("%02d:%02d", tt.hour(), tt.minute()));
0537     setProperty("altTransit", talt.toDMSString());
0538 
0539     // Restore the position and other time-dependent parameters
0540     selectedObject->recomputeCoords(ut, geo);
0541 }
0542 
0543 void DetailDialogLite::createLogTab()
0544 {
0545     SkyObject *selectedObject = SkyMapLite::Instance()->getClickedObjectLite()->getObject();
0546     //Don't create a log tab for an unnamed star
0547     if (selectedObject->name() == QString("star"))
0548     {
0549         setProperty("isLogOn", false);
0550         return;
0551     }
0552     setProperty("isLogOn", true);
0553 
0554     if (selectedObject->userLog().isEmpty())
0555     {
0556         setProperty("userLog",
0557                     i18n("Record here observation logs and/or data on %1.", selectedObject->translatedName()));
0558     }
0559     else
0560     {
0561         setProperty("userLog", selectedObject->userLog());
0562     }
0563 
0564     /*//Automatically save the log contents when the widget loses focus
0565     connect( Log->UserLog, SIGNAL(focusOut()), this, SLOT(saveLogData()) );*/
0566 }
0567 
0568 void DetailDialogLite::createLinksTab()
0569 {
0570     SkyObject *selectedObject = SkyMapLite::Instance()->getClickedObjectLite()->getObject();
0571 
0572     //No links for unnamed stars
0573     if (selectedObject->name() == QString("star"))
0574     {
0575         setProperty("isLinksOn", false);
0576         return;
0577     }
0578 
0579     setProperty("isLinksOn", true);
0580 
0581     QStringList newInfoList;
0582     foreach (const QString &s, selectedObject->InfoTitle())
0583         newInfoList.append(i18nc("Image/info menu item (should be translated)", s.toLocal8Bit()));
0584     setProperty("infoTitleList", newInfoList);
0585 
0586     QStringList newImageList;
0587     foreach (const QString &s, selectedObject->ImageTitle())
0588         newImageList.append(i18nc("Image/info menu item (should be translated)", s.toLocal8Bit()));
0589 
0590     setProperty("imageTitleList", newImageList);
0591 }
0592 
0593 void DetailDialogLite::updateLocalDatabase(int type, const QString &search_line, const QString &replace_line)
0594 {
0595     QString TempFileName, file_line;
0596     QFile URLFile;
0597     QTemporaryFile TempFile;
0598     QTextStream *temp_stream = nullptr;
0599     QTextStream *out_stream  = nullptr;
0600     bool replace             = !replace_line.isEmpty();
0601 
0602     if (search_line.isEmpty())
0603         return;
0604 
0605     TempFile.setAutoRemove(false);
0606     TempFile.open();
0607     TempFileName = TempFile.fileName();
0608 
0609     switch (type)
0610     {
0611         // Info Links
0612         case 0:
0613             // Get name for our local info_url file
0614             URLFile.setFileName(QDir(KSPaths::writableLocation(QStandardPaths::AppDataLocation)).filePath("info_url.dat"));
0615             break;
0616 
0617         // Image Links
0618         case 1:
0619             // Get name for our local info_url file
0620             URLFile.setFileName(QDir(KSPaths::writableLocation(QStandardPaths::AppDataLocation)).filePath("image_url.dat"));
0621             break;
0622     }
0623 
0624     if (!URLFile.open(QIODevice::ReadWrite))
0625     {
0626         qDebug() << "DetailDialog: Failed to open " << URLFile.fileName();
0627         qDebug() << "KStars cannot save to user database";
0628         return;
0629     }
0630 
0631     // Copy URL file to temp file
0632     TempFile.write(URLFile.readAll());
0633     //Return pointers to initial positions
0634     TempFile.seek(0);
0635     //Clear URLFile
0636     URLFile.resize(0);
0637 
0638     // Get streams;
0639     temp_stream = new QTextStream(&TempFile);
0640     out_stream  = new QTextStream(&URLFile);
0641 
0642     while (!temp_stream->atEnd())
0643     {
0644         file_line = temp_stream->readLine();
0645         // If we find a match, either replace, or remove (by skipping).
0646         if (file_line == search_line)
0647         {
0648             if (replace)
0649                 (*out_stream) << replace_line << endl;
0650             else
0651                 continue;
0652         }
0653         else
0654             (*out_stream) << file_line << endl;
0655     }
0656 
0657     URLFile.close();
0658     delete temp_stream;
0659     delete out_stream;
0660 }
0661 
0662 void DetailDialogLite::addLink(const QString &url, const QString &desc, bool isImageLink)
0663 {
0664     SkyObject *selectedObject = SkyMapLite::Instance()->getClickedObjectLite()->getObject();
0665 
0666     if (url.isEmpty() || desc.isEmpty())
0667         return; //Do nothing if empty url or desc were provided
0668 
0669     QString entry;
0670     QFile file;
0671 
0672     if (isImageLink)
0673     {
0674         //Add link to object's ImageList, and descriptive text to its ImageTitle list
0675         selectedObject->ImageList().append(url);
0676         selectedObject->ImageTitle().append(desc);
0677 
0678         //Also, update the user's custom image links database
0679         //check for user's image-links database.  If it doesn't exist, create it.
0680         //determine filename in local user KDE directory tree.
0681         file.setFileName(QDir(KSPaths::writableLocation(QStandardPaths::AppDataLocation)).filePath("image_url.dat"));
0682 
0683         if (!file.open(QIODevice::ReadWrite | QIODevice::Append))
0684         {
0685             QString message =
0686                 i18n("Custom image-links file could not be opened.\nLink cannot be recorded for future sessions.");
0687             qDebug() << message;
0688             return;
0689         }
0690         else
0691         {
0692             entry = selectedObject->name() + ':' + desc + ':' + url;
0693             QTextStream stream(&file);
0694             stream << entry << endl;
0695             file.close();
0696             setProperty("imageTitleList", selectedObject->ImageTitle());
0697         }
0698     }
0699     else
0700     {
0701         selectedObject->InfoList().append(url);
0702         selectedObject->InfoTitle().append(desc);
0703 
0704         //check for user's image-links database.  If it doesn't exist, create it.
0705         //determine filename in local user KDE directory tree.
0706         file.setFileName(QDir(KSPaths::writableLocation(QStandardPaths::AppDataLocation)).filePath("info_url.dat"));
0707 
0708         if (!file.open(QIODevice::ReadWrite | QIODevice::Append))
0709         {
0710             QString message = i18n(
0711                 "Custom information-links file could not be opened.\nLink cannot be recorded for future sessions.");
0712             qDebug() << message;
0713             return;
0714         }
0715         else
0716         {
0717             entry = selectedObject->name() + ':' + desc + ':' + url;
0718             QTextStream stream(&file);
0719             stream << entry << endl;
0720             file.close();
0721             setProperty("infoTitleList", selectedObject->InfoTitle());
0722         }
0723     }
0724 }
0725 
0726 void DetailDialogLite::removeLink(int itemIndex, bool isImage)
0727 {
0728     SkyObject *selectedObject = SkyMapLite::Instance()->getClickedObjectLite()->getObject();
0729 
0730     QString currentItemURL, currentItemTitle, LineEntry, TempFileName, FileLine;
0731     QFile URLFile;
0732     QTemporaryFile TempFile;
0733     TempFile.setAutoRemove(false);
0734     TempFile.open();
0735     TempFileName = TempFile.fileName();
0736 
0737     //Check if it is a valid index
0738     if (itemIndex < 0)
0739     {
0740         return;
0741     }
0742     else if (isImage && itemIndex >= selectedObject->ImageTitle().length())
0743     {
0744         return;
0745     }
0746     else if (!isImage && itemIndex >= selectedObject->InfoTitle().length())
0747     {
0748         return;
0749     }
0750     //if (title.isEmpty() || url.isEmpty()) return;
0751 
0752     if (!isImage) //Information
0753     {
0754         currentItemTitle = selectedObject->InfoTitle()[itemIndex];
0755         currentItemURL   = selectedObject->InfoList()[itemIndex];
0756         LineEntry        = selectedObject->name();
0757         LineEntry += ':';
0758         LineEntry += currentItemTitle;
0759         LineEntry += ':';
0760         LineEntry += currentItemURL;
0761     }
0762     else //Image
0763     {
0764         currentItemTitle = selectedObject->ImageTitle()[itemIndex];
0765         currentItemURL   = selectedObject->ImageList()[itemIndex];
0766         LineEntry        = selectedObject->name();
0767         LineEntry += ':';
0768         LineEntry += currentItemTitle;
0769         LineEntry += ':';
0770         LineEntry += currentItemURL;
0771     }
0772 
0773     /*if (KMessageBox::warningContinueCancel( 0, i18n("Are you sure you want to remove the %1 link?", currentItemTitle), i18n("Delete Confirmation"),KStandardGuiItem::del())!=KMessageBox::Continue)
0774         return;*/
0775 
0776     if (isImage)
0777     {
0778         selectedObject->ImageTitle().removeAt(itemIndex);
0779         selectedObject->ImageList().removeAt(itemIndex);
0780     }
0781     else
0782     {
0783         selectedObject->InfoTitle().removeAt(itemIndex);
0784         selectedObject->InfoList().removeAt(itemIndex);
0785     }
0786 
0787     // Remove link from file
0788     updateLocalDatabase(isImage ? 1 : 0, LineEntry);
0789 
0790     setProperty("infoTitleList", selectedObject->InfoTitle());
0791     setProperty("imageTitleList", selectedObject->ImageTitle());
0792 }
0793 
0794 void DetailDialogLite::editLink(int itemIndex, bool isImage, const QString &desc, const QString &url)
0795 {
0796     SkyObject *selectedObject = SkyMapLite::Instance()->getClickedObjectLite()->getObject();
0797 
0798     if (url.isEmpty() || desc.isEmpty())
0799         return; //Do nothing if empty url or desc were provided
0800 
0801     QString search_line, replace_line, currentItemTitle, currentItemURL;
0802 
0803     //Check if it is a valid index
0804     if (itemIndex < 0)
0805     {
0806         return;
0807     }
0808     else if (isImage && itemIndex >= selectedObject->ImageTitle().length())
0809     {
0810         return;
0811     }
0812     else if (!isImage && itemIndex >= selectedObject->InfoTitle().length())
0813     {
0814         return;
0815     }
0816 
0817     if (!isImage) //Information
0818     {
0819         currentItemTitle = selectedObject->InfoTitle()[itemIndex];
0820         currentItemURL   = selectedObject->InfoList()[itemIndex];
0821         search_line      = selectedObject->name();
0822         search_line += ':';
0823         search_line += currentItemTitle;
0824         search_line += ':';
0825         search_line += currentItemURL;
0826     }
0827     else //Image
0828     {
0829         currentItemTitle = selectedObject->ImageTitle()[itemIndex];
0830         currentItemURL   = selectedObject->ImageList()[itemIndex];
0831         search_line      = selectedObject->name();
0832         search_line += ':';
0833         search_line += currentItemTitle;
0834         search_line += ':';
0835         search_line += currentItemURL;
0836     }
0837 
0838     bool go(true);
0839 
0840     // If nothing changed, skip th action
0841     if (url == currentItemURL && desc == currentItemTitle)
0842         go = false;
0843 
0844     if (go)
0845     {
0846         replace_line = selectedObject->name() + ':' + desc + ':' + url;
0847 
0848         // Info Link
0849         if (!isImage)
0850         {
0851             selectedObject->InfoTitle().replace(itemIndex, desc);
0852             selectedObject->InfoList().replace(itemIndex, url);
0853 
0854             // Image Links
0855         }
0856         else
0857         {
0858             selectedObject->ImageTitle().replace(itemIndex, desc);
0859             selectedObject->ImageList().replace(itemIndex, url);
0860         }
0861 
0862         // Update local files
0863         updateLocalDatabase(isImage ? 1 : 0, search_line, replace_line);
0864 
0865         setProperty("infoTitleList", selectedObject->InfoTitle());
0866         setProperty("imageTitleList", selectedObject->ImageTitle());
0867     }
0868 }
0869 
0870 QString DetailDialogLite::getInfoURL(int index)
0871 {
0872     SkyObject *selectedObject = SkyMapLite::Instance()->getClickedObjectLite()->getObject();
0873     QStringList urls          = selectedObject->InfoList();
0874     if (index >= 0 && index < urls.size())
0875     {
0876         return urls[index];
0877     }
0878     else
0879     {
0880         return "";
0881     }
0882 }
0883 
0884 QString DetailDialogLite::getImageURL(int index)
0885 {
0886     SkyObject *selectedObject = SkyMapLite::Instance()->getClickedObjectLite()->getObject();
0887     QStringList urls          = selectedObject->ImageList();
0888     if (index >= 0 && index < urls.size())
0889     {
0890         return urls[index];
0891     }
0892     else
0893     {
0894         return "";
0895     }
0896 }
0897 
0898 void DetailDialogLite::setupThumbnail()
0899 {
0900     SkyObject *selectedObject = SkyMapLite::Instance()->getClickedObjectLite()->getObject();
0901     //No image if object is a star
0902     if (selectedObject->type() == SkyObject::STAR || selectedObject->type() == SkyObject::CATALOG_STAR)
0903     {
0904         /*Thumbnail->scaled( Data->Image->width(), Data->Image->height() );
0905         Thumbnail->fill( Data->DataFrame->palette().color( QPalette::Window ) );
0906         Data->Image->setPixmap( *Thumbnail );*/
0907         setProperty("thumbnail", "");
0908         return;
0909     }
0910 
0911     //Try to load the object's image from disk
0912     //If no image found, load "no image" image
0913     QFile file;
0914     QString fname = "thumb-" + selectedObject->name().toLower().remove(' ') + ".png";
0915     if (KSUtils::openDataFile(file, fname))
0916     {
0917         file.close();
0918         setProperty("thumbnail", file.fileName());
0919     }
0920     else
0921     {
0922         setProperty("thumbnail", "");
0923     }
0924 }
0925 
0926 /*void DetailDialogLite::viewResource(int itemIndex, bool isImage) {
0927     QString url;
0928     if(isImage) {
0929         url = getImageURL(itemIndex);
0930     } else {
0931         url = getInfoURL(itemIndex);
0932     }
0933     QDesktopServices::openUrl(QUrl(url, QUrl::TolerantMode));
0934 }*/
0935 
0936 void DetailDialogLite::saveLogData(const QString &userLog)
0937 {
0938     SkyObject *selectedObject = SkyMapLite::Instance()->getClickedObjectLite()->getObject();
0939 
0940     selectedObject->saveUserLog(userLog);
0941 }