diff options
Diffstat (limited to 'src/widgets/widgets/qdatetimeedit.cpp')
-rw-r--r-- | src/widgets/widgets/qdatetimeedit.cpp | 900 |
1 files changed, 528 insertions, 372 deletions
diff --git a/src/widgets/widgets/qdatetimeedit.cpp b/src/widgets/widgets/qdatetimeedit.cpp index acab768d75..a9b5babde5 100644 --- a/src/widgets/widgets/qdatetimeedit.cpp +++ b/src/widgets/widgets/qdatetimeedit.cpp @@ -1,48 +1,11 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWidgets module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +#include <private/qapplication_p.h> #include <private/qdatetimeedit_p.h> #include <qabstractspinbox.h> #include <qapplication.h> #include <qdatetimeedit.h> -#include <qdesktopwidget.h> -#include <private/qdesktopwidget_p.h> #include <qdebug.h> #include <qevent.h> #include <qlineedit.h> @@ -52,6 +15,7 @@ #include <qlayout.h> #include <qset.h> #include <qstyle.h> +#include <qstylepainter.h> #include <algorithm> @@ -66,6 +30,8 @@ QT_BEGIN_NAMESPACE +using namespace Qt::StringLiterals; + // --- QDateTimeEdit --- /*! @@ -89,12 +55,10 @@ QT_BEGIN_NAMESPACE today's date, and restricted the valid date range to today plus or minus 365 days. We've set the order to month, day, year. - The minimum value for QDateTimeEdit is 14 September 1752. You can - change this by calling setMinimumDate(), taking into account that - the minimum value for QDate is 2 January 4713BC. - - Other useful functions are setMaximumDate(), setMinimumTime() - and setMaximumTime(). + The range of valid values for a QDateTimeEdit is controlled by the properties + \l minimumDateTime, \l maximumDateTime, and their respective date and time + components. By default, any date-time from the start of 100 CE to the end of + 9999 CE is valid. \section1 Using a Pop-up Calendar Widget @@ -104,6 +68,23 @@ QT_BEGIN_NAMESPACE calendar pop-up by calling the setCalendarWidget() function. The existing calendar widget can be retrieved with calendarWidget(). + \section1 Keyboard Tracking + + When \l{QAbstractSpinBox::keyboardTracking}{keyboard tracking} is enabled + (the default), every keystroke of editing a field triggers signals for value + changes. + + When the allowed \l{QDateTimeEdit::setDateTimeRange}{range} is narrower than + some time interval whose end it straddles, keyboard tracking prevents the + user editing the date or time to access the later part of the interval. For + example, for a range from 29.04.2020 to 02.05.2020 and an initial date of + 30.04.2020, the user can change neither the month (May 30th is outside the + range) nor the day (April 2nd is outside the range). + + When keyboard tracking is disabled, changes are only signalled when focus + leaves the text field after edits have modified the content. This allows the + user to edit via an invalid date-time to reach a valid one. + \sa QDateEdit, QTimeEdit, QDate, QTime */ @@ -128,20 +109,26 @@ QT_BEGIN_NAMESPACE This signal is emitted whenever the date or time is changed. The new date and time is passed in \a datetime. + + \sa {Keyboard Tracking} */ /*! - \fn void QDateTimeEdit::timeChanged(const QTime &time) + \fn void QDateTimeEdit::timeChanged(QTime time) This signal is emitted whenever the time is changed. The new time is passed in \a time. + + \sa {Keyboard Tracking} */ /*! - \fn void QDateTimeEdit::dateChanged(const QDate &date) + \fn void QDateTimeEdit::dateChanged(QDate date) This signal is emitted whenever the date is changed. The new date is passed in \a date. + + \sa {Keyboard Tracking} */ @@ -153,7 +140,7 @@ QDateTimeEdit::QDateTimeEdit(QWidget *parent) : QAbstractSpinBox(*new QDateTimeEditPrivate, parent) { Q_D(QDateTimeEdit); - d->init(QDateTime(QDATETIMEEDIT_DATE_INITIAL, QDATETIMEEDIT_TIME_MIN)); + d->init(QDATETIMEEDIT_DATE_INITIAL.startOfDay()); } /*! @@ -165,18 +152,17 @@ QDateTimeEdit::QDateTimeEdit(const QDateTime &datetime, QWidget *parent) : QAbstractSpinBox(*new QDateTimeEditPrivate, parent) { Q_D(QDateTimeEdit); - d->init(datetime.isValid() ? datetime : QDateTime(QDATETIMEEDIT_DATE_INITIAL, - QDATETIMEEDIT_TIME_MIN)); + d->init(datetime.isValid() ? datetime : QDATETIMEEDIT_DATE_INITIAL.startOfDay()); } /*! - \fn QDateTimeEdit::QDateTimeEdit(const QDate &date, QWidget *parent) + \fn QDateTimeEdit::QDateTimeEdit(QDate date, QWidget *parent) Constructs an empty date time editor with a \a parent. The value is set to \a date. */ -QDateTimeEdit::QDateTimeEdit(const QDate &date, QWidget *parent) +QDateTimeEdit::QDateTimeEdit(QDate date, QWidget *parent) : QAbstractSpinBox(*new QDateTimeEditPrivate, parent) { Q_D(QDateTimeEdit); @@ -184,13 +170,13 @@ QDateTimeEdit::QDateTimeEdit(const QDate &date, QWidget *parent) } /*! - \fn QDateTimeEdit::QDateTimeEdit(const QTime &time, QWidget *parent) + \fn QDateTimeEdit::QDateTimeEdit(QTime time, QWidget *parent) Constructs an empty date time editor with a \a parent. The value is set to \a time. */ -QDateTimeEdit::QDateTimeEdit(const QTime &time, QWidget *parent) +QDateTimeEdit::QDateTimeEdit(QTime time, QWidget *parent) : QAbstractSpinBox(*new QDateTimeEditPrivate, parent) { Q_D(QDateTimeEdit); @@ -200,9 +186,10 @@ QDateTimeEdit::QDateTimeEdit(const QTime &time, QWidget *parent) /*! \internal */ - -QDateTimeEdit::QDateTimeEdit(const QVariant &var, QVariant::Type parserType, QWidget *parent) - : QAbstractSpinBox(*new QDateTimeEditPrivate, parent) +QDateTimeEdit::QDateTimeEdit(const QVariant &var, QMetaType::Type parserType, QWidget *parent) + : QAbstractSpinBox(*new QDateTimeEditPrivate(parserType == QMetaType::QDateTime + ? QTimeZone::LocalTime : QTimeZone::UTC), + parent) { Q_D(QDateTimeEdit); d->parserType = parserType; @@ -218,15 +205,21 @@ QDateTimeEdit::~QDateTimeEdit() /*! \property QDateTimeEdit::dateTime - \brief the QDateTime that is set in the QDateTimeEdit + \brief The QDateTime that is set in the QDateTimeEdit. + + When setting this property, the new QDateTime is converted to the time system + of the QDateTimeEdit, which thus remains unchanged. - When setting this property the timespec of the QDateTimeEdit remains the same - and the timespec of the new QDateTime is ignored. + By default, this property is set to the start of 2000 CE. It can only be set + to a valid QDateTime value. If any operation causes this property to have an + invalid date-time as value, it is reset to the value of the \l minimumDateTime + property. - By default, this property contains a date that refers to January 1, - 2000 and a time of 00:00:00 and 0 milliseconds. + If the QDateTimeEdit has no date fields, setting this property sets the + widget's date-range to start and end on the date of the new value of this + property. - \sa date, time + \sa date, time, minimumDateTime, maximumDateTime, timeZone */ QDateTime QDateTimeEdit::dateTime() const @@ -239,17 +232,20 @@ void QDateTimeEdit::setDateTime(const QDateTime &datetime) { Q_D(QDateTimeEdit); if (datetime.isValid()) { + QDateTime when = d->convertTimeZone(datetime); + Q_ASSERT(when.timeRepresentation() == d->timeZone); + d->clearCache(); - const QDate date = datetime.date(); + const QDate date = when.date(); if (!(d->sections & DateSections_Mask)) setDateRange(date, date); - d->setValue(QDateTime(date, datetime.time(), d->spec), EmitIfChanged); + d->setValue(when, EmitIfChanged); } } /*! \property QDateTimeEdit::date - \brief the QDate that is set in the widget + \brief The QDate that is set in the widget. By default, this property contains a date that refers to January 1, 2000. @@ -265,7 +261,7 @@ QDate QDateTimeEdit::date() const return d->value.toDate(); } -void QDateTimeEdit::setDate(const QDate &date) +void QDateTimeEdit::setDate(QDate date) { Q_D(QDateTimeEdit); if (date.isValid()) { @@ -273,20 +269,16 @@ void QDateTimeEdit::setDate(const QDate &date) setDateRange(date, date); d->clearCache(); - QDateTime when(date, d->value.toTime(), d->spec); - // The specified time might not exist on the specified day, - // i.e. the time is in the gap a spring-forward jumps over. - if (!when.isValid()) - when = QDateTime::fromMSecsSinceEpoch(when.toMSecsSinceEpoch(), d->spec); + QDateTime when = d->dateTimeValue(date, d->value.toTime()); Q_ASSERT(when.isValid()); d->setValue(when, EmitIfChanged); - d->updateTimeSpec(); + d->updateTimeZone(); } } /*! \property QDateTimeEdit::time - \brief the QTime that is set in the widget + \brief The QTime that is set in the widget. By default, this property contains a time of 00:00:00 and 0 milliseconds. @@ -302,36 +294,67 @@ QTime QDateTimeEdit::time() const return d->value.toTime(); } -void QDateTimeEdit::setTime(const QTime &time) +void QDateTimeEdit::setTime(QTime time) { Q_D(QDateTimeEdit); if (time.isValid()) { d->clearCache(); - d->setValue(QDateTime(d->value.toDate(), time, d->spec), EmitIfChanged); + d->setValue(d->dateTimeValue(d->value.toDate(), time), EmitIfChanged); } } +/*! + \since 5.14 + Report the calendar system in use by this widget. + + \sa setCalendar() +*/ + +QCalendar QDateTimeEdit::calendar() const +{ + Q_D(const QDateTimeEdit); + return d->calendar; +} /*! - \property QDateTimeEdit::minimumDateTime - \since 4.4 + \since 5.14 + Set \a calendar as the calendar system to be used by this widget. + + The widget can use any supported calendar system. + By default, it uses the Gregorian calendar. + + \sa calendar() +*/ + +void QDateTimeEdit::setCalendar(QCalendar calendar) +{ + Q_D(QDateTimeEdit); + // Set invalid date time to prevent runtime crashes on calendar change + QDateTime previousValue = d->value.toDateTime(); + setDateTime(QDateTime()); + d->setCalendar(calendar); + setDateTime(previousValue); +} - \brief the minimum datetime of the date time edit +/*! + \since 4.4 + \property QDateTimeEdit::minimumDateTime - When setting this property the \l maximumDateTime() is adjusted if - necessary to ensure that the range remains valid. If the datetime is - not a valid QDateTime object, this function does nothing. + \brief The minimum datetime of the date time edit. - The default minimumDateTime can be restored with - clearMinimumDateTime() + Changing this property implicitly updates the \l minimumDate and \l + minimumTime properties to the date and time parts of this property, + respectively. When setting this property, the \l maximumDateTime is adjusted, + if necessary, to ensure that the range remains valid. Otherwise, changing this + property preserves the \l maximumDateTime property. - By default, this property contains a date that refers to September 14, - 1752 and a time of 00:00:00 and 0 milliseconds. + This property can only be set to a valid QDateTime value. The earliest + date-time that setMinimumDateTime() accepts is the start of 100 CE. The + property's default is the start of September 14, 1752 CE. This default can be + restored with clearMinimumDateTime(). - \sa maximumDateTime(), minimumTime(), maximumTime(), minimumDate(), - maximumDate(), setDateTimeRange(), setDateRange(), setTimeRange(), - clearMaximumDateTime(), clearMinimumDate(), - clearMaximumDate(), clearMinimumTime(), clearMaximumTime() + \sa maximumDateTime, minimumTime, minimumDate, setDateTimeRange(), + QDateTime::isValid(), {Keyboard Tracking} */ QDateTime QDateTimeEdit::minimumDateTime() const @@ -342,39 +365,38 @@ QDateTime QDateTimeEdit::minimumDateTime() const void QDateTimeEdit::clearMinimumDateTime() { - setMinimumDateTime(QDateTime(QDATETIMEEDIT_COMPAT_DATE_MIN, QDATETIMEEDIT_TIME_MIN)); + setMinimumDateTime(QDATETIMEEDIT_COMPAT_DATE_MIN.startOfDay()); } void QDateTimeEdit::setMinimumDateTime(const QDateTime &dt) { Q_D(QDateTimeEdit); if (dt.isValid() && dt.date() >= QDATETIMEEDIT_DATE_MIN) { - const QDateTime m = dt.toTimeSpec(d->spec); + const QDateTime m = dt.toTimeZone(d->timeZone); const QDateTime max = d->maximum.toDateTime(); d->setRange(m, (max > m ? max : m)); } } /*! - \property QDateTimeEdit::maximumDateTime \since 4.4 + \property QDateTimeEdit::maximumDateTime - \brief the maximum datetime of the date time edit + \brief The maximum datetime of the date time edit. - When setting this property the \l minimumDateTime() is adjusted if - necessary to ensure that the range remains valid. If the datetime is - not a valid QDateTime object, this function does nothing. + Changing this property implicitly updates the \l maximumDate and \l + maximumTime properties to the date and time parts of this property, + respectively. When setting this property, the \l minimumDateTime is adjusted, + if necessary, to ensure that the range remains valid. Otherwise, changing this + property preserves the \l minimumDateTime property. - The default maximumDateTime can be restored with + This property can only be set to a valid QDateTime value. The latest + date-time that setMaximumDateTime() accepts is the end of 9999 CE. This is the + default for this property. This default can be restored with clearMaximumDateTime(). - By default, this property contains a date that refers to 31 December, - 9999 and a time of 23:59:59 and 999 milliseconds. - - \sa minimumDateTime(), minimumTime(), maximumTime(), minimumDate(), - maximumDate(), setDateTimeRange(), setDateRange(), setTimeRange(), - clearMinimumDateTime(), clearMinimumDate(), - clearMaximumDate(), clearMinimumTime(), clearMaximumTime() + \sa minimumDateTime, maximumTime, maximumDate(), setDateTimeRange(), + QDateTime::isValid(), {Keyboard Tracking} */ QDateTime QDateTimeEdit::maximumDateTime() const @@ -385,24 +407,25 @@ QDateTime QDateTimeEdit::maximumDateTime() const void QDateTimeEdit::clearMaximumDateTime() { - setMaximumDateTime(QDATETIMEEDIT_DATETIME_MAX); + setMaximumDateTime(QDATETIMEEDIT_DATE_MAX.endOfDay()); } void QDateTimeEdit::setMaximumDateTime(const QDateTime &dt) { Q_D(QDateTimeEdit); if (dt.isValid() && dt.date() <= QDATETIMEEDIT_DATE_MAX) { - const QDateTime m = dt.toTimeSpec(d->spec); + const QDateTime m = dt.toTimeZone(d->timeZone); const QDateTime min = d->minimum.toDateTime(); d->setRange((min < m ? min : m), m); } } - /*! - Convenience function to set minimum and maximum date time with one - function call. \since 4.4 + \brief Set the range of allowed date-times for the date time edit. + + This convenience function sets the \l minimumDateTime and \l maximumDateTime + properties. \snippet code/src_gui_widgets_qdatetimeedit.cpp 1 @@ -410,38 +433,46 @@ void QDateTimeEdit::setMaximumDateTime(const QDateTime &dt) \snippet code/src_gui_widgets_qdatetimeedit.cpp 2 - If either \a min or \a max are not valid, this function does - nothing. + If either \a min or \a max is invalid, this function does nothing. If \a max + is less than \a min, \a min is used also as \a max. + + If the range is narrower then a time interval whose end it spans, for example + a week that spans the end of a month, users can only edit the date-time to one + in the later part of the range if keyboard-tracking is disabled. - \sa setMinimumDate(), maximumDate(), setMaximumDate(), - clearMinimumDate(), setMinimumTime(), maximumTime(), - setMaximumTime(), clearMinimumTime(), QDateTime::isValid() + \sa minimumDateTime, maximumDateTime, setDateRange(), setTimeRange(), + QDateTime::isValid(), {Keyboard Tracking} */ void QDateTimeEdit::setDateTimeRange(const QDateTime &min, const QDateTime &max) { Q_D(QDateTimeEdit); - const QDateTime minimum = min.toTimeSpec(d->spec); - QDateTime maximum = max.toTimeSpec(d->spec); - if (min > max) - maximum = minimum; + // FIXME: does none of the range checks applied to setMin/setMax methods ! + const QDateTime minimum = min.toTimeZone(d->timeZone); + const QDateTime maximum = (min > max ? minimum : max.toTimeZone(d->timeZone)); d->setRange(minimum, maximum); } /*! \property QDateTimeEdit::minimumDate - \brief the minimum date of the date time edit + \brief The minimum date of the date time edit. - When setting this property the \l maximumDate is adjusted if - necessary, to ensure that the range remains valid. If the date is - not a valid QDate object, this function does nothing. + Changing this property updates the date of the \l minimumDateTime property + while preserving the \l minimumTime property. When setting this property, + the \l maximumDate is adjusted, if necessary, to ensure that the range remains + valid. When this happens, the \l maximumTime property is also adjusted if it + is less than the \l minimumTime property. Otherwise, changes to this property + preserve the \l maximumDateTime property. - By default, this property contains a date that refers to September 14, 1752. - The minimum date must be at least the first day in year 100, otherwise - setMinimumDate() has no effect. + This property can only be set to a valid QDate object describing a date on + which the current \l minimumTime property makes a valid QDateTime object. The + earliest date that setMinimumDate() accepts is the start of 100 CE. The + default for this property is September 14, 1752 CE. This default can be + restored with clearMinimumDateTime(). - \sa minimumTime(), maximumTime(), setDateRange() + \sa maximumDate, minimumTime, minimumDateTime, setDateRange(), + QDate::isValid(), {Keyboard Tracking} */ QDate QDateTimeEdit::minimumDate() const @@ -450,12 +481,11 @@ QDate QDateTimeEdit::minimumDate() const return d->minimum.toDate(); } -void QDateTimeEdit::setMinimumDate(const QDate &min) +void QDateTimeEdit::setMinimumDate(QDate min) { Q_D(QDateTimeEdit); - if (min.isValid() && min >= QDATETIMEEDIT_DATE_MIN) { - setMinimumDateTime(QDateTime(min, d->minimum.toTime(), d->spec)); - } + if (min.isValid() && min >= QDATETIMEEDIT_DATE_MIN) + setMinimumDateTime(d->dateTimeValue(min, d->minimum.toTime())); } void QDateTimeEdit::clearMinimumDate() @@ -466,15 +496,23 @@ void QDateTimeEdit::clearMinimumDate() /*! \property QDateTimeEdit::maximumDate - \brief the maximum date of the date time edit + \brief The maximum date of the date time edit. - When setting this property the \l minimumDate is adjusted if - necessary to ensure that the range remains valid. If the date is - not a valid QDate object, this function does nothing. + Changing this property updates the date of the \l maximumDateTime property + while preserving the \l maximumTime property. When setting this property, the + \l minimumDate is adjusted, if necessary, to ensure that the range remains + valid. When this happens, the \l minimumTime property is also adjusted if it + is greater than the \l maximumTime property. Otherwise, changes to this + property preserve the \l minimumDateTime property. - By default, this property contains a date that refers to December 31, 9999. + This property can only be set to a valid QDate object describing a date on + which the current \l maximumTime property makes a valid QDateTime object. The + latest date that setMaximumDate() accepts is the end of 9999 CE. This is the + default for this property. This default can be restored with + clearMaximumDateTime(). - \sa minimumDate, minimumTime, maximumTime, setDateRange() + \sa minimumDate, maximumTime, maximumDateTime, setDateRange(), + QDate::isValid(), {Keyboard Tracking} */ QDate QDateTimeEdit::maximumDate() const @@ -483,12 +521,11 @@ QDate QDateTimeEdit::maximumDate() const return d->maximum.toDate(); } -void QDateTimeEdit::setMaximumDate(const QDate &max) +void QDateTimeEdit::setMaximumDate(QDate max) { Q_D(QDateTimeEdit); - if (max.isValid()) { - setMaximumDateTime(QDateTime(max, d->maximum.toTime(), d->spec)); - } + if (max.isValid()) + setMaximumDateTime(d->dateTimeValue(max, d->maximum.toTime())); } void QDateTimeEdit::clearMaximumDate() @@ -499,15 +536,21 @@ void QDateTimeEdit::clearMaximumDate() /*! \property QDateTimeEdit::minimumTime - \brief the minimum time of the date time edit + \brief The minimum time of the date time edit. - When setting this property the \l maximumTime is adjusted if - necessary, to ensure that the range remains valid. If the time is - not a valid QTime object, this function does nothing. + Changing this property updates the time of the \l minimumDateTime property + while preserving the \l minimumDate and \l maximumDate properties. If those + date properties coincide, when setting this property, the \l maximumTime + property is adjusted, if necessary, to ensure that the range remains + valid. Otherwise, changing this property preserves the \l maximumDateTime + property. - By default, this property contains a time of 00:00:00 and 0 milliseconds. + This property can be set to any valid QTime value. By default, this property + contains a time of 00:00:00 and 0 milliseconds. This default can be restored + with clearMinimumTime(). - \sa maximumTime, minimumDate, maximumDate, setTimeRange() + \sa maximumTime, minimumDate, minimumDateTime, setTimeRange(), + QTime::isValid(), {Keyboard Tracking} */ QTime QDateTimeEdit::minimumTime() const @@ -516,13 +559,11 @@ QTime QDateTimeEdit::minimumTime() const return d->minimum.toTime(); } -void QDateTimeEdit::setMinimumTime(const QTime &min) +void QDateTimeEdit::setMinimumTime(QTime min) { Q_D(QDateTimeEdit); - if (min.isValid()) { - const QDateTime m(d->minimum.toDate(), min, d->spec); - setMinimumDateTime(m); - } + if (min.isValid()) + setMinimumDateTime(d->dateTimeValue(d->minimum.toDate(), min)); } void QDateTimeEdit::clearMinimumTime() @@ -533,15 +574,21 @@ void QDateTimeEdit::clearMinimumTime() /*! \property QDateTimeEdit::maximumTime - \brief the maximum time of the date time edit + \brief The maximum time of the date time edit. - When setting this property, the \l minimumTime is adjusted if - necessary to ensure that the range remains valid. If the time is - not a valid QTime object, this function does nothing. + Changing this property updates the time of the \l maximumDateTime property + while preserving the \l minimumDate and \l maximumDate properties. If those + date properties coincide, when setting this property, the \l minimumTime + property is adjusted, if necessary, to ensure that the range remains + valid. Otherwise, changing this property preserves the \l minimumDateTime + property. - By default, this property contains a time of 23:59:59 and 999 milliseconds. + This property can be set to any valid QTime value. By default, this property + contains a time of 23:59:59 and 999 milliseconds. This default can be restored + with clearMaximumTime(). - \sa minimumTime, minimumDate, maximumDate, setTimeRange() + \sa minimumTime, maximumDate, maximumDateTime, setTimeRange(), + QTime::isValid(), {Keyboard Tracking} */ QTime QDateTimeEdit::maximumTime() const { @@ -549,13 +596,11 @@ QTime QDateTimeEdit::maximumTime() const return d->maximum.toTime(); } -void QDateTimeEdit::setMaximumTime(const QTime &max) +void QDateTimeEdit::setMaximumTime(QTime max) { Q_D(QDateTimeEdit); - if (max.isValid()) { - const QDateTime m(d->maximum.toDate(), max, d->spec); - setMaximumDateTime(m); - } + if (max.isValid()) + setMaximumDateTime(d->dateTimeValue(d->maximum.toDate(), max)); } void QDateTimeEdit::clearMaximumTime() @@ -564,8 +609,10 @@ void QDateTimeEdit::clearMaximumTime() } /*! - Convenience function to set minimum and maximum date with one - function call. + \brief Set the range of allowed dates for the date time edit. + + This convenience function sets the \l minimumDate and \l maximumDate + properties. \snippet code/src_gui_widgets_qdatetimeedit.cpp 3 @@ -573,26 +620,40 @@ void QDateTimeEdit::clearMaximumTime() \snippet code/src_gui_widgets_qdatetimeedit.cpp 4 - If either \a min or \a max are not valid, this function does - nothing. + If either \a min or \a max is invalid, this function does nothing. This + function preserves the \l minimumTime property. If \a max is less than \a min, + the new maximumDateTime property shall be the new minimumDateTime property. If + \a max is equal to \a min and the \l maximumTime property was less then the \l + minimumTime property, the \l maximumTime property is set to the \l minimumTime + property. Otherwise, this preserves the \l maximumTime property. - \sa setMinimumDate(), maximumDate(), setMaximumDate(), - clearMinimumDate(), setMinimumTime(), maximumTime(), - setMaximumTime(), clearMinimumTime(), QDate::isValid() + If the range is narrower then a time interval whose end it spans, for example + a week that spans the end of a month, users can only edit the date to one in + the later part of the range if keyboard-tracking is disabled. + + \sa minimumDate, maximumDate, setDateTimeRange(), QDate::isValid(), {Keyboard Tracking} */ -void QDateTimeEdit::setDateRange(const QDate &min, const QDate &max) +void QDateTimeEdit::setDateRange(QDate min, QDate max) { Q_D(QDateTimeEdit); if (min.isValid() && max.isValid()) { - setDateTimeRange(QDateTime(min, d->minimum.toTime(), d->spec), - QDateTime(max, d->maximum.toTime(), d->spec)); + setDateTimeRange(d->dateTimeValue(min, d->minimum.toTime()), + d->dateTimeValue(max, d->maximum.toTime())); } } /*! - Convenience function to set minimum and maximum time with one - function call. + \brief Set the range of allowed times for the date time edit. + + This convenience function sets the \l minimumTime and \l maximumTime + properties. + + Note that these only constrain the date time edit's value on, + respectively, the \l minimumDate and \l maximumDate. When these date + properties do not coincide, times after \a max are allowed on dates + before \l maximumDate and times before \a min are allowed on dates + after \l minimumDate. \snippet code/src_gui_widgets_qdatetimeedit.cpp 5 @@ -600,30 +661,35 @@ void QDateTimeEdit::setDateRange(const QDate &min, const QDate &max) \snippet code/src_gui_widgets_qdatetimeedit.cpp 6 - If either \a min or \a max are not valid, this function does - nothing. + If either \a min or \a max is invalid, this function does nothing. This + function preserves the \l minimumDate and \l maximumDate properties. If those + properties coincide and \a max is less than \a min, \a min is used as \a max. + + If the range is narrower then a time interval whose end it spans, for example + the interval from ten to an hour to ten past the same hour, users can only + edit the time to one in the later part of the range if keyboard-tracking is + disabled. - \sa setMinimumDate(), maximumDate(), setMaximumDate(), - clearMinimumDate(), setMinimumTime(), maximumTime(), - setMaximumTime(), clearMinimumTime(), QTime::isValid() + \sa minimumTime, maximumTime, setDateTimeRange(), QTime::isValid(), {Keyboard Tracking} */ -void QDateTimeEdit::setTimeRange(const QTime &min, const QTime &max) +void QDateTimeEdit::setTimeRange(QTime min, QTime max) { Q_D(QDateTimeEdit); if (min.isValid() && max.isValid()) { - setDateTimeRange(QDateTime(d->minimum.toDate(), min, d->spec), - QDateTime(d->maximum.toDate(), max, d->spec)); + setDateTimeRange(d->dateTimeValue(d->minimum.toDate(), min), + d->dateTimeValue(d->maximum.toDate(), max)); } } /*! \property QDateTimeEdit::displayedSections - \brief the currently displayed fields of the date time edit + \brief The currently displayed fields of the date time edit. Returns a bit set of the displayed sections for this format. - \a setDisplayFormat(), displayFormat() + + \sa setDisplayFormat(), displayFormat() */ QDateTimeEdit::Sections QDateTimeEdit::displayedSections() const @@ -635,18 +701,17 @@ QDateTimeEdit::Sections QDateTimeEdit::displayedSections() const /*! \property QDateTimeEdit::currentSection - \brief the current section of the spinbox - \a setCurrentSection() + \brief The current section of the spinbox. */ QDateTimeEdit::Section QDateTimeEdit::currentSection() const { Q_D(const QDateTimeEdit); #ifdef QT_KEYPAD_NAVIGATION - if (QApplication::keypadNavigationEnabled() && d->focusOnButton) + if (QApplicationPrivate::keypadNavigationEnabled() && d->focusOnButton) return NoSection; #endif - return d->convertToPublic(d->sectionType(d->currentSectionIndex)); + return QDateTimeEditPrivate::convertToPublic(d->sectionType(d->currentSectionIndex)); } void QDateTimeEdit::setCurrentSection(Section section) @@ -660,7 +725,7 @@ void QDateTimeEdit::setCurrentSection(Section section) int index = d->currentSectionIndex + 1; for (int i=0; i<2; ++i) { while (index < size) { - if (d->convertToPublic(d->sectionType(index)) == section) { + if (QDateTimeEditPrivate::convertToPublic(d->sectionType(index)) == section) { d->edit->setCursorPosition(d->sectionPos(index)); QDTEDEBUG << d->sectionPos(index); return; @@ -686,7 +751,7 @@ QDateTimeEdit::Section QDateTimeEdit::sectionAt(int index) const Q_D(const QDateTimeEdit); if (index < 0 || index >= d->sectionNodes.size()) return NoSection; - return d->convertToPublic(d->sectionType(index)); + return QDateTimeEditPrivate::convertToPublic(d->sectionType(index)); } /*! @@ -694,7 +759,7 @@ QDateTimeEdit::Section QDateTimeEdit::sectionAt(int index) const \property QDateTimeEdit::sectionCount - \brief the number of sections displayed. + \brief The number of sections displayed. If the format is 'yyyy/yy/yyyy', sectionCount returns 3 */ @@ -710,14 +775,13 @@ int QDateTimeEdit::sectionCount() const \property QDateTimeEdit::currentSectionIndex - \brief the current section index of the spinbox + \brief The current section index of the spinbox. If the format is 'yyyy/MM/dd', the displayText is '2001/05/21', and the cursorPosition is 5, currentSectionIndex returns 1. If the cursorPosition is 3, currentSectionIndex is 0, and so on. - \a setCurrentSection() - \sa currentSection() + \sa setCurrentSection(), currentSection() */ int QDateTimeEdit::currentSectionIndex() const @@ -748,7 +812,7 @@ QCalendarWidget *QDateTimeEdit::calendarWidget() const { Q_D(const QDateTimeEdit); if (!d->calendarPopup || !(d->sections & QDateTimeParser::DateSectionMask)) - return 0; + return nullptr; if (!d->monthCalendar) { const_cast<QDateTimeEditPrivate*>(d)->initCalendarPopup(); } @@ -834,7 +898,7 @@ QString QDateTimeEdit::sectionText(Section section) const /*! \property QDateTimeEdit::displayFormat - \brief the format used to display the time/date of the date time edit + \brief The format used to display the time/date of the date time edit. This format is described in QDateTime::toString() and QDateTime::fromString() @@ -849,7 +913,7 @@ QString QDateTimeEdit::sectionText(Section section) const Note that if you specify a two digit year, it will be interpreted to be in the century in which the date time edit was initialized. - The default century is the 21 (2000-2099). + The default century is the 21st (2000-2099). If you specify an invalid format the format will not be set. @@ -880,7 +944,7 @@ void QDateTimeEdit::setDisplayFormat(const QString &format) } d->formatExplicitlySet = true; - d->sections = d->convertSections(d->display); + d->sections = QDateTimeEditPrivate::convertSections(d->display); d->clearCache(); d->currentSectionIndex = qMin(d->currentSectionIndex, d->sectionNodes.size() - 1); @@ -897,16 +961,16 @@ void QDateTimeEdit::setDisplayFormat(const QString &format) } } else if (dateShown && !timeShown) { setTimeRange(QDATETIMEEDIT_TIME_MIN, QDATETIMEEDIT_TIME_MAX); - d->value = QDateTime(d->value.toDate(), QTime(), d->spec); + d->value = d->value.toDate().startOfDay(d->timeZone); } d->updateEdit(); - d->_q_editorCursorPositionChanged(-1, 0); + d->editorCursorPositionChanged(-1, 0); } } /*! \property QDateTimeEdit::calendarPopup - \brief the current calendar pop-up show mode. + \brief The current calendar pop-up show mode. \since 4.2 The calendar pop-up will be shown upon clicking the arrow button. @@ -936,24 +1000,70 @@ void QDateTimeEdit::setCalendarPopup(bool enable) update(); } +#if QT_DEPRECATED_SINCE(6, 10) /*! \property QDateTimeEdit::timeSpec - \brief the current timespec used by the date time edit. \since 4.4 + \deprecated[6.10] Use QDateTimeEdit::timeZone instead. + \brief The current timespec used by the date time edit. + + Since Qt 6.7 this is an indirect accessor for the timeZone property. + + \sa QDateTimeEdit::timeZone */ Qt::TimeSpec QDateTimeEdit::timeSpec() const { Q_D(const QDateTimeEdit); - return d->spec; + return d->timeZone.timeSpec(); } void QDateTimeEdit::setTimeSpec(Qt::TimeSpec spec) { Q_D(QDateTimeEdit); - if (spec != d->spec) { - d->spec = spec; - d->updateTimeSpec(); + if (spec != d->timeZone.timeSpec()) { + switch (spec) { + case Qt::UTC: + setTimeZone(QTimeZone::UTC); + break; + case Qt::LocalTime: + setTimeZone(QTimeZone::LocalTime); + break; + default: + qWarning() << "Ignoring attempt to set time-spec" << spec + << "which needs ancillary data: see setTimeZone()"; + return; + } + } +} +#endif // 6.10 deprecation + +// TODO: enable user input to control timeZone, when the format includes it. +/*! + \property QDateTimeEdit::timeZone + \since 6.7 + \brief The current timezone used by the datetime editing widget + + If the datetime format in use includes a timezone indicator - that is, a + \c{t}, \c{tt}, \c{ttt} or \c{tttt} format specifier - the user's input is + re-expressed in this timezone whenever it is parsed, overriding any timezone + the user may have specified. + + \sa QDateTimeEdit::displayFormat +*/ + +QTimeZone QDateTimeEdit::timeZone() const +{ + Q_D(const QDateTimeEdit); + return d->timeZone; +} + +void QDateTimeEdit::setTimeZone(const QTimeZone &zone) +{ + Q_D(QDateTimeEdit); + if (zone != d->timeZone) { + d->timeZone = zone; + d->updateTimeZone(); } } @@ -971,9 +1081,9 @@ QSize QDateTimeEdit::sizeHint() const int h = d->edit->sizeHint().height(); int w = 0; QString s; - s = d->textFromValue(d->minimum) + QLatin1Char(' '); + s = d->textFromValue(d->minimum) + u' '; w = qMax<int>(w, fm.horizontalAdvance(s)); - s = d->textFromValue(d->maximum) + QLatin1Char(' '); + s = d->textFromValue(d->maximum) + u' '; w = qMax<int>(w, fm.horizontalAdvance(s)); if (d->specialValueText.size()) { s = d->specialValueText; @@ -992,8 +1102,7 @@ QSize QDateTimeEdit::sizeHint() const { QStyleOptionSpinBox opt; initStyleOption(&opt); - d->cachedSizeHint = style()->sizeFromContents(QStyle::CT_SpinBox, &opt, hint, this) - .expandedTo(QApplication::globalStrut()); + d->cachedSizeHint = style()->sizeFromContents(QStyle::CT_SpinBox, &opt, hint, this); } d->cachedMinimumSizeHint = d->cachedSizeHint; @@ -1056,7 +1165,7 @@ void QDateTimeEdit::keyPressEvent(QKeyEvent *event) switch (event->key()) { #ifdef QT_KEYPAD_NAVIGATION case Qt::Key_NumberSign: //shortcut to popup calendar - if (QApplication::keypadNavigationEnabled() && d->calendarPopupEnabled()) { + if (QApplicationPrivate::keypadNavigationEnabled() && d->calendarPopupEnabled()) { d->initCalendarPopup(); d->positionCalendarPopup(); d->monthCalendar->show(); @@ -1064,7 +1173,7 @@ void QDateTimeEdit::keyPressEvent(QKeyEvent *event) } break; case Qt::Key_Select: - if (QApplication::keypadNavigationEnabled()) { + if (QApplicationPrivate::keypadNavigationEnabled()) { if (hasEditFocus()) { if (d->focusOnButton) { d->initCalendarPopup(); @@ -1096,7 +1205,7 @@ void QDateTimeEdit::keyPressEvent(QKeyEvent *event) return; default: #ifdef QT_KEYPAD_NAVIGATION - if (QApplication::keypadNavigationEnabled() && !hasEditFocus() + if (QApplicationPrivate::keypadNavigationEnabled() && !hasEditFocus() && !event->text().isEmpty() && event->text().at(0).isLetterOrNumber()) { setEditFocus(true); @@ -1118,23 +1227,13 @@ void QDateTimeEdit::keyPressEvent(QKeyEvent *event) if (event->key() == Qt::Key_Left || event->key() == Qt::Key_Right) { if ( #ifdef QT_KEYPAD_NAVIGATION - QApplication::keypadNavigationEnabled() && !hasEditFocus() - || !QApplication::keypadNavigationEnabled() && + QApplicationPrivate::keypadNavigationEnabled() && !hasEditFocus() + || !QApplicationPrivate::keypadNavigationEnabled() && #endif !(event->modifiers() & Qt::ControlModifier)) { select = false; break; } -#if 0 // Used to be included in Qt4 for Q_WS_MAC - else -#ifdef QT_KEYPAD_NAVIGATION - if (!QApplication::keypadNavigationEnabled()) -#endif - { - select = (event->modifiers() & Qt::ShiftModifier); - break; - } -#endif } Q_FALLTHROUGH(); case Qt::Key_Backtab: @@ -1148,7 +1247,7 @@ void QDateTimeEdit::keyPressEvent(QKeyEvent *event) && (event->key() != Qt::Key_Tab || !(event->modifiers() & Qt::ShiftModifier)); #ifdef QT_KEYPAD_NAVIGATION int newSection = d->nextPrevSection(d->currentSectionIndex, forward); - if (QApplication::keypadNavigationEnabled()) { + if (QApplicationPrivate::keypadNavigationEnabled()) { if (d->focusOnButton) { newSection = forward ? 0 : d->sectionNodes.size() - 1; d->focusOnButton = false; @@ -1215,9 +1314,9 @@ void QDateTimeEdit::focusInEvent(QFocusEvent *event) { Q_D(QDateTimeEdit); QAbstractSpinBox::focusInEvent(event); - QString *frm = 0; const int oldPos = d->edit->cursorPosition(); if (!d->formatExplicitlySet) { + QString *frm = nullptr; if (d->displayFormat == d->defaultTimeFormat) { frm = &d->defaultTimeFormat; } else if (d->displayFormat == d->defaultDateFormat) { @@ -1248,6 +1347,7 @@ void QDateTimeEdit::focusInEvent(QFocusEvent *event) case Qt::ActiveWindowFocusReason: if (oldHasHadFocus) return; + break; case Qt::ShortcutFocusReason: case Qt::TabFocusReason: default: @@ -1291,7 +1391,7 @@ void QDateTimeEdit::stepBy(int steps) Q_D(QDateTimeEdit); #ifdef QT_KEYPAD_NAVIGATION // with keypad navigation and not editFocus, left right change the date/time by a fixed amount. - if (QApplication::keypadNavigationEnabled() && !hasEditFocus()) { + if (QApplicationPrivate::keypadNavigationEnabled() && !hasEditFocus()) { // if date based, shift by day. else shift by 15min if (d->sections & DateSections_Mask) { setDateTime(dateTime().addDays(steps)); @@ -1340,7 +1440,7 @@ void QDateTimeEdit::stepBy(int steps) d->updateCache(d->value, d->displayText()); d->setSelected(d->currentSectionIndex); - d->updateTimeSpec(); + d->updateTimeZone(); } /*! @@ -1354,7 +1454,7 @@ void QDateTimeEdit::stepBy(int steps) QString QDateTimeEdit::textFromDateTime(const QDateTime &dateTime) const { Q_D(const QDateTimeEdit); - return locale().toString(dateTime, d->displayFormat); + return locale().toString(dateTime, d->displayFormat, d->calendar); } @@ -1398,7 +1498,12 @@ void QDateTimeEdit::fixup(QString &input) const QValidator::State state; int copy = d->edit->cursorPosition(); - d->validateAndInterpret(input, copy, state, true); + QDateTime value = d->validateAndInterpret(input, copy, state, true); + // CorrectToPreviousValue correction is handled by QAbstractSpinBox. + // The value might not match the input if the input represents a date-time + // skipped over by its time representation, such as a spring-forward. + if (d->correctionMode == QAbstractSpinBox::CorrectToNearestValue) + input = textFromDateTime(value); } @@ -1410,19 +1515,19 @@ QDateTimeEdit::StepEnabled QDateTimeEdit::stepEnabled() const { Q_D(const QDateTimeEdit); if (d->readOnly) - return StepEnabled(0); + return {}; if (d->specialValue()) { - return (d->minimum == d->maximum ? StepEnabled(0) : StepEnabled(StepUpEnabled)); + return (d->minimum == d->maximum ? StepEnabled{} : StepEnabled(StepUpEnabled)); } - QAbstractSpinBox::StepEnabled ret = 0; + QAbstractSpinBox::StepEnabled ret = { }; #ifdef QT_KEYPAD_NAVIGATION - if (QApplication::keypadNavigationEnabled() && !hasEditFocus()) { + if (QApplicationPrivate::keypadNavigationEnabled() && !hasEditFocus()) { if (d->wrapping) return StepEnabled(StepUpEnabled | StepDownEnabled); // 3 cases. date, time, datetime. each case look - // at just the relavant component. + // at just the relevant component. QVariant max, min, val; if (!(d->sections & DateSections_Mask)) { // time only, no date @@ -1450,7 +1555,7 @@ QDateTimeEdit::StepEnabled QDateTimeEdit::stepEnabled() const switch (d->sectionType(d->currentSectionIndex)) { case QDateTimeParser::NoSection: case QDateTimeParser::FirstSection: - case QDateTimeParser::LastSection: return 0; + case QDateTimeParser::LastSection: return { }; default: break; } if (d->wrapping) @@ -1480,7 +1585,7 @@ void QDateTimeEdit::mousePressEvent(QMouseEvent *event) QAbstractSpinBox::mousePressEvent(event); return; } - d->updateHoverControl(event->pos()); + d->updateHoverControl(event->position().toPoint()); if (d->hoverControl == QStyle::SC_ComboBoxArrow) { event->accept(); if (d->readOnly) { @@ -1528,7 +1633,7 @@ void QDateTimeEdit::mousePressEvent(QMouseEvent *event) QTimeEdit::QTimeEdit(QWidget *parent) - : QDateTimeEdit(QDATETIMEEDIT_TIME_MIN, QVariant::Time, parent) + : QDateTimeEdit(QDATETIMEEDIT_TIME_MIN, QMetaType::QTime, parent) { connect(this, &QTimeEdit::timeChanged, this, &QTimeEdit::userTimeChanged); } @@ -1538,8 +1643,8 @@ QTimeEdit::QTimeEdit(QWidget *parent) to \a time. */ -QTimeEdit::QTimeEdit(const QTime &time, QWidget *parent) - : QDateTimeEdit(time, QVariant::Time, parent) +QTimeEdit::QTimeEdit(QTime time, QWidget *parent) + : QDateTimeEdit(time.isValid() ? time : QDATETIMEEDIT_TIME_MIN, QMetaType::QTime, parent) { connect(this, &QTimeEdit::timeChanged, this, &QTimeEdit::userTimeChanged); } @@ -1558,7 +1663,7 @@ QTimeEdit::~QTimeEdit() */ /*! - \fn void QTimeEdit::userTimeChanged(const QTime &time) + \fn void QTimeEdit::userTimeChanged(QTime time) This signal only exists to fully implement the time Q_PROPERTY on the class. Normally timeChanged should be used instead. @@ -1598,7 +1703,7 @@ QTimeEdit::~QTimeEdit() */ QDateEdit::QDateEdit(QWidget *parent) - : QDateTimeEdit(QDATETIMEEDIT_DATE_INITIAL, QVariant::Date, parent) + : QDateTimeEdit(QDATETIMEEDIT_DATE_INITIAL, QMetaType::QDate, parent) { connect(this, &QDateEdit::dateChanged, this, &QDateEdit::userDateChanged); } @@ -1608,8 +1713,8 @@ QDateEdit::QDateEdit(QWidget *parent) to \a date. */ -QDateEdit::QDateEdit(const QDate &date, QWidget *parent) - : QDateTimeEdit(date, QVariant::Date, parent) +QDateEdit::QDateEdit(QDate date, QWidget *parent) + : QDateTimeEdit(date.isValid() ? date : QDATETIMEEDIT_DATE_INITIAL, QMetaType::QDate, parent) { connect(this, &QDateEdit::dateChanged, this, &QDateEdit::userDateChanged); } @@ -1628,7 +1733,7 @@ QDateEdit::~QDateEdit() */ /*! - \fn void QDateEdit::userDateChanged(const QDate &date) + \fn void QDateEdit::userDateChanged(QDate date) This signal only exists to fully implement the date Q_PROPERTY on the class. Normally dateChanged should be used instead. @@ -1645,48 +1750,42 @@ QDateEdit::~QDateEdit() */ -QDateTimeEditPrivate::QDateTimeEditPrivate() - : QDateTimeParser(QVariant::DateTime, QDateTimeParser::DateTimeEdit) +QDateTimeEditPrivate::QDateTimeEditPrivate(const QTimeZone &zone) + : QDateTimeParser(QMetaType::QDateTime, QDateTimeParser::DateTimeEdit, QCalendar()), + timeZone(zone) { - hasHadFocus = false; - formatExplicitlySet = false; - cacheGuard = false; fixday = true; - type = QVariant::DateTime; - sections = 0; - cachedDay = -1; + type = QMetaType::QDateTime; currentSectionIndex = FirstSectionIndex; first.pos = 0; - sections = 0; - calendarPopup = false; - minimum = QDATETIMEEDIT_COMPAT_DATETIME_MIN; - maximum = QDATETIMEEDIT_DATETIME_MAX; - arrowState = QStyle::State_None; - monthCalendar = 0; + minimum = QDATETIMEEDIT_COMPAT_DATE_MIN.startOfDay(timeZone); + maximum = QDATETIMEEDIT_DATE_MAX.endOfDay(timeZone); readLocaleSettings(); +} -#ifdef QT_KEYPAD_NAVIGATION - focusOnButton = false; -#endif +QDateTime QDateTimeEditPrivate::convertTimeZone(const QDateTime &datetime) +{ + return datetime.toTimeZone(timeZone); } -QDateTimeEditPrivate::~QDateTimeEditPrivate() +QDateTime QDateTimeEditPrivate::dateTimeValue(QDate date, QTime time) const { + return QDateTime(date, time, timeZone); } -void QDateTimeEditPrivate::updateTimeSpec() +void QDateTimeEditPrivate::updateTimeZone() { - minimum = minimum.toDateTime().toTimeSpec(spec); - maximum = maximum.toDateTime().toTimeSpec(spec); - value = value.toDateTime().toTimeSpec(spec); + minimum = minimum.toDateTime().toTimeZone(timeZone); + maximum = maximum.toDateTime().toTimeZone(timeZone); + value = value.toDateTime().toTimeZone(timeZone); // time zone changes can lead to 00:00:00 becomes 01:00:00 and 23:59:59 becomes 00:59:59 (invalid range) const bool dateShown = (sections & QDateTimeEdit::DateSections_Mask); if (!dateShown) { if (minimum.toTime() >= maximum.toTime()){ - minimum = QDateTime(value.toDate(), QDATETIMEEDIT_TIME_MIN, spec); - maximum = QDateTime(value.toDate(), QDATETIMEEDIT_TIME_MAX, spec); + minimum = value.toDate().startOfDay(timeZone); + maximum = value.toDate().endOfDay(timeZone); } } } @@ -1703,7 +1802,7 @@ void QDateTimeEditPrivate::updateEdit() if (!specialValue() #ifdef QT_KEYPAD_NAVIGATION - && !(QApplication::keypadNavigationEnabled() && !edit->hasEditFocus()) + && !(QApplicationPrivate::keypadNavigationEnabled() && !edit->hasEditFocus()) #endif ) { int cursor = sectionPos(currentSectionIndex); @@ -1721,6 +1820,27 @@ void QDateTimeEditPrivate::updateEdit() } } +QDateTime QDateTimeEditPrivate::getMinimum() const +{ + if (keyboardTracking) + return minimum.toDateTime(); + + if (timeZone.timeSpec() == Qt::LocalTime) + return QDateTimeParser::getMinimum(); + + return QDATETIMEEDIT_DATE_MIN.startOfDay(timeZone); +} + +QDateTime QDateTimeEditPrivate::getMaximum() const +{ + if (keyboardTracking) + return maximum.toDateTime(); + + if (timeZone.timeSpec() == Qt::LocalTime) + return QDateTimeParser::getMaximum(); + + return QDATETIMEEDIT_DATE_MAX.endOfDay(timeZone); +} /*! \internal @@ -1732,7 +1852,7 @@ void QDateTimeEditPrivate::setSelected(int sectionIndex, bool forward) { if (specialValue() #ifdef QT_KEYPAD_NAVIGATION - || (QApplication::keypadNavigationEnabled() && !edit->hasEditFocus()) + || (QApplicationPrivate::keypadNavigationEnabled() && !edit->hasEditFocus()) #endif ) { edit->selectAll(); @@ -1766,7 +1886,7 @@ int QDateTimeEditPrivate::sectionAt(int pos) const const int textSize = text.size(); if (textSize - pos < separators.last().size() + 1) { if (separators.last().size() == 0) { - return sectionNodes.count() - 1; + return sectionNodes.size() - 1; } return (pos == textSize ? LastSectionIndex : NoSectionIndex); } @@ -1796,7 +1916,7 @@ int QDateTimeEditPrivate::closestSection(int pos, bool forward) const const QString text = displayText(); if (text.size() - pos < separators.last().size() + 1) - return forward ? LastSectionIndex : sectionNodes.size() - 1; + return forward ? LastSectionIndex : int(sectionNodes.size() - 1); updateCache(value, text); for (int i=0; i<sectionNodes.size(); ++i) { @@ -1828,7 +1948,7 @@ int QDateTimeEditPrivate::nextPrevSection(int current, bool forward) const switch (current) { case FirstSectionIndex: return forward ? 0 : FirstSectionIndex; - case LastSectionIndex: return (forward ? LastSectionIndex : sectionNodes.size() - 1); + case LastSectionIndex: return (forward ? LastSectionIndex : int(sectionNodes.size() - 1)); case NoSectionIndex: return FirstSectionIndex; default: break; } @@ -1852,7 +1972,7 @@ int QDateTimeEditPrivate::nextPrevSection(int current, bool forward) const void QDateTimeEditPrivate::clearSection(int index) { - const QLatin1Char space(' '); + const auto space = u' '; int cursorPos = edit->cursorPosition(); const QSignalBlocker blocker(edit); QString t = edit->text(); @@ -1929,8 +2049,18 @@ QDateTime QDateTimeEditPrivate::validateAndInterpret(QString &input, int &positi return minimum.toDateTime(); } } + StateNode tmp = parse(input, position, value.toDateTime(), fixup); - input = tmp.input; + // Take note of any corrections imposed during parsing: + input = m_text; + // TODO: if the format specifies time-zone, update timeZone to match the + // parsed text; but we're in const context, so can't - QTBUG-118393. + // Impose this widget's time system: + tmp.value = tmp.value.toTimeZone(timeZone); + // ... but that might turn a valid datetime into an invalid one: + if (!tmp.value.isValid() && tmp.state == Acceptable) + tmp.state = Intermediate; + position += tmp.padded; state = QValidator::State(int(tmp.state)); if (state == QValidator::Acceptable) { @@ -1972,7 +2102,7 @@ QString QDateTimeEditPrivate::textFromValue(const QVariant &f) const QVariant QDateTimeEditPrivate::valueFromText(const QString &f) const { Q_Q(const QDateTimeEdit); - return q->dateTimeFromText(f).toTimeSpec(spec); + return q->dateTimeFromText(f).toTimeZone(timeZone); } @@ -1994,41 +2124,73 @@ QDateTime QDateTimeEditPrivate::stepBy(int sectionIndex, int steps, bool test) c int pos = edit->cursorPosition(); const SectionNode sn = sectionNode(sectionIndex); - int val; // to make sure it behaves reasonably when typing something and then stepping in non-tracking mode - if (!test && pendingEmit) { - if (q->validate(str, pos) != QValidator::Acceptable) { - v = value.toDateTime(); - } else { - v = q->dateTimeFromText(str); - } - val = getDigit(v, sectionIndex); - } else { - val = getDigit(v, sectionIndex); - } - - val += steps; + if (!test && pendingEmit && q->validate(str, pos) == QValidator::Acceptable) + v = q->dateTimeFromText(str); + int val = getDigit(v, sectionIndex); const int min = absoluteMin(sectionIndex); const int max = absoluteMax(sectionIndex, value.toDateTime()); - if (val < min) { - val = (wrapping ? max - (min - val) + 1 : min); - } else if (val > max) { - val = (wrapping ? min + val - max - 1 : max); - } - - - const int oldDay = v.date().day(); + if (sn.type & DayOfWeekSectionMask) { + // Must take locale's first day of week into account when *not* + // wrapping; min and max don't help us. +#ifndef QT_ALWAYS_WRAP_WEEKDAY // (documentation, not an actual define) + if (!wrapping) { + /* It's not clear this is ever really a desirable behavior. + + It refuses to step backwards from the first day of the week or + forwards from the day before, only allowing day-of-week stepping + from start to end of one week. That's strictly what non-wrapping + behavior must surely mean, when put in locale-neutral terms. + + It is, however, likely that users would prefer the "more natural" + behavior of cycling through the week. + */ + const int first = int(locale().firstDayOfWeek()); // Mon = 1 through 7 = Sun + val = qBound(val < first ? first - 7 : first, + val + steps, + val < first ? first - 1 : first + 6); + } else +#endif + { + val += steps; + } - setDigit(v, sectionIndex, val); + // Restore to range from 1 through 7: + val = val % 7; + if (val <= 0) + val += 7; + } else { + val += steps; + const int span = max - min + 1; + if (val < min) + val = wrapping ? val + span : min; + else if (val > max) + val = wrapping ? val - span : max; + } + + const int oldDay = v.date().day(calendar); + + /* + Stepping into a daylight saving time that doesn't exist (setDigit() is + true when date and time are valid, even if the date-time returned + isn't), so use the time that has the same distance from epoch. + */ + if (setDigit(v, sectionIndex, val) && getDigit(v, sectionIndex) != val + && sn.type & HourSectionMask && steps < 0) { + // decreasing from e.g 3am to 2am would get us back to 3am, but we want 1am + auto msecsSinceEpoch = v.toMSecsSinceEpoch() - 3600 * 1000; + v = QDateTime::fromMSecsSinceEpoch(msecsSinceEpoch, v.timeRepresentation()); + } // if this sets year or month it will make // sure that days are lowered if needed. const QDateTime minimumDateTime = minimum.toDateTime(); const QDateTime maximumDateTime = maximum.toDateTime(); // changing one section should only modify that section, if possible - if (sn.type != AmPmSection && (v < minimumDateTime || v > maximumDateTime)) { + if (sn.type != AmPmSection && !(sn.type & DayOfWeekSectionMask) + && (v < minimumDateTime || v > maximumDateTime)) { const int localmin = getDigit(minimumDateTime, sectionIndex); const int localmax = getDigit(maximumDateTime, sectionIndex); @@ -2038,10 +2200,10 @@ QDateTime QDateTimeEditPrivate::stepBy(int sectionIndex, int steps, bool test) c if (steps > 0) { setDigit(v, sectionIndex, min); if (!(sn.type & DaySectionMask) && sections & DateSectionMask) { - const int daysInMonth = v.date().daysInMonth(); - if (v.date().day() < oldDay && v.date().day() < daysInMonth) { + const int daysInMonth = v.date().daysInMonth(calendar); + if (v.date().day(calendar) < oldDay && v.date().day(calendar) < daysInMonth) { const int adds = qMin(oldDay, daysInMonth); - v = v.addDays(adds - v.date().day()); + v = v.addDays(adds - v.date().day(calendar)); } } @@ -2053,10 +2215,10 @@ QDateTime QDateTimeEditPrivate::stepBy(int sectionIndex, int steps, bool test) c } else { setDigit(v, sectionIndex, max); if (!(sn.type & DaySectionMask) && sections & DateSectionMask) { - const int daysInMonth = v.date().daysInMonth(); - if (v.date().day() < oldDay && v.date().day() < daysInMonth) { + const int daysInMonth = v.date().daysInMonth(calendar); + if (v.date().day(calendar) < oldDay && v.date().day(calendar) < daysInMonth) { const int adds = qMin(oldDay, daysInMonth); - v = v.addDays(adds - v.date().day()); + v = v.addDays(adds - v.date().day(calendar)); } } @@ -2070,7 +2232,7 @@ QDateTime QDateTimeEditPrivate::stepBy(int sectionIndex, int steps, bool test) c setDigit(v, sectionIndex, (steps > 0 ? localmax : localmin)); } } - if (!test && oldDay != v.date().day() && !(sn.type & DaySectionMask)) { + if (!test && oldDay != v.date().day(calendar) && !(sn.type & DaySectionMask)) { // this should not happen when called from stepEnabled cachedDay = qMax<int>(oldDay, cachedDay); } @@ -2115,8 +2277,7 @@ QDateTime QDateTimeEditPrivate::stepBy(int sectionIndex, int steps, bool test) c } } - const QDateTime ret = bound(v, value, steps).toDateTime().toTimeSpec(spec); - return ret; + return bound(v, value, steps).toDateTime().toTimeZone(timeZone); } /*! @@ -2152,7 +2313,7 @@ void QDateTimeEditPrivate::emitSignals(EmitPolicy ep, const QVariant &old) \internal */ -void QDateTimeEditPrivate::_q_editorCursorPositionChanged(int oldpos, int newpos) +void QDateTimeEditPrivate::editorCursorPositionChanged(int oldpos, int newpos) { if (ignoreCursorPositionChanged || specialValue()) return; @@ -2210,7 +2371,7 @@ void QDateTimeEditPrivate::_q_editorCursorPositionChanged(int oldpos, int newpos currentSectionIndex = s; Q_ASSERT_X(currentSectionIndex < sectionNodes.size(), - "QDateTimeEditPrivate::_q_editorCursorPositionChanged()", + "QDateTimeEditPrivate::editorCursorPositionChanged()", qPrintable(QString::fromLatin1("Internal error (%1 %2)"). arg(currentSectionIndex). arg(sectionNodes.size()))); @@ -2255,7 +2416,7 @@ QDateTimeEdit::Section QDateTimeEditPrivate::convertToPublic(QDateTimeParser::Se QDateTimeEdit::Sections QDateTimeEditPrivate::convertSections(QDateTimeParser::Sections s) { - QDateTimeEdit::Sections ret = 0; + QDateTimeEdit::Sections ret; if (s & QDateTimeParser::MSecSection) ret |= QDateTimeEdit::MSecSection; if (s & QDateTimeParser::SecondSection) @@ -2293,7 +2454,7 @@ void QDateTimeEdit::paintEvent(QPaintEvent *event) QStyleOptionComboBox optCombo; - optCombo.init(this); + optCombo.initFrom(this); optCombo.editable = true; optCombo.frame = opt.frame; optCombo.subControls = opt.subControls; @@ -2303,17 +2464,8 @@ void QDateTimeEdit::paintEvent(QPaintEvent *event) optCombo.state &= ~QStyle::State_Enabled; } - QPainter p(this); - style()->drawComplexControl(QStyle::CC_ComboBox, &optCombo, &p, this); -} - -QString QDateTimeEditPrivate::getAmPmText(AmPm ap, Case cs) const -{ - if (ap == AmText) { - return (cs == UpperCase ? QDateTimeParser::tr("AM") : QDateTimeParser::tr("am")); - } else { - return (cs == UpperCase ? QDateTimeParser::tr("PM") : QDateTimeParser::tr("pm")); - } + QStylePainter p(this); + p.drawComplexControl(QStyle::CC_ComboBox, optCombo); } int QDateTimeEditPrivate::absoluteIndex(QDateTimeEdit::Section s, int index) const @@ -2326,7 +2478,7 @@ int QDateTimeEditPrivate::absoluteIndex(QDateTimeEdit::Section s, int index) con return NoSectionIndex; } -int QDateTimeEditPrivate::absoluteIndex(const SectionNode &s) const +int QDateTimeEditPrivate::absoluteIndex(SectionNode s) const { return sectionNodes.indexOf(s); } @@ -2343,7 +2495,7 @@ void QDateTimeEditPrivate::interpret(EmitPolicy ep) || currentSectionIndex < 0 || !(fieldInfo(currentSectionIndex) & AllowPartial))) { setValue(value, ep); - updateTimeSpec(); + updateTimeZone(); } else { QAbstractSpinBoxPrivate::interpret(ep); } @@ -2382,34 +2534,34 @@ void QDateTimeEdit::initStyleOption(QStyleOptionSpinBox *option) const void QDateTimeEditPrivate::init(const QVariant &var) { Q_Q(QDateTimeEdit); - switch (var.type()) { - case QVariant::Date: - value = QDateTime(var.toDate(), QDATETIMEEDIT_TIME_MIN); - updateTimeSpec(); + switch (var.userType()) { + case QMetaType::QDate: + value = var.toDate().startOfDay(timeZone); + updateTimeZone(); q->setDisplayFormat(defaultDateFormat); if (sectionNodes.isEmpty()) // ### safeguard for broken locale - q->setDisplayFormat(QLatin1String("dd/MM/yyyy")); + q->setDisplayFormat("dd/MM/yyyy"_L1); break; - case QVariant::DateTime: + case QMetaType::QDateTime: value = var; - updateTimeSpec(); + updateTimeZone(); q->setDisplayFormat(defaultDateTimeFormat); if (sectionNodes.isEmpty()) // ### safeguard for broken locale - q->setDisplayFormat(QLatin1String("dd/MM/yyyy hh:mm:ss")); + q->setDisplayFormat("dd/MM/yyyy hh:mm:ss"_L1); break; - case QVariant::Time: - value = QDateTime(QDATETIMEEDIT_DATE_INITIAL, var.toTime()); - updateTimeSpec(); + case QMetaType::QTime: + value = dateTimeValue(QDATETIMEEDIT_DATE_INITIAL, var.toTime()); + updateTimeZone(); q->setDisplayFormat(defaultTimeFormat); if (sectionNodes.isEmpty()) // ### safeguard for broken locale - q->setDisplayFormat(QLatin1String("hh:mm:ss")); + q->setDisplayFormat("hh:mm:ss"_L1); break; default: Q_ASSERT_X(0, "QDateTimeEditPrivate::init", "Internal error"); break; } #ifdef QT_KEYPAD_NAVIGATION - if (QApplication::keypadNavigationEnabled()) + if (QApplicationPrivate::keypadNavigationEnabled()) q->setCalendarPopup(true); #endif q->setInputMethodHints(Qt::ImhPreferNumbers); @@ -2450,7 +2602,7 @@ QStyle::SubControl QDateTimeEditPrivate::newHoverControl(const QPoint &pos) Q_Q(QDateTimeEdit); QStyleOptionComboBox optCombo; - optCombo.init(q); + optCombo.initFrom(q); optCombo.editable = true; optCombo.subControls = QStyle::SC_All; hoverControl = q->style()->hitTestComplexControl(QStyle::CC_ComboBox, &optCombo, pos, q); @@ -2467,7 +2619,7 @@ void QDateTimeEditPrivate::updateEditFieldGeometry() Q_Q(QDateTimeEdit); QStyleOptionComboBox optCombo; - optCombo.init(q); + optCombo.initFrom(q); optCombo.editable = true; optCombo.subControls = QStyle::SC_ComboBoxEditField; edit->setGeometry(q->style()->subControlRect(QStyle::CC_ComboBox, &optCombo, @@ -2476,8 +2628,8 @@ void QDateTimeEditPrivate::updateEditFieldGeometry() QVariant QDateTimeEditPrivate::getZeroVariant() const { - Q_ASSERT(type == QVariant::DateTime); - return QDateTime(QDATETIMEEDIT_DATE_INITIAL, QTime(), spec); + Q_ASSERT(type == QMetaType::QDateTime); + return QDATETIMEEDIT_DATE_INITIAL.startOfDay(timeZone); } void QDateTimeEditPrivate::setRange(const QVariant &min, const QVariant &max) @@ -2505,8 +2657,8 @@ void QDateTimeEditPrivate::initCalendarPopup(QCalendarWidget *cw) { Q_Q(QDateTimeEdit); if (!monthCalendar) { - monthCalendar = new QCalendarPopup(q, cw); - monthCalendar->setObjectName(QLatin1String("qt_datetimedit_calendar")); + monthCalendar = new QCalendarPopup(q, cw, calendar); + monthCalendar->setObjectName("qt_datetimedit_calendar"_L1); QObject::connect(monthCalendar, SIGNAL(newDateSelected(QDate)), q, SLOT(setDate(QDate))); QObject::connect(monthCalendar, SIGNAL(hidingCalendar(QDate)), q, SLOT(setDate(QDate))); QObject::connect(monthCalendar, SIGNAL(activated(QDate)), q, SLOT(setDate(QDate))); @@ -2526,28 +2678,31 @@ void QDateTimeEditPrivate::positionCalendarPopup() pos = q->mapToGlobal(pos); pos2 = q->mapToGlobal(pos2); QSize size = monthCalendar->sizeHint(); - QRect screen = QDesktopWidgetPrivate::availableGeometry(pos); + QScreen *screen = QGuiApplication::screenAt(pos); + if (!screen) + screen = QGuiApplication::primaryScreen(); + const QRect screenRect = screen->availableGeometry(); //handle popup falling "off screen" if (q->layoutDirection() == Qt::RightToLeft) { pos.setX(pos.x()-size.width()); pos2.setX(pos2.x()-size.width()); - if (pos.x() < screen.left()) - pos.setX(qMax(pos.x(), screen.left())); - else if (pos.x()+size.width() > screen.right()) - pos.setX(qMax(pos.x()-size.width(), screen.right()-size.width())); + if (pos.x() < screenRect.left()) + pos.setX(qMax(pos.x(), screenRect.left())); + else if (pos.x()+size.width() > screenRect.right()) + pos.setX(qMax(pos.x()-size.width(), screenRect.right()-size.width())); } else { - if (pos.x()+size.width() > screen.right()) - pos.setX(screen.right()-size.width()); - pos.setX(qMax(pos.x(), screen.left())); + if (pos.x()+size.width() > screenRect.right()) + pos.setX(screenRect.right()-size.width()); + pos.setX(qMax(pos.x(), screenRect.left())); } - if (pos.y() + size.height() > screen.bottom()) + if (pos.y() + size.height() > screenRect.bottom()) pos.setY(pos2.y() - size.height()); - else if (pos.y() < screen.top()) - pos.setY(screen.top()); - if (pos.y() < screen.top()) - pos.setY(screen.top()); - if (pos.y()+size.height() > screen.bottom()) - pos.setY(screen.bottom()-size.height()); + else if (pos.y() < screenRect.top()) + pos.setY(screenRect.top()); + if (pos.y() < screenRect.top()) + pos.setY(screenRect.top()); + if (pos.y()+size.height() > screenRect.bottom()) + pos.setY(screenRect.bottom()-size.height()); monthCalendar->move(pos); } @@ -2566,8 +2721,8 @@ void QDateTimeEditPrivate::syncCalendarWidget() } } -QCalendarPopup::QCalendarPopup(QWidget * parent, QCalendarWidget *cw) - : QWidget(parent, Qt::Popup) +QCalendarPopup::QCalendarPopup(QWidget *parent, QCalendarWidget *cw, QCalendar ca) + : QWidget(parent, Qt::Popup), calendarSystem(ca) { setAttribute(Qt::WA_WindowPropagation); @@ -2583,9 +2738,10 @@ QCalendarWidget *QCalendarPopup::verifyCalendarInstance() { if (calendar.isNull()) { QCalendarWidget *cw = new QCalendarWidget(this); + cw->setCalendar(calendarSystem); cw->setVerticalHeaderFormat(QCalendarWidget::NoVerticalHeader); #ifdef QT_KEYPAD_NAVIGATION - if (QApplication::keypadNavigationEnabled()) + if (QApplicationPrivate::keypadNavigationEnabled()) cw->setHorizontalHeaderFormat(QCalendarWidget::SingleLetterDayNames); #endif setCalendarWidget(cw); @@ -2601,7 +2757,7 @@ void QCalendarPopup::setCalendarWidget(QCalendarWidget *cw) QVBoxLayout *widgetLayout = qobject_cast<QVBoxLayout*>(layout()); if (!widgetLayout) { widgetLayout = new QVBoxLayout(this); - widgetLayout->setMargin(0); + widgetLayout->setContentsMargins(QMargins()); widgetLayout->setSpacing(0); } delete calendar.data(); @@ -2616,13 +2772,13 @@ void QCalendarPopup::setCalendarWidget(QCalendarWidget *cw) } -void QCalendarPopup::setDate(const QDate &date) +void QCalendarPopup::setDate(QDate date) { oldDate = date; verifyCalendarInstance()->setSelectedDate(date); } -void QCalendarPopup::setDateRange(const QDate &min, const QDate &max) +void QCalendarPopup::setDateRange(QDate min, QDate max) { QCalendarWidget *cw = verifyCalendarInstance(); cw->setMinimumDate(min); @@ -2634,11 +2790,11 @@ void QCalendarPopup::mousePressEvent(QMouseEvent *event) QDateTimeEdit *dateTime = qobject_cast<QDateTimeEdit *>(parentWidget()); if (dateTime) { QStyleOptionComboBox opt; - opt.init(dateTime); + opt.initFrom(dateTime); QRect arrowRect = dateTime->style()->subControlRect(QStyle::CC_ComboBox, &opt, QStyle::SC_ComboBoxArrow, dateTime); arrowRect.moveTo(dateTime->mapToGlobal(arrowRect .topLeft())); - if (arrowRect.contains(event->globalPos()) || rect().contains(event->pos())) + if (arrowRect.contains(event->globalPosition().toPoint()) || rect().contains(event->position().toPoint())) setAttribute(Qt::WA_NoMouseReplay); } QWidget::mousePressEvent(event); @@ -2666,7 +2822,7 @@ void QCalendarPopup::dateSelectionChanged() dateChanged = true; emit newDateSelected(verifyCalendarInstance()->selectedDate()); } -void QCalendarPopup::dateSelected(const QDate &date) +void QCalendarPopup::dateSelected(QDate date) { dateChanged = true; emit activated(date); |