From b106e723e28e24867b863ae5b936df0b340efdd0 Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Thu, 26 Mar 2020 11:56:16 +1000 Subject: [PATCH] [processing] Accept drag and dropped paths for output parameter values from either explorer or browser (if you want to write to an existing path, it's a great shortcut!) --- ...gsprocessingoutputdestinationwidget.sip.in | 8 ++ .../qgsprocessingoutputdestinationwidget.cpp | 113 ++++++++++++++++++ .../qgsprocessingoutputdestinationwidget.h | 7 ++ .../qgsprocessingdestinationwidgetbase.ui | 9 +- 4 files changed, 136 insertions(+), 1 deletion(-) diff --git a/python/gui/auto_generated/processing/qgsprocessingoutputdestinationwidget.sip.in b/python/gui/auto_generated/processing/qgsprocessingoutputdestinationwidget.sip.in index 12b5eab00d1..12631986bd9 100644 --- a/python/gui/auto_generated/processing/qgsprocessingoutputdestinationwidget.sip.in +++ b/python/gui/auto_generated/processing/qgsprocessingoutputdestinationwidget.sip.in @@ -65,6 +65,14 @@ Emitted whenever the "skip output" option is toggled in the widget. %Docstring Emitted whenever the destination value is changed in the widget. %End + protected: + + virtual void dragEnterEvent( QDragEnterEvent *event ); + + virtual void dragLeaveEvent( QDragLeaveEvent *event ); + + virtual void dropEvent( QDropEvent *event ); + }; diff --git a/src/gui/processing/qgsprocessingoutputdestinationwidget.cpp b/src/gui/processing/qgsprocessingoutputdestinationwidget.cpp index 505532cd032..f4b5a48c5a0 100644 --- a/src/gui/processing/qgsprocessingoutputdestinationwidget.cpp +++ b/src/gui/processing/qgsprocessingoutputdestinationwidget.cpp @@ -38,6 +38,8 @@ QgsProcessingLayerOutputDestinationWidget::QgsProcessingLayerOutputDestinationWi setupUi( this ); + leText->setClearButtonEnabled( false ); + connect( leText, &QLineEdit::textEdited, this, &QgsProcessingLayerOutputDestinationWidget::textChanged ); mMenu = new QMenu( this ); @@ -62,6 +64,9 @@ QgsProcessingLayerOutputDestinationWidget::QgsProcessingLayerOutputDestinationWi } setToolTip( mParameter->toolTip() ); + + setAcceptDrops( true ); + leText->setAcceptDrops( false ); } bool QgsProcessingLayerOutputDestinationWidget::outputIsSkipped() const @@ -447,5 +452,113 @@ void QgsProcessingLayerOutputDestinationWidget::textChanged( const QString &text emit destinationChanged(); } +QString QgsProcessingLayerOutputDestinationWidget::mimeDataToPath( const QMimeData *data ) +{ + const QgsMimeDataUtils::UriList uriList = QgsMimeDataUtils::decodeUriList( data ); + for ( const QgsMimeDataUtils::Uri &u : uriList ) + { + if ( ( mParameter->type() == QgsProcessingParameterFeatureSink::typeName() + || mParameter->type() == QgsProcessingParameterVectorDestination::typeName() + || mParameter->type() == QgsProcessingParameterFileDestination::typeName() ) + && u.layerType == QLatin1String( "vector" ) && u.providerKey == QLatin1String( "ogr" ) ) + { + return u.uri; + } + else if ( ( mParameter->type() == QgsProcessingParameterRasterDestination::typeName() + || mParameter->type() == QgsProcessingParameterFileDestination::typeName() ) + && u.layerType == QLatin1String( "raster" ) && u.providerKey == QLatin1String( "gdal" ) ) + return u.uri; +#if 0 + else if ( ( mParameter->type() == QgsProcessingParameterMeshDestination::typeName() + || mParameter->type() == QgsProcessingParameterFileDestination::typeName() ) + && u.layerType == QLatin1String( "mesh" ) && u.providerKey == QLatin1String( "mdal" ) ) + return u.uri; + +#endif + else if ( mParameter->type() == QgsProcessingParameterFolderDestination::typeName() + && u.layerType == QLatin1String( "directory" ) ) + { + return u.uri; + } + } + if ( !uriList.isEmpty() ) + return QString(); + + // files dragged from file explorer, outside of QGIS + QStringList rawPaths; + if ( data->hasUrls() ) + { + const QList< QUrl > urls = data->urls(); + rawPaths.reserve( urls.count() ); + for ( const QUrl &url : urls ) + { + const QString local = url.toLocalFile(); + if ( !rawPaths.contains( local ) ) + rawPaths.append( local ); + } + } + if ( !data->text().isEmpty() && !rawPaths.contains( data->text() ) ) + rawPaths.append( data->text() ); + + for ( const QString &path : qgis::as_const( rawPaths ) ) + { + QFileInfo file( path ); + if ( file.isFile() && ( mParameter->type() == QgsProcessingParameterFeatureSink::typeName() + || mParameter->type() == QgsProcessingParameterVectorDestination::typeName() + || mParameter->type() == QgsProcessingParameterRasterDestination::typeName() + || mParameter->type() == QgsProcessingParameterVectorDestination::typeName() + || mParameter->type() == QgsProcessingParameterFileDestination::typeName() ) ) + { + // TODO - we should check to see if it's a valid extension for the parameter, but that's non-trivial + return path; + } + else if ( file.isDir() && ( mParameter->type() == QgsProcessingParameterFolderDestination::typeName() ) ) + return path; + } + + return QString(); +} + +void QgsProcessingLayerOutputDestinationWidget::dragEnterEvent( QDragEnterEvent *event ) +{ + if ( !( event->possibleActions() & Qt::CopyAction ) ) + return; + + const QString path = mimeDataToPath( event->mimeData() ); + if ( !path.isEmpty() ) + { + // dragged an acceptable path, phew + event->setDropAction( Qt::CopyAction ); + event->accept(); + leText->setHighlighted( true ); + } +} + +void QgsProcessingLayerOutputDestinationWidget::dragLeaveEvent( QDragLeaveEvent *event ) +{ + QWidget::dragLeaveEvent( event ); + if ( leText->isHighlighted() ) + { + event->accept(); + leText->setHighlighted( false ); + } +} + +void QgsProcessingLayerOutputDestinationWidget::dropEvent( QDropEvent *event ) +{ + if ( !( event->possibleActions() & Qt::CopyAction ) ) + return; + + const QString path = mimeDataToPath( event->mimeData() ); + if ( !path.isEmpty() ) + { + // dropped an acceptable path, phew + setFocus( Qt::MouseFocusReason ); + event->setDropAction( Qt::CopyAction ); + event->accept(); + setValue( path ); + } + leText->setHighlighted( false ); +} ///@endcond diff --git a/src/gui/processing/qgsprocessingoutputdestinationwidget.h b/src/gui/processing/qgsprocessingoutputdestinationwidget.h index 3b9bbfba16a..6841d477f93 100644 --- a/src/gui/processing/qgsprocessingoutputdestinationwidget.h +++ b/src/gui/processing/qgsprocessingoutputdestinationwidget.h @@ -77,6 +77,11 @@ class GUI_EXPORT QgsProcessingLayerOutputDestinationWidget : public QWidget, pri * Emitted whenever the destination value is changed in the widget. */ void destinationChanged(); + protected: + + void dragEnterEvent( QDragEnterEvent *event ) override; + void dragLeaveEvent( QDragLeaveEvent *event ) override; + void dropEvent( QDropEvent *event ) override; private slots: @@ -92,6 +97,8 @@ class GUI_EXPORT QgsProcessingLayerOutputDestinationWidget : public QWidget, pri private: + QString mimeDataToPath( const QMimeData *data ); + const QgsProcessingDestinationParameter *mParameter = nullptr; QMenu *mMenu = nullptr; diff --git a/src/ui/processing/qgsprocessingdestinationwidgetbase.ui b/src/ui/processing/qgsprocessingdestinationwidgetbase.ui index 8fa217a24bb..a8bc3a35739 100644 --- a/src/ui/processing/qgsprocessingdestinationwidgetbase.ui +++ b/src/ui/processing/qgsprocessingdestinationwidgetbase.ui @@ -30,7 +30,7 @@ 0 - + 0 @@ -54,6 +54,13 @@ + + + QgsHighlightableLineEdit + QLineEdit +
qgshighlightablelineedit.h
+
+