29 #include <QHBoxLayout> 
   30 #include <QVBoxLayout> 
   31 #include <QToolButton> 
   33 #include <QDragEnterEvent> 
   36 #include <QFileDialog> 
   43   , mParameter( parameter->clone() )
 
   45   QHBoxLayout *layout = 
new QHBoxLayout();
 
   46   layout->setContentsMargins( 0, 0, 0, 0 );
 
   47   layout->setSpacing( 6 );
 
   50   layout->addWidget( mCombo );
 
   51   layout->setAlignment( mCombo, Qt::AlignTop );
 
   56     mIterateButton = 
new QToolButton();
 
   58     mIterateButton->setToolTip( tr( 
"Iterate over this layer, creating a separate output for every feature in the layer" ) );
 
   59     mIterateButton->setCheckable( 
true );
 
   60     mIterateButton->setAutoRaise( 
true );
 
   63     mIterateButton->setFixedSize( 2 * 
static_cast< int >( 1.25 * 
iconSize / 2.0 ), 2 * 
static_cast< int >( 
iconSize * 1.1 / 2.0 ) );
 
   66     layout->addWidget( mIterateButton );
 
   67     layout->setAlignment( mIterateButton, Qt::AlignTop );
 
   72     mSettingsButton = 
new QToolButton();
 
   74     mSettingsButton->setToolTip( tr( 
"Advanced options" ) );
 
   77     mSettingsButton->setFixedSize( 2 * 
static_cast< int >( 1.25 * 
iconSize / 2.0 ), 2 * 
static_cast< int >( 
iconSize * 1.1 / 2.0 ) );
 
   79     mSettingsButton->setAutoRaise( 
true );
 
   81     connect( mSettingsButton, &QToolButton::clicked, 
this, &QgsProcessingMapLayerComboBox::showSourceOptions );
 
   82     layout->addWidget( mSettingsButton );
 
   83     layout->setAlignment( mSettingsButton, Qt::AlignTop );
 
   86   mSelectButton = 
new QToolButton();
 
   87   mSelectButton->setText( QString( QChar( 0x2026 ) ) );
 
   88   mSelectButton->setToolTip( tr( 
"Select input" ) );
 
   89   layout->addWidget( mSelectButton );
 
   90   layout->setAlignment( mSelectButton, Qt::AlignTop );
 
   93     mFeatureSourceMenu = 
new QMenu( 
this );
 
   94     QAction *selectFromFileAction = 
new QAction( tr( 
"Select File…" ), mFeatureSourceMenu );
 
   95     connect( selectFromFileAction, &QAction::triggered, 
this, &QgsProcessingMapLayerComboBox::selectFromFile );
 
   96     mFeatureSourceMenu->addAction( selectFromFileAction );
 
   97     QAction *browseForLayerAction = 
new QAction( tr( 
"Browse for Layer…" ), mFeatureSourceMenu );
 
   98     connect( browseForLayerAction, &QAction::triggered, 
this, &QgsProcessingMapLayerComboBox::browseForLayer );
 
   99     mFeatureSourceMenu->addAction( browseForLayerAction );
 
  100     mSelectButton->setMenu( mFeatureSourceMenu );
 
  101     mSelectButton->setPopupMode( QToolButton::InstantPopup );
 
  105     connect( mSelectButton, &QToolButton::clicked, 
this, &QgsProcessingMapLayerComboBox::selectFromFile );
 
  108   QVBoxLayout *vl = 
new QVBoxLayout();
 
  109   vl->setContentsMargins( 0, 0, 0, 0 );
 
  111   vl->addLayout( layout );
 
  113   QgsMapLayerProxyModel::Filters filters = QgsMapLayerProxyModel::Filters();
 
  117     mUseSelectionCheckBox = 
