Warning, file /frameworks/kwidgetsaddons/src/kdatecombobox.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 SPDX-FileCopyrightText: 2011 John Layt <john@layt.net> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "kdatecombobox.h" 0008 0009 #include "common_helpers_p.h" 0010 #include "kdatepickerpopup.h" 0011 #include "kdaterangecontrol_p.h" 0012 0013 #include <QAbstractItemView> 0014 #include <QApplication> 0015 #include <QDate> 0016 #include <QKeyEvent> 0017 #include <QLineEdit> 0018 #include <QMenu> 0019 #include <QScreen> 0020 #include <QVector> 0021 #include <QWidgetAction> 0022 0023 #include "kdatepicker.h" 0024 #include "kmessagebox.h" 0025 0026 class KDateComboBoxPrivate : public KDateRangeControlPrivate 0027 { 0028 public: 0029 KDateComboBoxPrivate(KDateComboBox *qq); 0030 0031 // TODO: Find a way to get that from QLocale 0032 #if 0 0033 QDate defaultMinDate(); 0034 QDate defaultMaxDate(); 0035 #endif 0036 0037 QString dateFormat(QLocale::FormatType format); 0038 QString formatDate(const QDate &date); 0039 0040 void initDateWidget(); 0041 void updateDateWidget(); 0042 void setDateRange(const QDate &minDate, const QDate &maxDate, const QString &minWarnMsg, const QString &maxWarnMsg); 0043 using KDateRangeControlPrivate::setDateRange; 0044 0045 void editDate(const QString &text); 0046 void enterDate(const QDate &date); 0047 void parseDate(); 0048 void warnDate(); 0049 0050 KDateComboBox *const q; 0051 KDatePickerPopup *m_dateMenu; 0052 0053 QDate m_date; 0054 KDateComboBox::Options m_options; 0055 QString m_minWarnMsg; 0056 QString m_maxWarnMsg; 0057 bool m_warningShown; 0058 bool m_edited; // and dateChanged not yet emitted 0059 QLocale::FormatType m_displayFormat; 0060 }; 0061 0062 KDateComboBoxPrivate::KDateComboBoxPrivate(KDateComboBox *qq) 0063 : q(qq) 0064 , m_dateMenu(new KDatePickerPopup(KDatePickerPopup::DatePicker | KDatePickerPopup::Words, QDate::currentDate(), qq)) 0065 , m_warningShown(false) 0066 , m_edited(false) 0067 , m_displayFormat(QLocale::ShortFormat) 0068 { 0069 m_options = KDateComboBox::EditDate | KDateComboBox::SelectDate | KDateComboBox::DatePicker | KDateComboBox::DateKeywords; 0070 m_date = QDate::currentDate(); 0071 // m_minDate = defaultMinDate(); 0072 // m_maxDate = defaultMaxDate(); 0073 } 0074 0075 #if 0 0076 QDate KDateComboBoxPrivate::defaultMinDate() 0077 { 0078 return m_date.calendar()->earliestValidDate(); 0079 } 0080 0081 QDate KDateComboBoxPrivate::defaultMaxDate() 0082 { 0083 return m_date.calendar()->latestValidDate(); 0084 } 0085 #endif 0086 0087 QString KDateComboBoxPrivate::dateFormat(QLocale::FormatType format) 0088 { 0089 return dateFormatWith4DigitYear(q->locale(), format); 0090 } 0091 0092 QString KDateComboBoxPrivate::formatDate(const QDate &date) 0093 { 0094 return q->locale().toString(date, dateFormat(m_displayFormat)); 0095 } 0096 0097 void KDateComboBoxPrivate::initDateWidget() 0098 { 0099 q->blockSignals(true); 0100 q->clear(); 0101 0102 // If EditTime then set the line edit 0103 q->lineEdit()->setReadOnly((m_options & KDateComboBox::EditDate) != KDateComboBox::EditDate); 0104 0105 // If SelectDate then make list items visible 0106 if ((m_options & KDateComboBox::SelectDate) == KDateComboBox::SelectDate // 0107 || (m_options & KDateComboBox::DatePicker) == KDateComboBox::DatePicker // 0108 || (m_options & KDateComboBox::DatePicker) == KDateComboBox::DateKeywords) { 0109 q->setMaxVisibleItems(1); 0110 } else { 0111 q->setMaxVisibleItems(0); 0112 } 0113 0114 q->setSizeAdjustPolicy(QComboBox::AdjustToContents); 0115 q->addItem(formatDate(m_date)); 0116 q->setCurrentIndex(0); 0117 q->setSizeAdjustPolicy(QComboBox::AdjustToContentsOnFirstShow); 0118 q->blockSignals(false); 0119 0120 KDatePickerPopup::Modes modes; 0121 if (m_options & KDateComboBox::DatePicker) { 0122 modes |= KDatePickerPopup::DatePicker; 0123 } 0124 if (m_options & KDateComboBox::DateKeywords) { 0125 modes |= KDatePickerPopup::Words; 0126 } 0127 m_dateMenu->setModes(modes); 0128 } 0129 0130 void KDateComboBoxPrivate::updateDateWidget() 0131 { 0132 q->blockSignals(true); 0133 m_dateMenu->setDate(m_date); 0134 int pos = q->lineEdit()->cursorPosition(); 0135 q->setItemText(0, formatDate(m_date)); 0136 q->lineEdit()->setText(formatDate(m_date)); 0137 q->lineEdit()->setCursorPosition(pos); 0138 q->blockSignals(false); 0139 } 0140 0141 void KDateComboBoxPrivate::setDateRange(const QDate &minDate, const QDate &maxDate, const QString &minWarnMsg, const QString &maxWarnMsg) 0142 { 0143 if (!setDateRange(minDate, maxDate)) { 0144 return; 0145 } 0146 0147 m_dateMenu->setDateRange(minDate, maxDate); 0148 m_minWarnMsg = minWarnMsg; 0149 m_maxWarnMsg = maxWarnMsg; 0150 } 0151 0152 void KDateComboBoxPrivate::editDate(const QString &text) 0153 { 0154 m_warningShown = false; 0155 m_date = q->locale().toDate(text, dateFormat(m_displayFormat)); 0156 m_edited = true; 0157 Q_EMIT q->dateEdited(m_date); 0158 } 0159 0160 void KDateComboBoxPrivate::parseDate() 0161 { 0162 m_date = q->locale().toDate(q->lineEdit()->text(), dateFormat(m_displayFormat)); 0163 } 0164 0165 void KDateComboBoxPrivate::enterDate(const QDate &date) 0166 { 0167 q->setDate(date); 0168 // Re-add the combo box item in order to retain the correct widget width 0169 q->blockSignals(true); 0170 q->clear(); 0171 q->setSizeAdjustPolicy(QComboBox::AdjustToContents); 0172 q->addItem(formatDate(m_date)); 0173 q->setCurrentIndex(0); 0174 q->setSizeAdjustPolicy(QComboBox::AdjustToContentsOnFirstShow); 0175 q->blockSignals(false); 0176 0177 m_dateMenu->hide(); 0178 warnDate(); 0179 Q_EMIT q->dateEntered(m_date); 0180 } 0181 0182 void KDateComboBoxPrivate::warnDate() 0183 { 0184 if (!m_warningShown && !q->isValid() && (m_options & KDateComboBox::WarnOnInvalid) == KDateComboBox::WarnOnInvalid) { 0185 QString warnMsg; 0186 if (!m_date.isValid()) { 0187 warnMsg = KDateComboBox::tr("The date you entered is invalid", "@info"); 0188 } else if (m_minDate.isValid() && m_date < m_minDate) { 0189 if (m_minWarnMsg.isEmpty()) { 0190 warnMsg = KDateComboBox::tr("Date cannot be earlier than %1", "@info").arg(formatDate(m_minDate)); 0191 } else { 0192 warnMsg = m_minWarnMsg; 0193 warnMsg.replace(QLatin1String("%1"), formatDate(m_minDate)); 0194 } 0195 } else if (m_maxDate.isValid() && m_date > m_maxDate) { 0196 if (m_maxWarnMsg.isEmpty()) { 0197 warnMsg = KDateComboBox::tr("Date cannot be later than %1", "@info").arg(formatDate(m_maxDate)); 0198 } else { 0199 warnMsg = m_maxWarnMsg; 0200 warnMsg.replace(QLatin1String("%1"), formatDate(m_maxDate)); 0201 } 0202 } 0203 m_warningShown = true; 0204 KMessageBox::error(q, warnMsg); 0205 } 0206 } 0207 0208 KDateComboBox::KDateComboBox(QWidget *parent) 0209 : QComboBox(parent) 0210 , d(new KDateComboBoxPrivate(this)) 0211 { 0212 setEditable(true); 0213 setMaxVisibleItems(1); 0214 setInsertPolicy(QComboBox::NoInsert); 0215 d->initDateWidget(); 0216 d->updateDateWidget(); 0217 0218 connect(d->m_dateMenu, &KDatePickerPopup::dateChanged, this, [this](QDate date) { 0219 if (d->isInDateRange(date)) { 0220 d->enterDate(date); 0221 } 0222 }); 0223 0224 connect(this, &QComboBox::editTextChanged, this, [this](const QString &text) { 0225 d->editDate(text); 0226 }); 0227 0228 connect(lineEdit(), &QLineEdit::returnPressed, this, [this]() { 0229 if (d->m_edited) { 0230 d->enterDate(date()); 0231 Q_EMIT dateChanged(date()); 0232 } 0233 }); 0234 } 0235 0236 KDateComboBox::~KDateComboBox() = default; 0237 0238 QDate KDateComboBox::date() const 0239 { 0240 d->parseDate(); 0241 return d->m_date; 0242 } 0243 0244 void KDateComboBox::setDate(const QDate &date) 0245 { 0246 if (date == d->m_date) { 0247 return; 0248 } 0249 0250 d->m_edited = false; 0251 assignDate(date); 0252 d->updateDateWidget(); 0253 Q_EMIT dateChanged(d->m_date); 0254 } 0255 0256 void KDateComboBox::assignDate(const QDate &date) 0257 { 0258 d->m_date = date; 0259 } 0260 0261 bool KDateComboBox::isValid() const 0262 { 0263 d->parseDate(); 0264 return d->isInDateRange(d->m_date); 0265 } 0266 0267 bool KDateComboBox::isNull() const 0268 { 0269 return lineEdit()->text().isEmpty(); 0270 } 0271 0272 KDateComboBox::Options KDateComboBox::options() const 0273 { 0274 return d->m_options; 0275 } 0276 0277 void KDateComboBox::setOptions(Options options) 0278 { 0279 if (options != d->m_options) { 0280 d->m_options = options; 0281 d->initDateWidget(); 0282 d->updateDateWidget(); 0283 } 0284 } 0285 0286 QDate KDateComboBox::minimumDate() const 0287 { 0288 return d->m_minDate; 0289 } 0290 0291 void KDateComboBox::setMinimumDate(const QDate &minDate, const QString &minWarnMsg) 0292 { 0293 if (minDate.isValid()) { 0294 d->setDateRange(minDate, d->m_maxDate, minWarnMsg, d->m_maxWarnMsg); 0295 } 0296 } 0297 0298 void KDateComboBox::resetMinimumDate() 0299 { 0300 d->setDateRange(QDate(), d->m_maxDate, QString(), d->m_maxWarnMsg); 0301 } 0302 0303 QDate KDateComboBox::maximumDate() const 0304 { 0305 return d->m_maxDate; 0306 } 0307 0308 void KDateComboBox::setMaximumDate(const QDate &maxDate, const QString &maxWarnMsg) 0309 { 0310 if (maxDate.isValid()) { 0311 d->setDateRange(d->m_minDate, maxDate, d->m_minWarnMsg, maxWarnMsg); 0312 } 0313 } 0314 0315 void KDateComboBox::resetMaximumDate() 0316 { 0317 d->setDateRange(d->m_minDate, QDate(), d->m_minWarnMsg, QString()); 0318 } 0319 0320 void KDateComboBox::setDateRange(const QDate &minDate, const QDate &maxDate, const QString &minWarnMsg, const QString &maxWarnMsg) 0321 { 0322 if (minDate.isValid() && maxDate.isValid()) { 0323 d->setDateRange(minDate, maxDate, minWarnMsg, maxWarnMsg); 0324 } 0325 } 0326 0327 void KDateComboBox::resetDateRange() 0328 { 0329 d->setDateRange(QDate(), QDate(), QString(), QString()); 0330 } 0331 0332 QLocale::FormatType KDateComboBox::displayFormat() const 0333 { 0334 return d->m_displayFormat; 0335 } 0336 0337 void KDateComboBox::setDisplayFormat(QLocale::FormatType format) 0338 { 0339 if (format != d->m_displayFormat) { 0340 d->m_displayFormat = format; 0341 d->initDateWidget(); 0342 d->updateDateWidget(); 0343 } 0344 } 0345 0346 QMap<QDate, QString> KDateComboBox::dateMap() const 0347 { 0348 return d->m_dateMenu->dateMap(); 0349 } 0350 0351 void KDateComboBox::setDateMap(QMap<QDate, QString> dateMap) 0352 { 0353 d->m_dateMenu->setDateMap(dateMap); 0354 } 0355 0356 bool KDateComboBox::eventFilter(QObject *object, QEvent *event) 0357 { 0358 return QComboBox::eventFilter(object, event); 0359 } 0360 0361 void KDateComboBox::keyPressEvent(QKeyEvent *keyEvent) 0362 { 0363 QDate temp; 0364 switch (keyEvent->key()) { 0365 case Qt::Key_Down: 0366 temp = d->m_date.addDays(-1); 0367 break; 0368 case Qt::Key_Up: 0369 temp = d->m_date.addDays(1); 0370 break; 0371 case Qt::Key_PageDown: 0372 temp = d->m_date.addMonths(-1); 0373 break; 0374 case Qt::Key_PageUp: 0375 temp = d->m_date.addMonths(1); 0376 break; 0377 default: 0378 QComboBox::keyPressEvent(keyEvent); 0379 return; 0380 } 0381 if (d->isInDateRange(temp)) { 0382 d->enterDate(temp); 0383 } 0384 } 0385 0386 void KDateComboBox::focusOutEvent(QFocusEvent *event) 0387 { 0388 d->parseDate(); 0389 d->warnDate(); 0390 if (d->m_edited) { 0391 d->m_edited = false; 0392 Q_EMIT dateChanged(d->m_date); 0393 } 0394 QComboBox::focusOutEvent(event); 0395 } 0396 0397 void KDateComboBox::showPopup() 0398 { 0399 if (!isEditable() || !d->m_dateMenu // 0400 || (d->m_options & KDateComboBox::SelectDate) != KDateComboBox::SelectDate) { 0401 return; 0402 } 0403 0404 d->m_dateMenu->setDate(d->m_date); 0405 0406 const QRect desk = screen()->geometry(); 0407 0408 QPoint popupPoint = mapToGlobal(QPoint(0, 0)); 0409 0410 const int dateFrameHeight = d->m_dateMenu->sizeHint().height(); 0411 if (popupPoint.y() + height() + dateFrameHeight > desk.bottom()) { 0412 popupPoint.setY(popupPoint.y() - dateFrameHeight); 0413 } else { 0414 popupPoint.setY(popupPoint.y() + height()); 0415 } 0416 0417 const int dateFrameWidth = d->m_dateMenu->sizeHint().width(); 0418 if (popupPoint.x() + dateFrameWidth > desk.right()) { 0419 popupPoint.setX(desk.right() - dateFrameWidth); 0420 } 0421 0422 if (popupPoint.x() < desk.left()) { 0423 popupPoint.setX(desk.left()); 0424 } 0425 0426 if (popupPoint.y() < desk.top()) { 0427 popupPoint.setY(desk.top()); 0428 } 0429 0430 d->m_dateMenu->popup(popupPoint); 0431 } 0432 0433 void KDateComboBox::hidePopup() 0434 { 0435 QComboBox::hidePopup(); 0436 } 0437 0438 void KDateComboBox::mousePressEvent(QMouseEvent *event) 0439 { 0440 QComboBox::mousePressEvent(event); 0441 } 0442 0443 void KDateComboBox::wheelEvent(QWheelEvent *event) 0444 { 0445 QDate temp; 0446 if (event->angleDelta().y() < 0) { 0447 temp = d->m_date.addDays(-1); 0448 } else { 0449 temp = d->m_date.addDays(1); 0450 } 0451 if (d->isInDateRange(temp)) { 0452 d->enterDate(temp); 0453 } 0454 } 0455 0456 void KDateComboBox::focusInEvent(QFocusEvent *event) 0457 { 0458 QComboBox::focusInEvent(event); 0459 } 0460 0461 void KDateComboBox::resizeEvent(QResizeEvent *event) 0462 { 0463 QComboBox::resizeEvent(event); 0464 } 0465 0466 #include "moc_kdatecombobox.cpp"