QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
qgsscalecombobox.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsscalecombobox.h
3 ------------------------
4 begin : January 7, 2012
5 copyright : (C) 2012 by Alexander Bruy
6 email : alexander dot bruy at gmail dot com
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 "qgis.h"
20#include "qgsscalecombobox.h"
22
23#include <QAbstractItemView>
24#include <QLocale>
25#include <QLineEdit>
26
28 : QComboBox( parent )
29{
31
32 setEditable( true );
33 setInsertPolicy( QComboBox::NoInsert );
34 setCompleter( nullptr );
35 connect( this, qOverload< int >( &QComboBox::activated ), this, &QgsScaleComboBox::fixupScale );
36 connect( lineEdit(), &QLineEdit::editingFinished, this, &QgsScaleComboBox::fixupScale );
37 fixupScale();
38}
39
40void QgsScaleComboBox::updateScales( const QStringList &scales )
41{
42 QStringList myScalesList;
43 const QString oldScale = currentText();
44
45 if ( scales.isEmpty() )
46 {
48 }
49 else
50 {
51 QStringList::const_iterator scaleIt = scales.constBegin();
52 for ( ; scaleIt != scales.constEnd(); ++scaleIt )
53 {
54 myScalesList.append( *scaleIt );
55 }
56 }
57
58 QStringList myCleanedScalesList;
59
60 for ( int i = 0; i < myScalesList.size(); ++i )
61 {
62 const QStringList parts = myScalesList[ i ] .split( ':' );
63 if ( parts.size() < 2 )
64 continue;
65
66 bool ok = false;
67 const double denominator = QLocale().toDouble( parts[1], &ok );
68 if ( ok )
69 {
70 myCleanedScalesList.push_back( toString( denominator ) );
71 }
72 else
73 {
74 const double denominator = parts[1].toDouble( &ok );
75 if ( ok )
76 {
77 myCleanedScalesList.push_back( toString( denominator ) );
78 }
79 }
80 }
81
82 blockSignals( true );
83 clear();
84 addItems( myCleanedScalesList );
85 setScaleString( oldScale );
86 blockSignals( false );
87}
88
90{
91 QComboBox::showPopup();
92
93 if ( !currentText().contains( ':' ) )
94 {
95 return;
96 }
97 QStringList parts = currentText().split( ':' );
98 bool ok;
99 int idx = 0;
100 int min = 999999;
101 const long currScale = parts.at( 1 ).toLong( &ok );
102 long nextScale, delta;
103 for ( int i = 0; i < count(); i++ )
104 {
105 parts = itemText( i ).split( ':' );
106 nextScale = parts.at( 1 ).toLong( &ok );
107 delta = std::labs( currScale - nextScale );
108 if ( delta < min )
109 {
110 min = delta;
111 idx = i;
112 }
113 }
114
115 blockSignals( true );
116 view()->setCurrentIndex( model()->index( idx, 0 ) );
117 blockSignals( false );
118 view()->setMinimumWidth( view()->sizeHintForColumn( 0 ) );
119}
120
122{
123 return toString( mScale );
124}
125
126bool QgsScaleComboBox::setScaleString( const QString &string )
127{
128 const double oldScale = mScale;
129 if ( mAllowNull && string.trimmed().isEmpty() )
130 {
131 mScale = std::numeric_limits< double >::quiet_NaN();
132 setEditText( toString( mScale ) );
133 clearFocus();
134 if ( !std::isnan( oldScale ) )
135 {
136 emit scaleChanged( mScale );
137 }
138 return true;
139 }
140
141 bool ok;
142 double newScale = toDouble( string, &ok );
143 if ( newScale > mMinScale && newScale != 0 && mMinScale != 0 )
144 {
145 newScale = mMinScale;
146 }
147 if ( ! ok )
148 {
149 return false;
150 }
151 else
152 {
153 mScale = newScale;
154 setEditText( toString( mScale ) );
155 clearFocus();
156 if ( mScale != oldScale )
157 {
158 emit scaleChanged( mScale );
159 }
160 return true;
161 }
162}
163
165{
166 return mScale;
167}
168
170{
171 return std::isnan( mScale );
172}
173
174void QgsScaleComboBox::setScale( double scale )
175{
177}
178
179void QgsScaleComboBox::fixupScale()
180{
181 if ( mAllowNull && currentText().trimmed().isEmpty() )
182 {
183 setScale( std::numeric_limits< double >::quiet_NaN() );
184 return;
185 }
186
187 const QStringList txtList = currentText().split( ':' );
188 const bool userSetScale = txtList.size() != 2;
189
190 bool ok;
191 double newScale = toDouble( currentText(), &ok );
192
193 // Valid string representation
194 if ( ok )
195 {
196 // if a user types scale = 2345, we transform to 1:2345
197 if ( userSetScale && newScale < 1.0 && !qgsDoubleNear( newScale, 0.0 ) )
198 {
199 newScale = 1 / newScale;
200 }
201 setScale( newScale );
202 }
203 else
204 {
205 setScale( mScale );
206 }
207}
208
209QString QgsScaleComboBox::toString( double scale )
210{
211 if ( std::isnan( scale ) )
212 {
213 return QString();
214 }
215 if ( scale == 0 )
216 {
217 return QStringLiteral( "0" );
218 }
219 else if ( scale <= 1 )
220 {
221 return QStringLiteral( "%1:1" ).arg( QLocale().toString( static_cast< int >( std::round( 1.0 / scale ) ) ) );
222 }
223 else
224 {
225 return QStringLiteral( "1:%1" ).arg( QLocale().toString( static_cast< float >( std::round( scale ) ), 'f', 0 ) );
226 }
227}
228
229double QgsScaleComboBox::toDouble( const QString &scaleString, bool *returnOk )
230{
231 bool ok = false;
232 QString scaleTxt( scaleString );
233
234 const double denominator = qgsPermissiveToDouble( scaleTxt, ok );
235 double scale = !qgsDoubleNear( denominator, 0.0 ) ? 1.0 / denominator : 0.0;
236 if ( ok )
237 {
238 // Create a text version and set that text and rescan
239 // Idea is to get the same rounding.
240 scaleTxt = toString( scale );
241 }
242 else
243 {
244 // It is now either X:Y or not valid
245 QStringList txtList = scaleTxt.split( ':' );
246 if ( 2 == txtList.size() )
247 {
248 bool okX = false;
249 bool okY = false;
250 const int x = qgsPermissiveToInt( txtList[ 0 ], okX );
251 const int y = qgsPermissiveToInt( txtList[ 1 ], okY );
252 if ( okX && okY && x != 0 )
253 {
254 // Scale is fraction of x and y
255 scale = static_cast< double >( y ) / static_cast< double >( x );
256 ok = true;
257 }
258 }
259 }
260
261 // Set up optional return flag
262 if ( returnOk )
263 {
264 *returnOk = ok;
265 }
266 return scale;
267}
268
269void QgsScaleComboBox::setAllowNull( bool allowNull )
270{
271 mAllowNull = allowNull;
272 lineEdit()->setClearButtonEnabled( allowNull );
273 updateScales();
274}
275
277{
278 return mAllowNull;
279}
280
282{
283 mMinScale = scale;
284 if ( mScale > mMinScale && mScale != 0 && mMinScale != 0 )
285 {
286 setScale( mMinScale );
287 }
288}
289
291{
292 if ( allowNull() )
293 setScale( std::numeric_limits< double >::quiet_NaN() );
294}
static QString toString(double scale)
Helper function to convert a scale double to scale string.
void updateScales(const QStringList &scales=QStringList())
Sets the list of predefined scales to show in the combobox.
QString scaleString() const
Returns the selected scale as a string, e.g.
bool setScaleString(const QString &string)
Set the selected scale from a string, e.g.
void setAllowNull(bool allowNull)
Sets whether the scale combobox can be set to a NULL value.
QgsScaleComboBox(QWidget *parent=nullptr)
Constructor for QgsScaleComboBox.
bool isNull() const
Returns true if the combo box is currently set to a "null" value.
bool allowNull() const
Returns true if the combobox can be set to a NULL value.
static double toDouble(const QString &string, bool *ok=nullptr)
Helper function to convert a scale string to double.
void setScale(double scale)
Set the selected scale from a double.
void showPopup() override
void setNull()
Sets the combo box to the null value.
void setMinScale(double scale)
Set the minimum allowed scale.
void scaleChanged(double scale)
Emitted when user has finished editing/selecting a new scale.
T value(const QString &dynamicKeyPart=QString()) const
Returns settings value.
static const QgsSettingsEntryStringList * settingsMapScales
double qgsPermissiveToDouble(QString string, bool &ok)
Converts a string to a double in a permissive way, e.g., allowing for incorrect numbers of digits bet...
Definition: qgis.cpp:72
int qgsPermissiveToInt(QString string, bool &ok)
Converts a string to an integer in a permissive way, e.g., allowing for incorrect numbers of digits b...
Definition: qgis.cpp:79
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:5207