1 /***************************************************************************
2     qgstextformatwidget.h
3     ---------------------
4     begin                : June 2009
5     copyright            : (C) Martin Dobias
6     email                : wonder dot sk at gmail dot com
7  ***************************************************************************
8  *                                                                         *
9  *   This program is free software; you can redistribute it and/or modify  *
10  *   it under the terms of the GNU General Public License as published by  *
11  *   the Free Software Foundation; either version 2 of the License, or     *
12  *   (at your option) any later version.                                   *
13  *                                                                         *
14  ***************************************************************************/
15 
16 #include "qgstextformatwidget.h"
17 #include "qgsmapcanvas.h"
18 #include "qgscharacterselectordialog.h"
19 #include "qgslogger.h"
20 #include "qgsfontutils.h"
21 #include "qgssymbollayerutils.h"
22 #include "qgssvgcache.h"
23 #include "qgssvgselectorwidget.h"
24 #include "qgssubstitutionlistwidget.h"
25 #include "qgspallabeling.h" // for enum values
26 #include "qgspathresolver.h"
27 #include "qgsproject.h"
28 #include "qgssettings.h"
29 #include "qgseffectstack.h"
30 #include "qgspainteffectregistry.h"
31 #include "qgsstylesavedialog.h"
32 #include "qgsexpressioncontextutils.h"
33 #include "qgsgui.h"
34 #include "qgsvectorlayer.h"
35 #include "qgsauxiliarystorage.h"
36 #include "qgsnewauxiliarylayerdialog.h"
37 #include "qgshelp.h"
38 
39 #include <QButtonGroup>
40 #include <QMessageBox>
41 
QgsTextFormatWidget(const QgsTextFormat & format,QgsMapCanvas * mapCanvas,QWidget * parent,QgsVectorLayer * layer)42 QgsTextFormatWidget::QgsTextFormatWidget( const QgsTextFormat &format, QgsMapCanvas *mapCanvas, QWidget *parent, QgsVectorLayer *layer )
43   : QWidget( parent )
44   , mMapCanvas( mapCanvas )
45   , mLayer( layer )
46 {
47   initWidget();
48   setWidgetMode( Text );
49   populateDataDefinedButtons();
50   updateWidgetForFormat( format );
51 }
52 
QgsTextFormatWidget(QgsMapCanvas * mapCanvas,QWidget * parent,Mode mode,QgsVectorLayer * layer)53 QgsTextFormatWidget::QgsTextFormatWidget( QgsMapCanvas *mapCanvas, QWidget *parent, Mode mode, QgsVectorLayer *layer )
54   : QWidget( parent )
55   , mMapCanvas( mapCanvas )
56   , mLayer( layer )
57   , mWidgetMode( mode )
58 {
59   initWidget();
60   if ( mode == Text )
61     populateDataDefinedButtons();
62   setWidgetMode( mode );
63 }
64 
initWidget()65 void QgsTextFormatWidget::initWidget()
66 {
67   setupUi( this );
68 
69   mGeometryGeneratorGroupBox->setCollapsed( true );
70 
71   connect( mShapeSVGPathLineEdit, &QLineEdit::textChanged, this, &QgsTextFormatWidget::mShapeSVGPathLineEdit_textChanged );
72   connect( mFontSizeSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsTextFormatWidget::mFontSizeSpinBox_valueChanged );
73   connect( mFontFamilyCmbBx, &QFontComboBox::currentFontChanged, this, &QgsTextFormatWidget::mFontFamilyCmbBx_currentFontChanged );
74   connect( mFontStyleComboBox, &QComboBox::currentTextChanged, this, &QgsTextFormatWidget::mFontStyleComboBox_currentIndexChanged );
75   connect( mFontUnderlineBtn, &QToolButton::toggled, this, &QgsTextFormatWidget::mFontUnderlineBtn_toggled );
76   connect( mFontStrikethroughBtn, &QToolButton::toggled, this, &QgsTextFormatWidget::mFontStrikethroughBtn_toggled );
77   connect( mFontWordSpacingSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsTextFormatWidget::mFontWordSpacingSpinBox_valueChanged );
78   connect( mFontLetterSpacingSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsTextFormatWidget::mFontLetterSpacingSpinBox_valueChanged );
79   connect( mFontSizeUnitWidget, &QgsUnitSelectionWidget::changed, this, &QgsTextFormatWidget::mFontSizeUnitWidget_changed );
80   connect( mFontMinPixelSpinBox, static_cast < void ( QSpinBox::* )( int ) > ( &QSpinBox::valueChanged ), this, &QgsTextFormatWidget::mFontMinPixelSpinBox_valueChanged );
81   connect( mFontMaxPixelSpinBox, static_cast < void ( QSpinBox::* )( int ) > ( &QSpinBox::valueChanged ), this, &QgsTextFormatWidget::mFontMaxPixelSpinBox_valueChanged );
82   connect( mBufferUnitWidget, &QgsUnitSelectionWidget::changed, this, &QgsTextFormatWidget::mBufferUnitWidget_changed );
83   connect( mMaskBufferUnitWidget, &QgsUnitSelectionWidget::changed, this, &QgsTextFormatWidget::mMaskBufferUnitWidget_changed );
84   connect( mCoordXDDBtn, &QgsPropertyOverrideButton::changed, this, &QgsTextFormatWidget::mCoordXDDBtn_changed );
85   connect( mCoordYDDBtn, &QgsPropertyOverrideButton::changed, this, &QgsTextFormatWidget::mCoordYDDBtn_changed );
86   connect( mShapeTypeCmbBx, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsTextFormatWidget::mShapeTypeCmbBx_currentIndexChanged );
87   connect( mShapeRotationCmbBx, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsTextFormatWidget::mShapeRotationCmbBx_currentIndexChanged );
88   connect( mShapeSVGParamsBtn, &QPushButton::clicked, this, &QgsTextFormatWidget::mShapeSVGParamsBtn_clicked );
89   connect( mShapeSVGSelectorBtn, &QPushButton::clicked, this, &QgsTextFormatWidget::mShapeSVGSelectorBtn_clicked );
90   connect( mPreviewTextEdit, &QLineEdit::textChanged, this, &QgsTextFormatWidget::mPreviewTextEdit_textChanged );
91   connect( mPreviewTextBtn, &QToolButton::clicked, this, &QgsTextFormatWidget::mPreviewTextBtn_clicked );
92   connect( mPreviewBackgroundBtn, &QgsColorButton::colorChanged, this, &QgsTextFormatWidget::mPreviewBackgroundBtn_colorChanged );
93   connect( mDirectSymbLeftToolBtn, &QToolButton::clicked, this, &QgsTextFormatWidget::mDirectSymbLeftToolBtn_clicked );
94   connect( mDirectSymbRightToolBtn, &QToolButton::clicked, this, &QgsTextFormatWidget::mDirectSymbRightToolBtn_clicked );
95   connect( chkLineOrientationDependent, &QCheckBox::toggled, this, &QgsTextFormatWidget::chkLineOrientationDependent_toggled );
96   connect( mToolButtonConfigureSubstitutes, &QToolButton::clicked, this, &QgsTextFormatWidget::mToolButtonConfigureSubstitutes_clicked );
97   connect( mKerningCheckBox, &QCheckBox::toggled, this, &QgsTextFormatWidget::kerningToggled );
98 
99   const int iconSize = QgsGuiUtils::scaleIconSize( 20 );
100   mOptionsTab->setIconSize( QSize( iconSize, iconSize ) );
101   mLabelingOptionsListWidget->setIconSize( QSize( iconSize, iconSize ) ) ;
102   const int iconSize32 = QgsGuiUtils::scaleIconSize( 32 );
103   const int iconSize24 = QgsGuiUtils::scaleIconSize( 24 );
104   const int iconSize18 = QgsGuiUtils::scaleIconSize( 18 );
105   const int iconSize16 = QgsGuiUtils::scaleIconSize( 16 );
106 
107   mPreviewTextBtn->setIconSize( QSize( iconSize16, iconSize16 ) );
108   mPointOffsetAboveLeft->setIconSize( QSize( iconSize32, iconSize18 ) );
109   mPointOffsetAbove->setIconSize( QSize( iconSize32, iconSize18 ) );
110   mPointOffsetAboveRight->setIconSize( QSize( iconSize32, iconSize18 ) );
111   mPointOffsetLeft->setIconSize( QSize( iconSize32, iconSize18 ) );
112   mPointOffsetOver ->setIconSize( QSize( iconSize32, iconSize18 ) );
113   mPointOffsetRight->setIconSize( QSize( iconSize32, iconSize18 ) );
114   mPointOffsetBelowLeft->setIconSize( QSize( iconSize32, iconSize18 ) );
115   mPointOffsetBelow->setIconSize( QSize( iconSize32, iconSize18 ) );
116   mPointOffsetBelowRight->setIconSize( QSize( iconSize32, iconSize18 ) );
117   mLabelMinScale->setPixmap( QgsApplication::getThemeIcon( QStringLiteral( "/mActionZoomOut.svg" ) ).pixmap( QSize( iconSize24, iconSize24 ) ) );
118   mLabelMaxScale->setPixmap( QgsApplication::getThemeIcon( QStringLiteral( "/mActionZoomIn.svg" ) ).pixmap( QSize( iconSize24, iconSize24 ) ) );
119 
120   const int buttonSize = QgsGuiUtils::scaleIconSize( 24 );
121   mFontUnderlineBtn->setMinimumSize( buttonSize, buttonSize );
122   mFontUnderlineBtn->setMaximumSize( buttonSize, buttonSize );
123   mFontStrikethroughBtn->setMinimumSize( buttonSize, buttonSize );
124   mFontStrikethroughBtn->setMaximumSize( buttonSize, buttonSize );
125   mFontBoldBtn->setMinimumSize( buttonSize, buttonSize );
126   mFontBoldBtn->setMaximumSize( buttonSize, buttonSize );
127   mFontItalicBtn->setMinimumSize( buttonSize, buttonSize );
128   mFontItalicBtn->setMaximumSize( buttonSize, buttonSize );
129 
130   mPreviewScaleComboBox->setMapCanvas( mMapCanvas );
131   mPreviewScaleComboBox->setShowCurrentScaleButton( true );
132   connect( mPreviewScaleComboBox, &QgsScaleWidget::scaleChanged, this, &QgsTextFormatWidget::previewScaleChanged );
133 
134   const auto unitWidgets = findChildren<QgsUnitSelectionWidget *>();
135   for ( QgsUnitSelectionWidget *unitWidget : unitWidgets )
136   {
137     unitWidget->setMapCanvas( mMapCanvas );
138   }
139   mFontSizeUnitWidget->setUnits( QgsUnitTypes::RenderUnitList() << QgsUnitTypes::RenderPoints << QgsUnitTypes::RenderMetersInMapUnits << QgsUnitTypes::RenderMapUnits
140                                  << QgsUnitTypes::RenderMillimeters << QgsUnitTypes::RenderPixels << QgsUnitTypes::RenderInches );
141   mBufferUnitWidget->setUnits( QgsUnitTypes::RenderUnitList() << QgsUnitTypes::RenderMillimeters << QgsUnitTypes::RenderMetersInMapUnits << QgsUnitTypes::RenderMapUnits << QgsUnitTypes::RenderPixels
142                                << QgsUnitTypes::RenderPoints << QgsUnitTypes::RenderInches );
143   mMaskBufferUnitWidget->setUnits( QgsUnitTypes::RenderUnitList() << QgsUnitTypes::RenderMillimeters << QgsUnitTypes::RenderMetersInMapUnits << QgsUnitTypes::RenderMapUnits << QgsUnitTypes::RenderPixels
144                                    << QgsUnitTypes::RenderPoints << QgsUnitTypes::RenderInches );  mShapeSizeUnitWidget->setUnits( QgsUnitTypes::RenderUnitList() << QgsUnitTypes::RenderMillimeters << QgsUnitTypes::RenderMetersInMapUnits << QgsUnitTypes::RenderMapUnits << QgsUnitTypes::RenderPixels
145                                        << QgsUnitTypes::RenderPoints << QgsUnitTypes::RenderInches );
146   mShapeOffsetUnitWidget->setUnits( QgsUnitTypes::RenderUnitList() << QgsUnitTypes::RenderMillimeters << QgsUnitTypes::RenderMetersInMapUnits << QgsUnitTypes::RenderMapUnits << QgsUnitTypes::RenderPixels
147                                     << QgsUnitTypes::RenderPoints << QgsUnitTypes::RenderInches );
148   mShapeRadiusUnitWidget->setUnits( QgsUnitTypes::RenderUnitList() << QgsUnitTypes::RenderMillimeters << QgsUnitTypes::RenderMetersInMapUnits << QgsUnitTypes::RenderMapUnits
149                                     << QgsUnitTypes::RenderPixels << QgsUnitTypes::RenderPercentage
150                                     << QgsUnitTypes::RenderPoints << QgsUnitTypes::RenderInches );
151   mShapeStrokeWidthUnitWidget->setUnits( QgsUnitTypes::RenderUnitList() << QgsUnitTypes::RenderMillimeters << QgsUnitTypes::RenderMetersInMapUnits << QgsUnitTypes::RenderMapUnits << QgsUnitTypes::RenderPixels
152                                          << QgsUnitTypes::RenderPoints << QgsUnitTypes::RenderInches );
153   mShadowOffsetUnitWidget->setUnits( QgsUnitTypes::RenderUnitList() << QgsUnitTypes::RenderMillimeters << QgsUnitTypes::RenderMetersInMapUnits << QgsUnitTypes::RenderMapUnits << QgsUnitTypes::RenderPixels
154                                      << QgsUnitTypes::RenderPoints << QgsUnitTypes::RenderInches );
155   mShadowRadiusUnitWidget->setUnits( QgsUnitTypes::RenderUnitList() << QgsUnitTypes::RenderMillimeters << QgsUnitTypes::RenderMetersInMapUnits << QgsUnitTypes::RenderMapUnits << QgsUnitTypes::RenderPixels
156                                      << QgsUnitTypes::RenderPoints << QgsUnitTypes::RenderInches );
157   mPointOffsetUnitWidget->setUnits( QgsUnitTypes::RenderUnitList() << QgsUnitTypes::RenderMillimeters << QgsUnitTypes::RenderMetersInMapUnits << QgsUnitTypes::RenderMapUnits << QgsUnitTypes::RenderPixels
158                                     << QgsUnitTypes::RenderPoints << QgsUnitTypes::RenderInches );
159   mLineDistanceUnitWidget->setUnits( QgsUnitTypes::RenderUnitList() << QgsUnitTypes::RenderMillimeters << QgsUnitTypes::RenderMetersInMapUnits << QgsUnitTypes::RenderMapUnits << QgsUnitTypes::RenderPixels
160                                      << QgsUnitTypes::RenderPoints << QgsUnitTypes::RenderInches );
161   mRepeatDistanceUnitWidget->setUnits( QgsUnitTypes::RenderUnitList() << QgsUnitTypes::RenderMillimeters << QgsUnitTypes::RenderMetersInMapUnits << QgsUnitTypes::RenderMapUnits << QgsUnitTypes::RenderPixels
162                                        << QgsUnitTypes::RenderPoints << QgsUnitTypes::RenderInches );
163   mOverrunDistanceUnitWidget->setUnits( QgsUnitTypes::RenderUnitList() << QgsUnitTypes::RenderMillimeters << QgsUnitTypes::RenderMetersInMapUnits << QgsUnitTypes::RenderMapUnits << QgsUnitTypes::RenderPixels
164                                         << QgsUnitTypes::RenderPoints << QgsUnitTypes::RenderInches );
165   mFontLineHeightSpinBox->setClearValue( 1.0 );
166   mShapeRotationDblSpnBx->setClearValue( 0.0 );
167   mShapeOffsetXSpnBx->setClearValue( 0.0 );
168   mShapeOffsetYSpnBx->setClearValue( 0.0 );
169   mPointOffsetXSpinBox->setClearValue( 0.0 );
170   mPointOffsetYSpinBox->setClearValue( 0.0 );
171   mPointAngleSpinBox->setClearValue( 0.0 );
172   mFontLetterSpacingSpinBox->setClearValue( 0.0 );
173   mFontWordSpacingSpinBox->setClearValue( 0.0 );
174   mZIndexSpinBox->setClearValue( 0.0 );
175   mLineDistanceSpnBx->setClearValue( 0.0 );
176 
177   mOffsetTypeComboBox->addItem( tr( "From Point" ), QgsPalLayerSettings::FromPoint );
178   mOffsetTypeComboBox->addItem( tr( "From Symbol Bounds" ), QgsPalLayerSettings::FromSymbolBounds );
179 
180   mShapeTypeCmbBx->addItem( tr( "Rectangle" ), QgsTextBackgroundSettings::ShapeRectangle );
181   mShapeTypeCmbBx->addItem( tr( "Square" ), QgsTextBackgroundSettings::ShapeSquare );
182   mShapeTypeCmbBx->addItem( tr( "Ellipse" ), QgsTextBackgroundSettings::ShapeEllipse );
183   mShapeTypeCmbBx->addItem( tr( "Circle" ), QgsTextBackgroundSettings::ShapeCircle );
184   mShapeTypeCmbBx->addItem( tr( "SVG" ), QgsTextBackgroundSettings::ShapeSVG );
185   mShapeTypeCmbBx->addItem( tr( "Marker Symbol" ), QgsTextBackgroundSettings::ShapeMarkerSymbol );
186 
187   updateAvailableShadowPositions();
188 
189   mBackgroundSymbolButton->setSymbolType( QgsSymbol::Marker );
190   mBackgroundSymbolButton->setDialogTitle( tr( "Background Symbol" ) );
191   mBackgroundSymbolButton->registerExpressionContextGenerator( this );
192   mBackgroundSymbolButton->setMapCanvas( mMapCanvas );
193 
194   mCharDlg = new QgsCharacterSelectorDialog( this );
195 
196   mRefFont = lblFontPreview->font();
197 
198   // internal connections
199   connect( mShadowOffsetAngleDial, &QAbstractSlider::valueChanged, mShadowOffsetAngleSpnBx, &QSpinBox::setValue );
200   connect( mShadowOffsetAngleSpnBx, static_cast < void ( QSpinBox::* )( int ) > ( &QSpinBox::valueChanged ), mShadowOffsetAngleDial, &QAbstractSlider::setValue );
201   connect( mLimitLabelChkBox, &QAbstractButton::toggled, mLimitLabelSpinBox, &QWidget::setEnabled );
202   connect( mCheckBoxSubstituteText, &QAbstractButton::toggled, mToolButtonConfigureSubstitutes, &QWidget::setEnabled );
203 
204   //connections to prevent users removing all line placement positions
205   connect( chkLineAbove, &QAbstractButton::toggled, this, &QgsTextFormatWidget::updateLinePlacementOptions );
206   connect( chkLineBelow, &QAbstractButton::toggled, this, &QgsTextFormatWidget::updateLinePlacementOptions );
207   connect( chkLineOn, &QAbstractButton::toggled, this, &QgsTextFormatWidget::updateLinePlacementOptions );
208 
209   mTextOrientationComboBox->addItem( tr( "Horizontal" ), QgsTextFormat::HorizontalOrientation );
210   mTextOrientationComboBox->addItem( tr( "Vertical" ), QgsTextFormat::VerticalOrientation );
211 
212   populateFontCapitalsComboBox();
213 
214   // color buttons
215   mPreviewBackgroundBtn->setColorDialogTitle( tr( "Select Fill Color" ) );
216   mPreviewBackgroundBtn->setContext( QStringLiteral( "labeling" ) );
217   mPreviewBackgroundBtn->setColor( QColor( 255, 255, 255 ) );
218   btnTextColor->setColorDialogTitle( tr( "Select Text Color" ) );
219   btnTextColor->setContext( QStringLiteral( "labeling" ) );
220   btnTextColor->setDefaultColor( Qt::black );
221   btnBufferColor->setColorDialogTitle( tr( "Select Buffer Color" ) );
222   btnBufferColor->setContext( QStringLiteral( "labeling" ) );
223   btnBufferColor->setDefaultColor( Qt::white );
224   mShapeStrokeColorBtn->setColorDialogTitle( tr( "Select Stroke Color" ) );
225   mShapeStrokeColorBtn->setContext( QStringLiteral( "labeling" ) );
226   mShapeFillColorBtn->setColorDialogTitle( tr( "Select Fill Color" ) );
227   mShapeFillColorBtn->setContext( QStringLiteral( "labeling" ) );
228   mShadowColorBtn->setColorDialogTitle( tr( "Select Shadow Color" ) );
229   mShadowColorBtn->setContext( QStringLiteral( "labeling" ) );
230   mShadowColorBtn->setDefaultColor( Qt::black );
231 
232   mFontColorDDBtn->registerLinkedWidget( btnTextColor );
233   mBufferColorDDBtn->registerLinkedWidget( btnBufferColor );
234   mShapeStrokeColorDDBtn->registerLinkedWidget( mShapeStrokeColorBtn );
235   mShapeFillColorDDBtn->registerLinkedWidget( mShapeFillColorBtn );
236   mShadowColorDDBtn->registerLinkedWidget( mShadowColorBtn );
237 
238   // set up quadrant offset button group
239   mQuadrantBtnGrp = new QButtonGroup( this );
240   mQuadrantBtnGrp->addButton( mPointOffsetAboveLeft, static_cast<int>( QgsPalLayerSettings::QuadrantAboveLeft ) );
241   mQuadrantBtnGrp->addButton( mPointOffsetAbove, static_cast<int>( QgsPalLayerSettings::QuadrantAbove ) );
242   mQuadrantBtnGrp->addButton( mPointOffsetAboveRight, static_cast<int>( QgsPalLayerSettings::QuadrantAboveRight ) );
243   mQuadrantBtnGrp->addButton( mPointOffsetLeft, static_cast<int>( QgsPalLayerSettings::QuadrantLeft ) );
244   mQuadrantBtnGrp->addButton( mPointOffsetOver, static_cast<int>( QgsPalLayerSettings::QuadrantOver ) );
245   mQuadrantBtnGrp->addButton( mPointOffsetRight, static_cast<int>( QgsPalLayerSettings::QuadrantRight ) );
246   mQuadrantBtnGrp->addButton( mPointOffsetBelowLeft, static_cast<int>( QgsPalLayerSettings::QuadrantBelowLeft ) );
247   mQuadrantBtnGrp->addButton( mPointOffsetBelow, static_cast<int>( QgsPalLayerSettings::QuadrantBelow ) );
248   mQuadrantBtnGrp->addButton( mPointOffsetBelowRight, static_cast<int>( QgsPalLayerSettings::QuadrantBelowRight ) );
249   mQuadrantBtnGrp->setExclusive( true );
250 
251   // setup direction symbol(s) button group
252   mDirectSymbBtnGrp = new QButtonGroup( this );
253   mDirectSymbBtnGrp->addButton( mDirectSymbRadioBtnLR, static_cast<int>( QgsLabelLineSettings::DirectionSymbolPlacement::SymbolLeftRight ) );
254   mDirectSymbBtnGrp->addButton( mDirectSymbRadioBtnAbove, static_cast<int>( QgsLabelLineSettings::DirectionSymbolPlacement::SymbolAbove ) );
255   mDirectSymbBtnGrp->addButton( mDirectSymbRadioBtnBelow, static_cast<int>( QgsLabelLineSettings::DirectionSymbolPlacement::SymbolBelow ) );
256   mDirectSymbBtnGrp->setExclusive( true );
257 
258   // upside-down labels button group
259   mUpsidedownBtnGrp = new QButtonGroup( this );
260   mUpsidedownBtnGrp->addButton( mUpsidedownRadioOff, static_cast<int>( QgsPalLayerSettings::Upright ) );
261   mUpsidedownBtnGrp->addButton( mUpsidedownRadioDefined, static_cast<int>( QgsPalLayerSettings::ShowDefined ) );
262   mUpsidedownBtnGrp->addButton( mUpsidedownRadioAll, static_cast<int>( QgsPalLayerSettings::ShowAll ) );
263   mUpsidedownBtnGrp->setExclusive( true );
264 
265   //mShapeCollisionsChkBx->setVisible( false ); // until implemented
266 
267   // post updatePlacementWidgets() connections
268   connect( chkLineAbove, &QAbstractButton::toggled, this, &QgsTextFormatWidget::updatePlacementWidgets );
269   connect( chkLineBelow, &QAbstractButton::toggled, this, &QgsTextFormatWidget::updatePlacementWidgets );
270   connect( mCheckAllowLabelsOutsidePolygons, &QAbstractButton::toggled, this, &QgsTextFormatWidget::updatePlacementWidgets );
271   connect( mAllowOutsidePolygonsDDBtn, &QgsPropertyOverrideButton::changed, this, &QgsTextFormatWidget::updatePlacementWidgets );
272 
273   connect( mPlacementModeComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsTextFormatWidget::updatePlacementWidgets );
274 
275   // Global settings group for groupboxes' saved/restored collapsed state
276   // maintains state across different dialogs
277   const auto groupBoxes = findChildren<QgsCollapsibleGroupBox *>();
278   for ( QgsCollapsibleGroupBox *grpbox : groupBoxes )
279   {
280     grpbox->setSettingGroup( QStringLiteral( "mAdvLabelingDlg" ) );
281   }
282 
283   connect( groupBox_mPreview, &QgsCollapsibleGroupBoxBasic::collapsedStateChanged, this, &QgsTextFormatWidget::collapseSample );
284 
285   // get rid of annoying outer focus rect on Mac
286   mLabelingOptionsListWidget->setAttribute( Qt::WA_MacShowFocusRect, false );
287 
288   QgsSettings settings;
289 
290   // reset horiz stretch of left side of options splitter (set to 1 for previewing in Qt Designer)
291   QSizePolicy policy( mLabelingOptionsListFrame->sizePolicy() );
292   policy.setHorizontalStretch( 0 );
293   mLabelingOptionsListFrame->setSizePolicy( policy );
294   if ( !settings.contains( QStringLiteral( "/Windows/Labeling/OptionsSplitState" ) ) )
295   {
296     // set left list widget width on initial showing
297     QList<int> splitsizes;
298     splitsizes << 115;
299     mLabelingOptionsSplitter->setSizes( splitsizes );
300   }
301 
302   // set up reverse connection from stack to list
303   connect( mLabelStackedWidget, &QStackedWidget::currentChanged, this, &QgsTextFormatWidget::optionsStackedWidget_CurrentChanged );
304 
305   // restore dialog, splitters and current tab
306   mFontPreviewSplitter->restoreState( settings.value( QStringLiteral( "Windows/Labeling/FontPreviewSplitState" ) ).toByteArray() );
307   mLabelingOptionsSplitter->restoreState( settings.value( QStringLiteral( "Windows/Labeling/OptionsSplitState" ) ).toByteArray() );
308 
309   mLabelingOptionsListWidget->setCurrentRow( settings.value( QStringLiteral( "Windows/Labeling/Tab" ), 0 ).toInt() );
310 
311   mBufferEffect.reset( QgsPaintEffectRegistry::defaultStack() );
312   connect( mBufferEffectWidget, &QgsEffectStackCompactWidget::changed, this, &QgsTextFormatWidget::updatePreview );
313   mBufferEffectWidget->setPaintEffect( mBufferEffect.get() );
314 
315   mMaskEffect.reset( QgsPaintEffectRegistry::defaultStack() );
316   connect( mMaskEffectWidget, &QgsEffectStackCompactWidget::changed, this, &QgsTextFormatWidget::updatePreview );
317   mMaskEffectWidget->setPaintEffect( mMaskEffect.get() );
318 
319   mBackgroundEffect.reset( QgsPaintEffectRegistry::defaultStack() );
320   connect( mBackgroundEffectWidget, &QgsEffectStackCompactWidget::changed, this, &QgsTextFormatWidget::updatePreview );
321   mBackgroundEffectWidget->setPaintEffect( mBackgroundEffect.get() );
322 
323   setDockMode( false );
324 
325   QList<QWidget *> widgets;
326   widgets << btnBufferColor
327           << btnTextColor
328           << chkLabelPerFeaturePart
329           << chkLineAbove
330           << chkLineBelow
331           << chkLineOn
332           << chkLineOrientationDependent
333           << chkMergeLines
334           << chkPreserveRotation
335           << comboBlendMode
336           << comboBufferBlendMode
337           << mBufferDrawChkBx
338           << mBufferJoinStyleComboBox
339           << mBufferTranspFillChbx
340           << mBufferOpacityWidget
341           << mCentroidInsideCheckBox
342           << mChkNoObstacle
343           << mDirectSymbChkBx
344           << mDirectSymbLeftLineEdit
345           << mDirectSymbRevChkBx
346           << mDirectSymbRightLineEdit
347           << mFitInsidePolygonCheckBox
348           << mFontCapitalsComboBox
349           << mFontLetterSpacingSpinBox
350           << mFontLimitPixelChkBox
351           << mFontLineHeightSpinBox
352           << mFontMaxPixelSpinBox
353           << mFontMinPixelSpinBox
354           << mFontMultiLineAlignComboBox
355           << mFontSizeSpinBox
356           << mFontStyleComboBox
357           << mTextOrientationComboBox
358           << mTextOpacityWidget
359           << mFontWordSpacingSpinBox
360           << mFormatNumChkBx
361           << mFormatNumDecimalsSpnBx
362           << mFormatNumPlusSignChkBx
363           << mLimitLabelChkBox
364           << mLimitLabelSpinBox
365           << mLineDistanceSpnBx
366           << mLineDistanceUnitWidget
367           << mMaxCharAngleInDSpinBox
368           << mMaxCharAngleOutDSpinBox
369           << mMinSizeSpinBox
370           << mOffsetTypeComboBox
371           << mPalShowAllLabelsForLayerChkBx
372           << mPointAngleSpinBox
373           << mPointOffsetUnitWidget
374           << mPointOffsetXSpinBox
375           << mPointOffsetYSpinBox
376           << mPreviewBackgroundBtn
377           << mPreviewTextEdit
378           << mPrioritySlider
379           << mRepeatDistanceSpinBox
380           << mRepeatDistanceUnitWidget
381           << mOverrunDistanceSpinBox
382           << mOverrunDistanceUnitWidget
383           << mScaleBasedVisibilityChkBx
384           << mMaxScaleWidget
385           << mMinScaleWidget
386           << mShadowBlendCmbBx
387           << mShadowColorBtn
388           << mShadowDrawChkBx
389           << mShadowOffsetAngleSpnBx
390           << mShadowOffsetGlobalChkBx
391           << mShadowOffsetSpnBx
392           << mShadowOffsetUnitWidget
393           << mShadowRadiusAlphaChkBx
394           << mShadowRadiusDblSpnBx
395           << mShadowRadiusUnitWidget
396           << mShadowScaleSpnBx
397           << mShadowOpacityWidget
398           << mShadowUnderCmbBx
399           << mShapeBlendCmbBx
400           << mShapeStrokeColorBtn
401           << mShapeStrokeWidthSpnBx
402           << mShapeStrokeWidthUnitWidget
403           << mShapeDrawChkBx
404           << mShapeFillColorBtn
405           << mShapeOffsetXSpnBx
406           << mShapeOffsetYSpnBx
407           << mShapeOffsetUnitWidget
408           << mShapePenStyleCmbBx
409           << mShapeRadiusXDbSpnBx
410           << mShapeRadiusYDbSpnBx
411           << mShapeRotationCmbBx
412           << mShapeRotationDblSpnBx
413           << mShapeRadiusUnitWidget
414           << mShapeSVGPathLineEdit
415           << mShapeSizeCmbBx
416           << mShapeSizeUnitWidget
417           << mShapeSizeXSpnBx
418           << mShapeSizeYSpnBx
419           << mBackgroundOpacityWidget
420           << mShapeTypeCmbBx
421           << mZIndexSpinBox
422           << spinBufferSize
423           << wrapCharacterEdit
424           << mAutoWrapLengthSpinBox
425           << mAutoWrapTypeComboBox
426           << mCentroidRadioVisible
427           << mCentroidRadioWhole
428           << mDirectSymbRadioBtnAbove
429           << mDirectSymbRadioBtnBelow
430           << mDirectSymbRadioBtnLR
431           << mUpsidedownRadioAll
432           << mUpsidedownRadioDefined
433           << mUpsidedownRadioOff
434           << mPlacementModeComboBox
435           << mFieldExpressionWidget
436           << mCheckBoxSubstituteText
437           << mGeometryGeneratorGroupBox
438           << mGeometryGenerator
439           << mGeometryGeneratorType
440           << mBackgroundSymbolButton
441           << mCalloutsDrawCheckBox
442           << mCalloutStyleComboBox
443           << mKerningCheckBox
444           << mEnableMaskChkBx
445           << mMaskJoinStyleComboBox
446           << mMaskBufferSizeSpinBox
447           << mMaskOpacityWidget
448           << mCheckAllowLabelsOutsidePolygons
449           << mHtmlFormattingCheckBox;
450 
451   connectValueChanged( widgets, SLOT( updatePreview() ) );
452 
453   connect( mQuadrantBtnGrp, static_cast<void ( QButtonGroup::* )( int )>( &QButtonGroup::buttonClicked ), this, &QgsTextFormatWidget::updatePreview );
454 
455   connect( mBufferDrawDDBtn, &QgsPropertyOverrideButton::activated, this, &QgsTextFormatWidget::updateBufferFrameStatus );
456   connect( mBufferDrawChkBx, &QCheckBox::stateChanged, this, [ = ]( int )
457   {
458     updateBufferFrameStatus();
459   } );
460   connect( mShapeDrawDDBtn, &QgsPropertyOverrideButton::activated, this, &QgsTextFormatWidget::updateShapeFrameStatus );
461   connect( mShapeDrawChkBx, &QCheckBox::stateChanged, this, [ = ]( int )
462   {
463     updateShapeFrameStatus();
464   } );
465   connect( mShadowDrawDDBtn, &QgsPropertyOverrideButton::activated, this, &QgsTextFormatWidget::updateShadowFrameStatus );
466   connect( mShadowDrawChkBx, &QCheckBox::stateChanged, this, [ = ]( int )
467   {
468     updateShadowFrameStatus();
469   } );
470   connect( mCalloutDrawDDBtn, &QgsPropertyOverrideButton::activated, this, &QgsTextFormatWidget::updateCalloutFrameStatus );
471   connect( mCalloutsDrawCheckBox, &QCheckBox::stateChanged, this, [ = ]( int )
472   {
473     updateCalloutFrameStatus();
474   } );
475 
476   mGeometryGeneratorType->addItem( QgsApplication::getThemeIcon( QStringLiteral( "/mIconPolygonLayer.svg" ) ), tr( "Polygon / MultiPolygon" ), QgsWkbTypes::GeometryType::PolygonGeometry );
477   mGeometryGeneratorType->addItem( QgsApplication::getThemeIcon( QStringLiteral( "/mIconLineLayer.svg" ) ), tr( "LineString / MultiLineString" ), QgsWkbTypes::GeometryType::LineGeometry );
478   mGeometryGeneratorType->addItem( QgsApplication::getThemeIcon( QStringLiteral( "/mIconPointLayer.svg" ) ), tr( "Point / MultiPoint" ), QgsWkbTypes::GeometryType::PointGeometry );
479 
480   // set correct initial tab to match displayed setting page
481   whileBlocking( mOptionsTab )->setCurrentIndex( mLabelStackedWidget->currentIndex() );
482   mOptionsTab->tabBar()->setUsesScrollButtons( true );
483 
484 
485   if ( mMapCanvas )
486   {
487     lblFontPreview->setMapUnits( mMapCanvas->mapSettings().mapUnits() );
488     mPreviewScaleComboBox->setScale( mMapCanvas->mapSettings().scale() );
489   }
490 
491   mTextFormatsListWidget->setStyle( QgsStyle::defaultStyle() );
492   mTextFormatsListWidget->setEntityType( QgsStyle::TextFormatEntity );
493   connect( mTextFormatsListWidget, &QgsStyleItemsListWidget::selectionChanged, this, &QgsTextFormatWidget::setFormatFromStyle );
494   connect( mTextFormatsListWidget, &QgsStyleItemsListWidget::saveEntity, this, &QgsTextFormatWidget::saveFormat );
495 }
496 
setWidgetMode(QgsTextFormatWidget::Mode mode)497 void QgsTextFormatWidget::setWidgetMode( QgsTextFormatWidget::Mode mode )
498 {
499   mWidgetMode = mode;
500   switch ( mode )
501   {
502     case Labeling:
503       toggleDDButtons( true );
504       mTextFormatsListWidget->setEntityTypes( QList< QgsStyle::StyleEntity >() << QgsStyle::TextFormatEntity << QgsStyle::LabelSettingsEntity );
505       mTextOrientationComboBox->addItem( tr( "Rotation-based" ), QgsTextFormat::RotationBasedOrientation );
506       break;
507 
508     case Text:
509     {
510       const int prevIndex = mOptionsTab->currentIndex();
511       toggleDDButtons( true );
512       delete mLabelingOptionsListWidget->takeItem( 8 ); // rendering
513       delete mLabelingOptionsListWidget->takeItem( 7 ); // placement
514       delete mLabelingOptionsListWidget->takeItem( 6 ); // callouts
515       delete mLabelingOptionsListWidget->takeItem( 3 ); // mask
516       mOptionsTab->removeTab( 8 );
517       mOptionsTab->removeTab( 7 );
518       mOptionsTab->removeTab( 6 );
519       mOptionsTab->removeTab( 3 );
520       mLabelStackedWidget->removeWidget( mLabelPage_Rendering );
521       mLabelStackedWidget->removeWidget( mLabelPage_Callouts );
522       mLabelStackedWidget->removeWidget( mLabelPage_Mask );
523       mLabelStackedWidget->removeWidget( mLabelPage_Placement );
524       switch ( prevIndex )
525       {
526         case 0:
527         case 1:
528         case 2:
529           break;
530 
531         case 4: // background - account for removed mask tab
532         case 5: // shadow
533           mLabelStackedWidget->setCurrentIndex( prevIndex - 1 );
534           mOptionsTab->setCurrentIndex( prevIndex - 1 );
535           break;
536 
537         case 3: // mask
538         case 6: // callouts
539         case 7: // placement
540         case 8: // rendering
541           mLabelStackedWidget->setCurrentIndex( 0 );
542           mOptionsTab->setCurrentIndex( 0 );
543           break;
544       }
545 
546       frameLabelWith->hide();
547       mDirectSymbolsFrame->hide();
548       mFormatNumFrame->hide();
549       mFormatNumChkBx->hide();
550       mFormatNumDDBtn->hide();
551       mCheckBoxSubstituteText->hide();
552       mToolButtonConfigureSubstitutes->hide();
553       mLabelWrapOnCharacter->hide();
554       wrapCharacterEdit->hide();
555       mWrapCharDDBtn->hide();
556       mLabelWrapLinesTo->hide();
557       mAutoWrapLengthSpinBox->hide();
558       mAutoWrapLengthDDBtn->hide();
559       mAutoWrapTypeComboBox->hide();
560       mFontMultiLineLabel->hide();
561       mFontMultiLineAlignComboBox->hide();
562       mFontMultiLineAlignDDBtn->hide();
563 
564       mTextOrientationComboBox->removeItem( mTextOrientationComboBox->findData( QgsTextFormat::RotationBasedOrientation ) );
565       break;
566     }
567   }
568 }
569 
toggleDDButtons(bool visible)570 void QgsTextFormatWidget::toggleDDButtons( bool visible )
571 {
572   const auto buttons = findChildren< QgsPropertyOverrideButton * >();
573   for ( QgsPropertyOverrideButton *button : buttons )
574   {
575     button->setVisible( visible );
576   }
577 }
578 
setDockMode(bool enabled)579 void QgsTextFormatWidget::setDockMode( bool enabled )
580 {
581   mOptionsTab->setVisible( enabled );
582   mOptionsTab->setTabToolTip( 0, tr( "Text" ) );
583   mOptionsTab->setTabToolTip( 1, tr( "Formatting" ) );
584   mOptionsTab->setTabToolTip( 2, tr( "Buffer" ) );
585   mOptionsTab->setTabToolTip( 3, tr( "Mask" ) );
586   mOptionsTab->setTabToolTip( 4, tr( "Background" ) );
587   mOptionsTab->setTabToolTip( 5, tr( "Shadow" ) );
588   mOptionsTab->setTabToolTip( 6, tr( "Callouts" ) );
589   mOptionsTab->setTabToolTip( 7, tr( "Placement" ) );
590   mOptionsTab->setTabToolTip( 8, tr( "Rendering" ) );
591 
592   mLabelingOptionsListFrame->setVisible( !enabled );
593   groupBox_mPreview->setVisible( !enabled );
594   mDockMode = enabled;
595 }
596 
connectValueChanged(const QList<QWidget * > & widgets,const char * slot)597 void QgsTextFormatWidget::connectValueChanged( const QList<QWidget *> &widgets, const char *slot )
598 {
599   const auto constWidgets = widgets;
600   for ( QWidget *widget : constWidgets )
601   {
602     if ( QgsSymbolButton *w = qobject_cast<QgsSymbolButton *>( widget ) )
603     {
604       connect( w, SIGNAL( changed() ), this, slot );
605     }
606     else if ( QgsFieldExpressionWidget *w = qobject_cast< QgsFieldExpressionWidget *>( widget ) )
607     {
608       connect( w, SIGNAL( fieldChanged( QString ) ), this,  slot );
609     }
610     else if ( QgsOpacityWidget *w = qobject_cast< QgsOpacityWidget *>( widget ) )
611     {
612       connect( w, SIGNAL( opacityChanged( double ) ), this,  slot );
613     }
614     else if ( QgsScaleWidget *w = qobject_cast< QgsScaleWidget *>( widget ) )
615     {
616       connect( w, SIGNAL( scaleChanged( double ) ), this,  slot );
617     }
618     else if ( QgsUnitSelectionWidget *w = qobject_cast<QgsUnitSelectionWidget *>( widget ) )
619     {
620       connect( w, SIGNAL( changed() ), this,  slot );
621     }
622     else if ( QComboBox *w = qobject_cast<QComboBox *>( widget ) )
623     {
624       connect( w, SIGNAL( currentIndexChanged( int ) ), this, slot );
625     }
626     else if ( QSpinBox *w = qobject_cast<QSpinBox *>( widget ) )
627     {
628       connect( w, SIGNAL( valueChanged( int ) ), this, slot );
629     }
630     else if ( QDoubleSpinBox *w = qobject_cast<QDoubleSpinBox *>( widget ) )
631     {
632       connect( w, SIGNAL( valueChanged( double ) ), this, slot );
633     }
634     else if ( QgsColorButton *w = qobject_cast<QgsColorButton *>( widget ) )
635     {
636       connect( w, SIGNAL( colorChanged( QColor ) ), this, slot );
637     }
638     else if ( QCheckBox *w = qobject_cast<QCheckBox *>( widget ) )
639     {
640       connect( w, SIGNAL( toggled( bool ) ), this, slot );
641     }
642     else if ( QRadioButton *w = qobject_cast<QRadioButton *>( widget ) )
643     {
644       connect( w, SIGNAL( toggled( bool ) ), this, slot );
645     }
646     else if ( QLineEdit *w = qobject_cast<QLineEdit *>( widget ) )
647     {
648       connect( w, SIGNAL( textEdited( QString ) ), this, slot );
649     }
650     else if ( QSlider *w = qobject_cast<QSlider *>( widget ) )
651     {
652       connect( w, SIGNAL( valueChanged( int ) ), this, slot );
653     }
654     else if ( QGroupBox *w = qobject_cast<QGroupBox *>( widget ) )
655     {
656       connect( w, SIGNAL( toggled( bool ) ), this, slot );
657     }
658     else if ( QgsCodeEditorExpression *w = qobject_cast<QgsCodeEditorExpression *>( widget ) )
659     {
660       connect( w, SIGNAL( textChanged() ), this, slot );
661     }
662     else
663     {
664       QgsLogger::warning( QStringLiteral( "Could not create connection for widget %1" ).arg( widget->objectName() ) );
665     }
666   }
667 }
668 
populateDataDefinedButtons()669 void QgsTextFormatWidget::populateDataDefinedButtons()
670 {
671   // text style
672   registerDataDefinedButton( mFontDDBtn, QgsPalLayerSettings::Family );
673   registerDataDefinedButton( mFontStyleDDBtn, QgsPalLayerSettings::FontStyle );
674   registerDataDefinedButton( mFontUnderlineDDBtn, QgsPalLayerSettings::Underline );
675   registerDataDefinedButton( mFontStrikeoutDDBtn, QgsPalLayerSettings::Strikeout );
676   registerDataDefinedButton( mFontBoldDDBtn, QgsPalLayerSettings::Bold );
677   registerDataDefinedButton( mFontItalicDDBtn, QgsPalLayerSettings::Italic );
678   registerDataDefinedButton( mFontSizeDDBtn, QgsPalLayerSettings::Size );
679   registerDataDefinedButton( mFontUnitsDDBtn, QgsPalLayerSettings::FontSizeUnit );
680   registerDataDefinedButton( mFontColorDDBtn, QgsPalLayerSettings::Color );
681   registerDataDefinedButton( mFontOpacityDDBtn, QgsPalLayerSettings::FontOpacity );
682   registerDataDefinedButton( mFontCaseDDBtn, QgsPalLayerSettings::FontCase );
683   registerDataDefinedButton( mFontLetterSpacingDDBtn, QgsPalLayerSettings::FontLetterSpacing );
684   registerDataDefinedButton( mFontWordSpacingDDBtn, QgsPalLayerSettings::FontWordSpacing );
685   registerDataDefinedButton( mFontBlendModeDDBtn, QgsPalLayerSettings::FontBlendMode );
686 
687   // text formatting
688   registerDataDefinedButton( mWrapCharDDBtn, QgsPalLayerSettings::MultiLineWrapChar );
689   registerDataDefinedButton( mAutoWrapLengthDDBtn, QgsPalLayerSettings::AutoWrapLength );
690   registerDataDefinedButton( mFontLineHeightDDBtn, QgsPalLayerSettings::MultiLineHeight );
691   registerDataDefinedButton( mFontMultiLineAlignDDBtn, QgsPalLayerSettings::MultiLineAlignment );
692   registerDataDefinedButton( mTextOrientationDDBtn, QgsPalLayerSettings::TextOrientation );
693 
694   registerDataDefinedButton( mDirectSymbDDBtn, QgsPalLayerSettings::DirSymbDraw );
695   mDirectSymbDDBtn->registerCheckedWidget( mDirectSymbChkBx );
696   registerDataDefinedButton( mDirectSymbLeftDDBtn, QgsPalLayerSettings::DirSymbLeft );
697   registerDataDefinedButton( mDirectSymbRightDDBtn, QgsPalLayerSettings::DirSymbRight );
698 
699   registerDataDefinedButton( mDirectSymbPlacementDDBtn, QgsPalLayerSettings::DirSymbPlacement );
700   registerDataDefinedButton( mDirectSymbRevDDBtn, QgsPalLayerSettings::DirSymbReverse );
701 
702   registerDataDefinedButton( mFormatNumDDBtn, QgsPalLayerSettings::NumFormat );
703   mFormatNumDDBtn->registerCheckedWidget( mFormatNumChkBx );
704   registerDataDefinedButton( mFormatNumDecimalsDDBtn, QgsPalLayerSettings::NumDecimals );
705   registerDataDefinedButton( mFormatNumPlusSignDDBtn, QgsPalLayerSettings::NumPlusSign );
706 
707   // text buffer
708   registerDataDefinedButton( mBufferDrawDDBtn, QgsPalLayerSettings::BufferDraw );
709   registerDataDefinedButton( mBufferSizeDDBtn, QgsPalLayerSettings::BufferSize );
710   registerDataDefinedButton( mBufferUnitsDDBtn, QgsPalLayerSettings::BufferUnit );
711   registerDataDefinedButton( mBufferColorDDBtn, QgsPalLayerSettings::BufferColor );
712   registerDataDefinedButton( mBufferOpacityDDBtn, QgsPalLayerSettings::BufferOpacity );
713   registerDataDefinedButton( mBufferJoinStyleDDBtn, QgsPalLayerSettings::BufferJoinStyle );
714   registerDataDefinedButton( mBufferBlendModeDDBtn, QgsPalLayerSettings::BufferBlendMode );
715 
716   // mask
717   registerDataDefinedButton( mEnableMaskDDBtn, QgsPalLayerSettings::MaskEnabled );
718   mEnableMaskDDBtn->registerCheckedWidget( mEnableMaskChkBx );
719   registerDataDefinedButton( mMaskBufferSizeDDBtn, QgsPalLayerSettings::MaskBufferSize );
720   registerDataDefinedButton( mMaskBufferUnitsDDBtn, QgsPalLayerSettings::MaskBufferUnit );
721   registerDataDefinedButton( mMaskOpacityDDBtn, QgsPalLayerSettings::MaskOpacity );
722   registerDataDefinedButton( mMaskJoinStyleDDBtn, QgsPalLayerSettings::MaskJoinStyle );
723 
724   // background
725   registerDataDefinedButton( mShapeDrawDDBtn, QgsPalLayerSettings::ShapeDraw );
726   mShapeDrawDDBtn->registerCheckedWidget( mShapeDrawChkBx );
727   registerDataDefinedButton( mShapeTypeDDBtn, QgsPalLayerSettings::ShapeKind );
728   registerDataDefinedButton( mShapeSVGPathDDBtn, QgsPalLayerSettings::ShapeSVGFile );
729   registerDataDefinedButton( mShapeSizeTypeDDBtn, QgsPalLayerSettings::ShapeSizeType );
730   registerDataDefinedButton( mShapeSizeXDDBtn, QgsPalLayerSettings::ShapeSizeX );
731   registerDataDefinedButton( mShapeSizeYDDBtn, QgsPalLayerSettings::ShapeSizeY );
732   registerDataDefinedButton( mShapeSizeUnitsDDBtn, QgsPalLayerSettings::ShapeSizeUnits );
733   registerDataDefinedButton( mShapeRotationTypeDDBtn, QgsPalLayerSettings::ShapeRotationType );
734   registerDataDefinedButton( mShapeRotationDDBtn, QgsPalLayerSettings::ShapeRotation );
735   registerDataDefinedButton( mShapeOffsetDDBtn, QgsPalLayerSettings::ShapeOffset );
736   registerDataDefinedButton( mShapeOffsetUnitsDDBtn, QgsPalLayerSettings::ShapeOffsetUnits );
737   registerDataDefinedButton( mShapeRadiusDDBtn, QgsPalLayerSettings::ShapeRadii );
738   registerDataDefinedButton( mShapeRadiusUnitsDDBtn, QgsPalLayerSettings::ShapeRadiiUnits );
739   registerDataDefinedButton( mShapeOpacityDDBtn, QgsPalLayerSettings::ShapeOpacity );
740   registerDataDefinedButton( mShapeBlendModeDDBtn, QgsPalLayerSettings::ShapeBlendMode );
741   registerDataDefinedButton( mShapeFillColorDDBtn, QgsPalLayerSettings::ShapeFillColor );
742   registerDataDefinedButton( mShapeStrokeColorDDBtn, QgsPalLayerSettings::ShapeStrokeColor );
743   registerDataDefinedButton( mShapeStrokeWidthDDBtn, QgsPalLayerSettings::ShapeStrokeWidth );
744   registerDataDefinedButton( mShapeStrokeUnitsDDBtn, QgsPalLayerSettings::ShapeStrokeWidthUnits );
745   registerDataDefinedButton( mShapePenStyleDDBtn, QgsPalLayerSettings::ShapeJoinStyle );
746 
747   // drop shadows
748   registerDataDefinedButton( mShadowDrawDDBtn, QgsPalLayerSettings::ShadowDraw );
749   mShadowDrawDDBtn->registerCheckedWidget( mShadowDrawChkBx );
750   registerDataDefinedButton( mShadowUnderDDBtn, QgsPalLayerSettings::ShadowUnder );
751   registerDataDefinedButton( mShadowOffsetAngleDDBtn, QgsPalLayerSettings::ShadowOffsetAngle );
752   registerDataDefinedButton( mShadowOffsetDDBtn, QgsPalLayerSettings::ShadowOffsetDist );
753   registerDataDefinedButton( mShadowOffsetUnitsDDBtn, QgsPalLayerSettings::ShadowOffsetUnits );
754   registerDataDefinedButton( mShadowRadiusDDBtn, QgsPalLayerSettings::ShadowRadius );
755   registerDataDefinedButton( mShadowRadiusUnitsDDBtn, QgsPalLayerSettings::ShadowRadiusUnits );
756   registerDataDefinedButton( mShadowOpacityDDBtn, QgsPalLayerSettings::ShadowOpacity );
757   registerDataDefinedButton( mShadowScaleDDBtn, QgsPalLayerSettings::ShadowScale );
758   registerDataDefinedButton( mShadowColorDDBtn, QgsPalLayerSettings::ShadowColor );
759   registerDataDefinedButton( mShadowBlendDDBtn, QgsPalLayerSettings::ShadowBlendMode );
760 
761   // placement
762   registerDataDefinedButton( mCentroidDDBtn, QgsPalLayerSettings::CentroidWhole );
763   registerDataDefinedButton( mPointQuadOffsetDDBtn, QgsPalLayerSettings::OffsetQuad );
764   registerDataDefinedButton( mPointPositionOrderDDBtn, QgsPalLayerSettings::PredefinedPositionOrder );
765   registerDataDefinedButton( mLinePlacementFlagsDDBtn, QgsPalLayerSettings::LinePlacementOptions );
766   registerDataDefinedButton( mPointOffsetDDBtn, QgsPalLayerSettings::OffsetXY );
767   registerDataDefinedButton( mPointOffsetUnitsDDBtn, QgsPalLayerSettings::OffsetUnits );
768   registerDataDefinedButton( mLineDistanceDDBtn, QgsPalLayerSettings::LabelDistance );
769   registerDataDefinedButton( mLineDistanceUnitDDBtn, QgsPalLayerSettings::DistanceUnits );
770   registerDataDefinedButton( mPriorityDDBtn, QgsPalLayerSettings::Priority );
771   registerDataDefinedButton( mAllowOutsidePolygonsDDBtn, QgsPalLayerSettings::PolygonLabelOutside );
772 
773   // TODO: is this necessary? maybe just use the data defined-only rotation?
774   //mPointAngleDDBtn, QgsPalLayerSettings::OffsetRotation,
775   //                        QgsPropertyOverrideButton::AnyType, QgsPropertyOverrideButton::double180RotDesc() );
776   registerDataDefinedButton( mMaxCharAngleDDBtn, QgsPalLayerSettings::CurvedCharAngleInOut );
777   registerDataDefinedButton( mRepeatDistanceDDBtn, QgsPalLayerSettings::RepeatDistance );
778   registerDataDefinedButton( mRepeatDistanceUnitDDBtn, QgsPalLayerSettings::RepeatDistanceUnit );
779   registerDataDefinedButton( mOverrunDistanceDDBtn, QgsPalLayerSettings::OverrunDistance );
780 
781   // data defined-only
782   registerDataDefinedButton( mCoordXDDBtn, QgsPalLayerSettings::PositionX );
783   registerDataDefinedButton( mCoordYDDBtn, QgsPalLayerSettings::PositionY );
784   registerDataDefinedButton( mCoordAlignmentHDDBtn, QgsPalLayerSettings::Hali );
785   registerDataDefinedButton( mCoordAlignmentVDDBtn, QgsPalLayerSettings::Vali );
786   registerDataDefinedButton( mCoordRotationDDBtn, QgsPalLayerSettings::LabelRotation );
787 
788   // rendering
789   QString ddScaleVisInfo = tr( "Value &lt; 0 represents a scale closer than 1:1, e.g. -10 = 10:1<br>"
790                                "Value of 0 disables the specific limit." );
791   registerDataDefinedButton( mScaleBasedVisibilityDDBtn, QgsPalLayerSettings::ScaleVisibility );
792   mScaleBasedVisibilityDDBtn->registerCheckedWidget( mScaleBasedVisibilityChkBx );
793   registerDataDefinedButton( mScaleBasedVisibilityMinDDBtn, QgsPalLayerSettings::MinimumScale );
794   mScaleBasedVisibilityMinDDBtn->setUsageInfo( ddScaleVisInfo );
795   registerDataDefinedButton( mScaleBasedVisibilityMaxDDBtn, QgsPalLayerSettings::MaximumScale );
796   mScaleBasedVisibilityMaxDDBtn->setUsageInfo( ddScaleVisInfo );
797 
798   registerDataDefinedButton( mFontLimitPixelDDBtn, QgsPalLayerSettings::FontLimitPixel );
799   mFontLimitPixelDDBtn->registerCheckedWidget( mFontLimitPixelChkBox );
800   registerDataDefinedButton( mFontMinPixelDDBtn, QgsPalLayerSettings::FontMinPixel );
801   registerDataDefinedButton( mFontMaxPixelDDBtn, QgsPalLayerSettings::FontMaxPixel );
802 
803   registerDataDefinedButton( mShowLabelDDBtn, QgsPalLayerSettings::Show );
804 
805   registerDataDefinedButton( mAlwaysShowDDBtn, QgsPalLayerSettings::AlwaysShow );
806 
807   registerDataDefinedButton( mIsObstacleDDBtn, QgsPalLayerSettings::IsObstacle );
808   registerDataDefinedButton( mZIndexDDBtn, QgsPalLayerSettings::ZIndex );
809 
810   registerDataDefinedButton( mCalloutDrawDDBtn, QgsPalLayerSettings::CalloutDraw );
811   mCalloutDrawDDBtn->registerCheckedWidget( mCalloutsDrawCheckBox );
812 
813   registerDataDefinedButton( mLabelAllPartsDDBtn, QgsPalLayerSettings::LabelAllParts );
814 }
815 
registerDataDefinedButton(QgsPropertyOverrideButton * button,QgsPalLayerSettings::Property key)816 void QgsTextFormatWidget::registerDataDefinedButton( QgsPropertyOverrideButton *button, QgsPalLayerSettings::Property key )
817 {
818   button->init( key, mDataDefinedProperties, QgsPalLayerSettings::propertyDefinitions(), mLayer, true );
819   if ( !mButtons.contains( key ) )
820   {
821     connect( button, &QgsPropertyOverrideButton::changed, this, &QgsTextFormatWidget::updateProperty );
822     connect( button, &QgsPropertyOverrideButton::createAuxiliaryField, this, &QgsTextFormatWidget::createAuxiliaryField );
823     button->registerExpressionContextGenerator( this );
824     mButtons[key] = button;
825   }
826 }
827 
updateWidgetForFormat(const QgsTextFormat & format)828 void QgsTextFormatWidget::updateWidgetForFormat( const QgsTextFormat &format )
829 {
830   QgsTextBufferSettings buffer = format.buffer();
831   QgsTextMaskSettings mask = format.mask();
832   QgsTextBackgroundSettings background = format.background();
833   QgsTextShadowSettings shadow = format.shadow();
834 
835   if ( mWidgetMode != Labeling )
836   {
837     mDataDefinedProperties = format.dataDefinedProperties();
838   }
839 
840   // buffer
841   mBufferDrawChkBx->setChecked( buffer.enabled() );
842   mBufferFrame->setEnabled( buffer.enabled() );
843   spinBufferSize->setValue( buffer.size() );
844   mBufferUnitWidget->setUnit( buffer.sizeUnit() );
845   mBufferUnitWidget->setMapUnitScale( buffer.sizeMapUnitScale() );
846   btnBufferColor->setColor( buffer.color() );
847   mBufferOpacityWidget->setOpacity( buffer.opacity() );
848   mBufferJoinStyleComboBox->setPenJoinStyle( buffer.joinStyle() );
849   mBufferTranspFillChbx->setChecked( buffer.fillBufferInterior() );
850   comboBufferBlendMode->setBlendMode( buffer.blendMode() );
851   if ( auto *lPaintEffect = buffer.paintEffect() )
852     mBufferEffect.reset( lPaintEffect->clone() );
853   else
854   {
855     mBufferEffect.reset( QgsPaintEffectRegistry::defaultStack() );
856     mBufferEffect->setEnabled( false );
857   }
858   mBufferEffectWidget->setPaintEffect( mBufferEffect.get() );
859 
860   // mask
861   mMaskedSymbolLayers = mask.maskedSymbolLayers();
862   mEnableMaskChkBx->setChecked( mask.enabled() );
863   mMaskBufferSizeSpinBox->setValue( mask.size() );
864   mMaskBufferUnitWidget->setUnit( mask.sizeUnit() );
865   mMaskBufferUnitWidget->setMapUnitScale( mask.sizeMapUnitScale() );
866   mMaskOpacityWidget->setOpacity( mask.opacity() );
867   mMaskJoinStyleComboBox->setPenJoinStyle( mask.joinStyle() );
868   if ( auto *lPaintEffect = mask.paintEffect() )
869     mMaskEffect.reset( lPaintEffect->clone() );
870   else
871   {
872     mMaskEffect.reset( QgsPaintEffectRegistry::defaultStack() );
873     mMaskEffect->setEnabled( false );
874   }
875   mMaskEffectWidget->setPaintEffect( mMaskEffect.get() );
876 
877   mFontSizeUnitWidget->setUnit( format.sizeUnit() );
878   mFontSizeUnitWidget->setMapUnitScale( format.sizeMapUnitScale() );
879   mRefFont = format.font();
880   mFontSizeSpinBox->setValue( format.size() );
881   btnTextColor->setColor( format.color() );
882   mTextOpacityWidget->setOpacity( format.opacity() );
883   comboBlendMode->setBlendMode( format.blendMode() );
884   mTextOrientationComboBox->setCurrentIndex( mTextOrientationComboBox->findData( format.orientation() ) );
885   mHtmlFormattingCheckBox->setChecked( format.allowHtmlFormatting() );
886 
887   mFontWordSpacingSpinBox->setValue( format.font().wordSpacing() );
888   mFontLetterSpacingSpinBox->setValue( format.font().letterSpacing() );
889   whileBlocking( mKerningCheckBox )->setChecked( format.font().kerning() );
890 
891   whileBlocking( mFontCapitalsComboBox )->setCurrentIndex( mFontCapitalsComboBox->findData( format.capitalization() ) );
892   QgsFontUtils::updateFontViaStyle( mRefFont, format.namedStyle() );
893   updateFont( mRefFont );
894 
895   // show 'font not found' if substitution has occurred (should come after updateFont())
896   mFontMissingLabel->setVisible( !format.fontFound() );
897   if ( !format.fontFound() )
898   {
899     QString missingTxt = tr( "%1 not found. Default substituted." );
900     QString txtPrepend = tr( "Chosen font" );
901     if ( !format.resolvedFontFamily().isEmpty() )
902     {
903       txtPrepend = QStringLiteral( "'%1'" ).arg( format.resolvedFontFamily() );
904     }
905     mFontMissingLabel->setText( missingTxt.arg( txtPrepend ) );
906 
907     // ensure user is sent to 'Text style' section to see notice
908     mLabelingOptionsListWidget->setCurrentRow( 0 );
909     whileBlocking( mOptionsTab )->setCurrentIndex( 0 );
910   }
911   mFontLineHeightSpinBox->setValue( format.lineHeight() );
912 
913   // shape background
914   mShapeDrawChkBx->setChecked( background.enabled() );
915   mShapeFrame->setEnabled( background.enabled() );
916   mShapeTypeCmbBx->blockSignals( true );
917   mShapeTypeCmbBx->setCurrentIndex( mShapeTypeCmbBx->findData( background.type() ) );
918   mShapeTypeCmbBx->blockSignals( false );
919   updateAvailableShadowPositions();
920   mShapeSVGPathLineEdit->setText( background.svgFile() );
921 
922   mShapeSizeCmbBx->setCurrentIndex( background.sizeType() );
923   mShapeSizeXSpnBx->setValue( background.size().width() );
924   mShapeSizeYSpnBx->setValue( background.size().height() );
925   mShapeSizeUnitWidget->setUnit( background.sizeUnit() );
926   mShapeSizeUnitWidget->setMapUnitScale( background.sizeMapUnitScale() );
927   mShapeRotationCmbBx->setCurrentIndex( background.rotationType() );
928   mShapeRotationDblSpnBx->setEnabled( background.rotationType() != QgsTextBackgroundSettings::RotationSync );
929   mShapeRotationDDBtn->setEnabled( background.rotationType() != QgsTextBackgroundSettings::RotationSync );
930   mShapeRotationDblSpnBx->setValue( background.rotation() );
931   mShapeOffsetXSpnBx->setValue( background.offset().x() );
932   mShapeOffsetYSpnBx->setValue( background.offset().y() );
933   mShapeOffsetUnitWidget->setUnit( background.offsetUnit() );
934   mShapeOffsetUnitWidget->setMapUnitScale( background.offsetMapUnitScale() );
935   mShapeRadiusXDbSpnBx->setValue( background.radii().width() );
936   mShapeRadiusYDbSpnBx->setValue( background.radii().height() );
937   mShapeRadiusUnitWidget->setUnit( background.radiiUnit() );
938   mShapeRadiusUnitWidget->setMapUnitScale( background.radiiMapUnitScale() );
939 
940   mShapeFillColorBtn->setColor( background.fillColor() );
941   mShapeStrokeColorBtn->setColor( background.strokeColor() );
942   mShapeStrokeWidthSpnBx->setValue( background.strokeWidth() );
943   mShapeStrokeWidthUnitWidget->setUnit( background.strokeWidthUnit() );
944   mShapeStrokeWidthUnitWidget->setMapUnitScale( background.strokeWidthMapUnitScale() );
945   mShapePenStyleCmbBx->setPenJoinStyle( background.joinStyle() );
946 
947   mBackgroundOpacityWidget->setOpacity( background.opacity() );
948   mShapeBlendCmbBx->setBlendMode( background.blendMode() );
949 
950   mLoadSvgParams = false;
951   mShapeTypeCmbBx_currentIndexChanged( background.type() ); // force update of shape background gui
952 
953   if ( auto *lPaintEffect = background.paintEffect() )
954     mBackgroundEffect.reset( lPaintEffect->clone() );
955   else
956   {
957     mBackgroundEffect.reset( QgsPaintEffectRegistry::defaultStack() );
958     mBackgroundEffect->setEnabled( false );
959   }
960   mBackgroundEffectWidget->setPaintEffect( mBackgroundEffect.get() );
961 
962   mBackgroundSymbolButton->setSymbol( background.markerSymbol() ? background.markerSymbol()->clone() : QgsSymbol::defaultSymbol( QgsWkbTypes::PointGeometry ) );
963 
964   // drop shadow
965   mShadowDrawChkBx->setChecked( shadow.enabled() );
966   mShadowFrame->setEnabled( shadow.enabled() );
967   mShadowUnderCmbBx->setCurrentIndex( mShadowUnderCmbBx->findData( shadow.shadowPlacement() ) );
968   mShadowOffsetAngleSpnBx->setValue( shadow.offsetAngle() );
969   mShadowOffsetSpnBx->setValue( shadow.offsetDistance() );
970   mShadowOffsetUnitWidget->setUnit( shadow.offsetUnit() );
971   mShadowOffsetUnitWidget->setMapUnitScale( shadow.offsetMapUnitScale() );
972   mShadowOffsetGlobalChkBx->setChecked( shadow.offsetGlobal() );
973 
974   mShadowRadiusDblSpnBx->setValue( shadow.blurRadius() );
975   mShadowRadiusUnitWidget->setUnit( shadow.blurRadiusUnit() );
976   mShadowRadiusUnitWidget->setMapUnitScale( shadow.blurRadiusMapUnitScale() );
977   mShadowRadiusAlphaChkBx->setChecked( shadow.blurAlphaOnly() );
978   mShadowOpacityWidget->setOpacity( shadow.opacity() );
979   mShadowScaleSpnBx->setValue( shadow.scale() );
980 
981   mShadowColorBtn->setColor( shadow.color() );
982   mShadowBlendCmbBx->setBlendMode( shadow.blendMode() );
983 
984   mPreviewBackgroundBtn->setColor( format.previewBackgroundColor() );
985   mPreviewBackgroundBtn->setDefaultColor( format.previewBackgroundColor() );
986   setPreviewBackground( format.previewBackgroundColor() );
987 
988   populateDataDefinedButtons();
989 }
990 
~QgsTextFormatWidget()991 QgsTextFormatWidget::~QgsTextFormatWidget()
992 {
993   QgsSettings settings;
994   settings.setValue( QStringLiteral( "Windows/Labeling/FontPreviewSplitState" ), mFontPreviewSplitter->saveState() );
995   settings.setValue( QStringLiteral( "Windows/Labeling/OptionsSplitState" ), mLabelingOptionsSplitter->saveState() );
996 
997   int prevIndex = mLabelingOptionsListWidget->currentRow();
998   if ( mWidgetMode == Text )
999   {
1000     switch ( prevIndex )
1001     {
1002       case 3: // background - account for removed mask tab
1003       case 4: // shadow - account for removed mask tab
1004         prevIndex++;
1005         break;
1006     }
1007   }
1008 
1009   settings.setValue( QStringLiteral( "Windows/Labeling/Tab" ), prevIndex );
1010 }
1011 
format(bool includeDataDefinedProperties) const1012 QgsTextFormat QgsTextFormatWidget::format( bool includeDataDefinedProperties ) const
1013 {
1014   QgsTextFormat format;
1015   format.setColor( btnTextColor->color() );
1016   format.setFont( mRefFont );
1017   format.setSize( mFontSizeSpinBox->value() );
1018   format.setNamedStyle( mFontStyleComboBox->currentText() );
1019   format.setOpacity( mTextOpacityWidget->opacity() );
1020   format.setBlendMode( comboBlendMode->blendMode() );
1021   format.setSizeUnit( mFontSizeUnitWidget->unit() );
1022   format.setSizeMapUnitScale( mFontSizeUnitWidget->getMapUnitScale() );
1023   format.setLineHeight( mFontLineHeightSpinBox->value() );
1024   format.setPreviewBackgroundColor( mPreviewBackgroundColor );
1025   format.setOrientation( static_cast< QgsTextFormat::TextOrientation >( mTextOrientationComboBox->currentData().toInt() ) );
1026   format.setAllowHtmlFormatting( mHtmlFormattingCheckBox->isChecked( ) );
1027   format.setCapitalization( static_cast< QgsStringUtils::Capitalization >( mFontCapitalsComboBox->currentData().toInt() ) );
1028 
1029   // buffer
1030   QgsTextBufferSettings buffer;
1031   buffer.setEnabled( mBufferDrawChkBx->isChecked() );
1032   buffer.setSize( spinBufferSize->value() );
1033   buffer.setColor( btnBufferColor->color() );
1034   buffer.setOpacity( mBufferOpacityWidget->opacity() );
1035   buffer.setSizeUnit( mBufferUnitWidget->unit() );
1036   buffer.setSizeMapUnitScale( mBufferUnitWidget->getMapUnitScale() );
1037   buffer.setJoinStyle( mBufferJoinStyleComboBox->penJoinStyle() );
1038   buffer.setFillBufferInterior( mBufferTranspFillChbx->isChecked() );
1039   buffer.setBlendMode( comboBufferBlendMode->blendMode() );
1040   if ( mBufferEffect && ( !QgsPaintEffectRegistry::isDefaultStack( mBufferEffect.get() ) || mBufferEffect->enabled() ) )
1041     buffer.setPaintEffect( mBufferEffect->clone() );
1042   else
1043     buffer.setPaintEffect( nullptr );
1044   format.setBuffer( buffer );
1045 
1046   // mask
1047   QgsTextMaskSettings mask;
1048   mask.setEnabled( mEnableMaskChkBx->isChecked() );
1049   mask.setSize( mMaskBufferSizeSpinBox->value() );
1050   mask.setOpacity( mMaskOpacityWidget->opacity() );
1051   mask.setSizeUnit( mMaskBufferUnitWidget->unit() );
1052   mask.setSizeMapUnitScale( mMaskBufferUnitWidget->getMapUnitScale() );
1053   mask.setJoinStyle( mMaskJoinStyleComboBox->penJoinStyle() );
1054   if ( mMaskEffect && ( !QgsPaintEffectRegistry::isDefaultStack( mMaskEffect.get() ) || mMaskEffect->enabled() ) )
1055     mask.setPaintEffect( mMaskEffect->clone() );
1056   else
1057     mask.setPaintEffect( nullptr );
1058   mask.setMaskedSymbolLayers( mMaskedSymbolLayers );
1059   format.setMask( mask );
1060 
1061   // shape background
1062   QgsTextBackgroundSettings background;
1063   background.setEnabled( mShapeDrawChkBx->isChecked() );
1064   background.setType( static_cast< QgsTextBackgroundSettings::ShapeType >( mShapeTypeCmbBx->currentData().toInt() ) );
1065   background.setSvgFile( mShapeSVGPathLineEdit->text() );
1066   background.setSizeType( static_cast< QgsTextBackgroundSettings::SizeType >( mShapeSizeCmbBx->currentIndex() ) );
1067   background.setSize( QSizeF( mShapeSizeXSpnBx->value(), mShapeSizeYSpnBx->value() ) );
1068   background.setSizeUnit( mShapeSizeUnitWidget->unit() );
1069   background.setSizeMapUnitScale( mShapeSizeUnitWidget->getMapUnitScale() );
1070   background.setRotationType( static_cast< QgsTextBackgroundSettings::RotationType >( mShapeRotationCmbBx->currentIndex() ) );
1071   background.setRotation( mShapeRotationDblSpnBx->value() );
1072   background.setOffset( QPointF( mShapeOffsetXSpnBx->value(), mShapeOffsetYSpnBx->value() ) );
1073   background.setOffsetUnit( mShapeOffsetUnitWidget->unit() );
1074   background.setOffsetMapUnitScale( mShapeOffsetUnitWidget->getMapUnitScale() );
1075   background.setRadii( QSizeF( mShapeRadiusXDbSpnBx->value(), mShapeRadiusYDbSpnBx->value() ) );
1076   background.setRadiiUnit( mShapeRadiusUnitWidget->unit() );
1077   background.setRadiiMapUnitScale( mShapeRadiusUnitWidget->getMapUnitScale() );
1078 
1079   background.setFillColor( mShapeFillColorBtn->color() );
1080   background.setStrokeColor( mShapeStrokeColorBtn->color() );
1081   background.setStrokeWidth( mShapeStrokeWidthSpnBx->value() );
1082   background.setStrokeWidthUnit( mShapeStrokeWidthUnitWidget->unit() );
1083   background.setStrokeWidthMapUnitScale( mShapeStrokeWidthUnitWidget->getMapUnitScale() );
1084   background.setJoinStyle( mShapePenStyleCmbBx->penJoinStyle() );
1085   background.setOpacity( mBackgroundOpacityWidget->opacity() );
1086   background.setBlendMode( mShapeBlendCmbBx->blendMode() );
1087   if ( mBackgroundEffect && ( !QgsPaintEffectRegistry::isDefaultStack( mBackgroundEffect.get() ) || mBackgroundEffect->enabled() ) )
1088     background.setPaintEffect( mBackgroundEffect->clone() );
1089   else
1090     background.setPaintEffect( nullptr );
1091   background.setMarkerSymbol( mBackgroundSymbolButton->clonedSymbol< QgsMarkerSymbol >() );
1092   format.setBackground( background );
1093 
1094   // drop shadow
1095   QgsTextShadowSettings shadow;
1096   shadow.setEnabled( mShadowDrawChkBx->isChecked() );
1097   shadow.setShadowPlacement( static_cast< QgsTextShadowSettings::ShadowPlacement >( mShadowUnderCmbBx->currentData().toInt() ) );
1098   shadow.setOffsetAngle( mShadowOffsetAngleSpnBx->value() );
1099   shadow.setOffsetDistance( mShadowOffsetSpnBx->value() );
1100   shadow.setOffsetUnit( mShadowOffsetUnitWidget->unit() );
1101   shadow.setOffsetMapUnitScale( mShadowOffsetUnitWidget->getMapUnitScale() );
1102   shadow.setOffsetGlobal( mShadowOffsetGlobalChkBx->isChecked() );
1103   shadow.setBlurRadius( mShadowRadiusDblSpnBx->value() );
1104   shadow.setBlurRadiusUnit( mShadowRadiusUnitWidget->unit() );
1105   shadow.setBlurRadiusMapUnitScale( mShadowRadiusUnitWidget->getMapUnitScale() );
1106   shadow.setBlurAlphaOnly( mShadowRadiusAlphaChkBx->isChecked() );
1107   shadow.setOpacity( mShadowOpacityWidget->opacity() );
1108   shadow.setScale( mShadowScaleSpnBx->value() );
1109   shadow.setColor( mShadowColorBtn->color() );
1110   shadow.setBlendMode( mShadowBlendCmbBx->blendMode() );
1111   format.setShadow( shadow );
1112 
1113   if ( includeDataDefinedProperties )
1114     format.setDataDefinedProperties( mDataDefinedProperties );
1115 
1116   return format;
1117 }
1118 
setFormat(const QgsTextFormat & format)1119 void QgsTextFormatWidget::setFormat( const QgsTextFormat &format )
1120 {
1121   if ( mWidgetMode != Labeling )
1122   {
1123     // we need to combine any data defined properties from the text format with existing ones from the label settings
1124     const QgsPropertyCollection formatProps = format.dataDefinedProperties();
1125     for ( int key : formatProps.propertyKeys() )
1126     {
1127       if ( formatProps.isActive( key ) )
1128       {
1129         mDataDefinedProperties.setProperty( key, formatProps.property( key ) );
1130       }
1131     }
1132   }
1133 
1134   updateWidgetForFormat( format );
1135 }
1136 
context() const1137 QgsSymbolWidgetContext QgsTextFormatWidget::context() const
1138 {
1139   return mContext;
1140 }
1141 
deactivateField(QgsPalLayerSettings::Property key)1142 void QgsTextFormatWidget::deactivateField( QgsPalLayerSettings::Property key )
1143 {
1144   if ( mButtons.contains( key ) )
1145   {
1146     QgsPropertyOverrideButton *button = mButtons[ key ];
1147     QgsProperty p = button->toProperty();
1148     p.setField( QString() );
1149     p.setActive( false );
1150     button->updateFieldLists();
1151     button->setToProperty( p );
1152     mDataDefinedProperties.setProperty( key, p );
1153   }
1154 }
1155 
optionsStackedWidget_CurrentChanged(int indx)1156 void QgsTextFormatWidget::optionsStackedWidget_CurrentChanged( int indx )
1157 {
1158   mLabelingOptionsListWidget->blockSignals( true );
1159   mLabelingOptionsListWidget->setCurrentRow( indx );
1160   mLabelingOptionsListWidget->blockSignals( false );
1161 }
1162 
setContext(const QgsSymbolWidgetContext & context)1163 void QgsTextFormatWidget::setContext( const QgsSymbolWidgetContext &context )
1164 {
1165   mContext = context;
1166 
1167   if ( auto *lExpressionContext = mContext.expressionContext() )
1168   {
1169     mPreviewExpressionContext = *lExpressionContext;
1170     if ( mLayer )
1171       mPreviewExpressionContext.appendScope( QgsExpressionContextUtils::layerScope( mLayer ) );
1172   }
1173 
1174   const auto symbolButtonWidgets = findChildren<QgsSymbolButton *>();
1175   for ( QgsSymbolButton *symbolWidget : symbolButtonWidgets )
1176   {
1177     symbolWidget->setMapCanvas( mContext.mapCanvas() );
1178     symbolWidget->setMessageBar( mContext.messageBar() );
1179   }
1180 }
1181 
collapseSample(bool collapse)1182 void QgsTextFormatWidget::collapseSample( bool collapse )
1183 {
1184   if ( collapse )
1185   {
1186     QList<int> splitSizes = mFontPreviewSplitter->sizes();
1187     if ( splitSizes[0] > groupBox_mPreview->height() )
1188     {
1189       int delta = splitSizes[0] - groupBox_mPreview->height();
1190       splitSizes[0] -= delta;
1191       splitSizes[1] += delta;
1192       mFontPreviewSplitter->setSizes( splitSizes );
1193     }
1194   }
1195 }
1196 
changeTextColor(const QColor & color)1197 void QgsTextFormatWidget::changeTextColor( const QColor &color )
1198 {
1199   Q_UNUSED( color )
1200   updatePreview();
1201 }
1202 
updateFont(const QFont & font)1203 void QgsTextFormatWidget::updateFont( const QFont &font )
1204 {
1205   // update background reference font
1206   if ( font != mRefFont )
1207   {
1208     mRefFont = font;
1209   }
1210 
1211   // test if font is actually available
1212   // NOTE: QgsFontUtils::fontMatchOnSystem may fail here, just crosscheck family
1213   mFontMissingLabel->setVisible( !QgsFontUtils::fontFamilyMatchOnSystem( mRefFont.family() ) );
1214 
1215   mDirectSymbLeftLineEdit->setFont( mRefFont );
1216   mDirectSymbRightLineEdit->setFont( mRefFont );
1217 
1218   blockFontChangeSignals( true );
1219   mFontFamilyCmbBx->setCurrentFont( mRefFont );
1220   populateFontStyleComboBox();
1221   mFontUnderlineBtn->setChecked( mRefFont.underline() );
1222   mFontStrikethroughBtn->setChecked( mRefFont.strikeOut() );
1223   mKerningCheckBox->setChecked( mRefFont.kerning() );
1224   blockFontChangeSignals( false );
1225 
1226   // update font name with font face
1227 //  font.setPixelSize( 24 );
1228 
1229   updatePreview();
1230 }
1231 
blockFontChangeSignals(bool blk)1232 void QgsTextFormatWidget::blockFontChangeSignals( bool blk )
1233 {
1234   mFontFamilyCmbBx->blockSignals( blk );
1235   mFontStyleComboBox->blockSignals( blk );
1236   mFontCapitalsComboBox->blockSignals( blk );
1237   mFontUnderlineBtn->blockSignals( blk );
1238   mFontStrikethroughBtn->blockSignals( blk );
1239   mFontWordSpacingSpinBox->blockSignals( blk );
1240   mFontLetterSpacingSpinBox->blockSignals( blk );
1241   mKerningCheckBox->blockSignals( blk );
1242 }
1243 
updatePreview()1244 void QgsTextFormatWidget::updatePreview()
1245 {
1246   // In dock mode we don't have a preview we
1247   // just let stuff know we have changed because
1248   // there might be live updates connected.
1249   if ( mDockMode )
1250   {
1251     emit widgetChanged();
1252     return;
1253   }
1254 
1255   scrollPreview();
1256   lblFontPreview->setFormat( format() );
1257 }
1258 
scrollPreview()1259 void QgsTextFormatWidget::scrollPreview()
1260 {
1261   scrollArea_mPreview->ensureVisible( 0, 0, 0, 0 );
1262 }
1263 
setPreviewBackground(const QColor & color)1264 void QgsTextFormatWidget::setPreviewBackground( const QColor &color )
1265 {
1266   mPreviewBackgroundColor = color;
1267 
1268   scrollArea_mPreview->widget()->setStyleSheet( QStringLiteral( "background: rgb(%1, %2, %3);" ).arg( QString::number( color.red() ),
1269       QString::number( color.green() ),
1270       QString::number( color.blue() ) ) );
1271 }
1272 
changeBufferColor(const QColor & color)1273 void QgsTextFormatWidget::changeBufferColor( const QColor &color )
1274 {
1275   Q_UNUSED( color )
1276   updatePreview();
1277 }
1278 
updatePlacementWidgets()1279 void QgsTextFormatWidget::updatePlacementWidgets()
1280 {
1281   const QgsWkbTypes::GeometryType currentGeometryType = labelGeometryType();
1282   bool showLineFrame = false;
1283   bool showCentroidFrame = false;
1284   bool showQuadrantFrame = false;
1285   bool showFixedQuadrantFrame = false;
1286   bool showPlacementPriorityFrame = false;
1287   bool showOffsetTypeFrame = false;
1288   bool showOffsetFrame = false;
1289   bool showDistanceFrame = false;
1290   bool showRotationFrame = false;
1291   bool showMaxCharAngleFrame = false;
1292 
1293   const QgsPalLayerSettings::Placement currentPlacement = static_cast< QgsPalLayerSettings::Placement >( mPlacementModeComboBox->currentData().toInt() );
1294   bool showPolygonPlacementOptions = ( currentGeometryType == QgsWkbTypes::PolygonGeometry && currentPlacement != QgsPalLayerSettings::Line && currentPlacement != QgsPalLayerSettings::PerimeterCurved && currentPlacement != QgsPalLayerSettings::OutsidePolygons );
1295 
1296   bool enableMultiLinesFrame = true;
1297 
1298   if ( currentPlacement == QgsPalLayerSettings::AroundPoint
1299        && ( currentGeometryType == QgsWkbTypes::PointGeometry || currentGeometryType == QgsWkbTypes::PolygonGeometry ) )
1300   {
1301     showCentroidFrame = currentGeometryType == QgsWkbTypes::PolygonGeometry;
1302     showDistanceFrame = true;
1303     //showRotationFrame = true; // TODO: uncomment when supported
1304     showQuadrantFrame = currentGeometryType == QgsWkbTypes::PointGeometry;
1305   }
1306   else if ( currentPlacement == QgsPalLayerSettings::OverPoint
1307             && ( currentGeometryType == QgsWkbTypes::PointGeometry || currentGeometryType == QgsWkbTypes::PolygonGeometry ) )
1308   {
1309     showCentroidFrame = currentGeometryType == QgsWkbTypes::PolygonGeometry;
1310     showQuadrantFrame = true;
1311     showFixedQuadrantFrame = true;
1312     showOffsetFrame = true;
1313     showRotationFrame = true;
1314   }
1315   else if ( currentGeometryType == QgsWkbTypes::PointGeometry && currentPlacement == QgsPalLayerSettings::OrderedPositionsAroundPoint )
1316   {
1317     showDistanceFrame = true;
1318     showPlacementPriorityFrame = true;
1319     showOffsetTypeFrame  = true;
1320   }
1321   else if ( ( currentGeometryType == QgsWkbTypes::LineGeometry && currentPlacement == QgsPalLayerSettings::Line )
1322             || ( currentGeometryType == QgsWkbTypes::PolygonGeometry && currentPlacement == QgsPalLayerSettings::Line )
1323             || ( currentGeometryType == QgsWkbTypes::LineGeometry && currentPlacement == QgsPalLayerSettings::Curved )
1324             || ( currentGeometryType == QgsWkbTypes::PolygonGeometry && currentPlacement == QgsPalLayerSettings::PerimeterCurved ) )
1325   {
1326     showLineFrame = true;
1327     showDistanceFrame = true;
1328     //showRotationFrame = true; // TODO: uncomment when supported
1329 
1330     bool offline = chkLineAbove->isChecked() || chkLineBelow->isChecked();
1331     chkLineOrientationDependent->setEnabled( offline );
1332     mPlacementDistanceFrame->setEnabled( offline );
1333 
1334     bool isCurved = ( currentGeometryType == QgsWkbTypes::LineGeometry && currentPlacement == QgsPalLayerSettings::Curved )
1335                     || ( currentGeometryType == QgsWkbTypes::PolygonGeometry && currentPlacement == QgsPalLayerSettings::PerimeterCurved );
1336     showMaxCharAngleFrame = isCurved;
1337     // TODO: enable mMultiLinesFrame when supported for curved labels
1338     enableMultiLinesFrame = !isCurved;
1339   }
1340   else if ( currentGeometryType == QgsWkbTypes::PolygonGeometry
1341             && ( currentPlacement == QgsPalLayerSettings::OutsidePolygons || mCheckAllowLabelsOutsidePolygons->isChecked() || mAllowOutsidePolygonsDDBtn->isActive() ) )
1342   {
1343     showDistanceFrame = true;
1344   }
1345 
1346   mPlacementLineFrame->setVisible( showLineFrame );
1347   mPlacementPolygonFrame->setVisible( showPolygonPlacementOptions );
1348   mPlacementCentroidFrame->setVisible( showCentroidFrame );
1349   mPlacementQuadrantFrame->setVisible( showQuadrantFrame );
1350   mPlacementFixedQuadrantFrame->setVisible( showFixedQuadrantFrame );
1351   mPlacementCartographicFrame->setVisible( showPlacementPriorityFrame );
1352   mPlacementOffsetFrame->setVisible( showOffsetFrame );
1353   mPlacementDistanceFrame->setVisible( showDistanceFrame );
1354   mPlacementOffsetTypeFrame->setVisible( showOffsetTypeFrame );
1355   mPlacementRotationFrame->setVisible( showRotationFrame );
1356   mPlacementRepeatGroupBox->setVisible( currentGeometryType == QgsWkbTypes::LineGeometry || ( currentGeometryType == QgsWkbTypes::PolygonGeometry &&
1357                                         ( currentPlacement == QgsPalLayerSettings::Line || currentPlacement == QgsPalLayerSettings::PerimeterCurved ) ) );
1358   mPlacementOverrunGroupBox->setVisible( currentGeometryType == QgsWkbTypes::LineGeometry && currentPlacement != QgsPalLayerSettings::Horizontal );
1359   mLineAnchorGroupBox->setVisible( currentGeometryType == QgsWkbTypes::LineGeometry );
1360   mPlacementMaxCharAngleFrame->setVisible( showMaxCharAngleFrame );
1361 
1362   mMultiLinesFrame->setEnabled( enableMultiLinesFrame );
1363 
1364 
1365   QString helperText;
1366   switch ( currentPlacement )
1367   {
1368     case QgsPalLayerSettings::AroundPoint:
1369       if ( currentGeometryType == QgsWkbTypes::PointGeometry )
1370         helperText = tr( "Arranges label candidates in a clockwise circle around the feature, preferring placements to the top-right of the feature." );
1371       else if ( currentGeometryType == QgsWkbTypes::PolygonGeometry )
1372         helperText = tr( "Arranges label candidates in a cluster around the feature's centroid, preferring placements directly over the centroid." );
1373       break;
1374     case QgsPalLayerSettings::OverPoint:
1375       if ( currentGeometryType == QgsWkbTypes::PointGeometry )
1376         helperText = tr( "Arranges label candidates directly over the feature or at a preset offset from the feature." );
1377       else if ( currentGeometryType == QgsWkbTypes::PolygonGeometry )
1378         helperText = tr( "Arranges label candidates directly over the feature's centroid, or at a preset offset from the centroid." );
1379       break;
1380     case QgsPalLayerSettings::Line:
1381       if ( currentGeometryType == QgsWkbTypes::LineGeometry )
1382         helperText = tr( "Arranges label candidates parallel to a generalised line representing the feature. Placements which fall over straighter portions of the line are preferred." );
1383       else if ( currentGeometryType == QgsWkbTypes::PolygonGeometry )
1384         helperText = tr( "Arranges label candidates parallel to a generalised line representing the polygon's perimeter. Placements which fall over straighter portions of the perimeter are preferred." );
1385       break;
1386     case QgsPalLayerSettings::Curved:
1387       if ( currentGeometryType == QgsWkbTypes::LineGeometry )
1388         helperText = tr( "Arranges candidates following the curvature of a line feature. Placements which fall over straighter portions of the line are preferred." );
1389       break;
1390     case QgsPalLayerSettings::Horizontal:
1391       if ( currentGeometryType == QgsWkbTypes::PolygonGeometry )
1392         helperText = tr( "Arranges label candidates scattered throughout the polygon. Labels will always be placed horizontally, with placements further from the edges of the polygon preferred." );
1393       else if ( currentGeometryType == QgsWkbTypes::LineGeometry )
1394         helperText = tr( "Label candidates are arranged horizontally along the length of the feature." );
1395       break;
1396     case QgsPalLayerSettings::Free:
1397       if ( currentGeometryType == QgsWkbTypes::PolygonGeometry )
1398         helperText = tr( "Arranges label candidates scattered throughout the polygon. Labels are rotated to respect the polygon's orientation, with placements further from the edges of the polygon preferred." );
1399       break;
1400     case QgsPalLayerSettings::OrderedPositionsAroundPoint:
1401       if ( currentGeometryType == QgsWkbTypes::PointGeometry )
1402         helperText = tr( "Label candidates are placed in predefined positions around the features. Preference is given to positions with greatest cartographic appeal, e.g., top right and bottom right of the feature." );
1403       break;
1404     case QgsPalLayerSettings::PerimeterCurved:
1405       if ( currentGeometryType == QgsWkbTypes::PolygonGeometry )
1406         helperText = tr( "Arranges candidates following the curvature of the feature's perimeter. Placements which fall over straighter portions of the perimeter are preferred." );
1407       break;
1408     case QgsPalLayerSettings::OutsidePolygons:
1409       if ( currentGeometryType == QgsWkbTypes::PolygonGeometry )
1410         helperText = tr( "Label candidates are placed outside of the features, preferring placements which give greatest visual association between the label and the feature." );
1411       break;
1412   }
1413   mPlacementModeDescriptionLabel->setText( QStringLiteral( "<i>%1</i>" ).arg( helperText ) );
1414 }
1415 
populateFontCapitalsComboBox()1416 void QgsTextFormatWidget::populateFontCapitalsComboBox()
1417 {
1418   mFontCapitalsComboBox->addItem( tr( "No Change" ), QgsStringUtils::MixedCase );
1419   mFontCapitalsComboBox->addItem( tr( "All Uppercase" ), QgsStringUtils::AllUppercase );
1420   mFontCapitalsComboBox->addItem( tr( "All Lowercase" ), QgsStringUtils::AllLowercase );
1421   // Small caps doesn't work right with QPainterPath::addText()
1422   // https://bugreports.qt.io/browse/QTBUG-13965
1423 //  mFontCapitalsComboBox->addItem( tr( "Small Caps" ), QVariant( 3 ) );
1424   mFontCapitalsComboBox->addItem( tr( "Title Case" ), QgsStringUtils::TitleCase );
1425   mFontCapitalsComboBox->addItem( tr( "Force First Letter to Capital" ), QgsStringUtils::ForceFirstLetterToCapital );
1426 }
1427 
populateFontStyleComboBox()1428 void QgsTextFormatWidget::populateFontStyleComboBox()
1429 {
1430   mFontStyleComboBox->clear();
1431   QStringList styles = mFontDB.styles( mRefFont.family() );
1432   const auto constStyles = styles;
1433   for ( const QString &style : constStyles )
1434   {
1435     mFontStyleComboBox->addItem( style );
1436   }
1437 
1438   QString targetStyle = mFontDB.styleString( mRefFont );
1439   if ( !styles.contains( targetStyle ) )
1440   {
1441     QFont f = QFont( mRefFont.family() );
1442     targetStyle = QFontInfo( f ).styleName();
1443     mRefFont.setStyleName( targetStyle );
1444   }
1445   int curIndx = 0;
1446   int stylIndx = mFontStyleComboBox->findText( targetStyle );
1447   if ( stylIndx > -1 )
1448   {
1449     curIndx = stylIndx;
1450   }
1451 
1452   mFontStyleComboBox->setCurrentIndex( curIndx );
1453 }
1454 
mFontSizeSpinBox_valueChanged(double d)1455 void QgsTextFormatWidget::mFontSizeSpinBox_valueChanged( double d )
1456 {
1457   mRefFont.setPointSizeF( d );
1458   updateFont( mRefFont );
1459 }
1460 
mFontFamilyCmbBx_currentFontChanged(const QFont & f)1461 void QgsTextFormatWidget::mFontFamilyCmbBx_currentFontChanged( const QFont &f )
1462 {
1463   mRefFont.setFamily( f.family() );
1464   updateFont( mRefFont );
1465 }
1466 
mFontStyleComboBox_currentIndexChanged(const QString & text)1467 void QgsTextFormatWidget::mFontStyleComboBox_currentIndexChanged( const QString &text )
1468 {
1469   QgsFontUtils::updateFontViaStyle( mRefFont, text );
1470   updateFont( mRefFont );
1471 }
1472 
mFontUnderlineBtn_toggled(bool ckd)1473 void QgsTextFormatWidget::mFontUnderlineBtn_toggled( bool ckd )
1474 {
1475   mRefFont.setUnderline( ckd );
1476   updateFont( mRefFont );
1477 }
1478 
mFontStrikethroughBtn_toggled(bool ckd)1479 void QgsTextFormatWidget::mFontStrikethroughBtn_toggled( bool ckd )
1480 {
1481   mRefFont.setStrikeOut( ckd );
1482   updateFont( mRefFont );
1483 }
1484 
kerningToggled(bool checked)1485 void QgsTextFormatWidget::kerningToggled( bool checked )
1486 {
1487   mRefFont.setKerning( checked );
1488   updateFont( mRefFont );
1489 }
1490 
mFontWordSpacingSpinBox_valueChanged(double spacing)1491 void QgsTextFormatWidget::mFontWordSpacingSpinBox_valueChanged( double spacing )
1492 {
1493   mRefFont.setWordSpacing( spacing );
1494   updateFont( mRefFont );
1495 }
1496 
mFontLetterSpacingSpinBox_valueChanged(double spacing)1497 void QgsTextFormatWidget::mFontLetterSpacingSpinBox_valueChanged( double spacing )
1498 {
1499   mRefFont.setLetterSpacing( QFont::AbsoluteSpacing, spacing );
1500   updateFont( mRefFont );
1501 }
1502 
mFontSizeUnitWidget_changed()1503 void QgsTextFormatWidget::mFontSizeUnitWidget_changed()
1504 {
1505   // disable pixel size limiting for labels defined in points
1506   if ( mFontSizeUnitWidget->unit() != QgsUnitTypes::RenderMapUnits )
1507   {
1508     mFontLimitPixelChkBox->setChecked( false );
1509   }
1510   else if ( mMinPixelLimit == 0 )
1511   {
1512     // initial minimum trigger value set, turn on pixel size limiting by default
1513     // for labels defined in map units (ignored after first settings save)
1514     mFontLimitPixelChkBox->setChecked( true );
1515   }
1516   updateFont( mRefFont );
1517 }
1518 
mFontMinPixelSpinBox_valueChanged(int px)1519 void QgsTextFormatWidget::mFontMinPixelSpinBox_valueChanged( int px )
1520 {
1521   // ensure max font pixel size for map unit labels can't be lower than min
1522   mFontMaxPixelSpinBox->setMinimum( px );
1523   mFontMaxPixelSpinBox->update();
1524 }
1525 
mFontMaxPixelSpinBox_valueChanged(int px)1526 void QgsTextFormatWidget::mFontMaxPixelSpinBox_valueChanged( int px )
1527 {
1528   // ensure max font pixel size for map unit labels can't be lower than min
1529   if ( px < mFontMinPixelSpinBox->value() )
1530   {
1531     mFontMaxPixelSpinBox->blockSignals( true );
1532     mFontMaxPixelSpinBox->setValue( mFontMinPixelSpinBox->value() );
1533     mFontMaxPixelSpinBox->blockSignals( false );
1534   }
1535   mFontMaxPixelSpinBox->setMinimum( mFontMinPixelSpinBox->value() );
1536 }
1537 
mBufferUnitWidget_changed()1538 void QgsTextFormatWidget::mBufferUnitWidget_changed()
1539 {
1540   updateFont( mRefFont );
1541 }
1542 
mMaskBufferUnitWidget_changed()1543 void QgsTextFormatWidget::mMaskBufferUnitWidget_changed()
1544 {
1545   updateFont( mRefFont );
1546 }
1547 
mCoordXDDBtn_changed()1548 void QgsTextFormatWidget::mCoordXDDBtn_changed( )
1549 {
1550   if ( !mCoordXDDBtn->isActive() ) //no data defined alignment without data defined position
1551   {
1552     enableDataDefinedAlignment( false );
1553   }
1554   else if ( mCoordYDDBtn->isActive() )
1555   {
1556     enableDataDefinedAlignment( true );
1557   }
1558 }
1559 
mCoordYDDBtn_changed()1560 void QgsTextFormatWidget::mCoordYDDBtn_changed( )
1561 {
1562   if ( !mCoordYDDBtn->isActive() ) //no data defined alignment without data defined position
1563   {
1564     enableDataDefinedAlignment( false );
1565   }
1566   else if ( mCoordXDDBtn->isActive() )
1567   {
1568     enableDataDefinedAlignment( true );
1569   }
1570 }
1571 
mShapeTypeCmbBx_currentIndexChanged(int)1572 void QgsTextFormatWidget::mShapeTypeCmbBx_currentIndexChanged( int )
1573 {
1574   // shape background
1575   QgsTextBackgroundSettings::ShapeType type = static_cast< QgsTextBackgroundSettings::ShapeType >( mShapeTypeCmbBx->currentData().toInt() );
1576   const bool isRect = type == QgsTextBackgroundSettings::ShapeRectangle || type == QgsTextBackgroundSettings::ShapeSquare;
1577   const bool isSVG = type == QgsTextBackgroundSettings::ShapeSVG;
1578   const bool isMarker = type == QgsTextBackgroundSettings::ShapeMarkerSymbol;
1579 
1580   showBackgroundPenStyle( isRect );
1581   showBackgroundRadius( isRect );
1582 
1583   mShapeSVGPathFrame->setVisible( isSVG );
1584   mBackgroundSymbolButton->setVisible( isMarker );
1585 
1586   // symbology SVG and marker renderers only support size^2 scaling,
1587   // so we only use the x size spinbox
1588   mShapeSizeYLabel->setVisible( !isSVG && !isMarker );
1589   mShapeSizeYSpnBx->setVisible( !isSVG && !isMarker );
1590   mShapeSizeYDDBtn->setVisible( !isSVG && !isMarker );
1591   mShapeSizeXLabel->setText( tr( "Size%1" ).arg( !isSVG && !isMarker ? tr( " X" ) : QString() ) );
1592 
1593   // SVG parameter setting doesn't support color's alpha component yet
1594   mShapeFillColorBtn->setAllowOpacity( !isSVG );
1595   mShapeFillColorBtn->setButtonBackground();
1596   mShapeStrokeColorBtn->setAllowOpacity( !isSVG );
1597   mShapeStrokeColorBtn->setButtonBackground();
1598 
1599   // Hide parameter widgets not used by marker symbol
1600   mShapeFillColorLabel->setVisible( !isMarker );
1601   mShapeFillColorLabel->setEnabled( !isMarker );
1602   mShapeFillColorBtn->setVisible( !isMarker );
1603   mShapeFillColorBtn->setEnabled( !isMarker );
1604   mShapeFillColorDDBtn->setVisible( !isMarker );
1605   mShapeFillColorDDBtn->setEnabled( !isMarker );
1606   mShapeStrokeColorLabel->setVisible( !isMarker );
1607   mShapeStrokeColorLabel->setEnabled( !isMarker );
1608   mShapeStrokeColorBtn->setVisible( !isMarker );
1609   mShapeStrokeColorBtn->setEnabled( !isMarker );
1610   mShapeStrokeColorDDBtn->setVisible( !isMarker );
1611   mShapeStrokeColorDDBtn->setEnabled( !isMarker );
1612   mShapeStrokeWidthLabel->setVisible( !isMarker );
1613   mShapeStrokeWidthLabel->setEnabled( !isMarker );
1614   mShapeStrokeWidthSpnBx->setVisible( !isMarker );
1615   mShapeStrokeWidthSpnBx->setEnabled( !isMarker );
1616   mShapeStrokeWidthDDBtn->setVisible( !isMarker );
1617   mShapeStrokeWidthDDBtn->setEnabled( !isMarker );
1618 
1619   // configure SVG parameter widgets
1620   mShapeSVGParamsBtn->setVisible( isSVG );
1621   if ( isSVG )
1622   {
1623     updateSvgWidgets( mShapeSVGPathLineEdit->text() );
1624   }
1625   // TODO: fix overriding SVG symbol's stroke width units in QgsSvgCache
1626   // currently broken, fall back to symbol units only
1627   mShapeStrokeWidthUnitWidget->setVisible( !isSVG && !isMarker );
1628   mShapeSVGUnitsLabel->setVisible( isSVG );
1629   mShapeStrokeUnitsDDBtn->setVisible( !isSVG && !isMarker );
1630   mShapeStrokeUnitsDDBtn->setEnabled( !isSVG && !isMarker );
1631 
1632   updateAvailableShadowPositions();
1633 }
1634 
mShapeSVGPathLineEdit_textChanged(const QString & text)1635 void QgsTextFormatWidget::mShapeSVGPathLineEdit_textChanged( const QString &text )
1636 {
1637   updateSvgWidgets( text );
1638 }
1639 
updateLinePlacementOptions()1640 void QgsTextFormatWidget::updateLinePlacementOptions()
1641 {
1642   int numOptionsChecked = ( chkLineAbove->isChecked() ? 1 : 0 ) +
1643                           ( chkLineBelow->isChecked() ? 1 : 0 ) +
1644                           ( chkLineOn->isChecked() ? 1 : 0 );
1645 
1646   if ( numOptionsChecked == 1 )
1647   {
1648     //prevent unchecking last option
1649     chkLineAbove->setEnabled( !chkLineAbove->isChecked() );
1650     chkLineBelow->setEnabled( !chkLineBelow->isChecked() );
1651     chkLineOn->setEnabled( !chkLineOn->isChecked() );
1652   }
1653   else
1654   {
1655     chkLineAbove->setEnabled( true );
1656     chkLineBelow->setEnabled( true );
1657     chkLineOn->setEnabled( true );
1658   }
1659 }
1660 
onSubstitutionsChanged(const QgsStringReplacementCollection & substitutions)1661 void QgsTextFormatWidget::onSubstitutionsChanged( const QgsStringReplacementCollection &substitutions )
1662 {
1663   mSubstitutions = substitutions;
1664   emit widgetChanged();
1665 }
1666 
previewScaleChanged(double scale)1667 void QgsTextFormatWidget::previewScaleChanged( double scale )
1668 {
1669   lblFontPreview->setScale( scale );
1670 }
1671 
updateSvgWidgets(const QString & svgPath)1672 void QgsTextFormatWidget::updateSvgWidgets( const QString &svgPath )
1673 {
1674   if ( mShapeSVGPathLineEdit->text() != svgPath )
1675   {
1676     mShapeSVGPathLineEdit->setText( svgPath );
1677   }
1678 
1679   QString resolvedPath;
1680   bool validSVG = true;
1681   if ( ! svgPath.startsWith( QLatin1String( "base64:" ), Qt::CaseInsensitive ) )
1682   {
1683     resolvedPath = QgsSymbolLayerUtils::svgSymbolNameToPath( svgPath, QgsProject::instance()->pathResolver() );
1684     validSVG = QFileInfo::exists( resolvedPath );
1685   }
1686   else
1687   {
1688     resolvedPath = svgPath;
1689     validSVG = true;
1690   }
1691 
1692   // draw red text for path field if invalid (path can't be resolved)
1693   mShapeSVGPathLineEdit->setStyleSheet( !validSVG ? QStringLiteral( "QLineEdit{ color: rgb(225, 0, 0); }" ) : QString() );
1694   mShapeSVGPathLineEdit->setToolTip( !validSVG ? tr( "File not found" ) : resolvedPath );
1695 
1696   QColor fill, stroke;
1697   double strokeWidth = 0.0;
1698   bool fillParam = false, strokeParam = false, strokeWidthParam = false;
1699   if ( validSVG )
1700   {
1701     QgsApplication::svgCache()->containsParams( resolvedPath, fillParam, fill, strokeParam, stroke, strokeWidthParam, strokeWidth );
1702   }
1703 
1704   mShapeSVGParamsBtn->setEnabled( validSVG && ( fillParam || strokeParam || strokeWidthParam ) );
1705 
1706   mShapeFillColorLabel->setEnabled( validSVG && fillParam );
1707   mShapeFillColorBtn->setEnabled( validSVG && fillParam );
1708   mShapeFillColorDDBtn->setEnabled( validSVG && fillParam );
1709   if ( mLoadSvgParams && validSVG && fillParam )
1710     mShapeFillColorBtn->setColor( fill );
1711 
1712   mShapeStrokeColorLabel->setEnabled( validSVG && strokeParam );
1713   mShapeStrokeColorBtn->setEnabled( validSVG && strokeParam );
1714   mShapeStrokeColorDDBtn->setEnabled( validSVG && strokeParam );
1715   if ( mLoadSvgParams && validSVG && strokeParam )
1716     mShapeStrokeColorBtn->setColor( stroke );
1717 
1718   mShapeStrokeWidthLabel->setEnabled( validSVG && strokeWidthParam );
1719   mShapeStrokeWidthSpnBx->setEnabled( validSVG && strokeWidthParam );
1720   mShapeStrokeWidthDDBtn->setEnabled( validSVG && strokeWidthParam );
1721   if ( mLoadSvgParams && validSVG && strokeWidthParam )
1722     mShapeStrokeWidthSpnBx->setValue( strokeWidth );
1723 
1724   // TODO: fix overriding SVG symbol's stroke width units in QgsSvgCache
1725   // currently broken, fall back to symbol's
1726   //mShapeStrokeWidthUnitWidget->setEnabled( validSVG && strokeWidthParam );
1727   //mShapeStrokeUnitsDDBtn->setEnabled( validSVG && strokeWidthParam );
1728   mShapeSVGUnitsLabel->setEnabled( validSVG && strokeWidthParam );
1729 }
1730 
updateAvailableShadowPositions()1731 void QgsTextFormatWidget::updateAvailableShadowPositions()
1732 {
1733   if ( mShadowUnderCmbBx->count() == 0
1734        || ( mShadowUnderCmbBx->findData( QgsTextShadowSettings::ShadowShape ) > -1 && mShapeTypeCmbBx->currentData().toInt() == QgsTextBackgroundSettings::ShapeMarkerSymbol )
1735        || ( mShadowUnderCmbBx->findData( QgsTextShadowSettings::ShadowShape ) == -1 && mShapeTypeCmbBx->currentData().toInt() != QgsTextBackgroundSettings::ShapeMarkerSymbol ) )
1736   {
1737     // showing invalid choices, have to rebuild the list
1738     QgsTextShadowSettings::ShadowPlacement currentPlacement = static_cast< QgsTextShadowSettings::ShadowPlacement >( mShadowUnderCmbBx->currentData().toInt() );
1739     mShadowUnderCmbBx->clear();
1740 
1741     mShadowUnderCmbBx->addItem( tr( "Lowest Label Component" ), QgsTextShadowSettings::ShadowLowest );
1742     mShadowUnderCmbBx->addItem( tr( "Text" ), QgsTextShadowSettings::ShadowText );
1743     mShadowUnderCmbBx->addItem( tr( "Buffer" ), QgsTextShadowSettings::ShadowBuffer );
1744     if ( mShapeTypeCmbBx->currentData().toInt() != QgsTextBackgroundSettings::ShapeMarkerSymbol )
1745       mShadowUnderCmbBx->addItem( tr( "Background" ), QgsTextShadowSettings::ShadowShape ); // not supported for marker symbol background shapes
1746 
1747     mShadowUnderCmbBx->setCurrentIndex( mShadowUnderCmbBx->findData( currentPlacement ) );
1748     if ( mShadowUnderCmbBx->currentIndex() == -1 )
1749       mShadowUnderCmbBx->setCurrentIndex( 0 );
1750   }
1751 }
1752 
updateProperty()1753 void QgsTextFormatWidget::updateProperty()
1754 {
1755   QgsPropertyOverrideButton *button = qobject_cast<QgsPropertyOverrideButton *>( sender() );
1756   QgsPalLayerSettings::Property key = static_cast< QgsPalLayerSettings::Property >( button->propertyKey() );
1757   mDataDefinedProperties.setProperty( key, button->toProperty() );
1758   updatePreview();
1759 }
1760 
createAuxiliaryField()1761 void QgsTextFormatWidget::createAuxiliaryField()
1762 {
1763   if ( !mLayer )
1764     return;
1765 
1766   // try to create an auxiliary layer if not yet created
1767   if ( !mLayer->auxiliaryLayer() )
1768   {
1769     QgsNewAuxiliaryLayerDialog dlg( mLayer, this );
1770     dlg.exec();
1771   }
1772 
1773   // return if still not exists
1774   if ( !mLayer->auxiliaryLayer() )
1775     return;
1776 
1777   QgsPropertyOverrideButton *button = qobject_cast<QgsPropertyOverrideButton *>( sender() );
1778   const QgsPalLayerSettings::Property key = static_cast< QgsPalLayerSettings::Property >( button->propertyKey() );
1779   const QgsPropertyDefinition def = QgsPalLayerSettings::propertyDefinitions()[key];
1780 
1781   // create property in auxiliary storage if necessary
1782   if ( !mLayer->auxiliaryLayer()->exists( def ) )
1783     mLayer->auxiliaryLayer()->addAuxiliaryField( def );
1784 
1785   // update property with join field name from auxiliary storage
1786   QgsProperty property = button->toProperty();
1787   property.setField( QgsAuxiliaryLayer::nameFromProperty( def, true ) );
1788   property.setActive( true );
1789   button->updateFieldLists();
1790   button->setToProperty( property );
1791   mDataDefinedProperties.setProperty( key, button->toProperty() );
1792   updatePreview();
1793   emit auxiliaryFieldCreated();
1794 }
1795 
1796 
updateShapeFrameStatus()1797 void QgsTextFormatWidget::updateShapeFrameStatus()
1798 {
1799   mShapeFrame->setEnabled( mShapeDrawDDBtn->isActive() || mShapeDrawChkBx->isChecked() );
1800 }
1801 
updateBufferFrameStatus()1802 void QgsTextFormatWidget::updateBufferFrameStatus()
1803 {
1804   mBufferFrame->setEnabled( mBufferDrawDDBtn->isActive() || mBufferDrawChkBx->isChecked() );
1805 }
1806 
updateShadowFrameStatus()1807 void QgsTextFormatWidget::updateShadowFrameStatus()
1808 {
1809   mShadowFrame->setEnabled( mShadowDrawDDBtn->isActive() || mShadowDrawChkBx->isChecked() );
1810 }
1811 
updateCalloutFrameStatus()1812 void QgsTextFormatWidget::updateCalloutFrameStatus()
1813 {
1814   mCalloutFrame->setEnabled( mCalloutDrawDDBtn->isActive() || mCalloutsDrawCheckBox->isChecked() );
1815 }
1816 
setFormatFromStyle(const QString & name,QgsStyle::StyleEntity type)1817 void QgsTextFormatWidget::setFormatFromStyle( const QString &name, QgsStyle::StyleEntity type )
1818 {
1819   switch ( type )
1820   {
1821     case QgsStyle::SymbolEntity:
1822     case QgsStyle::ColorrampEntity:
1823     case QgsStyle::TagEntity:
1824     case QgsStyle::SmartgroupEntity:
1825     case QgsStyle::LegendPatchShapeEntity:
1826     case QgsStyle::Symbol3DEntity:
1827       return;
1828 
1829     case QgsStyle::TextFormatEntity:
1830     {
1831       if ( !QgsStyle::defaultStyle()->textFormatNames().contains( name ) )
1832         return;
1833 
1834       QgsTextFormat newFormat = QgsStyle::defaultStyle()->textFormat( name );
1835       setFormat( newFormat );
1836       break;
1837     }
1838 
1839     case QgsStyle::LabelSettingsEntity:
1840     {
1841       if ( !QgsStyle::defaultStyle()->labelSettingsNames().contains( name ) )
1842         return;
1843 
1844       QgsTextFormat newFormat = QgsStyle::defaultStyle()->labelSettings( name ).format();
1845       setFormat( newFormat );
1846       break;
1847     }
1848   }
1849 }
1850 
saveFormat()1851 void QgsTextFormatWidget::saveFormat()
1852 {
1853   QgsStyle *style = QgsStyle::defaultStyle();
1854   if ( !style )
1855     return;
1856 
1857   QgsStyleSaveDialog saveDlg( this, QgsStyle::TextFormatEntity );
1858   saveDlg.setDefaultTags( mTextFormatsListWidget->currentTagFilter() );
1859   if ( !saveDlg.exec() )
1860     return;
1861 
1862   if ( saveDlg.name().isEmpty() )
1863     return;
1864 
1865   // check if there is no format with same name
1866   if ( style->textFormatNames().contains( saveDlg.name() ) )
1867   {
1868     int res = QMessageBox::warning( this, tr( "Save Text Format" ),
1869                                     tr( "Format with name '%1' already exists. Overwrite?" )
1870                                     .arg( saveDlg.name() ),
1871                                     QMessageBox::Yes | QMessageBox::No );
1872     if ( res != QMessageBox::Yes )
1873     {
1874       return;
1875     }
1876     style->removeTextFormat( saveDlg.name() );
1877   }
1878 
1879   QStringList symbolTags = saveDlg.tags().split( ',' );
1880 
1881   QgsTextFormat newFormat = format();
1882   style->addTextFormat( saveDlg.name(), newFormat );
1883   style->saveTextFormat( saveDlg.name(), newFormat, saveDlg.isFavorite(), symbolTags );
1884 }
1885 
mShapeSVGSelectorBtn_clicked()1886 void QgsTextFormatWidget::mShapeSVGSelectorBtn_clicked()
1887 {
1888   QgsSvgSelectorDialog svgDlg( this );
1889   svgDlg.setWindowTitle( tr( "Select SVG file" ) );
1890   svgDlg.svgSelector()->setSvgPath( mShapeSVGPathLineEdit->text().trimmed() );
1891 
1892   if ( svgDlg.exec() == QDialog::Accepted )
1893   {
1894     QString svgPath = svgDlg.svgSelector()->currentSvgPath();
1895     if ( !svgPath.isEmpty() )
1896     {
1897       mShapeSVGPathLineEdit->setText( svgPath );
1898       updatePreview();
1899     }
1900   }
1901 }
1902 
mShapeSVGParamsBtn_clicked()1903 void QgsTextFormatWidget::mShapeSVGParamsBtn_clicked()
1904 {
1905   QString svgPath = mShapeSVGPathLineEdit->text();
1906   mLoadSvgParams = true;
1907   updateSvgWidgets( svgPath );
1908   mLoadSvgParams = false;
1909 }
1910 
mShapeRotationCmbBx_currentIndexChanged(int index)1911 void QgsTextFormatWidget::mShapeRotationCmbBx_currentIndexChanged( int index )
1912 {
1913   mShapeRotationDblSpnBx->setEnabled( static_cast< QgsTextBackgroundSettings::RotationType >( index ) != QgsTextBackgroundSettings::RotationSync );
1914   mShapeRotationDDBtn->setEnabled( static_cast< QgsTextBackgroundSettings::RotationType >( index ) != QgsTextBackgroundSettings::RotationSync );
1915 }
1916 
mPreviewTextEdit_textChanged(const QString & text)1917 void QgsTextFormatWidget::mPreviewTextEdit_textChanged( const QString &text )
1918 {
1919   lblFontPreview->setText( text );
1920   updatePreview();
1921 }
1922 
mPreviewTextBtn_clicked()1923 void QgsTextFormatWidget::mPreviewTextBtn_clicked()
1924 {
1925   mPreviewTextEdit->setText( QStringLiteral( "Lorem Ipsum" ) );
1926   updatePreview();
1927 }
1928 
mPreviewBackgroundBtn_colorChanged(const QColor & color)1929 void QgsTextFormatWidget::mPreviewBackgroundBtn_colorChanged( const QColor &color )
1930 {
1931   setPreviewBackground( color );
1932 }
1933 
mDirectSymbLeftToolBtn_clicked()1934 void QgsTextFormatWidget::mDirectSymbLeftToolBtn_clicked()
1935 {
1936   bool gotChar = false;
1937 
1938   const QChar initial = !mDirectSymbLeftLineEdit->text().isEmpty() ? mDirectSymbLeftLineEdit->text().at( 0 ) : QChar();
1939   QChar dirSymb = mCharDlg->selectCharacter( &gotChar, mRefFont, mFontDB.styleString( mRefFont ), initial );
1940 
1941   if ( !gotChar )
1942     return;
1943 
1944   if ( !dirSymb.isNull() )
1945     mDirectSymbLeftLineEdit->setText( QString( dirSymb ) );
1946 }
1947 
mDirectSymbRightToolBtn_clicked()1948 void QgsTextFormatWidget::mDirectSymbRightToolBtn_clicked()
1949 {
1950   bool gotChar = false;
1951   const QChar initial = !mDirectSymbRightLineEdit->text().isEmpty() ? mDirectSymbRightLineEdit->text().at( 0 ) : QChar();
1952   QChar dirSymb = mCharDlg->selectCharacter( &gotChar, mRefFont, mFontDB.styleString( mRefFont ), initial );
1953 
1954   if ( !gotChar )
1955     return;
1956 
1957   if ( !dirSymb.isNull() )
1958     mDirectSymbRightLineEdit->setText( QString( dirSymb ) );
1959 }
1960 
chkLineOrientationDependent_toggled(bool active)1961 void QgsTextFormatWidget::chkLineOrientationDependent_toggled( bool active )
1962 {
1963   if ( active )
1964   {
1965     chkLineAbove->setText( tr( "Left of line" ) );
1966     chkLineBelow->setText( tr( "Right of line" ) );
1967   }
1968   else
1969   {
1970     chkLineAbove->setText( tr( "Above line" ) );
1971     chkLineBelow->setText( tr( "Below line" ) );
1972   }
1973 }
1974 
1975 
mToolButtonConfigureSubstitutes_clicked()1976 void QgsTextFormatWidget::mToolButtonConfigureSubstitutes_clicked()
1977 {
1978   QgsPanelWidget *panel = QgsPanelWidget::findParentPanel( this );
1979   if ( panel && panel->dockMode() )
1980   {
1981     QgsSubstitutionListWidget *widget = new QgsSubstitutionListWidget( panel );
1982     widget->setPanelTitle( tr( "Substitutions" ) );
1983     widget->setSubstitutions( mSubstitutions );
1984     connect( widget, &QgsSubstitutionListWidget::substitutionsChanged, this, &QgsTextFormatWidget::onSubstitutionsChanged );
1985     panel->openPanel( widget );
1986     return;
1987   }
1988 
1989   QgsSubstitutionListDialog dlg( this );
1990   dlg.setSubstitutions( mSubstitutions );
1991   if ( dlg.exec() == QDialog::Accepted )
1992   {
1993     mSubstitutions = dlg.substitutions();
1994     emit widgetChanged();
1995   }
1996 }
1997 
showBackgroundRadius(bool show)1998 void QgsTextFormatWidget::showBackgroundRadius( bool show )
1999 {
2000   mShapeRadiusLabel->setVisible( show );
2001   mShapeRadiusXDbSpnBx->setVisible( show );
2002 
2003   mShapeRadiusYDbSpnBx->setVisible( show );
2004 
2005   mShapeRadiusUnitWidget->setVisible( show );
2006 
2007   mShapeRadiusDDBtn->setVisible( show );
2008   mShapeRadiusUnitsDDBtn->setVisible( show );
2009 }
2010 
showBackgroundPenStyle(bool show)2011 void QgsTextFormatWidget::showBackgroundPenStyle( bool show )
2012 {
2013   mShapePenStyleLabel->setVisible( show );
2014   mShapePenStyleCmbBx->setVisible( show );
2015 
2016   mShapePenStyleDDBtn->setVisible( show );
2017 }
2018 
enableDataDefinedAlignment(bool enable)2019 void QgsTextFormatWidget::enableDataDefinedAlignment( bool enable )
2020 {
2021   mCoordAlignmentFrame->setEnabled( enable );
2022 }
2023 
createExpressionContext() const2024 QgsExpressionContext QgsTextFormatWidget::createExpressionContext() const
2025 {
2026   if ( auto *lExpressionContext = mContext.expressionContext() )
2027     return *lExpressionContext;
2028 
2029   QgsExpressionContext expContext;
2030   expContext << QgsExpressionContextUtils::globalScope()
2031              << QgsExpressionContextUtils::projectScope( QgsProject::instance() )
2032              << QgsExpressionContextUtils::atlasScope( nullptr );
2033   if ( mMapCanvas )
2034     expContext << QgsExpressionContextUtils::mapSettingsScope( mMapCanvas->mapSettings() );
2035 
2036   if ( mLayer )
2037     expContext << QgsExpressionContextUtils::layerScope( mLayer );
2038 
2039   //TODO - show actual value
2040   expContext.setOriginalValueVariable( QVariant() );
2041   expContext.setHighlightedVariables( QStringList() << QgsExpressionContext::EXPR_ORIGINAL_VALUE );
2042 
2043   return expContext;
2044 }
2045 
labelGeometryType() const2046 QgsWkbTypes::GeometryType QgsTextFormatWidget::labelGeometryType() const
2047 {
2048   if ( mGeometryGeneratorGroupBox->isChecked() )
2049     return mGeometryGeneratorType->currentData().value<QgsWkbTypes::GeometryType>();
2050   else if ( mLayer )
2051     return mLayer->geometryType();
2052   else
2053     return mGeomType;
2054 }
2055 
2056 
2057 //
2058 // QgsTextFormatDialog
2059 //
2060 
QgsTextFormatDialog(const QgsTextFormat & format,QgsMapCanvas * mapCanvas,QWidget * parent,Qt::WindowFlags fl,QgsVectorLayer * layer)2061 QgsTextFormatDialog::QgsTextFormatDialog( const QgsTextFormat &format, QgsMapCanvas *mapCanvas, QWidget *parent, Qt::WindowFlags fl, QgsVectorLayer *layer )
2062   : QDialog( parent, fl )
2063 {
2064   setWindowTitle( tr( "Text Settings" ) );
2065 
2066   mFormatWidget = new QgsTextFormatWidget( format, mapCanvas, this, layer );
2067   mFormatWidget->layout()->setContentsMargins( 0, 0, 0, 0 );
2068 
2069   QVBoxLayout *layout = new QVBoxLayout( this );
2070   layout->addWidget( mFormatWidget );
2071 
2072   mButtonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::Help, Qt::Horizontal, this );
2073   layout->addWidget( mButtonBox );
2074 
2075   setLayout( layout );
2076   QgsGui::instance()->enableAutoGeometryRestore( this );
2077 
2078   connect( mButtonBox->button( QDialogButtonBox::Ok ), &QAbstractButton::clicked, this, &QDialog::accept );
2079   connect( mButtonBox->button( QDialogButtonBox::Cancel ), &QAbstractButton::clicked, this, &QDialog::reject );
2080   connect( mButtonBox->button( QDialogButtonBox::Help ), &QAbstractButton::clicked, this, &QgsTextFormatDialog::showHelp );
2081 }
2082 
format() const2083 QgsTextFormat QgsTextFormatDialog::format() const
2084 {
2085   return mFormatWidget->format();
2086 }
2087 
showHelp()2088 void QgsTextFormatDialog::showHelp()
2089 {
2090   QgsHelp::openHelp( QStringLiteral( "style_library/label_settings.html#formatting-the-label-text" ) );
2091 }
2092 
setContext(const QgsSymbolWidgetContext & context)2093 void QgsTextFormatDialog::setContext( const QgsSymbolWidgetContext &context )
2094 {
2095   mFormatWidget->setContext( context );
2096 }
2097 
buttonBox() const2098 QDialogButtonBox *QgsTextFormatDialog::buttonBox() const
2099 {
2100   return mButtonBox;
2101 }
2102 
QgsTextFormatPanelWidget(const QgsTextFormat & format,QgsMapCanvas * mapCanvas,QWidget * parent,QgsVectorLayer * layer)2103 QgsTextFormatPanelWidget::QgsTextFormatPanelWidget( const QgsTextFormat &format, QgsMapCanvas *mapCanvas, QWidget *parent, QgsVectorLayer *layer )
2104   : QgsPanelWidgetWrapper( new QgsTextFormatWidget( format, mapCanvas, nullptr, layer ), parent )
2105 {
2106   mFormatWidget = qobject_cast< QgsTextFormatWidget * >( widget() );
2107   connect( mFormatWidget, &QgsTextFormatWidget::widgetChanged, this, [ = ]
2108   {
2109     if ( !mBlockSignals )
2110       emit widgetChanged();
2111   } );
2112 }
2113 
format() const2114 QgsTextFormat QgsTextFormatPanelWidget::format() const
2115 {
2116   return mFormatWidget->format();
2117 }
2118 
setFormat(const QgsTextFormat & format)2119 void QgsTextFormatPanelWidget::setFormat( const QgsTextFormat &format )
2120 {
2121   mBlockSignals = true;
2122   mFormatWidget->setFormat( format );
2123   mBlockSignals = false;
2124 }
2125 
setContext(const QgsSymbolWidgetContext & context)2126 void QgsTextFormatPanelWidget::setContext( const QgsSymbolWidgetContext &context )
2127 {
2128   mFormatWidget->setContext( context );
2129 }
2130 
setDockMode(bool dockMode)2131 void QgsTextFormatPanelWidget::setDockMode( bool dockMode )
2132 {
2133   mFormatWidget->setDockMode( dockMode );
2134   QgsPanelWidgetWrapper::setDockMode( dockMode );
2135 }
2136