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

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