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