QGIS API Documentation  2.18.21-Las Palmas (9fba24a)
qgsmaplayerstylemanager.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsmaplayerstylemanager.cpp
3  --------------------------------------
4  Date : January 2015
5  Copyright : (C) 2015 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 
17 
18 #include "qgslogger.h"
19 #include "qgsmaplayer.h"
20 
21 #include <QDomElement>
22 #include <QTextStream>
23 
25  : mLayer( layer )
26  , mOverriddenOriginalStyle( nullptr )
27 {
28  reset();
29 }
30 
32 {
33  mStyles.insert( QString(), QgsMapLayerStyle() ); // insert entry for the default current style
34  mCurrentStyle.clear();
35 }
36 
38 {
39  mCurrentStyle = mgrElement.attribute( "current" );
40 
41  mStyles.clear();
42  QDomElement ch = mgrElement.firstChildElement( "map-layer-style" );
43  while ( !ch.isNull() )
44  {
45  QString name = ch.attribute( "name" );
47  style.readXml( ch );
48  mStyles.insert( name, style );
49 
50  ch = ch.nextSiblingElement( "map-layer-style" );
51  }
52 }
53 
55 {
56  QDomDocument doc = mgrElement.ownerDocument();
57  mgrElement.setAttribute( "current", mCurrentStyle );
58 
59  Q_FOREACH ( const QString& name, styles() )
60  {
61  QDomElement ch = doc.createElement( "map-layer-style" );
62  ch.setAttribute( "name", name );
63  mStyles[name].writeXml( ch );
64  mgrElement.appendChild( ch );
65  }
66 }
67 
69 {
70  return mStyles.keys();
71 }
72 
74 {
75  if ( name == mCurrentStyle )
76  {
77  // current style's entry is always kept invalid - get the style data from layer's properties
78  QgsMapLayerStyle curr;
79  curr.readFromLayer( mLayer );
80  return curr;
81  }
82 
83  return mStyles.value( name );
84 }
85 
87 {
88  if ( mStyles.contains( name ) )
89  return false;
90  if ( !style.isValid() )
91  return false;
92 
93  mStyles.insert( name, style );
94  emit styleAdded( name );
95  return true;
96 }
97 
99 {
101  style.readFromLayer( mLayer );
102  return addStyle( name, style );
103 }
104 
106 {
107  if ( !mStyles.contains( name ) )
108  return false;
109  if ( mStyles.count() == 1 )
110  return false; // cannot remove the last one
111 
112  // change to a different style if this one is the current
113  if ( mCurrentStyle == name )
114  {
115  QStringList keys = mStyles.keys();
116  QString newCurrent = keys[0];
117  if ( newCurrent == name )
118  newCurrent = keys[1]; // there must be at least one more
119  setCurrentStyle( newCurrent );
120  }
121 
122  mStyles.remove( name );
123  emit styleRemoved( name );
124  return true;
125 }
126 
128 {
129  if ( !mStyles.contains( name ) || mStyles.contains( newName ) )
130  return false;
131 
132  if ( name == mCurrentStyle )
133  mCurrentStyle = newName;
134 
135  mStyles[newName] = mStyles[name];
136  mStyles.remove( name );
137  emit styleRenamed( name, newName );
138  return true;
139 }
140 
142 {
143  return mCurrentStyle;
144 }
145 
147 {
148  if ( !mStyles.contains( name ) )
149  return false;
150 
151  if ( mCurrentStyle == name )
152  return true; // nothing to do
153 
154  mStyles[mCurrentStyle].readFromLayer( mLayer ); // sync before unloading it
155  mCurrentStyle = name;
156  mStyles[mCurrentStyle].writeToLayer( mLayer );
157  mStyles[mCurrentStyle].clear(); // current style does not keep any stored data
158  emit currentStyleChanged( mCurrentStyle );
159 
160  mLayer->triggerRepaint();
161  return true;
162 }
163 
165 {
166  if ( mOverriddenOriginalStyle )
167  return false; // cannot override the style more than once!
168 
169  mLayer->blockSignals( true );
170  if ( mStyles.contains( styleDef ) )
171  {
172  mOverriddenOriginalStyle = new QgsMapLayerStyle;
173  mOverriddenOriginalStyle->readFromLayer( mLayer );
174 
175  // apply style name
176  mStyles[styleDef].writeToLayer( mLayer );
177  }
178  else if ( styleDef.startsWith( '<' ) )
179  {
180  mOverriddenOriginalStyle = new QgsMapLayerStyle;
181  mOverriddenOriginalStyle->readFromLayer( mLayer );
182 
183  // apply style XML
184  QgsMapLayerStyle overrideStyle( styleDef );
185  overrideStyle.writeToLayer( mLayer );
186  }
187  mLayer->blockSignals( false );
188 
189  return true;
190 }
191 
193 {
194  if ( !mOverriddenOriginalStyle )
195  return false;
196 
197  mLayer->blockSignals( true );
198  mOverriddenOriginalStyle->writeToLayer( mLayer );
199  mLayer->blockSignals( false );
200 
201  delete mOverriddenOriginalStyle;
202  mOverriddenOriginalStyle = nullptr;
203  return true;
204 }
205 
206 
207 // -----
208 
210 {
211 }
212 
214  : mXmlData( xmlData )
215 {
216 }
217 
219 {
220  return !mXmlData.isEmpty();
221 }
222 
224 {
225  mXmlData.clear();
226 }
227 
229 {
230  return mXmlData;
231 }
232 
234 {
235  QString errorMsg;
236  QDomDocument doc;
237  layer->exportNamedStyle( doc, errorMsg );
238  if ( !errorMsg.isEmpty() )
239  {
240  QgsDebugMsg( "Failed to export style from layer: " + errorMsg );
241  return;
242  }
243 
244  mXmlData.clear();
245  QTextStream stream( &mXmlData );
246  doc.documentElement().save( stream, 0 );
247 }
248 
250 {
251  QDomDocument doc( "qgis" );
252  if ( !doc.setContent( mXmlData ) )
253  {
254  QgsDebugMsg( "Failed to parse XML of previously stored XML data - this should not happen!" );
255  return;
256  }
257 
258  QString errorMsg;
259  if ( !layer->importNamedStyle( doc, errorMsg ) )
260  {
261  QgsDebugMsg( "Failed to import style to layer: " + errorMsg );
262  }
263 }
264 
265 void QgsMapLayerStyle::readXml( const QDomElement& styleElement )
266 {
267  mXmlData.clear();
268  QTextStream stream( &mXmlData );
269  styleElement.firstChildElement().save( stream, 0 );
270 }
271 
272 void QgsMapLayerStyle::writeXml( QDomElement& styleElement ) const
273 {
274  // the currently selected style has no content stored here (layer has all the information inside)
275  if ( !isValid() )
276  return;
277 
278  QDomDocument docX;
279  docX.setContent( mXmlData );
280  styleElement.appendChild( docX.documentElement() );
281 }
bool restoreOverrideStyle()
Restore the original store after a call to setOverrideStyle()
void clear()
Remove any stored style data (will get invalid)
QgsMapLayerStyleManager(QgsMapLayer *layer)
Construct a style manager associated with a map layer (must not be null)
Base class for all map layer types.
Definition: qgsmaplayer.h:49
QStringList styles() const
Return list of all defined style names.
void currentStyleChanged(const QString &currentName)
Emitted when the current style has been changed.
void styleRenamed(const QString &oldName, const QString &newName)
Emitted when a style has been renamed.
bool contains(const Key &key) const
QDomNode appendChild(const QDomNode &newChild)
void reset()
Reset the style manager to a basic state - with one default style which is set as current...
QString xmlData() const
Return XML content of the style.
void readXml(const QDomElement &styleElement)
Read style configuration (for project file reading)
QString attribute(const QString &name, const QString &defValue) const
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
QDomElement nextSiblingElement(const QString &tagName) const
bool isValid() const
Tell whether the style is valid (i.e. there is something stored in it)
QDomElement documentElement() const
void clear()
void clear()
void styleAdded(const QString &name)
Emitted when a new style has been added.
void readFromLayer(QgsMapLayer *layer)
Store layer&#39;s active style information in the instance.
QList< Key > keys() const
Stores style information (renderer, transparency, labeling, diagrams etc.) applicable to a map layer...
const char * name() const
bool removeStyle(const QString &name)
Remove a stored style.
QDomDocument ownerDocument() const
void setAttribute(const QString &name, const QString &value)
void readXml(const QDomElement &mgrElement)
Read configuration (for project loading)
bool isEmpty() const
void styleRemoved(const QString &name)
Emitted when a style has been removed.
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const
void triggerRepaint()
Will advice the map canvas (and any other interested party) that this layer requires to be repainted...
QgsMapLayerStyle style(const QString &name) const
Return data of a stored style - accessed by its unique name.
virtual bool importNamedStyle(QDomDocument &doc, QString &errorMsg)
Import the properties of this layer from a QDomDocument.
void writeToLayer(QgsMapLayer *layer) const
Apply stored layer&#39;s style information to the layer.
bool addStyleFromLayer(const QString &name)
Add style by cloning the current one.
bool blockSignals(bool block)
QgsMapLayerStyle()
construct invalid style
bool isNull() const
QString currentStyle() const
Return name of the current style.
virtual void exportNamedStyle(QDomDocument &doc, QString &errorMsg)
Export the properties of this layer as named style in a QDomDocument.
bool renameStyle(const QString &name, const QString &newName)
Rename a stored style to a different name.
void save(QTextStream &str, int indent) const
void writeXml(QDomElement &styleElement) const
Write style configuration (for project file writing)
QDomElement firstChildElement(const QString &tagName) const
iterator insert(const Key &key, const T &value)
bool addStyle(const QString &name, const QgsMapLayerStyle &style)
Add a style with given name and data.
bool setOverrideStyle(const QString &styleDef)
Temporarily apply a different style to the layer.
QDomElement createElement(const QString &tagName)
void writeXml(QDomElement &mgrElement) const
Write configuration (for project saving)
int count(const Key &key) const
bool setCurrentStyle(const QString &name)
Set a different style as the current style - will apply it to the layer.
bool setContent(const QByteArray &data, bool namespaceProcessing, QString *errorMsg, int *errorLine, int *errorColumn)
const T value(const Key &key) const
int remove(const Key &key)