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

source code of qtbase/src/widgets/widgets/qdatetimeedit.cpp