QGIS API Documentation  3.2.0-Bonn (bc43194)
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 
20 #include <QDomElement>
21 #include <QTextStream>
22 
24  : mLayer( layer )
25 
26 {
27  reset();
28 }
29 
30 QString QgsMapLayerStyleManager::defaultStyleName() const
31 {
32  return tr( "default" );
33 }
34 
35 
37 {
38  mStyles.insert( defaultStyleName(), QgsMapLayerStyle() ); // insert entry for the default current style
39  mCurrentStyle = defaultStyleName();
40 }
41 
42 void QgsMapLayerStyleManager::readXml( const QDomElement &mgrElement )
43 {
44  mCurrentStyle = mgrElement.attribute( QStringLiteral( "current" ) );
45  if ( mCurrentStyle.isEmpty() )
46  {
47  // For old project made with QGIS 2, we migrate to "default".
48  mCurrentStyle = defaultStyleName();
49  }
50 
51  mStyles.clear();
52  QDomElement ch = mgrElement.firstChildElement( QStringLiteral( "map-layer-style" ) );
53  while ( !ch.isNull() )
54  {
55  QString name = ch.attribute( QStringLiteral( "name" ) );
56  if ( name.isEmpty() )
57  {
58  // For old project made with QGIS 2, we migrate to "default".
59  name = defaultStyleName();
60  }
62  style.readXml( ch );
63  mStyles.insert( name, style );
64 
65  ch = ch.nextSiblingElement( QStringLiteral( "map-layer-style" ) );
66  }
67 }
68 
69 void QgsMapLayerStyleManager::writeXml( QDomElement &mgrElement ) const
70 {
71  QDomDocument doc = mgrElement.ownerDocument();
72  mgrElement.setAttribute( QStringLiteral( "current" ), mCurrentStyle );
73 
74  Q_FOREACH ( const QString &name, styles() )
75  {
76  QDomElement ch = doc.createElement( QStringLiteral( "map-layer-style" ) );
77  ch.setAttribute( QStringLiteral( "name" ), name );
78  mStyles[name].writeXml( ch );
79  mgrElement.appendChild( ch );
80  }
81 }
82 
84 {
85  return mStyles.keys();
86 }
87 
88 QMap<QString, QgsMapLayerStyle> QgsMapLayerStyleManager::mapLayerStyles() const
89 {
90  return mStyles;
91 }
92 
94 {
95  if ( name == mCurrentStyle )
96  {
97  // current style's entry is always kept invalid - get the style data from layer's properties
98  QgsMapLayerStyle curr;
99  curr.readFromLayer( mLayer );
100  return curr;
101  }
102 
103  return mStyles.value( name );
104 }
105 
106 bool QgsMapLayerStyleManager::addStyle( const QString &name, const QgsMapLayerStyle &style )
107 {
108  if ( mStyles.contains( name ) )
109  return false;
110  if ( !style.isValid() )
111  return false;
112 
113  mStyles.insert( name, style );
114  emit styleAdded( name );
115  return true;
116 }
117 
119 {
121  style.readFromLayer( mLayer );
122  return addStyle( name, style );
123 }
124 
125 bool QgsMapLayerStyleManager::removeStyle( const QString &name )
126 {
127  if ( !mStyles.contains( name ) )
128  return false;
129  if ( mStyles.count() == 1 )
130  return false; // cannot remove the last one
131 
132  // change to a different style if this one is the current
133  if ( mCurrentStyle == name )
134  {
135  QStringList keys = mStyles.keys();
136  QString newCurrent = keys[0];
137  if ( newCurrent == name )
138  newCurrent = keys[1]; // there must be at least one more
139  setCurrentStyle( newCurrent );
140  }
141 
142  mStyles.remove( name );
143  emit styleRemoved( name );
144  return true;
145 }
146 
147 bool QgsMapLayerStyleManager::renameStyle( const QString &name, const QString &newName )
148 {
149  if ( !mStyles.contains( name ) || mStyles.contains( newName ) )
150  return false;
151 
152  if ( name == mCurrentStyle )
153  mCurrentStyle = newName;
154 
155  mStyles[newName] = mStyles[name];
156  mStyles.remove( name );
157  emit styleRenamed( name, newName );
158  return true;
159 }
160 
162 {
163  return mCurrentStyle;
164 }
165 
166 bool QgsMapLayerStyleManager::setCurrentStyle( const QString &name )
167 {
168  if ( !mStyles.contains( name ) )
169  return false;
170 
171  if ( mCurrentStyle == name )
172  return true; // nothing to do
173 
174  mStyles[mCurrentStyle].readFromLayer( mLayer ); // sync before unloading it
175  mCurrentStyle = name;
176  mStyles[mCurrentStyle].writeToLayer( mLayer );
177  mStyles[mCurrentStyle].clear(); // current style does not keep any stored data
178  emit currentStyleChanged( mCurrentStyle );
179 
180  mLayer->triggerRepaint();
181  return true;
182 }
183 
184 bool QgsMapLayerStyleManager::setOverrideStyle( const QString &styleDef )
185 {
186  if ( mOverriddenOriginalStyle )
187  return false; // cannot override the style more than once!
188 
189  mLayer->blockSignals( true );
190  if ( mStyles.contains( styleDef ) )
191  {
192  mOverriddenOriginalStyle = new QgsMapLayerStyle;
193  mOverriddenOriginalStyle->readFromLayer( mLayer );
194 
195  // apply style name
196  mStyles[styleDef].writeToLayer( mLayer );
197  }
198  else if ( styleDef.startsWith( '<' ) )
199  {
200  mOverriddenOriginalStyle = new QgsMapLayerStyle;
201  mOverriddenOriginalStyle->readFromLayer( mLayer );
202 
203  // apply style XML
204  QgsMapLayerStyle overrideStyle( styleDef );
205  overrideStyle.writeToLayer( mLayer );
206  }
207  mLayer->blockSignals( false );
208 
209  return true;
210 }
211 
213 {
214  if ( !mOverriddenOriginalStyle )
215  return false;
216 
217  mLayer->blockSignals( true );
218  mOverriddenOriginalStyle->writeToLayer( mLayer );
219  mLayer->blockSignals( false );
220 
221  delete mOverriddenOriginalStyle;
222  mOverriddenOriginalStyle = nullptr;
223  return true;
224 }
225 
226 bool QgsMapLayerStyleManager::isDefault( const QString &styleName ) const
227 {
228  return styleName == defaultStyleName();
229 }
230 
231 
232 // -----
233 
234 QgsMapLayerStyle::QgsMapLayerStyle( const QString &xmlData )
235  : mXmlData( xmlData )
236 {
237 }
238 
240 {
241  return !mXmlData.isEmpty();
242 }
243 
245 {
246  mXmlData.clear();
247 }
248 
250 {
251  return mXmlData;
252 }
253 
255 {
256  QString errorMsg;
257  QDomDocument doc;
258  layer->exportNamedStyle( doc, errorMsg );
259  if ( !errorMsg.isEmpty() )
260  {
261  QgsDebugMsg( "Failed to export style from layer: " + errorMsg );
262  return;
263  }
264 
265  mXmlData.clear();
266  QTextStream stream( &mXmlData );
267  doc.documentElement().save( stream, 0 );
268 }
269 
271 {
272  if ( !isValid() )
273  {
274  return;
275  }
276 
277  QDomDocument doc( QStringLiteral( "qgis" ) );
278  if ( !doc.setContent( mXmlData ) )
279  {
280  QgsDebugMsg( "Failed to parse XML of previously stored XML data - this should not happen!" );
281  return;
282  }
283 
284  QString errorMsg;
285  if ( !layer->importNamedStyle( doc, errorMsg ) )
286  {
287  QgsDebugMsg( "Failed to import style to layer: " + errorMsg );
288  }
289 }
290 
291 void QgsMapLayerStyle::readXml( const QDomElement &styleElement )
292 {
293  mXmlData.clear();
294  QTextStream stream( &mXmlData );
295  styleElement.firstChildElement().save( stream, 0 );
296 }
297 
298 void QgsMapLayerStyle::writeXml( QDomElement &styleElement ) const
299 {
300  // the currently selected style has no content stored here (layer has all the information inside)
301  if ( !isValid() )
302  return;
303 
304  QDomDocument docX;
305  docX.setContent( mXmlData );
306  styleElement.appendChild( docX.documentElement() );
307 }
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:61
QStringList styles() const
Returns 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.
void reset()
Reset the style manager to a basic state - with one default style which is set as current...
QString xmlData() const
Returns XML content of the style.
void readXml(const QDomElement &styleElement)
Read style configuration (for project file reading)
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
bool isValid() const
Tell whether the style is valid (i.e. there is something stored in it)
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.
Stores style information (renderer, opacity, labeling, diagrams etc.) applicable to a map layer...
bool removeStyle(const QString &name)
Remove a stored style.
void triggerRepaint(bool deferredUpdate=false)
Will advise the map canvas (and any other interested party) that this layer requires to be repainted...
void readXml(const QDomElement &mgrElement)
Read configuration (for project loading)
void styleRemoved(const QString &name)
Emitted when a style has been removed.
QgsMapLayerStyle style(const QString &name) const
Returns 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.
QgsMapLayerStyle()=default
construct invalid style
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 isDefault(const QString &styleName) const
Returns true if this is the default style.
QString currentStyle() const
Returns name of the current style.
bool renameStyle(const QString &name, const QString &newName)
Rename a stored style to a different name.
void writeXml(QDomElement &styleElement) const
Write style configuration (for project file writing)
QMap< QString, QgsMapLayerStyle > mapLayerStyles() const
Gets available styles for the associated map layer.
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.
void writeXml(QDomElement &mgrElement) const
Write configuration (for project saving)
bool setCurrentStyle(const QString &name)
Set a different style as the current style - will apply it to the layer.
virtual void exportNamedStyle(QDomDocument &doc, QString &errorMsg) const
Export the properties of this layer as named style in a QDomDocument.