Skip to content

Commit

Permalink
Layers can now be added to elevation plots via drag and drop from
Browse files Browse the repository at this point in the history
layer tree.

Unfortunately due to qt api limitations, we can't just enable direct
drag and drop from the layer tree -- users have to explicitly hold
the "Ctrl" key while dragging in order to force the copy action.
  • Loading branch information
nyalldawson committed May 23, 2023
1 parent 29f0d47 commit a382930
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 19 deletions.
39 changes: 22 additions & 17 deletions src/app/elevation/qgselevationprofilewidget.cpp
Expand Up @@ -157,6 +157,7 @@ QgsElevationProfileWidget::QgsElevationProfileWidget( const QString &name )
mPanTool = new QgsPlotToolPan( mCanvas );

mLayerTreeView = new QgsAppElevationProfileLayerTreeView( mLayerTree.get() );
connect( mLayerTreeView, &QgsAppElevationProfileLayerTreeView::addLayers, this, &QgsElevationProfileWidget::addLayersInternal );

connect( mLayerTreeView, &QAbstractItemView::doubleClicked, this, [ = ]( const QModelIndex & index )
{
Expand Down Expand Up @@ -482,28 +483,32 @@ void QgsElevationProfileWidget::addLayers()

if ( addDialog.exec() == QDialog::Accepted )
{
const QList<QgsMapLayer *> layers = addDialog.selectedLayers();
QList< QgsMapLayer * > updatedLayers;
if ( !layers.empty() )
addLayersInternal( addDialog.selectedLayers() );
}
}

void QgsElevationProfileWidget::addLayersInternal( const QList<QgsMapLayer *> &layers )
{
QList< QgsMapLayer * > updatedLayers;
if ( !layers.empty() )
{
for ( QgsMapLayer *layer : layers )
{
for ( QgsMapLayer *layer : layers )
{
if ( QgsElevationUtils::enableElevationForLayer( layer ) )
updatedLayers << layer;
}
if ( QgsElevationUtils::enableElevationForLayer( layer ) )
updatedLayers << layer;
}

mLayerTreeView->proxyModel()->invalidate();
for ( QgsMapLayer *layer : std::as_const( updatedLayers ) )
mLayerTreeView->proxyModel()->invalidate();
for ( QgsMapLayer *layer : std::as_const( updatedLayers ) )
{
if ( QgsLayerTreeLayer *node = mLayerTree->findLayer( layer ) )
{
if ( QgsLayerTreeLayer *node = mLayerTree->findLayer( layer ) )
{
node->setItemVisibilityChecked( true );
}
node->setItemVisibilityChecked( true );
}

updateCanvasLayers();
scheduleUpdate();
}

updateCanvasLayers();
scheduleUpdate();
}
}

Expand Down
1 change: 1 addition & 0 deletions src/app/elevation/qgselevationprofilewidget.h
Expand Up @@ -116,6 +116,7 @@ class QgsElevationProfileWidget : public QWidget

private slots:
void addLayers();
void addLayersInternal( const QList<QgsMapLayer *> &layers );
void updateCanvasLayers();
void onTotalPendingJobsCountChanged( int count );
void setProfileCurve( const QgsGeometry &curve, bool resetView );
Expand Down
64 changes: 62 additions & 2 deletions src/gui/elevation/qgselevationprofilelayertreeview.cpp
Expand Up @@ -255,6 +255,27 @@ bool QgsElevationProfileLayerTreeModel::setData( const QModelIndex &index, const
return QgsLayerTreeModel::setData( index, value, role );
}

bool QgsElevationProfileLayerTreeModel::canDropMimeData( const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent ) const
{
if ( action == Qt::IgnoreAction )
return true;

if ( !data->hasFormat( QStringLiteral( "application/qgis.layertreemodeldata" ) ) )
return false;

// don't accept moves from other layer trees -- only allow internal drag
if ( action == Qt::MoveAction )
{
const QString source = data->data( QStringLiteral( "application/qgis.layertree.source" ) );
if ( source.isEmpty() || source != QStringLiteral( ":0x%1" ).arg( reinterpret_cast<quintptr>( this ), 2 * QT_POINTER_SIZE, 16, QLatin1Char( '0' ) ) )
{
return false;
}
}

return QgsLayerTreeModel::canDropMimeData( data, action, row, column, parent );
}

bool QgsElevationProfileLayerTreeModel::dropMimeData( const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent )
{
if ( action == Qt::IgnoreAction )
Expand All @@ -263,10 +284,48 @@ bool QgsElevationProfileLayerTreeModel::dropMimeData( const QMimeData *data, Qt:
if ( !data->hasFormat( QStringLiteral( "application/qgis.layertreemodeldata" ) ) )
return false;

// don't accept drags from other layer trees -- only allow internal drag
// don't accept moves from other layer trees -- only allow internal drag
const QString source = data->data( QStringLiteral( "application/qgis.layertree.source" ) );
if ( source.isEmpty() || source != QStringLiteral( ":0x%1" ).arg( reinterpret_cast<quintptr>( this ), 2 * QT_POINTER_SIZE, 16, QLatin1Char( '0' ) ) )
return false;
{
if ( action == Qt::CopyAction )
{
QByteArray encodedLayerTreeData = data->data( QStringLiteral( "application/qgis.layertreemodeldata" ) );

QDomDocument layerTreeDoc;
if ( !layerTreeDoc.setContent( QString::fromUtf8( encodedLayerTreeData ) ) )
return false;

QDomElement rootLayerTreeElem = layerTreeDoc.documentElement();
if ( rootLayerTreeElem.tagName() != QLatin1String( "layer_tree_model_data" ) )
return false;

QList<QgsMapLayer *> layersToAdd;

QDomElement elem = rootLayerTreeElem.firstChildElement();
while ( !elem.isNull() )
{
std::unique_ptr< QgsLayerTreeNode > node( QgsLayerTreeNode::readXml( elem, QgsProject::instance() ) );
if ( node && QgsLayerTree::isLayer( node.get() ) )
{
if ( QgsMapLayer *layer = qobject_cast< QgsLayerTreeLayer * >( node.get() )->layer() )
{
layersToAdd << layer;
}
}
elem = elem.nextSiblingElement();
}

if ( !layersToAdd.empty() )
emit addLayers( layersToAdd );

return true;
}
else
{
return false;
}
}

return QgsLayerTreeModel::dropMimeData( data, action, row, column, parent );
}
Expand Down Expand Up @@ -356,6 +415,7 @@ QgsElevationProfileLayerTreeView::QgsElevationProfileLayerTreeView( QgsLayerTree
, mLayerTree( rootNode )
{
mModel = new QgsElevationProfileLayerTreeModel( rootNode, this );
connect( mModel, &QgsElevationProfileLayerTreeModel::addLayers, this, &QgsElevationProfileLayerTreeView::addLayers );
mProxyModel = new QgsElevationProfileLayerTreeProxyModel( mModel, this );

setHeaderHidden( true );
Expand Down
20 changes: 20 additions & 0 deletions src/gui/elevation/qgselevationprofilelayertreeview.h
Expand Up @@ -58,9 +58,20 @@ class GUI_EXPORT QgsElevationProfileLayerTreeModel : public QgsLayerTreeModel
QVariant data( const QModelIndex &index, int role = Qt::DisplayRole ) const override;
bool setData( const QModelIndex &index, const QVariant &value, int role = Qt::EditRole ) override;

bool canDropMimeData( const QMimeData *data, Qt::DropAction action,
int row, int column, const QModelIndex &parent ) const override;
bool dropMimeData( const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent ) override;
QMimeData *mimeData( const QModelIndexList &indexes ) const override;

signals:

/**
* Emitted when layers should be added to the profile, e.g. via a drag and drop action.
*
* \since QGIS 3.32
*/
void addLayers( const QList< QgsMapLayer * > &layers );

private:

#ifdef SIP_RUN
Expand Down Expand Up @@ -136,6 +147,15 @@ class GUI_EXPORT QgsElevationProfileLayerTreeView : public QTreeView
*/
QgsElevationProfileLayerTreeProxyModel *proxyModel();

signals:

/**
* Emitted when layers should be added to the profile, e.g. via a drag and drop action.
*
* \since QGIS 3.32
*/
void addLayers( const QList< QgsMapLayer * > &layers );

protected:

void resizeEvent( QResizeEvent *event ) override;
Expand Down

0 comments on commit a382930

Please sign in to comment.