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