Skip to content

Commit 37efb4d

Browse files
authoredMay 6, 2020
Merge pull request #35382 from 3nids/dnd-across-qgis-instances
[FEATURE] allow to drag'n'drop layer across several QGIS instances
2 parents b1873a4 + c6ee633 commit 37efb4d

File tree

7 files changed

+121
-31
lines changed

7 files changed

+121
-31
lines changed
 

‎python/core/auto_generated/layertree/qgslayertreemodel.sip.in

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,15 @@ displays.
319319
``standardSize`` should be set to a standard icon size, e.g. 16, 24, 48, etc.
320320

321321
.. versionadded:: 3.6
322+
%End
323+
324+
signals:
325+
326+
void messageEmitted( const QString &message, Qgis::MessageLevel level = Qgis::Info, int duration = 5 );
327+
%Docstring
328+
Emits a message than can be displayed to the user in a GUI class
329+
330+
.. versionadded:: 3.14
322331
%End
323332

324333
protected slots:

‎python/gui/auto_generated/layertree/qgslayertreeview.sip.in

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010

1111

12+
1213
class QgsLayerTreeView : QTreeView
1314
{
1415
%Docstring
@@ -211,6 +212,13 @@ Set width of contextual menu mark, at right of layer node items.
211212
.. seealso:: :py:func:`layerMarkWidth`
212213

213214
.. versionadded:: 3.8
215+
%End
216+
217+
void setMessageBar( QgsMessageBar *messageBar );
218+
%Docstring
219+
Set the message bar to display messages from the layer tree
220+
221+
.. versionadded:: 3.14
214222
%End
215223

216224
signals:

‎src/app/qgisapp.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1687,6 +1687,7 @@ QgisApp::QgisApp()
16871687
mUndoWidget = new QgsUndoWidget( nullptr, mMapCanvas );
16881688
mUserInputDockWidget = new QgsUserInputWidget( this );
16891689
mInfoBar = new QgsMessageBar( centralWidget() );
1690+
mLayerTreeView->setMessageBar( mInfoBar );
16901691
mAdvancedDigitizingDockWidget = new QgsAdvancedDigitizingDockWidget( mMapCanvas, this );
16911692
mPanelMenu = new QMenu( this );
16921693
mProgressBar = new QProgressBar( this );
@@ -4590,6 +4591,8 @@ void QgisApp::initLayerTreeView()
45904591
model->setAutoCollapseLegendNodes( 10 );
45914592

45924593
mLayerTreeView->setModel( model );
4594+
mLayerTreeView->setMessageBar( mInfoBar );
4595+
45934596
mLayerTreeView->setMenuProvider( new QgsAppLayerTreeViewMenuProvider( mLayerTreeView, mMapCanvas ) );
45944597
new QgsLayerTreeViewFilterIndicatorProvider( mLayerTreeView ); // gets parented to the layer view
45954598
new QgsLayerTreeViewEmbeddedIndicatorProvider( mLayerTreeView ); // gets parented to the layer view

‎src/core/layertree/qgslayertreemodel.cpp

Lines changed: 60 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,16 @@
1313
* *
1414
***************************************************************************/
1515

16+
#include <QMimeData>
17+
#include <QTextStream>
18+
1619
#include "qgslayertreemodel.h"
1720

1821
#include "qgslayertree.h"
1922
#include "qgslayertreeutils.h"
2023
#include "qgslayertreemodellegendnode.h"
2124
#include "qgsproject.h"
2225
#include "qgsapplication.h"
23-
24-
#include <QMimeData>
25-
#include <QTextStream>
26-
2726
#include "qgsdataitem.h"
2827
#include "qgsmaphittest.h"
2928
#include "qgsmaplayer.h"
@@ -34,6 +33,8 @@
3433
#include "qgsrenderer.h"
3534
#include "qgssymbollayerutils.h"
3635
#include "qgsvectorlayer.h"
36+
#include "qgslayerdefinition.h"
37+
3738

3839
QgsLayerTreeModel::QgsLayerTreeModel( QgsLayerTree *rootNode, QObject *parent )
3940
: QAbstractItemModel( parent )
@@ -1052,15 +1053,25 @@ QMimeData *QgsLayerTreeModel::mimeData( const QModelIndexList &indexes ) const
10521053

10531054
QMimeData *mimeData = new QMimeData();
10541055

1055-
QDomDocument doc;
1056-
QDomElement rootElem = doc.createElement( QStringLiteral( "layer_tree_model_data" ) );
1056+
QDomDocument layerTreeDoc;
1057+
QDomElement rootLayerTreeElem = layerTreeDoc.createElement( QStringLiteral( "layer_tree_model_data" ) );
1058+
10571059
for ( QgsLayerTreeNode *node : qgis::as_const( nodesFinal ) )
1058-
node->writeXml( rootElem, QgsReadWriteContext() );
1059-
doc.appendChild( rootElem );
1060-
QString txt = doc.toString();
1060+
{
1061+
node->writeXml( rootLayerTreeElem, QgsReadWriteContext() );
1062+
}
1063+
layerTreeDoc.appendChild( rootLayerTreeElem );
10611064

1062-
mimeData->setData( QStringLiteral( "application/qgis.layertreemodeldata" ), txt.toUtf8() );
1065+
QString errorMessage;
1066+
QgsReadWriteContext readWriteContext;
1067+
QDomDocument layerDefinitionsDoc( QStringLiteral( "qgis-layer-definition" ) );
1068+
QgsLayerDefinition::exportLayerDefinition( layerDefinitionsDoc, nodesFinal, errorMessage, QgsReadWriteContext() );
10631069

1070+
QString txt = layerDefinitionsDoc.toString();
1071+
1072+
mimeData->setData( QStringLiteral( "application/qgis.layertreemodeldata" ), layerTreeDoc.toString().toUtf8() );
1073+
mimeData->setData( QStringLiteral( "application/qgis.application.pid" ), QString::number( QCoreApplication::applicationPid() ).toUtf8() );
1074+
mimeData->setData( QStringLiteral( "application/qgis.layertree.layerdefinitions" ), txt.toUtf8() );
10641075
mimeData->setData( QStringLiteral( "application/x-vnd.qgis.qgis.uri" ), QgsMimeDataUtils::layerTreeNodesToUriList( nodesFinal ) );
10651076

10661077
return mimeData;
@@ -1081,36 +1092,54 @@ bool QgsLayerTreeModel::dropMimeData( const QMimeData *data, Qt::DropAction acti
10811092
if ( !QgsLayerTree::isGroup( nodeParent ) )
10821093
return false;
10831094

1084-
QByteArray encodedData = data->data( QStringLiteral( "application/qgis.layertreemodeldata" ) );
1095+
if ( parent.isValid() && row == -1 )
1096+
row = 0; // if dropped directly onto group item, insert at first position
10851097

1086-
QDomDocument doc;
1087-
if ( !doc.setContent( QString::fromUtf8( encodedData ) ) )
1088-
return false;
1098+
// if we are coming from another QGIS instance, we need to add the layers too
1099+
bool ok = false;
1100+
// the application pid is only provided from QGIS 3.14, so do not check to OK before defaulting to moving in the legend
1101+
int qgisPid = data->data( QStringLiteral( "application/qgis.application.pid" ) ).toInt( &ok );
10891102

1090-
QDomElement rootElem = doc.documentElement();
1091-
if ( rootElem.tagName() != QLatin1String( "layer_tree_model_data" ) )
1092-
return false;
1103+
if ( ok && qgisPid != QString::number( QCoreApplication::applicationPid() ) )
1104+
{
1105+
QByteArray encodedLayerDefinitionData = data->data( QStringLiteral( "application/qgis.layertree.layerdefinitions" ) );
1106+
QDomDocument layerDefinitionDoc;
1107+
if ( !layerDefinitionDoc.setContent( QString::fromUtf8( encodedLayerDefinitionData ) ) )
1108+
return false;
1109+
QgsReadWriteContext context;
1110+
QString errorMessage;
1111+
QgsLayerDefinition::loadLayerDefinition( layerDefinitionDoc, QgsProject::instance(), QgsLayerTree::toGroup( nodeParent ), errorMessage, context );
1112+
emit messageEmitted( tr( "New layers added from another QGIS instance" ) );
1113+
}
1114+
else
1115+
{
1116+
QByteArray encodedLayerTreeData = data->data( QStringLiteral( "application/qgis.layertreemodeldata" ) );
10931117

1094-
QList<QgsLayerTreeNode *> nodes;
1118+
QDomDocument layerTreeDoc;
1119+
if ( !layerTreeDoc.setContent( QString::fromUtf8( encodedLayerTreeData ) ) )
1120+
return false;
10951121

1096-
QDomElement elem = rootElem.firstChildElement();
1097-
while ( !elem.isNull() )
1098-
{
1099-
QgsLayerTreeNode *node = QgsLayerTreeNode::readXml( elem, QgsProject::instance() );
1100-
if ( node )
1101-
nodes << node;
1122+
QDomElement rootLayerTreeElem = layerTreeDoc.documentElement();
1123+
if ( rootLayerTreeElem.tagName() != QLatin1String( "layer_tree_model_data" ) )
1124+
return false;
11021125

1103-
elem = elem.nextSiblingElement();
1104-
}
1126+
QList<QgsLayerTreeNode *> nodes;
11051127

1106-
if ( nodes.isEmpty() )
1107-
return false;
1128+
QDomElement elem = rootLayerTreeElem.firstChildElement();
1129+
while ( !elem.isNull() )
1130+
{
1131+
QgsLayerTreeNode *node = QgsLayerTreeNode::readXml( elem, QgsProject::instance() );
1132+
if ( node )
1133+
nodes << node;
11081134

1109-
if ( parent.isValid() && row == -1 )
1110-
row = 0; // if dropped directly onto group item, insert at first position
1135+
elem = elem.nextSiblingElement();
1136+
}
11111137

1112-
QgsLayerTree::toGroup( nodeParent )->insertChildNodes( row, nodes );
1138+
if ( nodes.isEmpty() )
1139+
return false;
11131140

1141+
QgsLayerTree::toGroup( nodeParent )->insertChildNodes( row, nodes );
1142+
}
11141143
return true;
11151144
}
11161145

‎src/core/layertree/qgslayertreemodel.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,14 @@ class CORE_EXPORT QgsLayerTreeModel : public QAbstractItemModel
286286
*/
287287
static int scaleIconSize( int standardSize );
288288

289+
signals:
290+
291+
/**
292+
* Emits a message than can be displayed to the user in a GUI class
293+
* \since QGIS 3.14
294+
*/
295+
void messageEmitted( const QString &message, Qgis::MessageLevel level = Qgis::Info, int duration = 5 );
296+
289297
protected slots:
290298
void nodeWillAddChildren( QgsLayerTreeNode *node, int indexFrom, int indexTo );
291299
void nodeAddedChildren( QgsLayerTreeNode *node, int indexFrom, int indexTo );

‎src/gui/layertree/qgslayertreeview.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
#include "qgslayertreeutils.h"
2323
#include "qgslayertreeviewdefaultactions.h"
2424
#include "qgsmaplayer.h"
25+
#include "qgsmessagebar.h"
26+
2527
#include "qgsgui.h"
2628

2729
#include <QMenu>
@@ -77,6 +79,12 @@ void QgsLayerTreeView::setModel( QAbstractItemModel *model )
7779
connect( model, &QAbstractItemModel::rowsInserted, this, &QgsLayerTreeView::modelRowsInserted );
7880
connect( model, &QAbstractItemModel::rowsRemoved, this, &QgsLayerTreeView::modelRowsRemoved );
7981

82+
if ( mMessageBar )
83+
connect( layerTreeModel(), &QgsLayerTreeModel::messageEmitted,
84+
[ = ]( const QString & message, Qgis::MessageLevel level = Qgis::Info, int duration = 5 )
85+
{mMessageBar->pushMessage( message, level, duration );}
86+
);
87+
8088
QTreeView::setModel( model );
8189

8290
connect( layerTreeModel()->rootGroup(), &QgsLayerTreeNode::expandedChanged, this, &QgsLayerTreeView::onExpandedChanged );
@@ -490,6 +498,20 @@ void QgsLayerTreeView::collapseAllNodes()
490498
collapseAll();
491499
}
492500

