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