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