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