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