Skip to content

Commit

Permalink
Merge pull request #4054 from nyalldawson/fix_2738
Browse files Browse the repository at this point in the history
[composer] Restore legend customisation from composer templates (Fix #2738)
  • Loading branch information
nyalldawson committed Mar 22, 2017
2 parents e8deab2 + 75b20ca commit 616868e
Show file tree
Hide file tree
Showing 11 changed files with 327 additions and 24 deletions.
19 changes: 15 additions & 4 deletions python/core/layertree/qgslayertreegroup.sip
Expand Up @@ -55,12 +55,23 @@ class QgsLayerTreeGroup : QgsLayerTreeNode
//! Find group node with specified name. Searches recursively the whole sub-tree.
QgsLayerTreeGroup* findGroup( const QString& name );

//! Read group (tree) from XML element <layer-tree-group> and return the newly created group (or null on error)
static QgsLayerTreeGroup* readXML( QDomElement& element ) /Factory/;
/**
* Read group (tree) from XML element <layer-tree-group> and return the newly
* created group (or null on error). If the looseMatch
* parameter is true then child legend layers will use looser matching criteria,
* eg testing layer source instead of layer IDs.
*/
static QgsLayerTreeGroup* readXML( QDomElement& element, bool looseMatch = false ) /Factory/;

//! Write group (tree) as XML element <layer-tree-group> and add it to the given parent element
virtual void writeXML( QDomElement& parentElement );
//! Read children from XML and append them to the group.
void readChildrenFromXML( QDomElement& element );

/**
* Read children from XML and append them to the group. If the looseMatch
* parameter is true then legend layers will use looser matching criteria,
* eg testing layer source instead of layer IDs.
*/
void readChildrenFromXML( QDomElement& element, bool looseMatch = false );

//! Return text representation of the tree. For debugging purposes only.
virtual QString dump() const;
Expand Down
36 changes: 35 additions & 1 deletion python/core/layertree/qgslayertreelayer.sip
Expand Up @@ -21,10 +21,30 @@ class QgsLayerTreeLayer : QgsLayerTreeNode
%TypeHeaderCode
#include <qgslayertreelayer.h>
%End
public:

//! Parameters for loose layer matching
struct LayerMatchParams
{
//! Layer public source
QString source;
//! Layer name
QString name;
//! Provider
QString providerKey;
};

public:
explicit QgsLayerTreeLayer( QgsMapLayer* layer );

/**
* Creates a layer node which will attach to a layer with matching
* parameters. This can be used for "looser" layer matching,
* avoiding the usual layer id check in favour of attaching to any layer
* with an equal source/name/provider.
*/
static QgsLayerTreeLayer* createLayerFromParams( const LayerMatchParams& source ) /Factory/;

explicit QgsLayerTreeLayer( const QString& layerId, const QString& name = QString() );

QString layerId() const;
Expand All @@ -38,13 +58,27 @@ class QgsLayerTreeLayer : QgsLayerTreeNode
//! @note added in 2.18.1
void setName( const QString& n );

/**
* Attempts to attach this layer node to a layer with a matching
* QgsMapLayer::publicSource(). This can be used for "looser" layer matching,
* avoiding the usual layer id check in favour of attaching to any layer
* with an equal source.
*/
void attachToSource( const LayerMatchParams &source );

QString layerName() const;
void setLayerName( const QString& n );

Qt::CheckState isVisible() const;
void setVisible( Qt::CheckState visible );

static QgsLayerTreeLayer* readXML( QDomElement& element ) /Factory/;
/**
* Creates a new layer from an XML definition. If the looseMatch
* parameter is true then legend layers will use looser matching criteria,
* eg testing layer source instead of layer IDs.
*/
static QgsLayerTreeLayer* readXML( QDomElement& element, bool looseMatch = false ) /Factory/;

virtual void writeXML( QDomElement& parentElement );

virtual QString dump() const;
Expand Down
9 changes: 7 additions & 2 deletions python/core/layertree/qgslayertreenode.sip
Expand Up @@ -83,8 +83,13 @@ class QgsLayerTreeNode : QObject
//! @note added in 2.18.1
virtual void setName( const QString& name ) = 0;

//! Read layer tree from XML. Returns new instance
static QgsLayerTreeNode *readXML( QDomElement &element );
/**
* Read layer tree from XML. Returns new instance. If the looseMatch
* parameter is true then child legend layers will use looser matching criteria,
* eg testing layer source instead of layer IDs.
*/
static QgsLayerTreeNode *readXML( QDomElement &element, bool looseMatch = false ) /Factory/;

//! Write layer tree to XML
virtual void writeXML( QDomElement &parentElement ) = 0;

Expand Down
2 changes: 1 addition & 1 deletion src/core/composer/qgscomposerlegend.cpp
Expand Up @@ -535,7 +535,7 @@ bool QgsComposerLegend::readXML( const QDomElement& itemElem, const QDomDocument
{
// QGIS >= 2.6
QDomElement layerTreeElem = itemElem.firstChildElement( "layer-tree-group" );
setCustomLayerTree( QgsLayerTreeGroup::readXML( layerTreeElem ) );
setCustomLayerTree( QgsLayerTreeGroup::readXML( layerTreeElem, true ) );
}

//restore general composer item properties
Expand Down
8 changes: 4 additions & 4 deletions src/core/layertree/qgslayertreegroup.cpp
Expand Up @@ -254,7 +254,7 @@ QgsLayerTreeGroup* QgsLayerTreeGroup::findGroup( const QString& name )
return nullptr;
}

QgsLayerTreeGroup* QgsLayerTreeGroup::readXML( QDomElement& element )
QgsLayerTreeGroup* QgsLayerTreeGroup::readXML( QDomElement& element, bool looseMatch )
{
if ( element.tagName() != "layer-tree-group" )
return nullptr;
Expand All @@ -270,7 +270,7 @@ QgsLayerTreeGroup* QgsLayerTreeGroup::readXML( QDomElement& element )

groupNode->readCommonXML( element );

groupNode->readChildrenFromXML( element );
groupNode->readChildrenFromXML( element, looseMatch );

groupNode->setIsMutuallyExclusive( isMutuallyExclusive, mutuallyExclusiveChildIndex );

Expand Down Expand Up @@ -298,13 +298,13 @@ void QgsLayerTreeGroup::writeXML( QDomElement& parentElement )
parentElement.appendChild( elem );
}

void QgsLayerTreeGroup::readChildrenFromXML( QDomElement& element )
void QgsLayerTreeGroup::readChildrenFromXML( QDomElement& element, bool looseMatch )
{
QList<QgsLayerTreeNode*> nodes;
QDomElement childElem = element.firstChildElement();
while ( !childElem.isNull() )
{
QgsLayerTreeNode* newNode = QgsLayerTreeNode::readXML( childElem );
QgsLayerTreeNode* newNode = QgsLayerTreeNode::readXML( childElem, looseMatch );
if ( newNode )
nodes << newNode;

Expand Down
18 changes: 14 additions & 4 deletions src/core/layertree/qgslayertreegroup.h
Expand Up @@ -76,12 +76,22 @@ class CORE_EXPORT QgsLayerTreeGroup : public QgsLayerTreeNode
//! Find group node with specified name. Searches recursively the whole sub-tree.
QgsLayerTreeGroup* findGroup( const QString& name );

//! Read group (tree) from XML element <layer-tree-group> and return the newly created group (or null on error)
static QgsLayerTreeGroup* readXML( QDomElement& element );
/**
* Read group (tree) from XML element <layer-tree-group> and return the newly
* created group (or null on error). If the looseMatch
* parameter is true then child legend layers will use looser matching criteria,
* eg testing layer source instead of layer IDs.
*/
static QgsLayerTreeGroup* readXML( QDomElement& element, bool looseMatch = false );

//! Write group (tree) as XML element <layer-tree-group> and add it to the given parent element
virtual void writeXML( QDomElement& parentElement ) override;
//! Read children from XML and append them to the group.
void readChildrenFromXML( QDomElement& element );
/**
* Read children from XML and append them to the group. If the looseMatch
* parameter is true then legend layers will use looser matching criteria,
* eg testing layer source instead of layer IDs.
*/
void readChildrenFromXML( QDomElement& element, bool looseMatch = false );

//! Return text representation of the tree. For debugging purposes only.
virtual QString dump() const override;
Expand Down
109 changes: 107 additions & 2 deletions src/core/layertree/qgslayertreelayer.cpp
Expand Up @@ -18,7 +18,10 @@
#include "qgslayertreeutils.h"
#include "qgsmaplayer.h"
#include "qgsmaplayerregistry.h"

#include "qgsvectorlayer.h"
#include "qgsrasterlayer.h"
#include "qgsvectordataprovider.h"
#include "qgsrasterdataprovider.h"

QgsLayerTreeLayer::QgsLayerTreeLayer( QgsMapLayer *layer )
: QgsLayerTreeNode( NodeLayer )
Expand Down Expand Up @@ -50,6 +53,13 @@ QgsLayerTreeLayer::QgsLayerTreeLayer( const QgsLayerTreeLayer& other )
attachToLayer();
}

QgsLayerTreeLayer *QgsLayerTreeLayer::createLayerFromParams( const LayerMatchParams &source )
{
QgsLayerTreeLayer* l = new QgsLayerTreeLayer( QString() );
l->attachToSource( source );
return l;
}

void QgsLayerTreeLayer::attachToLayer()
{
// layer is not necessarily already loaded
Expand All @@ -71,6 +81,35 @@ void QgsLayerTreeLayer::attachToLayer()
}
}

bool QgsLayerTreeLayer::layerMatchesSource( QgsMapLayer* layer, const QgsLayerTreeLayer::LayerMatchParams &params ) const
{
if ( layer->publicSource() != params.source ||
layer->name() != params.name )
return false;

switch ( layer->type() )
{
case QgsMapLayer::VectorLayer:
{
QgsVectorLayer* vl = qobject_cast< QgsVectorLayer* >( layer );
if ( vl->dataProvider()->name() != params.providerKey )
return false;
break;
}
case QgsMapLayer::RasterLayer:
{
QgsRasterLayer* rl = qobject_cast< QgsRasterLayer* >( layer );
if ( rl->dataProvider()->name() != params.providerKey )
return false;
break;
}
case QgsMapLayer::PluginLayer:
break;

}
return true;
}

QString QgsLayerTreeLayer::name() const
{
return layerName();
Expand All @@ -81,6 +120,29 @@ void QgsLayerTreeLayer::setName( const QString& n )
setLayerName( n );
}

void QgsLayerTreeLayer::attachToSource( const LayerMatchParams &source )
{
// check if matching source already open
bool foundMatch = false;
Q_FOREACH ( QgsMapLayer* layer, QgsMapLayerRegistry::instance()->mapLayers() )
{
if ( layerMatchesSource( layer, source ) )
{
// found a source! need to disconnect from layersAdded signal as original attachToLayer call
// will have set this up
disconnect( QgsMapLayerRegistry::instance(), SIGNAL( layersAdded( QList<QgsMapLayer*> ) ), this, SLOT( registryLayersAdded( QList<QgsMapLayer*> ) ) );
mLayerId = layer->id();
attachToLayer();
emit layerLoaded();
foundMatch = true;
break;
}
}

if ( !foundMatch )
mLooseMatchParams = source; // no need to store source if match already made
}

QString QgsLayerTreeLayer::layerName() const
{
return mLayer ? mLayer->name() : mLayerName;
Expand Down Expand Up @@ -113,13 +175,17 @@ void QgsLayerTreeLayer::setVisible( Qt::CheckState state )
emit visibilityChanged( this, state );
}

QgsLayerTreeLayer* QgsLayerTreeLayer::readXML( QDomElement& element )
QgsLayerTreeLayer* QgsLayerTreeLayer::readXML( QDomElement& element , bool looseMatch )
{
if ( element.tagName() != "layer-tree-layer" )
return nullptr;

QString layerID = element.attribute( "id" );
QString layerName = element.attribute( "name" );

QString source = element.attribute( "source" );
QString providerKey = element.attribute( "providerKey" );

Qt::CheckState checked = QgsLayerTreeUtils::checkStateFromXml( element.attribute( "checked" ) );
bool isExpanded = ( element.attribute( "expanded", "1" ) == "1" );

Expand All @@ -129,6 +195,14 @@ QgsLayerTreeLayer* QgsLayerTreeLayer::readXML( QDomElement& element )

if ( layer )
nodeLayer = new QgsLayerTreeLayer( layer );
else if ( looseMatch && !source.isEmpty() )
{
LayerMatchParams params;
params.name = layerName;
params.source = source;
params.providerKey = providerKey;
nodeLayer = QgsLayerTreeLayer::createLayerFromParams( params );
}
else
nodeLayer = new QgsLayerTreeLayer( layerID, layerName );

Expand All @@ -144,6 +218,31 @@ void QgsLayerTreeLayer::writeXML( QDomElement& parentElement )
QDomDocument doc = parentElement.ownerDocument();
QDomElement elem = doc.createElement( "layer-tree-layer" );
elem.setAttribute( "id", mLayerId );
if ( mLayer )
{
elem.setAttribute( "source", mLayer->publicSource() );

QString providerKey;
switch ( mLayer->type() )
{
case QgsMapLayer::VectorLayer:
{
QgsVectorLayer* vl = qobject_cast< QgsVectorLayer* >( mLayer );
providerKey = vl->dataProvider()->name();
break;
}
case QgsMapLayer::RasterLayer:
{
QgsRasterLayer* rl = qobject_cast< QgsRasterLayer* >( mLayer );
providerKey = rl->dataProvider()->name();
break;
}
case QgsMapLayer::PluginLayer:
break;
}
elem.setAttribute( "providerKey", providerKey );
}

elem.setAttribute( "name", layerName() );
elem.setAttribute( "checked", QgsLayerTreeUtils::checkStateToXml( mVisible ) );
elem.setAttribute( "expanded", mExpanded ? "1" : "0" );
Expand All @@ -167,6 +266,12 @@ void QgsLayerTreeLayer::registryLayersAdded( const QList<QgsMapLayer*>& layers )
{
Q_FOREACH ( QgsMapLayer* l, layers )
{
if ( !mLooseMatchParams.source.isEmpty() && layerMatchesSource( l, mLooseMatchParams ) )
{
// we are loosely matching, and found a layer with a matching source.
// Attach to this!
mLayerId = l->id();
}
if ( l->id() == mLayerId )
{
disconnect( QgsMapLayerRegistry::instance(), SIGNAL( layersAdded( QList<QgsMapLayer*> ) ), this, SLOT( registryLayersAdded( QList<QgsMapLayer*> ) ) );
Expand Down

0 comments on commit 616868e

Please sign in to comment.