QGIS API Documentation  3.14.0-Pi (9f7028fd23)
qgsmapthemecollection.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsmapthemecollection.cpp
3  --------------------------------------
4  Date : September 2014
5  Copyright : (C) 2014 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 
16 #include "qgsmapthemecollection.h"
17 
18 #include "qgslayertree.h"
19 #include "qgslayertreemodel.h"
21 #include "qgsmaplayerlistutils.h"
23 #include "qgsproject.h"
24 #include "qgsrenderer.h"
25 #include "qgsvectorlayer.h"
26 
27 #include <QInputDialog>
28 
30  : mProject( project )
31 {
32  connect( project, static_cast<void ( QgsProject::* )( const QStringList & )>( &QgsProject::layersWillBeRemoved ), this, &QgsMapThemeCollection::registryLayersRemoved );
33 }
34 
35 QgsMapThemeCollection::MapThemeLayerRecord QgsMapThemeCollection::createThemeLayerRecord( QgsLayerTreeLayer *nodeLayer, QgsLayerTreeModel *model )
36 {
37  MapThemeLayerRecord layerRec( nodeLayer->layer() );
38  layerRec.isVisible = nodeLayer->isVisible();
39  layerRec.usingCurrentStyle = true;
40  layerRec.currentStyle = nodeLayer->layer()->styleManager()->currentStyle();
41  layerRec.expandedLayerNode = nodeLayer->isExpanded();
42  layerRec.expandedLegendItems = qgis::listToSet( nodeLayer->customProperty( QStringLiteral( "expandedLegendNodes" ) ).toStringList() );
43 
44  // get checked legend items
45  bool hasCheckableItems = false;
46  bool someItemsUnchecked = false;
47  QSet<QString> checkedItems;
48  const QList<QgsLayerTreeModelLegendNode *> layerLegendNodes = model->layerLegendNodes( nodeLayer, true );
49  for ( QgsLayerTreeModelLegendNode *legendNode : layerLegendNodes )
50  {
51  if ( legendNode->flags() & Qt::ItemIsUserCheckable )
52  {
53  hasCheckableItems = true;
54 
55  if ( legendNode->data( Qt::CheckStateRole ).toInt() == Qt::Checked )
56  checkedItems << legendNode->data( QgsLayerTreeModelLegendNode::RuleKeyRole ).toString();
57  else
58  someItemsUnchecked = true;
59  }
60  }
61 
62  if ( hasCheckableItems && someItemsUnchecked )
63  {
64  layerRec.usingLegendItems = true;
65  layerRec.checkedLegendItems = checkedItems;
66  }
67  return layerRec;
68 }
69 
70 static QString _groupId( QgsLayerTreeNode *node )
71 {
72  QStringList lst;
73  while ( node->parent() )
74  {
75  lst.prepend( node->name() );
76  node = node->parent();
77  }
78  return lst.join( '/' );
79 }
80 
82 {
83  const QList<QgsLayerTreeNode *> constChildren = parent->children();
84  for ( QgsLayerTreeNode *node : constChildren )
85  {
86  if ( QgsLayerTree::isGroup( node ) )
87  {
89  if ( node->isExpanded() )
90  rec.mExpandedGroupNodes.insert( _groupId( node ) );
91  if ( node->itemVisibilityChecked() != Qt::Unchecked )
92  rec.mCheckedGroupNodes.insert( _groupId( node ) );
93  }
94  else if ( QgsLayerTree::isLayer( node ) )
95  {
96  QgsLayerTreeLayer *nodeLayer = QgsLayerTree::toLayer( node );
97  if ( node->itemVisibilityChecked() != Qt::Unchecked && nodeLayer->layer() )
98  rec.mLayerRecords << createThemeLayerRecord( nodeLayer, model );
99  }
100  }
101 }
102 
104 {
106  rec.setHasExpandedStateInfo( true ); // all newly created theme records have expanded state info
107  rec.setHasCheckedStateInfo( true ); // all newly created theme records have checked state info
108  createThemeFromCurrentState( root, model, rec );
109  return rec;
110 }
111 
112 bool QgsMapThemeCollection::findRecordForLayer( QgsMapLayer *layer, const QgsMapThemeCollection::MapThemeRecord &rec, QgsMapThemeCollection::MapThemeLayerRecord &layerRec )
113 {
114  for ( const QgsMapThemeCollection::MapThemeLayerRecord &lr : qgis::as_const( rec.mLayerRecords ) )
115  {
116  if ( lr.layer() == layer )
117  {
118  layerRec = lr;
119  return true;
120  }
121  }
122  return false;
123 }
124 
125 void QgsMapThemeCollection::applyThemeToLayer( QgsLayerTreeLayer *nodeLayer, QgsLayerTreeModel *model, const QgsMapThemeCollection::MapThemeRecord &rec )
126 {
127  MapThemeLayerRecord layerRec;
128  const bool recordExists = findRecordForLayer( nodeLayer->layer(), rec, layerRec );
129 
130  // Make sure the whole tree is visible
131  if ( recordExists )
132  {
133  if ( rec.hasCheckedStateInfo() )
134  nodeLayer->setItemVisibilityChecked( true );
135  else
136  nodeLayer->setItemVisibilityCheckedParentRecursive( true );
137  }
138  else
139  nodeLayer->setItemVisibilityChecked( false );
140 
141  if ( !recordExists )
142  return;
143 
144  if ( layerRec.usingCurrentStyle )
145  {
146  // apply desired style first
147  nodeLayer->layer()->styleManager()->setCurrentStyle( layerRec.currentStyle );
148  }
149 
150  if ( layerRec.usingLegendItems )
151  {
152  // some nodes are not checked
153  const QList<QgsLayerTreeModelLegendNode *> constLayerLegendNodes = model->layerLegendNodes( nodeLayer, true );
154  for ( QgsLayerTreeModelLegendNode *legendNode : constLayerLegendNodes )
155  {
156  QString ruleKey = legendNode->data( QgsLayerTreeModelLegendNode::RuleKeyRole ).toString();
157  Qt::CheckState shouldHaveState = layerRec.checkedLegendItems.contains( ruleKey ) ? Qt::Checked : Qt::Unchecked;
158  if ( ( legendNode->flags() & Qt::ItemIsUserCheckable ) &&
159  legendNode->data( Qt::CheckStateRole ).toInt() != shouldHaveState )
160  legendNode->setData( shouldHaveState, Qt::CheckStateRole );
161  }
162  }
163  else
164  {
165  // all nodes should be checked
166  const QList<QgsLayerTreeModelLegendNode *> constLayerLegendNodes = model->layerLegendNodes( nodeLayer, true );
167  for ( QgsLayerTreeModelLegendNode *legendNode : constLayerLegendNodes )
168  {
169  if ( ( legendNode->flags() & Qt::ItemIsUserCheckable ) &&
170  legendNode->data( Qt::CheckStateRole ).toInt() != Qt::Checked )
171  legendNode->setData( Qt::Checked, Qt::CheckStateRole );
172  }
173  }
174 
175  // apply expanded/collapsed state to the layer and its legend nodes
176  if ( rec.hasExpandedStateInfo() )
177  {
178  nodeLayer->setExpanded( layerRec.expandedLayerNode );
179  nodeLayer->setCustomProperty( QStringLiteral( "expandedLegendNodes" ), QStringList( qgis::setToList( layerRec.expandedLegendItems ) ) );
180  }
181 }
182 
183 
184 void QgsMapThemeCollection::applyThemeToGroup( QgsLayerTreeGroup *parent, QgsLayerTreeModel *model, const QgsMapThemeCollection::MapThemeRecord &rec )
185 {
186  const QList<QgsLayerTreeNode *> constChildren = parent->children();
187  for ( QgsLayerTreeNode *node : constChildren )
188  {
189  if ( QgsLayerTree::isGroup( node ) )
190  {
191  applyThemeToGroup( QgsLayerTree::toGroup( node ), model, rec );
192  if ( rec.hasExpandedStateInfo() )
193  node->setExpanded( rec.expandedGroupNodes().contains( _groupId( node ) ) );
194  if ( rec.hasCheckedStateInfo() )
195  node->setItemVisibilityChecked( rec.checkedGroupNodes().contains( _groupId( node ) ) );
196  }
197  else if ( QgsLayerTree::isLayer( node ) )
198  applyThemeToLayer( QgsLayerTree::toLayer( node ), model, rec );
199  }
200 }
201 
202 
204 {
205  applyThemeToGroup( root, model, mapThemeState( name ) );
206 
207  // also make sure that the preset is up-to-date (not containing any non-existent legend items)
208  update( name, createThemeFromCurrentState( root, model ) );
209 }
210 
212 {
213  return mProject;
214 }
215 
217 {
218  if ( project == mProject )
219  return;
220 
221  disconnect( mProject, static_cast<void ( QgsProject::* )( const QStringList & )>( &QgsProject::layersWillBeRemoved ), this, &QgsMapThemeCollection::registryLayersRemoved );
222  mProject = project;
223  connect( mProject, static_cast<void ( QgsProject::* )( const QStringList & )>( &QgsProject::layersWillBeRemoved ), this, &QgsMapThemeCollection::registryLayersRemoved );
224  emit projectChanged();
225 }
226 
227 QList<QgsMapLayer *> QgsMapThemeCollection::masterLayerOrder() const
228 {
229  if ( !mProject )
230  return QList< QgsMapLayer * >();
231 
232  return mProject->layerTreeRoot()->layerOrder();
233 }
234 
235 QList<QgsMapLayer *> QgsMapThemeCollection::masterVisibleLayers() const
236 {
237  const QList< QgsMapLayer *> allLayers = masterLayerOrder();
238  const QList< QgsMapLayer * > visibleLayers = mProject->layerTreeRoot()->checkedLayers();
239 
240  if ( allLayers.isEmpty() )
241  {
242  // no project layer order set
243  return visibleLayers;
244  }
245  else
246  {
247  QList< QgsMapLayer * > orderedVisibleLayers;
248  for ( QgsMapLayer *layer : allLayers )
249  {
250  if ( visibleLayers.contains( layer ) )
251  orderedVisibleLayers << layer;
252  }
253  return orderedVisibleLayers;
254  }
255 }
256 
257 
258 bool QgsMapThemeCollection::hasMapTheme( const QString &name ) const
259 {
260  return mMapThemes.contains( name );
261 }
262 
264 {
265  mMapThemes.insert( name, state );
266 
267  reconnectToLayersStyleManager();
268  emit mapThemeChanged( name );
269  emit mapThemesChanged();
270 }
271 
272 void QgsMapThemeCollection::update( const QString &name, const MapThemeRecord &state )
273 {
274  if ( !mMapThemes.contains( name ) )
275  return;
276 
277  mMapThemes[name] = state;
278 
279  reconnectToLayersStyleManager();
280  emit mapThemeChanged( name );
281  emit mapThemesChanged();
282 }
283 
284 bool QgsMapThemeCollection::renameMapTheme( const QString &name, const QString &newName )
285 {
286  if ( !mMapThemes.contains( name ) || mMapThemes.contains( newName ) )
287  return false;
288 
289  const MapThemeRecord state = mMapThemes[name];
290  const MapThemeRecord newState = state;
291  insert( newName, newState );
292  emit mapThemeRenamed( name, newName );
293  removeMapTheme( name );
294  return true;
295 }
296 
297 void QgsMapThemeCollection::removeMapTheme( const QString &name )
298 {
299  if ( !mMapThemes.contains( name ) )
300  return;
301 
302  mMapThemes.remove( name );
303 
304  reconnectToLayersStyleManager();
305  emit mapThemesChanged();
306 }
307 
309 {
310  mMapThemes.clear();
311 
312  reconnectToLayersStyleManager();
313  emit mapThemesChanged();
314 }
315 
316 QStringList QgsMapThemeCollection::mapThemes() const
317 {
318  return mMapThemes.keys();
319 }
320 
321 QStringList QgsMapThemeCollection::mapThemeVisibleLayerIds( const QString &name ) const
322 {
323  QStringList layerIds;
324  const QList<QgsMapLayer *> constMapThemeVisibleLayers = mapThemeVisibleLayers( name );
325  for ( QgsMapLayer *layer : constMapThemeVisibleLayers )
326  {
327  layerIds << layer->id();
328  }
329  return layerIds;
330 }
331 
332 QList<QgsMapLayer *> QgsMapThemeCollection::mapThemeVisibleLayers( const QString &name ) const
333 {
334  QList<QgsMapLayer *> layers;
335  const QList<MapThemeLayerRecord> recs = mMapThemes.value( name ).mLayerRecords;
336  const QList<QgsMapLayer *> layerOrder = masterLayerOrder();
337  if ( layerOrder.isEmpty() )
338  {
339  // no master layer order - so we have to just use the stored theme layer order as a fallback
340  const QList<MapThemeLayerRecord> records { mMapThemes.value( name ).mLayerRecords };
341  for ( const MapThemeLayerRecord &layerRec : records )
342  {
343  if ( layerRec.isVisible && layerRec.layer() )
344  layers << layerRec.layer();
345  }
346  }
347  else
348  {
349  for ( QgsMapLayer *layer : layerOrder )
350  {
351  for ( const MapThemeLayerRecord &layerRec : recs )
352  {
353  if ( layerRec.isVisible && layerRec.layer() == layer )
354  layers << layerRec.layer();
355  }
356  }
357  }
358 
359  return layers;
360 }
361 
362 
363 void QgsMapThemeCollection::applyMapThemeCheckedLegendNodesToLayer( const MapThemeLayerRecord &layerRec, QgsMapLayer *layer )
364 {
365  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
366  if ( !vlayer || !vlayer->renderer() )
367  return;
368 
369  QgsFeatureRenderer *renderer = vlayer->renderer();
370  if ( !renderer->legendSymbolItemsCheckable() )
371  return; // no need to do anything
372 
373  bool someNodesUnchecked = layerRec.usingLegendItems;
374 
375  const auto constLegendSymbolItems = vlayer->renderer()->legendSymbolItems();
376  for ( const QgsLegendSymbolItem &item : constLegendSymbolItems )
377  {
378  bool checked = renderer->legendSymbolItemChecked( item.ruleKey() );
379  bool shouldBeChecked = someNodesUnchecked ? layerRec.checkedLegendItems.contains( item.ruleKey() ) : true;
380  if ( checked != shouldBeChecked )
381  renderer->checkLegendSymbolItem( item.ruleKey(), shouldBeChecked );
382  }
383 }
384 
385 
386 QMap<QString, QString> QgsMapThemeCollection::mapThemeStyleOverrides( const QString &presetName )
387 {
388  QMap<QString, QString> styleOverrides;
389  if ( !mMapThemes.contains( presetName ) )
390  return styleOverrides;
391 
392  const QList<MapThemeLayerRecord> records {mMapThemes.value( presetName ).mLayerRecords};
393  for ( const MapThemeLayerRecord &layerRec : records )
394  {
395  if ( !layerRec.layer() )
396  continue;
397 
398  if ( layerRec.usingCurrentStyle )
399  {
400  QgsMapLayer *layer = layerRec.layer();
401  QgsMapLayerStyleOverride styleOverride( layer );
402  styleOverride.setOverrideStyle( layerRec.currentStyle );
403 
404  // set the checked legend nodes
405  applyMapThemeCheckedLegendNodesToLayer( layerRec, layer );
406 
407  // save to overrides
408  QgsMapLayerStyle layerStyle;
409  layerStyle.readFromLayer( layer );
410  styleOverrides[layer->id()] = layerStyle.xmlData();
411  }
412  }
413  return styleOverrides;
414 }
415 
416 void QgsMapThemeCollection::reconnectToLayersStyleManager()
417 {
418  // disconnect( 0, 0, this, SLOT( layerStyleRenamed( QString, QString ) ) );
419 
420  QSet<QgsMapLayer *> layers;
421  for ( const MapThemeRecord &rec : qgis::as_const( mMapThemes ) )
422  {
423  for ( const MapThemeLayerRecord &layerRec : qgis::as_const( rec.mLayerRecords ) )
424  {
425  if ( layerRec.layer() )
426  layers << layerRec.layer();
427  }
428  }
429 
430  const QSet<QgsMapLayer *> constLayers = layers;
431  for ( QgsMapLayer *ml : constLayers )
432  {
433  connect( ml->styleManager(), &QgsMapLayerStyleManager::styleRenamed, this, &QgsMapThemeCollection::layerStyleRenamed );
434  }
435 }
436 
437 void QgsMapThemeCollection::readXml( const QDomDocument &doc )
438 {
439  clear();
440 
441  QDomElement visPresetsElem = doc.firstChildElement( QStringLiteral( "qgis" ) ).firstChildElement( QStringLiteral( "visibility-presets" ) );
442  if ( visPresetsElem.isNull() )
443  return;
444 
445  QDomElement visPresetElem = visPresetsElem.firstChildElement( QStringLiteral( "visibility-preset" ) );
446  while ( !visPresetElem.isNull() )
447  {
448  QHash<QString, MapThemeLayerRecord> layerRecords; // key = layer ID
449 
450  bool expandedStateInfo = false;
451  if ( visPresetElem.hasAttribute( QStringLiteral( "has-expanded-info" ) ) )
452  expandedStateInfo = visPresetElem.attribute( QStringLiteral( "has-expanded-info" ) ).toInt();
453 
454  bool checkedStateInfo = false;
455  if ( visPresetElem.hasAttribute( QStringLiteral( "has-checked-group-info" ) ) )
456  checkedStateInfo = visPresetElem.attribute( QStringLiteral( "has-checked-group-info" ) ).toInt();
457 
458  QString presetName = visPresetElem.attribute( QStringLiteral( "name" ) );
459  QDomElement visPresetLayerElem = visPresetElem.firstChildElement( QStringLiteral( "layer" ) );
460  while ( !visPresetLayerElem.isNull() )
461  {
462  QString layerID = visPresetLayerElem.attribute( QStringLiteral( "id" ) );
463  if ( QgsMapLayer *layer = mProject->mapLayer( layerID ) )
464  {
465  layerRecords[layerID] = MapThemeLayerRecord( layer );
466  layerRecords[layerID].isVisible = visPresetLayerElem.attribute( QStringLiteral( "visible" ), QStringLiteral( "1" ) ).toInt();
467 
468  if ( visPresetLayerElem.hasAttribute( QStringLiteral( "style" ) ) )
469  {
470  layerRecords[layerID].usingCurrentStyle = true;
471  layerRecords[layerID].currentStyle = visPresetLayerElem.attribute( QStringLiteral( "style" ) );
472  }
473 
474  if ( visPresetLayerElem.hasAttribute( QStringLiteral( "expanded" ) ) )
475  layerRecords[layerID].expandedLayerNode = visPresetLayerElem.attribute( QStringLiteral( "expanded" ) ).toInt();
476  }
477  visPresetLayerElem = visPresetLayerElem.nextSiblingElement( QStringLiteral( "layer" ) );
478  }
479 
480  QDomElement checkedLegendNodesElem = visPresetElem.firstChildElement( QStringLiteral( "checked-legend-nodes" ) );
481  while ( !checkedLegendNodesElem.isNull() )
482  {
483  QSet<QString> checkedLegendNodes;
484 
485  QDomElement checkedLegendNodeElem = checkedLegendNodesElem.firstChildElement( QStringLiteral( "checked-legend-node" ) );
486  while ( !checkedLegendNodeElem.isNull() )
487  {
488  checkedLegendNodes << checkedLegendNodeElem.attribute( QStringLiteral( "id" ) );
489  checkedLegendNodeElem = checkedLegendNodeElem.nextSiblingElement( QStringLiteral( "checked-legend-node" ) );
490  }
491 
492  QString layerID = checkedLegendNodesElem.attribute( QStringLiteral( "id" ) );
493  if ( mProject->mapLayer( layerID ) ) // only use valid IDs
494  {
495  layerRecords[layerID].usingLegendItems = true;
496  layerRecords[layerID].checkedLegendItems = checkedLegendNodes;
497  }
498  checkedLegendNodesElem = checkedLegendNodesElem.nextSiblingElement( QStringLiteral( "checked-legend-nodes" ) );
499  }
500 
501  QSet<QString> expandedGroupNodes;
502  if ( expandedStateInfo )
503  {
504  // expanded state of legend nodes
505  QDomElement expandedLegendNodesElem = visPresetElem.firstChildElement( QStringLiteral( "expanded-legend-nodes" ) );
506  while ( !expandedLegendNodesElem.isNull() )
507  {
508  QSet<QString> expandedLegendNodes;
509 
510  QDomElement expandedLegendNodeElem = expandedLegendNodesElem.firstChildElement( QStringLiteral( "expanded-legend-node" ) );
511  while ( !expandedLegendNodeElem.isNull() )
512  {
513  expandedLegendNodes << expandedLegendNodeElem.attribute( QStringLiteral( "id" ) );
514  expandedLegendNodeElem = expandedLegendNodeElem.nextSiblingElement( QStringLiteral( "expanded-legend-node" ) );
515  }
516 
517  QString layerID = expandedLegendNodesElem.attribute( QStringLiteral( "id" ) );
518  if ( mProject->mapLayer( layerID ) ) // only use valid IDs
519  {
520  layerRecords[layerID].expandedLegendItems = expandedLegendNodes;
521  }
522  expandedLegendNodesElem = expandedLegendNodesElem.nextSiblingElement( QStringLiteral( "expanded-legend-nodes" ) );
523  }
524 
525  // expanded state of group nodes
526  QDomElement expandedGroupNodesElem = visPresetElem.firstChildElement( QStringLiteral( "expanded-group-nodes" ) );
527  if ( !expandedGroupNodesElem.isNull() )
528  {
529  QDomElement expandedGroupNodeElem = expandedGroupNodesElem.firstChildElement( QStringLiteral( "expanded-group-node" ) );
530  while ( !expandedGroupNodeElem.isNull() )
531  {
532  expandedGroupNodes << expandedGroupNodeElem.attribute( QStringLiteral( "id" ) );
533  expandedGroupNodeElem = expandedGroupNodeElem.nextSiblingElement( QStringLiteral( "expanded-group-node" ) );
534  }
535  }
536  }
537 
538  QSet<QString> checkedGroupNodes;
539  if ( checkedStateInfo )
540  {
541  // expanded state of legend nodes
542  QDomElement checkedGroupNodesElem = visPresetElem.firstChildElement( QStringLiteral( "checked-group-nodes" ) );
543  if ( !checkedGroupNodesElem.isNull() )
544  {
545  QDomElement checkedGroupNodeElem = checkedGroupNodesElem.firstChildElement( QStringLiteral( "checked-group-node" ) );
546  while ( !checkedGroupNodeElem.isNull() )
547  {
548  checkedGroupNodes << checkedGroupNodeElem.attribute( QStringLiteral( "id" ) );
549  checkedGroupNodeElem = checkedGroupNodeElem.nextSiblingElement( QStringLiteral( "checked-group-node" ) );
550  }
551  }
552  }
553 
554  MapThemeRecord rec;
555  rec.setLayerRecords( layerRecords.values() );
556  rec.setHasExpandedStateInfo( expandedStateInfo );
557  rec.setExpandedGroupNodes( expandedGroupNodes );
558  rec.setHasCheckedStateInfo( checkedStateInfo );
559  rec.setCheckedGroupNodes( checkedGroupNodes );
560  mMapThemes.insert( presetName, rec );
561  emit mapThemeChanged( presetName );
562 
563  visPresetElem = visPresetElem.nextSiblingElement( QStringLiteral( "visibility-preset" ) );
564  }
565 
566  reconnectToLayersStyleManager();
567  emit mapThemesChanged();
568 }
569 
570 void QgsMapThemeCollection::writeXml( QDomDocument &doc )
571 {
572  QDomElement visPresetsElem = doc.createElement( QStringLiteral( "visibility-presets" ) );
573 
574  QList< QString > keys = mMapThemes.keys();
575 
576  std::sort( keys.begin(), keys.end() );
577 
578  for ( const QString &grpName : qgis::as_const( keys ) )
579  {
580  const MapThemeRecord &rec = mMapThemes.value( grpName );
581  QDomElement visPresetElem = doc.createElement( QStringLiteral( "visibility-preset" ) );
582  visPresetElem.setAttribute( QStringLiteral( "name" ), grpName );
583  if ( rec.hasExpandedStateInfo() )
584  visPresetElem.setAttribute( QStringLiteral( "has-expanded-info" ), QStringLiteral( "1" ) );
585  if ( rec.hasCheckedStateInfo() )
586  visPresetElem.setAttribute( QStringLiteral( "has-checked-group-info" ), QStringLiteral( "1" ) );
587  for ( const MapThemeLayerRecord &layerRec : qgis::as_const( rec.mLayerRecords ) )
588  {
589  if ( !layerRec.layer() )
590  continue;
591  QString layerID = layerRec.layer()->id();
592  QDomElement layerElem = doc.createElement( QStringLiteral( "layer" ) );
593  layerElem.setAttribute( QStringLiteral( "id" ), layerID );
594  layerElem.setAttribute( QStringLiteral( "visible" ), layerRec.isVisible ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
595  if ( layerRec.usingCurrentStyle )
596  layerElem.setAttribute( QStringLiteral( "style" ), layerRec.currentStyle );
597  visPresetElem.appendChild( layerElem );
598 
599  if ( layerRec.usingLegendItems )
600  {
601  QDomElement checkedLegendNodesElem = doc.createElement( QStringLiteral( "checked-legend-nodes" ) );
602  checkedLegendNodesElem.setAttribute( QStringLiteral( "id" ), layerID );
603  for ( const QString &checkedLegendNode : qgis::as_const( layerRec.checkedLegendItems ) )
604  {
605  QDomElement checkedLegendNodeElem = doc.createElement( QStringLiteral( "checked-legend-node" ) );
606  checkedLegendNodeElem.setAttribute( QStringLiteral( "id" ), checkedLegendNode );
607  checkedLegendNodesElem.appendChild( checkedLegendNodeElem );
608  }
609  visPresetElem.appendChild( checkedLegendNodesElem );
610  }
611 
612  if ( rec.hasExpandedStateInfo() )
613  {
614  layerElem.setAttribute( QStringLiteral( "expanded" ), layerRec.expandedLayerNode ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
615 
616  QDomElement expandedLegendNodesElem = doc.createElement( QStringLiteral( "expanded-legend-nodes" ) );
617  expandedLegendNodesElem.setAttribute( QStringLiteral( "id" ), layerID );
618  for ( const QString &expandedLegendNode : qgis::as_const( layerRec.expandedLegendItems ) )
619  {
620  QDomElement expandedLegendNodeElem = doc.createElement( QStringLiteral( "expanded-legend-node" ) );
621  expandedLegendNodeElem.setAttribute( QStringLiteral( "id" ), expandedLegendNode );
622  expandedLegendNodesElem.appendChild( expandedLegendNodeElem );
623  }
624  visPresetElem.appendChild( expandedLegendNodesElem );
625  }
626  }
627 
628  if ( rec.hasCheckedStateInfo() )
629  {
630  QDomElement checkedGroupElems = doc.createElement( QStringLiteral( "checked-group-nodes" ) );
631  const QSet<QString> checkedGroupNodes = rec.checkedGroupNodes();
632  for ( const QString &groupId : checkedGroupNodes )
633  {
634  QDomElement checkedGroupElem = doc.createElement( QStringLiteral( "checked-group-node" ) );
635  checkedGroupElem.setAttribute( QStringLiteral( "id" ), groupId );
636  checkedGroupElems.appendChild( checkedGroupElem );
637  }
638  visPresetElem.appendChild( checkedGroupElems );
639  }
640 
641  if ( rec.hasExpandedStateInfo() )
642  {
643  QDomElement expandedGroupElems = doc.createElement( QStringLiteral( "expanded-group-nodes" ) );
644  const QSet<QString> expandedGroupNodes = rec.expandedGroupNodes();
645  for ( const QString &groupId : expandedGroupNodes )
646  {
647  QDomElement expandedGroupElem = doc.createElement( QStringLiteral( "expanded-group-node" ) );
648  expandedGroupElem.setAttribute( QStringLiteral( "id" ), groupId );
649  expandedGroupElems.appendChild( expandedGroupElem );
650  }
651  visPresetElem.appendChild( expandedGroupElems );
652  }
653 
654  visPresetsElem.appendChild( visPresetElem );
655  }
656 
657  doc.firstChildElement( QStringLiteral( "qgis" ) ).appendChild( visPresetsElem );
658 }
659 
660 void QgsMapThemeCollection::registryLayersRemoved( const QStringList &layerIDs )
661 {
662  // while layers are stored as weak pointers, this triggers the mapThemeChanged signal for
663  // affected themes
664  QSet< QString > changedThemes;
665  MapThemeRecordMap::iterator it = mMapThemes.begin();
666  for ( ; it != mMapThemes.end(); ++it )
667  {
668  MapThemeRecord &rec = it.value();
669  for ( int i = 0; i < rec.mLayerRecords.count(); ++i )
670  {
671  MapThemeLayerRecord &layerRec = rec.mLayerRecords[i];
672  if ( layerRec.layer() && layerIDs.contains( layerRec.layer()->id() ) )
673  {
674  rec.mLayerRecords.removeAt( i-- );
675  changedThemes << it.key();
676  }
677  }
678  }
679 
680  for ( const QString &theme : qgis::as_const( changedThemes ) )
681  {
682  emit mapThemeChanged( theme );
683  }
684  emit mapThemesChanged();
685 }
686 
687 void QgsMapThemeCollection::layerStyleRenamed( const QString &oldName, const QString &newName )
688 {
689  QgsMapLayerStyleManager *styleMgr = qobject_cast<QgsMapLayerStyleManager *>( sender() );
690  if ( !styleMgr )
691  return;
692 
693  QSet< QString > changedThemes;
694 
695  MapThemeRecordMap::iterator it = mMapThemes.begin();
696  for ( ; it != mMapThemes.end(); ++it )
697  {
698  MapThemeRecord &rec = it.value();
699  for ( int i = 0; i < rec.mLayerRecords.count(); ++i )
700  {
701  MapThemeLayerRecord &layerRec = rec.mLayerRecords[i];
702  if ( layerRec.layer() == styleMgr->layer() )
703  {
704  if ( layerRec.currentStyle == oldName )
705  {
706  layerRec.currentStyle = newName;
707  changedThemes << it.key();
708  }
709  }
710  }
711  }
712 
713  for ( const QString &theme : qgis::as_const( changedThemes ) )
714  {
715  emit mapThemeChanged( theme );
716  }
717  emit mapThemesChanged();
718 }
719 
721 {
722  for ( int i = 0; i < mLayerRecords.length(); ++i )
723  {
724  if ( mLayerRecords.at( i ).layer() == layer )
725  mLayerRecords.removeAt( i );
726  }
727 }
728 
730 {
731  mLayerRecords.append( record );
732 }
733 
734 QHash<QgsMapLayer *, QgsMapThemeCollection::MapThemeLayerRecord> QgsMapThemeCollection::MapThemeRecord::validLayerRecords() const
735 {
736  QHash<QgsMapLayer *, MapThemeLayerRecord> validSet;
737  for ( const MapThemeLayerRecord &layerRec : mLayerRecords )
738  {
739  if ( layerRec.layer() )
740  validSet.insert( layerRec.layer(), layerRec );
741  }
742  return validSet;
743 }
744 
746 {
747  mLayer = layer;
748 }
QgsMapLayerStyleManager::styleRenamed
void styleRenamed(const QString &oldName, const QString &newName)
Emitted when a style has been renamed.
QgsMapThemeCollection::MapThemeRecord::addLayerRecord
void addLayerRecord(const QgsMapThemeCollection::MapThemeLayerRecord &record)
Add a new record for a layer.
Definition: qgsmapthemecollection.cpp:729
QgsMapThemeCollection::mapThemes
QStringList mapThemes
Definition: qgsmapthemecollection.h:48
QgsLayerTreeNode
Definition: qgslayertreenode.h:74
qgsmaplayerstylemanager.h
QgsMapThemeCollection::mapThemeRenamed
void mapThemeRenamed(const QString &name, const QString &newName)
Emitted when a map theme within the collection is renamed.
QgsProject::layersWillBeRemoved
void layersWillBeRemoved(const QStringList &layerIds)
Emitted when one or more layers are about to be removed from the registry.
qgslayertreemodellegendnode.h
QgsLayerTreeNode::setItemVisibilityChecked
void setItemVisibilityChecked(bool checked)
Check or uncheck a node (independently of its ancestors or children)
Definition: qgslayertreenode.cpp:78
QgsLayerTreeModelLegendNode::data
virtual QVariant data(int role) const =0
Returns data associated with the item. Must be implemented in derived class.
QgsMapThemeCollection::MapThemeRecord::hasExpandedStateInfo
bool hasExpandedStateInfo() const
Returns whether information about expanded/collapsed state of nodes has been recorded and thus whethe...
Definition: qgsmapthemecollection.h:157
qgsmapthemecollection.h
QgsMapThemeCollection::mapThemeVisibleLayerIds
QStringList mapThemeVisibleLayerIds(const QString &name) const
Returns the list of layer IDs that are visible for the specified map theme.
Definition: qgsmapthemecollection.cpp:321
QgsMapThemeCollection::readXml
void readXml(const QDomDocument &doc)
Reads the map theme collection state from XML.
Definition: qgsmapthemecollection.cpp:437
QgsMapThemeCollection::masterVisibleLayers
QList< QgsMapLayer * > masterVisibleLayers() const
Returns the master list of visible layers.
Definition: qgsmapthemecollection.cpp:235
QgsMapThemeCollection::MapThemeRecord
Definition: qgsmapthemecollection.h:120
QgsMapThemeCollection::mapThemeChanged
void mapThemeChanged(const QString &theme)
Emitted when a map theme changes definition.
QgsMapLayerStyle::readFromLayer
void readFromLayer(QgsMapLayer *layer)
Store layer's active style information in the instance.
Definition: qgsmaplayerstyle.cpp:48
QgsMapLayer::styleManager
QgsMapLayerStyleManager * styleManager() const
Gets access to the layer's style manager.
Definition: qgsmaplayer.cpp:1797
QgsMapThemeCollection::QgsMapThemeCollection
QgsMapThemeCollection(QgsProject *project=nullptr)
Create map theme collection that handles themes of the given project.
Definition: qgsmapthemecollection.cpp:29
QgsMapThemeCollection::MapThemeRecord::validLayerRecords
QHash< QgsMapLayer *, QgsMapThemeCollection::MapThemeLayerRecord > validLayerRecords() const
Returns set with only records for valid layers.
Definition: qgsmapthemecollection.cpp:734
QgsFeatureRenderer::legendSymbolItems
virtual QgsLegendSymbolList legendSymbolItems() const
Returns a list of symbology items for the legend.
Definition: qgsrenderer.cpp:353
QgsLayerTree::toLayer
static QgsLayerTreeLayer * toLayer(QgsLayerTreeNode *node)
Cast node to a layer.
Definition: qgslayertree.h:88
QgsMapThemeCollection::removeMapTheme
void removeMapTheme(const QString &name)
Removes an existing map theme from collection.
Definition: qgsmapthemecollection.cpp:297
QgsLayerTreeModel
Definition: qgslayertreemodel.h:53
QgsMapThemeCollection::MapThemeRecord::setLayerRecords
void setLayerRecords(const QList< QgsMapThemeCollection::MapThemeLayerRecord > &records)
Sets layer records for the theme.
Definition: qgsmapthemecollection.h:139
QgsProject::mapLayer
Q_INVOKABLE QgsMapLayer * mapLayer(const QString &layerId) const
Retrieve a pointer to a registered layer by layer ID.
Definition: qgsproject.cpp:3124
QgsMapLayerStyleOverride
Definition: qgsmaplayerstyle.h:81
QgsLayerTreeNode::setItemVisibilityCheckedParentRecursive
void setItemVisibilityCheckedParentRecursive(bool checked)
Check or uncheck a node and all its parents.
Definition: qgslayertreenode.cpp:91
QgsLayerTree::toGroup
static QgsLayerTreeGroup * toGroup(QgsLayerTreeNode *node)
Cast node to a group.
Definition: qgslayertree.h:77
QgsProject
Definition: qgsproject.h:92
QgsMapThemeCollection::mapThemeVisibleLayers
QList< QgsMapLayer * > mapThemeVisibleLayers(const QString &name) const
Returns the list of layers that are visible for the specified map theme.
Definition: qgsmapthemecollection.cpp:332
QgsMapThemeCollection::MapThemeRecord::setCheckedGroupNodes
void setCheckedGroupNodes(const QSet< QString > &checkedGroupNodes)
Sets a set of group identifiers for group nodes that should have checked state.
Definition: qgsmapthemecollection.h:208
QgsMapThemeCollection::mapThemeStyleOverrides
QMap< QString, QString > mapThemeStyleOverrides(const QString &name)
Gets layer style overrides (for QgsMapSettings) of the visible layers for given map theme.
Definition: qgsmapthemecollection.cpp:386
QgsFeatureRenderer::legendSymbolItemChecked
virtual bool legendSymbolItemChecked(const QString &key)
items of symbology items in legend is checked
Definition: qgsrenderer.cpp:335
QgsLayerTreeModelLegendNode::flags
virtual Qt::ItemFlags flags() const
Returns item flags associated with the item. Default implementation returns Qt::ItemIsEnabled.
Definition: qgslayertreemodellegendnode.cpp:51
QgsFeatureRenderer::legendSymbolItemsCheckable
virtual bool legendSymbolItemsCheckable() const
items of symbology items in legend should be checkable
Definition: qgsrenderer.cpp:330
QgsMapThemeCollection::applyTheme
void applyTheme(const QString &name, QgsLayerTreeGroup *root, QgsLayerTreeModel *model)
Apply theme given by its name and modify layer tree, current style of layers and checked legend items...
Definition: qgsmapthemecollection.cpp:203
QgsLegendSymbolItem
Definition: qgslegendsymbolitem.h:36
QgsProject::layerTreeRoot
QgsLayerTree * layerTreeRoot() const
Returns pointer to the root (invisible) node of the project's layer tree.
Definition: qgsproject.cpp:3016
QgsMapThemeCollection::project
QgsProject project
Definition: qgsmapthemecollection.h:49
QgsMapLayerStyleManager
Definition: qgsmaplayerstylemanager.h:55
QgsMapThemeCollection::MapThemeRecord::checkedGroupNodes
QSet< QString > checkedGroupNodes() const
Returns a set of group identifiers for group nodes that should have checked state (other group nodes ...
Definition: qgsmapthemecollection.h:196
QgsMapThemeCollection::insert
void insert(const QString &name, const QgsMapThemeCollection::MapThemeRecord &state)
Inserts a new map theme to the collection.
Definition: qgsmapthemecollection.cpp:263
QgsFeatureRenderer::checkLegendSymbolItem
virtual void checkLegendSymbolItem(const QString &key, bool state=true)
item in symbology was checked
Definition: qgsrenderer.cpp:341
QgsMapThemeCollection::clear
void clear()
Removes all map themes from the collection.
Definition: qgsmapthemecollection.cpp:308
QgsLayerTree::layerOrder
QList< QgsMapLayer * > layerOrder() const
The order in which layers will be rendered on the canvas.
Definition: qgslayertree.cpp:75
QgsLayerTreeLayer
Definition: qgslayertreelayer.h:43
QgsMapThemeCollection::mapThemesChanged
void mapThemesChanged()
Emitted when map themes within the collection are changed.
QgsMapThemeCollection::MapThemeRecord::setHasExpandedStateInfo
void setHasExpandedStateInfo(bool hasInfo)
Sets whether the map theme contains valid expanded/collapsed state of nodes.
Definition: qgsmapthemecollection.h:171
QgsLayerTreeGroup
Definition: qgslayertreegroup.h:34
QgsMapThemeCollection::MapThemeRecord::expandedGroupNodes
QSet< QString > expandedGroupNodes() const
Returns a set of group identifiers for group nodes that should have expanded state (other group nodes...
Definition: qgsmapthemecollection.h:187
QgsMapLayer::id
QString id() const
Returns the layer's unique ID, which is used to access this layer from QgsProject.
Definition: qgsmaplayer.cpp:148
QgsMapThemeCollection::projectChanged
void projectChanged()
Emitted when the project changes.
qgsrenderer.h
QgsMapLayerStyleManager::layer
QgsMapLayer * layer() const
Gets pointer to the associated map layer.
Definition: qgsmaplayerstylemanager.h:67
QgsLayerTreeLayer::layer
QgsMapLayer * layer() const
Returns the map layer associated with this node.
Definition: qgslayertreelayer.h:74
qgslayertree.h
QgsMapLayerStyleManager::currentStyle
QString currentStyle() const
Returns name of the current style.
Definition: qgsmaplayerstylemanager.cpp:164
QgsLayerTreeNode::isExpanded
bool isExpanded() const
Returns whether the node should be shown as expanded or collapsed in GUI.
Definition: qgslayertreenode.cpp:104
QgsWms::legendNode
QgsLayerTreeModelLegendNode * legendNode(const QString &rule, QgsLayerTreeModel &model)
Definition: qgswmsgetlegendgraphics.cpp:358
qgsvectorlayer.h
QgsLayerTreeModelLegendNode::setData
virtual bool setData(const QVariant &value, int role)
Sets some data associated with the item. Default implementation does nothing and returns false.
Definition: qgslayertreemodellegendnode.cpp:56
QgsMapThemeCollection::masterLayerOrder
QList< QgsMapLayer * > masterLayerOrder() const
Returns the master layer order (this will always match the project's QgsProject::layerOrder() ).
Definition: qgsmapthemecollection.cpp:227
QgsLayerTree::isLayer
static bool isLayer(const QgsLayerTreeNode *node)
Check whether the node is a valid layer node.
Definition: qgslayertree.h:66
QgsMapThemeCollection::createThemeFromCurrentState
static QgsMapThemeCollection::MapThemeRecord createThemeFromCurrentState(QgsLayerTreeGroup *root, QgsLayerTreeModel *model)
Static method to create theme from the current state of layer visibilities in layer tree,...
Definition: qgsmapthemecollection.cpp:103
QgsFeatureRenderer
Definition: qgsrenderer.h:102
QgsMapThemeCollection::MapThemeLayerRecord
Definition: qgsmapthemecollection.h:58
QgsLayerTreeNode::setCustomProperty
void setCustomProperty(const QString &key, const QVariant &value)
Sets a custom property for the node. Properties are stored in a map and saved in project file.
Definition: qgslayertreenode.cpp:180
QgsMapThemeCollection::writeXml
void writeXml(QDomDocument &doc)
Writes the map theme collection state to XML.
Definition: qgsmapthemecollection.cpp:570
QgsVectorLayer
Definition: qgsvectorlayer.h:385
QgsLayerTreeNode::itemVisibilityChecked
bool itemVisibilityChecked() const
Returns whether a node is checked (independently of its ancestors or children)
Definition: qgslayertreenode.h:171
QgsLayerTreeNode::customProperty
QVariant customProperty(const QString &key, const QVariant &defaultValue=QVariant()) const
Read a custom property from layer. Properties are stored in a map and saved in project file.
Definition: qgslayertreenode.cpp:189
QgsMapLayer
Definition: qgsmaplayer.h:81
QgsMapThemeCollection::MapThemeRecord::setExpandedGroupNodes
void setExpandedGroupNodes(const QSet< QString > &expandedGroupNodes)
Sets a set of group identifiers for group nodes that should have expanded state.
Definition: qgsmapthemecollection.h:202
QgsLayerTreeNode::children
QList< QgsLayerTreeNode * > children()
Gets list of children of the node. Children are owned by the parent.
Definition: qgslayertreenode.h:112
QgsMapThemeCollection::MapThemeRecord::setHasCheckedStateInfo
void setHasCheckedStateInfo(bool hasInfo)
Sets whether the map theme contains valid checked/unchecked state of group nodes.
Definition: qgsmapthemecollection.h:178
QgsMapLayerStyle::xmlData
QString xmlData() const
Returns XML content of the style.
Definition: qgsmaplayerstyle.cpp:43
QgsLayerTreeNode::name
virtual QString name() const =0
Returns name of the node.
QgsMapLayerStyleManager::setCurrentStyle
bool setCurrentStyle(const QString &name)
Set a different style as the current style - will apply it to the layer.
Definition: qgsmaplayerstylemanager.cpp:169
QgsMapLayerStyleOverride::setOverrideStyle
void setOverrideStyle(const QString &style)
Temporarily apply a different style to the layer.
Definition: qgsmaplayerstyle.cpp:110
qgslayertreemodel.h
QgsMapThemeCollection::MapThemeRecord::removeLayerRecord
void removeLayerRecord(QgsMapLayer *layer)
Removes a record for layer if present.
Definition: qgsmapthemecollection.cpp:720
QgsLayerTreeNode::parent
QgsLayerTreeNode * parent()
Gets pointer to the parent. If parent is nullptr, the node is a root node.
Definition: qgslayertreenode.h:110
QgsMapThemeCollection::setProject
void setProject(QgsProject *project)
Definition: qgsmapthemecollection.cpp:216
QgsMapThemeCollection::hasMapTheme
bool hasMapTheme(const QString &name) const
Returns whether a map theme with a matching name exists.
Definition: qgsmapthemecollection.cpp:258
QgsMapLayerStyle
Definition: qgsmaplayerstyle.h:42
qgsmaplayerlistutils.h
QgsMapThemeCollection::renameMapTheme
bool renameMapTheme(const QString &name, const QString &newName)
Renames the existing map theme called name to newName.
Definition: qgsmapthemecollection.cpp:284
QgsLayerTreeNode::isVisible
bool isVisible() const
Returns whether a node is really visible (ie checked and all its ancestors checked as well)
Definition: qgslayertreenode.cpp:98
QgsLayerTreeModel::layerLegendNodes
QList< QgsLayerTreeModelLegendNode * > layerLegendNodes(QgsLayerTreeLayer *nodeLayer, bool skipNodeEmbeddedInParent=false)
Returns filtered list of active legend nodes attached to a particular layer node (by default it retur...
Definition: qgslayertreemodel.cpp:1533
QgsMapThemeCollection::MapThemeRecord::hasCheckedStateInfo
bool hasCheckedStateInfo() const
Returns whether information about checked/unchecked state of groups has been recorded and thus whethe...
Definition: qgsmapthemecollection.h:165
QgsMapThemeCollection::MapThemeLayerRecord::setLayer
void setLayer(QgsMapLayer *layer)
Sets the map layer for this record.
Definition: qgsmapthemecollection.cpp:745
QgsLayerTreeModelLegendNode::RuleKeyRole
@ RuleKeyRole
Rule key of the node (QString)
Definition: qgslayertreemodellegendnode.h:65
QgsLayerTreeNode::setExpanded
void setExpanded(bool expanded)
Sets whether the node should be shown as expanded or collapsed in GUI.
Definition: qgslayertreenode.cpp:170
QgsLayerTree::isGroup
static bool isGroup(QgsLayerTreeNode *node)
Check whether the node is a valid group node.
Definition: qgslayertree.h:56
QgsMapThemeCollection::mapThemeState
QgsMapThemeCollection::MapThemeRecord mapThemeState(const QString &name) const
Returns the recorded state of a map theme.
Definition: qgsmapthemecollection.h:285
qgsproject.h
QgsLayerTreeNode::checkedLayers
QList< QgsMapLayer * > checkedLayers() const
Returns a list of any checked layers which belong to this node or its children.
Definition: qgslayertreenode.cpp:151
QgsMapThemeCollection::update
void update(const QString &name, const QgsMapThemeCollection::MapThemeRecord &state)
Updates a map theme within the collection.
Definition: qgsmapthemecollection.cpp:272
QgsVectorLayer::renderer
QgsFeatureRenderer * renderer()
Returns renderer.
Definition: qgsvectorlayer.h:881
QgsLayerTreeModelLegendNode
Definition: qgslayertreemodellegendnode.h:50