Skip to content

Commit 8365340

Browse files
authoredMar 21, 2019
changing EOL format
1 parent b6a3214 commit 8365340

File tree

2 files changed

+579
-579
lines changed

2 files changed

+579
-579
lines changed
 

‎src/app/qgshandlebadlayers.cpp

Lines changed: 499 additions & 499 deletions
Original file line numberDiff line numberDiff line change
@@ -1,499 +1,499 @@
1-
/***************************************************************************
2-
qgshandlebadlayers.cpp - description
3-
-------------------
4-
begin : Sat 5 Mar 2011
5-
copyright : (C) 2011 by Juergen E. Fischer, norBIT GmbH
6-
email : jef at norbit dot de
7-
***************************************************************************/
8-
9-
/***************************************************************************
10-
* *
11-
* This program is free software; you can redistribute it and/or modify *
12-
* it under the terms of the GNU General Public License as published by *
13-
* the Free Software Foundation; either version 2 of the License, or *
14-
* (at your option) any later version. *
15-
* *
16-
***************************************************************************/
17-
18-
#include "qgshandlebadlayers.h"
19-
#include "qgisapp.h"
20-
#include "qgsauthconfigselect.h"
21-
#include "qgsdataprovider.h"
22-
#include "qgsguiutils.h"
23-
#include "qgsdatasourceuri.h"
24-
#include "qgslogger.h"
25-
#include "qgsrasterlayer.h"
26-
#include "qgsproviderregistry.h"
27-
#include "qgsmessagebar.h"
28-
#include "qgssettings.h"
29-
#include "qgslayertreeregistrybridge.h"
30-
#include "qgsapplication.h"
31-
32-
#include <QDomDocument>
33-
#include <QDomElement>
34-
#include <QFileDialog>
35-
#include <QPushButton>
36-
#include <QToolButton>
37-
#include <QMessageBox>
38-
#include <QDialogButtonBox>
39-
#include <QUrl>
40-
41-
void QgsHandleBadLayersHandler::handleBadLayers( const QList<QDomNode> &layers )
42-
{
43-
QApplication::setOverrideCursor( Qt::ArrowCursor );
44-
QgsHandleBadLayers *dialog = new QgsHandleBadLayers( layers );
45-
46-
dialog->buttonBox->button( QDialogButtonBox::Ignore )->setToolTip( tr( "Import all unavailable layers unmodified (you can fix them later)." ) );
47-
dialog->buttonBox->button( QDialogButtonBox::Ignore )->setText( tr( "Keep Unavailable Layers" ) );
48-
dialog->buttonBox->button( QDialogButtonBox::Discard )->setToolTip( tr( "Remove all unavailable layers from the project" ) );
49-
dialog->buttonBox->button( QDialogButtonBox::Discard )->setText( tr( "Remove Unavailable Layers" ) );
50-
dialog->buttonBox->button( QDialogButtonBox::Discard )->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionDeleteSelected.svg" ) ) );
51-
52-
if ( dialog->layerCount() < layers.size() )
53-
QgisApp::instance()->messageBar()->pushMessage(
54-
tr( "Handle unavailable layers" ),
55-
tr( "%1 of %2 unavailable layers were not fixable." )
56-
.arg( layers.size() - dialog->layerCount() )
57-
.arg( layers.size() ),
58-
Qgis::Warning, QgisApp::instance()->messageTimeout() );
59-
60-
if ( dialog->layerCount() > 0 )
61-
{
62-
if ( dialog->exec() == dialog->Accepted )
63-
{
64-
emit layersChanged();
65-
}
66-
}
67-
68-
delete dialog;
69-
QApplication::restoreOverrideCursor();
70-
}
71-
72-
73-
QgsHandleBadLayers::QgsHandleBadLayers( const QList<QDomNode> &layers )
74-
: QDialog( QgisApp::instance() )
75-
, mLayers( layers )
76-
{
77-
setupUi( this );
78-
79-
mVectorFileFilter = QgsProviderRegistry::instance()->fileVectorFilters();
80-
mRasterFileFilter = QgsProviderRegistry::instance()->fileRasterFilters();
81-
82-
mBrowseButton = new QPushButton( tr( "Browse" ) );
83-
buttonBox->addButton( mBrowseButton, QDialogButtonBox::ActionRole );
84-
mBrowseButton->setDisabled( true );
85-
mApplyButton = new QPushButton( tr( "Apply Changes" ) );
86-
mApplyButton->setToolTip( tr( "Apply fixes to unavailable layers (remaining unavailable layers will be removed from the project)." ) );
87-
buttonBox->addButton( mApplyButton, QDialogButtonBox::ActionRole );
88-
89-
connect( mLayerList, &QTableWidget::itemSelectionChanged, this, &QgsHandleBadLayers::selectionChanged );
90-
connect( mBrowseButton, &QAbstractButton::clicked, this, &QgsHandleBadLayers::browseClicked );
91-
connect( mApplyButton, &QAbstractButton::clicked, this, &QgsHandleBadLayers::apply );
92-
connect( buttonBox->button( QDialogButtonBox::Ignore ), &QPushButton::clicked, this, &QgsHandleBadLayers::reject );
93-
connect( buttonBox->button( QDialogButtonBox::Discard ), &QPushButton::clicked, this, &QgsHandleBadLayers::accept );
94-
95-
mLayerList->clear();
96-
mLayerList->setSortingEnabled( true );
97-
mLayerList->setSelectionBehavior( QAbstractItemView::SelectRows );
98-
mLayerList->setColumnCount( 5 );
99-
mLayerList->setColumnWidth( 3, 75 );
100-
101-
mLayerList->setHorizontalHeaderLabels( QStringList()
102-
<< tr( "Layer name" )
103-
<< tr( "Type" )
104-
<< tr( "Provider" )
105-
<< tr( "Auth config" )
106-
<< tr( "Datasource" )
107-
);
108-
109-
int j = 0;
110-
for ( int i = 0; i < mLayers.size(); i++ )
111-
{
112-
const QDomNode &node = mLayers[i];
113-
114-
QString name = node.namedItem( QStringLiteral( "layername" ) ).toElement().text();
115-
QString type = node.toElement().attribute( QStringLiteral( "type" ) );
116-
QString datasource = node.namedItem( QStringLiteral( "datasource" ) ).toElement().text();
117-
QString provider = node.namedItem( QStringLiteral( "provider" ) ).toElement().text();
118-
QString vectorProvider = type == QLatin1String( "vector" ) ? provider : tr( "none" );
119-
bool providerFileBased = ( QgsProviderRegistry::instance()->providerCapabilities( provider ) & QgsDataProvider::File ) != 0;
120-
const QString basepath = datasource.left( datasource.lastIndexOf('/') );
121-
mFileBase[name].append( basepath );
122-
123-
QgsDebugMsg( QStringLiteral( "name=%1 type=%2 provider=%3 datasource='%4'" )
124-
.arg( name,
125-
type,
126-
vectorProvider,
127-
datasource ) );
128-
129-
mLayerList->setRowCount( j + 1 );
130-
131-
QTableWidgetItem *item = nullptr;
132-
133-
item = new QTableWidgetItem( name );
134-
item->setData( Qt::UserRole + 0, i );
135-
item->setFlags( item->flags() & ~Qt::ItemIsEditable );
136-
mLayerList->setItem( j, 0, item );
137-
138-
item = new QTableWidgetItem( type );
139-
item->setData( Qt::UserRole + 0, providerFileBased );
140-
item->setFlags( item->flags() & ~Qt::ItemIsEditable );
141-
mLayerList->setItem( j, 1, item );
142-
143-
item = new QTableWidgetItem( vectorProvider );
144-
item->setFlags( item->flags() & ~Qt::ItemIsEditable );
145-
mLayerList->setItem( j, 2, item );
146-
147-
if ( QgsAuthConfigUriEdit::hasConfigId( datasource ) )
148-
{
149-
QToolButton *btn = new QToolButton( this );
150-
btn->setMaximumWidth( 75 );
151-
btn->setMinimumHeight( 24 );
152-
btn->setText( tr( "Edit" ) );
153-
btn->setProperty( "row", j );
154-
connect( btn, &QAbstractButton::clicked, this, &QgsHandleBadLayers::editAuthCfg );
155-
mLayerList->setCellWidget( j, 3, btn );
156-
}
157-
else
158-
{
159-
item = new QTableWidgetItem( QString() );
160-
mLayerList->setItem( j, 3, item );
161-
}
162-
163-
item = new QTableWidgetItem( datasource );
164-
mLayerList->setItem( j, 4, item );
165-
166-
j++;
167-
}
168-
169-
// mLayerList->resizeColumnsToContents();
170-
}
171-
172-
void QgsHandleBadLayers::selectionChanged()
173-
{
174-
175-
mRows.clear();
176-
177-
Q_FOREACH ( QTableWidgetItem *item, mLayerList->selectedItems() )
178-
{
179-
if ( item->column() != 0 )
180-
continue;
181-
182-
bool providerFileBased = mLayerList->item( item->row(), 1 )->data( Qt::UserRole + 0 ).toBool();
183-
if ( !providerFileBased )
184-
continue;
185-
186-
mRows << item->row();
187-
}
188-
189-
mBrowseButton->setEnabled( !mRows.isEmpty() );
190-
}
191-
192-
QString QgsHandleBadLayers::filename( int row )
193-
{
194-
QString type = mLayerList->item( row, 1 )->text();
195-
QString provider = mLayerList->item( row, 2 )->text();
196-
QString datasource = mLayerList->item( row, 4 )->text();
197-
198-
if ( type == QLatin1String( "vector" ) )
199-
{
200-
const QVariantMap parts = QgsProviderRegistry::instance()->decodeUri( provider, datasource );
201-
// if parts is empty then provider doesn't handle this method!
202-
return parts.empty() ? datasource : parts.value( QStringLiteral( "path" ) ).toString();
203-
}
204-
else
205-
{
206-
return datasource;
207-
}
208-
}
209-
210-
void QgsHandleBadLayers::setFilename( int row, const QString &filename )
211-
{
212-
if ( !QFileInfo::exists( filename ) )
213-
return;
214-
215-
QString type = mLayerList->item( row, 1 )->text();
216-
QString provider = mLayerList->item( row, 2 )->text();
217-
QTableWidgetItem *item = mLayerList->item( row, 4 );
218-
219-
QString datasource = item->text();
220-
221-
if ( type == QLatin1String( "vector" ) )
222-
{
223-
if ( provider == QLatin1String( "spatialite" ) )
224-
{
225-
QgsDataSourceUri uri( datasource );
226-
uri.setDatabase( filename );
227-
datasource = uri.uri();
228-
}
229-
else if ( provider == QLatin1String( "ogr" ) )
230-
{
231-
QStringList theURIParts = datasource.split( '|' );
232-
theURIParts[0] = filename;
233-
datasource = theURIParts.join( QStringLiteral( "|" ) );
234-
}
235-
else if ( provider == QLatin1String( "delimitedtext" ) )
236-
{
237-
QUrl uriSource = QUrl::fromEncoded( datasource.toLatin1() );
238-
QUrl uriDest = QUrl::fromLocalFile( filename );
239-
uriDest.setQueryItems( uriSource.queryItems() );
240-
datasource = QString::fromLatin1( uriDest.toEncoded() );
241-
}
242-
}
243-
else
244-
{
245-
datasource = filename;
246-
}
247-
248-
item->setText( datasource );
249-
}
250-
251-
void QgsHandleBadLayers::browseClicked()
252-
{
253-
254-
if ( mRows.size() == 1 )
255-
{
256-
int row = mRows.at( 0 );
257-
QString type = mLayerList->item( row, 1 )->text();
258-
259-
QString memoryQualifier, fileFilter;
260-
if ( type == QLatin1String( "vector" ) )
261-
{
262-
memoryQualifier = QStringLiteral( "lastVectorFileFilter" );
263-
fileFilter = mVectorFileFilter;
264-
}
265-
else
266-
{
267-
memoryQualifier = QStringLiteral( "lastRasterFileFilter" );
268-
fileFilter = mRasterFileFilter;
269-
}
270-
271-
QString fn = filename( row );
272-
if ( fn.isNull() )
273-
return;
274-
275-
QStringList selectedFiles;
276-
QString enc;
277-
QString title = tr( "Select File to Replace '%1'" ).arg( fn );
278-
279-
QgsGuiUtils::openFilesRememberingFilter( memoryQualifier, fileFilter, selectedFiles, enc, title );
280-
if ( selectedFiles.size() != 1 )
281-
{
282-
QMessageBox::information( this, title, tr( "Please select exactly one file." ) );
283-
return;
284-
}
285-
286-
setFilename( row, selectedFiles[0] );
287-
}
288-
else if ( mRows.size() > 1 )
289-
{
290-
QString title = tr( "Select New Directory of Selected Files" );
291-
292-
QgsSettings settings;
293-
QString lastDir = settings.value( QStringLiteral( "UI/missingDirectory" ), QDir::homePath() ).toString();
294-
QString selectedFolder = QFileDialog::getExistingDirectory( this, title, lastDir );
295-
if ( selectedFolder.isEmpty() )
296-
{
297-
return;
298-
}
299-
300-
QDir dir( selectedFolder );
301-
if ( !dir.exists() )
302-
{
303-
return;
304-
}
305-
306-
Q_FOREACH ( int row, mRows )
307-
{
308-
bool providerFileBased = mLayerList->item( row, 1 )->data( Qt::UserRole + 0 ).toBool();
309-
if ( !providerFileBased )
310-
continue;
311-
312-
QString fn = filename( row );
313-
if ( fn.isEmpty() )
314-
continue;
315-
316-
QFileInfo fi( fn );
317-
fi.setFile( dir, fi.fileName() );
318-
if ( !fi.exists() )
319-
continue;
320-
321-
setFilename( row, fi.absoluteFilePath() );
322-
}
323-
}
324-
}
325-
326-
void QgsHandleBadLayers::editAuthCfg()
327-
{
328-
QToolButton *btn = qobject_cast<QToolButton *>( sender() );
329-
int row = -1;
330-
for ( int i = 0; i < mLayerList->rowCount(); i++ )
331-
{
332-
if ( mLayerList->cellWidget( i, 3 ) == btn )
333-
{
334-
row = i;
335-
break;
336-
}
337-
}
338-
339-
if ( row == -1 )
340-
return;
341-
342-
QString provider = mLayerList->item( row, 2 )->text();
343-
if ( provider == QLatin1String( "none" ) )
344-
provider.clear();
345-
346-
QString prevuri = mLayerList->item( row, 4 )->text();
347-
348-
QgsAuthConfigUriEdit *dlg = new QgsAuthConfigUriEdit( this, prevuri, provider );
349-
dlg->setWindowModality( Qt::WindowModal );
350-
dlg->resize( 500, 500 );
351-
if ( dlg->exec() )
352-
{
353-
QString newuri( dlg->dataSourceUri() );
354-
if ( newuri != prevuri )
355-
{
356-
mLayerList->item( row, 4 )->setText( newuri );
357-
}
358-
}
359-
dlg->deleteLater();
360-
}
361-
362-
void QgsHandleBadLayers::apply()
363-
{
364-
QgsProject::instance()->layerTreeRegistryBridge()->setEnabled( true );
365-
buttonBox->button( QDialogButtonBox::Ignore )->setEnabled( false );
366-
QHash<QString, QString> baseChange;
367-
368-
369-
370-
for ( int i = 0; i < mLayerList->rowCount(); i++ )
371-
{
372-
int idx = mLayerList->item( i, 0 )->data( Qt::UserRole ).toInt();
373-
QDomNode &node = const_cast<QDomNode &>( mLayers[ idx ] );
374-
375-
const QString name = mLayerList->item( i, 0 )->text();
376-
QTableWidgetItem *item = mLayerList->item( i, 4 );
377-
QString datasource = item->text();
378-
const QString basepath = datasource.left( datasource.lastIndexOf('/') );
379-
bool changed = false;
380-
381-
if ( mFileBase[ name ].size() == 1 )
382-
{
383-
if ( mFileBase[ name ][0] != basepath && !baseChange.contains( mFileBase[ name ][0] ) )
384-
{
385-
baseChange[ mFileBase[ name ][0] ] = basepath;
386-
changed = true;
387-
}
388-
}
389-
else if ( mFileBase[ name ].size() > 1 )
390-
{
391-
if ( mFileBase[ name ].indexOf( basepath ) == -1 )
392-
{
393-
const QList<QString> fileBases = mFileBase[ name ];
394-
for ( QString fileBase : fileBases )
395-
{
396-
if ( !baseChange.contains( fileBase ) )
397-
{
398-
baseChange[ fileBase ] = basepath;
399-
changed = true;
400-
}
401-
}
402-
}
403-
}
404-
if ( !changed && baseChange.contains( basepath ) )
405-
datasource = datasource.replace( basepath, baseChange[basepath] );
406-
407-
408-
bool dataSourceChanged { false };
409-
const QString layerId { node.namedItem( QStringLiteral( "id" ) ).toElement().text() };
410-
const QString provider { node.namedItem( QStringLiteral( "provider" ) ).toElement().text() };
411-
412-
// Try first to change the datasource of the existing layers, this will
413-
// maintain the current status (checked/unchecked) and group
414-
if ( QgsProject::instance()->mapLayer( layerId ) )
415-
{
416-
QgsDataProvider::ProviderOptions options;
417-
QgsMapLayer *mapLayer = QgsProject::instance()->mapLayer( layerId );
418-
if ( mapLayer )
419-
{
420-
mapLayer->setDataSource( datasource, name, provider, options );
421-
dataSourceChanged = mapLayer->isValid();
422-
}
423-
}
424-
425-
// If the data source was changed successfully, remove the bad layer from the dialog
426-
// otherwise, try to set the new datasource in the XML node and reload the layer,
427-
// finally marks with red all remaining bad layers.
428-
if ( dataSourceChanged )
429-
{
430-
mLayerList->removeRow( i-- );
431-
}
432-
else
433-
{
434-
node.namedItem( QStringLiteral( "datasource" ) ).toElement().firstChild().toText().setData( datasource );
435-
if ( QgsProject::instance()->readLayer( node ) )
436-
{
437-
mLayerList->removeRow( i-- );
438-
}
439-
else
440-
{
441-
item->setForeground( QBrush( Qt::red ) );
442-
if ( mFileBase[ name ].size() == 1 )
443-
mFileBase[ name ][0] = basepath ;
444-
else if ( mFileBase[ name ].size() > 1 )
445-
mFileBase[ name ].append( basepath );
446-
}
447-
}
448-
}
449-
450-
// Final cleanup: remove any bad layer (it should not be any btw)
451-
if ( mLayerList->rowCount() == 0 )
452-
{
453-
QList<QgsMapLayer *> toRemove;
454-
const auto mapLayers = QgsProject::instance()->mapLayers();
455-
for ( const auto &l : mapLayers )
456-
{
457-
if ( ! l->isValid() )
458-
toRemove << l;
459-
}
460-
QgsProject::instance()->removeMapLayers( toRemove );
461-
accept();
462-
}
463-
464-
QgsProject::instance()->layerTreeRegistryBridge()->setEnabled( false );
465-
466-
}
467-
468-
void QgsHandleBadLayers::accept()
469-
{
470-
471-
if ( mLayerList->rowCount() > 0 &&
472-
QMessageBox::warning( this,
473-
tr( "Unhandled layer will be lost." ),
474-
tr( "There are still %n unhandled layer(s). If they are not fixed, they will be disabled/deactivated until the project is opened again.",
475-
"unhandled layers",
476-
mLayerList->rowCount() ),
477-
QMessageBox::Ok | QMessageBox::Cancel,
478-
QMessageBox::Cancel ) == QMessageBox::Cancel )
479-
{
480-
return;
481-
}
482-
QList<QgsMapLayer *> toRemove;
483-
for ( const auto &l : QgsProject::instance()->mapLayers( ) )
484-
{
485-
if ( ! l->isValid() )
486-
toRemove << l;
487-
}
488-
QgsProject::instance()->layerTreeRegistryBridge()->setEnabled( true );
489-
QgsProject::instance()->removeMapLayers( toRemove );
490-
QgsProject::instance()->layerTreeRegistryBridge()->setEnabled( false );
491-
mLayerList->clear();
492-
493-
QDialog::accept();
494-
}
495-
496-
int QgsHandleBadLayers::layerCount()
497-
{
498-
return mLayerList->rowCount();
499-
}
1+
/***************************************************************************
2+
qgshandlebadlayers.cpp - description
3+
-------------------
4+
begin : Sat 5 Mar 2011
5+
copyright : (C) 2011 by Juergen E. Fischer, norBIT GmbH
6+
email : jef at norbit dot de
7+
***************************************************************************/
8+
9+
/***************************************************************************
10+
* *
11+
* This program is free software; you can redistribute it and/or modify *
12+
* it under the terms of the GNU General Public License as published by *
13+
* the Free Software Foundation; either version 2 of the License, or *
14+
* (at your option) any later version. *
15+
* *
16+
***************************************************************************/
17+
18+
#include "qgshandlebadlayers.h"
19+
#include "qgisapp.h"
20+
#include "qgsauthconfigselect.h"
21+
#include "qgsdataprovider.h"
22+
#include "qgsguiutils.h"
23+
#include "qgsdatasourceuri.h"
24+
#include "qgslogger.h"
25+
#include "qgsrasterlayer.h"
26+
#include "qgsproviderregistry.h"
27+
#include "qgsmessagebar.h"
28+
#include "qgssettings.h"
29+
#include "qgslayertreeregistrybridge.h"
30+
#include "qgsapplication.h"
31+
32+
#include <QDomDocument>
33+
#include <QDomElement>
34+
#include <QFileDialog>
35+
#include <QPushButton>
36+
#include <QToolButton>
37+
#include <QMessageBox>
38+
#include <QDialogButtonBox>
39+
#include <QUrl>
40+
41+
void QgsHandleBadLayersHandler::handleBadLayers( const QList<QDomNode> &layers )
42+
{
43+
QApplication::setOverrideCursor( Qt::ArrowCursor );
44+
QgsHandleBadLayers *dialog = new QgsHandleBadLayers( layers );
45+
46+
dialog->buttonBox->button( QDialogButtonBox::Ignore )->setToolTip( tr( "Import all unavailable layers unmodified (you can fix them later)." ) );
47+
dialog->buttonBox->button( QDialogButtonBox::Ignore )->setText( tr( "Keep Unavailable Layers" ) );
48+
dialog->buttonBox->button( QDialogButtonBox::Discard )->setToolTip( tr( "Remove all unavailable layers from the project" ) );
49+
dialog->buttonBox->button( QDialogButtonBox::Discard )->setText( tr( "Remove Unavailable Layers" ) );
50+
dialog->buttonBox->button( QDialogButtonBox::Discard )->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionDeleteSelected.svg" ) ) );
51+
52+
if ( dialog->layerCount() < layers.size() )
53+
QgisApp::instance()->messageBar()->pushMessage(
54+
tr( "Handle unavailable layers" ),
55+
tr( "%1 of %2 unavailable layers were not fixable." )
56+
.arg( layers.size() - dialog->layerCount() )
57+
.arg( layers.size() ),
58+
Qgis::Warning, QgisApp::instance()->messageTimeout() );
59+
60+
if ( dialog->layerCount() > 0 )
61+
{
62+
if ( dialog->exec() == dialog->Accepted )
63+
{
64+
emit layersChanged();
65+
}
66+
}
67+
68+
delete dialog;
69+
QApplication::restoreOverrideCursor();
70+
}
71+
72+
73+
QgsHandleBadLayers::QgsHandleBadLayers( const QList<QDomNode> &layers )
74+
: QDialog( QgisApp::instance() )
75+
, mLayers( layers )
76+
{
77+
setupUi( this );
78+
79+
mVectorFileFilter = QgsProviderRegistry::instance()->fileVectorFilters();
80+
mRasterFileFilter = QgsProviderRegistry::instance()->fileRasterFilters();
81+
82+
mBrowseButton = new QPushButton( tr( "Browse" ) );
83+
buttonBox->addButton( mBrowseButton, QDialogButtonBox::ActionRole );
84+
mBrowseButton->setDisabled( true );
85+
mApplyButton = new QPushButton( tr( "Apply Changes" ) );
86+
mApplyButton->setToolTip( tr( "Apply fixes to unavailable layers (remaining unavailable layers will be removed from the project)." ) );
87+
buttonBox->addButton( mApplyButton, QDialogButtonBox::ActionRole );
88+
89+
connect( mLayerList, &QTableWidget::itemSelectionChanged, this, &QgsHandleBadLayers::selectionChanged );
90+
connect( mBrowseButton, &QAbstractButton::clicked, this, &QgsHandleBadLayers::browseClicked );
91+
connect( mApplyButton, &QAbstractButton::clicked, this, &QgsHandleBadLayers::apply );
92+
connect( buttonBox->button( QDialogButtonBox::Ignore ), &QPushButton::clicked, this, &QgsHandleBadLayers::reject );
93+
connect( buttonBox->button( QDialogButtonBox::Discard ), &QPushButton::clicked, this, &QgsHandleBadLayers::accept );
94+
95+
mLayerList->clear();
96+
mLayerList->setSortingEnabled( true );
97+
mLayerList->setSelectionBehavior( QAbstractItemView::SelectRows );
98+
mLayerList->setColumnCount( 5 );
99+
mLayerList->setColumnWidth( 3, 75 );
100+
101+
mLayerList->setHorizontalHeaderLabels( QStringList()
102+
<< tr( "Layer name" )
103+
<< tr( "Type" )
104+
<< tr( "Provider" )
105+
<< tr( "Auth config" )
106+
<< tr( "Datasource" )
107+
);
108+
109+
int j = 0;
110+
for ( int i = 0; i < mLayers.size(); i++ )
111+
{
112+
const QDomNode &node = mLayers[i];
113+
114+
QString name = node.namedItem( QStringLiteral( "layername" ) ).toElement().text();
115+
QString type = node.toElement().attribute( QStringLiteral( "type" ) );
116+
QString datasource = node.namedItem( QStringLiteral( "datasource" ) ).toElement().text();
117+
QString provider = node.namedItem( QStringLiteral( "provider" ) ).toElement().text();
118+
QString vectorProvider = type == QLatin1String( "vector" ) ? provider : tr( "none" );
119+
bool providerFileBased = ( QgsProviderRegistry::instance()->providerCapabilities( provider ) & QgsDataProvider::File ) != 0;
120+
const QString basepath = datasource.left( datasource.lastIndexOf('/') );
121+
mFileBase[name].append( basepath );
122+
123+
QgsDebugMsg( QStringLiteral( "name=%1 type=%2 provider=%3 datasource='%4'" )
124+
.arg( name,
125+
type,
126+
vectorProvider,
127+
datasource ) );
128+
129+
mLayerList->setRowCount( j + 1 );
130+
131+
QTableWidgetItem *item = nullptr;
132+
133+
item = new QTableWidgetItem( name );
134+
item->setData( Qt::UserRole + 0, i );
135+
item->setFlags( item->flags() & ~Qt::ItemIsEditable );
136+
mLayerList->setItem( j, 0, item );
137+
138+
item = new QTableWidgetItem( type );
139+
item->setData( Qt::UserRole + 0, providerFileBased );
140+
item->setFlags( item->flags() & ~Qt::ItemIsEditable );
141+
mLayerList->setItem( j, 1, item );
142+
143+
item = new QTableWidgetItem( vectorProvider );
144+
item->setFlags( item->flags() & ~Qt::ItemIsEditable );
145+
mLayerList->setItem( j, 2, item );
146+
147+
if ( QgsAuthConfigUriEdit::hasConfigId( datasource ) )
148+
{
149+
QToolButton *btn = new QToolButton( this );
150+
btn->setMaximumWidth( 75 );
151+
btn->setMinimumHeight( 24 );
152+
btn->setText( tr( "Edit" ) );
153+
btn->setProperty( "row", j );
154+
connect( btn, &QAbstractButton::clicked, this, &QgsHandleBadLayers::editAuthCfg );
155+
mLayerList->setCellWidget( j, 3, btn );
156+
}
157+
else
158+
{
159+
item = new QTableWidgetItem( QString() );
160+
mLayerList->setItem( j, 3, item );
161+
}
162+
163+
item = new QTableWidgetItem( datasource );
164+
mLayerList->setItem( j, 4, item );
165+
166+
j++;
167+
}
168+
169+
// mLayerList->resizeColumnsToContents();
170+
}
171+
172+
void QgsHandleBadLayers::selectionChanged()
173+
{
174+
175+
mRows.clear();
176+
177+
Q_FOREACH ( QTableWidgetItem *item, mLayerList->selectedItems() )
178+
{
179+
if ( item->column() != 0 )
180+
continue;
181+
182+
bool providerFileBased = mLayerList->item( item->row(), 1 )->data( Qt::UserRole + 0 ).toBool();
183+
if ( !providerFileBased )
184+
continue;
185+
186+
mRows << item->row();
187+
}
188+
189+
mBrowseButton->setEnabled( !mRows.isEmpty() );
190+
}
191+
192+
QString QgsHandleBadLayers::filename( int row )
193+
{
194+
QString type = mLayerList->item( row, 1 )->text();
195+
QString provider = mLayerList->item( row, 2 )->text();
196+
QString datasource = mLayerList->item( row, 4 )->text();
197+
198+
if ( type == QLatin1String( "vector" ) )
199+
{
200+
const QVariantMap parts = QgsProviderRegistry::instance()->decodeUri( provider, datasource );
201+
// if parts is empty then provider doesn't handle this method!
202+
return parts.empty() ? datasource : parts.value( QStringLiteral( "path" ) ).toString();
203+
}
204+
else
205+
{
206+
return datasource;
207+
}
208+
}
209+
210+
void QgsHandleBadLayers::setFilename( int row, const QString &filename )
211+
{
212+
if ( !QFileInfo::exists( filename ) )
213+
return;
214+
215+
QString type = mLayerList->item( row, 1 )->text();
216+
QString provider = mLayerList->item( row, 2 )->text();
217+
QTableWidgetItem *item = mLayerList->item( row, 4 );
218+
219+
QString datasource = item->text();
220+
221+
if ( type == QLatin1String( "vector" ) )
222+
{
223+
if ( provider == QLatin1String( "spatialite" ) )
224+
{
225+
QgsDataSourceUri uri( datasource );
226+
uri.setDatabase( filename );
227+
datasource = uri.uri();
228+
}
229+
else if ( provider == QLatin1String( "ogr" ) )
230+
{
231+
QStringList theURIParts = datasource.split( '|' );
232+
theURIParts[0] = filename;
233+
datasource = theURIParts.join( QStringLiteral( "|" ) );
234+
}
235+
else if ( provider == QLatin1String( "delimitedtext" ) )
236+
{
237+
QUrl uriSource = QUrl::fromEncoded( datasource.toLatin1() );
238+
QUrl uriDest = QUrl::fromLocalFile( filename );
239+
uriDest.setQueryItems( uriSource.queryItems() );
240+
datasource = QString::fromLatin1( uriDest.toEncoded() );
241+
}
242+
}
243+
else
244+
{
245+
datasource = filename;
246+
}
247+
248+
item->setText( datasource );
249+
}
250+
251+
void QgsHandleBadLayers::browseClicked()
252+
{
253+
254+
if ( mRows.size() == 1 )
255+
{
256+
int row = mRows.at( 0 );
257+
QString type = mLayerList->item( row, 1 )->text();
258+
259+
QString memoryQualifier, fileFilter;
260+
if ( type == QLatin1String( "vector" ) )
261+
{
262+
memoryQualifier = QStringLiteral( "lastVectorFileFilter" );
263+
fileFilter = mVectorFileFilter;
264+
}
265+
else
266+
{
267+
memoryQualifier = QStringLiteral( "lastRasterFileFilter" );
268+
fileFilter = mRasterFileFilter;
269+
}
270+
271+
QString fn = filename( row );
272+
if ( fn.isNull() )
273+
return;
274+
275+
QStringList selectedFiles;
276+
QString enc;
277+
QString title = tr( "Select File to Replace '%1'" ).arg( fn );
278+
279+
QgsGuiUtils::openFilesRememberingFilter( memoryQualifier, fileFilter, selectedFiles, enc, title );
280+
if ( selectedFiles.size() != 1 )
281+
{
282+
QMessageBox::information( this, title, tr( "Please select exactly one file." ) );
283+
return;
284+
}
285+
286+
setFilename( row, selectedFiles[0] );
287+
}
288+
else if ( mRows.size() > 1 )
289+
{
290+
QString title = tr( "Select New Directory of Selected Files" );
291+
292+
QgsSettings settings;
293+
QString lastDir = settings.value( QStringLiteral( "UI/missingDirectory" ), QDir::homePath() ).toString();
294+
QString selectedFolder = QFileDialog::getExistingDirectory( this, title, lastDir );
295+
if ( selectedFolder.isEmpty() )
296+
{
297+
return;
298+
}
299+
300+
QDir dir( selectedFolder );
301+
if ( !dir.exists() )
302+
{
303+
return;
304+
}
305+
306+
Q_FOREACH ( int row, mRows )
307+
{
308+
bool providerFileBased = mLayerList->item( row, 1 )->data( Qt::UserRole + 0 ).toBool();
309+
if ( !providerFileBased )
310+
continue;
311+
312+
QString fn = filename( row );
313+
if ( fn.isEmpty() )
314+
continue;
315+
316+
QFileInfo fi( fn );
317+
fi.setFile( dir, fi.fileName() );
318+
if ( !fi.exists() )
319+
continue;
320+
321+
setFilename( row, fi.absoluteFilePath() );
322+
}
323+
}
324+
}
325+
326+
void QgsHandleBadLayers::editAuthCfg()
327+
{
328+
QToolButton *btn = qobject_cast<QToolButton *>( sender() );
329+
int row = -1;
330+
for ( int i = 0; i < mLayerList->rowCount(); i++ )
331+
{
332+
if ( mLayerList->cellWidget( i, 3 ) == btn )
333+
{
334+
row = i;
335+
break;
336+
}
337+
}
338+
339+
if ( row == -1 )
340+
return;
341+
342+
QString provider = mLayerList->item( row, 2 )->text();
343+
if ( provider == QLatin1String( "none" ) )
344+
provider.clear();
345+
346+
QString prevuri = mLayerList->item( row, 4 )->text();
347+
348+
QgsAuthConfigUriEdit *dlg = new QgsAuthConfigUriEdit( this, prevuri, provider );
349+
dlg->setWindowModality( Qt::WindowModal );
350+
dlg->resize( 500, 500 );
351+
if ( dlg->exec() )
352+
{
353+
QString newuri( dlg->dataSourceUri() );
354+
if ( newuri != prevuri )
355+
{
356+
mLayerList->item( row, 4 )->setText( newuri );
357+
}
358+
}
359+
dlg->deleteLater();
360+
}
361+
362+
void QgsHandleBadLayers::apply()
363+
{
364+
QgsProject::instance()->layerTreeRegistryBridge()->setEnabled( true );
365+
buttonBox->button( QDialogButtonBox::Ignore )->setEnabled( false );
366+
QHash<QString, QString> baseChange;
367+
368+
369+
370+
for ( int i = 0; i < mLayerList->rowCount(); i++ )
371+
{
372+
int idx = mLayerList->item( i, 0 )->data( Qt::UserRole ).toInt();
373+
QDomNode &node = const_cast<QDomNode &>( mLayers[ idx ] );
374+
375+
const QString name = mLayerList->item( i, 0 )->text();
376+
QTableWidgetItem *item = mLayerList->item( i, 4 );
377+
QString datasource = item->text();
378+
const QString basepath = datasource.left( datasource.lastIndexOf('/') );
379+
bool changed = false;
380+
381+
if ( mFileBase[ name ].size() == 1 )
382+
{
383+
if ( mFileBase[ name ][0] != basepath && !baseChange.contains( mFileBase[ name ][0] ) )
384+
{
385+
baseChange[ mFileBase[ name ][0] ] = basepath;
386+
changed = true;
387+
}
388+
}
389+
else if ( mFileBase[ name ].size() > 1 )
390+
{
391+
if ( mFileBase[ name ].indexOf( basepath ) == -1 )
392+
{
393+
const QList<QString> fileBases = mFileBase[ name ];
394+
for ( QString fileBase : fileBases )
395+
{
396+
if ( !baseChange.contains( fileBase ) )
397+
{
398+
baseChange[ fileBase ] = basepath;
399+
changed = true;
400+
}
401+
}
402+
}
403+
}
404+
if ( !changed && baseChange.contains( basepath ) )
405+
datasource = datasource.replace( basepath, baseChange[basepath] );
406+
407+
408+
bool dataSourceChanged { false };
409+
const QString layerId { node.namedItem( QStringLiteral( "id" ) ).toElement().text() };
410+
const QString provider { node.namedItem( QStringLiteral( "provider" ) ).toElement().text() };
411+
412+
// Try first to change the datasource of the existing layers, this will
413+
// maintain the current status (checked/unchecked) and group
414+
if ( QgsProject::instance()->mapLayer( layerId ) )
415+
{
416+
QgsDataProvider::ProviderOptions options;
417+
QgsMapLayer *mapLayer = QgsProject::instance()->mapLayer( layerId );
418+
if ( mapLayer )
419+
{
420+
mapLayer->setDataSource( datasource, name, provider, options );
421+
dataSourceChanged = mapLayer->isValid();
422+
}
423+
}
424+
425+
// If the data source was changed successfully, remove the bad layer from the dialog
426+
// otherwise, try to set the new datasource in the XML node and reload the layer,
427+
// finally marks with red all remaining bad layers.
428+
if ( dataSourceChanged )
429+
{
430+
mLayerList->removeRow( i-- );
431+
}
432+
else
433+
{
434+
node.namedItem( QStringLiteral( "datasource" ) ).toElement().firstChild().toText().setData( datasource );
435+
if ( QgsProject::instance()->readLayer( node ) )
436+
{
437+
mLayerList->removeRow( i-- );
438+
}
439+
else
440+
{
441+
item->setForeground( QBrush( Qt::red ) );
442+
if ( mFileBase[ name ].size() == 1 )
443+
mFileBase[ name ][0] = basepath ;
444+
else if ( mFileBase[ name ].size() > 1 )
445+
mFileBase[ name ].append( basepath );
446+
}
447+
}
448+
}
449+
450+
// Final cleanup: remove any bad layer (it should not be any btw)
451+
if ( mLayerList->rowCount() == 0 )
452+
{
453+
QList<QgsMapLayer *> toRemove;
454+
const auto mapLayers = QgsProject::instance()->mapLayers();
455+
for ( const auto &l : mapLayers )
456+
{
457+
if ( ! l->isValid() )
458+
toRemove << l;
459+
}
460+
QgsProject::instance()->removeMapLayers( toRemove );
461+
accept();
462+
}
463+
464+
QgsProject::instance()->layerTreeRegistryBridge()->setEnabled( false );
465+
466+
}
467+
468+
void QgsHandleBadLayers::accept()
469+
{
470+
471+
if ( mLayerList->rowCount() > 0 &&
472+
QMessageBox::warning( this,
473+
tr( "Unhandled layer will be lost." ),
474+
tr( "There are still %n unhandled layer(s). If they are not fixed, they will be disabled/deactivated until the project is opened again.",
475+
"unhandled layers",
476+
mLayerList->rowCount() ),
477+
QMessageBox::Ok | QMessageBox::Cancel,
478+
QMessageBox::Cancel ) == QMessageBox::Cancel )
479+
{
480+
return;
481+
}
482+
QList<QgsMapLayer *> toRemove;
483+
for ( const auto &l : QgsProject::instance()->mapLayers( ) )
484+
{
485+
if ( ! l->isValid() )
486+
toRemove << l;
487+
}
488+
QgsProject::instance()->layerTreeRegistryBridge()->setEnabled( true );
489+
QgsProject::instance()->removeMapLayers( toRemove );
490+
QgsProject::instance()->layerTreeRegistryBridge()->setEnabled( false );
491+
mLayerList->clear();
492+
493+
QDialog::accept();
494+
}
495+
496+
int QgsHandleBadLayers::layerCount()
497+
{
498+
return mLayerList->rowCount();
499+
}

