File indexing completed on 2025-07-13 12:44:33
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 }