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