‎src/app/qgshandlebadlayers.h

Lines changed: 80 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1,80 +1,80 @@
1-
/***************************************************************************
2-
qgshandlebadlayers.h - description
3-
-------------------
4-
begin : Sat 05 Mar 2011
5-
copyright : (C) 2011 by Juergen E. Fischer, norBIT GmbH
6-
email : jef at norbit dot de
7-
***************************************************************************/
8-
9-
/***************************************************************************
10-
* *
11-
* This program is free software; you can redistribute it and/or modify *
12-
* it under the terms of the GNU General Public License as published by *
13-
* the Free Software Foundation; either version 2 of the License, or *
14-
* (at your option) any later version. *
15-
* *
16-
***************************************************************************/
17-
#ifndef QGSHANDLEBADLAYERS_H
18-
#define QGSHANDLEBADLAYERS_H
19-
20-
#include "ui_qgshandlebadlayersbase.h"
21-
#include "qgsprojectbadlayerhandler.h"
22-
#include "qgis_app.h"
23-
24-
class APP_EXPORT QgsHandleBadLayersHandler
25-
: public QObject
26-
, public QgsProjectBadLayerHandler
27-
{
28-
Q_OBJECT
29-
30-
public:
31-
QgsHandleBadLayersHandler() = default;
32-
33-
//! Implementation of the handler
34-
void handleBadLayers( const QList<QDomNode> &layers ) override;
35-
36-
signals:
37-
38-
/**
39-
* Emitted when layers have changed
40-
* \since QGIS 3.6
41-
*/
42-
void layersChanged();
43-
44-
};
45-
46-
47-
class QPushButton;
48-
49-
class APP_EXPORT QgsHandleBadLayers
50-
: public QDialog
51-
, public Ui::QgsHandleBadLayersBase
52-
{
53-
Q_OBJECT
54-
55-
public:
56-
QgsHandleBadLayers( const QList<QDomNode> &layers );
57-
58-
int layerCount();
59-
60-
private slots:
61-
void selectionChanged();
62-
void browseClicked();
63-
void editAuthCfg();
64-
void apply();
65-
void accept() override;
66-
67-
private:
68-
QPushButton *mBrowseButton = nullptr;
69-
QPushButton *mApplyButton = nullptr;
70-
const QList<QDomNode> &mLayers;
71-
QList<int> mRows;
72-
QString mVectorFileFilter;
73-
QString mRasterFileFilter;
74-
QHash <QString, QList<QString> > mFileBase;
75-
76-
QString filename( int row );
77-
void setFilename( int row, const QString &filename );
78-
};
79-
80-
#endif
1+
/***************************************************************************
2+
qgshandlebadlayers.h - description
3+
-------------------
4+
begin : Sat 05 Mar 2011
5+
copyright : (C) 2011 by Juergen E. Fischer, norBIT GmbH
6+
email : jef at norbit dot de
7+
***************************************************************************/
8+
9+
/***************************************************************************
10+
* *
11+
* This program is free software; you can redistribute it and/or modify *
12+
* it under the terms of the GNU General Public License as published by *
13+
* the Free Software Foundation; either version 2 of the License, or *
14+
* (at your option) any later version. *
15+
* *
16+
***************************************************************************/
17+
#ifndef QGSHANDLEBADLAYERS_H
18+
#define QGSHANDLEBADLAYERS_H
19+
20+
#include "ui_qgshandlebadlayersbase.h"
21+
#include "qgsprojectbadlayerhandler.h"
22+
#include "qgis_app.h"
23+
24+
class APP_EXPORT QgsHandleBadLayersHandler
25+
: public QObject
26+
, public QgsProjectBadLayerHandler
27+
{
28+
Q_OBJECT
29+
30+
public:
31+
QgsHandleBadLayersHandler() = default;
32+
33+
//! Implementation of the handler
34+
void handleBadLayers( const QList<QDomNode> &layers ) override;
35+
36+
signals:
37+
38+
/**
39+
* Emitted when layers have changed
40+
* \since QGIS 3.6
41+
*/
42+
void layersChanged();
43+
44+
};
45+
46+
47+
class QPushButton;
48+
49+
class APP_EXPORT QgsHandleBadLayers
50+
: public QDialog
51+
, public Ui::QgsHandleBadLayersBase
52+
{
53+
Q_OBJECT
54+
55+
public:
56+
QgsHandleBadLayers( const QList<QDomNode> &layers );
57+
58+
int layerCount();
59+
60+
private slots:
61+
void selectionChanged();
62+
void browseClicked();
63+
void editAuthCfg();
64+
void apply();
65+
void accept() override;
66+
67+
private:
68+
QPushButton *mBrowseButton = nullptr;
69+
QPushButton *mApplyButton = nullptr;
70+
const QList<QDomNode> &mLayers;
71+
QList<int> mRows;
72+
QString mVectorFileFilter;
73+
QString mRasterFileFilter;
74+
QHash <QString, QList<QString> > mFileBase;
75+
76+
QString filename( int row );
77+
void setFilename( int row, const QString &filename );
78+
};
79+
80+
#endif

0 commit comments

Comments
 (0)
Please sign in to comment.