F:/KPlato/koffice/kplato/kptdurationwidget.cc

Aller à la documentation de ce fichier.
00001 /* This file is part of the KDE project
00002    Copyright (C) 2004 - 2006 Dag Andersen <danders@get2net.dk>
00003 
00004    This library is free software; you can redistribute it and/or
00005    modify it under the terms of the GNU Library General Public
00006    License as published by the Free Software Foundation;
00007    version 2 of the License.
00008 
00009    This library is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY; without even the implied warranty of
00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012    Library General Public License for more details.
00013 
00014    You should have received a copy of the GNU Library General Public License
00015    along with this library; see the file COPYING.LIB.  If not, write to
00016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00017  * Boston, MA 02110-1301, USA.
00018 */
00019 
00020 #include "kptdurationwidget.h"
00021 
00022 #include <kdebug.h>
00023 #include <kglobal.h>
00024 #include <klocale.h>
00025 
00026 #include <cmath>
00027 #include <QLabel>
00028 #include <QRegExp>
00029 #include <QRegExpValidator>
00030 #include <QString>
00031 
00032 namespace KPlato
00033 {
00034 
00035 #define setField(f, l, ls, c, fmt, r, rs, s, fs, sc, u) \
00036 do \
00037 { \
00038     m_fields[f].left = l; \
00039     m_fields[f].leftScale = ls; \
00040     m_fields[f].current = c; \
00041     m_fields[f].format = fmt; \
00042     m_fields[f].right = r; \
00043     m_fields[f].rightScale = rs; \
00044     m_fields[f].separator = s; \
00045     m_fields[f].fullScale = fs; \
00046     m_fields[f].scale = sc; \
00047     m_fields[f].unit = u; \
00048 } while (0)
00049 
00050 
00051 DurationWidget::DurationWidget(QWidget *parent)
00052     : QFrame(parent)
00053 {
00054     setupUi(this);
00055     // Use the user's decimal point!
00056     m_decimalPoint = KGlobal::locale()->decimalSymbol();
00057     
00058     //NOTE: 
00059     //      This isn't as flexible/general as Shaheed once made it.
00060     //      It's now a long list of hacks.
00061     //      Introducing double in scales and allowing leftscale/scale/rightscale
00062     //      *NOT* to be multiples of each other increases the complexity and also
00063     //      introduces rounding errors. (eg. hour = 60 minutes, day = 7,5 hours)
00064     //
00065     //      If you know how, please make a better solution!
00066     //
00067     // Any field can be entered as an integer or a floating point value. Whatever
00068     // is entered is treated as follows:
00069     //
00070     //    - any fractional part is moved right one field
00071     //
00072     //    - any overflow from the integer part is carried left one field
00073     //
00074     // and the process repeated until the rightmost and leftmost fields are reached.
00075     QRegExp re(QString("\\d{1,10}|\\d{1,7}\\") + m_decimalPoint + 
00076         QString("\\d{0,10}|\\d{0,7}\\") + m_decimalPoint + 
00077         QString("\\d{1,3}"));
00078     m_validator = new QRegExpValidator(re, this);
00079     m_ddd->setValidator(m_validator);
00080     m_hh->setValidator(m_validator);
00081     m_mm->setValidator(m_validator);
00082     m_ss->setValidator(m_validator);
00083     m_ms->setValidator(m_validator);
00084     
00085     m_ddUnit->hide();
00086     m_hhUnit->hide();
00087     m_mmUnit->hide();
00088     m_ssUnit->hide();
00089     m_msUnit->hide();
00090     
00091     m_fields = new FieldDescriptor[5];
00092     setField(0, NULL, 0, m_ddd, "%u", m_hh, 24, m_hhSpace, 24, 24, m_ddUnit);
00093     setField(1, m_ddd, 24, m_hh, "%02u", m_mm, 60, m_mmColon, 60, 60, m_hhUnit);
00094     setField(2, m_hh, 60, m_mm, "%02u", m_ss, 60, NULL, 60, 60, m_mmUnit);
00095     setField(3, m_mm, 60, m_ss, "%02u", m_ms, 1000, m_ssColon, 60, 60, m_ssUnit);
00096     setField(4, m_ss, 1000, m_ms, "%03u", NULL, 0, m_dot, 1000, 1000, m_msUnit);
00097 
00098 
00099     connect( m_ddd, SIGNAL( lostFocus() ), this, SLOT( dddLostFocus() ) );
00100     connect( m_hh, SIGNAL( lostFocus() ), this, SLOT( hhLostFocus() ) );
00101     connect( m_mm, SIGNAL( lostFocus() ), this, SLOT( mmLostFocus() ) );
00102     connect( m_ss, SIGNAL( lostFocus() ), this, SLOT( ssLostFocus() ) );
00103     connect( m_ms, SIGNAL( lostFocus() ), this, SLOT( msLostFocus() ) );
00104 
00105 }
00106 
00107 DurationWidget::~DurationWidget()
00108 {
00109     delete m_fields;
00110     //delete m_validator;  //QWidget takes care of this
00111 }
00112 
00113 qint64 DurationWidget::setValueMilliseconds(qint64 milliseconds)
00114 {
00115     unsigned sc = (unsigned)m_fields[4].leftScale;
00116     qint64 secs = milliseconds / sc;
00117     qint64 ms = milliseconds % sc;
00118     QString tmp;
00119     tmp.sprintf(m_fields[4].format, ms);
00120     m_fields[4].current->setText(tmp);
00121     return secs;
00122 }
00123 
00124 qint64 DurationWidget::setValueSeconds(qint64 seconds)
00125 {
00126     unsigned sc = (unsigned)m_fields[3].leftScale;
00127     qint64 mins = seconds / sc;
00128     qint64 s = seconds % sc;
00129     QString tmp;
00130     tmp.sprintf(m_fields[3].format, s);
00131     m_fields[3].current->setText(tmp);
00132     return mins;
00133 }
00134 
00135 qint64 DurationWidget::setValueMinutes(qint64 mins)
00136 {
00137     unsigned sc = (unsigned)m_fields[2].leftScale;
00138     qint64 hours = mins / sc;
00139     qint64 m = mins % sc;
00140     QString tmp;
00141     tmp.sprintf(m_fields[2].format, m);
00142     m_fields[2].current->setText(tmp);
00143     return hours;
00144 }
00145 
00146 // NOTE: Input is minutes and also returns minutes!
00147 qint64 DurationWidget::setValueHours(qint64 mins)
00148 {
00149     if (m_fields[1].current->isHidden())
00150         return mins;
00151     unsigned sc = (unsigned)m_fields[1].rightScale;
00152     qint64 hours = (qint64)(mins / sc);
00153     qint64 m = mins - (qint64)(hours * sc);
00154     //kDebug()<<k_funcinfo<<"mins="<<mins<<" -> hours="<<hours<<" rem="<<m<<endl;
00155     QString tmp;
00156     tmp.sprintf(m_fields[1].format, hours);
00157     m_fields[1].current->setText(tmp);
00158     return m;
00159 }
00160 
00161 // NOTE: Input is minutes and also returns minutes!
00162 qint64 DurationWidget::setValueDays(qint64 mins)
00163 {
00164     if (m_fields[0].current->isHidden())
00165         return mins;
00166     double sc = m_fields[1].rightScale * m_fields[0].rightScale;
00167     qint64 days = (qint64)(mins / sc);
00168     qint64 m = mins - (qint64)(days * sc);
00169     //kDebug()<<k_funcinfo<<"mins="<<mins<<" -> days="<<days<<" rem="<<m<<endl;
00170     QString tmp;
00171     tmp.sprintf(m_fields[0].format, days);
00172     m_fields[0].current->setText(tmp);
00173     return m;
00174 }
00175 
00176 void DurationWidget::setValue(const KPlato::Duration &newDuration)
00177 {
00178     qint64 value = newDuration.milliseconds();
00179     //kDebug()<<k_funcinfo<<f<<": value="<<value<<endl;
00180     value = setValueMilliseconds(value); // returns seconds
00181     value = setValueSeconds(value); // returns minutes
00182     // Now call days first to allow for fractional scales
00183     value = setValueDays(value); // NOTE  returns minutes
00184     value = setValueHours(value); // NOTE  returns minutes
00185     value = setValueMinutes(value); // returns hours: Should be 0
00186     if (value > 0) kError()<<k_funcinfo<<"Remainder > 0: "<<value<<endl;
00187     
00188     emit valueChanged();
00189 }
00190 
00191 Duration DurationWidget::value() const
00192 {
00193     Duration d;
00194     int i=0;
00195     if (!m_fields[i].current->isHidden() &&
00196         m_fields[i].scale > 0 &&
00197         m_fields[i].scale <= m_fields[i].fullScale)
00198     {
00199         double v = m_fields[i].current->text().toDouble();
00200         v = v * m_fields[i].scale / m_fields[i].fullScale;;
00201         d.addMilliseconds((qint64)(v*(1000*60*60*24)));
00202     }
00203     ++i;
00204     if (!m_fields[i].current->isHidden() &&
00205         m_fields[i].scale > 0 &&
00206         m_fields[i].scale <= m_fields[i].fullScale)
00207     {
00208         double v = m_fields[i].current->text().toDouble();
00209         v = v * m_fields[i].scale / m_fields[i].fullScale;;
00210         d.addMilliseconds((qint64)(v*(1000*60*60)));
00211     }
00212     ++i;
00213     if (!m_fields[i].current->isHidden() &&
00214         m_fields[i].scale > 0 &&
00215         m_fields[i].scale <= m_fields[i].fullScale)
00216     {
00217         double v = m_fields[i].current->text().toDouble();
00218         v = v * m_fields[i].scale / m_fields[i].fullScale;;
00219         d.addMilliseconds((qint64)(v*(1000*60)));
00220     }
00221     ++i;
00222     if (!m_fields[i].current->isHidden() &&
00223         m_fields[i].scale > 0 &&
00224         m_fields[i].scale <= m_fields[i].fullScale)
00225     {
00226         double v = m_fields[i].current->text().toDouble();
00227         v = v * m_fields[i].scale / m_fields[i].fullScale;;
00228         d.addMilliseconds((qint64)(v*(1000)));
00229     }
00230     ++i;
00231     if (!m_fields[i].current->isHidden())
00232     {
00233         qint64 v = m_fields[i].current->text().toUInt();
00234         d.addMilliseconds(v);
00235     }
00236     return d;
00237 }
00238 
00239 void DurationWidget::dddLostFocus()
00240 {
00241     handleLostFocus(0);
00242     emit valueChanged();
00243 }
00244 
00245 void DurationWidget::hhLostFocus( )
00246 {
00247     handleLostFocus(1);
00248     emit valueChanged();
00249 }
00250 
00251 void DurationWidget::mmLostFocus()
00252 {
00253     handleLostFocus(2);
00254     emit valueChanged();
00255 }
00256 
00257 void DurationWidget::ssLostFocus()
00258 {
00259     handleLostFocus(3);
00260     emit valueChanged();
00261 }
00262 
00263 void DurationWidget::msLostFocus()
00264 {
00265     handleLostFocus(4);
00266     emit valueChanged();
00267 }
00268 
00269 void DurationWidget::handleLostFocus(
00270     int field)
00271 {
00272     // Get our own info, and that of our left and right neighbours.
00273     QLineEdit *left = m_fields[field].left;
00274     double leftScale = m_fields[field].leftScale; 
00275     const char *leftFormat = left ? m_fields[field - 1].format : NULL; 
00276     QLineEdit *current = m_fields[field].current; 
00277     const char *currentFormat = m_fields[field].format;
00278     QLineEdit *right = m_fields[field].right;
00279     double rightScale = m_fields[field].rightScale; 
00280     const char *rightFormat = right ? m_fields[field + 1].format : NULL;
00281     
00282     // avoid possible crash
00283     if (leftScale == 0)
00284         leftScale = 1;
00285         
00286     // Get the text and start processing...
00287     QString newValue(current->text());
00288     double v = KGlobal::locale()->readNumber(newValue);
00289     unsigned currentValue = 0;
00290     QString tmp;
00291     //kDebug()<<k_funcinfo<<field<<": value="<<v<<" v="<<v<<endl;
00292     if (left && v >= leftScale)
00293     {
00294         //kDebug()<<k_funcinfo<<field<<": value="<<v<<" leftScale="<<leftScale<<endl;
00295         // Carry overflow, recurse as required.
00296         tmp.sprintf(leftFormat, (unsigned)(v / leftScale));
00297         left->setText(tmp);
00298         handleLostFocus(field - 1);
00299 
00300         // Get remainder.
00301         v = v - (tmp.toUInt() * leftScale);
00302         newValue = KGlobal::locale()->formatNumber(v);
00303     }
00304     int point = newValue.find(m_decimalPoint);
00305     if (point != -1)
00306     {
00307         //HACK doubles may be rounded(at fractions > 6 digits on my system)
00308         int p;
00309         double frac = fraction(newValue, &p);
00310         if (right && frac > 0.0)
00311         {
00312             //kDebug()<<k_funcinfo<<field<<": value="<<newValue<<" rightScale="<<rightScale<<" frac="<<frac<<" ("<<newValue.mid(point)<<")"<<endl;
00313             // Propagate fraction
00314             v = rightScale * (frac*power(10.0, -p));
00315             frac = fraction(KGlobal::locale()->formatNumber(v, 19), 0);
00316             //kDebug()<<k_funcinfo<<field<<": v="<<v<<" ("<<(unsigned)v<<") rest="<<frac<<endl;
00317             if (frac > 0.0)
00318             {
00319                 tmp = KGlobal::locale()->formatNumber(v, 19);
00320                 right->setText(tmp);
00321                 handleLostFocus(field + 1);
00322             } else {
00323                 tmp.sprintf(rightFormat, (unsigned)(v));
00324                 right->setText(tmp);
00325             }
00326             
00327         }
00328         // TODO keep fraction for last shown field
00329         newValue = newValue.left(point);
00330     }
00331     currentValue = newValue.toUInt();
00332     tmp.sprintf(currentFormat, currentValue);
00333     current->setText(tmp);
00334 }
00335 
00336 // Set which fields are visible.
00337 void DurationWidget::setVisibleFields( int fieldMask )
00338 {
00339     int i;
00340     for (i = 0; i < 5; i++)
00341     {
00342         bool visible = ((fieldMask >> i) & 1) == 1;
00343         
00344         // Set the visibility of the fields, and of any associated separator.
00345         if (visible)
00346         {
00347             m_fields[i].current->show();
00348             if (m_fields[i].separator)
00349             {
00350                 m_fields[i].separator->show();
00351             }
00352             if (m_fields[i].unit)
00353             {
00354                 m_fields[i].unit->show();
00355      }
00356         }
00357         else
00358         {
00359             m_fields[i].current->hide();
00360             if (m_fields[i].separator)
00361             {
00362                 m_fields[i].separator->hide();
00363             }
00364             if (m_fields[i].unit)
00365             {
00366                 m_fields[i].unit->hide();
00367      }
00368         }
00369     }
00370 }
00371 
00372 // Retreive the visible fields.
00373 int DurationWidget::visibleFields()
00374 {
00375     int i;
00376     int fieldMask = 0;
00377     for (i = 0; i < 5; i++)
00378     {
00379         if (m_fields[i].current->isHidden())
00380         {   
00381             fieldMask |= (1 << i);
00382         }
00383     }
00384     return fieldMask;
00385 }
00386 
00387 void DurationWidget::setFieldLeftscale(int f, double ls)
00388 {
00389     m_fields[f].leftScale = ls;
00390 }
00391 
00392 void DurationWidget::setFieldRightscale(int f, double rs)
00393 {
00394     m_fields[f].rightScale = rs;
00395 }
00396 
00397 void DurationWidget::setFieldScale(int f, double scale)
00398 {
00399     m_fields[f].scale = scale;
00400 }
00401 
00402 void DurationWidget::setFieldUnit(int f, QString unit)
00403 {
00404     if (m_fields[f].unit)
00405     {
00406  m_fields[f].unit->setText(unit);
00407     }
00408 }
00409 
00410 double DurationWidget::power(double m, int e) {
00411     int c = e > 0 ? e : -e;
00412     double value = 1;
00413     for (int i=0; i < c; ++i) {
00414         value = e > 0 ? value * m : value / m;
00415     }
00416     return value;
00417 }
00418 
00419 double DurationWidget::fraction(QString number, int *exp) {
00420     int point = number.find(m_decimalPoint);
00421     if (point == -1) {
00422         return 0.0;
00423     }
00424     QString v;
00425     if (exp) {
00426         v = number.mid(point+m_decimalPoint.length());
00427         *exp = v.length();
00428         
00429     } else {
00430          v = number.mid(point);
00431     }
00432     return KGlobal::locale()->readNumber(v);
00433 }
00434 
00435 }  //KPlato namespace
00436 
00437 #include "kptdurationwidget.moc"
00438 

Généré le Wed Nov 22 23:40:56 2006 pour KPlato par  doxygen 1.5.1-p1