1 /***************************************************************************
2 qgscalloutwidget.cpp
3 ---------------------
4 begin : July 2019
5 copyright : (C) 2019 by Nyall Dawson
6 email : nyall dot dawson 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 "qgscalloutwidget.h"
17 #include "qgsvectorlayer.h"
18 #include "qgsexpressioncontextutils.h"
19 #include "qgsunitselectionwidget.h"
20 #include "qgscallout.h"
21 #include "qgsnewauxiliaryfielddialog.h"
22 #include "qgsnewauxiliarylayerdialog.h"
23 #include "qgsauxiliarystorage.h"
24 #include "qgslinesymbol.h"
25 #include "qgsfillsymbol.h"
26
createExpressionContext() const27 QgsExpressionContext QgsCalloutWidget::createExpressionContext() const
28 {
29 if ( auto *lExpressionContext = mContext.expressionContext() )
30 return *lExpressionContext;
31
32 QgsExpressionContext expContext( mContext.globalProjectAtlasMapLayerScopes( vectorLayer() ) );
33 QgsExpressionContextScope *symbolScope = QgsExpressionContextUtils::updateSymbolScope( nullptr, new QgsExpressionContextScope() );
34 symbolScope->addVariable( QgsExpressionContextScope::StaticVariable( QgsExpressionContext::EXPR_SYMBOL_COLOR, QColor(), true ) );
35 expContext << symbolScope;
36
37 // additional scopes
38 const auto constAdditionalExpressionContextScopes = mContext.additionalExpressionContextScopes();
39 for ( const QgsExpressionContextScope &scope : constAdditionalExpressionContextScopes )
40 {
41 expContext.appendScope( new QgsExpressionContextScope( scope ) );
42 }
43
44 //TODO - show actual value
45 expContext.setOriginalValueVariable( QVariant() );
46
47 expContext.setHighlightedVariables( QStringList() << QgsExpressionContext::EXPR_ORIGINAL_VALUE << QgsExpressionContext::EXPR_SYMBOL_COLOR );
48
49 return expContext;
50 }
51
setContext(const QgsSymbolWidgetContext & context)52 void QgsCalloutWidget::setContext( const QgsSymbolWidgetContext &context )
53 {
54 mContext = context;
55 const auto unitSelectionWidgets = findChildren<QgsUnitSelectionWidget *>();
56 for ( QgsUnitSelectionWidget *unitWidget : unitSelectionWidgets )
57 {
58 unitWidget->setMapCanvas( mContext.mapCanvas() );
59 }
60 const auto symbolButtonWidgets = findChildren<QgsSymbolButton *>();
61 for ( QgsSymbolButton *symbolWidget : symbolButtonWidgets )
62 {
63 symbolWidget->setMapCanvas( mContext.mapCanvas() );
64 symbolWidget->setMessageBar( mContext.messageBar() );
65 }
66 }
67
context() const68 QgsSymbolWidgetContext QgsCalloutWidget::context() const
69 {
70 return mContext;
71 }
72
registerDataDefinedButton(QgsPropertyOverrideButton * button,QgsCallout::Property key)73 void QgsCalloutWidget::registerDataDefinedButton( QgsPropertyOverrideButton *button, QgsCallout::Property key )
74 {
75 button->init( key, callout()->dataDefinedProperties(), QgsCallout::propertyDefinitions(), mVectorLayer, true );
76 connect( button, &QgsPropertyOverrideButton::changed, this, &QgsCalloutWidget::updateDataDefinedProperty );
77 connect( button, &QgsPropertyOverrideButton::createAuxiliaryField, this, &QgsCalloutWidget::createAuxiliaryField );
78
79 button->registerExpressionContextGenerator( this );
80 }
81
createAuxiliaryField()82 void QgsCalloutWidget::createAuxiliaryField()
83 {
84 // try to create an auxiliary layer if not yet created
85 if ( !mVectorLayer->auxiliaryLayer() )
86 {
87 QgsNewAuxiliaryLayerDialog dlg( mVectorLayer, this );
88 dlg.exec();
89 }
90
91 // return if still not exists
92 if ( !mVectorLayer->auxiliaryLayer() )
93 return;
94
95 QgsPropertyOverrideButton *button = qobject_cast<QgsPropertyOverrideButton *>( sender() );
96 const QgsCallout::Property key = static_cast< QgsCallout::Property >( button->propertyKey() );
97 const QgsPropertyDefinition def = QgsCallout::propertyDefinitions()[key];
98
99 // create property in auxiliary storage if necessary
100 if ( !mVectorLayer->auxiliaryLayer()->exists( def ) )
101 {
102 mVectorLayer->auxiliaryLayer()->addAuxiliaryField( def );
103 }
104
105 // update property with join field name from auxiliary storage
106 QgsProperty property = button->toProperty();
107 property.setField( QgsAuxiliaryLayer::nameFromProperty( def, true ) );
108 property.setActive( true );
109 button->updateFieldLists();
110 button->setToProperty( property );
111
112 callout()->dataDefinedProperties().setProperty( key, button->toProperty() );
113
114 emit changed();
115 }
116
updateDataDefinedProperty()117 void QgsCalloutWidget::updateDataDefinedProperty()
118 {
119 QgsPropertyOverrideButton *button = qobject_cast<QgsPropertyOverrideButton *>( sender() );
120 const QgsCallout::Property key = static_cast< QgsCallout::Property >( button->propertyKey() );
121 callout()->dataDefinedProperties().setProperty( key, button->toProperty() );
122 emit changed();
123 }
124
125 /// @cond PRIVATE
126
127 //
128 // QgsSimpleLineCalloutWidget
129 //
130
QgsSimpleLineCalloutWidget(QgsVectorLayer * vl,QWidget * parent)131 QgsSimpleLineCalloutWidget::QgsSimpleLineCalloutWidget( QgsVectorLayer *vl, QWidget *parent )
132 : QgsCalloutWidget( parent, vl )
133 {
134 setupUi( this );
135
136 // Callout options - to move to custom widgets when multiple callout styles exist
137 mCalloutLineStyleButton->setSymbolType( Qgis::SymbolType::Line );
138 mCalloutLineStyleButton->setDialogTitle( tr( "Callout Symbol" ) );
139 mCalloutLineStyleButton->registerExpressionContextGenerator( this );
140
141 mCalloutLineStyleButton->setLayer( vl );
142 mMinCalloutWidthUnitWidget->setUnits( QgsUnitTypes::RenderUnitList() << QgsUnitTypes::RenderMillimeters << QgsUnitTypes::RenderMetersInMapUnits << QgsUnitTypes::RenderMapUnits << QgsUnitTypes::RenderPixels
143 << QgsUnitTypes::RenderPoints << QgsUnitTypes::RenderInches );
144 mOffsetFromAnchorUnitWidget->setUnits( QgsUnitTypes::RenderUnitList() << QgsUnitTypes::RenderMillimeters << QgsUnitTypes::RenderMetersInMapUnits << QgsUnitTypes::RenderMapUnits << QgsUnitTypes::RenderPixels
145 << QgsUnitTypes::RenderPoints << QgsUnitTypes::RenderInches );
146 mOffsetFromLabelUnitWidget->setUnits( QgsUnitTypes::RenderUnitList() << QgsUnitTypes::RenderMillimeters << QgsUnitTypes::RenderMetersInMapUnits << QgsUnitTypes::RenderMapUnits << QgsUnitTypes::RenderPixels
147 << QgsUnitTypes::RenderPoints << QgsUnitTypes::RenderInches );
148
149 connect( mMinCalloutWidthUnitWidget, &QgsUnitSelectionWidget::changed, this, &QgsSimpleLineCalloutWidget::minimumLengthUnitWidgetChanged );
150 connect( mMinCalloutLengthSpin, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsSimpleLineCalloutWidget::minimumLengthChanged );
151
152 connect( mOffsetFromAnchorUnitWidget, &QgsUnitSelectionWidget::changed, this, &QgsSimpleLineCalloutWidget::offsetFromAnchorUnitWidgetChanged );
153 connect( mOffsetFromAnchorSpin, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsSimpleLineCalloutWidget::offsetFromAnchorChanged );
154 connect( mOffsetFromLabelUnitWidget, &QgsUnitSelectionWidget::changed, this, &QgsSimpleLineCalloutWidget::offsetFromLabelUnitWidgetChanged );
155 connect( mOffsetFromLabelSpin, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsSimpleLineCalloutWidget::offsetFromLabelChanged );
156
157 connect( mDrawToAllPartsCheck, &QCheckBox::toggled, this, &QgsSimpleLineCalloutWidget::drawToAllPartsToggled );
158
159 // Anchor point options
160 mAnchorPointComboBox->addItem( tr( "Pole of Inaccessibility" ), static_cast< int >( QgsCallout::PoleOfInaccessibility ) );
161 mAnchorPointComboBox->addItem( tr( "Point on Exterior" ), static_cast< int >( QgsCallout::PointOnExterior ) );
162 mAnchorPointComboBox->addItem( tr( "Point on Surface" ), static_cast< int >( QgsCallout::PointOnSurface ) );
163 mAnchorPointComboBox->addItem( tr( "Centroid" ), static_cast< int >( QgsCallout::Centroid ) );
164 connect( mAnchorPointComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsSimpleLineCalloutWidget::mAnchorPointComboBox_currentIndexChanged );
165
166 mLabelAnchorPointComboBox->addItem( tr( "Closest Point" ), static_cast< int >( QgsCallout::LabelPointOnExterior ) );
167 mLabelAnchorPointComboBox->addItem( tr( "Centroid" ), static_cast< int >( QgsCallout::LabelCentroid ) );
168 mLabelAnchorPointComboBox->addItem( tr( "Top Left" ), static_cast< int >( QgsCallout::LabelTopLeft ) );
169 mLabelAnchorPointComboBox->addItem( tr( "Top Center" ), static_cast< int >( QgsCallout::LabelTopMiddle ) );
170 mLabelAnchorPointComboBox->addItem( tr( "Top Right" ), static_cast< int >( QgsCallout::LabelTopRight ) );
171 mLabelAnchorPointComboBox->addItem( tr( "Left Middle" ), static_cast< int >( QgsCallout::LabelMiddleLeft ) );
172 mLabelAnchorPointComboBox->addItem( tr( "Right Middle" ), static_cast< int >( QgsCallout::LabelMiddleRight ) );
173 mLabelAnchorPointComboBox->addItem( tr( "Bottom Left" ), static_cast< int >( QgsCallout::LabelBottomLeft ) );
174 mLabelAnchorPointComboBox->addItem( tr( "Bottom Center" ), static_cast< int >( QgsCallout::LabelBottomMiddle ) );
175 mLabelAnchorPointComboBox->addItem( tr( "Bottom Right" ), static_cast< int >( QgsCallout::LabelBottomRight ) );
176 connect( mLabelAnchorPointComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsSimpleLineCalloutWidget::mLabelAnchorPointComboBox_currentIndexChanged );
177
178 connect( mCalloutLineStyleButton, &QgsSymbolButton::changed, this, &QgsSimpleLineCalloutWidget::lineSymbolChanged );
179
180 connect( mCalloutBlendComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsSimpleLineCalloutWidget::mCalloutBlendComboBox_currentIndexChanged );
181 }
182
setCallout(QgsCallout * callout)183 void QgsSimpleLineCalloutWidget::setCallout( QgsCallout *callout )
184 {
185 if ( !callout )
186 return;
187
188 mCallout.reset( dynamic_cast<QgsSimpleLineCallout *>( callout->clone() ) );
189 if ( !mCallout )
190 return;
191
192 mMinCalloutWidthUnitWidget->blockSignals( true );
193 mMinCalloutWidthUnitWidget->setUnit( mCallout->minimumLengthUnit() );
194 mMinCalloutWidthUnitWidget->setMapUnitScale( mCallout->minimumLengthMapUnitScale() );
195 mMinCalloutWidthUnitWidget->blockSignals( false );
196
197 whileBlocking( mMinCalloutLengthSpin )->setValue( mCallout->minimumLength() );
198
199 mOffsetFromAnchorUnitWidget->blockSignals( true );
200 mOffsetFromAnchorUnitWidget->setUnit( mCallout->offsetFromAnchorUnit() );
201 mOffsetFromAnchorUnitWidget->setMapUnitScale( mCallout->offsetFromAnchorMapUnitScale() );
202 mOffsetFromAnchorUnitWidget->blockSignals( false );
203 mOffsetFromLabelUnitWidget->blockSignals( true );
204 mOffsetFromLabelUnitWidget->setUnit( mCallout->offsetFromLabelUnit() );
205 mOffsetFromLabelUnitWidget->setMapUnitScale( mCallout->offsetFromLabelMapUnitScale() );
206 mOffsetFromLabelUnitWidget->blockSignals( false );
207 whileBlocking( mOffsetFromAnchorSpin )->setValue( mCallout->offsetFromAnchor() );
208 whileBlocking( mOffsetFromLabelSpin )->setValue( mCallout->offsetFromLabel() );
209
210 whileBlocking( mCalloutLineStyleButton )->setSymbol( mCallout->lineSymbol()->clone() );
211
212 whileBlocking( mDrawToAllPartsCheck )->setChecked( mCallout->drawCalloutToAllParts() );
213
214 whileBlocking( mAnchorPointComboBox )->setCurrentIndex( mAnchorPointComboBox->findData( static_cast< int >( callout->anchorPoint() ) ) );
215 whileBlocking( mLabelAnchorPointComboBox )->setCurrentIndex( mLabelAnchorPointComboBox->findData( static_cast< int >( callout->labelAnchorPoint() ) ) );
216
217 whileBlocking( mCalloutBlendComboBox )->setBlendMode( mCallout->blendMode() );
218
219 registerDataDefinedButton( mMinCalloutLengthDDBtn, QgsCallout::MinimumCalloutLength );
220 registerDataDefinedButton( mOffsetFromAnchorDDBtn, QgsCallout::OffsetFromAnchor );
221 registerDataDefinedButton( mOffsetFromLabelDDBtn, QgsCallout::OffsetFromLabel );
222 registerDataDefinedButton( mDrawToAllPartsDDBtn, QgsCallout::DrawCalloutToAllParts );
223 registerDataDefinedButton( mAnchorPointDDBtn, QgsCallout::AnchorPointPosition );
224 registerDataDefinedButton( mLabelAnchorPointDDBtn, QgsCallout::LabelAnchorPointPosition );
225 registerDataDefinedButton( mCalloutBlendModeDDBtn, QgsCallout::BlendMode );
226
227 registerDataDefinedButton( mOriginXDDBtn, QgsCallout::OriginX );
228 registerDataDefinedButton( mOriginYDDBtn, QgsCallout::OriginY );
229 registerDataDefinedButton( mDestXDDBtn, QgsCallout::DestinationX );
230 registerDataDefinedButton( mDestYDDBtn, QgsCallout::DestinationY );
231 }
232
setGeometryType(QgsWkbTypes::GeometryType type)233 void QgsSimpleLineCalloutWidget::setGeometryType( QgsWkbTypes::GeometryType type )
234 {
235 const bool isPolygon = type == QgsWkbTypes::PolygonGeometry;
236 mAnchorPointLbl->setEnabled( isPolygon );
237 mAnchorPointLbl->setVisible( isPolygon );
238 mAnchorPointComboBox->setEnabled( isPolygon );
239 mAnchorPointComboBox->setVisible( isPolygon );
240 mAnchorPointDDBtn->setEnabled( isPolygon );
241 mAnchorPointDDBtn->setVisible( isPolygon );
242 }
243
callout()244 QgsCallout *QgsSimpleLineCalloutWidget::callout()
245 {
246 return mCallout.get();
247 }
248
minimumLengthChanged()249 void QgsSimpleLineCalloutWidget::minimumLengthChanged()
250 {
251 mCallout->setMinimumLength( mMinCalloutLengthSpin->value() );
252 emit changed();
253 }
254
minimumLengthUnitWidgetChanged()255 void QgsSimpleLineCalloutWidget::minimumLengthUnitWidgetChanged()
256 {
257 mCallout->setMinimumLengthUnit( mMinCalloutWidthUnitWidget->unit() );
258 mCallout->setMinimumLengthMapUnitScale( mMinCalloutWidthUnitWidget->getMapUnitScale() );
259 emit changed();
260 }
261
offsetFromAnchorUnitWidgetChanged()262 void QgsSimpleLineCalloutWidget::offsetFromAnchorUnitWidgetChanged()
263 {
264 mCallout->setOffsetFromAnchorUnit( mOffsetFromAnchorUnitWidget->unit() );
265 mCallout->setOffsetFromAnchorMapUnitScale( mOffsetFromAnchorUnitWidget->getMapUnitScale() );
266 emit changed();
267 }
268
offsetFromAnchorChanged()269 void QgsSimpleLineCalloutWidget::offsetFromAnchorChanged()
270 {
271 mCallout->setOffsetFromAnchor( mOffsetFromAnchorSpin->value() );
272 emit changed();
273 }
274
offsetFromLabelUnitWidgetChanged()275 void QgsSimpleLineCalloutWidget::offsetFromLabelUnitWidgetChanged()
276 {
277 mCallout->setOffsetFromLabelUnit( mOffsetFromLabelUnitWidget->unit() );
278 mCallout->setOffsetFromLabelMapUnitScale( mOffsetFromLabelUnitWidget->getMapUnitScale() );
279 emit changed();
280 }
281
offsetFromLabelChanged()282 void QgsSimpleLineCalloutWidget::offsetFromLabelChanged()
283 {
284 mCallout->setOffsetFromLabel( mOffsetFromLabelSpin->value() );
285 emit changed();
286 }
287
lineSymbolChanged()288 void QgsSimpleLineCalloutWidget::lineSymbolChanged()
289 {
290 mCallout->setLineSymbol( mCalloutLineStyleButton->clonedSymbol< QgsLineSymbol >() );
291 emit changed();
292 }
293
mAnchorPointComboBox_currentIndexChanged(int index)294 void QgsSimpleLineCalloutWidget::mAnchorPointComboBox_currentIndexChanged( int index )
295 {
296 mCallout->setAnchorPoint( static_cast<QgsCallout::AnchorPoint>( mAnchorPointComboBox->itemData( index ).toInt() ) );
297 emit changed();
298 }
299
mLabelAnchorPointComboBox_currentIndexChanged(int index)300 void QgsSimpleLineCalloutWidget::mLabelAnchorPointComboBox_currentIndexChanged( int index )
301 {
302 mCallout->setLabelAnchorPoint( static_cast<QgsCallout::LabelAnchorPoint>( mLabelAnchorPointComboBox->itemData( index ).toInt() ) );
303 emit changed();
304 }
305
mCalloutBlendComboBox_currentIndexChanged(int)306 void QgsSimpleLineCalloutWidget::mCalloutBlendComboBox_currentIndexChanged( int )
307 {
308 mCallout->setBlendMode( mCalloutBlendComboBox->blendMode() );
309 emit changed();
310 }
311
drawToAllPartsToggled(bool active)312 void QgsSimpleLineCalloutWidget::drawToAllPartsToggled( bool active )
313 {
314 mCallout->setDrawCalloutToAllParts( active );
315 emit changed();
316 }
317
318
319 //
320 // QgsManhattanLineCalloutWidget
321 //
322
QgsManhattanLineCalloutWidget(QgsVectorLayer * vl,QWidget * parent)323 QgsManhattanLineCalloutWidget::QgsManhattanLineCalloutWidget( QgsVectorLayer *vl, QWidget *parent )
324 : QgsSimpleLineCalloutWidget( vl, parent )
325 {
326
327 }
328
329
330 //
331 // QgsCurvedLineCalloutWidget
332 //
333
QgsCurvedLineCalloutWidget(QgsVectorLayer * vl,QWidget * parent)334 QgsCurvedLineCalloutWidget::QgsCurvedLineCalloutWidget( QgsVectorLayer *vl, QWidget *parent )
335 : QgsCalloutWidget( parent, vl )
336 {
337 setupUi( this );
338
339 // Callout options - to move to custom widgets when multiple callout styles exist
340 mCalloutLineStyleButton->setSymbolType( Qgis::SymbolType::Line );
341 mCalloutLineStyleButton->setDialogTitle( tr( "Callout Symbol" ) );
342 mCalloutLineStyleButton->registerExpressionContextGenerator( this );
343
344 mCalloutLineStyleButton->setLayer( vl );
345 mMinCalloutWidthUnitWidget->setUnits( QgsUnitTypes::RenderUnitList() << QgsUnitTypes::RenderMillimeters << QgsUnitTypes::RenderMetersInMapUnits << QgsUnitTypes::RenderMapUnits << QgsUnitTypes::RenderPixels
346 << QgsUnitTypes::RenderPoints << QgsUnitTypes::RenderInches );
347 mOffsetFromAnchorUnitWidget->setUnits( QgsUnitTypes::RenderUnitList() << QgsUnitTypes::RenderMillimeters << QgsUnitTypes::RenderMetersInMapUnits << QgsUnitTypes::RenderMapUnits << QgsUnitTypes::RenderPixels
348 << QgsUnitTypes::RenderPoints << QgsUnitTypes::RenderInches );
349 mOffsetFromLabelUnitWidget->setUnits( QgsUnitTypes::RenderUnitList() << QgsUnitTypes::RenderMillimeters << QgsUnitTypes::RenderMetersInMapUnits << QgsUnitTypes::RenderMapUnits << QgsUnitTypes::RenderPixels
350 << QgsUnitTypes::RenderPoints << QgsUnitTypes::RenderInches );
351
352 connect( mMinCalloutWidthUnitWidget, &QgsUnitSelectionWidget::changed, this, &QgsCurvedLineCalloutWidget::minimumLengthUnitWidgetChanged );
353 connect( mMinCalloutLengthSpin, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsCurvedLineCalloutWidget::minimumLengthChanged );
354
355 connect( mOffsetFromAnchorUnitWidget, &QgsUnitSelectionWidget::changed, this, &QgsCurvedLineCalloutWidget::offsetFromAnchorUnitWidgetChanged );
356 connect( mOffsetFromAnchorSpin, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsCurvedLineCalloutWidget::offsetFromAnchorChanged );
357 connect( mOffsetFromLabelUnitWidget, &QgsUnitSelectionWidget::changed, this, &QgsCurvedLineCalloutWidget::offsetFromLabelUnitWidgetChanged );
358 connect( mOffsetFromLabelSpin, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsCurvedLineCalloutWidget::offsetFromLabelChanged );
359
360 connect( mDrawToAllPartsCheck, &QCheckBox::toggled, this, &QgsCurvedLineCalloutWidget::drawToAllPartsToggled );
361
362 mOrientationComboBox->addItem( tr( "Automatic" ), static_cast< int >( QgsCurvedLineCallout::Automatic ) );
363 mOrientationComboBox->addItem( tr( "Clockwise" ), static_cast< int >( QgsCurvedLineCallout::Clockwise ) );
364 mOrientationComboBox->addItem( tr( "Counter Clockwise" ), static_cast< int >( QgsCurvedLineCallout::CounterClockwise ) );
365 connect( mOrientationComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, [ = ]( int index )
366 {
367 mCallout->setOrientation( static_cast<QgsCurvedLineCallout::Orientation>( mOrientationComboBox->itemData( index ).toInt() ) );
368 emit changed();
369 } );
370
371 // Anchor point options
372 mAnchorPointComboBox->addItem( tr( "Pole of Inaccessibility" ), static_cast< int >( QgsCallout::PoleOfInaccessibility ) );
373 mAnchorPointComboBox->addItem( tr( "Point on Exterior" ), static_cast< int >( QgsCallout::PointOnExterior ) );
374 mAnchorPointComboBox->addItem( tr( "Point on Surface" ), static_cast< int >( QgsCallout::PointOnSurface ) );
375 mAnchorPointComboBox->addItem( tr( "Centroid" ), static_cast< int >( QgsCallout::Centroid ) );
376 connect( mAnchorPointComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsCurvedLineCalloutWidget::mAnchorPointComboBox_currentIndexChanged );
377
378 mLabelAnchorPointComboBox->addItem( tr( "Closest Point" ), static_cast< int >( QgsCallout::LabelPointOnExterior ) );
379 mLabelAnchorPointComboBox->addItem( tr( "Centroid" ), static_cast< int >( QgsCallout::LabelCentroid ) );
380 mLabelAnchorPointComboBox->addItem( tr( "Top Left" ), static_cast< int >( QgsCallout::LabelTopLeft ) );
381 mLabelAnchorPointComboBox->addItem( tr( "Top Center" ), static_cast< int >( QgsCallout::LabelTopMiddle ) );
382 mLabelAnchorPointComboBox->addItem( tr( "Top Right" ), static_cast< int >( QgsCallout::LabelTopRight ) );
383 mLabelAnchorPointComboBox->addItem( tr( "Left Middle" ), static_cast< int >( QgsCallout::LabelMiddleLeft ) );
384 mLabelAnchorPointComboBox->addItem( tr( "Right Middle" ), static_cast< int >( QgsCallout::LabelMiddleRight ) );
385 mLabelAnchorPointComboBox->addItem( tr( "Bottom Left" ), static_cast< int >( QgsCallout::LabelBottomLeft ) );
386 mLabelAnchorPointComboBox->addItem( tr( "Bottom Center" ), static_cast< int >( QgsCallout::LabelBottomMiddle ) );
387 mLabelAnchorPointComboBox->addItem( tr( "Bottom Right" ), static_cast< int >( QgsCallout::LabelBottomRight ) );
388 connect( mLabelAnchorPointComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsCurvedLineCalloutWidget::mLabelAnchorPointComboBox_currentIndexChanged );
389
390 connect( mCalloutLineStyleButton, &QgsSymbolButton::changed, this, &QgsCurvedLineCalloutWidget::lineSymbolChanged );
391
392 connect( mCurvatureSlider, &QSlider::valueChanged, this, [ = ]( int value ) { mCurvatureSpinBox->setValue( value / 10.0 ); } );
393 connect( mCurvatureSpinBox, static_cast < void ( QgsDoubleSpinBox::* )( double ) > ( &QgsDoubleSpinBox::valueChanged ), this, [ = ]( double value ) { whileBlocking( mCurvatureSlider )->setValue( value * 10 ); } );
394 connect( mCurvatureSpinBox, static_cast < void ( QgsDoubleSpinBox::* )( double ) > ( &QgsDoubleSpinBox::valueChanged ), this, [ = ]( double value )
395 {
396 mCallout->setCurvature( value / 100.0 );
397 emit changed();
398 } );
399
400 connect( mCalloutBlendComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsCurvedLineCalloutWidget::mCalloutBlendComboBox_currentIndexChanged );
401 }
402
setCallout(QgsCallout * callout)403 void QgsCurvedLineCalloutWidget::setCallout( QgsCallout *callout )
404 {
405 if ( !callout )
406 return;
407
408 mCallout.reset( dynamic_cast<QgsCurvedLineCallout *>( callout->clone() ) );
409 if ( !mCallout )
410 return;
411
412 mMinCalloutWidthUnitWidget->blockSignals( true );
413 mMinCalloutWidthUnitWidget->setUnit( mCallout->minimumLengthUnit() );
414 mMinCalloutWidthUnitWidget->setMapUnitScale( mCallout->minimumLengthMapUnitScale() );
415 mMinCalloutWidthUnitWidget->blockSignals( false );
416
417 whileBlocking( mMinCalloutLengthSpin )->setValue( mCallout->minimumLength() );
418
419 mOffsetFromAnchorUnitWidget->blockSignals( true );
420 mOffsetFromAnchorUnitWidget->setUnit( mCallout->offsetFromAnchorUnit() );
421 mOffsetFromAnchorUnitWidget->setMapUnitScale( mCallout->offsetFromAnchorMapUnitScale() );
422 mOffsetFromAnchorUnitWidget->blockSignals( false );
423 mOffsetFromLabelUnitWidget->blockSignals( true );
424 mOffsetFromLabelUnitWidget->setUnit( mCallout->offsetFromLabelUnit() );
425 mOffsetFromLabelUnitWidget->setMapUnitScale( mCallout->offsetFromLabelMapUnitScale() );
426 mOffsetFromLabelUnitWidget->blockSignals( false );
427 whileBlocking( mOffsetFromAnchorSpin )->setValue( mCallout->offsetFromAnchor() );
428 whileBlocking( mOffsetFromLabelSpin )->setValue( mCallout->offsetFromLabel() );
429
430 whileBlocking( mCalloutLineStyleButton )->setSymbol( mCallout->lineSymbol()->clone() );
431
432 whileBlocking( mDrawToAllPartsCheck )->setChecked( mCallout->drawCalloutToAllParts() );
433
434 whileBlocking( mOrientationComboBox )->setCurrentIndex( mOrientationComboBox->findData( static_cast< int >( mCallout->orientation() ) ) );
435
436 whileBlocking( mAnchorPointComboBox )->setCurrentIndex( mAnchorPointComboBox->findData( static_cast< int >( callout->anchorPoint() ) ) );
437 whileBlocking( mLabelAnchorPointComboBox )->setCurrentIndex( mLabelAnchorPointComboBox->findData( static_cast< int >( callout->labelAnchorPoint() ) ) );
438
439 whileBlocking( mCalloutBlendComboBox )->setBlendMode( mCallout->blendMode() );
440
441 whileBlocking( mCurvatureSpinBox )->setValue( mCallout->curvature() * 100.0 );
442 whileBlocking( mCurvatureSlider )->setValue( mCallout->curvature() * 1000.0 );
443
444 registerDataDefinedButton( mMinCalloutLengthDDBtn, QgsCallout::MinimumCalloutLength );
445 registerDataDefinedButton( mOffsetFromAnchorDDBtn, QgsCallout::OffsetFromAnchor );
446 registerDataDefinedButton( mOffsetFromLabelDDBtn, QgsCallout::OffsetFromLabel );
447 registerDataDefinedButton( mDrawToAllPartsDDBtn, QgsCallout::DrawCalloutToAllParts );
448 registerDataDefinedButton( mAnchorPointDDBtn, QgsCallout::AnchorPointPosition );
449 registerDataDefinedButton( mLabelAnchorPointDDBtn, QgsCallout::LabelAnchorPointPosition );
450 registerDataDefinedButton( mCalloutBlendModeDDBtn, QgsCallout::BlendMode );
451 registerDataDefinedButton( mCalloutCurvatureDDBtn, QgsCallout::Curvature );
452 registerDataDefinedButton( mCalloutOrientationDDBtn, QgsCallout::Orientation );
453
454 registerDataDefinedButton( mOriginXDDBtn, QgsCallout::OriginX );
455 registerDataDefinedButton( mOriginYDDBtn, QgsCallout::OriginY );
456 registerDataDefinedButton( mDestXDDBtn, QgsCallout::DestinationX );
457 registerDataDefinedButton( mDestYDDBtn, QgsCallout::DestinationY );
458 }
459
setGeometryType(QgsWkbTypes::GeometryType type)460 void QgsCurvedLineCalloutWidget::setGeometryType( QgsWkbTypes::GeometryType type )
461 {
462 const bool isPolygon = type == QgsWkbTypes::PolygonGeometry;
463 mAnchorPointLbl->setEnabled( isPolygon );
464 mAnchorPointLbl->setVisible( isPolygon );
465 mAnchorPointComboBox->setEnabled( isPolygon );
466 mAnchorPointComboBox->setVisible( isPolygon );
467 mAnchorPointDDBtn->setEnabled( isPolygon );
468 mAnchorPointDDBtn->setVisible( isPolygon );
469 }
470
callout()471 QgsCallout *QgsCurvedLineCalloutWidget::callout()
472 {
473 return mCallout.get();
474 }
475
minimumLengthChanged()476 void QgsCurvedLineCalloutWidget::minimumLengthChanged()
477 {
478 mCallout->setMinimumLength( mMinCalloutLengthSpin->value() );
479 emit changed();
480 }
481
minimumLengthUnitWidgetChanged()482 void QgsCurvedLineCalloutWidget::minimumLengthUnitWidgetChanged()
483 {
484 mCallout->setMinimumLengthUnit( mMinCalloutWidthUnitWidget->unit() );
485 mCallout->setMinimumLengthMapUnitScale( mMinCalloutWidthUnitWidget->getMapUnitScale() );
486 emit changed();
487 }
488
offsetFromAnchorUnitWidgetChanged()489 void QgsCurvedLineCalloutWidget::offsetFromAnchorUnitWidgetChanged()
490 {
491 mCallout->setOffsetFromAnchorUnit( mOffsetFromAnchorUnitWidget->unit() );
492 mCallout->setOffsetFromAnchorMapUnitScale( mOffsetFromAnchorUnitWidget->getMapUnitScale() );
493 emit changed();
494 }
495
offsetFromAnchorChanged()496 void QgsCurvedLineCalloutWidget::offsetFromAnchorChanged()
497 {
498 mCallout->setOffsetFromAnchor( mOffsetFromAnchorSpin->value() );
499 emit changed();
500 }
501
offsetFromLabelUnitWidgetChanged()502 void QgsCurvedLineCalloutWidget::offsetFromLabelUnitWidgetChanged()
503 {
504 mCallout->setOffsetFromLabelUnit( mOffsetFromLabelUnitWidget->unit() );
505 mCallout->setOffsetFromLabelMapUnitScale( mOffsetFromLabelUnitWidget->getMapUnitScale() );
506 emit changed();
507 }
508
offsetFromLabelChanged()509 void QgsCurvedLineCalloutWidget::offsetFromLabelChanged()
510 {
511 mCallout->setOffsetFromLabel( mOffsetFromLabelSpin->value() );
512 emit changed();
513 }
514
lineSymbolChanged()515 void QgsCurvedLineCalloutWidget::lineSymbolChanged()
516 {
517 mCallout->setLineSymbol( mCalloutLineStyleButton->clonedSymbol< QgsLineSymbol >() );
518 emit changed();
519 }
520
mAnchorPointComboBox_currentIndexChanged(int index)521 void QgsCurvedLineCalloutWidget::mAnchorPointComboBox_currentIndexChanged( int index )
522 {
523 mCallout->setAnchorPoint( static_cast<QgsCallout::AnchorPoint>( mAnchorPointComboBox->itemData( index ).toInt() ) );
524 emit changed();
525 }
526
mLabelAnchorPointComboBox_currentIndexChanged(int index)527 void QgsCurvedLineCalloutWidget::mLabelAnchorPointComboBox_currentIndexChanged( int index )
528 {
529 mCallout->setLabelAnchorPoint( static_cast<QgsCallout::LabelAnchorPoint>( mLabelAnchorPointComboBox->itemData( index ).toInt() ) );
530 emit changed();
531 }
532
mCalloutBlendComboBox_currentIndexChanged(int)533 void QgsCurvedLineCalloutWidget::mCalloutBlendComboBox_currentIndexChanged( int )
534 {
535 mCallout->setBlendMode( mCalloutBlendComboBox->blendMode() );
536 emit changed();
537 }
538
drawToAllPartsToggled(bool active)539 void QgsCurvedLineCalloutWidget::drawToAllPartsToggled( bool active )
540 {
541 mCallout->setDrawCalloutToAllParts( active );
542 emit changed();
543 }
544
545
546 //
547 // QgsBalloonCalloutWidget
548 //
549
QgsBalloonCalloutWidget(QgsVectorLayer * vl,QWidget * parent)550 QgsBalloonCalloutWidget::QgsBalloonCalloutWidget( QgsVectorLayer *vl, QWidget *parent )
551 : QgsCalloutWidget( parent, vl )
552 {
553 setupUi( this );
554
555 // Callout options - to move to custom widgets when multiple callout styles exist
556 mCalloutFillStyleButton->setSymbolType( Qgis::SymbolType::Fill );
557 mCalloutFillStyleButton->setDialogTitle( tr( "Balloon Symbol" ) );
558 mCalloutFillStyleButton->registerExpressionContextGenerator( this );
559
560 mCalloutFillStyleButton->setLayer( vl );
561 mOffsetFromAnchorUnitWidget->setUnits( QgsUnitTypes::RenderUnitList() << QgsUnitTypes::RenderMillimeters << QgsUnitTypes::RenderMetersInMapUnits << QgsUnitTypes::RenderMapUnits << QgsUnitTypes::RenderPixels
562 << QgsUnitTypes::RenderPoints << QgsUnitTypes::RenderInches );
563 mMarginUnitWidget->setUnits( QgsUnitTypes::RenderUnitList() << QgsUnitTypes::RenderMillimeters << QgsUnitTypes::RenderMetersInMapUnits << QgsUnitTypes::RenderMapUnits << QgsUnitTypes::RenderPixels
564 << QgsUnitTypes::RenderPoints << QgsUnitTypes::RenderInches );
565 mWedgeWidthUnitWidget->setUnits( QgsUnitTypes::RenderUnitList() << QgsUnitTypes::RenderMillimeters << QgsUnitTypes::RenderMetersInMapUnits << QgsUnitTypes::RenderMapUnits << QgsUnitTypes::RenderPixels
566 << QgsUnitTypes::RenderPoints << QgsUnitTypes::RenderInches );
567 mCornerRadiusUnitWidget->setUnits( QgsUnitTypes::RenderUnitList() << QgsUnitTypes::RenderMillimeters << QgsUnitTypes::RenderMetersInMapUnits << QgsUnitTypes::RenderMapUnits << QgsUnitTypes::RenderPixels
568 << QgsUnitTypes::RenderPoints << QgsUnitTypes::RenderInches );
569
570 mSpinBottomMargin->setClearValue( 0 );
571 mSpinTopMargin->setClearValue( 0 );
572 mSpinRightMargin->setClearValue( 0 );
573 mSpinLeftMargin->setClearValue( 0 );
574 mWedgeWidthSpin->setClearValue( 2.64 );
575 mCornerRadiusSpin->setClearValue( 0.0 );
576
577 connect( mOffsetFromAnchorUnitWidget, &QgsUnitSelectionWidget::changed, this, &QgsBalloonCalloutWidget::offsetFromAnchorUnitWidgetChanged );
578 connect( mOffsetFromAnchorSpin, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsBalloonCalloutWidget::offsetFromAnchorChanged );
579
580 // Anchor point options
581 mAnchorPointComboBox->addItem( tr( "Pole of Inaccessibility" ), static_cast< int >( QgsCallout::PoleOfInaccessibility ) );
582 mAnchorPointComboBox->addItem( tr( "Point on Exterior" ), static_cast< int >( QgsCallout::PointOnExterior ) );
583 mAnchorPointComboBox->addItem( tr( "Point on Surface" ), static_cast< int >( QgsCallout::PointOnSurface ) );
584 mAnchorPointComboBox->addItem( tr( "Centroid" ), static_cast< int >( QgsCallout::Centroid ) );
585 connect( mAnchorPointComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsBalloonCalloutWidget::mAnchorPointComboBox_currentIndexChanged );
586
587 connect( mCalloutFillStyleButton, &QgsSymbolButton::changed, this, &QgsBalloonCalloutWidget::fillSymbolChanged );
588
589 connect( mSpinBottomMargin, qOverload< double >( &QDoubleSpinBox::valueChanged ), this, [ = ]( double value )
590 {
591 QgsMargins margins = mCallout->margins();
592 margins.setBottom( value );
593 mCallout->setMargins( margins );
594 emit changed();
595 } );
596 connect( mSpinTopMargin, qOverload< double >( &QDoubleSpinBox::valueChanged ), this, [ = ]( double value )
597 {
598 QgsMargins margins = mCallout->margins();
599 margins.setTop( value );
600 mCallout->setMargins( margins );
601 emit changed();
602 } );
603 connect( mSpinLeftMargin, qOverload< double >( &QDoubleSpinBox::valueChanged ), this, [ = ]( double value )
604 {
605 QgsMargins margins = mCallout->margins();
606 margins.setLeft( value );
607 mCallout->setMargins( margins );
608 emit changed();
609 } );
610 connect( mSpinRightMargin, qOverload< double >( &QDoubleSpinBox::valueChanged ), this, [ = ]( double value )
611 {
612 QgsMargins margins = mCallout->margins();
613 margins.setRight( value );
614 mCallout->setMargins( margins );
615 emit changed();
616 } );
617 connect( mMarginUnitWidget, &QgsUnitSelectionWidget::changed, this, [ = ]
618 {
619 mCallout->setMarginsUnit( mMarginUnitWidget->unit() );
620 emit changed();
621 } );
622
623 connect( mWedgeWidthUnitWidget, &QgsUnitSelectionWidget::changed, this, [ = ]
624 {
625 mCallout->setWedgeWidthUnit( mWedgeWidthUnitWidget->unit() );
626 mCallout->setWedgeWidthMapUnitScale( mWedgeWidthUnitWidget->getMapUnitScale() );
627 emit changed();
628 } );
629 connect( mWedgeWidthSpin, qOverload< double >( &QDoubleSpinBox::valueChanged ), this, [ = ]( double value )
630 {
631 mCallout->setWedgeWidth( value );
632 emit changed();
633 } );
634
635 connect( mCornerRadiusUnitWidget, &QgsUnitSelectionWidget::changed, this, [ = ]
636 {
637 mCallout->setCornerRadiusUnit( mCornerRadiusUnitWidget->unit() );
638 mCallout->setCornerRadiusMapUnitScale( mCornerRadiusUnitWidget->getMapUnitScale() );
639 emit changed();
640 } );
641 connect( mCornerRadiusSpin, qOverload< double >( &QDoubleSpinBox::valueChanged ), this, [ = ]( double value )
642 {
643 mCallout->setCornerRadius( value );
644 emit changed();
645 } );
646
647 connect( mCalloutBlendComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsBalloonCalloutWidget::mCalloutBlendComboBox_currentIndexChanged );
648 }
649
setCallout(QgsCallout * callout)650 void QgsBalloonCalloutWidget::setCallout( QgsCallout *callout )
651 {
652 if ( !callout )
653 return;
654
655 mCallout.reset( dynamic_cast<QgsBalloonCallout *>( callout->clone() ) );
656 if ( !mCallout )
657 return;
658
659 mOffsetFromAnchorUnitWidget->blockSignals( true );
660 mOffsetFromAnchorUnitWidget->setUnit( mCallout->offsetFromAnchorUnit() );
661 mOffsetFromAnchorUnitWidget->setMapUnitScale( mCallout->offsetFromAnchorMapUnitScale() );
662 mOffsetFromAnchorUnitWidget->blockSignals( false );
663 whileBlocking( mOffsetFromAnchorSpin )->setValue( mCallout->offsetFromAnchor() );
664
665 whileBlocking( mSpinBottomMargin )->setValue( mCallout->margins().bottom() );
666 whileBlocking( mSpinTopMargin )->setValue( mCallout->margins().top() );
667 whileBlocking( mSpinLeftMargin )->setValue( mCallout->margins().left() );
668 whileBlocking( mSpinRightMargin )->setValue( mCallout->margins().right() );
669 whileBlocking( mMarginUnitWidget )->setUnit( mCallout->marginsUnit() );
670
671 mWedgeWidthUnitWidget->blockSignals( true );
672 mWedgeWidthUnitWidget->setUnit( mCallout->wedgeWidthUnit() );
673 mWedgeWidthUnitWidget->setMapUnitScale( mCallout->wedgeWidthMapUnitScale() );
674 mWedgeWidthUnitWidget->blockSignals( false );
675 whileBlocking( mWedgeWidthSpin )->setValue( mCallout->wedgeWidth() );
676
677 mCornerRadiusUnitWidget->blockSignals( true );
678 mCornerRadiusUnitWidget->setUnit( mCallout->cornerRadiusUnit() );
679 mCornerRadiusUnitWidget->setMapUnitScale( mCallout->cornerRadiusMapUnitScale() );
680 mCornerRadiusUnitWidget->blockSignals( false );
681 whileBlocking( mCornerRadiusSpin )->setValue( mCallout->cornerRadius() );
682
683 whileBlocking( mCalloutFillStyleButton )->setSymbol( mCallout->fillSymbol()->clone() );
684
685 whileBlocking( mAnchorPointComboBox )->setCurrentIndex( mAnchorPointComboBox->findData( static_cast< int >( callout->anchorPoint() ) ) );
686
687 whileBlocking( mCalloutBlendComboBox )->setBlendMode( mCallout->blendMode() );
688
689 registerDataDefinedButton( mOffsetFromAnchorDDBtn, QgsCallout::OffsetFromAnchor );
690 registerDataDefinedButton( mAnchorPointDDBtn, QgsCallout::AnchorPointPosition );
691 registerDataDefinedButton( mCalloutBlendModeDDBtn, QgsCallout::BlendMode );
692
693 registerDataDefinedButton( mDestXDDBtn, QgsCallout::DestinationX );
694 registerDataDefinedButton( mDestYDDBtn, QgsCallout::DestinationY );
695 registerDataDefinedButton( mMarginsDDBtn, QgsCallout::Margins );
696 registerDataDefinedButton( mWedgeWidthDDBtn, QgsCallout::WedgeWidth );
697 registerDataDefinedButton( mCornerRadiusDDBtn, QgsCallout::CornerRadius );
698 }
699
setGeometryType(QgsWkbTypes::GeometryType type)700 void QgsBalloonCalloutWidget::setGeometryType( QgsWkbTypes::GeometryType type )
701 {
702 const bool isPolygon = type == QgsWkbTypes::PolygonGeometry;
703 mAnchorPointLbl->setEnabled( isPolygon );
704 mAnchorPointLbl->setVisible( isPolygon );
705 mAnchorPointComboBox->setEnabled( isPolygon );
706 mAnchorPointComboBox->setVisible( isPolygon );
707 mAnchorPointDDBtn->setEnabled( isPolygon );
708 mAnchorPointDDBtn->setVisible( isPolygon );
709 }
710
callout()711 QgsCallout *QgsBalloonCalloutWidget::callout()
712 {
713 return mCallout.get();
714 }
715
offsetFromAnchorUnitWidgetChanged()716 void QgsBalloonCalloutWidget::offsetFromAnchorUnitWidgetChanged()
717 {
718 mCallout->setOffsetFromAnchorUnit( mOffsetFromAnchorUnitWidget->unit() );
719 mCallout->setOffsetFromAnchorMapUnitScale( mOffsetFromAnchorUnitWidget->getMapUnitScale() );
720 emit changed();
721 }
722
offsetFromAnchorChanged()723 void QgsBalloonCalloutWidget::offsetFromAnchorChanged()
724 {
725 mCallout->setOffsetFromAnchor( mOffsetFromAnchorSpin->value() );
726 emit changed();
727 }
728
fillSymbolChanged()729 void QgsBalloonCalloutWidget::fillSymbolChanged()
730 {
731 mCallout->setFillSymbol( mCalloutFillStyleButton->clonedSymbol< QgsFillSymbol >() );
732 emit changed();
733 }
734
mAnchorPointComboBox_currentIndexChanged(int index)735 void QgsBalloonCalloutWidget::mAnchorPointComboBox_currentIndexChanged( int index )
736 {
737 mCallout->setAnchorPoint( static_cast<QgsCallout::AnchorPoint>( mAnchorPointComboBox->itemData( index ).toInt() ) );
738 emit changed();
739 }
740
mCalloutBlendComboBox_currentIndexChanged(int)741 void QgsBalloonCalloutWidget::mCalloutBlendComboBox_currentIndexChanged( int )
742 {
743 mCallout->setBlendMode( mCalloutBlendComboBox->blendMode() );
744 emit changed();
745 }
746
747 ///@endcond
748
749