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