1/* -*- C++ -*-
2 This file is part of the KDE libraries
3 SPDX-FileCopyrightText: 1997 Tim D. Gilman <tdgilman@best.org>
4 SPDX-FileCopyrightText: 1998-2001 Mirko Boehm (mirko@kde.org)
5 SPDX-FileCopyrightText: 2007 John Layt <john@layt.net>
6 SPDX-FileCopyrightText: 2022 g10 Code GmbH
7 SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
8
9 SPDX-License-Identifier: LGPL-2.0-or-later
10*/
11
12#include "kdatepicker.h"
13#include "kdatepicker_p.h"
14
15#include "common_helpers_p.h"
16#include "kdatetable_p.h"
17#include <kpopupframe.h>
18
19#include <QApplication>
20#include <QComboBox>
21#include <QFont>
22#include <QFontDatabase>
23#include <QKeyEvent>
24#include <QLayout>
25#include <QMenu>
26#include <QStyle>
27#include <QToolButton>
28
29#include "loggingcategory.h"
30#include "moc_kdatepicker.cpp"
31#include "moc_kdatepicker_p.cpp"
32
33class DatePickerValidator : public QValidator
34{
35 Q_OBJECT
36public:
37 explicit DatePickerValidator(KDatePicker *parent)
38 : QValidator(parent)
39 , picker(parent)
40 {
41 }
42
43 State validate(QString &text, int &) const override
44 {
45 return toDate(text).isValid() ? QValidator::Acceptable : QValidator::Intermediate;
46 }
47
48 QDate toDate(const QString &text) const
49 {
50 QLocale locale = picker->locale();
51 const QList<QString> formats{
52 locale.dateFormat(format: QLocale::LongFormat),
53 locale.dateFormat(format: QLocale::ShortFormat),
54 locale.dateFormat(format: QLocale::NarrowFormat),
55 dateFormatWith4DigitYear(locale, format: QLocale::ShortFormat),
56 QStringLiteral("yyyy-MM-dd"),
57 };
58
59 QDate date;
60 for (const auto &format : formats) {
61 date = locale.toDate(string: text, format);
62 if (date.isValid()) {
63 break;
64 }
65 }
66 if (!date.isValid()) {
67 qCDebug(KWidgetsAddonsLog) << "Could not parse text as date:" << text;
68 }
69 return date;
70 }
71
72private:
73 KDatePicker *const picker;
74};
75
76// Week numbers are defined by ISO 8601
77// See http://www.merlyn.demon.co.uk/weekinfo.htm for details
78
79KDatePickerPrivateYearSelector::KDatePickerPrivateYearSelector(const QDate &currentDate, QWidget *parent)
80 : QLineEdit(parent)
81 , val(new QIntValidator(this))
82 , result(0)
83 , oldDate{currentDate}
84{
85 setFont(QFontDatabase::systemFont(type: QFontDatabase::GeneralFont));
86
87 setFrame(false);
88
89 // TODO: Find a way to get that from QLocale
90 // val->setRange( calendar->year( calendar->earliestValidDate() ),
91 // calendar->year( calendar->latestValidDate() ) );
92 setValidator(val);
93
94 connect(sender: this, signal: &QLineEdit::returnPressed, context: this, slot: &KDatePickerPrivateYearSelector::yearEnteredSlot);
95}
96
97void KDatePickerPrivateYearSelector::yearEnteredSlot()
98{
99 bool ok;
100 int newYear;
101
102 // check if entered value is a number
103 newYear = text().toInt(ok: &ok);
104 if (!ok) {
105 QApplication::beep();
106 return;
107 }
108
109 // check if new year will lead to a valid date
110 if (QDate(newYear, oldDate.month(), oldDate.day()).isValid()) {
111 result = newYear;
112 Q_EMIT closeMe(t1: 1);
113 } else {
114 QApplication::beep();
115 }
116}
117
118int KDatePickerPrivateYearSelector::year()
119{
120 return result;
121}
122
123void KDatePickerPrivateYearSelector::setYear(const QDate &year)
124{
125 setText(locale().toString(date: year, QStringLiteral("yyyy")).rightJustified(width: 4, fill: QLatin1Char('0')));
126}
127
128class KDatePickerPrivate
129{
130 Q_DECLARE_TR_FUNCTIONS(KDatePicker)
131
132public:
133 explicit KDatePickerPrivate(KDatePicker *qq)
134 : q(qq)
135 {
136 }
137
138 void fillWeeksCombo();
139 QDate validDateInYearMonth(int year, int month);
140
141 /// the date table
142 KDatePicker *q;
143
144 QToolButton *closeButton = nullptr;
145 QComboBox *selectWeek = nullptr;
146 QToolButton *todayButton = nullptr;
147 QBoxLayout *navigationLayout = nullptr;
148
149 /// the year forward button
150 QToolButton *yearForward = nullptr;
151 /// the year backward button
152 QToolButton *yearBackward = nullptr;
153 /// the month forward button
154 QToolButton *monthForward = nullptr;
155 /// the month backward button
156 QToolButton *monthBackward = nullptr;
157 /// the button for selecting the month directly
158 QToolButton *selectMonth = nullptr;
159 /// the button for selecting the year directly
160 QToolButton *selectYear = nullptr;
161 /// the line edit to enter the date directly
162 QLineEdit *line = nullptr;
163 /// the validator for the line edit:
164 DatePickerValidator *val = nullptr;
165 /// the date table
166 KDateTable *table = nullptr;
167 /// the widest month string in pixels:
168 QSize maxMonthRect;
169
170 /// the font size for the widget
171 int fontsize = -1;
172};
173
174void KDatePickerPrivate::fillWeeksCombo()
175{
176 // every year can have a different number of weeks
177 // it could be that we had 53,1..52 and now 1..53 which is the same number but different
178 // so always fill with new values
179 // We show all week numbers for all weeks between first day of year to last day of year
180 // This of course can be a list like 53,1,2..52
181
182 const QDate thisDate = q->date();
183 const int thisYear = thisDate.year();
184 QDate day(thisDate.year(), 1, 1);
185 const QDate lastDayOfYear = QDate(thisDate.year() + 1, 1, 1).addDays(days: -1);
186
187 selectWeek->clear();
188
189 // Starting from the first day in the year, loop through the year a week at a time
190 // adding an entry to the week combo for each week in the year
191
192 for (; day.isValid() && day <= lastDayOfYear; day = day.addDays(days: 7)) {
193 // Get the ISO week number for the current day and what year that week is in
194 // e.g. 1st day of this year may fall in week 53 of previous year
195 int weekYear = thisYear;
196 const int week = day.weekNumber(yearNum: &weekYear);
197 QString weekString = tr(sourceText: "Week %1").arg(a: q->locale().toString(i: week));
198
199 // show that this is a week from a different year
200 if (weekYear != thisYear) {
201 weekString += QLatin1Char('*');
202 }
203
204 // when the week is selected, go to the same weekday as the one
205 // that is currently selected in the date table
206 QDate targetDate = day.addDays(days: thisDate.dayOfWeek() - day.dayOfWeek());
207 selectWeek->addItem(atext: weekString, auserData: targetDate);
208
209 // make sure that the week of the lastDayOfYear is always inserted: in Chinese calendar
210 // system, this is not always the case
211 if (day < lastDayOfYear //
212 && day.daysTo(d: lastDayOfYear) < 7 //
213 && lastDayOfYear.weekNumber() != day.weekNumber()) {
214 day = lastDayOfYear.addDays(days: -7);
215 }
216 }
217}
218
219QDate KDatePickerPrivate::validDateInYearMonth(int year, int month)
220{
221 QDate newDate;
222
223 // Try to create a valid date in this year and month
224 // First try the first of the month, then try last of month
225 if (QDate(year, month, 1).isValid()) {
226 newDate = QDate(year, month, 1);
227 } else if (QDate(year, month + 1, 1).isValid()) {
228 newDate = QDate(year, month + 1, 1).addDays(days: -1);
229 } else {
230 newDate = QDate::fromJulianDay(jd_: 0);
231 }
232
233 return newDate;
234}
235
236KDatePicker::KDatePicker(QWidget *parent)
237 : QFrame(parent)
238 , d(new KDatePickerPrivate(this))
239{
240 initWidget(date: QDate::currentDate());
241}
242
243KDatePicker::KDatePicker(const QDate &date_, QWidget *parent)
244 : QFrame(parent)
245 , d(new KDatePickerPrivate(this))
246{
247 initWidget(date: date_);
248}
249
250void KDatePicker::initWidget(const QDate &date_)
251{
252 const int horizontalSpacing = style()->pixelMetric(metric: QStyle::PM_LayoutHorizontalSpacing);
253
254 QBoxLayout *topLayout = new QVBoxLayout(this);
255 topLayout->setSpacing(0);
256 topLayout->setContentsMargins(left: 0, top: 0, right: 0, bottom: 0);
257
258 d->navigationLayout = new QHBoxLayout();
259 d->navigationLayout->setSpacing(0);
260 d->navigationLayout->setContentsMargins(left: 0, top: 0, right: 0, bottom: 0);
261 topLayout->addLayout(layout: d->navigationLayout);
262 d->navigationLayout->addStretch();
263 d->yearBackward = new QToolButton(this);
264 d->yearBackward->setAutoRaise(true);
265 d->navigationLayout->addWidget(d->yearBackward);
266 d->monthBackward = new QToolButton(this);
267 d->monthBackward->setAutoRaise(true);
268 d->navigationLayout->addWidget(d->monthBackward);
269 d->navigationLayout->addSpacing(size: horizontalSpacing);
270
271 d->selectMonth = new QToolButton(this);
272 d->selectMonth->setAutoRaise(true);
273 d->navigationLayout->addWidget(d->selectMonth);
274 d->selectYear = new QToolButton(this);
275 d->selectYear->setCheckable(true);
276 d->selectYear->setAutoRaise(true);
277 d->navigationLayout->addWidget(d->selectYear);
278 d->navigationLayout->addSpacing(size: horizontalSpacing);
279
280 d->monthForward = new QToolButton(this);
281 d->monthForward->setAutoRaise(true);
282 d->navigationLayout->addWidget(d->monthForward);
283 d->yearForward = new QToolButton(this);
284 d->yearForward->setAutoRaise(true);
285 d->navigationLayout->addWidget(d->yearForward);
286 d->navigationLayout->addStretch();
287
288 d->line = new QLineEdit(this);
289 d->val = new DatePickerValidator(this);
290 d->table = new KDateTable(this);
291 d->table->setObjectName(QStringLiteral("table"));
292 setFocusProxy(d->table);
293
294 d->fontsize = QFontDatabase::systemFont(type: QFontDatabase::GeneralFont).pointSize();
295 if (d->fontsize == -1) {
296 d->fontsize = QFontInfo(QFontDatabase::systemFont(type: QFontDatabase::GeneralFont)).pointSize();
297 }
298
299 d->fontsize++; // Make a little bigger
300
301 d->selectWeek = new QComboBox(this); // read only week selection
302 d->selectWeek->setFocusPolicy(Qt::NoFocus);
303 d->todayButton = new QToolButton(this);
304 d->todayButton->setIcon(QIcon::fromTheme(QStringLiteral("go-jump-today")));
305
306 d->yearForward->setToolTip(tr(s: "Next year", c: "@info:tooltip"));
307 d->yearBackward->setToolTip(tr(s: "Previous year", c: "@info:tooltip"));
308 d->monthForward->setToolTip(tr(s: "Next month", c: "@info:tooltip"));
309 d->monthBackward->setToolTip(tr(s: "Previous month", c: "@info:tooltip"));
310 d->selectWeek->setToolTip(tr(s: "Select a week", c: "@info:tooltip"));
311 d->selectMonth->setToolTip(tr(s: "Select a month", c: "@info:tooltip"));
312 d->selectYear->setToolTip(tr(s: "Select a year", c: "@info:tooltip"));
313 d->todayButton->setToolTip(tr(s: "Select the current day", c: "@info:tooltip"));
314
315 // -----
316 setFontSize(d->fontsize);
317 d->line->setValidator(d->val);
318 d->line->installEventFilter(filterObj: this);
319 if (QApplication::isRightToLeft()) {
320 d->yearForward->setIcon(QIcon::fromTheme(QStringLiteral("arrow-left-double")));
321 d->yearBackward->setIcon(QIcon::fromTheme(QStringLiteral("arrow-right-double")));
322 d->monthForward->setIcon(QIcon::fromTheme(QStringLiteral("arrow-left")));
323 d->monthBackward->setIcon(QIcon::fromTheme(QStringLiteral("arrow-right")));
324 } else {
325 d->yearForward->setIcon(QIcon::fromTheme(QStringLiteral("arrow-right-double")));
326 d->yearBackward->setIcon(QIcon::fromTheme(QStringLiteral("arrow-left-double")));
327 d->monthForward->setIcon(QIcon::fromTheme(QStringLiteral("arrow-right")));
328 d->monthBackward->setIcon(QIcon::fromTheme(QStringLiteral("arrow-left")));
329 }
330
331 connect(sender: d->table, signal: &KDateTable::dateChanged, context: this, slot: &KDatePicker::dateChangedSlot);
332 connect(sender: d->table, signal: &KDateTable::tableClicked, context: this, slot: &KDatePicker::tableClickedSlot);
333 connect(sender: d->monthForward, signal: &QAbstractButton::clicked, context: this, slot: &KDatePicker::monthForwardClicked);
334 connect(sender: d->monthBackward, signal: &QAbstractButton::clicked, context: this, slot: &KDatePicker::monthBackwardClicked);
335 connect(sender: d->yearForward, signal: &QAbstractButton::clicked, context: this, slot: &KDatePicker::yearForwardClicked);
336 connect(sender: d->yearBackward, signal: &QAbstractButton::clicked, context: this, slot: &KDatePicker::yearBackwardClicked);
337 connect(sender: d->selectWeek, signal: &QComboBox::activated, context: this, slot: &KDatePicker::weekSelected);
338 connect(sender: d->todayButton, signal: &QAbstractButton::clicked, context: this, slot: &KDatePicker::todayButtonClicked);
339 connect(sender: d->selectMonth, signal: &QAbstractButton::clicked, context: this, slot: &KDatePicker::selectMonthClicked);
340 connect(sender: d->selectYear, signal: &QAbstractButton::toggled, context: this, slot: &KDatePicker::selectYearClicked);
341 connect(sender: d->line, signal: &QLineEdit::returnPressed, context: this, slot: &KDatePicker::lineEnterPressed);
342
343 topLayout->addWidget(d->table);
344
345 QBoxLayout *bottomLayout = new QHBoxLayout();
346 bottomLayout->setContentsMargins(left: 0, top: 0, right: 0, bottom: 0);
347 bottomLayout->setSpacing(0);
348 topLayout->addLayout(layout: bottomLayout);
349
350 bottomLayout->addWidget(d->todayButton);
351 bottomLayout->addWidget(d->line);
352 bottomLayout->addWidget(d->selectWeek);
353
354 d->table->setDate(date_);
355 dateChangedSlot(date: date_); // needed because table emits changed only when newDate != oldDate
356}
357
358KDatePicker::~KDatePicker() = default;
359
360bool KDatePicker::eventFilter(QObject *o, QEvent *e)
361{
362 if (e->type() == QEvent::KeyPress) {
363 QKeyEvent *k = (QKeyEvent *)e;
364
365 if ((k->key() == Qt::Key_PageUp) //
366 || (k->key() == Qt::Key_PageDown) //
367 || (k->key() == Qt::Key_Up) //
368 || (k->key() == Qt::Key_Down)) {
369 QApplication::sendEvent(receiver: d->table, event: e);
370 d->table->setFocus();
371 return true; // eat event
372 }
373 }
374 return QFrame::eventFilter(watched: o, event: e);
375}
376
377void KDatePicker::resizeEvent(QResizeEvent *e)
378{
379 QWidget::resizeEvent(event: e);
380}
381
382void KDatePicker::dateChangedSlot(const QDate &date_)
383{
384 d->line->setText(locale().toString(date: date_, format: dateFormatWith4DigitYear(locale: locale(), format: QLocale::ShortFormat)));
385 d->selectMonth->setText(locale().standaloneMonthName(date_.month(), format: QLocale::LongFormat));
386 d->fillWeeksCombo();
387
388 // calculate the item num in the week combo box; normalize selected day so as if 1.1. is the first day of the week
389 QDate firstDay(date_.year(), 1, 1);
390 // If we cannot successfully create the 1st of the year, this can only mean that
391 // the 1st is before the earliest valid date in the current calendar system, so use
392 // the earliestValidDate as the first day.
393 // In particular covers the case of Gregorian where 1/1/-4713 is not a valid QDate
394 d->selectWeek->setCurrentIndex((date_.dayOfYear() + firstDay.dayOfWeek() - 2) / 7);
395 d->selectYear->setText(locale().toString(date: date_, QStringLiteral("yyyy")).rightJustified(width: 4, fill: QLatin1Char('0')));
396
397 Q_EMIT dateChanged(t1: date_);
398}
399
400void KDatePicker::tableClickedSlot()
401{
402 Q_EMIT dateSelected(t1: date());
403 Q_EMIT tableClicked();
404}
405
406// TODO KF6: remove the const & from the returned QDate
407const QDate &KDatePicker::date() const
408{
409 return d->table->date();
410}
411
412bool KDatePicker::setDate(const QDate &date_)
413{
414 // the table setDate does validity checking for us
415 // this also emits dateChanged() which then calls our dateChangedSlot()
416 return d->table->setDate(date_);
417}
418
419void KDatePicker::monthForwardClicked()
420{
421 if (!setDate(date().addMonths(months: 1))) {
422 QApplication::beep();
423 }
424 d->table->setFocus();
425}
426
427void KDatePicker::monthBackwardClicked()
428{
429 if (!setDate(date().addMonths(months: -1))) {
430 QApplication::beep();
431 }
432 d->table->setFocus();
433}
434
435void KDatePicker::yearForwardClicked()
436{
437 if (!setDate(d->table->date().addYears(years: 1))) {
438 QApplication::beep();
439 }
440 d->table->setFocus();
441}
442
443void KDatePicker::yearBackwardClicked()
444{
445 if (!setDate(d->table->date().addYears(years: -1))) {
446 QApplication::beep();
447 }
448 d->table->setFocus();
449}
450
451void KDatePicker::weekSelected(int index)
452{
453 QDate targetDay = d->selectWeek->itemData(index).toDateTime().date();
454
455 if (!setDate(targetDay)) {
456 QApplication::beep();
457 }
458 d->table->setFocus();
459}
460
461void KDatePicker::selectMonthClicked()
462{
463 QDate thisDate(date());
464 d->table->setFocus();
465
466 QMenu popup(d->selectMonth);
467 // Populate the pick list with all the month names, this may change by year
468 // JPL do we need to do something here for months that fall outside valid range?
469 const int monthsInYear = QDate(thisDate.year() + 1, 1, 1).addDays(days: -1).month();
470 for (int m = 1; m <= monthsInYear; m++) {
471 popup.addAction(text: locale().standaloneMonthName(m))->setData(m);
472 }
473
474 QAction *item = popup.actions()[thisDate.month() - 1];
475 // if this happens the above should already given an assertion
476 if (item) {
477 popup.setActiveAction(item);
478 }
479
480 // cancelled
481 if ((item = popup.exec(pos: d->selectMonth->mapToGlobal(QPoint(0, 0)), at: item)) == nullptr) {
482 return;
483 }
484
485 // We need to create a valid date in the month selected so we can find out how many days are
486 // in the month.
487 QDate newDate(thisDate.year(), item->data().toInt(), 1);
488
489 // If we have succeeded in creating a date in the new month, then try to create the new date,
490 // checking we don't set a day after the last day of the month
491 newDate.setDate(year: newDate.year(), month: newDate.month(), day: qMin(a: thisDate.day(), b: newDate.daysInMonth()));
492
493 // Set the date, if it's invalid in any way then alert user and don't update
494 if (!setDate(newDate)) {
495 QApplication::beep();
496 }
497}
498
499void KDatePicker::selectYearClicked()
500{
501 if (!d->selectYear->isChecked()) {
502 return;
503 }
504
505 QDate thisDate(date());
506
507 KPopupFrame *popup = new KPopupFrame(this);
508 KDatePickerPrivateYearSelector *picker = new KDatePickerPrivateYearSelector(date(), popup);
509 picker->resize(picker->sizeHint());
510 picker->setYear(thisDate);
511 picker->selectAll();
512 popup->setMainWidget(picker);
513 connect(sender: picker, signal: &KDatePickerPrivateYearSelector::closeMe, context: popup, slot: &KPopupFrame::close);
514 picker->setFocus();
515
516 if (popup->exec(p: d->selectYear->mapToGlobal(QPoint(0, d->selectMonth->height())))) {
517 // We need to create a valid date in the year/month selected so we can find out how many
518 // days are in the month.
519 QDate newDate(picker->year(), thisDate.month(), 1);
520
521 // If we have succeeded in creating a date in the new month, then try to create the new
522 // date, checking we don't set a day after the last day of the month
523 newDate = QDate(newDate.year(), newDate.month(), qMin(a: thisDate.day(), b: newDate.daysInMonth()));
524
525 // Set the date, if it's invalid in any way then alert user and don't update
526 if (!setDate(newDate)) {
527 QApplication::beep();
528 }
529 }
530 delete popup;
531
532 d->selectYear->setChecked(false);
533}
534
535void KDatePicker::uncheckYearSelector()
536{
537 d->selectYear->setChecked(false);
538 d->selectYear->update();
539}
540
541void KDatePicker::changeEvent(QEvent *event)
542{
543 if (event && event->type() == QEvent::EnabledChange) {
544 if (isEnabled()) {
545 d->table->setFocus();
546 }
547 }
548}
549
550KDateTable *KDatePicker::dateTable() const
551{
552 return d->table;
553}
554
555void KDatePicker::lineEnterPressed()
556{
557 const QDate newDate = d->val->toDate(text: d->line->text());
558
559 if (newDate.isValid()) {
560 Q_EMIT dateEntered(t1: newDate);
561 setDate(newDate);
562 d->table->setFocus();
563 } else {
564 QApplication::beep();
565 }
566}
567
568void KDatePicker::todayButtonClicked()
569{
570 setDate(QDate::currentDate());
571 d->table->setFocus();
572}
573
574QSize KDatePicker::sizeHint() const
575{
576 return QWidget::sizeHint();
577}
578
579void KDatePicker::setFontSize(int s)
580{
581 QWidget *const buttons[] = {
582 d->selectMonth,
583 d->selectYear,
584 };
585 QFont font;
586 QRect r;
587 // -----
588 d->fontsize = s;
589 for (QWidget *button : buttons) {
590 font = button->font();
591 font.setPointSize(s);
592 button->setFont(font);
593 }
594 d->table->setFontSize(s);
595
596 QFontMetrics metrics(d->selectMonth->fontMetrics());
597 QString longestMonth;
598
599 for (int i = 1;; ++i) {
600 QString str = locale().standaloneMonthName(i, format: QLocale::LongFormat);
601 if (str.isNull()) {
602 break;
603 }
604 r = metrics.boundingRect(text: str);
605
606 if (r.width() > d->maxMonthRect.width()) {
607 d->maxMonthRect.setWidth(r.width());
608 longestMonth = str;
609 }
610 if (r.height() > d->maxMonthRect.height()) {
611 d->maxMonthRect.setHeight(r.height());
612 }
613 }
614
615 QStyleOptionToolButton opt;
616 opt.initFrom(w: d->selectMonth);
617 opt.text = longestMonth;
618
619 // stolen from QToolButton
620 QSize textSize = metrics.size(flags: Qt::TextShowMnemonic, str: longestMonth);
621 textSize.setWidth(textSize.width() + metrics.horizontalAdvance(QLatin1Char(' ')) * 2);
622 int w = textSize.width();
623 int h = textSize.height();
624 opt.rect.setHeight(h); // PM_MenuButtonIndicator depends on the height
625
626 QSize metricBound = style()->sizeFromContents(ct: QStyle::CT_ToolButton, opt: &opt, contentsSize: QSize(w, h), w: d->selectMonth);
627
628 d->selectMonth->setMinimumSize(metricBound);
629}
630
631int KDatePicker::fontSize() const
632{
633 return d->fontsize;
634}
635
636void KDatePicker::setCloseButton(bool enable)
637{
638 if (enable == (d->closeButton != nullptr)) {
639 return;
640 }
641
642 if (enable) {
643 d->closeButton = new QToolButton(this);
644 d->closeButton->setAutoRaise(true);
645 const int horizontalSpacing = style()->pixelMetric(metric: QStyle::PM_LayoutHorizontalSpacing);
646 d->navigationLayout->addSpacing(size: horizontalSpacing);
647 d->navigationLayout->addWidget(d->closeButton);
648 d->closeButton->setToolTip(tr(s: "Close", c: "@action:button"));
649 d->closeButton->setIcon(QIcon::fromTheme(QStringLiteral("window-close")));
650 connect(sender: d->closeButton, signal: &QAbstractButton::clicked, context: topLevelWidget(), slot: &QWidget::close);
651 } else {
652 delete d->closeButton;
653 d->closeButton = nullptr;
654 }
655
656 updateGeometry();
657}
658
659bool KDatePicker::hasCloseButton() const
660{
661 return (d->closeButton);
662}
663
664void KDatePicker::setDateRange(const QDate &minDate, const QDate &maxDate)
665{
666 d->table->setDateRange(minDate, maxDate);
667}
668
669#include "kdatepicker.moc"
670

source code of kwidgetsaddons/src/kdatepicker.cpp