17#include "moc_qgsprocessingmultipleselectiondialog.cpp"
30#include <QStandardItemModel>
31#include <QStandardItem>
36#include <QDirIterator>
38#include <QDragEnterEvent>
42QgsProcessingMultipleSelectionPanelWidget::QgsProcessingMultipleSelectionPanelWidget(
const QVariantList &availableOptions,
43 const QVariantList &selectedOptions,
46 , mValueFormatter( []( const QVariant & v )->QString
48 if ( v.userType() == qMetaTypeId<QgsProcessingModelChildParameterSource>() )
49 return v.value< QgsProcessingModelChildParameterSource >().staticValue().toString();
58 mSelectionList->setSelectionBehavior( QAbstractItemView::SelectRows );
59 mSelectionList->setSelectionMode( QAbstractItemView::ExtendedSelection );
60 mSelectionList->setDragDropMode( QAbstractItemView::InternalMove );
62 mButtonSelectAll =
new QPushButton( tr(
"Select All" ) );
63 mButtonBox->addButton( mButtonSelectAll, QDialogButtonBox::ActionRole );
65 mButtonClearSelection =
new QPushButton( tr(
"Clear Selection" ) );
66 mButtonBox->addButton( mButtonClearSelection, QDialogButtonBox::ActionRole );
68 mButtonToggleSelection =
new QPushButton( tr(
"Toggle Selection" ) );
69 mButtonBox->addButton( mButtonToggleSelection, QDialogButtonBox::ActionRole );
71 connect( mButtonSelectAll, &QPushButton::clicked,
this, [ = ] { selectAll(
true ); } );
72 connect( mButtonClearSelection, &QPushButton::clicked,
this, [ = ] { selectAll(
false ); } );
73 connect( mButtonToggleSelection, &QPushButton::clicked,
this, &QgsProcessingMultipleSelectionPanelWidget::toggleSelection );
75 connect( mButtonBox, &QDialogButtonBox::accepted,
this, &QgsProcessingMultipleSelectionPanelWidget::acceptClicked );
76 populateList( availableOptions, selectedOptions );
78 connect( mModel, &QStandardItemModel::itemChanged,
this, &QgsProcessingMultipleSelectionPanelWidget::selectionChanged );
82 connect( mModel, &QStandardItemModel::rowsRemoved,
this, &QgsProcessingMultipleSelectionPanelWidget::selectionChanged );
85void QgsProcessingMultipleSelectionPanelWidget::setValueFormatter(
const std::function<QString(
const QVariant & )> &
formatter )
89 for (
int i = 0; i < mModel->rowCount(); ++i )
91 mModel->item( i )->setText( mValueFormatter( mModel->item( i )->data( Qt::UserRole ) ) );
95QVariantList QgsProcessingMultipleSelectionPanelWidget::selectedOptions()
const
98 options.reserve( mModel->rowCount() );
99 bool hasModelSources =
false;
100 for (
int i = 0; i < mModel->rowCount(); ++i )
102 QStandardItem *item = mModel->item( i );
108 if ( item->checkState() == Qt::Checked )
110 const QVariant option = item->data( Qt::UserRole );
112 if ( option.userType() == qMetaTypeId<QgsProcessingModelChildParameterSource>() )
113 hasModelSources =
true;
119 if ( hasModelSources )
122 QVariantList originalOptions = options;
124 for (
const QVariant &option : originalOptions )
126 if ( option.userType() == qMetaTypeId<QgsProcessingModelChildParameterSource>() )
129 options << QVariant::fromValue( QgsProcessingModelChildParameterSource::fromStaticValue( option ) );
137void QgsProcessingMultipleSelectionPanelWidget::selectAll(
const bool checked )
139 const QList<QStandardItem *> items = currentItems();
140 for ( QStandardItem *item : items )
142 item->setCheckState( checked ? Qt::Checked : Qt::Unchecked );
146void QgsProcessingMultipleSelectionPanelWidget::toggleSelection()
148 const QList<QStandardItem *> items = currentItems();
149 for ( QStandardItem *item : items )
151 item->setCheckState( item->checkState() == Qt::Unchecked ? Qt::Checked : Qt::Unchecked );
155QList<QStandardItem *> QgsProcessingMultipleSelectionPanelWidget::currentItems()
157 QList<QStandardItem *> items;
158 const QModelIndexList selection = mSelectionList->selectionModel()->selectedIndexes();
159 if ( selection.size() > 1 )
161 items.reserve( selection.size() );
162 for (
const QModelIndex &index : selection )
164 items << mModel->itemFromIndex( index );
169 items.reserve( mModel->rowCount() );
170 for (
int i = 0; i < mModel->rowCount(); ++i )
172 items << mModel->item( i );
178void QgsProcessingMultipleSelectionPanelWidget::populateList(
const QVariantList &availableOptions,
const QVariantList &selectedOptions )
180 mModel =
new QStandardItemModel(
this );
182 QVariantList remainingOptions = availableOptions;
185 for (
const QVariant &option : selectedOptions )
191 addOption( option, mValueFormatter( option ),
true );
192 remainingOptions.removeAll( option );
195 for (
const QVariant &option : std::as_const( remainingOptions ) )
197 addOption( option, mValueFormatter( option ),
false );
200 mSelectionList->setModel( mModel );
203QList< int> QgsProcessingMultipleSelectionPanelWidget::existingMapLayerFromMimeData(
const QMimeData *data )
const
212 for (
int i = 0; i < mModel->rowCount(); ++i )
215 QString userRole = mModel->item( i )->data( Qt::UserRole ).toString();
216 if ( userRole == layer->id() || userRole == layer->source() )
226void QgsProcessingMultipleSelectionPanelWidget::dragEnterEvent( QDragEnterEvent *event )
228 if ( !( event->possibleActions() & Qt::CopyAction ) )
231 const QList< int> indexes = existingMapLayerFromMimeData( event->mimeData() );
232 if ( !indexes.isEmpty() )
235 event->setDropAction( Qt::CopyAction );
240void QgsProcessingMultipleSelectionPanelWidget::dropEvent( QDropEvent *event )
242 if ( !( event->possibleActions() & Qt::CopyAction ) )
245 const QList< int> indexes = existingMapLayerFromMimeData( event->mimeData() );
246 if ( !indexes.isEmpty() )
249 setFocus( Qt::MouseFocusReason );
250 event->setDropAction( Qt::CopyAction );
253 for (
const int i : indexes )
255 mModel->item( i )->setCheckState( Qt::Checked );
257 emit selectionChanged();
261void QgsProcessingMultipleSelectionPanelWidget::addOption(
const QVariant &value,
const QString &title,
bool selected,
bool updateExistingTitle )
264 for (
int i = 0; i < mModel->rowCount(); ++i )
266 if ( mModel->item( i )->data( Qt::UserRole ) == value ||
267 ( mModel->item( i )->data( Qt::UserRole ).userType() == qMetaTypeId<QgsProcessingModelChildParameterSource>() &&
268 value.userType() == qMetaTypeId<QgsProcessingModelChildParameterSource>() &&
269 mModel->item( i )->data( Qt::UserRole ).value< QgsProcessingModelChildParameterSource >() ==
270 value.value< QgsProcessingModelChildParameterSource >() )
273 if ( updateExistingTitle )
274 mModel->item( i )->setText( title );
279 std::unique_ptr< QStandardItem > item = std::make_unique< QStandardItem >( title );
280 item->setData( value, Qt::UserRole );
281 item->setCheckState( selected ? Qt::Checked : Qt::Unchecked );
282 item->setCheckable(
true );
283 item->setDropEnabled(
false );
284 mModel->appendRow( item.release() );
293QgsProcessingMultipleSelectionDialog::QgsProcessingMultipleSelectionDialog(
const QVariantList &availableOptions,
const QVariantList &selectedOptions, QWidget *parent, Qt::WindowFlags flags )
294 : QDialog( parent, flags )
296 setWindowTitle( tr(
"Multiple Selection" ) );
297 QVBoxLayout *vLayout =
new QVBoxLayout();
298 mWidget =
new QgsProcessingMultipleSelectionPanelWidget( availableOptions, selectedOptions );
299 vLayout->addWidget( mWidget );
300 mWidget->buttonBox()->addButton( QDialogButtonBox::Cancel );
301 connect( mWidget->buttonBox(), &QDialogButtonBox::accepted,
this, &QDialog::accept );
302 connect( mWidget->buttonBox(), &QDialogButtonBox::rejected,
this, &QDialog::reject );
303 setLayout( vLayout );
306void QgsProcessingMultipleSelectionDialog::setValueFormatter(
const std::function<QString(
const QVariant & )> &
formatter )
311QVariantList QgsProcessingMultipleSelectionDialog::selectedOptions()
const
313 return mWidget->selectedOptions();
322 const QList<QgsProcessingModelChildParameterSource> &modelSources,
323 QgsProcessingModelAlgorithm *model, QWidget *parent )
324 : QgsProcessingMultipleSelectionPanelWidget( QVariantList(), selectedOptions, parent )
325 , mParameter( parameter )
327 QPushButton *addFileButton =
new QPushButton( tr(
"Add File(s)…" ) );
328 connect( addFileButton, &QPushButton::clicked,
this, &QgsProcessingMultipleInputPanelWidget::addFiles );
329 buttonBox()->addButton( addFileButton, QDialogButtonBox::ActionRole );
331 QPushButton *addDirButton =
new QPushButton( tr(
"Add Directory…" ) );
332 connect( addDirButton, &QPushButton::clicked,
this, &QgsProcessingMultipleInputPanelWidget::addDirectory );
333 buttonBox()->addButton( addDirButton, QDialogButtonBox::ActionRole );
334 setAcceptDrops(
true );
335 for (
const QgsProcessingModelChildParameterSource &source : modelSources )
337 addOption( QVariant::fromValue( source ), source.friendlyIdentifier( model ),
false,
true );
341void QgsProcessingMultipleInputPanelWidget::setProject(
QgsProject *project )
344 populateFromProject( project );
347void QgsProcessingMultipleInputPanelWidget::addFiles()
350 QString path = settings.
value( QStringLiteral(
"/Processing/LastInputPath" ), QDir::homePath() ).toString();
354 filter = generator->createFileFilter();
356 filter = QObject::tr(
"All files (*.*)" );
358 const QStringList filenames = QFileDialog::getOpenFileNames(
this, tr(
"Select File(s)" ), path, filter );
359 if ( filenames.empty() )
362 settings.
setValue( QStringLiteral(
"/Processing/LastInputPath" ), QFileInfo( filenames.at( 0 ) ).path() );
364 for (
const QString &file : filenames )
366 addOption( file, file,
true );
369 emit selectionChanged();
372void QgsProcessingMultipleInputPanelWidget::addDirectory()
375 const QString path = settings.
value( QStringLiteral(
"/Processing/LastInputPath" ), QDir::homePath() ).toString();
377 const QString dir = QFileDialog::getExistingDirectory(
this, tr(
"Select Directory" ), path );
381 settings.
setValue( QStringLiteral(
"/Processing/LastInputPath" ), dir );
383 QStringList nameFilters;
387 for (
const QString &extension : extensions )
389 nameFilters << QStringLiteral(
"*.%1" ).arg( extension );
390 nameFilters << QStringLiteral(
"*.%1" ).arg( extension.toUpper() );
391 nameFilters << QStringLiteral(
"*.%1" ).arg( extension.toLower() );
395 QDirIterator it( dir, nameFilters, QDir::Files | QDir::NoSymLinks | QDir::NoDotAndDotDot, QDirIterator::Subdirectories );
396 while ( it.hasNext() )
398 const QString fullPath = it.next();
399 if ( fullPath.endsWith( QLatin1String(
".dbf" ), Qt::CaseInsensitive ) )
401 if ( QFileInfo::exists( QStringLiteral(
"%1.shp" ).arg( fullPath.chopped( 4 ) ) ) ||
402 QFileInfo::exists( QStringLiteral(
"%1.SHP" ).arg( fullPath.chopped( 4 ) ) ) )
408 else if ( fullPath.endsWith( QLatin1String(
".aux.xml" ), Qt::CaseInsensitive ) ||
409 fullPath.endsWith( QLatin1String(
".shp.xml" ), Qt::CaseInsensitive ) )
414 addOption( fullPath, fullPath,
true );
416 emit selectionChanged();
419QList< int> QgsProcessingMultipleInputPanelWidget::existingMapLayerFromMimeData(
const QMimeData *data,
QgsMimeDataUtils::UriList &handledUrls )
const
427 bool matched =
false;
430 for (
int i = 0; i < mModel->rowCount(); ++i )
433 const QString userRole = mModel->item( i )->data( Qt::UserRole ).toString();
434 if ( userRole == layer->id() || userRole == layer->source() )
444 handledUrls.append( u );
453 QStringList skipUrlData;
454 skipUrlData.reserve( skipUrls.size() );
457 skipUrlData.append( u.data() );
465 if ( skipUrlData.contains( u.data() ) )
476 && u.layerType == QLatin1String(
"vector" ) )
478 bool acceptable =
false;
509 && u.layerType == QLatin1String(
"raster" ) && u.providerKey == QLatin1String(
"gdal" ) )
512 && u.layerType == QLatin1String(
"mesh" ) && u.providerKey == QLatin1String(
"mdal" ) )
515 && u.layerType == QLatin1String(
"pointcloud" ) )
518 && u.layerType == QLatin1String(
"vector-tile" ) )
522 if ( !uriList.isEmpty() )
526 QStringList rawPaths;
527 if ( data->hasUrls() )
529 const QList< QUrl > urls = data->urls();
530 rawPaths.reserve( urls.count() );
531 for (
const QUrl &url : urls )
533 const QString local = url.toLocalFile();
534 if ( !rawPaths.contains( local ) )
535 rawPaths.append( local );
538 if ( !data->text().isEmpty() && !rawPaths.contains( data->text() ) )
539 rawPaths.append( data->text() );
541 for (
const QString &path : std::as_const( rawPaths ) )
543 QFileInfo file( path );
554void QgsProcessingMultipleInputPanelWidget::dragEnterEvent( QDragEnterEvent *event )
556 if ( !( event->possibleActions() & Qt::CopyAction ) )
561 const QList< int> indexes = existingMapLayerFromMimeData( event->mimeData(), handledUris );
562 if ( !indexes.isEmpty() )
565 event->setDropAction( Qt::CopyAction );
571 const QStringList uris = compatibleUrisFromMimeData( mParameter, event->mimeData(), handledUris );
572 if ( !uris.isEmpty() )
575 event->setDropAction( Qt::CopyAction );
580void QgsProcessingMultipleInputPanelWidget::dropEvent( QDropEvent *event )
582 if ( !( event->possibleActions() & Qt::CopyAction ) )
586 const QList< int> indexes = existingMapLayerFromMimeData( event->mimeData(), handledUris );
587 if ( !indexes.isEmpty() )
590 setFocus( Qt::MouseFocusReason );
591 event->setDropAction( Qt::CopyAction );
594 for (
const int i : indexes )
596 mModel->item( i )->setCheckState( Qt::Checked );
598 emit selectionChanged();
602 const QStringList uris = compatibleUrisFromMimeData( mParameter, event->mimeData(), handledUris );
603 if ( !uris.isEmpty() )
605 for (
const QString &uri : uris )
607 addOption( uri, uri,
true );
609 emit selectionChanged();
613void QgsProcessingMultipleInputPanelWidget::populateFromProject(
QgsProject *project )
617 for (
int i = 0; i < mModel->rowCount(); ++i )
619 const QStandardItem *item = mModel->item( i );
620 if ( item->data( Qt::UserRole ) == layerId )
622 bool isChecked = ( item->checkState() == Qt::Checked );
623 mModel->removeRow( i );
626 emit selectionChanged();
636 const QString authid = layer->crs().authid();
638 if ( settings.value( QStringLiteral(
"Processing/Configuration/SHOW_CRS_DEF" ),
true ).toBool() && !authid.isEmpty() )
639 title = QStringLiteral(
"%1 [%2]" ).arg( layer->name(), authid );
641 title = layer->name();
644 QString
id = layer->id();
646 id = QStringLiteral(
"main" );
648 for (
int i = 0; i < mModel->rowCount(); ++i )
651 if ( mModel->item( i )->data( Qt::UserRole ) == layer->id() )
656 else if ( mModel->item( i )->data( Qt::UserRole ) == layer->source() )
658 id = layer->source();
663 addOption(
id, title,
false,
true );
666 switch ( mParameter->layerType() )
803 const QList< QgsProcessingModelChildParameterSource > &modelSources, QgsProcessingModelAlgorithm *model, QWidget *parent, Qt::WindowFlags flags )
804 : QDialog( parent, flags )
806 setWindowTitle( tr(
"Multiple Selection" ) );
807 QVBoxLayout *vLayout =
new QVBoxLayout();
808 mWidget =
new QgsProcessingMultipleInputPanelWidget( parameter, selectedOptions, modelSources, model );
809 vLayout->addWidget( mWidget );
810 mWidget->buttonBox()->addButton( QDialogButtonBox::Cancel );
811 connect( mWidget->buttonBox(), &QDialogButtonBox::accepted,
this, &QDialog::accept );
812 connect( mWidget->buttonBox(), &QDialogButtonBox::rejected,
this, &QDialog::reject );
813 setLayout( vLayout );
814 setAcceptDrops(
true );
817QVariantList QgsProcessingMultipleInputDialog::selectedOptions()
const
819 return mWidget->selectedOptions();
822void QgsProcessingMultipleInputDialog::setProject(
QgsProject *project )
824 mWidget->setProject( project );
@ File
Files (i.e. non map layer sources, such as text files)
@ Annotation
Annotation layers.
@ Vector
Tables (i.e. vector layers with or without geometry). When used for a sink this indicates the sink ha...
@ VectorTile
Vector tile layers.
@ MapLayer
Any map layer type (raster, vector, mesh, point cloud, annotation or plugin layer)
@ VectorAnyGeometry
Any vector layer with geometry.
@ VectorPoint
Vector point layers.
@ VectorPolygon
Vector polygon layers.
@ VectorLine
Vector line layers.
@ PointCloud
Point cloud layers.
Represents a map layer containing a set of georeferenced annotations, e.g.
Abstract interface for classes which generate a file filter string.
static QStringList extensionsFromFilter(const QString &filter)
Returns a list of the extensions contained within a file filter string.
static void enableAutoGeometryRestore(QWidget *widget, const QString &key=QString())
Register the widget to allow its position to be automatically saved and restored when open and closed...
Base class for all map layer types.
Represents a mesh layer supporting display of data on structured or unstructured meshes.
QList< QgsMimeDataUtils::Uri > UriList
static UriList decodeUriList(const QMimeData *data)
Base class for plugin layers.
Represents a map layer supporting display of point clouds.
A parameter for processing algorithms which accepts multiple map layers.
Qgis::ProcessingSourceType layerType() const
Returns the layer type for layers acceptable by the parameter.
static QList< QgsAnnotationLayer * > compatibleAnnotationLayers(QgsProject *project, bool sort=true)
Returns a list of annotation layers from a project which are compatible with the processing framework...
static QString encodeProviderKeyAndUri(const QString &providerKey, const QString &uri)
Encodes a provider key and layer uri to a single string, for use with decodeProviderKeyAndUri()
static QList< QgsRasterLayer * > compatibleRasterLayers(QgsProject *project, bool sort=true)
Returns a list of raster layers from a project which are compatible with the processing framework.
static QList< QgsPluginLayer * > compatiblePluginLayers(QgsProject *project, bool sort=true)
Returns a list of plugin layers from a project which are compatible with the processing framework.
static QList< QgsVectorLayer * > compatibleVectorLayers(QgsProject *project, const QList< int > &sourceTypes=QList< int >(), bool sort=true)
Returns a list of vector layers from a project which are compatible with the processing framework.
static QList< QgsVectorTileLayer * > compatibleVectorTileLayers(QgsProject *project, bool sort=true)
Returns a list of vector tile layers from a project which are compatible with the processing framewor...
static QList< QgsPointCloudLayer * > compatiblePointCloudLayers(QgsProject *project, bool sort=true)
Returns a list of point cloud layers from a project which are compatible with the processing framewor...
static QList< QgsMeshLayer * > compatibleMeshLayers(QgsProject *project, bool sort=true)
Returns a list of mesh layers from a project which are compatible with the processing framework.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
QgsAnnotationLayer * mainAnnotationLayer()
Returns the main annotation layer associated with the project.
void layerRemoved(const QString &layerId)
Emitted after a layer was removed from the registry.
Represents a raster layer.
This class is a composition of two QSettings instances:
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
Represents a vector layer which manages a vector based data sets.
Implements a map layer that is dedicated to rendering of vector tiles.
static Qgis::GeometryType geometryType(Qgis::WkbType type)
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...