diff --git a/engine/gtfs/gtfsservice.cpp b/engine/gtfs/gtfsservice.cpp index 6bbbbeb..5a1a861 100644 --- a/engine/gtfs/gtfsservice.cpp +++ b/engine/gtfs/gtfsservice.cpp @@ -1,677 +1,680 @@ /* * Copyright 2013 Friedrich Pülz * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2 or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // Own includes #include "gtfsservice.h" #include "serviceprovider.h" #include "serviceproviderdata.h" #include "serviceproviderglobal.h" #include "serviceprovidergtfs.h" // KDE includes #include -#include + #include #include #include #include #include #include // Qt includes #include #include #include +#include +#include const qreal ImportGtfsToDatabaseJob::PROGRESS_PART_FOR_FEED_DOWNLOAD = 0.1; AbstractGtfsDatabaseJob::AbstractGtfsDatabaseJob( const QString &destination, const QString &operation, const QMap< QString, QVariant > ¶meters, QObject *parent ) : ServiceJob(destination, operation, parameters, parent) { Q_ASSERT( qobject_cast(parent) ); Q_ASSERT( qobject_cast(parent->parent()) ); } ImportGtfsToDatabaseJob::ImportGtfsToDatabaseJob( const QString &destination, const QString &operation, const QMap< QString, QVariant > ¶meters, QObject *parent ) : AbstractGtfsDatabaseJob(destination, operation, parameters, parent), m_state(Initializing), m_data(0), m_importer(0), m_onlyGetInformation(false) { setCapabilities( Suspendable | Killable ); QString errorMessage; m_data = ServiceProviderDataReader::read( parameters["serviceProviderId"].toString(), &errorMessage ); if ( !m_data ) { setError( GtfsErrorInvalidProviderId ); setErrorText( errorMessage ); return; } if ( m_data->type() != Enums::GtfsProvider ) { setError( GtfsErrorWrongProviderType ); setErrorText( i18nc("@info/plain", "Not a GTFS provider") ); } } UpdateGtfsToDatabaseJob::UpdateGtfsToDatabaseJob( const QString& destination, const QString &operation, const QMap< QString, QVariant > ¶meters, QObject *parent ) : ImportGtfsToDatabaseJob(destination, operation, parameters, parent) { } ImportGtfsToDatabaseJob::~ImportGtfsToDatabaseJob() { if ( m_importer ) { m_importer->quit(); kDebug() << "Wait 10 second for the import thread to quit..."; m_importer->wait( 10000 ); delete m_importer; } } DeleteGtfsDatabaseJob::DeleteGtfsDatabaseJob( const QString &destination, const QString &operation, const QMap< QString, QVariant > ¶meters, QObject *parent ) : AbstractGtfsDatabaseJob(destination, operation, parameters, parent) { m_serviceProviderId = parameters["serviceProviderId"].toString(); } bool AbstractGtfsDatabaseJob::canImportGtfsFeed() { GtfsService *service = qobject_cast< GtfsService* >( parent() ); Plasma::DataEngine *engine = qobject_cast< Plasma::DataEngine* >( service->parent() ); Q_ASSERT( engine ); // Get the QMetaObject of the engine const QMetaObject *meta = engine->metaObject(); // Find the slot of the engine to start the request const int slotIndex = meta->indexOfSlot( "tryToStartGtfsFeedImportJob(Plasma::ServiceJob*)" ); Q_ASSERT( slotIndex != -1 ); meta->method( slotIndex ).invoke( engine, Qt::DirectConnection, Q_RETURN_ARG(bool, m_canAccessGtfsDatabase), Q_ARG(const Plasma::ServiceJob*, this) ); // If an import is already running for the provider, the operation cannot be executed now, // only updateGtfsFeedInfo can be executed while importing a GTFS feed if ( !m_canAccessGtfsDatabase ) { qWarning() << "The GTFS feed already gets imported"; return false; } return true; } void AbstractGtfsDatabaseJob::start() { QTimer::singleShot( 0, this, SLOT(tryToWork()) ); } void AbstractGtfsDatabaseJob::tryToWork() { if ( error() != NoGtfsError ) { kDebug() << error(); // Error found in constructor, eg. no provider with the given ID found or no GTFS provider setResult( false ); return; } if ( isAccessingGtfsDatabase() && !canImportGtfsFeed() ) { // Cannot start another GTFS database accessing job kDebug() << "Import is already running"; setError( GtfsErrorFeedImportAlreadyRunning ); setErrorText( i18nc("@info/plain", "The GTFS feed already gets imported.") ); setResult( false ); return; } // No problems, do the work now work(); } void ImportGtfsToDatabaseJob::registerAtJobTracker() { KIO::getJobTracker()->registerJob( this ); emitDescription(); } void ImportGtfsToDatabaseJob::emitDescription() { if ( error() != NoGtfsError || !data() ) { kDebug() << error(); // Error found in constructor, eg. no provider with the given ID found or no GTFS provider emit description( this, errorString() ); return; } // Emit a description about what's done in this job const QPair< QString, QString > field1 = qMakePair( i18nc("@info/plain Label for GTFS service provider", "Service Provider"), data()->name() ); const QPair< QString, QString > field2 = qMakePair( i18nc("@info/plain Label for GTFS feed source URLs", "Source"), m_data->feedUrl() ); if ( m_onlyGetInformation ) { emit description( this, i18nc("@info", "Update GTFS feed info"), field1, field2 ); } else { emit description( this, i18nc("@info", "Import GTFS feed"), field1, field2 ); } } void UpdateGtfsToDatabaseJob::emitDescription() { if ( error() != NoGtfsError || !data() ) { kDebug() << error(); // Error found in constructor, eg. no provider with the given ID found or no GTFS provider emit description( this, errorString() ); return; } emit description( this, i18nc("@info", "Updating GTFS feed"), qMakePair(i18nc("@info/plain Label for GTFS service provider", "Service Provider"), data()->name()), qMakePair(i18nc("@info/plain Label for GTFS feed source URLs", "Source"), data()->feedUrl()) ); } void ImportGtfsToDatabaseJob::work() { Q_ASSERT( m_data ); emitDescription(); // Start the job by first requesting GTFS feed information statFeed(); } void UpdateGtfsToDatabaseJob::tryToWork() { QString errorMessage; if ( ServiceProviderGtfs::isGtfsFeedImportFinished(data()->id(), data()->feedUrl(), ServiceProviderGlobal::cache(), &errorMessage) ) { AbstractGtfsDatabaseJob::tryToWork(); } else { setError( GtfsErrorFeedImportRequired ); setErrorText( errorMessage ); setResult( false ); } } void UpdateGtfsToDatabaseJob::work() { QString errorMessage; if ( ServiceProviderGtfs::isGtfsFeedImportFinished(data()->id(), data()->feedUrl(), ServiceProviderGlobal::cache(), &errorMessage) ) { // Emit a description about what's done in this job emitDescription(); setCapabilities( Suspendable | Killable ); // Start the job by first requesting GTFS feed information statFeed(); } else { setError( GtfsErrorFeedImportRequired ); setErrorText( errorMessage ); setResult( false ); } } void DeleteGtfsDatabaseJob::work() { // Close the database before deleting it, // otherwise a newly created database won't get opened, // because the already opened database connection gets used instead GtfsDatabase::closeDatabase( m_serviceProviderId ); // Delete the database file const QString databasePath = GtfsDatabase::databasePath( m_serviceProviderId ); if ( !QFile::remove(databasePath) ) { kDebug() << "Failed to delete GTFS database"; setError( GtfsErrorCannotDeleteDatabase ); setErrorText( i18nc("@info/plain", "The GTFS database could not be deleted.") ); setResult( false ); return; } kDebug() << "Finished deleting GTFS database of" << m_serviceProviderId; // Update the accessor cache file to indicate that the GTFS feed needs to be imported again KConfig config( ServiceProviderGlobal::cacheFileName(), KConfig::SimpleConfig ); KConfigGroup group = config.group( m_serviceProviderId ); KConfigGroup gtfsGroup = group.group( "gtfs" ); gtfsGroup.writeEntry( "feedImportFinished", false ); // Write to disk now, important for the data engine to get the correct state // directly after this job has finished gtfsGroup.sync(); // Finished successfully setResult( true ); } bool ImportGtfsToDatabaseJob::doKill() { if ( m_state == ReadingFeed ) { m_importer->quit(); } m_state = KillingJob; return true; } bool ImportGtfsToDatabaseJob::doSuspend() { if ( m_state == ReadingFeed ) { m_importer->suspend(); } return true; } bool ImportGtfsToDatabaseJob::doResume() { if ( m_state == ReadingFeed ) { m_importer->resume(); } return true; } void ImportGtfsToDatabaseJob::statFeed() { if ( m_state == DownloadingFeed || m_state == ReadingFeed || m_state == StatingFeed ) { kDebug() << "Feed already gets downloaded / was downloaded and gets imported / gets stated"; return; } if ( !m_data ) { // There was an error in the constructor, error already set setResult( false ); return; } kDebug() << "Request GTFS feed information for" << m_data->id(); emit infoMessage( this, i18nc("@info/plain", "Checking GTFS feed source") ); m_state = StatingFeed; QNetworkAccessManager *manager = new QNetworkAccessManager( this ); connect( manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(statFeedFinished(QNetworkReply*)) ); QNetworkRequest request( m_data->feedUrl() ); manager->head( request ); } void ImportGtfsToDatabaseJob::statFeedFinished( QNetworkReply *reply ) { if ( m_state == KillingJob || isSuspended() ) { return; } // Check if there is a valid redirection QString redirectUrl = reply->attribute( QNetworkRequest::RedirectionTargetAttribute ).toString(); if ( reply->error() != QNetworkReply::NoError && reply->header(QNetworkRequest::ContentLengthHeader).toInt() == 0 && reply->url() != m_lastRedirectUrl ) { // Headers were requested, empty result // Now download completely, using get instead of head kDebug() << "Possible redirection, requesting headers lead to an error reply" << reply->url(); m_lastRedirectUrl = reply->url().toString(); reply->manager()->get( QNetworkRequest(reply->url()) ); reply->deleteLater(); return; } else if( !redirectUrl.isEmpty() && redirectUrl != m_lastRedirectUrl ) { // Redirect to redirectUrl, store last redirection kDebug() << "Redirecting to" << redirectUrl; m_lastRedirectUrl = redirectUrl; reply->manager()->head( QNetworkRequest(redirectUrl) ); reply->deleteLater(); return; } // Not redirected anymore m_lastRedirectUrl.clear(); if ( reply->error() == QNetworkReply::NoError ) { QString contentType = reply->header( QNetworkRequest::ContentTypeHeader ).toString(); // If ';' is not included in contentType, QString::left() returns the whole string - const KMimeType::Ptr mimeType = - KMimeType::mimeType( contentType.left(contentType.indexOf(';')) ); - if ( mimeType && mimeType->isValid() ) { - if ( !mimeType->is("application/zip") && !mimeType->is("application/octet-stream") ) { + QMimeDatabase db; + const QMimeType mimeType = db.mimeTypeForName( contentType.left(contentType.indexOf(';')) ); + if ( mimeType.isValid() ) { + if ( !mimeType.inherits("application/zip") && !mimeType.inherits("application/octet-stream") ) { kDebug() << "Invalid mime type:" << reply->header(QNetworkRequest::ContentTypeHeader).toString(); setError( GtfsErrorWrongFeedFormat ); - setErrorText( i18nc("@info/plain", "Wrong GTFS feed format: %1", mimeType->name()) ); + setErrorText( i18nc("@info/plain", "Wrong GTFS feed format: %1", mimeType.name()) ); setResult( false ); return; } } else { kDebug() << "Could not create KMimeType object for" << reply->header(QNetworkRequest::ContentTypeHeader).toString(); } // Use KDateTime and UTC time to not get confused with different locales KDateTime newLastModified = KDateTime::fromString( reply->header(QNetworkRequest::LastModifiedHeader).toString() ).toUtc(); qulonglong newSizeInBytes = reply->header( QNetworkRequest::ContentLengthHeader ).toULongLong(); // emit size and lastModified? // qDebug() << ">>>>> GTFS feed was last modified (UTC):" << newLastModified // << "and it's size is:" << (newSizeInBytes/qreal(1024*1024)) << "MB"; // Read accessor information cache QSharedPointer cache = ServiceProviderGlobal::cache(); QString errorMessage; const bool importFinished = ServiceProviderGtfs::isGtfsFeedImportFinished( data()->id(), data()->feedUrl(), cache, &errorMessage ); KConfigGroup group = cache->group( data()->id() ); KConfigGroup gtfsGroup = group.group( "gtfs" ); KDateTime lastModified = KDateTime::fromString( gtfsGroup.readEntry("feedModifiedTime", QString()) ); qulonglong sizeInBytes = gtfsGroup.readEntry( "feedSizeInBytes", qulonglong(-1) ); gtfsGroup.writeEntry( "feedModifiedTime", newLastModified.toString() ); gtfsGroup.writeEntry( "feedSizeInBytes", newSizeInBytes ); gtfsGroup.writeEntry( "feedUrl", data()->feedUrl() ); // Needed to have the GTFS feed information available directly after this job is finished gtfsGroup.sync(); // Stop here for "updateGtfsFeedInfo" operation if ( m_onlyGetInformation ) { m_state = Ready; setResult( importFinished ); return; } if ( !importFinished ) { qDebug() << errorMessage << m_data->id(); } // qDebug() << "Old last modified:" << lastModified // << "new size:" << (sizeInBytes/qreal(1024*1024)) << "MB"; if ( !importFinished // GTFS import not finished or never started? || (newLastModified.isValid() && lastModified.isValid() && // GTFS feed modified? newLastModified != lastModified) || (newSizeInBytes > 0 && newSizeInBytes != sizeInBytes) // Size changed? || (newSizeInBytes == 0 && !newLastModified.isValid() && // If both are not available... lastModified.daysTo(KDateTime::currentUtcDateTime()) > 7) ) // ...upate weekly { kDebug() << "Download new GTFS feed version for" << m_data->id(); // A newer GTFS feed is available or it was not imported / import did not finish m_state = Initializing; downloadFeed(); } else { // Newest version of the GTFS feed is already downloaded and completely imported m_state = Ready; setResult( true ); } } else { // Track this job to show the error registerAtJobTracker(); kDebug() << "GTFS feed not available: " << m_data->feedUrl() << reply->errorString(); m_state = ErrorDownloadingFeed; setError( GtfsErrorDownloadFailed ); setErrorText( reply->errorString() ); // TODO setResult( false ); } reply->manager()->deleteLater(); reply->deleteLater(); } void ImportGtfsToDatabaseJob::slotSpeed( KJob *job, ulong speed ) { Q_UNUSED( job ); emitSpeed( speed ); } void ImportGtfsToDatabaseJob::downloadFeed() { if ( m_state == DownloadingFeed || m_state == ReadingFeed || m_state == StatingFeed ) { kDebug() << "Feed already gets downloaded / was downloaded and gets imported / gets stated"; return; } if ( m_state == KillingJob || isSuspended() ) { return; } // Track this job at least from now on, // because the download/import can take some time registerAtJobTracker(); kDebug() << "Start GTFS feed import for" << m_data->id(); KTemporaryFile tmpFile; if ( tmpFile.open() ) { kDebug() << "Downloading GTFS feed from" << m_data->feedUrl() << "to" << tmpFile.fileName(); emit infoMessage( this, i18nc("@info/plain", "Downloading GTFS feed") ); m_state = DownloadingFeed; tmpFile.setAutoRemove( false ); // Do not remove the target file while downloading // Set progress to 0 m_progress = 0.0; emitPercent( 0, 1000 ); // KFileItem gtfsFeed( m_info->feedUrl(), QString(), S_IFREG | S_IFSOCK ); // kDebug() << "LAST MODIFIED:" << gtfsFeed.timeString( KFileItem::ModificationTime ); // Update provider cache KConfig config( ServiceProviderGlobal::cacheFileName(), KConfig::SimpleConfig ); KConfigGroup group = config.group( data()->id() ); KConfigGroup gtfsGroup = group.group( "gtfs" ); gtfsGroup.writeEntry( "feedImportFinished", false ); KIO::FileCopyJob *job = KIO::file_copy( m_data->feedUrl(), QUrl(tmpFile.fileName()), -1, KIO::Overwrite | KIO::HideProgressInfo ); connect( job, SIGNAL(result(KJob*)), this, SLOT(feedReceived(KJob*)) ); connect( job, SIGNAL(percent(KJob*,ulong)), this, SLOT(downloadProgress(KJob*,ulong)) ); connect( job, SIGNAL(mimetype(KIO::Job*,QString)), this, SLOT(mimeType(KIO::Job*,QString)) ); connect( job, SIGNAL(totalSize(KJob*,qulonglong)), this, SLOT(totalSize(KJob*,qulonglong)) ); connect( job, SIGNAL(speed(KJob*,ulong)), this, SLOT(slotSpeed(KJob*,ulong)) ); } else { kDebug() << "Could not create a temporary file to download the GTFS feed"; // TODO emit error... } } void ImportGtfsToDatabaseJob::mimeType( KIO::Job *job, const QString &type ) { Q_UNUSED( job ); - const KMimeType::Ptr mimeType = KMimeType::mimeType( type ); - if ( mimeType && mimeType->isValid() ) { - if ( !mimeType->is("application/zip") && !mimeType->is("application/octet-stream") ) { + QMimeDatabase db; + const QMimeType mimeType = db.mimeTypeForName( type ); + if ( mimeType.isValid() ) { + if ( !mimeType.inherits("application/zip") && !mimeType.inherits("application/octet-stream") ) { kDebug() << "Invalid mime type:" << type; setError( GtfsErrorWrongFeedFormat ); - setErrorText( i18nc("@info/plain", "Wrong GTFS feed format: %1", mimeType->name()) ); + setErrorText( i18nc("@info/plain", "Wrong GTFS feed format: %1", mimeType.name()) ); setResult( false ); return; } } else { kDebug() << "Could not create KMimeType object for" << type; } } void ImportGtfsToDatabaseJob::totalSize( KJob* job, qulonglong size ) { Q_UNUSED( job ); KConfig config( ServiceProviderGlobal::cacheFileName(), KConfig::SimpleConfig ); KConfigGroup group = config.group( data()->id() ); KConfigGroup gtfsGroup = group.group( "gtfs" ); gtfsGroup.writeEntry( "feedSizeInBytes", size ); } void ImportGtfsToDatabaseJob::downloadProgress( KJob *job, ulong percent ) { Q_UNUSED( job ); m_progress = (qreal(percent) / 100.0) * PROGRESS_PART_FOR_FEED_DOWNLOAD; // TODO Remove m_progress emitPercent( percent * 10 * PROGRESS_PART_FOR_FEED_DOWNLOAD, 1000 ); } void ImportGtfsToDatabaseJob::feedReceived( KJob *job ) { if ( m_state == KillingJob || isSuspended() ) { return; } // Emit progress for finished download m_progress = PROGRESS_PART_FOR_FEED_DOWNLOAD; emitPercent( 1000 * PROGRESS_PART_FOR_FEED_DOWNLOAD, 1000 ); KIO::FileCopyJob *fileCopyJob = qobject_cast( job ); QString tmpFilePath = fileCopyJob->destUrl().path(); if ( job->error() != 0 ) { kDebug() << "Error downloading GTFS feed:" << job->errorString(); emit infoMessage( this, i18nc("@info/plain", "Error downloading GTFS feed: " "%1", job->errorString()) ); m_state = ErrorDownloadingFeed; if ( !QFile::remove(tmpFilePath) ) { kDebug() << "Could not remove the temporary GTFS feed file"; } setError( GtfsErrorDownloadFailed ); setErrorText( job->errorString() ); // TODO setResult( false ); return; } kDebug() << "GTFS feed received at" << tmpFilePath; // Read feed and write data into the DB m_state = ReadingFeed; emit infoMessage( this, i18nc("@info/plain", "Importing GTFS feed") ); m_importer = new GtfsImporter( m_data->id() ); connect( m_importer, SIGNAL(logMessage(QString)), this, SLOT(logMessage(QString)) ); connect( m_importer, SIGNAL(progress(qreal,QString)), this, SLOT(importerProgress(qreal,QString)) ); connect( m_importer, SIGNAL(finished(GtfsImporter::State,QString)), this, SLOT(importerFinished(GtfsImporter::State,QString)) ); m_importer->startImport( tmpFilePath ); } void ImportGtfsToDatabaseJob::logMessage( const QString &message ) { emit warning( this, message ); } void ImportGtfsToDatabaseJob::importerProgress( qreal importerProgress, const QString ¤tTableName ) { m_progress = PROGRESS_PART_FOR_FEED_DOWNLOAD * (1.0 - importerProgress) + importerProgress; emitPercent( m_progress * 1000, 1000 ); if ( currentTableName != m_lastTableName ) { emit infoMessage( this, i18nc("@info/plain", "Importing GTFS feed (%1)", currentTableName) ); m_lastTableName = currentTableName; } } void ImportGtfsToDatabaseJob::importerFinished( GtfsImporter::State state, const QString &errorText ) { // Remove temporary file if ( m_importer && !QFile::remove(m_importer->sourceFileName()) ) { qWarning() << "Could not remove the temporary GTFS feed file"; } // Update 'feedImportFinished' field in the cache KConfig config( ServiceProviderGlobal::cacheFileName(), KConfig::SimpleConfig ); KConfigGroup group = config.group( data()->id() ); KConfigGroup gtfsGroup = group.group( "gtfs" ); gtfsGroup.writeEntry( "feedImportFinished", state != GtfsImporter::FatalError ); // Write to disk now, important for the data engine to get the correct state // directly after this job has finished gtfsGroup.sync(); // Emit progress with 1.0, ie. finsihed m_progress = 1.0; emitPercent( 1000, 1000 ); kDebug() << "Finished" << state << errorText; // Ignore GtfsImporter::FinishedWithErrors if ( state == GtfsImporter::FatalError ) { m_state = ErrorReadingFeed; kDebug() << "There was an error importing the GTFS feed into the database" << errorText; emit infoMessage( this, errorText ); } else { m_state = Ready; emit infoMessage( this, i18nc("@info/plain", "GTFS feed has been successfully imported") ); } if ( m_importer ) { m_importer->quit(); m_importer->wait(); delete m_importer; m_importer = 0; } if ( m_state == Ready ) { // Update accessor information cache setResult( true ); } else { setError( GtfsErrorImportFailed ); setErrorText( errorText ); setResult( false ); } } QString ImportGtfsToDatabaseJob::serviceProviderId() const { return m_data ? m_data->id() : QString(); } GtfsService::GtfsService( const QString &name, QObject *parent ) : Service(parent), m_name(name) { // This associates the service with the "publictransport.operations" file setName( "publictransport" ); } Plasma::ServiceJob* GtfsService::createJob( const QString &operation, QMap< QString, QVariant > ¶meters ) { // Check if a valid provider ID is available in the parameters const QString providerId = parameters["serviceProviderId"].toString(); if ( providerId.isEmpty() ) { qWarning() << "No 'serviceProviderId' parameter given to GTFS service operation"; return 0; } if ( operation == QLatin1String("updateGtfsDatabase") ) { UpdateGtfsToDatabaseJob *updateJob = new UpdateGtfsToDatabaseJob( "PublicTransport", operation, parameters, this ); return updateJob; } else if ( operation == QLatin1String("importGtfsFeed") ) { ImportGtfsToDatabaseJob *importJob = new ImportGtfsToDatabaseJob( "PublicTransport", operation, parameters, this ); // Directly register import jobs, ie. also show "Check Feed Source" importJob->registerAtJobTracker(); return importJob; } else if ( operation == QLatin1String("deleteGtfsDatabase") ) { DeleteGtfsDatabaseJob *deleteJob = new DeleteGtfsDatabaseJob( "PublicTransport", operation, parameters, this ); return deleteJob; } else if ( operation == QLatin1String("updateGtfsFeedInfo") ) { ImportGtfsToDatabaseJob *updateFeedInfoJob = new ImportGtfsToDatabaseJob( "PublicTransport", operation, parameters, this ); updateFeedInfoJob->setOnlyGetInformation( true ); return updateFeedInfoJob; } else { qWarning() << "Operation" << operation << "not supported"; return 0; } } diff --git a/engine/serviceproviderglobal.cpp b/engine/serviceproviderglobal.cpp index b6ef1f1..7a2a200 100644 --- a/engine/serviceproviderglobal.cpp +++ b/engine/serviceproviderglobal.cpp @@ -1,383 +1,385 @@ /* * Copyright 2012 Friedrich Pülz * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2 or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // Header #include "serviceproviderglobal.h" // Own includes #include "config.h" // KDE includes #include #include -#include + #include #include // Qt includes #include #include #include +#include +#include QList< Enums::ServiceProviderType > ServiceProviderGlobal::availableProviderTypes() { QList< Enums::ServiceProviderType > types; #ifdef BUILD_PROVIDER_TYPE_SCRIPT types << Enums::ScriptedProvider; #endif #ifdef BUILD_PROVIDER_TYPE_GTFS types << Enums::GtfsProvider; #endif return types; } bool ServiceProviderGlobal::isProviderTypeAvailable( Enums::ServiceProviderType type ) { switch ( type ) { case Enums::ScriptedProvider: #ifdef BUILD_PROVIDER_TYPE_SCRIPT return true; #else return false; #endif case Enums::GtfsProvider: #ifdef BUILD_PROVIDER_TYPE_GTFS return true; #else return false; #endif case Enums::InvalidProvider: default: return false; } } QString ServiceProviderGlobal::defaultProviderForLocation( const QString &location ) { QStringList locationProviders; const QString subDirectory = installationSubDirectory(); foreach ( const QString &pattern, filePatterns() ) { locationProviders << KGlobal::dirs()->findAllResources( "data", subDirectory + location + '_' + pattern ); } if ( locationProviders.isEmpty() ) { qWarning() << "Couldn't find any providers for location" << location; return QString(); } // Simply return first found provider as default provider return locationProviders.first(); } QString ServiceProviderGlobal::cacheFileName() { return KGlobal::dirs()->saveLocation("data", "plasma_engine_publictransport/").append( QLatin1String("datacache") ); } QSharedPointer< KConfig > ServiceProviderGlobal::cache() { return QSharedPointer< KConfig >( new KConfig(cacheFileName(), KConfig::SimpleConfig) ); } void ServiceProviderGlobal::cleanupCache( const QSharedPointer< KConfig > &_cache ) { QSharedPointer< KConfig > cache = _cache.isNull() ? ServiceProviderGlobal::cache() : _cache; const QStringList installedProviderPaths = ServiceProviderGlobal::installedProviders(); QStringList installedProviderIDs; foreach ( const QString &installedProviderPath, installedProviderPaths ) { installedProviderIDs << idFromFileName( installedProviderPath ); } foreach ( const QString &group, cache->groupList() ) { if ( group != QLatin1String("script") && group != QLatin1String("gtfs") && !installedProviderIDs.contains(group) ) { // Found a group for a provider that is no longer installed kDebug() << "Cleanup cache data for no longer installed provider" << group; clearCache( group, cache, false ); } } cache->sync(); } void ServiceProviderGlobal::clearCache( const QString &providerId, const QSharedPointer< KConfig > &_cache, bool syncCache ) { QSharedPointer< KConfig > cache = _cache.isNull() ? ServiceProviderGlobal::cache() : _cache; if ( !cache->hasGroup(providerId) ) { // No data cached for the provider return; } // Remove all data for the provider from the cache cache->deleteGroup( providerId ); // Remove provider from "usingProviders" lists for included script files KConfigGroup globalScriptGroup = cache->group( "script" ); const QStringList globalScriptGroupNames = globalScriptGroup.groupList(); foreach ( const QString &globalScriptGroupName, globalScriptGroupNames ) { if ( !globalScriptGroupName.startsWith(QLatin1String("include_")) ) { continue; } // Check if the provider to remove from the cache is listed as using the current include KConfigGroup includeFileGroup = globalScriptGroup.group( globalScriptGroupName ); QStringList usingProviders = includeFileGroup.readEntry( "usingProviders", QStringList() ); if ( usingProviders.contains(providerId) ) { // Remove provider from the list of providers using the current include script file usingProviders.removeOne( providerId ); includeFileGroup.writeEntry( "usingProviders", usingProviders ); } } if ( syncCache ) { cache->sync(); } } QString ServiceProviderGlobal::idFromFileName( const QString &serviceProviderFileName ) { // Get the service provider substring from the XML filename, ie. "/path/to/xml/.pts/xml" return QFileInfo( serviceProviderFileName ).baseName(); } QString ServiceProviderGlobal::fileNameFromId( const QString &serviceProviderId ) { const QString subDirectory = installationSubDirectory(); foreach ( const QString &extension, fileExtensions() ) { const QString fileName = KStandardDirs::locate( "data", subDirectory + serviceProviderId + '.' + extension ); if ( !fileName.isEmpty() ) { return fileName; } } // File not found kDebug() << "No service provider plugin found with this ID:" << serviceProviderId; return QString(); } Enums::ServiceProviderType ServiceProviderGlobal::typeFromString( const QString &serviceProviderType ) { QString s = serviceProviderType.toLower(); if ( s == QLatin1String("script") || s == QLatin1String("html") ) // DEPRECATED { return Enums::ScriptedProvider; } else if ( s == QLatin1String("gtfs") ) { return Enums::GtfsProvider; } else { return Enums::InvalidProvider; } } QString ServiceProviderGlobal::typeToString( Enums::ServiceProviderType type ) { switch ( type ) { case Enums::ScriptedProvider: return "script"; case Enums::GtfsProvider: return "gtfs"; case Enums::InvalidProvider: default: return "invalid"; } } QString ServiceProviderGlobal::typeName( Enums::ServiceProviderType type, ProviderTypeNameOptions options ) { QString name; switch ( type ) { case Enums::ScriptedProvider: name = i18nc("@info/plain Name of a service provider plugin type", "Scripted"); break; case Enums::GtfsProvider: name = i18nc("@info/plain Name of a service provider plugin type", "GTFS"); break; case Enums::InvalidProvider: default: qWarning() << "Invalid provider type" << type; return i18nc("@info/plain Name of the invalid service provider plugin type", "Invalid"); } // Append "(unsupported)" if the engine gets build without support for the provider type if ( options == AppendHintForUnsupportedProviderTypes && !isProviderTypeAvailable(type) ) { name += ' ' + i18nc("@info/plain Gets appended to service provider plugin type names, " "if the engine gets build without support for that type", "(unsupported)"); } return name; } QStringList ServiceProviderGlobal::filePatterns() { - const KMimeType::Ptr mimeType = - KMimeType::mimeType("application/x-publictransport-serviceprovider"); - if ( mimeType.isNull() ) { + QMimeDatabase db; + const QMimeType mimeType = db.mimeTypeForName("application/x-publictransport-serviceprovider"); + if (!mimeType.isValid() ) { qWarning() << "The application/x-publictransport-serviceprovider mime type was not found!"; qWarning() << "No provider plugins will get loaded."; kDebug() << "Solution: Make sure 'serviceproviderplugin.xml' is installed correctly " "and run kbuildsycoca4."; return QStringList(); } else { - return mimeType->patterns(); + return mimeType.globPatterns(); } } QStringList ServiceProviderGlobal::fileExtensions() { QStringList extensions = filePatterns(); for ( QStringList::Iterator it = extensions.begin(); it != extensions.end(); ++it ) { const int pos = it->lastIndexOf( '.' ); if ( pos == -1 || pos == it->length() - 1 ) { qWarning() << "Could not extract file extension from mime type pattern!\n" "Check the \"application/x-publictransport-serviceprovider\" mime type."; continue; } // Cut away everything but the file name extension *it = it->mid( pos + 1 ); } return extensions; } QStringList ServiceProviderGlobal::installedProviders() { QStringList providers; const QString subDirectory = installationSubDirectory(); foreach ( const QString &pattern, filePatterns() ) { providers << QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, subDirectory + pattern ); } return providers; } bool ServiceProviderGlobal::isProviderInstalled( const QString &providerId ) { const QString subDirectory = installationSubDirectory(); foreach ( const QString &extension, fileExtensions() ) { const QString providerFile = KStandardDirs::locate( "data", subDirectory + providerId + '.' + extension ); if ( !providerFile.isEmpty() ) { // Found the provider plugin source file in an installation directory return true; } } // Provider is not installed return false; } bool ServiceProviderGlobal::isSourceFileModified( const QString &providerId, const QSharedPointer &cache ) { // Check if the script file was modified since the cache was last updated const KConfigGroup group = cache->group( providerId ); const QDateTime modifiedTime = group.readEntry( "modifiedTime", QDateTime() ); const QString fileName = fileNameFromId( providerId ); return fileName.isEmpty() ? true : QFileInfo(fileName).lastModified() != modifiedTime; } QString ServiceProviderGlobal::featureName( Enums::ProviderFeature feature ) { switch ( feature ) { case Enums::ProvidesDepartures: return i18nc("@info/plain A short string indicating support for departure from a stop", "Departures"); case Enums::ProvidesArrivals: return i18nc("@info/plain A short string indicating support for arrivals to a stop.", "Arrivals"); case Enums::ProvidesJourneys: return i18nc("@info/plain A short string indicating support for journeys", "Journey search"); case Enums::ProvidesAdditionalData: return i18nc("@info/plain A short string indicating support for additional data", "Get additional data later"); case Enums::ProvidesDelays: return i18nc("@info/plain A short string indicating that delay information can be provided", "Delays"); case Enums::ProvidesNews: return i18nc("@info/plain A short string indicating that news about timetable items can be provided", "News"); case Enums::ProvidesPlatform: return i18nc("@info/plain A short string indicating that platform information can be provided", "Platform"); case Enums::ProvidesStopSuggestions: return i18nc("@info/plain A short string indicating support for stop suggestions", "Stop suggestions"); case Enums::ProvidesStopsByGeoPosition: return i18nc("@info/plain A short string indicating support for querying stops by geo position", "Stops by geolocation"); case Enums::ProvidesStopID: return i18nc("@info/plain A short string indicating that stop IDs can be provided", "Stop ID"); case Enums::ProvidesStopGeoPosition: return i18nc("@info/plain A short string indicating that stop geographical positions can be provided", "Stop geolocation"); case Enums::ProvidesPricing: return i18nc("@info/plain A short string indicating that pricing information can be provided", "Pricing"); case Enums::ProvidesRouteInformation: return i18nc("@info/plain A short string indicating that route information can be provided", "Route information"); case Enums::ProvidesMoreJourneys: return i18nc("@info/plain A short string indicating that earlier later journeys can be " "provided for existing journey data sources", "Get earlier/later journeys"); default: qWarning() << "Unexpected feature value" << feature; return QString(); } } QStringList ServiceProviderGlobal::featureNames( const QList &features ) { QStringList names; foreach ( Enums::ProviderFeature feature, features ) { names << featureName( feature ); } return names; } QStringList ServiceProviderGlobal::featureStrings( const QList &features ) { QStringList names; foreach ( Enums::ProviderFeature feature, features ) { names << Enums::toString( feature ); } return names; } QList< Enums::ProviderFeature > ServiceProviderGlobal::featuresFromFeatureStrings( const QStringList &featureNames, bool *ok ) { QList< Enums::ProviderFeature > features; if ( ok ) *ok = true; foreach ( const QString &featureName, featureNames ) { Enums::ProviderFeature feature = Enums::stringToFeature( featureName.toAscii().data() ); if ( feature == Enums::InvalidProviderFeature ) { if ( ok ) *ok = false; } else { features << feature; } } return features; } diff --git a/engine/timetablemate/src/networkmonitormodel.cpp b/engine/timetablemate/src/networkmonitormodel.cpp index 31430b7..249dc9c 100644 --- a/engine/timetablemate/src/networkmonitormodel.cpp +++ b/engine/timetablemate/src/networkmonitormodel.cpp @@ -1,786 +1,791 @@ /* * Copyright 2012 Friedrich Pülz * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2 or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // Header #include "networkmonitormodel.h" // Public Transport engine includes #include // KDE includes #include #include #include #include #include -#include + #include #include #include #include #include // Qt includes #include #include #include #include #include #include #include +#include +#include NetworkMonitorModel::NetworkMonitorModel( QObject *parent ) : QAbstractListModel( parent ) { } NetworkMonitorModel::~NetworkMonitorModel() { qDeleteAll( m_data ); } bool NetworkMonitorModel::removeRows( int row, int count, const QModelIndex &parent ) { if ( parent.isValid() || row < 0 || row + count > m_data.count() ) { return false; } beginRemoveRows( parent, row, row + count - 1 ); for ( int i = row + count - 1; i >= row; --i ) { delete m_data.takeAt( i ); } endRemoveRows(); return true; } int NetworkMonitorModel::rowCount( const QModelIndex &parent ) const { if ( parent.isValid() ) { return 0; } else { return m_data.count(); } } void NetworkMonitorModel::clear() { beginRemoveRows( QModelIndex(), 0, rowCount() ); m_data.clear(); endRemoveRows(); } QModelIndex NetworkMonitorModel::index( int row, int column, const QModelIndex &parent ) const { if ( column < 0 || column >= ColumnCount || row < 0 ) { return QModelIndex(); } if ( parent.isValid() ) { return QModelIndex(); } else { return row >= m_data.count() ? QModelIndex() : createIndex(row, column, m_data[row]); } } QModelIndex NetworkMonitorModel::indexFromMonitorData( NetworkMonitorModelItem *data, int column ) const { const int row = m_data.indexOf( data ); if ( row == -1 ) { return QModelIndex(); } else { return index( row, column ); } } NetworkMonitorModelItem *NetworkMonitorModel::monitorDataFromIndex( const QModelIndex &index ) const { return static_cast< NetworkMonitorModelItem* >( index.internalPointer() ); } QString NetworkMonitorModel::encodeHtml( const QString &html ) { return QString(html).replace( "<", "<" ).replace( ">", ">" ); } QString NetworkMonitorModel::limitLineCount( const QString &string, bool htmlFormat, int maxLineCount, int maxCharactersPerLine ) { QString result; QStringList lines = string.split( '\n' ); foreach ( const QString &line, lines ) { if ( maxCharactersPerLine > 0 && line.length() > maxCharactersPerLine ) { QString currentLine = line; while ( !currentLine.isEmpty() ) { if ( !result.isEmpty() ) { result += '\n'; } result += currentLine.left( maxCharactersPerLine ); currentLine = currentLine.mid( maxCharactersPerLine ); if ( !currentLine.isEmpty() && !htmlFormat ) { // Append a return symbol result += QString::fromUtf8( "\342\217\216" ); } } } else { if ( !result.isEmpty() ) { result += '\n'; } result += line; } int lineCount = 0; int pos = -1; while ( (pos = result.indexOf('\n', pos + 1)) != -1 ) { ++lineCount; if ( lineCount >= maxLineCount ) { return result.left( pos ) + "..."; } } } return result; } QVariant NetworkMonitorModel::headerData( int section, Qt::Orientation orientation, int role ) const { switch ( role ) { case Qt::DisplayRole: if ( orientation == Qt::Horizontal && role == Qt::DisplayRole ) { switch ( static_cast(section) ) { case TypeColumn: return i18nc("@title:column", "Type"); case ContentTypeColumn: return i18nc("@title:column", "Content"); case TimeColumn: return i18nc("@title:column", "Time"); case UrlColumn: return i18nc("@title:column", "URL"); case DataColumn: return i18nc("@title:column", "Data"); default: return QVariant(); } } else { return QVariant(); } default: return QVariant(); } } QString NetworkMonitorModel::decodeHtml( const QByteArray &data ) const { QTextCodec *textCodec = QTextCodec::codecForHtml( data, QTextCodec::codecForUtfText(data, QTextCodec::codecForName("UTF-8")) ); return textCodec ? textCodec->toUnicode(data) : data; } QVariant NetworkMonitorModel::data( const QModelIndex &index, int role ) const { if ( !index.isValid() ) { return QVariant(); } NetworkMonitorModelItem *data = monitorDataFromIndex( index ); if ( !data ) { return QVariant(); } switch ( role ) { case Qt::DisplayRole: switch ( index.column() ) { case TypeColumn: return NetworkMonitorModelItem::nameFromType( data->type() ); case ContentTypeColumn: return NetworkMonitorModelItem::nameFromContentType( data->contentType() ); case TimeColumn: return KGlobal::locale()->formatLocaleTime( data->time() ); case UrlColumn: return data->url(); case DataColumn: { const bool htmlFormat = data->type() == NetworkMonitorModelItem::Reply && (data->contentType() == NetworkMonitorModelItem::HtmlData || data->contentType() == NetworkMonitorModelItem::XmlData); const bool textData = htmlFormat || data->contentType() == NetworkMonitorModelItem::CssData || data->contentType() == NetworkMonitorModelItem::ScriptData || data->contentType() == NetworkMonitorModelItem::UnknownTextData; if ( data->type() == NetworkMonitorModelItem::Reply && !textData ) { return data->mimeType(); } QString result = data->type() == NetworkMonitorModelItem::Reply ? decodeHtml(data->data()) : data->data(); // TODO fallback charset in ServiceProviderData // Remove carriage return characters, they would get drawn in views for some reason result = result.replace( '\r', QString() ); return limitLineCount( result.trimmed() ); } default: return QVariant(); } case Qt::EditRole: switch ( index.column() ) { case UrlColumn: return data->url(); case DataColumn: if ( data->contentType() == NetworkMonitorModelItem::ImageData && data->imageData() ) { // Read image from temporary file and convert to a pixmap // Shouldn't be used to often, eg. for copy to clipboard actions // The pixmap isn't stored in memory to save space return QPixmap::fromImage( QImage(data->imageData()->tempFile->fileName()) ); } else if ( data->type() == NetworkMonitorModelItem::Reply ) { return decodeHtml( data->data() ); // TODO fallback charset in ServiceProviderData } else { return data->data(); } default: return QVariant(); } case Qt::ToolTipRole: switch ( index.column() ) { case UrlColumn: return data->url(); case DataColumn: { const QString limited = limitLineCount( data->data(), true, 15 ); switch ( data->contentType() ) { case NetworkMonitorModelItem::ImageData: if ( data->imageData() && data->imageData()->tempFile ) { const int maxImageDimension = KGlobalSettings::desktopGeometry(QPoint(0, 0)).height() / 2; int width = data->imageData()->size.width(); int height = data->imageData()->size.height(); if ( width > height ) { if ( width > maxImageDimension ) { height *= maxImageDimension / width; width = maxImageDimension; } } else { if ( height > maxImageDimension ) { width *= maxImageDimension / height; height = maxImageDimension; } } return QString("%6, %2x%3:
" "") .arg(data->imageData()->tempFile->fileName()) .arg(data->imageData()->size.width()) .arg(data->imageData()->size.height()) .arg(width).arg(height).arg(data->mimeType()); } else { return QVariant(); } case NetworkMonitorModelItem::HtmlData: case NetworkMonitorModelItem::XmlData: return encodeHtml( limited ); default: return limited; } } default: return QVariant(); } case Qt::DecorationRole: switch ( index.column() ) { case TypeColumn: switch ( data->type() ) { case NetworkMonitorModelItem::GetRequest: case NetworkMonitorModelItem::PostRequest: return KIcon("download"); case NetworkMonitorModelItem::Reply: return KIcon("go-up"); default: return QVariant(); } case ContentTypeColumn: switch ( data->contentType() ) { case NetworkMonitorModelItem::HtmlData: return KIcon("text-html"); case NetworkMonitorModelItem::XmlData: return KIcon("text-xml"); case NetworkMonitorModelItem::CssData: return KIcon("text-css"); case NetworkMonitorModelItem::ScriptData: return KIcon("text-x-script"); case NetworkMonitorModelItem::UnknownTextData: return KIcon("text-plain"); case NetworkMonitorModelItem::ImageData: return KIcon("image-x-generic"); case NetworkMonitorModelItem::RetrievingContentType: return KIcon("task-ongoing"); default: return QVariant(); } case DataColumn: switch ( data->contentType() ) { case NetworkMonitorModelItem::ImageData: return data->imageData() ? data->imageData()->icon : QVariant(); default: return QVariant(); } default: return QVariant(); } case DataTypeRole: return data->type(); case ContentTypeRole: return data->contentType(); default: return QVariant(); } } Qt::ItemFlags NetworkMonitorModel::flags( const QModelIndex &index ) const { return index.isValid() ? Qt::ItemIsEnabled | Qt::ItemIsSelectable : Qt::NoItemFlags; } NetworkMonitorModelItem::NetworkMonitorModelItem( NetworkMonitorModelItem::Type type, const QString &url, const QByteArray &data, QObject *parent ) : QObject(parent), m_model(0), m_type(type), m_contentType(NetworkMonitorModelItem::RetrievingContentType), m_time(QTime::currentTime()), m_url(url), m_data(data), m_imageData(0) { const QUrl _url( url ); if ( !data.isEmpty() ) { m_contentType = contentTypeFromContent( data, _url ); if ( m_contentType == NetworkMonitorModelItem::UnknownData ) { if ( type == Reply ) { m_contentType = contentTypeFromUrl( _url ); } else { m_contentType = UnknownTextData; } } if ( m_contentType == ImageData ) { prepareAdditionalImageData(); } } else { m_contentType = contentTypeFromUrl( _url ); } } NetworkMonitorModelItem::~NetworkMonitorModelItem() { delete m_imageData; } void NetworkMonitorModelItem::prepareAdditionalImageData() { // Delete old image data, if any if ( m_imageData ) { delete m_imageData; } // Read image data QImage image = QImage::fromData( m_data ); if ( image.isNull() ) { // Cannot read image from data, maybe empty data return; } // Create new object with additional data for items containing image data m_imageData = new AdditionalImageData(); m_imageData->size = image.size(); QPixmap smallPixmap = QPixmap::fromImage( image.scaledToHeight(32, Qt::SmoothTransformation) ); if ( smallPixmap.isNull() ) { // Scaling produced an empty pixmap (very high and narrow or very wide and little height) // Create an empty 16x16 pixmap smallPixmap = QPixmap( 32, 32 ); smallPixmap.fill( Qt::transparent ); } m_imageData->icon = KIcon( smallPixmap ); // Write image data to a temporary file, for display in a QToolTip using an HTML tag m_imageData->tempFile = new KTemporaryFile(); m_imageData->tempFile->setAutoRemove( true ); m_imageData->tempFile->setPrefix( QUrl(m_url).fileName() ); if ( m_imageData->tempFile->open() ) { m_imageData->tempFile->write( m_data ); m_imageData->tempFile->close(); } } NetworkMonitorModelItem::ContentType NetworkMonitorModelItem::contentTypeFromUrl( const QUrl &url ) { - KMimeType::Ptr mimeType = KMimeType::findByUrl( url ); +QMimeDatabase db; + QMimeType mimeType = db.mimeTypeForUrl( url ); if ( mimeType == KMimeType::defaultMimeTypePtr() ) { // Mime type not found, use KIO to get the mime type asynchronously KIO::MimetypeJob *job = KIO::mimetype( url, KIO::HideProgressInfo ); connect( job, SIGNAL(mimetype(KIO::Job*,QString)), this, SLOT(mimetype(KIO::Job*,QString)) ); connect( job, SIGNAL(finished(KJob*)), this, SLOT(mimetypeJobFinished(KJob*)) ); job->start(); return RetrievingContentType; } else { // Mime type found - m_mimeType = mimeType->name(); - return contentTypeFromMimeType( mimeType->name() ); + m_mimeType = mimeType.name(); + return contentTypeFromMimeType( mimeType.name() ); } } NetworkMonitorModelItem::ContentType NetworkMonitorModelItem::contentTypeFromContent( const QByteArray &content, const QUrl &url ) { int accuracy; - KMimeType::Ptr mimeType = KMimeType::findByContent( content, &accuracy ); + QMimeDatabase db; + QMimeType mimeType = db.mimeTypeForData( content, &accuracy ); if ( (mimeType == KMimeType::defaultMimeTypePtr() || accuracy < 70) && url.isValid() ) { // No accurate mime type found, find mime type from URL - mimeType = KMimeType::findByUrl( url ); +QMimeDatabase db; + mimeType = db.mimeTypeForUrl( url ); } - m_mimeType = mimeType->name(); - return contentTypeFromMimeType( mimeType->name() ); + m_mimeType = mimeType.name(); + return contentTypeFromMimeType( mimeType.name() ); } QString NetworkMonitorModelItem::nameFromType( NetworkMonitorModelItem::Type type ) { switch ( type ) { case GetRequest: return i18nc("@info/plain", "Request (GET)"); case PostRequest: return i18nc("@info/plain", "Request (POST)"); case Reply: return i18nc("@info/plain", "Reply"); default: kDebug() << "Unknown type" << type; return QString(); } } QString NetworkMonitorModelItem::nameFromContentType( NetworkMonitorModelItem::ContentType contentType ) { switch ( contentType ) { case HtmlData: return i18nc("@info/plain", "HTML"); case XmlData: return i18nc("@info/plain", "XML"); case ImageData: return i18nc("@info/plain", "Image"); case CssData: return i18nc("@info/plain", "CSS"); case ScriptData: return i18nc("@info/plain", "Script"); case UnknownTextData: return i18nc("@info/plain", "Text"); case RetrievingContentType: return i18nc("@info/plain", "(wait)"); case UnknownData: default: return i18nc("@info/plain", "Unknown"); } } NetworkMonitorModelItem::Type NetworkMonitorModelItem::typeFromOperation( QNetworkAccessManager::Operation operation ) { switch ( operation ) { case QNetworkAccessManager::HeadOperation: case QNetworkAccessManager::GetOperation: return GetRequest; case QNetworkAccessManager::PostOperation: return PostRequest; default: return Invalid; } } NetworkMonitorModelItem::ContentType NetworkMonitorModelItem::contentTypeFromMimeType( const QString &type ) { if ( type.startsWith(QLatin1String("image")) ) { return ImageData; } else if ( type.contains(QLatin1String("html")) ) { return HtmlData; } else if ( type.endsWith(QLatin1String("/xml")) ) { return XmlData; } else if ( type == QLatin1String("text/css") ) { return CssData; } else if ( type.endsWith(QLatin1String("script")) ) { return ScriptData; } else if ( type.startsWith(QLatin1String("text/")) ) { return UnknownTextData; } else { return UnknownData; } } void NetworkMonitorModelItem::mimetype( KIO::Job *job, const QString &type ) { Q_UNUSED( job ); m_mimeType = type; m_contentType = contentTypeFromMimeType( type ); if ( m_contentType == ImageData ) { prepareAdditionalImageData(); } if ( m_model ) { m_model->slotDataChanged( this ); } } void NetworkMonitorModelItem::mimetypeJobFinished( KJob *job ) { Q_UNUSED( job ); if ( m_contentType == RetrievingContentType ) { // Mimetype not found kDebug() << "Mimetype not found"; m_contentType = UnknownData; if ( m_model ) { m_model->slotDataChanged( this ); } } } void NetworkMonitorModel::slotDataChanged( NetworkMonitorModelItem *data ) { emit dataChanged( indexFromMonitorData(data, 0), indexFromMonitorData(data, ColumnCount - 1) ); } void NetworkMonitorModel::requestCreated( NetworkMonitorModelItem::Type type, const QString &url, const QByteArray &data, QNetworkReply *reply ) { // Create new item for the request NetworkMonitorModelItem *requestItem = new NetworkMonitorModelItem( type, url, data, this ); requestItem->setModel( this ); // Insert request item beginInsertRows( QModelIndex(), 0, 0 ); m_data.prepend( requestItem ); endInsertRows(); if ( reply ) { // Connect to the finished signal to add a reply item when the reply has finished connect( reply, SIGNAL(finished()), this, SLOT(replyFinished()) ); } } void NetworkMonitorModel::replyFinished() { QNetworkReply *reply = qobject_cast< QNetworkReply* >( sender() ); if ( reply ) { // Get data from cache QAbstractNetworkCache *cache = reply->manager()->cache(); QIODevice *cachedData = cache->data( reply->request().url() ); if ( !cachedData ) { cachedData = cache->data( reply->url() ); } QByteArray data; if ( cachedData ) { data = cachedData->readAll(); delete cachedData; } // Insert reply item beginInsertRows( QModelIndex(), 0, 0 ); NetworkMonitorModelItem *newData = new NetworkMonitorModelItem( NetworkMonitorModelItem::Reply, reply->url().toString(), data, this ); newData->setModel( this ); m_data.prepend( newData ); endInsertRows(); } } bool NetworkMonitorFilterModel::filterAcceptsRow( int sourceRow, const QModelIndex &sourceParent ) const { if ( sourceParent.isValid() ) { return false; } // Filter by type const QModelIndex sourceIndex = sourceModel()->index( sourceRow, 0, sourceParent ); const NetworkMonitorModelItem::Type type = static_cast< NetworkMonitorModelItem::Type >( sourceModel()->data(sourceIndex, NetworkMonitorModel::DataTypeRole).toInt() ); if ( !m_types.testFlag(type) ) { return false; } // Filter by content type const NetworkMonitorModelItem::ContentType contentType = static_cast< NetworkMonitorModelItem::ContentType >( sourceModel()->data(sourceIndex, NetworkMonitorModel::ContentTypeRole).toInt() ); return m_contentTypes.testFlag( contentType ); } void NetworkMonitorFilterModel::setTypeFilter( NetworkMonitorModelItem::Types types ) { m_types = types; invalidateFilter(); } void NetworkMonitorFilterModel::setContentTypeFilter( NetworkMonitorModelItem::ContentTypes contentTypes ) { m_contentTypes = contentTypes; invalidateFilter(); } NetworkMemoryCache::~NetworkMemoryCache() { clear(); } void NetworkMemoryCache::clear() { qDeleteAll( m_orderedData ); m_orderedData.clear(); m_data.clear(); } QIODevice *NetworkMemoryCache::prepare( const QNetworkCacheMetaData &metaData ) { if ( !metaData.isValid() || !metaData.url().isValid() ) { return 0; } QBuffer *buffer = new QBuffer( this ); if ( !buffer->open(QIODevice::ReadWrite) ) { delete buffer; return 0; } m_prepared.insert( buffer, metaData ); return buffer; } void NetworkMemoryCache::insert( QIODevice *device ) { if ( !m_prepared.contains(device) ) { qWarning() << "Call prepare() first"; return; } // Get prepared meta data object and insert it with the data read from device const QNetworkCacheMetaData metaData = m_prepared.take( device ); device->reset(); // Go to beginning (after writing to device it is at the end) const QByteArray data = device->readAll(); CacheData *cacheData = new CacheData( metaData, data ); m_data.insert( metaData.url(), cacheData ); m_orderedData << cacheData; device->close(); delete device; QTimer::singleShot( TIMEOUT, this, SLOT(removeOldestCacheData()) ); } void NetworkMemoryCache::removeOldestCacheData() { if ( m_orderedData.isEmpty() ) { return; } CacheData *cacheData = m_orderedData.first(); if ( !remove(cacheData->metaData.url()) ) { qWarning() << "Could not remove old cache item" << cacheData->metaData.url(); m_orderedData.removeFirst(); } } bool NetworkMemoryCache::remove( const QUrl &url ) { if ( m_data.contains(url) ) { // Data cached for url, remove it CacheData *cacheData = m_data.take( url ); m_orderedData.removeOne( cacheData ); delete cacheData; return true; } else { return false; } } void NetworkMemoryCache::updateMetaData( const QNetworkCacheMetaData &metaData ) { if ( !m_data.contains(metaData.url()) ) { return; } CacheData *data = m_data[ metaData.url() ]; data->metaData = metaData; } QIODevice *NetworkMemoryCache::data( const QUrl &url ) { if ( !m_data.contains(url) ) { return 0; } CacheData *data = m_data[ url ]; QBuffer *buffer = new QBuffer( this ); buffer->setData( data->data ); if ( !buffer->open(QIODevice::ReadOnly) ) { delete buffer; return 0; } else { return buffer; } } QNetworkCacheMetaData NetworkMemoryCache::metaData( const QUrl &url ) { if ( !m_data.contains(url) ) { return QNetworkCacheMetaData(); } CacheData *data = m_data[ url ]; return data->metaData; } NetworkMemoryCache::CacheData::CacheData( const QNetworkCacheMetaData &metaData, const QByteArray &data ) : metaData(metaData), data(data) { } QNetworkReply *MonitorNetworkAccessManager::createRequest( QNetworkAccessManager::Operation op, const QNetworkRequest &request, QIODevice *outgoingData ) { QByteArray data; QNetworkReply *reply; QNetworkRequest _request( request ); _request.setAttribute( QNetworkRequest::CacheSaveControlAttribute, true ); if ( outgoingData ) { QBuffer *buffer = qobject_cast< QBuffer* >( outgoingData ); if ( buffer ) { // Read from buffer without changing the position in outgoingData data = buffer->buffer(); reply = QNetworkAccessManager::createRequest( op, _request, outgoingData ); } else if ( !outgoingData->isSequential() ) { // Read from random access device and reset to the start position data = outgoingData->readAll(); outgoingData->reset(); reply = QNetworkAccessManager::createRequest( op, _request, outgoingData ); } else { // Read from sequential device and create a new device after reading, // because sequential devices can only be read once and would be empty when send to // QNetworkAccessManager::createRequest() buffer = new QBuffer( outgoingData->parent() ); buffer->setData( outgoingData->readAll() ); data = buffer->buffer(); outgoingData->close(); outgoingData->deleteLater(); reply = QNetworkAccessManager::createRequest( op, _request, buffer ); } } else { reply = QNetworkAccessManager::createRequest( op, _request ); } emit requestCreated( NetworkMonitorModelItem::typeFromOperation(op), _request.url().toString(), data, reply ); return reply; } diff --git a/engine/timetablemate/src/projectsettingsdialog.cpp b/engine/timetablemate/src/projectsettingsdialog.cpp index d777c6d..83f82c0 100644 --- a/engine/timetablemate/src/projectsettingsdialog.cpp +++ b/engine/timetablemate/src/projectsettingsdialog.cpp @@ -1,979 +1,982 @@ /* * Copyright 2012 Friedrich Pülz * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2 or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // Header #include "projectsettingsdialog.h" // Own includes #include "ui_timetablemateview_base.h" #include "project.h" #include "settings.h" #include "changelogwidget.h" #include "serviceproviderdatawriter.h" #include "serviceproviderdatatester.h" // PublicTransport engine includes #include #include #include #include #include // KDE includes #include #include #include #include #include #include #include #include #include #include #ifdef MARBLE_FOUND #include #endif // Qt includes #include #include #include #include #include #include #include #include #include #include #include #include +#include +#include ProjectSettingsDialog::ProjectSettingsDialog( QWidget *parent ) : KDialog(parent), ui_provider(new Ui::timetablemateview_base()), m_providerData(0), m_shortAuthorAutoFilled(false), m_shortUrlAutoFilled(false), #ifdef BUILD_PROVIDER_TYPE_SCRIPT m_newScriptTemplateType(Project::NoScriptTemplate), #endif m_cityName(0), m_cityReplacement(0), m_changelog(0), m_actions(new KActionCollection(this)), m_mapper(0) { #ifdef BUILD_PROVIDER_TYPE_GTFS KGlobal::locale()->insertCatalog( "timezones4" ); #endif QWidget *widget = new QWidget( this ); widget->setAutoFillBackground( false ); ui_provider->setupUi( widget ); setMainWidget( widget ); setCaption( i18nc("@title:window", "Project Settings") ); setButtons( KDialog::Ok | KDialog::Cancel | KDialog::User1 ); setButtonIcon( KDialog::User1, KIcon("dialog-ok-apply") ); setButtonText( KDialog::User1, i18nc("@info/plain", "Check") ); QToolBar *notesToolBar = new QToolBar( "notesToolBar", ui_provider->tabNotes ); QToolBar *notesToolBar2 = new QToolBar( "notesToolBar2", ui_provider->tabNotes ); ui_provider->notesLayout->insertWidget( 0, notesToolBar ); ui_provider->notesLayout->insertWidget( 1, notesToolBar2 ); ui_provider->notes->createActions( m_actions ); QAction *separator1 = new QAction( this ), *separator2 = new QAction( this ); separator1->setSeparator( true ); separator2->setSeparator( true ); notesToolBar->addActions( QList() << m_actions->action("format_text_bold") << m_actions->action("format_text_italic") << m_actions->action("format_text_underline") << m_actions->action("format_text_strikeout") << separator1 << m_actions->action("format_align_left") << m_actions->action("format_align_center") << m_actions->action("format_align_right") << m_actions->action("format_align_justify") << separator2 << m_actions->action("insert_horizontal_rule") << m_actions->action("manage_link") << m_actions->action("format_painter") ); notesToolBar2->addActions( QList() << m_actions->action("format_font_family") << m_actions->action("format_font_size") << m_actions->action("format_list_style") ); settingsChanged(); #ifdef BUILD_PROVIDER_TYPE_SCRIPT // Initialize script file buttons ui_provider->btnBrowseForScriptFile->setIcon( KIcon("document-open") ); ui_provider->btnCreateScriptFile->setIcon( KIcon("document-new") ); ui_provider->btnDetachScriptFile->setIcon( KIcon("list-remove") ); ui_provider->btnDetachScriptFile->setVisible( false ); connect( ui_provider->btnBrowseForScriptFile, SIGNAL(clicked(bool)), this, SLOT(browseForScriptFile()) ); connect( ui_provider->btnCreateScriptFile, SIGNAL(clicked(bool)), this, SLOT(createScriptFile()) ); connect( ui_provider->btnDetachScriptFile, SIGNAL(clicked(bool)), this, SLOT(detachScriptFile()) ); #endif // Initialize the language button ui_provider->currentLanguage->loadAllLanguages(); ui_provider->currentLanguage->insertLanguage( "en", QString(), 0 ); ui_provider->currentLanguage->insertSeparator( 1 ); connect( ui_provider->currentLanguage, SIGNAL(activated(QString)), this, SLOT(languageActivated(QString)) ); // Autofill short author/URL fields, if they are empty // while editing the full author/URL fields connect( ui_provider->author, SIGNAL(textEdited(QString)), this, SLOT(authorEdited(QString)) ); connect( ui_provider->shortAuthor, SIGNAL(textEdited(QString)), this, SLOT(shortAuthorEdited(QString)) ); connect( ui_provider->url, SIGNAL(textEdited(QString)), this, SLOT(urlEdited(QString)) ); connect( ui_provider->shortUrl, SIGNAL(textEdited(QString)), this, SLOT(shortUrlEdited(QString)) ); // Initialize the KEditListWidget for predefined cities QWidget *repWidget = new QWidget( this ); QHBoxLayout *customEditorLayout = new QHBoxLayout( repWidget ); m_cityName = new KLineEdit( this ); m_cityReplacement = new KLineEdit( this ); QLabel *lblCityReplacement = new QLabel( i18nc("@info", "Replace with:"), this ); lblCityReplacement->setAlignment( Qt::AlignRight | Qt::AlignVCenter ); customEditorLayout->addWidget( m_cityName ); customEditorLayout->addWidget( lblCityReplacement ); customEditorLayout->addWidget( m_cityReplacement ); KLineEdit *defaultLineEdit = new KLineEdit(); m_predefinedCitiesCustomEditor.setLineEdit( defaultLineEdit ); defaultLineEdit->hide(); m_predefinedCitiesCustomEditor.setRepresentationWidget( repWidget ); ui_provider->predefinedCities->setCustomEditor( m_predefinedCitiesCustomEditor ); connect( m_cityName, SIGNAL(textChanged(QString)), this, SLOT(predefinedCityNameChanged(QString)) ); connect( m_cityReplacement, SIGNAL(textChanged(QString)), this, SLOT(predefinedCityReplacementChanged(QString)) ); connect( defaultLineEdit, SIGNAL(textChanged(QString)), this, SLOT(currentPredefinedCityChanged(QString)) ); // Set a validator for version line edits, allow major, minor and path version QRegExpValidator *versionValidator = new QRegExpValidator( QRegExp("\\d+(\\.\\d+)?(\\.\\d+)?"), this ); ui_provider->version->setValidator( versionValidator ); // Set a validator for the email line edit // The reg exp is "inspired" by http://www.regular-expressions.info/email.html QRegExp rx( "[a-z0-9!#$%&\\._-]+@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z]{2,4}", Qt::CaseInsensitive ); QRegExpValidator *emailValidator = new QRegExpValidator( rx, this ); ui_provider->email->setValidator( emailValidator ); // Install event filters to filter out focus out events // if the line edit's text cannot be validated ui_provider->version->installEventFilter( this ); ui_provider->name->installEventFilter( this ); ui_provider->description->installEventFilter( this ); ui_provider->author->installEventFilter( this ); ui_provider->shortAuthor->installEventFilter( this ); ui_provider->email->installEventFilter( this ); ui_provider->url->installEventFilter( this ); ui_provider->shortUrl->installEventFilter( this ); // Set icons and connections for the "open url buttons" ui_provider->btnUrlOpen->setIcon( KIcon("document-open-remote") ); connect( ui_provider->btnUrlOpen, SIGNAL(clicked(bool)), this, SLOT(openUrlClicked()) ); // Add changelog widget into a scroll area QVBoxLayout *changelogAreaLayout = new QVBoxLayout( ui_provider->tabChangelog ); QScrollArea *changelogArea = new QScrollArea( ui_provider->tabChangelog ); changelogArea->setFrameStyle( QFrame::NoFrame ); changelogArea->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); changelogArea->setWidgetResizable( true ); changelogAreaLayout->addWidget( changelogArea ); QWidget *changelogAreaWidget = new QWidget( changelogArea ); changelogArea->setWidget( changelogAreaWidget ); QVBoxLayout *changelogLayout = new QVBoxLayout( changelogAreaWidget ); m_changelog = new ChangelogWidget( changelogAreaWidget ); m_changelog->clear(); changelogLayout->addWidget( m_changelog ); changelogLayout->addStretch(); connect( m_changelog, SIGNAL(changelogEntryWidgetAdded(ChangelogEntryWidget*)), this, SLOT(changelogEntryWidgetAdded(ChangelogEntryWidget*)) ); // Add vehicle types with icons to the default vehicle type combo box ui_provider->defaultVehicleType->addItem( KIcon("status_unknown"), i18nc("@item:listbox", "Unknown"), Enums::toString(Enums::UnknownVehicleType) ); ui_provider->defaultVehicleType->addItem( KIcon("vehicle_type_tram"), i18nc("@item:listbox", "Tram"), Enums::toString(Enums::Tram) ); ui_provider->defaultVehicleType->addItem( KIcon("vehicle_type_bus"), i18nc("@item:listbox", "Bus"), Enums::toString(Enums::Bus) ); ui_provider->defaultVehicleType->addItem( KIcon("vehicle_type_subway"), i18nc("@item:listbox", "Subway"), Enums::toString(Enums::Subway) ); ui_provider->defaultVehicleType->addItem( KIcon("vehicle_type_train_interurban"), i18nc("@item:listbox", "Interurban Train"), Enums::toString(Enums::InterurbanTrain) ); ui_provider->defaultVehicleType->addItem( KIcon("vehicle_type_metro"), i18nc("@item:listbox", "Metro"), Enums::toString(Enums::Metro) ); ui_provider->defaultVehicleType->addItem( KIcon("vehicle_type_trolleybus"), i18nc( "@item:listbox", "Trolley Bus"), Enums::toString(Enums::TrolleyBus) ); ui_provider->defaultVehicleType->addItem( KIcon("vehicle_type_train_regional"), // TODO: Currently no special icon i18nc("@item:listbox", "Regional Train"), Enums::toString(Enums::RegionalTrain) ); ui_provider->defaultVehicleType->addItem( KIcon("vehicle_type_train_regional"), i18nc("@item:listbox", "Regional Express Train"), Enums::toString(Enums::RegionalExpressTrain) ); ui_provider->defaultVehicleType->addItem( KIcon("vehicle_type_train_interregional"), i18nc("@item:listbox", "Interregional Train"), Enums::toString(Enums::InterregionalTrain) ); ui_provider->defaultVehicleType->addItem( KIcon("vehicle_type_train_intercity"), i18nc("@item:listbox", "Intercity/Eurocity Train"), Enums::toString(Enums::IntercityTrain) ); ui_provider->defaultVehicleType->addItem( KIcon("vehicle_type_train_highspeed"), i18nc("@item:listbox", "Intercity Express Train"), Enums::toString(Enums::HighSpeedTrain) ); ui_provider->defaultVehicleType->addItem( KIcon("vehicle_type_ferry"), i18nc("@item:listbox", "Ferry"), Enums::toString(Enums::Ferry) ); ui_provider->defaultVehicleType->addItem( KIcon("vehicle_type_ferry"), i18nc("@item:listbox", "Ship"), Enums::toString(Enums::Ship) ); ui_provider->defaultVehicleType->addItem( KIcon("vehicle_type_plane"), i18nc("@item:listbox", "Plane"), Enums::toString(Enums::Plane) ); #ifdef MARBLE_FOUND // Create and insert Marble::LatLonEdit widgets ui_sampleLongitude = new Marble::LatLonEdit( widget, Marble::Longitude ); ui_sampleLatitude = new Marble::LatLonEdit( widget, Marble::Latitude ); QFormLayout *layout = qobject_cast< QFormLayout* >( ui_provider->tabSamples->layout() ); layout->addRow( i18nc("@info", "Sample &Longitude:"), ui_sampleLongitude ); layout->addRow( i18nc("@info", "Sample &Latitude:"), ui_sampleLatitude ); #endif // Connect all change signals of the widgets to the changed() signal m_mapper = new QSignalMapper( this ); #ifdef BUILD_PROVIDER_TYPE_SCRIPT connect( ui_provider->scriptFile, SIGNAL(textChanged(QString)), m_mapper, SLOT(map()) ); connect( ui_provider->scriptExtensions, SIGNAL(itemChanged(QListWidgetItem*)), m_mapper, SLOT(map()) ); m_mapper->setMapping( ui_provider->scriptFile, ui_provider->scriptFile ); m_mapper->setMapping( ui_provider->scriptExtensions, ui_provider->scriptExtensions ); #endif #ifdef BUILD_PROVIDER_TYPE_GTFS connect( ui_provider->gtfsFeed, SIGNAL(textChanged(QString)), m_mapper, SLOT(map()) ); connect( ui_provider->gtfsTripUpdates, SIGNAL(textChanged(QString)), m_mapper, SLOT(map()) ); connect( ui_provider->gtfsAlerts, SIGNAL(textChanged(QString)), m_mapper, SLOT(map()) ); connect( ui_provider->timeZone, SIGNAL(itemSelectionChanged()), m_mapper, SLOT(map()) ); m_mapper->setMapping( ui_provider->gtfsFeed, ui_provider->gtfsFeed ); m_mapper->setMapping( ui_provider->gtfsTripUpdates, ui_provider->gtfsTripUpdates ); m_mapper->setMapping( ui_provider->gtfsAlerts, ui_provider->gtfsAlerts ); m_mapper->setMapping( ui_provider->timeZone, ui_provider->timeZone ); #endif #ifdef MARBLE_FOUND connect( ui_sampleLongitude, SIGNAL(valueChanged(qreal)), m_mapper, SLOT(map()) ); connect( ui_sampleLatitude, SIGNAL(valueChanged(qreal)), m_mapper, SLOT(map()) ); m_mapper->setMapping( ui_sampleLongitude, ui_sampleLongitude ); m_mapper->setMapping( ui_sampleLatitude, ui_sampleLatitude ); #endif connect( ui_provider->name, SIGNAL(textChanged(QString)), m_mapper, SLOT(map()) ); connect( ui_provider->description, SIGNAL(textChanged()), m_mapper, SLOT(map()) ); connect( ui_provider->version, SIGNAL(textChanged(QString)), m_mapper, SLOT(map()) ); connect( ui_provider->useCityValue, SIGNAL(stateChanged(int)), m_mapper, SLOT(map()) ); connect( ui_provider->onlyAllowPredefinedCities, SIGNAL(stateChanged(int)), m_mapper, SLOT(map()) ); connect( ui_provider->url, SIGNAL(textChanged(QString)), m_mapper, SLOT(map()) ); connect( ui_provider->shortUrl, SIGNAL(textChanged(QString)), m_mapper, SLOT(map()) ); connect( ui_provider->credit, SIGNAL(textChanged(QString)), m_mapper, SLOT(map()) ); connect( ui_provider->minFetchWait, SIGNAL(valueChanged(int)), m_mapper, SLOT(map()) ); connect( ui_provider->author, SIGNAL(textChanged(QString)), m_mapper, SLOT(map()) ); connect( ui_provider->shortAuthor, SIGNAL(textChanged(QString)), m_mapper, SLOT(map()) ); connect( ui_provider->email, SIGNAL(textChanged(QString)), m_mapper, SLOT(map()) ); connect( ui_provider->defaultVehicleType, SIGNAL(currentIndexChanged(int)), m_mapper, SLOT(map()) ); connect( ui_provider->predefinedCities, SIGNAL(changed()), m_mapper, SLOT(map()) ); connect( ui_provider->sampleStopNames, SIGNAL(changed()), m_mapper, SLOT(map()) ); connect( ui_provider->sampleCity, SIGNAL(textChanged(QString)), m_mapper, SLOT(map()) ); connect( m_changelog, SIGNAL(added(QWidget*)), m_mapper, SLOT(map()) ); connect( m_changelog, SIGNAL(removed(QWidget*,int)), m_mapper, SLOT(map()) ); connect( m_changelog, SIGNAL(changed()), m_mapper, SLOT(map()) ); // TODO Map changes in the changelog, ie. changing the version or message text m_mapper->setMapping( ui_provider->name, ui_provider->name ); m_mapper->setMapping( ui_provider->description, ui_provider->description ); m_mapper->setMapping( ui_provider->version, ui_provider->version ); m_mapper->setMapping( ui_provider->useCityValue, ui_provider->useCityValue ); m_mapper->setMapping( ui_provider->onlyAllowPredefinedCities, ui_provider->onlyAllowPredefinedCities ); m_mapper->setMapping( ui_provider->url, ui_provider->url ); m_mapper->setMapping( ui_provider->shortUrl, ui_provider->shortUrl ); m_mapper->setMapping( ui_provider->credit, ui_provider->credit ); m_mapper->setMapping( ui_provider->minFetchWait, ui_provider->minFetchWait ); m_mapper->setMapping( ui_provider->author, ui_provider->author ); m_mapper->setMapping( ui_provider->shortAuthor, ui_provider->shortAuthor ); m_mapper->setMapping( ui_provider->email, ui_provider->email ); m_mapper->setMapping( ui_provider->defaultVehicleType, ui_provider->defaultVehicleType ); m_mapper->setMapping( ui_provider->predefinedCities, ui_provider->predefinedCities ); m_mapper->setMapping( ui_provider->sampleStopNames, ui_provider->sampleStopNames ); m_mapper->setMapping( ui_provider->sampleCity, ui_provider->sampleCity ); m_mapper->setMapping( m_changelog, m_changelog ); connect( m_mapper, SIGNAL(mapped(QWidget*)), this, SLOT(slotChanged(QWidget*)) ); } ProjectSettingsDialog::~ProjectSettingsDialog() { delete ui_provider; } void ProjectSettingsDialog::slotChanged( QWidget *changedWidget ) { #ifdef BUILD_PROVIDER_TYPE_SCRIPT if( changedWidget == ui_provider->scriptFile ) { // Script file changed const QString fileName = ui_provider->scriptFile->text(); ui_provider->btnCreateScriptFile->setVisible( fileName.isEmpty() ); ui_provider->btnDetachScriptFile->setVisible( !fileName.isEmpty() ); emit scriptFileChanged( fileName ); } else #endif if( changedWidget == ui_provider->url ) { // Home page URL changed ui_provider->btnUrlOpen->setDisabled( ui_provider->url->text().isEmpty() ); } else if( changedWidget == ui_provider->shortAuthor ) { // Short author name changed, update changed log click messages QList entryWidgets = m_changelog->entryWidgets(); foreach( const ChangelogEntryWidget * entryWidget, entryWidgets ) { entryWidget->authorLineEdit()->setClickMessage( m_providerData->shortAuthor() ); } } fillValuesFromWidgets(); emit changed(); } bool ProjectSettingsDialog::check() { bool result = testWidget( ui_provider->email ); result = testWidget( ui_provider->version ) && result; result = testWidget( ui_provider->name ) && result; result = testWidget( ui_provider->description ) && result; result = testWidget( ui_provider->author ) && result; result = testWidget( ui_provider->shortAuthor ) && result; result = testWidget( ui_provider->url ) && result; result = testWidget( ui_provider->shortUrl ) && result; return result; } void ProjectSettingsDialog::slotButtonClicked( int button ) { if ( button == KDialog::Ok ) { fillValuesFromWidgets(); accept(); } else if ( button == KDialog::User1 ) { if ( check() ) { KMessageWidget *messageWidget = new KMessageWidget( i18nc("@info", "All settings are valid"), this ); messageWidget->setMessageType( KMessageWidget::Positive ); mainWidget()->layout()->addWidget( messageWidget ); messageWidget->animatedShow(); // Install an event filter to delete the message widget after the widget was hidden messageWidget->installEventFilter( this ); // Hide after 4 seconds QTimer::singleShot( 4000, messageWidget, SLOT(animatedHide()) ); } } else { KDialog::slotButtonClicked( button ); } } void ProjectSettingsDialog::accept() { // if ( check() ) { QDialog::accept(); // } } bool ProjectSettingsDialog::eventFilter( QObject *object, QEvent *event ) { QWidget *widget = qobject_cast< QWidget* >( object ); if ( widget && event->type() == QEvent::FocusOut ) { testWidget( widget ); } KMessageWidget *messageWidget = qobject_cast< KMessageWidget*> ( object ); if ( messageWidget && event->type() == QEvent::Hide ) { // Delete message widgets after they are hidden messageWidget->deleteLater(); } return QDialog::eventFilter( object, event ); } TestModel::Test testFromWidget( Ui::timetablemateview_base *ui, QWidget *widget ) { if ( widget == ui->email ) { return TestModel::ServiceProviderDataEmailTest; } else if ( widget == ui->name ) { return TestModel::ServiceProviderDataNameTest; } else if ( widget == ui->version ) { return TestModel::ServiceProviderDataVersionTest; } else if ( widget == ui->author ) { return TestModel::ServiceProviderDataAuthorNameTest; } else if ( widget == ui->shortAuthor ) { return TestModel::ServiceProviderDataShortAuthorNameTest; } else if ( widget == ui->url ) { return TestModel::ServiceProviderDataUrlTest; } else if ( widget == ui->shortUrl ) { return TestModel::ServiceProviderDataShortUrlTest; } else if ( widget == ui->scriptFile ) { return TestModel::ServiceProviderDataScriptFileNameTest; } else if ( widget == ui->gtfsFeed ) { return TestModel::ServiceProviderDataGtfsFeedUrlTest; } else if ( widget == ui->description ) { return TestModel::ServiceProviderDataDescriptionTest; } else { qWarning() << "Unknown widget"; return TestModel::InvalidTest; } } bool ProjectSettingsDialog::testWidget( QWidget *widget ) { TestModel::Test test = testFromWidget( ui_provider, widget ); if ( test == TestModel::InvalidTest ) { // Unknown widget return true; } QString text; KLineEdit *lineEdit = qobject_cast< KLineEdit* >( widget ); if ( lineEdit ) { text = lineEdit->text(); } else { KRichTextWidget *richText = qobject_cast< KRichTextWidget* >( widget ); if ( richText ) { text = richText->textOrHtml(); } else { qWarning() << "Unknown widget type"; return false; } } QString errorMessage; if ( !ServiceProviderDataTester::runServiceProviderDataTest(test, text, &errorMessage) ) { appendMessageWidgetAfter( widget, errorMessage ); return false; } return true; } void ProjectSettingsDialog::appendMessageWidgetAfter( QWidget *after, const QString &errorMessage ) { if ( !after || !after->isVisible() ) { // The widget after which the error message should be shown is not visible, // ie. it is not in the current tab, use the last widget of the current tab instead QWidget *tab = ui_provider->tabWidget->currentWidget(); after = tab->layout()->itemAt( tab->layout()->count() - 1 )->widget(); if ( !after ) { qWarning() << "Could not find last widget in current tab to show this error message:" << errorMessage; return; } } QFormLayout *formLayout = qobject_cast< QFormLayout* >( after->parentWidget()->layout() ); Q_ASSERT( formLayout ); // Get the position of the QWidget after which the message widget should be inserted int row; QFormLayout::ItemRole role; formLayout->getWidgetPosition( after, &row, &role ); // Check if there already is a KMessageWidget QLayoutItem *item = row == -1 ? 0 : formLayout->itemAt(row + 1, QFormLayout::FieldRole); KMessageWidget *messageWidget = !item ? 0 : qobject_cast< KMessageWidget* >( item->widget() ); // Check if a message was already shown if ( messageWidget ) { // Found an existing KMessageWidget after the widget, // update it instead of creating a new one messageWidget->setText( errorMessage ); } else { // Create a message widget showing where the error is messageWidget = new KMessageWidget( errorMessage, after->parentWidget() ); messageWidget->setMessageType( KMessageWidget::Error ); // Insert the message widget after the widget with the erroneous content formLayout->insertRow( row + 1, messageWidget ); messageWidget->animatedShow(); // Install an event filter to delete the message widget after the widget was hidden messageWidget->installEventFilter( this ); // Hide after 4 seconds QTimer::singleShot( 4000, messageWidget, SLOT(animatedHide()) ); } } void ProjectSettingsDialog::fillValuesFromWidgets() { // Fill struct with current values of the widgets QString lang = ui_provider->currentLanguage->current(); if( lang == QLatin1String("en_US") ) { lang = "en"; } QHash< QString, QString > names = m_providerData->names(); QHash< QString, QString > descriptions = m_providerData->descriptions(); names[lang] = ui_provider->name->text(); descriptions[lang] = ui_provider->description->toPlainText(); const Enums::VehicleType defaultVehicleType = Global::vehicleTypeFromString( ui_provider->defaultVehicleType->itemData( ui_provider->defaultVehicleType->currentIndex()).toString() ); QStringList cities; QHash< QString, QString > cityNameReplacements; const QStringList cityReplacements = ui_provider->predefinedCities->items(); foreach( const QString & cityReplacement, cityReplacements ) { QStringList values = cityReplacement.split( " -> " ); if( values.count() == 2 ) { cities << values.at( 0 ); cityNameReplacements.insert( values.at(0).toLower(), values.at(1) ); } else { cities << cityReplacement; } } // Update values that can be edited in this dialog m_providerData->setNames( names ); m_providerData->setDescriptions( descriptions ); m_providerData->setVersion( ui_provider->version->text() ); m_providerData->setFileFormatVersion( "1.1" ); // Update to current format version m_providerData->setUseSeparateCityValue( ui_provider->useCityValue->isChecked() ); m_providerData->setOnlyUseCitiesInList( ui_provider->onlyAllowPredefinedCities->isChecked() ); m_providerData->setUrl( ui_provider->url->text(), ui_provider->shortUrl->text() ); m_providerData->setCredit( ui_provider->credit->text() ); m_providerData->setMinFetchWait( ui_provider->minFetchWait->value() ); m_providerData->setAuthor( ui_provider->author->text(), ui_provider->shortAuthor->text(), ui_provider->email->text() ); m_providerData->setDefaultVehicleType( defaultVehicleType ); m_providerData->setChangelog( m_changelog->changelog() ); m_providerData->setCities( cities ); m_providerData->setCityNameToValueReplacementHash( cityNameReplacements ); m_providerData->setSampleCity( ui_provider->sampleCity->text() ); m_providerData->setSampleStops( ui_provider->sampleStopNames->items() ); #ifdef MARBLE_FOUND m_providerData->setSampleCoordinates( ui_sampleLongitude->value(), ui_sampleLatitude->value() ); #endif m_providerData->setNotes( ui_provider->notes->textOrHtml() ); switch ( m_providerData->type() ) { #ifdef BUILD_PROVIDER_TYPE_SCRIPT case Enums::ScriptedProvider: m_providerData->setScriptFile( ui_provider->scriptFile->text(), scriptExtensionsFromWidget() ); break; #endif #ifdef BUILD_PROVIDER_TYPE_GTFS case Enums::GtfsProvider: m_providerData->setFeedUrl( ui_provider->gtfsFeed->text() ); m_providerData->setRealtimeTripUpdateUrl( ui_provider->gtfsTripUpdates->text() ); m_providerData->setRealtimeAlertsUrl( ui_provider->gtfsAlerts->text() ); m_providerData->setTimeZone( ui_provider->timeZone->selection().isEmpty() ? QString() : ui_provider->timeZone->selection().first() ); break; #endif default: break; } } #ifdef BUILD_PROVIDER_TYPE_SCRIPT QStringList ProjectSettingsDialog::scriptExtensionsFromWidget() const { QStringList scriptExtensions; for ( int i = 0; i < ui_provider->scriptExtensions->count(); ++i ) { QListWidgetItem *item = ui_provider->scriptExtensions->item( i ); if ( item->checkState() == Qt::Checked ) { scriptExtensions << item->text(); } } return scriptExtensions; } void ProjectSettingsDialog::checkScriptExtensionsInWidget( const QStringList &scriptExtensions ) const { for ( int i = 0; i < ui_provider->scriptExtensions->count(); ++i ) { QListWidgetItem *item = ui_provider->scriptExtensions->item( i ); item->setCheckState( scriptExtensions.contains(item->text()) ? Qt::Checked : Qt::Unchecked ); } } #endif void ProjectSettingsDialog::changelogEntryWidgetAdded( ChangelogEntryWidget *entryWidget ) { const int comparison = ServiceProviderData::compareVersions( entryWidget->version(), ui_provider->version->text()); if ( comparison > 0 ) { int result = KMessageBox::questionYesNo( this, i18nc("@info", "The new changelog entry references a newer version than the " "current project version. Do you want to update the project version to %1?", entryWidget->version()) ); if ( result == KMessageBox::Yes ) { // Yes clicked, update version value ui_provider->version->setText( entryWidget->version() ); } } // Use short author name as author is none is set if ( entryWidget->author().isEmpty() ) { entryWidget->setAuthor( ui_provider->shortAuthor->text() ); } // Use "Initial version" as default changelog text for the first entry if ( m_changelog->entryWidgets().count() == 1 ) { entryWidget->setDescription( i18nc("@info/plain Default changelog entry for the " "first version", "Initial version") ); } } void ProjectSettingsDialog::authorEdited( const QString &newAuthor ) { if ( ui_provider->shortAuthor->text().isEmpty() || m_shortAuthorAutoFilled ) { // Update short author value if it is empty m_shortAuthorAutoFilled = true; // Set to auto filled ui_provider->shortAuthor->setText( ServiceProviderData::shortAuthorFromAuthor(newAuthor) ); } } void ProjectSettingsDialog::urlEdited( const QString &newUrl ) { if ( ui_provider->shortUrl->text().isEmpty() || m_shortUrlAutoFilled ) { // Update short URL value if it is empty m_shortUrlAutoFilled = true; // Set to auto filled ui_provider->shortUrl->setText( ServiceProviderData::shortUrlFromUrl(newUrl) ); } } void ProjectSettingsDialog::shortAuthorEdited( const QString &shortAuthor ) { Q_UNUSED( shortAuthor ); m_shortAuthorAutoFilled = false; } void ProjectSettingsDialog::shortUrlEdited( const QString &shortUrl ) { Q_UNUSED( shortUrl ); m_shortUrlAutoFilled = false; } void ProjectSettingsDialog::currentPredefinedCityChanged( const QString ¤tCityText ) { QStringList values = currentCityText.split( " -> " ); if( values.count() == 2 ) { m_cityName->blockSignals( true ); m_cityReplacement->blockSignals( true ); m_cityName->setText( values.at( 0 ) ); m_cityReplacement->setText( values.at( 1 ) ); m_cityName->blockSignals( false ); m_cityReplacement->blockSignals( false ); } else { m_cityName->blockSignals( true ); m_cityReplacement->blockSignals( true ); m_cityName->setText( currentCityText ); m_cityReplacement->setText( QString() ); m_cityName->blockSignals( false ); m_cityReplacement->blockSignals( false ); } } void ProjectSettingsDialog::predefinedCityNameChanged( const QString &newCityName ) { QString text = newCityName; if( !m_cityReplacement->text().isEmpty() ) { text += " -> " + m_cityReplacement->text(); } m_predefinedCitiesCustomEditor.lineEdit()->setText( text ); } void ProjectSettingsDialog::predefinedCityReplacementChanged( const QString &newReplacement ) { QString text = m_cityName->text(); if( !newReplacement.isEmpty() ) { text += " -> " + newReplacement; } m_predefinedCitiesCustomEditor.lineEdit()->setText( text ); } void ProjectSettingsDialog::languageActivated( const QString &languageCode ) { QString code = languageCode == QLatin1String("en_US") ? "en" : languageCode; ui_provider->name->blockSignals( true ); ui_provider->name->setText( m_providerData->names()[code] ); ui_provider->name->blockSignals( false ); ui_provider->description->blockSignals( true ); ui_provider->description->setText( m_providerData->descriptions()[code] ); ui_provider->description->blockSignals( false ); } void ProjectSettingsDialog::openUrlClicked() { emit urlShouldBeOpened( ui_provider->url->text() ); } #ifdef BUILD_PROVIDER_TYPE_SCRIPT void ProjectSettingsDialog::createScriptFile() { if( m_openedPath.isEmpty() ) { KMessageBox::information( this, i18nc("@info/plain", "Please save the XML file first. " "The script file needs to be in the same folder.") ); return; } // Get a name for the new script file based on the current country code // and the current service provider ID QString scriptFile = m_providerData->id() + ".js"; // Get fileName for the new script file QString scriptFilePath = QUrl( m_openedPath ).directory( QUrl::AppendTrailingSlash ) + scriptFile; // Check if the file already exists QFile file( scriptFilePath ); if( file.exists() ) { int result = KMessageBox::questionYesNoCancel( this, i18nc("@info/plain", "The script file %1 already exists." "Do you want to overwrite it or open and use it as script file?", scriptFile), i18nc("@title:window", "File Already Exists"), KStandardGuiItem::overwrite(), KStandardGuiItem::open() ); if( result == KMessageBox::No ) { // open ui_provider->scriptFile->setText( scriptFile ); return; } else if( result == KMessageBox::Cancel ) { return; } } // Create the file if( !file.open( QIODevice::WriteOnly ) ) { KMessageBox::information( this, i18nc("@info/plain", "A new script file with the name " "%1 could not be created.", scriptFilePath) ); return; } file.close(); const QString scriptType = KInputDialog::getItem( i18nc("@title:window", "Choose Script Type"), i18nc("@info", "Script Type"), QStringList() << "JavaScript" << "Ruby" << "Python", 0, false, 0, this ); if( scriptType == QLatin1String("JavaScript") ) { m_newScriptTemplateType = Project::ScriptQtScriptTemplate; } else if( scriptType == QLatin1String("Ruby") ) { m_newScriptTemplateType = Project::ScriptRubyTemplate; } else if( scriptType == QLatin1String("Python") ) { m_newScriptTemplateType = Project::ScriptPythonTemplate; } else { qWarning() << "Unexpected script type" << scriptType; return; } ui_provider->scriptFile->setText( scriptFile ); emit scriptAdded( scriptFilePath ); } void ProjectSettingsDialog::detachScriptFile() { ui_provider->scriptFile->setText( QString() ); ui_provider->scriptExtensions->clear(); m_newScriptTemplateType = Project::NoScriptTemplate; } void ProjectSettingsDialog::browseForScriptFile() { if( m_openedPath.isEmpty() ) { KMessageBox::information( this, i18nc( "@info/plain", "Please save the " "XML file first. The script file needs to be in the same folder." ) ); return; } QUrl openedUrl( m_openedPath ); // Get a list of all script files in the directory of the XML file QStringList scriptFiles; int current = -1; QDir dir( openedUrl.adjusted(QUrl::RemoveFilename|QUrl::StripTrailingSlash).path() ); QStringList fileNames = dir.entryList(); for( int i = 0; i < fileNames.count(); ++i ) { QString fileName = fileNames.at( i ); - KMimeType::Ptr mimeType = KMimeType::findByUrl( QUrl( fileName ) ); - if( mimeType->is("application/javascript") || - mimeType->is("application/x-ruby") || - mimeType->is("text/x-python") ) +QMimeDatabase db; + QMimeType mimeType = db.mimeTypeForUrl( QUrl( fileName ) ); + if( mimeType.inherits("application/javascript") || + mimeType.inherits("application/x-ruby") || + mimeType.inherits("text/x-python") ) { scriptFiles << fileName; if( fileName == ui_provider->scriptFile->text() ) { current = i; } } } bool ok; QString selectedFile = KInputDialog::getItem( i18nc( "@title:window", "Choose Script File" ), i18nc( "@info", "Script File for Parsing Documents" ), scriptFiles, current, false, &ok, this ); if( ok ) { ui_provider->scriptFile->setText( selectedFile ); } } void ProjectSettingsDialog::setScriptFile( const QString &scriptFile ) { ui_provider->scriptFile->setText( scriptFile ); } #endif // BUILD_PROVIDER_TYPE_SCRIPT const ServiceProviderData *ProjectSettingsDialog::providerData( QObject *parent ) const { return m_providerData->clone(parent); } void ProjectSettingsDialog::setProviderData( const ServiceProviderData *data, const QString &fileName ) { // Disable changed signals from widgets while setting the read values m_mapper->blockSignals( true ); m_shortAuthorAutoFilled = false; m_shortUrlAutoFilled = false; m_providerData = data->clone( data->parent() ); m_openedPath = fileName; // Show settings widgets specific to the provider type ui_provider->scriptSettingsWidget->setVisible( data->type() == Enums::ScriptedProvider ); ui_provider->gtfsSettingsWidget->setVisible( data->type() == Enums::GtfsProvider ); if ( data->fileFormatVersion() != QLatin1String("1.1") ) { KMessageBox::information( this, i18nc("@info", "Invalid Provider Plugin Format Version" "The provider plugin format version has been set to " "1.1, because the specified version " "%1 is not supported." "Please make sure that the plugin complies with that version " "and update it if necessary." "You can cancel the settings dialog to not change anything." "", data->fileFormatVersion()) ); } #ifdef BUILD_PROVIDER_TYPE_SCRIPT ui_provider->scriptFile->setText( data->scriptFileName() ); checkScriptExtensionsInWidget( data->scriptExtensions() ); #endif #ifdef BUILD_PROVIDER_TYPE_GTFS ui_provider->gtfsFeed->setText( data->feedUrl() ); ui_provider->gtfsTripUpdates->setText( data->realtimeTripUpdateUrl() ); ui_provider->gtfsAlerts->setText( data->realtimeAlertsUrl() ); QString errorMessage; if ( ServiceProviderDataTester::isTimeZoneValid(data->timeZone(), &errorMessage) == TestModel::TestFinishedSuccessfully ) { // Valid time zone name ui_provider->timeZone->setSelected( data->timeZone(), true ); } else { // No time zone with that name found appendMessageWidgetAfter( ui_provider->timeZone, errorMessage ); } #endif ui_provider->savePath->setText( fileName ); ui_provider->currentLanguage->setCurrentItem( "en" ); ui_provider->name->setText( data->names()["en"] ); ui_provider->description->setText( data->descriptions()["en"] ); ui_provider->version->setText( data->version() ); ui_provider->useCityValue->setChecked( data->useSeparateCityValue() ); ui_provider->onlyAllowPredefinedCities->setChecked( data->onlyUseCitiesInList() ); ui_provider->url->setText( data->url() ); ui_provider->shortUrl->setText( data->shortUrl() ); ui_provider->credit->setText( data->credit() ); ui_provider->minFetchWait->setValue( data->minFetchWait() ); ui_provider->shortAuthor->setText( data->shortAuthor() ); if ( data->author().isEmpty() ) { // Insert user name if empty (and if set in global KDE settings) ui_provider->author->setText( KUser().property(KUser::FullName).toString() ); // If no short author string was chosen create one from the full name if ( data->shortAuthor().isEmpty() ) { ui_provider->shortAuthor->setText( ServiceProviderData::shortAuthorFromAuthor(ui_provider->author->text()) ); } } else { ui_provider->author->setText( data->author() ); } if ( data->email().isEmpty() ) { // Insert user email if empty (and if set in global KDE settings) ui_provider->email->setText( KEMailSettings().getSetting(KEMailSettings::EmailAddress) ); } else { ui_provider->email->setText( data->email() ); } int defaultVehicleTypeIndex = ui_provider->defaultVehicleType->findData( Enums::toString(data->defaultVehicleType()) ); ui_provider->defaultVehicleType->setCurrentIndex( defaultVehicleTypeIndex > 0 ? defaultVehicleTypeIndex : 0 ); m_changelog->clear(); m_changelog->addChangelog( data->changelog(), data->shortAuthor() ); ui_provider->predefinedCities->clear(); foreach( const QString & city, data->cities() ) { QString lowerCity = city.toLower(); if( data->cityNameToValueReplacementHash().contains( lowerCity ) ) { ui_provider->predefinedCities->insertItem( city + " -> " + data->cityNameToValueReplacementHash()[lowerCity] ); } else { ui_provider->predefinedCities->insertItem( city ); } } ui_provider->sampleStopNames->setItems( data->sampleStopNames() ); ui_provider->sampleCity->setText( data->sampleCity() ); #ifdef MARBLE_FOUND ui_sampleLongitude->setValue( data->sampleLongitude() ); ui_sampleLatitude->setValue( data->sampleLatitude() ); #endif ui_provider->notes->setText( data->notes() ); // Enable changed signals from widgets again and emit changed signals once m_mapper->blockSignals( false ); emit changed(); #ifdef BUILD_PROVIDER_TYPE_SCRIPT emit scriptFileChanged( fileName ); #endif } void ProjectSettingsDialog::settingsChanged() { emit signalChangeStatusbar( i18n("Settings changed") ); } #include "projectsettingsdialog.moc" // kate: indent-mode cstyle; indent-width 4; replace-tabs on;