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
37namespace KPlato
38{
39
40DurationSpinBox::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
53void 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
64void 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
77void 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
90void 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
101void 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
112void 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
131QAbstractSpinBox::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
148bool 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
155void 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
177void 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
189double 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
200QString 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
208QValidator::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
228QString DurationSpinBox::extractUnit ( 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
245QString DurationSpinBox::extractValue ( 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
255void 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