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