926void QDateTimeEdit::setDisplayFormat(
const QString &format)
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();
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());
943 d->formatExplicitlySet =
true;
944 d->sections = QDateTimeEditPrivate::convertSections(d->display);
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);
959 }
else if (dateShown && !timeShown) {
960 setTimeRange(QDATETIMEEDIT_TIME_MIN, QDATETIMEEDIT_TIME_MAX);
961 d->value = d->value.toDate().startOfDay(d->timeZone);
964 d->editorCursorPositionChanged(-1, 0);
1067QSize QDateTimeEdit::sizeHint()
const
1069 Q_D(
const QDateTimeEdit);
1070 if (d->cachedSizeHint.isEmpty()) {
1073 const QFontMetrics fm(fontMetrics());
1074 int h = d->edit->sizeHint().height();
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));
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();
1099 d->cachedMinimumSizeHint = d->cachedSizeHint;
1102 return d->cachedSizeHint;
1149void QDateTimeEdit::keyPressEvent(QKeyEvent *event)
1152 int oldCurrent = d->currentSectionIndex;
1154 bool inserted =
false;
1156 switch (event->key()) {
1158 case Qt::Key_Return:
1159 d->interpret(AlwaysEmit);
1160 d->setSelected(d->currentSectionIndex,
true);
1162 emit editingFinished();
1163 emit d->edit->returnPressed();
1166 if (!d->isSeparatorKey(event)) {
1167 inserted = select = !event->text().isEmpty() && event->text().at(0).isPrint()
1168 && !(event->modifiers() & ~(Qt::ShiftModifier|Qt::KeypadModifier));
1174 if (event->key() == Qt::Key_Left || event->key() == Qt::Key_Right) {
1175 if (!(event->modifiers() & Qt::ControlModifier)) {
1181 case Qt::Key_Backtab:
1184 if (d->specialValue()) {
1185 d->edit->setSelection(d->edit->cursorPosition(), 0);
1188 const bool forward = event->key() != Qt::Key_Left && event->key() != Qt::Key_Backtab
1189 && (event->key() != Qt::Key_Tab || !(event->modifiers() & Qt::ShiftModifier));
1191 if (event->key() != Qt::Key_Backtab && event->key() != Qt::Key_Tab)
1192 focusNextPrevChild(forward);
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);
1210 d->currentSectionIndex = tmp;
1213 if (d->currentSectionIndex != oldCurrent) {
1214 d->setSelected(d->currentSectionIndex);
1217 if (d->specialValue()) {
1218 d->edit->setSelection(d->edit->cursorPosition(), 0);
1237void QDateTimeEdit::focusInEvent(QFocusEvent *event)
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;
1253 d->readLocaleSettings();
1254 if (d->displayFormat != *frm) {
1255 setDisplayFormat(*frm);
1256 d->formatExplicitlySet =
false;
1257 d->edit->setCursorPosition(oldPos);
1261 const bool oldHasHadFocus = d->hasHadFocus;
1262 d->hasHadFocus =
true;
1264 switch (event->reason()) {
1265 case Qt::BacktabFocusReason:
1268 case Qt::MouseFocusReason:
1269 case Qt::PopupFocusReason:
1271 case Qt::ActiveWindowFocusReason:
1275 case Qt::ShortcutFocusReason:
1276 case Qt::TabFocusReason:
1280 if (isRightToLeft())
1284 d->setSelected(first ? 0 : d->sectionNodes.size() - 1);
1401QDateTimeEdit::StepEnabled QDateTimeEdit::stepEnabled()
const
1403 Q_D(
const QDateTimeEdit);
1406 if (d->specialValue()) {
1407 return (d->minimum == d->maximum ? StepEnabled{} : StepEnabled(StepUpEnabled));
1410 QAbstractSpinBox::StepEnabled ret = { };
1412 switch (d->sectionType(d->currentSectionIndex)) {
1413 case QDateTimeParser::NoSection:
1414 case QDateTimeParser::FirstSection:
1415 case QDateTimeParser::LastSection:
return { };
1419 return StepEnabled(StepDownEnabled|StepUpEnabled);
1421 QVariant v = d->stepBy(d->currentSectionIndex, 1,
true);
1422 if (v != d->value) {
1423 ret |= QAbstractSpinBox::StepUpEnabled;
1425 v = d->stepBy(d->currentSectionIndex, -1,
true);
1426 if (v != d->value) {
1427 ret |= QAbstractSpinBox::StepDownEnabled;
1763int QDateTimeEditPrivate::closestSection(
int pos,
bool forward)
const
1766 if (pos < separators.first().size())
1767 return forward ? 0 : FirstSectionIndex;
1769 const QString text = displayText();
1770 if (text.size() - pos < separators.last().size() + 1)
1771 return forward ? LastSectionIndex :
int(sectionNodes.size() - 1);
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) {
1781 }
else if (i == sectionNodes.size() - 1 && pos > tmp) {
1785 qWarning(
"QDateTimeEdit: Internal Error: closestSection returned NoSection");
1786 return NoSectionIndex;
1868QDateTime QDateTimeEditPrivate::validateAndInterpret(QString &input,
int &position,
1869 QValidator::State &state,
bool fixup)
const
1871 if (input.isEmpty()) {
1872 if (sectionNodes.size() == 1 || !specialValueText.isEmpty()) {
1873 state = QValidator::Intermediate;
1875 state = QValidator::Invalid;
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());
1885 for (i=0; i<max; ++i) {
1886 const QChar ic = input.at(i);
1887 const QChar sc = specialValueText.at(i);
1889 if (sc.toLower() == ic.toLower()) {
1897 state = specialValueText.size() == input.size() ? QValidator::Acceptable : QValidator::Intermediate;
1899 input = specialValueText.left(max);
1901 return minimum.toDateTime();
1905 StateNode tmp = parse(input, position, value.toDateTime(), fixup);
1911 tmp.value = tmp.value.toTimeZone(timeZone);
1913 if (!tmp.value.isValid() && tmp.state == Acceptable)
1914 tmp.state = Intermediate;
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;
1922 input = textFromValue(tmp.value);
1923 updateCache(tmp.value, input);
1924 conflictGuard.clear();
1927 cachedState = state;
1928 cachedValue = tmp.value;
1933 return (tmp.value.isNull() ? getZeroVariant().toDateTime() : tmp.value);
1971QDateTime QDateTimeEditPrivate::stepBy(
int sectionIndex,
int steps,
bool test)
const
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);
1980 if (!test && pendingEmit && q->validate(str, pos) == QValidator::Acceptable)
1981 v = q->dateTimeFromText(str);
1982 int val = getDigit(v, sectionIndex);
1984 const int min = absoluteMin(sectionIndex);
1985 const int max = absoluteMax(sectionIndex, value.toDateTime());
1987 if (sn.type & DayOfWeekSectionMask) {
1990#ifndef QT_ALWAYS_WRAP_WEEKDAY
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002 const int first =
int(locale().firstDayOfWeek());
2003 val = qBound(val < first ? first - 7 : first,
2005 val < first ? first - 1 : first + 6);
2018 const int span = max - min + 1;
2020 val = wrapping ? val + span : min;
2022 val = wrapping ? val - span : max;
2025 const int oldDay = v.date().day(calendar);
2028
2029
2030
2031
2032 if (setDigit(v, sectionIndex, val) && getDigit(v, sectionIndex) != val
2033 && sn.type & HourSectionMask && steps < 0) {
2035 auto msecsSinceEpoch = v.toMSecsSinceEpoch() - 3600 * 1000;
2036 v = QDateTime::fromMSecsSinceEpoch(msecsSinceEpoch, v.timeRepresentation());
2041 const QDateTime minimumDateTime = minimum.toDateTime();
2042 const QDateTime maximumDateTime = maximum.toDateTime();
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);
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));
2062 if (v < minimumDateTime) {
2063 setDigit(v, sectionIndex, localmin);
2064 if (v < minimumDateTime)
2065 setDigit(v, sectionIndex, localmin + 1);
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));
2077 if (v > maximumDateTime) {
2078 setDigit(v, sectionIndex, localmax);
2079 if (v > maximumDateTime)
2080 setDigit(v, sectionIndex, localmax - 1);
2084 setDigit(v, sectionIndex, (steps > 0 ? localmax : localmin));
2087 if (!test && oldDay != v.date().day(calendar) && !(sn.type & DaySectionMask)) {
2089 cachedDay = qMax<
int>(oldDay, cachedDay);
2092 if (v < minimumDateTime) {
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
2101 : minimumDateTime, sectionIndex));
2102 mincmp = (t >= minimumDateTime);
2103 maxcmp = (t <= maximumDateTime);
2105 if (mincmp && maxcmp) {
2109 v = value.toDateTime();
2111 }
else if (v > maximumDateTime) {
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 ?
2120 maximumDateTime, sectionIndex));
2121 mincmp = (t >= minimumDateTime);
2122 maxcmp = (t <= maximumDateTime);
2124 if (mincmp && maxcmp) {
2128 v = value.toDateTime();
2132 return bound(std::move(v), value, steps).toDateTime().toTimeZone(timeZone);
2168void QDateTimeEditPrivate::editorCursorPositionChanged(
int oldpos,
int newpos)
2170 if (ignoreCursorPositionChanged || specialValue())
2172 const QString oldText = displayText();
2173 updateCache(value, oldText);
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);
2185 const int selstart = edit->selectionStart();
2186 const int selSection = sectionAt(selstart);
2187 const int l = selSection != -1 ? sectionSize(selSection) : 0;
2189 if (s == NoSectionIndex) {
2190 if (l > 0 && selstart == sectionPos(selSection) && edit->selectedText().size() == l) {
2193 setSelected(selSection,
true);
2196 int closest = closestSection(newpos, forward);
2197 c = sectionPos(closest) + (forward ? 0 : qMax<
int>(0, sectionSize(closest)));
2200 edit->setCursorPosition(c);
2207 if (allowChange && currentSectionIndex != s) {
2208 interpret(EmitIfChanged);
2211 setSelected(s,
true);
2212 }
else if (!edit->hasSelectedText()) {
2213 if (oldpos < newpos) {
2214 edit->setCursorPosition(displayText().size() - (oldText.size() - c));
2216 edit->setCursorPosition(c);
2220 QDTEDEBUG <<
"currentSectionIndex is set to" << sectionNode(s).name()
2222 <<
"was" << sectionNode(currentSectionIndex).name();
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())));
2231 ignoreCursorPositionChanged =
false;
2269QDateTimeEdit::Sections QDateTimeEditPrivate::convertSections(QDateTimeParser::Sections s)
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;
2505void QDateTimeEditPrivate::initCalendarPopup(QCalendarWidget *cw)
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()));
2517 monthCalendar->setCalendarWidget(cw);
2519 syncCalendarWidget();
2522void QDateTimeEditPrivate::positionCalendarPopup()
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);
2532 screen = QGuiApplication::primaryScreen();
2533 const QRect screenRect = screen->availableGeometry();
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()));
2543 if (pos.x()+size.width() > screenRect.right())
2544 pos.setX(screenRect.right()-size.width());
2545 pos.setX(qMax(pos.x(), screenRect.left()));
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);
2599void QCalendarPopup::setCalendarWidget(QCalendarWidget *cw)
2602 QVBoxLayout *widgetLayout = qobject_cast<QVBoxLayout*>(layout());
2603 if (!widgetLayout) {
2604 widgetLayout =
new QVBoxLayout(
this);
2605 widgetLayout->setContentsMargins(QMargins());
2606 widgetLayout->setSpacing(0);
2608 delete calendar.data();
2609 calendar = QPointer<QCalendarWidget>(cw);
2610 widgetLayout->addWidget(cw);
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()));