QGIS API Documentation  2.99.0-Master (40f86b2)
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 "qgscomposerlegend.h"
20 #include "qgscomposermap.h"
21 #include "qgscomposition.h"
22 #include "qgscomposermodel.h"
23 #include "qgslayertree.h"
24 #include "qgslayertreemodel.h"
25 #include "qgslegendrenderer.h"
26 #include "qgslegendstyle.h"
27 #include "qgslogger.h"
28 #include "qgsmapsettings.h"
29 #include "qgsproject.h"
30 #include "qgssymbollayerutils.h"
31 #include "qgslayertreeutils.h"
32 #include <QDomDocument>
33 #include <QDomElement>
34 #include <QPainter>
35 
37  : QgsComposerItem( composition )
38  , mCustomLayerTree( nullptr )
39  , mComposerMap( nullptr )
40  , mLegendFilterByMap( false )
41  , mLegendFilterByExpression( false )
42  , mFilterOutAtlas( false )
43  , mFilterAskedForUpdate( false )
44  , mInAtlas( false )
45  , mInitialMapScaleCalculated( false )
46  , mForceResize( false )
47  , mSizeToContents( true )
48 {
49  mLegendModel = new QgsLegendModel( mComposition->project()->layerTreeRoot() );
50 
51  connect( &composition->atlasComposition(), &QgsAtlasComposition::renderEnded, this, &QgsComposerLegend::onAtlasEnded );
52  connect( &composition->atlasComposition(), &QgsAtlasComposition::featureChanged, this, &QgsComposerLegend::onAtlasFeature );
53 
54  // Connect to the main layertreeroot.
55  // It serves in "auto update mode" as a medium between the main app legend and this one
56  connect( mComposition->project()->layerTreeRoot(), &QgsLayerTreeNode::customPropertyChanged, this, &QgsComposerLegend::nodeCustomPropertyChanged );
57 }
58 
60  : QgsComposerItem( nullptr )
61  , mLegendModel( nullptr )
62  , mCustomLayerTree( nullptr )
63  , mComposerMap( nullptr )
64  , mLegendFilterByMap( false )
65  , mLegendFilterByExpression( false )
66  , mFilterOutAtlas( false )
67  , mFilterAskedForUpdate( false )
68  , mInAtlas( false )
69  , mInitialMapScaleCalculated( false )
70  , mForceResize( false )
71  , mSizeToContents( true )
72 {
73 
74 }
75 
77 {
78  delete mLegendModel;
79  delete mCustomLayerTree;
80 }
81 
82 void QgsComposerLegend::paint( QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget )
83 {
84  Q_UNUSED( itemStyle );
85  Q_UNUSED( pWidget );
86 
87  if ( !painter )
88  return;
89 
90  if ( !shouldDrawItem() )
91  {
92  return;
93  }
94 
95  if ( mFilterAskedForUpdate )
96  {
97  mFilterAskedForUpdate = false;
98  doUpdateFilterByMap();
99  }
100 
101  int dpi = painter->device()->logicalDpiX();
102  double dotsPerMM = dpi / 25.4;
103 
104  if ( mComposition )
105  {
107  mSettings.setDpi( dpi );
108  }
109  if ( mComposerMap )
110  {
111  mSettings.setMmPerMapUnit( mComposerMap->mapUnitsToMM() );
112 
113  // use a temporary QgsMapSettings to find out real map scale
114  QSizeF mapSizePixels = QSizeF( mComposerMap->rect().width() * dotsPerMM, mComposerMap->rect().height() * dotsPerMM );
115  QgsRectangle mapExtent = *mComposerMap->currentMapExtent();
116 
117  QgsMapSettings ms = mComposerMap->mapSettings( mapExtent, mapSizePixels, dpi );
118  mSettings.setMapScale( ms.scale() );
119  }
120  mInitialMapScaleCalculated = true;
121 
122  QgsLegendRenderer legendRenderer( mLegendModel, mSettings );
123  legendRenderer.setLegendSize( mForceResize && mSizeToContents ? QSize() : rect().size() );
124 
125  //adjust box if width or height is too small
126  if ( mSizeToContents )
127  {
128  QSizeF size = legendRenderer.minimumSize();
129  if ( mForceResize )
130  {
131  mForceResize = false;
132  //set new rect, respecting position mode and data defined size/position
133  QRectF targetRect = QRectF( pos().x(), pos().y(), size.width(), size.height() );
134  setSceneRect( evalItemRect( targetRect, true ) );
135  }
136  else if ( size.height() > rect().height() || size.width() > rect().width() )
137  {
138  //need to resize box
139  QRectF targetRect = QRectF( pos().x(), pos().y(), rect().width(), rect().height() );
140  if ( size.height() > targetRect.height() )
141  targetRect.setHeight( size.height() );
142  if ( size.width() > rect().width() )
143  targetRect.setWidth( size.width() );
144 
145  //set new rect, respecting position mode and data defined size/position
146  setSceneRect( evalItemRect( targetRect, true ) );
147  }
148  }
149 
150  drawBackground( painter );
151  painter->save();
152  //antialiasing on
153  painter->setRenderHint( QPainter::Antialiasing, true );
154  painter->setPen( QPen( QColor( 0, 0, 0 ) ) );
155 
156  if ( !mSizeToContents )
157  {
158  // set a clip region to crop out parts of legend which don't fit
159  QRectF thisPaintRect = QRectF( 0, 0, rect().width(), rect().height() );
160  painter->setClipRect( thisPaintRect );
161  }
162 
163  legendRenderer.drawLegend( painter );
164 
165  painter->restore();
166 
167  //draw frame and selection boxes if necessary
168  drawFrame( painter );
169  if ( isSelected() )
170  {
171  drawSelectionBoxes( painter );
172  }
173 }
174 
175 QSizeF QgsComposerLegend::paintAndDetermineSize( QPainter *painter )
176 {
177  if ( mFilterAskedForUpdate )
178  {
179  mFilterAskedForUpdate = false;
180  doUpdateFilterByMap();
181  }
182 
183  QgsLegendRenderer legendRenderer( mLegendModel, mSettings );
184  QSizeF size = legendRenderer.minimumSize();
185  if ( painter )
186  legendRenderer.drawLegend( painter );
187  return size;
188 }
189 
190 
192 {
193  if ( !mSizeToContents )
194  return;
195 
196  if ( !mInitialMapScaleCalculated )
197  {
198  // this is messy - but until we have painted the item we have no knowledge of the current DPI
199  // and so cannot correctly calculate the map scale. This results in incorrect size calculations
200  // for marker symbols with size in map units, causing the legends to initially expand to huge
201  // sizes if we attempt to calculate the box size first.
202  return;
203  }
204 
205  QgsLegendRenderer legendRenderer( mLegendModel, mSettings );
206  QSizeF size = legendRenderer.minimumSize();
207  QgsDebugMsg( QString( "width = %1 height = %2" ).arg( size.width() ).arg( size.height() ) );
208  if ( size.isValid() )
209  {
210  QRectF targetRect = QRectF( pos().x(), pos().y(), size.width(), size.height() );
211  //set new rect, respecting position mode and data defined size/position
212  setSceneRect( evalItemRect( targetRect, true ) );
213  }
214 }
215 
217 {
218  mSizeToContents = enabled;
219 }
220 
222 {
223  return mSizeToContents;
224 }
225 
226 void QgsComposerLegend::setCustomLayerTree( QgsLayerTree *rootGroup )
227 {
228  mLegendModel->setRootGroup( rootGroup ? rootGroup : mComposition->project()->layerTreeRoot() );
229 
230  delete mCustomLayerTree;
231  mCustomLayerTree = rootGroup;
232 }
233 
234 
236 {
237  if ( autoUpdate == autoUpdateModel() )
238  return;
239 
240  setCustomLayerTree( autoUpdate ? nullptr : mComposition->project()->layerTreeRoot()->clone() );
241  adjustBoxSize();
242  updateItem();
243 }
244 
245 void QgsComposerLegend::nodeCustomPropertyChanged( QgsLayerTreeNode *, const QString & )
246 {
247  if ( autoUpdateModel() )
248  {
249  // in "auto update" mode, some parameters on the main app legend may have been changed (expression filtering)
250  // we must then call updateItem to reflect the changes
251  updateItem();
252  }
253 }
254 
256 {
257  return !mCustomLayerTree;
258 }
259 
261 {
262  mLegendFilterByMap = enabled;
263  updateItem();
264 }
265 
266 void QgsComposerLegend::setTitle( const QString &t )
267 {
268  mTitle = t;
269  mSettings.setTitle( t );
270 
271  if ( mComposition && id().isEmpty() )
272  {
273  //notify the model that the display name has changed
275  }
276 }
277 QString QgsComposerLegend::title() const { return mTitle; }
278 
279 Qt::AlignmentFlag QgsComposerLegend::titleAlignment() const { return mSettings.titleAlignment(); }
280 void QgsComposerLegend::setTitleAlignment( Qt::AlignmentFlag alignment ) { mSettings.setTitleAlignment( alignment ); }
281 
285 
286 QFont QgsComposerLegend::styleFont( QgsLegendStyle::Style s ) const { return mSettings.style( s ).font(); }
288 
289 void QgsComposerLegend::setStyleMargin( QgsLegendStyle::Style s, double margin ) { rstyle( s ).setMargin( margin ); }
290 void QgsComposerLegend::setStyleMargin( QgsLegendStyle::Style s, QgsLegendStyle::Side side, double margin ) { rstyle( s ).setMargin( side, margin ); }
291 
292 double QgsComposerLegend::lineSpacing() const { return mSettings.lineSpacing(); }
293 void QgsComposerLegend::setLineSpacing( double spacing ) { mSettings.setLineSpacing( spacing ); }
294 
295 double QgsComposerLegend::boxSpace() const { return mSettings.boxSpace(); }
296 void QgsComposerLegend::setBoxSpace( double s ) { mSettings.setBoxSpace( s ); }
297 
298 double QgsComposerLegend::columnSpace() const { return mSettings.columnSpace(); }
299 void QgsComposerLegend::setColumnSpace( double s ) { mSettings.setColumnSpace( s ); }
300 
301 QColor QgsComposerLegend::fontColor() const { return mSettings.fontColor(); }
302 void QgsComposerLegend::setFontColor( const QColor &c ) { mSettings.setFontColor( c ); }
303 
304 double QgsComposerLegend::symbolWidth() const { return mSettings.symbolSize().width(); }
305 void QgsComposerLegend::setSymbolWidth( double w ) { mSettings.setSymbolSize( QSizeF( w, mSettings.symbolSize().height() ) ); }
306 
307 double QgsComposerLegend::symbolHeight() const { return mSettings.symbolSize().height(); }
308 void QgsComposerLegend::setSymbolHeight( double h ) { mSettings.setSymbolSize( QSizeF( mSettings.symbolSize().width(), h ) ); }
309 
310 double QgsComposerLegend::wmsLegendWidth() const { return mSettings.wmsLegendSize().width(); }
311 void QgsComposerLegend::setWmsLegendWidth( double w ) { mSettings.setWmsLegendSize( QSizeF( w, mSettings.wmsLegendSize().height() ) ); }
312 
313 double QgsComposerLegend::wmsLegendHeight() const {return mSettings.wmsLegendSize().height(); }
314 void QgsComposerLegend::setWmsLegendHeight( double h ) { mSettings.setWmsLegendSize( QSizeF( mSettings.wmsLegendSize().width(), h ) ); }
315 
316 void QgsComposerLegend::setWrapChar( const QString &t ) { mSettings.setWrapChar( t ); }
317 QString QgsComposerLegend::wrapChar() const {return mSettings.wrapChar(); }
318 
319 int QgsComposerLegend::columnCount() const { return mColumnCount; }
320 void QgsComposerLegend::setColumnCount( int c ) { mColumnCount = c; mSettings.setColumnCount( c ); }
321 
322 bool QgsComposerLegend::splitLayer() const { return mSettings.splitLayer(); }
323 void QgsComposerLegend::setSplitLayer( bool s ) { mSettings.setSplitLayer( s ); }
324 
325 bool QgsComposerLegend::equalColumnWidth() const { return mSettings.equalColumnWidth(); }
327 
328 bool QgsComposerLegend::drawRasterStroke() const { return mSettings.drawRasterStroke(); }
329 void QgsComposerLegend::setDrawRasterStroke( bool enabled ) { mSettings.setDrawRasterStroke( enabled ); }
330 
331 QColor QgsComposerLegend::rasterStrokeColor() const { return mSettings.rasterStrokeColor(); }
332 void QgsComposerLegend::setRasterStrokeColor( const QColor &color ) { mSettings.setRasterStrokeColor( color ); }
333 
334 double QgsComposerLegend::rasterStrokeWidth() const { return mSettings.rasterStrokeWidth(); }
335 void QgsComposerLegend::setRasterStrokeWidth( double width ) { mSettings.setRasterStrokeWidth( width ); }
336 
338 {
339  adjustBoxSize();
340  updateItem();
341 }
342 
344 {
345  adjustBoxSize();
346  updateItem();
347 }
348 
350 {
351  if ( !updatesEnabled() )
352  return;
353 
354  updateFilterByMap( false );
356 }
357 
358 bool QgsComposerLegend::writeXml( QDomElement &elem, QDomDocument &doc ) const
359 {
360  if ( elem.isNull() )
361  {
362  return false;
363  }
364 
365  QDomElement composerLegendElem = doc.createElement( QStringLiteral( "ComposerLegend" ) );
366  elem.appendChild( composerLegendElem );
367 
368  //write general properties
369  composerLegendElem.setAttribute( QStringLiteral( "title" ), mTitle );
370  composerLegendElem.setAttribute( QStringLiteral( "titleAlignment" ), QString::number( static_cast< int >( mSettings.titleAlignment() ) ) );
371  composerLegendElem.setAttribute( QStringLiteral( "columnCount" ), QString::number( mColumnCount ) );
372  composerLegendElem.setAttribute( QStringLiteral( "splitLayer" ), QString::number( mSettings.splitLayer() ) );
373  composerLegendElem.setAttribute( QStringLiteral( "equalColumnWidth" ), QString::number( mSettings.equalColumnWidth() ) );
374 
375  composerLegendElem.setAttribute( QStringLiteral( "boxSpace" ), QString::number( mSettings.boxSpace() ) );
376  composerLegendElem.setAttribute( QStringLiteral( "columnSpace" ), QString::number( mSettings.columnSpace() ) );
377 
378  composerLegendElem.setAttribute( QStringLiteral( "symbolWidth" ), QString::number( mSettings.symbolSize().width() ) );
379  composerLegendElem.setAttribute( QStringLiteral( "symbolHeight" ), QString::number( mSettings.symbolSize().height() ) );
380  composerLegendElem.setAttribute( QStringLiteral( "lineSpacing" ), QString::number( mSettings.lineSpacing() ) );
381 
382  composerLegendElem.setAttribute( QStringLiteral( "rasterBorder" ), mSettings.drawRasterStroke() );
383  composerLegendElem.setAttribute( QStringLiteral( "rasterBorderColor" ), QgsSymbolLayerUtils::encodeColor( mSettings.rasterStrokeColor() ) );
384  composerLegendElem.setAttribute( QStringLiteral( "rasterBorderWidth" ), QString::number( mSettings.rasterStrokeWidth() ) );
385 
386  composerLegendElem.setAttribute( QStringLiteral( "wmsLegendWidth" ), QString::number( mSettings.wmsLegendSize().width() ) );
387  composerLegendElem.setAttribute( QStringLiteral( "wmsLegendHeight" ), QString::number( mSettings.wmsLegendSize().height() ) );
388  composerLegendElem.setAttribute( QStringLiteral( "wrapChar" ), mSettings.wrapChar() );
389  composerLegendElem.setAttribute( QStringLiteral( "fontColor" ), mSettings.fontColor().name() );
390 
391  composerLegendElem.setAttribute( QStringLiteral( "resizeToContents" ), mSizeToContents );
392 
393  if ( mComposerMap )
394  {
395  composerLegendElem.setAttribute( QStringLiteral( "map" ), mComposerMap->id() );
396  }
397 
398  QDomElement composerLegendStyles = doc.createElement( QStringLiteral( "styles" ) );
399  composerLegendElem.appendChild( composerLegendStyles );
400 
401  style( QgsLegendStyle::Title ).writeXml( QStringLiteral( "title" ), composerLegendStyles, doc );
402  style( QgsLegendStyle::Group ).writeXml( QStringLiteral( "group" ), composerLegendStyles, doc );
403  style( QgsLegendStyle::Subgroup ).writeXml( QStringLiteral( "subgroup" ), composerLegendStyles, doc );
404  style( QgsLegendStyle::Symbol ).writeXml( QStringLiteral( "symbol" ), composerLegendStyles, doc );
405  style( QgsLegendStyle::SymbolLabel ).writeXml( QStringLiteral( "symbolLabel" ), composerLegendStyles, doc );
406 
407  if ( mCustomLayerTree )
408  {
409  // if not using auto-update - store the custom layer tree
410  mCustomLayerTree->writeXml( composerLegendElem );
411  }
412 
413  if ( mLegendFilterByMap )
414  {
415  composerLegendElem.setAttribute( QStringLiteral( "legendFilterByMap" ), QStringLiteral( "1" ) );
416  }
417 
418  return _writeXml( composerLegendElem, doc );
419 }
420 
421 bool QgsComposerLegend::readXml( const QDomElement &itemElem, const QDomDocument &doc )
422 {
423  if ( itemElem.isNull() )
424  {
425  return false;
426  }
427 
428  //read general properties
429  mTitle = itemElem.attribute( QStringLiteral( "title" ) );
430  mSettings.setTitle( mTitle );
431  if ( !itemElem.attribute( QStringLiteral( "titleAlignment" ) ).isEmpty() )
432  {
433  mSettings.setTitleAlignment( static_cast< Qt::AlignmentFlag >( itemElem.attribute( QStringLiteral( "titleAlignment" ) ).toInt() ) );
434  }
435  int colCount = itemElem.attribute( QStringLiteral( "columnCount" ), QStringLiteral( "1" ) ).toInt();
436  if ( colCount < 1 ) colCount = 1;
437  mColumnCount = colCount;
438  mSettings.setColumnCount( mColumnCount );
439  mSettings.setSplitLayer( itemElem.attribute( QStringLiteral( "splitLayer" ), QStringLiteral( "0" ) ).toInt() == 1 );
440  mSettings.setEqualColumnWidth( itemElem.attribute( QStringLiteral( "equalColumnWidth" ), QStringLiteral( "0" ) ).toInt() == 1 );
441 
442  QDomNodeList stylesNodeList = itemElem.elementsByTagName( QStringLiteral( "styles" ) );
443  if ( !stylesNodeList.isEmpty() )
444  {
445  QDomNode stylesNode = stylesNodeList.at( 0 );
446  for ( int i = 0; i < stylesNode.childNodes().size(); i++ )
447  {
448  QDomElement styleElem = stylesNode.childNodes().at( i ).toElement();
450  style.readXml( styleElem, doc );
451  QString name = styleElem.attribute( QStringLiteral( "name" ) );
453  if ( name == QLatin1String( "title" ) ) s = QgsLegendStyle::Title;
454  else if ( name == QLatin1String( "group" ) ) s = QgsLegendStyle::Group;
455  else if ( name == QLatin1String( "subgroup" ) ) s = QgsLegendStyle::Subgroup;
456  else if ( name == QLatin1String( "symbol" ) ) s = QgsLegendStyle::Symbol;
457  else if ( name == QLatin1String( "symbolLabel" ) ) s = QgsLegendStyle::SymbolLabel;
458  else continue;
459  setStyle( s, style );
460  }
461  }
462 
463  //font color
464  QColor fontClr;
465  fontClr.setNamedColor( itemElem.attribute( QStringLiteral( "fontColor" ), QStringLiteral( "#000000" ) ) );
466  mSettings.setFontColor( fontClr );
467 
468  //spaces
469  mSettings.setBoxSpace( itemElem.attribute( QStringLiteral( "boxSpace" ), QStringLiteral( "2.0" ) ).toDouble() );
470  mSettings.setColumnSpace( itemElem.attribute( QStringLiteral( "columnSpace" ), QStringLiteral( "2.0" ) ).toDouble() );
471 
472  mSettings.setSymbolSize( QSizeF( itemElem.attribute( QStringLiteral( "symbolWidth" ), QStringLiteral( "7.0" ) ).toDouble(), itemElem.attribute( QStringLiteral( "symbolHeight" ), QStringLiteral( "14.0" ) ).toDouble() ) );
473  mSettings.setWmsLegendSize( QSizeF( itemElem.attribute( QStringLiteral( "wmsLegendWidth" ), QStringLiteral( "50" ) ).toDouble(), itemElem.attribute( QStringLiteral( "wmsLegendHeight" ), QStringLiteral( "25" ) ).toDouble() ) );
474  mSettings.setLineSpacing( itemElem.attribute( QStringLiteral( "lineSpacing" ), "1.0" ).toDouble() );
475 
476  mSettings.setDrawRasterStroke( itemElem.attribute( QStringLiteral( "rasterBorder" ), QStringLiteral( "1" ) ) != QLatin1String( "0" ) );
477  mSettings.setRasterStrokeColor( QgsSymbolLayerUtils::decodeColor( itemElem.attribute( QStringLiteral( "rasterBorderColor" ), QStringLiteral( "0,0,0" ) ) ) );
478  mSettings.setRasterStrokeWidth( itemElem.attribute( QStringLiteral( "rasterBorderWidth" ), QStringLiteral( "0" ) ).toDouble() );
479 
480  mSettings.setWrapChar( itemElem.attribute( QStringLiteral( "wrapChar" ) ) );
481 
482  mSizeToContents = itemElem.attribute( QStringLiteral( "resizeToContents" ), QStringLiteral( "1" ) ) != QLatin1String( "0" );
483 
484  //composer map
485  mLegendFilterByMap = itemElem.attribute( QStringLiteral( "legendFilterByMap" ), QStringLiteral( "0" ) ).toInt();
486  if ( !itemElem.attribute( QStringLiteral( "map" ) ).isEmpty() )
487  {
488  setComposerMap( mComposition->getComposerMapById( itemElem.attribute( QStringLiteral( "map" ) ).toInt() ) );
489  }
490 
491  // QGIS >= 2.6
492  QDomElement layerTreeElem = itemElem.firstChildElement( QStringLiteral( "layer-tree" ) );
493  if ( layerTreeElem.isNull() )
494  layerTreeElem = itemElem.firstChildElement( QStringLiteral( "layer-tree-group" ) );
495 
496  setCustomLayerTree( QgsLayerTree::readXml( layerTreeElem ) );
497 
498  //restore general composer item properties
499  QDomNodeList composerItemList = itemElem.elementsByTagName( QStringLiteral( "ComposerItem" ) );
500  if ( !composerItemList.isEmpty() )
501  {
502  QDomElement composerItemElem = composerItemList.at( 0 ).toElement();
503  _readXml( composerItemElem, doc );
504  }
505 
506  // < 2.0 projects backward compatibility >>>>>
507  //title font
508  QString titleFontString = itemElem.attribute( QStringLiteral( "titleFont" ) );
509  if ( !titleFontString.isEmpty() )
510  {
511  rstyle( QgsLegendStyle::Title ).rfont().fromString( titleFontString );
512  }
513  //group font
514  QString groupFontString = itemElem.attribute( QStringLiteral( "groupFont" ) );
515  if ( !groupFontString.isEmpty() )
516  {
517  rstyle( QgsLegendStyle::Group ).rfont().fromString( groupFontString );
518  }
519 
520  //layer font
521  QString layerFontString = itemElem.attribute( QStringLiteral( "layerFont" ) );
522  if ( !layerFontString.isEmpty() )
523  {
524  rstyle( QgsLegendStyle::Subgroup ).rfont().fromString( layerFontString );
525  }
526  //item font
527  QString itemFontString = itemElem.attribute( QStringLiteral( "itemFont" ) );
528  if ( !itemFontString.isEmpty() )
529  {
530  rstyle( QgsLegendStyle::SymbolLabel ).rfont().fromString( itemFontString );
531  }
532 
533  if ( !itemElem.attribute( QStringLiteral( "groupSpace" ) ).isEmpty() )
534  {
535  rstyle( QgsLegendStyle::Group ).setMargin( QgsLegendStyle::Top, itemElem.attribute( QStringLiteral( "groupSpace" ), QStringLiteral( "3.0" ) ).toDouble() );
536  }
537  if ( !itemElem.attribute( QStringLiteral( "layerSpace" ) ).isEmpty() )
538  {
539  rstyle( QgsLegendStyle::Subgroup ).setMargin( QgsLegendStyle::Top, itemElem.attribute( QStringLiteral( "layerSpace" ), QStringLiteral( "3.0" ) ).toDouble() );
540  }
541  if ( !itemElem.attribute( QStringLiteral( "symbolSpace" ) ).isEmpty() )
542  {
543  rstyle( QgsLegendStyle::Symbol ).setMargin( QgsLegendStyle::Top, itemElem.attribute( QStringLiteral( "symbolSpace" ), QStringLiteral( "2.0" ) ).toDouble() );
544  rstyle( QgsLegendStyle::SymbolLabel ).setMargin( QgsLegendStyle::Top, itemElem.attribute( QStringLiteral( "symbolSpace" ), QStringLiteral( "2.0" ) ).toDouble() );
545  }
546  // <<<<<<< < 2.0 projects backward compatibility
547 
548  emit itemChanged();
549  return true;
550 }
551 
553 {
554  if ( !id().isEmpty() )
555  {
556  return id();
557  }
558 
559  //if no id, default to portion of title text
560  QString text = mSettings.title();
561  if ( text.isEmpty() )
562  {
563  return tr( "<legend>" );
564  }
565  if ( text.length() > 25 )
566  {
567  return QString( tr( "%1..." ) ).arg( text.left( 25 ) );
568  }
569  else
570  {
571  return text;
572  }
573 }
574 
576 {
577  if ( mComposerMap )
578  {
579  disconnect( mComposerMap, &QObject::destroyed, this, &QgsComposerLegend::invalidateCurrentMap );
580  disconnect( mComposerMap, &QgsComposerObject::itemChanged, this, &QgsComposerLegend::updateFilterByMapAndRedraw );
581  disconnect( mComposerMap, &QgsComposerMap::extentChanged, this, &QgsComposerLegend::updateFilterByMapAndRedraw );
582  disconnect( mComposerMap, &QgsComposerMap::layerStyleOverridesChanged, this, &QgsComposerLegend::mapLayerStyleOverridesChanged );
583  }
584 
585  mComposerMap = map;
586 
587  if ( map )
588  {
589  connect( map, &QObject::destroyed, this, &QgsComposerLegend::invalidateCurrentMap );
590  connect( map, &QgsComposerObject::itemChanged, this, &QgsComposerLegend::updateFilterByMapAndRedraw );
591  connect( map, &QgsComposerMap::extentChanged, this, &QgsComposerLegend::updateFilterByMapAndRedraw );
592  connect( map, &QgsComposerMap::layerStyleOverridesChanged, this, &QgsComposerLegend::mapLayerStyleOverridesChanged );
593  }
594 
595  updateItem();
596 }
597 
599 {
600  setComposerMap( nullptr );
601 }
602 
604 {
606  const QgsExpressionContext *evalContext = context ? context : &scopedContext;
607 
608  bool forceUpdate = false;
609  //updates data defined properties and redraws item to match
610  if ( property == QgsComposerObject::LegendTitle || property == QgsComposerObject::AllProperties )
611  {
612  bool ok = false;
613  QString t = mDataDefinedProperties.valueAsString( QgsComposerObject::LegendTitle, *evalContext, mTitle, &ok );
614  if ( ok )
615  {
616  mSettings.setTitle( t );
617  forceUpdate = true;
618  }
619  }
621  {
622  bool ok = false;
623  int cols = mDataDefinedProperties.valueAsInt( QgsComposerObject::LegendColumnCount, *evalContext, mColumnCount, &ok );
624  if ( ok && cols >= 0 )
625  {
626  mSettings.setColumnCount( cols );
627  forceUpdate = true;
628  }
629  }
630  if ( forceUpdate )
631  {
632  adjustBoxSize();
633  update();
634  }
635 
637 }
638 
639 void QgsComposerLegend::updateFilterByMapAndRedraw()
640 {
641  updateFilterByMap( true );
642 }
643 
644 void QgsComposerLegend::mapLayerStyleOverridesChanged()
645 {
646  if ( !mComposerMap )
647  return;
648 
649  // map's style has been changed, so make sure to update the legend here
650  if ( mLegendFilterByMap )
651  {
652  // legend is being filtered by map, so we need to re run the hit test too
653  // as the style overrides may also have affected the visible symbols
654  updateFilterByMap( false );
655  }
656  else
657  {
658  mLegendModel->setLayerStyleOverrides( mComposerMap->layerStyleOverrides() );
659 
660  Q_FOREACH ( QgsLayerTreeLayer *nodeLayer, mLegendModel->rootGroup()->findLayers() )
661  mLegendModel->refreshLayerLegend( nodeLayer );
662  }
663 
664  adjustBoxSize();
665  updateItem();
666 }
667 
668 void QgsComposerLegend::updateFilterByMap( bool redraw )
669 {
670  if ( isRemoved() )
671  return;
672  // ask for update
673  // the actual update will take place before the redraw.
674  // This is to avoid multiple calls to the filter
675  mFilterAskedForUpdate = true;
676 
677  if ( redraw )
679 }
680 
681 void QgsComposerLegend::doUpdateFilterByMap()
682 {
683  if ( mComposerMap )
684  mLegendModel->setLayerStyleOverrides( mComposerMap->layerStyleOverrides() );
685  else
686  mLegendModel->setLayerStyleOverrides( QMap<QString, QString>() );
687 
688 
689  bool filterByExpression = QgsLayerTreeUtils::hasLegendFilterExpression( *( mCustomLayerTree ? mCustomLayerTree : mComposition->project()->layerTreeRoot() ) );
690 
691  if ( mComposerMap && ( mLegendFilterByMap || filterByExpression || mInAtlas ) )
692  {
693  int dpi = mComposition->printResolution();
694 
695  QgsRectangle requestRectangle;
696  mComposerMap->requestedExtent( requestRectangle );
697 
698  QSizeF size( requestRectangle.width(), requestRectangle.height() );
699  size *= mComposerMap->mapUnitsToMM() * dpi / 25.4;
700 
701  QgsMapSettings ms = mComposerMap->mapSettings( requestRectangle, size, dpi );
702 
703  QgsGeometry filterPolygon;
704  if ( mInAtlas )
705  {
706  filterPolygon = composition()->atlasComposition().currentGeometry( mComposerMap->crs() );
707  }
708  mLegendModel->setLegendFilter( &ms, /* useExtent */ mInAtlas || mLegendFilterByMap, filterPolygon, /* useExpressions */ true );
709  }
710  else
711  mLegendModel->setLegendFilterByMap( nullptr );
712 
713  mForceResize = true;
714 }
715 
717 {
718  mFilterOutAtlas = doFilter;
719 }
720 
722 {
723  return mFilterOutAtlas;
724 }
725 
726 void QgsComposerLegend::onAtlasFeature( QgsFeature *feat )
727 {
728  if ( !feat )
729  return;
730  mInAtlas = mFilterOutAtlas;
731  updateFilterByMap();
732 }
733 
734 void QgsComposerLegend::onAtlasEnded()
735 {
736  mInAtlas = false;
737  updateFilterByMap();
738 }
739 
740 // -------------------------------------------------------------------------
742 #include "qgsvectorlayer.h"
743 
744 QgsLegendModel::QgsLegendModel( QgsLayerTree *rootNode, QObject *parent )
745  : QgsLayerTreeModel( rootNode, parent )
746 {
749 }
750 
751 QVariant QgsLegendModel::data( const QModelIndex &index, int role ) const
752 {
753  // handle custom layer node labels
754  if ( QgsLayerTreeNode *node = index2node( index ) )
755  {
756  if ( QgsLayerTree::isLayer( node ) && ( role == Qt::DisplayRole || role == Qt::EditRole ) && !node->customProperty( QStringLiteral( "legend/title-label" ) ).isNull() )
757  {
758  QgsLayerTreeLayer *nodeLayer = QgsLayerTree::toLayer( node );
759  QString name = node->customProperty( QStringLiteral( "legend/title-label" ) ).toString();
760  if ( nodeLayer->customProperty( QStringLiteral( "showFeatureCount" ), 0 ).toInt() && role == Qt::DisplayRole )
761  {
762  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( nodeLayer->layer() );
763  if ( vlayer && vlayer->featureCount() >= 0 )
764  name += QStringLiteral( " [%1]" ).arg( vlayer->featureCount() );
765  }
766  return name;
767  }
768  }
769 
770  return QgsLayerTreeModel::data( index, role );
771 }
772 
773 Qt::ItemFlags QgsLegendModel::flags( const QModelIndex &index ) const
774 {
775  // make the legend nodes selectable even if they are not by default
776  if ( index2legendNode( index ) )
777  return QgsLayerTreeModel::flags( index ) | Qt::ItemIsSelectable;
778 
779  return QgsLayerTreeModel::flags( index );
780 }
void setTitleAlignment(Qt::AlignmentFlag alignment)
Sets the alignment of the legend title.
void setWrapChar(const QString &t)
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)
static QgsLayerTreeLayer * toLayer(QgsLayerTreeNode *node)
Cast node to a layer.
Definition: qgslayertree.h:74
A rectangle specified with double values.
Definition: qgsrectangle.h:36
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)
Item model implementation based on layer tree model for composer legend.
void renderEnded()
Is emitted when atlas rendering has ended.
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.
void setRasterStrokeWidth(double width)
Sets the stroke width for the stroke drawn around raster symbol items.
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:36
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)
bool drawRasterStroke() const
Returns whether a stroke will be drawn around raster symbol items.
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...
QFont font() const
virtual void refreshDataDefinedProperty(const DataDefinedProperty property=AllProperties, const QgsExpressionContext *context=nullptr)
Refreshes a data defined property for the item by reevaluating the property&#39;s value and redrawing the...
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.
QgsLegendStyle style(QgsLegendStyle::Style s) const
Returns style.
Composer legend components style.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:79
bool writeXml(QDomElement &elem, QDomDocument &doc) const override
Stores state in Dom node.
void setLineSpacing(double spacing)
Sets the spacing in-between multiple lines.
virtual void drawFrame(QPainter *p)
Draw black frame around item.
bool drawRasterStroke() const
Returns whether a stroke will be drawn around raster symbol items.
void setTitle(const QString &t)
QColor rasterStrokeColor() const
Returns the stroke color for the stroke drawn around raster symbol items.
bool updatesEnabled() const
Returns whether updates to the item are enabled.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:136
static QgsLayerTree * readXml(QDomElement &element)
Load the layer tree from an XML element.
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.
DataDefinedProperty
Data defined properties for different item types.
QColor rasterStrokeColor() const
Returns the stroke color for the stroke drawn around raster symbol items.
void setWmsLegendHeight(double h)
Allow check boxes for legend nodes (if supported by layer&#39;s legend)
void setResizeToContents(bool enabled)
Sets whether the legend should automatically resize to fit its contents.
void setStyle(QgsLegendStyle::Style s, const QgsLegendStyle &style)
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)
void setStyleMargin(QgsLegendStyle::Style s, double margin)
Set style margin.
The QgsLayerTreeModel class is model implementation for Qt item views framework.
QSizeF wmsLegendSize() const
QgsLegendStyle style(QgsLegendStyle::Style s) const
Returns style.
bool _writeXml(QDomElement &itemElem, QDomDocument &doc) const
Writes parameter that are not subclass specific in document. Usually called from writeXml methods of ...
void setFont(const QFont &font)
Namespace with helper functions for layer tree operations.
Definition: qgslayertree.h:31
bool equalColumnWidth() const
void setSymbolHeight(double h)
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.
virtual void refreshDataDefinedProperty(const QgsComposerObject::DataDefinedProperty property=QgsComposerObject::AllProperties, const QgsExpressionContext *context=nullptr) override
double width() const
Width of the rectangle.
Definition: qgsrectangle.h:215
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.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void setMapScale(double scale)
QVariant data(const QModelIndex &index, int role) const override
double rasterStrokeWidth() const
Returns the stroke width (in millimeters) for the stroke drawn around raster symbol items...
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)
void synchronizeWithModel()
Data changed.
void setRasterStrokeColor(const QColor &color)
Sets the stroke color for the stroke drawn around raster symbol items.
QgsLegendModel(QgsLayerTree *rootNode, QObject *parent=nullptr)
Construct the model based on the given layer tree.
void setTitle(const QString &t)
QFont styleFont(QgsLegendStyle::Style s) const
QgsPropertyCollection mDataDefinedProperties
static bool isLayer(const QgsLayerTreeNode *node)
Check whether the node is a valid layer node.
Definition: qgslayertree.h:52
This class is a base class for nodes in a layer tree.
void setRasterStrokeWidth(double width)
Sets the stroke width for the stroke drawn around raster symbol items.
void setMargin(Side side, double margin)
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 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 _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...
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 invalidateCurrentMap()
Sets mCompositionMap to 0 if the map is deleted.
virtual QString displayName() const override
Get item display name.
void featureChanged(QgsFeature *feature)
Is emitted when the current atlas feature changes.
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
void setLineSpacing(double s)
virtual QgsExpressionContext createExpressionContext() const override
Creates an expression context relating to the item&#39;s current state.
QgsLayerTree * layerTreeRoot() const
Return pointer to the root (invisible) node of the project&#39;s layer tree.
bool resizeToContents() const
Returns whether the legend should automatically resize to fit its contents.
QgsMapLayer * layer() const
void readXml(const QDomElement &elem, const QDomDocument &doc)
double symbolHeight() const
QgsComposition * mComposition
void layerStyleOverridesChanged()
Emitted when layer style overrides are changed...
QString valueAsString(int key, const QgsExpressionContext &context, const QString &defaultString=QString(), bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as a string...
void setWmsLegendSize(QSizeF s)
QgsLegendStyle & rstyle(QgsLegendStyle::Style s)
Returns reference to modifiable style.
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...
QgsCoordinateReferenceSystem crs() const
Returns coordinate reference system used for rendering the map.
void setUseAdvancedEffects(bool use)
void setAutoUpdateModel(bool autoUpdate)
QgsLayerTreeNode * index2node(const QModelIndex &index) const
Return layer tree node for given index.
void setDrawRasterStroke(bool enabled)
Sets whether a stroke will be drawn around raster symbol items.
const QgsComposition * composition() const
Returns the composition the item is attached to.
QgsLayerTree * rootGroup() const
Return pointer to the root node of the layer tree. Always a non-null pointer.
void setWrapChar(const QString &t)
void setStyle(QgsLegendStyle::Style s, const QgsLegendStyle &style)
QgsProject * project() const
The project associated with the composition.
bool equalColumnWidth() const
void setRootGroup(QgsLayerTree *newRootGroup)
Reset the model and use a new root group node.
virtual void drawBackground(QPainter *p)
Draw background.
void setColumnCount(int c)
double columnSpace() const
void requestedExtent(QgsRectangle &extent) const
Calculates the extent to request and the yShift of the top-left point in case of rotation.
void setDrawRasterStroke(bool enabled)
Sets whether a stroke will be drawn around raster symbol items.
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.
QFont & rfont()
virtual void updateItem()
Updates (redraws) the item, with the possibility to do custom update for subclasses.
void writeXml(const QString &name, QDomElement &elem, QDomDocument &doc) const
void setStyleFont(QgsLegendStyle::Style s, const QFont &f)
Set style font.
QgsAtlasComposition & atlasComposition()
double wmsLegendWidth() const
Flags flags() const
Return OR-ed combination of model flags.
void extentChanged()
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.
void customPropertyChanged(QgsLayerTreeNode *node, const QString &key)
Emitted when a custom property of a node within the tree has been changed or removed.
void setFontColor(const QColor &c)
double lineSpacing() const
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.
QgsLegendStyle & rstyle(QgsLegendStyle::Style s)
Returns reference to modifiable style.
bool splitLayer() const
int valueAsInt(int key, const QgsExpressionContext &context, int defaultValue=0, bool *ok=nullptr) const
Calculates the current value of the property with the specified key and interprets it as an integer...
double rasterStrokeWidth() const
Returns the stroke width (in millimeters) for the stroke drawn around raster symbol items...
void setRasterStrokeColor(const QColor &color)
Sets the stroke color for the stroke drawn around raster symbol items.
virtual QgsLayerTree * clone() const override
Return a clone of the group. The children are cloned too.
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 lineSpacing() const
Returns the spacing in-between lines in mm.
double columnSpace() const
void setSplitLayer(bool s)
static QColor decodeColor(const QString &str)
All properties for item.
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const override
double height() const
Height of the rectangle.
Definition: qgsrectangle.h:220
Layer tree node points to a map layer.
The QgsLegendRenderer class handles automatic layout and rendering of legend.
QString title() const
Qt::AlignmentFlag titleAlignment() const
Returns the alignment of the legend title.
double boxSpace() const