Skip to content

Commit b3d0515

Browse files
authoredApr 19, 2017
Merge pull request #4370 from nyalldawson/backport_composer_template_fixes
Backport composer template fixes
2 parents 1cc2c41 + 4c7cff5 commit b3d0515

11 files changed

+691
-52
lines changed
 

‎src/core/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -672,6 +672,8 @@ SET(QGIS_CORE_HDRS
672672
qgslegendsettings.h
673673
qgslogger.h
674674
qgsmaphittest.h
675+
qgsmaplayerlistutils.h
676+
qgsmaplayerref.h
675677
qgsmaplayerrenderer.h
676678
qgsmaplayerstylemanager.h
677679
qgsmapsettings.h
@@ -723,6 +725,7 @@ SET(QGIS_CORE_HDRS
723725

724726
qgsvectordataprovider.h
725727
qgsvectorlayercache.h
728+
qgsvectorlayerref.h
726729
qgsvectorfilewriter.h
727730
qgsvectorlayerdiagramprovider.h
728731
qgsvectorlayereditutils.h

‎src/core/composer/qgsatlascomposition.cpp

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ QgsAtlasComposition::QgsAtlasComposition( QgsComposition* composition )
3535
, mEnabled( false )
3636
, mHideCoverage( false )
3737
, mFilenamePattern( "'output_'||@atlas_featurenumber" )
38-
, mCoverageLayer( nullptr )
3938
, mSingleFile( false )
4039
, mSortFeatures( false )
4140
, mSortAscending( true )
@@ -73,10 +72,10 @@ void QgsAtlasComposition::removeLayers( const QStringList& layers )
7372

7473
Q_FOREACH ( const QString& layerId, layers )
7574
{
76-
if ( layerId == mCoverageLayer->id() )
75+
if ( layerId == mCoverageLayer.layerId )
7776
{
7877
//current coverage layer removed
79-
mCoverageLayer = nullptr;
78+
mCoverageLayer.setLayer( nullptr );
8079
setEnabled( false );
8180
return;
8281
}
@@ -85,12 +84,12 @@ void QgsAtlasComposition::removeLayers( const QStringList& layers )
8584

8685
void QgsAtlasComposition::setCoverageLayer( QgsVectorLayer* layer )
8786
{
88-
if ( layer == mCoverageLayer )
87+
if ( layer == mCoverageLayer.get() )
8988
{
9089
return;
9190
}
9291

93-
mCoverageLayer = layer;
92+
mCoverageLayer.setLayer( layer );
9493
emit coverageLayerChanged( layer );
9594
}
9695

@@ -644,7 +643,10 @@ void QgsAtlasComposition::writeXML( QDomElement& elem, QDomDocument& doc ) const
644643

645644
if ( mCoverageLayer )
646645
{
647-
atlasElem.setAttribute( "coverageLayer", mCoverageLayer->id() );
646+
atlasElem.setAttribute( "coverageLayer", mCoverageLayer.layerId );
647+
atlasElem.setAttribute( "coverageLayerName", mCoverageLayer.name );
648+
atlasElem.setAttribute( "coverageLayerSource", mCoverageLayer.source );
649+
atlasElem.setAttribute( "coverageLayerProvider", mCoverageLayer.provider );
648650
}
649651
else
650652
{
@@ -682,16 +684,13 @@ void QgsAtlasComposition::readXML( const QDomElement& atlasElem, const QDomDocum
682684
}
683685

684686
// look for stored layer name
685-
mCoverageLayer = nullptr;
686-
QMap<QString, QgsMapLayer*> layers = QgsMapLayerRegistry::instance()->mapLayers();
687-
for ( QMap<QString, QgsMapLayer*>::const_iterator it = layers.begin(); it != layers.end(); ++it )
688-
{
689-
if ( it.key() == atlasElem.attribute( "coverageLayer" ) )
690-
{
691-
mCoverageLayer = dynamic_cast<QgsVectorLayer*>( it.value() );
692-
break;
693-
}
694-
}
687+
QString layerId = atlasElem.attribute( "coverageLayer" );
688+
QString layerName = atlasElem.attribute( "coverageLayerName" );
689+
QString layerSource = atlasElem.attribute( "coverageLayerSource" );
690+
QString layerProvider = atlasElem.attribute( "coverageLayerProvider" );
691+
692+
mCoverageLayer = QgsVectorLayerRef( layerId, layerName, layerSource, layerProvider );
693+
mCoverageLayer.resolveWeakly();
695694

696695
mPageNameExpression = atlasElem.attribute( "pageNameExpression", QString() );
697696
mSingleFile = atlasElem.attribute( "singleFile", "false" ) == "true" ? true : false;

‎src/core/composer/qgsatlascomposition.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "qgscoordinatetransform.h"
2020
#include "qgsfeature.h"
2121
#include "qgsgeometry.h"
22+
#include "qgsvectorlayerref.h"
2223

2324
#include <memory>
2425
#include <QString>
@@ -101,7 +102,7 @@ class CORE_EXPORT QgsAtlasComposition : public QObject
101102
* @returns atlas coverage layer
102103
* @see setCoverageLayer
103104
*/
104-
QgsVectorLayer* coverageLayer() const { return mCoverageLayer; }
105+
QgsVectorLayer* coverageLayer() const { return mCoverageLayer.get(); }
105106

106107
/** Sets the coverage layer to use for the atlas features
107108
* @param layer vector coverage layer
@@ -355,7 +356,7 @@ class CORE_EXPORT QgsAtlasComposition : public QObject
355356
bool mEnabled;
356357
bool mHideCoverage;
357358
QString mFilenamePattern;
358-
QgsVectorLayer* mCoverageLayer;
359+
QgsVectorLayerRef mCoverageLayer;
359360
bool mSingleFile;
360361

361362
QString mCurrentFilename;

‎src/core/composer/qgscomposerattributetablev2.cpp

Lines changed: 18 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@ bool QgsComposerAttributeTableCompareV2::operator()( const QgsComposerTableRow&
4949
QgsComposerAttributeTableV2::QgsComposerAttributeTableV2( QgsComposition* composition, bool createUndoCommands )
5050
: QgsComposerTableV2( composition, createUndoCommands )
5151
, mSource( LayerAttributes )
52-
, mVectorLayer( nullptr )
5352
, mCurrentAtlasLayer( nullptr )
5453
, mComposerMap( nullptr )
5554
, mMaximumNumberOfFeatures( 30 )
@@ -67,15 +66,15 @@ QgsComposerAttributeTableV2::QgsComposerAttributeTableV2( QgsComposition* compos
6766
QgsVectorLayer* vl = dynamic_cast<QgsVectorLayer*>( mapIt.value() );
6867
if ( vl )
6968
{
70-
mVectorLayer = vl;
69+
mVectorLayer.setLayer( vl );
7170
break;
7271
}
7372
}
7473
if ( mVectorLayer )
7574
{
7675
resetColumns();
7776
//listen for modifications to layer and refresh table when they occur
78-
connect( mVectorLayer, SIGNAL( layerModified() ), this, SLOT( refreshAttributes() ) );
77+
connect( mVectorLayer.get(), SIGNAL( layerModified() ), this, SLOT( refreshAttributes() ) );
7978
}
8079
connect( QgsMapLayerRegistry::instance(), SIGNAL( layerWillBeRemoved( QString ) ), this, SLOT( removeLayer( const QString& ) ) );
8180

@@ -104,14 +103,14 @@ QString QgsComposerAttributeTableV2::displayName() const
104103

105104
void QgsComposerAttributeTableV2::setVectorLayer( QgsVectorLayer* layer )
106105
{
107-
if ( layer == mVectorLayer )
106+
if ( layer == mVectorLayer.get() )
108107
{
109108
//no change
110109
return;
111110
}
112111

113112
QgsVectorLayer* prevLayer = sourceLayer();
114-
mVectorLayer = layer;
113+
mVectorLayer.setLayer( layer );
115114

116115
if ( mSource == QgsComposerAttributeTableV2::LayerAttributes && layer != prevLayer )
117116
{
@@ -125,7 +124,7 @@ void QgsComposerAttributeTableV2::setVectorLayer( QgsVectorLayer* layer )
125124
resetColumns();
126125

127126
//listen for modifications to layer and refresh table when they occur
128-
connect( mVectorLayer, SIGNAL( layerModified() ), this, SLOT( refreshAttributes() ) );
127+
connect( mVectorLayer.get(), SIGNAL( layerModified() ), this, SLOT( refreshAttributes() ) );
129128
}
130129

131130
refreshAttributes();
@@ -591,7 +590,7 @@ QgsExpressionContext *QgsComposerAttributeTableV2::createExpressionContext() con
591590

592591
if ( mSource == LayerAttributes )
593592
{
594-
context->appendScope( QgsExpressionContextUtils::layerScope( mVectorLayer ) );
593+
context->appendScope( QgsExpressionContextUtils::layerScope( mVectorLayer.get() ) );
595594
}
596595

597596
return context;
@@ -615,7 +614,7 @@ QgsVectorLayer *QgsComposerAttributeTableV2::sourceLayer()
615614
case QgsComposerAttributeTableV2::AtlasFeature:
616615
return mComposition->atlasComposition().coverageLayer();
617616
case QgsComposerAttributeTableV2::LayerAttributes:
618-
return mVectorLayer;
617+
return mVectorLayer.get();
619618
case QgsComposerAttributeTableV2::RelationChildren:
620619
{
621620
QgsRelation relation = QgsProject::instance()->relationManager()->relation( mRelationId );
@@ -631,7 +630,7 @@ void QgsComposerAttributeTableV2::removeLayer( const QString& layerId )
631630
{
632631
if ( layerId == mVectorLayer->id() )
633632
{
634-
mVectorLayer = nullptr;
633+
mVectorLayer.setLayer( nullptr );
635634
//remove existing columns
636635
qDeleteAll( mColumns );
637636
mColumns.clear();
@@ -709,7 +708,10 @@ bool QgsComposerAttributeTableV2::writeXML( QDomElement& elem, QDomDocument & do
709708
}
710709
if ( mVectorLayer )
711710
{
712-
composerTableElem.setAttribute( "vectorLayer", mVectorLayer->id() );
711+
composerTableElem.setAttribute( "vectorLayer", mVectorLayer.layerId );
712+
composerTableElem.setAttribute( "vectorLayerName", mVectorLayer.name );
713+
composerTableElem.setAttribute( "vectorLayerSource", mVectorLayer.source );
714+
composerTableElem.setAttribute( "vectorLayerProvider", mVectorLayer.provider );
713715
}
714716

715717
bool ok = QgsComposerTableV2::writeXML( composerTableElem, doc, ignoreFrames );
@@ -778,19 +780,12 @@ bool QgsComposerAttributeTableV2::readXML( const QDomElement& itemElem, const QD
778780
}
779781

780782
//vector layer
781-
QString layerId = itemElem.attribute( "vectorLayer", "not_existing" );
782-
if ( layerId == "not_existing" )
783-
{
784-
mVectorLayer = nullptr;
785-
}
786-
else
787-
{
788-
QgsMapLayer* ml = QgsMapLayerRegistry::instance()->mapLayer( layerId );
789-
if ( ml )
790-
{
791-
mVectorLayer = dynamic_cast<QgsVectorLayer*>( ml );
792-
}
793-
}
783+
QString layerId = itemElem.attribute( "vectorLayer" );
784+
QString layerName = itemElem.attribute( "vectorLayerName" );
785+
QString layerSource = itemElem.attribute( "vectorLayerSource" );
786+
QString layerProvider = itemElem.attribute( "vectorLayerProvider" );
787+
mVectorLayer = QgsVectorLayerRef( layerId, layerName, layerSource, layerProvider );
788+
mVectorLayer.resolveWeakly();
794789

795790
//connect to new layer
796791
connect( sourceLayer(), SIGNAL( layerModified() ), this, SLOT( refreshAttributes() ) );

‎src/core/composer/qgscomposerattributetablev2.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
#include "qgscomposertablev2.h"
2222
#include "qgscomposerattributetable.h"
23+
#include "qgsvectorlayerref.h"
2324

2425
class QgsComposerMap;
2526
class QgsVectorLayer;
@@ -120,7 +121,7 @@ class CORE_EXPORT QgsComposerAttributeTableV2: public QgsComposerTableV2
120121
* @returns attribute table's current vector layer
121122
* @see setVectorLayer
122123
*/
123-
QgsVectorLayer* vectorLayer() const { return mVectorLayer; }
124+
QgsVectorLayer* vectorLayer() const { return mVectorLayer.get(); }
124125

125126
/** Sets the relation id from which to display child features
126127
* @param relationId id for relation to display child features from
@@ -314,7 +315,7 @@ class CORE_EXPORT QgsComposerAttributeTableV2: public QgsComposerTableV2
314315
/** Attribute source*/
315316
ContentSource mSource;
316317
/** Associated vector layer*/
317-
QgsVectorLayer* mVectorLayer;
318+
QgsVectorLayerRef mVectorLayer;
318319
/** Relation id, if in relation children mode*/
319320
QString mRelationId;
320321

‎src/core/composer/qgscomposermap.cpp

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
#include "qgsexpression.h"
3636
#include "qgsvisibilitypresetcollection.h"
3737
#include "qgsannotation.h"
38+
#include "qgsvectorlayerref.h"
3839

3940
#include "qgslabel.h"
4041
#include "qgslabelattributes.h"
@@ -1323,12 +1324,21 @@ bool QgsComposerMap::writeXML( QDomElement& elem, QDomDocument & doc ) const
13231324

13241325
//layer set
13251326
QDomElement layerSetElem = doc.createElement( "LayerSet" );
1326-
QStringList::const_iterator layerIt = mLayerSet.constBegin();
1327-
for ( ; layerIt != mLayerSet.constEnd(); ++layerIt )
1327+
Q_FOREACH ( const QString &layerId, mLayerSet )
13281328
{
1329+
QgsVectorLayerRef layerRef( layerId );
1330+
layerRef.resolve();
1331+
1332+
if ( !layerRef )
1333+
continue;
1334+
13291335
QDomElement layerElem = doc.createElement( "Layer" );
1330-
QDomText layerIdText = doc.createTextNode( *layerIt );
1336+
QDomText layerIdText = doc.createTextNode( layerRef.layerId );
13311337
layerElem.appendChild( layerIdText );
1338+
layerElem.setAttribute( "name", layerRef.name );
1339+
layerElem.setAttribute( "source", layerRef.source );
1340+
layerElem.setAttribute( "provider", layerRef.provider );
1341+
13321342
layerSetElem.appendChild( layerElem );
13331343
}
13341344
composerMapElem.appendChild( layerSetElem );
@@ -1341,7 +1351,15 @@ bool QgsComposerMap::writeXML( QDomElement& elem, QDomDocument & doc ) const
13411351
for ( ; styleIt != mLayerStyleOverrides.constEnd(); ++styleIt )
13421352
{
13431353
QDomElement styleElem = doc.createElement( "LayerStyle" );
1344-
styleElem.setAttribute( "layerid", styleIt.key() );
1354+
1355+
QgsMapLayerRef ref( styleIt.key() );
1356+
ref.resolve();
1357+
1358+
styleElem.setAttribute( "layerid", ref.layerId );
1359+
styleElem.setAttribute( "name", ref.name );
1360+
styleElem.setAttribute( "source", ref.source );
1361+
styleElem.setAttribute( "provider", ref.provider );
1362+
13451363
QgsMapLayerStyle style( styleIt.value() );
13461364
style.writeXml( styleElem );
13471365
stylesElem.appendChild( styleElem );
@@ -1456,8 +1474,15 @@ bool QgsComposerMap::readXML( const QDomElement& itemElem, const QDomDocument& d
14561474
layerSet.reserve( layerIdNodeList.size() );
14571475
for ( int i = 0; i < layerIdNodeList.size(); ++i )
14581476
{
1459-
const QDomElement& layerIdElement = layerIdNodeList.at( i ).toElement();
1460-
layerSet << layerIdElement.text();
1477+
QDomElement layerElem = layerIdNodeList.at( i ).toElement();
1478+
QString layerId = layerElem.text();
1479+
QString layerName = layerElem.attribute( "name" );
1480+
QString layerSource = layerElem.attribute( "source" );
1481+
QString layerProvider = layerElem.attribute( "provider" );
1482+
1483+
QgsMapLayerRef ref( layerId, layerName, layerSource, layerProvider );
1484+
ref.resolveWeakly();
1485+
layerSet << ref.layerId;
14611486
}
14621487
}
14631488
mLayerSet = layerSet;
@@ -1473,9 +1498,15 @@ bool QgsComposerMap::readXML( const QDomElement& itemElem, const QDomDocument& d
14731498
{
14741499
const QDomElement& layerStyleElement = layerStyleNodeList.at( i ).toElement();
14751500
QString layerId = layerStyleElement.attribute( "layerid" );
1501+
QString layerName = layerStyleElement.attribute( "name" );
1502+
QString layerSource = layerStyleElement.attribute( "source" );
1503+
QString layerProvider = layerStyleElement.attribute( "provider" );
1504+
QgsMapLayerRef ref( layerId, layerName, layerSource, layerProvider );
1505+
ref.resolveWeakly();
1506+
14761507
QgsMapLayerStyle style;
14771508
style.readXml( layerStyleElement );
1478-
mLayerStyleOverrides.insert( layerId, style.xmlData() );
1509+
mLayerStyleOverrides.insert( ref.layerId, style.xmlData() );
14791510
}
14801511
}
14811512

‎src/core/qgsmaplayerlistutils.h

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
#ifndef QGSMAPLAYERLISTUTILS_H
2+
#define QGSMAPLAYERLISTUTILS_H
3+
4+
//
5+
// W A R N I N G
6+
// -------------
7+
//
8+
// This file is not part of the QGIS API. It exists purely as an
9+
// implementation detail. This header file may change from version to
10+
// version without notice, or even be removed.
11+
//
12+
13+
#include <QPointer>
14+
15+
#include "qgsmaplayer.h"
16+
#include "qgsmaplayerref.h"
17+
18+
/// @cond PRIVATE
19+
20+
inline QList<QgsMapLayer *> _qgis_listRefToRaw( const QList< QgsMapLayerRef > &layers )
21+
{
22+
QList<QgsMapLayer *> lst;
23+
lst.reserve( layers.count() );
24+
Q_FOREACH ( const QgsMapLayerRef &layer, layers )
25+
{
26+
if ( layer )
27+
lst.append( layer.get() );
28+
}
29+
return lst;
30+
}
31+
32+
inline QList< QgsMapLayerRef > _qgis_listRawToRef( const QList<QgsMapLayer *> &layers )
33+
{
34+
QList< QgsMapLayerRef > lst;
35+
lst.reserve( layers.count() );
36+
Q_FOREACH ( QgsMapLayer *layer, layers )
37+
{
38+
lst.append( QgsMapLayerRef( layer ) );
39+
}
40+
return lst;
41+
}
42+
43+
inline void _qgis_removeLayers( QList< QgsMapLayerRef > &list, QList< QgsMapLayer *> layersToRemove )
44+
{
45+
QMutableListIterator<QgsMapLayerRef> it( list );
46+
while ( it.hasNext() )
47+
{
48+
QgsMapLayerRef &ref = it.next();
49+
if ( layersToRemove.contains( ref.get() ) )
50+
it.remove();
51+
}
52+
}
53+
54+
55+
///@endcond
56+
57+
#endif // QGSMAPLAYERLISTUTILS_H

‎src/core/qgsmaplayerref.h

Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
/***************************************************************************
2+
qgsmaplayerref.h
3+
--------------------------------------
4+
Date : January 2017
5+
Copyright : (C) 2017 by Martin Dobias
6+
Email : wonder dot sk at gmail dot com
7+
***************************************************************************
8+
* *
9+
* This program is free software; you can redistribute it and/or modify *
10+
* it under the terms of the GNU General Public License as published by *
11+
* the Free Software Foundation; either version 2 of the License, or *
12+
* (at your option) any later version. *
13+
* *
14+
***************************************************************************/
15+
16+
#ifndef QGSMAPLAYERREF_H
17+
#define QGSMAPLAYERREF_H
18+
19+
#include <QPointer>
20+
21+
#include "qgsmaplayer.h"
22+
#include "qgsdataprovider.h"
23+
#include "qgsmaplayerregistry.h"
24+
#include "qgsvectorlayer.h"
25+
#include "qgsvectordataprovider.h"
26+
#include "qgsrasterlayer.h"
27+
#include "qgsrasterdataprovider.h"
28+
29+
/** Internal structure to keep weak pointer to QgsMapLayer or layerId
30+
* if the layer is not available yet.
31+
* \note not available in Python bindings
32+
*/
33+
template<typename TYPE>
34+
struct _LayerRef
35+
{
36+
37+
/**
38+
* Constructor for a layer reference from an existing map layer.
39+
* The layerId, source, name and provider members will automatically
40+
* be populated from this layer.
41+
*/
42+
_LayerRef( TYPE *l = nullptr )
43+
: layer( l )
44+
, layerId( l ? l->id() : QString() )
45+
, source( l ? l->publicSource() : QString() )
46+
, name( l ? l->name() : QString() )
47+
, provider( layerProviderName( l ) )
48+
{}
49+
50+
/**
51+
* Constructor for a weak layer reference, using a combination of layer ID,
52+
* \a name, public \a source and \a provider key.
53+
*/
54+
_LayerRef( const QString &id, const QString &name = QString(), const QString &source = QString(), const QString &provider = QString() )
55+
: layer()
56+
, layerId( id )
57+
, source( source )
58+
, name( name )
59+
, provider( provider )
60+
{}
61+
62+
/**
63+
* Sets the reference to point to a specified layer.
64+
*/
65+
void setLayer( TYPE *l )
66+
{
67+
layer = l;
68+
layerId = l ? l->id() : QString();
69+
source = l ? l->publicSource() : QString();
70+
name = l ? l->name() : QString();
71+
provider = layerProviderName( l );
72+
}
73+
74+
/**
75+
* Returns true if the layer reference is resolved and contains a reference to an existing
76+
* map layer.
77+
*/
78+
operator bool() const
79+
{
80+
return static_cast< bool >( layer.data() );
81+
}
82+
83+
/**
84+
* Forwards the to map layer.
85+
*/
86+
TYPE *operator->() const
87+
{
88+
return layer.data();
89+
}
90+
91+
/**
92+
* Returns a pointer to the layer, or nullptr if the reference has not yet been matched
93+
* to a layer.
94+
*/
95+
TYPE *get() const
96+
{
97+
return layer.data();
98+
}
99+
100+
//! Weak pointer to map layer
101+
QPointer<TYPE> layer;
102+
103+
//! Original layer ID
104+
QString layerId;
105+
106+
//! Weak reference to layer public source
107+
QString source;
108+
//! Weak reference to layer name
109+
QString name;
110+
//! Weak reference to layer provider
111+
QString provider;
112+
113+
/**
114+
* Returns true if a layer matches the weak references to layer public source,
115+
* layer name and data provider contained in this layer reference.
116+
* \see resolveWeakly()
117+
*/
118+
bool layerMatchesSource( QgsMapLayer *layer ) const
119+
{
120+
if ( layer->publicSource() != source ||
121+
layer->name() != name )
122+
return false;
123+
124+
if ( layerProviderName( layer ) != provider )
125+
return false;
126+
127+
return true;
128+
}
129+
130+
/**
131+
* Resolves the map layer by attempting to find a layer with matching ID
132+
* within the map layer registry. If found, this reference will be updated to match
133+
* the found layer and the layer will be returned. If no matching layer is
134+
* found, a nullptr is returned.
135+
* \see resolveWeakly()
136+
*/
137+
TYPE *resolve()
138+
{
139+
if ( !layerId.isEmpty() )
140+
{
141+
if ( TYPE *l = qobject_cast<TYPE *>( QgsMapLayerRegistry::instance()->mapLayer( layerId ) ) )
142+
{
143+
setLayer( l );
144+
return l;
145+
}
146+
}
147+
return nullptr;
148+
}
149+
150+
/**
151+
* Resolves the map layer by attempting to find a matching layer
152+
* in the map layer registry using a weak match.
153+
*
154+
* First, the layer is attempted to match to registry layers using the
155+
* layer's ID (calling this method implicitly calls resolve()).
156+
*
157+
* Failing a match by layer ID, the layer will be matched by using
158+
* the weak references to layer public source, layer name and data
159+
* provider contained in this layer reference.
160+
*
161+
* If a matching layer is found, this reference will be updated to match
162+
* the found layer and the layer will be returned. If no matching layer is
163+
* found, a nullptr is returned.
164+
* \see resolve()
165+
* \see layerMatchesSource()
166+
*/
167+
TYPE *resolveWeakly()
168+
{
169+
// first try matching by layer ID
170+
if ( resolve() )
171+
return layer;
172+
173+
if ( !name.isEmpty() )
174+
{
175+
Q_FOREACH ( QgsMapLayer *l, QgsMapLayerRegistry::instance()->mapLayersByName( name ) )
176+
{
177+
if ( TYPE *tl = qobject_cast< TYPE *>( l ) )
178+
{
179+
if ( layerMatchesSource( tl ) )
180+
{
181+
setLayer( tl );
182+
return tl;
183+
}
184+
}
185+
}
186+
}
187+
return nullptr;
188+
}
189+
190+
private:
191+
192+
static QString layerProviderName( const QgsMapLayer *layer )
193+
{
194+
if ( !layer )
195+
return QString();
196+
197+
switch ( layer->type() )
198+
{
199+
case QgsMapLayer::VectorLayer:
200+
{
201+
const QgsVectorLayer *vl = qobject_cast< const QgsVectorLayer *>( layer );
202+
return vl->dataProvider()->name();
203+
}
204+
205+
case QgsMapLayer::RasterLayer:
206+
{
207+
const QgsRasterLayer *rl = qobject_cast< const QgsRasterLayer *>( layer );
208+
return rl->dataProvider()->name();
209+
}
210+
case QgsMapLayer::PluginLayer:
211+
return QString();
212+
}
213+
return QString();
214+
215+
}
216+
};
217+
218+
typedef _LayerRef<QgsMapLayer> QgsMapLayerRef;
219+
220+
#endif // QGSMAPLAYERREF_H

‎src/core/qgsvectorlayerref.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#ifndef QGSVECTORLAYERREF_H
2+
#define QGSVECTORLAYERREF_H
3+
4+
#include "qgsmaplayerref.h"
5+
6+
#include "qgsvectorlayer.h"
7+
8+
typedef _LayerRef<QgsVectorLayer> QgsVectorLayerRef;
9+
10+
#endif // QGSVECTORLAYERREF_H

‎tests/src/core/testqgscomposition.cpp

Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
#include "qgsapplication.h"
1919
#include "qgscomposition.h"
20+
#include "qgscomposerattributetablev2.h"
2021
#include "qgscomposerlabel.h"
2122
#include "qgscomposershape.h"
2223
#include "qgscomposerarrow.h"
@@ -60,6 +61,10 @@ class TestQgsComposition : public QObject
6061
void georeference();
6162
void variablesEdited();
6263
void legendRestoredFromTemplate();
64+
void attributeTableRestoredFromTemplate();
65+
void mapLayersRestoredFromTemplate();
66+
void mapLayersStyleOverrideRestoredFromTemplate();
67+
void atlasLayerRestoredFromTemplate();
6368

6469
private:
6570
QgsComposition *mComposition;
@@ -688,5 +693,215 @@ void TestQgsComposition::legendRestoredFromTemplate()
688693
QCOMPARE( model3->data( model->node2index( layerNode3 ), Qt::DisplayRole ).toString(), QString( "new title!" ) );
689694
}
690695

696+
void TestQgsComposition::attributeTableRestoredFromTemplate()
697+
{
698+
QgsMapLayerRegistry::instance()->removeAllMapLayers();
699+
700+
// load some layers
701+
QFileInfo vectorFileInfo( QString( TEST_DATA_DIR ) + "/points.shp" );
702+
QgsVectorLayer *layer = new QgsVectorLayer( vectorFileInfo.filePath(),
703+
vectorFileInfo.completeBaseName(),
704+
"ogr" );
705+
QgsVectorLayer *layer2 = new QgsVectorLayer( "Point", "memory", "memory" );
706+
QgsMapLayerRegistry::instance()->addMapLayer( layer2 );
707+
QgsMapLayerRegistry::instance()->addMapLayer( layer );
708+
709+
// create composition
710+
QgsMapSettings ms;
711+
QgsComposition c( ms );
712+
// add an attribute table
713+
QgsComposerAttributeTableV2 *table = new QgsComposerAttributeTableV2( &c, false );
714+
c.addMultiFrame( table );
715+
table->setVectorLayer( layer );
716+
QgsComposerFrame *frame = new QgsComposerFrame( &c, table, 1, 1, 10, 10 );
717+
c.addComposerTableFrame( table, frame );
718+
table->addFrame( frame );
719+
720+
// save composition to template
721+
QDomDocument doc;
722+
QDomElement composerElem = doc.createElement( "Composer" );
723+
doc.appendChild( composerElem );
724+
c.writeXML( composerElem, doc );
725+
c.atlasComposition().writeXML( composerElem, doc );
726+
727+
// new project
728+
QgsMapLayerRegistry::instance()->removeAllMapLayers();
729+
QgsVectorLayer *layer3 = new QgsVectorLayer( vectorFileInfo.filePath(),
730+
vectorFileInfo.completeBaseName(),
731+
"ogr" );
732+
QgsVectorLayer *layer4 = new QgsVectorLayer( "Point", "memory", "memory" );
733+
QgsMapLayerRegistry::instance()->addMapLayer( layer4 );
734+
QgsMapLayerRegistry::instance()->addMapLayer( layer3 );
735+
736+
// make a new composition from template
737+
QgsComposition c2( ms );
738+
QVERIFY( c2.loadFromTemplate( doc ) );
739+
// get table from new composition
740+
QList< QgsComposerFrame * > frames2;
741+
c2.composerItems( frames2 );
742+
QgsComposerAttributeTableV2 *table2 = static_cast< QgsComposerAttributeTableV2 *>( frames2.at( 0 )->multiFrame() );
743+
QVERIFY( table2 );
744+
745+
QCOMPARE( table2->vectorLayer(), layer3 );
746+
}
747+
748+
void TestQgsComposition::mapLayersRestoredFromTemplate()
749+
{
750+
QgsMapLayerRegistry::instance()->removeAllMapLayers();
751+
752+
// load some layers
753+
QFileInfo vectorFileInfo( QString( TEST_DATA_DIR ) + "/points.shp" );
754+
QgsVectorLayer *layer = new QgsVectorLayer( vectorFileInfo.filePath(),
755+
vectorFileInfo.completeBaseName(),
756+
"ogr" );
757+
QFileInfo vectorFileInfo2( QString( TEST_DATA_DIR ) + "/polys.shp" );
758+
QgsVectorLayer *layer2 = new QgsVectorLayer( vectorFileInfo2.filePath(),
759+
vectorFileInfo2.completeBaseName(),
760+
"ogr" );
761+
762+
QgsMapLayerRegistry::instance()->addMapLayer( layer2 );
763+
QgsMapLayerRegistry::instance()->addMapLayer( layer );
764+
765+
// create composition
766+
QgsMapSettings ms;
767+
QgsComposition c( ms );
768+
// add a map
769+
QgsComposerMap *map = new QgsComposerMap( &c, 1, 1, 10, 10 );
770+
c.addComposerMap( map );
771+
map->setLayerSet( QStringList() << layer->id() << layer2->id() );
772+
773+
// save composition to template
774+
QDomDocument doc;
775+
QDomElement composerElem = doc.createElement( "Composer" );
776+
doc.appendChild( composerElem );
777+
c.writeXML( composerElem, doc );
778+
c.atlasComposition().writeXML( composerElem, doc );
779+
780+
// new project
781+
QgsMapLayerRegistry::instance()->removeAllMapLayers();
782+
QgsVectorLayer *layer3 = new QgsVectorLayer( vectorFileInfo.filePath(),
783+
vectorFileInfo.completeBaseName(),
784+
"ogr" );
785+
QgsVectorLayer *layer4 = new QgsVectorLayer( vectorFileInfo2.filePath(),
786+
vectorFileInfo2.completeBaseName(),
787+
"ogr" );
788+
QgsMapLayerRegistry::instance()->addMapLayer( layer4 );
789+
QgsMapLayerRegistry::instance()->addMapLayer( layer3 );
790+
791+
// make a new composition from template
792+
QgsComposition c2( ms );
793+
QVERIFY( c2.loadFromTemplate( doc ) );
794+
// get map from new composition
795+
QList< QgsComposerMap * > maps;
796+
c2.composerItems( maps );
797+
QgsComposerMap *map2 = static_cast< QgsComposerMap *>( maps.at( 0 ) );
798+
QVERIFY( map2 );
799+
800+
QCOMPARE( map2->layerSet(), QStringList() << layer3->id() << layer4->id() );
801+
}
802+
803+
void TestQgsComposition::mapLayersStyleOverrideRestoredFromTemplate()
804+
{
805+
QgsMapLayerRegistry::instance()->removeAllMapLayers();
806+
807+
// load some layers
808+
QFileInfo vectorFileInfo( QString( TEST_DATA_DIR ) + "/points.shp" );
809+
QgsVectorLayer *layer = new QgsVectorLayer( vectorFileInfo.filePath(),
810+
vectorFileInfo.completeBaseName(),
811+
"ogr" );
812+
QFileInfo vectorFileInfo2( QString( TEST_DATA_DIR ) + "/polys.shp" );
813+
QgsVectorLayer *layer2 = new QgsVectorLayer( vectorFileInfo2.filePath(),
814+
vectorFileInfo2.completeBaseName(),
815+
"ogr" );
816+
QgsMapLayerRegistry::instance()->addMapLayer( layer2 );
817+
QgsMapLayerRegistry::instance()->addMapLayer( layer );
818+
819+
// create composition
820+
QgsMapSettings ms;
821+
QgsComposition c( ms );
822+
// add a map
823+
QgsComposerMap *map = new QgsComposerMap( &c, 1, 1, 10, 10 );
824+
c.addComposerMap( map );
825+
map->setKeepLayerStyles( true );
826+
QgsStringMap styles;
827+
// just close your eyes and pretend these are real styles
828+
styles.insert( layer->id(), "<b>xxxxx</b>" );
829+
styles.insert( layer2->id(), "<blink>yyyyy</blink>" );
830+
map->setLayerStyleOverrides( styles );
831+
832+
// save composition to template
833+
QDomDocument doc;
834+
QDomElement composerElem = doc.createElement( "Composer" );
835+
doc.appendChild( composerElem );
836+
c.writeXML( composerElem, doc );
837+
c.atlasComposition().writeXML( composerElem, doc );
838+
839+
// new project
840+
QgsMapLayerRegistry::instance()->removeAllMapLayers();
841+
QgsVectorLayer *layer3 = new QgsVectorLayer( vectorFileInfo.filePath(),
842+
vectorFileInfo.completeBaseName(),
843+
"ogr" );
844+
QgsVectorLayer *layer4 = new QgsVectorLayer( vectorFileInfo2.filePath(),
845+
vectorFileInfo2.completeBaseName(),
846+
"ogr" );
847+
QgsMapLayerRegistry::instance()->addMapLayer( layer4 );
848+
QgsMapLayerRegistry::instance()->addMapLayer( layer3 );
849+
850+
// make a new composition from template
851+
QgsComposition c2( ms );
852+
QVERIFY( c2.loadFromTemplate( doc ) );
853+
// get map from new composition
854+
QList< QgsComposerMap * > maps;
855+
c2.composerItems( maps );
856+
QgsComposerMap *map2 = static_cast< QgsComposerMap *>( maps.at( 0 ) );
857+
QVERIFY( map2 );
858+
QVERIFY( map2->keepLayerStyles() );
859+
860+
QgsStringMap restoredStyles = map2->layerStyleOverrides();
861+
QVERIFY( restoredStyles.contains( layer3->id() ) );
862+
QCOMPARE( restoredStyles.value( layer3->id() ).trimmed(), QString( "<b>xxxxx</b>" ) );
863+
QVERIFY( restoredStyles.contains( layer4->id() ) );
864+
QCOMPARE( restoredStyles.value( layer4->id() ).trimmed(), QString( "<blink>yyyyy</blink>" ) );
865+
}
866+
867+
void TestQgsComposition::atlasLayerRestoredFromTemplate()
868+
{
869+
QgsMapLayerRegistry::instance()->removeAllMapLayers();
870+
871+
// load some layers
872+
QFileInfo vectorFileInfo( QString( TEST_DATA_DIR ) + "/points.shp" );
873+
QgsVectorLayer *layer = new QgsVectorLayer( vectorFileInfo.filePath(),
874+
vectorFileInfo.completeBaseName(),
875+
"ogr" );
876+
QgsMapLayerRegistry::instance()->addMapLayer( layer );
877+
878+
// create composition
879+
QgsMapSettings ms;
880+
QgsComposition c( ms );
881+
// set atlas layer
882+
c.atlasComposition().setEnabled( true );
883+
c.atlasComposition().setCoverageLayer( layer );
884+
885+
// save composition to template
886+
QDomDocument doc;
887+
QDomElement composerElem = doc.createElement( "Composer" );
888+
doc.appendChild( composerElem );
889+
c.writeXML( composerElem, doc );
890+
c.atlasComposition().writeXML( composerElem, doc );
891+
892+
// new project
893+
QgsMapLayerRegistry::instance()->removeAllMapLayers();
894+
QgsVectorLayer *layer2 = new QgsVectorLayer( vectorFileInfo.filePath(),
895+
vectorFileInfo.completeBaseName(),
896+
"ogr" );
897+
QgsMapLayerRegistry::instance()->addMapLayer( layer2 );
898+
899+
// make a new composition from template
900+
QgsComposition c2( ms );
901+
QVERIFY( c2.loadFromTemplate( doc ) );
902+
// check atlas layer
903+
QCOMPARE( c2.atlasComposition().coverageLayer(), layer2 );
904+
}
905+
691906
QTEST_MAIN( TestQgsComposition )
692907
#include "testqgscomposition.moc"

‎tests/src/core/testqgsmaplayer.cpp

Lines changed: 109 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
#include <qgsvectorlayer.h>
2626
#include <qgsapplication.h>
2727
#include <qgsproviderregistry.h>
28+
#include "qgsvectorlayerref.h"
29+
#include "qgsmaplayerlistutils.h"
2830

2931
class TestSignalReceiver : public QObject
3032
{
@@ -68,9 +70,11 @@ class TestQgsMapLayer : public QObject
6870
void isInScaleRange_data();
6971
void isInScaleRange();
7072

73+
void layerRef();
74+
void layerRefListUtils();
7175

7276
private:
73-
QgsMapLayer * mpLayer;
77+
QgsVectorLayer * mpLayer;
7478
};
7579

7680
void TestQgsMapLayer::initTestCase()
@@ -94,11 +98,12 @@ void TestQgsMapLayer::init()
9498
QFileInfo myMapFileInfo( myFileName );
9599
mpLayer = new QgsVectorLayer( myMapFileInfo.filePath(),
96100
myMapFileInfo.completeBaseName(), "ogr" );
101+
QgsMapLayerRegistry::instance()->addMapLayer( mpLayer );
97102
}
98103

99104
void TestQgsMapLayer::cleanup()
100105
{
101-
delete mpLayer;
106+
QgsMapLayerRegistry::instance()->removeAllMapLayers();
102107
}
103108

104109
void TestQgsMapLayer::cleanupTestCase()
@@ -152,5 +157,107 @@ void TestQgsMapLayer::isInScaleRange()
152157

153158
}
154159

160+
void TestQgsMapLayer::layerRef()
161+
{
162+
// construct from layer
163+
QgsVectorLayerRef ref( mpLayer );
164+
QCOMPARE( ref.get(), mpLayer );
165+
QCOMPARE( ref.layer.data(), mpLayer );
166+
QCOMPARE( ref.layerId, mpLayer->id() );
167+
QCOMPARE( ref.name, QString( "points" ) );
168+
QCOMPARE( ref.source, mpLayer->publicSource() );
169+
QCOMPARE( ref.provider, QString( "ogr" ) );
170+
171+
// bool operator
172+
QVERIFY( ref );
173+
// -> operator
174+
QCOMPARE( ref->id(), mpLayer->id() );
175+
176+
// verify that layer matches layer
177+
QVERIFY( ref.layerMatchesSource( mpLayer ) );
178+
179+
// create a weak reference
180+
QgsVectorLayerRef ref2( mpLayer->id(), QString( "points" ), mpLayer->publicSource(), QString( "ogr" ) );
181+
QVERIFY( !ref2 );
182+
QVERIFY( !ref2.get() );
183+
QVERIFY( !ref2.layer.data() );
184+
QCOMPARE( ref2.layerId, mpLayer->id() );
185+
QCOMPARE( ref2.name, QString( "points" ) );
186+
QCOMPARE( ref2.source, mpLayer->publicSource() );
187+
QCOMPARE( ref2.provider, QString( "ogr" ) );
188+
189+
// verify that weak reference matches layer
190+
QVERIFY( ref2.layerMatchesSource( mpLayer ) );
191+
192+
// resolve layer using project
193+
QCOMPARE( ref2.resolve(), mpLayer );
194+
QVERIFY( ref2 );
195+
QCOMPARE( ref2.get(), mpLayer );
196+
QCOMPARE( ref2.layer.data(), mpLayer );
197+
QCOMPARE( ref2.layerId, mpLayer->id() );
198+
QCOMPARE( ref2.name, QString( "points" ) );
199+
QCOMPARE( ref2.source, mpLayer->publicSource() );
200+
QCOMPARE( ref2.provider, QString( "ogr" ) );
201+
202+
// setLayer
203+
QgsVectorLayerRef ref3;
204+
QVERIFY( !ref3.get() );
205+
ref3.setLayer( mpLayer );
206+
QCOMPARE( ref3.get(), mpLayer );
207+
QCOMPARE( ref3.layer.data(), mpLayer );
208+
QCOMPARE( ref3.layerId, mpLayer->id() );
209+
QCOMPARE( ref3.name, QString( "points" ) );
210+
QCOMPARE( ref3.source, mpLayer->publicSource() );
211+
QCOMPARE( ref3.provider, QString( "ogr" ) );
212+
213+
// weak resolve
214+
QgsVectorLayerRef ref4( QString( "badid" ), QString( "points" ), mpLayer->publicSource(), QString( "ogr" ) );
215+
QVERIFY( !ref4 );
216+
QVERIFY( !ref4.resolve() );
217+
QCOMPARE( ref4.resolveWeakly(), mpLayer );
218+
QCOMPARE( ref4.get(), mpLayer );
219+
QCOMPARE( ref4.layer.data(), mpLayer );
220+
QCOMPARE( ref4.layerId, mpLayer->id() );
221+
QCOMPARE( ref4.name, QString( "points" ) );
222+
QCOMPARE( ref4.source, mpLayer->publicSource() );
223+
QCOMPARE( ref4.provider, QString( "ogr" ) );
224+
225+
// try resolving a bad reference
226+
QgsVectorLayerRef ref5( QString( "badid" ), QString( "points" ), mpLayer->publicSource(), QString( "xxx" ) );
227+
QVERIFY( !ref5.get() );
228+
QVERIFY( !ref5.resolve() );
229+
QVERIFY( !ref5.resolveWeakly() );
230+
}
231+
232+
void TestQgsMapLayer::layerRefListUtils()
233+
{
234+
// conversion utils
235+
QgsVectorLayer *vlA = new QgsVectorLayer( "Point", "a", "memory" );
236+
QgsVectorLayer *vlB = new QgsVectorLayer( "Point", "b", "memory" );
237+
238+
QList<QgsMapLayer *> listRawSource;
239+
listRawSource << vlA << vlB;
240+
241+
QList< QgsMapLayerRef > refs = _qgis_listRawToRef( listRawSource );
242+
QCOMPARE( refs.at( 0 ).get(), vlA );
243+
QCOMPARE( refs.at( 1 ).get(), vlB );
244+
245+
QList<QgsMapLayer *> raw = _qgis_listRefToRaw( refs );
246+
QCOMPARE( raw, QList< QgsMapLayer *>() << vlA << vlB );
247+
248+
//remove layers
249+
QgsVectorLayer *vlC = new QgsVectorLayer( "Point", "c", "memory" );
250+
QgsVectorLayer *vlD = new QgsVectorLayer( "Point", "d", "memory" );
251+
refs << QgsMapLayerRef( vlC ) << QgsMapLayerRef( vlD );
252+
253+
_qgis_removeLayers( refs, QList< QgsMapLayer *>() << vlB << vlD );
254+
QCOMPARE( refs.size(), 2 );
255+
QCOMPARE( refs.at( 0 ).get(), vlA );
256+
QCOMPARE( refs.at( 1 ).get(), vlC );
257+
258+
259+
}
260+
261+
155262
QTEST_MAIN( TestQgsMapLayer )
156263
#include "testqgsmaplayer.moc"

0 commit comments

Comments
 (0)
Please sign in to comment.