• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • kdeedu
  • Sitemap
  • Contact Us
 

kstars

kstarsdata.cpp

Go to the documentation of this file.
00001 /***************************************************************************
00002                           kstarsdata.cpp  -  K Desktop Planetarium
00003                              -------------------
00004     begin                : Sun Jul 29 2001
00005     copyright            : (C) 2001 by Heiko Evermann
00006     email                : heiko@evermann.de
00007  ***************************************************************************/
00008 
00009 /***************************************************************************
00010  *                                                                         *
00011  *   This program is free software; you can redistribute it and/or modify  *
00012  *   it under the terms of the GNU General Public License as published by  *
00013  *   the Free Software Foundation; either version 2 of the License, or     *
00014  *   (at your option) any later version.                                   *
00015  *                                                                         *
00016  ***************************************************************************/
00017 
00018 #include "kstarsdata.h"
00019 
00020 #include <QApplication>
00021 #include <QRegExp>
00022 #include <QDir>
00023 #include <QFileInfo>
00024 #include <QTextStream>
00025 
00026 #include <kcomponentdata.h>
00027 #include <kmessagebox.h>
00028 #include <kdebug.h>
00029 #include <klocale.h>
00030 #include <kstandarddirs.h>
00031 
00032 #include "Options.h"
00033 #include "dms.h"
00034 #include "skymap.h"
00035 #include "ksutils.h"
00036 #include "ksfilereader.h"
00037 #include "ksnumbers.h"
00038 #include "skypoint.h"
00039 #include "skyobject.h"
00040 
00041 #include "simclock.h"
00042 #include "timezonerule.h"
00043 
00044 #include <config-kstars.h>
00045 
00046 #ifdef HAVE_INDI_H
00047 #include "indidriver.h"
00048 #include "lilxml.h"
00049 #include "indistd.h"
00050 #endif
00051 
00052 #include "detaildialog.h"
00053 
00054 //Initialize static members
00055 QList<GeoLocation*> KStarsData::geoList = QList<GeoLocation*>();
00056 
00057 QMap<QString, TimeZoneRule> KStarsData::Rulebook = QMap<QString, TimeZoneRule>();
00058 
00059 int KStarsData::objects = 0;
00060 
00061 KStarsData* KStarsData::pinstance = 0;
00062 
00063 KStarsData* KStarsData::Create( KStars* kstars )
00064 {
00065     if ( pinstance ) delete pinstance;
00066     pinstance = new KStarsData( kstars );
00067     return pinstance;
00068 }
00069 
00070 KStarsData* KStarsData::Instance( )
00071 {
00072     return pinstance;
00073 }
00074 
00075 
00076 KStarsData::KStarsData(KStars* kstars) : locale(0),
00077         LST(0), HourAngle(0), m_kstars(kstars),
00078         m_preUpdateID(0), m_preUpdateNumID(0),
00079         m_preUpdateNum( J2000 ), m_updateNum( J2000 )
00080 {
00081     startupComplete = false;
00082     objects++;
00083 
00084     TypeName[0] = i18n( "star" );
00085     TypeName[1] = i18n( "multiple star" );
00086     TypeName[2] = i18n( "planet" );
00087     TypeName[3] = i18n( "open cluster" );
00088     TypeName[4] = i18n( "globular cluster" );
00089     TypeName[5] = i18n( "gaseous nebula" );
00090     TypeName[6] = i18n( "planetary nebula" );
00091     TypeName[7] = i18n( "supernova remnant" );
00092     TypeName[8] = i18n( "galaxy" );
00093     TypeName[9] = i18n( "comet" );
00094     TypeName[10] = i18n( "asteroid" );
00095     TypeName[11] = i18n( "constellation" );
00096 
00097     //standard directories and locale objects
00098     locale = new KLocale( "kstars" );
00099 
00100     m_SkyComposite = new SkyMapComposite( 0, this );
00101 
00102     //Instantiate LST and HourAngle
00103     LST = new dms();
00104     HourAngle = new dms();
00105 
00106     //initialize FOV symbol
00107     fovSymbol = FOV();
00108 
00109     // at startup times run forward
00110     setTimeDirection( 0.0 );
00111 
00112     //The StoredDate is used when saving user settings in a script; initialize to invalid date
00113     StoredDate = KDateTime();
00114 
00115     temporaryTrail = false;
00116 }
00117 
00118 KStarsData::~KStarsData() {
00119     //FIXME: Do we still need this?
00120     objects--; //the number of existing KStarsData objects
00121 
00122     //FIXME: Verify list of deletes
00123     delete locale;
00124     delete LST;
00125     delete HourAngle;
00126 
00127     while ( ! geoList.isEmpty() )
00128         delete geoList.takeFirst();
00129 
00130     while ( !VariableStarsList.isEmpty())
00131         delete VariableStarsList.takeFirst();
00132 
00133     while ( !INDIHostsList.isEmpty())
00134         delete INDIHostsList.takeFirst();
00135 
00136     while ( !ADVtreeList.isEmpty())
00137         delete ADVtreeList.takeFirst();
00138 
00139 }
00140 
00141 QString KStarsData::typeName( int i ) {
00142     QString result = i18n( "no type" );
00143     if ( i >= 0 && i < 12 ) result = TypeName[i];
00144 
00145     return result;
00146 }
00147 
00148 void KStarsData::initialize() {
00149     if (startupComplete) return;
00150 
00151     QTimer::singleShot(0, this, SLOT( slotInitialize() ) );
00152     initCounter = 0;
00153 }
00154 
00155 void KStarsData::initError(const QString &s, bool required = false) {
00156     QString message, caption;
00157 
00158     if (required) {
00159         message = i18n( "The file %1 could not be found. "
00160                         "KStars cannot run properly without this file. "
00161                         "To continue loading, place the file in one of the "
00162                         "following locations, then press Retry:\n\n", s )
00163                   + QString( "\t$(KDEDIR)/share/apps/kstars/%1\n" ).arg( s )
00164                   + QString( "\t~/.kde/share/apps/kstars/%1\n\n" ).arg( s )
00165                   + i18n( "Otherwise, press Cancel to shutdown." );
00166         caption = i18n( "Critical File Not Found: %1", s );
00167     } else {
00168         message = i18n( "The file %1 could not be found. "
00169                         "KStars can still run without this file. "
00170                         "However, to avoid seeing this message in the future, you can "
00171                         "place the file in one of the following locations, then press Retry:\n\n", s )
00172                   + QString( "\t$(KDEDIR)/share/apps/kstars/%1\n" ).arg( s )
00173                   + QString( "\t~/.kde/share/apps/kstars/%1\n\n" ).arg( s )
00174                   + i18n( "Otherwise, press Cancel to continue loading without this file.");
00175         caption = i18n( "Non-Critical File Not Found: %1", s );
00176     }
00177 
00178     if ( KMessageBox::warningContinueCancel( 0, message, caption, KGuiItem( i18n( "Retry" ) ) ) == KMessageBox::Continue ) {
00179         initCounter--;
00180         QTimer::singleShot(0, this, SLOT( slotInitialize() ) );
00181     } else {
00182         if (required) {
00183             emit initFinished(false);
00184         } else {
00185             QTimer::singleShot(0, this, SLOT( slotInitialize() ) );
00186         }
00187     }
00188 }
00189 
00190 void KStarsData::slotInitialize() {
00191 
00192     qApp->flush(); // flush all paint events before loading data
00193 
00194     switch ( initCounter )
00195     {
00196     case 0: //Load Time Zone Rules//
00197         emit progressText( i18n("Reading time zone rules") );
00198 
00199         if (objects==1) {
00200             // timezone rules
00201             if ( !readTimeZoneRulebook( ) )
00202                 initError( "TZrules.dat", true );
00203         }
00204 
00205 
00206         break;
00207 
00208     case 1: //Load Cities//
00209         {
00210             if (objects>1) break;
00211 
00212             emit progressText( i18n("Loading city data") );
00213 
00214             if ( !readCityData( ) )
00215                 initError( "Cities.dat", true );
00216 
00217             break;
00218         }
00219 
00220     case 2: //Initialize SkyMapComposite//
00221 
00222         emit progressText(i18n("Loading sky objects" ) );
00223         skyComposite()->init( this );
00224         break;
00225 
00226     case 3: //Load Image URLs//
00227 
00228         emit progressText( i18n("Loading Image URLs" ) );
00229         if ( !readURLData( "image_url.dat", 0 ) ) {
00230             initError( "image_url.dat", false );
00231         }
00232 
00233         break;
00234 
00235     case 4: //Load Information URLs//
00236 
00237         emit progressText( i18n("Loading Information URLs" ) );
00238         if ( !readURLData( "info_url.dat", 1 ) ) {
00239             initError( "info_url.dat", false );
00240         }
00241 
00242         break;
00243 
00244     case 5:
00245         emit progressText( i18n("Loading Variable Stars" ) );
00246         readINDIHosts();
00247         readUserLog();
00248         readVARData();
00249         readADVTreeData();
00250         break;
00251 
00252     default:
00253         startupComplete = true;
00254         emit initFinished(true);
00255         break;
00256     } // switch ( initCounter )
00257 
00258     initCounter++;  // before processEvents!
00259     if(!startupComplete)
00260         QTimer::singleShot(0, this, SLOT( slotInitialize() ) );
00261     qApp->processEvents();
00262 }
00263 
00264 void KStarsData::updateTime( GeoLocation *geo, SkyMap *skymap, const bool automaticDSTchange ) {
00265     // sync LTime with the simulation clock
00266     LTime = geo->UTtoLT( ut() );
00267     syncLST();
00268 
00269     //Only check DST if (1) TZrule is not the empty rule, and (2) if we have crossed
00270     //the DST change date/time.
00271     if ( !geo->tzrule()->isEmptyRule() ) {
00272         if ( TimeRunsForward ) {
00273             // timedirection is forward
00274             // DST change happens if current date is bigger than next calculated dst change
00275             if ( ut() > NextDSTChange ) resetToNewDST(geo, automaticDSTchange);
00276         } else {
00277             // timedirection is backward
00278             // DST change happens if current date is smaller than next calculated dst change
00279             if ( ut() < NextDSTChange ) resetToNewDST(geo, automaticDSTchange);
00280         }
00281     }
00282 
00283     KSNumbers num( ut().djd() );
00284 
00285     //TIMING
00286     QTime t;
00287 
00288     if ( fabs( ut().djd() - LastNumUpdate.djd() ) > 1.0 ) {
00289         LastNumUpdate = ut().djd();
00290         //TIMING
00291         //      t.start();
00292 
00293         m_preUpdateNumID++;
00294         m_preUpdateNum = KSNumbers( num );
00295         skyComposite()->update( this, &num );
00296 
00297         //TIMING
00298         //      kDebug() << QString("SkyMapComposite::update() took %1 ms").arg(t.elapsed());
00299     }
00300 
00301     if ( fabs( ut().djd() - LastPlanetUpdate.djd() ) > 0.01 ) {
00302         LastPlanetUpdate = ut().djd();
00303         //TIMING
00304         //      t.start();
00305 
00306         skyComposite()->updatePlanets( this, &num );
00307 
00308         //TIMING
00309         //      kDebug() << QString("SkyMapComposite::updatePlanets() took %1 ms").arg(t.elapsed());
00310     }
00311 
00312     // Moon moves ~30 arcmin/hr, so update its position every minute.
00313     if ( fabs( ut().djd() - LastMoonUpdate.djd() ) > 0.00069444 ) {
00314         LastMoonUpdate = ut();
00315         //TIMING
00316         //      t.start();
00317 
00318         skyComposite()->updateMoons( this, &num );
00319 
00320         //TIMING
00321         //      kDebug() << QString("SkyMapComposite::updateMoons() took %1 ms").arg(t.elapsed());
00322     }
00323 
00324     //Update Alt/Az coordinates.  Timescale varies with zoom level
00325     //If Clock is in Manual Mode, always update. (?)
00326     if ( fabs( ut().djd() - LastSkyUpdate.djd() ) > 0.1/Options::zoomFactor() || clock()->isManualMode() ) {
00327         LastSkyUpdate = ut();
00328         //TIMING
00329         //      t.start();
00330 
00331         m_preUpdateID++;
00332         skyComposite()->update( this ); //omit KSNumbers arg == just update Alt/Az coords
00333 
00334         //Update focus
00335         skymap->updateFocus();
00336 
00337         //TIMING
00338         //      kDebug() << QString("SkyMapComposite::update() for Alt/Az took %1 ms").arg(t.elapsed());
00339 
00340         if ( clock()->isManualMode() )
00341             QTimer::singleShot( 0, skymap, SLOT( forceUpdateNow() ) );
00342         else skymap->forceUpdate();
00343     }
00344 }
00345 
00346 void KStarsData::syncUpdateIDs()
00347 {
00348     m_updateID = m_preUpdateID;
00349     if ( m_updateNumID == m_preUpdateNumID ) return;
00350     m_updateNumID = m_preUpdateNumID;
00351     m_updateNum = KSNumbers( m_preUpdateNum );
00352 }
00353 
00354 void KStarsData::setFullTimeUpdate() {
00355     //Set the update markers to invalid dates to trigger updates in each category
00356     LastSkyUpdate = KDateTime();
00357     LastPlanetUpdate = KDateTime();
00358     LastMoonUpdate = KDateTime();
00359     LastNumUpdate = KDateTime();
00360 }
00361 
00362 void KStarsData::syncLST() {
00363     LST->set( geo()->GSTtoLST( ut().gst() ) );
00364 }
00365 
00366 void KStarsData::changeDateTime( const KStarsDateTime &newDate ) {
00367     //Turn off animated slews for the next time step.
00368     setSnapNextFocus();
00369 
00370     clock()->setUTC( newDate );
00371 
00372     LTime = geo()->UTtoLT( ut() );
00373     //set local sideral time
00374     syncLST();
00375 
00376     //Make sure Numbers, Moon, planets, and sky objects are updated immediately
00377     setFullTimeUpdate();
00378 
00379     // reset tzrules data with new local time and time direction (forward or backward)
00380     geo()->tzrule()->reset_with_ltime(LTime, geo()->TZ0(), isTimeRunningForward() );
00381 
00382     // reset next dst change time
00383     setNextDSTChange( geo()->tzrule()->nextDSTChange() );
00384 }
00385 
00386 void KStarsData::resetToNewDST(const GeoLocation *geo, const bool automaticDSTchange) {
00387     // reset tzrules data with local time, timezone offset and time direction (forward or backward)
00388     // force a DST change with option true for 3. parameter
00389     geo->tzrule()->reset_with_ltime( LTime, geo->TZ0(), TimeRunsForward, automaticDSTchange );
00390     // reset next DST change time
00391     setNextDSTChange( geo->tzrule()->nextDSTChange() );
00392     //reset LTime, because TZoffset has changed
00393     LTime = geo->UTtoLT( ut() );
00394 }
00395 
00396 void KStarsData::setTimeDirection( float scale ) {
00397     TimeRunsForward = ( scale < 0 ? false : true );
00398 }
00399 
00400 GeoLocation* KStarsData::locationNamed( const QString &city, const QString &province, const QString &country ) {
00401     foreach ( GeoLocation *loc, geoList ) {
00402         if ( loc->translatedName() == city &&
00403                 ( province.isEmpty() || loc->translatedProvince() == province ) &&
00404                 ( country.isEmpty() || loc->translatedCountry() == country ) ) {
00405             return loc;
00406         }
00407     }
00408 
00409     return 0;
00410 }
00411 
00412 void KStarsData::setLocationFromOptions() {
00413     setLocation( GeoLocation ( Options::longitude(), Options::latitude(),
00414                                Options::cityName(), Options::provinceName(), Options::countryName(),
00415                                Options::timeZone(), &(Rulebook[ Options::dST() ]), 4, Options::elevation() ) );
00416 }
00417 
00418 void KStarsData::setLocation( const GeoLocation &l ) {
00419     Geo = GeoLocation(l);
00420     if ( Geo.lat()->Degrees() >= 90.0 ) Geo.setLat( 89.99 );
00421     if ( Geo.lat()->Degrees() <= -90.0 ) Geo.setLat( -89.99 );
00422 
00423     //store data in the Options objects
00424     Options::setCityName( Geo.name() );
00425     Options::setProvinceName( Geo.province() );
00426     Options::setCountryName( Geo.country() );
00427     Options::setTimeZone( Geo.TZ0() );
00428     Options::setElevation( Geo.height() );
00429     Options::setLongitude( Geo.lng()->Degrees() );
00430     Options::setLatitude( Geo.lat()->Degrees() );
00431 }
00432 
00433 SkyObject* KStarsData::objectNamed( const QString &name ) {
00434     if ( (name== "star") || (name== "nothing") || name.isEmpty() ) return NULL;
00435 
00436     return skyComposite()->findByName( name );
00437 }
00438 
00439 bool KStarsData::readCityData( void ) {
00440     QFile file;
00441     bool citiesFound = false;
00442 
00443     // begin new code
00444     if ( KSUtils::openDataFile( file, "Cities.dat" ) ) {
00445         KSFileReader fileReader( file ); // close file is included
00446         while ( fileReader.hasMoreLines() ) {
00447             citiesFound |= processCity( fileReader.readLine() );
00448         }
00449         file.close();  // -jbb because I changed KSFileReader.
00450     }
00451     // end new code
00452 
00453     //check for local cities database, but don't require it.
00454     file.setFileName( KStandardDirs::locate( "appdata", "mycities.dat" ) ); //determine filename in local user KDE directory tree.
00455     if ( file.exists() && file.open( QIODevice::ReadOnly ) ) {
00456         QTextStream stream( &file );
00457 
00458         while ( !stream.atEnd() ) {
00459             QString line = stream.readLine();
00460             citiesFound |= processCity( line );
00461         }   // while ( !stream.atEnd() )
00462         file.close();
00463     }   // if ( fileopen() )
00464 
00465     return citiesFound;
00466 }
00467 
00468 bool KStarsData::processCity( const QString& line ) {
00469     QString totalLine;
00470     QString name, province, country;
00471     QStringList fields;
00472     TimeZoneRule *TZrule;
00473     bool intCheck = true;
00474     QChar latsgn, lngsgn;
00475     int lngD, lngM, lngS;
00476     int latD, latM, latS;
00477     double TZ;
00478     float lng, lat;
00479 
00480     totalLine = line;
00481 
00482     // separate fields
00483     fields = line.split( ':' );
00484 
00485     for ( int i=0; i< fields.size(); ++i )
00486         fields[i] = fields[i].trimmed();
00487 
00488     if ( fields.size() < 11 ) {
00489         kDebug()<< i18n( "Cities.dat: Ran out of fields.  Line was:" );
00490         kDebug()<< totalLine.toLocal8Bit();
00491         return false;
00492     } else if ( fields.size() < 12 ) {
00493         // allow old format (without TZ) for mycities.dat
00494         fields.append(QString());
00495         fields.append("--");
00496     } else if ( fields.size() < 13 ) {
00497         // Set TZrule to "--"
00498         fields.append("--");
00499     }
00500 
00501     name = fields[0];
00502     province = fields[1];
00503     country = fields[2];
00504 
00505     latD = fields[3].toInt( &intCheck );
00506     if ( !intCheck ) {
00507         kDebug() << fields[3] << i18n( "\nCities.dat: Bad integer.  Line was:\n" ) << totalLine;
00508         return false;
00509     }
00510 
00511     latM = fields[4].toInt( &intCheck );
00512     if ( !intCheck ) {
00513         kDebug() << fields[4] << i18n( "\nCities.dat: Bad integer.  Line was:\n" ) << totalLine;
00514         return false;
00515     }
00516 
00517     latS = fields[5].toInt( &intCheck );
00518     if ( !intCheck ) {
00519         kDebug() << fields[5] << i18n( "\nCities.dat: Bad integer.  Line was:\n" ) << totalLine;
00520         return false;
00521     }
00522 
00523     QChar ctemp = fields[6].at(0);
00524     latsgn = ctemp;
00525     if (latsgn != 'N' && latsgn != 'S') {
00526         kDebug() << latsgn << i18n( "\nCities.dat: Invalid latitude sign.  Line was:\n" ) << totalLine;
00527         return false;
00528     }
00529 
00530     lngD = fields[7].toInt( &intCheck );
00531     if ( !intCheck ) {
00532         kDebug() << fields[7] << i18n( "\nCities.dat: Bad integer.  Line was:\n" ) << totalLine;
00533         return false;
00534     }
00535 
00536     lngM = fields[8].toInt( &intCheck );
00537     if ( !intCheck ) {
00538         kDebug() << fields[8] << i18n( "\nCities.dat: Bad integer.  Line was:\n" ) << totalLine;
00539         return false;
00540     }
00541 
00542     lngS = fields[9].toInt( &intCheck );
00543     if ( !intCheck ) {
00544         kDebug() << fields[9] << i18n( "\nCities.dat: Bad integer.  Line was:\n" ) << totalLine;
00545         return false;
00546     }
00547 
00548     ctemp = fields[10].at(0);
00549     lngsgn = ctemp;
00550     if (lngsgn != 'E' && lngsgn != 'W') {
00551         kDebug() << latsgn << i18n( "\nCities.dat: Invalid longitude sign.  Line was:\n" ) << totalLine;
00552         return false;
00553     }
00554 
00555     lat = (float)latD + ((float)latM + (float)latS/60.0)/60.0;
00556     lng = (float)lngD + ((float)lngM + (float)lngS/60.0)/60.0;
00557 
00558     if ( latsgn == 'S' ) lat *= -1.0;
00559     if ( lngsgn == 'W' ) lng *= -1.0;
00560 
00561     // find time zone. Use value from Cities.dat if available.
00562     // otherwise use the old approximation: int(lng/15.0);
00563     if ( fields[11].isEmpty() || ('x' == fields[11].at(0)) ) {
00564         TZ = int(lng/15.0);
00565     } else {
00566         bool doubleCheck = true;
00567         TZ = fields[11].toDouble( &doubleCheck);
00568         if ( !doubleCheck ) {
00569             kDebug() << fields[11] << i18n( "\nCities.dat: Bad time zone.  Line was:\n" ) << totalLine;
00570             return false;
00571         }
00572     }
00573 
00574     //last field is the TimeZone Rule ID.
00575     TZrule = &( Rulebook[ fields[12] ] );
00576 
00577     //  if ( fields[12]=="--" )
00578     //      kDebug() << "Empty rule start month: " << TZrule->StartMonth;
00579     geoList.append ( new GeoLocation( lng, lat, name, province, country, TZ, TZrule ));  // appends city names to list
00580     return true;
00581 }
00582 
00583 bool KStarsData::readTimeZoneRulebook( void ) {
00584     QFile file;
00585     QString id;
00586 
00587     if ( KSUtils::openDataFile( file, "TZrules.dat" ) ) {
00588         QTextStream stream( &file );
00589 
00590         while ( !stream.atEnd() ) {
00591             QString line = stream.readLine().trimmed();
00592             if ( line.length() && !line.startsWith('#') ) { //ignore commented and blank lines
00593                 QStringList fields = line.split( ' ', QString::SkipEmptyParts );
00594                 id = fields[0];
00595                 QTime stime = QTime( fields[3].left( fields[3].indexOf(':')).toInt() ,
00596                                      fields[3].mid( fields[3].indexOf(':')+1, fields[3].length()).toInt() );
00597                 QTime rtime = QTime( fields[6].left( fields[6].indexOf(':')).toInt(),
00598                                      fields[6].mid( fields[6].indexOf(':')+1, fields[6].length()).toInt() );
00599 
00600                 Rulebook[ id ] = TimeZoneRule( fields[1], fields[2], stime, fields[4], fields[5], rtime );
00601             }
00602         }
00603         return true;
00604     } else {
00605         return false;
00606     }
00607 }
00608 
00609 bool KStarsData::openUrlFile(const QString &urlfile, QFile & file) {
00610     //QFile file;
00611     QString localFile;
00612     bool fileFound = false;
00613     QFile localeFile;
00614 
00615     if ( locale->language() != "en_US" )
00616         localFile = locale->language() + '/' + urlfile;
00617 
00618     if ( ! localFile.isEmpty() && KSUtils::openDataFile( file, localFile ) ) {
00619         fileFound = true;
00620     } else {
00621         // Try to load locale file, if not successful, load regular urlfile and then copy it to locale.
00622         file.setFileName( KStandardDirs::locateLocal( "appdata", urlfile ) );
00623         if ( file.open( QIODevice::ReadOnly ) ) {
00624             //local file found.  Now, if global file has newer timestamp, then merge the two files.
00625             //First load local file into QStringList
00626             bool newDataFound( false );
00627             QStringList urlData;
00628             QTextStream lStream( &file );
00629             while ( ! lStream.atEnd() ) urlData.append( lStream.readLine() );
00630 
00631             //Find global file(s) in findAllResources() list.
00632             QFileInfo fi_local( file.fileName() );
00633             QStringList flist = KGlobal::mainComponent().dirs()->findAllResources( "appdata", urlfile );
00634             for ( int i=0; i< flist.size(); i++ ) {
00635                 if ( flist[i] != file.fileName() ) {
00636                     QFileInfo fi_global( flist[i] );
00637 
00638                     //Is this global file newer than the local file?
00639                     if ( fi_global.lastModified() > fi_local.lastModified() ) {
00640                         //Global file has newer timestamp than local.  Add lines in global file that don't already exist in local file.
00641                         //be smart about this; in some cases the URL is updated but the image is probably the same if its
00642                         //label string is the same.  So only check strings up to last ":"
00643                         QFile globalFile( flist[i] );
00644                         if ( globalFile.open( QIODevice::ReadOnly ) ) {
00645                             QTextStream gStream( &globalFile );
00646                             while ( ! gStream.atEnd() ) {
00647                                 QString line = gStream.readLine();
00648 
00649                                 //If global-file line begins with "XXX:" then this line should be removed from the local file.
00650                                 if ( line.startsWith(QLatin1String("XXX:"))  && urlData.contains( line.mid( 4 ) ) ) {
00651                                     urlData.removeAt( urlData.indexOf( line.mid( 4 ) ) );
00652                                 } else {
00653                                     //does local file contain the current global file line, up to second ':' ?
00654 
00655                                     bool linefound( false );
00656                                     for ( int j=0; j< urlData.size(); ++j ) {
00657                                         if ( urlData[j].contains( line.left( line.indexOf( ':', line.indexOf( ':' ) + 1 ) ) ) ) {
00658                                             //replace line in urlData with its equivalent in the newer global file.
00659                                             urlData.replace( j, line );
00660                                             if ( !newDataFound ) newDataFound = true;
00661                                             linefound = true;
00662                                             break;
00663                                         }
00664                                     }
00665                                     if ( ! linefound ) {
00666                                         urlData.append( line );
00667                                         if ( !newDataFound ) newDataFound = true;
00668                                     }
00669                                 }
00670                             }
00671                         }
00672                     }
00673                 }
00674             }
00675 
00676             file.close();
00677 
00678             //(possibly) write appended local file
00679             if ( newDataFound ) {
00680                 if ( file.open( QIODevice::WriteOnly ) ) {
00681                     QTextStream outStream( &file );
00682                     for ( int i=0; i<urlData.size(); i++ ) {
00683                         outStream << urlData[i] << endl;
00684                     }
00685                     file.close();
00686                 }
00687             }
00688 
00689             if ( file.open( QIODevice::ReadOnly ) ) fileFound = true;
00690 
00691         } else {
00692             if ( KSUtils::openDataFile( file, urlfile ) ) {
00693                 if ( locale->language() != "en_US" ) kDebug() << i18n( "No localized URL file; using default English file." );
00694                 // we found urlfile, we need to copy it to locale
00695                 localeFile.setFileName( KStandardDirs::locateLocal( "appdata", urlfile ) );
00696                 if (localeFile.open(QIODevice::WriteOnly)) {
00697                     QTextStream readStream(&file);
00698                     QTextStream writeStream(&localeFile);
00699                     while ( ! readStream.atEnd() ) {
00700                         QString line = readStream.readLine();
00701                         if ( !line.startsWith("XXX:") ) //do not write "deleted" lines
00702                             writeStream << line << endl;
00703                     }
00704 
00705                     localeFile.close();
00706                     file.reset();
00707                 } else {
00708                     kDebug() << i18n( "Failed to copy default URL file to locale folder, modifying default object links is not possible" );
00709                 }
00710                 fileFound = true;
00711             }
00712         }
00713     }
00714     return fileFound;
00715 }
00716 
00717 bool KStarsData::readURLData( const QString &urlfile, int type, bool deepOnly ) {
00718     QFile file;
00719     if (!openUrlFile(urlfile, file)) return false;
00720 
00721     QTextStream stream(&file);
00722 
00723     while ( !stream.atEnd() ) {
00724         QString line = stream.readLine();
00725 
00726         //ignore comment lines
00727         if ( !line.startsWith('#') ) {
00728             int idx = line.indexOf(':');
00729             QString name = line.left( idx );
00730             QString sub = line.mid( idx + 1 );
00731             idx = sub.indexOf(':');
00732             QString title = sub.left( idx );
00733             QString url = sub.mid( idx + 1 );
00734             SkyObject *o = skyComposite()->findByName(name);
00735 
00736             if ( !o ) {
00737                 kWarning() << i18n( "Object named %1 not found", name ) ;
00738             } else {
00739                 if ( ! deepOnly || ( o->type() > 2 && o->type() < 9 ) ) {
00740                     if ( type==0 ) { //image URL
00741                         o->ImageList().append( url );
00742                         o->ImageTitle().append( title );
00743                     } else if ( type==1 ) { //info URL
00744                         o->InfoList().append( url );
00745                         o->InfoTitle().append( title );
00746                     }
00747                 }
00748             }
00749         }
00750     }
00751     file.close();
00752     return true;
00753 }
00754 
00755 bool KStarsData::readUserLog(void)
00756 {
00757     QFile file;
00758     QString buffer;
00759     QString sub, name, data;
00760 
00761     if (!KSUtils::openDataFile( file, "userlog.dat" )) return false;
00762 
00763     QTextStream stream(&file);
00764 
00765     if (!stream.atEnd()) buffer = stream.readAll();
00766 
00767     while (!buffer.isEmpty()) {
00768         int startIndex, endIndex;
00769 
00770         startIndex = buffer.indexOf(QLatin1String("[KSLABEL:"));
00771         sub = buffer.mid(startIndex);
00772         endIndex = sub.indexOf(QLatin1String("[KSLogEnd]"));
00773 
00774         // Read name after KSLABEL identifer
00775         name = sub.mid(startIndex + 9, sub.indexOf(']') - (startIndex + 9));
00776         // Read data and skip new line
00777         data   = sub.mid(sub.indexOf(']') + 2, endIndex - (sub.indexOf(']') + 2));
00778         buffer = buffer.mid(endIndex + 11);
00779 
00780         //Find the sky object named 'name'.
00781         //Note that ObjectNameList::find() looks for the ascii representation
00782         //of star genetive names, so stars are identified that way in the user log.
00783         SkyObject *o = skyComposite()->findByName(name);
00784         if ( !o ) {
00785             kWarning() << name << " not found" ;
00786         } else {
00787             o->userLog() = data;
00788         }
00789 
00790     } // end while
00791     file.close();
00792     return true;
00793 }
00794 
00795 bool KStarsData::readADVTreeData(void)
00796 {
00797     QFile file;
00798     QString Interface;
00799     QString Name, Link, subName;
00800 
00801     if (!KSUtils::openDataFile(file, "advinterface.dat"))
00802         return false;
00803 
00804     QTextStream stream(&file);
00805     QString Line;
00806 
00807     while  (!stream.atEnd())
00808     {
00809         int Type, interfaceIndex;
00810 
00811         Line = stream.readLine();
00812 
00813         if (Line.startsWith(QLatin1String("[KSLABEL]")))
00814         {
00815             Name = Line.mid(9);
00816             Type = 0;
00817         }
00818         else if (Line.startsWith(QLatin1String("[END]")))
00819             Type = 1;
00820         else if (Line.startsWith(QLatin1String("[KSINTERFACE]")))
00821         {
00822             Interface = Line.mid(13);
00823             continue;
00824         }
00825 
00826         else
00827         {
00828             int idx = Line.indexOf(':');
00829             Name = Line.left(idx);
00830             Link = Line.mid(idx + 1);
00831 
00832             // Link is empty, using Interface instead
00833             if (Link.isEmpty())
00834             {
00835                 Link = Interface;
00836                 subName = Name;
00837                 interfaceIndex = Link.indexOf(QLatin1String("KSINTERFACE"));
00838                 Link.remove(interfaceIndex, 11);
00839                 Link = Link.insert(interfaceIndex, subName.replace(' ', '+'));
00840 
00841             }
00842 
00843             Type = 2;
00844         }
00845 
00846         ADVTreeData *ADVData = new ADVTreeData;
00847 
00848         ADVData->Name = Name;
00849         ADVData->Link = Link;
00850         ADVData->Type = Type;
00851 
00852         ADVtreeList.append(ADVData);
00853     }
00854 
00855     return true;
00856 }
00857 
00858 bool KStarsData::readVARData(void)
00859 {
00860     QString varFile("valaav.txt");
00861     QFile localeFile;
00862     QFile file;
00863 
00864     file.setFileName( KStandardDirs::locateLocal( "appdata", varFile ) );
00865     if ( !file.open( QIODevice::ReadOnly ) )
00866     {
00867         // Open default variable stars file
00868         if ( KSUtils::openDataFile( file, varFile ) )
00869         {
00870             // we found urlfile, we need to copy it to locale
00871             localeFile.setFileName( KStandardDirs::locateLocal( "appdata", varFile ) );
00872 
00873             if (localeFile.open(QIODevice::WriteOnly))
00874             {
00875                 QTextStream readStream(&file);
00876                 QTextStream writeStream(&localeFile);
00877                 writeStream <<  readStream.readAll();
00878                 localeFile.close();
00879                 file.reset();
00880             }
00881         }
00882         else
00883             return false;
00884     }
00885 
00886 
00887     QTextStream stream(&file);
00888     stream.readLine();
00889 
00890     QString Name, Designation, Line;
00891     while  (!stream.atEnd())
00892     {
00893         Line = stream.readLine();
00894 
00895         if (Line.startsWith('*'))
00896             break;
00897 
00898         Designation = Line.left(8).trimmed();
00899         Name        = Line.mid(10,20).simplified();
00900 
00901         VariableStarInfo *VInfo = new VariableStarInfo;
00902 
00903         VInfo->Designation = Designation;
00904         VInfo->Name        = Name;
00905         VariableStarsList.append(VInfo);
00906     }
00907 
00908     return true;
00909 }
00910 
00911 
00912 bool KStarsData::readINDIHosts(void)
00913 {
00914 #ifdef HAVE_INDI_H
00915     QString indiFile("indihosts.xml");
00916     QFile localeFile;
00917     QFile file;
00918     char errmsg[1024];
00919     char c;
00920     LilXML *xmlParser = newLilXML();
00921     XMLEle *root = NULL;
00922     XMLAtt *ap;
00923 
00924     file.setFileName( KStandardDirs::locate( "appdata", indiFile ) );
00925     if ( file.fileName().isEmpty() || !file.open( QIODevice::ReadOnly ) )
00926         return false;
00927 
00928     while ( file.getChar( &c ) )
00929     {
00930         root = readXMLEle(xmlParser, c, errmsg);
00931 
00932         if (root)
00933         {
00934             // Get host name
00935             ap = findXMLAtt(root, "name");
00936             if (!ap) {
00937                 delLilXML(xmlParser);
00938                 return false;
00939             }
00940 
00941             INDIHostsInfo *VInfo = new INDIHostsInfo;
00942             VInfo->name = QString(valuXMLAtt(ap));
00943 
00944             // Get host name
00945             ap = findXMLAtt(root, "hostname");
00946 
00947             if (!ap) {
00948                 delete VInfo;