QGIS API Documentation  3.24.2-Tisler (13c1a02865)
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 
@ Marker
Marker symbol.
@ Line
Line symbol.
@ Fill
Fill symbol.
void changed()
Emitted when the alignment is changed.
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
static QString iconPath(const QString &iconFile)
Returns path to the desired icon file.
void colorChanged(const QColor &color)
Emitted whenever a new color is set for the button.
Settings for a color ramp legend node.
A legend node which renders a color ramp.
A generic dialog for building expression strings.
Single scope for storing variables and functions for use within a QgsExpressionContext.
void addVariable(const QgsExpressionContextScope::StaticVariable &variable)
Adds a variable into the context scope.
static QgsExpressionContextScope * layerScope(const QgsMapLayer *layer)
Creates a new scope which contains variables and functions relating to a QgsMapLayer.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
void setHighlightedVariables(const QStringList &variableNames)
Sets the list of variable names within the context intended to be highlighted to the user.
virtual QgsLegendSymbolList legendSymbolItems() const
Returns a list of symbology items for the legend.
@ ModeQFont
Configure font settings for use with QFont objects.
Definition: qgsfontbutton.h:62
void changed()
Emitted when the widget's text format settings are changed.
Layer tree group node serves as a container for layers and further groups.
void insertChildNode(int index, QgsLayerTreeNode *node)
Insert existing node at specified position.
void setName(const QString &n) override
Sets the group's name.
void removeChildNode(QgsLayerTreeNode *node)
Remove a child node from this group.
QString name() const override
Returns the group's name.
Layer tree node points to a map layer.
void setPatchSize(QSizeF size)
Sets the user (overridden) size for the legend node.
QString labelExpression() const
Returns the expression member of the LayerTreeNode.
LegendNodesSplitBehavior
Legend node column split behavior.
@ AllowSplittingLegendNodesOverMultipleColumns
Allow splitting node's legend nodes across multiple columns.
@ PreventSplittingLegendNodesOverMultipleColumns
Prevent splitting node's legend nodes across multiple columns.
@ UseDefaultLegendSetting
Inherit default legend column splitting setting.
void setLabelExpression(const QString &expression)
set the expression to evaluate
void setPatchShape(const QgsLegendPatchShape &shape)
Sets the symbol patch shape to use when rendering the legend node symbol.
QgsMapLayer * layer() const
Returns the map layer associated with this node.
The QgsLegendRendererItem class is abstract interface for legend items returned from QgsMapLayerLegen...
@ SimpleLegend
Simple label with icon legend node type.
@ RasterSymbolLegend
Raster symbol legend node type.
@ ImageLegend
Raster image legend node type.
@ DataDefinedSizeLegend
Marker symbol legend node type.
@ EmbeddedWidget
Embedded widget placeholder node type.
@ ColorRampLegend
Color ramp legend (since QGIS 3.18)
@ SymbolLegend
Vector symbol legend node type.
QgsLayerTreeLayer * layerNode() const
Returns pointer to the parent layer node.
@ NodeTypeRole
Type of node. Added in 3.16.
QgsLayerTreeModel * model() const
Returns pointer to model owning this legend node.
QgsLayerTreeModelLegendNode * legendNodeEmbeddedInParent(QgsLayerTreeLayer *nodeLayer) const
Returns legend node that may be embedded in parent (i.e.
QList< QgsLayerTreeModelLegendNode * > layerOriginalLegendNodes(QgsLayerTreeLayer *nodeLayer)
Returns original (unfiltered) list of legend nodes attached to a particular layer node.
This class is a base class for nodes in a layer tree.
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.
virtual QgsLayerTreeNode * clone() const =0
Create a copy of the node. Returns new instance.
void removeCustomProperty(const QString &key)
Remove a custom property from layer. Properties are stored in a map and saved in project file.
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.
QgsLayerTreeNode * parent()
Gets pointer to the parent. If parent is nullptr, the node is a root node.
QStringList customProperties() const
Returns list of keys stored in custom properties.
static void setLegendFilterByExpression(QgsLayerTreeLayer &layer, const QString &expr, bool enabled=true)
Sets the expression filter of a legend layer.
static QString legendFilterByExpression(const QgsLayerTreeLayer &layer, bool *enabled=nullptr)
Returns the expression filter of a legend layer.
The QgsLayerTreeView class extends QTreeView and provides some additional functionality when working ...
static bool isLayer(const QgsLayerTreeNode *node)
Check whether the node is a valid layer node.
Definition: qgslayertree.h:53
static QgsLayerTreeLayer * toLayer(QgsLayerTreeNode *node)
Cast node to a layer.
Definition: qgslayertree.h:75
static bool isGroup(QgsLayerTreeNode *node)
Check whether the node is a valid group node.
Definition: qgslayertree.h:43
static QgsLayerTreeGroup * toGroup(QgsLayerTreeNode *node)
Cast node to a group.
Definition: qgslayertree.h:64
Class used to render QgsLayout as an atlas, by iterating over the features from an associated vector ...
void toggled(bool)
Emitted when atlas is enabled or disabled.
A base class for property widgets for layout items.
void itemChanged(QgsLayoutItem *item)
Emitted whenever the currently selected item changes.
A layout item subclass for map legends.
QgsLegendModel * model()
Returns the legend model.
Layout graphical items for displaying a map.
A widget for controlling the common properties of layout items (e.g.
Base class for graphical items within a QgsLayout.
@ UndoLegendMinSymbolSize
Legend minimum symbol size.
@ UndoLegendTitleSpaceBottom
Legend title space.
@ UndoLegendIconSymbolSpace
Legend icon symbol space.
@ UndoLegendLayerFont
Legend layer font.
@ UndoLegendLineSpacing
Legend line spacing.
@ UndoLegendBoxSpace
Legend box space.
@ UndoLegendRasterStrokeColor
Legend raster stroke color.
@ UndoLegendTitleFont
Legend title font.
@ UndoLegendFontColor
Legend font color.
@ UndoLegendItemFont
Legend item font.
@ UndoLegendWmsLegendWidth
Legend WMS width.
@ UndoLegendLayerSpace
Legend layer spacing.
@ UndoLegendColumnSpace
Legend column space.
@ UndoLegendMaxSymbolSize
Legend maximum symbol size.
@ UndoLegendSymbolSpace
Legend symbol spacing.
@ UndoLegendSymbolWidth
Legend symbol width.
@ UndoLegendColumnCount
Legend column count.
@ UndoLegendSymbolHeight
Legend symbol height.
@ UndoLegendGroupSpace
Legend group spacing.
@ UndoLegendSubgroupIndent
Legend subgroup indent.
@ UndoLegendRasterStrokeWidth
Legend raster stroke width.
@ UndoLegendGroupIndent
Legend group indent.
@ UndoLegendText
Legend text.
@ UndoLegendWmsLegendHeight
Legend WMS height.
@ UndoLegendGroupFont
Legend group font.
int type() const override
Returns a unique graphics item type identifier.
A dialog to add new layers to the legend.
This class provides a method of converting QgsLayoutMeasurements from one unit to another.
QgsLayoutMeasurement convert(QgsLayoutMeasurement measurement, QgsUnitTypes::LayoutUnit targetUnits) const
Converts a measurement from one unit to another.
void setDpi(const double dpi)
Sets the dots per inch (dpi) for the measurement converter.
const QgsLayout * layout() const
Returns the layout the object is attached to.
void changed()
Emitted when the object's properties change.
@ LegendTitle
Legend title.
@ LegendColumnCount
Legend column count.
void layerChanged(QgsVectorLayer *layer)
Emitted when the context's layer is changed.
Base class for layouts, which can contain items such as maps, labels, scalebars, etc.
Definition: qgslayout.h:51
QgsLayoutReportContext & reportContext()
Returns a reference to the layout's report context, which stores information relating to the current ...
Definition: qgslayout.cpp:369
void changed()
Emitted when the shape's settings are changed.
Represents a patch shape for use in map legends.
void setSymbolType(Qgis::SymbolType type)
Sets the symbol type associated with this patch.
bool isNull() const
Returns true if the patch shape is a null QgsLegendPatchShape, which indicates that the default legen...
static void setNodeLegendStyle(QgsLayerTreeNode *node, QgsLegendStyle::Style style)
Sets the style of a node.
static QgsLegendStyle::Style nodeLegendStyle(QgsLayerTreeNode *node, QgsLayerTreeModel *model)
Returns the style for the given node, within the specified model.
static QString styleLabel(Style s)
Returns a translated string representing a style component, for use in UI.
@ Left
Left side.
@ Bottom
Bottom side.
@ Top
Top side.
Style
Component of legends which can be styled.
@ Group
Legend group title.
@ Symbol
Symbol icon (excluding label)
@ Subgroup
Legend subgroup title.
@ Title
Legend title.
@ Hidden
Special style, item is hidden including margins around.
@ SymbolLabel
Symbol label (excluding icon)
Map canvas is a class for displaying all GIS data types on a canvas.
Definition: qgsmapcanvas.h:90
static void setLegendNodeOrder(QgsLayerTreeLayer *nodeLayer, const QList< int > &order)
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 ...
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...
static void setLegendNodeUserLabel(QgsLayerTreeLayer *nodeLayer, int originalIndex, const QString &newLabel)
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...
static void setLegendNodeColumnBreak(QgsLayerTreeLayer *nodeLayer, int originalIndex, bool columnBreakBeforeNode)
Sets whether a forced column break should occur before the node.
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...
static QList< int > legendNodeOrder(QgsLayerTreeLayer *nodeLayer)
Base class for all map layer types.
Definition: qgsmaplayer.h:73
Interface for master layout type objects, such as print layouts and reports.
Base class for any widget that can be shown as a inline panel.
void widgetChanged()
Emitted when the widget state changes.
void acceptPanel()
Accept the panel.
virtual void setDockMode(bool dockMode)
Set the widget in dock mode which tells the widget to emit panel widgets and not open dialogs.
void changed()
Emitted when the symbol's settings are changed.
Implementation of legend node interface for displaying preview of vector symbols and their labels and...
Abstract base class for all rendered symbols.
Definition: qgssymbol.h:38
@ LayoutPixels
Pixels.
Definition: qgsunittypes.h:190
Represents a vector layer which manages a vector based data sets.
QgsExpressionContextScope * createExpressionContextScope() const FINAL
This method needs to be reimplemented in all classes which implement this interface and return an exp...
long long featureCount(const QString &legendKey) const
Number of features rendered with specified legend key.
QgsFeatureRenderer * renderer()
Returns the feature renderer used for rendering the features in the layer in 2D map views.
@ VectorLayer
Vector layer.
QSize iconSize(bool dockableToolbar)
Returns the user-preferred size of a window's toolbar icons.
QgsLayerTreeModelLegendNode * legendNode(const QString &rule, QgsLayerTreeModel &model)
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
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition: qgis.h:1517
QList< QgsLegendSymbolItem > QgsLegendSymbolList
Q_GUI_EXPORT int qt_defaultDpiX()
Single variable definition for use within a QgsExpressionContextScope.