1 /***************************************************************************
2 qgslabelpropertydialog.cpp
3 --------------------------
4 begin : 2010-11-12
5 copyright : (C) 2010 by Marco Hugentobler
6 email : marco dot hugentobler at sourcepole dot ch
7 ***************************************************************************/
8
9 /***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18 #include "qgslabelpropertydialog.h"
19 #include "qgscallout.h"
20 #include "qgsfontutils.h"
21 #include "qgslogger.h"
22 #include "qgsfeatureiterator.h"
23 #include "qgsproject.h"
24 #include "qgsvectorlayer.h"
25 #include "qgisapp.h"
26 #include "qgsmapcanvas.h"
27 #include "qgsvectorlayerlabeling.h"
28 #include "qgsproperty.h"
29 #include "qgssettings.h"
30 #include "qgsexpressioncontextutils.h"
31 #include "qgsgui.h"
32 #include "qgshelp.h"
33 #include "qgsexpressionnodeimpl.h"
34
35 #include <QColorDialog>
36 #include <QFontDatabase>
37 #include <QDialogButtonBox>
38
39
QgsLabelPropertyDialog(const QString & layerId,const QString & providerId,QgsFeatureId featureId,const QFont & labelFont,const QString & labelText,bool isPinned,const QgsPalLayerSettings & layerSettings,QgsMapCanvas * canvas,QWidget * parent,Qt::WindowFlags f)40 QgsLabelPropertyDialog::QgsLabelPropertyDialog( const QString &layerId, const QString &providerId, QgsFeatureId featureId, const QFont &labelFont, const QString &labelText, bool isPinned, const QgsPalLayerSettings &layerSettings, QgsMapCanvas *canvas, QWidget *parent, Qt::WindowFlags f )
41 : QDialog( parent, f )
42 , mCanvas( canvas )
43 , mLabelFont( labelFont )
44 , mIsPinned( isPinned )
45 {
46 setupUi( this );
47 QgsGui::instance()->enableAutoGeometryRestore( this );
48
49 // set defaults to layer defaults
50 mLabelAllPartsCheckBox->setChecked( layerSettings.labelPerPart );
51
52 connect( buttonBox, &QDialogButtonBox::clicked, this, &QgsLabelPropertyDialog::buttonBox_clicked );
53 connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsLabelPropertyDialog::showHelp );
54 connect( mShowLabelChkbx, &QCheckBox::toggled, this, &QgsLabelPropertyDialog::mShowLabelChkbx_toggled );
55 connect( mAlwaysShowChkbx, &QCheckBox::toggled, this, &QgsLabelPropertyDialog::mAlwaysShowChkbx_toggled );
56 connect( mShowCalloutChkbx, &QCheckBox::toggled, this, &QgsLabelPropertyDialog::showCalloutToggled );
57 connect( mBufferDrawChkbx, &QCheckBox::toggled, this, &QgsLabelPropertyDialog::bufferDrawToggled );
58 connect( mLabelDistanceSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsLabelPropertyDialog::mLabelDistanceSpinBox_valueChanged );
59 connect( mXCoordSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsLabelPropertyDialog::mXCoordSpinBox_valueChanged );
60 connect( mYCoordSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsLabelPropertyDialog::mYCoordSpinBox_valueChanged );
61 connect( mFontFamilyCmbBx, &QFontComboBox::currentFontChanged, this, &QgsLabelPropertyDialog::mFontFamilyCmbBx_currentFontChanged );
62 connect( mFontStyleCmbBx, &QComboBox::currentTextChanged, this, &QgsLabelPropertyDialog::mFontStyleCmbBx_currentIndexChanged );
63 connect( mFontUnderlineBtn, &QToolButton::toggled, this, &QgsLabelPropertyDialog::mFontUnderlineBtn_toggled );
64 connect( mFontStrikethroughBtn, &QToolButton::toggled, this, &QgsLabelPropertyDialog::mFontStrikethroughBtn_toggled );
65 connect( mFontBoldBtn, &QToolButton::toggled, this, &QgsLabelPropertyDialog::mFontBoldBtn_toggled );
66 connect( mFontItalicBtn, &QToolButton::toggled, this, &QgsLabelPropertyDialog::mFontItalicBtn_toggled );
67 connect( mFontSizeSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsLabelPropertyDialog::mFontSizeSpinBox_valueChanged );
68 connect( mBufferSizeSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsLabelPropertyDialog::mBufferSizeSpinBox_valueChanged );
69 connect( mRotationSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsLabelPropertyDialog::mRotationSpinBox_valueChanged );
70 connect( mFontColorButton, &QgsColorButton::colorChanged, this, &QgsLabelPropertyDialog::mFontColorButton_colorChanged );
71 connect( mBufferColorButton, &QgsColorButton::colorChanged, this, &QgsLabelPropertyDialog::mBufferColorButton_colorChanged );
72 connect( mMultiLineAlignComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsLabelPropertyDialog::mMultiLineAlignComboBox_currentIndexChanged );
73 connect( mHaliComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsLabelPropertyDialog::mHaliComboBox_currentIndexChanged );
74 connect( mValiComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsLabelPropertyDialog::mValiComboBox_currentIndexChanged );
75 connect( mLabelTextLineEdit, &QLineEdit::textChanged, this, &QgsLabelPropertyDialog::mLabelTextLineEdit_textChanged );
76 connect( mLabelAllPartsCheckBox, &QCheckBox::toggled, this, &QgsLabelPropertyDialog::labelAllPartsToggled );
77
78 mRotationSpinBox->setClearValue( 0 );
79 fillMultiLineAlignComboBox();
80 fillHaliComboBox();
81 fillValiComboBox();
82
83 init( layerId, providerId, featureId, labelText );
84
85 connect( mMinScaleWidget, &QgsScaleWidget::scaleChanged, this, &QgsLabelPropertyDialog::minScaleChanged );
86 connect( mMaxScaleWidget, &QgsScaleWidget::scaleChanged, this, &QgsLabelPropertyDialog::maxScaleChanged );
87 }
88
setMapCanvas(QgsMapCanvas * canvas)89 void QgsLabelPropertyDialog::setMapCanvas( QgsMapCanvas *canvas )
90 {
91 mMinScaleWidget->setMapCanvas( canvas );
92 mMinScaleWidget->setShowCurrentScaleButton( true );
93 mMaxScaleWidget->setMapCanvas( canvas );
94 mMaxScaleWidget->setShowCurrentScaleButton( true );
95 }
96
buttonBox_clicked(QAbstractButton * button)97 void QgsLabelPropertyDialog::buttonBox_clicked( QAbstractButton *button )
98 {
99 if ( buttonBox->buttonRole( button ) == QDialogButtonBox::ApplyRole )
100 {
101 emit applied();
102 }
103 }
104
init(const QString & layerId,const QString & providerId,QgsFeatureId featureId,const QString & labelText)105 void QgsLabelPropertyDialog::init( const QString &layerId, const QString &providerId, QgsFeatureId featureId, const QString &labelText )
106 {
107 //get feature attributes
108 QgsVectorLayer *vlayer = qobject_cast< QgsVectorLayer * >( mCanvas->layer( layerId ) );
109 if ( !vlayer )
110 {
111 return;
112 }
113 if ( !vlayer->labeling() )
114 {
115 return;
116 }
117
118 if ( !vlayer->getFeatures( QgsFeatureRequest().setFilterFid( featureId ).setFlags( QgsFeatureRequest::NoGeometry ) ).nextFeature( mCurLabelFeat ) )
119 {
120 return;
121 }
122 const QgsAttributes attributeValues = mCurLabelFeat.attributes();
123
124 //get layerproperties. Problem: only for pallabeling...
125
126 blockElementSignals( true );
127
128 QgsPalLayerSettings layerSettings = vlayer->labeling()->settings( providerId );
129
130 //get label field and fill line edit
131 if ( layerSettings.isExpression && !labelText.isNull() )
132 {
133 mLabelTextLineEdit->setText( labelText );
134 mLabelTextLineEdit->setEnabled( false );
135 mLabelTextLabel->setText( tr( "Expression result" ) );
136 }
137 else
138 {
139 const QString labelFieldName = layerSettings.fieldName;
140 if ( !labelFieldName.isEmpty() )
141 {
142 mCurLabelField = vlayer->fields().lookupField( labelFieldName );
143 if ( mCurLabelField >= 0 )
144 {
145 mLabelTextLineEdit->setText( attributeValues.at( mCurLabelField ).toString() );
146
147 if ( vlayer->isEditable() )
148 mLabelTextLineEdit->setEnabled( true );
149 else
150 mLabelTextLineEdit->setEnabled( false );
151
152 const QgsFields &layerFields = vlayer->fields();
153 switch ( layerFields.at( mCurLabelField ).type() )
154 {
155 case QVariant::Double:
156 mLabelTextLineEdit->setValidator( new QDoubleValidator( this ) );
157 break;
158 case QVariant::Int:
159 case QVariant::UInt:
160 case QVariant::LongLong:
161 mLabelTextLineEdit->setValidator( new QIntValidator( this ) );
162 break;
163 default:
164 break;
165 }
166 }
167 else
168 {
169 mLabelTextLineEdit->setEnabled( false );
170 }
171 }
172 }
173
174 //get attributes of the feature and fill data defined values
175
176 // font is set directly from QgsLabelPosition
177 updateFont( mLabelFont, false );
178
179 QgsTextFormat format = layerSettings.format();
180 const QgsTextBufferSettings buffer = format.buffer();
181
182 //set all the gui elements to the default layer-level values
183 mLabelDistanceSpinBox->clear();
184 mLabelDistanceSpinBox->setSpecialValueText( tr( "Layer default (%1)" ).arg( QString::number( layerSettings.dist, 'f', mLabelDistanceSpinBox->decimals() ) ) );
185 mBufferSizeSpinBox->clear();
186 mBufferSizeSpinBox->setSpecialValueText( tr( "Layer default (%1)" ).arg( QString::number( buffer.size(), 'f', mBufferSizeSpinBox->decimals() ) ) );
187 mRotationSpinBox->clear();
188 mXCoordSpinBox->clear();
189 mYCoordSpinBox->clear();
190
191 mShowLabelChkbx->setChecked( true );
192 mBufferDrawChkbx->setChecked( buffer.enabled() );
193 mShowCalloutChkbx->setChecked( layerSettings.callout() ? layerSettings.callout()->enabled() : false );
194 mFontColorButton->setColor( format.color() );
195 mBufferColorButton->setColor( buffer.color() );
196 mMinScaleWidget->setScale( layerSettings.minimumScale );
197 mMaxScaleWidget->setScale( layerSettings.maximumScale );
198
199 QString defaultMultilineAlign;
200 switch ( layerSettings.multilineAlign )
201 {
202 case QgsPalLayerSettings::MultiLeft:
203 defaultMultilineAlign = QStringLiteral( "left" );
204 break;
205 case QgsPalLayerSettings::MultiCenter:
206 defaultMultilineAlign = QStringLiteral( "center" );
207 break;
208 case QgsPalLayerSettings::MultiRight:
209 defaultMultilineAlign = QStringLiteral( "right" );
210 break;
211 case QgsPalLayerSettings::MultiJustify:
212 defaultMultilineAlign = QStringLiteral( "justify" );
213 break;
214 case QgsPalLayerSettings::MultiFollowPlacement:
215 defaultMultilineAlign = QStringLiteral( "follow label placement" );
216 break;
217 }
218 mMultiLineAlignComboBox->setItemText( mMultiLineAlignComboBox->findData( "" ), tr( "Layer default (%1)" ).arg( defaultMultilineAlign ) );
219 mMultiLineAlignComboBox->setCurrentIndex( mMultiLineAlignComboBox->findData( "" ) );
220
221 mHaliComboBox->setCurrentIndex( mHaliComboBox->findData( "left", Qt::UserRole, Qt::MatchFixedString ) );
222 mValiComboBox->setCurrentIndex( mValiComboBox->findData( "bottom", Qt::UserRole, Qt::MatchFixedString ) );
223 mFontColorButton->setColorDialogTitle( tr( "Font Color" ) );
224 mBufferColorButton->setColorDialogTitle( tr( "Buffer Color" ) );
225
226 disableGuiElements();
227
228 mDataDefinedProperties = layerSettings.dataDefinedProperties();
229
230 //set widget values from data defined results
231 setDataDefinedValues( vlayer );
232 //enable widgets connected to data defined fields
233 enableDataDefinedWidgets( vlayer );
234
235 blockElementSignals( false );
236 }
237
disableGuiElements()238 void QgsLabelPropertyDialog::disableGuiElements()
239 {
240 mShowLabelChkbx->setEnabled( false );
241 mAlwaysShowChkbx->setEnabled( false );
242 mShowCalloutChkbx->setEnabled( false );
243 mMinScaleWidget->setEnabled( false );
244 mMaxScaleWidget->setEnabled( false );
245 mFontFamilyCmbBx->setEnabled( false );
246 mFontStyleCmbBx->setEnabled( false );
247 mFontUnderlineBtn->setEnabled( false );
248 mFontStrikethroughBtn->setEnabled( false );
249 mFontBoldBtn->setEnabled( false );
250 mFontItalicBtn->setEnabled( false );
251 mFontSizeSpinBox->setEnabled( false );
252 mBufferSizeSpinBox->setEnabled( false );
253 mFontColorButton->setEnabled( false );
254 mBufferColorButton->setEnabled( false );
255 mBufferDrawChkbx->setEnabled( false );
256 mLabelDistanceSpinBox->setEnabled( false );
257 mXCoordSpinBox->setEnabled( false );
258 mYCoordSpinBox->setEnabled( false );
259 mMultiLineAlignComboBox->setEnabled( false );
260 mHaliComboBox->setEnabled( false );
261 mValiComboBox->setEnabled( false );
262 mRotationSpinBox->setEnabled( false );
263 mLabelAllPartsCheckBox->setEnabled( false );
264 }
265
blockElementSignals(bool block)266 void QgsLabelPropertyDialog::blockElementSignals( bool block )
267 {
268 mShowLabelChkbx->blockSignals( block );
269 mAlwaysShowChkbx->blockSignals( block );
270 mShowCalloutChkbx->blockSignals( block );
271 mMinScaleWidget->blockSignals( block );
272 mMaxScaleWidget->blockSignals( block );
273 mFontFamilyCmbBx->blockSignals( block );
274 mFontStyleCmbBx->blockSignals( block );
275 mFontUnderlineBtn->blockSignals( block );
276 mFontStrikethroughBtn->blockSignals( block );
277 mFontBoldBtn->blockSignals( block );
278 mFontItalicBtn->blockSignals( block );
279 mFontSizeSpinBox->blockSignals( block );
280 mBufferSizeSpinBox->blockSignals( block );
281 mFontColorButton->blockSignals( block );
282 mBufferColorButton->blockSignals( block );
283 mBufferDrawChkbx->blockSignals( block );
284 mLabelDistanceSpinBox->blockSignals( block );
285 mXCoordSpinBox->blockSignals( block );
286 mYCoordSpinBox->blockSignals( block );
287 mMultiLineAlignComboBox->blockSignals( block );
288 mHaliComboBox->blockSignals( block );
289 mValiComboBox->blockSignals( block );
290 mRotationSpinBox->blockSignals( block );
291 mLabelAllPartsCheckBox->blockSignals( block );
292 }
293
dataDefinedColumnIndex(QgsPalLayerSettings::Property p,const QgsVectorLayer * vlayer,const QgsExpressionContext & context) const294 int QgsLabelPropertyDialog::dataDefinedColumnIndex( QgsPalLayerSettings::Property p, const QgsVectorLayer *vlayer, const QgsExpressionContext &context ) const
295 {
296 if ( !mDataDefinedProperties.isActive( p ) )
297 return -1;
298
299 const QgsProperty property = mDataDefinedProperties.property( p );
300
301 QString fieldName;
302 switch ( property.propertyType() )
303 {
304 case QgsProperty::InvalidProperty:
305 case QgsProperty::StaticProperty:
306 break;
307
308 case QgsProperty::FieldBasedProperty:
309 fieldName = property.field();
310 break;
311
312 case QgsProperty::ExpressionBasedProperty:
313 {
314 // an expression based property may still be a effectively a single field reference in the map canvas context.
315 // e.g. if it is a expression like '"some_field"', or 'case when @some_project_var = 'a' then "field_a" else "field_b" end'
316 QgsExpression expression( property.expressionString() );
317 if ( expression.prepare( &context ) )
318 {
319 const QgsExpressionNode *node = expression.rootNode()->effectiveNode();
320 if ( node->nodeType() == QgsExpressionNode::ntColumnRef )
321 {
322 const QgsExpressionNodeColumnRef *columnRef = qgis::down_cast<const QgsExpressionNodeColumnRef *>( node );
323 fieldName = columnRef->name();
324 }
325 // ok, it's not. But let's be super smart and helpful for users!
326 // maybe it's a COALESCE("some field", 'some' || 'fallback' || 'expression') type expression, where the user wants to override
327 // some labels with a value stored in a field but all others use some expression
328 else if ( node->nodeType() == QgsExpressionNode::ntFunction )
329 {
330 const QgsExpressionNodeFunction *functionNode = qgis::down_cast<const QgsExpressionNodeFunction *>( node );
331 if ( const QgsExpressionFunction *function = QgsExpression::QgsExpression::Functions()[functionNode->fnIndex()] )
332 {
333 if ( function->name() == QLatin1String( "coalesce" ) )
334 {
335 if ( const QgsExpressionNode *firstArg = functionNode->args()->list().value( 0 ) )
336 {
337 const QgsExpressionNode *firstArgNode = firstArg->effectiveNode();
338 if ( firstArgNode->nodeType() == QgsExpressionNode::ntColumnRef )
339 {
340 const QgsExpressionNodeColumnRef *columnRef = qgis::down_cast<const QgsExpressionNodeColumnRef *>( firstArgNode );
341 fieldName = columnRef->name();
342 }
343 }
344 }
345 }
346 }
347 }
348 break;
349 }
350 }
351
352 if ( !fieldName.isEmpty() )
353 return vlayer->fields().lookupField( fieldName );
354 return -1;
355 }
356
setDataDefinedValues(QgsVectorLayer * vlayer)357 void QgsLabelPropertyDialog::setDataDefinedValues( QgsVectorLayer *vlayer )
358 {
359 //loop through data defined properties and set all the GUI widget values. We can do this
360 //even if the data defined property is set to an expression, as it's useful to show
361 //users what the evaluated property is...
362
363 QgsExpressionContext context;
364 context << QgsExpressionContextUtils::globalScope()
365 << QgsExpressionContextUtils::projectScope( QgsProject::instance() )
366 << QgsExpressionContextUtils::atlasScope( nullptr )
367 << QgsExpressionContextUtils::mapSettingsScope( QgisApp::instance()->mapCanvas()->mapSettings() )
368 << QgsExpressionContextUtils::layerScope( vlayer );
369 context.setFeature( mCurLabelFeat );
370
371 const auto constPropertyKeys = mDataDefinedProperties.propertyKeys();
372 for ( const int key : constPropertyKeys )
373 {
374 if ( !mDataDefinedProperties.isActive( key ) )
375 continue;
376
377 //TODO - pass expression context
378 const QVariant result = mDataDefinedProperties.value( key, context );
379 if ( !result.isValid() || result.isNull() )
380 {
381 //could not evaluate data defined value
382 continue;
383 }
384
385 bool ok = false;
386 switch ( key )
387 {
388 case QgsPalLayerSettings::Show:
389 {
390 const int showLabel = result.toInt( &ok );
391 mShowLabelChkbx->setChecked( !ok || showLabel != 0 );
392 break;
393 }
394 case QgsPalLayerSettings::AlwaysShow:
395 mAlwaysShowChkbx->setChecked( result.toBool() );
396 break;
397
398 case QgsPalLayerSettings::CalloutDraw:
399 mShowCalloutChkbx->setChecked( result.toBool() );
400 break;
401
402 case QgsPalLayerSettings::LabelAllParts:
403 mLabelAllPartsCheckBox->setChecked( result.toBool() );
404 break;
405
406 case QgsPalLayerSettings::MinimumScale:
407 {
408 const double minScale = result.toDouble( &ok );
409 if ( ok )
410 {
411 mMinScaleWidget->setScale( minScale );
412 }
413 break;
414 }
415 case QgsPalLayerSettings::MaximumScale:
416 {
417 const double maxScale = result.toDouble( &ok );
418 if ( ok )
419 {
420 mMaxScaleWidget->setScale( maxScale );
421 }
422 break;
423 }
424 case QgsPalLayerSettings::BufferSize:
425 {
426 const double bufferSize = result.toDouble( &ok );
427 if ( ok )
428 {
429 mBufferSizeSpinBox->setValue( bufferSize );
430 }
431 break;
432 }
433 case QgsPalLayerSettings::PositionX:
434 {
435 const double posX = result.toDouble( &ok );
436 if ( ok )
437 {
438 mXCoordSpinBox->setValue( posX );
439 }
440 break;
441 }
442 case QgsPalLayerSettings::PositionY:
443 {
444 const double posY = result.toDouble( &ok );
445 if ( ok )
446 {
447 mYCoordSpinBox->setValue( posY );
448 }
449 break;
450 }
451 case QgsPalLayerSettings::LabelDistance:
452 {
453 const double labelDist = result.toDouble( &ok );
454 if ( ok )
455 {
456 mLabelDistanceSpinBox->setValue( labelDist );
457 }
458 break;
459 }
460 case QgsPalLayerSettings::MultiLineAlignment:
461 mMultiLineAlignComboBox->setCurrentIndex( mMultiLineAlignComboBox->findData( result.toString(), Qt::UserRole, Qt::MatchFixedString ) );
462 break;
463 case QgsPalLayerSettings::Hali:
464 mHaliComboBox->setCurrentIndex( mHaliComboBox->findData( result.toString(), Qt::UserRole, Qt::MatchFixedString ) );
465 break;
466 case QgsPalLayerSettings::Vali:
467 mValiComboBox->setCurrentIndex( mValiComboBox->findData( result.toString(), Qt::UserRole, Qt::MatchFixedString ) );
468 break;
469 case QgsPalLayerSettings::BufferColor:
470 mBufferColorButton->setColor( QColor( result.toString() ) );
471 break;
472
473 case QgsPalLayerSettings::BufferDraw:
474 mBufferDrawChkbx->setChecked( result.toBool() );
475 break;
476
477 case QgsPalLayerSettings::Color:
478 mFontColorButton->setColor( QColor( result.toString() ) );
479 break;
480 case QgsPalLayerSettings::LabelRotation:
481 {
482 const double rot = result.toDouble( &ok );
483 if ( ok )
484 {
485 mRotationSpinBox->setValue( rot );
486 }
487 break;
488 }
489
490 case QgsPalLayerSettings::Size:
491 {
492 const double size = result.toDouble( &ok );
493 if ( ok )
494 {
495 mFontSizeSpinBox->setValue( size );
496 }
497 else
498 {
499 mFontSizeSpinBox->setValue( 0 );
500 }
501 break;
502 }
503 default:
504 break;
505 }
506 }
507 enableWidgetsForPinnedLabels();
508 }
509
enableDataDefinedWidgets(QgsVectorLayer * vlayer)510 void QgsLabelPropertyDialog::enableDataDefinedWidgets( QgsVectorLayer *vlayer )
511 {
512 QgsExpressionContext context = mCanvas->createExpressionContext();
513 context.appendScope( vlayer->createExpressionContextScope() );
514
515 //loop through data defined properties, this time setting whether or not the widgets are enabled
516 //this can only be done for properties which are assigned to fields
517 const auto constPropertyKeys = mDataDefinedProperties.propertyKeys();
518 for ( const int key : constPropertyKeys )
519 {
520 const QgsProperty prop = mDataDefinedProperties.property( key );
521 if ( !prop || !prop.isActive() )
522 {
523 continue;
524 }
525
526 const int ddIndex = dataDefinedColumnIndex( static_cast< QgsPalLayerSettings::Property >( key ), vlayer, context );
527 mPropertyToFieldMap[ key ] = ddIndex;
528 if ( ddIndex < 0 )
529 continue; // can only modify attributes with an active data definition of a mapped field
530
531 switch ( key )
532 {
533 case QgsPalLayerSettings::Show:
534 mShowLabelChkbx->setEnabled( true );
535 break;
536 case QgsPalLayerSettings::AlwaysShow:
537 mAlwaysShowChkbx->setEnabled( true );
538 break;
539 case QgsPalLayerSettings::MinimumScale:
540 mMinScaleWidget->setEnabled( true );
541 break;
542 case QgsPalLayerSettings::MaximumScale:
543 mMaxScaleWidget->setEnabled( true );
544 break;
545 case QgsPalLayerSettings::BufferSize:
546 mBufferSizeSpinBox->setEnabled( true );
547 break;
548 case QgsPalLayerSettings::PositionX:
549 mXCoordSpinBox->setEnabled( true );
550 break;
551 case QgsPalLayerSettings::PositionY:
552 mYCoordSpinBox->setEnabled( true );
553 break;
554 case QgsPalLayerSettings::LabelDistance:
555 mLabelDistanceSpinBox->setEnabled( true );
556 break;
557 case QgsPalLayerSettings::MultiLineAlignment:
558 mMultiLineAlignComboBox->setEnabled( true );
559 break;
560 case QgsPalLayerSettings::Hali:
561 mCanSetHAlignment = true;
562 mHaliComboBox->setEnabled( mIsPinned );
563 break;
564 case QgsPalLayerSettings::Vali:
565 mCanSetVAlignment = true;
566 mValiComboBox->setEnabled( mIsPinned );
567 break;
568 case QgsPalLayerSettings::BufferColor:
569 mBufferColorButton->setEnabled( true );
570 break;
571 case QgsPalLayerSettings::BufferDraw:
572 mBufferDrawChkbx->setEnabled( true );
573 break;
574 case QgsPalLayerSettings::Color:
575 mFontColorButton->setEnabled( true );
576 break;
577 case QgsPalLayerSettings::LabelRotation:
578 mRotationSpinBox->setEnabled( true );
579 break;
580 //font related properties
581 case QgsPalLayerSettings::Family:
582 mFontFamilyCmbBx->setEnabled( true );
583 break;
584 case QgsPalLayerSettings::FontStyle:
585 mFontStyleCmbBx->setEnabled( true );
586 break;
587 case QgsPalLayerSettings::Underline:
588 mFontUnderlineBtn->setEnabled( true );
589 break;
590 case QgsPalLayerSettings::Strikeout:
591 mFontStrikethroughBtn->setEnabled( true );
592 break;
593 case QgsPalLayerSettings::Bold:
594 mFontBoldBtn->setEnabled( true );
595 break;
596 case QgsPalLayerSettings::Italic:
597 mFontItalicBtn->setEnabled( true );
598 break;
599 case QgsPalLayerSettings::Size:
600 mFontSizeSpinBox->setEnabled( true );
601 break;
602 case QgsPalLayerSettings::CalloutDraw:
603 mShowCalloutChkbx->setEnabled( true );
604 break;
605 case QgsPalLayerSettings::LabelAllParts:
606 mLabelAllPartsCheckBox->setEnabled( true );
607 break;
608 default:
609 break;
610 }
611 }
612 }
613
updateFont(const QFont & font,bool block)614 void QgsLabelPropertyDialog::updateFont( const QFont &font, bool block )
615 {
616 // update background reference font
617 if ( font != mLabelFont )
618 {
619 mLabelFont = font;
620 }
621
622 if ( block )
623 blockElementSignals( true );
624
625 mFontFamilyCmbBx->setCurrentFont( mLabelFont );
626 populateFontStyleComboBox();
627 mFontUnderlineBtn->setChecked( mLabelFont.underline() );
628 mFontStrikethroughBtn->setChecked( mLabelFont.strikeOut() );
629 mFontBoldBtn->setChecked( mLabelFont.bold() );
630 mFontItalicBtn->setChecked( mLabelFont.italic() );
631 if ( block )
632 blockElementSignals( false );
633 }
634
populateFontStyleComboBox()635 void QgsLabelPropertyDialog::populateFontStyleComboBox()
636 {
637 mFontStyleCmbBx->clear();
638 const auto constFamily = mFontDB.styles( mLabelFont.family() );
639 for ( const QString &style : constFamily )
640 {
641 mFontStyleCmbBx->addItem( style );
642 }
643
644 int curIndx = 0;
645 const int stylIndx = mFontStyleCmbBx->findText( mFontDB.styleString( mLabelFont ) );
646 if ( stylIndx > -1 )
647 {
648 curIndx = stylIndx;
649 }
650
651 mFontStyleCmbBx->setCurrentIndex( curIndx );
652 }
653
fillMultiLineAlignComboBox()654 void QgsLabelPropertyDialog::fillMultiLineAlignComboBox()
655 {
656 mMultiLineAlignComboBox->addItem( tr( "Layer Default" ), "" );
657 mMultiLineAlignComboBox->addItem( tr( "Left" ), "Left" );
658 mMultiLineAlignComboBox->addItem( tr( "Center" ), "Center" );
659 mMultiLineAlignComboBox->addItem( tr( "Right" ), "Right" );
660 mMultiLineAlignComboBox->addItem( tr( "Justify" ), "Justify" );
661 }
662
fillHaliComboBox()663 void QgsLabelPropertyDialog::fillHaliComboBox()
664 {
665 mHaliComboBox->addItem( tr( "Left" ), "Left" );
666 mHaliComboBox->addItem( tr( "Center" ), "Center" );
667 mHaliComboBox->addItem( tr( "Right" ), "Right" );
668 }
669
fillValiComboBox()670 void QgsLabelPropertyDialog::fillValiComboBox()
671 {
672 mValiComboBox->addItem( tr( "Bottom" ), "Bottom" );
673 mValiComboBox->addItem( tr( "Base" ), "Base" );
674 mValiComboBox->addItem( tr( "Half" ), "Half" );
675 mValiComboBox->addItem( tr( "Cap" ), "Cap" );
676 mValiComboBox->addItem( tr( "Top" ), "Top" );
677 }
678
mShowLabelChkbx_toggled(bool chkd)679 void QgsLabelPropertyDialog::mShowLabelChkbx_toggled( bool chkd )
680 {
681 insertChangedValue( QgsPalLayerSettings::Show, ( chkd ? 1 : 0 ) );
682 }
683
mAlwaysShowChkbx_toggled(bool chkd)684 void QgsLabelPropertyDialog::mAlwaysShowChkbx_toggled( bool chkd )
685 {
686 insertChangedValue( QgsPalLayerSettings::AlwaysShow, ( chkd ? 1 : 0 ) );
687 }
688
labelAllPartsToggled(bool checked)689 void QgsLabelPropertyDialog::labelAllPartsToggled( bool checked )
690 {
691 insertChangedValue( QgsPalLayerSettings::LabelAllParts, ( checked ? 1 : 0 ) );
692 }
693
showCalloutToggled(bool chkd)694 void QgsLabelPropertyDialog::showCalloutToggled( bool chkd )
695 {
696 insertChangedValue( QgsPalLayerSettings::CalloutDraw, ( chkd ? 1 : 0 ) );
697 }
698
bufferDrawToggled(bool chkd)699 void QgsLabelPropertyDialog::bufferDrawToggled( bool chkd )
700 {
701 insertChangedValue( QgsPalLayerSettings::BufferDraw, ( chkd ? 1 : 0 ) );
702 }
703
minScaleChanged(double scale)704 void QgsLabelPropertyDialog::minScaleChanged( double scale )
705 {
706 insertChangedValue( QgsPalLayerSettings::MinimumScale, scale );
707 }
708
maxScaleChanged(double scale)709 void QgsLabelPropertyDialog::maxScaleChanged( double scale )
710 {
711 insertChangedValue( QgsPalLayerSettings::MaximumScale, scale );
712 }
713
mLabelDistanceSpinBox_valueChanged(double d)714 void QgsLabelPropertyDialog::mLabelDistanceSpinBox_valueChanged( double d )
715 {
716 QVariant distance( d );
717 if ( d < 0 )
718 {
719 //null value so that distance is reset to default
720 distance.clear();
721 }
722 insertChangedValue( QgsPalLayerSettings::LabelDistance, distance );
723 }
724
mXCoordSpinBox_valueChanged(double d)725 void QgsLabelPropertyDialog::mXCoordSpinBox_valueChanged( double d )
726 {
727 QVariant x( d );
728 if ( d < mXCoordSpinBox->minimum() + mXCoordSpinBox->singleStep() )
729 {
730 //null value
731 x.clear();
732 }
733
734 insertChangedValue( QgsPalLayerSettings::PositionX, x );
735 enableWidgetsForPinnedLabels();
736 }
737
mYCoordSpinBox_valueChanged(double d)738 void QgsLabelPropertyDialog::mYCoordSpinBox_valueChanged( double d )
739 {
740 QVariant y( d );
741 if ( d < mYCoordSpinBox->minimum() + mYCoordSpinBox->singleStep() )
742 {
743 //null value
744 y.clear();
745 }
746 insertChangedValue( QgsPalLayerSettings::PositionY, y );
747 enableWidgetsForPinnedLabels();
748 }
749
mFontFamilyCmbBx_currentFontChanged(const QFont & f)750 void QgsLabelPropertyDialog::mFontFamilyCmbBx_currentFontChanged( const QFont &f )
751 {
752 mLabelFont.setFamily( f.family() );
753 updateFont( mLabelFont );
754 insertChangedValue( QgsPalLayerSettings::Family, f.family() );
755 }
756
mFontStyleCmbBx_currentIndexChanged(const QString & text)757 void QgsLabelPropertyDialog::mFontStyleCmbBx_currentIndexChanged( const QString &text )
758 {
759 QgsFontUtils::updateFontViaStyle( mLabelFont, text );
760 updateFont( mLabelFont );
761 insertChangedValue( QgsPalLayerSettings::FontStyle, text );
762 }
763
mFontUnderlineBtn_toggled(bool ckd)764 void QgsLabelPropertyDialog::mFontUnderlineBtn_toggled( bool ckd )
765 {
766 mLabelFont.setUnderline( ckd );
767 updateFont( mLabelFont );
768 insertChangedValue( QgsPalLayerSettings::Underline, ckd );
769 }
770
mFontStrikethroughBtn_toggled(bool ckd)771 void QgsLabelPropertyDialog::mFontStrikethroughBtn_toggled( bool ckd )
772 {
773 mLabelFont.setStrikeOut( ckd );
774 updateFont( mLabelFont );
775 insertChangedValue( QgsPalLayerSettings::Strikeout, ckd );
776 }
777
mFontBoldBtn_toggled(bool ckd)778 void QgsLabelPropertyDialog::mFontBoldBtn_toggled( bool ckd )
779 {
780 mLabelFont.setBold( ckd );
781 updateFont( mLabelFont );
782 insertChangedValue( QgsPalLayerSettings::Bold, ckd );
783 }
784
mFontItalicBtn_toggled(bool ckd)785 void QgsLabelPropertyDialog::mFontItalicBtn_toggled( bool ckd )
786 {
787 mLabelFont.setItalic( ckd );
788 updateFont( mLabelFont );
789 insertChangedValue( QgsPalLayerSettings::Italic, ckd );
790 }
791
mFontSizeSpinBox_valueChanged(double d)792 void QgsLabelPropertyDialog::mFontSizeSpinBox_valueChanged( double d )
793 {
794 QVariant size( d );
795 if ( d <= 0 )
796 {
797 //null value so that font size is reset to default
798 size.clear();
799 }
800 insertChangedValue( QgsPalLayerSettings::Size, size );
801 }
802
mBufferSizeSpinBox_valueChanged(double d)803 void QgsLabelPropertyDialog::mBufferSizeSpinBox_valueChanged( double d )
804 {
805 QVariant size( d );
806 if ( d < 0 )
807 {
808 //null value so that size is reset to default
809 size.clear();
810 }
811 insertChangedValue( QgsPalLayerSettings::BufferSize, size );
812 }
813
mRotationSpinBox_valueChanged(double d)814 void QgsLabelPropertyDialog::mRotationSpinBox_valueChanged( double d )
815 {
816 QVariant rotation( d );
817 if ( d < 0 )
818 {
819 //null value so that size is reset to default
820 rotation.clear();
821 }
822 insertChangedValue( QgsPalLayerSettings::LabelRotation, rotation );
823 }
824
mFontColorButton_colorChanged(const QColor & color)825 void QgsLabelPropertyDialog::mFontColorButton_colorChanged( const QColor &color )
826 {
827 insertChangedValue( QgsPalLayerSettings::Color, color.name() );
828 }
829
mBufferColorButton_colorChanged(const QColor & color)830 void QgsLabelPropertyDialog::mBufferColorButton_colorChanged( const QColor &color )
831 {
832 insertChangedValue( QgsPalLayerSettings::BufferColor, color.name() );
833 }
834
mMultiLineAlignComboBox_currentIndexChanged(const int index)835 void QgsLabelPropertyDialog::mMultiLineAlignComboBox_currentIndexChanged( const int index )
836 {
837 insertChangedValue( QgsPalLayerSettings::MultiLineAlignment, mMultiLineAlignComboBox->itemData( index ) );
838 }
839
mHaliComboBox_currentIndexChanged(const int index)840 void QgsLabelPropertyDialog::mHaliComboBox_currentIndexChanged( const int index )
841 {
842 insertChangedValue( QgsPalLayerSettings::Hali, mHaliComboBox->itemData( index ) );
843 }
844
mValiComboBox_currentIndexChanged(const int index)845 void QgsLabelPropertyDialog::mValiComboBox_currentIndexChanged( const int index )
846 {
847 insertChangedValue( QgsPalLayerSettings::Vali, mValiComboBox->itemData( index ) );
848 }
849
mLabelTextLineEdit_textChanged(const QString & text)850 void QgsLabelPropertyDialog::mLabelTextLineEdit_textChanged( const QString &text )
851 {
852 if ( mCurLabelField != -1 )
853 {
854 mChangedProperties.insert( mCurLabelField, text );
855 }
856 }
857
insertChangedValue(QgsPalLayerSettings::Property p,const QVariant & value)858 void QgsLabelPropertyDialog::insertChangedValue( QgsPalLayerSettings::Property p, const QVariant &value )
859 {
860 if ( mDataDefinedProperties.isActive( p ) )
861 {
862 const QgsProperty prop = mDataDefinedProperties.property( p );
863 if ( const int index = mPropertyToFieldMap.value( p ); index >= 0 )
864 {
865 mChangedProperties.insert( index, value );
866 }
867 }
868 }
869
enableWidgetsForPinnedLabels()870 void QgsLabelPropertyDialog::enableWidgetsForPinnedLabels()
871 {
872 const bool pinned = mXCoordSpinBox->value() >= ( mXCoordSpinBox->minimum() + mXCoordSpinBox->singleStep() ) &&
873 mYCoordSpinBox->value() >= ( mYCoordSpinBox->minimum() + mYCoordSpinBox->singleStep() );
874
875 mHaliComboBox->setEnabled( pinned && mCanSetHAlignment );
876 mValiComboBox->setEnabled( pinned && mCanSetVAlignment );
877
878 if ( !pinned )
879 {
880 mHaliComboBox->setToolTip( tr( "Alignment can only be set for pinned labels" ) );
881 mValiComboBox->setToolTip( tr( "Alignment can only be set for pinned labels" ) );
882 }
883 else
884 {
885 mHaliComboBox->setToolTip( QString() );
886 mValiComboBox->setToolTip( QString() );
887 }
888 }
889
showHelp()890 void QgsLabelPropertyDialog::showHelp()
891 {
892 QgsHelp::openHelp( QStringLiteral( "working_with_vector/vector_properties.html#the-label-toolbar" ) );
893 }
894