QGIS API Documentation  3.16.0-Hannover (43b64b13f3)
qgslayoutlegendwidget.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgslayoutlegendwidget.cpp
3  -------------------------
4  begin : October 2017
5  copyright : (C) 2017 by Nyall Dawson
6  email : nyall dot dawson at gmail dot com
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 
18 #include "qgslayoutlegendwidget.h"
19 #include "qgslayoutitemlegend.h"
21 #include "qgslayoutitemwidget.h"
22 #include "qgslayoutitemmap.h"
23 #include "qgslayout.h"
24 #include "qgsguiutils.h"
25 
26 #include "qgsapplication.h"
27 #include "qgslayertree.h"
28 #include "qgslayertreeutils.h"
29 #include "qgslayertreemodel.h"
31 #include "qgslegendrenderer.h"
32 #include "qgsmapcanvas.h"
33 #include "qgsmaplayerlegend.h"
34 #include "qgsproject.h"
35 #include "qgsrenderer.h"
36 #include "qgsvectorlayer.h"
37 #include "qgslayoutatlas.h"
38 #include "qgslayoutitemlegend.h"
40 #include "qgsunittypes.h"
44 
45 #include <QMenu>
46 #include <QMessageBox>
47 #include <QInputDialog>
48 
50 
51 Q_GUI_EXPORT extern int qt_defaultDpiX();
52 
53 static int _unfilteredLegendNodeIndex( QgsLayerTreeModelLegendNode *legendNode )
54 {
56 }
57 
58 static int _originalLegendNodeIndex( QgsLayerTreeModelLegendNode *legendNode )
59 {
60  // figure out index of the legend node as it comes out of the map layer legend.
61  // first legend nodes may be reordered, output of that is available in layerOriginalLegendNodes().
62  // next the nodes may be further filtered (by scale, map content etc).
63  // so here we go in reverse order: 1. find index before filtering, 2. find index before reorder
64  int unfilteredNodeIndex = _unfilteredLegendNodeIndex( legendNode );
66  return ( unfilteredNodeIndex >= 0 && unfilteredNodeIndex < order.count() ? order[unfilteredNodeIndex] : -1 );
67 }
68 
69 
70 QgsLayoutLegendWidget::QgsLayoutLegendWidget( QgsLayoutItemLegend *legend, QgsMapCanvas *mapCanvas )
71  : QgsLayoutItemBaseWidget( nullptr, legend )
72  , mLegend( legend )
73  , mMapCanvas( mapCanvas )
74 {
75  Q_ASSERT( mLegend );
76 
77  setupUi( this );
78  connect( mWrapCharLineEdit, &QLineEdit::textChanged, this, &QgsLayoutLegendWidget::mWrapCharLineEdit_textChanged );
79  connect( mTitleLineEdit, &QLineEdit::textChanged, this, &QgsLayoutLegendWidget::mTitleLineEdit_textChanged );
80  connect( mTitleAlignCombo, &QgsAlignmentComboBox::changed, this, &QgsLayoutLegendWidget::titleAlignmentChanged );
81  connect( mGroupAlignCombo, &QgsAlignmentComboBox::changed, this, &QgsLayoutLegendWidget::groupAlignmentChanged );
82  connect( mSubgroupAlignCombo, &QgsAlignmentComboBox::changed, this, &QgsLayoutLegendWidget::subgroupAlignmentChanged );
83  connect( mItemAlignCombo, &QgsAlignmentComboBox::changed, this, &QgsLayoutLegendWidget::itemAlignmentChanged );
84  connect( mColumnCountSpinBox, static_cast < void ( QSpinBox::* )( int ) > ( &QSpinBox::valueChanged ), this, &QgsLayoutLegendWidget::mColumnCountSpinBox_valueChanged );
85  connect( mSplitLayerCheckBox, &QCheckBox::toggled, this, &QgsLayoutLegendWidget::mSplitLayerCheckBox_toggled );
86  connect( mEqualColumnWidthCheckBox, &QCheckBox::toggled, this, &QgsLayoutLegendWidget::mEqualColumnWidthCheckBox_toggled );
87  connect( mSymbolWidthSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsLayoutLegendWidget::mSymbolWidthSpinBox_valueChanged );
88  connect( mSymbolHeightSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsLayoutLegendWidget::mSymbolHeightSpinBox_valueChanged );
89  connect( mMaxSymbolSizeSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsLayoutLegendWidget::mMaxSymbolSizeSpinBox_valueChanged );
90  connect( mMinSymbolSizeSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsLayoutLegendWidget::mMinSymbolSizeSpinBox_valueChanged );
91  connect( mWmsLegendWidthSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsLayoutLegendWidget::mWmsLegendWidthSpinBox_valueChanged );
92  connect( mWmsLegendHeightSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsLayoutLegendWidget::mWmsLegendHeightSpinBox_valueChanged );
93  connect( mTitleSpaceBottomSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsLayoutLegendWidget::mTitleSpaceBottomSpinBox_valueChanged );
94  connect( mGroupSpaceSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsLayoutLegendWidget::mGroupSpaceSpinBox_valueChanged );
95  connect( mSpaceBelowGroupHeadingSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsLayoutLegendWidget::spaceBelowGroupHeadingChanged );
96  connect( mGroupSideSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsLayoutLegendWidget::spaceGroupSideChanged );
97  connect( mLayerSpaceSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsLayoutLegendWidget::mLayerSpaceSpinBox_valueChanged );
98  connect( mSpaceBelowSubgroupHeadingSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsLayoutLegendWidget::spaceBelowSubGroupHeadingChanged );
99  connect( mSubgroupSideSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsLayoutLegendWidget::spaceSubGroupSideChanged );
100  connect( mSymbolSpaceSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsLayoutLegendWidget::mSymbolSpaceSpinBox_valueChanged );
101  connect( mSymbolSideSpaceSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsLayoutLegendWidget::spaceSymbolSideChanged );
102  connect( mIconLabelSpaceSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsLayoutLegendWidget::mIconLabelSpaceSpinBox_valueChanged );
103  connect( mFontColorButton, &QgsColorButton::colorChanged, this, &QgsLayoutLegendWidget::mFontColorButton_colorChanged );
104  connect( mBoxSpaceSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsLayoutLegendWidget::mBoxSpaceSpinBox_valueChanged );
105  connect( mColumnSpaceSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsLayoutLegendWidget::mColumnSpaceSpinBox_valueChanged );
106  connect( mLineSpacingSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsLayoutLegendWidget::mLineSpacingSpinBox_valueChanged );
107  connect( mCheckBoxAutoUpdate, &QCheckBox::stateChanged, this, [ = ]( int state ) { mCheckBoxAutoUpdate_stateChanged( state ); } );
108  connect( mCheckboxResizeContents, &QCheckBox::toggled, this, &QgsLayoutLegendWidget::mCheckboxResizeContents_toggled );
109  connect( mRasterStrokeGroupBox, &QgsCollapsibleGroupBoxBasic::toggled, this, &QgsLayoutLegendWidget::mRasterStrokeGroupBox_toggled );
110  connect( mRasterStrokeWidthSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsLayoutLegendWidget::mRasterStrokeWidthSpinBox_valueChanged );
111  connect( mRasterStrokeColorButton, &QgsColorButton::colorChanged, this, &QgsLayoutLegendWidget::mRasterStrokeColorButton_colorChanged );
112  connect( mMoveDownToolButton, &QToolButton::clicked, this, &QgsLayoutLegendWidget::mMoveDownToolButton_clicked );
113  connect( mMoveUpToolButton, &QToolButton::clicked, this, &QgsLayoutLegendWidget::mMoveUpToolButton_clicked );
114  connect( mRemoveToolButton, &QToolButton::clicked, this, &QgsLayoutLegendWidget::mRemoveToolButton_clicked );
115  connect( mAddToolButton, &QToolButton::clicked, this, &QgsLayoutLegendWidget::mAddToolButton_clicked );
116  connect( mEditPushButton, &QToolButton::clicked, this, &QgsLayoutLegendWidget::mEditPushButton_clicked );
117  connect( mCountToolButton, &QToolButton::clicked, this, &QgsLayoutLegendWidget::mCountToolButton_clicked );
118  connect( mExpressionFilterButton, &QgsLegendFilterButton::toggled, this, &QgsLayoutLegendWidget::mExpressionFilterButton_toggled );
119  connect( mLayerExpressionButton, &QToolButton::clicked, this, &QgsLayoutLegendWidget::mLayerExpressionButton_clicked );
120  connect( mFilterByMapCheckBox, &QCheckBox::toggled, this, &QgsLayoutLegendWidget::mFilterByMapCheckBox_toggled );
121  connect( mUpdateAllPushButton, &QToolButton::clicked, this, &QgsLayoutLegendWidget::mUpdateAllPushButton_clicked );
122  connect( mAddGroupToolButton, &QToolButton::clicked, this, &QgsLayoutLegendWidget::mAddGroupToolButton_clicked );
123  connect( mFilterLegendByAtlasCheckBox, &QCheckBox::toggled, this, &QgsLayoutLegendWidget::mFilterLegendByAtlasCheckBox_toggled );
124  connect( mItemTreeView, &QgsLayerTreeView::doubleClicked, this, &QgsLayoutLegendWidget::mItemTreeView_doubleClicked );
125  setPanelTitle( tr( "Legend Properties" ) );
126 
127  mTitleFontButton->setMode( QgsFontButton::ModeQFont );
128  mGroupFontButton->setMode( QgsFontButton::ModeQFont );
129  mLayerFontButton->setMode( QgsFontButton::ModeQFont );
130  mItemFontButton->setMode( QgsFontButton::ModeQFont );
131 
132  mTitleAlignCombo->setAvailableAlignments( Qt::AlignLeft | Qt::AlignHCenter | Qt::AlignRight );
133  mGroupAlignCombo->setAvailableAlignments( Qt::AlignLeft | Qt::AlignHCenter | Qt::AlignRight );
134  mSubgroupAlignCombo->setAvailableAlignments( Qt::AlignLeft | Qt::AlignHCenter | Qt::AlignRight );
135  mItemAlignCombo->setAvailableAlignments( Qt::AlignLeft | Qt::AlignHCenter | Qt::AlignRight );
136 
137  mArrangementCombo->setAvailableAlignments( Qt::AlignLeft | Qt::AlignRight );
138  connect( mArrangementCombo, &QgsAlignmentComboBox::changed, this, &QgsLayoutLegendWidget::arrangementChanged );
139  mArrangementCombo->customizeAlignmentDisplay( Qt::AlignLeft, tr( "Symbols on Left" ), QgsApplication::getThemeIcon( QStringLiteral( "/mIconArrangeSymbolsLeft.svg" ) ) );
140  mArrangementCombo->customizeAlignmentDisplay( Qt::AlignRight, tr( "Symbols on Right" ), QgsApplication::getThemeIcon( QStringLiteral( "/mIconArrangeSymbolsRight.svg" ) ) );
141 
142  mSpaceBelowGroupHeadingSpinBox->setClearValue( 0 );
143  mGroupSideSpinBox->setClearValue( 0 );
144  mSpaceBelowSubgroupHeadingSpinBox->setClearValue( 0 );
145  mSubgroupSideSpinBox->setClearValue( 0 );
146  mSymbolSideSpaceSpinBox->setClearValue( 0 );
147 
148  // setup icons
149  mAddToolButton->setIcon( QIcon( QgsApplication::iconPath( "symbologyAdd.svg" ) ) );
150  mEditPushButton->setIcon( QIcon( QgsApplication::iconPath( "symbologyEdit.svg" ) ) );
151  mRemoveToolButton->setIcon( QIcon( QgsApplication::iconPath( "symbologyRemove.svg" ) ) );
152  mMoveUpToolButton->setIcon( QIcon( QgsApplication::iconPath( "mActionArrowUp.svg" ) ) );
153  mMoveDownToolButton->setIcon( QIcon( QgsApplication::iconPath( "mActionArrowDown.svg" ) ) );
154  mCountToolButton->setIcon( QIcon( QgsApplication::iconPath( "mActionSum.svg" ) ) );
155  mLayerExpressionButton->setIcon( QIcon( QgsApplication::iconPath( "mIconExpression.svg" ) ) );
156 
157  mMoveDownToolButton->setIconSize( QgsGuiUtils::iconSize( true ) );
158  mMoveUpToolButton->setIconSize( QgsGuiUtils::iconSize( true ) );
159  mAddGroupToolButton->setIconSize( QgsGuiUtils::iconSize( true ) );
160  mAddToolButton->setIconSize( QgsGuiUtils::iconSize( true ) );
161  mRemoveToolButton->setIconSize( QgsGuiUtils::iconSize( true ) );
162  mEditPushButton->setIconSize( QgsGuiUtils::iconSize( true ) );
163  mCountToolButton->setIconSize( QgsGuiUtils::iconSize( true ) );
164  mExpressionFilterButton->setIconSize( QgsGuiUtils::iconSize( true ) );
165  mLayerExpressionButton->setIconSize( QgsGuiUtils::iconSize( true ) );
166 
167  mFontColorButton->setColorDialogTitle( tr( "Select Font Color" ) );
168  mFontColorButton->setContext( QStringLiteral( "composer" ) );
169 
170  mRasterStrokeColorButton->setColorDialogTitle( tr( "Select Stroke Color" ) );
171  mRasterStrokeColorButton->setAllowOpacity( true );
172  mRasterStrokeColorButton->setContext( QStringLiteral( "composer " ) );
173 
174  mMapComboBox->setCurrentLayout( legend->layout() );
175  mMapComboBox->setItemType( QgsLayoutItemRegistry::LayoutMap );
176  connect( mMapComboBox, &QgsLayoutItemComboBox::itemChanged, this, &QgsLayoutLegendWidget::composerMapChanged );
177 
178  //add widget for general composer item properties
179  mItemPropertiesWidget = new QgsLayoutItemPropertiesWidget( this, legend );
180  mainLayout->addWidget( mItemPropertiesWidget );
181 
182  mItemTreeView->setHeaderHidden( true );
183 
184  mItemTreeView->setModel( legend->model() );
185  mItemTreeView->setMenuProvider( new QgsLayoutLegendMenuProvider( mItemTreeView, this ) );
186  setLegendMapViewData();
187  connect( legend, &QgsLayoutObject::changed, this, &QgsLayoutLegendWidget::setGuiElements );
188 
189  // connect atlas state to the filter legend by atlas checkbox
190  if ( layoutAtlas() )
191  {
192  connect( layoutAtlas(), &QgsLayoutAtlas::toggled, this, &QgsLayoutLegendWidget::updateFilterLegendByAtlasButton );
193  }
194  connect( &legend->layout()->reportContext(), &QgsLayoutReportContext::layerChanged, this, &QgsLayoutLegendWidget::updateFilterLegendByAtlasButton );
195 
196  registerDataDefinedButton( mLegendTitleDDBtn, QgsLayoutObject::LegendTitle );
197  registerDataDefinedButton( mColumnsDDBtn, QgsLayoutObject::LegendColumnCount );
198 
199  setGuiElements();
200 
201  connect( mItemTreeView->selectionModel(), &QItemSelectionModel::currentChanged,
202  this, &QgsLayoutLegendWidget::selectedChanged );
203  connect( mTitleFontButton, &QgsFontButton::changed, this, &QgsLayoutLegendWidget::titleFontChanged );
204  connect( mGroupFontButton, &QgsFontButton::changed, this, &QgsLayoutLegendWidget::groupFontChanged );
205  connect( mLayerFontButton, &QgsFontButton::changed, this, &QgsLayoutLegendWidget::layerFontChanged );
206  connect( mItemFontButton, &QgsFontButton::changed, this, &QgsLayoutLegendWidget::itemFontChanged );
207 }
208 
209 void QgsLayoutLegendWidget::setMasterLayout( QgsMasterLayoutInterface *masterLayout )
210 {
211  if ( mItemPropertiesWidget )
212  mItemPropertiesWidget->setMasterLayout( masterLayout );
213 }
214 
215 void QgsLayoutLegendWidget::setGuiElements()
216 {
217  if ( !mLegend )
218  {
219  return;
220  }
221 
222  blockAllSignals( true );
223  mTitleLineEdit->setText( mLegend->title() );
224  whileBlocking( mTitleAlignCombo )->setCurrentAlignment( mLegend->titleAlignment() );
225  whileBlocking( mGroupAlignCombo )->setCurrentAlignment( mLegend->style( QgsLegendStyle::Group ).alignment() );
226  whileBlocking( mSubgroupAlignCombo )->setCurrentAlignment( mLegend->style( QgsLegendStyle::Subgroup ).alignment() );
227  whileBlocking( mItemAlignCombo )->setCurrentAlignment( mLegend->style( QgsLegendStyle::SymbolLabel ).alignment() );
228  whileBlocking( mArrangementCombo )->setCurrentAlignment( mLegend->symbolAlignment() );
229  mFilterByMapCheckBox->setChecked( mLegend->legendFilterByMapEnabled() );
230  mColumnCountSpinBox->setValue( mLegend->columnCount() );
231  mSplitLayerCheckBox->setChecked( mLegend->splitLayer() );
232  mEqualColumnWidthCheckBox->setChecked( mLegend->equalColumnWidth() );
233  mSymbolWidthSpinBox->setValue( mLegend->symbolWidth() );
234  mSymbolHeightSpinBox->setValue( mLegend->symbolHeight() );
235  mMaxSymbolSizeSpinBox->setValue( mLegend->maximumSymbolSize() );
236  mMinSymbolSizeSpinBox->setValue( mLegend->minimumSymbolSize() );
237  mWmsLegendWidthSpinBox->setValue( mLegend->wmsLegendWidth() );
238  mWmsLegendHeightSpinBox->setValue( mLegend->wmsLegendHeight() );
239  mTitleSpaceBottomSpinBox->setValue( mLegend->style( QgsLegendStyle::Title ).margin( QgsLegendStyle::Bottom ) );
240  mGroupSpaceSpinBox->setValue( mLegend->style( QgsLegendStyle::Group ).margin( QgsLegendStyle::Top ) );
241  mGroupSideSpinBox->setValue( mLegend->style( QgsLegendStyle::Group ).margin( QgsLegendStyle::Left ) );
242  mSpaceBelowGroupHeadingSpinBox->setValue( mLegend->style( QgsLegendStyle::Group ).margin( QgsLegendStyle::Bottom ) );
243  mLayerSpaceSpinBox->setValue( mLegend->style( QgsLegendStyle::Subgroup ).margin( QgsLegendStyle::Top ) );
244  mSpaceBelowSubgroupHeadingSpinBox->setValue( mLegend->style( QgsLegendStyle::Subgroup ).margin( QgsLegendStyle::Bottom ) );
245  mSubgroupSideSpinBox->setValue( mLegend->style( QgsLegendStyle::Subgroup ).margin( QgsLegendStyle::Left ) );
246  // We keep Symbol and SymbolLabel Top in sync for now
247  mSymbolSpaceSpinBox->setValue( mLegend->style( QgsLegendStyle::Symbol ).margin( QgsLegendStyle::Top ) );
248  mIconLabelSpaceSpinBox->setValue( mLegend->style( QgsLegendStyle::SymbolLabel ).margin( QgsLegendStyle::Left ) );
249  mSymbolSideSpaceSpinBox->setValue( mLegend->style( QgsLegendStyle::Symbol ).margin( QgsLegendStyle::Left ) );
250  mBoxSpaceSpinBox->setValue( mLegend->boxSpace() );
251  mColumnSpaceSpinBox->setValue( mLegend->columnSpace() );
252  mLineSpacingSpinBox->setValue( mLegend->lineSpacing() );
253 
254  mRasterStrokeGroupBox->setChecked( mLegend->drawRasterStroke() );
255  mRasterStrokeWidthSpinBox->setValue( mLegend->rasterStrokeWidth() );
256  mRasterStrokeColorButton->setColor( mLegend->rasterStrokeColor() );
257 
258  mCheckBoxAutoUpdate->setChecked( mLegend->autoUpdateModel() );
259 
260  mCheckboxResizeContents->setChecked( mLegend->resizeToContents() );
261  mFilterLegendByAtlasCheckBox->setChecked( mLegend->legendFilterOutAtlas() );
262  mWrapCharLineEdit->setText( mLegend->wrapString() );
263 
264  QgsLayoutItemMap *map = mLegend->linkedMap();
265  mMapComboBox->setItem( map );
266  mFontColorButton->setColor( mLegend->fontColor() );
267  mTitleFontButton->setCurrentFont( mLegend->style( QgsLegendStyle::Title ).font() );
268  mGroupFontButton->setCurrentFont( mLegend->style( QgsLegendStyle::Group ).font() );
269  mLayerFontButton->setCurrentFont( mLegend->style( QgsLegendStyle::Subgroup ).font() );
270  mItemFontButton->setCurrentFont( mLegend->style( QgsLegendStyle::SymbolLabel ).font() );
271 
272  blockAllSignals( false );
273 
274  mCheckBoxAutoUpdate_stateChanged( mLegend->autoUpdateModel() ? Qt::Checked : Qt::Unchecked, false );
275  updateDataDefinedButton( mLegendTitleDDBtn );
276  updateDataDefinedButton( mColumnsDDBtn );
277 }
278 
279 void QgsLayoutLegendWidget::mWrapCharLineEdit_textChanged( const QString &text )
280 {
281  if ( mLegend )
282  {
283  mLegend->beginCommand( tr( "Change Legend Wrap" ) );
284  mLegend->setWrapString( text );
285  mLegend->adjustBoxSize();
286  mLegend->update();
287  mLegend->endCommand();
288  }
289 }
290 
291 void QgsLayoutLegendWidget::mTitleLineEdit_textChanged( const QString &text )
292 {
293  if ( mLegend )
294  {
295  mLegend->beginCommand( tr( "Change Legend Title" ), QgsLayoutItem::UndoLegendText );
296  mLegend->setTitle( text );
297  mLegend->adjustBoxSize();
298  mLegend->update();
299  mLegend->endCommand();
300  }
301 }
302 
303 void QgsLayoutLegendWidget::titleAlignmentChanged()
304 {
305  if ( mLegend )
306  {
307  Qt::AlignmentFlag alignment = static_cast< Qt::AlignmentFlag >( static_cast< int >( mTitleAlignCombo->currentAlignment() & Qt::AlignHorizontal_Mask ) );
308  mLegend->beginCommand( tr( "Change Title Alignment" ) );
309  mLegend->setTitleAlignment( alignment );
310  mLegend->update();
311  mLegend->endCommand();
312  }
313 }
314 
315 void QgsLayoutLegendWidget::groupAlignmentChanged()
316 {
317  if ( mLegend )
318  {
319  mLegend->beginCommand( tr( "Change Group Alignment" ) );
320  mLegend->rstyle( QgsLegendStyle::Group ).setAlignment( mGroupAlignCombo->currentAlignment() );
321  mLegend->update();
322  mLegend->endCommand();
323  }
324 }
325 
326 void QgsLayoutLegendWidget::subgroupAlignmentChanged()
327 {
328  if ( mLegend )
329  {
330  mLegend->beginCommand( tr( "Change Subgroup Alignment" ) );
331  mLegend->rstyle( QgsLegendStyle::Subgroup ).setAlignment( mSubgroupAlignCombo->currentAlignment() );
332  mLegend->update();
333  mLegend->endCommand();
334  }
335 }
336 
337 void QgsLayoutLegendWidget::itemAlignmentChanged()
338 {
339  if ( mLegend )
340  {
341  mLegend->beginCommand( tr( "Change Item Alignment" ) );
342  mLegend->rstyle( QgsLegendStyle::SymbolLabel ).setAlignment( mItemAlignCombo->currentAlignment() );
343  mLegend->update();
344  mLegend->endCommand();
345  }
346 }
347 
348 void QgsLayoutLegendWidget::arrangementChanged()
349 {
350  if ( mLegend )
351  {
352  Qt::AlignmentFlag alignment = static_cast< Qt::AlignmentFlag >( static_cast< int >( mArrangementCombo->currentAlignment() & Qt::AlignHorizontal_Mask ) );
353  mLegend->beginCommand( tr( "Change Legend Arrangement" ) );
354  mLegend->setSymbolAlignment( alignment );
355  mLegend->update();
356  mLegend->endCommand();
357  }
358 }
359 
360 void QgsLayoutLegendWidget::mColumnCountSpinBox_valueChanged( int c )
361 {
362  if ( mLegend )
363  {
364  mLegend->beginCommand( tr( "Change Column Count" ), QgsLayoutItem::UndoLegendColumnCount );
365  mLegend->setColumnCount( c );
366  mLegend->adjustBoxSize();
367  mLegend->update();
368  mLegend->endCommand();
369  }
370  mSplitLayerCheckBox->setEnabled( c > 1 );
371  mEqualColumnWidthCheckBox->setEnabled( c > 1 );
372 }
373 
374 void QgsLayoutLegendWidget::mSplitLayerCheckBox_toggled( bool checked )
375 {
376  if ( mLegend )
377  {
378  mLegend->beginCommand( tr( "Split Legend Layers" ) );
379  mLegend->setSplitLayer( checked );
380  mLegend->adjustBoxSize();
381  mLegend->update();
382  mLegend->endCommand();
383  }
384 }
385 
386 void QgsLayoutLegendWidget::mEqualColumnWidthCheckBox_toggled( bool checked )
387 {
388  if ( mLegend )
389  {
390  mLegend->beginCommand( tr( "Legend Column Width" ) );
391  mLegend->setEqualColumnWidth( checked );
392  mLegend->adjustBoxSize();
393  mLegend->update();
394  mLegend->endCommand();
395  }
396 }
397 
398 void QgsLayoutLegendWidget::mSymbolWidthSpinBox_valueChanged( double d )
399 {
400  if ( mLegend )
401  {
402  mLegend->beginCommand( tr( "Resize Symbol Width" ), QgsLayoutItem::UndoLegendSymbolWidth );
403  mLegend->setSymbolWidth( d );
404  mLegend->adjustBoxSize();
405  mLegend->update();
406  mLegend->endCommand();
407  }
408 }
409 
410 void QgsLayoutLegendWidget::mMaxSymbolSizeSpinBox_valueChanged( double d )
411 {
412  if ( mLegend )
413  {
414  mLegend->beginCommand( tr( "Change Legend Maximum Symbol Size" ), QgsLayoutItem::UndoLegendMaxSymbolSize );
415  mLegend->setMaximumSymbolSize( d );
416  mLegend->adjustBoxSize();
417  mLegend->update();
418  mLegend->endCommand();
419  }
420 }
421 
422 void QgsLayoutLegendWidget::mMinSymbolSizeSpinBox_valueChanged( double d )
423 {
424  if ( mLegend )
425  {
426  mLegend->beginCommand( tr( "Change Legend Minimum Symbol Size" ), QgsLayoutItem::UndoLegendMinSymbolSize );
427  mLegend->setMinimumSymbolSize( d );
428  mLegend->adjustBoxSize();
429  mLegend->update();
430  mLegend->endCommand();
431  }
432 }
433 
434 void QgsLayoutLegendWidget::mSymbolHeightSpinBox_valueChanged( double d )
435 {
436  if ( mLegend )
437  {
438  mLegend->beginCommand( tr( "Resize Symbol Height" ), QgsLayoutItem::UndoLegendSymbolHeight );
439  mLegend->setSymbolHeight( d );
440  mLegend->adjustBoxSize();
441  mLegend->update();
442  mLegend->endCommand();
443  }
444 }
445 
446 void QgsLayoutLegendWidget::mWmsLegendWidthSpinBox_valueChanged( double d )
447 {
448  if ( mLegend )
449  {
450  mLegend->beginCommand( tr( "Resize WMS Width" ), QgsLayoutItem::UndoLegendWmsLegendWidth );
451  mLegend->setWmsLegendWidth( d );
452  mLegend->adjustBoxSize();
453  mLegend->update();
454  mLegend->endCommand();
455  }
456 }
457 
458 void QgsLayoutLegendWidget::mWmsLegendHeightSpinBox_valueChanged( double d )
459 {
460  if ( mLegend )
461  {
462  mLegend->beginCommand( tr( "Resize WMS Height" ), QgsLayoutItem::UndoLegendWmsLegendHeight );
463  mLegend->setWmsLegendHeight( d );
464  mLegend->adjustBoxSize();
465  mLegend->update();
466  mLegend->endCommand();
467  }
468 }
469 
470 void QgsLayoutLegendWidget::mTitleSpaceBottomSpinBox_valueChanged( double d )
471 {
472  if ( mLegend )
473  {
474  mLegend->beginCommand( tr( "Change Title Space" ), QgsLayoutItem::UndoLegendTitleSpaceBottom );
475  mLegend->rstyle( QgsLegendStyle::Title ).setMargin( QgsLegendStyle::Bottom, d );
476  mLegend->adjustBoxSize();
477  mLegend->update();
478  mLegend->endCommand();
479  }
480 }
481 
482 void QgsLayoutLegendWidget::mGroupSpaceSpinBox_valueChanged( double d )
483 {
484  if ( mLegend )
485  {
486  mLegend->beginCommand( tr( "Change Group Space" ), QgsLayoutItem::UndoLegendGroupSpace );
487  mLegend->rstyle( QgsLegendStyle::Group ).setMargin( QgsLegendStyle::Top, d );
488  mLegend->adjustBoxSize();
489  mLegend->update();
490  mLegend->endCommand();
491  }
492 }
493 
494 void QgsLayoutLegendWidget::spaceBelowGroupHeadingChanged( double space )
495 {
496  if ( mLegend )
497  {
498  mLegend->beginCommand( tr( "Change Group Space" ), QgsLayoutItem::UndoLegendGroupSpace );
499  mLegend->rstyle( QgsLegendStyle::Group ).setMargin( QgsLegendStyle::Bottom, space );
500  mLegend->adjustBoxSize();
501  mLegend->update();
502  mLegend->endCommand();
503  }
504 }
505 
506 void QgsLayoutLegendWidget::spaceGroupSideChanged( double space )
507 {
508  if ( mLegend )
509  {
510  mLegend->beginCommand( tr( "Change Side of Group Space" ), QgsLayoutItem::UndoLegendGroupSpace );
511  mLegend->rstyle( QgsLegendStyle::Group ).setMargin( QgsLegendStyle::Left, space );
512  mLegend->adjustBoxSize();
513  mLegend->update();
514  mLegend->endCommand();
515  }
516 }
517 
518 void QgsLayoutLegendWidget::spaceSubGroupSideChanged( double space )
519 {
520  if ( mLegend )
521  {
522  mLegend->beginCommand( tr( "Change Side of Subgroup Space" ), QgsLayoutItem::UndoLegendLayerSpace );
523  mLegend->rstyle( QgsLegendStyle::Subgroup ).setMargin( QgsLegendStyle::Left, space );
524  mLegend->adjustBoxSize();
525  mLegend->update();
526  mLegend->endCommand();
527  }
528 }
529 
530 void QgsLayoutLegendWidget::spaceSymbolSideChanged( double space )
531 {
532  if ( mLegend )
533  {
534  mLegend->beginCommand( tr( "Change Side of Symbol Space" ), QgsLayoutItem::UndoLegendSymbolSpace );
535  mLegend->rstyle( QgsLegendStyle::Symbol ).setMargin( QgsLegendStyle::Left, space );
536  mLegend->adjustBoxSize();
537  mLegend->update();
538  mLegend->endCommand();
539  }
540 }
541 
542 void QgsLayoutLegendWidget::mLayerSpaceSpinBox_valueChanged( double d )
543 {
544  if ( mLegend )
545  {
546  mLegend->beginCommand( tr( "Change Subgroup Space" ), QgsLayoutItem::UndoLegendLayerSpace );
547  mLegend->rstyle( QgsLegendStyle::Subgroup ).setMargin( QgsLegendStyle::Top, d );
548  mLegend->adjustBoxSize();
549  mLegend->update();
550  mLegend->endCommand();
551  }
552 }
553 
554 void QgsLayoutLegendWidget::mSymbolSpaceSpinBox_valueChanged( double d )
555 {
556  if ( mLegend )
557  {
558  mLegend->beginCommand( tr( "Change Symbol Space" ), QgsLayoutItem::UndoLegendSymbolSpace );
559  // We keep Symbol and SymbolLabel Top in sync for now
560  mLegend->rstyle( QgsLegendStyle::Symbol ).setMargin( QgsLegendStyle::Top, d );
561  mLegend->rstyle( QgsLegendStyle::SymbolLabel ).setMargin( QgsLegendStyle::Top, d );
562  mLegend->adjustBoxSize();
563  mLegend->update();
564  mLegend->endCommand();
565  }
566 }
567 
568 void QgsLayoutLegendWidget::mIconLabelSpaceSpinBox_valueChanged( double d )
569 {
570  if ( mLegend )
571  {
572  mLegend->beginCommand( tr( "Change Label Space" ), QgsLayoutItem::UndoLegendIconSymbolSpace );
573  mLegend->rstyle( QgsLegendStyle::SymbolLabel ).setMargin( QgsLegendStyle::Left, d );
574  mLegend->adjustBoxSize();
575  mLegend->update();
576  mLegend->endCommand();
577  }
578 }
579 
580 void QgsLayoutLegendWidget::titleFontChanged()
581 {
582  if ( mLegend )
583  {
584  mLegend->beginCommand( tr( "Change Title Font" ), QgsLayoutItem::UndoLegendTitleFont );
585  mLegend->setStyleFont( QgsLegendStyle::Title, mTitleFontButton->currentFont() );
586  mLegend->adjustBoxSize();
587  mLegend->update();
588  mLegend->endCommand();
589  }
590 }
591 
592 void QgsLayoutLegendWidget::groupFontChanged()
593 {
594  if ( mLegend )
595  {
596  mLegend->beginCommand( tr( "Change Group Font" ), QgsLayoutItem::UndoLegendGroupFont );
597  mLegend->setStyleFont( QgsLegendStyle::Group, mGroupFontButton->currentFont() );
598  mLegend->adjustBoxSize();
599  mLegend->update();
600  mLegend->endCommand();
601  }
602 }
603 
604 void QgsLayoutLegendWidget::layerFontChanged()
605 {
606  if ( mLegend )
607  {
608  mLegend->beginCommand( tr( "Change Layer Font" ), QgsLayoutItem::UndoLegendLayerFont );
609  mLegend->setStyleFont( QgsLegendStyle::Subgroup, mLayerFontButton->currentFont() );
610  mLegend->adjustBoxSize();
611  mLegend->update();
612  mLegend->endCommand();
613  }
614 }
615 
616 void QgsLayoutLegendWidget::itemFontChanged()
617 {
618  if ( mLegend )
619  {
620  mLegend->beginCommand( tr( "Change Item Font" ), QgsLayoutItem::UndoLegendItemFont );
621  mLegend->setStyleFont( QgsLegendStyle::SymbolLabel, mItemFontButton->currentFont() );
622  mLegend->adjustBoxSize();
623  mLegend->update();
624  mLegend->endCommand();
625  }
626 }
627 
628 void QgsLayoutLegendWidget::spaceBelowSubGroupHeadingChanged( double space )
629 {
630  if ( mLegend )
631  {
632  mLegend->beginCommand( tr( "Change Subgroup Space" ), QgsLayoutItem::UndoLegendLayerSpace );
633  mLegend->rstyle( QgsLegendStyle::Subgroup ).setMargin( QgsLegendStyle::Bottom, space );
634  mLegend->adjustBoxSize();
635  mLegend->update();
636  mLegend->endCommand();
637  }
638 }
639 
640 void QgsLayoutLegendWidget::mFontColorButton_colorChanged( const QColor &newFontColor )
641 {
642  if ( !mLegend )
643  {
644  return;
645  }
646 
647  mLegend->beginCommand( tr( "Change Font Color" ), QgsLayoutItem::UndoLegendFontColor );
648  mLegend->setFontColor( newFontColor );
649  mLegend->update();
650  mLegend->endCommand();
651 }
652 
653 void QgsLayoutLegendWidget::mBoxSpaceSpinBox_valueChanged( double d )
654 {
655  if ( mLegend )
656  {
657  mLegend->beginCommand( tr( "Change Box Space" ), QgsLayoutItem::UndoLegendBoxSpace );
658  mLegend->setBoxSpace( d );
659  mLegend->adjustBoxSize();
660  mLegend->update();
661  mLegend->endCommand();
662  }
663 }
664 
665 void QgsLayoutLegendWidget::mColumnSpaceSpinBox_valueChanged( double d )
666 {
667  if ( mLegend )
668  {
669  mLegend->beginCommand( tr( "Change Column Space" ), QgsLayoutItem::UndoLegendColumnSpace );
670  mLegend->setColumnSpace( d );
671  mLegend->adjustBoxSize();
672  mLegend->update();
673  mLegend->endCommand();
674  }
675 }
676 
677 void QgsLayoutLegendWidget::mLineSpacingSpinBox_valueChanged( double d )
678 {
679  if ( mLegend )
680  {
681  mLegend->beginCommand( tr( "Change Line Space" ), QgsLayoutItem::UndoLegendLineSpacing );
682  mLegend->setLineSpacing( d );
683  mLegend->adjustBoxSize();
684  mLegend->update();
685  mLegend->endCommand();
686  }
687 }
688 
689 static void _moveLegendNode( QgsLayerTreeLayer *nodeLayer, int legendNodeIndex, int offset )
690 {
691  QList<int> order = QgsMapLayerLegendUtils::legendNodeOrder( nodeLayer );
692 
693  if ( legendNodeIndex < 0 || legendNodeIndex >= order.count() )
694  return;
695  if ( legendNodeIndex + offset < 0 || legendNodeIndex + offset >= order.count() )
696  return;
697 
698  int id = order.takeAt( legendNodeIndex );
699  order.insert( legendNodeIndex + offset, id );
700 
701  QgsMapLayerLegendUtils::setLegendNodeOrder( nodeLayer, order );
702 }
703 
704 
705 void QgsLayoutLegendWidget::mMoveDownToolButton_clicked()
706 {
707  if ( !mLegend )
708  {
709  return;
710  }
711 
712  QModelIndex index = mItemTreeView->selectionModel()->currentIndex();
713  QModelIndex parentIndex = index.parent();
714  if ( !index.isValid() || index.row() == mItemTreeView->model()->rowCount( parentIndex ) - 1 )
715  return;
716 
717  QgsLayerTreeNode *node = mItemTreeView->layerTreeModel()->index2node( index );
718  QgsLayerTreeModelLegendNode *legendNode = mItemTreeView->layerTreeModel()->index2legendNode( index );
719  if ( !node && !legendNode )
720  return;
721 
722  mLegend->beginCommand( tr( "Moved Legend Item Down" ) );
723 
724  if ( node )
725  {
726  QgsLayerTreeGroup *parentGroup = QgsLayerTree::toGroup( node->parent() );
727  parentGroup->insertChildNode( index.row() + 2, node->clone() );
728  parentGroup->removeChildNode( node );
729  }
730  else // legend node
731  {
732  _moveLegendNode( legendNode->layerNode(), _unfilteredLegendNodeIndex( legendNode ), 1 );
733  mItemTreeView->layerTreeModel()->refreshLayerLegend( legendNode->layerNode() );
734  }
735 
736  mItemTreeView->setCurrentIndex( mItemTreeView->layerTreeModel()->index( index.row() + 1, 0, parentIndex ) );
737 
738  mLegend->update();
739  mLegend->endCommand();
740 }
741 
742 void QgsLayoutLegendWidget::mMoveUpToolButton_clicked()
743 {
744  if ( !mLegend )
745  {
746  return;
747  }
748 
749  QModelIndex index = mItemTreeView->selectionModel()->currentIndex();
750  QModelIndex parentIndex = index.parent();
751  if ( !index.isValid() || index.row() == 0 )
752  return;
753 
754  QgsLayerTreeNode *node = mItemTreeView->layerTreeModel()->index2node( index );
755  QgsLayerTreeModelLegendNode *legendNode = mItemTreeView->layerTreeModel()->index2legendNode( index );
756  if ( !node && !legendNode )
757  return;
758 
759  mLegend->beginCommand( tr( "Move Legend Item Up" ) );
760 
761  if ( node )
762  {
763  QgsLayerTreeGroup *parentGroup = QgsLayerTree::toGroup( node->parent() );
764  parentGroup->insertChildNode( index.row() - 1, node->clone() );
765  parentGroup->removeChildNode( node );
766  }
767  else // legend node
768  {
769  _moveLegendNode( legendNode->layerNode(), _unfilteredLegendNodeIndex( legendNode ), -1 );
770  mItemTreeView->layerTreeModel()->refreshLayerLegend( legendNode->layerNode() );
771  }
772 
773  mItemTreeView->setCurrentIndex( mItemTreeView->layerTreeModel()->index( index.row() - 1, 0, parentIndex ) );
774 
775  mLegend->update();
776  mLegend->endCommand();
777 }
778 
779 void QgsLayoutLegendWidget::mCheckBoxAutoUpdate_stateChanged( int state, bool userTriggered )
780 {
781  if ( userTriggered )
782  {
783  mLegend->beginCommand( tr( "Change Auto Update" ) );
784 
785  mLegend->setAutoUpdateModel( state == Qt::Checked );
786 
787  mLegend->updateFilterByMap();
788  mLegend->endCommand();
789  }
790 
791  // do not allow editing of model if auto update is on - we would modify project's layer tree
792  QList<QWidget *> widgets;
793  widgets << mMoveDownToolButton << mMoveUpToolButton << mRemoveToolButton << mAddToolButton
794  << mEditPushButton << mCountToolButton << mUpdateAllPushButton << mAddGroupToolButton
795  << mExpressionFilterButton;
796  for ( QWidget *w : qgis::as_const( widgets ) )
797  w->setEnabled( state != Qt::Checked );
798 
799  if ( state == Qt::Unchecked )
800  {
801  // update widgets state based on current selection
802  selectedChanged( QModelIndex(), QModelIndex() );
803  }
804 }
805 
806 void QgsLayoutLegendWidget::composerMapChanged( QgsLayoutItem *item )
807 {
808  if ( !mLegend )
809  {
810  return;
811  }
812 
813  QgsLayout *layout = mLegend->layout();
814  if ( !layout )
815  {
816  return;
817  }
818 
819  QgsLayoutItemMap *map = qobject_cast< QgsLayoutItemMap * >( item );
820  if ( map )
821  {
822  mLegend->beginCommand( tr( "Change Legend Map" ) );
823  mLegend->setLinkedMap( map );
824  mLegend->updateFilterByMap();
825  mLegend->endCommand();
826 
827  setLegendMapViewData();
828  }
829 }
830 
831 void QgsLayoutLegendWidget::mCheckboxResizeContents_toggled( bool checked )
832 {
833  if ( !mLegend )
834  {
835  return;
836  }
837 
838  mLegend->beginCommand( tr( "Resize Legend to Contents" ) );
839  mLegend->setResizeToContents( checked );
840  if ( checked )
841  mLegend->adjustBoxSize();
842  mLegend->updateFilterByMap();
843  mLegend->endCommand();
844 }
845 
846 void QgsLayoutLegendWidget::mRasterStrokeGroupBox_toggled( bool state )
847 {
848  if ( !mLegend )
849  {
850  return;
851  }
852 
853  mLegend->beginCommand( tr( "Change Legend Borders" ) );
854  mLegend->setDrawRasterStroke( state );
855  mLegend->adjustBoxSize();
856  mLegend->update();
857  mLegend->endCommand();
858 }
859 
860 void QgsLayoutLegendWidget::mRasterStrokeWidthSpinBox_valueChanged( double d )
861 {
862  if ( !mLegend )
863  {
864  return;
865  }
866 
867  mLegend->beginCommand( tr( "Resize Legend Borders" ), QgsLayoutItem::UndoLegendRasterStrokeWidth );
868  mLegend->setRasterStrokeWidth( d );
869  mLegend->adjustBoxSize();
870  mLegend->update();
871  mLegend->endCommand();
872 }
873 
874 void QgsLayoutLegendWidget::mRasterStrokeColorButton_colorChanged( const QColor &newColor )
875 {
876  if ( !mLegend )
877  {
878  return;
879  }
880 
881  mLegend->beginCommand( tr( "Change Legend Border Color" ), QgsLayoutItem::UndoLegendRasterStrokeColor );
882  mLegend->setRasterStrokeColor( newColor );
883  mLegend->update();
884  mLegend->endCommand();
885 }
886 
887 void QgsLayoutLegendWidget::mAddToolButton_clicked()
888 {
889  if ( !mLegend )
890  {
891  return;
892  }
893 
894  QList< QgsMapLayer * > visibleLayers;
895  if ( mLegend->linkedMap() )
896  {
897  visibleLayers = mLegend->linkedMap()->layersToRender();
898  }
899  if ( visibleLayers.isEmpty() )
900  {
901  // just use current canvas layers as visible layers
902  visibleLayers = mMapCanvas->layers();
903  }
904 
905  QgsLayoutLegendLayersDialog addDialog( this );
906  addDialog.setVisibleLayers( visibleLayers );
907  if ( addDialog.exec() == QDialog::Accepted )
908  {
909  const QList<QgsMapLayer *> layers = addDialog.selectedLayers();
910  if ( !layers.empty() )
911  {
912  mLegend->beginCommand( tr( "Add Legend Item(s)" ) );
913  for ( QgsMapLayer *layer : layers )
914  {
915  mLegend->model()->rootGroup()->addLayer( layer );
916  }
917  mLegend->updateLegend();
918  mLegend->update();
919  mLegend->endCommand();
920  }
921  }
922 }
923 
924 void QgsLayoutLegendWidget::mRemoveToolButton_clicked()
925 {
926  if ( !mLegend )
927  {
928  return;
929  }
930 
931  QItemSelectionModel *selectionModel = mItemTreeView->selectionModel();
932  if ( !selectionModel )
933  {
934  return;
935  }
936 
937  mLegend->beginCommand( tr( "Remove Legend Item" ) );
938 
939  QList<QPersistentModelIndex> indexes;
940  const auto constSelectedIndexes = selectionModel->selectedIndexes();
941  for ( const QModelIndex &index : constSelectedIndexes )
942  indexes << index;
943 
944  // first try to remove legend nodes
945  QHash<QgsLayerTreeLayer *, QList<int> > nodesWithRemoval;
946  for ( const QPersistentModelIndex &index : qgis::as_const( indexes ) )
947  {
948  if ( QgsLayerTreeModelLegendNode *legendNode = mItemTreeView->layerTreeModel()->index2legendNode( index ) )
949  {
950  QgsLayerTreeLayer *nodeLayer = legendNode->layerNode();
951  nodesWithRemoval[nodeLayer].append( _unfilteredLegendNodeIndex( legendNode ) );
952  }
953  }
954  for ( auto it = nodesWithRemoval.constBegin(); it != nodesWithRemoval.constEnd(); ++it )
955  {
956  QList<int> toDelete = it.value();
957  std::sort( toDelete.begin(), toDelete.end(), std::greater<int>() );
958  QList<int> order = QgsMapLayerLegendUtils::legendNodeOrder( it.key() );
959 
960  const auto constToDelete = toDelete;
961  for ( int i : constToDelete )
962  {
963  if ( i >= 0 && i < order.count() )
964  order.removeAt( i );
965  }
966 
968  mItemTreeView->layerTreeModel()->refreshLayerLegend( it.key() );
969  }
970 
971  // then remove layer tree nodes
972  for ( const QPersistentModelIndex &index : qgis::as_const( indexes ) )
973  {
974  if ( index.isValid() && mItemTreeView->layerTreeModel()->index2node( index ) )
975  mLegend->model()->removeRow( index.row(), index.parent() );
976  }
977 
978  mLegend->updateLegend();
979  mLegend->update();
980  mLegend->endCommand();
981 }
982 
983 void QgsLayoutLegendWidget::mEditPushButton_clicked()
984 {
985  if ( !mLegend )
986  {
987  return;
988  }
989 
990  QModelIndex idx = mItemTreeView->selectionModel()->currentIndex();
991  mItemTreeView_doubleClicked( idx );
992 }
993 
994 void QgsLayoutLegendWidget::resetLayerNodeToDefaults()
995 {
996  if ( !mLegend )
997  {
998  return;
999  }
1000 
1001  //get current item
1002  QModelIndex currentIndex = mItemTreeView->currentIndex();
1003  if ( !currentIndex.isValid() )
1004  {
1005  return;
1006  }
1007 
1008  QgsLayerTreeLayer *nodeLayer = nullptr;
1009  if ( QgsLayerTreeNode *node = mItemTreeView->layerTreeModel()->index2node( currentIndex ) )
1010  {
1011  if ( QgsLayerTree::isLayer( node ) )
1012  nodeLayer = QgsLayerTree::toLayer( node );
1013  }
1014  if ( QgsLayerTreeModelLegendNode *legendNode = mItemTreeView->layerTreeModel()->index2legendNode( currentIndex ) )
1015  {
1016  nodeLayer = legendNode->layerNode();
1017  }
1018 
1019  if ( !nodeLayer )
1020  return;
1021 
1022  mLegend->beginCommand( tr( "Update Legend" ) );
1023 
1024  const auto constCustomProperties = nodeLayer->customProperties();
1025  for ( const QString &key : constCustomProperties )
1026  {
1027  if ( key.startsWith( QLatin1String( "legend/" ) ) )
1028  nodeLayer->removeCustomProperty( key );
1029  }
1030 
1031  nodeLayer->setPatchShape( QgsLegendPatchShape() );
1032  nodeLayer->setPatchSize( QSizeF() );
1033 
1034  mItemTreeView->layerTreeModel()->refreshLayerLegend( nodeLayer );
1035 
1036  mLegend->updateLegend();
1037  mLegend->update();
1038  mLegend->endCommand();
1039 }
1040 
1041 void QgsLayoutLegendWidget::mCountToolButton_clicked( bool checked )
1042 {
1043  if ( !mLegend )
1044  {
1045  return;
1046  }
1047 
1048  const QList< QModelIndex > selectedIndexes = mItemTreeView->selectionModel()->selectedIndexes();
1049  if ( selectedIndexes.empty() )
1050  return;
1051 
1052  mLegend->beginCommand( tr( "Update Legend" ) );
1053  for ( const QModelIndex &index : selectedIndexes )
1054  {
1055  QgsLayerTreeNode *currentNode = mItemTreeView->layerTreeModel()->index2node( index );
1056  if ( !QgsLayerTree::isLayer( currentNode ) )
1057  continue;
1058 
1059  currentNode->setCustomProperty( QStringLiteral( "showFeatureCount" ), checked ? 1 : 0 );
1060  }
1061  mLegend->updateFilterByMap();
1062  mLegend->adjustBoxSize();
1063  mLegend->endCommand();
1064 }
1065 
1066 void QgsLayoutLegendWidget::mFilterByMapCheckBox_toggled( bool checked )
1067 {
1068  mLegend->beginCommand( tr( "Update Legend" ) );
1069  mLegend->setLegendFilterByMapEnabled( checked );
1070  mLegend->adjustBoxSize();
1071  mLegend->update();
1072  mLegend->endCommand();
1073 }
1074 
1075 void QgsLayoutLegendWidget::mExpressionFilterButton_toggled( bool checked )
1076 {
1077  if ( !mLegend )
1078  {
1079  return;
1080  }
1081 
1082  //get current item
1083  QModelIndex currentIndex = mItemTreeView->currentIndex();
1084  if ( !currentIndex.isValid() )
1085  {
1086  return;
1087  }
1088 
1089  QgsLayerTreeNode *currentNode = mItemTreeView->currentNode();
1090  if ( !QgsLayerTree::isLayer( currentNode ) )
1091  return;
1092 
1093  QgsLayerTreeUtils::setLegendFilterByExpression( *qobject_cast<QgsLayerTreeLayer *>( currentNode ),
1094  mExpressionFilterButton->expressionText(),
1095  checked );
1096 
1097  mLegend->beginCommand( tr( "Update Legend" ) );
1098  mLegend->updateFilterByMap();
1099  mLegend->adjustBoxSize();
1100  mLegend->endCommand();
1101 }
1102 
1103 void QgsLayoutLegendWidget::mLayerExpressionButton_clicked()
1104 {
1105  if ( !mLegend )
1106  {
1107  return;
1108  }
1109 
1110  QModelIndex currentIndex = mItemTreeView->currentIndex();
1111  if ( !currentIndex.isValid() )
1112  return;
1113 
1114  QgsLayerTreeNode *currentNode = mItemTreeView->currentNode();
1115  if ( !QgsLayerTree::isLayer( currentNode ) )
1116  return;
1117 
1118  QgsLayerTreeLayer *layerNode = qobject_cast<QgsLayerTreeLayer *>( currentNode );
1119  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layerNode->layer() );
1120 
1121  if ( !vl )
1122  return;
1123 
1124  QString currentExpression;
1125  if ( layerNode->labelExpression().isEmpty() )
1126  currentExpression = QStringLiteral( "@symbol_label" );
1127  else
1128  currentExpression = layerNode->labelExpression();
1129  QgsExpressionContext legendContext = mLegend->createExpressionContext();
1130  legendContext.appendScope( vl->createExpressionContextScope() );
1131 
1132  QgsExpressionContextScope *symbolLegendScope = new QgsExpressionContextScope( tr( "Symbol scope" ) );
1133 
1134  QgsFeatureRenderer *r = vl->renderer();
1135 
1136  QStringList highlighted;
1137  if ( r )
1138  {
1139  const QgsLegendSymbolList legendSymbols = r->legendSymbolItems();
1140 
1141  if ( !legendSymbols.empty() )
1142  {
1143  QgsSymbolLegendNode legendNode( layerNode, legendSymbols.first() );
1144 
1145  symbolLegendScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "symbol_label" ), legendNode.symbolLabel().remove( QStringLiteral( "[%" ) ).remove( QStringLiteral( "%]" ) ), true ) );
1146  symbolLegendScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "symbol_id" ), legendSymbols.first().ruleKey(), true ) );
1147  highlighted << QStringLiteral( "symbol_label" ) << QStringLiteral( "symbol_id" );
1148  symbolLegendScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "symbol_count" ), QVariant::fromValue( vl->featureCount( legendSymbols.first().ruleKey() ) ), true ) );
1149  highlighted << QStringLiteral( "symbol_count" );
1150  }
1151  }
1152 
1153  legendContext.appendScope( symbolLegendScope );
1154 
1155  legendContext.setHighlightedVariables( highlighted );
1156 
1157  QgsExpressionBuilderDialog expressiondialog( vl, currentExpression, nullptr, QStringLiteral( "generic" ), legendContext );
1158  if ( expressiondialog.exec() )
1159  layerNode->setLabelExpression( expressiondialog.expressionText() );
1160 
1161  mLegend->beginCommand( tr( "Update Legend" ) );
1162  mLegend->refresh();
1163  mLegend->adjustBoxSize();
1164  mLegend->endCommand();
1165 }
1166 
1167 void QgsLayoutLegendWidget::mUpdateAllPushButton_clicked()
1168 {
1169  updateLegend();
1170 }
1171 
1172 void QgsLayoutLegendWidget::mAddGroupToolButton_clicked()
1173 {
1174  if ( mLegend )
1175  {
1176  mLegend->beginCommand( tr( "Add Legend Group" ) );
1177  mLegend->model()->rootGroup()->addGroup( tr( "Group" ) );
1178  mLegend->updateLegend();
1179  mLegend->update();
1180  mLegend->endCommand();
1181  }
1182 }
1183 
1184 void QgsLayoutLegendWidget::mFilterLegendByAtlasCheckBox_toggled( bool toggled )
1185 {
1186  Q_UNUSED( toggled )
1187  if ( mLegend )
1188  {
1189  mLegend->setLegendFilterOutAtlas( toggled );
1190  // force update of legend when in preview mode
1191  mLegend->refresh();
1192  }
1193 }
1194 
1195 void QgsLayoutLegendWidget::updateLegend()
1196 {
1197  if ( mLegend )
1198  {
1199  mLegend->beginCommand( tr( "Update Legend" ) );
1200 
1201  // this will reset the model completely, losing any changes
1202  mLegend->setAutoUpdateModel( true );
1203  mLegend->setAutoUpdateModel( false );
1204  mLegend->updateFilterByMap();
1205  mLegend->endCommand();
1206  }
1207 }
1208 
1209 void QgsLayoutLegendWidget::setReportTypeString( const QString &string )
1210 {
1211  mFilterLegendByAtlasCheckBox->setText( tr( "Only show items inside current %1 feature" ).arg( string ) );
1212  mFilterLegendByAtlasCheckBox->setToolTip( tr( "Filter out legend elements that lie outside the current %1 feature." ).arg( string ) );
1213 }
1214 
1215 bool QgsLayoutLegendWidget::setNewItem( QgsLayoutItem *item )
1216 {
1217  if ( item->type() != QgsLayoutItemRegistry::LayoutLegend )
1218  return false;
1219 
1220  if ( mLegend )
1221  {
1222  disconnect( mLegend, &QgsLayoutObject::changed, this, &QgsLayoutLegendWidget::setGuiElements );
1223  }
1224 
1225  mLegend = qobject_cast< QgsLayoutItemLegend * >( item );
1226  mItemPropertiesWidget->setItem( mLegend );
1227 
1228  if ( mLegend )
1229  {
1230  mItemTreeView->setModel( mLegend->model() );
1231  connect( mLegend, &QgsLayoutObject::changed, this, &QgsLayoutLegendWidget::setGuiElements );
1232  }
1233 
1234  setGuiElements();
1235 
1236  return true;
1237 }
1238 
1239 void QgsLayoutLegendWidget::blockAllSignals( bool b )
1240 {
1241  mTitleLineEdit->blockSignals( b );
1242  mTitleAlignCombo->blockSignals( b );
1243  mItemTreeView->blockSignals( b );
1244  mCheckBoxAutoUpdate->blockSignals( b );
1245  mMapComboBox->blockSignals( b );
1246  mFilterByMapCheckBox->blockSignals( b );
1247  mColumnCountSpinBox->blockSignals( b );
1248  mSplitLayerCheckBox->blockSignals( b );
1249  mEqualColumnWidthCheckBox->blockSignals( b );
1250  mSymbolWidthSpinBox->blockSignals( b );
1251  mSymbolHeightSpinBox->blockSignals( b );
1252  mMaxSymbolSizeSpinBox->blockSignals( b );
1253  mMinSymbolSizeSpinBox->blockSignals( b );
1254  mGroupSpaceSpinBox->blockSignals( b );
1255  mSpaceBelowGroupHeadingSpinBox->blockSignals( b );
1256  mGroupSideSpinBox->blockSignals( b );
1257  mSpaceBelowSubgroupHeadingSpinBox->blockSignals( b );
1258  mSubgroupSideSpinBox->blockSignals( b );
1259  mLayerSpaceSpinBox->blockSignals( b );
1260  mSymbolSpaceSpinBox->blockSignals( b );
1261  mSymbolSideSpaceSpinBox->blockSignals( b );
1262  mIconLabelSpaceSpinBox->blockSignals( b );
1263  mBoxSpaceSpinBox->blockSignals( b );
1264  mColumnSpaceSpinBox->blockSignals( b );
1265  mFontColorButton->blockSignals( b );
1266  mRasterStrokeGroupBox->blockSignals( b );
1267  mRasterStrokeColorButton->blockSignals( b );
1268  mRasterStrokeWidthSpinBox->blockSignals( b );
1269  mWmsLegendWidthSpinBox->blockSignals( b );
1270  mWmsLegendHeightSpinBox->blockSignals( b );
1271  mCheckboxResizeContents->blockSignals( b );
1272  mTitleSpaceBottomSpinBox->blockSignals( b );
1273  mFilterLegendByAtlasCheckBox->blockSignals( b );
1274  mTitleFontButton->blockSignals( b );
1275  mGroupFontButton->blockSignals( b );
1276  mLayerFontButton->blockSignals( b );
1277  mItemFontButton->blockSignals( b );
1278  mWrapCharLineEdit->blockSignals( b );
1279  mLineSpacingSpinBox->blockSignals( b );
1280 }
1281 
1282 void QgsLayoutLegendWidget::selectedChanged( const QModelIndex &current, const QModelIndex &previous )
1283 {
1284  Q_UNUSED( current )
1285  Q_UNUSED( previous )
1286 
1287  mLayerExpressionButton->setEnabled( false );
1288 
1289  if ( mLegend && mLegend->autoUpdateModel() )
1290  {
1291  QgsLayerTreeNode *currentNode = mItemTreeView->currentNode();
1292  if ( !QgsLayerTree::isLayer( currentNode ) )
1293  return;
1294 
1295  QgsLayerTreeLayer *currentLayerNode = QgsLayerTree::toLayer( currentNode );
1296  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( currentLayerNode->layer() );
1297  if ( !vl )
1298  return;
1299 
1300  mLayerExpressionButton->setEnabled( true );
1301  return;
1302  }
1303 
1304  mCountToolButton->setChecked( false );
1305  mCountToolButton->setEnabled( false );
1306 
1307 
1308  mExpressionFilterButton->blockSignals( true );
1309  mExpressionFilterButton->setChecked( false );
1310  mExpressionFilterButton->setEnabled( false );
1311  mExpressionFilterButton->blockSignals( false );
1312 
1313  QgsLayerTreeNode *currentNode = mItemTreeView->currentNode();
1314  if ( !QgsLayerTree::isLayer( currentNode ) )
1315  return;
1316 
1317  QgsLayerTreeLayer *currentLayerNode = QgsLayerTree::toLayer( currentNode );
1318  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( currentLayerNode->layer() );
1319  if ( !vl )
1320  return;
1321 
1322  mCountToolButton->setChecked( currentNode->customProperty( QStringLiteral( "showFeatureCount" ), 0 ).toInt() );
1323  mCountToolButton->setEnabled( true );
1324  mLayerExpressionButton->setEnabled( true );
1325 
1326  bool exprEnabled;
1327  QString expr = QgsLayerTreeUtils::legendFilterByExpression( *qobject_cast<QgsLayerTreeLayer *>( currentNode ), &exprEnabled );
1328  mExpressionFilterButton->blockSignals( true );
1329  mExpressionFilterButton->setExpressionText( expr );
1330  mExpressionFilterButton->setVectorLayer( vl );
1331  mExpressionFilterButton->setEnabled( true );
1332  mExpressionFilterButton->setChecked( exprEnabled );
1333  mExpressionFilterButton->blockSignals( false );
1334 }
1335 
1336 void QgsLayoutLegendWidget::setCurrentNodeStyleFromAction()
1337 {
1338  QAction *a = qobject_cast<QAction *>( sender() );
1339  if ( !a || !mItemTreeView->currentNode() )
1340  return;
1341 
1342  QgsLegendRenderer::setNodeLegendStyle( mItemTreeView->currentNode(), static_cast< QgsLegendStyle::Style >( a->data().toInt() ) );
1343  mLegend->updateFilterByMap();
1344 }
1345 
1346 void QgsLayoutLegendWidget::setLegendMapViewData()
1347 {
1348  if ( mLegend->linkedMap() )
1349  {
1350  int dpi = qt_defaultDpiX();
1352  measurementConverter.setDpi( dpi );
1353  double mapWidth = measurementConverter.convert( mLegend->linkedMap()->sizeWithUnits(), QgsUnitTypes::LayoutPixels ).width();
1354  double mapHeight = measurementConverter.convert( mLegend->linkedMap()->sizeWithUnits(), QgsUnitTypes::LayoutPixels ).height();
1355  double mapUnitsPerPixelX = mLegend->linkedMap()->extent().width() / mapWidth;
1356  double mapUnitsPerPixelY = mLegend->linkedMap()->extent().height() / mapHeight;
1357  mLegend->model()->setLegendMapViewData( ( mapUnitsPerPixelX > mapUnitsPerPixelY ? mapUnitsPerPixelX : mapUnitsPerPixelY ), dpi, mLegend->linkedMap()->scale() );
1358  }
1359 }
1360 
1361 void QgsLayoutLegendWidget::updateFilterLegendByAtlasButton()
1362 {
1363  if ( QgsLayoutAtlas *atlas = layoutAtlas() )
1364  {
1365  mFilterLegendByAtlasCheckBox->setEnabled( atlas->enabled() && mLegend->layout()->reportContext().layer() && mLegend->layout()->reportContext().layer()->geometryType() == QgsWkbTypes::PolygonGeometry );
1366  }
1367 }
1368 
1369 void QgsLayoutLegendWidget::mItemTreeView_doubleClicked( const QModelIndex &idx )
1370 {
1371  if ( !mLegend || !idx.isValid() )
1372  {
1373  return;
1374  }
1375 
1376  if ( mLegend->autoUpdateModel() )
1377  return;
1378 
1379  QgsLayerTreeModel *model = mItemTreeView->layerTreeModel();
1380  QgsLayerTreeNode *currentNode = model->index2node( idx );
1382 
1383  int originalIndex = -1;
1384  if ( legendNode )
1385  {
1386  originalIndex = _originalLegendNodeIndex( legendNode );
1387  currentNode = legendNode->layerNode();
1388  }
1389 
1390  QgsLayoutLegendNodeWidget *widget = new QgsLayoutLegendNodeWidget( mLegend, currentNode, legendNode, originalIndex );
1391  openPanel( widget );
1392 }
1393 
1394 
1395 //
1396 // QgsComposerLegendMenuProvider
1397 //
1398 
1399 QgsLayoutLegendMenuProvider::QgsLayoutLegendMenuProvider( QgsLayerTreeView *view, QgsLayoutLegendWidget *w )
1400  : mView( view )
1401  , mWidget( w )
1402 {}
1403 
1404 QMenu *QgsLayoutLegendMenuProvider::createContextMenu()
1405 {
1406  if ( !mView->currentNode() )
1407  return nullptr;
1408 
1409  if ( mWidget->legend()->autoUpdateModel() )
1410  return nullptr; // no editing allowed
1411 
1412  QMenu *menu = new QMenu();
1413 
1414  if ( QgsLayerTree::isLayer( mView->currentNode() ) )
1415  {
1416  menu->addAction( QObject::tr( "Reset to Defaults" ), mWidget, &QgsLayoutLegendWidget::resetLayerNodeToDefaults );
1417  menu->addSeparator();
1418  }
1419 
1420  QgsLegendStyle::Style currentStyle = QgsLegendRenderer::nodeLegendStyle( mView->currentNode(), mView->layerTreeModel() );
1421 
1422  QList<QgsLegendStyle::Style> lst;
1424  for ( QgsLegendStyle::Style style : qgis::as_const( lst ) )
1425  {
1426  QAction *action = menu->addAction( QgsLegendStyle::styleLabel( style ), mWidget, &QgsLayoutLegendWidget::setCurrentNodeStyleFromAction );
1427  action->setCheckable( true );
1428  action->setChecked( currentStyle == style );
1429  action->setData( static_cast< int >( style ) );
1430  }
1431 
1432  return menu;
1433 }
1434 
1435 //
1436 // QgsLayoutLegendNodeWidget
1437 //
1438 QgsLayoutLegendNodeWidget::QgsLayoutLegendNodeWidget( QgsLayoutItemLegend *legend, QgsLayerTreeNode *node, QgsLayerTreeModelLegendNode *legendNode, int originalLegendNodeIndex, QWidget *parent )
1439  : QgsPanelWidget( parent )
1440  , mLegend( legend )
1441  , mNode( node )
1442  , mLayer( qobject_cast< QgsLayerTreeLayer* >( node ) )
1443  , mLegendNode( legendNode )
1444  , mOriginalLegendNodeIndex( originalLegendNodeIndex )
1445 {
1446  setupUi( this );
1447  setPanelTitle( tr( "Legend Item Properties" ) );
1448 
1449  // auto close panel if layer removed
1450  connect( node, &QObject::destroyed, this, &QgsPanelWidget::acceptPanel );
1451 
1452  mColumnSplitBehaviorComboBox->addItem( tr( "Follow Legend Default" ), QgsLayerTreeLayer::UseDefaultLegendSetting );
1453  mColumnSplitBehaviorComboBox->addItem( tr( "Allow Splitting Over Columns" ), QgsLayerTreeLayer::AllowSplittingLegendNodesOverMultipleColumns );
1454  mColumnSplitBehaviorComboBox->addItem( tr( "Prevent Splitting Over Columns" ), QgsLayerTreeLayer::PreventSplittingLegendNodesOverMultipleColumns );
1455 
1456  QString currentLabel;
1457  if ( mLegendNode )
1458  {
1459  currentLabel = mLegendNode->data( Qt::EditRole ).toString();
1460  mColumnBreakBeforeCheckBox->setChecked( mLegendNode->columnBreak() );
1461  }
1462  else if ( mLayer )
1463  {
1464  currentLabel = mLayer->name();
1465  QVariant v = mLayer->customProperty( QStringLiteral( "legend/title-label" ) );
1466  if ( !v.isNull() )
1467  currentLabel = v.toString();
1468  mColumnBreakBeforeCheckBox->setChecked( mLayer->customProperty( QStringLiteral( "legend/column-break" ) ).toInt() );
1469 
1470  mColumnSplitBehaviorComboBox->setCurrentIndex( mColumnSplitBehaviorComboBox->findData( mLayer->legendSplitBehavior() ) );
1471  }
1472  else
1473  {
1474  currentLabel = QgsLayerTree::toGroup( mNode )->name();
1475  mColumnBreakBeforeCheckBox->setChecked( mNode->customProperty( QStringLiteral( "legend/column-break" ) ).toInt() );
1476  }
1477 
1478  mWidthSpinBox->setClearValue( 0, tr( "Default" ) );
1479  mHeightSpinBox->setClearValue( 0, tr( "Default" ) );
1480  mWidthSpinBox->setVisible( mLegendNode || mLayer );
1481  mHeightSpinBox->setVisible( mLegendNode || mLayer );
1482  mPatchGroup->setVisible( mLegendNode || mLayer );
1483  mPatchWidthLabel->setVisible( mLegendNode || mLayer );
1484  mPatchHeightLabel->setVisible( mLegendNode || mLayer );
1485  mCustomSymbolCheckBox->setVisible( mLegendNode || mLegend->model()->legendNodeEmbeddedInParent( mLayer ) );
1486  mColumnSplitLabel->setVisible( mLayer && !mLegendNode );
1487  mColumnSplitBehaviorComboBox->setVisible( mLayer && !mLegendNode );
1488  if ( mLegendNode )
1489  {
1490  mWidthSpinBox->setValue( mLegendNode->userPatchSize().width() );
1491  mHeightSpinBox->setValue( mLegendNode->userPatchSize().height() );
1492  }
1493  else if ( mLayer )
1494  {
1495  mWidthSpinBox->setValue( mLayer->patchSize().width() );
1496  mHeightSpinBox->setValue( mLayer->patchSize().height() );
1497  }
1498 
1499  mCustomSymbolCheckBox->setChecked( false );
1500 
1501  QgsLegendPatchShape patchShape;
1502  if ( QgsSymbolLegendNode *symbolLegendNode = dynamic_cast< QgsSymbolLegendNode * >( mLegendNode ) )
1503  {
1504  patchShape = symbolLegendNode->patchShape();
1505 
1506  std::unique_ptr< QgsSymbol > customSymbol( symbolLegendNode->customSymbol() ? symbolLegendNode->customSymbol()->clone() : nullptr );
1507  mCustomSymbolCheckBox->setChecked( customSymbol.get() );
1508  if ( customSymbol )
1509  {
1510  mPatchShapeButton->setPreviewSymbol( customSymbol->clone() );
1511  mCustomSymbolButton->setSymbolType( customSymbol->type() );
1512  mCustomSymbolButton->setSymbol( customSymbol.release() );
1513  }
1514  else if ( symbolLegendNode->symbol() )
1515  {
1516  mPatchShapeButton->setPreviewSymbol( symbolLegendNode->symbol()->clone() );
1517  mCustomSymbolButton->setSymbolType( symbolLegendNode->symbol()->type() );
1518  mCustomSymbolButton->setSymbol( symbolLegendNode->symbol()->clone() );
1519  }
1520  }
1521  else if ( !mLegendNode && mLayer )
1522  {
1523  patchShape = mLayer->patchShape();
1524  if ( QgsSymbolLegendNode *symbolLegendNode = dynamic_cast< QgsSymbolLegendNode * >( mLegend->model()->legendNodeEmbeddedInParent( mLayer ) ) )
1525  {
1526  if ( QgsSymbol *customSymbol = symbolLegendNode->customSymbol() )
1527  {
1528  mCustomSymbolCheckBox->setChecked( true );
1529  mPatchShapeButton->setPreviewSymbol( customSymbol->clone() );
1530  mCustomSymbolButton->setSymbolType( customSymbol->type() );
1531  mCustomSymbolButton->setSymbol( customSymbol->clone() );
1532  }
1533  else
1534  {
1535  mPatchShapeButton->setPreviewSymbol( symbolLegendNode->symbol()->clone() );
1536  mCustomSymbolButton->setSymbolType( symbolLegendNode->symbol()->type() );
1537  mCustomSymbolButton->setSymbol( symbolLegendNode->symbol()->clone() );
1538  }
1539  }
1540  }
1541 
1542  if ( mLayer && mLayer->layer() && mLayer->layer()->type() == QgsMapLayerType::VectorLayer )
1543  {
1544  switch ( qobject_cast< QgsVectorLayer * >( mLayer->layer() )->geometryType() )
1545  {
1547  mPatchShapeButton->setSymbolType( QgsSymbol::Fill );
1548  break;
1549 
1551  mPatchShapeButton->setSymbolType( QgsSymbol::Line );
1552  break;
1553 
1555  mPatchShapeButton->setSymbolType( QgsSymbol::Marker );
1556  break;
1557 
1558  default:
1559  mPatchShapeLabel->hide();
1560  mPatchShapeButton->hide();
1561  break;
1562  }
1563  if ( !patchShape.isNull() )
1564  mPatchShapeButton->setShape( patchShape );
1565 
1566  }
1567  else
1568  {
1569  mPatchShapeLabel->hide();
1570  mPatchShapeButton->hide();
1571  }
1572 
1573  mLabelEdit->setPlainText( currentLabel );
1574  connect( mLabelEdit, &QPlainTextEdit::textChanged, this, &QgsLayoutLegendNodeWidget::labelChanged );
1575  connect( mPatchShapeButton, &QgsLegendPatchShapeButton::changed, this, &QgsLayoutLegendNodeWidget::patchChanged );
1576  connect( mInsertExpressionButton, &QPushButton::clicked, this, &QgsLayoutLegendNodeWidget::insertExpression );
1577 
1578  connect( mWidthSpinBox, qgis::overload<double>::of( &QgsDoubleSpinBox::valueChanged ), this, &QgsLayoutLegendNodeWidget::sizeChanged );
1579  connect( mHeightSpinBox, qgis::overload<double>::of( &QgsDoubleSpinBox::valueChanged ), this, &QgsLayoutLegendNodeWidget::sizeChanged );
1580 
1581  connect( mCustomSymbolCheckBox, &QGroupBox::toggled, this, &QgsLayoutLegendNodeWidget::customSymbolChanged );
1582  connect( mCustomSymbolButton, &QgsSymbolButton::changed, this, &QgsLayoutLegendNodeWidget::customSymbolChanged );
1583 
1584  connect( mColumnBreakBeforeCheckBox, &QCheckBox::toggled, this, &QgsLayoutLegendNodeWidget::columnBreakToggled );
1585 
1586  connect( mColumnSplitBehaviorComboBox, qgis::overload<int>::of( &QComboBox::currentIndexChanged ), this, &QgsLayoutLegendNodeWidget::columnSplitChanged );
1587 }
1588 
1589 void QgsLayoutLegendNodeWidget::labelChanged()
1590 {
1591  mLegend->beginCommand( tr( "Edit Legend Item" ), QgsLayoutItem::UndoLegendText );
1592 
1593  const QString label = mLabelEdit->toPlainText();
1594  if ( QgsLayerTree::isGroup( mNode ) )
1595  {
1596  QgsLayerTree::toGroup( mNode )->setName( label );
1597  }
1598  else if ( mLegendNode )
1599  {
1600  QgsMapLayerLegendUtils::setLegendNodeUserLabel( mLayer, mOriginalLegendNodeIndex, label );
1601  mLegend->model()->refreshLayerLegend( mLayer );
1602  }
1603  else if ( mLayer )
1604  {
1605  mLayer->setCustomProperty( QStringLiteral( "legend/title-label" ), label );
1606 
1607  // force update of label of the legend node with embedded icon (a bit clumsy i know)
1608  if ( QgsLayerTreeModelLegendNode *embeddedNode = mLegend->model()->legendNodeEmbeddedInParent( mLayer ) )
1609  embeddedNode->setUserLabel( QString() );
1610  }
1611 
1612  mLegend->adjustBoxSize();
1613  mLegend->updateFilterByMap();
1614  mLegend->endCommand();
1615 }
1616 
1617 void QgsLayoutLegendNodeWidget::patchChanged()
1618 {
1619  mLegend->beginCommand( tr( "Edit Legend Item" ) );
1620 
1621  QgsLegendPatchShape shape = mPatchShapeButton->shape();
1622  if ( mLegendNode )
1623  {
1624  QgsMapLayerLegendUtils::setLegendNodePatchShape( mLayer, mOriginalLegendNodeIndex, shape );
1625  mLegend->model()->refreshLayerLegend( mLayer );
1626  }
1627  else if ( mLayer )
1628  {
1629  mLayer->setPatchShape( shape );
1630  const QList<QgsLayerTreeModelLegendNode *> layerLegendNodes = mLegend->model()->layerLegendNodes( mLayer, false );
1631  for ( QgsLayerTreeModelLegendNode *node : layerLegendNodes )
1632  {
1633  QgsMapLayerLegendUtils::setLegendNodePatchShape( mLayer, _originalLegendNodeIndex( node ), shape );
1634  }
1635  mLegend->model()->refreshLayerLegend( mLayer );
1636  }
1637 
1638  mLegend->adjustBoxSize();
1639  mLegend->updateFilterByMap();
1640  mLegend->endCommand();
1641 }
1642 
1643 void QgsLayoutLegendNodeWidget::insertExpression()
1644 {
1645  if ( !mLegend )
1646  return;
1647 
1648  QString selText = mLabelEdit->textCursor().selectedText();
1649 
1650  // html editor replaces newlines with Paragraph Separator characters - see https://github.com/qgis/QGIS/issues/27568
1651  selText = selText.replace( QChar( 0x2029 ), QChar( '\n' ) );
1652 
1653  // edit the selected expression if there's one
1654  if ( selText.startsWith( QLatin1String( "[%" ) ) && selText.endsWith( QLatin1String( "%]" ) ) )
1655  selText = selText.mid( 2, selText.size() - 4 );
1656 
1657  // use the atlas coverage layer, if any
1658  QgsVectorLayer *layer = mLegend->layout() ? mLegend->layout()->reportContext().layer() : nullptr;
1659 
1660  QgsExpressionContext context = mLegend->createExpressionContext();
1661 
1662  if ( mLayer && mLayer->layer() )
1663  {
1664  context.appendScope( QgsExpressionContextUtils::layerScope( mLayer->layer() ) );
1665  }
1666 
1667  context.setHighlightedVariables( QStringList() << QStringLiteral( "legend_title" )
1668  << QStringLiteral( "legend_column_count" )
1669  << QStringLiteral( "legend_split_layers" )
1670  << QStringLiteral( "legend_wrap_string" )
1671  << QStringLiteral( "legend_filter_by_map" )
1672  << QStringLiteral( "legend_filter_out_atlas" ) );
1673 
1674  QgsExpressionBuilderDialog exprDlg( layer, selText, this, QStringLiteral( "generic" ), context );
1675 
1676  exprDlg.setWindowTitle( tr( "Insert Expression" ) );
1677  if ( exprDlg.exec() == QDialog::Accepted )
1678  {
1679  QString expression = exprDlg.expressionText();
1680  if ( !expression.isEmpty() )
1681  {
1682  mLegend->beginCommand( tr( "Insert expression" ) );
1683  mLabelEdit->insertPlainText( "[%" + expression + "%]" );
1684  mLegend->endCommand();
1685  }
1686  }
1687 }
1688 
1689 void QgsLayoutLegendNodeWidget::sizeChanged( double )
1690 {
1691  mLegend->beginCommand( tr( "Edit Legend Item" ) );
1692  const QSizeF size = QSizeF( mWidthSpinBox->value(), mHeightSpinBox->value() );
1693 
1694  if ( mLegendNode )
1695  {
1696  QgsMapLayerLegendUtils::setLegendNodeSymbolSize( mLayer, mOriginalLegendNodeIndex, size );
1697  mLegend->model()->refreshLayerLegend( mLayer );
1698  }
1699  else if ( mLayer )
1700  {
1701  mLayer->setPatchSize( size );
1702  const QList<QgsLayerTreeModelLegendNode *> layerLegendNodes = mLegend->model()->layerLegendNodes( mLayer, false );
1703  for ( QgsLayerTreeModelLegendNode *node : layerLegendNodes )
1704  {
1705  QgsMapLayerLegendUtils::setLegendNodeSymbolSize( mLayer, _originalLegendNodeIndex( node ), size );
1706  }
1707  mLegend->model()->refreshLayerLegend( mLayer );
1708  }
1709 
1710  mLegend->adjustBoxSize();
1711  mLegend->updateFilterByMap();
1712  mLegend->endCommand();
1713 }
1714 
1715 void QgsLayoutLegendNodeWidget::customSymbolChanged()
1716 {
1717  mLegend->beginCommand( tr( "Edit Legend Item" ) );
1718 
1719  if ( mCustomSymbolCheckBox->isChecked() )
1720  {
1721  if ( mLegendNode )
1722  {
1723  QgsMapLayerLegendUtils::setLegendNodeCustomSymbol( mLayer, mOriginalLegendNodeIndex, mCustomSymbolButton->symbol() );
1724  mLegend->model()->refreshLayerLegend( mLayer );
1725  }
1726  else if ( mLayer )
1727  {
1728  const QList<QgsLayerTreeModelLegendNode *> layerLegendNodes = mLegend->model()->layerLegendNodes( mLayer, false );
1729  for ( QgsLayerTreeModelLegendNode *node : layerLegendNodes )
1730  {
1731  QgsMapLayerLegendUtils::setLegendNodeCustomSymbol( mLayer, _originalLegendNodeIndex( node ), mCustomSymbolButton->symbol() );
1732  }
1733  mLegend->model()->refreshLayerLegend( mLayer );
1734  }
1735  }
1736  else
1737  {
1738  if ( mLegendNode )
1739  {
1740  QgsMapLayerLegendUtils::setLegendNodeCustomSymbol( mLayer, mOriginalLegendNodeIndex, nullptr );
1741  mLegend->model()->refreshLayerLegend( mLayer );
1742  }
1743  else if ( mLayer )
1744  {
1745  const QList<QgsLayerTreeModelLegendNode *> layerLegendNodes = mLegend->model()->layerLegendNodes( mLayer, false );
1746  for ( QgsLayerTreeModelLegendNode *node : layerLegendNodes )
1747  {
1748  QgsMapLayerLegendUtils::setLegendNodeCustomSymbol( mLayer, _originalLegendNodeIndex( node ), nullptr );
1749  }
1750  mLegend->model()->refreshLayerLegend( mLayer );
1751  }
1752  }
1753 
1754  mLegend->adjustBoxSize();
1755  mLegend->updateFilterByMap();
1756  mLegend->endCommand();
1757 }
1758 
1759 void QgsLayoutLegendNodeWidget::columnBreakToggled( bool checked )
1760 {
1761  mLegend->beginCommand( tr( "Edit Legend Columns" ) );
1762 
1763  if ( mLegendNode )
1764  {
1765  QgsMapLayerLegendUtils::setLegendNodeColumnBreak( mLayer, mOriginalLegendNodeIndex, checked );
1766  mLegend->model()->refreshLayerLegend( mLayer );
1767  }
1768  else if ( mLayer )
1769  {
1770  mLayer->setCustomProperty( QStringLiteral( "legend/column-break" ), QString( checked ? '1' : '0' ) );
1771  }
1772  else if ( mNode )
1773  {
1774  mNode->setCustomProperty( QStringLiteral( "legend/column-break" ), QString( checked ? '1' : '0' ) );
1775  }
1776 
1777  mLegend->adjustBoxSize();
1778  mLegend->updateFilterByMap();
1779  mLegend->endCommand();
1780 }
1781 
1782 void QgsLayoutLegendNodeWidget::columnSplitChanged()
1783 {
1784  mLegend->beginCommand( tr( "Edit Legend Columns" ) );
1785 
1786  if ( mLayer && !mLegendNode )
1787  {
1788  mLayer->setLegendSplitBehavior( static_cast< QgsLayerTreeLayer::LegendNodesSplitBehavior >( mColumnSplitBehaviorComboBox->currentData().toInt() ) );
1789  }
1790 
1791  mLegend->adjustBoxSize();
1792  mLegend->updateFilterByMap();
1793  mLegend->endCommand();
1794 }
1795 
QgsMapLayerLegendUtils::setLegendNodePatchShape
static void setLegendNodePatchShape(QgsLayerTreeLayer *nodeLayer, int originalIndex, const QgsLegendPatchShape &shape)
Sets the legend patch shape for the legend node belonging to nodeLayer at the specified originalIndex...
Definition: qgsmaplayerlegend.cpp:149
QgsLayoutItem::UndoLegendSymbolSpace
@ UndoLegendSymbolSpace
Legend symbol spacing.
Definition: qgslayoutitem.h:262
QgsExpressionContext
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
Definition: qgsexpressioncontext.h:370
QgsLayerTreeLayer::labelExpression
QString labelExpression() const
Returns the expression member of the LayerTreeNode.
Definition: qgslayertreelayer.h:144
QgsLayoutObject::layout
const QgsLayout * layout() const
Returns the layout the object is attached to.
Definition: qgslayoutobject.cpp:126
qgsexpressioncontextutils.h
QgsLegendStyle::Symbol
@ Symbol
Symbol icon (excluding label)
Definition: qgslegendstyle.h:47
QgsLegendStyle::Style
Style
Component of legends which can be styled.
Definition: qgslegendstyle.h:41
QgsLegendRenderer::setNodeLegendStyle
static void setNodeLegendStyle(QgsLayerTreeNode *node, QgsLegendStyle::Style style)
Sets the style of a node.
Definition: qgslegendrenderer.cpp:898
QgsVectorLayer::createExpressionContextScope
QgsExpressionContextScope * createExpressionContextScope() const FINAL
This method needs to be reimplemented in all classes which implement this interface and return an exp...
Definition: qgsvectorlayer.cpp:4957
QgsLayerTreeNode
This class is a base class for nodes in a layer tree.
Definition: qgslayertreenode.h:75
QgsLegendPatchShapeButton::changed
void changed()
Emitted when the shape's settings are changed.
QgsSymbolButton::changed
void changed()
Emitted when the symbol's settings are changed.
QgsApplication::getThemeIcon
static QIcon getThemeIcon(const QString &name)
Helper to get a theme icon.
Definition: qgsapplication.cpp:626
qgslayertreemodellegendnode.h
QgsLayerTreeGroup::removeChildNode
void removeChildNode(QgsLayerTreeNode *node)
Remove a child node from this group.
Definition: qgslayertreegroup.cpp:132
QgsUnitTypes::LayoutPixels
@ LayoutPixels
Pixels.
Definition: qgsunittypes.h:189
qgsmapcanvas.h
QgsMapLayerType::VectorLayer
@ VectorLayer
QgsExpressionContextScope::addVariable
void addVariable(const QgsExpressionContextScope::StaticVariable &variable)
Adds a variable into the context scope.
Definition: qgsexpressioncontext.cpp:93
QgsLayoutItem::UndoLegendBoxSpace
@ UndoLegendBoxSpace
Legend box space.
Definition: qgslayoutitem.h:265
QgsLayoutItem::UndoLegendText
@ UndoLegendText
Legend text.
Definition: qgslayoutitem.h:251
qt_defaultDpiX
Q_GUI_EXPORT int qt_defaultDpiX()
QgsExpressionContextUtils::layerScope
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
Definition: qgsexpressioncontextutils.cpp:265
QgsMapCanvas
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:85
QgsLayoutObject::LegendColumnCount
@ LegendColumnCount
Legend column count.
Definition: qgslayoutobject.h:194
QgsLegendStyle::Bottom
@ Bottom
Bottom side.
Definition: qgslegendstyle.h:57
qgsunittypes.h
QgsMapLayerLegendUtils::setLegendNodeColumnBreak
static void setLegendNodeColumnBreak(QgsLayerTreeLayer *nodeLayer, int originalIndex, bool columnBreakBeforeNode)
Sets whether a forced column break should occur before the node.
Definition: qgsmaplayerlegend.cpp:219
QgsFeatureRenderer::legendSymbolItems
virtual QgsLegendSymbolList legendSymbolItems() const
Returns a list of symbology items for the legend.
Definition: qgsrenderer.cpp:353
QgsLayoutItemLegend::model
QgsLegendModel * model()
Returns the legend model.
Definition: qgslayoutitemlegend.h:159
QgsLegendRenderer::nodeLegendStyle
static QgsLegendStyle::Style nodeLegendStyle(QgsLayerTreeNode *node, QgsLayerTreeModel *model)
Returns the style for the given node, within the specified model.
Definition: qgslegendrenderer.cpp:870
QgsLayerTree::toLayer
static QgsLayerTreeLayer * toLayer(QgsLayerTreeNode *node)
Cast node to a layer.
Definition: qgslayertree.h:75
QgsLayerTreeModel
The QgsLayerTreeModel class is model implementation for Qt item views framework.
Definition: qgslayertreemodel.h:54
QgsLayoutItem::UndoLegendTitleFont
@ UndoLegendTitleFont
Legend title font.
Definition: qgslayoutitem.h:270
QgsLayerTreeModel::legendNodeEmbeddedInParent
QgsLayerTreeModelLegendNode * legendNodeEmbeddedInParent(QgsLayerTreeLayer *nodeLayer) const
Returns legend node that may be embedded in parent (i.e.
Definition: qgslayertreemodel.cpp:1535
QgsSymbol
Abstract base class for all rendered symbols.
Definition: qgssymbol.h:64
QgsApplication::iconPath
static QString iconPath(const QString &iconFile)
Returns path to the desired icon file.
Definition: qgsapplication.cpp:615
QgsLegendStyle::SymbolLabel
@ SymbolLabel
Symbol label (excluding icon)
Definition: qgslegendstyle.h:48
QgsVectorLayer::featureCount
long featureCount(const QString &legendKey) const
Number of features rendered with specified legend key.
Definition: qgsvectorlayer.cpp:751
QgsLayoutObject::changed
void changed()
Emitted when the object's properties change.
QgsLayoutMeasurementConverter::convert
QgsLayoutMeasurement convert(QgsLayoutMeasurement measurement, QgsUnitTypes::LayoutUnit targetUnits) const
Converts a measurement from one unit to another.
Definition: qgslayoutmeasurementconverter.cpp:21
QgsLayoutItem::UndoLegendRasterStrokeWidth
@ UndoLegendRasterStrokeWidth
Legend raster stroke width.
Definition: qgslayoutitem.h:268
QgsLayerTreeModelLegendNode::model
QgsLayerTreeModel * model() const
Returns pointer to model owning this legend node.
Definition: qgslayertreemodellegendnode.cpp:46
QgsLayoutMeasurementConverter::setDpi
void setDpi(const double dpi)
Sets the dots per inch (dpi) for the measurement converter.
Definition: qgslayoutmeasurementconverter.h:54
QgsLayoutItemPropertiesWidget
A widget for controlling the common properties of layout items (e.g.
Definition: qgslayoutitemwidget.h:219
QgsWkbTypes::PolygonGeometry
@ PolygonGeometry
Definition: qgswkbtypes.h:144
QgsLegendPatchShape
Represents a patch shape for use in map legends.
Definition: qgslegendpatchshape.h:31
QgsLayerTree::toGroup
static QgsLayerTreeGroup * toGroup(QgsLayerTreeNode *node)
Cast node to a group.
Definition: qgslayertree.h:64
QgsGuiUtils::iconSize
QSize iconSize(bool dockableToolbar)
Returns the user-preferred size of a window's toolbar icons.
Definition: qgsguiutils.cpp:250
QgsColorButton::colorChanged
void colorChanged(const QColor &color)
Emitted whenever a new color is set for the button.
QgsLayoutItem::UndoLegendLineSpacing
@ UndoLegendLineSpacing
Legend line spacing.
Definition: qgslayoutitem.h:267
QgsLayoutItem::type
int type() const override
Returns a unique graphics item type identifier.
Definition: qgslayoutitem.cpp:124
qgsapplication.h
QgsLayoutItemLegend
A layout item subclass for map legends.
Definition: qgslayoutitemlegend.h:114
QgsLegendStyle::Title
@ Title
Legend title.
Definition: qgslegendstyle.h:44
QgsLayoutItem::UndoLegendItemFont
@ UndoLegendItemFont
Legend item font.
Definition: qgslayoutitem.h:273
QgsLayout::reportContext
QgsLayoutReportContext & reportContext()
Returns a reference to the layout's report context, which stores information relating to the current ...
Definition: qgslayout.cpp:369
QgsLegendStyle::styleLabel
static QString styleLabel(Style s)
Returns a translated string representing a style component, for use in UI.
Definition: qgslegendstyle.cpp:124
qgslayoutmeasurementconverter.h
QgsLayoutLegendLayersDialog
A dialog to add new layers to the legend.
Definition: qgslayoutlegendlayersdialog.h:35
QgsLayerTreeModel::index2legendNode
static QgsLayerTreeModelLegendNode * index2legendNode(const QModelIndex &index)
Returns legend node for given index.
Definition: qgslayertreemodel.cpp:1413
qgslayoutitemwidget.h
QgsMapLayerLegendUtils::setLegendNodeOrder
static void setLegendNodeOrder(QgsLayerTreeLayer *nodeLayer, const QList< int > &order)
Definition: qgsmaplayerlegend.cpp:65
QgsLayerTreeLayer::setPatchSize
void setPatchSize(QSizeF size)
Sets the user (overridden) size for the legend node.
Definition: qgslayertreelayer.h:182
QgsPanelWidget
Base class for any widget that can be shown as a inline panel.
Definition: qgspanelwidget.h:30
QgsLayerTreeLayer::UseDefaultLegendSetting
@ UseDefaultLegendSetting
Inherit default legend column splitting setting.
Definition: qgslayertreelayer.h:191
whileBlocking
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition: qgis.h:262
qgslayoutlegendlayersdialog.h
QgsLayoutItem::UndoLegendLayerSpace
@ UndoLegendLayerSpace
Legend layer spacing.
Definition: qgslayoutitem.h:261
QgsLayoutItem::UndoLegendIconSymbolSpace
@ UndoLegendIconSymbolSpace
Legend icon symbol space.
Definition: qgslayoutitem.h:263
QgsExpressionBuilderDialog
A generic dialog for building expression strings.
Definition: qgsexpressionbuilderdialog.h:31
QgsLayerTreeLayer
Layer tree node points to a map layer.
Definition: qgslayertreelayer.h:44
QgsPanelWidget::acceptPanel
void acceptPanel()
Accept the panel.
Definition: qgspanelwidget.cpp:107
QgsLayoutItem::UndoLegendColumnCount
@ UndoLegendColumnCount
Legend column count.
Definition: qgslayoutitem.h:252
QgsLayerTreeLayer::LegendNodesSplitBehavior
LegendNodesSplitBehavior
Legend node column split behavior.
Definition: qgslayertreelayer.h:190
QgsLayerTreeGroup
Layer tree group node serves as a container for layers and further groups.
Definition: qgslayertreegroup.h:35
QgsLayoutItem::UndoLegendFontColor
@ UndoLegendFontColor
Legend font color.
Definition: qgslayoutitem.h:264
QgsLayerTreeGroup::setName
void setName(const QString &n) override
Sets the group's name.
Definition: qgslayertreegroup.cpp:48
QgsLayerTreeLayer::AllowSplittingLegendNodesOverMultipleColumns
@ AllowSplittingLegendNodesOverMultipleColumns
Allow splitting node's legend nodes across multiple columns.
Definition: qgslayertreelayer.h:192
QgsMapLayerLegendUtils::setLegendNodeCustomSymbol
static void setLegendNodeCustomSymbol(QgsLayerTreeLayer *nodeLayer, int originalIndex, const QgsSymbol *symbol)
Sets a custom legend symbol size for the legend node belonging to nodeLayer at the specified original...
Definition: qgsmaplayerlegend.cpp:188
QgsLayoutItem::UndoLegendMinSymbolSize
@ UndoLegendMinSymbolSize
Legend minimum symbol size.
Definition: qgslayoutitem.h:256
QgsSymbol::Fill
@ Fill
Fill symbol.
Definition: qgssymbol.h:89
QgsLayoutItem
Base class for graphical items within a QgsLayout.
Definition: qgslayoutitem.h:113
qgsrenderer.h
QgsLayoutItem::UndoLegendSymbolHeight
@ UndoLegendSymbolHeight
Legend symbol height.
Definition: qgslayoutitem.h:254
QgsLayerTreeLayer::layer
QgsMapLayer * layer() const
Returns the map layer associated with this node.
Definition: qgslayertreelayer.h:74
qgslayertree.h
qgslayout.h
QgsExpressionContextScope
Single scope for storing variables and functions for use within a QgsExpressionContext.
Definition: qgsexpressioncontext.h:112
QgsLayerTreeLayer::PreventSplittingLegendNodesOverMultipleColumns
@ PreventSplittingLegendNodesOverMultipleColumns
Prevent splitting node's legend nodes across multiple columns.
Definition: qgslayertreelayer.h:193
QgsWms::legendNode
QgsLayerTreeModelLegendNode * legendNode(const QString &rule, QgsLayerTreeModel &model)
Definition: qgswmsgetlegendgraphics.cpp:345
QgsLayoutItemBaseWidget
A base class for property widgets for layout items.
Definition: qgslayoutitemwidget.h:123
QgsLayoutMeasurementConverter
This class provides a method of converting QgsLayoutMeasurements from one unit to another.
Definition: qgslayoutmeasurementconverter.h:41
QgsMapLayerLegendUtils::legendNodeOrder
static QList< int > legendNodeOrder(QgsLayerTreeLayer *nodeLayer)
Definition: qgsmaplayerlegend.cpp:102
qgslayoutlegendwidget.h
QgsLayerTreeNode::customProperties
QStringList customProperties() const
Returns list of keys stored in custom properties.
Definition: qgslayertreenode.cpp:203
QgsLayoutItem::UndoLegendColumnSpace
@ UndoLegendColumnSpace
Legend column space.
Definition: qgslayoutitem.h:266
QgsExpressionContext::appendScope
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
Definition: qgsexpressioncontext.cpp:490
qgsvectorlayer.h
QgsLayoutItem::UndoLegendSymbolWidth
@ UndoLegendSymbolWidth
Legend symbol width.
Definition: qgslayoutitem.h:253
QgsLayerTreeModel::index2node
QgsLayerTreeNode * index2node(const QModelIndex &index) const
Returns layer tree node for given index.
Definition: qgslayertreemodel.cpp:63
QgsLayerTreeUtils::setLegendFilterByExpression
static void setLegendFilterByExpression(QgsLayerTreeLayer &layer, const QString &expr, bool enabled=true)
Sets the expression filter of a legend layer.
Definition: qgslayertreeutils.cpp:429
qgslayoutitemlegend.h
QgsLayoutItemMap
Layout graphical items for displaying a map.
Definition: qgslayoutitemmap.h:318
QgsLayoutItem::UndoLegendMaxSymbolSize
@ UndoLegendMaxSymbolSize
Legend maximum symbol size.
Definition: qgslayoutitem.h:255
QgsLayerTreeGroup::insertChildNode
void insertChildNode(int index, QgsLayerTreeNode *node)
Insert existing node at specified position.
Definition: qgslayertreegroup.cpp:92
QgsLayoutObject::LegendTitle
@ LegendTitle
Legend title.
Definition: qgslayoutobject.h:193
QgsLegendStyle::Left
@ Left
Left side.
Definition: qgslegendstyle.h:58
QgsMapLayerLegendUtils::setLegendNodeUserLabel
static void setLegendNodeUserLabel(QgsLayerTreeLayer *nodeLayer, int originalIndex, const QString &newLabel)
Definition: qgsmaplayerlegend.cpp:134
QgsLayerTree::isLayer
static bool isLayer(const QgsLayerTreeNode *node)
Check whether the node is a valid layer node.
Definition: qgslayertree.h:53
QgsFontButton::ModeQFont
@ ModeQFont
Configure font settings for use with QFont objects.
Definition: qgsfontbutton.h:61
QgsLayerTreeLayer::setLabelExpression
void setLabelExpression(const QString &expression)
set the expression to evaluate
Definition: qgslayertreelayer.cpp:222
QgsWkbTypes::LineGeometry
@ LineGeometry
Definition: qgswkbtypes.h:143
QgsWkbTypes::PointGeometry
@ PointGeometry
Definition: qgswkbtypes.h:142
QgsLegendPatchShape::setSymbolType
void setSymbolType(QgsSymbol::SymbolType type)
Sets the symbol type associated with this patch.
Definition: qgslegendpatchshape.cpp:212
QgsLayerTreeModelLegendNode::layerNode
QgsLayerTreeLayer * layerNode() const
Returns pointer to the parent layer node.
Definition: qgslayertreemodellegendnode.h:70
QgsFeatureRenderer
Definition: qgsrenderer.h:103
c
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
Definition: porting_processing.dox:1
QgsLayout
Base class for layouts, which can contain items such as maps, labels, scalebars, etc.
Definition: qgslayout.h:50
QgsLayerTreeNode::setCustomProperty
void setCustomProperty(const QString &key, const QVariant &value)
Sets a custom property for the node. Properties are stored in a map and saved in project file.
Definition: qgslayertreenode.cpp:180
QgsVectorLayer
Represents a vector layer which manages a vector based data sets.
Definition: qgsvectorlayer.h:387
QgsLayerTreeNode::customProperty
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.
Definition: qgslayertreenode.cpp:189
QgsMapLayer
Base class for all map layer types.
Definition: qgsmaplayer.h:83
QgsLayoutItem::UndoLegendWmsLegendHeight
@ UndoLegendWmsLegendHeight
Legend WMS height.
Definition: qgslayoutitem.h:258
QgsLayoutAtlas::toggled
void toggled(bool)
Emitted when atlas is enabled or disabled.
QgsLayoutItemRegistry::LayoutMap
@ LayoutMap
Map item.
Definition: qgslayoutitemregistry.h:317
QgsMapLayerLegendUtils::setLegendNodeSymbolSize
static void setLegendNodeSymbolSize(QgsLayerTreeLayer *nodeLayer, int originalIndex, QSizeF size)
Sets the legend symbol size for the legend node belonging to nodeLayer at the specified originalIndex...
Definition: qgsmaplayerlegend.cpp:171
QgsSymbol::Line
@ Line
Line symbol.
Definition: qgssymbol.h:88
qgsmaplayerlegend.h
QgsLayerTreeGroup::name
QString name() const override
Returns the group's name.
Definition: qgslayertreegroup.cpp:43
QgsSymbol::Marker
@ Marker
Marker symbol.
Definition: qgssymbol.h:87
QgsLayerTreeNode::clone
virtual QgsLayerTreeNode * clone() const =0
Create a copy of the node. Returns new instance.
qgslayertreeutils.h
QgsLayoutItem::UndoLegendGroupFont
@ UndoLegendGroupFont
Legend group font.
Definition: qgslayoutitem.h:271
QgsExpressionContext::setHighlightedVariables
void setHighlightedVariables(const QStringList &variableNames)
Sets the list of variable names within the context intended to be highlighted to the user.
Definition: qgsexpressioncontext.cpp:324
QgsLayoutItemComboBox::itemChanged
void itemChanged(QgsLayoutItem *item)
Emitted whenever the currently selected item changes.
QgsLayoutItem::UndoLegendTitleSpaceBottom
@ UndoLegendTitleSpaceBottom
Legend title space.
Definition: qgslayoutitem.h:259
qgslayertreemodel.h
QgsLayerTreeNode::parent
QgsLayerTreeNode * parent()
Gets pointer to the parent. If parent is nullptr, the node is a root node.
Definition: qgslayertreenode.h:110
QgsLayerTreeView
The QgsLayerTreeView class extends QTreeView and provides some additional functionality when working ...
Definition: qgslayertreeview.h:52
QgsLegendStyle::Hidden
@ Hidden
Special style, item is hidden including margins around.
Definition: qgslegendstyle.h:43
QgsMasterLayoutInterface
Interface for master layout type objects, such as print layouts and reports.
Definition: qgsmasterlayoutinterface.h:43
qgsguiutils.h
QgsLayoutReportContext::layerChanged
void layerChanged(QgsVectorLayer *layer)
Emitted when the context's layer is changed.
QgsExpressionContextScope::StaticVariable
Single variable definition for use within a QgsExpressionContextScope.
Definition: qgsexpressioncontext.h:119
QgsLayoutItem::UndoLegendWmsLegendWidth
@ UndoLegendWmsLegendWidth
Legend WMS width.
Definition: qgslayoutitem.h:257
QgsLayerTreeUtils::legendFilterByExpression
static QString legendFilterByExpression(const QgsLayerTreeLayer &layer, bool *enabled=nullptr)
Returns the expression filter of a legend layer.
Definition: qgslayertreeutils.cpp:435
QgsLegendSymbolList
QList< QgsLegendSymbolItem > QgsLegendSymbolList
Definition: qgslegendsymbolitem.h:144
QgsSymbolLegendNode
Implementation of legend node interface for displaying preview of vector symbols and their labels and...
Definition: qgslayertreemodellegendnode.h:292
QgsLayerTreeNode::removeCustomProperty
void removeCustomProperty(const QString &key)
Remove a custom property from layer. Properties are stored in a map and saved in project file.
Definition: qgslayertreenode.cpp:194
QgsLegendStyle::Top
@ Top
Top side.
Definition: qgslegendstyle.h:56
QgsLayoutItem::UndoLegendRasterStrokeColor
@ UndoLegendRasterStrokeColor
Legend raster stroke color.
Definition: qgslayoutitem.h:269
QgsLayerTree::isGroup
static bool isGroup(QgsLayerTreeNode *node)
Check whether the node is a valid group node.
Definition: qgslayertree.h:43
QgsLayoutItemRegistry::LayoutLegend
@ LayoutLegend
Legend item.
Definition: qgslayoutitemregistry.h:320
qgslegendrenderer.h
QgsLayoutAtlas
Class used to render QgsLayout as an atlas, by iterating over the features from an associated vector ...
Definition: qgslayoutatlas.h:42
QgsLegendStyle::Group
@ Group
Legend group title.
Definition: qgslegendstyle.h:45
QgsLegendStyle::Subgroup
@ Subgroup
Legend subgroup title.
Definition: qgslegendstyle.h:46
QgsLayoutItem::UndoLegendLayerFont
@ UndoLegendLayerFont
Legend layer font.
Definition: qgslayoutitem.h:272
qgsproject.h
QgsLayerTreeLayer::setPatchShape
void setPatchShape(const QgsLegendPatchShape &shape)
Sets the symbol patch shape to use when rendering the legend node symbol.
Definition: qgslayertreelayer.cpp:232
QgsLayoutItem::UndoLegendGroupSpace
@ UndoLegendGroupSpace
Legend group spacing.
Definition: qgslayoutitem.h:260
QgsAlignmentComboBox::changed
void changed()
Emitted when the alignment is changed.
qgslayoutitemmap.h
QgsVectorLayer::renderer
QgsFeatureRenderer * renderer()
Returns renderer.
Definition: qgsvectorlayer.h:892
qgsexpressionbuilderdialog.h
qgslayoutatlas.h
qgslegendpatchshapewidget.h
QgsFontButton::changed
void changed()
Emitted when the widget's text format settings are changed.
QgsLayerTreeModel::layerOriginalLegendNodes
QList< QgsLayerTreeModelLegendNode * > layerOriginalLegendNodes(QgsLayerTreeLayer *nodeLayer)
Returns original (unfiltered) list of legend nodes attached to a particular layer node.
Definition: qgslayertreemodel.cpp:1562
QgsLegendPatchShape::isNull
bool isNull() const
Returns true if the patch shape is a null QgsLegendPatchShape, which indicates that the default legen...
Definition: qgslegendpatchshape.cpp:32
QgsLayerTreeModelLegendNode
The QgsLegendRendererItem class is abstract interface for legend items returned from QgsMapLayerLegen...
Definition: qgslayertreemodellegendnode.h:51