17#include "moc_qgsprocessingmultipleselectiondialog.cpp" 
   31#include <QStandardItemModel> 
   32#include <QStandardItem> 
   37#include <QDirIterator> 
   39#include <QDragEnterEvent> 
   43QgsProcessingMultipleSelectionPanelWidget::QgsProcessingMultipleSelectionPanelWidget( 
const QVariantList &availableOptions, 
const QVariantList &selectedOptions, QWidget *parent )
 
   45  , mValueFormatter( []( const QVariant &v ) -> QString {
 
   46    if ( v.userType() == qMetaTypeId<QgsProcessingModelChildParameterSource>() )
 
   47      return v.value<QgsProcessingModelChildParameterSource>().staticValue().toString();
 
   56  mSelectionList->setSelectionBehavior( QAbstractItemView::SelectRows );
 
   57  mSelectionList->setSelectionMode( QAbstractItemView::ExtendedSelection );
 
   58  mSelectionList->setDragDropMode( QAbstractItemView::InternalMove );
 
   60  mButtonSelectAll = 
new QPushButton( tr( 
"Select All" ) );
 
   61  mButtonBox->addButton( mButtonSelectAll, QDialogButtonBox::ActionRole );
 
   63  mButtonClearSelection = 
new QPushButton( tr( 
"Clear Selection" ) );
 
   64  mButtonBox->addButton( mButtonClearSelection, QDialogButtonBox::ActionRole );
 
   66  mButtonToggleSelection = 
new QPushButton( tr( 
"Toggle Selection" ) );
 
   67  mButtonBox->addButton( mButtonToggleSelection, QDialogButtonBox::ActionRole );
 
   69  connect( mButtonSelectAll, &QPushButton::clicked, 
this, [
this] { selectAll( 
true ); } );
 
   70  connect( mButtonClearSelection, &QPushButton::clicked, 
this, [
this] { selectAll( 
false ); } );
 
   71  connect( mButtonToggleSelection, &QPushButton::clicked, 
this, &QgsProcessingMultipleSelectionPanelWidget::toggleSelection );
 
   73  connect( mButtonBox, &QDialogButtonBox::accepted, 
this, &QgsProcessingMultipleSelectionPanelWidget::acceptClicked );
 
   74  populateList( availableOptions, selectedOptions );
 
   76  connect( mModel, &QStandardItemModel::itemChanged, 
this, &QgsProcessingMultipleSelectionPanelWidget::selectionChanged );
 
   80  connect( mModel, &QStandardItemModel::rowsRemoved, 
this, &QgsProcessingMultipleSelectionPanelWidget::selectionChanged );
 
   83void QgsProcessingMultipleSelectionPanelWidget::setValueFormatter( 
const std::function<QString( 
const QVariant & )> &formatter )
 
   85  mValueFormatter = formatter;
 
   87  for ( 
int i = 0; i < mModel->rowCount(); ++i )
 
   89    mModel->item( i )->setText( mValueFormatter( mModel->item( i )->data( Qt::UserRole ) ) );
 
   93QVariantList QgsProcessingMultipleSelectionPanelWidget::selectedOptions()
 const 
   96  options.reserve( mModel->rowCount() );
 
   97  bool hasModelSources = 
false;
 
   98  for ( 
int i = 0; i < mModel->rowCount(); ++i )
 
  100    QStandardItem *item = mModel->item( i );
 
  106    if ( item->checkState() == Qt::Checked )
 
  108      const QVariant option = item->data( Qt::UserRole );
 
  110      if ( option.userType() == qMetaTypeId<QgsProcessingModelChildParameterSource>() )
 
  111        hasModelSources = 
true;
 
  117  if ( hasModelSources )
 
  120    QVariantList originalOptions = options;
 
  122    for ( 
const QVariant &option : originalOptions )
 
  124      if ( option.userType() == qMetaTypeId<QgsProcessingModelChildParameterSource>() )
 
  127        options << QVariant::fromValue( QgsProcessingModelChildParameterSource::fromStaticValue( option ) );
 
  135void QgsProcessingMultipleSelectionPanelWidget::selectAll( 
const bool checked )
 
  137  const QList<QStandardItem *> items = currentItems();
 
  138  for ( QStandardItem *item : items )
 
  140    item->setCheckState( checked ? Qt::Checked : Qt::Unchecked );
 
  144void QgsProcessingMultipleSelectionPanelWidget::toggleSelection()
 
  146  const QList<QStandardItem *> items = currentItems();
 
  147  for ( QStandardItem *item : items )
 
  149    item->setCheckState( item->checkState() == Qt::Unchecked ? Qt::Checked : Qt::Unchecked );
 
  153QList<QStandardItem *> QgsProcessingMultipleSelectionPanelWidget::currentItems()
 
  155  QList<QStandardItem *> items;
 
  156  const QModelIndexList selection = mSelectionList->selectionModel()->selectedIndexes();
 
  157  if ( selection.size() > 1 )
 
  159    items.reserve( selection.size() );
 
  160    for ( 
const QModelIndex &index : selection )
 
  162      items << mModel->itemFromIndex( index );
 
  167    items.reserve( mModel->rowCount() );
 
  168    for ( 
int i = 0; i < mModel->rowCount(); ++i )
 
  170      items << mModel->item( i );
 
  176void QgsProcessingMultipleSelectionPanelWidget::populateList( 
const QVariantList &availableOptions, 
const QVariantList &selectedOptions )
 
  178  mModel = 
new QStandardItemModel( 
this );
 
  180  QVariantList remainingOptions = availableOptions;
 
  183  for ( 
const QVariant &option : selectedOptions )
 
  189    addOption( option, mValueFormatter( option ), 
true );
 
  190    remainingOptions.removeAll( option );
 
  193  for ( 
const QVariant &option : std::as_const( remainingOptions ) )
 
  195    addOption( option, mValueFormatter( option ), 
false );
 
  198  mSelectionList->setModel( mModel );
 
  201QList<int> QgsProcessingMultipleSelectionPanelWidget::existingMapLayerFromMimeData( 
const QMimeData *data )
 const 
  210      for ( 
int i = 0; i < mModel->rowCount(); ++i )
 
  213        QString userRole = mModel->item( i )->data( Qt::UserRole ).toString();
 
  214        if ( userRole == layer->id() || userRole == layer->source() )
 
  224void QgsProcessingMultipleSelectionPanelWidget::dragEnterEvent( QDragEnterEvent *event )
 
  226  if ( !( event->possibleActions() & Qt::CopyAction ) )
 
  229  const QList<int> indexes = existingMapLayerFromMimeData( event->mimeData() );
 
  230  if ( !indexes.isEmpty() )
 
  233    event->setDropAction( Qt::CopyAction );
 
  238void QgsProcessingMultipleSelectionPanelWidget::dropEvent( QDropEvent *event )
 
  240  if ( !( event->possibleActions() & Qt::CopyAction ) )
 
  243  const QList<int> indexes = existingMapLayerFromMimeData( event->mimeData() );
 
  244  if ( !indexes.isEmpty() )
 
  247    setFocus( Qt::MouseFocusReason );
 
  248    event->setDropAction( Qt::CopyAction );
 
  251    for ( 
const int i : indexes )
 
  253      mModel->item( i )->setCheckState( Qt::Checked );
 
  255    emit selectionChanged();
 
  259void QgsProcessingMultipleSelectionPanelWidget::addOption( 
const QVariant &value, 
const QString &title, 
bool selected, 
bool updateExistingTitle )
 
  262  for ( 
int i = 0; i < mModel->rowCount(); ++i )
 
  264    if ( mModel->item( i )->data( Qt::UserRole ) == value || ( mModel->item( i )->data( Qt::UserRole ).userType() == qMetaTypeId<QgsProcessingModelChildParameterSource>() && value.userType() == qMetaTypeId<QgsProcessingModelChildParameterSource>() && mModel->item( i )->data( Qt::UserRole ).value<QgsProcessingModelChildParameterSource>() == value.value<QgsProcessingModelChildParameterSource>() ) )
 
  266      if ( updateExistingTitle )
 
  267        mModel->item( i )->setText( title );
 
  272  auto item = std::make_unique<QStandardItem>( title );
 
  273  item->setData( value, Qt::UserRole );
 
  274  item->setCheckState( selected ? Qt::Checked : Qt::Unchecked );
 
  275  item->setCheckable( 
true );
 
  276  item->setDropEnabled( 
false );
 
  277  mModel->appendRow( item.release() );
 
  285QgsProcessingMultipleSelectionDialog::QgsProcessingMultipleSelectionDialog( 
const QVariantList &availableOptions, 
const QVariantList &selectedOptions, QWidget *parent, Qt::WindowFlags flags )
 
  286  : QDialog( parent, flags )
 
  288  setWindowTitle( tr( 
"Multiple Selection" ) );
 
  289  QVBoxLayout *vLayout = 
new QVBoxLayout();
 
  290  mWidget = 
new QgsProcessingMultipleSelectionPanelWidget( availableOptions, selectedOptions );
 
  291  vLayout->addWidget( mWidget );
 
  292  mWidget->buttonBox()->addButton( QDialogButtonBox::Cancel );
 
  293  connect( mWidget->buttonBox(), &QDialogButtonBox::accepted, 
this, &QDialog::accept );
 
  294  connect( mWidget->buttonBox(), &QDialogButtonBox::rejected, 
this, &QDialog::reject );
 
  295  setLayout( vLayout );
 
  298void QgsProcessingMultipleSelectionDialog::setValueFormatter( 
const std::function<QString( 
const QVariant & )> &formatter )
 
  300  mWidget->setValueFormatter( formatter );
 
  303QVariantList QgsProcessingMultipleSelectionDialog::selectedOptions()
 const 
  305  return mWidget->selectedOptions();
 
  313QgsProcessingMultipleInputPanelWidget::QgsProcessingMultipleInputPanelWidget( 
const QgsProcessingParameterMultipleLayers *parameter, 
const QVariantList &selectedOptions, 
const QList<QgsProcessingModelChildParameterSource> &modelSources, QgsProcessingModelAlgorithm *model, QWidget *parent )
 
  314  : QgsProcessingMultipleSelectionPanelWidget( QVariantList(), selectedOptions, parent )
 
  315  , mParameter( parameter )
 
  317  QPushButton *addFileButton = 
new QPushButton( tr( 
"Add File(s)…" ) );
 
  318  connect( addFileButton, &QPushButton::clicked, 
this, &QgsProcessingMultipleInputPanelWidget::addFiles );
 
  319  buttonBox()->addButton( addFileButton, QDialogButtonBox::ActionRole );
 
  321  QPushButton *addDirButton = 
new QPushButton( tr( 
"Add Directory…" ) );
 
  322  connect( addDirButton, &QPushButton::clicked, 
this, &QgsProcessingMultipleInputPanelWidget::addDirectory );
 
  323  buttonBox()->addButton( addDirButton, QDialogButtonBox::ActionRole );
 
  324  setAcceptDrops( 
true );
 
  325  for ( 
const QgsProcessingModelChildParameterSource &source : modelSources )
 
  327    addOption( QVariant::fromValue( source ), source.friendlyIdentifier( model ), 
false, 
true );
 
  331void QgsProcessingMultipleInputPanelWidget::setProject( 
QgsProject *project )
 
  334    populateFromProject( project );
 
  337void QgsProcessingMultipleInputPanelWidget::addFiles()
 
  340  QString path = settings.
value( QStringLiteral( 
"/Processing/LastInputPath" ), QDir::homePath() ).toString();
 
  344    filter = generator->createFileFilter();
 
  346    filter = QObject::tr( 
"All files (*.*)" );
 
  348  const QStringList filenames = QFileDialog::getOpenFileNames( 
this, tr( 
"Select File(s)" ), path, filter );
 
  349  if ( filenames.empty() )
 
  352  settings.
setValue( QStringLiteral( 
"/Processing/LastInputPath" ), QFileInfo( filenames.at( 0 ) ).path() );
 
  354  for ( 
const QString &file : filenames )
 
  356    addOption( file, file, 
true );
 
  359  emit selectionChanged();
 
  362void QgsProcessingMultipleInputPanelWidget::addDirectory()
 
  365  const QString path = settings.
value( QStringLiteral( 
"/Processing/LastInputPath" ), QDir::homePath() ).toString();
 
  367  const QString dir = QFileDialog::getExistingDirectory( 
this, tr( 
"Select Directory" ), path );
 
  371  settings.
setValue( QStringLiteral( 
"/Processing/LastInputPath" ), dir );
 
  373  QStringList nameFilters;
 
  377    for ( 
const QString &extension : extensions )
 
  379      nameFilters << QStringLiteral( 
"*.%1" ).arg( extension );
 
  380      nameFilters << QStringLiteral( 
"*.%1" ).arg( extension.toUpper() );
 
  381      nameFilters << QStringLiteral( 
"*.%1" ).arg( extension.toLower() );
 
  385  QDirIterator it( dir, nameFilters, QDir::Files | QDir::NoSymLinks | QDir::NoDotAndDotDot, QDirIterator::Subdirectories );
 
  386  while ( it.hasNext() )
 
  388    const QString fullPath = it.next();
 
  389    if ( fullPath.endsWith( QLatin1String( 
".dbf" ), Qt::CaseInsensitive ) )
 
  391      if ( QFileInfo::exists( QStringLiteral( 
"%1.shp" ).arg( fullPath.chopped( 4 ) ) ) || QFileInfo::exists( QStringLiteral( 
"%1.SHP" ).arg( fullPath.chopped( 4 ) ) ) )
 
  397    else if ( fullPath.endsWith( QLatin1String( 
".aux.xml" ), Qt::CaseInsensitive ) || fullPath.endsWith( QLatin1String( 
".shp.xml" ), Qt::CaseInsensitive ) )
 
  402    addOption( fullPath, fullPath, 
true );
 
  404  emit selectionChanged();
 
  407QList<int> QgsProcessingMultipleInputPanelWidget::existingMapLayerFromMimeData( 
const QMimeData *data, 
QgsMimeDataUtils::UriList &handledUrls )
 const 
  415    bool matched = 
false;
 
  418      for ( 
int i = 0; i < mModel->rowCount(); ++i )
 
  421        const QString userRole = mModel->item( i )->data( Qt::UserRole ).toString();
 
  422        if ( userRole == layer->id() || userRole == layer->source() )
 
  432      handledUrls.append( u );
 
  441  QStringList skipUrlData;
 
  442  skipUrlData.reserve( skipUrls.size() );
 
  445    skipUrlData.append( u.data() );
 
  453    if ( skipUrlData.contains( u.data() ) )
 
  466      bool acceptable = 
false;
 
  510  if ( !uriList.isEmpty() )
 
  514  QStringList rawPaths;
 
  515  if ( data->hasUrls() )
 
  517    const QList<QUrl> urls = data->urls();
 
  518    rawPaths.reserve( urls.count() );
 
  519    for ( 
const QUrl &url : urls )
 
  521      const QString local = url.toLocalFile();
 
  522      if ( !rawPaths.contains( local ) )
 
  523        rawPaths.append( local );
 
  526  if ( !data->text().isEmpty() && !rawPaths.contains( data->text() ) )
 
  527    rawPaths.append( data->text() );
 
  529  for ( 
const QString &path : std::as_const( rawPaths ) )
 
  531    QFileInfo file( path );
 
  542void QgsProcessingMultipleInputPanelWidget::dragEnterEvent( QDragEnterEvent *event )
 
  544  if ( !( event->possibleActions() & Qt::CopyAction ) )
 
  549  const QList<int> indexes = existingMapLayerFromMimeData( event->mimeData(), handledUris );
 
  550  if ( !indexes.isEmpty() )
 
  553    event->setDropAction( Qt::CopyAction );
 
  559  const QStringList uris = compatibleUrisFromMimeData( mParameter, event->mimeData(), handledUris );
 
  560  if ( !uris.isEmpty() )
 
  563    event->setDropAction( Qt::CopyAction );
 
  568void QgsProcessingMultipleInputPanelWidget::dropEvent( QDropEvent *event )
 
  570  if ( !( event->possibleActions() & Qt::CopyAction ) )
 
  574  const QList<int> indexes = existingMapLayerFromMimeData( event->mimeData(), handledUris );
 
  575  if ( !indexes.isEmpty() )
 
  578    setFocus( Qt::MouseFocusReason );
 
  579    event->setDropAction( Qt::CopyAction );
 
  582    for ( 
const int i : indexes )
 
  584      mModel->item( i )->setCheckState( Qt::Checked );
 
  586    emit selectionChanged();
 
  590  const QStringList uris = compatibleUrisFromMimeData( mParameter, event->mimeData(), handledUris );
 
  591  if ( !uris.isEmpty() )
 
  593    for ( 
const QString &uri : uris )
 
  595      addOption( uri, uri, 
true );
 
  597    emit selectionChanged();
 
  601void QgsProcessingMultipleInputPanelWidget::populateFromProject( 
QgsProject *project )
 
  604    for ( 
int i = 0; i < mModel->rowCount(); ++i )
 
  606      const QStandardItem *item = mModel->item( i );
 
  607      if ( item->data( Qt::UserRole ) == layerId )
 
  609        bool isChecked = ( item->checkState() == Qt::Checked );
 
  610        mModel->removeRow( i );
 
  613          emit selectionChanged();
 
  622    const QString authid = layer->crs().authid();
 
  624    if ( settings.value( QStringLiteral( 
"Processing/Configuration/SHOW_CRS_DEF" ), 
true ).toBool() && !authid.isEmpty() )
 
  625      title = QStringLiteral( 
"%1 [%2]" ).arg( layer->name(), authid );
 
  627      title = layer->name();
 
  630    QString 
id = layer->id();
 
  632      id = QStringLiteral( 
"main" );
 
  634    for ( 
int i = 0; i < mModel->rowCount(); ++i )
 
  637      if ( mModel->item( i )->data( Qt::UserRole ) == layer->id() )
 
  642      else if ( mModel->item( i )->data( Qt::UserRole ) == layer->source() )
 
  644        id = layer->source();
 
  649    addOption( 
id, title, 
false, 
true );
 
  652  switch ( mParameter->layerType() )
 
  788QgsProcessingMultipleInputDialog::QgsProcessingMultipleInputDialog( 
const QgsProcessingParameterMultipleLayers *parameter, 
const QVariantList &selectedOptions, 
const QList<QgsProcessingModelChildParameterSource> &modelSources, QgsProcessingModelAlgorithm *model, QWidget *parent, Qt::WindowFlags flags )
 
  789  : QDialog( parent, flags )
 
  791  setWindowTitle( tr( 
"Multiple Selection" ) );
 
  792  QVBoxLayout *vLayout = 
new QVBoxLayout();
 
  793  mWidget = 
new QgsProcessingMultipleInputPanelWidget( parameter, selectedOptions, modelSources, model );
 
  794  vLayout->addWidget( mWidget );
 
  795  mWidget->buttonBox()->addButton( QDialogButtonBox::Cancel );
 
  796  connect( mWidget->buttonBox(), &QDialogButtonBox::accepted, 
this, &QDialog::accept );
 
  797  connect( mWidget->buttonBox(), &QDialogButtonBox::rejected, 
this, &QDialog::reject );
 
  798  setLayout( vLayout );
 
  799  setAcceptDrops( 
true );
 
  802QVariantList QgsProcessingMultipleInputDialog::selectedOptions()
 const 
  804  return mWidget->selectedOptions();
 
  807void QgsProcessingMultipleInputDialog::setProject( 
QgsProject *project )
 
  809  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.
 
@ VectorTile
Vector tile layer. Added in QGIS 3.14.
 
@ Mesh
Mesh layer. Added in QGIS 3.2.
 
@ PointCloud
Point cloud layer. Added in QGIS 3.18.
 
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...
 
static QString typeToString(Qgis::LayerType type)
Converts a map layer type to a string value.
 
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.
 
Stores settings for use within QGIS.
 
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 dataset.
 
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...