File indexing completed on 2024-05-19 03:49:42

0001 /*
0002     File                 : DateTimeSpinBox.cpp
0003     Project              : LabPlot
0004     Description          : widget for setting datetimes with a spinbox
0005     --------------------------------------------------------------------
0006     SPDX-FileCopyrightText: 2019 Martin Marmsoler <martin.marmsoler@gmail.com>
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 
0010 #include "DateTimeSpinBox.h"
0011 
0012 #include <QKeyEvent>
0013 #include <QLineEdit>
0014 #include <QRegularExpressionValidator>
0015 
0016 DateTimeSpinBox::DateTimeSpinBox(QWidget* parent)
0017     : QAbstractSpinBox(parent) {
0018     lineEdit()->setText(QStringLiteral("0000.00.00 00:00:00.001"));
0019     DateTimeSpinBox::stepEnabled();
0020 
0021     m_regularExpressionValidator = new QRegularExpressionValidator();
0022 
0023     QRegularExpression regExp(QStringLiteral(
0024         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})"));
0025     m_regularExpressionValidator->setRegularExpression(regExp);
0026 
0027     lineEdit()->setValidator(m_regularExpressionValidator);
0028 }
0029 
0030 DateTimeSpinBox::~DateTimeSpinBox() {
0031     delete m_regularExpressionValidator;
0032 }
0033 
0034 void DateTimeSpinBox::keyPressEvent(QKeyEvent* event) {
0035     if (event->key() >= Qt::Key_0 && event->key() <= Qt::Key_9) {
0036         int cursorPos = lineEdit()->cursorPosition();
0037         int textLenght = lineEdit()->text().length();
0038         QAbstractSpinBox::keyPressEvent(event);
0039         getValue();
0040         if (lineEdit()->text().length() != textLenght)
0041             lineEdit()->setCursorPosition(cursorPos + 1);
0042         else
0043             lineEdit()->setCursorPosition(cursorPos);
0044     } else if (event->key() == Qt::Key_Up) {
0045         Type type = determineType(lineEdit()->cursorPosition());
0046         increaseValue(type, 1);
0047         writeValue();
0048         setCursorPosition(type);
0049     } else if (event->key() == Qt::Key_Down) {
0050         Type type = determineType(lineEdit()->cursorPosition());
0051         increaseValue(type, -1);
0052         writeValue();
0053         setCursorPosition(type);
0054     } else {
0055         QAbstractSpinBox::keyPressEvent(event);
0056         getValue();
0057     }
0058 }
0059 
0060 QAbstractSpinBox::StepEnabled DateTimeSpinBox::stepEnabled() const {
0061     return QAbstractSpinBox::StepEnabledFlag::StepUpEnabled | QAbstractSpinBox::StepEnabledFlag::StepDownEnabled; // for testing
0062 }
0063 
0064 void DateTimeSpinBox::stepBy(int steps) {
0065     Type type = determineType(lineEdit()->cursorPosition());
0066     increaseValue(type, steps);
0067     writeValue();
0068     setCursorPosition(type);
0069 }
0070 
0071 /*!
0072  * Write value to lineEdit of the spinbox
0073  */
0074 void DateTimeSpinBox::writeValue() {
0075     lineEdit()->setText(QString::number(mDateTime.year) + QLatin1Char('.') + QStringLiteral("%1").arg(mDateTime.month, 2, 10, QLatin1Char('0'))
0076                         + QLatin1Char('.') + QStringLiteral("%1").arg(mDateTime.day, 2, 10, QLatin1Char('0')) + QLatin1Char(' ')
0077                         + QStringLiteral("%1").arg(mDateTime.hour, 2, 10, QLatin1Char('0')) + QLatin1Char(':')
0078                         + QStringLiteral("%1").arg(mDateTime.minute, 2, 10, QLatin1Char('0')) + QLatin1Char(':')
0079                         + QStringLiteral("%1").arg(mDateTime.second, 2, 10, QLatin1Char('0')) + QLatin1Char('.')
0080                         + QStringLiteral("%1").arg(mDateTime.millisecond, 3, 10, QLatin1Char('0')));
0081     Q_EMIT valueChanged();
0082 }
0083 
0084 void DateTimeSpinBox::setValue(qint64 increment) {
0085     mDateTime = DateTime::dateTime(increment);
0086     writeValue();
0087 }
0088 
0089 qint64 DateTimeSpinBox::value() {
0090     return DateTime::createValue(mDateTime.year, mDateTime.month, mDateTime.day, mDateTime.hour, mDateTime.minute, mDateTime.second, mDateTime.millisecond);
0091 }
0092 
0093 /*!
0094  * Read value from lineEdit of the spinbox
0095  */
0096 void DateTimeSpinBox::getValue() {
0097     QString text = lineEdit()->text();
0098 
0099     int counter = 0;
0100     int startIndex = 0;
0101     for (int i = 0; i < text.length(); i++) {
0102         if (text[i] == QLatin1Char('.') || text[i] == QLatin1Char(':') || text[i] == QLatin1Char(' ') || i == text.length() - 1) {
0103             switch (counter) {
0104             case Type::year:
0105                 mDateTime.year = text.mid(startIndex, i - startIndex).toInt();
0106                 break;
0107             case Type::month:
0108                 mDateTime.month = text.mid(startIndex, i - startIndex).toInt();
0109                 break;
0110             case Type::day:
0111                 mDateTime.day = text.mid(startIndex, i - startIndex).toInt();
0112                 break;
0113             case Type::hour:
0114                 mDateTime.hour = text.mid(startIndex, i - startIndex).toInt();
0115                 break;
0116             case Type::minute:
0117                 mDateTime.minute = text.mid(startIndex, i - startIndex).toInt();
0118                 break;
0119             case Type::second:
0120                 mDateTime.second = text.mid(startIndex, i - startIndex).toInt();
0121                 break;
0122             case Type::millisecond:
0123                 mDateTime.millisecond = text.mid(startIndex, i - startIndex + 1).toInt(); // because of the condition (i == text.length()-1)
0124                 break;
0125             }
0126             startIndex = i + 1;
0127             counter++;
0128         }
0129     }
0130 
0131     Q_EMIT valueChanged();
0132 }
0133 
0134 void DateTimeSpinBox::setCursorPosition(Type type) {
0135     QString text = lineEdit()->text();
0136     int counter = 0;
0137     for (int i = 0; i < text.length(); i++) {
0138         if (text[i] == QLatin1Char('.') || text[i] == QLatin1Char(':') || text[i] == QLatin1Char(' '))
0139             counter++;
0140 
0141         if (counter - 1 == type) {
0142             lineEdit()->setCursorPosition(i);
0143             break;
0144         }
0145     }
0146 }
0147 
0148 bool DateTimeSpinBox::valid() {
0149     return true;
0150 }
0151 
0152 // step can also be negative
0153 bool DateTimeSpinBox::increaseValue(DateTimeSpinBox::Type type, int step) {
0154     switch (type) {
0155     case Type::year: {
0156         if (mDateTime.year + step < 0 && step < 0) {
0157             mDateTime.year = 0;
0158             return false;
0159         }
0160         mDateTime.year += step;
0161         return true;
0162     } break;
0163     case Type::month:
0164         return changeValue(mDateTime.month, Type::year, step);
0165         break;
0166     case Type::day:
0167         return changeValue(mDateTime.day, Type::month, step);
0168         break;
0169     case Type::hour:
0170         return changeValue(mDateTime.hour, Type::day, step);
0171         break;
0172     case Type::minute:
0173         return changeValue(mDateTime.minute, Type::hour, step);
0174         break;
0175     case Type::second:
0176         return changeValue(mDateTime.second, Type::minute, step);
0177         break;
0178     case Type::millisecond:
0179         return changeValue(mDateTime.millisecond, Type::second, step);
0180         break;
0181     default:
0182         return false;
0183         break;
0184     }
0185 }
0186 
0187 bool DateTimeSpinBox::changeValue(qint64& thisType, DateTimeSpinBox::Type nextTypeType, int step) {
0188     int maxValue = 1;
0189     switch (nextTypeType) {
0190     case (Type::year):
0191         maxValue = 12;
0192         break;
0193     case (Type::month):
0194         maxValue = 30;
0195         break;
0196     case (Type::day):
0197         maxValue = 24;
0198         break;
0199     case (Type::hour):
0200         maxValue = 60;
0201         break;
0202     case (Type::minute):
0203         maxValue = 60;
0204         break;
0205     case (Type::second):
0206         maxValue = 1000;
0207         break;
0208     case (Type::millisecond):
0209         return false;
0210     }
0211 
0212     int nextTypeCounter = step / maxValue;
0213     step -= nextTypeCounter * maxValue;
0214     if (thisType + step < 0 && step < 0) {
0215         nextTypeCounter--;
0216         if (increaseValue(nextTypeType, nextTypeCounter)) {
0217             step += maxValue;
0218             thisType += step;
0219             return true;
0220         } else {
0221             thisType = 0;
0222             return false;
0223         }
0224     } else if (thisType + step > maxValue - 1 && step > 0) {
0225         step -= nextTypeCounter * maxValue;
0226         if (thisType + step > maxValue - 1) {
0227             nextTypeCounter++;
0228             step -= maxValue;
0229             thisType += step;
0230         } else
0231             thisType += step;
0232 
0233         return increaseValue(nextTypeType, nextTypeCounter);
0234     }
0235     thisType += step;
0236     return true;
0237 }
0238 
0239 DateTimeSpinBox::Type DateTimeSpinBox::determineType(int cursorPos) const {
0240     QString text = lineEdit()->text();
0241 
0242     if (cursorPos > text.length())
0243         cursorPos = text.length();
0244 
0245     int counter = 0;
0246     for (int i = 0; i < cursorPos; i++) {
0247         if (text[i] == QLatin1Char('.') || text[i] == QLatin1Char(':') || text[i] == QLatin1Char(' '))
0248             counter++;
0249     }
0250 
0251     if (counter <= Type::millisecond)
0252         return static_cast<Type>(counter);
0253 
0254     return Type::millisecond;
0255 }