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