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