new QCheckBox( tr( 
"Selected features only" ) );
 
  118     mUseSelectionCheckBox->setChecked( 
false );
 
  119     mUseSelectionCheckBox->setEnabled( 
false );
 
  120     vl->addWidget( mUseSelectionCheckBox );
 
  125     QList<int> dataTypes;
 
  156     QList<int> dataTypes;
 
  178   if ( settings.
value( QStringLiteral( 
"Processing/Configuration/SHOW_CRS_DEF" ), true ).toBool() )
 
  179     mCombo->setShowCrs( 
true );
 
  182     mCombo->setFilters( filters );
 
  183   mCombo->setExcludedProviders( QStringList() << QStringLiteral( 
"grass" ) ); 
 
  187     mCombo->setAllowEmptyLayer( 
true );
 
  188     mCombo->setLayer( 
nullptr );
 
  192   if ( mUseSelectionCheckBox )
 
  193     connect( mUseSelectionCheckBox, &QCheckBox::toggled, 
this, [ = ]
 
  195     if ( !mBlockChangedSignal )
 
  201   setAcceptDrops( 
true );
 
  203   onLayerChanged( mCombo->currentLayer() );
 
  206 QgsProcessingMapLayerComboBox::~QgsProcessingMapLayerComboBox() = 
default;
 
  208 void QgsProcessingMapLayerComboBox::setLayer( 
QgsMapLayer *layer )
 
  211     mCombo->setLayer( layer );
 
  214 QgsMapLayer *QgsProcessingMapLayerComboBox::currentLayer()
 
  216   return mCombo->currentLayer();
 
  219 QString QgsProcessingMapLayerComboBox::currentText()
 
  221   return mCombo->currentText();
 
  224 void QgsProcessingMapLayerComboBox::setValue( 
const QVariant &value, 
QgsProcessingContext &context )
 
  232   QVariant val = value;
 
  234   bool selectedOnly = 
false;
 
  235   bool iterate = 
false;
 
  241     iterate = fromVar.
flags & QgsProcessingFeatureSourceDefinition::Flag::FlagCreateIndividualOutputPerInputFeature;
 
  243     mIsOverridingDefaultGeometryCheck = fromVar.
flags & QgsProcessingFeatureSourceDefinition::Flag::FlagOverrideDefaultGeometryCheck;
 
  249     mIsOverridingDefaultGeometryCheck = 
false;
 
  265   QgsMapLayer *layer = qobject_cast< QgsMapLayer * >( val.value< QObject * >() );
 
  266   if ( !layer && val.type() == QVariant::String )
 
  273     mBlockChangedSignal++;
 
  276     found = 
static_cast< bool >( currentLayer() );
 
  277     bool changed = found && ( currentLayer() != prevLayer );
 
  278     if ( found && mUseSelectionCheckBox )
 
  280       const bool hasSelection = qobject_cast< QgsVectorLayer * >( layer ) && qobject_cast< QgsVectorLayer * >( layer )->selectedFeatureCount() > 0;
 
  281       changed = changed | ( ( hasSelection && selectedOnly ) != mUseSelectionCheckBox->isChecked() );
 
  284         mUseSelectionCheckBox->setEnabled( 
true );
 
  285         mUseSelectionCheckBox->setChecked( selectedOnly );
 
  289         mUseSelectionCheckBox->setChecked( 
false );
 
  290         mUseSelectionCheckBox->setEnabled( 
false );
 
  293       if ( mIterateButton )
 
  295         mIterateButton->setChecked( iterate );
 
  298     mBlockChangedSignal--;
 
  305     const QString 
string = val.toString();
 
  306     if ( mIterateButton )
 
  307       mIterateButton->setChecked( iterate );
 
  309     if ( !
string.isEmpty() )
 
  311       mBlockChangedSignal++;
 
  312       if ( mCombo->findText( 
string ) < 0 )
 
  314         QStringList additional = mCombo->additionalItems();
 
  315         additional.append( 
string );
 
  316         mCombo->setAdditionalItems( additional );
 
  318       mCombo->setCurrentIndex( mCombo->findText( 
string ) ); 
 
  319       if ( mUseSelectionCheckBox )
 
  321         mUseSelectionCheckBox->setChecked( 
false );
 
  322         mUseSelectionCheckBox->setEnabled( 
false );
 
  324       mBlockChangedSignal--;
 
  325       if ( !mBlockChangedSignal )
 
  330       mCombo->setLayer( 
nullptr );
 
  331       if ( mUseSelectionCheckBox )
 
  333         mUseSelectionCheckBox->setChecked( 
false );
 
  334         mUseSelectionCheckBox->setEnabled( 
false );
 
  340 QVariant QgsProcessingMapLayerComboBox::value()
 const 
  342   if ( isEditable() && mCombo->currentText() != mCombo->itemText( mCombo->currentIndex() ) )
 
  343     return mCombo->currentText();
 
  345   const bool iterate = mIterateButton && mIterateButton->isChecked();
 
  346   const bool selectedOnly = mUseSelectionCheckBox && mUseSelectionCheckBox->isChecked();
 
  349     if ( selectedOnly || iterate || mFeatureLimit != -1 || mIsOverridingDefaultGeometryCheck )
 
  351              ( iterate ? QgsProcessingFeatureSourceDefinition::Flag::FlagCreateIndividualOutputPerInputFeature : QgsProcessingFeatureSourceDefinition::Flags() )
 
  352              | ( mIsOverridingDefaultGeometryCheck ? QgsProcessingFeatureSourceDefinition::Flag::FlagOverrideDefaultGeometryCheck : QgsProcessingFeatureSourceDefinition::Flags() ),
 
  359     if ( !mCombo->currentText().isEmpty() )
 
  361       if ( selectedOnly || iterate || mFeatureLimit != -1 || mIsOverridingDefaultGeometryCheck )
 
  363                ( iterate ? QgsProcessingFeatureSourceDefinition::Flag::FlagCreateIndividualOutputPerInputFeature : QgsProcessingFeatureSourceDefinition::Flags() )
 
  364                | ( mIsOverridingDefaultGeometryCheck ? QgsProcessingFeatureSourceDefinition::Flag::FlagOverrideDefaultGeometryCheck : QgsProcessingFeatureSourceDefinition::Flags() ),
 
  367         return mCombo->currentText();
 
  376   mCombo->setProject( context.
project() );
 
  379 void QgsProcessingMapLayerComboBox::setEditable( 
bool editable )
 
  381   mCombo->setEditable( editable );
 
  384 bool QgsProcessingMapLayerComboBox::isEditable()
 const 
  386   return mCombo->isEditable();
 
  389 QgsMapLayer *QgsProcessingMapLayerComboBox::compatibleMapLayerFromMimeData( 
const QMimeData *data, 
bool &incompatibleLayerSelected )
 const 
  391   incompatibleLayerSelected = 
false;
 
  398       if ( mCombo->mProxyModel->acceptsLayer( layer ) )
 
  402         incompatibleLayerSelected = 
true;
 
  411 QString QgsProcessingMapLayerComboBox::compatibleUriFromMimeData( 
const QMimeData *data )
 const 
  418          && u.layerType == QLatin1String( 
"vector" ) )
 
  423       bool acceptable = 
