KWidgetsAddons

kdatecombobox.cpp
1/*
2 SPDX-FileCopyrightText: 2011 John Layt <john@layt.net>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7#include "kdatecombobox.h"
8
9#include "common_helpers_p.h"
10#include "kdatepickerpopup.h"
11#include "kdaterangecontrol_p.h"
12
13#include <QAbstractItemView>
14#include <QApplication>
15#include <QDate>
16#include <QKeyEvent>
17#include <QLineEdit>
18#include <QList>
19#include <QMenu>
20#include <QScreen>
21#include <QWidgetAction>
22
23#include "kdatepicker.h"
24#include "kmessagebox.h"
25
26class KDateComboBoxPrivate : public KDateRangeControlPrivate
27{
28public:
29 KDateComboBoxPrivate(KDateComboBox *qq);
30
31 // TODO: Find a way to get that from QLocale
32#if 0
33 QDate defaultMinDate();
34 QDate defaultMaxDate();
35#endif
36
37 QString dateFormat(QLocale::FormatType format);
38 QString formatDate(const QDate &date);
39
40 void initDateWidget();
41 void updateDateWidget();
42 void setDateRange(const QDate &minDate, const QDate &maxDate, const QString &minWarnMsg, const QString &maxWarnMsg);
43 using KDateRangeControlPrivate::setDateRange;
44
45 void editDate(const QString &text);
46 void enterDate(const QDate &date);
47 void parseDate();
48 void warnDate();
49
50 KDateComboBox *const q;
51 KDatePickerPopup *m_dateMenu;
52
53 QDate m_date;
54 KDateComboBox::Options m_options;
55 QString m_minWarnMsg;
56 QString m_maxWarnMsg;
57 bool m_warningShown;
58 bool m_edited; // and dateChanged not yet emitted
59 QLocale::FormatType m_displayFormat;
60};
61
62KDateComboBoxPrivate::KDateComboBoxPrivate(KDateComboBox *qq)
63 : q(qq)
64 , m_dateMenu(new KDatePickerPopup(KDatePickerPopup::DatePicker | KDatePickerPopup::Words, QDate::currentDate(), qq))
65 , m_warningShown(false)
66 , m_edited(false)
67 , m_displayFormat(QLocale::ShortFormat)
68{
70 m_date = QDate::currentDate();
71 // m_minDate = defaultMinDate();
72 // m_maxDate = defaultMaxDate();
73}
74
75#if 0
76QDate KDateComboBoxPrivate::defaultMinDate()
77{
78 return m_date.calendar()->earliestValidDate();
79}
80
81QDate KDateComboBoxPrivate::defaultMaxDate()
82{
83 return m_date.calendar()->latestValidDate();
84}
85#endif
86
87QString KDateComboBoxPrivate::dateFormat(QLocale::FormatType format)
88{
89 return dateFormatWith4DigitYear(q->locale(), format);
90}
91
92QString KDateComboBoxPrivate::formatDate(const QDate &date)
93{
94 return q->locale().toString(date, dateFormat(m_displayFormat));
95}
96
97void KDateComboBoxPrivate::initDateWidget()
98{
99 q->blockSignals(true);
100 q->clear();
101
102 // If EditTime then set the line edit
104
105 // If SelectDate then make list items visible
109 q->setMaxVisibleItems(1);
110 } else {
111 q->setMaxVisibleItems(0);
112 }
113
115 q->addItem(formatDate(m_date));
116 q->setCurrentIndex(0);
118 q->blockSignals(false);
119
121 if (m_options & KDateComboBox::DatePicker) {
123 }
124 if (m_options & KDateComboBox::DateKeywords) {
126 }
127 m_dateMenu->setModes(modes);
128}
129
130void KDateComboBoxPrivate::updateDateWidget()
131{
132 q->blockSignals(true);
133 m_dateMenu->setDate(m_date);
134 int pos = q->lineEdit()->cursorPosition();
135 q->setItemText(0, formatDate(m_date));
136 q->lineEdit()->setText(formatDate(m_date));
137 q->lineEdit()->setCursorPosition(pos);
138 q->blockSignals(false);
139}
140
141void KDateComboBoxPrivate::setDateRange(const QDate &minDate, const QDate &maxDate, const QString &minWarnMsg, const QString &maxWarnMsg)
142{
143 if (!setDateRange(minDate, maxDate)) {
144 return;
145 }
146
147 m_dateMenu->setDateRange(minDate, maxDate);
148 m_minWarnMsg = minWarnMsg;
149 m_maxWarnMsg = maxWarnMsg;
150}
151
152void KDateComboBoxPrivate::editDate(const QString &text)
153{
154 m_warningShown = false;
155 m_date = q->locale().toDate(text, dateFormat(m_displayFormat));
156 m_edited = true;
157 Q_EMIT q->dateEdited(m_date);
158}
159
160void KDateComboBoxPrivate::parseDate()
161{
162 m_date = q->locale().toDate(q->lineEdit()->text(), dateFormat(m_displayFormat));
163}
164
165void KDateComboBoxPrivate::enterDate(const QDate &date)
166{
167 q->setDate(date);
168 // Re-add the combo box item in order to retain the correct widget width
169 q->blockSignals(true);
170 q->clear();
172 q->addItem(formatDate(m_date));
173 q->setCurrentIndex(0);
175 q->blockSignals(false);
176
177 m_dateMenu->hide();
178 warnDate();
179 Q_EMIT q->dateEntered(m_date);
180}
181
182void KDateComboBoxPrivate::warnDate()
183{
184 if (!m_warningShown && !q->isValid() && (m_options & KDateComboBox::WarnOnInvalid) == KDateComboBox::WarnOnInvalid) {
185 QString warnMsg;
186 if (!m_date.isValid()) {
187 warnMsg = KDateComboBox::tr("The date you entered is invalid", "@info");
188 } else if (m_minDate.isValid() && m_date < m_minDate) {
189 if (m_minWarnMsg.isEmpty()) {
190 warnMsg = KDateComboBox::tr("Date cannot be earlier than %1", "@info").arg(formatDate(m_minDate));
191 } else {
192 warnMsg = m_minWarnMsg;
193 warnMsg.replace(QLatin1String("%1"), formatDate(m_minDate));
194 }
195 } else if (m_maxDate.isValid() && m_date > m_maxDate) {
196 if (m_maxWarnMsg.isEmpty()) {
197 warnMsg = KDateComboBox::tr("Date cannot be later than %1", "@info").arg(formatDate(m_maxDate));
198 } else {
199 warnMsg = m_maxWarnMsg;
200 warnMsg.replace(QLatin1String("%1"), formatDate(m_maxDate));
201 }
202 }
203 m_warningShown = true;
204 KMessageBox::error(q, warnMsg);
205 }
206}
207
209 : QComboBox(parent)
210 , d(new KDateComboBoxPrivate(this))
211{
212 setEditable(true);
215 d->initDateWidget();
216 d->updateDateWidget();
217
218 connect(d->m_dateMenu, &KDatePickerPopup::dateChanged, this, [this](QDate date) {
219 if (d->isInDateRange(date)) {
220 d->enterDate(date);
221 }
222 });
223
224 connect(this, &QComboBox::editTextChanged, this, [this](const QString &text) {
225 d->editDate(text);
226 });
227
228 connect(lineEdit(), &QLineEdit::returnPressed, this, [this]() {
229 if (d->m_edited) {
230 d->enterDate(date());
231 Q_EMIT dateChanged(date());
232 }
233 });
234}
235
237
238QDate KDateComboBox::date() const
239{
240 d->parseDate();
241 return d->m_date;
242}
243
245{
246 if (date == d->m_date) {
247 return;
248 }
249
250 d->m_edited = false;
251 assignDate(date);
252 d->updateDateWidget();
253 Q_EMIT dateChanged(d->m_date);
254}
255
257{
258 d->m_date = date;
259}
260
262{
263 d->parseDate();
264 return d->isInDateRange(d->m_date);
265}
266
268{
269 return lineEdit()->text().isEmpty();
270}
271
272KDateComboBox::Options KDateComboBox::options() const
273{
274 return d->m_options;
275}
276
278{
279 if (options != d->m_options) {
280 d->m_options = options;
281 d->initDateWidget();
282 d->updateDateWidget();
283 }
284}
285
286QDate KDateComboBox::minimumDate() const
287{
288 return d->m_minDate;
289}
290
291void KDateComboBox::setMinimumDate(const QDate &minDate, const QString &minWarnMsg)
292{
293 if (minDate.isValid()) {
294 d->setDateRange(minDate, d->m_maxDate, minWarnMsg, d->m_maxWarnMsg);
295 }
296}
297
299{
300 d->setDateRange(QDate(), d->m_maxDate, QString(), d->m_maxWarnMsg);
301}
302
303QDate KDateComboBox::maximumDate() const
304{
305 return d->m_maxDate;
306}
307
308void KDateComboBox::setMaximumDate(const QDate &maxDate, const QString &maxWarnMsg)
309{
310 if (maxDate.isValid()) {
311 d->setDateRange(d->m_minDate, maxDate, d->m_minWarnMsg, maxWarnMsg);
312 }
313}
314
316{
317 d->setDateRange(d->m_minDate, QDate(), d->m_minWarnMsg, QString());
318}
319
320void KDateComboBox::setDateRange(const QDate &minDate, const QDate &maxDate, const QString &minWarnMsg, const QString &maxWarnMsg)
321{
322 if (minDate.isValid() && maxDate.isValid()) {
323 d->setDateRange(minDate, maxDate, minWarnMsg, maxWarnMsg);
324 }
325}
326
328{
329 d->setDateRange(QDate(), QDate(), QString(), QString());
330}
331
333{
334 return d->m_displayFormat;
335}
336
338{
339 if (format != d->m_displayFormat) {
340 d->m_displayFormat = format;
341 d->initDateWidget();
342 d->updateDateWidget();
343 }
344}
345
347{
348 return d->m_dateMenu->dateMap();
349}
350
352{
353 d->m_dateMenu->setDateMap(dateMap);
354}
355
356bool KDateComboBox::eventFilter(QObject *object, QEvent *event)
357{
358 return QComboBox::eventFilter(object, event);
359}
360
361void KDateComboBox::keyPressEvent(QKeyEvent *keyEvent)
362{
363 QDate temp;
364 switch (keyEvent->key()) {
365 case Qt::Key_Down:
366 temp = d->m_date.addDays(-1);
367 break;
368 case Qt::Key_Up:
369 temp = d->m_date.addDays(1);
370 break;
371 case Qt::Key_PageDown:
372 temp = d->m_date.addMonths(-1);
373 break;
374 case Qt::Key_PageUp:
375 temp = d->m_date.addMonths(1);
376 break;
377 default:
378 QComboBox::keyPressEvent(keyEvent);
379 return;
380 }
381 if (d->isInDateRange(temp)) {
382 d->enterDate(temp);
383 }
384}
385
386void KDateComboBox::focusOutEvent(QFocusEvent *event)
387{
388 d->parseDate();
389 d->warnDate();
390 if (d->m_edited) {
391 d->m_edited = false;
392 Q_EMIT dateChanged(d->m_date);
393 }
395}
396
397void KDateComboBox::showPopup()
398{
399 if (!isEditable() || !d->m_dateMenu //
401 return;
402 }
403
404 d->m_dateMenu->setDate(d->m_date);
405
406 const QRect desk = screen()->geometry();
407
409
410 const int dateFrameHeight = d->m_dateMenu->sizeHint().height();
411 if (popupPoint.y() + height() + dateFrameHeight > desk.bottom()) {
413 } else {
414 popupPoint.setY(popupPoint.y() + height());
415 }
416
417 const int dateFrameWidth = d->m_dateMenu->sizeHint().width();
418 if (popupPoint.x() + dateFrameWidth > desk.right()) {
419 popupPoint.setX(desk.right() - dateFrameWidth);
420 }
421
422 if (popupPoint.x() < desk.left()) {
423 popupPoint.setX(desk.left());
424 }
425
426 if (popupPoint.y() < desk.top()) {
427 popupPoint.setY(desk.top());
428 }
429
430 d->m_dateMenu->popup(popupPoint);
431}
432
433void KDateComboBox::hidePopup()
434{
436}
437
438void KDateComboBox::mousePressEvent(QMouseEvent *event)
439{
441}
442
443void KDateComboBox::wheelEvent(QWheelEvent *event)
444{
445 QDate temp;
446 if (event->angleDelta().y() < 0) {
447 temp = d->m_date.addDays(-1);
448 } else {
449 temp = d->m_date.addDays(1);
450 }
451 if (d->isInDateRange(temp)) {
452 d->enterDate(temp);
453 }
454}
455
456void KDateComboBox::focusInEvent(QFocusEvent *event)
457{
459}
460
461void KDateComboBox::resizeEvent(QResizeEvent *event)
462{
464}
465
466#include "moc_kdatecombobox.cpp"
A combobox for dates.
KDateComboBox(QWidget *parent=nullptr)
Create a new KDateComboBox widget.
void setOptions(Options options)
Set the new widget options.
void setDisplayFormat(QLocale::FormatType format)
Sets the date format to display.
void dateEdited(const QDate &date)
Signal if the date is being manually edited by the user.
bool isNull() const
Return if the current user input is null.
void resetMinimumDate()
Reset the minimum date to the default.
@ SelectDate
Allow the user to select the date from a drop-down menu.
@ DateKeywords
Show date keywords in the drop-down.
@ WarnOnInvalid
Show a warning on focus out if the date is invalid.
@ EditDate
Allow the user to manually edit the date in the combo line edit.
@ DatePicker
Show a date picker in the drop-down.
void setMinimumDate(const QDate &minDate, const QString &minWarnMsg=QString())
Set the minimum allowed date.
void setDateMap(QMap< QDate, QString > dateMap)
Set the list of dates able to be selected from the drop-down and the string form to display for those...
void setDate(const QDate &date)
Set the currently selected date.
void setMaximumDate(const QDate &maxDate, const QString &maxWarnMsg=QString())
Set the maximum allowed date.
void resetDateRange()
Reset the minimum and maximum date to the default values.
QMap< QDate, QString > dateMap() const
Return the map of dates listed in the drop-down and their displayed string forms.
virtual void assignDate(const QDate &date)
Assign the date for the widget.
void resetMaximumDate()
Reset the maximum date to the default.
bool isValid() const
Return if the current user input is valid.
void dateChanged(const QDate &date)
Signal if the date has been changed either manually by the user or programmatically.
void dateEntered(const QDate &date)
Signal if the date has been manually entered (by typing a date and losing focus, or pressing Enter) o...
~KDateComboBox() override
Destroy the widget.
QLocale::FormatType displayFormat() const
Return the currently set date display format.
void setDateRange(const QDate &minDate, const QDate &maxDate, const QString &minWarnMsg=QString(), const QString &maxWarnMsg=QString())
Set the valid date range to be applied by isValid().
This menu helps the user to select a date quickly.
void setDateRange(const QDate &minDate, const QDate &maxDate)
Sets the range of dates that can be accepted.
@ Words
A menu-item with list of words that describe a date.
@ DatePicker
A menu-item with a KDatePicker.
void dateChanged(const QDate &date)
This signal is emitted whenever the user has selected a new date.
void setModes(Modes modes)
Set the selection modes to use.
void setDate(QDate date)
Sets the current date.
void error(QWidget *parent, const QString &text, const QString &title, Options options)
Display an "Error" dialog.
void addItem(const QIcon &icon, const QString &text, const QVariant &userData)
void clear()
void setCurrentIndex(int index)
void editTextChanged(const QString &text)
void setEditable(bool editable)
virtual bool event(QEvent *event) override
virtual void focusInEvent(QFocusEvent *e) override
virtual void focusOutEvent(QFocusEvent *e) override
virtual void hidePopup()
void setInsertPolicy(InsertPolicy policy)
virtual void keyPressEvent(QKeyEvent *e) override
QLineEdit * lineEdit() const const
void setMaxVisibleItems(int maxItems)
virtual void mousePressEvent(QMouseEvent *e) override
virtual void resizeEvent(QResizeEvent *e) override
void setItemText(int index, const QString &text)
void setSizeAdjustPolicy(SizeAdjustPolicy policy)
QDate currentDate()
bool isValid(int year, int month, int day)
void setReadOnly(bool)
void returnPressed()
void setText(const QString &)
Q_EMITQ_EMIT
bool blockSignals(bool block)
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
virtual bool eventFilter(QObject *watched, QEvent *event)
T qobject_cast(QObject *object)
QString tr(const char *sourceText, const char *disambiguation, int n)
QString arg(Args &&... args) const const
bool isEmpty() const const
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
Key_Down
void keyEvent(KeyAction action, QWidget *widget, Qt::Key key, Qt::KeyboardModifiers modifier, int delay)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
void hide()
QPoint mapToGlobal(const QPoint &pos) const const
QScreen * screen() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:14:43 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.