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