kplato

kptdurationwidget.ui.h
1//
2// ui.h extension file, included from the uic-generated form implementation.
3//
4// If you wish to add, delete or rename functions or slots use
5// TQt Designer which will update this file, preserving your code. Create an
6// init() function in place of a constructor, and a destroy() function in
7// place of a destructor.
8//
9
10/* This file is part of the KDE project
11 Copyright (C) 2004 - 2006 Dag Andersen <danders@get2net.dk>
12
13 This library is free software; you can redistribute it and/or
14 modify it under the terms of the GNU Library General Public
15 License as published by the Free Software Foundation;
16 version 2 of the License.
17
18 This library is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 Library General Public License for more details.
22
23 You should have received a copy of the GNU Library General Public License
24 along with this library; see the file COPYING.LIB. If not, write to
25 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
26 * Boston, MA 02110-1301, USA.
27*/
28
29#include <kdebug.h>
30#include <tdeglobal.h>
31#include <tdelocale.h>
32
33#include <cmath>
34
35namespace KPlato
36{
37
42{
43 // Which field is to my left, and what conversion factor separates us?
44 TQLineEdit *left;
45 double leftScale;
46
47 // Which field am I, and how am I formatted?
48 TQLineEdit *current;
49 const char *format;
50
51 // Which field is to my right, and what conversion factor separates us?
52 TQLineEdit *right;
53 double rightScale;
54
55 // If I am hidden, who else hides with me?
56 TQLabel *separator;
57
58 // Used for calculating a correct duration
59 double fullScale;
60 double scale;
61
62 // Used for displaying a unit behind each field
63 TQLabel *unit;
64};
65
66#define setField(f, l, ls, c, fmt, r, rs, s, fs, sc, u) \
67do \
68{ \
69 m_fields[f].left = l; \
70 m_fields[f].leftScale = ls; \
71 m_fields[f].current = c; \
72 m_fields[f].format = fmt; \
73 m_fields[f].right = r; \
74 m_fields[f].rightScale = rs; \
75 m_fields[f].separator = s; \
76 m_fields[f].fullScale = fs; \
77 m_fields[f].scale = sc; \
78 m_fields[f].unit = u; \
79} while (0)
80
81void DurationWidget::init()
82{
83 // Use the user's decimal point!
84 m_decimalPoint = TDEGlobal::locale()->decimalSymbol();
85
86 //NOTE:
87 // This isn't as flexible/general as Shaheed once made it.
88 // It's now a long list of hacks.
89 // Introducing double in scales and allowing leftscale/scale/rightscale
90 // *NOT* to be multiples of each other increases the complexity and also
91 // introduces rounding errors. (eg. hour = 60 minutes, day = 7,5 hours)
92 //
93 // If you know how, please make a better solution!
94 //
95 // Any field can be entered as an integer or a floating point value. Whatever
96 // is entered is treated as follows:
97 //
98 // - any fractional part is moved right one field
99 //
100 // - any overflow from the integer part is carried left one field
101 //
102 // and the process repeated until the rightmost and leftmost fields are reached.
103 TQRegExp re(TQString("\\d{1,10}|\\d{1,7}\\") + m_decimalPoint +
104 TQString("\\d{0,10}|\\d{0,7}\\") + m_decimalPoint +
105 TQString("\\d{1,3}"));
106 m_validator = new TQRegExpValidator(re, this);
107 m_ddd->setValidator(m_validator);
108 m_hh->setValidator(m_validator);
109 m_mm->setValidator(m_validator);
110 m_ss->setValidator(m_validator);
111 m_ms->setValidator(m_validator);
112
113 m_ddUnit->hide();
114 m_hhUnit->hide();
115 m_mmUnit->hide();
116 m_ssUnit->hide();
117 m_msUnit->hide();
118
119 m_fields = new FieldDescriptor[5];
120 setField(0, NULL, 0, m_ddd, "%u", m_hh, 24, m_hhSpace, 24, 24, m_ddUnit);
121 setField(1, m_ddd, 24, m_hh, "%02u", m_mm, 60, m_mmColon, 60, 60, m_hhUnit);
122 setField(2, m_hh, 60, m_mm, "%02u", m_ss, 60, NULL, 60, 60, m_mmUnit);
123 setField(3, m_mm, 60, m_ss, "%02u", m_ms, 1000, m_ssColon, 60, 60, m_ssUnit);
124 setField(4, m_ss, 1000, m_ms, "%03u", NULL, 0, m_dot, 1000, 1000, m_msUnit);
125}
126
127void DurationWidget::destroy()
128{
129 delete m_fields;
130 //delete m_validator; //TQWidget takes care of this
131}
132
133TQ_INT64 DurationWidget::setValueMilliseconds(TQ_INT64 milliseconds)
134{
135 unsigned sc = (unsigned)m_fields[4].leftScale;
136 TQ_INT64 secs = milliseconds / sc;
137 TQ_INT64 ms = milliseconds % sc;
138 TQString tmp;
139 tmp.sprintf(m_fields[4].format, ms);
140 m_fields[4].current->setText(tmp);
141 return secs;
142}
143
144TQ_INT64 DurationWidget::setValueSeconds(TQ_INT64 seconds)
145{
146 unsigned sc = (unsigned)m_fields[3].leftScale;
147 TQ_INT64 mins = seconds / sc;
148 TQ_INT64 s = seconds % sc;
149 TQString tmp;
150 tmp.sprintf(m_fields[3].format, s);
151 m_fields[3].current->setText(tmp);
152 return mins;
153}
154
155TQ_INT64 DurationWidget::setValueMinutes(TQ_INT64 mins)
156{
157 unsigned sc = (unsigned)m_fields[2].leftScale;
158 TQ_INT64 hours = mins / sc;
159 TQ_INT64 m = mins % sc;
160 TQString tmp;
161 tmp.sprintf(m_fields[2].format, m);
162 m_fields[2].current->setText(tmp);
163 return hours;
164}
165
166// NOTE: Input is minutes and also returns minutes!
167TQ_INT64 DurationWidget::setValueHours(TQ_INT64 mins)
168{
169 if (m_fields[1].current->isHidden())
170 return mins;
171 unsigned sc = (unsigned)m_fields[1].rightScale;
172 TQ_INT64 hours = (TQ_INT64)(mins / sc);
173 TQ_INT64 m = mins - (TQ_INT64)(hours * sc);
174 //kdDebug()<<k_funcinfo<<"mins="<<mins<<" -> hours="<<hours<<" rem="<<m<<endl;
175 TQString tmp;
176 tmp.sprintf(m_fields[1].format, hours);
177 m_fields[1].current->setText(tmp);
178 return m;
179}
180
181// NOTE: Input is minutes and also returns minutes!
182TQ_INT64 DurationWidget::setValueDays(TQ_INT64 mins)
183{
184 if (m_fields[0].current->isHidden())
185 return mins;
186 double sc = m_fields[1].rightScale * m_fields[0].rightScale;
187 TQ_INT64 days = (TQ_INT64)(mins / sc);
188 TQ_INT64 m = mins - (TQ_INT64)(days * sc);
189 //kdDebug()<<k_funcinfo<<"mins="<<mins<<" -> days="<<days<<" rem="<<m<<endl;
190 TQString tmp;
191 tmp.sprintf(m_fields[0].format, days);
192 m_fields[0].current->setText(tmp);
193 return m;
194}
195
196void DurationWidget::setValue(const KPlato::Duration &newDuration)
197{
198 TQ_INT64 value = newDuration.milliseconds();
199 //kdDebug()<<k_funcinfo<<f<<": value="<<value<<endl;
200 value = setValueMilliseconds(value); // returns seconds
201 value = setValueSeconds(value); // returns minutes
202 // Now call days first to allow for fractional scales
203 value = setValueDays(value); // NOTE returns minutes
204 value = setValueHours(value); // NOTE returns minutes
205 value = setValueMinutes(value); // returns hours: Should be 0
206 if (value > 0) kdError()<<k_funcinfo<<"Remainder > 0: "<<value<<endl;
207
208 emit valueChanged();
209}
210
211Duration DurationWidget::value() const
212{
213 Duration d;
214 int i=0;
215 if (!m_fields[i].current->isHidden() &&
216 m_fields[i].scale > 0 &&
217 m_fields[i].scale <= m_fields[i].fullScale)
218 {
219 double v = m_fields[i].current->text().toDouble();
220 v = v * m_fields[i].scale / m_fields[i].fullScale;;
221 d.addMilliseconds((TQ_INT64)(v*(1000*60*60*24)));
222 }
223 ++i;
224 if (!m_fields[i].current->isHidden() &&
225 m_fields[i].scale > 0 &&
226 m_fields[i].scale <= m_fields[i].fullScale)
227 {
228 double v = m_fields[i].current->text().toDouble();
229 v = v * m_fields[i].scale / m_fields[i].fullScale;;
230 d.addMilliseconds((TQ_INT64)(v*(1000*60*60)));
231 }
232 ++i;
233 if (!m_fields[i].current->isHidden() &&
234 m_fields[i].scale > 0 &&
235 m_fields[i].scale <= m_fields[i].fullScale)
236 {
237 double v = m_fields[i].current->text().toDouble();
238 v = v * m_fields[i].scale / m_fields[i].fullScale;;
239 d.addMilliseconds((TQ_INT64)(v*(1000*60)));
240 }
241 ++i;
242 if (!m_fields[i].current->isHidden() &&
243 m_fields[i].scale > 0 &&
244 m_fields[i].scale <= m_fields[i].fullScale)
245 {
246 double v = m_fields[i].current->text().toDouble();
247 v = v * m_fields[i].scale / m_fields[i].fullScale;;
248 d.addMilliseconds((TQ_INT64)(v*(1000)));
249 }
250 ++i;
251 if (!m_fields[i].current->isHidden())
252 {
253 TQ_INT64 v = m_fields[i].current->text().toUInt();
254 d.addMilliseconds(v);
255 }
256 return d;
257}
258
259void DurationWidget::dddLostFocus()
260{
261 handleLostFocus(0);
262 emit valueChanged();
263}
264
265void DurationWidget::hhLostFocus( )
266{
267 handleLostFocus(1);
268 emit valueChanged();
269}
270
271void DurationWidget::mmLostFocus()
272{
273 handleLostFocus(2);
274 emit valueChanged();
275}
276
277void DurationWidget::ssLostFocus()
278{
279 handleLostFocus(3);
280 emit valueChanged();
281}
282
283void DurationWidget::msLostFocus()
284{
285 handleLostFocus(4);
286 emit valueChanged();
287}
288
289void DurationWidget::handleLostFocus(
290 int field)
291{
292 // Get our own info, and that of our left and right neighbours.
293 TQLineEdit *left = m_fields[field].left;
294 double leftScale = m_fields[field].leftScale;
295 const char *leftFormat = left ? m_fields[field - 1].format : NULL;
296 TQLineEdit *current = m_fields[field].current;
297 const char *currentFormat = m_fields[field].format;
298 TQLineEdit *right = m_fields[field].right;
299 double rightScale = m_fields[field].rightScale;
300 const char *rightFormat = right ? m_fields[field + 1].format : NULL;
301
302 // avoid possible crash
303 if (leftScale == 0)
304 leftScale = 1;
305
306 // Get the text and start processing...
307 TQString newValue(current->text());
308 double v = TDEGlobal::locale()->readNumber(newValue);
309 unsigned currentValue = 0;
310 TQString tmp;
311 //kdDebug()<<k_funcinfo<<field<<": value="<<v<<" v="<<v<<endl;
312 if (left && v >= leftScale)
313 {
314 //kdDebug()<<k_funcinfo<<field<<": value="<<v<<" leftScale="<<leftScale<<endl;
315 // Carry overflow, recurse as required.
316 tmp.sprintf(leftFormat, (unsigned)(v / leftScale));
317 left->setText(tmp);
318 handleLostFocus(field - 1);
319
320 // Get remainder.
321 v = v - (tmp.toUInt() * leftScale);
322 newValue = TDEGlobal::locale()->formatNumber(v);
323 }
324 int point = newValue.find(m_decimalPoint);
325 if (point != -1)
326 {
327 //HACK doubles may be rounded(at fractions > 6 digits on my system)
328 int p;
329 double frac = fraction(newValue, &p);
330 if (right && frac > 0.0)
331 {
332 //kdDebug()<<k_funcinfo<<field<<": value="<<newValue<<" rightScale="<<rightScale<<" frac="<<frac<<" ("<<newValue.mid(point)<<")"<<endl;
333 // Propagate fraction
334 v = rightScale * (frac*power(10.0, -p));
335 frac = fraction(TDEGlobal::locale()->formatNumber(v, 19), 0);
336 //kdDebug()<<k_funcinfo<<field<<": v="<<v<<" ("<<(unsigned)v<<") rest="<<frac<<endl;
337 if (frac > 0.0)
338 {
339 tmp = TDEGlobal::locale()->formatNumber(v, 19);
340 right->setText(tmp);
341 handleLostFocus(field + 1);
342 } else {
343 tmp.sprintf(rightFormat, (unsigned)(v));
344 right->setText(tmp);
345 }
346
347 }
348 // TODO keep fraction for last shown field
349 newValue = newValue.left(point);
350 }
351 currentValue = newValue.toUInt();
352 tmp.sprintf(currentFormat, currentValue);
353 current->setText(tmp);
354}
355
356// Set which fields are visible.
357void DurationWidget::setVisibleFields( int fieldMask )
358{
359 int i;
360 for (i = 0; i < 5; i++)
361 {
362 bool visible = ((fieldMask >> i) & 1) == 1;
363
364 // Set the visibility of the fields, and of any associated separator.
365 if (visible)
366 {
367 m_fields[i].current->show();
368 if (m_fields[i].separator)
369 {
370 m_fields[i].separator->show();
371 }
372 if (m_fields[i].unit)
373 {
374 m_fields[i].unit->show();
375 }
376 }
377 else
378 {
379 m_fields[i].current->hide();
380 if (m_fields[i].separator)
381 {
382 m_fields[i].separator->hide();
383 }
384 if (m_fields[i].unit)
385 {
386 m_fields[i].unit->hide();
387 }
388 }
389 }
390}
391
392// Retreive the visible fields.
393int DurationWidget::visibleFields()
394{
395 int i;
396 int fieldMask = 0;
397 for (i = 0; i < 5; i++)
398 {
399 if (m_fields[i].current->isHidden())
400 {
401 fieldMask |= (1 << i);
402 }
403 }
404 return fieldMask;
405}
406
407void DurationWidget::setFieldLeftscale(int f, double ls)
408{
409 m_fields[f].leftScale = ls;
410}
411
412void DurationWidget::setFieldRightscale(int f, double rs)
413{
414 m_fields[f].rightScale = rs;
415}
416
417void DurationWidget::setFieldScale(int f, double scale)
418{
419 m_fields[f].scale = scale;
420}
421
422void DurationWidget::setFieldUnit(int f, TQString unit)
423{
424 if (m_fields[f].unit)
425 {
426 m_fields[f].unit->setText(unit);
427 }
428}
429
430double DurationWidget::power(double m, int e) {
431 int c = e > 0 ? e : -e;
432 double value = 1;
433 for (int i=0; i < c; ++i) {
434 value = e > 0 ? value * m : value / m;
435 }
436 return value;
437}
438
439double DurationWidget::fraction(TQString number, int *exp) {
440 int point = number.find(m_decimalPoint);
441 if (point == -1) {
442 return 0.0;
443 }
444 TQString v;
445 if (exp) {
446 v = number.mid(point+m_decimalPoint.length());
447 *exp = v.length();
448
449 } else {
450 v = number.mid(point);
451 }
452 return TDEGlobal::locale()->readNumber(v);
453}
454
455} //KPlato namespace
The duration class can be used to store a timespan in a convenient format.
Definition kptduration.h:34
This structure describes one of the fields shown.