Skip to content

Commit b03d61d

Browse files
authoredMar 21, 2019
base path handler
1 parent 6f92e7c commit b03d61d

File tree

2 files changed

+573
-540
lines changed

2 files changed

+573
-540
lines changed
 

‎src/app/qgshandlebadlayers.cpp

Lines changed: 493 additions & 461 deletions
Original file line numberDiff line numberDiff line change
@@ -1,461 +1,493 @@
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-
121-
QgsDebugMsg( QStringLiteral( "name=%1 type=%2 provider=%3 datasource='%4'" )
122-
.arg( name,
123-
type,
124-
vectorProvider,
125-
datasource ) );
126-
127-
mLayerList->setRowCount( j + 1 );
128-
129-
QTableWidgetItem *item = nullptr;
130-
131-
item = new QTableWidgetItem( name );
132-
item->setData( Qt::UserRole + 0, i );
133-
item->setFlags( item->flags() & ~Qt::ItemIsEditable );
134-
mLayerList->setItem( j, 0, item );
135-
136-
item = new QTableWidgetItem( type );
137-
item->setData( Qt::UserRole + 0, providerFileBased );
138-
item->setFlags( item->flags() & ~Qt::ItemIsEditable );
139-
mLayerList->setItem( j, 1, item );
140-
141-
item = new QTableWidgetItem( vectorProvider );
142-
item->setFlags( item->flags() & ~Qt::ItemIsEditable );
143-
mLayerList->setItem( j, 2, item );
144-
145-
if ( QgsAuthConfigUriEdit::hasConfigId( datasource ) )
146-
{
147-
QToolButton *btn = new QToolButton( this );
148-
btn->setMaximumWidth( 75 );
149-
btn->setMinimumHeight( 24 );
150-
btn->setText( tr( "Edit" ) );
151-
btn->setProperty( "row", j );
152-
connect( btn, &QAbstractButton::clicked, this, &QgsHandleBadLayers::editAuthCfg );
153-
mLayerList->setCellWidget( j, 3, btn );
154-
}
155-
else
156-
{
157-
item = new QTableWidgetItem( QString() );
158-
mLayerList->setItem( j, 3, item );
159-
}
160-
161-
item = new QTableWidgetItem( datasource );
162-
mLayerList->setItem( j, 4, item );
163-
164-
j++;
165-
}
166-
167-
// mLayerList->resizeColumnsToContents();
168-
}
169-
170-
void QgsHandleBadLayers::selectionChanged()
171-
{
172-
173-
mRows.clear();
174-
175-
Q_FOREACH ( QTableWidgetItem *item, mLayerList->selectedItems() )
176-
{
177-
if ( item->column() != 0 )
178-
continue;
179-
180-
bool providerFileBased = mLayerList->item( item->row(), 1 )->data( Qt::UserRole + 0 ).toBool();
181-
if ( !providerFileBased )
182-
continue;
183-
184-
mRows << item->row();
185-
}
186-
187-
mBrowseButton->setEnabled( !mRows.isEmpty() );
188-
}
189-
190-
QString QgsHandleBadLayers::filename( int row )
191-
{
192-
QString type = mLayerList->item( row, 1 )->text();
193-
QString provider = mLayerList->item( row, 2 )->text();
194-
QString datasource = mLayerList->item( row, 4 )->text();
195-
196-
if ( type == QLatin1String( "vector" ) )
197-
{
198-
const QVariantMap parts = QgsProviderRegistry::instance()->decodeUri( provider, datasource );
199-
// if parts is empty then provider doesn't handle this method!
200-
return parts.empty() ? datasource : parts.value( QStringLiteral( "path" ) ).toString();
201-
}
202-
else
203-
{
204-
return datasource;
205-
}
206-
}
207-
208-
void QgsHandleBadLayers::setFilename( int row, const QString &filename )
209-
{
210-
if ( !QFileInfo::exists( filename ) )
211-
return;
212-
213-
QString type = mLayerList->item( row, 1 )->text();
214-
QString provider = mLayerList->item( row, 2 )->text();
215-
QTableWidgetItem *item = mLayerList->item( row, 4 );
216-
217-
QString datasource = item->text();
218-
219-
if ( type == QLatin1String( "vector" ) )
220-
{
221-
if ( provider == QLatin1String( "spatialite" ) )
222-
{
223-
QgsDataSourceUri uri( datasource );
224-
uri.setDatabase( filename );
225-
datasource = uri.uri();
226-
}
227-
else if ( provider == QLatin1String( "ogr" ) )
228-
{
229-
QStringList theURIParts = datasource.split( '|' );
230-
theURIParts[0] = filename;
231-
datasource = theURIParts.join( QStringLiteral( "|" ) );
232-
}
233-
else if ( provider == QLatin1String( "delimitedtext" ) )
234-
{
235-
QUrl uriSource = QUrl::fromEncoded( datasource.toLatin1() );
236-
QUrl uriDest = QUrl::fromLocalFile( filename );
237-
uriDest.setQueryItems( uriSource.queryItems() );
238-
datasource = QString::fromLatin1( uriDest.toEncoded() );
239-
}
240-
}
241-
else
242-
{
243-
datasource = filename;
244-
}
245-
246-
item->setText( datasource );
247-
}
248-
249-
void QgsHandleBadLayers::browseClicked()
250-
{
251-
252-
if ( mRows.size() == 1 )
253-
{
254-
int row = mRows.at( 0 );
255-
QString type = mLayerList->item( row, 1 )->text();
256-
257-
QString memoryQualifier, fileFilter;
258-
if ( type == QLatin1String( "vector" ) )
259-
{
260-
memoryQualifier = QStringLiteral( "lastVectorFileFilter" );
261-
fileFilter = mVectorFileFilter;
262-
}
263-
else
264-
{
265-
memoryQualifier = QStringLiteral( "lastRasterFileFilter" );
266-
fileFilter = mRasterFileFilter;
267-
}
268-
269-
QString fn = filename( row );
270-
if ( fn.isNull() )
271-
return;
272-
273-
QStringList selectedFiles;
274-
QString enc;
275-
QString title = tr( "Select File to Replace '%1'" ).arg( fn );
276-
277-
QgsGuiUtils::openFilesRememberingFilter( memoryQualifier, fileFilter, selectedFiles, enc, title );
278-
if ( selectedFiles.size() != 1 )
279-
{
280-
QMessageBox::information( this, title, tr( "Please select exactly one file." ) );
281-
return;
282-
}
283-
284-
setFilename( row, selectedFiles[0] );
285-
}
286-
else if ( mRows.size() > 1 )
287-
{
288-
QString title = tr( "Select New Directory of Selected Files" );
289-
290-
QgsSettings settings;
291-
QString lastDir = settings.value( QStringLiteral( "UI/missingDirectory" ), QDir::homePath() ).toString();
292-
QString selectedFolder = QFileDialog::getExistingDirectory( this, title, lastDir );
293-
if ( selectedFolder.isEmpty() )
294-
{
295-
return;
296-
}
297-
298-
QDir dir( selectedFolder );
299-
if ( !dir.exists() )
300-
{
301-
return;
302-
}
303-
304-
Q_FOREACH ( int row, mRows )
305-
{
306-
bool providerFileBased = mLayerList->item( row, 1 )->data( Qt::UserRole + 0 ).toBool();
307-
if ( !providerFileBased )
308-
continue;
309-
310-
QString fn = filename( row );
311-
if ( fn.isEmpty() )
312-
continue;
313-
314-
QFileInfo fi( fn );
315-
fi.setFile( dir, fi.fileName() );
316-
if ( !fi.exists() )
317-
continue;
318-
319-
setFilename( row, fi.absoluteFilePath() );
320-
}
321-
}
322-
}
323-
324-
void QgsHandleBadLayers::editAuthCfg()
325-
{
326-
QToolButton *btn = qobject_cast<QToolButton *>( sender() );
327-
int row = -1;
328-
for ( int i = 0; i < mLayerList->rowCount(); i++ )
329-
{
330-
if ( mLayerList->cellWidget( i, 3 ) == btn )
331-
{
332-
row = i;
333-
break;
334-
}
335-
}
336-
337-
if ( row == -1 )
338-
return;
339-
340-
QString provider = mLayerList->item( row, 2 )->text();
341-
if ( provider == QLatin1String( "none" ) )
342-
provider.clear();
343-
344-
QString prevuri = mLayerList->item( row, 4 )->text();
345-
346-
QgsAuthConfigUriEdit *dlg = new QgsAuthConfigUriEdit( this, prevuri, provider );
347-
dlg->setWindowModality( Qt::WindowModal );
348-
dlg->resize( 500, 500 );
349-
if ( dlg->exec() )
350-
{
351-
QString newuri( dlg->dataSourceUri() );
352-
if ( newuri != prevuri )
353-
{
354-
mLayerList->item( row, 4 )->setText( newuri );
355-
}
356-
}
357-
dlg->deleteLater();
358-
}
359-
360-
void QgsHandleBadLayers::apply()
361-
{
362-
QgsProject::instance()->layerTreeRegistryBridge()->setEnabled( true );
363-
buttonBox->button( QDialogButtonBox::Ignore )->setEnabled( false );
364-
365-
for ( int i = 0; i < mLayerList->rowCount(); i++ )
366-
{
367-
int idx = mLayerList->item( i, 0 )->data( Qt::UserRole ).toInt();
368-
QDomNode &node = const_cast<QDomNode &>( mLayers[ idx ] );
369-
370-
QTableWidgetItem *item = mLayerList->item( i, 4 );
371-
QString datasource = item->text();
372-
373-
bool dataSourceChanged { false };
374-
const QString layerId { node.namedItem( QStringLiteral( "id" ) ).toElement().text() };
375-
const QString provider { node.namedItem( QStringLiteral( "provider" ) ).toElement().text() };
376-
const QString name { mLayerList->item( i, 0 )->text() };
377-
378-
// Try first to change the datasource of the existing layers, this will
379-
// maintain the current status (checked/unchecked) and group
380-
if ( QgsProject::instance()->mapLayer( layerId ) )
381-
{
382-
QgsDataProvider::ProviderOptions options;
383-
QgsMapLayer *mapLayer = QgsProject::instance()->mapLayer( layerId );
384-
if ( mapLayer )
385-
{
386-
mapLayer->setDataSource( datasource, name, provider, options );
387-
dataSourceChanged = mapLayer->isValid();
388-
}
389-
}
390-
391-
// If the data source was changed successfully, remove the bad layer from the dialog
392-
// otherwise, try to set the new datasource in the XML node and reload the layer,
393-
// finally marks with red all remaining bad layers.
394-
if ( dataSourceChanged )
395-
{
396-
mLayerList->removeRow( i-- );
397-
}
398-
else
399-
{
400-
node.namedItem( QStringLiteral( "datasource" ) ).toElement().firstChild().toText().setData( datasource );
401-
if ( QgsProject::instance()->readLayer( node ) )
402-
{
403-
mLayerList->removeRow( i-- );
404-
}
405-
else
406-
{
407-
item->setForeground( QBrush( Qt::red ) );
408-
}
409-
}
410-
}
411-
412-
// Final cleanup: remove any bad layer (it should not be any btw)
413-
if ( mLayerList->rowCount() == 0 )
414-
{
415-
QList<QgsMapLayer *> toRemove;
416-
const auto mapLayers = QgsProject::instance()->mapLayers();
417-
for ( const auto &l : mapLayers )
418-
{
419-
if ( ! l->isValid() )
420-
toRemove << l;
421-
}
422-
QgsProject::instance()->removeMapLayers( toRemove );
423-
accept();
424-
}
425-
426-
QgsProject::instance()->layerTreeRegistryBridge()->setEnabled( false );
427-
428-
}
429-
430-
void QgsHandleBadLayers::accept()
431-
{
432-
433-
if ( mLayerList->rowCount() > 0 &&
434-
QMessageBox::warning( this,
435-
tr( "Unhandled layer will be lost." ),
436-
tr( "There are still %n unhandled layer(s). If they are not fixed, they will be disabled/deactivated until the project is opened again.",
437-
"unhandled layers",
438-
mLayerList->rowCount() ),
439-
QMessageBox::Ok | QMessageBox::Cancel,
440-
QMessageBox::Cancel ) == QMessageBox::Cancel )
441-
{
442-
return;
443-
}
444-
QList<QgsMapLayer *> toRemove;
445-
for ( const auto &l : QgsProject::instance()->mapLayers( ) )
446-
{
447-
if ( ! l->isValid() )
448-
toRemove << l;
449-
}
450-
QgsProject::instance()->layerTreeRegistryBridge()->setEnabled( true );
451-
QgsProject::instance()->removeMapLayers( toRemove );
452-
QgsProject::instance()->layerTreeRegistryBridge()->setEnabled( false );
453-
mLayerList->clear();
454-
455-
QDialog::accept();
456-
}
457-
458-
int QgsHandleBadLayers::layerCount()
459-
{
460-
return mLayerList->rowCount();
461-
}
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+
121+
mFileBase[name].append( const datasource.left( datasource.lastIndexOf('/') ) );
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+
for ( int i = 0; i < mLayerList->rowCount(); i++ )
369+
{
370+
int idx = mLayerList->item( i, 0 )->data( Qt::UserRole ).toInt();
371+
QDomNode &node = const_cast<QDomNode &>( mLayers[ idx ] );
372+
373+
QTableWidgetItem *item = mLayerList->item( i, 4 );
374+
QString datasource = item->text();
375+
const QString basepath = datasource.left( datasource.lastIndexOf('/') )
376+
bool changed = false;
377+
378+
if ( mFileBase[ name ].size() == 1 )
379+
{
380+
if ( mFileBase[ name ][0] != basepath && !baseChange.contains( mFileBase[ name ][0] ) )
381+
{
382+
baseChange[ mFileBase[ name ][0] ] = basepath;
383+
changed = true;
384+
}
385+
}
386+
else if ( mFileBase[ name ].size() > 1 )
387+
{
388+
if ( mFileBase[ name ].indexOf( basepath ) == -1 )
389+
{
390+
const QList fileBases = mFileBase[ name ];
391+
for ( QString fileBase : fileBases )
392+
{
393+
if ( !baseChange.contains( fileBase ) )
394+
{
395+
baseChange[ fileBase ] = basepath;
396+
changed = true;
397+
}
398+
}
399+
}
400+
}
401+
if ( !changed && baseChange.contains( basepath ) )
402+
datasource = datasource.replace( basepath, baseChange( basepath ) );
403+
404+
405+
bool dataSourceChanged { false };
406+
const QString layerId { node.namedItem( QStringLiteral( "id" ) ).toElement().text() };
407+
const QString provider { node.namedItem( QStringLiteral( "provider" ) ).toElement().text() };
408+
const QString name { mLayerList->item( i, 0 )->text() };
409+
410+
// Try first to change the datasource of the existing layers, this will
411+
// maintain the current status (checked/unchecked) and group
412+
if ( QgsProject::instance()->mapLayer( layerId ) )
413+
{
414+
QgsDataProvider::ProviderOptions options;
415+
QgsMapLayer *mapLayer = QgsProject::instance()->mapLayer( layerId );
416+
if ( mapLayer )
417+
{
418+
mapLayer->setDataSource( datasource, name, provider, options );
419+
dataSourceChanged = mapLayer->isValid();
420+
}
421+
}
422+
423+
// If the data source was changed successfully, remove the bad layer from the dialog
424+
// otherwise, try to set the new datasource in the XML node and reload the layer,
425+
// finally marks with red all remaining bad layers.
426+
if ( dataSourceChanged )
427+
{
428+
mLayerList->removeRow( i-- );
429+
}
430+
else
431+
{
432+
node.namedItem( QStringLiteral( "datasource" ) ).toElement().firstChild().toText().setData( datasource );
433+
if ( QgsProject::instance()->readLayer( node ) )
434+
{
435+
mLayerList->removeRow( i-- );
436+
}
437+
else
438+
{
439+
item->setForeground( QBrush( Qt::red ) );
440+
}
441+
}
442+
}
443+
444+
// Final cleanup: remove any bad layer (it should not be any btw)
445+
if ( mLayerList->rowCount() == 0 )
446+
{
447+
QList<QgsMapLayer *> toRemove;
448+
const auto mapLayers = QgsProject::instance()->mapLayers();
449+
for ( const auto &l : mapLayers )
450+
{
451+
if ( ! l->isValid() )
452+
toRemove << l;
453+
}
454+
QgsProject::instance()->removeMapLayers( toRemove );
455+
accept();
456+
}
457+
458+
QgsProject::instance()->layerTreeRegistryBridge()->setEnabled( false );
459+
460+
}
461+
462+
void QgsHandleBadLayers::accept()
463+
{
464+
465+
if ( mLayerList->rowCount() > 0 &&
466+
QMessageBox::warning( this,
467+
tr( "Unhandled layer will be lost." ),
468+
tr( "There are still %n unhandled layer(s). If they are not fixed, they will be disabled/deactivated until the project is opened again.",
469+
"unhandled layers",
470+
mLayerList->rowCount() ),
471+
QMessageBox::Ok | QMessageBox::Cancel,
472+
QMessageBox::Cancel ) == QMessageBox::Cancel )
473+
{
474+
return;
475+
}
476+
QList<QgsMapLayer *> toRemove;
477+
for ( const auto &l : QgsProject::instance()->mapLayers( ) )
478+
{
479+
if ( ! l->isValid() )
480+
toRemove << l;
481+
}
482+
QgsProject::instance()->layerTreeRegistryBridge()->setEnabled( true );
483+
QgsProject::instance()->removeMapLayers( toRemove );
484+
QgsProject::instance()->layerTreeRegistryBridge()->setEnabled( false );
485+
mLayerList->clear();
486+
487+
QDialog::accept();
488+
}
489+
490+
int QgsHandleBadLayers::layerCount()
491+
{
492+
return mLayerList->rowCount();
493+
}

‎src/app/qgshandlebadlayers.h

Lines changed: 80 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -1,79 +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-
75-
QString filename( int row );
76-
void setFilename( int row, const QString &filename );
77-
};
78-
79-
#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.