From 3d7a460660bd5af7b3651c5fa16d1f24bbb4afd9 Mon Sep 17 00:00:00 2001 From: Salvatore Larosa Date: Sat, 17 Mar 2018 22:22:30 +0100 Subject: [PATCH 1/3] [FEATURE][needs-docs] Copy&Paste Group/Layers --- src/app/qgisapp.cpp | 62 +++++++++++++++++++-- src/app/qgisapp.h | 4 ++ src/app/qgsapplayertreeviewmenuprovider.cpp | 9 +++ src/core/qgsmaplayer.h | 5 ++ src/ui/qgisapp.ui | 23 +++++++- 5 files changed, 96 insertions(+), 7 deletions(-) diff --git a/src/app/qgisapp.cpp b/src/app/qgisapp.cpp index e54703b856b0..83e65055b1e1 100644 --- a/src/app/qgisapp.cpp +++ b/src/app/qgisapp.cpp @@ -1913,6 +1913,8 @@ void QgisApp::createActions() connect( mActionPasteAsNewMemoryVector, &QAction::triggered, this, [ = ] { pasteAsNewMemoryVector(); } ); connect( mActionCopyStyle, &QAction::triggered, this, [ = ] { copyStyle(); } ); connect( mActionPasteStyle, &QAction::triggered, this, [ = ] { pasteStyle(); } ); + connect( mActionCopyLayer, &QAction::triggered, this, [ = ] { copyLayer(); } ); + connect( mActionPasteLayer, &QAction::triggered, this, [ = ] { pasteLayer(); } ); connect( mActionAddFeature, &QAction::triggered, this, &QgisApp::addFeature ); connect( mActionCircularStringCurvePoint, &QAction::triggered, this, [ = ] { setMapTool( mMapTools.mCircularStringCurvePoint ); } ); connect( mActionCircularStringRadius, &QAction::triggered, this, [ = ] { setMapTool( mMapTools.mCircularStringRadius ); } ); @@ -8517,12 +8519,6 @@ void QgisApp::copyStyle( QgsMapLayer *sourceLayer ) } } -/** - \param destinationLayer The layer that the clipboard will be pasted to - (defaults to the active layer on the legend) - */ - - void QgisApp::pasteStyle( QgsMapLayer *destinationLayer ) { QgsMapLayer *selectionLayer = destinationLayer ? destinationLayer : activeLayer(); @@ -8563,6 +8559,56 @@ void QgisApp::pasteStyle( QgsMapLayer *destinationLayer ) } } +void QgisApp::copyLayer() +{ + QString errorMessage; + QgsReadWriteContext readWriteContext; + QDomDocument doc( QStringLiteral( "qgis-layer-definition" ) ); + + bool saved = QgsLayerDefinition::exportLayerDefinition( doc, mLayerTreeView->selectedNodes(), errorMessage, readWriteContext ); + + if ( !saved ) + { + messageBar()->pushMessage( tr( "Error copying layer" ), errorMessage, Qgis::Warning ); + } + + // Copies data in text form as well, so the XML can be pasted into a text editor + clipboard()->setData( QGSCLIPBOARD_MAPLAYER_MIME, doc.toByteArray(), doc.toString() ); + // Enables the paste menu element + mActionPasteLayer->setEnabled( true ); +} + +void QgisApp::pasteLayer() +{ + if ( clipboard()->hasFormat( QGSCLIPBOARD_MAPLAYER_MIME ) ) + { + QDomDocument doc; + QString errorMessage; + QgsReadWriteContext readWriteContext; + doc.setContent( clipboard()->data( QGSCLIPBOARD_MAPLAYER_MIME ) ); + + QgsLayerTreeNode *currentNode = mLayerTreeView->currentNode(); + QgsLayerTreeGroup *root = nullptr; + if ( QgsLayerTree::isGroup( currentNode ) ) + { + root = QgsLayerTree::toGroup( currentNode ); + } + else + { + root = QgsProject::instance()->layerTreeRoot(); + } + + bool loaded = QgsLayerDefinition::loadLayerDefinition( doc, QgsProject::instance(), root, + errorMessage, readWriteContext ); + + if ( !loaded ) + { + QgsDebugMsg( errorMessage ); + messageBar()->pushMessage( tr( "Error pasting layer" ), errorMessage, Qgis::Warning ); + } + } +} + void QgisApp::copyFeatures( QgsFeatureStore &featureStore ) { clipboard()->replaceWithCopyOf( featureStore ); @@ -11543,6 +11589,8 @@ void QgisApp::activateDeactivateLayerRelatedActions( QgsMapLayer *layer ) mActionPasteFeatures->setEnabled( false ); mActionCopyStyle->setEnabled( false ); mActionPasteStyle->setEnabled( false ); + mActionCopyLayer->setEnabled( false ); + mActionPasteLayer->setEnabled( false ); mUndoDock->widget()->setEnabled( false ); mActionUndo->setEnabled( false ); @@ -11589,6 +11637,8 @@ void QgisApp::activateDeactivateLayerRelatedActions( QgsMapLayer *layer ) mActionCopyStyle->setEnabled( true ); mActionPasteStyle->setEnabled( clipboard()->hasFormat( QGSCLIPBOARD_STYLE_MIME ) ); + mActionCopyLayer->setEnabled( true ); + mActionPasteLayer->setEnabled( clipboard()->hasFormat( QGSCLIPBOARD_MAPLAYER_MIME ) ); /***********Vector layers****************/ if ( layer->type() == QgsMapLayer::VectorLayer ) diff --git a/src/app/qgisapp.h b/src/app/qgisapp.h index 99d274d5ce71..0b940ed9bf59 100644 --- a/src/app/qgisapp.h +++ b/src/app/qgisapp.h @@ -821,6 +821,10 @@ class APP_EXPORT QgisApp : public QMainWindow, private Ui::MainWindow (defaults to the active layer on the legend) */ void pasteStyle( QgsMapLayer *destinationLayer = nullptr ); + //! copies group or layer on the clipboard + void copyLayer(); + //! pastes group or layer from the clipboard to layer tree + void pasteLayer(); //! copies features to internal clipboard void copyFeatures( QgsFeatureStore &featureStore ); diff --git a/src/app/qgsapplayertreeviewmenuprovider.cpp b/src/app/qgsapplayertreeviewmenuprovider.cpp index a57373240d00..1080b0d182ce 100644 --- a/src/app/qgsapplayertreeviewmenuprovider.cpp +++ b/src/app/qgsapplayertreeviewmenuprovider.cpp @@ -56,6 +56,8 @@ QMenu *QgsAppLayerTreeViewMenuProvider::createContextMenu() { // global menu menu->addAction( actions->actionAddGroup( menu ) ); + menu->addAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionEditPaste.svg" ) ), + tr( "Paste Layer/Group" ), QgisApp::instance(), SLOT( pasteLayer() ) ); menu->addAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionExpandTree.svg" ) ), tr( "&Expand All" ), mView, SLOT( expandAll() ) ); menu->addAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionCollapseTree.svg" ) ), tr( "&Collapse All" ), mView, SLOT( collapseAll() ) ); @@ -92,6 +94,12 @@ QMenu *QgsAppLayerTreeViewMenuProvider::createContextMenu() menu->addAction( tr( "Paste Style" ), QgisApp::instance(), SLOT( applyStyleToGroup() ) ); } + menu->addAction( tr( "Copy Group" ), QgisApp::instance(), SLOT( copyLayer() ) ); + if ( QgisApp::instance()->clipboard()->hasFormat( QGSCLIPBOARD_MAPLAYER_MIME ) ) + { + menu->addAction( tr( "Paste Layer/Group" ), QgisApp::instance(), SLOT( pasteLayer() ) ); + } + menu->addAction( tr( "Save As Layer Definition File…" ), QgisApp::instance(), SLOT( saveAsLayerDefinition() ) ); menu->addAction( actions->actionAddGroup( menu ) ); @@ -207,6 +215,7 @@ QMenu *QgsAppLayerTreeViewMenuProvider::createContextMenu() } menu->addSeparator(); + menu->addAction( tr( "Copy Layer" ), QgisApp::instance(), SLOT( copyLayer() ) ); if ( vlayer ) { diff --git a/src/core/qgsmaplayer.h b/src/core/qgsmaplayer.h index 1b17a0344fff..1a506697a020 100644 --- a/src/core/qgsmaplayer.h +++ b/src/core/qgsmaplayer.h @@ -48,6 +48,11 @@ class QDomDocument; class QKeyEvent; class QPainter; +/* + * Constants used to describe copy-paste MIME types + */ +#define QGSCLIPBOARD_MAPLAYER_MIME "application/qgis.layer" + /** * \ingroup core * Base class for all map layer types. diff --git a/src/ui/qgisapp.ui b/src/ui/qgisapp.ui index 72df4c2a7672..b9c1eee64ae3 100755 --- a/src/ui/qgisapp.ui +++ b/src/ui/qgisapp.ui @@ -17,7 +17,7 @@ 0 0 1030 - 28 + 22 @@ -173,6 +173,9 @@ + + + @@ -2983,6 +2986,24 @@ Acts on currently active editable layer Revert Project to Saved version + + + + :/images/themes/default/mActionEditCopy.svg:/images/themes/default/mActionEditCopy.svg + + + Copy Layer + + + + + + :/images/themes/default/mActionEditPaste.svg:/images/themes/default/mActionEditPaste.svg + + + Paste Layer + + From f6f7027c7fad97cae7f5822391bb209bbe8d97f7 Mon Sep 17 00:00:00 2001 From: Salvatore Larosa Date: Sun, 18 Mar 2018 21:49:59 +0100 Subject: [PATCH 2/3] fix failure to travis code_layout --- python/core/qgsmaplayer.sip.in | 1 + src/core/qgsmaplayer.h | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/python/core/qgsmaplayer.sip.in b/python/core/qgsmaplayer.sip.in index 3843c049960e..b87409f1eee0 100644 --- a/python/core/qgsmaplayer.sip.in +++ b/python/core/qgsmaplayer.sip.in @@ -12,6 +12,7 @@ + class QgsMapLayer : QObject { %Docstring diff --git a/src/core/qgsmaplayer.h b/src/core/qgsmaplayer.h index 1a506697a020..3563b92a01af 100644 --- a/src/core/qgsmaplayer.h +++ b/src/core/qgsmaplayer.h @@ -51,7 +51,7 @@ class QPainter; /* * Constants used to describe copy-paste MIME types */ -#define QGSCLIPBOARD_MAPLAYER_MIME "application/qgis.layer" +#define QGSCLIPBOARD_MAPLAYER_MIME "application/qgis.maplayer" /** * \ingroup core From 53244d17b44709e1cc197f68647303e95436231d Mon Sep 17 00:00:00 2001 From: Salvatore Larosa Date: Mon, 19 Mar 2018 14:51:15 +0100 Subject: [PATCH 3/3] use new signal/slot connetion and minor fixes: followup 3d7a460660bd5af7b3651c5fa16d1f24bbb4afd9 --- src/app/qgisapp.cpp | 5 ++--- src/app/qgsapplayertreeviewmenuprovider.cpp | 18 +++++++++++++----- src/ui/qgisapp.ui | 5 ++++- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/app/qgisapp.cpp b/src/app/qgisapp.cpp index 83e65055b1e1..d70a3a90c457 100644 --- a/src/app/qgisapp.cpp +++ b/src/app/qgisapp.cpp @@ -1913,8 +1913,8 @@ void QgisApp::createActions() connect( mActionPasteAsNewMemoryVector, &QAction::triggered, this, [ = ] { pasteAsNewMemoryVector(); } ); connect( mActionCopyStyle, &QAction::triggered, this, [ = ] { copyStyle(); } ); connect( mActionPasteStyle, &QAction::triggered, this, [ = ] { pasteStyle(); } ); - connect( mActionCopyLayer, &QAction::triggered, this, [ = ] { copyLayer(); } ); - connect( mActionPasteLayer, &QAction::triggered, this, [ = ] { pasteLayer(); } ); + connect( mActionCopyLayer, &QAction::triggered, this, &QgisApp::copyLayer ); + connect( mActionPasteLayer, &QAction::triggered, this, &QgisApp::pasteLayer ); connect( mActionAddFeature, &QAction::triggered, this, &QgisApp::addFeature ); connect( mActionCircularStringCurvePoint, &QAction::triggered, this, [ = ] { setMapTool( mMapTools.mCircularStringCurvePoint ); } ); connect( mActionCircularStringRadius, &QAction::triggered, this, [ = ] { setMapTool( mMapTools.mCircularStringRadius ); } ); @@ -8603,7 +8603,6 @@ void QgisApp::pasteLayer() if ( !loaded ) { - QgsDebugMsg( errorMessage ); messageBar()->pushMessage( tr( "Error pasting layer" ), errorMessage, Qgis::Warning ); } } diff --git a/src/app/qgsapplayertreeviewmenuprovider.cpp b/src/app/qgsapplayertreeviewmenuprovider.cpp index 1080b0d182ce..5d8691a1f51b 100644 --- a/src/app/qgsapplayertreeviewmenuprovider.cpp +++ b/src/app/qgsapplayertreeviewmenuprovider.cpp @@ -56,11 +56,15 @@ QMenu *QgsAppLayerTreeViewMenuProvider::createContextMenu() { // global menu menu->addAction( actions->actionAddGroup( menu ) ); - menu->addAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionEditPaste.svg" ) ), - tr( "Paste Layer/Group" ), QgisApp::instance(), SLOT( pasteLayer() ) ); - menu->addAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionExpandTree.svg" ) ), tr( "&Expand All" ), mView, SLOT( expandAll() ) ); menu->addAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionCollapseTree.svg" ) ), tr( "&Collapse All" ), mView, SLOT( collapseAll() ) ); + menu->addSeparator(); + if ( QgisApp::instance()->clipboard()->hasFormat( QGSCLIPBOARD_MAPLAYER_MIME ) ) + { + QAction *actionPasteLayerOrGroup = new QAction( QgsApplication::getThemeIcon( QStringLiteral( "/mActionEditPaste.svg" ) ), tr( "Paste Layer/Group" ), menu ); + connect( actionPasteLayerOrGroup, &QAction::triggered, QgisApp::instance(), &QgisApp::pasteLayer ); + menu->addAction( actionPasteLayerOrGroup ); + } // TODO: update drawing order } @@ -97,7 +101,9 @@ QMenu *QgsAppLayerTreeViewMenuProvider::createContextMenu() menu->addAction( tr( "Copy Group" ), QgisApp::instance(), SLOT( copyLayer() ) ); if ( QgisApp::instance()->clipboard()->hasFormat( QGSCLIPBOARD_MAPLAYER_MIME ) ) { - menu->addAction( tr( "Paste Layer/Group" ), QgisApp::instance(), SLOT( pasteLayer() ) ); + QAction *actionPasteLayerOrGroup = new QAction( tr( "Paste Layer/Group" ), menu ); + connect( actionPasteLayerOrGroup, &QAction::triggered, QgisApp::instance(), &QgisApp::pasteLayer ); + menu->addAction( actionPasteLayerOrGroup ); } menu->addAction( tr( "Save As Layer Definition File…" ), QgisApp::instance(), SLOT( saveAsLayerDefinition() ) ); @@ -215,7 +221,9 @@ QMenu *QgsAppLayerTreeViewMenuProvider::createContextMenu() } menu->addSeparator(); - menu->addAction( tr( "Copy Layer" ), QgisApp::instance(), SLOT( copyLayer() ) ); + QAction *actionCopyLayer = new QAction( tr( "Copy Layer" ), menu ); + connect( actionCopyLayer, &QAction::triggered, QgisApp::instance(), &QgisApp::copyLayer ); + menu->addAction( actionCopyLayer ); if ( vlayer ) { diff --git a/src/ui/qgisapp.ui b/src/ui/qgisapp.ui index b9c1eee64ae3..d7dc8c41eaf2 100755 --- a/src/ui/qgisapp.ui +++ b/src/ui/qgisapp.ui @@ -3001,7 +3001,10 @@ Acts on currently active editable layer :/images/themes/default/mActionEditPaste.svg:/images/themes/default/mActionEditPaste.svg - Paste Layer + Paste Layer/Group + + + Paste Layer/Group