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 setFocusProxy(d->table);
292
293 d->fontsize = QFontDatabase::systemFont(type: QFontDatabase::GeneralFont).pointSize();
294 if (d->fontsize == -1) {
295 d->fontsize = QFontInfo(QFontDatabase::systemFont(type: QFontDatabase::GeneralFont)).pointSize();
296 }
297
298 d->fontsize++; // Make a little bigger
299
300 d->selectWeek = new QComboBox(this); // read only week selection
301 d->selectWeek->setFocusPolicy(Qt::NoFocus);
302 d->todayButton = new QToolButton(this);
303 d->todayButton->setIcon(QIcon::fromTheme(QStringLiteral("go-jump-today")));
304
305 d->yearForward->setToolTip(tr(s: "Next year", c: "@info:tooltip"));
306 d->yearBackward->setToolTip(tr(s: "Previous year", c: "@info:tooltip"));
307 d->monthForward->setToolTip(tr(s: "Next month", c: "@info:tooltip"));
308 d->monthBackward->setToolTip(tr(s: "Previous month", c: "@info:tooltip"));
309 d->selectWeek->setToolTip(tr(s: "Select a week", c: "@info:tooltip"));
310 d->selectMonth->setToolTip(tr(s: "Select a month", c: "@info:tooltip"));
311 d->selectYear->setToolTip(tr(s: "Select a year", c: "@info:tooltip"));
312 d->todayButton->setToolTip(tr(s: "Select the current day", c: "@info:tooltip"));
313
314 // -----
315 setFontSize(d->fontsize);
316 d->line->setValidator(d->val);
317 d->line->installEventFilter(filterObj: this);
318 if (QApplication::isRightToLeft()) {
319 d->yearForward->setIcon(QIcon::fromTheme(QStringLiteral("arrow-left-double")));
320 d->yearBackward->setIcon(QIcon::fromTheme(QStringLiteral("arrow-right-double")));
321 d->monthForward->setIcon(QIcon::fromTheme(QStringLiteral("arrow-left")));
322 d->monthBackward->setIcon(QIcon::fromTheme(QStringLiteral("arrow-right")));
323 } else {
324 d->yearForward->setIcon(QIcon::fromTheme(QStringLiteral("arrow-right-double")));
325 d->yearBackward->setIcon(QIcon::fromTheme(QStringLiteral("arrow-left-double")));
326 d->monthForward->setIcon(QIcon::fromTheme(QStringLiteral("arrow-right")));
327 d->monthBackward->setIcon(QIcon::fromTheme(QStringLiteral("arrow-left")));
328 }
329
330 connect(sender: d->table, signal: &KDateTable::dateChanged, context: this, slot: &KDatePicker::dateChangedSlot);
331 connect(sender: d->table, signal: &KDateTable::tableClicked, context: this, slot: &KDatePicker::tableClickedSlot);
332 connect(sender: d->monthForward, signal: &QAbstractButton::clicked, context: this, slot: &KDatePicker::monthForwardClicked);
333 connect(sender: d->monthBackward, signal: &QAbstractButton::clicked, context: this, slot: &KDatePicker::monthBackwardClicked);
334 connect(sender: d->yearForward, signal: &QAbstractButton::clicked, context: this, slot: &KDatePicker::yearForwardClicked);
335 connect(sender: d->yearBackward, signal: &QAbstractButton::clicked, context: this, slot: &KDatePicker::yearBackwardClicked);
336 connect(sender: d->selectWeek, signal: &QComboBox::activated, context: this, slot: &KDatePicker::weekSelected);
337 connect(sender: d->todayButton, signal: &QAbstractButton::clicked, context: this, slot: &KDatePicker::todayButtonClicked);
338 connect(sender: d->selectMonth, signal: &QAbstractButton::clicked, context: this, slot: &KDatePicker::selectMonthClicked);
339 connect(sender: d->selectYear, signal: &QAbstractButton::toggled, context: this, slot: &KDatePicker::selectYearClicked);
340 connect(sender: d->line, signal: &QLineEdit::returnPressed, context: this, slot: &KDatePicker::lineEnterPressed);
341
342 topLayout->addWidget(d->table);
343
344 QBoxLayout *bottomLayout = new QHBoxLayout();
345 bottomLayout->setContentsMargins(left: 0, top: 0, right: 0, bottom: 0);
346 bottomLayout->setSpacing(0);
347 topLayout->addLayout(layout: bottomLayout);
348
349 bottomLayout->addWidget(d->todayButton);
350 bottomLayout->addWidget(d->line);
351 bottomLayout->addWidget(d->selectWeek);
352
353 d->table->setDate(date_);
354 dateChangedSlot(date: date_); // needed because table emits changed only when newDate != oldDate
355}
356
357KDatePicker::~KDatePicker() = default;
358
359bool KDatePicker::eventFilter(QObject *o, QEvent *e)
360{
361 if (e->type() == QEvent::KeyPress) {
362 QKeyEvent *k = (QKeyEvent *)e;
363
364 if ((k->key() == Qt::Key_PageUp) //
365 || (k->key() == Qt::Key_PageDown) //
366 || (k->key() == Qt::Key_Up) //
367 || (k->key() == Qt::Key_Down)) {
368 QApplication::sendEvent(receiver: d->table, event: e);
369 d->table->setFocus();
370 return true; // eat event
371 }
372 }
373 return QFrame::eventFilter(watched: o, event: e);
374}
375
376void KDatePicker::resizeEvent(QResizeEvent *e)
377{
378 QWidget::resizeEvent(event: e);
379}
380
381void KDatePicker::dateChangedSlot(const QDate &date_)
382{
383 d->line->setText(locale().toString(date: date_, format: dateFormatWith4DigitYear(locale: locale(), format: QLocale::ShortFormat)));
384 d->selectMonth->setText(locale().standaloneMonthName(date_.month(), format: QLocale::LongFormat));
385 d->fillWeeksCombo();
386
387 // calculate the item num in the week combo box; normalize selected day so as if 1.1. is the first day of the week
388 QDate firstDay(date_.year(), 1, 1);
389 // If we cannot successfully create the 1st of the year, this can only mean that
390 // the 1st is before the earliest valid date in the current calendar system, so use
391 // the earliestValidDate as the first day.
392 // In particular covers the case of Gregorian where 1/1/-4713 is not a valid QDate
393 d->selectWeek->setCurrentIndex((date_.dayOfYear() + firstDay.dayOfWeek() - 2) / 7);
394 d->selectYear->setText(locale().toString(date: date_, QStringLiteral("yyyy")).rightJustified(width: 4, fill: QLatin1Char('0')));
395
396 Q_EMIT dateChanged(t1: date_);
397}
398
399void KDatePicker::tableClickedSlot()
400{
401 Q_EMIT dateSelected(t1: date());
402 Q_EMIT tableClicked();
403}
404
405// TODO KF6: remove the const & from the returned QDate
406const QDate &KDatePicker::date() const
407{
408 return d->table->date();
409}
410
411bool KDatePicker::setDate(const QDate &date_)
412{
413 // the table setDate does validity checking for us
414 // this also emits dateChanged() which then calls our dateChangedSlot()
415 return d->table->setDate(date_);
416}
417
418void KDatePicker::monthForwardClicked()
419{
420 if (!setDate(date().addMonths(months: 1))) {
421 QApplication::beep();
422 }
423 d->table->setFocus();
424}
425
426void KDatePicker::monthBackwardClicked()
427{
428 if (!setDate(date().addMonths(months: -1))) {
429 QApplication::beep();
430 }
431 d->table->setFocus();
432}
433
434void KDatePicker::yearForwardClicked()
435{
436 if (!setDate(d->table->date().addYears(years: 1))) {
437 QApplication::beep();
438 }
439 d->table->setFocus();
440}
441
442void KDatePicker::yearBackwardClicked()
443{
444 if (!setDate(d->table->date().addYears(years: -1))) {
445 QApplication::beep();
446 }
447 d->table->setFocus();
448}
449
450void KDatePicker::weekSelected(int index)
451{
452 QDate targetDay = d->selectWeek->itemData(index).toDateTime().date();
453
454 if (!setDate(targetDay)) {
455 QApplication::beep();
456 }
457 d->table->setFocus();
458}
459
460void KDatePicker::selectMonthClicked()
461{
462 QDate thisDate(date());
463 d->table->setFocus();
464
465 QMenu popup(d->selectMonth);
466 // Populate the pick list with all the month names, this may change by year
467 // JPL do we need to do something here for months that fall outside valid range?
468 const int monthsInYear = QDate(thisDate.year() + 1, 1, 1).addDays(days: -1).month();
469 for (int m = 1; m <= monthsInYear; m++) {
470 popup.addAction(text: locale().standaloneMonthName(m))->setData(m);
471 }
472
473 QAction *item = popup.actions()[thisDate.month() - 1];
474 // if this happens the above should already given an assertion
475 if (item) {
476 popup.setActiveAction(item);
477 }
478
479 // cancelled
480 if ((item = popup.exec(pos: d->selectMonth->mapToGlobal(QPoint(0, 0)), at: item)) == nullptr) {
481 return;
482 }
483
484 // We need to create a valid date in the month selected so we can find out how many days are
485 // in the month.
486 QDate newDate(thisDate.year(), item->data().toInt(), 1);
487
488 // If we have succeeded in creating a date in the new month, then try to create the new date,
489 // checking we don't set a day after the last day of the month
490 newDate.setDate(year: newDate.year(), month: newDate.month(), day: qMin(a: thisDate.day(), b: newDate.daysInMonth()));
491
492 // Set the date, if it's invalid in any way then alert user and don't update
493 if (!setDate(newDate)) {
494 QApplication::beep();
495 }
496}
497
498void KDatePicker::selectYearClicked()
499{
500 if (!d->selectYear->isChecked()) {
501 return;
502 }
503
504 QDate thisDate(date());
505
506 KPopupFrame *popup = new KPopupFrame(this);
507 KDatePickerPrivateYearSelector *picker = new KDatePickerPrivateYearSelector(date(), popup);
508 picker->resize(picker->sizeHint());
509 picker->setYear(thisDate);
510 picker->selectAll();
511 popup->setMainWidget(picker);
512 connect(sender: picker, signal: &KDatePickerPrivateYearSelector::closeMe, context: popup, slot: &KPopupFrame::close);
513 picker->setFocus();
514
515 if (popup->exec(p: d->selectYear->mapToGlobal(QPoint(0, d->selectMonth->height())))) {
516 // We need to create a valid date in the year/month selected so we can find out how many
517 // days are in the month.
518 QDate newDate(picker->year(), thisDate.month(), 1);
519
520 // If we have succeeded in creating a date in the new month, then try to create the new
521 // date, checking we don't set a day after the last day of the month
522 newDate = QDate(newDate.year(), newDate.month(), qMin(a: thisDate.day(), b: newDate.daysInMonth()));
523
524 // Set the date, if it's invalid in any way then alert user and don't update
525 if (!setDate(newDate)) {
526 QApplication::beep();
527 }
528 }
529 delete popup;
530
531 d->selectYear->setChecked(false);
532}
533
534void KDatePicker::uncheckYearSelector()
535{
536 d->selectYear->setChecked(false);
537 d->selectYear->update();
538}
539
540void KDatePicker::changeEvent(QEvent *event)
541{
542 if (event && event->type() == QEvent::EnabledChange) {
543 if (isEnabled()) {
544 d->table->setFocus();
545 }
546 }
547}
548
549KDateTable *KDatePicker::dateTable() const
550{
551 return d->table;
552}
553
554void KDatePicker::lineEnterPressed()
555{
556 const QDate newDate = d->val->toDate(text: d->line->text());
557
558 if (newDate.isValid()) {
559 Q_EMIT dateEntered(t1: newDate);
560 setDate(newDate);
561 d->table->setFocus();
562 } else {
563 QApplication::beep();
564 }
565}
566
567void KDatePicker::todayButtonClicked()
568{
569 setDate(QDate::currentDate());
570 d->table->setFocus();
571}
572
573QSize KDatePicker::sizeHint() const
574{
575 return QWidget::sizeHint();
576}
577
578void KDatePicker::setFontSize(int s)
579{
580 QWidget *const buttons[] = {
581 d->selectMonth,
582 d->selectYear,
583 };
584 QFont font;
585 QRect r;
586 // -----
587 d->fontsize = s;
588 for (QWidget *button : buttons) {
589 font = button->font();
590 font.setPointSize(s);
591 button->setFont(font);
592 }
593 d->table->setFontSize(s);
594
595 QFontMetrics metrics(d->selectMonth->fontMetrics());
596 QString longestMonth;
597
598 for (int i = 1;; ++i) {
599 QString str = locale().standaloneMonthName(i, format: QLocale::LongFormat);
600 if (str.isNull()) {
601 break;
602 }
603 r = metrics.boundingRect(text: str);
604
605 if (r.width() > d->maxMonthRect.width()) {
606 d->maxMonthRect.setWidth(r.width());
607 longestMonth = str;
608 }
609 if (r.height() > d->maxMonthRect.height()) {
610 d->maxMonthRect.setHeight(r.height());
611 }
612 }
613
614 QStyleOptionToolButton opt;
615 opt.initFrom(w: d->selectMonth);
616 opt.text = longestMonth;
617
618 // stolen from QToolButton
619 QSize textSize = metrics.size(flags: Qt::TextShowMnemonic, str: longestMonth);
620 textSize.setWidth(textSize.width() + metrics.horizontalAdvance(QLatin1Char(' ')) * 2);
621 int w = textSize.width();
622 int h = textSize.height();
623 opt.rect.setHeight(h); // PM_MenuButtonIndicator depends on the height
624
625 QSize metricBound = style()->sizeFromContents(ct: QStyle::CT_ToolButton, opt: &opt, contentsSize: QSize(w, h), w: d->selectMonth);
626
627 d->selectMonth->setMinimumSize(metricBound);
628}
629
630int KDatePicker::fontSize() const
631{
632 return d->fontsize;
633}
634
635void KDatePicker::setCloseButton(bool enable)
636{
637 if (enable == (d->closeButton != nullptr)) {
638 return;
639 }
640
641 if (enable) {
642 d->closeButton = new QToolButton(this);
643 d->closeButton->setAutoRaise(true);
644 const int horizontalSpacing = style()->pixelMetric(metric: QStyle::PM_LayoutHorizontalSpacing);
645 d->navigationLayout->addSpacing(size: horizontalSpacing);
646 d->navigationLayout->addWidget(d->closeButton);
647 d->closeButton->setToolTip(tr(s: "Close", c: "@action:button"));
648 d->closeButton->setIcon(QIcon::fromTheme(QStringLiteral("window-close")));
649 connect(sender: d->closeButton, signal: &QAbstractButton::clicked, context: topLevelWidget(), slot: &QWidget::close);
650 } else {
651 delete d->closeButton;
652 d->closeButton = nullptr;
653 }
654
655 updateGeometry();
656}
657
658bool KDatePicker::hasCloseButton() const
659{
660 return (d->closeButton);
661}
662
663#include "kdatepicker.moc"
664

source code of kwidgetsaddons/src/kdatepicker.cpp