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