File indexing completed on 2024-04-28 15:32:02

0001 /*
0002     SPDX-FileCopyrightText: 2011 John Layt <john@layt.net>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "kdatetimeedit.h"
0008 
0009 #include "kdatecombobox.h"
0010 #include "kdatepicker.h"
0011 #include "kmessagebox.h"
0012 
0013 #include "ui_kdatetimeedit.h"
0014 
0015 class KDateTimeEditPrivate
0016 {
0017 public:
0018     KDateTimeEditPrivate(KDateTimeEdit *qq);
0019     virtual ~KDateTimeEditPrivate();
0020 
0021     QDateTime defaultMinDateTime();
0022     QDateTime defaultMaxDateTime();
0023 
0024     void initWidgets();
0025     void initDateWidget();
0026     void initTimeWidget();
0027     void initCalendarWidget();
0028     void updateCalendarWidget();
0029     void initTimeZoneWidget();
0030     void updateTimeZoneWidget();
0031 
0032     void warnDateTime();
0033 
0034     // private Q_SLOTS:
0035     void selectCalendar(int index);
0036     void enterCalendar(const QLocale &calendarLocale);
0037     void selectTimeZone(int index);
0038     void enterTimeZone(const QByteArray &zoneId);
0039 
0040     KDateTimeEdit *const q;
0041 
0042     KDateTimeEdit::Options m_options;
0043     QDateTime m_dateTime;
0044     QDateTime m_minDateTime;
0045     QDateTime m_maxDateTime;
0046     QString m_minWarnMsg;
0047     QString m_maxWarnMsg;
0048 
0049     QList<QLocale> m_calendarLocales;
0050     QList<QTimeZone> m_zones;
0051 
0052     Ui::KDateTimeEdit ui;
0053 };
0054 
0055 KDateTimeEditPrivate::KDateTimeEditPrivate(KDateTimeEdit *qq)
0056     : q(qq)
0057 {
0058     m_options = KDateTimeEdit::ShowDate | KDateTimeEdit::EditDate | KDateTimeEdit::SelectDate | KDateTimeEdit::ShowTime | KDateTimeEdit::EditTime
0059         | KDateTimeEdit::SelectTime | KDateTimeEdit::DatePicker | KDateTimeEdit::DateKeywords;
0060     m_dateTime = QDateTime::currentDateTime();
0061     m_dateTime.setTime(QTime(0, 0, 0));
0062     m_calendarLocales << q->locale();
0063     const auto zoneIds = QTimeZone::availableTimeZoneIds();
0064     m_zones.reserve(zoneIds.size());
0065     for (const QByteArray &zoneId : zoneIds) {
0066         m_zones << QTimeZone(zoneId);
0067     }
0068 }
0069 
0070 KDateTimeEditPrivate::~KDateTimeEditPrivate()
0071 {
0072 }
0073 
0074 QDateTime KDateTimeEditPrivate::defaultMinDateTime()
0075 {
0076     // TODO: Find a way to get it from QLocale
0077     // return KDateTime(calendar()->earliestValidDate(), QTime(0, 0, 0, 0));
0078     return QDateTime();
0079 }
0080 
0081 QDateTime KDateTimeEditPrivate::defaultMaxDateTime()
0082 {
0083     // TODO: Find a way to get it from QLocale
0084     // return KDateTime(calendar()->latestValidDate(), QTime(23, 59, 59, 999));
0085     return QDateTime();
0086 }
0087 
0088 void KDateTimeEditPrivate::initWidgets()
0089 {
0090     initDateWidget();
0091     initTimeWidget();
0092     initCalendarWidget();
0093     initTimeZoneWidget();
0094 }
0095 
0096 void KDateTimeEditPrivate::initDateWidget()
0097 {
0098     ui.m_dateCombo->blockSignals(true);
0099     ui.m_dateCombo->setVisible((m_options & KDateTimeEdit::ShowDate) == KDateTimeEdit::ShowDate);
0100     KDateComboBox::Options options;
0101     if ((m_options & KDateTimeEdit::EditDate) == KDateTimeEdit::EditDate) {
0102         options = options | KDateComboBox::EditDate;
0103     }
0104     if ((m_options & KDateTimeEdit::SelectDate) == KDateTimeEdit::SelectDate) {
0105         options = options | KDateComboBox::SelectDate;
0106     }
0107     if ((m_options & KDateTimeEdit::DatePicker) == KDateTimeEdit::DatePicker) {
0108         options = options | KDateComboBox::DatePicker;
0109     }
0110     if ((m_options & KDateTimeEdit::DateKeywords) == KDateTimeEdit::DateKeywords) {
0111         options = options | KDateComboBox::DateKeywords;
0112     }
0113     ui.m_dateCombo->setOptions(options);
0114     ui.m_dateCombo->blockSignals(false);
0115 }
0116 
0117 void KDateTimeEditPrivate::initTimeWidget()
0118 {
0119     ui.m_timeCombo->blockSignals(true);
0120     ui.m_timeCombo->setVisible((m_options & KDateTimeEdit::ShowTime) == KDateTimeEdit::ShowTime);
0121     KTimeComboBox::Options options;
0122     if ((m_options & KDateTimeEdit::EditTime) == KDateTimeEdit::EditTime) {
0123         options = options | KTimeComboBox::EditTime;
0124     }
0125     if ((m_options & KDateTimeEdit::SelectTime) == KDateTimeEdit::SelectTime) {
0126         options = options | KTimeComboBox::SelectTime;
0127     }
0128     if ((m_options & KDateTimeEdit::ForceTime) == KDateTimeEdit::ForceTime) {
0129         options = options | KTimeComboBox::ForceTime;
0130     }
0131     ui.m_timeCombo->setOptions(options);
0132     ui.m_timeCombo->blockSignals(false);
0133 }
0134 
0135 void KDateTimeEditPrivate::initCalendarWidget()
0136 {
0137     ui.m_calendarCombo->blockSignals(true);
0138     ui.m_calendarCombo->clear();
0139     for (const QLocale &calendarLocale : std::as_const(m_calendarLocales)) {
0140         ui.m_calendarCombo->addItem(calendarLocale.name(), calendarLocale);
0141     }
0142     ui.m_calendarCombo->setCurrentIndex(ui.m_calendarCombo->findData(q->locale()));
0143     ui.m_calendarCombo->setVisible((m_options & KDateTimeEdit::ShowCalendar) == KDateTimeEdit::ShowCalendar);
0144     ui.m_calendarCombo->setEnabled((m_options & KDateTimeEdit::SelectCalendar) == KDateTimeEdit::SelectCalendar);
0145     ui.m_calendarCombo->setEditable(false);
0146     ui.m_calendarCombo->blockSignals(false);
0147 }
0148 
0149 void KDateTimeEditPrivate::updateCalendarWidget()
0150 {
0151     ui.m_calendarCombo->blockSignals(true);
0152     ui.m_calendarCombo->setCurrentIndex(ui.m_calendarCombo->findData(q->locale()));
0153     ui.m_calendarCombo->blockSignals(false);
0154 }
0155 
0156 void KDateTimeEditPrivate::selectCalendar(int index)
0157 {
0158     enterCalendar(ui.m_calendarCombo->itemData(index).toLocale());
0159 }
0160 
0161 void KDateTimeEditPrivate::enterCalendar(const QLocale &calendarLocale)
0162 {
0163     q->setLocale(calendarLocale);
0164     Q_EMIT q->calendarEntered(q->locale());
0165 }
0166 
0167 void KDateTimeEditPrivate::initTimeZoneWidget()
0168 {
0169     ui.m_timeZoneCombo->blockSignals(true);
0170     ui.m_timeZoneCombo->clear();
0171     ui.m_timeZoneCombo->addItem(KDateTimeEdit::tr("UTC", "@item:inlistbox UTC time zone"), QByteArray("UTC"));
0172     ui.m_timeZoneCombo->addItem(KDateTimeEdit::tr("Floating", "@item:inlistbox No specific time zone"), QByteArray());
0173     for (const QTimeZone &zone : std::as_const(m_zones)) {
0174         ui.m_timeZoneCombo->addItem(QString::fromUtf8(zone.id()), zone.id());
0175     }
0176     ui.m_timeZoneCombo->setVisible((m_options & KDateTimeEdit::ShowTimeZone) == KDateTimeEdit::ShowTimeZone);
0177     ui.m_timeZoneCombo->setEnabled((m_options & KDateTimeEdit::SelectTimeZone) == KDateTimeEdit::SelectTimeZone);
0178     ui.m_timeZoneCombo->setEditable(false);
0179     ui.m_timeZoneCombo->blockSignals(false);
0180 }
0181 
0182 void KDateTimeEditPrivate::updateTimeZoneWidget()
0183 {
0184     ui.m_timeZoneCombo->blockSignals(true);
0185     ui.m_timeZoneCombo->blockSignals(false);
0186 }
0187 
0188 void KDateTimeEditPrivate::selectTimeZone(int index)
0189 {
0190     enterTimeZone(ui.m_timeCombo->itemData(index).toByteArray());
0191 }
0192 
0193 void KDateTimeEditPrivate::enterTimeZone(const QByteArray &zoneId)
0194 {
0195     q->setTimeZone(QTimeZone(zoneId));
0196     Q_EMIT q->dateTimeEntered(m_dateTime);
0197     Q_EMIT q->timeZoneEntered(m_dateTime.timeZone());
0198 }
0199 
0200 void KDateTimeEditPrivate::warnDateTime()
0201 {
0202     if (!q->isValid() && (m_options & KDateTimeEdit::WarnOnInvalid) == KDateTimeEdit::WarnOnInvalid) {
0203         QString warnMsg;
0204         if (!m_dateTime.isValid()) {
0205             // TODO Add missing string
0206             // warnMsg = tr("The date or time you entered is invalid");
0207         } else if (m_minDateTime.isValid() && m_dateTime < m_minDateTime) {
0208             if (m_minWarnMsg.isEmpty()) {
0209                 // TODO Add datetime to string
0210                 // warnMsg = q->tr("Date and time cannot be earlier than %1", "@info").arg(formatDate(m_minDate));
0211                 warnMsg = KDateTimeEdit::tr("The entered date and time is before the minimum allowed date and time.", "@info");
0212             } else {
0213                 warnMsg = m_minWarnMsg;
0214                 // TODO localize properly
0215                 warnMsg.replace(QLatin1String("%1"), q->locale().toString(m_minDateTime));
0216             }
0217         } else if (m_maxDateTime.isValid() && m_dateTime > m_maxDateTime) {
0218             if (m_maxWarnMsg.isEmpty()) {
0219                 // TODO Add datetime to string
0220                 // warnMsg = q->tr("Date cannot be later than %1", "@info").arg(formatDate(m_maxDate));
0221                 warnMsg = KDateTimeEdit::tr("The entered date and time is after the maximum allowed date and time.", "@info");
0222             } else {
0223                 warnMsg = m_maxWarnMsg;
0224                 warnMsg.replace(QLatin1String("%1"), q->locale().toString(m_maxDateTime));
0225             }
0226         }
0227         KMessageBox::error(q, warnMsg);
0228     }
0229 }
0230 
0231 KDateTimeEdit::KDateTimeEdit(QWidget *parent)
0232     : QWidget(parent)
0233     , d(new KDateTimeEditPrivate(this))
0234 {
0235     d->ui.setupUi(this);
0236     // Need to do the min/max defaults here and not in private init as need to wait for ui to init
0237     // the KDateComboBox which holds the calendar object.  Revisit this???
0238     d->m_minDateTime = d->defaultMinDateTime();
0239     d->m_maxDateTime = d->defaultMaxDateTime();
0240     d->ui.m_calendarCombo->installEventFilter(this);
0241     d->ui.m_dateCombo->installEventFilter(this);
0242     d->ui.m_timeCombo->installEventFilter(this);
0243     d->ui.m_timeZoneCombo->installEventFilter(this);
0244     d->initWidgets();
0245 
0246     connect(d->ui.m_dateCombo, &KDateComboBox::dateChanged, this, &KDateTimeEdit::setDate);
0247     connect(d->ui.m_timeCombo, &KTimeComboBox::timeChanged, this, &KDateTimeEdit::setTime);
0248     connect(d->ui.m_calendarCombo, qOverload<int>(&QComboBox::activated), this, [this](int index) {
0249         d->selectCalendar(index);
0250     });
0251     connect(d->ui.m_timeZoneCombo, qOverload<int>(&QComboBox::activated), this, [this](int index) {
0252         d->selectTimeZone(index);
0253     });
0254 }
0255 
0256 KDateTimeEdit::~KDateTimeEdit() = default;
0257 
0258 QDateTime KDateTimeEdit::dateTime() const
0259 {
0260     return d->m_dateTime;
0261 }
0262 
0263 QDate KDateTimeEdit::date() const
0264 {
0265     return d->m_dateTime.date();
0266 }
0267 
0268 QTime KDateTimeEdit::time() const
0269 {
0270     return d->m_dateTime.time();
0271 }
0272 
0273 QTimeZone KDateTimeEdit::timeZone() const
0274 {
0275     return d->m_dateTime.timeZone();
0276 }
0277 
0278 bool KDateTimeEdit::isValid() const
0279 {
0280     return d->m_dateTime.isValid() //
0281         && (!d->m_minDateTime.isValid() || d->m_dateTime >= d->m_minDateTime) //
0282         && (!d->m_maxDateTime.isValid() || d->m_dateTime <= d->m_maxDateTime);
0283 }
0284 
0285 bool KDateTimeEdit::isNull() const
0286 {
0287     return isNullDate() && isNullTime();
0288 }
0289 
0290 bool KDateTimeEdit::isValidDate() const
0291 {
0292     return d->ui.m_dateCombo->isValid();
0293 }
0294 
0295 bool KDateTimeEdit::isNullDate() const
0296 {
0297     return d->ui.m_dateCombo->isNull();
0298 }
0299 
0300 bool KDateTimeEdit::isValidTime() const
0301 {
0302     return d->ui.m_timeCombo->isValid();
0303 }
0304 
0305 bool KDateTimeEdit::isNullTime() const
0306 {
0307     return d->ui.m_timeCombo->isNull();
0308 }
0309 
0310 void KDateTimeEdit::setOptions(Options options)
0311 {
0312     if (options != d->m_options) {
0313         d->m_options = options;
0314         d->initWidgets();
0315     }
0316 }
0317 
0318 KDateTimeEdit::Options KDateTimeEdit::options() const
0319 {
0320     return d->m_options;
0321 }
0322 
0323 void KDateTimeEdit::setDateTime(const QDateTime &dateTime)
0324 {
0325     if (dateTime != d->m_dateTime) {
0326         assignDateTime(dateTime);
0327         Q_EMIT dateTimeChanged(d->m_dateTime);
0328         Q_EMIT dateChanged(d->m_dateTime.date());
0329         Q_EMIT timeChanged(d->m_dateTime.time());
0330     }
0331 }
0332 
0333 void KDateTimeEdit::assignDateTime(const QDateTime &dateTime)
0334 {
0335     d->m_dateTime = dateTime;
0336     d->ui.m_dateCombo->setDate(dateTime.date());
0337     d->ui.m_timeCombo->setTime(dateTime.time());
0338 }
0339 
0340 void KDateTimeEdit::setDate(const QDate &date)
0341 {
0342     if (date != d->m_dateTime.date()) {
0343         assignDate(date);
0344         Q_EMIT dateTimeChanged(d->m_dateTime);
0345         Q_EMIT dateChanged(d->m_dateTime.date());
0346     }
0347 }
0348 
0349 void KDateTimeEdit::assignDate(const QDate &date)
0350 {
0351     d->m_dateTime.setDate(date);
0352     d->ui.m_dateCombo->setDate(date);
0353 }
0354 
0355 void KDateTimeEdit::setTime(const QTime &time)
0356 {
0357     if (time != d->m_dateTime.time()) {
0358         assignTime(time);
0359         Q_EMIT dateTimeChanged(d->m_dateTime);
0360         Q_EMIT timeChanged(d->m_dateTime.time());
0361     }
0362 }
0363 
0364 void KDateTimeEdit::assignTime(const QTime &time)
0365 {
0366     d->m_dateTime.setTime(time);
0367     d->ui.m_timeCombo->setTime(time);
0368 }
0369 
0370 void KDateTimeEdit::setTimeZone(const QTimeZone &zone)
0371 {
0372     if (zone == d->m_dateTime.timeZone() || !zone.isValid()) {
0373         return;
0374     }
0375 
0376     assignTimeZone(zone);
0377     Q_EMIT dateTimeChanged(d->m_dateTime);
0378     Q_EMIT timeZoneChanged(d->m_dateTime.timeZone());
0379 }
0380 
0381 void KDateTimeEdit::assignTimeZone(const QTimeZone &zone)
0382 {
0383     d->m_dateTime.setTimeZone(zone);
0384     d->updateTimeZoneWidget();
0385 }
0386 
0387 void KDateTimeEdit::setMinimumDateTime(const QDateTime &minDateTime, const QString &minWarnMsg)
0388 {
0389     setDateTimeRange(minDateTime, maximumDateTime(), minWarnMsg, d->m_maxWarnMsg);
0390 }
0391 
0392 QDateTime KDateTimeEdit::minimumDateTime() const
0393 {
0394     return d->m_minDateTime;
0395 }
0396 
0397 void KDateTimeEdit::resetMinimumDateTime()
0398 {
0399     d->m_minDateTime = d->defaultMinDateTime();
0400 }
0401 
0402 void KDateTimeEdit::setMaximumDateTime(const QDateTime &maxDateTime, const QString &maxWarnMsg)
0403 {
0404     setDateTimeRange(minimumDateTime(), maxDateTime, d->m_minWarnMsg, maxWarnMsg);
0405 }
0406 
0407 QDateTime KDateTimeEdit::maximumDateTime() const
0408 {
0409     return d->m_maxDateTime;
0410 }
0411 
0412 void KDateTimeEdit::resetMaximumDateTime()
0413 {
0414     d->m_maxDateTime = d->defaultMaxDateTime();
0415 }
0416 
0417 void KDateTimeEdit::setDateTimeRange(const QDateTime &minDateTime, const QDateTime &maxDateTime, const QString &minErrorMsg, const QString &maxErrorMsg)
0418 {
0419     if (minDateTime.isValid() && maxDateTime.isValid() && minDateTime <= maxDateTime) {
0420         d->m_minDateTime = minDateTime;
0421         d->m_minWarnMsg = minErrorMsg;
0422         d->m_maxDateTime = maxDateTime;
0423         d->m_maxWarnMsg = maxErrorMsg;
0424     }
0425 }
0426 
0427 void KDateTimeEdit::resetDateTimeRange()
0428 {
0429     setDateTimeRange(d->defaultMinDateTime(), d->defaultMaxDateTime());
0430 }
0431 
0432 void KDateTimeEdit::setCalendarLocalesList(const QList<QLocale> &calendarLocales)
0433 {
0434     if (calendarLocales != d->m_calendarLocales) {
0435         d->m_calendarLocales = calendarLocales;
0436         d->updateCalendarWidget();
0437     }
0438 }
0439 
0440 QList<QLocale> KDateTimeEdit::calendarLocalesList() const
0441 {
0442     return d->m_calendarLocales;
0443 }
0444 
0445 void KDateTimeEdit::setDateDisplayFormat(QLocale::FormatType format)
0446 {
0447     d->ui.m_dateCombo->setDisplayFormat(format);
0448 }
0449 
0450 QLocale::FormatType KDateTimeEdit::dateDisplayFormat() const
0451 {
0452     return d->ui.m_dateCombo->displayFormat();
0453 }
0454 
0455 void KDateTimeEdit::setDateMap(QMap<QDate, QString> dateMap)
0456 {
0457     d->ui.m_dateCombo->setDateMap(dateMap);
0458 }
0459 
0460 QMap<QDate, QString> KDateTimeEdit::dateMap() const
0461 {
0462     return d->ui.m_dateCombo->dateMap();
0463 }
0464 
0465 void KDateTimeEdit::setTimeDisplayFormat(QLocale::FormatType formatOptions)
0466 {
0467     d->ui.m_timeCombo->setDisplayFormat(formatOptions);
0468 }
0469 
0470 QLocale::FormatType KDateTimeEdit::timeDisplayFormat() const
0471 {
0472     return d->ui.m_timeCombo->displayFormat();
0473 }
0474 
0475 void KDateTimeEdit::setTimeListInterval(int minutes)
0476 {
0477     d->ui.m_timeCombo->setTimeListInterval(minutes);
0478 }
0479 
0480 int KDateTimeEdit::timeListInterval() const
0481 {
0482     return d->ui.m_timeCombo->timeListInterval();
0483 }
0484 
0485 void KDateTimeEdit::setTimeList(QList<QTime> timeList, const QString &minWarnMsg, const QString &maxWarnMsg)
0486 {
0487     d->ui.m_timeCombo->setTimeList(timeList, minWarnMsg, maxWarnMsg);
0488 }
0489 
0490 QList<QTime> KDateTimeEdit::timeList() const
0491 {
0492     return d->ui.m_timeCombo->timeList();
0493 }
0494 
0495 void KDateTimeEdit::setTimeZones(const QList<QTimeZone> &zones)
0496 {
0497     if (zones != d->m_zones) {
0498         d->m_zones = zones;
0499         d->updateTimeZoneWidget();
0500     }
0501 }
0502 
0503 QList<QTimeZone> KDateTimeEdit::timeZones() const
0504 {
0505     return d->m_zones;
0506 }
0507 
0508 bool KDateTimeEdit::eventFilter(QObject *object, QEvent *event)
0509 {
0510     return QWidget::eventFilter(object, event);
0511 }
0512 
0513 void KDateTimeEdit::focusInEvent(QFocusEvent *event)
0514 {
0515     QWidget::focusInEvent(event);
0516 }
0517 
0518 void KDateTimeEdit::focusOutEvent(QFocusEvent *event)
0519 {
0520     d->warnDateTime();
0521     QWidget::focusOutEvent(event);
0522 }
0523 
0524 void KDateTimeEdit::resizeEvent(QResizeEvent *event)
0525 {
0526     QWidget::resizeEvent(event);
0527 }
0528 
0529 #include "moc_kdatetimeedit.cpp"