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