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