QGIS API Documentation  2.99.0-Master (d55fa22)
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 
410  return _writeXml( composerLegendElem, doc );
411 }
412 
413 bool QgsComposerLegend::readXml( const QDomElement &itemElem, const QDomDocument &doc )
414 {
415  if ( itemElem.isNull() )
416  {
417  return false;
418  }
419 
420  //read general properties
421  mTitle = itemElem.attribute( QStringLiteral( "title" ) );
422  mSettings.setTitle( mTitle );
423  if ( !itemElem.attribute( QStringLiteral( "titleAlignment" ) ).isEmpty() )
424  {
425  mSettings.setTitleAlignment( static_cast< Qt::AlignmentFlag >( itemElem.attribute( QStringLiteral( "titleAlignment" ) ).toInt() ) );
426  }
427  int colCount = itemElem.attribute( QStringLiteral( "columnCount" ), QStringLiteral( "1" ) ).toInt();
428  if ( colCount < 1 ) colCount = 1;
429  mColumnCount = colCount;
430  mSettings.setColumnCount( mColumnCount );
431  mSettings.setSplitLayer( itemElem.attribute( QStringLiteral( "splitLayer" ), QStringLiteral( "0" ) ).toInt() == 1 );
432  mSettings.setEqualColumnWidth( itemElem.attribute( QStringLiteral( "equalColumnWidth" ), QStringLiteral( "0" ) ).toInt() == 1 );
433 
434  QDomNodeList stylesNodeList = itemElem.elementsByTagName( QStringLiteral( "styles" ) );
435  if ( !stylesNodeList.isEmpty() )
436  {
437  QDomNode stylesNode = stylesNodeList.at( 0 );
438  for ( int i = 0; i < stylesNode.childNodes().size(); i++ )
439  {
440  QDomElement styleElem = stylesNode.childNodes().at( i ).toElement();
442  style.readXml( styleElem, doc );
443  QString name = styleElem.attribute( QStringLiteral( "name" ) );
445  if ( name == QLatin1String( "title" ) ) s = QgsLegendStyle::Title;
446  else if ( name == QLatin1String( "group" ) ) s = QgsLegendStyle::Group;
447  else if ( name == QLatin1String( "subgroup" ) ) s = QgsLegendStyle::Subgroup;
448  else if ( name == QLatin1String( "symbol" ) ) s = QgsLegendStyle::Symbol;
449  else if ( name == QLatin1String( "symbolLabel" ) ) s = QgsLegendStyle::SymbolLabel;
450  else continue;
451  setStyle( s, style );
452  }
453  }
454 
455  //font color
456  QColor fontClr;
457  fontClr.setNamedColor( itemElem.attribute( QStringLiteral( "fontColor" ), QStringLiteral( "#000000" ) ) );
458  mSettings.setFontColor( fontClr );
459 
460  //spaces
461  mSettings.setBoxSpace( itemElem.attribute( QStringLiteral( "boxSpace" ), QStringLiteral( "2.0" ) ).toDouble() );
462  mSettings.setColumnSpace( itemElem.attribute( QStringLiteral( "columnSpace" ), QStringLiteral( "2.0" ) ).toDouble() );
463 
464  mSettings.setSymbolSize( QSizeF( itemElem.attribute( QStringLiteral( "symbolWidth" ), QStringLiteral( "7.0" ) ).toDouble(), itemElem.attribute( QStringLiteral( "symbolHeight" ), QStringLiteral( "14.0" ) ).toDouble() ) );
465  mSettings.setWmsLegendSize( QSizeF( itemElem.attribute( QStringLiteral( "wmsLegendWidth" ), QStringLiteral( "50" ) ).toDouble(), itemElem.attribute( QStringLiteral( "wmsLegendHeight" ), QStringLiteral( "25" ) ).toDouble() ) );
466  mSettings.setLineSpacing( itemElem.attribute( QStringLiteral( "lineSpacing" ), "1.0" ).toDouble() );
467 
468  mSettings.setDrawRasterStroke( itemElem.attribute( QStringLiteral( "rasterBorder" ), QStringLiteral( "1" ) ) != QLatin1String( "0" ) );
469  mSettings.setRasterStrokeColor( QgsSymbolLayerUtils::decodeColor( itemElem.attribute( QStringLiteral( "rasterBorderColor" ), QStringLiteral( "0,0,0" ) ) ) );
470  mSettings.setRasterStrokeWidth( itemElem.attribute( QStringLiteral( "rasterBorderWidth" ), QStringLiteral( "0" ) ).toDouble() );
471 
472  mSettings.setWrapChar( itemElem.attribute( QStringLiteral( "wrapChar" ) ) );
473 
474  mSizeToContents = itemElem.attribute( QStringLiteral( "resizeToContents" ), QStringLiteral( "1" ) ) != QLatin1String( "0" );
475 
476  //composer map
477  mLegendFilterByMap = itemElem.attribute( QStringLiteral( "legendFilterByMap" ), QStringLiteral( "0" ) ).toInt();
478  if ( !itemElem.attribute( QStringLiteral( "map" ) ).isEmpty() )
479  {
480  setComposerMap( mComposition->getComposerMapById( itemElem.attribute( QStringLiteral( "map" ) ).toInt() ) );
481  }
482 
483  // QGIS >= 2.6
484  QDomElement layerTreeElem = itemElem.firstChildElement( QStringLiteral( "layer-tree" ) );
485  if ( layerTreeElem.isNull() )
486  layerTreeElem = itemElem.firstChildElement( QStringLiteral( "layer-tree-group" ) );
487 
488  if ( !layerTreeElem.isNull() )
489  {
490  std::unique_ptr< QgsLayerTree > tree( QgsLayerTree::readXml( layerTreeElem ) );
491  if ( mComposition )
492  tree->resolveReferences( mComposition->project(), true );
493  setCustomLayerTree( tree.release() );
494  }
495  else
496  setCustomLayerTree( nullptr );
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.get() : 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: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
Return the calculated scale of the map.
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)
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