false;
 
  454               && u.layerType == QLatin1String( 
"raster" ) && u.providerKey == QLatin1String( 
"gdal" ) )
 
  457               && u.layerType == QLatin1String( 
"mesh" ) && u.providerKey == QLatin1String( 
"mdal" ) )
 
  467       if ( u.layerType == QLatin1String( 
"vector" ) && u.providerKey == QLatin1String( 
"ogr" ) )
 
  493       else if ( u.layerType == QLatin1String( 
"raster" ) && u.providerKey == QLatin1String( 
"gdal" )
 
  496       else if ( u.layerType == QLatin1String( 
"mesh" ) && u.providerKey == QLatin1String( 
"mdal" )
 
  501   if ( !uriList.isEmpty() )
 
  505   QStringList rawPaths;
 
  506   if ( data->hasUrls() )
 
  508     const QList< QUrl > urls = data->urls();
 
  509     rawPaths.reserve( urls.count() );
 
  510     for ( 
const QUrl &url : urls )
 
  512       const QString local =  url.toLocalFile();
 
  513       if ( !rawPaths.contains( local ) )
 
  514         rawPaths.append( local );
 
  517   if ( !data->text().isEmpty() && !rawPaths.contains( data->text() ) )
 
  518     rawPaths.append( data->text() );
 
  520   for ( 
const QString &path : std::as_const( rawPaths ) )
 
  522     QFileInfo file( path );
 
  533 void QgsProcessingMapLayerComboBox::dragEnterEvent( QDragEnterEvent *event )
 
  535   if ( !( event->possibleActions() & Qt::CopyAction ) )
 
  538   bool incompatibleLayerSelected = 
false;
 
  539   QgsMapLayer *layer = compatibleMapLayerFromMimeData( event->mimeData(), incompatibleLayerSelected );
 
  540   const QString uri = compatibleUriFromMimeData( event->mimeData() );
 
  541   if ( layer || ( !incompatibleLayerSelected && !uri.isEmpty() ) )
 
  544     event->setDropAction( Qt::CopyAction );
 
  547     mCombo->mHighlight = 
true;
 
  552 void QgsProcessingMapLayerComboBox::dragLeaveEvent( QDragLeaveEvent *event )
 
  554   QWidget::dragLeaveEvent( event );
 
  559     mCombo->mHighlight = 
false;
 
  564 void QgsProcessingMapLayerComboBox::dropEvent( QDropEvent *event )
 
  566   if ( !( event->possibleActions() & Qt::CopyAction ) )
 
  569   bool incompatibleLayerSelected = 
false;
 
  570   QgsMapLayer *layer = compatibleMapLayerFromMimeData( event->mimeData(), incompatibleLayerSelected );
 
  571   const QString uri = compatibleUriFromMimeData( event->mimeData() );
 
  572   if ( layer || ( !incompatibleLayerSelected && !uri.isEmpty() ) )
 
  575     setFocus( Qt::MouseFocusReason );
 
  576     event->setDropAction( Qt::CopyAction );
 
  579     setValue( layer ? QVariant::fromValue( layer ) : QVariant::fromValue( uri ), context );
 
  582   mCombo->mHighlight = 
false;
 
  586 void QgsProcessingMapLayerComboBox::onLayerChanged( 
QgsMapLayer *layer )
 
  590     if ( 
QgsVectorLayer *vl = qobject_cast< QgsVectorLayer * >( layer ) )
 
  592       if ( 
QgsVectorLayer *prevLayer = qobject_cast< QgsVectorLayer * >( mPrevLayer ) )
 
  596       if ( vl->selectedFeatureCount() == 0 )
 
  597         mUseSelectionCheckBox->setChecked( 
false );
 
  598       mUseSelectionCheckBox->setEnabled( vl->selectedFeatureCount() > 0 );
 
  604   if ( !mBlockChangedSignal )
 
  610   if ( selected.isEmpty() )
 
  611     mUseSelectionCheckBox->setChecked( 
false );
 
  612   mUseSelectionCheckBox->setEnabled( !selected.isEmpty() );
 
  615 void QgsProcessingMapLayerComboBox::showSourceOptions()
 
  619     QgsProcessingFeatureSourceOptionsWidget *widget = 
new QgsProcessingFeatureSourceOptionsWidget();
 
  620     widget->setPanelTitle( tr( 
"%1 Options" ).arg( mParameter->description() ) );
 
  622     widget->setGeometryCheckMethod( mIsOverridingDefaultGeometryCheck, mGeometryCheck );
 
  623     widget->setFeatureLimit( mFeatureLimit );
 
  625     panel->openPanel( widget );
 
  629       bool changed = 
false;
 
  630       changed = changed | ( widget->featureLimit() != mFeatureLimit );
 
  631       changed = changed | ( widget->isOverridingInvalidGeometryCheck() != mIsOverridingDefaultGeometryCheck );
 
  632       changed = changed | ( widget->geometryCheckMethod() != mGeometryCheck );
 
  634       mFeatureLimit = widget->featureLimit();
 
  635       mIsOverridingDefaultGeometryCheck = widget->isOverridingInvalidGeometryCheck();
 
  636       mGeometryCheck = widget->geometryCheckMethod();
 
  644 void QgsProcessingMapLayerComboBox::selectFromFile()
 
  647   const QString initialValue = currentText();
 
  650   if ( QFileInfo( initialValue ).isDir() && QFileInfo::exists( initialValue ) )
 
  652   else if ( QFileInfo::exists( QFileInfo( initialValue ).path() ) && QFileInfo( initialValue ).path() != 
'.' )
 
  653     path = QFileInfo( initialValue ).path();
 
  654   else if ( settings.
contains( QStringLiteral( 
"/Processing/LastInputPath" ) ) )
 
  655     path = settings.
value( QStringLiteral( 
"/Processing/LastInputPath" ) ).toString();
 
  659     filter = generator->createFileFilter();
 
  661     filter = QObject::tr( 
"All files (*.*)" );
 
  663   const QString filename = QFileDialog::getOpenFileName( 
this, tr( 
"Select File" ), path, filter );
 
  664   if ( filename.isEmpty() )
 
  667   settings.
setValue( QStringLiteral( 
"/Processing/LastInputPath" ), QFileInfo( filename ).path() );
 
  669   setValue( filename, context );
 
  672 void QgsProcessingMapLayerComboBox::browseForLayer()
 
  677     widget->
setPanelTitle( tr( 
"Browse for \"%1\"" ).arg( mParameter->description() ) );
 
  679     panel->openPanel( widget );
 
  688       if ( widget->
uri().
uri.isEmpty() )
 
  689         setValue( QVariant(), context );
 
  691         setValue( widget->
uri().
uri, context );