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 );