1 | /* This file is part of the KDE project |
2 | Copyright (C) 2007 Dag Andersen <danders@get2net.dk> |
3 | |
4 | This library is free software; you can redistribute it and/or |
5 | modify it under the terms of the GNU Library General Public |
6 | License as published by the Free Software Foundation; either |
7 | version 2 of the License, or (at your option) any later version. |
8 | |
9 | This library is distributed in the hope that it will be useful, |
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | Library General Public License for more details. |
13 | |
14 | You should have received a copy of the GNU Library General Public License |
15 | along with this library; see the file COPYING.LIB. If not, write to |
16 | the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
17 | * Boston, MA 02110-1301, USA. |
18 | */ |
19 | |
20 | |
21 | #include <kptdurationspinbox.h> |
22 | |
23 | #include "kptnode.h" |
24 | |
25 | #include <QEvent> |
26 | #include <QLineEdit> |
27 | #include <QLocale> |
28 | #include <QValidator> |
29 | #include <QKeyEvent> |
30 | |
31 | #include <kdebug.h> |
32 | #include <knumvalidator.h> |
33 | |
34 | #include <math.h> |
35 | #include <limits.h> |
36 | |
37 | namespace KPlato |
38 | { |
39 | |
40 | DurationSpinBox::DurationSpinBox(QWidget *parent) |
41 | : QDoubleSpinBox(parent), |
42 | m_unit( Duration::Unit_d ), |
43 | m_minunit( Duration::Unit_h ), |
44 | m_maxunit( Duration::Unit_Y ) |
45 | { |
46 | setUnit( Duration::Unit_h ); |
47 | setMaximum(140737488355328.0); //Hmmmm |
48 | |
49 | connect( lineEdit(), SIGNAL(textChanged(QString)), SLOT(editorTextChanged(QString)) ); |
50 | |
51 | } |
52 | |
53 | void DurationSpinBox::setUnit( Duration::Unit unit ) |
54 | { |
55 | if ( unit < m_maxunit ) { |
56 | m_maxunit = unit; |
57 | } else if ( unit > m_minunit ) { |
58 | m_minunit = unit; |
59 | } |
60 | m_unit = unit; |
61 | setValue( value() ); |
62 | } |
63 | |
64 | void DurationSpinBox::setMaximumUnit( Duration::Unit unit ) |
65 | { |
66 | //NOTE Year = 0, Milliseconds = 7 !!! |
67 | m_maxunit = unit; |
68 | if ( m_minunit < unit ) { |
69 | m_minunit = unit; |
70 | } |
71 | if ( m_unit < unit ) { |
72 | setUnit( unit ); |
73 | emit unitChanged( m_unit ); |
74 | } |
75 | } |
76 | |
77 | void DurationSpinBox::setMinimumUnit( Duration::Unit unit ) |
78 | { |
79 | //NOTE Year = 0, Milliseconds = 7 !!! |
80 | m_minunit = unit; |
81 | if ( m_maxunit > unit ) { |
82 | m_maxunit = unit; |
83 | } |
84 | if ( m_unit > unit ) { |
85 | setUnit( unit ); |
86 | emit unitChanged( m_unit ); |
87 | } |
88 | } |
89 | |
90 | void DurationSpinBox::stepUnitUp() |
91 | { |
92 | //kDebug(planDbg())<<m_unit<<">"<<m_maxunit; |
93 | if ( m_unit > m_maxunit ) { |
94 | setUnit( static_cast<Duration::Unit>(m_unit - 1) ); |
95 | // line may change length, make sure cursor stays within unit |
96 | lineEdit()->setCursorPosition( lineEdit()->displayText().length() - suffix().length() ); |
97 | emit unitChanged( m_unit ); |
98 | } |
99 | } |
100 | |
101 | void DurationSpinBox::stepUnitDown() |
102 | { |
103 | //kDebug(planDbg())<<m_unit<<"<"<<m_minunit; |
104 | if ( m_unit < m_minunit ) { |
105 | setUnit( static_cast<Duration::Unit>(m_unit + 1) ); |
106 | // line may change length, make sure cursor stays within unit |
107 | lineEdit()->setCursorPosition( lineEdit()->displayText().length() - suffix().length() ); |
108 | emit unitChanged( m_unit ); |
109 | } |
110 | } |
111 | |
112 | void DurationSpinBox::stepBy( int steps ) |
113 | { |
114 | //kDebug(planDbg())<<steps; |
115 | int cpos = lineEdit()->cursorPosition(); |
116 | if ( isOnUnit() ) { |
117 | // we are in unit |
118 | if ( steps > 0 ) { |
119 | stepUnitUp(); |
120 | } else if ( steps < 0 ) { |
121 | stepUnitDown(); |
122 | } |
123 | lineEdit()->setCursorPosition( cpos ); |
124 | return; |
125 | } |
126 | QDoubleSpinBox::stepBy( steps ); |
127 | // QDoubleSpinBox selects the whole text and the cursor might end up at the end (in the unit field) |
128 | lineEdit()->setCursorPosition( cpos ); // also deselects |
129 | } |
130 | |
131 | QAbstractSpinBox::StepEnabled DurationSpinBox::stepEnabled () const |
132 | { |
133 | if ( isOnUnit() ) { |
134 | if ( m_unit >= m_minunit ) { |
135 | //kDebug(planDbg())<<"inside unit, up"<<m_unit<<m_minunit<<m_maxunit; |
136 | return QAbstractSpinBox::StepUpEnabled; |
137 | } |
138 | if ( m_unit <= m_maxunit ) { |
139 | //kDebug(planDbg())<<"inside unit, down"<<m_unit<<m_minunit<<m_maxunit; |
140 | return QAbstractSpinBox::StepDownEnabled; |
141 | } |
142 | //kDebug(planDbg())<<"inside unit, up|down"<<m_unit<<m_minunit<<m_maxunit; |
143 | return QAbstractSpinBox::StepUpEnabled | QAbstractSpinBox::StepDownEnabled; |
144 | } |
145 | return QDoubleSpinBox::stepEnabled(); |
146 | } |
147 | |
148 | bool DurationSpinBox::isOnUnit() const |
149 | { |
150 | int pos = lineEdit()->cursorPosition(); |
151 | return ( pos <= text().size() - suffix().size() ) && |
152 | ( pos > text().size() - suffix().size() - Duration::unitToString( m_unit, true ).size() ); |
153 | } |
154 | |
155 | void DurationSpinBox::keyPressEvent( QKeyEvent * event ) |
156 | { |
157 | //kDebug(planDbg())<<lineEdit()->cursorPosition()<<","<<(text().size() - Duration::unitToString( m_unit, true ).size())<<""<<event->text().isEmpty(); |
158 | if ( isOnUnit() ) { |
159 | // we are in unit |
160 | switch (event->key()) { |
161 | case Qt::Key_Up: |
162 | event->accept(); |
163 | stepBy( 1 ); |
164 | return; |
165 | case Qt::Key_Down: |
166 | event->accept(); |
167 | stepBy( -1 ); |
168 | return; |
169 | default: |
170 | break; |
171 | } |
172 | } |
173 | QDoubleSpinBox::keyPressEvent(event); |
174 | } |
175 | |
176 | // handle unit, QDoubleSpinBox handles value, signals etc |
177 | void DurationSpinBox::editorTextChanged( const QString &text ) { |
178 | //kDebug(planDbg())<<text; |
179 | QString s = text; |
180 | int pos = lineEdit()->cursorPosition(); |
181 | if ( validate( s, pos ) == QValidator::Acceptable ) { |
182 | s = extractUnit( s ); |
183 | if ( ! s.isEmpty() ) { |
184 | updateUnit( (Duration::Unit)Duration::unitList( true ).indexOf( s ) ); |
185 | } |
186 | } |
187 | } |
188 | |
189 | double DurationSpinBox::valueFromText( const QString & text ) const |
190 | { |
191 | QString s = extractValue( text ).remove( KGlobal::locale()->thousandsSeparator() ); |
192 | bool ok = false; |
193 | double v = KGlobal::locale()->readNumber( s, &ok ); |
194 | if ( ! ok ) { |
195 | v = QDoubleSpinBox::valueFromText( s ); |
196 | } |
197 | return v; |
198 | } |
199 | |
200 | QString DurationSpinBox::textFromValue ( double value ) const |
201 | { |
202 | QString s = KGlobal::locale()->formatNumber( qMin( qMax( minimum(), value ), maximum() ), decimals() ); |
203 | s += Duration::unitToString( m_unit, true ); |
204 | //kDebug(planDbg())<<2<<value<<s; |
205 | return s; |
206 | } |
207 | |
208 | QValidator::State DurationSpinBox::validate ( QString & input, int & pos ) const |
209 | { |
210 | //kDebug(planDbg())<<input; |
211 | KDoubleValidator validator( minimum(), maximum(), decimals(), 0 ); |
212 | if ( input.isEmpty() ) { |
213 | return validator.validate ( input, pos ); |
214 | } |
215 | QString s = extractUnit( input ); |
216 | if ( s.isEmpty() ) { |
217 | return validator.validate ( input, pos ); |
218 | } |
219 | int idx = Duration::unitList( true ).indexOf( s ); |
220 | if ( idx < m_maxunit || idx > m_minunit ) { |
221 | return QValidator::Invalid; |
222 | } |
223 | s = extractValue( input ); |
224 | int p = 0; |
225 | return validator.validate ( s, p ); // pos doesn't matter |
226 | } |
227 | |
228 | QString DurationSpinBox:: ( const QString &text ) const |
229 | { |
230 | //kDebug(planDbg())<<text; |
231 | QString s; |
232 | for ( int i = text.length() - 1; i >= 0; --i ) { |
233 | QChar c = text[ i ]; |
234 | if ( ! c.isLetter() ) { |
235 | break; |
236 | } |
237 | s.prepend( c ); |
238 | } |
239 | if ( Duration::unitList( true ).contains( s ) ) { |
240 | return s; |
241 | } |
242 | return QString(); |
243 | } |
244 | |
245 | QString DurationSpinBox:: ( const QString &text ) const |
246 | { |
247 | //kDebug(planDbg())<<text; |
248 | QString s = extractUnit( text ); |
249 | if ( Duration::unitList( true ).contains( s ) ) { |
250 | return text.left( text.length() - s.length() ); |
251 | } |
252 | return text; |
253 | } |
254 | |
255 | void DurationSpinBox::updateUnit( Duration::Unit unit ) |
256 | { |
257 | if ( unit < m_maxunit ) { |
258 | m_unit = m_maxunit; |
259 | } else if ( unit > m_minunit ) { |
260 | m_unit = m_minunit; |
261 | } |
262 | if ( m_unit != unit ) { |
263 | m_unit = unit; |
264 | emit unitChanged( unit ); |
265 | } |
266 | } |
267 | |
268 | |
269 | } //namespace KPlato |
270 | |
271 | #include "kptdurationspinbox.moc" |
272 | |
273 | |