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 }