src/hbwidgets/editors/hbdatetimevalidator_p.cpp
changeset 0 16d8024aca5e
equal deleted inserted replaced
-1:000000000000 0:16d8024aca5e
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2008-2010 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (developer.feedback@nokia.com)
       
     6 **
       
     7 ** This file is part of the HbWidgets module of the UI Extensions for Mobile.
       
     8 **
       
     9 ** GNU Lesser General Public License Usage
       
    10 ** This file may be used under the terms of the GNU Lesser General Public
       
    11 ** License version 2.1 as published by the Free Software Foundation and
       
    12 ** appearing in the file LICENSE.LGPL included in the packaging of this file.
       
    13 ** Please review the following information to ensure the GNU Lesser General
       
    14 ** Public License version 2.1 requirements will be met:
       
    15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    16 **
       
    17 ** In addition, as a special exception, Nokia gives you certain additional
       
    18 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    20 **
       
    21 ** If you have questions regarding the use of this file, please contact
       
    22 ** Nokia at developer.feedback@nokia.com.
       
    23 **
       
    24 ****************************************************************************/
       
    25 
       
    26 //
       
    27 //  W A R N I N G
       
    28 //  -------------
       
    29 //
       
    30 // This file is not part of the Hb API.  It exists purely as an
       
    31 // implementation detail.  This file may change from version to
       
    32 // version without notice, or even be removed.
       
    33 //
       
    34 // We mean it.
       
    35 //
       
    36 
       
    37 #include <QIntValidator>
       
    38 #include <QRegExpValidator>
       
    39 #include <QTextCursor>
       
    40 #include <QRegExp>
       
    41 #include <QtDebug>
       
    42 #include <QTextDocument>
       
    43 #include <QDate>
       
    44 
       
    45 #include "hbdatetimevalidator_p_p.h"
       
    46 #include "hbvalidator_p.h"
       
    47 
       
    48 #define DEFAULT_YEAR_VALUE "2009"
       
    49 
       
    50 const int KMinimumYear = -4713;
       
    51 const int KMaximumYear = 11000000; // see more in QDate documentation
       
    52 
       
    53 HbDateTimeValidatorPrivate::HbDateTimeValidatorPrivate() :
       
    54     HbValidatorPrivate()
       
    55 {
       
    56 
       
    57 }
       
    58 
       
    59 HbDateTimeValidatorPrivate::~HbDateTimeValidatorPrivate()
       
    60 {
       
    61 
       
    62 }
       
    63 
       
    64 class HbFixableIntQValidator : public QIntValidator
       
    65 {
       
    66 public:
       
    67     HbFixableIntQValidator( QObject * parent ) :
       
    68             QIntValidator ( parent ),
       
    69             mMaxLeght(10000)
       
    70     {
       
    71     }
       
    72 
       
    73     HbFixableIntQValidator ( int minimum, int maximum, QObject * parent ) :
       
    74             QIntValidator ( minimum, maximum, parent )
       
    75     {
       
    76         int topLenght = QString::number(top()).length();
       
    77         int bottomLenght = QString::number(bottom()).length();
       
    78         mMaxLeght = qMax(topLenght, bottomLenght);
       
    79     }
       
    80 
       
    81     void fixup ( QString & input ) const
       
    82     {
       
    83         QString newTextValue=input;
       
    84         newTextValue.remove(QRegExp((bottom()>=0)?"[^0-9]":"[^\\-0-9]"));
       
    85         bool ok;
       
    86         int value = newTextValue.toInt(&ok);
       
    87         if(ok) {
       
    88             value = qBound(bottom(), value, top());
       
    89             newTextValue.setNum(value);
       
    90             if(newTextValue!=input) {
       
    91                 input = newTextValue;
       
    92             }
       
    93         }
       
    94     }
       
    95 
       
    96     State validate ( QString & input, int & pos ) const
       
    97     {
       
    98         if(input.length()>mMaxLeght) {
       
    99             return Invalid;
       
   100         }
       
   101 
       
   102         return QIntValidator::validate(input, pos);
       
   103     }
       
   104 
       
   105 private:
       
   106     int mMaxLeght;
       
   107 };
       
   108 
       
   109 void HbDateTimeValidatorPrivate::addDateTimeField(const DataPositions& data)
       
   110 {
       
   111     Q_Q(HbDateTimeValidator);
       
   112     QValidator *fieldValidator = NULL;
       
   113     QString defaultValue;
       
   114 
       
   115     switch(data.type) {
       
   116     case Year: {
       
   117             if(data.lenght() == 4) {
       
   118                 fieldValidator = new HbFixableIntQValidator(KMinimumYear,
       
   119                                                             KMaximumYear,
       
   120                                                             q);
       
   121                 defaultValue = "2009";
       
   122             } else {
       
   123                 fieldValidator = new HbFixableIntQValidator(0,99,q);
       
   124                 defaultValue = "09";
       
   125             }
       
   126         }
       
   127         break;
       
   128     case Month: {
       
   129             fieldValidator = new HbFixableIntQValidator(1,12,q);
       
   130             defaultValue = (data.lenght() == 1)?"1":"01";
       
   131         }
       
   132         break;
       
   133     case Day: {
       
   134             fieldValidator = new HbFixableIntQValidator(1,31,q);
       
   135             defaultValue = (data.lenght() == 1)?"1":"01";
       
   136         }
       
   137         break;
       
   138     case AmPm: {
       
   139             QRegExp regUxp("AM|PM", Qt::CaseInsensitive);
       
   140             fieldValidator = new QRegExpValidator(regUxp,q);
       
   141             defaultValue = "AM";
       
   142         }
       
   143         break;
       
   144     case Hour24: {
       
   145             fieldValidator = new HbFixableIntQValidator(0,23,q);
       
   146             defaultValue = (data.lenght() == 1)?"0":"00";
       
   147         }
       
   148         break;
       
   149     case Hour12: {
       
   150             fieldValidator = new HbFixableIntQValidator(1,12,q);
       
   151             defaultValue = "12";
       
   152         }
       
   153         break;
       
   154     case Minutes: {
       
   155             fieldValidator = new HbFixableIntQValidator(0,59,q);
       
   156             defaultValue = (data.lenght() == 1)?"0":"00";
       
   157         }
       
   158         break;
       
   159     case Seconds: {
       
   160             fieldValidator = new HbFixableIntQValidator(0,59,q);
       
   161             defaultValue = (data.lenght() == 1)?"0":"00";
       
   162         }
       
   163         break;
       
   164     case MilliSeconds: {
       
   165             fieldValidator = new HbFixableIntQValidator(0,999,q);
       
   166             defaultValue = (data.lenght() == 1)?"0":"000";
       
   167         }
       
   168         break;
       
   169     default:
       
   170         Q_ASSERT(false);
       
   171     }
       
   172     fieldTypes.append(data.type);
       
   173     addField(separatorToAdd,fieldValidator,defaultValue);
       
   174 }
       
   175 
       
   176 bool HbDateTimeValidatorPrivate::fixDate(QTextCursor *cursor, bool updateCursor)
       
   177 {
       
   178     Q_ASSERT(!fieldTypes.isEmpty());
       
   179 
       
   180     QRegExp regExp = QRegExp(parseString,Qt::CaseInsensitive);
       
   181 
       
   182     Q_ASSERT(regExp.isValid());
       
   183 
       
   184     bool result = false;
       
   185 
       
   186     if(regExp.exactMatch(cursor->document()->toPlainText())) {
       
   187         QTextCursor fixingCursor(cursor->document());
       
   188         fixingCursor.beginEditBlock();
       
   189 
       
   190         // step one: fixing each field separately
       
   191         for(int i = fieldTypes.count()-1; i>=0; --i ) {
       
   192             int pos = regExp.pos(i+1);
       
   193             QString fieldTxt = regExp.cap(i+1);
       
   194             fixingCursor.setPosition(pos);
       
   195             fixingCursor.setPosition(pos + fieldTxt.length());
       
   196 
       
   197             int x = fieldTxt.length();
       
   198             if( QValidator::Acceptable != fields.at(i)->mValidator->validate(fieldTxt, x) ) {
       
   199                 fields.at(i)->mValidator->fixup(fieldTxt);
       
   200                 if(QValidator::Acceptable == fields.at(i)->mValidator->validate(fieldTxt, x)) {
       
   201                     // successful fix
       
   202                     fixingCursor.insertText(fieldTxt);
       
   203                     result = true;
       
   204                 }
       
   205             }
       
   206         }
       
   207 
       
   208         // step two: fixing correlations between fields
       
   209         // Note: this functionality is specific for HbDateTimeValidator
       
   210         int yearCapture  = fieldTypes.indexOf(Year)+1;
       
   211         int monthCapture = fieldTypes.indexOf(Month)+1;
       
   212         int dayCapture   = fieldTypes.indexOf(Day)+1;
       
   213 
       
   214         if(dayCapture<=0 || monthCapture<=0) {
       
   215             fixingCursor.endEditBlock();
       
   216             return result; // nothing to fix no date
       
   217         }
       
   218 
       
   219         // match content again
       
   220         if(regExp.exactMatch(cursor->document()->toPlainText())) {
       
   221             int year=1900, month, day;
       
   222 
       
   223             if(yearCapture>0) {
       
   224                 year = regExp.cap(yearCapture).toInt();
       
   225             }
       
   226             month = regExp.cap(monthCapture).toInt();
       
   227 
       
   228             int daySize =0;
       
   229             QString s = regExp.cap(dayCapture);
       
   230             daySize = s.length();
       
   231             day   = s.toInt();
       
   232 
       
   233             if( !QDate::isValid(year, month, day) ) {
       
   234                 QDate dateFoFix(year,month,1);
       
   235                 if(dateFoFix.daysInMonth()<day) {
       
   236                     dateFoFix.setDate(year,month,dateFoFix.daysInMonth());
       
   237                     int dayCapPos = regExp.pos(dayCapture);
       
   238                     fixingCursor.setPosition(dayCapPos);
       
   239                     fixingCursor.setPosition(dayCapPos+daySize, QTextCursor::KeepAnchor);
       
   240 
       
   241                     // updating date by changing document
       
   242                     // this will prevent some unexpected isues
       
   243                     fixingCursor.insertText(QString().setNum(dateFoFix.daysInMonth()));
       
   244 
       
   245                     if(updateCursor) {
       
   246                         fixingCursor.setPosition(dayCapPos);
       
   247                         fixingCursor.setPosition(dayCapPos+daySize, QTextCursor::KeepAnchor);
       
   248                         *cursor = fixingCursor;
       
   249                     }
       
   250                     result = true;
       
   251                 }
       
   252             }
       
   253         }
       
   254         fixingCursor.endEditBlock();
       
   255     }
       
   256 
       
   257     return result;
       
   258 }
       
   259 
       
   260 /*!
       
   261     \class HbDateTimeValidator
       
   262     \brief HbDateTimeValidator provides validation services for QAbstractEdit derived editors.
       
   263 */
       
   264 
       
   265 /*!
       
   266     Constructs a HbDateTimeValidator object with a /a parent
       
   267 */
       
   268 HbDateTimeValidator::HbDateTimeValidator(QObject *parent) :
       
   269     HbValidator(*new HbDateTimeValidatorPrivate, parent)
       
   270 {
       
   271     Q_D(HbDateTimeValidator);
       
   272     d->q_ptr = this;
       
   273 }
       
   274 
       
   275 HbDateTimeValidator::HbDateTimeValidator(HbDateTimeValidatorPrivate &dd, QObject *parent) :
       
   276     HbValidator(dd, parent)
       
   277 {
       
   278     Q_D(HbDateTimeValidator);
       
   279     d->q_ptr = this;
       
   280 }
       
   281 
       
   282 /*!
       
   283     Destructor
       
   284 */
       
   285 HbDateTimeValidator::~HbDateTimeValidator()
       
   286 {
       
   287 }
       
   288 
       
   289 inline void appendIfFound(DataType type, QList<DataPositions>* where, QRegExp& what, const QString& from)
       
   290 {
       
   291     int pos = from.indexOf(what);
       
   292     if(pos>=0) {
       
   293         int matchLenght = what.cap(0).length();
       
   294         Q_ASSERT( matchLenght>0 );
       
   295         Q_ASSERT( from.indexOf(what, pos+matchLenght)==-1 );
       
   296         
       
   297         where->append(
       
   298                 DataPositions(type, pos, matchLenght)
       
   299                 );
       
   300     }
       
   301 }
       
   302 
       
   303 void HbDateTimeValidator::setDisplayFormat(const QString &format)
       
   304 {
       
   305     QList<DataPositions> positions;
       
   306 
       
   307     QRegExp regExp;
       
   308     QString newParseString = QRegExp::escape(format);
       
   309     const QString captureNumber = "([0-9]+)";
       
   310 
       
   311     regExp.setPattern("yy(yy)?");
       
   312     appendIfFound(Year, &positions, regExp, format);
       
   313     newParseString.replace(regExp, captureNumber);
       
   314 
       
   315     regExp.setPattern("M{1,3}");
       
   316     appendIfFound(Month, &positions, regExp, format);
       
   317     newParseString.replace(regExp, captureNumber);
       
   318 
       
   319     regExp.setPattern("d{1,3}");
       
   320     appendIfFound(Day, &positions, regExp, format);
       
   321     newParseString.replace(regExp, captureNumber);
       
   322 
       
   323     const QString captureAmPm("(AM|PM|am|pm)");
       
   324     regExp.setPattern("ap");
       
   325     appendIfFound(AmPm, &positions, regExp, format);
       
   326     newParseString.replace(regExp, captureAmPm);
       
   327 
       
   328     regExp.setPattern("AP");
       
   329     appendIfFound(AmPm, &positions, regExp, format);
       
   330     newParseString.replace(regExp, captureAmPm);
       
   331 
       
   332     DataType hourType = Hour24;
       
   333     if(!positions.isEmpty()) {
       
   334         if(positions.last().type==AmPm) {
       
   335             hourType = Hour12;
       
   336         }
       
   337     }
       
   338 
       
   339     regExp.setPattern("hh?");
       
   340     appendIfFound(hourType, &positions, regExp, format);
       
   341     newParseString.replace(regExp, captureNumber);
       
   342 
       
   343     regExp.setPattern("HH?");
       
   344     appendIfFound(hourType, &positions, regExp, format);
       
   345     newParseString.replace(regExp, captureNumber);
       
   346 
       
   347     regExp.setPattern("mm?");
       
   348     appendIfFound(Minutes, &positions, regExp, format);
       
   349     newParseString.replace(regExp, captureNumber);
       
   350 
       
   351     regExp.setPattern("ss?");
       
   352     appendIfFound(Seconds, &positions, regExp, format);
       
   353     newParseString.replace(regExp, captureNumber);
       
   354 
       
   355     regExp.setPattern("z(zz)?");
       
   356     appendIfFound(MilliSeconds, &positions, regExp, format);
       
   357     newParseString.replace(regExp, captureNumber);
       
   358 
       
   359     if(positions.size()==0) {
       
   360         qWarning() << "HbDateTimeValidator::setDisplayFormat: format: \""
       
   361                 << format << "\" is invalid so it was ignored" ;
       
   362         return;
       
   363     }
       
   364 
       
   365     qSort(positions);
       
   366 
       
   367     Q_D(HbDateTimeValidator);
       
   368     d->format = format;
       
   369     // ignore trailing separators:
       
   370     if(positions.back().stop < format.length()) {
       
   371         newParseString.chop(QRegExp::escape(format.mid(positions.back().stop)).length());
       
   372         d->format.resize(positions.back().stop);
       
   373     }
       
   374 
       
   375     // ignore front separators:
       
   376     if(positions.front().start != 0) {
       
   377         newParseString.remove(0,positions.front().start);
       
   378     }
       
   379 
       
   380     d->parseString = newParseString;
       
   381     d->fieldTypes.clear();
       
   382     d->deleteAllFields();
       
   383 
       
   384     const int n=positions.size()-1;
       
   385     for(int i=0; i<n; ++i) {
       
   386         d->addDateTimeField(positions.at(i));
       
   387         int separatorBegin = positions.at(i).stop;
       
   388         int separatorLenght = positions.at(i+1).start - separatorBegin;
       
   389         if(separatorLenght>0) {
       
   390             setDefaultSeparator(format.mid(separatorBegin,separatorLenght));
       
   391         } else {
       
   392             setDefaultSeparator(" ");
       
   393         }
       
   394     }
       
   395     d->addDateTimeField(positions.at(n));
       
   396 }
       
   397 
       
   398 const QString HbDateTimeValidator::displayFormat() const
       
   399 {
       
   400     Q_D(const HbDateTimeValidator);
       
   401     return d->format;
       
   402 }
       
   403 
       
   404 bool HbDateTimeValidator::fixDate(QTextCursor *cursor, bool updateCursor)
       
   405 {
       
   406     return d_func()->fixDate(cursor, updateCursor);
       
   407 }