diff --git a/autotests/kdatecomboboxtest.cpp b/autotests/kdatecomboboxtest.cpp index 6421eaa..7f65dcf 100644 --- a/autotests/kdatecomboboxtest.cpp +++ b/autotests/kdatecomboboxtest.cpp @@ -1,156 +1,224 @@ /* Copyright 2011 John Layt This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kdatecomboboxtest.h" #include #include #include #include "kdatecombobox.h" QTEST_MAIN(KDateComboBoxTest) void KDateComboBoxTest::testDefaults() { QScopedPointer combo(new KDateComboBox); QCOMPARE(combo->date(), QDate::currentDate()); // Missing support in QLocale; //QCOMPARE(m_combo->minimumDate(), KLocale::global()->calendar()->earliestValidDate()); //QCOMPARE(m_combo->maximumDate(), KLocale::global()->calendar()->latestValidDate()); QCOMPARE(combo->isValid(), true); QCOMPARE(combo->isNull(), false); QCOMPARE(combo->options(), KDateComboBox::EditDate | KDateComboBox::SelectDate | KDateComboBox::DatePicker | KDateComboBox::DateKeywords); QCOMPARE(combo->displayFormat(), QLocale::ShortFormat); } void KDateComboBoxTest::testValidNull() { QScopedPointer combo(new KDateComboBox); QCOMPARE(combo->isValid(), true); QCOMPARE(combo->isNull(), false); combo->setDate(QDate()); QCOMPARE(combo->isValid(), false); QCOMPARE(combo->isNull(), true); combo->setDate(QDate(2000, 1, 1)); combo->lineEdit()->setText(QStringLiteral("invalid")); QCOMPARE(combo->isValid(), false); QCOMPARE(combo->isNull(), false); } void KDateComboBoxTest::testDateRange() { QScopedPointer combo(new KDateComboBox); combo->setDate(QDate(2000, 1, 1)); // Missing support in QLocale; //QCOMPARE(m_combo->minimumDate(), KLocale::global()->calendar()->earliestValidDate()); //QCOMPARE(m_combo->maximumDate(), KLocale::global()->calendar()->latestValidDate()); QCOMPARE(combo->isValid(), true); combo->setDateRange(QDate(2001, 1, 1), QDate(2002, 1, 1)); QCOMPARE(combo->minimumDate(), QDate(2001, 1, 1)); QCOMPARE(combo->maximumDate(), QDate(2002, 1, 1)); QCOMPARE(combo->isValid(), false); combo->setDate(QDate(2003, 1, 1)); QCOMPARE(combo->isValid(), false); combo->setDate(QDate(2001, 6, 1)); QCOMPARE(combo->isValid(), true); combo->setDate(QDate(2001, 1, 1)); QCOMPARE(combo->isValid(), true); combo->setDate(QDate(2002, 1, 1)); QCOMPARE(combo->isValid(), true); combo->setDate(QDate(2000, 12, 31)); QCOMPARE(combo->isValid(), false); combo->setDate(QDate(2002, 1, 2)); QCOMPARE(combo->isValid(), false); combo->setDateRange(QDate(1995, 1, 1), QDate(1990, 1, 1)); QCOMPARE(combo->minimumDate(), QDate(2001, 1, 1)); QCOMPARE(combo->maximumDate(), QDate(2002, 1, 1)); combo->setMinimumDate(QDate(2000, 1, 1)); QCOMPARE(combo->minimumDate(), QDate(2000, 1, 1)); QCOMPARE(combo->maximumDate(), QDate(2002, 1, 1)); combo->setMaximumDate(QDate(2003, 1, 1)); QCOMPARE(combo->minimumDate(), QDate(2000, 1, 1)); QCOMPARE(combo->maximumDate(), QDate(2003, 1, 1)); combo->resetDateRange(); QVERIFY(!combo->minimumDate().isValid()); QVERIFY(!combo->maximumDate().isValid()); // Check functioning when the minimum or maximum date is not already set combo->setMinimumDate(QDate(2000, 1, 1)); QCOMPARE(combo->minimumDate(), QDate(2000, 1, 1)); QVERIFY(!combo->maximumDate().isValid()); combo->resetMinimumDate(); QVERIFY(!combo->minimumDate().isValid()); QVERIFY(!combo->maximumDate().isValid()); combo->setMaximumDate(QDate(2003, 1, 1)); QVERIFY(!combo->minimumDate().isValid()); QCOMPARE(combo->maximumDate(), QDate(2003, 1, 1)); combo->resetMaximumDate(); QVERIFY(!combo->minimumDate().isValid()); QVERIFY(!combo->maximumDate().isValid()); } void KDateComboBoxTest::testDateList() { QScopedPointer combo(new KDateComboBox); QMap map; // Test default map QCOMPARE(combo->dateMap(), map); // Test basic map map.clear(); map.insert(QDate(2000, 1, 1), QStringLiteral("New Years Day")); map.insert(QDate(2000, 1, 2), QString()); map.insert(QDate(2000, 1, 3), QStringLiteral("separator")); map.insert(QDate(), QStringLiteral("No Date")); combo->setDateMap(map); QCOMPARE(combo->dateMap(), map); } void KDateComboBoxTest::testOptions() { QScopedPointer combo(new KDateComboBox); KDateComboBox::Options options = KDateComboBox::EditDate | KDateComboBox::SelectDate | KDateComboBox::DatePicker | KDateComboBox::DateKeywords; QCOMPARE(combo->options(), options); options = KDateComboBox::EditDate | KDateComboBox::WarnOnInvalid; combo->setOptions(options); QCOMPARE(combo->options(), options); } void KDateComboBoxTest::testDisplayFormat() { QScopedPointer combo(new KDateComboBox); QLocale::FormatType format = QLocale::ShortFormat; QCOMPARE(combo->displayFormat(), format); format = QLocale::NarrowFormat; combo->setDisplayFormat(format); QCOMPARE(combo->displayFormat(), format); } + +void KDateComboBoxTest::testSignals() +{ + // GIVEN + QScopedPointer combo(new KDateComboBox); + QSignalSpy spyDateEntered(combo.data(), &KDateComboBox::dateEntered); + QSignalSpy spyDateEdited(combo.data(), &KDateComboBox::dateEdited); + QSignalSpy spyDateChanged(combo.data(), &KDateComboBox::dateChanged); + auto clearSpies = [&]() { + spyDateEntered.clear(); + spyDateEdited.clear(); + spyDateChanged.clear(); + }; + + // WHEN setting a date programmatically + combo->setDate(QDate(2016, 05, 30)); + + // THEN + QCOMPARE(spyDateEntered.count(), 0); + QCOMPARE(spyDateEdited.count(), 0); + QCOMPARE(spyDateChanged.count(), 1); + clearSpies(); + + combo->show(); + combo->activateWindow(); + QVERIFY(QTest::qWaitForWindowActive(combo.data())); + + // WHEN typing a new day (Home, Del, '2' -> 20 may 2016) + QTest::keyClick(combo.data(), Qt::Key_Home); + QTest::keyClick(combo.data(), Qt::Key_Delete); + QTest::keyClick(combo.data(), Qt::Key_2); + + // THEN + QCOMPARE(combo->date(), QDate(2016, 05, 20)); + + // and losing focus + combo->clearFocus(); + + // THEN + QCOMPARE(spyDateEdited.count(), 2); + QCOMPARE(spyDateEntered.count(), 0); + QCOMPARE(spyDateChanged.count(), 1); + clearSpies(); + + // WHEN typing Key_Up + QTest::keyClick(combo.data(), Qt::Key_Up); + + // THEN + QCOMPARE(combo->date(), QDate(2016, 05, 21)); + QCOMPARE(spyDateEdited.count(), 0); + QCOMPARE(spyDateEntered.count(), 1); + QCOMPARE(spyDateChanged.count(), 1); + clearSpies(); + + // WHEN typing a digit ('1' -> 11 may 2016) then Return + QTest::keyClick(combo.data(), Qt::Key_Home); + QTest::keyClick(combo.data(), Qt::Key_Delete); + QTest::keyClick(combo.data(), Qt::Key_1); + QTest::keyClick(combo.data(), Qt::Key_Return); + + // THEN + QCOMPARE(combo->date(), QDate(2016, 05, 11)); + QCOMPARE(spyDateEdited.count(), 2); + QCOMPARE(spyDateEntered.count(), 1); + QCOMPARE(spyDateChanged.count(), 1); + clearSpies(); + +} diff --git a/autotests/kdatecomboboxtest.h b/autotests/kdatecomboboxtest.h index fd55aa6..de89521 100644 --- a/autotests/kdatecomboboxtest.h +++ b/autotests/kdatecomboboxtest.h @@ -1,40 +1,41 @@ /* Copyright 2011 John Layt This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KDATECOMBOBOXTEST_H #define KDATECOMBOBOXTEST_H #include class KDateComboBox; class KDateComboBoxTest : public QWidget { Q_OBJECT private Q_SLOTS: void testDefaults(); void testValidNull(); void testDateRange(); void testDateList(); void testOptions(); void testDisplayFormat(); + void testSignals(); }; #endif // KDATECOMBOBOXTEST_H diff --git a/src/kdatecombobox.cpp b/src/kdatecombobox.cpp index 6a02c11..b09c0ab 100644 --- a/src/kdatecombobox.cpp +++ b/src/kdatecombobox.cpp @@ -1,590 +1,606 @@ /* Copyright 2011 John Layt This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kdatecombobox.h" #include #include #include #include #include #include #include #include #include "kdatepicker.h" #include "kmessagebox.h" class KDateComboBoxPrivate { public: KDateComboBoxPrivate(KDateComboBox *q); virtual ~KDateComboBoxPrivate(); // TODO: Find a way to get that from QLocale #if 0 QDate defaultMinDate(); QDate defaultMaxDate(); #endif QString dateFormat(QLocale::FormatType format); QString formatDate(const QDate &date); void initDateWidget(); void addMenuAction(const QString &text, const QDate &date); void enableMenuDates(); void updateDateWidget(); void setDateRange(const QDate &minDate, const QDate &maxDate, const QString &minWarnMsg, const QString &maxWarnMsg); bool isInDateRange(const QDate &date) const; void clickDate(); void selectDate(QAction *action); void editDate(const QString &text); void enterDate(const QDate &date); void parseDate(); void warnDate(); KDateComboBox *const q; QMenu *m_dateMenu; QVector m_actions; KDatePicker *m_datePicker; QWidgetAction *m_datePickerAction; QDate m_date; KDateComboBox::Options m_options; QDate m_minDate; QDate m_maxDate; QString m_minWarnMsg; QString m_maxWarnMsg; bool m_warningShown; + bool m_edited; // and dateChanged not yet emitted QLocale::FormatType m_displayFormat; QMap m_dateMap; }; KDateComboBoxPrivate::KDateComboBoxPrivate(KDateComboBox *q) : q(q), m_dateMenu(new QMenu(q)), m_datePicker(new KDatePicker(q)), m_datePickerAction(new QWidgetAction(q)), m_warningShown(false), + m_edited(false), m_displayFormat(QLocale::ShortFormat) { m_options = KDateComboBox::EditDate | KDateComboBox::SelectDate | KDateComboBox::DatePicker | KDateComboBox::DateKeywords; m_date = QDate::currentDate(); //m_minDate = defaultMinDate(); //m_maxDate = defaultMaxDate(); m_datePicker->setCloseButton(false); m_datePickerAction->setObjectName(QStringLiteral("DatePicker")); m_datePickerAction->setDefaultWidget(m_datePicker); } KDateComboBoxPrivate::~KDateComboBoxPrivate() { } #if 0 QDate KDateComboBoxPrivate::defaultMinDate() { return m_date.calendar()->earliestValidDate(); } QDate KDateComboBoxPrivate::defaultMaxDate() { return m_date.calendar()->latestValidDate(); } #endif QString KDateComboBoxPrivate::dateFormat(QLocale::FormatType format) { // Clearly a workaround for QLocale using "yy" way too often for years // and so you get no way to distinguish between 1913 and 2013 anymore... // bummer. QString res = q->locale().dateFormat(format); res.replace(QStringLiteral("yy"), QStringLiteral("yyyy")); res.replace(QStringLiteral("yyyyyyyy"), QStringLiteral("yyyy")); return res; } QString KDateComboBoxPrivate::formatDate(const QDate &date) { return q->locale().toString(date, dateFormat(m_displayFormat)); } void KDateComboBoxPrivate::initDateWidget() { q->blockSignals(true); q->clear(); // If EditTime then set the line edit q->lineEdit()->setReadOnly((m_options & KDateComboBox::EditDate) != KDateComboBox::EditDate); // If SelectDate then make list items visible if ((m_options & KDateComboBox::SelectDate) == KDateComboBox::SelectDate || (m_options & KDateComboBox::DatePicker) == KDateComboBox::DatePicker || (m_options & KDateComboBox::DatePicker) == KDateComboBox::DateKeywords) { q->setMaxVisibleItems(1); } else { q->setMaxVisibleItems(0); } q->setSizeAdjustPolicy(QComboBox::AdjustToContents); q->addItem(formatDate(m_date)); q->setCurrentIndex(0); q->setSizeAdjustPolicy(QComboBox::AdjustToContentsOnFirstShow); q->blockSignals(false); m_dateMenu->clear(); m_actions.clear(); if ((m_options & KDateComboBox::SelectDate) == KDateComboBox::SelectDate) { if ((m_options & KDateComboBox::DatePicker) == KDateComboBox::DatePicker) { m_dateMenu->addAction(m_datePickerAction); m_dateMenu->addSeparator(); } if ((m_options & KDateComboBox::DateKeywords) == KDateComboBox::DateKeywords) { if (m_dateMap.isEmpty()) { addMenuAction(KDateComboBox::tr("Next Year", "@option next year"), m_date.addYears(1)); addMenuAction(KDateComboBox::tr("Next Month", "@option next month"), m_date.addMonths(1)); addMenuAction(KDateComboBox::tr("Next Week", "@option next week"), m_date.addDays(7)); addMenuAction(KDateComboBox::tr("Tomorrow", "@option tomorrow"), m_date.addDays(1)); addMenuAction(KDateComboBox::tr("Today", "@option today"), m_date); addMenuAction(KDateComboBox::tr("Yesterday", "@option yesterday"), m_date.addDays(-1)); addMenuAction(KDateComboBox::tr("Last Week", "@option last week"), m_date.addDays(-7)); addMenuAction(KDateComboBox::tr("Last Month", "@option last month"), m_date.addMonths(-1)); addMenuAction(KDateComboBox::tr("Last Year", "@option last year"), m_date.addYears(-1)); m_dateMenu->addSeparator(); addMenuAction(KDateComboBox::tr("No Date", "@option do not specify a date"), QDate()); } else { QMapIterator i(m_dateMap); while (i.hasNext()) { i.next(); if (i.value().isEmpty()) { addMenuAction(formatDate(i.key()), i.key()); } else if (i.value().toLower() == QLatin1String("separator")) { m_dateMenu->addSeparator(); } else { addMenuAction(i.value(), i.key()); } } } enableMenuDates(); } } } void KDateComboBoxPrivate::addMenuAction(const QString &text, const QDate &date) { QAction *action = new QAction(m_dateMenu); action->setText(text); action->setData(date); m_dateMenu->addAction(action); m_actions << action; } void KDateComboBoxPrivate::enableMenuDates() { // Hide menu dates if they are outside the date range for (int i = 0; i < m_actions.count(); ++i) { QDate date = m_actions[i]->data().toDate(); m_actions[i]->setVisible(!date.isValid() || isInDateRange(date)); } } void KDateComboBoxPrivate::updateDateWidget() { q->blockSignals(true); m_datePicker->blockSignals(true); m_datePicker->setDate(m_date); int pos = q->lineEdit()->cursorPosition(); q->setItemText(0, formatDate(m_date)); q->lineEdit()->setText(formatDate(m_date)); q->lineEdit()->setCursorPosition(pos); m_datePicker->blockSignals(false); q->blockSignals(false); } void KDateComboBoxPrivate::setDateRange(const QDate &minDate, const QDate &maxDate, const QString &minWarnMsg, const QString &maxWarnMsg) { if (minDate.isValid() && maxDate.isValid() && minDate > maxDate) { return; } if (minDate != m_minDate || maxDate != m_maxDate || minWarnMsg != m_minWarnMsg || maxWarnMsg != m_maxWarnMsg) { m_minDate = minDate; m_maxDate = maxDate; m_minWarnMsg = minWarnMsg; m_maxWarnMsg = maxWarnMsg; } enableMenuDates(); } bool KDateComboBoxPrivate::isInDateRange(const QDate &date) const { return date.isValid() && (!m_minDate.isValid() || date >= m_minDate) && (!m_maxDate.isValid() || date <= m_maxDate); } void KDateComboBoxPrivate::selectDate(QAction *action) { if (action->objectName() != QLatin1String("DatePicker")) { QDate date = action->data().toDate(); if (isInDateRange(date)) { enterDate(date); } } } void KDateComboBoxPrivate::clickDate() { QDate date = m_datePicker->date(); if (isInDateRange(date)) { enterDate(date); } } void KDateComboBoxPrivate::editDate(const QString &text) { m_warningShown = false; m_date = q->locale().toDate(text, dateFormat(m_displayFormat)); + m_edited = true; emit q->dateEdited(m_date); } void KDateComboBoxPrivate::parseDate() { m_date = q->locale().toDate(q->lineEdit()->text(), dateFormat(m_displayFormat)); } void KDateComboBoxPrivate::enterDate(const QDate &date) { q->setDate(date); // Re-add the combo box item in order to retain the correct widget width q->blockSignals(true); q->clear(); q->setSizeAdjustPolicy(QComboBox::AdjustToContents); q->addItem(formatDate(m_date)); q->setCurrentIndex(0); q->setSizeAdjustPolicy(QComboBox::AdjustToContentsOnFirstShow); q->blockSignals(false); m_dateMenu->hide(); warnDate(); emit q->dateEntered(m_date); } void KDateComboBoxPrivate::warnDate() { if (!m_warningShown && !q->isValid() && (m_options & KDateComboBox::WarnOnInvalid) == KDateComboBox::WarnOnInvalid) { QString warnMsg; if (!m_date.isValid()) { warnMsg = KDateComboBox::tr("The date you entered is invalid", "@info"); } else if (m_minDate.isValid() && m_date < m_minDate) { if (m_minWarnMsg.isEmpty()) { warnMsg = KDateComboBox::tr("Date cannot be earlier than %1", "@info").arg(formatDate(m_minDate)); } else { warnMsg = m_minWarnMsg; warnMsg.replace(QStringLiteral("%1"), formatDate(m_minDate)); } } else if (m_maxDate.isValid() && m_date > m_maxDate) { if (m_maxWarnMsg.isEmpty()) { warnMsg = KDateComboBox::tr("Date cannot be later than %1", "@info").arg(formatDate(m_maxDate)); } else { warnMsg = m_maxWarnMsg; warnMsg.replace(QStringLiteral("%1"), formatDate(m_maxDate)); } } m_warningShown = true; KMessageBox::sorry(q, warnMsg); } } KDateComboBox::KDateComboBox(QWidget *parent) : QComboBox(parent), d(new KDateComboBoxPrivate(this)) { setEditable(true); setMaxVisibleItems(1); setInsertPolicy(QComboBox::NoInsert); d->m_datePicker->installEventFilter(this); d->initDateWidget(); d->updateDateWidget(); connect(d->m_dateMenu, &QMenu::triggered, this, [this](QAction *action) { d->selectDate(action); }); connect(this, &QComboBox::editTextChanged, this, [this](const QString &text) { d->editDate(text); }); + connect(lineEdit(), &QLineEdit::returnPressed, + this, [this]() { + if (d->m_edited) { + d->enterDate(date()); + emit dateChanged(date()); + } + }); + connect(d->m_datePicker, &KDatePicker::dateEntered, this, [this](const QDate &date) { d->enterDate(date); }); connect(d->m_datePicker, &KDatePicker::tableClicked, this, [this]() { d->clickDate(); }); } KDateComboBox::~KDateComboBox() { delete d; } QDate KDateComboBox::date() const { d->parseDate(); return d->m_date; } void KDateComboBox::setDate(const QDate &date) { if (date == d->m_date) { return; } + d->m_edited = false; assignDate(date); d->updateDateWidget(); emit dateChanged(d->m_date); } void KDateComboBox::assignDate(const QDate &date) { d->m_date = date; } bool KDateComboBox::isValid() const { d->parseDate(); return d->isInDateRange(d->m_date); } bool KDateComboBox::isNull() const { return lineEdit()->text().isEmpty(); } KDateComboBox::Options KDateComboBox::options() const { return d->m_options; } void KDateComboBox::setOptions(Options options) { if (options != d->m_options) { d->m_options = options; d->initDateWidget(); d->updateDateWidget(); } } QDate KDateComboBox::minimumDate() const { return d->m_minDate; } void KDateComboBox::setMinimumDate(const QDate &minDate, const QString &minWarnMsg) { if (minDate.isValid()) { d->setDateRange(minDate, d->m_maxDate, minWarnMsg, d->m_maxWarnMsg); } } void KDateComboBox::resetMinimumDate() { d->setDateRange(QDate(), d->m_maxDate, QString(), d->m_maxWarnMsg); } QDate KDateComboBox::maximumDate() const { return d->m_maxDate; } void KDateComboBox::setMaximumDate(const QDate &maxDate, const QString &maxWarnMsg) { if (maxDate.isValid()) { d->setDateRange(d->m_minDate, maxDate, d->m_minWarnMsg, maxWarnMsg); } } void KDateComboBox::resetMaximumDate() { d->setDateRange(d->m_minDate, QDate(), d->m_minWarnMsg, QString()); } void KDateComboBox::setDateRange(const QDate &minDate, const QDate &maxDate, const QString &minWarnMsg, const QString &maxWarnMsg) { if (minDate.isValid() && maxDate.isValid()) { d->setDateRange(minDate, maxDate, minWarnMsg, maxWarnMsg); } } void KDateComboBox::resetDateRange() { d->setDateRange(QDate(), QDate(), QString(), QString()); } QLocale::FormatType KDateComboBox::displayFormat() const { return d->m_displayFormat; } void KDateComboBox::setDisplayFormat(QLocale::FormatType format) { if (format != d->m_displayFormat) { d->m_displayFormat = format; d->initDateWidget(); d->updateDateWidget(); } } QMap KDateComboBox::dateMap() const { return d->m_dateMap; } void KDateComboBox::setDateMap(QMap dateMap) { if (dateMap != d->m_dateMap) { d->m_dateMap.clear(); d->m_dateMap = dateMap; d->initDateWidget(); } } bool KDateComboBox::eventFilter(QObject *object, QEvent *event) { return QComboBox::eventFilter(object, event); } void KDateComboBox::keyPressEvent(QKeyEvent *keyEvent) { QDate temp; switch (keyEvent->key()) { case Qt::Key_Down: temp = d->m_date.addDays(-1); break; case Qt::Key_Up: temp = d->m_date.addDays(1); break; case Qt::Key_PageDown: temp = d->m_date.addMonths(-1); break; case Qt::Key_PageUp: temp = d->m_date.addMonths(1); break; default: QComboBox::keyPressEvent(keyEvent); return; } if (d->isInDateRange(temp)) { d->enterDate(temp); } } void KDateComboBox::focusOutEvent(QFocusEvent *event) { d->parseDate(); d->warnDate(); + if (d->m_edited) { + d->m_edited = false; + emit dateChanged(d->m_date); + } QComboBox::focusOutEvent(event); } void KDateComboBox::showPopup() { if (!isEditable() || !d->m_dateMenu || (d->m_options & KDateComboBox::SelectDate) != KDateComboBox::SelectDate) { return; } d->m_datePicker->blockSignals(true); d->m_datePicker->setDate(d->m_date); d->m_datePicker->blockSignals(false); const QRect desk = QApplication::desktop()->screenGeometry(this); QPoint popupPoint = mapToGlobal(QPoint(0, 0)); const int dateFrameHeight = d->m_dateMenu->sizeHint().height(); if (popupPoint.y() + height() + dateFrameHeight > desk.bottom()) { popupPoint.setY(popupPoint.y() - dateFrameHeight); } else { popupPoint.setY(popupPoint.y() + height()); } const int dateFrameWidth = d->m_dateMenu->sizeHint().width(); if (popupPoint.x() + dateFrameWidth > desk.right()) { popupPoint.setX(desk.right() - dateFrameWidth); } if (popupPoint.x() < desk.left()) { popupPoint.setX(desk.left()); } if (popupPoint.y() < desk.top()) { popupPoint.setY(desk.top()); } d->m_dateMenu->popup(popupPoint); } void KDateComboBox::hidePopup() { QComboBox::hidePopup(); } void KDateComboBox::mousePressEvent(QMouseEvent *event) { QComboBox::mousePressEvent(event); } void KDateComboBox::wheelEvent(QWheelEvent *event) { QDate temp; if (event->delta() < 0) { temp = d->m_date.addDays(-1); } else { temp = d->m_date.addDays(1); } if (d->isInDateRange(temp)) { d->enterDate(temp); } } void KDateComboBox::focusInEvent(QFocusEvent *event) { QComboBox::focusInEvent(event); } void KDateComboBox::resizeEvent(QResizeEvent *event) { QComboBox::resizeEvent(event); } #include "moc_kdatecombobox.cpp" diff --git a/src/kdatecombobox.h b/src/kdatecombobox.h index 349918c..782b0d1 100644 --- a/src/kdatecombobox.h +++ b/src/kdatecombobox.h @@ -1,315 +1,316 @@ /* Copyright 2011 John Layt This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KDATECOMBOBOX_H #define KDATECOMBOBOX_H #include #include #include class KDateComboBoxPrivate; /** * @class KDateComboBox kdatecombobox.h KDateComboBox * * @short A combobox for dates. */ class KWIDGETSADDONS_EXPORT KDateComboBox : public QComboBox { Q_OBJECT Q_PROPERTY(QDate date READ date WRITE setDate NOTIFY dateChanged USER true) Q_PROPERTY(QDate minimumDate READ minimumDate WRITE setMinimumDate RESET resetMinimumDate) Q_PROPERTY(QDate maximumDate READ maximumDate WRITE setMaximumDate RESET resetMaximumDate) Q_PROPERTY(Options options READ options WRITE setOptions) public: /** * Options provided by the widget * @see options() * @see setOptions() */ enum Option { EditDate = 0x0001, /**< Allow the user to manually edit the date in the combo line edit */ SelectDate = 0x0002, /**< Allow the user to select the date from a drop-down menu */ DatePicker = 0x0004, /**< Show a date picker in the drop-down */ DateKeywords = 0x0008, /**< Show date keywords in the drop-down */ WarnOnInvalid = 0x0010 /**< Show a warning on focus out if the date is invalid */ }; Q_DECLARE_FLAGS(Options, Option) Q_FLAG(Options) /** * Create a new KDateComboBox widget * * By default the EditDate, SelectDate, DatePicker and DateKeywords options * are enabled, the ShortDate format is used and the date is set to the * current date. */ explicit KDateComboBox(QWidget *parent = nullptr); /** * Destroy the widget */ virtual ~KDateComboBox(); /** * Return the currently selected date * * @return the currently selected date */ QDate date() const; /** * Return if the current user input is valid * * If the user input is null then it is not valid * * @return if the current user input is valid * * @see isNull() */ bool isValid() const; /** * Return if the current user input is null * * @return if the current user input is null * * @see isValid() */ bool isNull() const; /** * Return the currently set widget options * * @return the currently set widget options */ Options options() const; /** * Return the currently set date display format * * By default this is the Short Format * * @return the currently set date format */ QLocale::FormatType displayFormat() const; /** * Return the current minimum date * * @return the current minimum date */ QDate minimumDate() const; /** * Return the current maximum date * * @return the current maximum date */ QDate maximumDate() const; /** * Return the map of dates listed in the drop-down and their displayed * string forms. * * @return the select date map * * @see setDateMap() */ QMap dateMap() const; Q_SIGNALS: /** - * Signal if the date has been manually entered or selected by the user. + * Signal if the date has been manually entered (by typing a date and losing focus, or pressing Enter) + * or selected by the user (using the popup selector, or up, down, page up, page down keys, or the mouse wheel). * - * The returned date may be invalid. + * The emitted date may be invalid. * * @param date the new date */ void dateEntered(const QDate &date); /** * Signal if the date has been changed either manually by the user * or programatically. * - * The returned date may be invalid. + * The emitted date may be invalid. * * @param date the new date */ void dateChanged(const QDate &date); /** * Signal if the date is being manually edited by the user. * - * The returned date may be invalid. + * The emitted date may be invalid, or may not yet be what the user intends as the final date. * * @param date the new date */ void dateEdited(const QDate &date); public Q_SLOTS: /** * Set the currently selected date * * You can set an invalid date or a date outside the valid range, validity * checking is only done via isValid(). * * @param date the new date */ void setDate(const QDate &date); /** * Set the new widget options * * @param options the new widget options */ void setOptions(Options options); /** * Sets the date format to display. * * By default is the Short Format. * * @param format the date format to use */ void setDisplayFormat(QLocale::FormatType format); /** * Set the valid date range to be applied by isValid(). * * Both dates must be valid and the minimum date must be less than or equal * to the maximum date, otherwise the date range will not be set. * * @param minDate the minimum date * @param maxDate the maximum date * @param minWarnMsg the minimum warning message * @param maxWarnMsg the maximum warning message */ void setDateRange(const QDate &minDate, const QDate &maxDate, const QString &minWarnMsg = QString(), const QString &maxWarnMsg = QString()); /** * Reset the minimum and maximum date to the default values. * @see setDateRange() */ void resetDateRange(); /** * Set the minimum allowed date. * * If the date is invalid, or greater than current maximum, * then the minimum will not be set. * * @param minDate the minimum date * @param minWarnMsg the minimum warning message * * @see minimumDate() * @see maximumDate() * @see setMaximumDate() * @see setDateRange() */ void setMinimumDate(const QDate &minDate, const QString &minWarnMsg = QString()); /** * Reset the minimum date to the default. * * The default is to have no minimum date. */ void resetMinimumDate(); /** * Set the maximum allowed date. * * If the date is invalid, or less than current minimum, * then the maximum will not be set. * * @param maxDate the maximum date * @param maxWarnMsg the maximum warning message * * @see minimumDate() * @see maximumDate() * @see setMaximumDate() * @see setDateRange() */ void setMaximumDate(const QDate &maxDate, const QString &maxWarnMsg = QString()); /** * Reset the maximum date to the default * * The default is to have no maximum date. */ void resetMaximumDate(); /** * Set the list of dates able to be selected from the drop-down and the * string form to display for those dates, e.g. "2010-01-01" and "Yesterday". * * Any invalid or duplicate dates will be used, the list will NOT be * sorted, and the minimum and maximum date will not be affected. * * The @p dateMap is keyed by the date to be listed and the value is the * string to be displayed. If you want the date to be displayed in the * default date format then the string should be null. If you want a * separator to be displayed then set the string to "separator". * * @param dateMap the map of dates able to be selected * * @see dateMap() */ void setDateMap(QMap dateMap); protected: bool eventFilter(QObject *object, QEvent *event) Q_DECL_OVERRIDE; void showPopup() Q_DECL_OVERRIDE; void hidePopup() Q_DECL_OVERRIDE; void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE; void wheelEvent(QWheelEvent *event) Q_DECL_OVERRIDE; void keyPressEvent(QKeyEvent *event) Q_DECL_OVERRIDE; void focusInEvent(QFocusEvent *event) Q_DECL_OVERRIDE; void focusOutEvent(QFocusEvent *event) Q_DECL_OVERRIDE; void resizeEvent(QResizeEvent *event) Q_DECL_OVERRIDE; /** * Assign the date for the widget. * * Virtual to allow sub-classes to apply extra validation rules. * * @param date the new date */ virtual void assignDate(const QDate &date); private: friend class KDateComboBoxPrivate; KDateComboBoxPrivate *const d; }; Q_DECLARE_OPERATORS_FOR_FLAGS(KDateComboBox::Options) #endif // KDATECOMBOBOX_H