1 // SPDX-License-Identifier: LGPL-2.1-or-later
2 //
3 // SPDX-FileCopyrightText: 2010 Dennis Nienhüser <nienhueser@kde.org>
4 //
5 
6 #include "MonavConfigWidget.h"
7 
8 #include "MonavMapsModel.h"
9 #include "MonavPlugin.h"
10 #include "MarbleDirs.h"
11 #include "MarbleDebug.h"
12 
13 #include <QFile>
14 #include <QProcess>
15 #include <QSignalMapper>
16 #include <QPushButton>
17 #include <QShowEvent>
18 #include <QSortFilterProxyModel>
19 #include <QMessageBox>
20 #include <QNetworkAccessManager>
21 #include <QNetworkReply>
22 #include <QDomDocument>
23 
24 namespace Marble
25 {
26 
27 class MonavStuffEntry
28 {
29 public:
30     void setPayload( const QString &payload );
31 
32     QString payload() const;
33 
34     void setName( const QString &name );
35 
36     QString name() const;
37 
38     bool isValid() const;
39 
40     QString continent() const;
41 
42     QString state() const;
43 
44     QString region() const;
45 
46     QString transport() const;
47 
48 private:
49     QString m_payload;
50 
51     QString m_name;
52 
53     QString m_continent;
54 
55     QString m_state;
56 
57     QString m_region;
58 
59     QString m_transport;
60 };
61 
62 class MonavConfigWidgetPrivate
63 {
64 public:
65     MonavConfigWidget* m_parent;
66 
67     MonavPlugin* m_plugin;
68 
69     QNetworkAccessManager m_networkAccessManager;
70 
71     QNetworkReply* m_currentReply;
72 
73     QProcess* m_unpackProcess;
74 
75     QSortFilterProxyModel* m_filteredModel;
76 
77     MonavMapsModel* m_mapsModel;
78 
79     bool m_initialized;
80 
81     QSignalMapper m_removeMapSignalMapper;
82 
83     QSignalMapper m_upgradeMapSignalMapper;
84 
85     QVector<MonavStuffEntry> m_remoteMaps;
86 
87     QMap<QString, QString> m_remoteVersions;
88 
89     QString m_currentDownload;
90 
91     QFile m_currentFile;
92 
93     QString m_transport;
94 
95     MonavConfigWidgetPrivate( MonavConfigWidget* parent, MonavPlugin* plugin );
96 
97     void parseNewStuff( const QByteArray &data );
98 
99     bool updateContinents( QComboBox* comboBox );
100 
101     bool updateStates( const QString &continent, QComboBox* comboBox );
102 
103     bool updateRegions( const QString &continent, const QString &state, QComboBox* comboBox );
104 
105     MonavStuffEntry map( const QString &continent, const QString &state, const QString &region ) const;
106 
107     void install();
108 
109     void installMap();
110 
111     void updateInstalledMapsView();
112 
113     void updateInstalledMapsViewButtons();
114 
115     void updateTransportPreference();
116 
117     static bool canExecute( const QString &executable );
118 
119     void setBusy( bool busy, const QString &message = QString() ) const;
120 
121 private:
122     static bool fillComboBox( QStringList items, QComboBox* comboBox );
123 };
124 
setPayload(const QString & payload)125 void MonavStuffEntry::setPayload( const QString &payload )
126 {
127     m_payload = payload;
128 }
129 
payload() const130 QString MonavStuffEntry::payload() const
131 {
132     return m_payload;
133 }
134 
setName(const QString & name)135 void MonavStuffEntry::setName( const QString &name )
136 {
137     m_name = name;
138     QStringList parsed = name.split( QLatin1Char( '/' ) );
139     int size = parsed.size();
140     m_continent = size > 0 ? parsed.at( 0 ).trimmed() : QString();
141     m_state = size > 1 ? parsed.at( 1 ).trimmed() : QString();
142     m_region = size > 2 ? parsed.at( 2 ).trimmed() : QString();
143     m_transport = "Motorcar"; // No i18n
144 
145     if ( size > 1 ) {
146         QString last = parsed.last().trimmed();
147         QRegExp regexp("([^(]+)\\(([^)]+)\\)");
148         if ( regexp.indexIn( last ) >= 0 ) {
149             QStringList matches = regexp.capturedTexts();
150             if ( matches.size() == 3 ) {
151                 m_transport = matches.at( 2 ).trimmed();
152                 if ( size > 2 ) {
153                     m_region = matches.at( 1 ).trimmed();
154                 } else {
155                     m_state = matches.at( 1 ).trimmed();
156                 }
157             }
158         }
159     }
160 }
161 
name() const162 QString MonavStuffEntry::name() const
163 {
164     return m_name;
165 }
166 
continent() const167 QString MonavStuffEntry::continent() const
168 {
169     return m_continent;
170 }
171 
state() const172 QString MonavStuffEntry::state() const
173 {
174     return m_state;
175 }
176 
region() const177 QString MonavStuffEntry::region() const
178 {
179     return m_region;
180 }
181 
transport() const182 QString MonavStuffEntry::transport() const
183 {
184     return m_transport;
185 }
186 
isValid() const187 bool MonavStuffEntry::isValid() const
188 {
189     return !m_continent.isEmpty() && !m_state.isEmpty() && m_payload.startsWith( QLatin1String( "http://" ) );
190 }
191 
MonavConfigWidgetPrivate(MonavConfigWidget * parent,MonavPlugin * plugin)192 MonavConfigWidgetPrivate::MonavConfigWidgetPrivate( MonavConfigWidget* parent, MonavPlugin* plugin ) :
193         m_parent( parent ), m_plugin( plugin ), m_networkAccessManager( nullptr ),
194         m_currentReply( nullptr ), m_unpackProcess( nullptr ), m_filteredModel( new QSortFilterProxyModel( parent) ),
195         m_mapsModel( nullptr ), m_initialized( false )
196 {
197     m_filteredModel->setFilterKeyColumn( 1 );
198 }
199 
parseNewStuff(const QByteArray & data)200 void MonavConfigWidgetPrivate::parseNewStuff( const QByteArray &data )
201 {
202     QDomDocument xml;
203     if ( !xml.setContent( data ) ) {
204         mDebug() << "Cannot parse xml file " << data;
205         return;
206     }
207 
208     QDomElement root = xml.documentElement();
209     QDomNodeList items = root.elementsByTagName(QStringLiteral("stuff"));
210     for (int i=0 ; i < items.length(); ++i ) {
211         MonavStuffEntry item;
212         QDomNode node = items.item( i );
213 
214         QDomNodeList names = node.toElement().elementsByTagName(QStringLiteral("name"));
215         if ( names.size() == 1 ) {
216             item.setName( names.at( 0 ).toElement().text() );
217         }
218 
219         QString releaseDate;
220         QDomNodeList dates = node.toElement().elementsByTagName(QStringLiteral("releasedate"));
221         if ( dates.size() == 1 ) {
222             releaseDate = dates.at( 0 ).toElement().text();
223         }
224 
225         QString filename;
226         QDomNodeList payloads = node.toElement().elementsByTagName(QStringLiteral("payload"));
227         if ( payloads.size() == 1 ) {
228             QString payload = payloads.at( 0 ).toElement().text();
229             filename = payload.mid(1 + payload.lastIndexOf(QLatin1Char('/')));
230             item.setPayload( payload );
231         }
232 
233         if ( item.isValid() ) {
234             m_remoteMaps.push_back( item );
235             if ( !filename.isEmpty() && !releaseDate.isEmpty() ) {
236                 m_remoteVersions[filename] = releaseDate;
237             }
238         }
239     }
240 
241     m_mapsModel->setInstallableVersions( m_remoteVersions );
242     updateInstalledMapsViewButtons();
243 }
244 
fillComboBox(QStringList items,QComboBox * comboBox)245 bool MonavConfigWidgetPrivate::fillComboBox( QStringList items, QComboBox* comboBox )
246 {
247     comboBox->clear();
248     std::sort( items.begin(), items.end() );
249     comboBox->addItems( items );
250     return !items.isEmpty();
251 }
252 
updateContinents(QComboBox * comboBox)253 bool MonavConfigWidgetPrivate::updateContinents( QComboBox* comboBox )
254 {
255     QSet<QString> continents;
256     for( const MonavStuffEntry &map: m_remoteMaps ) {
257         Q_ASSERT( map.isValid() );
258         continents << map.continent();
259     }
260 
261     return fillComboBox( continents.toList(), comboBox );
262 }
263 
updateStates(const QString & continent,QComboBox * comboBox)264 bool MonavConfigWidgetPrivate::updateStates( const QString &continent, QComboBox* comboBox )
265 {
266     QSet<QString> states;
267     for( const MonavStuffEntry &map: m_remoteMaps ) {
268         Q_ASSERT( map.isValid() );
269         if ( map.continent() == continent ) {
270             states << map.state();
271         }
272     }
273 
274     return fillComboBox( states.toList(), comboBox );
275 }
276 
updateRegions(const QString & continent,const QString & state,QComboBox * comboBox)277 bool MonavConfigWidgetPrivate::updateRegions( const QString &continent, const QString &state, QComboBox* comboBox )
278 {
279     comboBox->clear();
280     QMap<QString,QString> regions;
281     for( const MonavStuffEntry &map: m_remoteMaps ) {
282         Q_ASSERT( map.isValid() );
283         if ( map.continent() == continent && map.state() == state ) {
284             QString item = "%1 - %2";
285             if ( map.region().isEmpty() ) {
286                 item = item.arg( map.state() );
287                 comboBox->addItem( item.arg( map.transport() ), map.payload() );
288             } else {
289                 item = item.arg( map.region(), map.transport() );
290                 regions.insert( item, map.payload() );
291             }
292         }
293     }
294 
295     QMapIterator<QString, QString> iter( regions );
296     while ( iter.hasNext() ) {
297         iter.next();
298         comboBox->addItem( iter.key(), iter.value() );
299     }
300 
301     return true;
302 }
303 
map(const QString & continent,const QString & state,const QString & region) const304 MonavStuffEntry MonavConfigWidgetPrivate::map( const QString &continent, const QString &state, const QString &region ) const
305 {
306     for( const MonavStuffEntry &entry: m_remoteMaps ) {
307         if ( continent == entry.continent() && state == entry.state() && region == entry.region() ) {
308             return entry;
309         }
310     }
311 
312     return MonavStuffEntry();
313 }
314 
MonavConfigWidget(MonavPlugin * plugin)315 MonavConfigWidget::MonavConfigWidget( MonavPlugin* plugin ) :
316         d( new MonavConfigWidgetPrivate( this, plugin ) )
317 {
318     setupUi( this );
319     m_statusLabel->setText( plugin->statusMessage() );
320     m_statusLabel->setHidden( m_statusLabel->text().isEmpty() );
321     d->setBusy( false );
322     m_installedMapsListView->setModel( d->m_mapsModel );
323     m_configureMapsListView->setModel( d->m_filteredModel );
324     m_configureMapsListView->resizeColumnsToContents();
325 
326     updateComboBoxes();
327 
328     connect( m_continentComboBox, SIGNAL(currentIndexChanged(int)),
329              this, SLOT(updateStates()) );
330     connect( m_transportTypeComboBox, SIGNAL(currentIndexChanged(QString)),
331              this, SLOT(updateTransportTypeFilter(QString)) );
332     connect( m_stateComboBox, SIGNAL(currentIndexChanged(int)),
333              this, SLOT(updateRegions()) );
334     connect( m_installButton, SIGNAL(clicked()), this, SLOT(downloadMap()) );
335     connect( m_cancelButton, SIGNAL(clicked()), this, SLOT(cancelOperation()) );
336     connect( &d->m_removeMapSignalMapper, SIGNAL(mapped(int)),
337              this, SLOT(removeMap(int)) );
338     connect( &d->m_upgradeMapSignalMapper, SIGNAL(mapped(int)),
339              this, SLOT(upgradeMap(int)) );
340     connect( &d->m_networkAccessManager, SIGNAL(finished(QNetworkReply*)),
341              this, SLOT(retrieveMapList(QNetworkReply*)) );
342 }
343 
~MonavConfigWidget()344 MonavConfigWidget::~MonavConfigWidget()
345 {
346     delete d;
347 }
348 
loadSettings(const QHash<QString,QVariant> & settings)349 void MonavConfigWidget::loadSettings( const QHash<QString, QVariant> &settings )
350 {
351     d->m_transport = settings[QStringLiteral("transport")].toString();
352     d->updateTransportPreference();
353 }
354 
updateTransportPreference()355 void MonavConfigWidgetPrivate::updateTransportPreference()
356 {
357     if ( m_parent->m_transportTypeComboBox && m_mapsModel ) {
358         m_parent->m_transportTypeComboBox->blockSignals( true );
359         m_parent->m_transportTypeComboBox->clear();
360         QSet<QString> transportTypes;
361         for( int i=0; i<m_mapsModel->rowCount(); ++i ) {
362             QModelIndex index = m_mapsModel->index( i, 1 );
363             transportTypes << m_mapsModel->data( index ).toString();
364         }
365         m_parent->m_transportTypeComboBox->addItems( transportTypes.toList() );
366         m_parent->m_transportTypeComboBox->blockSignals( false );
367 
368         if ( !m_transport.isEmpty() && m_parent->m_transportTypeComboBox ) {
369             for ( int i=1; i<m_parent->m_transportTypeComboBox->count(); ++i ) {
370                 if ( m_parent->m_transportTypeComboBox->itemText( i ) == m_transport ) {
371                     m_parent->m_transportTypeComboBox->setCurrentIndex( i );
372                     return;
373                 }
374             }
375         }
376     }
377 }
378 
settings() const379 QHash<QString, QVariant> MonavConfigWidget::settings() const
380 {
381     QHash<QString, QVariant> settings;
382     settings.insert(QStringLiteral("transport"), d->m_transport);
383     return settings;
384 }
385 
retrieveMapList(QNetworkReply * reply)386 void MonavConfigWidget::retrieveMapList( QNetworkReply *reply )
387 {
388     if ( reply->isReadable() && d->m_currentDownload.isEmpty() ) {
389         // check if we are redirected
390         QVariant const redirectionAttribute = reply->attribute( QNetworkRequest::RedirectionTargetAttribute );
391         if ( !redirectionAttribute.isNull() ) {
392             d->m_networkAccessManager.get( QNetworkRequest( redirectionAttribute.toUrl() ) );
393         } else {
394             disconnect( &d->m_networkAccessManager, SIGNAL(finished(QNetworkReply*)),
395                          this, SLOT(retrieveMapList(QNetworkReply*)) );
396             d->parseNewStuff( reply->readAll() );
397             updateComboBoxes();
398         }
399     }
400 }
401 
retrieveData()402 void MonavConfigWidget::retrieveData()
403 {
404     if ( d->m_currentReply && d->m_currentReply->isReadable() && !d->m_currentDownload.isEmpty() ) {
405         // check if we are redirected
406         QVariant const redirectionAttribute = d->m_currentReply->attribute( QNetworkRequest::RedirectionTargetAttribute );
407         if ( !redirectionAttribute.isNull() ) {
408             d->m_currentReply = d->m_networkAccessManager.get( QNetworkRequest( redirectionAttribute.toUrl() ) );
409             connect( d->m_currentReply, SIGNAL(readyRead()),
410                          this, SLOT(retrieveData()) );
411             connect( d->m_currentReply, SIGNAL(readChannelFinished()),
412                          this, SLOT(retrieveData()) );
413             connect( d->m_currentReply, SIGNAL(downloadProgress(qint64,qint64)),
414                      this, SLOT(updateProgressBar(qint64,qint64)) );
415         } else {
416             d->m_currentFile.write( d->m_currentReply->readAll() );
417             if ( d->m_currentReply->isFinished() ) {
418                 d->m_currentReply->deleteLater();
419                 d->m_currentReply = nullptr;
420                 d->m_currentFile.close();
421                 d->installMap();
422                 d->m_currentDownload.clear();
423             }
424         }
425     }
426 }
427 
updateComboBoxes()428 void MonavConfigWidget::updateComboBoxes()
429 {
430     d->updateContinents( m_continentComboBox );
431     updateStates();
432     updateRegions();
433 }
434 
updateStates()435 void MonavConfigWidget::updateStates()
436 {
437     bool const haveContinents = m_continentComboBox->currentIndex() >= 0;
438     if ( haveContinents ) {
439         QString const continent = m_continentComboBox->currentText();
440         if ( d->updateStates( continent, m_stateComboBox ) ) {
441             updateRegions();
442         }
443     }
444 }
445 
updateRegions()446 void MonavConfigWidget::updateRegions()
447 {
448     bool haveRegions = false;
449     if ( m_continentComboBox->currentIndex() >= 0 ) {
450         if ( m_stateComboBox->currentIndex() >= 0 ) {
451             QString const continent = m_continentComboBox->currentText();
452             QString const state = m_stateComboBox->currentText();
453             haveRegions = d->updateRegions( continent, state, m_regionComboBox );
454         }
455     }
456 
457     m_regionLabel->setVisible( haveRegions );
458     m_regionComboBox->setVisible( haveRegions );
459 }
460 
downloadMap()461 void MonavConfigWidget::downloadMap()
462 {
463     if ( d->m_currentDownload.isEmpty() && !d->m_currentFile.isOpen() ) {
464         d->m_currentDownload = m_regionComboBox->itemData( m_regionComboBox->currentIndex() ).toString();
465         d->install();
466     }
467 }
468 
cancelOperation()469 void MonavConfigWidget::cancelOperation()
470 {
471     if ( !d->m_currentDownload.isEmpty() || d->m_currentFile.isOpen() ) {
472         d->m_currentReply->abort();
473         d->m_currentReply->deleteLater();
474         d->m_currentReply = nullptr;
475         d->m_currentDownload.clear();
476         d->setBusy( false );
477         d->m_currentFile.close();
478     }
479 }
480 
install()481 void MonavConfigWidgetPrivate::install()
482 {
483     if ( !m_currentDownload.isEmpty() ) {
484         int const index = m_currentDownload.lastIndexOf(QLatin1Char('/'));
485         const QString localFile = MarbleDirs::localPath() + QLatin1String("/maps") + m_currentDownload.mid(index);
486         m_currentFile.setFileName( localFile );
487         if ( m_currentFile.open( QFile::WriteOnly ) ) {
488             QFileInfo file( m_currentFile );
489             QString message = QObject::tr( "Downloading %1" ).arg( file.fileName() );
490             setBusy( true, message );
491             m_currentReply = m_networkAccessManager.get( QNetworkRequest( QUrl ( m_currentDownload ) ) );
492             QObject::connect( m_currentReply, SIGNAL(readyRead()),
493                          m_parent, SLOT(retrieveData()) );
494             QObject::connect( m_currentReply, SIGNAL(readChannelFinished()),
495                          m_parent, SLOT(retrieveData()) );
496             QObject::connect( m_currentReply, SIGNAL(downloadProgress(qint64,qint64)),
497                      m_parent, SLOT(updateProgressBar(qint64,qint64)) );
498         } else {
499             mDebug() << "Failed to write to " << localFile;
500         }
501     }
502 }
503 
installMap()504 void MonavConfigWidgetPrivate::installMap()
505 {
506     if ( m_unpackProcess ) {
507         m_unpackProcess->close();
508         delete m_unpackProcess;
509         m_unpackProcess = nullptr;
510         m_parent->m_installButton->setEnabled( true );
511     } else if ( m_currentFile.fileName().endsWith( QLatin1String( "tar.gz" ) ) && canExecute( "tar" ) ) {
512         QFileInfo file( m_currentFile );
513         QString message = QObject::tr( "Installing %1" ).arg( file.fileName() );
514         setBusy( true, message );
515         m_parent->m_progressBar->setMaximum( 0 );
516         if ( file.exists() && file.isReadable() ) {
517             m_unpackProcess = new QProcess;
518             QObject::connect( m_unpackProcess, SIGNAL(finished(int)),
519                      m_parent, SLOT(mapInstalled(int)) );
520             QStringList arguments = QStringList() << "-x" << "-z" << "-f" << file.fileName();
521             m_unpackProcess->setWorkingDirectory( file.dir().absolutePath() );
522             m_unpackProcess->start( "tar", arguments );
523         }
524     } else {
525         if ( !m_currentFile.fileName().endsWith( QLatin1String( "tar.gz" ) ) ) {
526             mDebug() << "Can only handle tar.gz files";
527         } else {
528             mDebug() << "Cannot extract archive: tar executable not found in PATH.";
529         }
530     }
531 }
532 
updateProgressBar(qint64 bytesReceived,qint64 bytesTotal)533 void MonavConfigWidget::updateProgressBar( qint64 bytesReceived, qint64 bytesTotal )
534 {
535     // Coarse MB resolution for the text to get it short,
536     // finer Kb resolution for the progress values to see changes easily
537     m_progressBar->setMaximum( bytesTotal / 1024 );
538     m_progressBar->setValue( bytesReceived / 1024 );
539     QString progress = "%1/%2 MB";
540     m_progressBar->setFormat( progress.arg( bytesReceived / 1024 / 1024 ).arg( bytesTotal / 1024 / 1024 ) );
541 }
542 
canExecute(const QString & executable)543 bool MonavConfigWidgetPrivate::canExecute( const QString &executable )
544 {
545     QString path = QProcessEnvironment::systemEnvironment().value(QStringLiteral("PATH"), QStringLiteral("/usr/local/bin:/usr/bin:/bin"));
546     for( const QString &dir: path.split( QLatin1Char( ':' ) ) ) {
547         QFileInfo application( QDir( dir ), executable );
548         if ( application.exists() ) {
549             return true;
550         }
551     }
552 
553     return false;
554 }
555 
mapInstalled(int exitStatus)556 void MonavConfigWidget::mapInstalled( int exitStatus )
557 {
558     d->m_unpackProcess = nullptr;
559     d->m_currentFile.remove();
560     d->setBusy( false );
561 
562     if ( exitStatus == 0 ) {
563         d->m_plugin->reloadMaps();
564         d->updateInstalledMapsView();
565         monavTabWidget->setCurrentIndex( 0 );
566     } else {
567         mDebug() << "Error when unpacking archive, process exited with status code " << exitStatus;
568     }
569 }
570 
showEvent(QShowEvent * event)571 void MonavConfigWidget::showEvent ( QShowEvent * event )
572 {
573     // Lazy initialization
574     RoutingRunnerPlugin::ConfigWidget::showEvent( event );
575     if ( !event->spontaneous() && !d->m_initialized ) {
576         d->m_initialized = true;
577         d->updateInstalledMapsView();
578         QUrl url = QUrl( "http://files.kde.org/marble/newstuff/maps-monav.xml" );
579         d->m_networkAccessManager.get( QNetworkRequest( url ) );
580     }
581 }
582 
updateInstalledMapsView()583 void MonavConfigWidgetPrivate::updateInstalledMapsView()
584 {
585     m_mapsModel = m_plugin->installedMapsModel();
586     m_mapsModel->setInstallableVersions( m_remoteVersions );
587     m_filteredModel->setSourceModel( m_mapsModel );
588     m_parent->m_installedMapsListView->setModel( m_mapsModel );
589 
590     m_parent->m_configureMapsListView->setColumnHidden( 1, true );
591     m_parent->m_installedMapsListView->setColumnHidden( 2, true );
592     m_parent->m_configureMapsListView->setColumnHidden( 3, true );
593     m_parent->m_configureMapsListView->setColumnHidden( 4, true );
594     m_parent->m_installedMapsListView->setColumnHidden( 5, true );
595 
596     m_parent->m_configureMapsListView->horizontalHeader()->setVisible( true );
597     m_parent->m_installedMapsListView->horizontalHeader()->setVisible( true );
598     m_parent->m_configureMapsListView->resizeColumnsToContents();
599     m_parent->m_installedMapsListView->resizeColumnsToContents();
600 
601     updateTransportPreference();
602     updateInstalledMapsViewButtons();
603 }
604 
updateInstalledMapsViewButtons()605 void MonavConfigWidgetPrivate::updateInstalledMapsViewButtons()
606 {
607     m_removeMapSignalMapper.removeMappings( m_parent );
608     m_upgradeMapSignalMapper.removeMappings( m_parent );
609     for( int i=0; i<m_mapsModel->rowCount(); ++i ) {
610         {
611             QPushButton* button = new QPushButton(QIcon(QStringLiteral(":/system-software-update.png")), QString());
612             button->setAutoFillBackground( true );
613             QModelIndex index = m_mapsModel->index( i, 3 );
614             m_parent->m_installedMapsListView->setIndexWidget( index, button );
615             m_upgradeMapSignalMapper.setMapping( button, index.row() );
616             QObject::connect( button, SIGNAL(clicked()), &m_upgradeMapSignalMapper, SLOT(map()) );
617             bool upgradable = m_mapsModel->data( index ).toBool();
618             QString canUpgradeText = QObject::tr( "An update is available. Click to install it." );
619             QString isLatestText = QObject::tr( "No update available. You are running the latest version." );
620             button->setToolTip( upgradable ? canUpgradeText : isLatestText );
621             button->setEnabled( upgradable );
622         }
623         {
624             QPushButton* button = new QPushButton(QIcon(QStringLiteral(":/edit-delete.png")), QString());
625             button->setAutoFillBackground( true );
626             QModelIndex index = m_mapsModel->index( i, 4 );
627             m_parent->m_installedMapsListView->setIndexWidget( index, button );
628             m_removeMapSignalMapper.setMapping( button, index.row() );
629             QObject::connect( button, SIGNAL(clicked()), &m_removeMapSignalMapper, SLOT(map()) );
630             bool const writable = m_mapsModel->data( index ).toBool();
631             button->setEnabled( writable );
632         }
633     }
634     m_parent->m_installedMapsListView->resizeColumnsToContents();
635 }
636 
updateTransportTypeFilter(const QString & filter)637 void MonavConfigWidget::updateTransportTypeFilter( const QString &filter )
638 {
639     d->m_filteredModel->setFilterFixedString( filter );
640     d->m_transport = filter;
641     m_configureMapsListView->resizeColumnsToContents();
642 }
643 
removeMap(int index)644 void MonavConfigWidget::removeMap( int index )
645 {
646     QMessageBox::StandardButtons buttons = QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel;
647     QString text = tr( "Are you sure you want to delete this map from the system?" );
648     if ( QMessageBox::question( this, tr( "Remove Map" ), text, buttons, QMessageBox::No ) == QMessageBox::Yes ) {
649         d->m_mapsModel->deleteMapFiles( index );
650         d->m_plugin->reloadMaps();
651         d->updateInstalledMapsView();
652     }
653 }
654 
upgradeMap(int index)655 void MonavConfigWidget::upgradeMap( int index )
656 {
657     QString payload = d->m_mapsModel->payload( index );
658     if ( !payload.isEmpty() ) {
659         for( const MonavStuffEntry &entry: d->m_remoteMaps ) {
660             if (entry.payload().endsWith(QLatin1Char('/') + payload)) {
661                 d->m_currentDownload = entry.payload();
662                 d->install();
663                 return;
664             }
665         }
666     }
667 }
668 
setBusy(bool busy,const QString & message) const669 void MonavConfigWidgetPrivate::setBusy( bool busy, const QString &message ) const
670 {
671     if ( busy ) {
672         m_parent->m_stackedWidget->removeWidget( m_parent->m_settingsPage );
673         m_parent->m_stackedWidget->addWidget( m_parent->m_progressPage );
674     } else {
675         m_parent->m_stackedWidget->removeWidget( m_parent->m_progressPage );
676         m_parent->m_stackedWidget->addWidget( m_parent->m_settingsPage );
677     }
678 
679     QString const defaultMessage = QObject::tr( "Nothing to do." );
680     m_parent->m_progressLabel->setText( message.isEmpty() ? defaultMessage : message );
681 }
682 
683 }
684 
685 #include "moc_MonavConfigWidget.cpp"
686