Skip to content

Commit 2473973

Browse files
authored
Merge pull request #8115 from 3nids/fix_cannot_paste_geom_style
[#19980] Fix cannot paste style when geometry doesn't match
2 parents 226fb47 + 7858b4c commit 2473973

File tree

5 files changed

+86
-24
lines changed

5 files changed

+86
-24
lines changed

python/core/auto_generated/qgsxmlutils.sip.in

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

1010

1111

12+
13+
14+
1215
class QgsXmlUtils
1316
{
1417
%Docstring
@@ -67,6 +70,7 @@ Supported types are
6770
%Docstring
6871
Read a QVariant from a QDomElement.
6972
%End
73+
7074
};
7175

7276

src/app/qgisapp.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8884,6 +8884,7 @@ void QgisApp::copyStyle( QgsMapLayer *sourceLayer, QgsMapLayer::StyleCategories
88848884
}
88858885
// Copies data in text form as well, so the XML can be pasted into a text editor
88868886
clipboard()->setData( QGSCLIPBOARD_STYLE_MIME, doc.toByteArray(), doc.toString() );
8887+
88878888
// Enables the paste menu element
88888889
mActionPasteStyle->setEnabled( true );
88898890
}

src/app/qgsapplayertreeviewmenuprovider.cpp

Lines changed: 33 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@
3939
#include "qgssymbolselectordialog.h"
4040
#include "qgssinglesymbolrenderer.h"
4141
#include "qgsmaplayerstylecategoriesmodel.h"
42+
#include "qgsxmlutils.h"
43+
4244

4345

