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