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