501+
void QgsLayerTreeView::setMessageBar( QgsMessageBar *messageBar )
502+
{
503+
if ( mMessageBar == messageBar )
504+
return;
505+
506+
mMessageBar = messageBar;
507+
508+
if ( mMessageBar )
509+
connect( layerTreeModel(), &QgsLayerTreeModel::messageEmitted,
510+
[ = ]( const QString & message, Qgis::MessageLevel level = Qgis::Info, int duration = 5 )
511+
{mMessageBar->pushMessage( message, level, duration );}
512+
);
513+
}
514+
493515
void QgsLayerTreeView::mouseReleaseEvent( QMouseEvent *event )
494516
{
495517
// we need to keep last mouse position in order to know whether to emit an indicator's clicked() signal

‎src/gui/layertree/qgslayertreeview.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ class QgsLayerTreeViewDefaultActions;
2929
class QgsLayerTreeViewIndicator;
3030
class QgsLayerTreeViewMenuProvider;
3131
class QgsMapLayer;
32+
class QgsMessageBar;
33+
3234

3335
/**
3436
* \ingroup gui
@@ -206,6 +208,12 @@ class GUI_EXPORT QgsLayerTreeView : public QTreeView
206208
*/
207209
void setLayerMarkWidth( int width ) { mLayerMarkWidth = width; }
208210

211+
/**
212+
* Set the message bar to display messages from the layer tree
213+
* \since QGIS 3.14
214+
*/
215+
void setMessageBar( QgsMessageBar *messageBar );
216+
209217
signals:
210218
//! Emitted when a current layer is changed
211219
void currentLayerChanged( QgsMapLayer *layer );
@@ -253,6 +261,9 @@ class GUI_EXPORT QgsLayerTreeView : public QTreeView
253261
//! Width of contextual menu mark for layer nodes
254262
int mLayerMarkWidth;
255263

264+
private:
265+
QgsMessageBar *mMessageBar = nullptr;
266+
256267
// friend so it can access viewOptions() method and mLastReleaseMousePos without making them public
257268
friend class QgsLayerTreeViewItemDelegate;
258269
};

0 commit comments

Comments
 (0)
Please sign in to comment.