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