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