20 #include <QSignalMapper>
21 #include <QStringListModel>
23 #include <QPushButton>
25 #include <QSortFilterProxyModel>
26 #include <QMessageBox>
27 #include <QNetworkAccessManager>
28 #include <QNetworkReply>
29 #include <QDomDocument>
37 void setPayload(
const QString &payload );
39 QString payload()
const;
41 void setName(
const QString &name );
47 QString continent()
const;
49 QString state()
const;
51 QString region()
const;
53 QString transport()
const;
69 class MonavConfigWidgetPrivate
72 MonavConfigWidget* m_parent;
74 MonavPlugin* m_plugin;
76 QNetworkAccessManager m_networkAccessManager;
78 QNetworkReply* m_currentReply;
80 QProcess* m_unpackProcess;
82 QSortFilterProxyModel* m_filteredModel;
84 MonavMapsModel* m_mapsModel;
88 QSignalMapper m_removeMapSignalMapper;
90 QSignalMapper m_upgradeMapSignalMapper;
92 QVector<MonavStuffEntry> m_remoteMaps;
94 QMap<QString, QString> m_remoteVersions;
96 QString m_currentDownload;
102 MonavConfigWidgetPrivate( MonavConfigWidget* parent, MonavPlugin* plugin );
104 void parseNewStuff(
const QByteArray &data );
106 bool updateContinents( QComboBox* comboBox );
108 bool updateStates(
const QString &continent, QComboBox* comboBox );
110 bool updateRegions(
const QString &continent,
const QString &state, QComboBox* comboBox );
112 MonavStuffEntry map(
const QString &continent,
const QString &state,
const QString ®ion )
const;
118 void updateInstalledMapsView();
120 void updateInstalledMapsViewButtons();
122 void updateTransportPreference();
124 bool canExecute(
const QString &executable )
const;
126 void setBusy(
bool busy,
const QString &message = QString() )
const;
129 bool fillComboBox( QStringList items, QComboBox* comboBox )
const;
132 void MonavStuffEntry::setPayload(
const QString &payload )
137 QString MonavStuffEntry::payload()
const
142 void MonavStuffEntry::setName(
const QString &name )
145 QStringList parsed = name.split( QLatin1Char(
'/' ) );
146 int size = parsed.size();
147 m_continent = size > 0 ? parsed.at( 0 ).trimmed() : QString();
148 m_state = size > 1 ? parsed.at( 1 ).trimmed() : QString();
149 m_region = size > 2 ? parsed.at( 2 ).trimmed() : QString();
150 m_transport =
"Motorcar";
153 QString last = parsed.last().trimmed();
154 QRegExp regexp(
"([^(]+)\\(([^)]+)\\)");
155 if ( regexp.indexIn( last ) >= 0 ) {
156 QStringList matches = regexp.capturedTexts();
157 if ( matches.size() == 3 ) {
158 m_transport = matches.at( 2 ).trimmed();
160 m_region = matches.at( 1 ).trimmed();
162 m_state = matches.at( 1 ).trimmed();
169 QString MonavStuffEntry::name()
const
174 QString MonavStuffEntry::continent()
const
179 QString MonavStuffEntry::state()
const
184 QString MonavStuffEntry::region()
const
189 QString MonavStuffEntry::transport()
const
194 bool MonavStuffEntry::isValid()
const
196 return !m_continent.isEmpty() && !m_state.isEmpty() && m_payload.startsWith( QLatin1String(
"http://" ) );
199 MonavConfigWidgetPrivate::MonavConfigWidgetPrivate( MonavConfigWidget* parent, MonavPlugin* plugin ) :
200 m_parent( parent ), m_plugin( plugin ), m_networkAccessManager( 0 ),
201 m_currentReply( 0 ), m_unpackProcess( 0 ), m_filteredModel( new QSortFilterProxyModel( parent) ),
202 m_mapsModel( 0 ), m_initialized( false )
204 m_filteredModel->setFilterKeyColumn( 1 );
207 void MonavConfigWidgetPrivate::parseNewStuff(
const QByteArray &data )
210 if ( !xml.setContent( data ) ) {
211 mDebug() <<
"Cannot parse xml file " << data;
215 QDomElement root = xml.documentElement();
216 QDomNodeList items = root.elementsByTagName(
"stuff" );
217 for (
unsigned int i = 0; i < items.length(); ++i ) {
218 MonavStuffEntry item;
219 QDomNode node = items.item( i );
221 QDomNodeList names = node.toElement().elementsByTagName(
"name" );
222 if ( names.size() == 1 ) {
223 item.setName( names.at( 0 ).toElement().text() );
227 QDomNodeList dates = node.toElement().elementsByTagName(
"releasedate" );
228 if ( dates.size() == 1 ) {
229 releaseDate = dates.at( 0 ).toElement().text();
233 QDomNodeList payloads = node.toElement().elementsByTagName(
"payload" );
234 if ( payloads.size() == 1 ) {
235 QString payload = payloads.at( 0 ).toElement().text();
236 filename = payload.mid( 1 + payload.lastIndexOf(
"/" ) );
237 item.setPayload( payload );
240 if ( item.isValid() ) {
241 m_remoteMaps.push_back( item );
242 if ( !filename.isEmpty() && !releaseDate.isEmpty() ) {
243 m_remoteVersions[filename] = releaseDate;
248 m_mapsModel->setInstallableVersions( m_remoteVersions );
249 updateInstalledMapsViewButtons();
252 bool MonavConfigWidgetPrivate::fillComboBox( QStringList items, QComboBox* comboBox )
const
256 comboBox->addItems( items );
257 return !items.isEmpty();
260 bool MonavConfigWidgetPrivate::updateContinents( QComboBox* comboBox )
262 QSet<QString> continents;
263 foreach(
const MonavStuffEntry &map, m_remoteMaps ) {
264 Q_ASSERT( map.isValid() );
265 continents << map.continent();
268 return fillComboBox( continents.toList(), comboBox );
271 bool MonavConfigWidgetPrivate::updateStates(
const QString &continent, QComboBox* comboBox )
273 QSet<QString> states;
274 foreach(
const MonavStuffEntry &map, m_remoteMaps ) {
275 Q_ASSERT( map.isValid() );
276 if ( map.continent() == continent ) {
277 states << map.state();
281 return fillComboBox( states.toList(), comboBox );
284 bool MonavConfigWidgetPrivate::updateRegions(
const QString &continent,
const QString &state, QComboBox* comboBox )
287 QMap<QString,QString> regions;
288 foreach(
const MonavStuffEntry &map, m_remoteMaps ) {
289 Q_ASSERT( map.isValid() );
290 if ( map.continent() == continent && map.state() == state ) {
291 QString item =
"%1 - %2";
292 if ( map.region().isEmpty() ) {
293 item = item.arg( map.state() );
294 comboBox->addItem( item.arg( map.transport() ), map.payload() );
296 item = item.arg( map.region(), map.transport() );
297 regions.insert( item, map.payload() );
302 QMapIterator<QString, QString> iter( regions );
303 while ( iter.hasNext() ) {
305 comboBox->addItem( iter.key(), iter.value() );
311 MonavStuffEntry MonavConfigWidgetPrivate::map(
const QString &continent,
const QString &state,
const QString ®ion )
const
313 foreach(
const MonavStuffEntry &entry, m_remoteMaps ) {
314 if ( continent == entry.continent() && state == entry.state() && region == entry.region() ) {
319 return MonavStuffEntry();
323 d( new MonavConfigWidgetPrivate( this, plugin ) )
327 m_statusLabel->setHidden( m_statusLabel->text().isEmpty() );
329 m_installedMapsListView->setModel( d->m_mapsModel );
330 m_configureMapsListView->setModel( d->m_filteredModel );
331 m_configureMapsListView->resizeColumnsToContents();
335 connect( m_continentComboBox, SIGNAL(currentIndexChanged(
int)),
336 this, SLOT(updateStates()) );
337 connect( m_transportTypeComboBox, SIGNAL(currentIndexChanged(QString)),
338 this, SLOT(updateTransportTypeFilter(QString)) );
339 connect( m_stateComboBox, SIGNAL(currentIndexChanged(
int)),
340 this, SLOT(updateRegions()) );
341 connect( m_installButton, SIGNAL(clicked()),
this, SLOT(downloadMap()) );
342 connect( m_cancelButton, SIGNAL(clicked()),
this, SLOT(cancelOperation()) );
343 connect( &d->m_removeMapSignalMapper, SIGNAL(mapped(
int)),
344 this, SLOT(removeMap(
int)) );
345 connect( &d->m_upgradeMapSignalMapper, SIGNAL(mapped(
int)),
346 this, SLOT(upgradeMap(
int)) );
347 connect( &d->m_networkAccessManager, SIGNAL(finished(QNetworkReply*)),
348 this, SLOT(retrieveMapList(QNetworkReply*)) );
358 d->m_transport = settings[
"transport"].toString();
359 d->updateTransportPreference();
362 void MonavConfigWidgetPrivate::updateTransportPreference()
364 if ( m_parent->m_transportTypeComboBox && m_mapsModel ) {
365 m_parent->m_transportTypeComboBox->blockSignals(
true );
366 m_parent->m_transportTypeComboBox->clear();
367 QSet<QString> transportTypes;
368 for(
int i=0; i<m_mapsModel->rowCount(); ++i ) {
369 QModelIndex index = m_mapsModel->index( i, 1 );
370 transportTypes << m_mapsModel->data( index ).toString();
372 m_parent->m_transportTypeComboBox->addItems( transportTypes.toList() );
373 m_parent->m_transportTypeComboBox->blockSignals(
false );
375 if ( !m_transport.isEmpty() && m_parent->m_transportTypeComboBox ) {
376 for (
int i=1; i<m_parent->m_transportTypeComboBox->count(); ++i ) {
377 if ( m_parent->m_transportTypeComboBox->itemText( i ) == m_transport ) {
378 m_parent->m_transportTypeComboBox->setCurrentIndex( i );
389 settings[
"transport"] = d->m_transport;
393 void MonavConfigWidget::retrieveMapList( QNetworkReply *reply )
395 if ( reply->isReadable() && d->m_currentDownload.isEmpty() ) {
397 QVariant
const redirectionAttribute = reply->attribute( QNetworkRequest::RedirectionTargetAttribute );
398 if ( !redirectionAttribute.isNull() ) {
399 d->m_networkAccessManager.get( QNetworkRequest( redirectionAttribute.toUrl() ) );
401 disconnect( &d->m_networkAccessManager, SIGNAL(finished(QNetworkReply*)),
402 this, SLOT(retrieveMapList(QNetworkReply*)) );
403 d->parseNewStuff( reply->readAll() );
409 void MonavConfigWidget::retrieveData()
411 if ( d->m_currentReply && d->m_currentReply->isReadable() && !d->m_currentDownload.isEmpty() ) {
413 QVariant
const redirectionAttribute = d->m_currentReply->attribute( QNetworkRequest::RedirectionTargetAttribute );
414 if ( !redirectionAttribute.isNull() ) {
415 d->m_currentReply = d->m_networkAccessManager.get( QNetworkRequest( redirectionAttribute.toUrl() ) );
416 connect( d->m_currentReply, SIGNAL(readyRead()),
417 this, SLOT(retrieveData()) );
418 connect( d->m_currentReply, SIGNAL(readChannelFinished()),
419 this, SLOT(retrieveData()) );
420 connect( d->m_currentReply, SIGNAL(downloadProgress(qint64,qint64)),
421 this, SLOT(updateProgressBar(qint64,qint64)) );
423 d->m_currentFile.write( d->m_currentReply->readAll() );
424 if ( d->m_currentReply->isFinished() ) {
425 d->m_currentReply->deleteLater();
426 d->m_currentReply = 0;
427 d->m_currentFile.close();
429 d->m_currentDownload.clear();
435 void MonavConfigWidget::updateComboBoxes()
437 d->updateContinents( m_continentComboBox );
442 void MonavConfigWidget::updateStates()
444 bool const haveContinents = m_continentComboBox->currentIndex() >= 0;
445 if ( haveContinents ) {
446 QString
const continent = m_continentComboBox->currentText();
447 if ( d->updateStates( continent, m_stateComboBox ) ) {
453 void MonavConfigWidget::updateRegions()
455 bool haveRegions =
false;
456 if ( m_continentComboBox->currentIndex() >= 0 ) {
457 if ( m_stateComboBox->currentIndex() >= 0 ) {
458 QString
const continent = m_continentComboBox->currentText();
459 QString
const state = m_stateComboBox->currentText();
460 haveRegions = d->updateRegions( continent, state, m_regionComboBox );
464 m_regionLabel->setVisible( haveRegions );
465 m_regionComboBox->setVisible( haveRegions );
468 void MonavConfigWidget::downloadMap()
470 if ( d->m_currentDownload.isEmpty() && !d->m_currentFile.isOpen() ) {
471 d->m_currentDownload = m_regionComboBox->itemData( m_regionComboBox->currentIndex() ).toString();
476 void MonavConfigWidget::cancelOperation()
478 if ( !d->m_currentDownload.isEmpty() || d->m_currentFile.isOpen() ) {
479 d->m_currentReply->abort();
480 d->m_currentReply->deleteLater();
481 d->m_currentReply = 0;
482 d->m_currentDownload.clear();
484 d->m_currentFile.close();
488 void MonavConfigWidgetPrivate::install()
490 if ( !m_currentDownload.isEmpty() ) {
491 int const index = m_currentDownload.lastIndexOf(
"/");
493 m_currentFile.setFileName( localFile );
494 if ( m_currentFile.open( QFile::WriteOnly ) ) {
495 QFileInfo file( m_currentFile );
496 QString message = QObject::tr(
"Downloading %1" ).arg( file.fileName() );
497 setBusy(
true, message );
498 m_currentReply = m_networkAccessManager.get( QNetworkRequest( m_currentDownload ) );
499 QObject::connect( m_currentReply, SIGNAL(readyRead()),
500 m_parent, SLOT(retrieveData()) );
501 QObject::connect( m_currentReply, SIGNAL(readChannelFinished()),
502 m_parent, SLOT(retrieveData()) );
503 QObject::connect( m_currentReply, SIGNAL(downloadProgress(qint64,qint64)),
504 m_parent, SLOT(updateProgressBar(qint64,qint64)) );
506 mDebug() <<
"Failed to write to " << localFile;
511 void MonavConfigWidgetPrivate::installMap()
513 if ( m_unpackProcess ) {
514 m_unpackProcess->close();
515 delete m_unpackProcess;
517 m_parent->m_installButton->setEnabled(
true );
518 }
else if ( m_currentFile.fileName().endsWith( QLatin1String(
"tar.gz" ) ) && canExecute(
"tar" ) ) {
519 QFileInfo file( m_currentFile );
520 QString message = QObject::tr(
"Installing %1" ).arg( file.fileName() );
521 setBusy(
true, message );
522 m_parent->m_progressBar->setMaximum( 0 );
523 if ( file.exists() && file.isReadable() ) {
524 m_unpackProcess =
new QProcess;
525 QObject::connect( m_unpackProcess, SIGNAL(finished(
int)),
526 m_parent, SLOT(mapInstalled(
int)) );
527 QStringList arguments = QStringList() <<
"-x" <<
"-z" <<
"-f" << file.fileName();
528 m_unpackProcess->setWorkingDirectory( file.dir().absolutePath() );
529 m_unpackProcess->start(
"tar", arguments );
532 if ( !m_currentFile.fileName().endsWith( QLatin1String(
"tar.gz" ) ) ) {
533 mDebug() <<
"Can only handle tar.gz files";
535 mDebug() <<
"Cannot extract archive: tar executable not found in PATH.";
540 void MonavConfigWidget::updateProgressBar( qint64 bytesReceived, qint64 bytesTotal )
544 m_progressBar->setMaximum( bytesTotal / 1024 );
545 m_progressBar->setValue( bytesReceived / 1024 );
546 QString progress =
"%1/%2 MB";
547 m_progressBar->setFormat( progress.arg( bytesReceived / 1024 / 1024 ).arg( bytesTotal / 1024 / 1024 ) );
550 bool MonavConfigWidgetPrivate::canExecute(
const QString &executable )
const
552 QString path = QProcessEnvironment::systemEnvironment().value(
"PATH",
"/usr/local/bin:/usr/bin:/bin" );
553 foreach(
const QString &dir, path.split( QLatin1Char(
':' ) ) ) {
554 QFileInfo application( QDir( dir ), executable );
555 if ( application.exists() ) {
563 void MonavConfigWidget::mapInstalled(
int exitStatus )
565 d->m_unpackProcess = 0;
566 d->m_currentFile.remove();
569 if ( exitStatus == 0 ) {
570 d->m_plugin->reloadMaps();
571 d->updateInstalledMapsView();
572 monavTabWidget->setCurrentIndex( 0 );
574 mDebug() <<
"Error when unpacking archive, process exited with status code " << exitStatus;
581 RoutingRunnerPlugin::ConfigWidget::showEvent( event );
582 if ( !event->spontaneous() && !d->m_initialized ) {
583 d->m_initialized =
true;
584 d->updateInstalledMapsView();
585 QUrl url = QUrl(
"http://files.kde.org/marble/newstuff/maps-monav.xml" );
586 d->m_networkAccessManager.get( QNetworkRequest( url ) );
590 void MonavConfigWidgetPrivate::updateInstalledMapsView()
592 m_mapsModel = m_plugin->installedMapsModel();
593 m_mapsModel->setInstallableVersions( m_remoteVersions );
594 m_filteredModel->setSourceModel( m_mapsModel );
595 m_parent->m_installedMapsListView->setModel( m_mapsModel );
597 m_parent->m_configureMapsListView->setColumnHidden( 1,
true );
598 m_parent->m_installedMapsListView->setColumnHidden( 2,
true );
599 m_parent->m_configureMapsListView->setColumnHidden( 3,
true );
600 m_parent->m_configureMapsListView->setColumnHidden( 4,
true );
601 m_parent->m_installedMapsListView->setColumnHidden( 5,
true );
603 m_parent->m_configureMapsListView->horizontalHeader()->setVisible(
true );
604 m_parent->m_installedMapsListView->horizontalHeader()->setVisible(
true );
605 m_parent->m_configureMapsListView->resizeColumnsToContents();
606 m_parent->m_installedMapsListView->resizeColumnsToContents();
608 updateTransportPreference();
609 updateInstalledMapsViewButtons();
612 void MonavConfigWidgetPrivate::updateInstalledMapsViewButtons()
614 m_removeMapSignalMapper.removeMappings( m_parent );
615 m_upgradeMapSignalMapper.removeMappings( m_parent );
616 for(
int i=0; i<m_mapsModel->rowCount(); ++i ) {
618 QPushButton* button =
new QPushButton( QIcon(
":/system-software-update.png" ),
"" );
619 button->setAutoFillBackground(
true );
620 QModelIndex index = m_mapsModel->index( i, 3 );
621 m_parent->m_installedMapsListView->setIndexWidget( index, button );
622 m_upgradeMapSignalMapper.setMapping( button, index.row() );
623 QObject::connect( button, SIGNAL(clicked()), &m_upgradeMapSignalMapper, SLOT(map()) );
624 bool upgradable = m_mapsModel->data( index ).toBool();
625 QString canUpgradeText = QObject::tr(
"An update is available. Click to install it." );
626 QString isLatestText = QObject::tr(
"No update available. You are running the latest version." );
627 button->setToolTip( upgradable ? canUpgradeText : isLatestText );
628 button->setEnabled( upgradable );
631 QPushButton* button =
new QPushButton( QIcon(
":/edit-delete.png" ),
"" );
632 button->setAutoFillBackground(
true );
633 QModelIndex index = m_mapsModel->index( i, 4 );
634 m_parent->m_installedMapsListView->setIndexWidget( index, button );
635 m_removeMapSignalMapper.setMapping( button, index.row() );
636 QObject::connect( button, SIGNAL(clicked()), &m_removeMapSignalMapper, SLOT(map()) );
637 bool const writable = m_mapsModel->data( index ).toBool();
638 button->setEnabled( writable );
641 m_parent->m_installedMapsListView->resizeColumnsToContents();
644 void MonavConfigWidget::updateTransportTypeFilter(
const QString &filter )
646 d->m_filteredModel->setFilterFixedString( filter );
647 d->m_transport = filter;
648 m_configureMapsListView->resizeColumnsToContents();
651 void MonavConfigWidget::removeMap(
int index )
653 QMessageBox::StandardButtons buttons = QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel;
654 QString text = tr(
"Are you sure you want to delete this map from the system?" );
655 if ( QMessageBox::question(
this, tr(
"Remove Map" ), text, buttons, QMessageBox::No ) == QMessageBox::Yes ) {
656 d->m_mapsModel->deleteMapFiles( index );
657 d->m_plugin->reloadMaps();
658 d->updateInstalledMapsView();
662 void MonavConfigWidget::upgradeMap(
int index )
664 QString payload = d->m_mapsModel->payload( index );
665 if ( !payload.isEmpty() ) {
666 foreach(
const MonavStuffEntry &entry, d->m_remoteMaps ) {
667 if ( entry.payload().endsWith(
'/' + payload ) ) {
668 d->m_currentDownload = entry.payload();
676 void MonavConfigWidgetPrivate::setBusy(
bool busy,
const QString &message )
const
679 m_parent->m_stackedWidget->removeWidget( m_parent->m_settingsPage );
680 m_parent->m_stackedWidget->addWidget( m_parent->m_progressPage );
682 m_parent->m_stackedWidget->removeWidget( m_parent->m_progressPage );
683 m_parent->m_stackedWidget->addWidget( m_parent->m_settingsPage );
686 QString
const defaultMessage = QObject::tr(
"Nothing to do." );
687 m_parent->m_progressLabel->setText( message.isEmpty() ? defaultMessage : message );
692 #include "MonavConfigWidget.moc"
static QString localPath()
QString statusMessage() const
A status message showing whether the plugin will be able to deliver results.
QDebug mDebug()
a function to replace qDebug() in Marble library code