#include "lts_date_time_edit.h" #include "lts_date_time_edit_p.h" #include "ui_lts_date_time_edit_touch.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static constexpr qint64 s_oneSecondMSec = 1000LL; static constexpr qint64 s_oneMinuteMSec = 60 * s_oneSecondMSec; static constexpr qint64 s_oneHourMSec = 60 * s_oneMinuteMSec; static constexpr qint64 s_oneDayMSec = 24 * s_oneHourMSec; namespace Ui { class LtsDateTimeEditLe { public: LtsWidgets::DigitsLineEdit *leLongDay = nullptr; LtsWidgets::DigitsLineEdit *leLongHHMMSS = nullptr; LtsWidgets::LineEdit *leLongTZ = nullptr; LtsWidgets::DigitsLineEdit *leLongMonth = nullptr; LtsWidgets::DigitsLineEdit *leLongYear = nullptr; QCheckBox *checkBox = nullptr; QGridLayout *gridLayout = nullptr; QPushButton *pbBBB = nullptr; QPushButton *pbBB = nullptr; QPushButton *pbB = nullptr; QPushButton *pbF = nullptr; QPushButton *pbFF = nullptr; QPushButton *pbFFF = nullptr; QPushButton *pbNow = nullptr; QSlider *slTime = nullptr; static void setFixedSize(QLineEdit *le, int charCount) { QSizePolicy sizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); sizePolicy.setVerticalStretch(0); sizePolicy.setHeightForWidth(false); const QFontMetricsF fm(le->font()); const auto textMargins = le->textMargins(); le->setSizePolicy(sizePolicy); charCount += 3; #if QT_VERSION < QT_VERSION_CHECK(5, 11, 0) const qreal w = fm.width(QLatin1Char('x')) * charCount + textMargins.left() + textMargins.right(); #else const qreal w = fm.horizontalAdvance(QLatin1Char('x')) * charCount + textMargins.left() + textMargins.right(); #endif le->setMinimumWidth(w); le->setMaximumWidth(w); } void setupUi(QWidget *parent) { gridLayout = new QGridLayout(parent); gridLayout->setMargin(0); int iStart = 3; checkBox = new QCheckBox; gridLayout->addWidget(checkBox, 0, iStart++, 1, 1); auto horizontalLayout = new QHBoxLayout; horizontalLayout->setSpacing(0); horizontalLayout->setContentsMargins(0, 0, 0, 0); leLongDay = new LtsWidgets::DigitsLineEdit(parent); setFixedSize(leLongDay, 2); leLongDay->setObjectName(QStringLiteral("leLongDay")); horizontalLayout->addWidget(leLongDay); leLongHHMMSS = new LtsWidgets::DigitsLineEdit(parent); leLongHHMMSS->setExactNumDigits(6); setFixedSize(leLongHHMMSS, 6); leLongHHMMSS->setObjectName(QStringLiteral("leLongHHMMSS")); horizontalLayout->addWidget(leLongHHMMSS); leLongTZ = new LtsWidgets::LineEdit(parent); leLongTZ->setAlignment(Qt::AlignCenter); setFixedSize(leLongTZ, 1); leLongTZ->setObjectName(QStringLiteral("leLongTZ")); horizontalLayout->addWidget(leLongTZ); leLongMonth = new LtsWidgets::DigitsLineEdit(parent); leLongMonth->setAlignment(Qt::AlignCenter); setFixedSize(leLongMonth, 3); leLongMonth->setObjectName(QStringLiteral("leLongMonth")); horizontalLayout->addWidget(leLongMonth); leLongYear = new LtsWidgets::DigitsLineEdit(parent); leLongYear->setExactNumDigits(2); setFixedSize(leLongYear, 2); leLongYear->setObjectName(QStringLiteral("leLongYear")); horizontalLayout->addWidget(leLongYear); gridLayout->addLayout(horizontalLayout, 0, iStart++, 1, 1); pbNow = new QPushButton(QApplication::translate("LtsDateTimeEdit", "Now", nullptr)); pbNow->setObjectName(QStringLiteral("pbNow")); gridLayout->addWidget(pbNow, 0, iStart++, 1, 1); QWidget::setTabOrder(checkBox, leLongDay); QWidget::setTabOrder(leLongDay, leLongHHMMSS); QWidget::setTabOrder(leLongHHMMSS, leLongTZ); QWidget::setTabOrder(leLongTZ, leLongMonth); QWidget::setTabOrder(leLongMonth, leLongYear); QWidget::setTabOrder(leLongYear, pbNow); parent->setFocusProxy(leLongDay); } static QPushButton *addButton(const QString &text, QWidget *parent) { QSizePolicy sizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); auto pb = new QPushButton(parent); pb->setMinimumWidth(27); pb->setMaximumWidth(27); pb->setText(text); sizePolicy.setHorizontalStretch(0); sizePolicy.setVerticalStretch(0); sizePolicy.setHeightForWidth(pb->sizePolicy().hasHeightForWidth()); pb->setSizePolicy(sizePolicy); return pb; } void addButtons(QWidget *parent) { pbBBB = addButton(QLatin1String("<<<"), parent); pbBB = addButton(QLatin1String("<<"), parent); pbB = addButton(QLatin1String("<"), parent); gridLayout->addWidget(pbBBB, 0, 0, 1, 1); gridLayout->addWidget(pbBB, 0, 1, 1, 1); gridLayout->addWidget(pbB, 0, 2, 1, 1); int iStart = 5; pbF = addButton(QLatin1String(">"), parent); pbFF = addButton(QLatin1String(">>"), parent); pbFFF = addButton(QLatin1String(">>>"), parent); gridLayout->addWidget(pbF, 0, iStart++, 1, 1); gridLayout->addWidget(pbFF, 0, iStart++, 1, 1); gridLayout->addWidget(pbFFF, 0, iStart++, 1, 1); } void addSlider(QWidget *parent) { slTime = new QSlider(parent); slTime->setOrientation(Qt::Horizontal); gridLayout->addWidget(slTime, 1, 0, 1, 7); } }; } namespace LtsWidgets { void setItemText(LtsWidgets::LineEdit *lineEdit, const QString &text) { if (!lineEdit) return; lineEdit->setText(text); } void setItemText(QComboBox *comboxBox, const QString &text) { if (!comboxBox) return; comboxBox->setCurrentIndex(comboxBox->findText(text)); } void setItemData(QComboBox *comboxBox, int data) { if (!comboxBox) return; comboxBox->setCurrentIndex(comboxBox->findData(data)); } void setItemValue(QSpinBox *spinBox, int value) { if (!spinBox) return; spinBox->setValue(value); } QString getItemText(LtsWidgets::LineEdit *lineEdit) { if (!lineEdit) return {}; return lineEdit->text(); } QString getItemText(QComboBox *comboxBox) { if (!comboxBox || (comboxBox->count() <= 0)) return {}; const QString &curTest = comboxBox->currentText(); if (curTest.isEmpty()) return {}; return curTest.at(0).toUpper(); } int getItemData(QComboBox *comboxBox) { if (!comboxBox || (comboxBox->count() <= 0)) return {}; int data = comboxBox->currentData().toInt(); return data; } int getItemValue(QSpinBox *spinBox) { if (!spinBox) return 0; return spinBox->value(); } LtsDateTimeEdit::LtsDateTimeEdit(QWidget *parent, bool withNowButton, bool checkable) : QWidget(parent) , m_scFixed(new QShortcut(QKeySequence(Qt::ShiftModifier + Qt::Key_F2), this, SLOT(setFixedDateTime()), nullptr, Qt::WidgetWithChildrenShortcut)) , m_scOffset(new QShortcut(QKeySequence(Qt::Key_F2), this, SLOT(setOffsetDateTime()), nullptr, Qt::WidgetWithChildrenShortcut)) , m_checkable(checkable) , m_withNowButton(withNowButton) { #ifdef TABLET_MODE setupUiTouch(); return; #else m_uiLe = new Ui::LtsDateTimeEditLe; m_uiLe->setupUi(this); setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); setFocusPolicy(Qt::StrongFocus); setContentsMargins(QMargins()); QCompleter *completer = new QCompleter(DTG::months(), this); completer->setCaseSensitivity(Qt::CaseInsensitive); completer->setCompletionMode(QCompleter::InlineCompletion); QRegularExpression rx(DTG::months().join(QLatin1Char('|'))); QRegularExpressionValidator *val = new QRegularExpressionValidator(rx, this); m_uiLe->leLongMonth->setCompleter(completer); m_uiLe->leLongMonth->setValidator(val); connect(m_uiLe->leLongDay, &DigitsLineEdit::textEdited, this, &LtsDateTimeEdit::valueDayChanged); connect(m_uiLe->leLongMonth, &LineEdit::textEdited, this, &LtsDateTimeEdit::valueMonthChanged2); connect(m_uiLe->leLongTZ, &LineEdit::textEdited, this, &LtsDateTimeEdit::valueTimezoneChangedStr); connect(m_uiLe->leLongYear, &DigitsLineEdit::textEdited, this, &LtsDateTimeEdit::valueYearChanged); connect(m_uiLe->leLongHHMMSS, &DigitsLineEdit::focusOut, this, &LtsDateTimeEdit::onFocusOutHHMMSS); connect(m_uiLe->leLongDay, &DigitsLineEdit::focusOut, this, &LtsDateTimeEdit::onFocusOutDay); connect(m_uiLe->leLongMonth, &LineEdit::focusOut, this, &LtsDateTimeEdit::onFocusOutMonth); connect(m_uiLe->leLongTZ, &LineEdit::focusOut, this, &LtsDateTimeEdit::onFocusOutTZ); connect(m_uiLe->leLongYear, &DigitsLineEdit::focusOut, this, &LtsDateTimeEdit::onFocusOutYear); connect(m_uiLe->leLongHHMMSS, &DigitsLineEdit::focusOutBackspace, this, &LtsDateTimeEdit::onFocusOutBackspace); connect(m_uiLe->leLongDay, &DigitsLineEdit::focusOutBackspace, this, &LtsDateTimeEdit::onFocusOutBackspace); connect(m_uiLe->leLongMonth, &LineEdit::focusOutBackspace, this, &LtsDateTimeEdit::onFocusOutBackspace); connect(m_uiLe->leLongTZ, &LineEdit::focusOutBackspace, this, &LtsDateTimeEdit::onFocusOutBackspace); connect(m_uiLe->leLongYear, &DigitsLineEdit::focusOutBackspace, this, &LtsDateTimeEdit::onFocusOutBackspace); connect(m_uiLe->leLongHHMMSS, &DigitsLineEdit::wheelEventReceived, this, &LtsDateTimeEdit::onHmsWheelEvent); connect(m_uiLe->leLongDay, &DigitsLineEdit::wheelEventReceived, this, &LtsDateTimeEdit::onDayWheelEvent); connect(m_uiLe->leLongMonth, &DigitsLineEdit::wheelEventReceived, this, &LtsDateTimeEdit::onMonthWheelEvent); connect(m_uiLe->leLongYear, &DigitsLineEdit::wheelEventReceived, this, &LtsDateTimeEdit::onYearWheelEvent); connect(m_uiLe->checkBox, &QCheckBox::toggled, this, &LtsDateTimeEdit::onCheckBoxChanged); connect(m_uiLe->pbNow, &QPushButton::clicked, this, &LtsDateTimeEdit::setOffsetDateTime); connect(checkBox(), &QCheckBox::toggled, this, &LtsDateTimeEdit::toggled); setTimezone(DTG::timezone()); setCheckable(m_checkable); setWithNowButton(m_withNowButton); onCheckBoxChanged(); #endif } LtsDateTimeEdit::~LtsDateTimeEdit() { delete m_uiLe; } bool LtsDateTimeEdit::setupUiTouch() { m_uiTouch = new Ui::LtsDateTimeEdit; m_uiTouch->setupUi(this); setFocusPolicy(Qt::StrongFocus); for (int i = 65; i < 91; i++ ) { if (QChar(i).toUpper() == QChar::fromLatin1('I')) continue; m_uiTouch->cbxTimeZone->addItem(QChar(i), QChar(i)); } connect(m_uiTouch->pbNow, &QPushButton::clicked, this, &LtsDateTimeEdit::setOffsetDateTime); connect(m_uiTouch->cbxMonth, static_cast(&QComboBox::currentIndexChanged), this, &LtsDateTimeEdit::dateTimeChanged); connect(m_uiTouch->spDay, &QSpinBox::editingFinished, this, &LtsDateTimeEdit::dateTimeChanged); connect(m_uiTouch->spHour, &QSpinBox::editingFinished, this, &LtsDateTimeEdit::dateTimeChanged); connect(m_uiTouch->spMinutes, &QSpinBox::editingFinished, this, &LtsDateTimeEdit::dateTimeChanged); connect(m_uiTouch->spSeconds, &QSpinBox::editingFinished, this, &LtsDateTimeEdit::dateTimeChanged); connect(m_uiTouch->spYear, &QSpinBox::editingFinished, this, &LtsDateTimeEdit::dateTimeChanged); connect(m_uiTouch->checkBox, &QCheckBox::toggled, this, &LtsDateTimeEdit::onCheckBoxChanged); connect(m_uiTouch->cbxTimeZone, static_cast(&QComboBox::currentIndexChanged), this, &LtsDateTimeEdit::valueTimezoneChangedInt); connect(m_uiTouch->cbxMonth, &QComboBox::currentTextChanged, this, &LtsDateTimeEdit::valueMonthChanged); connect(checkBox(), &QCheckBox::toggled, this, &LtsDateTimeEdit::toggled); setMinMaxMonth(1,12); setTimezone(DTG::timezone()); setCheckable(m_checkable); setWithNowButton(m_withNowButton); onCheckBoxChanged(); return true; } void LtsDateTimeEdit::setRetrieveCurrentTimeCallback(const CurrentTimeCallback &cb) { m_getCurrentTimeCallback = cb; } void LtsDateTimeEdit::setEditTimezone(const bool edit) { m_editTimezone = edit; if (m_uiLe) m_uiLe->leLongTZ->setEnabled(m_editTimezone); if (m_uiTouch) m_uiTouch->cbxTimeZone->setEnabled(m_editTimezone); } void LtsDateTimeEdit::setWithButtons(const bool buttons) { m_withButtons = buttons; if (buttons) { if (m_uiLe && !m_uiLe->pbBBB) { m_uiLe->addButtons(this); connect(m_uiLe->pbBBB, &QPushButton::clicked, this, &LtsDateTimeEdit::onBackHourClicked); connect(m_uiLe->pbBB, &QPushButton::clicked, this, &LtsDateTimeEdit::onBackMinuteClicked); connect(m_uiLe->pbB, &QPushButton::clicked, this, &LtsDateTimeEdit::onBackSecondClicked); connect(m_uiLe->pbF, &QPushButton::clicked, this, &LtsDateTimeEdit::onForwardSecondClicked); connect(m_uiLe->pbFF, &QPushButton::clicked, this, &LtsDateTimeEdit::onForwardMinuteClicked); connect(m_uiLe->pbFFF, &QPushButton::clicked, this, &LtsDateTimeEdit::onForwardHourClicked); disableControls(); } } else { if (m_uiLe) { delete m_uiLe->pbBBB; delete m_uiLe->pbBB; delete m_uiLe->pbB; delete m_uiLe->pbFFF; delete m_uiLe->pbFF; delete m_uiLe->pbF; m_uiLe->pbBBB = nullptr; m_uiLe->pbBB = nullptr; m_uiLe->pbB = nullptr; m_uiLe->pbFFF = nullptr; m_uiLe->pbFF = nullptr; m_uiLe->pbF = nullptr; } } } void LtsDateTimeEdit::setWithSlider(const bool slider) { m_withSlider = slider; if (!m_uiLe) return; if (slider) { m_uiLe->addSlider(this); m_uiLe->slTime->setMinimum(m_minTimeMSec / 1000); m_uiLe->slTime->setMaximum(m_maxTimeMSec > 0 ? m_maxTimeMSec / 1000 : time(nullptr)); connect(m_uiLe->slTime, &QSlider::sliderMoved, this, &LtsDateTimeEdit::setSliderValue); disableControls(); } else { delete m_uiLe->slTime; m_uiLe->slTime = nullptr; } } void LtsDateTimeEdit::setReadOnly(const bool readOnly) { if (m_readOnly == readOnly) return; m_readOnly = readOnly; if (m_uiLe) { m_uiLe->leLongDay->setReadOnly(m_readOnly); m_uiLe->leLongHHMMSS->setReadOnly(m_readOnly); m_uiLe->leLongMonth->setReadOnly(m_readOnly); m_uiLe->leLongTZ->setReadOnly(m_readOnly); m_uiLe->leLongYear->setReadOnly(m_readOnly); } if (m_uiTouch) { m_uiTouch->spDay->setReadOnly(m_readOnly); m_uiTouch->spHour->setReadOnly(m_readOnly); m_uiTouch->spMinutes->setReadOnly(m_readOnly); m_uiTouch->spSeconds->setReadOnly(m_readOnly); m_uiTouch->spYear->setReadOnly(m_readOnly); m_uiTouch->cbxTimeZone->setEnabled(m_readOnly); } } QCheckBox *LtsDateTimeEdit::checkBox() const { return m_uiLe ? m_uiLe->checkBox : m_uiTouch ? m_uiTouch->checkBox : nullptr; } void LtsDateTimeEdit::setCheckable(bool checkable) { if (!checkBox() || (m_checkable == checkable)) return; m_checkable = checkable; checkBox()->setVisible(checkable); if (!checkable) checkBox()->setChecked(true); } void LtsDateTimeEdit::setChecked(bool bChecked) { if (!checkBox()) return; checkBox()->setChecked(bChecked); } bool LtsDateTimeEdit::isChecked() const { if (!checkBox()) return false; return checkBox()->isChecked(); } QPushButton *LtsDateTimeEdit::pbNow() const { return m_uiLe ? m_uiLe->pbNow : m_uiTouch ? m_uiTouch->pbNow : nullptr; } void LtsDateTimeEdit::setWithNowButton(bool nowButton) { if (!pbNow()) return; pbNow()->setVisible(nowButton); } bool LtsDateTimeEdit::setMsecsSinceEpoch(qint64 msec, bool setToNearestPossible) { if (msec == 0 && isCheckable()) { if (m_uiLe) m_uiLe->checkBox->setChecked(false); m_dateIsAcceptable = true; return true; } if (m_uiLe) m_uiLe->checkBox->setChecked(true); m_dateIsAcceptable = false; if (msec == 0) return false; if (!setToNearestPossible) { if (!isDataAcceptable()) return false; } else { if (msec < m_minTimeMSec && m_minTimeMSec > 0) msec = m_minTimeMSec; if (msec > m_maxTimeMSec && m_maxTimeMSec > 0) msec = m_maxTimeMSec; } m_curTimeMSec = msec; const qint64 offsetTimeZoneMsec = getTimezoneAsSecs() * 1000; const QDateTime datetime(QDateTime::fromMSecsSinceEpoch(m_curTimeMSec + offsetTimeZoneMsec, Qt::UTC)); const QDate date(datetime.date()); const QTime time(datetime.time()); const int day = date.day(); const int month = date.month(); const int year = date.year(); const int hour = time.hour(); const int minute = time.minute(); const int second = time.second(); if (!setYear(year) || !setMonth(month) || !setDay(day) || !setHHMMSS(hour, minute, second)) return false; if (!isDataAcceptable()) return false; enableControls(); emit dateTimeChanged(); return true; } qint64 LtsDateTimeEdit::msecsSinceEpoch() const { if (isEmpty()) return 0; if (m_uiLe && !m_uiLe->checkBox->isChecked()) return 0; if (m_uiTouch && !m_uiTouch->checkBox->isChecked()) return 0; auto that = const_cast(this); if (!that->isDataAcceptable()) return 0; return m_curTimeMSec; } bool LtsDateTimeEdit::setTimezone(char tz) { tz = toupper(tz); if (!DTG::isTimezoneValid(tz)) return false; if (m_uiLe) setItemText(m_uiLe->leLongTZ, QString(QLatin1Char(tz))); if (m_uiTouch) setItemText(m_uiTouch->cbxTimeZone, QString(QLatin1Char(tz))); setMsecsSinceEpoch(m_curTimeMSec); return true; } char LtsDateTimeEdit::getTimezone() const { QString str; if (m_uiLe) str = getItemText(m_uiLe->leLongTZ); if (m_uiTouch) str = getItemText(m_uiTouch->cbxTimeZone); if (str.isEmpty()) return DTG::timezone(); return str[0].toUpper().toLatin1(); } qint64 LtsDateTimeEdit::getTimezoneAsSecs() const { const char tzGUI = getTimezone(); return DTG::timezone2Secs(tzGUI); } void LtsDateTimeEdit::clearTimezone() { if (m_uiLe) m_uiLe->leLongTZ->clear(); if (m_uiTouch) m_uiTouch->cbxTimeZone->setCurrentIndex(0); } void LtsDateTimeEdit::setMinMaxMonth(int minMonth, int maxMonth) { if (!m_uiTouch) return; m_minMonth = minMonth; m_maxMonth = maxMonth; const int currentMonth = m_uiTouch->cbxMonth->currentData().toInt(); m_uiTouch->cbxMonth->blockSignals(true); m_uiTouch->cbxMonth->clear(); for (int i = 1; i <= 12; ++i) { if (i < m_minMonth) continue; if (i > m_maxMonth) break; const QStringList &monthSl = DTG::months(); m_uiTouch->cbxMonth->addItem(monthSl[i-1], i); } m_uiTouch->cbxMonth->setCurrentIndex(m_uiTouch->cbxMonth->findData(currentMonth)); m_uiTouch->cbxMonth->blockSignals(false); } void LtsDateTimeEdit::setSubWidgetsProperties(const QString &name, const QVariant &value) { if (m_uiLe) { m_uiLe->leLongDay->setProperty(name.toStdString().c_str(), value); m_uiLe->leLongHHMMSS->setProperty(name.toStdString().c_str(), value); m_uiLe->leLongMonth->setProperty(name.toStdString().c_str(), value); m_uiLe->leLongYear->setProperty(name.toStdString().c_str(), value); m_uiLe->leLongTZ->setProperty(name.toStdString().c_str(), value); } if (m_uiTouch) { m_uiTouch->spDay->setProperty(name.toStdString().c_str(), value); m_uiTouch->spHour->setProperty(name.toStdString().c_str(), value); m_uiTouch->spMinutes->setProperty(name.toStdString().c_str(), value); m_uiTouch->spSeconds->setProperty(name.toStdString().c_str(), value); m_uiTouch->spYear->setProperty(name.toStdString().c_str(), value); } } bool LtsDateTimeEdit::setMonth(int month) { const auto months(DTG::months()); if (month < 1 || month > months.size()) return false; if (m_uiLe) setItemText(m_uiLe->leLongMonth, months.at(month - 1)); if (m_uiTouch) setItemData(m_uiTouch->cbxMonth, month); return true; } int LtsDateTimeEdit::getMonth() const { if (m_uiLe) { const QString str = getItemText(m_uiLe->leLongMonth); if (str.length() != 3) return -1; int idx = DTG::months().indexOf(str); if (idx < 0) return -1; return idx + 1; } if (m_uiTouch) { int month = getItemData(m_uiTouch->cbxMonth); return month; } return 0; } bool LtsDateTimeEdit::setDay(int day) { if (day < 1 || day > 31) return false; QString oldDay; if (m_uiLe) oldDay = m_uiLe->leLongDay->text(); int oldDayInt = 0; if (m_uiTouch) oldDayInt = getItemValue(m_uiTouch->spDay); if (m_uiLe) setItemText(m_uiLe->leLongDay, QStringLiteral("%1").arg(day, 2, 10, QLatin1Char('0'))); if (m_uiTouch) setItemValue(m_uiTouch->spDay, day); if (!isDayAcceptable()) { if (m_uiLe) setItemText(m_uiLe->leLongDay, oldDay); if (m_uiTouch) setItemValue(m_uiTouch->spDay, oldDayInt); return false; } return true; } int LtsDateTimeEdit::getDay() const { if (m_uiLe) return m_uiLe->leLongDay->getValue(); if (m_uiTouch) return getItemValue(m_uiTouch->spDay); return 0; } bool LtsDateTimeEdit::setHHMMSS(int hour, int minute, int second) { if (hour < 0 || hour > 23 || minute < 0 || minute > 59 || second < 0 || second > 59) return false; if (m_uiLe) m_uiLe->leLongHHMMSS->setValue(hour * 10000 + minute * 100 + second); if (m_uiTouch) { setItemValue(m_uiTouch->spHour , hour); setItemValue(m_uiTouch->spMinutes, minute); setItemValue(m_uiTouch->spSeconds, second); } return true; } int LtsDateTimeEdit::getHour() const { if (m_uiLe) { const int time = m_uiLe->leLongHHMMSS->getValue(); return time / 10000; } if (m_uiTouch) { return getItemValue(m_uiTouch->spHour); } return 0; } int LtsDateTimeEdit::getMinute() const { if (m_uiLe) { const int time = m_uiLe->leLongHHMMSS->getValue(); return (time / 100) % 100; } if (m_uiTouch) { return getItemValue(m_uiTouch->spMinutes); } return 0; } int LtsDateTimeEdit::getSecond() const { if (m_uiLe) { const int time = m_uiLe->leLongHHMMSS->getValue(); return time % 100; } if (m_uiTouch) { return getItemValue(m_uiTouch->spSeconds); } return 0; } bool LtsDateTimeEdit::setYear(int year) { int finalYear = -1; if (year >= 0 && year <= 99) finalYear = year; else if (year >= 2000 && year <= 2099) finalYear = year - 2000; if (finalYear >= 0) { if (m_uiLe) m_uiLe->leLongYear->setValue(finalYear); if (m_uiTouch) setItemValue(m_uiTouch->spYear, finalYear); return true; } return false; } int LtsDateTimeEdit::getYear() const { if (m_uiLe) return 2000 + m_uiLe->leLongYear->getValue(); if (m_uiTouch) return 2000 + m_uiTouch->spYear->value(); return 2000; } bool LtsDateTimeEdit::isEmpty() const { return (isEmptyDay() || isEmptyHHMMSS() || isEmptyMonth() || isEmptyTimezone() || isEmptyYear()); } bool LtsDateTimeEdit::isValidDate() const { return m_dateIsAcceptable; } bool LtsDateTimeEdit::isEmptyDay() const { if (m_uiLe) return m_uiLe->leLongDay->isEmpty(); return false; } bool LtsDateTimeEdit::isEmptyHHMMSS() const { if (m_uiLe) return m_uiLe->leLongHHMMSS->isEmpty(); return false; } bool LtsDateTimeEdit::isEmptyMonth() const { if (m_uiLe) return m_uiLe->leLongMonth->text().isEmpty(); return false; } bool LtsDateTimeEdit::isEmptyTimezone() const { if (m_uiLe) return m_uiLe->leLongTZ->text().isEmpty(); return false; } bool LtsDateTimeEdit::isEmptyYear() const { if (m_uiLe) return m_uiLe->leLongYear->isEmpty(); return false; } bool LtsDateTimeEdit::hasFocus() const { if (!m_uiLe) return false; return (m_uiLe->leLongDay->hasFocus() || m_uiLe->leLongHHMMSS->hasFocus() || m_uiLe->leLongMonth->hasFocus() || m_uiLe->leLongTZ->hasFocus() || m_uiLe->leLongYear->hasFocus()); } bool LtsDateTimeEdit::isDataAcceptable() { m_dateIsAcceptable = false; bool state = isDayAcceptable() && isHHMMSSAcceptable() && isMonthAcceptable() && isYearAcceptable() && QDate::isValid(getYear(), getMonth(), getDay()) && QTime::isValid(getHour(), getMinute(), getSecond()); if (m_editTimezone && state) state = isTzAcceptable(); showInvalidData(false); if (!state) { disableControls(); return false; } qint64 msec = QDateTime(QDate(getYear(), getMonth(), getDay()), QTime(getHour(), getMinute(), getSecond()), Qt::UTC).toMSecsSinceEpoch(); msec -= DTG::timezone2Secs(getTimezone()) * 1000LL; if (m_minTimeMSec > 0 && m_maxTimeMSec > 0 && m_minTimeMSec < m_maxTimeMSec) { if (msec < m_minTimeMSec || msec > m_maxTimeMSec) return false; } m_curTimeMSec = msec; enableControls(); m_dateIsAcceptable = true; return true; } bool LtsDateTimeEdit::isDayAcceptable() const { int value = getDay(); int max = 31; if (isMonthAcceptable()) { const int month = getMonth(); switch (month) { case 2: { max = 28; if (isYearAcceptable()) { if (getYear() % 4 == 0 && (getYear() % 100 != 0 || getYear() % 400 == 0)) max = 29; } break; } case 4: case 6: case 9: case 11: max = 30; break; default: break; } } return (value > 0 && value <= max); } bool LtsDateTimeEdit::isHHMMSSAcceptable() const { int hh = getHour(); if (hh < 0 || hh > 23) return false; int mm = getMinute(); if (mm < 0 || mm > 59) return false; int ss = getSecond(); if (ss < 0 || ss > 59) return false; return true; } bool LtsDateTimeEdit::isMonthAcceptable() const { int value = getMonth(); return (value > 0 && value <= 12); } bool LtsDateTimeEdit::isYearAcceptable() const { if (m_uiLe) return isLineEditDataAcceptable(m_uiLe->leLongYear); return true; } bool LtsDateTimeEdit::isTzAcceptable() const { if (m_uiLe) return isLineEditDataAcceptable(m_uiLe->leLongTZ); return true; } void LtsDateTimeEdit::showInvalidData(bool setFocus) { if (!m_uiLe) return; bool focus = setFocus; auto check = [&](QLineEdit * le, bool isValid) { if (isValid) le->setStyleSheet(QString()); else { le->setStyleSheet(QLatin1String("background-color: yellow;")); if (focus) { le->setFocus(); focus = false; } } }; check(m_uiLe->leLongDay, isDayAcceptable()); check(m_uiLe->leLongHHMMSS, isHHMMSSAcceptable()); if (m_editTimezone) check(m_uiLe->leLongTZ, isTzAcceptable()); check(m_uiLe->leLongMonth, isMonthAcceptable()); check(m_uiLe->leLongYear, isYearAcceptable()); } bool LtsDateTimeEdit::isLineEditDataAcceptable(QLineEdit *le) const { const QValidator *pValidator = le->validator(); if (!pValidator) return true; QString text = le->text(); int pos; QValidator::State state = pValidator->validate(text, pos); if (state == QValidator::Acceptable || state == QValidator::Intermediate) return true; return false; } void LtsDateTimeEdit::setFixedDateTime() { setMsecsSinceEpoch(LtsUtils::OperatorSettings::getInstance()->value(QStringLiteral("TimeSettings"), QStringLiteral("FixedTime"), 0).toLongLong()); } void LtsDateTimeEdit::setOffsetDateTime() { if (!m_getCurrentTimeCallback) return; qint64 offset = LtsUtils::OperatorSettings::getInstance()->value(QStringLiteral("TimeSettings"), QStringLiteral("Offset"), 0).toLongLong(); setMsecsSinceEpoch((m_getCurrentTimeCallback() / 1000) * 1000 + offset); } void LtsDateTimeEdit::setSliderValue(int val) { setMsecsSinceEpoch(val * 1000LL); } void LtsDateTimeEdit::valueDayChanged(const QString &str) { if (!m_uiLe) return; int curpos = m_uiLe->leLongDay->cursorPosition(); int cp = curpos; bool changed = false; QString tag(str); if (tag.length() > 2) { tag = tag.left(2); changed = true; } bool ok; int t = tag.toInt(&ok, 10); if (ok) { if (t > 31) { tag.remove(curpos - 1, 1); if (cp >= curpos) cp--; changed = true; } } if (changed) { m_uiLe->leLongDay->setText(tag); m_uiLe->leLongDay->setCursorPosition(cp); } if (tag.length() == 2) { showInvalidData(false); if (m_autoForward) focusNextPrevChild(true); } } void LtsDateTimeEdit::fixupHHMMSS() { if (!m_uiLe) return; int curpos = m_uiLe->leLongHHMMSS->cursorPosition(); int cp = curpos; bool changed = false; QString uhrzeit(m_uiLe->leLongHHMMSS->text()); QString hh; QString mm; QString ss; if (uhrzeit.length() > 1) hh = uhrzeit.left(2); else hh = uhrzeit + QLatin1Char('0'); if (uhrzeit.length() > 0) { if (uhrzeit.length() == 1) hh = uhrzeit.mid(0, 1) + QLatin1Char('0'); else if (uhrzeit.length() >= 2) { hh = uhrzeit.mid(0, 2); if (uhrzeit.length() > 2) { if (uhrzeit.length() == 3) mm = uhrzeit.mid(2, 1) + QLatin1Char('0'); else if (uhrzeit.length() >= 4) { mm = uhrzeit.mid(2, 2); if (uhrzeit.length() == 5) ss = uhrzeit.mid(4, 1) + QLatin1Char('0'); else if (uhrzeit.length() == 6) ss = uhrzeit.mid(4, 2); } } } } bool ok; int h = hh.toInt(&ok, 10); if (ok) { if (h > 23) { uhrzeit.remove(curpos - 1, 1); if (cp >= curpos) cp--; changed = true; } } int m = mm.toInt(&ok, 10); if (ok) { if ((hh.length() == 2) && (m > 59)) { uhrzeit.remove(curpos - 1, 1); if (cp >= curpos) cp--; changed = true; } } int s = ss.toInt(&ok, 10); if (ok) { if ((hh.length() == 2 && mm.length() == 2) && (s > 59)) { uhrzeit.remove(curpos - 1, 1); if (cp >= curpos) cp--; changed = true; } } if (changed) { m_uiLe->leLongHHMMSS->setText(uhrzeit); m_uiLe->leLongHHMMSS->setCursorPosition(cp); } isDataAcceptable(); emit dateTimeChanged(); } void LtsDateTimeEdit::valueMonthChanged2(const QString &str) { if (!m_uiLe) return; if (str.length() == 3 && m_uiLe->leLongMonth->selectedText().isEmpty()) focusNextPrevChild(true); } void LtsDateTimeEdit::valueMonthChanged(const QString &str) { if (!m_uiLe) return; int curpos = m_uiLe->leLongMonth->cursorPosition(); int cp = curpos; bool changed = false; QString monStr = str; const auto months = DTG::months(); for (int i = 0; i < months.size(); ++i) { const QString &mon = months.at(i); if (mon.startsWith(monStr, Qt::CaseInsensitive)) { monStr = mon; changed = true; break; } } if (!changed) { monStr.remove(curpos - 1, 1); if (cp >= curpos) cp--; changed = true; } if (changed) { m_uiLe->leLongMonth->setText(monStr); if (cp > 0) m_uiLe->leLongMonth->setCursorPosition(cp); else m_uiLe->leLongMonth->setCursorPosition(1); } if (monStr.size() == 3) { showInvalidData(false); if (m_autoForward) focusNextPrevChild(true); } } void LtsDateTimeEdit::valueTimezoneChangedStr(const QString &str) { if (str.length() > 1) { clearTimezone(); return; } if (str.length() == 0) return; QChar c = str.at(0); if (!c.isLetter() || c.toUpper() == QLatin1Char('J')) { clearTimezone(); return; } if (c.isLower()) setTimezone(c.toUpper().toLatin1()); showInvalidData(false); if (m_autoForward) focusNextPrevChild(true); } void LtsDateTimeEdit::valueTimezoneChangedInt(int newTimezone) { Q_UNUSED(newTimezone); if (!m_uiTouch) return; const QString &curText = m_uiTouch->cbxTimeZone->currentText(); if (curText.isEmpty()) return; const QChar tz = curText.at(0); setTimezone(tz.toUpper().toLatin1()); } void LtsDateTimeEdit::valueYearChanged(const QString &str) { if (!m_uiLe) return; int curpos = m_uiLe->leLongYear->cursorPosition(); int cp = curpos; bool changed = false; const QString &year = str; QString yy; if (year.length() > 2) { yy = year.left(2); changed = true; } if (changed) { m_uiLe->leLongYear->setText(yy); m_uiLe->leLongYear->setCursorPosition(cp); } if (year.length() == 2) { showInvalidData(false); if (m_autoForward) focusNextPrevChild(true); } } void LtsDateTimeEdit::onBackHourClicked() { if (!setMsecsSinceEpoch(m_curTimeMSec - s_oneHourMSec)) setMsecsSinceEpoch(m_minTimeMSec); } void LtsDateTimeEdit::onBackMinuteClicked() { const int ofs = QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier) ? 5 : 1; if (!setMsecsSinceEpoch(m_curTimeMSec - ofs * s_oneMinuteMSec)) setMsecsSinceEpoch(m_minTimeMSec); } void LtsDateTimeEdit::onBackSecondClicked() { const int ofs = QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier) ? 5 : 1; if (!setMsecsSinceEpoch(m_curTimeMSec - ofs * s_oneSecondMSec)) setMsecsSinceEpoch(m_minTimeMSec); } void LtsDateTimeEdit::onForwardSecondClicked() { const int ofs = QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier) ? 5 : 1; if (!setMsecsSinceEpoch(m_curTimeMSec + ofs * s_oneSecondMSec)) setMsecsSinceEpoch(m_maxTimeMSec); } void LtsDateTimeEdit::onForwardMinuteClicked() { const int ofs = QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier) ? 5 : 1; if (!setMsecsSinceEpoch(m_curTimeMSec + ofs * s_oneMinuteMSec)) setMsecsSinceEpoch(m_maxTimeMSec); } void LtsDateTimeEdit::onForwardHourClicked() { if (!setMsecsSinceEpoch(m_curTimeMSec + s_oneHourMSec)) setMsecsSinceEpoch(m_maxTimeMSec); } void LtsDateTimeEdit::onCheckBoxChanged() { QVector widgets; bool bEnable = false; if (m_uiLe) { widgets = {m_uiLe->leLongDay, m_uiLe->leLongHHMMSS, m_uiLe->leLongTZ, m_uiLe->leLongMonth, m_uiLe->leLongYear}; } if (m_uiTouch) { widgets = {m_uiTouch->spDay, m_uiTouch->spHour, m_uiTouch->spMinutes, m_uiTouch->spSeconds, m_uiTouch->spYear, m_uiTouch->cbxMonth, m_uiTouch->cbxTimeZone, m_uiTouch->pbNow}; } bEnable = checkBox()->isChecked(); for (auto w : qAsConst(widgets)) { if (w) w->setEnabled(bEnable); } enaDisaControls(bEnable); } void LtsDateTimeEdit::disableControls() { enaDisaControls(false); } void LtsDateTimeEdit::enableControls() { if (!m_uiLe) return; enaDisaControls(true); if (m_withSlider && m_minTimeMSec != 0 && m_maxTimeMSec > 0 && m_maxTimeMSec > m_minTimeMSec) { m_uiLe->slTime->setEnabled(true); m_uiLe->slTime->setValue(m_curTimeMSec / 1000LL); } } void LtsDateTimeEdit::enaDisaControls(bool bEnable) { if (!m_uiLe) return; const QVector widgets({m_uiLe->pbBBB, m_uiLe->pbBB, m_uiLe->pbB, m_uiLe->pbF, m_uiLe->pbFF, m_uiLe->pbFFF, m_uiLe->slTime}); for (auto w : widgets) { if (w) w->setEnabled(bEnable); } } void LtsDateTimeEdit::setMinimumTimeMSec(qint64 msec) { if (m_minTimeMSec == msec) return; m_minTimeMSec = msec; if (m_minTimeMSec > 0 && m_uiLe && m_uiLe->slTime) { m_uiLe->slTime->setMinimum(m_minTimeMSec / 1000LL); if (m_maxTimeMSec > m_minTimeMSec) m_uiLe->slTime->setEnabled(true); } if (m_curTimeMSec < m_minTimeMSec) setMsecsSinceEpoch(m_minTimeMSec); } void LtsDateTimeEdit::setMaximumTimeMSec(qint64 msec) { if (m_maxTimeMSec == msec) return; m_maxTimeMSec = msec; if (!m_uiLe || !m_uiLe->slTime) return; m_uiLe->slTime->setMaximum(m_maxTimeMSec / 1000LL); if (m_minTimeMSec > 0 && m_maxTimeMSec > m_minTimeMSec) m_uiLe->slTime->setEnabled(true); if (m_maxTimeMSec > 0 && m_curTimeMSec > m_maxTimeMSec) setMsecsSinceEpoch(m_maxTimeMSec); } void LtsDateTimeEdit::clear() { if (m_uiLe) { m_uiLe->leLongDay->clear(); m_uiLe->leLongHHMMSS->clear(); m_uiLe->leLongMonth->clear(); m_uiLe->leLongYear->clear(); m_uiLe->leLongTZ->clear(); m_curTimeMSec = 0; } if (m_uiTouch) { m_uiTouch->spDay->clear(); m_uiTouch->spHour->clear(); m_uiTouch->spMinutes->clear(); m_uiTouch->spSeconds->clear(); m_uiTouch->spYear->clear(); } emit dateTimeChanged(); } void LtsDateTimeEdit::onFocusOutDay() { if (!m_uiLe) return; if (m_backspacePressed) { m_backspacePressed = false; return; } if (!m_recursionAvoid && !hasFocus()) onFocusOut(); QString value = m_uiLe->leLongDay->text(); if (value.length() == 0 && !m_backspacePressed) { if (!isEmptyHHMMSS() || !isEmptyMonth() || (!isEmptyTimezone() && m_uiLe->leLongTZ->isEnabled()) || !isEmptyYear()) { if (m_getCurrentTimeCallback) { const QDateTime dt(QDateTime::fromMSecsSinceEpoch(m_getCurrentTimeCallback(), Qt::UTC)); setDay(dt.date().day()); } } } isDataAcceptable(); emit dateTimeChanged(); } void LtsDateTimeEdit::onFocusOutHHMMSS() { if (!m_uiLe) return; if (m_backspacePressed) { m_backspacePressed = false; return; } if (!m_recursionAvoid && !hasFocus()) onFocusOut(); QString value = m_uiLe->leLongHHMMSS->text(); if (value.length() == 0 && !m_backspacePressed) { if (!isEmptyDay() || !isEmptyMonth() || !(isEmptyTimezone() && m_uiLe->leLongTZ->isEnabled()) || !isEmptyYear()) { if (m_getCurrentTimeCallback) { const QDateTime dt(QDateTime::fromMSecsSinceEpoch(m_getCurrentTimeCallback(), Qt::UTC)); setHHMMSS(dt.time().hour(), dt.time().minute(), dt.time().second()); } } } value += QString(6 - value.length(), QLatin1Char('0')); m_uiLe->leLongHHMMSS->setText(value); isDataAcceptable(); emit dateTimeChanged(); } void LtsDateTimeEdit::onFocusOutTZ() { if (!m_uiLe) return; if (m_backspacePressed) { m_backspacePressed = false; return; } if (!m_recursionAvoid && !hasFocus()) onFocusOut(); QString value = m_uiLe->leLongTZ->text(); if (value.length() == 0 && !m_backspacePressed) { if (!isEmptyHHMMSS() || !isEmptyMonth() || !isEmptyDay() || !isEmptyYear()) setTimezone(DTG::timezone()); } isDataAcceptable(); emit dateTimeChanged(); } void LtsDateTimeEdit::onFocusOutMonth() { if (!m_uiLe) return; if (m_backspacePressed) { m_backspacePressed = false; return; } if (!m_recursionAvoid && !hasFocus()) onFocusOut(); QString value = m_uiLe->leLongMonth->text(); if (value.length() == 0 && !m_backspacePressed) { if (!isEmptyHHMMSS() || !isEmptyDay() || (!isEmptyTimezone() && m_uiLe->leLongTZ->isEnabled()) || !isEmptyYear()) { if (m_getCurrentTimeCallback) { const QDateTime dt(QDateTime::fromMSecsSinceEpoch(m_getCurrentTimeCallback(), Qt::UTC)); setMonth(dt.date().month()); } } } isDataAcceptable(); emit dateTimeChanged(); } void LtsDateTimeEdit::onFocusOutYear() { if (!m_uiLe) return; if (m_backspacePressed) { m_backspacePressed = false; return; } if (!m_recursionAvoid && !hasFocus()) onFocusOut(); QString value = m_uiLe->leLongYear->text(); if (value.length() == 1) { value = QLatin1Char('0') + value; m_uiLe->leLongYear->setText(value); } if (value.length() == 0 && !m_backspacePressed) { if (!isEmptyHHMMSS() || !isEmptyMonth() || (!isEmptyTimezone() && m_uiLe->leLongTZ->isEnabled()) || !isEmptyDay()) { if (m_getCurrentTimeCallback) { const QDateTime dt(QDateTime::fromMSecsSinceEpoch(m_getCurrentTimeCallback(), Qt::UTC)); setYear(dt.date().year()); } } } isDataAcceptable(); emit dateTimeChanged(); } void LtsDateTimeEdit::onFocusOutBackspace() { m_backspacePressed = true; } void LtsDateTimeEdit::onFocusOut() { if (!m_uiLe) return; if (m_backspacePressed) { m_backspacePressed = false; return; } m_recursionAvoid = true; onFocusOutDay(); onFocusOutHHMMSS(); onFocusOutMonth(); onFocusOutTZ(); onFocusOutYear(); m_recursionAvoid = false; } void LtsDateTimeEdit::onHmsWheelEvent(bool bForward, int cursorPos) { if (!m_uiLe) return; if (m_uiLe->leLongHHMMSS->text().size() != 6) return; fixupHHMMSS(); int selStart = 0; switch (cursorPos) { case 0: case 1: case 2: if (bForward) onForwardHourClicked(); else onBackHourClicked(); selStart = 0; break; case 3: case 4: if (bForward) onForwardMinuteClicked(); else onBackMinuteClicked(); selStart = 2; break; case 5: case 6: if (bForward) onForwardSecondClicked(); else onBackSecondClicked(); selStart = 4; break; default: return; } m_uiLe->leLongHHMMSS->setCursorPosition(cursorPos); m_uiLe->leLongHHMMSS->setSelection(selStart, 2); emit dateTimeChanged(); } void LtsDateTimeEdit::onDayWheelEvent(bool bForward, int cursorPos) { if (!m_uiLe) return; if (!isDayAcceptable()) return; const int ofs = bForward ? 1 : -1; setMsecsSinceEpoch(m_curTimeMSec + ofs * s_oneDayMSec, true); m_uiLe->leLongDay->setCursorPosition(cursorPos); m_uiLe->leLongDay->setSelection(0, 2); emit dateTimeChanged(); } void LtsDateTimeEdit::onMonthWheelEvent(bool bForward, int cursorPos) { if (!m_uiLe) return; if (!isMonthAcceptable()) return; const int ofs = bForward ? 1 : -1; const QDateTime dt(QDateTime::fromMSecsSinceEpoch(m_curTimeMSec, Qt::UTC).addMonths(ofs)); setMsecsSinceEpoch(dt.toMSecsSinceEpoch(), true); m_uiLe->leLongMonth->setCursorPosition(cursorPos); m_uiLe->leLongMonth->setSelection(0, 3); emit dateTimeChanged(); } void LtsDateTimeEdit::onYearWheelEvent(bool bForward, int cursorPos) { if (!m_uiLe) return; if (!isYearAcceptable()) return; const int ofs = bForward ? 1 : -1; const QDateTime dt(QDateTime::fromMSecsSinceEpoch(m_curTimeMSec, Qt::UTC).addYears(ofs)); setMsecsSinceEpoch(dt.toMSecsSinceEpoch(), true); m_uiLe->leLongYear->setCursorPosition(cursorPos); m_uiLe->leLongYear->setSelection(0, 2); emit dateTimeChanged(); } }