4446
QgsAppLayerTreeViewMenuProvider::QgsAppLayerTreeViewMenuProvider( QgsLayerTreeView *view, QgsMapCanvas *canvas )
@@ -349,24 +351,38 @@ QMenu *QgsAppLayerTreeViewMenuProvider::createContextMenu()
349351
{
350352
if ( layer->type() == QgsMapLayer::VectorLayer )
351353
{
352-
QMenu *pasteStyleMenu = menuStyleManager->addMenu( tr( "Paste Style" ) );
353-
pasteStyleMenu->setToolTipsVisible( true );
354-
355-
QgsMapLayerStyleCategoriesModel *model = new QgsMapLayerStyleCategoriesModel( pasteStyleMenu );
356-
model->setShowAllCategories( true );
357-
for ( int row = 0; row < model->rowCount(); ++row )
354+
QDomDocument doc( QStringLiteral( "qgis" ) );
355+
QString errorMsg;
356+
int errorLine, errorColumn;
357+
if ( doc.setContent( app->clipboard()->data( QGSCLIPBOARD_STYLE_MIME ), false, &errorMsg, &errorLine, &errorColumn ) )
358358
{
359-
QModelIndex index = model->index( row, 0 );
360-
QgsMapLayer::StyleCategory category = model->data( index, Qt::UserRole ).value<QgsMapLayer::StyleCategory>();
361-
QString name = model->data( index, Qt::DisplayRole ).toString();
362-
QString tooltip = model->data( index, Qt::ToolTipRole ).toString();
363-
QIcon icon = model->data( index, Qt::DecorationRole ).value<QIcon>();
364-
QAction *copyAction = new QAction( icon, name, pasteStyleMenu );
365-
copyAction->setToolTip( tooltip );
366-
connect( copyAction, &QAction::triggered, this, [ = ]() {app->pasteStyle( layer, category );} );
367-
pasteStyleMenu->addAction( copyAction );
368-
if ( category == QgsMapLayer::AllStyleCategories )
369-
pasteStyleMenu->addSeparator();
359+
QDomElement myRoot = doc.firstChildElement( QStringLiteral( "qgis" ) );
360+
if ( !myRoot.isNull() )
361+
{
362+
QMenu *pasteStyleMenu = menuStyleManager->addMenu( tr( "Paste Style" ) );
363+
pasteStyleMenu->setToolTipsVisible( true );
364+
365+
QgsMapLayer::StyleCategories sourceCategories = QgsXmlUtils::readFlagAttribute( myRoot, QStringLiteral( "styleCategories" ), QgsMapLayer::AllStyleCategories );
366+
367+
QgsMapLayerStyleCategoriesModel *model = new QgsMapLayerStyleCategoriesModel( pasteStyleMenu );
368+
model->setShowAllCategories( true );
369+
for ( int row = 0; row < model->rowCount(); ++row )
370+
{
371+
QModelIndex index = model->index( row, 0 );
372+
QgsMapLayer::StyleCategory category = model->data( index, Qt::UserRole ).value<QgsMapLayer::StyleCategory>();
373+
QString name = model->data( index, Qt::DisplayRole ).toString();
374+
QString tooltip = model->data( index, Qt::ToolTipRole ).toString();
375+
QIcon icon = model->data( index, Qt::DecorationRole ).value<QIcon>();
376+
QAction *pasteAction = new QAction( icon, name, pasteStyleMenu );
377+
pasteAction->setToolTip( tooltip );
378+
connect( pasteAction, &QAction::triggered, this, [ = ]() {app->pasteStyle( layer, category );} );
379+
pasteStyleMenu->addAction( pasteAction );
380+
if ( category == QgsMapLayer::AllStyleCategories )
381+
pasteStyleMenu->addSeparator();
382+
else
383+
pasteAction->setEnabled( sourceCategories.testFlag( category ) );
384+
}
385+
}
370386
}
371387
}
372388
else

src/core/qgsmaplayer.cpp

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -551,6 +551,11 @@ bool QgsMapLayer::writeLayerXml( QDomElement &layerElement, QDomDocument &docume
551551
void QgsMapLayer::writeCommonStyle( QDomElement &layerElement, QDomDocument &document,
552552
const QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories ) const
553553
{
554+
// save categories
555+
QMetaEnum metaEnum = QMetaEnum::fromType<QgsMapLayer::StyleCategories>();
556+
QString categoriesKeys( metaEnum.valueToKeys( static_cast<int>( categories ) ) );
557+
layerElement.setAttribute( QStringLiteral( "styleCategories" ), categoriesKeys );
558+
554559
if ( categories.testFlag( Rendering ) )
555560
{
556561
// use scale dependent visibility flag
@@ -1054,15 +1059,22 @@ bool QgsMapLayer::importNamedStyle( QDomDocument &myDocument, QString &myErrorMe
10541059
styleFile.updateRevision( thisVersion );
10551060
}
10561061

1062+
// Get source categories
1063+
QgsMapLayer::StyleCategories sourceCategories = QgsXmlUtils::readFlagAttribute( myRoot, QStringLiteral( "styleCategories" ), QgsMapLayer::AllStyleCategories );
1064+
10571065
//Test for matching geometry type on vector layers when applying, if geometry type is given in the style
1058-
if ( type() == QgsMapLayer::VectorLayer && !myRoot.firstChildElement( QStringLiteral( "layerGeometryType" ) ).isNull() )
1066+
if ( ( sourceCategories.testFlag( QgsMapLayer::Symbology ) || sourceCategories.testFlag( QgsMapLayer::Symbology3D ) ) &&
1067+
( categories.testFlag( QgsMapLayer::Symbology ) || categories.testFlag( QgsMapLayer::Symbology3D ) ) )
10591068
{
1060-
QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( this );
1061-
QgsWkbTypes::GeometryType importLayerGeometryType = static_cast<QgsWkbTypes::GeometryType>( myRoot.firstChildElement( QStringLiteral( "layerGeometryType" ) ).text().toInt() );
1062-
if ( vl->geometryType() != importLayerGeometryType )
1069+
if ( type() == QgsMapLayer::VectorLayer && !myRoot.firstChildElement( QStringLiteral( "layerGeometryType" ) ).isNull() )
10631070
{
1064-
myErrorMessage = tr( "Cannot apply style to layer with a different geometry type" );
1065-
return false;
1071+
QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( this );
1072+
QgsWkbTypes::GeometryType importLayerGeometryType = static_cast<QgsWkbTypes::GeometryType>( myRoot.firstChildElement( QStringLiteral( "layerGeometryType" ) ).text().toInt() );
1073+
if ( vl->geometryType() != importLayerGeometryType )
1074+
{
1075+
myErrorMessage = tr( "Cannot apply style with symbology to layer with a different geometry type" );
1076+
return false;
1077+
}
10661078
}
10671079
}
10681080

src/core/qgsxmlutils.h

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,17 @@
1616
#define QGSXMLUTILS_H
1717

1818
class QDomDocument;
19-
class QDomElement;
2019

2120
class QgsRectangle;
2221

22+
#include <QDomElement>
23+
2324
#include "qgis_core.h"
2425
#include "qgis.h"
2526
#include "qgsunittypes.h"
2627

28+
29+
2730
/**
2831
* \ingroup core
2932
* Assorted helper methods for reading and writing chunks of XML
@@ -75,6 +78,32 @@ class CORE_EXPORT QgsXmlUtils
7578
* Read a QVariant from a QDomElement.
7679
*/
7780
static QVariant readVariant( const QDomElement &element );
81+
82+
/**
83+
* Read a flag value from an attribute of the element.
84+
* \param element the element to read the attribute from
85+
* \param attributeName the attribute name
86+
* \param defaultValue the default value as a flag
87+
* \note The flag value is a text as returned by \see QMetaEnum::valueToKeys.
88+
* The flag must have been declared with Q_ENUM macro.
89+
* \since QGIS 3.4
90+
*/
91+
template<class T> static T readFlagAttribute( const QDomElement &element, const QString &attributeName, T defaultValue ) SIP_SKIP
92+
{
93+
T value = defaultValue;
94+
// Get source categories
95+
QMetaEnum metaEnum = QMetaEnum::fromType<T>();
96+
QString sourceCategoriesStr( element.attribute( attributeName, metaEnum.valueToKeys( static_cast<int>( defaultValue ) ) ) );
97+
if ( metaEnum.isValid() )
98+
{
99+
bool ok = false;
100+
const char *vs = sourceCategoriesStr.toUtf8().data();
101+
int newValue = metaEnum.keysToValue( vs, &ok );
102+
if ( ok )
103+
value = static_cast<T>( newValue );
104+
}
105+
return value;
106+
}
78107
};
79108

80109

0 commit comments

Comments
 (0)