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