1/*
2 SPDX-FileCopyrightText: 2011 John Layt <john@layt.net>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7#include "kdatetimeedit.h"
8
9#include "kdatecombobox.h"
10#include "kdatepicker.h"
11#include "kmessagebox.h"
12
13#include "ui_kdatetimeedit.h"
14
15class KDateTimeEditPrivate
16{
17public:
18 KDateTimeEditPrivate(KDateTimeEdit *qq);
19 virtual ~KDateTimeEditPrivate();
20
21 QDateTime defaultMinDateTime();
22 QDateTime defaultMaxDateTime();
23
24 void initWidgets();
25 void initDateWidget();
26 void initTimeWidget();
27 void initCalendarWidget();
28 void updateCalendarWidget();
29 void initTimeZoneWidget();
30 void updateTimeZoneWidget();
31
32 void warnDateTime();
33
34 // private Q_SLOTS:
35 void selectCalendar(int index);
36 void enterCalendar(const QLocale &calendarLocale);
37 void selectTimeZone(int index);
38 void enterTimeZone(const QByteArray &zoneId);
39
40 KDateTimeEdit *const q;
41
42 KDateTimeEdit::Options m_options;
43 QDateTime m_dateTime;
44 QDateTime m_minDateTime;
45 QDateTime m_maxDateTime;
46 QString m_minWarnMsg;
47 QString m_maxWarnMsg;
48
49 QList<QLocale> m_calendarLocales;
50 QList<QTimeZone> m_zones;
51
52 Ui::KDateTimeEdit ui;
53};
54
55KDateTimeEditPrivate::KDateTimeEditPrivate(KDateTimeEdit *qq)
56 : q(qq)
57{
58 m_options = KDateTimeEdit::ShowDate | KDateTimeEdit::EditDate | KDateTimeEdit::SelectDate | KDateTimeEdit::ShowTime | KDateTimeEdit::EditTime
59 | KDateTimeEdit::SelectTime | KDateTimeEdit::DatePicker | KDateTimeEdit::DateKeywords;
60 m_dateTime = QDateTime::currentDateTime();
61 m_dateTime.setTime(QTime(0, 0, 0));
62 m_calendarLocales << q->locale();
63 const auto zoneIds = QTimeZone::availableTimeZoneIds();
64 m_zones.reserve(asize: zoneIds.size());
65 for (const QByteArray &zoneId : zoneIds) {
66 m_zones << QTimeZone(zoneId);
67 }
68}
69
70KDateTimeEditPrivate::~KDateTimeEditPrivate()
71{
72}
73
74QDateTime KDateTimeEditPrivate::defaultMinDateTime()
75{
76 // TODO: Find a way to get it from QLocale
77 // return KDateTime(calendar()->earliestValidDate(), QTime(0, 0, 0, 0));
78 return QDateTime();
79}
80
81QDateTime KDateTimeEditPrivate::defaultMaxDateTime()
82{
83 // TODO: Find a way to get it from QLocale
84 // return KDateTime(calendar()->latestValidDate(), QTime(23, 59, 59, 999));
85 return QDateTime();
86}
87
88void KDateTimeEditPrivate::initWidgets()
89{
90 initDateWidget();
91 initTimeWidget();
92 initCalendarWidget();
93 initTimeZoneWidget();
94}
95
96void KDateTimeEditPrivate::initDateWidget()
97{
98 ui.m_dateCombo->blockSignals(b: true);
99 ui.m_dateCombo->setVisible((m_options & KDateTimeEdit::ShowDate) == KDateTimeEdit::ShowDate);
100 KDateComboBox::Options options;
101 if ((m_options & KDateTimeEdit::EditDate) == KDateTimeEdit::EditDate) {
102 options = options | KDateComboBox::EditDate;
103 }
104 if ((m_options & KDateTimeEdit::SelectDate) == KDateTimeEdit::SelectDate) {
105 options = options | KDateComboBox::SelectDate;
106 }
107 if ((m_options & KDateTimeEdit::DatePicker) == KDateTimeEdit::DatePicker) {
108 options = options | KDateComboBox::DatePicker;
109 }
110 if ((m_options & KDateTimeEdit::DateKeywords) == KDateTimeEdit::DateKeywords) {
111 options = options | KDateComboBox::DateKeywords;
112 }
113 ui.m_dateCombo->setOptions(options);
114 ui.m_dateCombo->blockSignals(b: false);
115}
116
117void KDateTimeEditPrivate::initTimeWidget()
118{
119 ui.m_timeCombo->blockSignals(b: true);
120 ui.m_timeCombo->setVisible((m_options & KDateTimeEdit::ShowTime) == KDateTimeEdit::ShowTime);
121 KTimeComboBox::Options options;
122 if ((m_options & KDateTimeEdit::EditTime) == KDateTimeEdit::EditTime) {
123 options = options | KTimeComboBox::EditTime;
124 }
125 if ((m_options & KDateTimeEdit::SelectTime) == KDateTimeEdit::SelectTime) {
126 options = options | KTimeComboBox::SelectTime;
127 }
128 if ((m_options & KDateTimeEdit::ForceTime) == KDateTimeEdit::ForceTime) {
129 options = options | KTimeComboBox::ForceTime;
130 }
131 ui.m_timeCombo->setOptions(options);
132 ui.m_timeCombo->blockSignals(b: false);
133}
134
135void KDateTimeEditPrivate::initCalendarWidget()
136{
137 ui.m_calendarCombo->blockSignals(b: true);
138 ui.m_calendarCombo->clear();
139 for (const QLocale &calendarLocale : std::as_const(t&: m_calendarLocales)) {
140 ui.m_calendarCombo->addItem(atext: calendarLocale.name(), auserData: calendarLocale);
141 }
142 ui.m_calendarCombo->setCurrentIndex(ui.m_calendarCombo->findData(data: q->locale()));
143 ui.m_calendarCombo->setVisible((m_options & KDateTimeEdit::ShowCalendar) == KDateTimeEdit::ShowCalendar);
144 ui.m_calendarCombo->setEnabled((m_options & KDateTimeEdit::SelectCalendar) == KDateTimeEdit::SelectCalendar);
145 ui.m_calendarCombo->setEditable(false);
146 ui.m_calendarCombo->blockSignals(b: false);
147}
148
149void KDateTimeEditPrivate::updateCalendarWidget()
150{
151 ui.m_calendarCombo->blockSignals(b: true);
152 ui.m_calendarCombo->setCurrentIndex(ui.m_calendarCombo->findData(data: q->locale()));
153 ui.m_calendarCombo->blockSignals(b: false);
154}
155
156void KDateTimeEditPrivate::selectCalendar(int index)
157{
158 enterCalendar(calendarLocale: ui.m_calendarCombo->itemData(index).toLocale());
159}
160
161void KDateTimeEditPrivate::enterCalendar(const QLocale &calendarLocale)
162{
163 q->setLocale(calendarLocale);
164 Q_EMIT q->calendarEntered(calendarLocale: q->locale());
165}
166
167void KDateTimeEditPrivate::initTimeZoneWidget()
168{
169 ui.m_timeZoneCombo->blockSignals(b: true);
170 ui.m_timeZoneCombo->clear();
171 ui.m_timeZoneCombo->addItem(atext: KDateTimeEdit::tr(s: "UTC", c: "@item:inlistbox UTC time zone"), auserData: QByteArray("UTC"));
172 ui.m_timeZoneCombo->addItem(atext: KDateTimeEdit::tr(s: "Floating", c: "@item:inlistbox No specific time zone"), auserData: QByteArray());
173 for (const QTimeZone &zone : std::as_const(t&: m_zones)) {
174 ui.m_timeZoneCombo->addItem(atext: QString::fromUtf8(ba: zone.id()), auserData: zone.id());
175 }
176 ui.m_timeZoneCombo->setVisible((m_options & KDateTimeEdit::ShowTimeZone) == KDateTimeEdit::ShowTimeZone);
177 ui.m_timeZoneCombo->setEnabled((m_options & KDateTimeEdit::SelectTimeZone) == KDateTimeEdit::SelectTimeZone);
178 ui.m_timeZoneCombo->setEditable(false);
179 ui.m_timeZoneCombo->blockSignals(b: false);
180}
181
182void KDateTimeEditPrivate::updateTimeZoneWidget()
183{
184 ui.m_timeZoneCombo->blockSignals(b: true);
185 ui.m_timeZoneCombo->blockSignals(b: false);
186}
187
188void KDateTimeEditPrivate::selectTimeZone(int index)
189{
190 enterTimeZone(zoneId: ui.m_timeCombo->itemData(index).toByteArray());
191}
192
193void KDateTimeEditPrivate::enterTimeZone(const QByteArray &zoneId)
194{
195 q->setTimeZone(QTimeZone(zoneId));
196 Q_EMIT q->dateTimeEntered(dateTime: m_dateTime);
197 Q_EMIT q->timeZoneEntered(zone: m_dateTime.timeZone());
198}
199
200void KDateTimeEditPrivate::warnDateTime()
201{
202 if (!q->isValid() && (m_options & KDateTimeEdit::WarnOnInvalid) == KDateTimeEdit::WarnOnInvalid) {
203 QString warnMsg;
204 if (!m_dateTime.isValid()) {
205 // TODO Add missing string
206 // warnMsg = tr("The date or time you entered is invalid");
207 } else if (m_minDateTime.isValid() && m_dateTime < m_minDateTime) {
208 if (m_minWarnMsg.isEmpty()) {
209 // TODO Add datetime to string
210 // warnMsg = q->tr("Date and time cannot be earlier than %1", "@info").arg(formatDate(m_minDate));
211 warnMsg = KDateTimeEdit::tr(s: "The entered date and time is before the minimum allowed date and time.", c: "@info");
212 } else {
213 warnMsg = m_minWarnMsg;
214 // TODO localize properly
215 warnMsg.replace(before: QLatin1String("%1"), after: q->locale().toString(dateTime: m_minDateTime));
216 }
217 } else if (m_maxDateTime.isValid() && m_dateTime > m_maxDateTime) {
218 if (m_maxWarnMsg.isEmpty()) {
219 // TODO Add datetime to string
220 // warnMsg = q->tr("Date cannot be later than %1", "@info").arg(formatDate(m_maxDate));
221 warnMsg = KDateTimeEdit::tr(s: "The entered date and time is after the maximum allowed date and time.", c: "@info");
222 } else {
223 warnMsg = m_maxWarnMsg;
224 warnMsg.replace(before: QLatin1String("%1"), after: q->locale().toString(dateTime: m_maxDateTime));
225 }
226 }
227 KMessageBox::error(parent: q, text: warnMsg);
228 }
229}
230
231KDateTimeEdit::KDateTimeEdit(QWidget *parent)
232 : QWidget(parent)
233 , d(new KDateTimeEditPrivate(this))
234{
235 d->ui.setupUi(this);
236 // Need to do the min/max defaults here and not in private init as need to wait for ui to init
237 // the KDateComboBox which holds the calendar object. Revisit this???
238 d->m_minDateTime = d->defaultMinDateTime();
239 d->m_maxDateTime = d->defaultMaxDateTime();
240 d->ui.m_calendarCombo->installEventFilter(filterObj: this);
241 d->ui.m_dateCombo->installEventFilter(filterObj: this);
242 d->ui.m_timeCombo->installEventFilter(filterObj: this);
243 d->ui.m_timeZoneCombo->installEventFilter(filterObj: this);
244 d->initWidgets();
245
246 connect(sender: d->ui.m_dateCombo, signal: &KDateComboBox::dateChanged, context: this, slot: &KDateTimeEdit::setDate);
247 connect(sender: d->ui.m_timeCombo, signal: &KTimeComboBox::timeChanged, context: this, slot: &KDateTimeEdit::setTime);
248 connect(sender: d->ui.m_calendarCombo, signal: &QComboBox::activated, context: this, slot: [this](int index) {
249 d->selectCalendar(index);
250 });
251 connect(sender: d->ui.m_timeZoneCombo, signal: &QComboBox::activated, context: this, slot: [this](int index) {
252 d->selectTimeZone(index);
253 });
254}
255
256KDateTimeEdit::~KDateTimeEdit() = default;
257
258QDateTime KDateTimeEdit::dateTime() const
259{
260 return d->m_dateTime;
261}
262
263QDate KDateTimeEdit::date() const
264{
265 return d->m_dateTime.date();
266}
267
268QTime KDateTimeEdit::time() const
269{
270 return d->m_dateTime.time();
271}
272
273QTimeZone KDateTimeEdit::timeZone() const
274{
275 return d->m_dateTime.timeZone();
276}
277
278bool KDateTimeEdit::isValid() const
279{
280 return d->m_dateTime.isValid() //
281 && (!d->m_minDateTime.isValid() || d->m_dateTime >= d->m_minDateTime) //
282 && (!d->m_maxDateTime.isValid() || d->m_dateTime <= d->m_maxDateTime);
283}
284
285bool KDateTimeEdit::isNull() const
286{
287 return isNullDate() && isNullTime();
288}
289
290bool KDateTimeEdit::isValidDate() const
291{
292 return d->ui.m_dateCombo->isValid();
293}
294
295bool KDateTimeEdit::isNullDate() const
296{
297 return d->ui.m_dateCombo->isNull();
298}
299
300bool KDateTimeEdit::isValidTime() const
301{
302 return d->ui.m_timeCombo->isValid();
303}
304
305bool KDateTimeEdit::isNullTime() const
306{
307 return d->ui.m_timeCombo->isNull();
308}
309
310void KDateTimeEdit::setOptions(Options options)
311{
312 if (options != d->m_options) {
313 d->m_options = options;
314 d->initWidgets();
315 }
316}
317
318KDateTimeEdit::Options KDateTimeEdit::options() const
319{
320 return d->m_options;
321}
322
323void KDateTimeEdit::setDateTime(const QDateTime &dateTime)
324{
325 if (dateTime != d->m_dateTime) {
326 assignDateTime(dateTime);
327 Q_EMIT dateTimeChanged(dateTime: d->m_dateTime);
328 Q_EMIT dateChanged(date: d->m_dateTime.date());
329 Q_EMIT timeChanged(time: d->m_dateTime.time());
330 }
331}
332
333void KDateTimeEdit::assignDateTime(const QDateTime &dateTime)
334{
335 d->m_dateTime = dateTime;
336 d->ui.m_dateCombo->setDate(dateTime.date());
337 d->ui.m_timeCombo->setTime(dateTime.time());
338}
339
340void KDateTimeEdit::setDate(const QDate &date)
341{
342 if (date != d->m_dateTime.date()) {
343 assignDate(date);
344 Q_EMIT dateTimeChanged(dateTime: d->m_dateTime);
345 Q_EMIT dateChanged(date: d->m_dateTime.date());
346 }
347}
348
349void KDateTimeEdit::assignDate(const QDate &date)
350{
351 d->m_dateTime.setDate(date);
352 d->ui.m_dateCombo->setDate(date);
353}
354
355void KDateTimeEdit::setTime(const QTime &time)
356{
357 if (time != d->m_dateTime.time()) {
358 assignTime(time);
359 Q_EMIT dateTimeChanged(dateTime: d->m_dateTime);
360 Q_EMIT timeChanged(time: d->m_dateTime.time());
361 }
362}
363
364void KDateTimeEdit::assignTime(const QTime &time)
365{
366 d->m_dateTime.setTime(time);
367 d->ui.m_timeCombo->setTime(time);
368}
369
370void KDateTimeEdit::setTimeZone(const QTimeZone &zone)
371{
372 if (zone == d->m_dateTime.timeZone() || !zone.isValid()) {
373 return;
374 }
375
376 assignTimeZone(zone);
377 Q_EMIT dateTimeChanged(dateTime: d->m_dateTime);
378 Q_EMIT timeZoneChanged(zone: d->m_dateTime.timeZone());
379}
380
381void KDateTimeEdit::assignTimeZone(const QTimeZone &zone)
382{
383 d->m_dateTime.setTimeZone(zone);
384 d->updateTimeZoneWidget();
385}
386
387void KDateTimeEdit::setMinimumDateTime(const QDateTime &minDateTime, const QString &minWarnMsg)
388{
389 setDateTimeRange(minDateTime, maxDateTime: maximumDateTime(), minWarnMsg, maxWarnMsg: d->m_maxWarnMsg);
390}
391
392QDateTime KDateTimeEdit::minimumDateTime() const
393{
394 return d->m_minDateTime;
395}
396
397void KDateTimeEdit::resetMinimumDateTime()
398{
399 d->m_minDateTime = d->defaultMinDateTime();
400}
401
402void KDateTimeEdit::setMaximumDateTime(const QDateTime &maxDateTime, const QString &maxWarnMsg)
403{
404 setDateTimeRange(minDateTime: minimumDateTime(), maxDateTime, minWarnMsg: d->m_minWarnMsg, maxWarnMsg);
405}
406
407QDateTime KDateTimeEdit::maximumDateTime() const
408{
409 return d->m_maxDateTime;
410}
411
412void KDateTimeEdit::resetMaximumDateTime()
413{
414 d->m_maxDateTime = d->defaultMaxDateTime();
415}
416
417void KDateTimeEdit::setDateTimeRange(const QDateTime &minDateTime, const QDateTime &maxDateTime, const QString &minErrorMsg, const QString &maxErrorMsg)
418{
419 if (minDateTime.isValid() && maxDateTime.isValid() && minDateTime <= maxDateTime) {
420 d->m_minDateTime = minDateTime;
421 d->m_minWarnMsg = minErrorMsg;
422 d->m_maxDateTime = maxDateTime;
423 d->m_maxWarnMsg = maxErrorMsg;
424 }
425}
426
427void KDateTimeEdit::resetDateTimeRange()
428{
429 setDateTimeRange(minDateTime: d->defaultMinDateTime(), maxDateTime: d->defaultMaxDateTime());
430}
431
432void KDateTimeEdit::setCalendarLocalesList(const QList<QLocale> &calendarLocales)
433{
434 if (calendarLocales != d->m_calendarLocales) {
435 d->m_calendarLocales = calendarLocales;
436 d->updateCalendarWidget();
437 }
438}
439
440QList<QLocale> KDateTimeEdit::calendarLocalesList() const
441{
442 return d->m_calendarLocales;
443}
444
445void KDateTimeEdit::setDateDisplayFormat(QLocale::FormatType format)
446{
447 d->ui.m_dateCombo->setDisplayFormat(format);
448}
449
450QLocale::FormatType KDateTimeEdit::dateDisplayFormat() const
451{
452 return d->ui.m_dateCombo->displayFormat();
453}
454
455void KDateTimeEdit::setDateMap(QMap<QDate, QString> dateMap)
456{
457 d->ui.m_dateCombo->setDateMap(dateMap);
458}
459
460QMap<QDate, QString> KDateTimeEdit::dateMap() const
461{
462 return d->ui.m_dateCombo->dateMap();
463}
464
465void KDateTimeEdit::setTimeDisplayFormat(QLocale::FormatType formatOptions)
466{
467 d->ui.m_timeCombo->setDisplayFormat(formatOptions);
468}
469
470QLocale::FormatType KDateTimeEdit::timeDisplayFormat() const
471{
472 return d->ui.m_timeCombo->displayFormat();
473}
474
475void KDateTimeEdit::setTimeListInterval(int minutes)
476{
477 d->ui.m_timeCombo->setTimeListInterval(minutes);
478}
479
480int KDateTimeEdit::timeListInterval() const
481{
482 return d->ui.m_timeCombo->timeListInterval();
483}
484
485void KDateTimeEdit::setTimeList(QList<QTime> timeList, const QString &minWarnMsg, const QString &maxWarnMsg)
486{
487 d->ui.m_timeCombo->setTimeList(timeList, minWarnMsg, maxWarnMsg);
488}
489
490QList<QTime> KDateTimeEdit::timeList() const
491{
492 return d->ui.m_timeCombo->timeList();
493}
494
495void KDateTimeEdit::setTimeZones(const QList<QTimeZone> &zones)
496{
497 if (zones != d->m_zones) {
498 d->m_zones = zones;
499 d->updateTimeZoneWidget();
500 }
501}
502
503QList<QTimeZone> KDateTimeEdit::timeZones() const
504{
505 return d->m_zones;
506}
507
508bool KDateTimeEdit::eventFilter(QObject *object, QEvent *event)
509{
510 return QWidget::eventFilter(watched: object, event);
511}
512
513void KDateTimeEdit::focusInEvent(QFocusEvent *event)
514{
515 QWidget::focusInEvent(event);
516}
517
518void KDateTimeEdit::focusOutEvent(QFocusEvent *event)
519{
520 d->warnDateTime();
521 QWidget::focusOutEvent(event);
522}
523
524void KDateTimeEdit::resizeEvent(QResizeEvent *event)
525{
526 QWidget::resizeEvent(event);
527}
528
529#include "moc_kdatetimeedit.cpp"
530

source code of kwidgetsaddons/src/kdatetimeedit.cpp