Skip to content

Commit

Permalink
[FEATURE] Drag'n'drop layers from layer tree view to browser dock
Browse files Browse the repository at this point in the history
Makes it easy to e.g. store your temporary layers in PostGIS
  • Loading branch information
wonder-sk committed Aug 22, 2016
1 parent b101510 commit 26dd130
Show file tree
Hide file tree
Showing 7 changed files with 109 additions and 9 deletions.
6 changes: 6 additions & 0 deletions python/core/qgsmimedatautils.sip
Expand Up @@ -40,4 +40,10 @@ class QgsMimeDataUtils

static UriList decodeUriList( const QMimeData* data );

/**
* Returns encoded URI list from a list of layer tree nodes.
* @note added in QGIS 3.0
*/
static QByteArray layerTreeNodesToUriList( const QList<QgsLayerTreeNode*>& nodes );

};
4 changes: 3 additions & 1 deletion src/app/qgisapp.cpp
Expand Up @@ -1273,7 +1273,9 @@ void QgisApp::dragEnterEvent( QDragEnterEvent *event )
{
if ( event->mimeData()->hasUrls() || event->mimeData()->hasFormat( "application/x-vnd.qgis.qgis.uri" ) )
{
event->acceptProposedAction();
// the mime data are coming from layer tree, so ignore that, do not import those layers again
if ( !event->mimeData()->hasFormat( "application/qgis.layertreemodeldata" ) )
event->acceptProposedAction();
}
}

Expand Down
23 changes: 23 additions & 0 deletions src/app/qgsbrowserdockwidget.cpp
Expand Up @@ -767,6 +767,12 @@ QgsDockBrowserTreeView::QgsDockBrowserTreeView( QWidget* parent ) : QgsBrowserTr

void QgsDockBrowserTreeView::dragEnterEvent( QDragEnterEvent* e )
{
// if this mime data come from layer tree, the proposed action will be MoveAction
// but for browser we really need CopyAction
if ( e->mimeData()->hasFormat( "application/qgis.layertreemodeldata" ) &&
e->mimeData()->hasFormat( "application/x-vnd.qgis.qgis.uri" ) )
e->setDropAction( Qt::CopyAction );

// accept drag enter so that our widget will not get ignored
// and drag events will not get passed to QgisApp
e->accept();
Expand All @@ -782,6 +788,12 @@ void QgsDockBrowserTreeView::dragMoveEvent( QDragMoveEvent* e )
return;
}*/

// if this mime data come from layer tree, the proposed action will be MoveAction
// but for browser we really need CopyAction
if ( e->mimeData()->hasFormat( "application/qgis.layertreemodeldata" ) &&
e->mimeData()->hasFormat( "application/x-vnd.qgis.qgis.uri" ) )
e->setDropAction( Qt::CopyAction );

QTreeView::dragMoveEvent( e );

if ( !e->mimeData()->hasFormat( "application/x-vnd.qgis.qgis.uri" ) )
Expand All @@ -791,6 +803,17 @@ void QgsDockBrowserTreeView::dragMoveEvent( QDragMoveEvent* e )
}
}

void QgsDockBrowserTreeView::dropEvent( QDropEvent *e )
{
// if this mime data come from layer tree, the proposed action will be MoveAction
// but for browser we really need CopyAction
if ( e->mimeData()->hasFormat( "application/qgis.layertreemodeldata" ) &&
e->mimeData()->hasFormat( "application/x-vnd.qgis.qgis.uri" ) )
e->setDropAction( Qt::CopyAction );

QTreeView::dropEvent( e );
}


//
// QgsBrowserTreeFilterProxyModel
Expand Down
1 change: 1 addition & 0 deletions src/app/qgsbrowserdockwidget.h
Expand Up @@ -173,6 +173,7 @@ class QgsDockBrowserTreeView : public QgsBrowserTreeView

void dragEnterEvent( QDragEnterEvent* e ) override;
void dragMoveEvent( QDragMoveEvent* e ) override;
void dropEvent( QDropEvent* e ) override;
};

