QGIS API Documentation  2.99.0-Master (6a61179)
qgscomposerlegend.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgscomposerlegend.cpp - description
3  ---------------------
4  begin : June 2008
5  copyright : (C) 2008 by Marco Hugentobler
6  email : marco dot hugentobler at karto dot baug dot ethz dot ch
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 #include <limits>
18 
19 #include "qgscomposerlegendstyle.h"
20 #include "qgscomposerlegend.h"
21 #include "qgscomposerlegenditem.h"
22 #include "qgscomposermap.h"
23 #include "qgscomposition.h"
24 #include "qgscomposermodel.h"
25 #include "qgsmaplayerregistry.h"
26 #include "qgslayertree.h"
27 #include "qgslayertreemodel.h"
28 #include "qgslegendrenderer.h"
29 #include "qgslogger.h"
30 #include "qgsmapsettings.h"
31 #include "qgsproject.h"
32 #include "qgssymbollayerutils.h"
33 #include "qgslayertreeutils.h"
34 #include <QDomDocument>
35 #include <QDomElement>
36 #include <QPainter>
37 
39  : QgsComposerItem( composition )
40  , mCustomLayerTree( nullptr )
41  , mComposerMap( nullptr )
42  , mLegendFilterByMap( false )
43  , mLegendFilterByExpression( false )
44  , mFilterOutAtlas( false )
45  , mFilterAskedForUpdate( false )
46  , mInAtlas( false )
47  , mInitialMapScaleCalculated( false )
48  , mForceResize( false )
49  , mSizeToContents( true )
50 {
51  mLegendModel = new QgsLegendModelV2( QgsProject::instance()->layerTreeRoot() );
52 
53  connect( &composition->atlasComposition(), SIGNAL( renderEnded() ), this, SLOT( onAtlasEnded() ) );
54  connect( &composition->atlasComposition(), SIGNAL( featureChanged( QgsFeature* ) ), this, SLOT( onAtlasFeature( QgsFeature* ) ) );
55 
56  // Connect to the main layertreeroot.
57  // It serves in "auto update mode" as a medium between the main app legend and this one
58  connect( QgsProject::instance()->layerTreeRoot(), SIGNAL( customPropertyChanged( QgsLayerTreeNode*, QString ) ), this, SLOT( nodeCustomPropertyChanged( QgsLayerTreeNode*, QString ) ) );
59 }
60 
62  : QgsComposerItem( nullptr )
63  , mLegendModel( nullptr )
64  , mCustomLayerTree( nullptr )
65  , mComposerMap( nullptr )
66  , mLegendFilterByMap( false )
67  , mLegendFilterByExpression( false )
68  , mFilterOutAtlas( false )
69  , mFilterAskedForUpdate( false )
70  , mInAtlas( false )
71  , mInitialMapScaleCalculated( false )
72  , mForceResize( false )
73  , mSizeToContents( true )
74 {
75 
76 }
77 
79 {
80  delete mLegendModel;
81  delete mCustomLayerTree;
82 }
83 
84 void QgsComposerLegend::paint( QPainter* painter, const QStyleOptionGraphicsItem* itemStyle, QWidget* pWidget )
85 {
86  Q_UNUSED( itemStyle );
87  Q_UNUSED( pWidget );
88 
89  if ( !painter )
90  return;
91 
92  if ( !shouldDrawItem() )
93  {
94  return;
95  }
96 
97  if ( mFilterAskedForUpdate )
98  {
99  mFilterAskedForUpdate = false;
100  doUpdateFilterByMap();
101  }
102 
103  int dpi = painter->device()->logicalDpiX();
104  double dotsPerMM = dpi / 25.4;
105 
106  if ( mComposition )
107  {
109  mSettings.setDpi( dpi );
110  }
111  if ( mComposerMap )
112  {
113  mSettings.setMmPerMapUnit( mComposerMap->mapUnitsToMM() );
114 
115  // use a temporary QgsMapSettings to find out real map scale
116  QgsMapSettings ms = mComposerMap->composition()->mapSettings();
117  ms.setOutputSize( QSizeF( mComposerMap->rect().width() * dotsPerMM, mComposerMap->rect().height() * dotsPerMM ).toSize() );
118  ms.setExtent( *mComposerMap->currentMapExtent() );
119  ms.setOutputDpi( dpi );
120  mSettings.setMapScale( ms.scale() );
121  }
122  mInitialMapScaleCalculated = true;
123 
124  QgsLegendRenderer legendRenderer( mLegendModel, mSettings );
125  legendRenderer.setLegendSize( mForceResize && mSizeToContents ? QSize() : rect().size() );
126 
127  //adjust box if width or height is too small
128  if ( mSizeToContents )
129  {
130  QSizeF size = legendRenderer.minimumSize();
131  if ( mForceResize )
132  {
133  mForceResize = false;
134  //set new rect, respecting position mode and data defined size/position
135  QRectF targetRect = QRectF( pos().x(), pos().y(), size.width(), size.height() );
136  setSceneRect( evalItemRect( targetRect, true ) );
137  }
138  else if ( size.height() > rect().height() || size.width() > rect().width() )
139  {
140  //need to resize box
141  QRectF targetRect = QRectF( pos().x(), pos().y(), rect().width(), rect().height() );
142  if ( size.height() > targetRect.height() )
143  targetRect.setHeight( size.height() );
144  if ( size.width() > rect().width() )
145  targetRect.setWidth( size.width() );
146 
147  //set new rect, respecting position mode and data defined size/position
148  setSceneRect( evalItemRect( targetRect, true ) );
149  }
150  }
151 
152  drawBackground( painter );
153  painter->save();
154  //antialiasing on
155  painter->setRenderHint( QPainter::Antialiasing, true );
156  painter->setPen( QPen( QColor( 0, 0, 0 ) ) );
157 
158  if ( !mSizeToContents )
159  {
160  // set a clip region to crop out parts of legend which don't fit
161  QRectF thisPaintRect = QRectF( 0, 0, rect().width(), rect().height() );
162  painter->setClipRect( thisPaintRect );
163  }
164 
165  legendRenderer.drawLegend( painter );
166 
167  painter->restore();
168 
169  //draw frame and selection boxes if necessary
170  drawFrame( painter );
171  if ( isSelected() )
172  {
173  drawSelectionBoxes( painter );
174  }
175 }
176 
177 QSizeF QgsComposerLegend::paintAndDetermineSize( QPainter* painter )
178 {
179  if ( mFilterAskedForUpdate )
180  {
181  mFilterAskedForUpdate = false;
182  doUpdateFilterByMap();
183  }
184 
185  QgsLegendRenderer legendRenderer( mLegendModel, mSettings );
186  QSizeF size = legendRenderer.minimumSize();
187  if ( painter )
188  legendRenderer.drawLegend( painter );
189  return size;
190 }
191 
192 
194 {
195  if ( !mSizeToContents )
196  return;
197 
198  if ( !mInitialMapScaleCalculated )
199  {
200  // this is messy - but until we have painted the item we have no knowledge of the current DPI
201  // and so cannot correctly calculate the map scale. This results in incorrect size calculations
202  // for marker symbols with size in map units, causing the legends to initially expand to huge
203  // sizes if we attempt to calculate the box size first.
204  return;
205  }
206 
207  QgsLegendRenderer legendRenderer( mLegendModel, mSettings );
208  QSizeF size = legendRenderer.minimumSize();
209  QgsDebugMsg( QString( "width = %1 height = %2" ).arg( size.width() ).arg( size.height() ) );
210  if ( size.isValid() )
211  {
212  QRectF targetRect = QRectF( pos().x(), pos().y(), size.width(), size.height() );
213  //set new rect, respecting position mode and data defined size/position
214  setSceneRect( evalItemRect( targetRect, true ) );
215  }
216 }
217 
219 {
220  mSizeToContents = enabled;
221 }
222 
224 {
225  return mSizeToContents;
226 }
227 
228 void QgsComposerLegend::setCustomLayerTree( QgsLayerTreeGroup* rootGroup )
229 {
230  mLegendModel->setRootGroup( rootGroup ? rootGroup : QgsProject::instance()->layerTreeRoot() );
231 
232  delete mCustomLayerTree;
233  mCustomLayerTree = rootGroup;
234 }
235 
236 
238 {
239  if ( autoUpdate == autoUpdateModel() )
240  return;
241 
242  setCustomLayerTree( autoUpdate ? nullptr : QgsLayerTree::toGroup( QgsProject::instance()->layerTreeRoot()->clone() ) );
243  adjustBoxSize();
244  updateItem();
245 }
246 
247 void QgsComposerLegend::nodeCustomPropertyChanged( QgsLayerTreeNode*, const QString& )
248 {
249  if ( autoUpdateModel() )
250  {
251  // in "auto update" mode, some parameters on the main app legend may have been changed (expression filtering)
252  // we must then call updateItem to reflect the changes
253  updateItem();
254  }
255 }
256 
258 {
259  return !mCustomLayerTree;
260 }
261 
263 {
264  mLegendFilterByMap = enabled;
265  updateItem();
266 }
267 
268 void QgsComposerLegend::setTitle( const QString& t )
269 {
270  mSettings.setTitle( t );
271 
272  if ( mComposition && id().isEmpty() )
273  {
274  //notify the model that the display name has changed
276  }
277 }
278 QString QgsComposerLegend::title() const { return mSettings.title(); }
279 
280 Qt::AlignmentFlag QgsComposerLegend::titleAlignment() const { return mSettings.titleAlignment(); }
281 void QgsComposerLegend::setTitleAlignment( Qt::AlignmentFlag alignment ) { mSettings.setTitleAlignment( alignment ); }
282 
286 
287 QFont QgsComposerLegend::styleFont( QgsComposerLegendStyle::Style s ) const { return mSettings.style( s ).font(); }
289 
292 
293 double QgsComposerLegend::boxSpace() const { return mSettings.boxSpace(); }
294 void QgsComposerLegend::setBoxSpace( double s ) { mSettings.setBoxSpace( s ); }
295 
296 double QgsComposerLegend::columnSpace() const { return mSettings.columnSpace(); }
297 void QgsComposerLegend::setColumnSpace( double s ) { mSettings.setColumnSpace( s ); }
298 
299 QColor QgsComposerLegend::fontColor() const { return mSettings.fontColor(); }
300 void QgsComposerLegend::setFontColor( const QColor& c ) { mSettings.setFontColor( c ); }
301 
302 double QgsComposerLegend::symbolWidth() const { return mSettings.symbolSize().width(); }
303 void QgsComposerLegend::setSymbolWidth( double w ) { mSettings.setSymbolSize( QSizeF( w, mSettings.symbolSize().height() ) ); }
304 
305 double QgsComposerLegend::symbolHeight() const { return mSettings.symbolSize().height(); }
306 void QgsComposerLegend::setSymbolHeight( double h ) { mSettings.setSymbolSize( QSizeF( mSettings.symbolSize().width(), h ) ); }
307 
308 double QgsComposerLegend::wmsLegendWidth() const { return mSettings.wmsLegendSize().width(); }
309 void QgsComposerLegend::setWmsLegendWidth( double w ) { mSettings.setWmsLegendSize( QSizeF( w, mSettings.wmsLegendSize().height() ) ); }
310 
311 double QgsComposerLegend::wmsLegendHeight() const {return mSettings.wmsLegendSize().height(); }
312 void QgsComposerLegend::setWmsLegendHeight( double h ) { mSettings.setWmsLegendSize( QSizeF( mSettings.wmsLegendSize().width(), h ) ); }
313 
314 void QgsComposerLegend::setWrapChar( const QString& t ) { mSettings.setWrapChar( t ); }
315 QString QgsComposerLegend::wrapChar() const {return mSettings.wrapChar(); }
316 
317 int QgsComposerLegend::columnCount() const { return mSettings.columnCount(); }
318 void QgsComposerLegend::setColumnCount( int c ) { mSettings.setColumnCount( c ); }
319 
320 bool QgsComposerLegend::splitLayer() const { return mSettings.splitLayer(); }
321 void QgsComposerLegend::setSplitLayer( bool s ) { mSettings.setSplitLayer( s ); }
322 
323 bool QgsComposerLegend::equalColumnWidth() const { return mSettings.equalColumnWidth(); }
325 
326 bool QgsComposerLegend::drawRasterBorder() const { return mSettings.drawRasterBorder(); }
327 void QgsComposerLegend::setDrawRasterBorder( bool enabled ) { mSettings.setDrawRasterBorder( enabled ); }
328 
329 QColor QgsComposerLegend::rasterBorderColor() const { return mSettings.rasterBorderColor(); }
330 void QgsComposerLegend::setRasterBorderColor( const QColor& color ) { mSettings.setRasterBorderColor( color ); }
331 
332 double QgsComposerLegend::rasterBorderWidth() const { return mSettings.rasterBorderWidth(); }
333 void QgsComposerLegend::setRasterBorderWidth( double width ) { mSettings.setRasterBorderWidth( width ); }
334 
336 {
337  adjustBoxSize();
338  updateItem();
339 }
340 
342 {
343  adjustBoxSize();
344  updateItem();
345 }
346 
348 {
349  updateFilterByMap( false );
351 }
352 
353 bool QgsComposerLegend::writeXml( QDomElement& elem, QDomDocument & doc ) const
354 {
355  if ( elem.isNull() )
356  {
357  return false;
358  }
359 
360  QDomElement composerLegendElem = doc.createElement( QStringLiteral( "ComposerLegend" ) );
361  elem.appendChild( composerLegendElem );
362 
363  //write general properties
364  composerLegendElem.setAttribute( QStringLiteral( "title" ), mSettings.title() );
365  composerLegendElem.setAttribute( QStringLiteral( "titleAlignment" ), QString::number( static_cast< int >( mSettings.titleAlignment() ) ) );
366  composerLegendElem.setAttribute( QStringLiteral( "columnCount" ), QString::number( mSettings.columnCount() ) );
367  composerLegendElem.setAttribute( QStringLiteral( "splitLayer" ), QString::number( mSettings.splitLayer() ) );
368  composerLegendElem.setAttribute( QStringLiteral( "equalColumnWidth" ), QString::number( mSettings.equalColumnWidth() ) );
369 
370  composerLegendElem.setAttribute( QStringLiteral( "boxSpace" ), QString::number( mSettings.boxSpace() ) );
371  composerLegendElem.setAttribute( QStringLiteral( "columnSpace" ), QString::number( mSettings.columnSpace() ) );
372 
373  composerLegendElem.setAttribute( QStringLiteral( "symbolWidth" ), QString::number( mSettings.symbolSize().width() ) );
374  composerLegendElem.setAttribute( QStringLiteral( "symbolHeight" ), QString::number( mSettings.symbolSize().height() ) );
375 
376  composerLegendElem.setAttribute( QStringLiteral( "rasterBorder" ), mSettings.drawRasterBorder() );
377  composerLegendElem.setAttribute( QStringLiteral( "rasterBorderColor" ), QgsSymbolLayerUtils::encodeColor( mSettings.rasterBorderColor() ) );
378  composerLegendElem.setAttribute( QStringLiteral( "rasterBorderWidth" ), QString::number( mSettings.rasterBorderWidth() ) );
379 
380  composerLegendElem.setAttribute( QStringLiteral( "wmsLegendWidth" ), QString::number( mSettings.wmsLegendSize().width() ) );
381  composerLegendElem.setAttribute( QStringLiteral( "wmsLegendHeight" ), QString::number( mSettings.wmsLegendSize().height() ) );
382  composerLegendElem.setAttribute( QStringLiteral( "wrapChar" ), mSettings.wrapChar() );
383  composerLegendElem.setAttribute( QStringLiteral( "fontColor" ), mSettings.fontColor().name() );
384 
385  composerLegendElem.setAttribute( QStringLiteral( "resizeToContents" ), mSizeToContents );
386 
387  if ( mComposerMap )
388  {
389  composerLegendElem.setAttribute( QStringLiteral( "map" ), mComposerMap->id() );
390  }
391 
392  QDomElement composerLegendStyles = doc.createElement( QStringLiteral( "styles" ) );
393  composerLegendElem.appendChild( composerLegendStyles );
394 
395  style( QgsComposerLegendStyle::Title ).writeXml( QStringLiteral( "title" ), composerLegendStyles, doc );
396  style( QgsComposerLegendStyle::Group ).writeXml( QStringLiteral( "group" ), composerLegendStyles, doc );
397  style( QgsComposerLegendStyle::Subgroup ).writeXml( QStringLiteral( "subgroup" ), composerLegendStyles, doc );
398  style( QgsComposerLegendStyle::Symbol ).writeXml( QStringLiteral( "symbol" ), composerLegendStyles, doc );
399  style( QgsComposerLegendStyle::SymbolLabel ).writeXml( QStringLiteral( "symbolLabel" ), composerLegendStyles, doc );
400 
401  if ( mCustomLayerTree )
402  {
403  // if not using auto-update - store the custom layer tree
404  mCustomLayerTree->writeXml( composerLegendElem );
405  }
406 
407  if ( mLegendFilterByMap )
408  {
409  composerLegendElem.setAttribute( QStringLiteral( "legendFilterByMap" ), QStringLiteral( "1" ) );
410  }
411 
412  return _writeXml( composerLegendElem, doc );
413 }
414 
415 static void _readOldLegendGroup( QDomElement& elem, QgsLayerTreeGroup* parentGroup )
416 {
417  QDomElement itemElem = elem.firstChildElement();
418 
419  while ( !itemElem.isNull() )
420  {
421 
422  if ( itemElem.tagName() == QLatin1String( "LayerItem" ) )
423  {
424  QString layerId = itemElem.attribute( QStringLiteral( "layerId" ) );
425  if ( QgsMapLayer* layer = QgsMapLayerRegistry::instance()->mapLayer( layerId ) )
426  {
427  QgsLayerTreeLayer* nodeLayer = parentGroup->addLayer( layer );
428  QString userText = itemElem.attribute( QStringLiteral( "userText" ) );
429  if ( !userText.isEmpty() )
430  nodeLayer->setCustomProperty( QStringLiteral( "legend/title-label" ), userText );
431  QString style = itemElem.attribute( QStringLiteral( "style" ) );
432  if ( !style.isEmpty() )
433  nodeLayer->setCustomProperty( QStringLiteral( "legend/title-style" ), style );
434  QString showFeatureCount = itemElem.attribute( QStringLiteral( "showFeatureCount" ) );
435  if ( showFeatureCount.toInt() )
436  nodeLayer->setCustomProperty( QStringLiteral( "showFeatureCount" ), 1 );
437 
438  // support for individual legend items (user text, order) not implemented yet
439  }
440  }
441  else if ( itemElem.tagName() == QLatin1String( "GroupItem" ) )
442  {
443  QgsLayerTreeGroup* nodeGroup = parentGroup->addGroup( itemElem.attribute( QStringLiteral( "userText" ) ) );
444  QString style = itemElem.attribute( QStringLiteral( "style" ) );
445  if ( !style.isEmpty() )
446  nodeGroup->setCustomProperty( QStringLiteral( "legend/title-style" ), style );
447 
448  _readOldLegendGroup( itemElem, nodeGroup );
449  }
450 
451  itemElem = itemElem.nextSiblingElement();
452  }
453 }
454 
455 bool QgsComposerLegend::readXml( const QDomElement& itemElem, const QDomDocument& doc )
456 {
457  if ( itemElem.isNull() )
458  {
459  return false;
460  }
461 
462  //read general properties
463  mSettings.setTitle( itemElem.attribute( QStringLiteral( "title" ) ) );
464  if ( !itemElem.attribute( QStringLiteral( "titleAlignment" ) ).isEmpty() )
465  {
466  mSettings.setTitleAlignment( static_cast< Qt::AlignmentFlag >( itemElem.attribute( QStringLiteral( "titleAlignment" ) ).toInt() ) );
467  }
468  int colCount = itemElem.attribute( QStringLiteral( "columnCount" ), QStringLiteral( "1" ) ).toInt();
469  if ( colCount < 1 ) colCount = 1;
470  mSettings.setColumnCount( colCount );
471  mSettings.setSplitLayer( itemElem.attribute( QStringLiteral( "splitLayer" ), QStringLiteral( "0" ) ).toInt() == 1 );
472  mSettings.setEqualColumnWidth( itemElem.attribute( QStringLiteral( "equalColumnWidth" ), QStringLiteral( "0" ) ).toInt() == 1 );
473 
474  QDomNodeList stylesNodeList = itemElem.elementsByTagName( QStringLiteral( "styles" ) );
475  if ( !stylesNodeList.isEmpty() )
476  {
477  QDomNode stylesNode = stylesNodeList.at( 0 );
478  for ( int i = 0; i < stylesNode.childNodes().size(); i++ )
479  {
480  QDomElement styleElem = stylesNode.childNodes().at( i ).toElement();
482  style.readXml( styleElem, doc );
483  QString name = styleElem.attribute( QStringLiteral( "name" ) );
485  if ( name == QLatin1String( "title" ) ) s = QgsComposerLegendStyle::Title;
486  else if ( name == QLatin1String( "group" ) ) s = QgsComposerLegendStyle::Group;
487  else if ( name == QLatin1String( "subgroup" ) ) s = QgsComposerLegendStyle::Subgroup;
488  else if ( name == QLatin1String( "symbol" ) ) s = QgsComposerLegendStyle::Symbol;
489  else if ( name == QLatin1String( "symbolLabel" ) ) s = QgsComposerLegendStyle::SymbolLabel;
490  else continue;
491  setStyle( s, style );
492  }
493  }
494 
495  //font color
496  QColor fontClr;
497  fontClr.setNamedColor( itemElem.attribute( QStringLiteral( "fontColor" ), QStringLiteral( "#000000" ) ) );
498  mSettings.setFontColor( fontClr );
499 
500  //spaces
501  mSettings.setBoxSpace( itemElem.attribute( QStringLiteral( "boxSpace" ), QStringLiteral( "2.0" ) ).toDouble() );
502  mSettings.setColumnSpace( itemElem.attribute( QStringLiteral( "columnSpace" ), QStringLiteral( "2.0" ) ).toDouble() );
503 
504  mSettings.setSymbolSize( QSizeF( itemElem.attribute( QStringLiteral( "symbolWidth" ), QStringLiteral( "7.0" ) ).toDouble(), itemElem.attribute( QStringLiteral( "symbolHeight" ), QStringLiteral( "14.0" ) ).toDouble() ) );
505  mSettings.setWmsLegendSize( QSizeF( itemElem.attribute( QStringLiteral( "wmsLegendWidth" ), QStringLiteral( "50" ) ).toDouble(), itemElem.attribute( QStringLiteral( "wmsLegendHeight" ), QStringLiteral( "25" ) ).toDouble() ) );
506 
507  mSettings.setDrawRasterBorder( itemElem.attribute( QStringLiteral( "rasterBorder" ), QStringLiteral( "1" ) ) != QLatin1String( "0" ) );
508  mSettings.setRasterBorderColor( QgsSymbolLayerUtils::decodeColor( itemElem.attribute( QStringLiteral( "rasterBorderColor" ), QStringLiteral( "0,0,0" ) ) ) );
509  mSettings.setRasterBorderWidth( itemElem.attribute( QStringLiteral( "rasterBorderWidth" ), QStringLiteral( "0" ) ).toDouble() );
510 
511  mSettings.setWrapChar( itemElem.attribute( QStringLiteral( "wrapChar" ) ) );
512 
513  mSizeToContents = itemElem.attribute( QStringLiteral( "resizeToContents" ), QStringLiteral( "1" ) ) != QLatin1String( "0" );
514 
515  //composer map
516  mLegendFilterByMap = itemElem.attribute( QStringLiteral( "legendFilterByMap" ), QStringLiteral( "0" ) ).toInt();
517  if ( !itemElem.attribute( QStringLiteral( "map" ) ).isEmpty() )
518  {
519  setComposerMap( mComposition->getComposerMapById( itemElem.attribute( QStringLiteral( "map" ) ).toInt() ) );
520  }
521 
522  QDomElement oldLegendModelElem = itemElem.firstChildElement( QStringLiteral( "Model" ) );
523  if ( !oldLegendModelElem.isNull() )
524  {
525  // QGIS <= 2.4
526  QgsLayerTreeGroup* nodeRoot = new QgsLayerTreeGroup();
527  _readOldLegendGroup( oldLegendModelElem, nodeRoot );
528  setCustomLayerTree( nodeRoot );
529  }
530  else
531  {
532  // QGIS >= 2.6
533  QDomElement layerTreeElem = itemElem.firstChildElement( QStringLiteral( "layer-tree-group" ) );
534  setCustomLayerTree( QgsLayerTreeGroup::readXml( layerTreeElem ) );
535  }
536 
537  //restore general composer item properties
538  QDomNodeList composerItemList = itemElem.elementsByTagName( QStringLiteral( "ComposerItem" ) );
539  if ( !composerItemList.isEmpty() )
540  {
541  QDomElement composerItemElem = composerItemList.at( 0 ).toElement();
542  _readXml( composerItemElem, doc );
543  }
544 
545  // < 2.0 projects backward compatibility >>>>>
546  //title font
547  QString titleFontString = itemElem.attribute( QStringLiteral( "titleFont" ) );
548  if ( !titleFontString.isEmpty() )
549  {
550  rstyle( QgsComposerLegendStyle::Title ).rfont().fromString( titleFontString );
551  }
552  //group font
553  QString groupFontString = itemElem.attribute( QStringLiteral( "groupFont" ) );
554  if ( !groupFontString.isEmpty() )
555  {
556  rstyle( QgsComposerLegendStyle::Group ).rfont().fromString( groupFontString );
557  }
558 
559  //layer font
560  QString layerFontString = itemElem.attribute( QStringLiteral( "layerFont" ) );
561  if ( !layerFontString.isEmpty() )
562  {
563  rstyle( QgsComposerLegendStyle::Subgroup ).rfont().fromString( layerFontString );
564  }
565  //item font
566  QString itemFontString = itemElem.attribute( QStringLiteral( "itemFont" ) );
567  if ( !itemFontString.isEmpty() )
568  {
569  rstyle( QgsComposerLegendStyle::SymbolLabel ).rfont().fromString( itemFontString );
570  }
571 
572  if ( !itemElem.attribute( QStringLiteral( "groupSpace" ) ).isEmpty() )
573  {
574  rstyle( QgsComposerLegendStyle::Group ).setMargin( QgsComposerLegendStyle::Top, itemElem.attribute( QStringLiteral( "groupSpace" ), QStringLiteral( "3.0" ) ).toDouble() );
575  }
576  if ( !itemElem.attribute( QStringLiteral( "layerSpace" ) ).isEmpty() )
577  {
578  rstyle( QgsComposerLegendStyle::Subgroup ).setMargin( QgsComposerLegendStyle::Top, itemElem.attribute( QStringLiteral( "layerSpace" ), QStringLiteral( "3.0" ) ).toDouble() );
579  }
580  if ( !itemElem.attribute( QStringLiteral( "symbolSpace" ) ).isEmpty() )
581  {
582  rstyle( QgsComposerLegendStyle::Symbol ).setMargin( QgsComposerLegendStyle::Top, itemElem.attribute( QStringLiteral( "symbolSpace" ), QStringLiteral( "2.0" ) ).toDouble() );
583  rstyle( QgsComposerLegendStyle::SymbolLabel ).setMargin( QgsComposerLegendStyle::Top, itemElem.attribute( QStringLiteral( "symbolSpace" ), QStringLiteral( "2.0" ) ).toDouble() );
584  }
585  // <<<<<<< < 2.0 projects backward compatibility
586 
587  emit itemChanged();
588  return true;
589 }
590 
592 {
593  if ( !id().isEmpty() )
594  {
595  return id();
596  }
597 
598  //if no id, default to portion of title text
599  QString text = mSettings.title();
600  if ( text.isEmpty() )
601  {
602  return tr( "<legend>" );
603  }
604  if ( text.length() > 25 )
605  {
606  return QString( tr( "%1..." ) ).arg( text.left( 25 ) );
607  }
608  else
609  {
610  return text;
611  }
612 }
613 
615 {
616  if ( mComposerMap )
617  {
618  disconnect( mComposerMap, SIGNAL( destroyed( QObject* ) ), this, SLOT( invalidateCurrentMap() ) );
619  disconnect( mComposerMap, SIGNAL( itemChanged() ), this, SLOT( updateFilterByMap() ) );
620  disconnect( mComposerMap, SIGNAL( extentChanged() ), this, SLOT( updateFilterByMap() ) );
621  disconnect( mComposerMap, SIGNAL( layerStyleOverridesChanged() ), this, SLOT( mapLayerStyleOverridesChanged() ) );
622  }
623 
624  mComposerMap = map;
625 
626  if ( map )
627  {
628  QObject::connect( map, SIGNAL( destroyed( QObject* ) ), this, SLOT( invalidateCurrentMap() ) );
629  QObject::connect( map, SIGNAL( itemChanged() ), this, SLOT( updateFilterByMap() ) );
630  QObject::connect( map, SIGNAL( extentChanged() ), this, SLOT( updateFilterByMap() ) );
631  QObject::connect( map, SIGNAL( layerStyleOverridesChanged() ), this, SLOT( mapLayerStyleOverridesChanged() ) );
632  }
633 
634  updateItem();
635 }
636 
638 {
639  setComposerMap( nullptr );
640 }
641 
642 void QgsComposerLegend::mapLayerStyleOverridesChanged()
643 {
644  if ( !mComposerMap )
645  return;
646 
647  // map's style has been changed, so make sure to update the legend here
648  if ( mLegendFilterByMap )
649  {
650  // legend is being filtered by map, so we need to re run the hit test too
651  // as the style overrides may also have affected the visible symbols
652  updateFilterByMap( false );
653  }
654  else
655  {
656  mLegendModel->setLayerStyleOverrides( mComposerMap->layerStyleOverrides() );
657 
658  Q_FOREACH ( QgsLayerTreeLayer* nodeLayer, mLegendModel->rootGroup()->findLayers() )
659  mLegendModel->refreshLayerLegend( nodeLayer );
660  }
661 
662  adjustBoxSize();
663  updateItem();
664 }
665 
666 void QgsComposerLegend::updateFilterByMap( bool redraw )
667 {
668  if ( isRemoved() )
669  return;
670  // ask for update
671  // the actual update will take place before the redraw.
672  // This is to avoid multiple calls to the filter
673  mFilterAskedForUpdate = true;
674 
675  if ( redraw )
677 }
678 
679 void QgsComposerLegend::doUpdateFilterByMap()
680 {
681  if ( mComposerMap )
682  mLegendModel->setLayerStyleOverrides( mComposerMap->layerStyleOverrides() );
683  else
684  mLegendModel->setLayerStyleOverrides( QMap<QString, QString>() );
685 
686 
687  bool filterByExpression = QgsLayerTreeUtils::hasLegendFilterExpression( *( mCustomLayerTree ? mCustomLayerTree : QgsProject::instance()->layerTreeRoot() ) );
688 
689  if ( mComposerMap && ( mLegendFilterByMap || filterByExpression || mInAtlas ) )
690  {
691  int dpi = mComposition->printResolution();
692 
693  QgsRectangle requestRectangle;
694  mComposerMap->requestedExtent( requestRectangle );
695 
696  QSizeF theSize( requestRectangle.width(), requestRectangle.height() );
697  theSize *= mComposerMap->mapUnitsToMM() * dpi / 25.4;
698 
699  QgsMapSettings ms = mComposerMap->mapSettings( requestRectangle, theSize, dpi );
700 
701  QgsGeometry filterPolygon;
702  if ( mInAtlas )
703  {
704  filterPolygon = composition()->atlasComposition().currentGeometry( composition()->mapSettings().destinationCrs() );
705  }
706  mLegendModel->setLegendFilter( &ms, /* useExtent */ mInAtlas || mLegendFilterByMap, filterPolygon, /* useExpressions */ true );
707  }
708  else
709  mLegendModel->setLegendFilterByMap( nullptr );
710 
711  mForceResize = true;
712 }
713 
715 {
716  mFilterOutAtlas = doFilter;
717 }
718 
720 {
721  return mFilterOutAtlas;
722 }
723 
724 void QgsComposerLegend::onAtlasFeature( QgsFeature* feat )
725 {
726  if ( !feat )
727  return;
728  mInAtlas = mFilterOutAtlas;
729  updateFilterByMap();
730 }
731 
732 void QgsComposerLegend::onAtlasEnded()
733 {
734  mInAtlas = false;
735  updateFilterByMap();
736 }
737 
738 // -------------------------------------------------------------------------
740 #include "qgsvectorlayer.h"
741 
743  : QgsLayerTreeModel( rootNode, parent )
744 {
747 }
748 
749 QVariant QgsLegendModelV2::data( const QModelIndex& index, int role ) const
750 {
751  // handle custom layer node labels
752  if ( QgsLayerTreeNode* node = index2node( index ) )
753  {
754  if ( QgsLayerTree::isLayer( node ) && ( role == Qt::DisplayRole || role == Qt::EditRole ) && !node->customProperty( QStringLiteral( "legend/title-label" ) ).isNull() )
755  {
756  QgsLayerTreeLayer* nodeLayer = QgsLayerTree::toLayer( node );
757  QString name = node->customProperty( QStringLiteral( "legend/title-label" ) ).toString();
758  if ( nodeLayer->customProperty( QStringLiteral( "showFeatureCount" ), 0 ).toInt() && role == Qt::DisplayRole )
759  {
760  QgsVectorLayer* vlayer = qobject_cast<QgsVectorLayer*>( nodeLayer->layer() );
761  if ( vlayer && vlayer->featureCount() >= 0 )
762  name += QStringLiteral( " [%1]" ).arg( vlayer->featureCount() );
763  }
764  return name;
765  }
766  }
767 
768  return QgsLayerTreeModel::data( index, role );
769 }
770 
771 Qt::ItemFlags QgsLegendModelV2::flags( const QModelIndex& index ) const
772 {
773  // make the legend nodes selectable even if they are not by default
774  if ( index2legendNode( index ) )
775  return QgsLayerTreeModel::flags( index ) | Qt::ItemIsSelectable;
776 
777  return QgsLayerTreeModel::flags( index );
778 }
void setTitleAlignment(Qt::AlignmentFlag alignment)
Sets the alignment of the legend title.
void setWrapChar(const QString &t)
Layer tree group node serves as a container for layers and further groups.
void setLegendSize(QSizeF s)
Set the preferred resulting legend size.
QgsMapSettings mapSettings(const QgsRectangle &extent, QSizeF size, int dpi) const
Return map settings that would be used for drawing of the map.
QgsComposerLegend(QgsComposition *composition)
A rectangle specified with double values.
Definition: qgsrectangle.h:35
Base class for all map layer types.
Definition: qgsmaplayer.h:49
void setExtent(const QgsRectangle &rect, bool magnified=true)
Set coordinates of the rectangle which should be rendered.
QgsLayerTreeGroup * rootGroup() const
Return pointer to the root node of the layer tree. Always a non-null pointer.
double mapUnitsToMM() const
Returns the conversion factor map units -> mm.
bool readXml(const QDomElement &itemElem, const QDomDocument &doc) override
Sets state from Dom document.
void setEqualColumnWidth(bool s)
QgsComposerLegendStyle style(QgsComposerLegendStyle::Style s) const
Returns style.
QgsLayerTreeGroup * addGroup(const QString &name)
Append a new group node with given name. Newly created node is owned by this group.
const QgsComposerMap * getComposerMapById(const int id) const
Returns the composer map with specified id.
void setBoxSpace(double s)
void setMmPerMapUnit(double mmPerMapUnit)
bool shouldDrawItem() const
Returns whether the item should be drawn in the current context.
int columnCount() const
void setDrawRasterBorder(bool enabled)
Sets whether a border will be drawn around raster symbol items.
QgsComposerLegendStyle & rstyle(QgsComposerLegendStyle::Style s)
Returns reference to modifiable style.
void setMargin(Side side, double margin)
Item model implementation based on layer tree model for composer legend.
QString wrapChar() const
void setSplitLayer(bool s)
void paint(QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget) override
Reimplementation of QCanvasItem::paint.
QgsComposerModel * itemsModel()
Returns the items model attached to the composition.
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
void itemChanged()
Emitted when the item changes.
void setSymbolSize(QSizeF s)
void drawLegend(QPainter *painter)
Draw the legend with given painter.
int printResolution() const
void setColumnSpace(double s)
A item that forms part of a map composition.
virtual void writeXml(QDomElement &parentElement) override
Write group (tree) as XML element <layer-tree-group> and add it to the given parent element...
double rasterBorderWidth() const
Returns the border width (in millimeters) for the border drawn around raster symbol items...
void setOutputDpi(double dpi)
Set DPI used for conversion between real world units (e.g. mm) and pixels.
QRectF evalItemRect(const QRectF &newRect, const bool resizeOnly=false, const QgsExpressionContext *context=nullptr)
Evaluates an item&#39;s bounding rect to consider data defined position and size of item and reference po...
int id() const
Get identification number.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:78
QVariant data(const QModelIndex &index, int role) const override
QColor rasterBorderColor() const
Returns the border color for the border drawn around raster symbol items.
bool writeXml(QDomElement &elem, QDomDocument &doc) const override
Stores state in Dom node.
static QgsLayerTreeGroup * readXml(QDomElement &element)
Read group (tree) from XML element <layer-tree-group> and return the newly created group (or null on ...
virtual void drawFrame(QPainter *p)
Draw black frame around item.
void setTitle(const QString &t)
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:135
void updateLegend()
Updates the model and all legend entries.
void updateItemDisplayName(QgsComposerItem *item)
Must be called when an item&#39;s display name is modified.
QgsLayerTreeGroup * toGroup(QgsLayerTreeNode *node)
Cast node to a group. No type checking is done - use isGroup() to find out whether this operation is ...
Definition: qgslayertree.h:46
void setWmsLegendHeight(double h)
Allow check boxes for legend nodes (if supported by layer&#39;s legend)
void setStyle(QgsComposerLegendStyle::Style s, const QgsComposerLegendStyle &style)
void setResizeToContents(bool enabled)
Sets whether the legend should automatically resize to fit its contents.
QgsComposerLegendStyle & rstyle(QgsComposerLegendStyle::Style s)
Returns reference to modifiable style.
QgsLayerTreeLayer * addLayer(QgsMapLayer *layer)
Append a new layer node for given map layer. Newly created node is owned by this group.
void adjustBoxSize()
Sets item box to the whole content.
The QgsMapSettings class contains configuration for rendering of the map.
static QString encodeColor(const QColor &color)
The QgsLayerTreeModel class is model implementation for Qt item views framework.
QSizeF wmsLegendSize() const
void setOutputSize(QSize size)
Set the size of the resulting map image.
bool _writeXml(QDomElement &itemElem, QDomDocument &doc) const
Writes parameter that are not subclass specific in document. Usually called from writeXml methods of ...
void setRasterBorderWidth(double width)
Sets the border width for the border drawn around raster symbol items.
void setRasterBorderWidth(double width)
Sets the border width for the border drawn around raster symbol items.
virtual void updateItem()
Updates item, with the possibility to do custom update for subclasses.
bool equalColumnWidth() const
void setSymbolHeight(double h)
double rasterBorderWidth() const
Returns the border width (in millimeters) for the border drawn around raster symbol items...
void setLegendFilterOutAtlas(bool doFilter)
When set to true, during an atlas rendering, it will filter out legend elements where features are ou...
virtual void drawSelectionBoxes(QPainter *p)
Draws additional graphics on selected items.
double scale() const
Return the calculated scale of the map.
void setLegendFilterByMap(const QgsMapSettings *settings)
Force only display of legend nodes which are valid for given map settings.
long featureCount(const QString &legendKey) const
Number of features rendered with specified legend key.
double width() const
Width of the rectangle.
Definition: qgsrectangle.h:211
static QgsLayerTreeModelLegendNode * index2legendNode(const QModelIndex &index)
Return legend node for given index.
bool useAdvancedEffects() const
Returns true if a composition should use advanced effects such as blend modes.
void setMapScale(double scale)
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...
QColor fontColor() const
void setComposerMap(const QgsComposerMap *map)
QgsComposerLegendStyle style(QgsComposerLegendStyle::Style s) const
Returns style.
void synchronizeWithModel()
Data changed.
void setStyleFont(QgsComposerLegendStyle::Style s, const QFont &f)
Set style font.
void setTitle(const QString &t)
This class is a base class for nodes in a layer tree.
QMap< QString, QString > layerStyleOverrides() const
Getter for stored overrides of styles for layers.
double symbolWidth() const
Qt::AlignmentFlag titleAlignment() const
Returns the alignment of the legend title.
double wmsLegendHeight() const
void setDrawRasterBorder(bool enabled)
Sets whether a border will be drawn around raster symbol items.
void setDpi(int dpi)
double boxSpace() const
QString wrapChar() const
void setFontColor(const QColor &c)
static bool hasLegendFilterExpression(const QgsLayerTreeGroup &group)
Test if one of the layers in a group has an expression filter.
bool isLayer(QgsLayerTreeNode *node)
Check whether the node is a valid layer node.
Definition: qgslayertree.h:40
bool _readXml(const QDomElement &itemElem, const QDomDocument &doc)
Reads parameter that are not subclass specific in document. Usually called from readXml methods of su...
void setColumnSpace(double s)
void setFlag(Flag f, bool on=true)
Enable or disable a model flag.
Graphics scene for map printing.
virtual void updateItem() override
Update() overloading.
QSizeF minimumSize()
Run the layout algorithm and determine the size required for legend.
void setLegendFilterByMapEnabled(bool enabled)
Set whether legend items should be filtered to show just the ones visible in the associated map...
const QgsMapSettings & mapSettings() const
Return setting of QGIS map canvas.
Object representing map window.
void setSymbolWidth(double w)
QSizeF paintAndDetermineSize(QPainter *painter)
Paints the legend and calculates its size. If painter is 0, only size is calculated.
void setRasterBorderColor(const QColor &color)
Sets the border color for the border drawn around raster symbol items.
void setRootGroup(QgsLayerTreeGroup *newRootGroup)
Reset the model and use a new root group node.
void invalidateCurrentMap()
Sets mCompositionMap to 0 if the map is deleted.
virtual QString displayName() const override
Get item display name.
bool autoUpdateModel() const
void refreshLayerLegend(QgsLayerTreeLayer *nodeLayer)
Force a refresh of legend nodes of a layer node.
virtual bool isRemoved() const
Returns whether this item has been removed from the composition.
void setWmsLegendWidth(double w)
QSizeF symbolSize() const
bool resizeToContents() const
Returns whether the legend should automatically resize to fit its contents.
QgsMapLayer * layer() const
double symbolHeight() const
QgsLegendModelV2(QgsLayerTreeGroup *rootNode, QObject *parent=nullptr)
QgsComposition * mComposition
Composer legend components style.
void setWmsLegendSize(QSizeF s)
QgsGeometry currentGeometry(const QgsCoordinateReferenceSystem &projectedTo=QgsCoordinateReferenceSystem()) const
Returns the current atlas geometry in the given projection system (default to the coverage layer&#39;s CR...
void setUseAdvancedEffects(bool use)
static QgsMapLayerRegistry * instance()
Returns the instance pointer, creating the object on the first call.
void setAutoUpdateModel(bool autoUpdate)
QgsLayerTreeNode * index2node(const QModelIndex &index) const
Return layer tree node for given index.
void setStyleMargin(QgsComposerLegendStyle::Style s, double margin)
Set style margin.
const QgsComposition * composition() const
Returns the composition the item is attached to.
QgsLayerTreeLayer * toLayer(QgsLayerTreeNode *node)
Cast node to a layer. No type checking is done - use isLayer() to find out whether this operation is ...
Definition: qgslayertree.h:52
void setWrapChar(const QString &t)
bool equalColumnWidth() const
virtual void drawBackground(QPainter *p)
Draw background.
void writeXml(const QString &name, QDomElement &elem, QDomDocument &doc) const
void setColumnCount(int c)
double columnSpace() const
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:348
void requestedExtent(QgsRectangle &extent) const
Calculates the extent to request and the yShift of the top-left point in case of rotation.
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const override
virtual void setSceneRect(const QRectF &rectangle)
Sets this items bound in scene coordinates such that 1 item size units corresponds to 1 scene size un...
void setEqualColumnWidth(bool s)
QString title() const
void setTitleAlignment(Qt::AlignmentFlag alignment)
Sets the alignment of the legend title.
void setStyle(QgsComposerLegendStyle::Style s, const QgsComposerLegendStyle &style)
QgsAtlasComposition & atlasComposition()
double wmsLegendWidth() const
void readXml(const QDomElement &elem, const QDomDocument &doc)
Flags flags() const
Return OR-ed combination of model flags.
void setLayerStyleOverrides(const QMap< QString, QString > &overrides)
Set map of map layer style overrides (key: layer ID, value: style name) where a different style shoul...
QList< QgsLayerTreeLayer * > findLayers() const
Find all layer nodes. Searches recursively the whole sub-tree.
QgsComposerItem(QgsComposition *composition, bool manageZValue=true)
Constructor.
QColor rasterBorderColor() const
Returns the border color for the border drawn around raster symbol items.
void setFontColor(const QColor &c)
bool legendFilterOutAtlas() const
Whether to filter out legend elements outside of the current atlas feature.
QColor fontColor() const
void setLegendFilter(const QgsMapSettings *settings, bool useExtent=true, const QgsGeometry &polygon=QgsGeometry(), bool useExpressions=true)
Filter display of legend nodes for given map settings.
Represents a vector layer which manages a vector based data sets.
bool drawRasterBorder() const
Returns whether a border will be drawn around raster symbol items.
bool splitLayer() const
QString id() const
Get item&#39;s id (which is not necessarly unique)
const QgsRectangle * currentMapExtent() const
Returns a pointer to the current map extent, which is either the original user specified extent or th...
void setBoxSpace(double s)
Allow reordering with drag&#39;n&#39;drop.
double columnSpace() const
static void _readOldLegendGroup(QDomElement &elem, QgsLayerTreeGroup *parentGroup)
void setSplitLayer(bool s)
bool drawRasterBorder() const
Returns whether a border will be drawn around raster symbol items.
static QColor decodeColor(const QString &str)
void setRasterBorderColor(const QColor &color)
Sets the border color for the border drawn around raster symbol items.
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
double height() const
Height of the rectangle.
Definition: qgsrectangle.h:216
void setCustomProperty(const QString &key, const QVariant &value)
Set a custom property for the node. Properties are stored in a map and saved in project file...
Layer tree node points to a map layer.
The QgsLegendRenderer class handles automatic layout and rendering of legend.
QString title() const
QFont styleFont(QgsComposerLegendStyle::Style s) const
Qt::AlignmentFlag titleAlignment() const
Returns the alignment of the legend title.
void setFont(const QFont &font)
double boxSpace() const