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

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