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