File indexing completed on 2024-05-12 15:27:41

0001 /***************************************************************************
0002     File                 : DateTimeSpinBox.cpp
0003     Project              : LabPlot
0004     Description          : widget for setting datetimes with a spinbox
0005     --------------------------------------------------------------------
0006     Copyright            : (C) 2019 Martin Marmsoler (martin.marmsoler@gmail.com)
0007 
0008  ***************************************************************************/
0009 
0010 /***************************************************************************
0011  *                                                                         *
0012  *  This program is free software; you can redistribute it and/or modify   *
0013  *  it under the terms of the GNU General Public License as published by   *
0014  *  the Free Software Foundation; either version 2 of the License, or      *
0015  *  (at your option) any later version.                                    *
0016  *                                                                         *
0017  *  This program is distributed in the hope that it will be useful,        *
0018  *  but WITHOUT ANY WARRANTY; without even the implied warranty of         *
0019  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          *
0020  *  GNU General Public License for more details.                           *
0021  *                                                                         *
0022  *   You should have received a copy of the GNU General Public License     *
0023  *   along with this program; if not, write to the Free Software           *
0024  *   Foundation, Inc., 51 Franklin Street, Fifth Floor,                    *
0025  *   Boston, MA  02110-1301  USA                                           *
0026  *                                                                         *
0027  ***************************************************************************/
0028 
0029 #include "DateTimeSpinBox.h"
0030 
0031 #include <QLineEdit>
0032 #include <QKeyEvent>
0033 #include <QRegularExpressionValidator>
0034 
0035 DateTimeSpinBox::DateTimeSpinBox(QWidget* parent) : QAbstractSpinBox(parent) {
0036     lineEdit()->setText("0000.00.00 00:00:00.001");
0037     DateTimeSpinBox::stepEnabled();
0038 
0039     m_regularExpressionValidator = new QRegularExpressionValidator();
0040 
0041     QRegularExpression regExp(R"(([0-9]+)\.(0[0-9]|1[0-2]|[0-9])\.(0[0-9]|[0-2][0-9]|30|[0-9]) ([0-1][0-9]|2[0-3]|[0-9])\:([0-5][0-9]|[0-9])\:([0-5][0-9]|[0-9])\.[0-9]{0,3})");
0042     m_regularExpressionValidator->setRegularExpression(regExp);
0043 
0044     lineEdit()->setValidator(m_regularExpressionValidator);
0045 }
0046 
0047 void DateTimeSpinBox::keyPressEvent(QKeyEvent* event) {
0048     if (event->key() >= Qt::Key_0 && event->key() <= Qt::Key_9) {
0049         int cursorPos = lineEdit()->cursorPosition();
0050         int textLenght = lineEdit()->text().length();
0051         QAbstractSpinBox::keyPressEvent(event);
0052         getValue();
0053         if (lineEdit()->text().length() != textLenght)
0054             lineEdit()->setCursorPosition(cursorPos + 1);
0055         else
0056             lineEdit()->setCursorPosition(cursorPos);
0057     } else if (event->key() == Qt::Key_Up) {
0058         Type type = determineType(lineEdit()->cursorPosition());
0059         increaseValue(type, 1);
0060         writeValue();
0061         setCursorPosition(type);
0062     } else if (event->key() == Qt::Key_Down) {
0063         Type type = determineType(lineEdit()->cursorPosition());
0064         increaseValue(type, -1);
0065         writeValue();
0066         setCursorPosition(type);
0067     } else {
0068         QAbstractSpinBox::keyPressEvent(event);
0069         getValue();
0070     }
0071 }
0072 
0073 QAbstractSpinBox::StepEnabled DateTimeSpinBox::stepEnabled() const {
0074     return QAbstractSpinBox::StepEnabledFlag::StepUpEnabled | QAbstractSpinBox::StepEnabledFlag::StepDownEnabled; // for testing
0075 }
0076 
0077 void DateTimeSpinBox::stepBy(int steps) {
0078     Type type = determineType(lineEdit()->cursorPosition());
0079     increaseValue(type, steps);
0080     writeValue();
0081     setCursorPosition(type);
0082 }
0083 
0084 /*!
0085  * Write value to lineEdit of the spinbox
0086  */
0087 void DateTimeSpinBox::writeValue() {
0088     lineEdit()->setText(QString::number(m_year) + '.' +
0089                         QString("%1").arg(m_month, 2, 10, QLatin1Char('0')) + QLatin1Char('.') +
0090                         QString("%1").arg(m_day, 2, 10, QLatin1Char('0')) + QLatin1Char(' ') +
0091                         QString("%1").arg(m_hour, 2, 10, QLatin1Char('0')) + QLatin1Char(':') +
0092                         QString("%1").arg(m_minute, 2, 10, QLatin1Char('0')) + QLatin1Char(':') +
0093                         QString("%1").arg(m_second, 2, 10, QLatin1Char('0')) + QLatin1Char('.') +
0094                         QString("%1").arg(m_millisecond, 3, 10, QLatin1Char('0')));
0095     emit valueChanged();
0096 }
0097 
0098 void DateTimeSpinBox::setValue(qint64 increment) {
0099     qint64 divisor = qint64(12) * 30 * 24 * 60 * 60 * 1000;
0100     qint64 rest;
0101     m_year = increment / divisor;
0102     rest = increment - m_year * divisor;
0103     divisor = qint64(30) * 24 * 60 * 60 * 1000;
0104     m_month = rest / divisor;
0105     rest = rest - m_month * divisor;
0106     divisor = qint64(24) * 60 * 60 * 1000;
0107     m_day = rest / divisor;
0108     rest = rest - m_day * divisor;
0109     divisor = qint64(60) * 60 * 1000;
0110     m_hour = rest / divisor;
0111     rest -= m_hour * divisor;
0112     divisor = qint64(60)* 1000;
0113     m_minute = rest / divisor;
0114     rest -= m_minute * divisor;
0115     divisor = qint64(1000);
0116     m_second = rest /divisor;
0117     rest -= m_second * divisor;
0118     m_millisecond = rest;
0119 
0120     writeValue();
0121 }
0122 
0123 qint64 DateTimeSpinBox::value() {
0124     return m_millisecond
0125             + 1000 * (m_second
0126             + 60 * (m_minute
0127             + 60 * (m_hour
0128             + 24 * (m_day
0129             + 30 * (m_month
0130             + 12 * m_year)))));
0131 }
0132 
0133 /*!
0134  * Read value from lineEdit of the spinbox
0135  */
0136 void DateTimeSpinBox::getValue() {
0137     QString text = lineEdit()->text();
0138 
0139     int counter = 0;
0140     int startIndex = 0;
0141     for (int i=0; i< text.length(); i++) {
0142         if (text[i] == '.' || text[i] == ':' || text[i] == ' ' || i == text.length()-1) {
0143             switch(counter) {
0144                 case Type::year:
0145                     m_year = text.midRef(startIndex, i - startIndex).toInt();
0146                     break;
0147                 case Type::month:
0148                     m_month = text.midRef(startIndex, i - startIndex).toInt();
0149                     break;
0150                 case Type::day:
0151                     m_day = text.midRef(startIndex, i - startIndex).toInt();
0152                     break;
0153                 case Type::hour:
0154                     m_hour = text.midRef(startIndex, i - startIndex).toInt();
0155                     break;
0156                 case Type::minute:
0157                     m_minute = text.midRef(startIndex, i - startIndex).toInt();
0158                     break;
0159                 case Type::second:
0160                     m_second = text.midRef(startIndex, i - startIndex).toInt();
0161                     break;
0162                 case Type::millisecond:
0163                     m_millisecond = text.midRef(startIndex, i - startIndex + 1).toInt(); // because of the condition (i == text.length()-1)
0164                     break;
0165             }
0166             startIndex = i+1;
0167             counter ++;
0168         }
0169     }
0170 
0171     emit valueChanged();
0172 }
0173 
0174 void DateTimeSpinBox::setCursorPosition(Type type) {
0175     QString text = lineEdit()->text();
0176     int counter = 0;
0177     for (int i = 0; i < text.length(); i++) {
0178         if (text[i] == '.' || text[i] == ':' || text[i] == ' ')
0179             counter ++;
0180 
0181         if (counter-1 == type) {
0182             lineEdit()->setCursorPosition(i);
0183             break;
0184         }
0185     }
0186 }
0187 
0188 bool DateTimeSpinBox::valid() {
0189     return true;
0190 }
0191 
0192 // step can also be negative
0193 bool DateTimeSpinBox::increaseValue(DateTimeSpinBox::Type type, int step) {
0194     switch (type) {
0195 
0196     case Type::year: {
0197         if (m_year + step < 0 && step < 0) {
0198             if (m_year + step < 0) {
0199                 m_year = 0;
0200                 return false;
0201             }
0202         }
0203         m_year += step;
0204         return true;
0205     }
0206         break;
0207     case Type::month:
0208         return changeValue(m_month, Type::year, step);
0209         break;
0210     case Type::day:
0211         return changeValue(m_day, Type::month, step);
0212         break;
0213     case Type::hour:
0214         return changeValue(m_hour, Type::day, step);
0215         break;
0216     case Type::minute:
0217         return changeValue(m_minute, Type::hour, step);
0218         break;
0219     case Type::second:
0220         return changeValue(m_second, Type::minute, step);
0221         break;
0222     case Type::millisecond:
0223         return changeValue(m_millisecond, Type::second, step);
0224         break;
0225     default:
0226         return false;
0227         break;
0228     }
0229 }
0230 
0231 bool DateTimeSpinBox::changeValue(qint64& thisType, DateTimeSpinBox::Type nextTypeType, int step) {
0232     int maxValue = 1;
0233     switch (nextTypeType) {
0234         case (Type::year):
0235             maxValue = 12;
0236         break;
0237         case (Type::month):
0238             maxValue = 30;
0239             break;
0240         case (Type::day):
0241             maxValue = 24;
0242             break;
0243         case (Type::hour):
0244             maxValue = 60;
0245             break;
0246         case (Type::minute):
0247             maxValue = 60;
0248             break;
0249         case (Type::second):
0250             maxValue = 1000;
0251             break;
0252         case (Type::millisecond):
0253             return false;
0254     }
0255 
0256     int nextTypeCounter = step / maxValue;
0257     step -= nextTypeCounter * maxValue;
0258     if (thisType + step < 0 && step < 0) {
0259         nextTypeCounter --;
0260         if (increaseValue(nextTypeType, nextTypeCounter)) {
0261             step += maxValue;
0262             thisType += step;
0263             return true;
0264         } else {
0265             thisType = 0;
0266             return false;
0267         }
0268     } else if ( thisType + step > maxValue-1 && step > 0) {
0269         step -= nextTypeCounter * maxValue;
0270         if (thisType + step > maxValue-1) {
0271             nextTypeCounter ++;
0272             step -= maxValue;
0273             thisType += step;
0274         } else
0275             thisType += step;
0276 
0277 
0278         return increaseValue(nextTypeType, nextTypeCounter);
0279     }
0280     thisType += step;
0281     return true;
0282 }
0283 
0284 DateTimeSpinBox::Type DateTimeSpinBox::determineType(int cursorPos) const{
0285     QString text = lineEdit()->text();
0286 
0287     if (cursorPos > text.length())
0288         cursorPos = text.length();
0289 
0290     int counter = 0;
0291     for (int i = 0; i < cursorPos; i++) {
0292         if (text[i] == '.' || text[i] == ':' || text[i] == ' ')
0293             counter ++;
0294     }
0295 
0296     if (counter <= Type::millisecond)
0297         return static_cast<Type>(counter);
0298 
0299     return Type::millisecond;
0300 }