QGIS API Documentation  2.99.0-Master (7705179)
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  , 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  connect( &composition->atlasComposition(), &QgsAtlasComposition::renderEnded, this, &QgsComposerLegend::onAtlasEnded );
50  connect( &composition->atlasComposition(), &QgsAtlasComposition::featureChanged, this, &QgsComposerLegend::onAtlasFeature );
51 
52  // Connect to the main layertreeroot.
53  // It serves in "auto update mode" as a medium between the main app legend and this one
54  connect( mComposition->project()->layerTreeRoot(), &QgsLayerTreeNode::customPropertyChanged, this, &QgsComposerLegend::nodeCustomPropertyChanged );
55 }
56 
58  : QgsComposerItem( nullptr )
59  , mLegendModel( nullptr )
60  , mCustomLayerTree( nullptr )
61  , mLegendFilterByMap( false )
62  , mLegendFilterByExpression( false )
63  , mFilterOutAtlas( false )
64  , mFilterAskedForUpdate( false )
65  , mInAtlas( false )
66  , mInitialMapScaleCalculated( false )
67  , mForceResize( false )
68  , mSizeToContents( true )
69 {
70 
71 }
72 
73 void QgsComposerLegend::paint( QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget )
74 {
75  Q_UNUSED( itemStyle );
76  Q_UNUSED( pWidget );
77 
78  if ( !painter )
79  return;
80 
81  if ( !shouldDrawItem() )
82  {
83  return;
84  }
85 
86  if ( mFilterAskedForUpdate )
87  {
88  mFilterAskedForUpdate = false;
89  doUpdateFilterByMap();
90  }
91 
92  int dpi = painter->device()->logicalDpiX();
93  double dotsPerMM = dpi / 25.4;
94 
95  if ( mComposition )
96  {
98  mSettings.setDpi( dpi );
99  }
100  if ( mComposerMap )
101  {
102  mSettings.setMmPerMapUnit( mComposerMap->mapUnitsToMM() );
103 
104  // use a temporary QgsMapSettings to find out real map scale
105  QSizeF mapSizePixels = QSizeF( mComposerMap->rect().width() * dotsPerMM, mComposerMap->rect().height() * dotsPerMM );
106  QgsRectangle mapExtent = *mComposerMap->currentMapExtent();
107 
108  QgsMapSettings ms = mComposerMap->mapSettings( mapExtent, mapSizePixels, dpi );
109  mSettings.setMapScale( ms.scale() );
110  }
111  mInitialMapScaleCalculated = true;
112 
113  QgsLegendRenderer legendRenderer( mLegendModel.get(), mSettings );
114  legendRenderer.setLegendSize( mForceResize && mSizeToContents ? QSize() : rect().size() );
115 
116  //adjust box if width or height is too small
117  if ( mSizeToContents )
118  {
119  QSizeF size = legendRenderer.minimumSize();
120  if ( mForceResize )
121  {
122  mForceResize = false;
123  //set new rect, respecting position mode and data defined size/position
124  QRectF targetRect = QRectF( pos().x(), pos().y(), size.width(), size.height() );
125  setSceneRect( evalItemRect( targetRect, true ) );
126  }
127  else if ( size.height() > rect().height() || size.width() > rect().width() )
128  {
129  //need to resize box
130  QRectF targetRect = QRectF( pos().x(), pos().y(), rect().width(), rect().height() );
131  if ( size.height() > targetRect.height() )
132  targetRect.setHeight( size.height() );
133  if ( size.width() > rect().width() )
134  targetRect.setWidth( size.width() );
135 
136  //set new rect, respecting position mode and data defined size/position
137  setSceneRect( evalItemRect( targetRect, true ) );
138  }
139  }
140 
141  drawBackground( painter );
142  painter->save();
143  //antialiasing on
144  painter->setRenderHint( QPainter::Antialiasing, true );
145  painter->setPen( QPen( QColor( 0, 0, 0 ) ) );
146 
147  if ( !mSizeToContents )
148  {
149  // set a clip region to crop out parts of legend which don't fit
150  QRectF thisPaintRect = QRectF( 0, 0, rect().width(), rect().height() );
151  painter->setClipRect( thisPaintRect );
152  }
153 
154  legendRenderer.drawLegend( painter );
155 
156  painter->restore();
157 
158  //draw frame and selection boxes if necessary
159  drawFrame( painter );
160  if ( isSelected() )
161  {
162  drawSelectionBoxes( painter );
163  }
164 }
165 
166 QSizeF QgsComposerLegend::paintAndDetermineSize( QPainter *painter )
167 {
168  if ( mFilterAskedForUpdate )
169  {
170  mFilterAskedForUpdate = false;
171  doUpdateFilterByMap();
172  }
173 
174  QgsLegendRenderer legendRenderer( mLegendModel.get(), mSettings );
175  QSizeF size = legendRenderer.minimumSize();
176  if ( painter )
177  legendRenderer.drawLegend( painter );
178  return size;
179 }
180 
181 
183 {
184  if ( !mSizeToContents )
185  return;
186 
187  if ( !mInitialMapScaleCalculated )
188  {
189  // this is messy - but until we have painted the item we have no knowledge of the current DPI
190  // and so cannot correctly calculate the map scale. This results in incorrect size calculations
191  // for marker symbols with size in map units, causing the legends to initially expand to huge
192  // sizes if we attempt to calculate the box size first.
193  return;
194  }
195 
196  QgsLegendRenderer legendRenderer( mLegendModel.get(), mSettings );
197  QSizeF size = legendRenderer.minimumSize();
198  QgsDebugMsg( QString( "width = %1 height = %2" ).arg( size.width() ).arg( size.height() ) );
199  if ( size.isValid() )
200  {
201  QRectF targetRect = QRectF( pos().x(), pos().y(), size.width(), size.height() );
202  //set new rect, respecting position mode and data defined size/position
203  setSceneRect( evalItemRect( targetRect, true ) );
204  }
205 }
206 
208 {
209  mSizeToContents = enabled;
210 }
211 
213 {
214  return mSizeToContents;
215 }
216 
217 void QgsComposerLegend::setCustomLayerTree( QgsLayerTree *rootGroup )
218 {
219  mLegendModel->setRootGroup( rootGroup ? rootGroup : ( mComposition ? mComposition->project()->layerTreeRoot() : nullptr ) );
220 
221  mCustomLayerTree.reset( rootGroup );
222 }
223 
224 
226 {
227  if ( autoUpdate == autoUpdateModel() )
228  return;
229 
230  setCustomLayerTree( autoUpdate ? nullptr : mComposition->project()->layerTreeRoot()->clone() );
231  adjustBoxSize();
232  updateItem();
233 }
234 
235 void QgsComposerLegend::nodeCustomPropertyChanged( QgsLayerTreeNode *, const QString & )
236 {
237  if ( autoUpdateModel() )
238  {
239  // in "auto update" mode, some parameters on the main app legend may have been changed (expression filtering)
240  // we must then call updateItem to reflect the changes
241  updateItem();
242  }
243 }
244 
246 {
247  return !mCustomLayerTree;
248 }
249 
251 {
252  mLegendFilterByMap = enabled;
253  updateItem();
254 }
255 
256 void QgsComposerLegend::setTitle( const QString &t )
257 {
258  mTitle = t;
259  mSettings.setTitle( t );
260 
261  if ( mComposition && id().isEmpty() )
262  {
263  //notify the model that the display name has changed
265  }
266 }
267 QString QgsComposerLegend::title() const { return mTitle; }
268 
269 Qt::AlignmentFlag QgsComposerLegend::titleAlignment() const { return mSettings.titleAlignment(); }
270 void QgsComposerLegend::setTitleAlignment( Qt::AlignmentFlag alignment ) { mSettings.setTitleAlignment( alignment ); }
271 
275 
276 QFont QgsComposerLegend::styleFont( QgsLegendStyle::Style s ) const { return mSettings.style( s ).font(); }
278 
279 void QgsComposerLegend::setStyleMargin( QgsLegendStyle::Style s, double margin ) { rstyle( s ).setMargin( margin ); }
280 void QgsComposerLegend::setStyleMargin( QgsLegendStyle::Style s, QgsLegendStyle::Side side, double margin ) { rstyle( s ).setMargin( side, margin ); }
281 
282 double QgsComposerLegend::lineSpacing() const { return mSettings.lineSpacing(); }
283 void QgsComposerLegend::setLineSpacing( double spacing ) { mSettings.setLineSpacing( spacing ); }
284 
285 double QgsComposerLegend::boxSpace() const { return mSettings.boxSpace(); }
286 void QgsComposerLegend::setBoxSpace( double s ) { mSettings.setBoxSpace( s ); }
287 
288 double QgsComposerLegend::columnSpace() const { return mSettings.columnSpace(); }
289 void QgsComposerLegend::setColumnSpace( double s ) { mSettings.setColumnSpace( s ); }
290 
291 QColor QgsComposerLegend::fontColor() const { return mSettings.fontColor(); }
292 void QgsComposerLegend::setFontColor( const QColor &c ) { mSettings.setFontColor( c ); }
293 
294 double QgsComposerLegend::symbolWidth() const { return mSettings.symbolSize().width(); }
295 void QgsComposerLegend::setSymbolWidth( double w ) { mSettings.setSymbolSize( QSizeF( w, mSettings.symbolSize().height() ) ); }
296 
297 double QgsComposerLegend::symbolHeight() const { return mSettings.symbolSize().height(); }
298 void QgsComposerLegend::setSymbolHeight( double h ) { mSettings.setSymbolSize( QSizeF( mSettings.symbolSize().width(), h ) ); }
299 
300 double QgsComposerLegend::wmsLegendWidth() const { return mSettings.wmsLegendSize().width(); }
301 void QgsComposerLegend::setWmsLegendWidth( double w ) { mSettings.setWmsLegendSize( QSizeF( w, mSettings.wmsLegendSize().height() ) ); }
302 
303 double QgsComposerLegend::wmsLegendHeight() const {return mSettings.wmsLegendSize().height(); }
304 void QgsComposerLegend::setWmsLegendHeight( double h ) { mSettings.setWmsLegendSize( QSizeF( mSettings.wmsLegendSize().width(), h ) ); }
305 
306 void QgsComposerLegend::setWrapChar( const QString &t ) { mSettings.setWrapChar( t ); }
307 QString QgsComposerLegend::wrapChar() const {return mSettings.wrapChar(); }
308 
309 int QgsComposerLegend::columnCount() const { return mColumnCount; }
310 void QgsComposerLegend::setColumnCount( int c ) { mColumnCount = c; mSettings.setColumnCount( c ); }
311 
312 bool QgsComposerLegend::splitLayer() const { return mSettings.splitLayer(); }
313 void QgsComposerLegend::setSplitLayer( bool s ) { mSettings.setSplitLayer( s ); }
314 
315 bool QgsComposerLegend::equalColumnWidth() const { return mSettings.equalColumnWidth(); }
317 
318 bool QgsComposerLegend::drawRasterStroke() const { return mSettings.drawRasterStroke(); }
319 void QgsComposerLegend::setDrawRasterStroke( bool enabled ) { mSettings.setDrawRasterStroke( enabled ); }
320 
321 QColor QgsComposerLegend::rasterStrokeColor() const { return mSettings.rasterStrokeColor(); }
322 void QgsComposerLegend::setRasterStrokeColor( const QColor &color ) { mSettings.setRasterStrokeColor( color ); }
323 
324 double QgsComposerLegend::rasterStrokeWidth() const { return mSettings.rasterStrokeWidth(); }
325 void QgsComposerLegend::setRasterStrokeWidth( double width ) { mSettings.setRasterStrokeWidth( width ); }
326 
328 {
329  adjustBoxSize();
330  updateItem();
331 }
332 
334 {
335  adjustBoxSize();
336  updateItem();
337 }
338 
340 {
341  if ( !updatesEnabled() )
342  return;
343 
344  updateFilterByMap( false );
346 }
347 
348 bool QgsComposerLegend::writeXml( QDomElement &elem, QDomDocument &doc ) const
349 {
350  if ( elem.isNull() )
351  {
352  return false;
353  }
354 
355  QDomElement composerLegendElem = doc.createElement( QStringLiteral( "ComposerLegend" ) );
356  elem.appendChild( composerLegendElem );
357 
358  //write general properties
359  composerLegendElem.setAttribute( QStringLiteral( "title" ), mTitle );
360  composerLegendElem.setAttribute( QStringLiteral( "titleAlignment" ), QString::number( static_cast< int >( mSettings.titleAlignment() ) ) );
361  composerLegendElem.setAttribute( QStringLiteral( "columnCount" ), QString::number( mColumnCount ) );
362  composerLegendElem.setAttribute( QStringLiteral( "splitLayer" ), QString::number( mSettings.splitLayer() ) );
363  composerLegendElem.setAttribute( QStringLiteral( "equalColumnWidth" ), QString::number( mSettings.equalColumnWidth() ) );
364 
365  composerLegendElem.setAttribute( QStringLiteral( "boxSpace" ), QString::number( mSettings.boxSpace() ) );
366  composerLegendElem.setAttribute( QStringLiteral( "columnSpace" ), QString::number( mSettings.columnSpace() ) );
367 
368  composerLegendElem.setAttribute( QStringLiteral( "symbolWidth" ), QString::number( mSettings.symbolSize().width() ) );
369  composerLegendElem.setAttribute( QStringLiteral( "symbolHeight" ), QString::number( mSettings.symbolSize().height() ) );
370  composerLegendElem.setAttribute( QStringLiteral( "lineSpacing" ), QString::number( mSettings.lineSpacing() ) );
371 
372  composerLegendElem.setAttribute( QStringLiteral( "rasterBorder" ), mSettings.drawRasterStroke() );
373  composerLegendElem.setAttribute( QStringLiteral( "rasterBorderColor" ), QgsSymbolLayerUtils::encodeColor( mSettings.rasterStrokeColor() ) );
374  composerLegendElem.setAttribute( QStringLiteral( "rasterBorderWidth" ), QString::number( mSettings.rasterStrokeWidth() ) );
375 
376  composerLegendElem.setAttribute( QStringLiteral( "wmsLegendWidth" ), QString::number( mSettings.wmsLegendSize().width() ) );
377  composerLegendElem.setAttribute( QStringLiteral( "wmsLegendHeight" ), QString::number( mSettings.wmsLegendSize().height() ) );
378  composerLegendElem.setAttribute( QStringLiteral( "wrapChar" ), mSettings.wrapChar() );
379  composerLegendElem.setAttribute( QStringLiteral( "fontColor" ), mSettings.fontColor().name() );
380 
381  composerLegendElem.setAttribute( QStringLiteral( "resizeToContents" ), mSizeToContents );
382 
383  if ( mComposerMap )
384  {
385  composerLegendElem.setAttribute( QStringLiteral( "map" ), mComposerMap->id() );
386  }
387 
388  QDomElement composerLegendStyles = doc.createElement( QStringLiteral( "styles" ) );
389  composerLegendElem.appendChild( composerLegendStyles );
390 
391  style( QgsLegendStyle::Title ).writeXml( QStringLiteral( "title" ), composerLegendStyles, doc );
392  style( QgsLegendStyle::Group ).writeXml( QStringLiteral( "group" ), composerLegendStyles, doc );
393  style( QgsLegendStyle::Subgroup ).writeXml( QStringLiteral( "subgroup" ), composerLegendStyles, doc );
394  style( QgsLegendStyle::Symbol ).writeXml( QStringLiteral( "symbol" ), composerLegendStyles, doc );
395  style( QgsLegendStyle::SymbolLabel ).writeXml( QStringLiteral( "symbolLabel" ), composerLegendStyles, doc );
396 
397  if ( mCustomLayerTree )
398  {
399  // if not using auto-update - store the custom layer tree
400  mCustomLayerTree->writeXml( composerLegendElem );
401  }
402 
403  if ( mLegendFilterByMap )
404  {
405  composerLegendElem.setAttribute( QStringLiteral( "legendFilterByMap" ), QStringLiteral( "1" ) );
406  }
407  composerLegendElem.setAttribute( QStringLiteral( "legendFilterByAtlas" ), mFilterOutAtlas ? QStringLiteral( "1" ) : QStringLiteral( "0" ) );
408 
409  return _writeXml( composerLegendElem, doc );
410 }
411 
412 bool QgsComposerLegend::readXml( const QDomElement &itemElem, const QDomDocument &doc )
413 {
414  if ( itemElem.isNull() )
415  {
416  return false;
417  }
418 
419  //read general properties
420  mTitle = itemElem.attribute( QStringLiteral( "title" ) );
421  mSettings.setTitle( mTitle );
422  if ( !itemElem.attribute( QStringLiteral( "titleAlignment" ) ).isEmpty() )
423  {
424  mSettings.setTitleAlignment( static_cast< Qt::AlignmentFlag >( itemElem.attribute( QStringLiteral( "titleAlignment" ) ).toInt() ) );
425  }
426  int colCount = itemElem.attribute( QStringLiteral( "columnCount" ), QStringLiteral( "1" ) ).toInt();
427  if ( colCount < 1 ) colCount = 1;
428  mColumnCount = colCount;
429  mSettings.setColumnCount( mColumnCount );
430  mSettings.setSplitLayer( itemElem.attribute( QStringLiteral( "splitLayer" ), QStringLiteral( "0" ) ).toInt() == 1 );
431  mSettings.setEqualColumnWidth( itemElem.attribute( QStringLiteral( "equalColumnWidth" ), QStringLiteral( "0" ) ).toInt() == 1 );
432 
433  QDomNodeList stylesNodeList = itemElem.elementsByTagName( QStringLiteral( "styles" ) );
434  if ( !stylesNodeList.isEmpty() )
435  {
436  QDomNode stylesNode = stylesNodeList.at( 0 );
437  for ( int i = 0; i < stylesNode.childNodes().size(); i++ )
438  {
439  QDomElement styleElem = stylesNode.childNodes().at( i ).toElement();
441  style.readXml( styleElem, doc );
442  QString name = styleElem.attribute( QStringLiteral( "name" ) );
444  if ( name == QLatin1String( "title" ) ) s = QgsLegendStyle::Title;
445  else if ( name == QLatin1String( "group" ) ) s = QgsLegendStyle::Group;
446  else if ( name == QLatin1String( "subgroup" ) ) s = QgsLegendStyle::Subgroup;
447  else if ( name == QLatin1String( "symbol" ) ) s = QgsLegendStyle::Symbol;
448  else if ( name == QLatin1String( "symbolLabel" ) ) s = QgsLegendStyle::SymbolLabel;
449  else continue;
450  setStyle( s, style );
451  }
452  }
453 
454  //font color
455  QColor fontClr;
456  fontClr.setNamedColor( itemElem.attribute( QStringLiteral( "fontColor" ), QStringLiteral( "#000000" ) ) );
457  mSettings.setFontColor( fontClr );
458 
459  //spaces
460  mSettings.setBoxSpace( itemElem.attribute( QStringLiteral( "boxSpace" ), QStringLiteral( "2.0" ) ).toDouble() );
461  mSettings.setColumnSpace( itemElem.attribute( QStringLiteral( "columnSpace" ), QStringLiteral( "2.0" ) ).toDouble() );
462 
463  mSettings.setSymbolSize( QSizeF( itemElem.attribute( QStringLiteral( "symbolWidth" ), QStringLiteral( "7.0" ) ).toDouble(), itemElem.attribute( QStringLiteral( "symbolHeight" ), QStringLiteral( "14.0" ) ).toDouble() ) );
464  mSettings.setWmsLegendSize( QSizeF( itemElem.attribute( QStringLiteral( "wmsLegendWidth" ), QStringLiteral( "50" ) ).toDouble(), itemElem.attribute( QStringLiteral( "wmsLegendHeight" ), QStringLiteral( "25" ) ).toDouble() ) );
465  mSettings.setLineSpacing( itemElem.attribute( QStringLiteral( "lineSpacing" ), QStringLiteral( "1.0" ) ).toDouble() );
466 
467  mSettings.setDrawRasterStroke( itemElem.attribute( QStringLiteral( "rasterBorder" ), QStringLiteral( "1" ) ) != QLatin1String( "0" ) );
468  mSettings.setRasterStrokeColor( QgsSymbolLayerUtils::decodeColor( itemElem.attribute( QStringLiteral( "rasterBorderColor" ), QStringLiteral( "0,0,0" ) ) ) );
469  mSettings.setRasterStrokeWidth( itemElem.attribute( QStringLiteral( "rasterBorderWidth" ), QStringLiteral( "0" ) ).toDouble() );
470 
471  mSettings.setWrapChar( itemElem.attribute( QStringLiteral( "wrapChar" ) ) );
472 
473  mSizeToContents = itemElem.attribute( QStringLiteral( "resizeToContents" ), QStringLiteral( "1" ) ) != QLatin1String( "0" );
474 
475  //composer map
476  mLegendFilterByMap = itemElem.attribute( QStringLiteral( "legendFilterByMap" ), QStringLiteral( "0" ) ).toInt();
477  if ( !itemElem.attribute( QStringLiteral( "map" ) ).isEmpty() )
478  {
479  setComposerMap( mComposition->getComposerMapById( itemElem.attribute( QStringLiteral( "map" ) ).toInt() ) );
480  }
481  mFilterOutAtlas = itemElem.attribute( QStringLiteral( "legendFilterByAtlas" ), QStringLiteral( "0" ) ).toInt();
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:92
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:123
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:130
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