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 | |
15 | class KDateTimeEditPrivate |
16 | { |
17 | public: |
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 | |
55 | KDateTimeEditPrivate::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 | |
70 | KDateTimeEditPrivate::~KDateTimeEditPrivate() |
71 | { |
72 | } |
73 | |
74 | QDateTime 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 | |
81 | QDateTime 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 | |
88 | void KDateTimeEditPrivate::initWidgets() |
89 | { |
90 | initDateWidget(); |
91 | initTimeWidget(); |
92 | initCalendarWidget(); |
93 | initTimeZoneWidget(); |
94 | } |
95 | |
96 | void 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 | |
117 | void 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 | |
135 | void 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 | |
149 | void 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 | |
156 | void KDateTimeEditPrivate::selectCalendar(int index) |
157 | { |
158 | enterCalendar(calendarLocale: ui.m_calendarCombo->itemData(index).toLocale()); |
159 | } |
160 | |
161 | void KDateTimeEditPrivate::enterCalendar(const QLocale &calendarLocale) |
162 | { |
163 | q->setLocale(calendarLocale); |
164 | Q_EMIT q->calendarEntered(calendarLocale: q->locale()); |
165 | } |
166 | |
167 | void 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 | |
182 | void KDateTimeEditPrivate::updateTimeZoneWidget() |
183 | { |
184 | ui.m_timeZoneCombo->blockSignals(b: true); |
185 | ui.m_timeZoneCombo->blockSignals(b: false); |
186 | } |
187 | |
188 | void KDateTimeEditPrivate::selectTimeZone(int index) |
189 | { |
190 | enterTimeZone(zoneId: ui.m_timeCombo->itemData(index).toByteArray()); |
191 | } |
192 | |
193 | void 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 | |
200 | void 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 | |
231 | KDateTimeEdit::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 | |
256 | KDateTimeEdit::~KDateTimeEdit() = default; |
257 | |
258 | QDateTime KDateTimeEdit::dateTime() const |
259 | { |
260 | return d->m_dateTime; |
261 | } |
262 | |
263 | QDate KDateTimeEdit::date() const |
264 | { |
265 | return d->m_dateTime.date(); |
266 | } |
267 | |
268 | QTime KDateTimeEdit::time() const |
269 | { |
270 | return d->m_dateTime.time(); |
271 | } |
272 | |
273 | QTimeZone KDateTimeEdit::timeZone() const |
274 | { |
275 | return d->m_dateTime.timeZone(); |
276 | } |
277 | |
278 | bool 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 | |
285 | bool KDateTimeEdit::isNull() const |
286 | { |
287 | return isNullDate() && isNullTime(); |
288 | } |
289 | |
290 | bool KDateTimeEdit::isValidDate() const |
291 | { |
292 | return d->ui.m_dateCombo->isValid(); |
293 | } |
294 | |
295 | bool KDateTimeEdit::isNullDate() const |
296 | { |
297 | return d->ui.m_dateCombo->isNull(); |
298 | } |
299 | |
300 | bool KDateTimeEdit::isValidTime() const |
301 | { |
302 | return d->ui.m_timeCombo->isValid(); |
303 | } |
304 | |
305 | bool KDateTimeEdit::isNullTime() const |
306 | { |
307 | return d->ui.m_timeCombo->isNull(); |
308 | } |
309 | |
310 | void KDateTimeEdit::setOptions(Options options) |
311 | { |
312 | if (options != d->m_options) { |
313 | d->m_options = options; |
314 | d->initWidgets(); |
315 | } |
316 | } |
317 | |
318 | KDateTimeEdit::Options KDateTimeEdit::options() const |
319 | { |
320 | return d->m_options; |
321 | } |
322 | |
323 | void 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 | |
333 | void 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 | |
340 | void 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 | |
349 | void KDateTimeEdit::assignDate(const QDate &date) |
350 | { |
351 | d->m_dateTime.setDate(date); |
352 | d->ui.m_dateCombo->setDate(date); |
353 | } |
354 | |
355 | void 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 | |
364 | void KDateTimeEdit::assignTime(const QTime &time) |
365 | { |
366 | d->m_dateTime.setTime(time); |
367 | d->ui.m_timeCombo->setTime(time); |
368 | } |
369 | |
370 | void 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 | |
381 | void KDateTimeEdit::assignTimeZone(const QTimeZone &zone) |
382 | { |
383 | d->m_dateTime.setTimeZone(zone); |
384 | d->updateTimeZoneWidget(); |
385 | } |
386 | |
387 | void KDateTimeEdit::setMinimumDateTime(const QDateTime &minDateTime, const QString &minWarnMsg) |
388 | { |
389 | setDateTimeRange(minDateTime, maxDateTime: maximumDateTime(), minWarnMsg, maxWarnMsg: d->m_maxWarnMsg); |
390 | } |
391 | |
392 | QDateTime KDateTimeEdit::minimumDateTime() const |
393 | { |
394 | return d->m_minDateTime; |
395 | } |
396 | |
397 | void KDateTimeEdit::resetMinimumDateTime() |
398 | { |
399 | d->m_minDateTime = d->defaultMinDateTime(); |
400 | } |
401 | |
402 | void KDateTimeEdit::setMaximumDateTime(const QDateTime &maxDateTime, const QString &maxWarnMsg) |
403 | { |
404 | setDateTimeRange(minDateTime: minimumDateTime(), maxDateTime, minWarnMsg: d->m_minWarnMsg, maxWarnMsg); |
405 | } |
406 | |
407 | QDateTime KDateTimeEdit::maximumDateTime() const |
408 | { |
409 | return d->m_maxDateTime; |
410 | } |
411 | |
412 | void KDateTimeEdit::resetMaximumDateTime() |
413 | { |
414 | d->m_maxDateTime = d->defaultMaxDateTime(); |
415 | } |
416 | |
417 | void 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 | |
427 | void KDateTimeEdit::resetDateTimeRange() |
428 | { |
429 | setDateTimeRange(minDateTime: d->defaultMinDateTime(), maxDateTime: d->defaultMaxDateTime()); |
430 | } |
431 | |
432 | void KDateTimeEdit::setCalendarLocalesList(const QList<QLocale> &calendarLocales) |
433 | { |
434 | if (calendarLocales != d->m_calendarLocales) { |
435 | d->m_calendarLocales = calendarLocales; |
436 | d->updateCalendarWidget(); |
437 | } |
438 | } |
439 | |
440 | QList<QLocale> KDateTimeEdit::calendarLocalesList() const |
441 | { |
442 | return d->m_calendarLocales; |
443 | } |
444 | |
445 | void KDateTimeEdit::setDateDisplayFormat(QLocale::FormatType format) |
446 | { |
447 | d->ui.m_dateCombo->setDisplayFormat(format); |
448 | } |
449 | |
450 | QLocale::FormatType KDateTimeEdit::dateDisplayFormat() const |
451 | { |
452 | return d->ui.m_dateCombo->displayFormat(); |
453 | } |
454 | |
455 | void KDateTimeEdit::setDateMap(QMap<QDate, QString> dateMap) |
456 | { |
457 | d->ui.m_dateCombo->setDateMap(dateMap); |
458 | } |
459 | |
460 | QMap<QDate, QString> KDateTimeEdit::dateMap() const |
461 | { |
462 | return d->ui.m_dateCombo->dateMap(); |
463 | } |
464 | |
465 | void KDateTimeEdit::setTimeDisplayFormat(QLocale::FormatType formatOptions) |
466 | { |
467 | d->ui.m_timeCombo->setDisplayFormat(formatOptions); |
468 | } |
469 | |
470 | QLocale::FormatType KDateTimeEdit::timeDisplayFormat() const |
471 | { |
472 | return d->ui.m_timeCombo->displayFormat(); |
473 | } |
474 | |
475 | void KDateTimeEdit::setTimeListInterval(int minutes) |
476 | { |
477 | d->ui.m_timeCombo->setTimeListInterval(minutes); |
478 | } |
479 | |
480 | int KDateTimeEdit::timeListInterval() const |
481 | { |
482 | return d->ui.m_timeCombo->timeListInterval(); |
483 | } |
484 | |
485 | void KDateTimeEdit::setTimeList(QList<QTime> timeList, const QString &minWarnMsg, const QString &maxWarnMsg) |
486 | { |
487 | d->ui.m_timeCombo->setTimeList(timeList, minWarnMsg, maxWarnMsg); |
488 | } |
489 | |
490 | QList<QTime> KDateTimeEdit::timeList() const |
491 | { |
492 | return d->ui.m_timeCombo->timeList(); |
493 | } |
494 | |
495 | void KDateTimeEdit::setTimeZones(const QList<QTimeZone> &zones) |
496 | { |
497 | if (zones != d->m_zones) { |
498 | d->m_zones = zones; |
499 | d->updateTimeZoneWidget(); |
500 | } |
501 | } |
502 | |
503 | QList<QTimeZone> KDateTimeEdit::timeZones() const |
504 | { |
505 | return d->m_zones; |
506 | } |
507 | |
508 | bool KDateTimeEdit::eventFilter(QObject *object, QEvent *event) |
509 | { |
510 | return QWidget::eventFilter(watched: object, event); |
511 | } |
512 | |
513 | void KDateTimeEdit::focusInEvent(QFocusEvent *event) |
514 | { |
515 | QWidget::focusInEvent(event); |
516 | } |
517 | |
518 | void KDateTimeEdit::focusOutEvent(QFocusEvent *event) |
519 | { |
520 | d->warnDateTime(); |
521 | QWidget::focusOutEvent(event); |
522 | } |
523 | |
524 | void KDateTimeEdit::resizeEvent(QResizeEvent *event) |
525 | { |
526 | QWidget::resizeEvent(event); |
527 | } |
528 | |
529 | #include "moc_kdatetimeedit.cpp" |
530 | |