1 /***************************************************************************
2     qgsrangewidgetwrapper.cpp
3      --------------------------------------
4     Date                 : 5.1.2014
5     Copyright            : (C) 2014 Matthias Kuhn
6     Email                : matthias at opengis dot ch
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 <QSettings>
17 
18 #include "qgsrangewidgetwrapper.h"
19 #include "qgsspinbox.h"
20 #include "qgsdoublespinbox.h"
21 #include "qgsvectorlayer.h"
22 #include "qgsdial.h"
23 #include "qgsslider.h"
24 #include "qgsapplication.h"
25 
26 
27 
QgsRangeWidgetWrapper(QgsVectorLayer * layer,int fieldIdx,QWidget * editor,QWidget * parent)28 QgsRangeWidgetWrapper::QgsRangeWidgetWrapper( QgsVectorLayer *layer, int fieldIdx, QWidget *editor, QWidget *parent )
29   : QgsEditorWidgetWrapper( layer, fieldIdx, editor, parent )
30 
31 {
32 }
33 
createWidget(QWidget * parent)34 QWidget *QgsRangeWidgetWrapper::createWidget( QWidget *parent )
35 {
36   QWidget *editor = nullptr;
37 
38   if ( config( QStringLiteral( "Style" ) ).toString() == QLatin1String( "Dial" ) )
39   {
40     editor = new QgsDial( parent );
41   }
42   else if ( config( QStringLiteral( "Style" ) ).toString() == QLatin1String( "Slider" ) )
43   {
44     editor = new QgsSlider( Qt::Horizontal, parent );
45   }
46   else
47   {
48     switch ( layer()->fields().at( fieldIdx() ).type() )
49     {
50       case QVariant::Double:
51       {
52         editor = new QgsDoubleSpinBox( parent );
53         static_cast<QgsDoubleSpinBox *>( editor )->setLineEditAlignment( Qt::AlignRight );
54         break;
55       }
56       case QVariant::Int:
57       case QVariant::LongLong:
58       default:
59         editor = new QgsSpinBox( parent );
60         static_cast<QgsSpinBox *>( editor )->setLineEditAlignment( Qt::AlignRight );
61         break;
62     }
63   }
64 
65   return editor;
66 }
67 
68 template<class T>
setupIntEditor(const QVariant & min,const QVariant & max,const QVariant & step,T * slider,QgsRangeWidgetWrapper * wrapper)69 static void setupIntEditor( const QVariant &min, const QVariant &max, const QVariant &step, T *slider, QgsRangeWidgetWrapper *wrapper )
70 {
71   // must use a template function because those methods are overloaded and not inherited by some classes
72   slider->setMinimum( min.isValid() ? min.toInt() : std::numeric_limits<int>::lowest() );
73   slider->setMaximum( max.isValid() ? max.toInt() : std::numeric_limits<int>::max() );
74   slider->setSingleStep( step.isValid() ? step.toInt() : 1 );
75   QObject::connect( slider, SIGNAL( valueChanged( int ) ), wrapper, SLOT( emitValueChanged() ) );
76 }
77 
initWidget(QWidget * editor)78 void QgsRangeWidgetWrapper::initWidget( QWidget *editor )
79 {
80   mDoubleSpinBox = qobject_cast<QDoubleSpinBox *>( editor );
81   mIntSpinBox = qobject_cast<QSpinBox *>( editor );
82 
83   mDial = qobject_cast<QDial *>( editor );
84   mSlider = qobject_cast<QSlider *>( editor );
85   mQgsDial = qobject_cast<QgsDial *>( editor );
86   mQgsSlider = qobject_cast<QgsSlider *>( editor );
87 
88   bool allowNull = config( QStringLiteral( "AllowNull" ), true ).toBool();
89 
90   QVariant min( config( QStringLiteral( "Min" ) ) );
91   QVariant max( config( QStringLiteral( "Max" ) ) );
92   QVariant step( config( QStringLiteral( "Step" ) ) );
93   QVariant precision( config( QStringLiteral( "Precision" ) ) );
94 
95   if ( mDoubleSpinBox )
96   {
97     double stepval = step.isValid() ? step.toDouble() : 1.0;
98     double minval = min.isValid() ? min.toDouble() : std::numeric_limits<double>::lowest();
99     double maxval  = max.isValid() ? max.toDouble() : std::numeric_limits<double>::max();
100     int precisionval = precision.isValid() ? precision.toInt() : layer()->fields().at( fieldIdx() ).precision();
101 
102     mDoubleSpinBox->setDecimals( precisionval );
103 
104     QgsDoubleSpinBox *qgsWidget = qobject_cast<QgsDoubleSpinBox *>( mDoubleSpinBox );
105 
106 
107     if ( qgsWidget )
108       qgsWidget->setShowClearButton( allowNull );
109     // Make room for null value: lower the minimum to allow for NULL special values
110     if ( allowNull )
111     {
112       double decr;
113       if ( precisionval > 0 )
114       {
115         decr = std::pow( 10, -precisionval );
116       }
117       else
118       {
119         decr = stepval;
120       }
121       minval -= decr;
122       // Note: call setMinimum here or setValue won't work
123       mDoubleSpinBox->setMinimum( minval );
124       mDoubleSpinBox->setValue( minval );
125       QgsDoubleSpinBox *doubleSpinBox( qobject_cast<QgsDoubleSpinBox *>( mDoubleSpinBox ) );
126       if ( doubleSpinBox )
127         doubleSpinBox->setSpecialValueText( QgsApplication::nullRepresentation() );
128       else
129         mDoubleSpinBox->setSpecialValueText( QgsApplication::nullRepresentation() );
130     }
131     mDoubleSpinBox->setMinimum( minval );
132     mDoubleSpinBox->setMaximum( maxval );
133     mDoubleSpinBox->setSingleStep( stepval );
134     if ( config( QStringLiteral( "Suffix" ) ).isValid() )
135       mDoubleSpinBox->setSuffix( config( QStringLiteral( "Suffix" ) ).toString() );
136 
137     connect( mDoubleSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ),
138     this, [ = ]( double ) { emitValueChanged(); } );
139   }
140   else if ( mIntSpinBox )
141   {
142     QgsSpinBox *qgsWidget = qobject_cast<QgsSpinBox *>( mIntSpinBox );
143     if ( qgsWidget )
144       qgsWidget->setShowClearButton( allowNull );
145     int minval =  min.isValid() ? min.toInt() : std::numeric_limits<int>::lowest();
146     int maxval = max.isValid() ? max.toInt() : std::numeric_limits<int>::max();
147     uint stepval = step.isValid() ? step.toUInt() : 1;
148     if ( allowNull )
149     {
150       // make sure there is room for a new value (i.e. signed integer does not overflow)
151       int minvalOverflow = uint( minval ) - stepval;
152       if ( minvalOverflow < minval )
153       {
154         minval = minvalOverflow;
155       }
156       mIntSpinBox->setValue( minval );
157       QgsSpinBox *intSpinBox( qobject_cast<QgsSpinBox *>( mIntSpinBox ) );
158       if ( intSpinBox )
159         intSpinBox->setSpecialValueText( QgsApplication::nullRepresentation() );
160       else
161         mIntSpinBox->setSpecialValueText( QgsApplication::nullRepresentation() );
162     }
163     setupIntEditor( minval, maxval, stepval, mIntSpinBox, this );
164     if ( config( QStringLiteral( "Suffix" ) ).isValid() )
165       mIntSpinBox->setSuffix( config( QStringLiteral( "Suffix" ) ).toString() );
166   }
167   else
168   {
169     ( void )field().convertCompatible( min );
170     ( void )field().convertCompatible( max );
171     ( void )field().convertCompatible( step );
172     if ( mQgsDial )
173       setupIntEditor( min, max, step, mQgsDial, this );
174     else if ( mQgsSlider )
175       setupIntEditor( min, max, step, mQgsSlider, this );
176     else if ( mDial )
177       setupIntEditor( min, max, step, mDial, this );
178     else if ( mSlider )
179       setupIntEditor( min, max, step, mSlider, this );
180   }
181 }
182 
valid() const183 bool QgsRangeWidgetWrapper::valid() const
184 {
185   return mSlider || mDial || mQgsDial || mQgsSlider || mIntSpinBox || mDoubleSpinBox;
186 }
187 
valueChangedVariant(const QVariant & v)188 void QgsRangeWidgetWrapper::valueChangedVariant( const QVariant &v )
189 {
190   if ( v.type() == QVariant::Int )
191   {
192     Q_NOWARN_DEPRECATED_PUSH
193     emit valueChanged( v.toInt() );
194     Q_NOWARN_DEPRECATED_POP
195     emit valuesChanged( v.toInt() );
196   }
197   if ( v.type() == QVariant::Double )
198   {
199     Q_NOWARN_DEPRECATED_PUSH
200     emit valueChanged( v.toDouble() );
201     Q_NOWARN_DEPRECATED_POP
202     emit valuesChanged( v.toDouble() );
203   }
204 }
205 
value() const206 QVariant QgsRangeWidgetWrapper::value() const
207 {
208   QVariant value;
209 
210   if ( mDoubleSpinBox )
211   {
212     value = mDoubleSpinBox->value();
213     if ( value == mDoubleSpinBox->minimum() && config( QStringLiteral( "AllowNull" ), true ).toBool() )
214     {
215       value = QVariant( field().type() );
216     }
217   }
218   else if ( mIntSpinBox )
219   {
220     value = mIntSpinBox->value();
221     if ( value == mIntSpinBox->minimum() && config( QStringLiteral( "AllowNull" ), true ).toBool() )
222     {
223       value = QVariant( field().type() );
224     }
225   }
226   else if ( mQgsDial )
227   {
228     value = mQgsDial->variantValue();
229   }
230   else if ( mQgsSlider )
231   {
232     value = mQgsSlider->variantValue();
233   }
234   else if ( mDial )
235   {
236     value = mDial->value();
237   }
238   else if ( mSlider )
239   {
240     value = mSlider->value();
241   }
242 
243   return value;
244 }
245 
updateValues(const QVariant & value,const QVariantList &)246 void QgsRangeWidgetWrapper::updateValues( const QVariant &value, const QVariantList & )
247 {
248   if ( mDoubleSpinBox )
249   {
250     if ( value.isNull() && config( QStringLiteral( "AllowNull" ), true ).toBool() )
251     {
252       mDoubleSpinBox->setValue( mDoubleSpinBox->minimum() );
253     }
254     else
255     {
256       mDoubleSpinBox->setValue( value.toDouble() );
257     }
258   }
259 
260   if ( mIntSpinBox )
261   {
262     if ( value.isNull() && config( QStringLiteral( "AllowNull" ), true ).toBool() )
263     {
264       mIntSpinBox->setValue( mIntSpinBox->minimum() );
265     }
266     else
267     {
268       mIntSpinBox->setValue( value.toInt() );
269     }
270   }
271 
272   if ( mQgsDial )
273   {
274     mQgsDial->setValue( value );
275   }
276   else if ( mQgsSlider )
277   {
278     mQgsSlider->setValue( value );
279   }
280   else if ( mDial )
281   {
282     mDial->setValue( value.toInt() );
283   }
284   else if ( mSlider )
285   {
286     mSlider->setValue( value.toInt() );
287   }
288 }
289