1 /****************************************************************************
2 **
3 ** Copyright (C) 2019 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtWidgets module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include <private/qapplication_p.h>
41 #include <private/qdatetimeedit_p.h>
42 #include <qabstractspinbox.h>
43 #include <qapplication.h>
44 #include <qdatetimeedit.h>
45 #include <qdesktopwidget.h>
46 #include <private/qdesktopwidget_p.h>
47 #include <qdebug.h>
48 #include <qevent.h>
49 #include <qlineedit.h>
50 #include <private/qlineedit_p.h>
51 #include <qlocale.h>
52 #include <qpainter.h>
53 #include <qlayout.h>
54 #include <qset.h>
55 #include <qstyle.h>
56 
57 #include <algorithm>
58 
59 //#define QDATETIMEEDIT_QDTEDEBUG
60 #ifdef QDATETIMEEDIT_QDTEDEBUG
61 #  define QDTEDEBUG qDebug() << QString::fromLatin1("%1:%2").arg(__FILE__).arg(__LINE__)
62 #  define QDTEDEBUGN qDebug
63 #else
64 #  define QDTEDEBUG if (false) qDebug()
65 #  define QDTEDEBUGN if (false) qDebug
66 #endif
67 
68 QT_BEGIN_NAMESPACE
69 
70 // --- QDateTimeEdit ---
71 
72 /*!
73   \class QDateTimeEdit
74   \brief The QDateTimeEdit class provides a widget for editing dates and times.
75 
76   \ingroup basicwidgets
77   \inmodule QtWidgets
78 
79   \image windows-datetimeedit.png
80 
81   QDateTimeEdit allows the user to edit dates by using the keyboard or
82   the arrow keys to increase and decrease date and time values. The
83   arrow keys can be used to move from section to section within the
84   QDateTimeEdit box. Dates and times appear in accordance with the
85   format set; see setDisplayFormat().
86 
87   \snippet code/src_gui_widgets_qdatetimeedit.cpp 0
88 
89   Here we've created a new QDateTimeEdit object initialized with
90   today's date, and restricted the valid date range to today plus or
91   minus 365 days. We've set the order to month, day, year.
92 
93   The range of valid values for a QDateTimeEdit is controlled by the properties
94   \l minimumDateTime, \l maximumDateTime, and their respective date and time
95   components. By default, any date-time from the start of 100 CE to the end of
96   9999 CE is valid.
97 
98   \section1 Using a Pop-up Calendar Widget
99 
100   QDateTimeEdit can be configured to allow a QCalendarWidget to be used
101   to select dates. This is enabled by setting the calendarPopup property.
102   Additionally, you can supply a custom calendar widget for use as the
103   calendar pop-up by calling the setCalendarWidget() function. The existing
104   calendar widget can be retrieved with calendarWidget().
105 
106   \section1 Keyboard Tracking
107 
108   When \l{QAbstractSpinBox::keyboardTracking}{keyboard tracking} is enabled
109   (the default), every keystroke of editing a field triggers signals for value
110   changes.
111 
112   When the allowed \l{QDateTimeEdit::setDateTimeRange}{range} is narrower than
113   some time interval whose end it straddles, keyboard tracking prevents the
114   user editing the date or time to access the later part of the interval. For
115   example, for a range from 29.04.2020 to 02.05.2020 and an initial date of
116   30.04.2020, the user can change neither the month (May 30th is outside the
117   range) nor the day (April 2nd is outside the range).
118 
119   When keyboard tracking is disabled, changes are only signalled when focus
120   leaves the text field after edits have modified the content. This allows the
121   user to edit via an invalid date-time to reach a valid one.
122 
123   \sa QDateEdit, QTimeEdit, QDate, QTime
124 */
125 
126 /*!
127   \enum QDateTimeEdit::Section
128 
129   \value NoSection
130   \value AmPmSection
131   \value MSecSection
132   \value SecondSection
133   \value MinuteSection
134   \value HourSection
135   \value DaySection
136   \value MonthSection
137   \value YearSection
138   \omitvalue DateSections_Mask
139   \omitvalue TimeSections_Mask
140 */
141 
142 /*!
143   \fn void QDateTimeEdit::dateTimeChanged(const QDateTime &datetime)
144 
145   This signal is emitted whenever the date or time is changed. The
146   new date and time is passed in \a datetime.
147 
148   \sa {Keyboard Tracking}
149 */
150 
151 /*!
152   \fn void QDateTimeEdit::timeChanged(const QTime &time)
153 
154   This signal is emitted whenever the time is changed. The new time
155   is passed in \a time.
156 
157   \sa {Keyboard Tracking}
158 */
159 
160 /*!
161   \fn void QDateTimeEdit::dateChanged(const QDate &date)
162 
163   This signal is emitted whenever the date is changed. The new date
164   is passed in \a date.
165 
166   \sa {Keyboard Tracking}
167 */
168 
169 
170 /*!
171   Constructs an empty date time editor with a \a parent.
172 */
173 
QDateTimeEdit(QWidget * parent)174 QDateTimeEdit::QDateTimeEdit(QWidget *parent)
175     : QAbstractSpinBox(*new QDateTimeEditPrivate, parent)
176 {
177     Q_D(QDateTimeEdit);
178     d->init(QDATETIMEEDIT_DATE_INITIAL.startOfDay());
179 }
180 
181 /*!
182   Constructs an empty date time editor with a \a parent. The value
183   is set to \a datetime.
184 */
185 
QDateTimeEdit(const QDateTime & datetime,QWidget * parent)186 QDateTimeEdit::QDateTimeEdit(const QDateTime &datetime, QWidget *parent)
187     : QAbstractSpinBox(*new QDateTimeEditPrivate, parent)
188 {
189     Q_D(QDateTimeEdit);
190     d->init(datetime.isValid() ? datetime : QDATETIMEEDIT_DATE_INITIAL.startOfDay());
191 }
192 
193 /*!
194   \fn QDateTimeEdit::QDateTimeEdit(const QDate &date, QWidget *parent)
195 
196   Constructs an empty date time editor with a \a parent.
197   The value is set to \a date.
198 */
199 
QDateTimeEdit(const QDate & date,QWidget * parent)200 QDateTimeEdit::QDateTimeEdit(const QDate &date, QWidget *parent)
201     : QAbstractSpinBox(*new QDateTimeEditPrivate, parent)
202 {
203     Q_D(QDateTimeEdit);
204     d->init(date.isValid() ? date : QDATETIMEEDIT_DATE_INITIAL);
205 }
206 
207 /*!
208   \fn QDateTimeEdit::QDateTimeEdit(const QTime &time, QWidget *parent)
209 
210   Constructs an empty date time editor with a \a parent.
211   The value is set to \a time.
212 */
213 
QDateTimeEdit(const QTime & time,QWidget * parent)214 QDateTimeEdit::QDateTimeEdit(const QTime &time, QWidget *parent)
215     : QAbstractSpinBox(*new QDateTimeEditPrivate, parent)
216 {
217     Q_D(QDateTimeEdit);
218     d->init(time.isValid() ? time : QDATETIMEEDIT_TIME_MIN);
219 }
220 
221 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
222 /*!
223   \internal
224 */
QDateTimeEdit(const QVariant & var,QVariant::Type parserType,QWidget * parent)225 QDateTimeEdit::QDateTimeEdit(const QVariant &var, QVariant::Type parserType, QWidget *parent)
226     : QDateTimeEdit(var, QMetaType::Type(parserType), parent)
227 { }
228 /*!
229   \internal
230 */
231 #endif
232 
QDateTimeEdit(const QVariant & var,QMetaType::Type parserType,QWidget * parent)233 QDateTimeEdit::QDateTimeEdit(const QVariant &var, QMetaType::Type parserType, QWidget *parent)
234     : QAbstractSpinBox(*new QDateTimeEditPrivate, parent)
235 {
236     Q_D(QDateTimeEdit);
237     d->parserType = parserType;
238     d->init(var);
239 }
240 
241 /*!
242     Destructor.
243 */
~QDateTimeEdit()244 QDateTimeEdit::~QDateTimeEdit()
245 {
246 }
247 
248 /*!
249   \property QDateTimeEdit::dateTime
250   \brief The QDateTime that is set in the QDateTimeEdit.
251 
252   When setting this property the timespec of the QDateTimeEdit remains the same
253   and the timespec of the new QDateTime is ignored.
254 
255   By default, this property is set to the start of 2000 CE. It can only be set
256   to a valid QDateTime value. If any operation causes this property to have an
257   invalid date-time as value, it is reset to the value of the \l minimumDateTime
258   property.
259 
260   If the QDateTimeEdit has no date fields, setting this property sets the
261   widget's date-range to start and end on the date of the new value of this
262   property.
263 
264   \sa date, time, minimumDateTime, maximumDateTime
265 */
266 
dateTime() const267 QDateTime QDateTimeEdit::dateTime() const
268 {
269     Q_D(const QDateTimeEdit);
270     return d->value.toDateTime();
271 }
272 
setDateTime(const QDateTime & datetime)273 void QDateTimeEdit::setDateTime(const QDateTime &datetime)
274 {
275     Q_D(QDateTimeEdit);
276     if (datetime.isValid()) {
277         d->clearCache();
278         const QDate date = datetime.date();
279         if (!(d->sections & DateSections_Mask))
280             setDateRange(date, date);
281         d->setValue(QDateTime(date, datetime.time(), d->spec), EmitIfChanged);
282     }
283 }
284 
285 /*!
286   \property QDateTimeEdit::date
287   \brief The QDate that is set in the widget.
288 
289   By default, this property contains a date that refers to January 1, 2000.
290 
291   \sa time, dateTime
292 */
293 
294 /*!
295     Returns the date of the date time edit.
296 */
date() const297 QDate QDateTimeEdit::date() const
298 {
299     Q_D(const QDateTimeEdit);
300     return d->value.toDate();
301 }
302 
setDate(const QDate & date)303 void QDateTimeEdit::setDate(const QDate &date)
304 {
305     Q_D(QDateTimeEdit);
306     if (date.isValid()) {
307         if (!(d->sections & DateSections_Mask))
308             setDateRange(date, date);
309 
310         d->clearCache();
311         QDateTime when(date, d->value.toTime(), d->spec);
312         // The specified time might not exist on the specified day,
313         // i.e. the time is in the gap a spring-forward jumps over.
314         if (!when.isValid())
315             when = QDateTime::fromMSecsSinceEpoch(when.toMSecsSinceEpoch(), d->spec);
316         Q_ASSERT(when.isValid());
317         d->setValue(when, EmitIfChanged);
318         d->updateTimeSpec();
319     }
320 }
321 
322 /*!
323   \property QDateTimeEdit::time
324   \brief The QTime that is set in the widget.
325 
326   By default, this property contains a time of 00:00:00 and 0 milliseconds.
327 
328   \sa date, dateTime
329 */
330 
331 /*!
332     Returns the time of the date time edit.
333 */
time() const334 QTime QDateTimeEdit::time() const
335 {
336     Q_D(const QDateTimeEdit);
337     return d->value.toTime();
338 }
339 
setTime(const QTime & time)340 void QDateTimeEdit::setTime(const QTime &time)
341 {
342     Q_D(QDateTimeEdit);
343     if (time.isValid()) {
344         d->clearCache();
345         d->setValue(QDateTime(d->value.toDate(), time, d->spec), EmitIfChanged);
346     }
347 }
348 
349 
calendar() const350 QCalendar QDateTimeEdit::calendar() const
351 {
352     Q_D(const QDateTimeEdit);
353     return d->calendar;
354 }
355 
setCalendar(QCalendar calendar)356 void QDateTimeEdit::setCalendar(QCalendar calendar)
357 {
358     Q_D(QDateTimeEdit);
359     // Set invalid date time to prevent runtime crashes on calendar change
360     QDateTime previousValue = d->value.toDateTime();
361     setDateTime(QDateTime());
362     d->setCalendar(calendar);
363     setDateTime(previousValue);
364 }
365 
366 /*!
367   \since 4.4
368   \property QDateTimeEdit::minimumDateTime
369 
370   \brief The minimum datetime of the date time edit.
371 
372   Changing this property implicitly updates the \l minimumDate and \l
373   minimumTime properties to the date and time parts of this property,
374   respectively. When setting this property, the \l maximumDateTime is adjusted,
375   if necessary, to ensure that the range remains valid. Otherwise, changing this
376   property preserves the \l minimumDateTime property.
377 
378   This property can only be set to a valid QDateTime value. The earliest
379   date-time that setMinimumDateTime() accepts is the start of 100 CE. The
380   property's default is the start of September 14, 1752 CE. This default can be
381   restored with clearMinimumDateTime().
382 
383   \sa maximumDateTime, minimumTime, minimumDate, setDateTimeRange(),
384       QDateTime::isValid(), {Keyboard Tracking}
385 */
386 
minimumDateTime() const387 QDateTime QDateTimeEdit::minimumDateTime() const
388 {
389     Q_D(const QDateTimeEdit);
390     return d->minimum.toDateTime();
391 }
392 
clearMinimumDateTime()393 void QDateTimeEdit::clearMinimumDateTime()
394 {
395     setMinimumDateTime(QDATETIMEEDIT_COMPAT_DATE_MIN.startOfDay());
396 }
397 
setMinimumDateTime(const QDateTime & dt)398 void QDateTimeEdit::setMinimumDateTime(const QDateTime &dt)
399 {
400     Q_D(QDateTimeEdit);
401     if (dt.isValid() && dt.date() >= QDATETIMEEDIT_DATE_MIN) {
402         const QDateTime m = dt.toTimeSpec(d->spec);
403         const QDateTime max = d->maximum.toDateTime();
404         d->setRange(m, (max > m ? max : m));
405     }
406 }
407 
408 /*!
409   \since 4.4
410   \property QDateTimeEdit::maximumDateTime
411 
412   \brief The maximum datetime of the date time edit.
413 
414   Changing this property implicitly updates the \l maximumDate and \l
415   maximumTime properties to the date and time parts of this property,
416   respectively. When setting this property, the \l minimumDateTime is adjusted,
417   if necessary, to ensure that the range remains valid. Otherwise, changing this
418   property preserves the \l minimumDateTime property.
419 
420   This property can only be set to a valid QDateTime value. The latest
421   date-time that setMaximumDateTime() accepts is the end of 9999 CE. This is the
422   default for this property. This default can be restored with
423   clearMaximumDateTime().
424 
425   \sa minimumDateTime, maximumTime, maximumDate(), setDateTimeRange(),
426       QDateTime::isValid(), {Keyboard Tracking}
427 */
428 
maximumDateTime() const429 QDateTime QDateTimeEdit::maximumDateTime() const
430 {
431     Q_D(const QDateTimeEdit);
432     return d->maximum.toDateTime();
433 }
434 
clearMaximumDateTime()435 void QDateTimeEdit::clearMaximumDateTime()
436 {
437     setMaximumDateTime(QDATETIMEEDIT_DATE_MAX.endOfDay());
438 }
439 
setMaximumDateTime(const QDateTime & dt)440 void QDateTimeEdit::setMaximumDateTime(const QDateTime &dt)
441 {
442     Q_D(QDateTimeEdit);
443     if (dt.isValid() && dt.date() <= QDATETIMEEDIT_DATE_MAX) {
444         const QDateTime m = dt.toTimeSpec(d->spec);
445         const QDateTime min = d->minimum.toDateTime();
446         d->setRange((min < m ? min : m), m);
447     }
448 }
449 
450 /*!
451   \since 4.4
452   \brief Set the range of allowed date-times for the date time edit.
453 
454   This convenience function sets the \l minimumDateTime and \l maximumDateTime
455   properties.
456 
457   \snippet code/src_gui_widgets_qdatetimeedit.cpp 1
458 
459   is analogous to:
460 
461   \snippet code/src_gui_widgets_qdatetimeedit.cpp 2
462 
463   If either \a min or \a max is invalid, this function does nothing. If \a max
464   is less than \a min, \a min is used also as \a max.
465 
466   If the range is narrower then a time interval whose end it spans, for example
467   a week that spans the end of a month, users can only edit the date-time to one
468   in the later part of the range if keyboard-tracking is disabled.
469 
470   \sa minimumDateTime, maximumDateTime, setDateRange(), setTimeRange(),
471       QDateTime::isValid(), {Keyboard Tracking}
472 */
473 
setDateTimeRange(const QDateTime & min,const QDateTime & max)474 void QDateTimeEdit::setDateTimeRange(const QDateTime &min, const QDateTime &max)
475 {
476     Q_D(QDateTimeEdit);
477     // FIXME: does none of the range checks applied to setMin/setMax methods !
478     const QDateTime minimum = min.toTimeSpec(d->spec);
479     const QDateTime maximum = (min > max ? minimum : max.toTimeSpec(d->spec));
480     d->setRange(minimum, maximum);
481 }
482 
483 /*!
484   \property QDateTimeEdit::minimumDate
485 
486   \brief The minimum date of the date time edit.
487 
488   Changing this property updates the date of the \l minimumDateTime property
489   while preserving the \l minimumTime property. When setting this property,
490   the \l maximumDate is adjusted, if necessary, to ensure that the range remains
491   valid. When this happens, the \l maximumTime property is also adjusted if it
492   is less than the \l minimumTime property. Otherwise, changes to this property
493   preserve the \l maximumDateTime property.
494 
495   This property can only be set to a valid QDate object describing a date on
496   which the current \l minimumTime property makes a valid QDateTime object. The
497   earliest date that setMinimumDate() accepts is the start of 100 CE. The
498   default for this property is September 14, 1752 CE. This default can be
499   restored with clearMinimumDateTime().
500 
501   \sa maximumDate, minimumTime, minimumDateTime, setDateRange(),
502       QDate::isValid(), {Keyboard Tracking}
503 */
504 
minimumDate() const505 QDate QDateTimeEdit::minimumDate() const
506 {
507     Q_D(const QDateTimeEdit);
508     return d->minimum.toDate();
509 }
510 
setMinimumDate(const QDate & min)511 void QDateTimeEdit::setMinimumDate(const QDate &min)
512 {
513     Q_D(QDateTimeEdit);
514     if (min.isValid() && min >= QDATETIMEEDIT_DATE_MIN) {
515         setMinimumDateTime(QDateTime(min, d->minimum.toTime(), d->spec));
516     }
517 }
518 
clearMinimumDate()519 void QDateTimeEdit::clearMinimumDate()
520 {
521     setMinimumDate(QDATETIMEEDIT_COMPAT_DATE_MIN);
522 }
523 
524 /*!
525   \property QDateTimeEdit::maximumDate
526 
527   \brief The maximum date of the date time edit.
528 
529   Changing this property updates the date of the \l maximumDateTime property
530   while preserving the \l maximumTime property. When setting this property, the
531   \l minimumDate is adjusted, if necessary, to ensure that the range remains
532   valid. When this happens, the \l minimumTime property is also adjusted if it
533   is greater than the \l maximumTime property. Otherwise, changes to this
534   property preserve the \l minimumDateTime property.
535 
536   This property can only be set to a valid QDate object describing a date on
537   which the current \l maximumTime property makes a valid QDateTime object. The
538   latest date that setMaximumDate() accepts is the end of 9999 CE. This is the
539   default for this property. This default can be restored with
540   clearMaximumDateTime().
541 
542   \sa minimumDate, maximumTime, maximumDateTime, setDateRange(),
543       QDate::isValid(), {Keyboard Tracking}
544 */
545 
maximumDate() const546 QDate QDateTimeEdit::maximumDate() const
547 {
548     Q_D(const QDateTimeEdit);
549     return d->maximum.toDate();
550 }
551 
setMaximumDate(const QDate & max)552 void QDateTimeEdit::setMaximumDate(const QDate &max)
553 {
554     Q_D(QDateTimeEdit);
555     if (max.isValid())
556         setMaximumDateTime(QDateTime(max, d->maximum.toTime(), d->spec));
557 }
558 
clearMaximumDate()559 void QDateTimeEdit::clearMaximumDate()
560 {
561     setMaximumDate(QDATETIMEEDIT_DATE_MAX);
562 }
563 
564 /*!
565   \property QDateTimeEdit::minimumTime
566 
567   \brief The minimum time of the date time edit.
568 
569   Changing this property updates the time of the \l minimumDateTime property
570   while preserving the \l minimumDate and \l maximumDate properties. If those
571   date properties coincide, when setting this property, the \l maximumTime
572   property is adjusted, if necessary, to ensure that the range remains
573   valid. Otherwise, changing this property preserves the \l maximumDateTime
574   property.
575 
576   This property can be set to any valid QTime value. By default, this property
577   contains a time of 00:00:00 and 0 milliseconds. This default can be restored
578   with clearMinimumTime().
579 
580   \sa maximumTime, minimumDate, minimumDateTime, setTimeRange(),
581       QTime::isValid(), {Keyboard Tracking}
582 */
583 
minimumTime() const584 QTime QDateTimeEdit::minimumTime() const
585 {
586     Q_D(const QDateTimeEdit);
587     return d->minimum.toTime();
588 }
589 
setMinimumTime(const QTime & min)590 void QDateTimeEdit::setMinimumTime(const QTime &min)
591 {
592     Q_D(QDateTimeEdit);
593     if (min.isValid()) {
594         const QDateTime m(d->minimum.toDate(), min, d->spec);
595         setMinimumDateTime(m);
596     }
597 }
598 
clearMinimumTime()599 void QDateTimeEdit::clearMinimumTime()
600 {
601     setMinimumTime(QDATETIMEEDIT_TIME_MIN);
602 }
603 
604 /*!
605   \property QDateTimeEdit::maximumTime
606 
607   \brief The maximum time of the date time edit.
608 
609   Changing this property updates the time of the \l maximumDateTime property
610   while preserving the \l minimumDate and \l maximumDate properties. If those
611   date properties coincide, when setting this property, the \l minimumTime
612   property is adjusted, if necessary, to ensure that the range remains
613   valid. Otherwise, changing this property preserves the \l minimumDateTime
614   property.
615 
616   This property can be set to any valid QTime value. By default, this property
617   contains a time of 23:59:59 and 999 milliseconds. This default can be restored
618   with clearMaximumTime().
619 
620   \sa minimumTime, maximumDate, maximumDateTime, setTimeRange(),
621       QTime::isValid(), {Keyboard Tracking}
622 */
maximumTime() const623 QTime QDateTimeEdit::maximumTime() const
624 {
625     Q_D(const QDateTimeEdit);
626     return d->maximum.toTime();
627 }
628 
setMaximumTime(const QTime & max)629 void QDateTimeEdit::setMaximumTime(const QTime &max)
630 {
631     Q_D(QDateTimeEdit);
632     if (max.isValid()) {
633         const QDateTime m(d->maximum.toDate(), max, d->spec);
634         setMaximumDateTime(m);
635     }
636 }
637 
clearMaximumTime()638 void QDateTimeEdit::clearMaximumTime()
639 {
640     setMaximumTime(QDATETIMEEDIT_TIME_MAX);
641 }
642 
643 /*!
644   \brief Set the range of allowed dates for the date time edit.
645 
646   This convenience function sets the \l minimumDate and \l maximumDate
647   properties.
648 
649   \snippet code/src_gui_widgets_qdatetimeedit.cpp 3
650 
651   is analogous to:
652 
653   \snippet code/src_gui_widgets_qdatetimeedit.cpp 4
654 
655   If either \a min or \a max is invalid, this function does nothing. This
656   function preserves the \l minimumTime property. If \a max is less than \a min,
657   the new maximumDateTime property shall be the new minimumDateTime property. If
658   \a max is equal to \a min and the \l maximumTime property was less then the \l
659   minimumTime property, the \l maximumTime property is set to the \l minimumTime
660   property. Otherwise, this preserves the \l maximumTime property.
661 
662   If the range is narrower then a time interval whose end it spans, for example
663   a week that spans the end of a month, users can only edit the date to one in
664   the later part of the range if keyboard-tracking is disabled.
665 
666   \sa minimumDate, maximumDate, setDateTimeRange(), QDate::isValid(), {Keyboard Tracking}
667 */
668 
setDateRange(const QDate & min,const QDate & max)669 void QDateTimeEdit::setDateRange(const QDate &min, const QDate &max)
670 {
671     Q_D(QDateTimeEdit);
672     if (min.isValid() && max.isValid()) {
673         setDateTimeRange(QDateTime(min, d->minimum.toTime(), d->spec),
674                          QDateTime(max, d->maximum.toTime(), d->spec));
675     }
676 }
677 
678 /*!
679   \brief Set the range of allowed times for the date time edit.
680 
681   This convenience function sets the \l minimumTime and \l maximumTime
682   properties.
683 
684   Note that these only constrain the date time edit's value on,
685   respectively, the \l minimumDate and \l maximumDate. When these date
686   properties do not coincide, times after \a max are allowed on dates
687   before \l maximumDate and times before \a min are allowed on dates
688   after \l minimumDate.
689 
690   \snippet code/src_gui_widgets_qdatetimeedit.cpp 5
691 
692   is analogous to:
693 
694   \snippet code/src_gui_widgets_qdatetimeedit.cpp 6
695 
696   If either \a min or \a max is invalid, this function does nothing. This
697   function preserves the \l minimumDate and \l maximumDate properties. If those
698   properties coincide and \a max is less than \a min, \a min is used as \a max.
699 
700   If the range is narrower then a time interval whose end it spans, for example
701   the interval from ten to an hour to ten past the same hour, users can only
702   edit the time to one in the later part of the range if keyboard-tracking is
703   disabled.
704 
705   \sa minimumTime, maximumTime, setDateTimeRange(), QTime::isValid(), {Keyboard Tracking}
706 */
707 
setTimeRange(const QTime & min,const QTime & max)708 void QDateTimeEdit::setTimeRange(const QTime &min, const QTime &max)
709 {
710     Q_D(QDateTimeEdit);
711     if (min.isValid() && max.isValid()) {
712         setDateTimeRange(QDateTime(d->minimum.toDate(), min, d->spec),
713                          QDateTime(d->maximum.toDate(), max, d->spec));
714     }
715 }
716 
717 /*!
718   \property QDateTimeEdit::displayedSections
719 
720   \brief The currently displayed fields of the date time edit.
721 
722   Returns a bit set of the displayed sections for this format.
723 
724   \sa setDisplayFormat(), displayFormat()
725 */
726 
displayedSections() const727 QDateTimeEdit::Sections QDateTimeEdit::displayedSections() const
728 {
729     Q_D(const QDateTimeEdit);
730     return d->sections;
731 }
732 
733 /*!
734   \property QDateTimeEdit::currentSection
735 
736   \brief The current section of the spinbox.
737 */
738 
currentSection() const739 QDateTimeEdit::Section QDateTimeEdit::currentSection() const
740 {
741     Q_D(const QDateTimeEdit);
742 #ifdef QT_KEYPAD_NAVIGATION
743     if (QApplicationPrivate::keypadNavigationEnabled() && d->focusOnButton)
744         return NoSection;
745 #endif
746     return QDateTimeEditPrivate::convertToPublic(d->sectionType(d->currentSectionIndex));
747 }
748 
setCurrentSection(Section section)749 void QDateTimeEdit::setCurrentSection(Section section)
750 {
751     Q_D(QDateTimeEdit);
752     if (section == NoSection || !(section & d->sections))
753         return;
754 
755     d->updateCache(d->value, d->displayText());
756     const int size = d->sectionNodes.size();
757     int index = d->currentSectionIndex + 1;
758     for (int i=0; i<2; ++i) {
759         while (index < size) {
760             if (QDateTimeEditPrivate::convertToPublic(d->sectionType(index)) == section) {
761                 d->edit->setCursorPosition(d->sectionPos(index));
762                 QDTEDEBUG << d->sectionPos(index);
763                 return;
764             }
765             ++index;
766         }
767         index = 0;
768     }
769 }
770 
771 /*!
772   \since 4.3
773 
774   Returns the Section at \a index.
775 
776   If the format is 'yyyy/MM/dd', sectionAt(0) returns YearSection,
777   sectionAt(1) returns MonthSection, and sectionAt(2) returns
778   YearSection,
779 */
780 
sectionAt(int index) const781 QDateTimeEdit::Section QDateTimeEdit::sectionAt(int index) const
782 {
783     Q_D(const QDateTimeEdit);
784     if (index < 0 || index >= d->sectionNodes.size())
785         return NoSection;
786     return QDateTimeEditPrivate::convertToPublic(d->sectionType(index));
787 }
788 
789 /*!
790   \since 4.3
791 
792   \property QDateTimeEdit::sectionCount
793 
794   \brief The number of sections displayed.
795   If the format is 'yyyy/yy/yyyy', sectionCount returns 3
796 */
797 
sectionCount() const798 int QDateTimeEdit::sectionCount() const
799 {
800     Q_D(const QDateTimeEdit);
801     return d->sectionNodes.size();
802 }
803 
804 
805 /*!
806   \since 4.3
807 
808   \property QDateTimeEdit::currentSectionIndex
809 
810   \brief The current section index of the spinbox.
811 
812   If the format is 'yyyy/MM/dd', the displayText is '2001/05/21', and
813   the cursorPosition is 5, currentSectionIndex returns 1. If the
814   cursorPosition is 3, currentSectionIndex is 0, and so on.
815 
816   \sa setCurrentSection(), currentSection()
817 */
818 
currentSectionIndex() const819 int QDateTimeEdit::currentSectionIndex() const
820 {
821     Q_D(const QDateTimeEdit);
822     return d->currentSectionIndex;
823 }
824 
setCurrentSectionIndex(int index)825 void QDateTimeEdit::setCurrentSectionIndex(int index)
826 {
827     Q_D(QDateTimeEdit);
828     if (index < 0 || index >= d->sectionNodes.size())
829         return;
830     d->edit->setCursorPosition(d->sectionPos(index));
831 }
832 
833 /*!
834   \since 4.4
835 
836   \brief Returns the calendar widget for the editor if calendarPopup is
837   set to true and (sections() & DateSections_Mask) != 0.
838 
839   This function creates and returns a calendar widget if none has been set.
840 */
841 
842 
calendarWidget() const843 QCalendarWidget *QDateTimeEdit::calendarWidget() const
844 {
845     Q_D(const QDateTimeEdit);
846     if (!d->calendarPopup || !(d->sections & QDateTimeParser::DateSectionMask))
847         return nullptr;
848     if (!d->monthCalendar) {
849         const_cast<QDateTimeEditPrivate*>(d)->initCalendarPopup();
850     }
851     return d->monthCalendar->calendarWidget();
852 }
853 
854 /*!
855   \since 4.4
856 
857   Sets the given \a calendarWidget as the widget to be used for the calendar
858   pop-up. The editor does not automatically take ownership of the calendar widget.
859 
860   \note calendarPopup must be set to true before setting the calendar widget.
861   \sa calendarPopup
862 */
setCalendarWidget(QCalendarWidget * calendarWidget)863 void QDateTimeEdit::setCalendarWidget(QCalendarWidget *calendarWidget)
864 {
865     Q_D(QDateTimeEdit);
866     if (Q_UNLIKELY(!calendarWidget)) {
867         qWarning("QDateTimeEdit::setCalendarWidget: Cannot set a null calendar widget");
868         return;
869     }
870 
871     if (Q_UNLIKELY(!d->calendarPopup)) {
872         qWarning("QDateTimeEdit::setCalendarWidget: calendarPopup is set to false");
873         return;
874     }
875 
876     if (Q_UNLIKELY(!(d->display & QDateTimeParser::DateSectionMask))) {
877         qWarning("QDateTimeEdit::setCalendarWidget: no date sections specified");
878         return;
879     }
880     d->initCalendarPopup(calendarWidget);
881 }
882 
883 
884 /*!
885   \since 4.2
886 
887   Selects \a section. If \a section doesn't exist in the currently
888   displayed sections, this function does nothing. If \a section is
889   NoSection, this function will unselect all text in the editor.
890   Otherwise, this function will move the cursor and the current section
891   to the selected section.
892 
893   \sa currentSection()
894 */
895 
setSelectedSection(Section section)896 void QDateTimeEdit::setSelectedSection(Section section)
897 {
898     Q_D(QDateTimeEdit);
899     if (section == NoSection) {
900         d->edit->setSelection(d->edit->cursorPosition(), 0);
901     } else if (section & d->sections) {
902         if (currentSection() != section)
903             setCurrentSection(section);
904         d->setSelected(d->currentSectionIndex);
905     }
906 }
907 
908 
909 
910 /*!
911   \fn QString QDateTimeEdit::sectionText(Section section) const
912 
913   Returns the text from the given \a section.
914 
915   \sa currentSection()
916 */
917 
sectionText(Section section) const918 QString QDateTimeEdit::sectionText(Section section) const
919 {
920     Q_D(const QDateTimeEdit);
921     if (section == QDateTimeEdit::NoSection || !(section & d->sections)) {
922         return QString();
923     }
924 
925     d->updateCache(d->value, d->displayText());
926     const int sectionIndex = d->absoluteIndex(section, 0);
927     return d->sectionText(sectionIndex);
928 }
929 
930 /*!
931   \property QDateTimeEdit::displayFormat
932 
933   \brief The format used to display the time/date of the date time edit.
934 
935   This format is described in QDateTime::toString() and QDateTime::fromString()
936 
937   Example format strings (assuming that the date is 2nd of July 1969):
938 
939   \table
940   \header \li Format \li Result
941   \row \li dd.MM.yyyy \li 02.07.1969
942   \row \li MMM d yy \li Jul 2 69
943   \row \li MMMM d yy \li July 2 69
944   \endtable
945 
946   Note that if you specify a two digit year, it will be interpreted
947   to be in the century in which the date time edit was initialized.
948   The default century is the 21st (2000-2099).
949 
950   If you specify an invalid format the format will not be set.
951 
952   \sa QDateTime::toString(), displayedSections()
953 */
954 
displayFormat() const955 QString QDateTimeEdit::displayFormat() const
956 {
957     Q_D(const QDateTimeEdit);
958     return isRightToLeft() ? d->unreversedFormat : d->displayFormat;
959 }
960 
setDisplayFormat(const QString & format)961 void QDateTimeEdit::setDisplayFormat(const QString &format)
962 {
963     Q_D(QDateTimeEdit);
964     if (d->parseFormat(format)) {
965         d->unreversedFormat.clear();
966         if (isRightToLeft()) {
967             d->unreversedFormat = format;
968             d->displayFormat.clear();
969             for (int i=d->sectionNodes.size() - 1; i>=0; --i) {
970                 d->displayFormat += d->separators.at(i + 1);
971                 d->displayFormat += d->sectionNode(i).format();
972             }
973             d->displayFormat += d->separators.at(0);
974             std::reverse(d->separators.begin(), d->separators.end());
975             std::reverse(d->sectionNodes.begin(), d->sectionNodes.end());
976         }
977 
978         d->formatExplicitlySet = true;
979         d->sections = QDateTimeEditPrivate::convertSections(d->display);
980         d->clearCache();
981 
982         d->currentSectionIndex = qMin(d->currentSectionIndex, d->sectionNodes.size() - 1);
983         const bool timeShown = (d->sections & TimeSections_Mask);
984         const bool dateShown = (d->sections & DateSections_Mask);
985         Q_ASSERT(dateShown || timeShown);
986         if (timeShown && !dateShown) {
987             QTime time = d->value.toTime();
988             setDateRange(d->value.toDate(), d->value.toDate());
989             if (d->minimum.toTime() >= d->maximum.toTime()) {
990                 setTimeRange(QDATETIMEEDIT_TIME_MIN, QDATETIMEEDIT_TIME_MAX);
991                 // if the time range became invalid during the adjustment, the time would have been reset
992                 setTime(time);
993             }
994         } else if (dateShown && !timeShown) {
995             setTimeRange(QDATETIMEEDIT_TIME_MIN, QDATETIMEEDIT_TIME_MAX);
996             d->value = QDateTime(d->value.toDate(), QTime(), d->spec);
997         }
998         d->updateEdit();
999         d->_q_editorCursorPositionChanged(-1, 0);
1000     }
1001 }
1002 
1003 /*!
1004     \property QDateTimeEdit::calendarPopup
1005     \brief The current calendar pop-up show mode.
1006     \since 4.2
1007 
1008     The calendar pop-up will be shown upon clicking the arrow button.
1009     This property is valid only if there is a valid date display format.
1010 
1011     \sa setDisplayFormat()
1012 */
1013 
calendarPopup() const1014 bool QDateTimeEdit::calendarPopup() const
1015 {
1016     Q_D(const QDateTimeEdit);
1017     return d->calendarPopup;
1018 }
1019 
setCalendarPopup(bool enable)1020 void QDateTimeEdit::setCalendarPopup(bool enable)
1021 {
1022     Q_D(QDateTimeEdit);
1023     if (enable == d->calendarPopup)
1024         return;
1025     setAttribute(Qt::WA_MacShowFocusRect, !enable);
1026     d->calendarPopup = enable;
1027 #ifdef QT_KEYPAD_NAVIGATION
1028     if (!enable)
1029         d->focusOnButton = false;
1030 #endif
1031     d->updateEditFieldGeometry();
1032     update();
1033 }
1034 
1035 /*!
1036     \property QDateTimeEdit::timeSpec
1037     \brief The current timespec used by the date time edit.
1038     \since 4.4
1039 */
1040 
timeSpec() const1041 Qt::TimeSpec QDateTimeEdit::timeSpec() const
1042 {
1043     Q_D(const QDateTimeEdit);
1044     return d->spec;
1045 }
1046 
setTimeSpec(Qt::TimeSpec spec)1047 void QDateTimeEdit::setTimeSpec(Qt::TimeSpec spec)
1048 {
1049     Q_D(QDateTimeEdit);
1050     if (spec != d->spec) {
1051         d->spec = spec;
1052         d->updateTimeSpec();
1053     }
1054 }
1055 
1056 /*!
1057   \reimp
1058 */
1059 
sizeHint() const1060 QSize QDateTimeEdit::sizeHint() const
1061 {
1062     Q_D(const QDateTimeEdit);
1063     if (d->cachedSizeHint.isEmpty()) {
1064         ensurePolished();
1065 
1066         const QFontMetrics fm(fontMetrics());
1067         int h = d->edit->sizeHint().height();
1068         int w = 0;
1069         QString s;
1070         s = d->textFromValue(d->minimum) + QLatin1Char(' ');
1071         w = qMax<int>(w, fm.horizontalAdvance(s));
1072         s = d->textFromValue(d->maximum) + QLatin1Char(' ');
1073         w = qMax<int>(w, fm.horizontalAdvance(s));
1074         if (d->specialValueText.size()) {
1075             s = d->specialValueText;
1076             w = qMax<int>(w, fm.horizontalAdvance(s));
1077         }
1078         w += 2; // cursor blinking space
1079 
1080         QSize hint(w, h);
1081 
1082 #ifdef Q_OS_MAC
1083         if (d->calendarPopupEnabled()) {
1084             QStyleOptionComboBox opt;
1085             d->cachedSizeHint = style()->sizeFromContents(QStyle::CT_ComboBox, &opt, hint, this);
1086         } else
1087 #endif
1088         {
1089             QStyleOptionSpinBox opt;
1090             initStyleOption(&opt);
1091             d->cachedSizeHint = style()->sizeFromContents(QStyle::CT_SpinBox, &opt, hint, this)
1092                                 .expandedTo(QApplication::globalStrut());
1093         }
1094 
1095         d->cachedMinimumSizeHint = d->cachedSizeHint;
1096         // essentially make minimumSizeHint return the same as sizeHint for datetimeedits
1097     }
1098     return d->cachedSizeHint;
1099 }
1100 
1101 
1102 /*!
1103   \reimp
1104 */
1105 
event(QEvent * event)1106 bool QDateTimeEdit::event(QEvent *event)
1107 {
1108     Q_D(QDateTimeEdit);
1109     switch (event->type()) {
1110     case QEvent::ApplicationLayoutDirectionChange: {
1111         const bool was = d->formatExplicitlySet;
1112         const QString oldFormat = d->displayFormat;
1113         d->displayFormat.clear();
1114         setDisplayFormat(oldFormat);
1115         d->formatExplicitlySet = was;
1116         break; }
1117     case QEvent::LocaleChange:
1118         d->updateEdit();
1119         break;
1120     case QEvent::StyleChange:
1121 #ifdef Q_OS_MAC
1122     case QEvent::MacSizeChange:
1123 #endif
1124         d->setLayoutItemMargins(QStyle::SE_DateTimeEditLayoutItem);
1125         break;
1126     default:
1127         break;
1128     }
1129     return QAbstractSpinBox::event(event);
1130 }
1131 
1132 /*!
1133   \reimp
1134 */
1135 
clear()1136 void QDateTimeEdit::clear()
1137 {
1138     Q_D(QDateTimeEdit);
1139     d->clearSection(d->currentSectionIndex);
1140 }
1141 /*!
1142   \reimp
1143 */
1144 
keyPressEvent(QKeyEvent * event)1145 void QDateTimeEdit::keyPressEvent(QKeyEvent *event)
1146 {
1147     Q_D(QDateTimeEdit);
1148     int oldCurrent = d->currentSectionIndex;
1149     bool select = true;
1150     bool inserted = false;
1151 
1152     switch (event->key()) {
1153 #ifdef QT_KEYPAD_NAVIGATION
1154     case Qt::Key_NumberSign:    //shortcut to popup calendar
1155         if (QApplicationPrivate::keypadNavigationEnabled() && d->calendarPopupEnabled()) {
1156             d->initCalendarPopup();
1157             d->positionCalendarPopup();
1158             d->monthCalendar->show();
1159             return;
1160         }
1161         break;
1162     case Qt::Key_Select:
1163         if (QApplicationPrivate::keypadNavigationEnabled()) {
1164             if (hasEditFocus()) {
1165                 if (d->focusOnButton) {
1166                     d->initCalendarPopup();
1167                     d->positionCalendarPopup();
1168                     d->monthCalendar->show();
1169                     d->focusOnButton = false;
1170                     return;
1171                 }
1172                 setEditFocus(false);
1173                 selectAll();
1174             } else {
1175                 setEditFocus(true);
1176 
1177                 //hide cursor
1178                 d->edit->d_func()->setCursorVisible(false);
1179                 d->edit->d_func()->control->setBlinkingCursorEnabled(false);
1180                 d->setSelected(0);
1181             }
1182         }
1183         return;
1184 #endif
1185     case Qt::Key_Enter:
1186     case Qt::Key_Return:
1187         d->interpret(AlwaysEmit);
1188         d->setSelected(d->currentSectionIndex, true);
1189         event->ignore();
1190         emit editingFinished();
1191         emit d->edit->returnPressed();
1192         return;
1193     default:
1194 #ifdef QT_KEYPAD_NAVIGATION
1195         if (QApplicationPrivate::keypadNavigationEnabled() && !hasEditFocus()
1196             && !event->text().isEmpty() && event->text().at(0).isLetterOrNumber()) {
1197             setEditFocus(true);
1198 
1199             //hide cursor
1200             d->edit->d_func()->setCursorVisible(false);
1201             d->edit->d_func()->control->setBlinkingCursorEnabled(false);
1202             d->setSelected(0);
1203             oldCurrent = 0;
1204         }
1205 #endif
1206         if (!d->isSeparatorKey(event)) {
1207             inserted = select = !event->text().isEmpty() && event->text().at(0).isPrint()
1208                        && !(event->modifiers() & ~(Qt::ShiftModifier|Qt::KeypadModifier));
1209             break;
1210         }
1211         Q_FALLTHROUGH();
1212     case Qt::Key_Left:
1213     case Qt::Key_Right:
1214         if (event->key() == Qt::Key_Left || event->key() == Qt::Key_Right) {
1215             if (
1216 #ifdef QT_KEYPAD_NAVIGATION
1217                 QApplicationPrivate::keypadNavigationEnabled() && !hasEditFocus()
1218                 || !QApplicationPrivate::keypadNavigationEnabled() &&
1219 #endif
1220                 !(event->modifiers() & Qt::ControlModifier)) {
1221                 select = false;
1222                 break;
1223             }
1224         }
1225         Q_FALLTHROUGH();
1226     case Qt::Key_Backtab:
1227     case Qt::Key_Tab: {
1228         event->accept();
1229         if (d->specialValue()) {
1230             d->edit->setSelection(d->edit->cursorPosition(), 0);
1231             return;
1232         }
1233         const bool forward = event->key() != Qt::Key_Left && event->key() != Qt::Key_Backtab
1234                              && (event->key() != Qt::Key_Tab || !(event->modifiers() & Qt::ShiftModifier));
1235 #ifdef QT_KEYPAD_NAVIGATION
1236         int newSection = d->nextPrevSection(d->currentSectionIndex, forward);
1237         if (QApplicationPrivate::keypadNavigationEnabled()) {
1238             if (d->focusOnButton) {
1239                 newSection = forward ? 0 : d->sectionNodes.size() - 1;
1240                 d->focusOnButton = false;
1241                 update();
1242             } else if (newSection < 0 && select && d->calendarPopupEnabled()) {
1243                 setSelectedSection(NoSection);
1244                 d->focusOnButton = true;
1245                 update();
1246                 return;
1247             }
1248         }
1249         // only allow date/time sections to be selected.
1250         if (newSection & ~(QDateTimeParser::TimeSectionMask | QDateTimeParser::DateSectionMask))
1251             return;
1252 #endif
1253         //key tab and backtab will be managed thrgout QWidget::event
1254         if (event->key() != Qt::Key_Backtab && event->key() != Qt::Key_Tab)
1255             focusNextPrevChild(forward);
1256 
1257         return; }
1258     }
1259     QAbstractSpinBox::keyPressEvent(event);
1260     if (select && !d->edit->hasSelectedText()) {
1261         if (inserted && d->sectionAt(d->edit->cursorPosition()) == QDateTimeParser::NoSectionIndex) {
1262             QString str = d->displayText();
1263             int pos = d->edit->cursorPosition();
1264             if (validate(str, pos) == QValidator::Acceptable
1265                 && (d->sectionNodes.at(oldCurrent).count != 1
1266                     || d->sectionMaxSize(oldCurrent) == d->sectionSize(oldCurrent)
1267                     || d->skipToNextSection(oldCurrent, d->value.toDateTime(), d->sectionText(oldCurrent)))) {
1268                 QDTEDEBUG << "Setting currentsection to"
1269                           << d->closestSection(d->edit->cursorPosition(), true) << event->key()
1270                           << oldCurrent << str;
1271                 const int tmp = d->closestSection(d->edit->cursorPosition(), true);
1272                 if (tmp >= 0)
1273                     d->currentSectionIndex = tmp;
1274             }
1275         }
1276         if (d->currentSectionIndex != oldCurrent) {
1277             d->setSelected(d->currentSectionIndex);
1278         }
1279     }
1280     if (d->specialValue()) {
1281         d->edit->setSelection(d->edit->cursorPosition(), 0);
1282     }
1283 }
1284 
1285 /*!
1286   \reimp
1287 */
1288 
1289 #if QT_CONFIG(wheelevent)
wheelEvent(QWheelEvent * event)1290 void QDateTimeEdit::wheelEvent(QWheelEvent *event)
1291 {
1292     QAbstractSpinBox::wheelEvent(event);
1293 }
1294 #endif
1295 
1296 /*!
1297   \reimp
1298 */
1299 
focusInEvent(QFocusEvent * event)1300 void QDateTimeEdit::focusInEvent(QFocusEvent *event)
1301 {
1302     Q_D(QDateTimeEdit);
1303     QAbstractSpinBox::focusInEvent(event);
1304     QString *frm = nullptr;
1305     const int oldPos = d->edit->cursorPosition();
1306     if (!d->formatExplicitlySet) {
1307         if (d->displayFormat == d->defaultTimeFormat) {
1308             frm = &d->defaultTimeFormat;
1309         } else if (d->displayFormat == d->defaultDateFormat) {
1310             frm = &d->defaultDateFormat;
1311         } else if (d->displayFormat == d->defaultDateTimeFormat) {
1312             frm = &d->defaultDateTimeFormat;
1313         }
1314 
1315         if (frm) {
1316             d->readLocaleSettings();
1317             if (d->displayFormat != *frm) {
1318                 setDisplayFormat(*frm);
1319                 d->formatExplicitlySet = false;
1320                 d->edit->setCursorPosition(oldPos);
1321             }
1322         }
1323     }
1324     const bool oldHasHadFocus = d->hasHadFocus;
1325     d->hasHadFocus = true;
1326     bool first = true;
1327     switch (event->reason()) {
1328     case Qt::BacktabFocusReason:
1329         first = false;
1330         break;
1331     case Qt::MouseFocusReason:
1332     case Qt::PopupFocusReason:
1333         return;
1334     case Qt::ActiveWindowFocusReason:
1335         if (oldHasHadFocus)
1336             return;
1337     case Qt::ShortcutFocusReason:
1338     case Qt::TabFocusReason:
1339     default:
1340         break;
1341     }
1342     if (isRightToLeft())
1343         first = !first;
1344     d->updateEdit(); // needed to make it update specialValueText
1345 
1346     d->setSelected(first ? 0 : d->sectionNodes.size() - 1);
1347 }
1348 
1349 /*!
1350   \reimp
1351 */
1352 
focusNextPrevChild(bool next)1353 bool QDateTimeEdit::focusNextPrevChild(bool next)
1354 {
1355     Q_D(QDateTimeEdit);
1356     const int newSection = d->nextPrevSection(d->currentSectionIndex, next);
1357     switch (d->sectionType(newSection)) {
1358     case QDateTimeParser::NoSection:
1359     case QDateTimeParser::FirstSection:
1360     case QDateTimeParser::LastSection:
1361         return QAbstractSpinBox::focusNextPrevChild(next);
1362     default:
1363         d->edit->deselect();
1364         d->edit->setCursorPosition(d->sectionPos(newSection));
1365         QDTEDEBUG << d->sectionPos(newSection);
1366         d->setSelected(newSection, true);
1367         return false;
1368     }
1369 }
1370 
1371 /*!
1372   \reimp
1373 */
1374 
stepBy(int steps)1375 void QDateTimeEdit::stepBy(int steps)
1376 {
1377     Q_D(QDateTimeEdit);
1378 #ifdef QT_KEYPAD_NAVIGATION
1379     // with keypad navigation and not editFocus, left right change the date/time by a fixed amount.
1380     if (QApplicationPrivate::keypadNavigationEnabled() && !hasEditFocus()) {
1381         // if date based, shift by day.  else shift by 15min
1382         if (d->sections & DateSections_Mask) {
1383             setDateTime(dateTime().addDays(steps));
1384         } else {
1385             int minutes = time().hour()*60 + time().minute();
1386             int blocks = minutes/15;
1387             blocks += steps;
1388             /* rounding involved */
1389             if (minutes % 15) {
1390                 if (steps < 0) {
1391                     blocks += 1; // do one less step;
1392                 }
1393             }
1394 
1395             minutes = blocks * 15;
1396 
1397             /* need to take wrapping into account */
1398             if (!d->wrapping) {
1399                 int max_minutes = d->maximum.toTime().hour()*60 + d->maximum.toTime().minute();
1400                 int min_minutes = d->minimum.toTime().hour()*60 + d->minimum.toTime().minute();
1401 
1402                 if (minutes >= max_minutes) {
1403                     setTime(maximumTime());
1404                     return;
1405                 } else if (minutes <= min_minutes) {
1406                     setTime(minimumTime());
1407                     return;
1408                 }
1409             }
1410             setTime(QTime(minutes/60, minutes%60));
1411         }
1412         return;
1413     }
1414 #endif
1415     // don't optimize away steps == 0. This is the only way to select
1416     // the currentSection in Qt 4.1.x
1417     if (d->specialValue() && displayedSections() != AmPmSection) {
1418         for (int i=0; i<d->sectionNodes.size(); ++i) {
1419             if (d->sectionType(i) != QDateTimeParser::AmPmSection) {
1420                 d->currentSectionIndex = i;
1421                 break;
1422             }
1423         }
1424     }
1425     d->setValue(d->stepBy(d->currentSectionIndex, steps, false), EmitIfChanged);
1426     d->updateCache(d->value, d->displayText());
1427 
1428     d->setSelected(d->currentSectionIndex);
1429     d->updateTimeSpec();
1430 }
1431 
1432 /*!
1433   This virtual function is used by the date time edit whenever it
1434   needs to display \a dateTime.
1435 
1436   If you reimplement this, you may also need to reimplement validate().
1437 
1438   \sa dateTimeFromText(), validate()
1439 */
textFromDateTime(const QDateTime & dateTime) const1440 QString QDateTimeEdit::textFromDateTime(const QDateTime &dateTime) const
1441 {
1442     Q_D(const QDateTimeEdit);
1443     return locale().toString(dateTime, d->displayFormat, d->calendar);
1444 }
1445 
1446 
1447 /*!
1448   Returns an appropriate datetime for the given \a text.
1449 
1450   This virtual function is used by the datetime edit whenever it
1451   needs to interpret text entered by the user as a value.
1452 
1453   \sa textFromDateTime(), validate()
1454 */
dateTimeFromText(const QString & text) const1455 QDateTime QDateTimeEdit::dateTimeFromText(const QString &text) const
1456 {
1457     Q_D(const QDateTimeEdit);
1458     QString copy = text;
1459     int pos = d->edit->cursorPosition();
1460     QValidator::State state = QValidator::Acceptable;
1461     return d->validateAndInterpret(copy, pos, state);
1462 }
1463 
1464 /*!
1465   \reimp
1466 */
1467 
validate(QString & text,int & pos) const1468 QValidator::State QDateTimeEdit::validate(QString &text, int &pos) const
1469 {
1470     Q_D(const QDateTimeEdit);
1471     QValidator::State state;
1472     d->validateAndInterpret(text, pos, state);
1473     return state;
1474 }
1475 
1476 /*!
1477   \reimp
1478 */
1479 
1480 
fixup(QString & input) const1481 void QDateTimeEdit::fixup(QString &input) const
1482 {
1483     Q_D(const QDateTimeEdit);
1484     QValidator::State state;
1485     int copy = d->edit->cursorPosition();
1486 
1487     QDateTime value = d->validateAndInterpret(input, copy, state, true);
1488     /*
1489         String was valid, but the datetime still is not; use the time that
1490         has the same distance from epoch.
1491         CorrectToPreviousValue correction is handled by QAbstractSpinBox.
1492     */
1493     if (!value.isValid() && d->correctionMode == QAbstractSpinBox::CorrectToNearestValue) {
1494         value = QDateTime::fromMSecsSinceEpoch(value.toMSecsSinceEpoch(), value.timeSpec());
1495         input = textFromDateTime(value);
1496     }
1497 }
1498 
1499 
1500 /*!
1501   \reimp
1502 */
1503 
stepEnabled() const1504 QDateTimeEdit::StepEnabled QDateTimeEdit::stepEnabled() const
1505 {
1506     Q_D(const QDateTimeEdit);
1507     if (d->readOnly)
1508         return {};
1509     if (d->specialValue()) {
1510         return (d->minimum == d->maximum ? StepEnabled{} : StepEnabled(StepUpEnabled));
1511     }
1512 
1513     QAbstractSpinBox::StepEnabled ret = { };
1514 
1515 #ifdef QT_KEYPAD_NAVIGATION
1516     if (QApplicationPrivate::keypadNavigationEnabled() && !hasEditFocus()) {
1517         if (d->wrapping)
1518             return StepEnabled(StepUpEnabled | StepDownEnabled);
1519         // 3 cases.  date, time, datetime.  each case look
1520         // at just the relavant component.
1521         QVariant max, min, val;
1522         if (!(d->sections & DateSections_Mask)) {
1523             // time only, no date
1524             max = d->maximum.toTime();
1525             min = d->minimum.toTime();
1526             val = d->value.toTime();
1527         } else if (!(d->sections & TimeSections_Mask)) {
1528             // date only, no time
1529             max = d->maximum.toDate();
1530             min = d->minimum.toDate();
1531             val = d->value.toDate();
1532         } else {
1533             // both
1534             max = d->maximum;
1535             min = d->minimum;
1536             val = d->value;
1537         }
1538         if (val != min)
1539             ret |= QAbstractSpinBox::StepDownEnabled;
1540         if (val != max)
1541             ret |= QAbstractSpinBox::StepUpEnabled;
1542         return ret;
1543     }
1544 #endif
1545     switch (d->sectionType(d->currentSectionIndex)) {
1546     case QDateTimeParser::NoSection:
1547     case QDateTimeParser::FirstSection:
1548     case QDateTimeParser::LastSection: return { };
1549     default: break;
1550     }
1551     if (d->wrapping)
1552         return StepEnabled(StepDownEnabled|StepUpEnabled);
1553 
1554     QVariant v = d->stepBy(d->currentSectionIndex, 1, true);
1555     if (v != d->value) {
1556         ret |= QAbstractSpinBox::StepUpEnabled;
1557     }
1558     v = d->stepBy(d->currentSectionIndex, -1, true);
1559     if (v != d->value) {
1560         ret |= QAbstractSpinBox::StepDownEnabled;
1561     }
1562 
1563     return ret;
1564 }
1565 
1566 
1567 /*!
1568   \reimp
1569 */
1570 
mousePressEvent(QMouseEvent * event)1571 void QDateTimeEdit::mousePressEvent(QMouseEvent *event)
1572 {
1573     Q_D(QDateTimeEdit);
1574     if (!d->calendarPopupEnabled()) {
1575         QAbstractSpinBox::mousePressEvent(event);
1576         return;
1577     }
1578     d->updateHoverControl(event->pos());
1579     if (d->hoverControl == QStyle::SC_ComboBoxArrow) {
1580         event->accept();
1581         if (d->readOnly) {
1582             return;
1583         }
1584         d->updateArrow(QStyle::State_Sunken);
1585         d->initCalendarPopup();
1586         d->positionCalendarPopup();
1587         //Show the calendar
1588         d->monthCalendar->show();
1589     } else {
1590         QAbstractSpinBox::mousePressEvent(event);
1591     }
1592 }
1593 
1594 /*!
1595   \class QTimeEdit
1596   \brief The QTimeEdit class provides a widget for editing times based on
1597   the QDateTimeEdit widget.
1598 
1599   \ingroup basicwidgets
1600   \inmodule QtWidgets
1601 
1602   \image windows-timeedit.png
1603 
1604   Many of the properties and functions provided by QTimeEdit are implemented in
1605   QDateTimeEdit. These are the relevant properties of this class:
1606 
1607   \list
1608   \li \l{QDateTimeEdit::time}{time} holds the time displayed by the widget.
1609   \li \l{QDateTimeEdit::minimumTime}{minimumTime} defines the minimum (earliest) time
1610      that can be set by the user.
1611   \li \l{QDateTimeEdit::maximumTime}{maximumTime} defines the maximum (latest) time
1612      that can be set by the user.
1613   \li \l{QDateTimeEdit::displayFormat}{displayFormat} contains a string that is used
1614      to format the time displayed in the widget.
1615   \endlist
1616 
1617   \sa QDateEdit, QDateTimeEdit
1618 */
1619 
1620 /*!
1621   Constructs an empty time editor with a \a parent.
1622 */
1623 
1624 
QTimeEdit(QWidget * parent)1625 QTimeEdit::QTimeEdit(QWidget *parent)
1626     : QDateTimeEdit(QDATETIMEEDIT_TIME_MIN, QMetaType::QTime, parent)
1627 {
1628     connect(this, &QTimeEdit::timeChanged, this, &QTimeEdit::userTimeChanged);
1629 }
1630 
1631 /*!
1632   Constructs an empty time editor with a \a parent. The time is set
1633   to \a time.
1634 */
1635 
QTimeEdit(const QTime & time,QWidget * parent)1636 QTimeEdit::QTimeEdit(const QTime &time, QWidget *parent)
1637     : QDateTimeEdit(time, QMetaType::QTime, parent)
1638 {
1639     connect(this, &QTimeEdit::timeChanged, this, &QTimeEdit::userTimeChanged);
1640 }
1641 
1642 /*!
1643   Destructor.
1644 */
~QTimeEdit()1645 QTimeEdit::~QTimeEdit()
1646 {
1647 }
1648 
1649 /*!
1650   \property QTimeEdit::time
1651   \internal
1652   \sa QDateTimeEdit::time
1653 */
1654 
1655 /*!
1656   \fn void QTimeEdit::userTimeChanged(const QTime &time)
1657 
1658   This signal only exists to fully implement the time Q_PROPERTY on the class.
1659   Normally timeChanged should be used instead.
1660 
1661   \internal
1662 */
1663 
1664 
1665 /*!
1666   \class QDateEdit
1667   \brief The QDateEdit class provides a widget for editing dates based on
1668   the QDateTimeEdit widget.
1669 
1670   \ingroup basicwidgets
1671   \inmodule QtWidgets
1672 
1673   \image windows-dateedit.png
1674 
1675   Many of the properties and functions provided by QDateEdit are implemented in
1676   QDateTimeEdit. These are the relevant properties of this class:
1677 
1678   \list
1679   \li \l{QDateTimeEdit::date}{date} holds the date displayed by the widget.
1680   \li \l{QDateTimeEdit::minimumDate}{minimumDate} defines the minimum (earliest)
1681      date that can be set by the user.
1682   \li \l{QDateTimeEdit::maximumDate}{maximumDate} defines the maximum (latest) date
1683      that can be set by the user.
1684   \li \l{QDateTimeEdit::displayFormat}{displayFormat} contains a string that is used
1685      to format the date displayed in the widget.
1686   \endlist
1687 
1688   \sa QTimeEdit, QDateTimeEdit
1689 */
1690 
1691 /*!
1692   Constructs an empty date editor with a \a parent.
1693 */
1694 
QDateEdit(QWidget * parent)1695 QDateEdit::QDateEdit(QWidget *parent)
1696     : QDateTimeEdit(QDATETIMEEDIT_DATE_INITIAL, QMetaType::QDate, parent)
1697 {
1698     connect(this, &QDateEdit::dateChanged, this, &QDateEdit::userDateChanged);
1699 }
1700 
1701 /*!
1702   Constructs an empty date editor with a \a parent. The date is set
1703   to \a date.
1704 */
1705 
QDateEdit(const QDate & date,QWidget * parent)1706 QDateEdit::QDateEdit(const QDate &date, QWidget *parent)
1707     : QDateTimeEdit(date, QMetaType::QDate, parent)
1708 {
1709     connect(this, &QDateEdit::dateChanged, this, &QDateEdit::userDateChanged);
1710 }
1711 
1712 /*!
1713   Destructor.
1714 */
~QDateEdit()1715 QDateEdit::~QDateEdit()
1716 {
1717 }
1718 
1719 /*!
1720   \property QDateEdit::date
1721   \internal
1722   \sa QDateTimeEdit::date
1723 */
1724 
1725 /*!
1726   \fn void QDateEdit::userDateChanged(const QDate &date)
1727 
1728   This signal only exists to fully implement the date Q_PROPERTY on the class.
1729   Normally dateChanged should be used instead.
1730 
1731   \internal
1732 */
1733 
1734 
1735 // --- QDateTimeEditPrivate ---
1736 
1737 /*!
1738   \internal
1739   Constructs a QDateTimeEditPrivate object
1740 */
1741 
1742 
QDateTimeEditPrivate()1743 QDateTimeEditPrivate::QDateTimeEditPrivate()
1744     : QDateTimeParser(QMetaType::QDateTime, QDateTimeParser::DateTimeEdit, QCalendar())
1745 {
1746     hasHadFocus = false;
1747     formatExplicitlySet = false;
1748     cacheGuard = false;
1749     fixday = true;
1750     type = QMetaType::QDateTime;
1751     sections = { };
1752     cachedDay = -1;
1753     currentSectionIndex = FirstSectionIndex;
1754 
1755     first.pos = 0;
1756     calendarPopup = false;
1757     minimum = QDATETIMEEDIT_COMPAT_DATE_MIN.startOfDay();
1758     maximum = QDATETIMEEDIT_DATE_MAX.endOfDay();
1759     arrowState = QStyle::State_None;
1760     monthCalendar = nullptr;
1761     readLocaleSettings();
1762 
1763 #ifdef QT_KEYPAD_NAVIGATION
1764     focusOnButton = false;
1765 #endif
1766 }
1767 
updateTimeSpec()1768 void QDateTimeEditPrivate::updateTimeSpec()
1769 {
1770     minimum = minimum.toDateTime().toTimeSpec(spec);
1771     maximum = maximum.toDateTime().toTimeSpec(spec);
1772     value = value.toDateTime().toTimeSpec(spec);
1773 
1774     // time zone changes can lead to 00:00:00 becomes 01:00:00 and 23:59:59 becomes 00:59:59 (invalid range)
1775     const bool dateShown = (sections & QDateTimeEdit::DateSections_Mask);
1776     if (!dateShown) {
1777         if (minimum.toTime() >= maximum.toTime()){
1778             minimum = value.toDate().startOfDay(spec);
1779             maximum = value.toDate().endOfDay(spec);
1780         }
1781     }
1782 }
1783 
updateEdit()1784 void QDateTimeEditPrivate::updateEdit()
1785 {
1786     const QString newText = (specialValue() ? specialValueText : textFromValue(value));
1787     if (newText == displayText())
1788         return;
1789     int selsize = edit->selectedText().size();
1790     const QSignalBlocker blocker(edit);
1791 
1792     edit->setText(newText);
1793 
1794     if (!specialValue()
1795 #ifdef QT_KEYPAD_NAVIGATION
1796         && !(QApplicationPrivate::keypadNavigationEnabled() && !edit->hasEditFocus())
1797 #endif
1798             ) {
1799         int cursor = sectionPos(currentSectionIndex);
1800         QDTEDEBUG << "cursor is " << cursor << currentSectionIndex;
1801         cursor = qBound(0, cursor, displayText().size());
1802         QDTEDEBUG << cursor;
1803         if (selsize > 0) {
1804             edit->setSelection(cursor, selsize);
1805             QDTEDEBUG << cursor << selsize;
1806         } else {
1807             edit->setCursorPosition(cursor);
1808             QDTEDEBUG << cursor;
1809 
1810         }
1811     }
1812 }
1813 
1814 
1815 /*!
1816   \internal
1817 
1818   Selects the section \a s. If \a forward is false selects backwards.
1819 */
1820 
setSelected(int sectionIndex,bool forward)1821 void QDateTimeEditPrivate::setSelected(int sectionIndex, bool forward)
1822 {
1823     if (specialValue()
1824 #ifdef QT_KEYPAD_NAVIGATION
1825         || (QApplicationPrivate::keypadNavigationEnabled() && !edit->hasEditFocus())
1826 #endif
1827         ) {
1828         edit->selectAll();
1829     } else {
1830         const SectionNode &node = sectionNode(sectionIndex);
1831         if (node.type == NoSection || node.type == LastSection || node.type == FirstSection)
1832             return;
1833 
1834         updateCache(value, displayText());
1835         const int size = sectionSize(sectionIndex);
1836         if (forward) {
1837             edit->setSelection(sectionPos(node), size);
1838         } else {
1839             edit->setSelection(sectionPos(node) + size, -size);
1840         }
1841     }
1842 }
1843 
1844 /*!
1845   \internal
1846 
1847   Returns the section at index \a index or NoSection if there are no sections there.
1848 */
1849 
sectionAt(int pos) const1850 int QDateTimeEditPrivate::sectionAt(int pos) const
1851 {
1852     if (pos < separators.first().size())
1853         return (pos == 0 ? FirstSectionIndex : NoSectionIndex);
1854 
1855     const QString text = displayText();
1856     const int textSize = text.size();
1857     if (textSize - pos < separators.last().size() + 1) {
1858         if (separators.last().size() == 0) {
1859             return sectionNodes.count() - 1;
1860         }
1861         return (pos == textSize ? LastSectionIndex : NoSectionIndex);
1862     }
1863     updateCache(value, text);
1864 
1865     for (int i=0; i<sectionNodes.size(); ++i) {
1866         const int tmp = sectionPos(i);
1867         if (pos < tmp + sectionSize(i)) {
1868             return (pos < tmp ? -1 : i);
1869         }
1870     }
1871     return -1;
1872 }
1873 
1874 /*!
1875   \internal
1876 
1877   Returns the closest section of index \a index. Searches forward
1878   for a section if \a forward is true. Otherwise searches backwards.
1879 */
1880 
closestSection(int pos,bool forward) const1881 int QDateTimeEditPrivate::closestSection(int pos, bool forward) const
1882 {
1883     Q_ASSERT(pos >= 0);
1884     if (pos < separators.first().size())
1885         return forward ? 0 : FirstSectionIndex;
1886 
1887     const QString text = displayText();
1888     if (text.size() - pos < separators.last().size() + 1)
1889         return forward ? LastSectionIndex : sectionNodes.size() - 1;
1890 
1891     updateCache(value, text);
1892     for (int i=0; i<sectionNodes.size(); ++i) {
1893         const int tmp = sectionPos(sectionNodes.at(i));
1894         if (pos < tmp + sectionSize(i)) {
1895             if (pos < tmp && !forward) {
1896                 return i-1;
1897             }
1898             return i;
1899         } else if (i == sectionNodes.size() - 1 && pos > tmp) {
1900             return i;
1901         }
1902     }
1903     qWarning("QDateTimeEdit: Internal Error: closestSection returned NoSection");
1904     return NoSectionIndex;
1905 }
1906 
1907 /*!
1908   \internal
1909 
1910   Returns a copy of the section that is before or after \a current, depending on \a forward.
1911 */
1912 
nextPrevSection(int current,bool forward) const1913 int QDateTimeEditPrivate::nextPrevSection(int current, bool forward) const
1914 {
1915     Q_Q(const QDateTimeEdit);
1916     if (q->isRightToLeft())
1917         forward = !forward;
1918 
1919     switch (current) {
1920     case FirstSectionIndex: return forward ? 0 : FirstSectionIndex;
1921     case LastSectionIndex: return (forward ? LastSectionIndex : sectionNodes.size() - 1);
1922     case NoSectionIndex: return FirstSectionIndex;
1923     default: break;
1924     }
1925     Q_ASSERT(current >= 0 && current < sectionNodes.size());
1926 
1927     current += (forward ? 1 : -1);
1928     if (current >= sectionNodes.size()) {
1929         return LastSectionIndex;
1930     } else if (current < 0) {
1931         return FirstSectionIndex;
1932     }
1933 
1934     return current;
1935 }
1936 
1937 /*!
1938   \internal
1939 
1940   Clears the text of section \a s.
1941 */
1942 
clearSection(int index)1943 void QDateTimeEditPrivate::clearSection(int index)
1944 {
1945     const QLatin1Char space(' ');
1946     int cursorPos = edit->cursorPosition();
1947     const QSignalBlocker blocker(edit);
1948     QString t = edit->text();
1949     const int pos = sectionPos(index);
1950     if (Q_UNLIKELY(pos == -1)) {
1951         qWarning("QDateTimeEdit: Internal error (%s:%d)", __FILE__, __LINE__);
1952         return;
1953     }
1954     const int size = sectionSize(index);
1955     t.replace(pos, size, QString().fill(space, size));
1956     edit->setText(t);
1957     edit->setCursorPosition(cursorPos);
1958     QDTEDEBUG << cursorPos;
1959 }
1960 
1961 
1962 /*!
1963   \internal
1964 
1965   updates the cached values
1966 */
1967 
updateCache(const QVariant & val,const QString & str) const1968 void QDateTimeEditPrivate::updateCache(const QVariant &val, const QString &str) const
1969 {
1970     if (val != cachedValue || str != cachedText || cacheGuard) {
1971         cacheGuard = true;
1972         QString copy = str;
1973         int unused = edit->cursorPosition();
1974         QValidator::State unusedState;
1975         validateAndInterpret(copy, unused, unusedState);
1976         cacheGuard = false;
1977     }
1978 }
1979 
1980 /*!
1981   \internal
1982 
1983   parses and validates \a input
1984 */
1985 
validateAndInterpret(QString & input,int & position,QValidator::State & state,bool fixup) const1986 QDateTime QDateTimeEditPrivate::validateAndInterpret(QString &input, int &position,
1987                                                      QValidator::State &state, bool fixup) const
1988 {
1989     if (input.isEmpty()) {
1990         if (sectionNodes.size() == 1 || !specialValueText.isEmpty()) {
1991             state = QValidator::Intermediate;
1992         } else {
1993             state = QValidator::Invalid;
1994         }
1995         return getZeroVariant().toDateTime();
1996     } else if (cachedText == input && !fixup) {
1997         state = cachedState;
1998         return cachedValue.toDateTime();
1999     } else if (!specialValueText.isEmpty()) {
2000         bool changeCase = false;
2001         const int max = qMin(specialValueText.size(), input.size());
2002         int i;
2003         for (i=0; i<max; ++i) {
2004             const QChar ic = input.at(i);
2005             const QChar sc = specialValueText.at(i);
2006             if (ic != sc) {
2007                 if (sc.toLower() == ic.toLower()) {
2008                     changeCase = true;
2009                 } else {
2010                     break;
2011                 }
2012             }
2013         }
2014         if (i == max) {
2015             state = specialValueText.size() == input.size() ? QValidator::Acceptable : QValidator::Intermediate;
2016             if (changeCase) {
2017                 input = specialValueText.left(max);
2018             }
2019             return minimum.toDateTime();
2020         }
2021     }
2022 
2023     StateNode tmp = parse(input, position, value.toDateTime(), fixup);
2024     // Impose this widget's spec:
2025     tmp.value = tmp.value.toTimeSpec(spec);
2026     // ... but that might turn a valid datetime into an invalid one:
2027     if (!tmp.value.isValid() && tmp.state == Acceptable)
2028         tmp.state = Intermediate;
2029 
2030     input = tmp.input;
2031     position += tmp.padded;
2032     state = QValidator::State(int(tmp.state));
2033     if (state == QValidator::Acceptable) {
2034         if (tmp.conflicts && conflictGuard != tmp.value) {
2035             conflictGuard = tmp.value;
2036             clearCache();
2037             input = textFromValue(tmp.value);
2038             updateCache(tmp.value, input);
2039             conflictGuard.clear();
2040         } else {
2041             cachedText = input;
2042             cachedState = state;
2043             cachedValue = tmp.value;
2044         }
2045     } else {
2046         clearCache();
2047     }
2048     return (tmp.value.isNull() ? getZeroVariant().toDateTime() : tmp.value);
2049 }
2050 
2051 
2052 /*!
2053   \internal
2054 */
2055 
textFromValue(const QVariant & f) const2056 QString QDateTimeEditPrivate::textFromValue(const QVariant &f) const
2057 {
2058     Q_Q(const QDateTimeEdit);
2059     return q->textFromDateTime(f.toDateTime());
2060 }
2061 
2062 /*!
2063   \internal
2064 
2065   This function's name is slightly confusing; it is not to be confused
2066   with QAbstractSpinBox::valueFromText().
2067 */
2068 
valueFromText(const QString & f) const2069 QVariant QDateTimeEditPrivate::valueFromText(const QString &f) const
2070 {
2071     Q_Q(const QDateTimeEdit);
2072     return q->dateTimeFromText(f).toTimeSpec(spec);
2073 }
2074 
2075 
2076 /*!
2077   \internal
2078 
2079   Internal function called by QDateTimeEdit::stepBy(). Also takes a
2080   Section for which section to step on and a bool \a test for
2081   whether or not to modify the internal cachedDay variable. This is
2082   necessary because the function is called from the const function
2083   QDateTimeEdit::stepEnabled() as well as QDateTimeEdit::stepBy().
2084 */
2085 
stepBy(int sectionIndex,int steps,bool test) const2086 QDateTime QDateTimeEditPrivate::stepBy(int sectionIndex, int steps, bool test) const
2087 {
2088     Q_Q(const QDateTimeEdit);
2089     QDateTime v = value.toDateTime();
2090     QString str = displayText();
2091     int pos = edit->cursorPosition();
2092     const SectionNode sn = sectionNode(sectionIndex);
2093 
2094     int val;
2095     // to make sure it behaves reasonably when typing something and then stepping in non-tracking mode
2096     if (!test && pendingEmit) {
2097         if (q->validate(str, pos) != QValidator::Acceptable) {
2098             v = value.toDateTime();
2099         } else {
2100             v = q->dateTimeFromText(str);
2101         }
2102         val = getDigit(v, sectionIndex);
2103     } else {
2104         val = getDigit(v, sectionIndex);
2105     }
2106 
2107     val += steps;
2108 
2109     const int min = absoluteMin(sectionIndex);
2110     const int max = absoluteMax(sectionIndex, value.toDateTime());
2111 
2112     if (val < min) {
2113         val = (wrapping ? max - (min - val) + 1 : min);
2114     } else if (val > max) {
2115         val = (wrapping ? min + val - max - 1 : max);
2116     }
2117 
2118     const int oldDay = v.date().day(calendar);
2119 
2120     setDigit(v, sectionIndex, val);
2121     /*
2122         Stepping into a daylight saving time that doesn't exist,
2123         so use the time that has the same distance from epoch.
2124     */
2125     if (!v.isValid()) {
2126         auto msecsSinceEpoch = v.toMSecsSinceEpoch();
2127         // decreasing from e.g 3am to 2am would get us back to 3am, but we want 1am
2128         if (steps < 0 && sn.type & HourSectionMask)
2129             msecsSinceEpoch -= 3600 * 1000;
2130         v = QDateTime::fromMSecsSinceEpoch(msecsSinceEpoch, v.timeSpec());
2131     }
2132     // if this sets year or month it will make
2133     // sure that days are lowered if needed.
2134 
2135     const QDateTime minimumDateTime = minimum.toDateTime();
2136     const QDateTime maximumDateTime = maximum.toDateTime();
2137     // changing one section should only modify that section, if possible
2138     if (sn.type != AmPmSection && (v < minimumDateTime || v > maximumDateTime)) {
2139         const int localmin = getDigit(minimumDateTime, sectionIndex);
2140         const int localmax = getDigit(maximumDateTime, sectionIndex);
2141 
2142         if (wrapping) {
2143             // just because we hit the roof in one direction, it
2144             // doesn't mean that we hit the floor in the other
2145             if (steps > 0) {
2146                 setDigit(v, sectionIndex, min);
2147                 if (!(sn.type & DaySectionMask) && sections & DateSectionMask) {
2148                     const int daysInMonth = v.date().daysInMonth(calendar);
2149                     if (v.date().day(calendar) < oldDay && v.date().day(calendar) < daysInMonth) {
2150                         const int adds = qMin(oldDay, daysInMonth);
2151                         v = v.addDays(adds - v.date().day(calendar));
2152                     }
2153                 }
2154 
2155                 if (v < minimumDateTime) {
2156                     setDigit(v, sectionIndex, localmin);
2157                     if (v < minimumDateTime)
2158                         setDigit(v, sectionIndex, localmin + 1);
2159                 }
2160             } else {
2161                 setDigit(v, sectionIndex, max);
2162                 if (!(sn.type & DaySectionMask) && sections & DateSectionMask) {
2163                     const int daysInMonth = v.date().daysInMonth(calendar);
2164                     if (v.date().day(calendar) < oldDay && v.date().day(calendar) < daysInMonth) {
2165                         const int adds = qMin(oldDay, daysInMonth);
2166                         v = v.addDays(adds - v.date().day(calendar));
2167                     }
2168                 }
2169 
2170                 if (v > maximumDateTime) {
2171                     setDigit(v, sectionIndex, localmax);
2172                     if (v > maximumDateTime)
2173                         setDigit(v, sectionIndex, localmax - 1);
2174                 }
2175             }
2176         } else {
2177             setDigit(v, sectionIndex, (steps > 0 ? localmax : localmin));
2178         }
2179     }
2180     if (!test && oldDay != v.date().day(calendar) && !(sn.type & DaySectionMask)) {
2181         // this should not happen when called from stepEnabled
2182         cachedDay = qMax<int>(oldDay, cachedDay);
2183     }
2184 
2185     if (v < minimumDateTime) {
2186         if (wrapping) {
2187             QDateTime t = v;
2188             setDigit(t, sectionIndex, steps < 0 ? max : min);
2189             bool mincmp = (t >= minimumDateTime);
2190             bool maxcmp = (t <= maximumDateTime);
2191             if (!mincmp || !maxcmp) {
2192                 setDigit(t, sectionIndex, getDigit(steps < 0
2193                                                    ? maximumDateTime
2194                                                    : minimumDateTime, sectionIndex));
2195                 mincmp = (t >= minimumDateTime);
2196                 maxcmp = (t <= maximumDateTime);
2197             }
2198             if (mincmp && maxcmp) {
2199                 v = t;
2200             }
2201         } else {
2202             v = value.toDateTime();
2203         }
2204     } else if (v > maximumDateTime) {
2205         if (wrapping) {
2206             QDateTime t = v;
2207             setDigit(t, sectionIndex, steps > 0 ? min : max);
2208             bool mincmp = (t >= minimumDateTime);
2209             bool maxcmp = (t <= maximumDateTime);
2210             if (!mincmp || !maxcmp) {
2211                 setDigit(t, sectionIndex, getDigit(steps > 0 ?
2212                                                    minimumDateTime :
2213                                                    maximumDateTime, sectionIndex));
2214                 mincmp = (t >= minimumDateTime);
2215                 maxcmp = (t <= maximumDateTime);
2216             }
2217             if (mincmp && maxcmp) {
2218                 v = t;
2219             }
2220         } else {
2221             v = value.toDateTime();
2222         }
2223     }
2224 
2225     const QDateTime ret = bound(v, value, steps).toDateTime().toTimeSpec(spec);
2226     return ret;
2227 }
2228 
2229 /*!
2230   \internal
2231 */
2232 
emitSignals(EmitPolicy ep,const QVariant & old)2233 void QDateTimeEditPrivate::emitSignals(EmitPolicy ep, const QVariant &old)
2234 {
2235     Q_Q(QDateTimeEdit);
2236     if (ep == NeverEmit) {
2237         return;
2238     }
2239     pendingEmit = false;
2240 
2241     const bool dodate = value.toDate().isValid() && (sections & DateSectionMask);
2242     const bool datechanged = (ep == AlwaysEmit || old.toDate() != value.toDate());
2243     const bool dotime = value.toTime().isValid() && (sections & TimeSectionMask);
2244     const bool timechanged = (ep == AlwaysEmit || old.toTime() != value.toTime());
2245 
2246     updateCache(value, displayText());
2247 
2248     syncCalendarWidget();
2249     if (datechanged || timechanged)
2250         emit q->dateTimeChanged(value.toDateTime());
2251     if (dodate && datechanged)
2252         emit q->dateChanged(value.toDate());
2253     if (dotime && timechanged)
2254         emit q->timeChanged(value.toTime());
2255 
2256 }
2257 
2258 /*!
2259   \internal
2260 */
2261 
_q_editorCursorPositionChanged(int oldpos,int newpos)2262 void QDateTimeEditPrivate::_q_editorCursorPositionChanged(int oldpos, int newpos)
2263 {
2264     if (ignoreCursorPositionChanged || specialValue())
2265         return;
2266     const QString oldText = displayText();
2267     updateCache(value, oldText);
2268 
2269     const bool allowChange = !edit->hasSelectedText();
2270     const bool forward = oldpos <= newpos;
2271     ignoreCursorPositionChanged = true;
2272     int s = sectionAt(newpos);
2273     if (s == NoSectionIndex && forward && newpos > 0) {
2274         s = sectionAt(newpos - 1);
2275     }
2276 
2277     int c = newpos;
2278 
2279     const int selstart = edit->selectionStart();
2280     const int selSection = sectionAt(selstart);
2281     const int l = selSection != -1 ? sectionSize(selSection) : 0;
2282 
2283     if (s == NoSectionIndex) {
2284         if (l > 0 && selstart == sectionPos(selSection) && edit->selectedText().size() == l) {
2285             s = selSection;
2286             if (allowChange)
2287                 setSelected(selSection, true);
2288             c = -1;
2289         } else {
2290             int closest = closestSection(newpos, forward);
2291             c = sectionPos(closest) + (forward ? 0 : qMax<int>(0, sectionSize(closest)));
2292 
2293             if (allowChange) {
2294                 edit->setCursorPosition(c);
2295                 QDTEDEBUG << c;
2296             }
2297             s = closest;
2298         }
2299     }
2300 
2301     if (allowChange && currentSectionIndex != s) {
2302         interpret(EmitIfChanged);
2303     }
2304     if (c == -1) {
2305         setSelected(s, true);
2306     } else if (!edit->hasSelectedText()) {
2307         if (oldpos < newpos) {
2308             edit->setCursorPosition(displayText().size() - (oldText.size() - c));
2309         } else {
2310             edit->setCursorPosition(c);
2311         }
2312     }
2313 
2314     QDTEDEBUG << "currentSectionIndex is set to" << sectionNode(s).name()
2315               << oldpos << newpos
2316               << "was" << sectionNode(currentSectionIndex).name();
2317 
2318     currentSectionIndex = s;
2319     Q_ASSERT_X(currentSectionIndex < sectionNodes.size(),
2320                "QDateTimeEditPrivate::_q_editorCursorPositionChanged()",
2321                qPrintable(QString::fromLatin1("Internal error (%1 %2)").
2322                           arg(currentSectionIndex).
2323                           arg(sectionNodes.size())));
2324 
2325     ignoreCursorPositionChanged = false;
2326 }
2327 
2328 /*!
2329   \internal
2330 
2331   Try to get the format from the local settings
2332 */
readLocaleSettings()2333 void QDateTimeEditPrivate::readLocaleSettings()
2334 {
2335     const QLocale loc;
2336     defaultTimeFormat = loc.timeFormat(QLocale::ShortFormat);
2337     defaultDateFormat = loc.dateFormat(QLocale::ShortFormat);
2338     defaultDateTimeFormat = loc.dateTimeFormat(QLocale::ShortFormat);
2339 }
2340 
convertToPublic(QDateTimeParser::Section s)2341 QDateTimeEdit::Section QDateTimeEditPrivate::convertToPublic(QDateTimeParser::Section s)
2342 {
2343     switch (s & ~Internal) {
2344     case AmPmSection: return QDateTimeEdit::AmPmSection;
2345     case MSecSection: return QDateTimeEdit::MSecSection;
2346     case SecondSection: return QDateTimeEdit::SecondSection;
2347     case MinuteSection: return QDateTimeEdit::MinuteSection;
2348     case DayOfWeekSectionShort:
2349     case DayOfWeekSectionLong:
2350     case DaySection: return QDateTimeEdit::DaySection;
2351     case MonthSection: return QDateTimeEdit::MonthSection;
2352     case YearSection2Digits:
2353     case YearSection: return QDateTimeEdit::YearSection;
2354     case Hour12Section:
2355     case Hour24Section: return QDateTimeEdit::HourSection;
2356     case FirstSection:
2357     case NoSection:
2358     case LastSection: break;
2359     }
2360     return QDateTimeEdit::NoSection;
2361 }
2362 
convertSections(QDateTimeParser::Sections s)2363 QDateTimeEdit::Sections QDateTimeEditPrivate::convertSections(QDateTimeParser::Sections s)
2364 {
2365     QDateTimeEdit::Sections ret;
2366     if (s & QDateTimeParser::MSecSection)
2367         ret |= QDateTimeEdit::MSecSection;
2368     if (s & QDateTimeParser::SecondSection)
2369         ret |= QDateTimeEdit::SecondSection;
2370     if (s & QDateTimeParser::MinuteSection)
2371         ret |= QDateTimeEdit::MinuteSection;
2372     if (s & (QDateTimeParser::HourSectionMask))
2373         ret |= QDateTimeEdit::HourSection;
2374     if (s & QDateTimeParser::AmPmSection)
2375         ret |= QDateTimeEdit::AmPmSection;
2376     if (s & (QDateTimeParser::DaySectionMask))
2377         ret |= QDateTimeEdit::DaySection;
2378     if (s & QDateTimeParser::MonthSection)
2379         ret |= QDateTimeEdit::MonthSection;
2380     if (s & (QDateTimeParser::YearSectionMask))
2381         ret |= QDateTimeEdit::YearSection;
2382 
2383     return ret;
2384 }
2385 
2386 /*!
2387     \reimp
2388 */
2389 
paintEvent(QPaintEvent * event)2390 void QDateTimeEdit::paintEvent(QPaintEvent *event)
2391 {
2392     Q_D(QDateTimeEdit);
2393     if (!d->calendarPopupEnabled()) {
2394         QAbstractSpinBox::paintEvent(event);
2395         return;
2396     }
2397 
2398     QStyleOptionSpinBox opt;
2399     initStyleOption(&opt);
2400 
2401     QStyleOptionComboBox optCombo;
2402 
2403     optCombo.init(this);
2404     optCombo.editable = true;
2405     optCombo.frame = opt.frame;
2406     optCombo.subControls = opt.subControls;
2407     optCombo.activeSubControls = opt.activeSubControls;
2408     optCombo.state = opt.state;
2409     if (d->readOnly) {
2410         optCombo.state &= ~QStyle::State_Enabled;
2411     }
2412 
2413     QPainter p(this);
2414     style()->drawComplexControl(QStyle::CC_ComboBox, &optCombo, &p, this);
2415 }
2416 
2417 /*
2418     Returns the string for AM and PM markers.
2419 
2420     If a translation for "AM" and "PM" is installed, then use that.
2421     Otherwise, use the default implementation, which uses the locale.
2422 */
getAmPmText(AmPm ap,Case cs) const2423 QString QDateTimeEditPrivate::getAmPmText(AmPm ap, Case cs) const
2424 {
2425     QString original;
2426     QString translated;
2427     if (ap == AmText) {
2428         original = QLatin1String(cs == UpperCase ? "AM" : "am");
2429         translated = (cs == UpperCase ? QDateTimeParser::tr("AM") : QDateTimeParser::tr("am"));
2430     } else {
2431         original = QLatin1String(cs == UpperCase ? "PM" : "pm");
2432         translated = (cs == UpperCase ? QDateTimeParser::tr("PM") : QDateTimeParser::tr("pm"));
2433     }
2434 
2435     // This logic fails if a translation exists but doesn't change the string,
2436     // which we can accept as a corner-case for which a locale-derived answer
2437     // will be acceptable.
2438     if (original != translated)
2439         return translated;
2440 
2441     return QDateTimeParser::getAmPmText(ap, cs);
2442 }
2443 
absoluteIndex(QDateTimeEdit::Section s,int index) const2444 int QDateTimeEditPrivate::absoluteIndex(QDateTimeEdit::Section s, int index) const
2445 {
2446     for (int i=0; i<sectionNodes.size(); ++i) {
2447         if (convertToPublic(sectionNodes.at(i).type) == s && index-- == 0) {
2448             return i;
2449         }
2450     }
2451     return NoSectionIndex;
2452 }
2453 
absoluteIndex(const SectionNode & s) const2454 int QDateTimeEditPrivate::absoluteIndex(const SectionNode &s) const
2455 {
2456     return sectionNodes.indexOf(s);
2457 }
2458 
interpret(EmitPolicy ep)2459 void QDateTimeEditPrivate::interpret(EmitPolicy ep)
2460 {
2461     Q_Q(QDateTimeEdit);
2462     QString tmp = displayText();
2463     int pos = edit->cursorPosition();
2464     const QValidator::State state = q->validate(tmp, pos);
2465     if (state != QValidator::Acceptable
2466         && correctionMode == QAbstractSpinBox::CorrectToPreviousValue
2467         && (state == QValidator::Invalid
2468             || currentSectionIndex < 0
2469             || !(fieldInfo(currentSectionIndex) & AllowPartial))) {
2470         setValue(value, ep);
2471         updateTimeSpec();
2472     } else {
2473         QAbstractSpinBoxPrivate::interpret(ep);
2474     }
2475 }
2476 
clearCache() const2477 void QDateTimeEditPrivate::clearCache() const
2478 {
2479     QAbstractSpinBoxPrivate::clearCache();
2480     cachedDay = -1;
2481 }
2482 
2483 /*!
2484     Initialize \a option with the values from this QDataTimeEdit. This method
2485     is useful for subclasses when they need a QStyleOptionSpinBox, but don't want
2486     to fill in all the information themselves.
2487 
2488     \sa QStyleOption::initFrom()
2489 */
initStyleOption(QStyleOptionSpinBox * option) const2490 void QDateTimeEdit::initStyleOption(QStyleOptionSpinBox *option) const
2491 {
2492     if (!option)
2493         return;
2494 
2495     Q_D(const QDateTimeEdit);
2496     QAbstractSpinBox::initStyleOption(option);
2497     if (d->calendarPopupEnabled()) {
2498         option->subControls = QStyle::SC_ComboBoxFrame | QStyle::SC_ComboBoxEditField
2499                               | QStyle::SC_ComboBoxArrow;
2500         if (d->arrowState == QStyle::State_Sunken)
2501             option->state |= QStyle::State_Sunken;
2502         else
2503             option->state &= ~QStyle::State_Sunken;
2504     }
2505 }
2506 
init(const QVariant & var)2507 void QDateTimeEditPrivate::init(const QVariant &var)
2508 {
2509     Q_Q(QDateTimeEdit);
2510     switch (var.userType()) {
2511     case QMetaType::QDate:
2512         value = var.toDate().startOfDay();
2513         updateTimeSpec();
2514         q->setDisplayFormat(defaultDateFormat);
2515         if (sectionNodes.isEmpty()) // ### safeguard for broken locale
2516             q->setDisplayFormat(QLatin1String("dd/MM/yyyy"));
2517         break;
2518     case QMetaType::QDateTime:
2519         value = var;
2520         updateTimeSpec();
2521         q->setDisplayFormat(defaultDateTimeFormat);
2522         if (sectionNodes.isEmpty()) // ### safeguard for broken locale
2523             q->setDisplayFormat(QLatin1String("dd/MM/yyyy hh:mm:ss"));
2524         break;
2525     case QMetaType::QTime:
2526         value = QDateTime(QDATETIMEEDIT_DATE_INITIAL, var.toTime());
2527         updateTimeSpec();
2528         q->setDisplayFormat(defaultTimeFormat);
2529         if (sectionNodes.isEmpty()) // ### safeguard for broken locale
2530             q->setDisplayFormat(QLatin1String("hh:mm:ss"));
2531         break;
2532     default:
2533         Q_ASSERT_X(0, "QDateTimeEditPrivate::init", "Internal error");
2534         break;
2535     }
2536 #ifdef QT_KEYPAD_NAVIGATION
2537     if (QApplicationPrivate::keypadNavigationEnabled())
2538         q->setCalendarPopup(true);
2539 #endif
2540     q->setInputMethodHints(Qt::ImhPreferNumbers);
2541     setLayoutItemMargins(QStyle::SE_DateTimeEditLayoutItem);
2542 }
2543 
_q_resetButton()2544 void QDateTimeEditPrivate::_q_resetButton()
2545 {
2546     updateArrow(QStyle::State_None);
2547 }
2548 
updateArrow(QStyle::StateFlag state)2549 void QDateTimeEditPrivate::updateArrow(QStyle::StateFlag state)
2550 {
2551     Q_Q(QDateTimeEdit);
2552 
2553     if (arrowState == state)
2554         return;
2555     arrowState = state;
2556     if (arrowState != QStyle::State_None)
2557         buttonState |= Mouse;
2558     else {
2559         buttonState = 0;
2560         hoverControl = QStyle::SC_ComboBoxFrame;
2561     }
2562     q->update();
2563 }
2564 
2565 /*!
2566     \internal
2567     Returns the hover control at \a pos.
2568     This will update the hoverRect and hoverControl.
2569 */
newHoverControl(const QPoint & pos)2570 QStyle::SubControl QDateTimeEditPrivate::newHoverControl(const QPoint &pos)
2571 {
2572     if (!calendarPopupEnabled())
2573         return QAbstractSpinBoxPrivate::newHoverControl(pos);
2574 
2575     Q_Q(QDateTimeEdit);
2576 
2577     QStyleOptionComboBox optCombo;
2578     optCombo.init(q);
2579     optCombo.editable = true;
2580     optCombo.subControls = QStyle::SC_All;
2581     hoverControl = q->style()->hitTestComplexControl(QStyle::CC_ComboBox, &optCombo, pos, q);
2582     return hoverControl;
2583 }
2584 
updateEditFieldGeometry()2585 void QDateTimeEditPrivate::updateEditFieldGeometry()
2586 {
2587     if (!calendarPopupEnabled()) {
2588         QAbstractSpinBoxPrivate::updateEditFieldGeometry();
2589         return;
2590     }
2591 
2592     Q_Q(QDateTimeEdit);
2593 
2594     QStyleOptionComboBox optCombo;
2595     optCombo.init(q);
2596     optCombo.editable = true;
2597     optCombo.subControls = QStyle::SC_ComboBoxEditField;
2598     edit->setGeometry(q->style()->subControlRect(QStyle::CC_ComboBox, &optCombo,
2599                                                  QStyle::SC_ComboBoxEditField, q));
2600 }
2601 
getZeroVariant() const2602 QVariant QDateTimeEditPrivate::getZeroVariant() const
2603 {
2604     Q_ASSERT(type == QMetaType::QDateTime);
2605     return QDateTime(QDATETIMEEDIT_DATE_INITIAL, QTime(), spec);
2606 }
2607 
setRange(const QVariant & min,const QVariant & max)2608 void QDateTimeEditPrivate::setRange(const QVariant &min, const QVariant &max)
2609 {
2610     QAbstractSpinBoxPrivate::setRange(min, max);
2611     syncCalendarWidget();
2612 }
2613 
2614 
isSeparatorKey(const QKeyEvent * ke) const2615 bool QDateTimeEditPrivate::isSeparatorKey(const QKeyEvent *ke) const
2616 {
2617     if (!ke->text().isEmpty() && currentSectionIndex + 1 < sectionNodes.size() && currentSectionIndex >= 0) {
2618         if (fieldInfo(currentSectionIndex) & Numeric) {
2619             if (ke->text().at(0).isNumber())
2620                 return false;
2621         } else if (ke->text().at(0).isLetterOrNumber()) {
2622             return false;
2623         }
2624         return separators.at(currentSectionIndex + 1).contains(ke->text());
2625     }
2626     return false;
2627 }
2628 
initCalendarPopup(QCalendarWidget * cw)2629 void QDateTimeEditPrivate::initCalendarPopup(QCalendarWidget *cw)
2630 {
2631     Q_Q(QDateTimeEdit);
2632     if (!monthCalendar) {
2633         monthCalendar = new QCalendarPopup(q, cw, calendar);
2634         monthCalendar->setObjectName(QLatin1String("qt_datetimedit_calendar"));
2635         QObject::connect(monthCalendar, SIGNAL(newDateSelected(QDate)), q, SLOT(setDate(QDate)));
2636         QObject::connect(monthCalendar, SIGNAL(hidingCalendar(QDate)), q, SLOT(setDate(QDate)));
2637         QObject::connect(monthCalendar, SIGNAL(activated(QDate)), q, SLOT(setDate(QDate)));
2638         QObject::connect(monthCalendar, SIGNAL(activated(QDate)), monthCalendar, SLOT(close()));
2639         QObject::connect(monthCalendar, SIGNAL(resetButton()), q, SLOT(_q_resetButton()));
2640     } else if (cw) {
2641         monthCalendar->setCalendarWidget(cw);
2642     }
2643     syncCalendarWidget();
2644 }
2645 
positionCalendarPopup()2646 void QDateTimeEditPrivate::positionCalendarPopup()
2647 {
2648     Q_Q(QDateTimeEdit);
2649     QPoint pos = (q->layoutDirection() == Qt::RightToLeft) ? q->rect().bottomRight() : q->rect().bottomLeft();
2650     QPoint pos2 = (q->layoutDirection() == Qt::RightToLeft) ? q->rect().topRight() : q->rect().topLeft();
2651     pos = q->mapToGlobal(pos);
2652     pos2 = q->mapToGlobal(pos2);
2653     QSize size = monthCalendar->sizeHint();
2654     QRect screen = QDesktopWidgetPrivate::availableGeometry(pos);
2655     //handle popup falling "off screen"
2656     if (q->layoutDirection() == Qt::RightToLeft) {
2657         pos.setX(pos.x()-size.width());
2658         pos2.setX(pos2.x()-size.width());
2659         if (pos.x() < screen.left())
2660             pos.setX(qMax(pos.x(), screen.left()));
2661         else if (pos.x()+size.width() > screen.right())
2662             pos.setX(qMax(pos.x()-size.width(), screen.right()-size.width()));
2663     } else {
2664         if (pos.x()+size.width() > screen.right())
2665             pos.setX(screen.right()-size.width());
2666         pos.setX(qMax(pos.x(), screen.left()));
2667     }
2668     if (pos.y() + size.height() > screen.bottom())
2669         pos.setY(pos2.y() - size.height());
2670     else if (pos.y() < screen.top())
2671         pos.setY(screen.top());
2672     if (pos.y() < screen.top())
2673         pos.setY(screen.top());
2674     if (pos.y()+size.height() > screen.bottom())
2675         pos.setY(screen.bottom()-size.height());
2676     monthCalendar->move(pos);
2677 }
2678 
calendarPopupEnabled() const2679 bool QDateTimeEditPrivate::calendarPopupEnabled() const
2680 {
2681     return (calendarPopup && (sections & (DateSectionMask)));
2682 }
2683 
syncCalendarWidget()2684 void QDateTimeEditPrivate::syncCalendarWidget()
2685 {
2686     Q_Q(QDateTimeEdit);
2687     if (monthCalendar) {
2688         const QSignalBlocker blocker(monthCalendar);
2689         monthCalendar->setDateRange(q->minimumDate(), q->maximumDate());
2690         monthCalendar->setDate(q->date());
2691     }
2692 }
2693 
QCalendarPopup(QWidget * parent,QCalendarWidget * cw,QCalendar ca)2694 QCalendarPopup::QCalendarPopup(QWidget *parent, QCalendarWidget *cw, QCalendar ca)
2695     : QWidget(parent, Qt::Popup), calendarSystem(ca)
2696 {
2697     setAttribute(Qt::WA_WindowPropagation);
2698 
2699     dateChanged = false;
2700     if (!cw) {
2701         verifyCalendarInstance();
2702     } else {
2703         setCalendarWidget(cw);
2704     }
2705 }
2706 
verifyCalendarInstance()2707 QCalendarWidget *QCalendarPopup::verifyCalendarInstance()
2708 {
2709     if (calendar.isNull()) {
2710         QCalendarWidget *cw = new QCalendarWidget(this);
2711         cw->setCalendar(calendarSystem);
2712         cw->setVerticalHeaderFormat(QCalendarWidget::NoVerticalHeader);
2713 #ifdef QT_KEYPAD_NAVIGATION
2714         if (QApplicationPrivate::keypadNavigationEnabled())
2715             cw->setHorizontalHeaderFormat(QCalendarWidget::SingleLetterDayNames);
2716 #endif
2717         setCalendarWidget(cw);
2718         return cw;
2719     } else {
2720         return calendar.data();
2721     }
2722 }
2723 
setCalendarWidget(QCalendarWidget * cw)2724 void QCalendarPopup::setCalendarWidget(QCalendarWidget *cw)
2725 {
2726     Q_ASSERT(cw);
2727     QVBoxLayout *widgetLayout = qobject_cast<QVBoxLayout*>(layout());
2728     if (!widgetLayout) {
2729         widgetLayout = new QVBoxLayout(this);
2730         widgetLayout->setContentsMargins(QMargins());
2731         widgetLayout->setSpacing(0);
2732     }
2733     delete calendar.data();
2734     calendar = QPointer<QCalendarWidget>(cw);
2735     widgetLayout->addWidget(cw);
2736 
2737     connect(cw, SIGNAL(activated(QDate)), this, SLOT(dateSelected(QDate)));
2738     connect(cw, SIGNAL(clicked(QDate)), this, SLOT(dateSelected(QDate)));
2739     connect(cw, SIGNAL(selectionChanged()), this, SLOT(dateSelectionChanged()));
2740 
2741     cw->setFocus();
2742 }
2743 
2744 
setDate(QDate date)2745 void QCalendarPopup::setDate(QDate date)
2746 {
2747     oldDate = date;
2748     verifyCalendarInstance()->setSelectedDate(date);
2749 }
2750 
setDateRange(QDate min,QDate max)2751 void QCalendarPopup::setDateRange(QDate min, QDate max)
2752 {
2753     QCalendarWidget *cw = verifyCalendarInstance();
2754     cw->setMinimumDate(min);
2755     cw->setMaximumDate(max);
2756 }
2757 
mousePressEvent(QMouseEvent * event)2758 void QCalendarPopup::mousePressEvent(QMouseEvent *event)
2759 {
2760     QDateTimeEdit *dateTime = qobject_cast<QDateTimeEdit *>(parentWidget());
2761     if (dateTime) {
2762         QStyleOptionComboBox opt;
2763         opt.init(dateTime);
2764         QRect arrowRect = dateTime->style()->subControlRect(QStyle::CC_ComboBox, &opt,
2765                                                             QStyle::SC_ComboBoxArrow, dateTime);
2766         arrowRect.moveTo(dateTime->mapToGlobal(arrowRect .topLeft()));
2767         if (arrowRect.contains(event->globalPos()) || rect().contains(event->pos()))
2768             setAttribute(Qt::WA_NoMouseReplay);
2769     }
2770     QWidget::mousePressEvent(event);
2771 }
2772 
mouseReleaseEvent(QMouseEvent *)2773 void QCalendarPopup::mouseReleaseEvent(QMouseEvent*)
2774 {
2775     emit resetButton();
2776 }
2777 
event(QEvent * event)2778 bool QCalendarPopup::event(QEvent *event)
2779 {
2780 #if QT_CONFIG(shortcut)
2781     if (event->type() == QEvent::KeyPress) {
2782         QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
2783         if (keyEvent->matches(QKeySequence::Cancel))
2784             dateChanged = false;
2785     }
2786 #endif
2787     return QWidget::event(event);
2788 }
2789 
dateSelectionChanged()2790 void QCalendarPopup::dateSelectionChanged()
2791 {
2792     dateChanged = true;
2793     emit newDateSelected(verifyCalendarInstance()->selectedDate());
2794 }
dateSelected(QDate date)2795 void QCalendarPopup::dateSelected(QDate date)
2796 {
2797     dateChanged = true;
2798     emit activated(date);
2799     close();
2800 }
2801 
hideEvent(QHideEvent *)2802 void QCalendarPopup::hideEvent(QHideEvent *)
2803 {
2804     emit resetButton();
2805     if (!dateChanged)
2806         emit hidingCalendar(oldDate);
2807 }
2808 
2809 QT_END_NAMESPACE
2810 #include "moc_qdatetimeedit.cpp"
2811 #include "moc_qdatetimeedit_p.cpp"
2812