Skip to content

Commit 1fecde4

Browse files
committedMay 21, 2014
Initial import of legend refactoring work
1 parent e2c2575 commit 1fecde4

17 files changed

+2113
-3
lines changed
 

‎src/app/CMakeLists.txt

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -428,11 +428,21 @@ INCLUDE_DIRECTORIES(
428428
${QWT_INCLUDE_DIR}
429429
${QT_QTUITOOLS_INCLUDE_DIR}
430430
${QEXTSERIALPORT_INCLUDE_DIR}
431-
../analysis/raster ../analysis/openstreetmap
431+
../analysis/raster
432+
../analysis/openstreetmap
432433
../core
433434
../core/gps
434-
../core/composer ../core/dxf ../core/raster ../core/symbology-ng
435-
../gui ../gui/symbology-ng ../gui/attributetable ../gui/raster ../gui/editorwidgets ../gui/editorwidgets/core
435+
../core/composer
436+
../core/dxf
437+
../core/layertree
438+
../core/raster
439+
../core/symbology-ng
440+
../gui
441+
../gui/symbology-ng
442+
../gui/attributetable
443+
../gui/raster
444+
../gui/editorwidgets
445+
../gui/editorwidgets/core
436446
../plugins
437447
../python
438448
gps

‎src/core/CMakeLists.txt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ SET(QGIS_CORE_SRCS
5151
diagram/qgstextdiagram.cpp
5252
diagram/qgshistogramdiagram.cpp
5353

54+
layertree/qgslayertreenode.cpp
55+
layertree/qgslayertreeregistrybridge.cpp
56+
layertree/qgslayertreeutils.cpp
57+
5458
qgis.cpp
5559
qgsapplication.cpp
5660
qgsattributeaction.cpp
@@ -375,6 +379,9 @@ SET(QGIS_CORE_MOC_HDRS
375379
symbology-ng/qgscptcityarchive.h
376380
symbology-ng/qgssvgcache.h
377381
symbology-ng/qgsstylev2.h
382+
383+
layertree/qgslayertreenode.h
384+
layertree/qgslayertreeregistrybridge.h
378385
)
379386

380387
IF (WITH_INTERNAL_QEXTSERIALPORT)
@@ -582,6 +589,10 @@ SET(QGIS_CORE_HDRS
582589
symbology-ng/qgsvectorcolorrampv2.h
583590
symbology-ng/qgscptcityarchive.h
584591
symbology-ng/qgsvectorfieldsymbollayer.h
592+
593+
layertree/qgslayertreenode.h
594+
layertree/qgslayertreeregistrybridge.h
595+
layertree/qgslayertreeutils.h
585596
)
586597

587598
IF (QT_MOBILITY_LOCATION_FOUND)
@@ -595,6 +606,7 @@ INCLUDE_DIRECTORIES(
595606
${CMAKE_CURRENT_SOURCE_DIR}
596607
composer
597608
dxf
609+
layertree
598610
pal
599611
raster
600612
renderer

‎src/core/layertree/qgslayertreenode.cpp

Lines changed: 413 additions & 0 deletions
Large diffs are not rendered by default.

‎src/core/layertree/qgslayertreenode.h

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
#ifndef QGSLAYERNODE_H
2+
#define QGSLAYERNODE_H
3+
4+
#include <QObject>
5+
6+
#include "qgsmaplayer.h"
7+
8+
/**
9+
* One node of the layer tree - group or layer.
10+
*
11+
* Once added to the tree, nodes are owned by their parent.
12+
*/
13+
class CORE_EXPORT QgsLayerTreeNode : public QObject
14+
{
15+
Q_OBJECT
16+
public:
17+
enum NodeType
18+
{
19+
NodeGroup,
20+
NodeLayer
21+
};
22+
23+
QgsLayerTreeNode(NodeType t): mNodeType(t), mParent(0), mExpanded(true) {}
24+
~QgsLayerTreeNode() { qDeleteAll(mChildren); }
25+
26+
NodeType nodeType() { return mNodeType; }
27+
QgsLayerTreeNode* parent() { return mParent; }
28+
QList<QgsLayerTreeNode*> children() { return mChildren; }
29+
30+
static QgsLayerTreeNode* readXML(QDomElement& element);
31+
virtual void writeXML(QDomElement& parentElement) = 0;
32+
33+
bool isExpanded() const { return mExpanded; }
34+
void setExpanded(bool expanded) { mExpanded = expanded; }
35+
36+
/** Set a custom property for the node. Properties are stored in a map and saved in project file. */
37+
void setCustomProperty( const QString& key, const QVariant& value );
38+
/** Read a custom property from layer. Properties are stored in a map and saved in project file. */
39+
QVariant customProperty( const QString& key, const QVariant& defaultValue = QVariant() ) const;
40+
/** Remove a custom property from layer. Properties are stored in a map and saved in project file. */
41+
void removeCustomProperty( const QString& key );
42+
/** Return list of keys stored in custom properties */
43+
QStringList customProperties() const;
44+
45+
signals:
46+
47+
// low-level signals (mainly for the model)
48+
49+
void willAddChildren(int indexFrom, int indexTo);
50+
void addedChildren(int indexFrom, int indexTo);
51+
52+
void willRemoveChildren(int indexFrom, int indexTo);
53+
void removedChildren(int indexFrom, int indexTo);
54+
55+
void visibilityChanged(Qt::CheckState state);
56+
57+
protected:
58+
59+
// low-level utility functions
60+
61+
void readCommonXML(QDomElement& element);
62+
void writeCommonXML(QDomElement& element);
63+
64+
// the child must not be in any tree yet!
65+
void addChild(QgsLayerTreeNode* node);
66+
void insertChildren(int index, QList<QgsLayerTreeNode*> nodes);
67+
void insertChild(int index, QgsLayerTreeNode* node);
68+
void removeChildAt(int i);
69+
void removeChildrenRange(int from, int count);
70+
71+
72+
protected:
73+
NodeType mNodeType;
74+
QgsLayerTreeNode* mParent;
75+
QList<QgsLayerTreeNode*> mChildren;
76+
bool mExpanded;
77+
QgsObjectCustomProperties mProperties;
78+
};
79+
80+
/**
81+
* Layer Node
82+
*
83+
* It is expected that the layer is registered in QgsMapLayerRegistry.
84+
*
85+
* One layer is supposed to be present in one layer tree just once. It is possible that temporarily a layer
86+
* temporarily exists in one tree more than once, e.g. while reordering items.
87+
*
88+
* Can exist also without a valid instance of a layer (just ID),
89+
* so that referenced layer does not need to be loaded in order to use it in layer tree.
90+
*/
91+
class QgsLayerTreeLayer : public QgsLayerTreeNode
92+
{
93+
Q_OBJECT
94+
public:
95+
explicit QgsLayerTreeLayer(QgsMapLayer* layer);
96+
97+
explicit QgsLayerTreeLayer(QString layerId, QString name = QString());
98+
99+
QString layerId() const { return mLayerId; }
100+
101+
QgsMapLayer* layer() const { return mLayer; }
102+
103+
QString layerName() const { return mLayer ? mLayer->name() : mLayerName; }
104+
void setLayerName(const QString& n) { if (mLayer) mLayer->setLayerName(n); else mLayerName = n; }
105+
106+
bool isVisible() const { return mVisible; }
107+
void setVisible(bool state);
108+
109+
static QgsLayerTreeLayer* readXML(QDomElement& element);
110+
virtual void writeXML(QDomElement& parentElement);
111+
112+
protected slots:
113+
void registryLayersAdded(QList<QgsMapLayer*> layers);
114+
115+
protected:
116+
117+
QString mLayerId;
118+
QString mLayerName; // only used if layer does not exist
119+
QgsMapLayer* mLayer; // not owned! may be null
120+
bool mVisible;
121+
};
122+
123+
124+
class QgsLayerTreeGroup : public QgsLayerTreeNode
125+
{
126+
Q_OBJECT
127+
public:
128+
QgsLayerTreeGroup(const QString& name = QString(), Qt::CheckState checked = Qt::Checked);
129+
130+
QString name() const { return mName; }
131+
void setName(const QString& n) { mName = n; }
132+
133+
QgsLayerTreeGroup* addGroup(const QString& name);
134+
QgsLayerTreeLayer* addLayer(QgsMapLayer* layer);
135+
136+
void insertChildNodes(int index, QList<QgsLayerTreeNode*> nodes);
137+
void insertChildNode(int index, QgsLayerTreeNode* node);
138+
void addChildNode(QgsLayerTreeNode* node);
139+
140+
void removeChildNode(QgsLayerTreeNode* node);
141+
142+
void removeLayer(QgsMapLayer* layer);
143+
144+
void removeChildren(int from, int count);
145+
146+
QgsLayerTreeLayer* findLayer(const QString& layerId);
147+
148+
static QgsLayerTreeGroup* readXML(QDomElement& element);
149+
virtual void writeXML(QDomElement& parentElement);
150+
151+
Qt::CheckState isVisible() const { return mChecked; }
152+
void setVisible(Qt::CheckState state);
153+
154+
protected slots:
155+
void layerDestroyed();
156+
void updateVisibilityFromChildren();
157+
158+
protected:
159+
void connectToChildNode(QgsLayerTreeNode* node);
160+
161+
protected:
162+
QString mName;
163+
Qt::CheckState mChecked;
164+
165+
bool mChangingChildVisibility;
166+
};
167+
168+
169+
170+
#endif // QGSLAYERNODE_H
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
#include "qgslayertreeregistrybridge.h"
2+
3+
#include "qgsmaplayerregistry.h"
4+
5+
#include "qgslayertreenode.h"
6+
7+
QgsLayerTreeRegistryBridge::QgsLayerTreeRegistryBridge(QgsLayerTreeGroup *root, QObject *parent)
8+
: QObject(parent)
9+
, mRoot(root)
10+
{
11+
connect(QgsMapLayerRegistry::instance(), SIGNAL(layersAdded(QList<QgsMapLayer*>)), this, SLOT(layersAdded(QList<QgsMapLayer*>)));
12+
connect(QgsMapLayerRegistry::instance(), SIGNAL(layersWillBeRemoved(QStringList)), this, SLOT(layersWillBeRemoved(QStringList)));
13+
14+
connectToGroup(mRoot);
15+
}
16+
17+
void QgsLayerTreeRegistryBridge::layersAdded(QList<QgsMapLayer*> layers)
18+
{
19+
foreach (QgsMapLayer* layer, layers)
20+
{
21+
mRoot->addLayer(layer);
22+
}
23+
}
24+
25+
void QgsLayerTreeRegistryBridge::layersWillBeRemoved(QStringList layerIds)
26+
{
27+
foreach (QString layerId, layerIds)
28+
{
29+
QgsLayerTreeLayer* nodeLayer = mRoot->findLayer(layerId);
30+
if (nodeLayer)
31+
qobject_cast<QgsLayerTreeGroup*>(nodeLayer->parent())->removeChildNode(nodeLayer);
32+
}
33+
}
34+
35+
36+
void QgsLayerTreeRegistryBridge::groupAddedChildren(int indexFrom, int indexTo)
37+
{
38+
QgsLayerTreeGroup* group = qobject_cast<QgsLayerTreeGroup*>(sender());
39+
Q_ASSERT(group);
40+
41+
for (int i = indexFrom; i <= indexTo; ++i)
42+
{
43+
QgsLayerTreeNode* child = group->children()[i];
44+
if (child->nodeType() == QgsLayerTreeNode::NodeGroup)
45+
connectToGroup(static_cast<QgsLayerTreeGroup*>(child));
46+
}
47+
}
48+
49+
static void _collectLayerIdsInGroup(QgsLayerTreeGroup* group, int indexFrom, int indexTo, QStringList& lst)
50+
{
51+
for (int i = indexFrom; i <= indexTo; ++i)
52+
{
53+
QgsLayerTreeNode* child = group->children()[i];
54+
if (child->nodeType() == QgsLayerTreeNode::NodeLayer)
55+
{
56+
lst << static_cast<QgsLayerTreeLayer*>(child)->layerId();
57+
}
58+
else if (child->nodeType() == QgsLayerTreeNode::NodeGroup)
59+
{
60+
_collectLayerIdsInGroup(static_cast<QgsLayerTreeGroup*>(child), 0, child->children().count()-1, lst);
61+
}
62+
}
63+
}
64+
65+
void QgsLayerTreeRegistryBridge::groupWillRemoveChildren(int indexFrom, int indexTo)
66+
{
67+
Q_ASSERT(mLayerIdsForRemoval.isEmpty());
68+
69+
QgsLayerTreeGroup* group = qobject_cast<QgsLayerTreeGroup*>(sender());
70+
Q_ASSERT(group);
71+
72+
_collectLayerIdsInGroup(group, indexFrom, indexTo, mLayerIdsForRemoval);
73+
}
74+
75+
void QgsLayerTreeRegistryBridge::groupRemovedChildren()
76+
{
77+
// remove only those that really do not exist in the tree
78+
// (ignores layers that were dragged'n'dropped: 1. drop new 2. remove old)
79+
QStringList toRemove;
80+
foreach (QString layerId, mLayerIdsForRemoval)
81+
if (!mRoot->findLayer(layerId))
82+
toRemove << layerId;
83+
mLayerIdsForRemoval.clear();
84+
85+
QgsMapLayerRegistry::instance()->removeMapLayers(toRemove);
86+
}
87+
88+
void QgsLayerTreeRegistryBridge::connectToGroup(QgsLayerTreeGroup* group)
89+
{
90+
connect(group, SIGNAL(addedChildren(int,int)), this, SLOT(groupAddedChildren(int,int)));
91+
connect(group, SIGNAL(willRemoveChildren(int,int)), this, SLOT(groupWillRemoveChildren(int,int)));
92+
connect(group, SIGNAL(removedChildren(int,int)), this, SLOT(groupRemovedChildren()));
93+
94+
foreach (QgsLayerTreeNode* child, group->children())
95+
{
96+
if (child->nodeType() == QgsLayerTreeNode::NodeGroup)
97+
connectToGroup(static_cast<QgsLayerTreeGroup*>(child));
98+
}
99+
}
100+
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#ifndef QGSLAYERTREEREGISTRYBRIDGE_H
2+
#define QGSLAYERTREEREGISTRYBRIDGE_H
3+
4+
#include <QObject>
5+
#include <QStringList>
6+
7+
class QgsLayerTreeGroup;
8+
9+
class QgsMapLayer;
10+
11+
/**
12+
* Listens to the updates in map layer registry and does changes in layer tree
13+
*/
14+
class CORE_EXPORT QgsLayerTreeRegistryBridge : public QObject
15+
{
16+
Q_OBJECT
17+
public:
18+
explicit QgsLayerTreeRegistryBridge(QgsLayerTreeGroup* root, QObject *parent = 0);
19+
20+
signals:
21+
22+
protected slots:
23+
void layersAdded(QList<QgsMapLayer*> layers);
24+
void layersWillBeRemoved(QStringList layerIds);
25+
26+
void groupAddedChildren(int indexFrom, int indexTo);
27+
void groupWillRemoveChildren(int indexFrom, int indexTo);
28+
void groupRemovedChildren();
29+
30+
protected:
31+
void connectToGroup(QgsLayerTreeGroup* group);
32+
33+
protected:
34+
QgsLayerTreeGroup* mRoot;
35+
QStringList mLayerIdsForRemoval;
36+
};
37+
38+
#endif // QGSLAYERTREEREGISTRYBRIDGE_H
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
#include "qgslayertreeutils.h"
2+
3+
#include "qgslayertreenode.h"
4+
5+
#include <QDomElement>
6+
7+
QgsLayerTreeGroup* QgsLayerTreeUtils::readOldLegend(const QDomElement& legendElem)
8+
{
9+
QDomNodeList legendChildren = legendElem.childNodes();
10+
QgsLayerTreeGroup* root = new QgsLayerTreeGroup;
11+
12+
for ( int i = 0; i < legendChildren.size(); ++i )
13+
{
14+
QDomElement currentChildElem = legendChildren.at( i ).toElement();
15+
if ( currentChildElem.tagName() == "legendlayer" )
16+
{
17+
addLegendLayerToTreeWidget( currentChildElem, root );
18+
}
19+
else if ( currentChildElem.tagName() == "legendgroup" )
20+
{
21+
addLegendGroupToTreeWidget( currentChildElem, root );
22+
}
23+
}
24+
25+
return root;
26+
}
27+
28+
QString QgsLayerTreeUtils::checkStateToXml(Qt::CheckState state)
29+
{
30+
switch (state)
31+
{
32+
case Qt::Unchecked: return "Qt::Unchecked";
33+
case Qt::PartiallyChecked: return "Qt::PartiallyChecked";
34+
case Qt::Checked: default: return "Qt::Checked";
35+
}
36+
}
37+
38+
Qt::CheckState QgsLayerTreeUtils::checkStateFromXml(QString txt)
39+
{
40+
if (txt == "Qt::Unchecked")
41+
return Qt::Unchecked;
42+
else if (txt == "Qt::PartiallyChecked")
43+
return Qt::PartiallyChecked;
44+
else // "Qt::Checked"
45+
return Qt::Checked;
46+
}
47+
48+
49+
50+
void QgsLayerTreeUtils::addLegendGroupToTreeWidget( const QDomElement& groupElem, QgsLayerTreeGroup* parent )
51+
{
52+
QDomNodeList groupChildren = groupElem.childNodes();
53+
54+
// TODO: embedded
55+
//if ( !mShowEmbeddedContent && groupElem.attribute( "embedded" ) == "1" )
56+
// return;
57+
58+
QgsLayerTreeGroup* groupNode = new QgsLayerTreeGroup(groupElem.attribute( "name" ));
59+
parent->addChildNode(groupNode);
60+
61+
for ( int i = 0; i < groupChildren.size(); ++i )
62+
{
63+
QDomElement currentChildElem = groupChildren.at( i ).toElement();
64+
if ( currentChildElem.tagName() == "legendlayer" )
65+
{
66+
addLegendLayerToTreeWidget( currentChildElem, groupNode );
67+
}
68+
else if ( currentChildElem.tagName() == "legendgroup" )
69+
{
70+
addLegendGroupToTreeWidget( currentChildElem, groupNode );
71+
}
72+
}
73+
}
74+
75+
void QgsLayerTreeUtils::addLegendLayerToTreeWidget( const QDomElement& layerElem, QgsLayerTreeGroup* parent )
76+
{
77+
// TODO: embedded
78+
//if ( !mShowEmbeddedContent && layerElem.attribute( "embedded" ) == "1" )
79+
// return;
80+
81+
QString layerId = layerElem.firstChildElement( "filegroup" ).firstChildElement( "legendlayerfile" ).attribute( "layerid" );
82+
QgsLayerTreeLayer* layerNode = new QgsLayerTreeLayer(layerId, layerElem.attribute( "name" ) );
83+
parent->addChildNode(layerNode);
84+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#ifndef QGSLAYERTREEUTILS_H
2+
#define QGSLAYERTREEUTILS_H
3+
4+
#include <qnamespace.h>
5+
6+
class QDomElement;
7+
8+
class QgsLayerTreeGroup;
9+
10+
class CORE_EXPORT QgsLayerTreeUtils
11+
{
12+
public:
13+
14+
// return a new instance
15+
static QgsLayerTreeGroup* readOldLegend(const QDomElement& legendElem);
16+
17+
static QString checkStateToXml(Qt::CheckState state);
18+
static Qt::CheckState checkStateFromXml(QString txt);
19+
20+
21+
22+
protected:
23+
static void addLegendGroupToTreeWidget( const QDomElement& groupElem, QgsLayerTreeGroup* parent );
24+
static void addLegendLayerToTreeWidget( const QDomElement& layerElem, QgsLayerTreeGroup* parent );
25+
26+
};
27+
28+
#endif // QGSLAYERTREEUTILS_H

‎src/gui/CMakeLists.txt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,11 @@ editorwidgets/qgsrelationreferencewidget.cpp
5858
editorwidgets/qgsrelationreferencefactory.cpp
5959
editorwidgets/qgsrelreferenceconfigdlg.cpp
6060

61+
layertree/qgscustomlayerorderwidget.cpp
62+
layertree/qgslayertreemapcanvasbridge.cpp
63+
layertree/qgslayertreemodel.cpp
64+
layertree/qgslayertreeview.cpp
65+
6166
qgisgui.cpp
6267
qgisinterface.cpp
6368
qgsannotationitem.cpp
@@ -214,6 +219,11 @@ editorwidgets/core/qgseditorwidgetwrapper.h
214219
editorwidgets/qgsrelationreferencewidget.h
215220
editorwidgets/qgsrelreferenceconfigdlg.h
216221

222+
layertree/qgscustomlayerorderwidget.h
223+
layertree/qgslayertreemapcanvasbridge.h
224+
layertree/qgslayertreemodel.h
225+
layertree/qgslayertreeview.h
226+
217227
qgisinterface.h
218228
qgsattributedialog.h
219229
qgsattributeeditor.h
@@ -381,6 +391,11 @@ editorwidgets/qgsrelationreferencefactory.h
381391
editorwidgets/qgsrelationreferencewidget.h
382392
editorwidgets/qgsrelreferenceconfigdlg.h
383393

394+
layertree/qgscustomlayerorderwidget.h
395+
layertree/qgslayertreemapcanvasbridge.h
396+
layertree/qgslayertreemodel.h
397+
layertree/qgslayertreeview.h
398+
384399
raster/qgsrasterrendererwidget.h
385400
)
386401

@@ -426,8 +441,10 @@ INCLUDE_DIRECTORIES(
426441
${CMAKE_CURRENT_SOURCE_DIR}/attributetable
427442
${CMAKE_CURRENT_SOURCE_DIR}/editorwidgets
428443
${CMAKE_CURRENT_SOURCE_DIR}/editorwidgets/core
444+
${CMAKE_CURRENT_SOURCE_DIR}/layertree
429445
../core
430446
../core/composer
447+
../core/layertree
431448
../core/raster
432449
../core/symbology-ng
433450
${CMAKE_CURRENT_BINARY_DIR}
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
#include "qgscustomlayerorderwidget.h"
2+
3+
#include <QCheckBox>
4+
#include <QListView>
5+
#include <QMimeData>
6+
#include <QVBoxLayout>
7+
8+
#include "qgslayertreemapcanvasbridge.h"
9+
10+
#include "qgsmaplayer.h"
11+
#include "qgsmaplayerregistry.h"
12+
13+
14+
class CustomLayerOrderModel : public QAbstractListModel
15+
{
16+
public:
17+
CustomLayerOrderModel(QgsLayerTreeMapCanvasBridge* bridge, QObject* parent = 0)
18+
: QAbstractListModel(parent), mBridge(bridge)
19+
{
20+
}
21+
22+
int rowCount(const QModelIndex & ) const
23+
{
24+
return mOrder.count();
25+
}
26+
27+
QVariant data(const QModelIndex &index, int role) const
28+
{
29+
if (role == Qt::DisplayRole)
30+
{
31+
QString id = mOrder.at(index.row());
32+
QgsMapLayer* layer = QgsMapLayerRegistry::instance()->mapLayer(id);
33+
if (layer)
34+
return layer->name();
35+
}
36+
37+
if (role == Qt::UserRole+1)
38+
{
39+
QString id = mOrder.at(index.row());
40+
QgsMapLayer* layer = QgsMapLayerRegistry::instance()->mapLayer(id);
41+
if (layer)
42+
return layer->id();
43+
}
44+
45+
if (role == Qt::CheckStateRole)
46+
{
47+
// TODO: layer visibility
48+
return Qt::Checked;
49+
}
50+
51+
return QVariant();
52+
}
53+
54+
Qt::ItemFlags flags(const QModelIndex &index) const
55+
{
56+
if (!index.isValid())
57+
return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDropEnabled;
58+
return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled; // | Qt::ItemIsUserCheckable;
59+
}
60+
61+
Qt::DropActions supportedDropActions() const
62+
{
63+
return Qt::MoveAction;
64+
}
65+
66+
QStringList mimeTypes() const
67+
{
68+
QStringList types;
69+
types << "application/qgis.layerorderdata";
70+
return types;
71+
}
72+
73+
QMimeData* mimeData(const QModelIndexList& indexes) const
74+
{
75+
QStringList lst;
76+
foreach (QModelIndex index, indexes)
77+
lst << data(index, Qt::UserRole+1).toString();
78+
79+
QMimeData* mimeData = new QMimeData();
80+
mimeData->setData("application/qgis.layerorderdata", lst.join("\n").toUtf8());
81+
return mimeData;
82+
}
83+
84+
bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
85+
{
86+
Q_UNUSED(parent);
87+
Q_UNUSED(column);
88+
89+
if (action == Qt::IgnoreAction)
90+
return true;
91+
92+
if (!data->hasFormat("application/qgis.layerorderdata"))
93+
return false;
94+
95+
QByteArray encodedData = data->data("application/qgis.layerorderdata");
96+
QStringList lst = QString::fromUtf8(encodedData).split("\n");
97+
98+
if (row < 0)
99+
row = mOrder.count();
100+
101+
beginInsertRows(QModelIndex(), row, row+lst.count()-1);
102+
for (int i = 0; i < lst.count(); ++i)
103+
mOrder.insert(row+i, lst[i]);
104+
endInsertRows();
105+
106+
return true;
107+
}
108+
109+
bool removeRows(int row, int count, const QModelIndex& parent)
110+
{
111+
Q_UNUSED(parent);
112+
if (count <= 0)
113+
return false;
114+
115+
beginRemoveRows(QModelIndex(), row, row+count-1);
116+
while (--count >= 0)
117+
mOrder.removeAt(row);
118+
endRemoveRows();
119+
return true;
120+
}
121+
122+
void refreshModel(const QStringList& order)
123+
{
124+
beginResetModel();
125+
mOrder = order;
126+
endResetModel();
127+
}
128+
129+
QStringList order() const { return mOrder; }
130+
131+
protected:
132+
QgsLayerTreeMapCanvasBridge* mBridge;
133+
QStringList mOrder;
134+
};
135+
136+
137+
QgsCustomLayerOrderWidget::QgsCustomLayerOrderWidget(QgsLayerTreeMapCanvasBridge* bridge, QWidget* parent)
138+
: QWidget(parent)
139+
, mBridge(bridge)
140+
{
141+
mModel = new CustomLayerOrderModel(bridge, this);
142+
143+
mView = new QListView(this);
144+
mView->setDragEnabled(true);
145+
mView->setAcceptDrops(true);
146+
mView->setDropIndicatorShown(true);
147+
mView->setDragDropMode(QAbstractItemView::InternalMove);
148+
mView->setSelectionMode(QAbstractItemView::ExtendedSelection);
149+
150+
mView->setModel(mModel);
151+
152+
mChkOverride = new QCheckBox( tr( "Control rendering order" ) );
153+
bridgeHasCustomLayerOrderChanged( bridge->hasCustomLayerOrder() );
154+
connect( mChkOverride, SIGNAL( toggled( bool ) ), bridge, SLOT( setHasCustomLayerOrder(bool) ) );
155+
156+
connect(bridge, SIGNAL(hasCustomLayerOrderChanged(bool)), this, SLOT(bridgeHasCustomLayerOrderChanged(bool)));
157+
connect(bridge, SIGNAL(customLayerOrderChanged(QStringList)), this, SLOT(bridgeCustomLayerOrderChanged(QStringList)));
158+
159+
connect(mModel, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(modelUpdated()));
160+
connect(mModel, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SLOT(modelUpdated()));
161+
162+
QVBoxLayout* l = new QVBoxLayout;
163+
l->setMargin( 0 );
164+
l->addWidget( mView );
165+
l->addWidget( mChkOverride );
166+
setLayout( l );
167+
}
168+
169+
void QgsCustomLayerOrderWidget::bridgeHasCustomLayerOrderChanged(bool override)
170+
{
171+
mChkOverride->setChecked(override);
172+
mModel->refreshModel( mBridge->hasCustomLayerOrder() ? mBridge->customLayerOrder() : mBridge->defaultLayerOrder() );
173+
mView->setEnabled(override);
174+
}
175+
176+
void QgsCustomLayerOrderWidget::bridgeCustomLayerOrderChanged(const QStringList& order)
177+
{
178+
Q_UNUSED(order);
179+
mModel->refreshModel(mBridge->hasCustomLayerOrder() ? mBridge->customLayerOrder() : mBridge->defaultLayerOrder());
180+
}
181+
182+
void QgsCustomLayerOrderWidget::modelUpdated()
183+
{
184+
mBridge->setCustomLayerOrder(mModel->order());
185+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#ifndef QGSCUSTOMLAYERORDERWIDGET_H
2+
#define QGSCUSTOMLAYERORDERWIDGET_H
3+
4+
#include <QWidget>
5+
6+
class CustomLayerOrderModel;
7+
class QgsLayerTreeMapCanvasBridge;
8+
9+
class QCheckBox;
10+
class QListView;
11+
12+
class GUI_EXPORT QgsCustomLayerOrderWidget : public QWidget
13+
{
14+
Q_OBJECT
15+
public:
16+
explicit QgsCustomLayerOrderWidget(QgsLayerTreeMapCanvasBridge* bridge, QWidget *parent = 0);
17+
18+
signals:
19+
20+
protected slots:
21+
void bridgeHasCustomLayerOrderChanged(bool override);
22+
void bridgeCustomLayerOrderChanged(const QStringList& order);
23+
24+
void modelUpdated();
25+
26+
protected:
27+
QgsLayerTreeMapCanvasBridge* mBridge;
28+
29+
QCheckBox* mChkOverride;
30+
CustomLayerOrderModel* mModel;
31+
QListView* mView;
32+
};
33+
34+
#endif // QGSCUSTOMLAYERORDERWIDGET_H
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
#include "qgslayertreemapcanvasbridge.h"
2+
3+
#include "qgslayertreenode.h"
4+
5+
#include "qgsmapcanvas.h"
6+
7+
QgsLayerTreeMapCanvasBridge::QgsLayerTreeMapCanvasBridge(QgsLayerTreeGroup *root, QgsMapCanvas *canvas)
8+
: mRoot(root)
9+
, mCanvas(canvas)
10+
, mPendingCanvasUpdate(false)
11+
, mHasCustomLayerOrder(false)
12+
{
13+
connectToNode(root);
14+
15+
setCanvasLayers();
16+
}
17+
18+
QStringList QgsLayerTreeMapCanvasBridge::defaultLayerOrder() const
19+
{
20+
QStringList order;
21+
defaultLayerOrder(mRoot, order);
22+
return order;
23+
}
24+
25+
void QgsLayerTreeMapCanvasBridge::defaultLayerOrder(QgsLayerTreeNode* node, QStringList& order) const
26+
{
27+
if (node->nodeType() == QgsLayerTreeNode::NodeLayer)
28+
{
29+
QgsLayerTreeLayer* nodeLayer = static_cast<QgsLayerTreeLayer*>(node);
30+
order << nodeLayer->layerId();
31+
}
32+
33+
foreach (QgsLayerTreeNode* child, node->children())
34+
defaultLayerOrder(child, order);
35+
}
36+
37+
38+
void QgsLayerTreeMapCanvasBridge::setHasCustomLayerOrder(bool override)
39+
{
40+
if (mHasCustomLayerOrder == override)
41+
return;
42+
43+
mHasCustomLayerOrder = override;
44+
emit hasCustomLayerOrderChanged(mHasCustomLayerOrder);
45+
46+
deferredSetCanvasLayers();
47+
}
48+
49+
void QgsLayerTreeMapCanvasBridge::setCustomLayerOrder(const QStringList& order)
50+
{
51+
if (mCustomLayerOrder == order)
52+
return;
53+
54+
// verify that the new order is correct
55+
QStringList defOrder = defaultLayerOrder();
56+
QStringList sortedNewOrder = order;
57+
qSort(defOrder);
58+
qSort(sortedNewOrder);
59+
if (defOrder != sortedNewOrder)
60+
return; // must be permutation of the default order
61+
62+
mCustomLayerOrder = order;
63+
emit customLayerOrderChanged(mCustomLayerOrder);
64+
65+
if (mHasCustomLayerOrder)
66+
deferredSetCanvasLayers();
67+
}
68+
69+
void QgsLayerTreeMapCanvasBridge::connectToNode(QgsLayerTreeNode *node)
70+
{
71+
connect(node, SIGNAL(addedChildren(int,int)), this, SLOT(nodeAddedChildren(int,int)));
72+
connect(node, SIGNAL(removedChildren(int,int)), this, SLOT(nodeRemovedChildren()));
73+
connect(node, SIGNAL(visibilityChanged(Qt::CheckState)), this, SLOT(nodeVisibilityChanged()));
74+
75+
if (node->nodeType() == QgsLayerTreeNode::NodeLayer)
76+
{
77+
QString layerId = static_cast<QgsLayerTreeLayer*>(node)->layerId();
78+
if (!mCustomLayerOrder.contains(layerId))
79+
mCustomLayerOrder.append(layerId);
80+
}
81+
82+
foreach (QgsLayerTreeNode* child, node->children())
83+
connectToNode(child);
84+
}
85+
86+
void QgsLayerTreeMapCanvasBridge::setCanvasLayers()
87+
{
88+
QList<QgsMapCanvasLayer> layers;
89+
90+
if (mHasCustomLayerOrder)
91+
{
92+
foreach (QString layerId, mCustomLayerOrder)
93+
{
94+
QgsLayerTreeLayer* nodeLayer = mRoot->findLayer(layerId);
95+
if (nodeLayer)
96+
layers << QgsMapCanvasLayer(nodeLayer->layer(), nodeLayer->isVisible(), nodeLayer->customProperty("overview", 0).toInt());
97+
}
98+
}
99+
else
100+
setCanvasLayers(mRoot, layers);
101+
102+
mCanvas->setLayerSet(layers);
103+
104+
mPendingCanvasUpdate = false;
105+
}
106+
107+
void QgsLayerTreeMapCanvasBridge::setCanvasLayers(QgsLayerTreeNode *node, QList<QgsMapCanvasLayer> &layers)
108+
{
109+
if (node->nodeType() == QgsLayerTreeNode::NodeLayer)
110+
{
111+
QgsLayerTreeLayer* nodeLayer = static_cast<QgsLayerTreeLayer*>(node);
112+
layers << QgsMapCanvasLayer(nodeLayer->layer(), nodeLayer->isVisible(), nodeLayer->customProperty("overview", 0).toInt());
113+
}
114+
115+
foreach (QgsLayerTreeNode* child, node->children())
116+
setCanvasLayers(child, layers);
117+
}
118+
119+
void QgsLayerTreeMapCanvasBridge::deferredSetCanvasLayers()
120+
{
121+
if (mPendingCanvasUpdate)
122+
return;
123+
124+
mPendingCanvasUpdate = true;
125+
QMetaObject::invokeMethod(this, "setCanvasLayers", Qt::QueuedConnection);
126+
}
127+
128+
void QgsLayerTreeMapCanvasBridge::nodeAddedChildren(int indexFrom, int indexTo)
129+
{
130+
// connect to new children
131+
Q_ASSERT(sender() && qobject_cast<QgsLayerTreeNode*>(sender()));
132+
QgsLayerTreeNode* node = qobject_cast<QgsLayerTreeNode*>(sender());
133+
for (int i = indexFrom; i <= indexTo; ++i)
134+
connectToNode(node->children()[i]);
135+
136+
emit customLayerOrderChanged(mCustomLayerOrder);
137+
138+
deferredSetCanvasLayers();
139+
}
140+
141+
void QgsLayerTreeMapCanvasBridge::nodeRemovedChildren()
142+
{
143+
// no need to disconnect from removed nodes as they are deleted
144+
145+
// check whether the layers are still there, if not, remove them from the layer order!
146+
QList<int> toRemove;
147+
for (int i = 0; i < mCustomLayerOrder.count(); ++i)
148+
{
149+
QgsLayerTreeLayer* node = mRoot->findLayer(mCustomLayerOrder[i]);
150+
if (!node)
151+
toRemove << i;
152+
}
153+
for (int i = toRemove.count()-1; i >= 0; --i)
154+
mCustomLayerOrder.removeAt(toRemove[i]);
155+
emit customLayerOrderChanged(mCustomLayerOrder);
156+
157+
deferredSetCanvasLayers();
158+
}
159+
160+
void QgsLayerTreeMapCanvasBridge::nodeVisibilityChanged()
161+
{
162+
deferredSetCanvasLayers();
163+
}
164+
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
#ifndef QGSLAYERTREEMAPCANVASBRIDGE_H
2+
#define QGSLAYERTREEMAPCANVASBRIDGE_H
3+
4+
#include <QObject>
5+
#include <QStringList>
6+
7+
class QgsMapCanvas;
8+
class QgsMapCanvasLayer;
9+
class QgsLayerTreeGroup;
10+
class QgsLayerTreeNode;
11+
12+
class GUI_EXPORT QgsLayerTreeMapCanvasBridge : public QObject
13+
{
14+
Q_OBJECT
15+
public:
16+
QgsLayerTreeMapCanvasBridge(QgsLayerTreeGroup* root, QgsMapCanvas* canvas);
17+
18+
bool hasCustomLayerOrder() const { return mHasCustomLayerOrder; }
19+
QStringList customLayerOrder() const { return mCustomLayerOrder; }
20+
21+
QStringList defaultLayerOrder() const;
22+
23+
public slots:
24+
void setHasCustomLayerOrder(bool override);
25+
void setCustomLayerOrder(const QStringList& order);
26+
27+
signals:
28+
void hasCustomLayerOrderChanged(bool);
29+
void customLayerOrderChanged(const QStringList& order);
30+
31+
protected:
32+
void connectToNode(QgsLayerTreeNode* node);
33+
34+
// TODO: disconnectFromNode
35+
36+
void defaultLayerOrder(QgsLayerTreeNode* node, QStringList& order) const;
37+
38+
void setCanvasLayers(QgsLayerTreeNode* node, QList<QgsMapCanvasLayer>& layers);
39+
40+
void deferredSetCanvasLayers();
41+
42+
protected slots:
43+
void nodeAddedChildren(int indexFrom, int indexTo);
44+
void nodeRemovedChildren();
45+
void nodeVisibilityChanged();
46+
47+
void setCanvasLayers();
48+
49+
protected:
50+
QgsLayerTreeGroup* mRoot;
51+
QgsMapCanvas* mCanvas;
52+
53+
bool mPendingCanvasUpdate;
54+
55+
bool mHasCustomLayerOrder;
56+
QStringList mCustomLayerOrder;
57+
};
58+
59+
#endif // QGSLAYERTREEMAPCANVASBRIDGE_H

‎src/gui/layertree/qgslayertreemodel.cpp

Lines changed: 499 additions & 0 deletions
Large diffs are not rendered by default.

‎src/gui/layertree/qgslayertreemodel.h

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
#ifndef QGSLAYERTREEMODEL_H
2+
#define QGSLAYERTREEMODEL_H
3+
4+
#include <QAbstractItemModel>
5+
#include <QIcon>
6+
7+
class QgsLayerTreeNode;
8+
class QgsLayerTreeGroup;
9+
class QgsLayerTreeLayer;
10+
11+
/** internal class, not in public API */
12+
class QgsLayerTreeModelSymbologyNode : public QObject
13+
{
14+
Q_OBJECT
15+
public:
16+
QgsLayerTreeModelSymbologyNode(QgsLayerTreeLayer* parent, const QString& name, const QIcon& icon = QIcon())
17+
: mParent(parent), mName(name), mIcon(icon) {}
18+
19+
QgsLayerTreeLayer* parent() const { return mParent; }
20+
QString name() const { return mName; }
21+
QIcon icon() const { return mIcon; }
22+
23+
// TODO: ref to renderer
24+
25+
protected:
26+
QgsLayerTreeLayer* mParent;
27+
QString mName;
28+
QIcon mIcon;
29+
};
30+
31+
32+
33+
class GUI_EXPORT QgsLayerTreeModel : public QAbstractItemModel
34+
{
35+
Q_OBJECT
36+
public:
37+
explicit QgsLayerTreeModel(QgsLayerTreeGroup* rootNode, QObject *parent = 0);
38+
~QgsLayerTreeModel();
39+
40+
int rowCount(const QModelIndex &parent = QModelIndex()) const;
41+
int columnCount(const QModelIndex &parent = QModelIndex()) const;
42+
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;
43+
QModelIndex parent(const QModelIndex &child) const;
44+
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
45+
Qt::ItemFlags flags(const QModelIndex &index) const;
46+
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
47+
Qt::DropActions supportedDropActions() const;
48+
QStringList mimeTypes() const;
49+
QMimeData* mimeData(const QModelIndexList& indexes) const;
50+
bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent);
51+
52+
bool removeRows(int row, int count, const QModelIndex& parent = QModelIndex());
53+
54+
enum Flag
55+
{
56+
AllowTreeManagement,
57+
ShowSymbology,
58+
ShowFeatureCounts, // TODO: this is per-layer
59+
};
60+
Q_DECLARE_FLAGS(Flags, Flag)
61+
62+
void setFlags(Flags f) { mFlags = f; }
63+
Flags flags() const { return mFlags; }
64+
bool testFlag(Flag f) const { return mFlags.testFlag(f); }
65+
66+
// conversion functions used by views
67+
68+
QgsLayerTreeNode* index2node(const QModelIndex& index) const;
69+
static QgsLayerTreeModelSymbologyNode* index2symnode(const QModelIndex& index);
70+
QModelIndex node2index(QgsLayerTreeNode* node);
71+
72+
QList<QgsLayerTreeNode*> indexes2nodes(const QModelIndexList& list, bool skipInternal = false) const;
73+
74+
QgsLayerTreeGroup* rootGroup() { return mRootNode; }
75+
76+
signals:
77+
78+
protected slots:
79+
void nodeWillAddChildren(int indexFrom, int indexTo);
80+
void nodeAddedChildren(int indexFrom, int indexTo);
81+
void nodeWillRemoveChildren(int indexFrom, int indexTo);
82+
void nodeRemovedChildren();
83+
84+
void nodeVisibilityChanded();
85+
86+
protected:
87+
void connectToNode(QgsLayerTreeNode* node);
88+
void removeSymbologyFromSubtree(QgsLayerTreeNode* node);
89+
void addSymbologyToLayer(QgsLayerTreeLayer* nodeL) const;
90+
91+
protected:
92+
QgsLayerTreeGroup* mRootNode; // not owned!
93+
Flags mFlags;
94+
95+
mutable QMap<QgsLayerTreeLayer*, QList<QgsLayerTreeModelSymbologyNode*> > mSymbologyNodes;
96+
};
97+
98+
Q_DECLARE_OPERATORS_FOR_FLAGS(QgsLayerTreeModel::Flags)
99+
100+
#endif // QGSLAYERTREEMODEL_H
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
#include "qgslayertreeview.h"
2+
3+
#include "qgslayertreemodel.h"
4+
#include "qgslayertreenode.h"
5+
6+
#include <QMenu>
7+
#include <QContextMenuEvent>
8+
9+
QgsLayerTreeView::QgsLayerTreeView(QWidget *parent) :
10+
QTreeView(parent)
11+
{
12+
setHeaderHidden(true);
13+
14+
setDragEnabled(true);
15+
setAcceptDrops(true);
16+
setDropIndicatorShown(true);
17+
18+
setSelectionMode(ExtendedSelection);
19+
20+
connect(this, SIGNAL(collapsed(QModelIndex)), this, SLOT(updateExpandedStateToNode(QModelIndex)));
21+
connect(this, SIGNAL(expanded(QModelIndex)), this, SLOT(updateExpandedStateToNode(QModelIndex)));
22+
}
23+
24+
void QgsLayerTreeView::setModel(QAbstractItemModel* model)
25+
{
26+
if (!qobject_cast<QgsLayerTreeModel*>(model))
27+
return;
28+
29+
connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(modelRowsInserted(QModelIndex,int,int)));
30+
31+
QTreeView::setModel(model);
32+
33+
updateExpandedStateFromNode(layerTreeModel()->rootGroup());
34+
}
35+
36+
QgsLayerTreeModel *QgsLayerTreeView::layerTreeModel()
37+
{
38+
return qobject_cast<QgsLayerTreeModel*>(model());
39+
}
40+
41+
42+
void QgsLayerTreeView::contextMenuEvent(QContextMenuEvent *event)
43+
{
44+
QMenu menu;
45+
46+
QModelIndex idx = indexAt(event->pos());
47+
if (!idx.isValid())
48+
{
49+
menu.addAction(create_addGroup(&menu));
50+
}
51+
else
52+
{
53+
QgsLayerTreeNode* node = layerTreeModel()->index2node(idx);
54+
if (!node)
55+
return; // probably a symbology item
56+
57+
if (node->nodeType() == QgsLayerTreeNode::NodeGroup)
58+
menu.addAction(create_addGroup(&menu, node));
59+
else if (node->nodeType() == QgsLayerTreeNode::NodeLayer)
60+
menu.addAction(create_showInOverview(&menu, node)); // TODO: should be custom action
61+
62+
menu.addAction(create_removeGroupOrLayer(&menu, node));
63+
}
64+
65+
menu.exec(mapToGlobal(event->pos()));
66+
}
67+
68+
QAction *QgsLayerTreeView::create_addGroup(QObject* parent, QgsLayerTreeNode* parentNode)
69+
{
70+
QAction* a = new QAction(tr("Add Group"), parent);
71+
connect(a, SIGNAL(triggered()), this, SLOT(addGroup()));
72+
a->setData(QVariant::fromValue((QObject*)parentNode));
73+
return a;
74+
}
75+
76+
QAction *QgsLayerTreeView::create_removeGroupOrLayer(QObject *parent, QgsLayerTreeNode *parentNode)
77+
{
78+
QAction* a = new QAction(tr("Remove"), parent);
79+
connect(a, SIGNAL(triggered()), this, SLOT(removeGroupOrLayer()));
80+
a->setData(QVariant::fromValue((QObject*)parentNode));
81+
return a;
82+
}
83+
84+
QAction* QgsLayerTreeView::create_showInOverview(QObject* parent, QgsLayerTreeNode* parentNode)
85+
{
86+
QAction* a = new QAction(tr("Show in overview"), parent);
87+
connect(a, SIGNAL(triggered()), this, SLOT(showInOverview()));
88+
a->setData(QVariant::fromValue((QObject*)parentNode));
89+
a->setCheckable(true);
90+
a->setChecked(parentNode->customProperty("overview", 0).toInt());
91+
return a;
92+
}
93+
94+
void QgsLayerTreeView::addGroup()
95+
{
96+
QVariant v = qobject_cast<QAction*>(sender())->data();
97+
QgsLayerTreeGroup* group = qobject_cast<QgsLayerTreeGroup*>(v.value<QObject*>());
98+
if (!group)
99+
group = layerTreeModel()->rootGroup();
100+
101+
group->addGroup("group");
102+
}
103+
104+
void QgsLayerTreeView::removeGroupOrLayer()
105+
{
106+
QList<QgsLayerTreeNode*> nodes = layerTreeModel()->indexes2nodes(selectionModel()->selectedIndexes(), true);
107+
foreach (QgsLayerTreeNode* node, nodes)
108+
{
109+
// could be more efficient if working directly with ranges instead of individual nodes
110+
qobject_cast<QgsLayerTreeGroup*>(node->parent())->removeChildNode(node);
111+
}
112+
}
113+
114+
void QgsLayerTreeView::showInOverview()
115+
{
116+
QVariant v = qobject_cast<QAction*>(sender())->data();
117+
QgsLayerTreeNode* node = qobject_cast<QgsLayerTreeNode*>(v.value<QObject*>());
118+
Q_ASSERT(node);
119+
120+
node->setCustomProperty("overview", node->customProperty("overview", 0).toInt() ? 0 : 1);
121+
}
122+
123+
void QgsLayerTreeView::modelRowsInserted(QModelIndex index, int start, int end)
124+
{
125+
QgsLayerTreeNode* parentNode = layerTreeModel()->index2node(index);
126+
if (!parentNode)
127+
return;
128+
129+
if (parentNode->nodeType() == QgsLayerTreeNode::NodeLayer)
130+
return; // layers have only symbology nodes (no expanded/collapsed handling)
131+
132+
for (int i = start; i <= end; ++i)
133+
{
134+
updateExpandedStateFromNode(parentNode->children()[i]);
135+
}
136+
}
137+
138+
void QgsLayerTreeView::updateExpandedStateToNode(QModelIndex index)
139+
{
140+
QgsLayerTreeNode* node = layerTreeModel()->index2node(index);
141+
if (!node)
142+
return;
143+
144+
node->setExpanded(isExpanded(index));
145+
}
146+
147+
void QgsLayerTreeView::updateExpandedStateFromNode(QgsLayerTreeNode* node)
148+
{
149+
QModelIndex idx = layerTreeModel()->node2index(node);
150+
setExpanded(idx, node->isExpanded());
151+
152+
foreach (QgsLayerTreeNode* child, node->children())
153+
updateExpandedStateFromNode(child);
154+
}

‎src/gui/layertree/qgslayertreeview.h

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#ifndef QGSLAYERTREEVIEW_H
2+
#define QGSLAYERTREEVIEW_H
3+
4+
#include <QTreeView>
5+
6+
class QgsLayerTreeModel;
7+
class QgsLayerTreeNode;
8+
9+
class GUI_EXPORT QgsLayerTreeView : public QTreeView
10+
{
11+
Q_OBJECT
12+
public:
13+
explicit QgsLayerTreeView(QWidget *parent = 0);
14+
15+
virtual void setModel(QAbstractItemModel* model);
16+
17+
QgsLayerTreeModel* layerTreeModel();
18+
19+
protected:
20+
void contextMenuEvent(QContextMenuEvent* event);
21+
22+
QAction* create_addGroup(QObject* parent, QgsLayerTreeNode* parentNode = 0);
23+
QAction* create_removeGroupOrLayer(QObject* parent, QgsLayerTreeNode* parentNode);
24+
QAction* create_showInOverview(QObject* parent, QgsLayerTreeNode* parentNode);
25+
26+
void updateExpandedStateFromNode(QgsLayerTreeNode* node);
27+
28+
signals:
29+
30+
public slots:
31+
32+
protected slots:
33+
void addGroup();
34+
void removeGroupOrLayer();
35+
void showInOverview();
36+
37+
void modelRowsInserted(QModelIndex index, int start, int end);
38+
39+
void updateExpandedStateToNode(QModelIndex index);
40+
41+
};
42+
43+
#endif // QGSLAYERTREEVIEW_H

0 commit comments

Comments
 (0)
Please sign in to comment.