/**
Expand Down
3 changes: 3 additions & 0 deletions src/core/layertree/qgslayertreemodel.cpp
Expand Up @@ -1011,6 +1011,9 @@ QMimeData* QgsLayerTreeModel::mimeData( const QModelIndexList& indexes ) const
QString txt = doc.toString();

mimeData->setData( "application/qgis.layertreemodeldata", txt.toUtf8() );

mimeData->setData( "application/x-vnd.qgis.qgis.uri", QgsMimeDataUtils::layerTreeNodesToUriList( nodesFinal ) );

return mimeData;
}

Expand Down
73 changes: 65 additions & 8 deletions src/core/qgsmimedatautils.cpp
Expand Up @@ -17,7 +17,13 @@
#include "qgsmimedatautils.h"

#include "qgsdataitem.h"
#include "qgslayertree.h"
#include "qgslogger.h"
#include "qgspluginlayer.h"
#include "qgsrasterdataprovider.h"
#include "qgsrasterlayer.h"
#include "qgsvectordataprovider.h"
#include "qgsvectorlayer.h"

static const char* QGIS_URILIST_MIMETYPE = "application/x-vnd.qgis.qgis.uri";

Expand Down Expand Up @@ -70,15 +76,8 @@ bool QgsMimeDataUtils::isUriList( const QMimeData* data )
QMimeData* QgsMimeDataUtils::encodeUriList( const QgsMimeDataUtils::UriList& layers )
{
QMimeData *mimeData = new QMimeData();
QByteArray encodedData;

QDataStream stream( &encodedData, QIODevice::WriteOnly );
Q_FOREACH ( const Uri& u, layers )
{
stream << u.data();
}

mimeData->setData( QGIS_URILIST_MIMETYPE, encodedData );
mimeData->setData( QGIS_URILIST_MIMETYPE, uriListToByteArray( layers ) );
return mimeData;
}

Expand All @@ -98,6 +97,52 @@ QgsMimeDataUtils::UriList QgsMimeDataUtils::decodeUriList( const QMimeData* data
return list;
}


static void _addLayerTreeNodeToUriList( QgsLayerTreeNode* node, QgsMimeDataUtils::UriList& uris )
{
if ( QgsLayerTree::isGroup( node ) )
{
Q_FOREACH ( QgsLayerTreeNode* child, QgsLayerTree::toGroup( node )->children() )
_addLayerTreeNodeToUriList( child, uris );
}
else if ( QgsLayerTree::isLayer( node ) )
{
QgsLayerTreeLayer* nodeLayer = QgsLayerTree::toLayer( node );
if ( !nodeLayer->layer() )
return;

QgsMimeDataUtils::Uri uri;
if ( QgsVectorLayer* vlayer = qobject_cast<QgsVectorLayer*>( nodeLayer->layer() ) )
{
uri.layerType = "vector";
uri.name = vlayer->name();
uri.providerKey = vlayer->dataProvider()->name();
uri.uri = vlayer->dataProvider()->dataSourceUri();
}
else if ( QgsRasterLayer* rlayer = qobject_cast<QgsRasterLayer*>( nodeLayer->layer() ) )
{
uri.layerType = "raster";
uri.name = rlayer->name();
uri.providerKey = rlayer->dataProvider()->name();
uri.uri = rlayer->dataProvider()->dataSourceUri();
}
else
{
// plugin layers do not have a standard way of storing their URI...
return;
}
uris << uri;
}
}

QByteArray QgsMimeDataUtils::layerTreeNodesToUriList( const QList<QgsLayerTreeNode *>& nodes )
{
UriList uris;
Q_FOREACH ( QgsLayerTreeNode* node, nodes )
_addLayerTreeNodeToUriList( node, uris );
return uriListToByteArray( uris );
}

QString QgsMimeDataUtils::encode( const QStringList& items )
{
QString encoded;
Expand Down Expand Up @@ -141,3 +186,15 @@ QStringList QgsMimeDataUtils::decode( const QString& encoded )
return items;
}


QByteArray QgsMimeDataUtils::uriListToByteArray( const QgsMimeDataUtils::UriList& layers )
{
QByteArray encodedData;

QDataStream stream( &encodedData, QIODevice::WriteOnly );
Q_FOREACH ( const Uri& u, layers )
{
stream << u.data();
}
return encodedData;
}
8 changes: 8 additions & 0 deletions src/core/qgsmimedatautils.h
Expand Up @@ -19,6 +19,7 @@
#include <QStringList>

class QgsLayerItem;
class QgsLayerTreeNode;

/** \ingroup core
* \class QgsMimeDataUtils
Expand Down Expand Up @@ -62,9 +63,16 @@ class CORE_EXPORT QgsMimeDataUtils

static UriList decodeUriList( const QMimeData* data );

/**
* Returns encoded URI list from a list of layer tree nodes.
* @note added in QGIS 3.0
*/
static QByteArray layerTreeNodesToUriList( const QList<QgsLayerTreeNode*>& nodes );

private:
static QString encode( const QStringList& items );
static QStringList decode( const QString& encoded );
static QByteArray uriListToByteArray( const UriList& layers );

};

Expand Down

2 comments on commit 26dd130

@3nids
Copy link
Member

@3nids 3nids commented on 26dd130 Aug 22, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or shape to Postgis with a simple drag'n'drop....so cool, thanks!

@gioman
Copy link
Contributor

@gioman gioman commented on 26dd130 Aug 22, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@3nids that was already possible from within the browser, but doing it from the layer tree gave users much more flexibility. Very cool indeed!

Please sign in to comment.