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