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