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
7 SPDX-License-Identifier: LGPL-2.0-or-later
8*/
9
10#include "kdatetable_p.h"
11
12#include <QAction>
13#include <QActionEvent>
14#include <QApplication>
15#include <QDate>
16#include <QFontDatabase>
17#include <QMenu>
18#include <QPainter>
19#include <QStyle>
20#include <QStyleOptionViewItem>
21
22#include <cmath>
23
24class KDateTable::KDateTablePrivate
25{
26public:
27 KDateTablePrivate(KDateTable *qq)
28 : q(qq)
29 {
30 m_popupMenuEnabled = false;
31 m_useCustomColors = false;
32 m_hoveredPos = -1;
33 setDate(QDate::currentDate());
34 }
35
36 ~KDateTablePrivate()
37 {
38 }
39
40 void setDate(const QDate &date);
41 void nextMonth();
42 void previousMonth();
43 void beginningOfMonth();
44 void endOfMonth();
45 void beginningOfWeek();
46 void endOfWeek();
47
48 KDateTable *q;
49
50 /**
51 * The currently selected date.
52 */
53 QDate m_date;
54
55 /**
56 * The weekday number of the first day in the month [1..daysInWeek()].
57 */
58 int m_weekDayFirstOfMonth;
59
60 /**
61 * The number of days in the current month.
62 */
63 int m_numDaysThisMonth;
64
65 /**
66 * Save the size of the largest used cell content.
67 */
68 QRectF m_maxCell;
69
70 /**
71 * How many week rows we are to draw.
72 */
73 int m_numWeekRows;
74
75 /**
76 * How many day columns we are to draw, i.e. days in a week.
77 */
78 int m_numDayColumns;
79
80 /**
81 * The font size of the displayed text.
82 */
83 int fontsize;
84
85 bool m_popupMenuEnabled;
86 bool m_useCustomColors;
87
88 struct DatePaintingMode {
89 QColor fgColor;
90 QColor bgColor;
91 BackgroundMode bgMode;
92 };
93 QHash<int, DatePaintingMode> m_customPaintingModes;
94
95 int m_hoveredPos;
96};
97
98KDateTable::KDateTable(const QDate &date, QWidget *parent)
99 : QWidget(parent)
100 , d(new KDateTablePrivate(this))
101{
102 initWidget(date);
103}
104
105KDateTable::KDateTable(QWidget *parent)
106 : QWidget(parent)
107 , d(std::make_unique<KDateTablePrivate>(args: this))
108{
109 initWidget(date: QDate::currentDate());
110}
111
112KDateTable::~KDateTable()
113{
114}
115
116void KDateTable::initWidget(const QDate &date)
117{
118 d->m_numWeekRows = 7;
119
120 setFontSize(10);
121 setFocusPolicy(Qt::StrongFocus);
122 setBackgroundRole(QPalette::Base);
123 setAutoFillBackground(true);
124 initAccels();
125 setAttribute(Qt::WA_Hover, on: true);
126
127 setDate(date);
128}
129
130void KDateTable::initAccels()
131{
132 QAction *next = new QAction(this);
133 next->setObjectName(QStringLiteral("next"));
134 next->setShortcuts(QKeySequence::keyBindings(key: QKeySequence::Forward));
135 next->setShortcutContext(Qt::WidgetWithChildrenShortcut);
136 connect(sender: next, signal: &QAction::triggered, context: this, slot: [this]() {
137 d->nextMonth();
138 });
139
140 QAction *prior = new QAction(this);
141 prior->setObjectName(QStringLiteral("prior"));
142 prior->setShortcuts(QKeySequence::keyBindings(key: QKeySequence::Back));
143 prior->setShortcutContext(Qt::WidgetWithChildrenShortcut);
144 connect(sender: prior, signal: &QAction::triggered, context: this, slot: [this]() {
145 d->previousMonth();
146 });
147
148 QAction *beginMonth = new QAction(this);
149 beginMonth->setObjectName(QStringLiteral("beginMonth"));
150 beginMonth->setShortcuts(QKeySequence::keyBindings(key: QKeySequence::MoveToStartOfDocument));
151 beginMonth->setShortcutContext(Qt::WidgetWithChildrenShortcut);
152 connect(sender: beginMonth, signal: &QAction::triggered, context: this, slot: [this]() {
153 d->beginningOfMonth();
154 });
155
156 QAction *endMonth = new QAction(this);
157 endMonth->setObjectName(QStringLiteral("endMonth"));
158 endMonth->setShortcuts(QKeySequence::keyBindings(key: QKeySequence::MoveToEndOfDocument));
159 endMonth->setShortcutContext(Qt::WidgetWithChildrenShortcut);
160 connect(sender: endMonth, signal: &QAction::triggered, context: this, slot: [this]() {
161 d->endOfMonth();
162 });
163
164 QAction *beginWeek = new QAction(this);
165 beginWeek->setObjectName(QStringLiteral("beginWeek"));
166 beginWeek->setShortcuts(QKeySequence::keyBindings(key: QKeySequence::MoveToStartOfLine));
167 beginWeek->setShortcutContext(Qt::WidgetWithChildrenShortcut);
168 connect(sender: beginWeek, signal: &QAction::triggered, context: this, slot: [this]() {
169 d->beginningOfWeek();
170 });
171
172 QAction *endWeek = new QAction(this);
173 endWeek->setObjectName(QStringLiteral("endWeek"));
174 endWeek->setShortcuts(QKeySequence::keyBindings(key: QKeySequence::MoveToEndOfLine));
175 endWeek->setShortcutContext(Qt::WidgetWithChildrenShortcut);
176 connect(sender: endWeek, signal: &QAction::triggered, context: this, slot: [this]() {
177 d->endOfWeek();
178 });
179}
180
181int KDateTable::posFromDate(const QDate &date)
182{
183 int initialPosition = date.day();
184 int offset = (d->m_weekDayFirstOfMonth - locale().firstDayOfWeek() + d->m_numDayColumns) % d->m_numDayColumns;
185
186 // make sure at least one day of the previous month is visible.
187 // adjust this < 1 if more days should be forced visible:
188 if (offset < 1) {
189 offset += d->m_numDayColumns;
190 }
191
192 return initialPosition + offset;
193}
194
195QDate KDateTable::dateFromPos(int position)
196{
197 int offset = (d->m_weekDayFirstOfMonth - locale().firstDayOfWeek() + d->m_numDayColumns) % d->m_numDayColumns;
198
199 // make sure at least one day of the previous month is visible.
200 // adjust this < 1 if more days should be forced visible:
201 if (offset < 1) {
202 offset += d->m_numDayColumns;
203 }
204
205 return QDate(d->m_date.year(), d->m_date.month(), 1).addDays(days: position - offset);
206}
207
208void KDateTable::paintEvent(QPaintEvent *e)
209{
210 QPainter p(this);
211 const QRect &rectToUpdate = e->rect();
212 double cellWidth = width() / (double)d->m_numDayColumns;
213 double cellHeight = height() / (double)d->m_numWeekRows;
214 int leftCol = (int)std::floor(x: rectToUpdate.left() / cellWidth);
215 int topRow = (int)std::floor(x: rectToUpdate.top() / cellHeight);
216 int rightCol = (int)std::ceil(x: rectToUpdate.right() / cellWidth);
217 int bottomRow = (int)std::ceil(x: rectToUpdate.bottom() / cellHeight);
218 bottomRow = qMin(a: bottomRow, b: d->m_numWeekRows - 1);
219 rightCol = qMin(a: rightCol, b: d->m_numDayColumns - 1);
220 if (layoutDirection() == Qt::RightToLeft) {
221 p.translate(dx: (d->m_numDayColumns - leftCol - 1) * cellWidth, dy: topRow * cellHeight);
222 } else {
223 p.translate(dx: leftCol * cellWidth, dy: topRow * cellHeight);
224 }
225 for (int i = leftCol; i <= rightCol; ++i) {
226 for (int j = topRow; j <= bottomRow; ++j) {
227 paintCell(painter: &p, row: j, col: i);
228 p.translate(dx: 0, dy: cellHeight);
229 }
230 if (layoutDirection() == Qt::RightToLeft) {
231 p.translate(dx: -cellWidth, dy: 0);
232 } else {
233 p.translate(dx: cellWidth, dy: 0);
234 }
235 p.translate(dx: 0, dy: -cellHeight * (bottomRow - topRow + 1));
236 }
237}
238
239void KDateTable::paintCell(QPainter *painter, int row, int col)
240{
241 double w = (width() / (double)d->m_numDayColumns) - 1;
242 double h = (height() / (double)d->m_numWeekRows) - 1;
243 QRectF cell = QRectF(0, 0, w, h);
244 QString cellText;
245 QColor cellBackgroundColor;
246 QColor cellTextColor;
247 QFont cellFont = QFontDatabase::systemFont(type: QFontDatabase::GeneralFont);
248 bool workingDay = false;
249 int cellWeekDay;
250 int pos;
251
252 // Calculate the position of the cell in the grid
253 pos = d->m_numDayColumns * (row - 1) + col;
254
255 // Calculate what day of the week the cell is
256 if (col + locale().firstDayOfWeek() <= d->m_numDayColumns) {
257 cellWeekDay = col + locale().firstDayOfWeek();
258 } else {
259 cellWeekDay = col + locale().firstDayOfWeek() - d->m_numDayColumns;
260 }
261
262 // FIXME This is wrong if the widget is not using the global!
263 // See if cell day is normally a working day
264 if (locale().weekdays().first() <= locale().weekdays().last()) {
265 if (cellWeekDay >= locale().weekdays().first() && cellWeekDay <= locale().weekdays().last()) {
266 workingDay = true;
267 }
268 } else {
269 if (cellWeekDay >= locale().weekdays().first() //
270 || cellWeekDay <= locale().weekdays().last()) {
271 workingDay = true;
272 }
273 }
274
275 if (row == 0) {
276 // We are drawing a header cell
277
278 // If not a normal working day, then use "do not work today" color
279 if (workingDay) {
280 cellTextColor = palette().color(cr: QPalette::WindowText);
281 } else {
282 cellTextColor = Qt::darkRed;
283 }
284 cellBackgroundColor = palette().color(cr: QPalette::Window);
285
286 // Set the text to the short day name and bold it
287 cellFont.setBold(true);
288 cellText = locale().dayName(cellWeekDay, format: QLocale::ShortFormat);
289
290 } else {
291 // We are drawing a day cell
292
293 // Calculate the date the cell represents
294 QDate cellDate = dateFromPos(position: pos);
295
296 bool validDay = cellDate.isValid();
297
298 // Draw the day number in the cell, if the date is not valid then we don't want to show it
299 if (validDay) {
300 cellText = locale().toString(i: cellDate.day());
301 } else {
302 cellText = QString();
303 }
304
305 if (!validDay || cellDate.month() != d->m_date.month()) {
306 // we are either
307 // ° painting an invalid day
308 // ° painting a day of the previous month or
309 // ° painting a day of the following month or
310 cellBackgroundColor = palette().color(cr: backgroundRole());
311 cellTextColor = palette().color(cg: QPalette::Disabled, cr: QPalette::Text);
312 } else {
313 // Paint a day of the current month
314
315 // Background Colour priorities will be (high-to-low):
316 // * Selected Day Background Colour
317 // * Customized Day Background Colour
318 // * Normal Day Background Colour
319
320 // Background Shape priorities will be (high-to-low):
321 // * Customized Day Shape
322 // * Normal Day Shape
323
324 // Text Colour priorities will be (high-to-low):
325 // * Customized Day Colour
326 // * Day of Pray Colour (Red letter)
327 // * Selected Day Colour
328 // * Normal Day Colour
329
330 // Determine various characteristics of the cell date
331 bool selectedDay = (cellDate == date());
332 bool currentDay = (cellDate == QDate::currentDate());
333 bool dayOfPray = (cellDate.dayOfWeek() == Qt::Sunday);
334 // TODO: Uncomment if QLocale ever gets the feature...
335 // bool dayOfPray = ( cellDate.dayOfWeek() == locale().dayOfPray() );
336 bool customDay = (d->m_useCustomColors && d->m_customPaintingModes.contains(key: cellDate.toJulianDay()));
337
338 // Default values for a normal cell
339 cellBackgroundColor = palette().color(cr: backgroundRole());
340 cellTextColor = palette().color(cr: foregroundRole());
341
342 // If we are drawing the current date, then draw it bold and active
343 if (currentDay) {
344 cellFont.setBold(true);
345 cellTextColor = palette().color(cr: QPalette::LinkVisited);
346 }
347
348 // if we are drawing the day cell currently selected in the table
349 if (selectedDay) {
350 // set the background to highlighted
351 cellBackgroundColor = palette().color(cr: QPalette::Highlight);
352 cellTextColor = palette().color(cr: QPalette::HighlightedText);
353 }
354
355 // If custom colors or shape are required for this date
356 if (customDay) {
357 KDateTablePrivate::DatePaintingMode mode = d->m_customPaintingModes[cellDate.toJulianDay()];
358 if (mode.bgMode != NoBgMode) {
359 if (!selectedDay) {
360 cellBackgroundColor = mode.bgColor;
361 }
362 }
363 cellTextColor = mode.fgColor;
364 }
365
366 // If the cell day is the day of religious observance, then always color text red unless Custom overrides
367 if (!customDay && dayOfPray) {
368 cellTextColor = Qt::darkRed;
369 }
370 }
371 }
372
373 // Draw the background
374 if (row == 0) {
375 painter->setPen(cellBackgroundColor);
376 painter->setBrush(cellBackgroundColor);
377 painter->drawRect(rect: cell);
378 } else if (cellBackgroundColor != palette().color(cr: backgroundRole()) || pos == d->m_hoveredPos) {
379 QStyleOptionViewItem opt;
380 opt.initFrom(w: this);
381 opt.rect = cell.toRect();
382 if (cellBackgroundColor != palette().color(cr: backgroundRole())) {
383 opt.palette.setBrush(acr: QPalette::Highlight, abrush: cellBackgroundColor);
384 opt.state |= QStyle::State_Selected;
385 }
386 if (pos == d->m_hoveredPos && opt.state & QStyle::State_Enabled) {
387 opt.state |= QStyle::State_MouseOver;
388 } else {
389 opt.state &= ~QStyle::State_MouseOver;
390 }
391 opt.showDecorationSelected = true;
392 opt.viewItemPosition = QStyleOptionViewItem::OnlyOne;
393 style()->drawPrimitive(pe: QStyle::PE_PanelItemViewItem, opt: &opt, p: painter, w: this);
394 }
395
396 // Draw the text
397 painter->setPen(cellTextColor);
398 painter->setFont(cellFont);
399 painter->drawText(r: cell, flags: Qt::AlignCenter, text: cellText, br: &cell);
400
401 // Draw the base line
402 if (row == 0) {
403 painter->setPen(palette().color(cr: foregroundRole()));
404 painter->drawLine(p1: QPointF(0, h), p2: QPointF(w, h));
405 }
406
407 // If the day cell we just drew is bigger than the current max cell sizes,
408 // then adjust the max to the current cell
409 if (cell.width() > d->m_maxCell.width()) {
410 d->m_maxCell.setWidth(cell.width());
411 }
412 if (cell.height() > d->m_maxCell.height()) {
413 d->m_maxCell.setHeight(cell.height());
414 }
415}
416
417void KDateTable::KDateTablePrivate::nextMonth()
418{
419 // setDate does validity checking for us
420 q->setDate(m_date.addMonths(months: 1));
421}
422
423void KDateTable::KDateTablePrivate::previousMonth()
424{
425 // setDate does validity checking for us
426 q->setDate(m_date.addMonths(months: -1));
427}
428
429void KDateTable::KDateTablePrivate::beginningOfMonth()
430{
431 // setDate does validity checking for us
432 q->setDate(QDate(m_date.year(), m_date.month(), 1));
433}
434
435void KDateTable::KDateTablePrivate::endOfMonth()
436{
437 // setDate does validity checking for us
438 q->setDate(QDate(m_date.year(), m_date.month() + 1, 0));
439}
440
441// JPL Do these make the assumption that first day of week is weekday 1? As it may not be.
442void KDateTable::KDateTablePrivate::beginningOfWeek()
443{
444 // setDate does validity checking for us
445 q->setDate(m_date.addDays(days: 1 - m_date.dayOfWeek()));
446}
447
448// JPL Do these make the assumption that first day of week is weekday 1? As it may not be.
449void KDateTable::KDateTablePrivate::endOfWeek()
450{
451 // setDate does validity checking for us
452 q->setDate(m_date.addDays(days: 7 - m_date.dayOfWeek()));
453}
454
455void KDateTable::keyPressEvent(QKeyEvent *e)
456{
457 switch (e->key()) {
458 case Qt::Key_Up:
459 // setDate does validity checking for us
460 setDate(d->m_date.addDays(days: -d->m_numDayColumns));
461 break;
462 case Qt::Key_Down:
463 // setDate does validity checking for us
464 setDate(d->m_date.addDays(days: d->m_numDayColumns));
465 break;
466 case Qt::Key_Left:
467 // setDate does validity checking for us
468 setDate(d->m_date.addDays(days: -1));
469 break;
470 case Qt::Key_Right:
471 // setDate does validity checking for us
472 setDate(d->m_date.addDays(days: 1));
473 break;
474 case Qt::Key_Minus:
475 // setDate does validity checking for us
476 setDate(d->m_date.addDays(days: -1));
477 break;
478 case Qt::Key_Plus:
479 // setDate does validity checking for us
480 setDate(d->m_date.addDays(days: 1));
481 break;
482 case Qt::Key_N:
483 // setDate does validity checking for us
484 setDate(QDate::currentDate());
485 break;
486 case Qt::Key_Return:
487 case Qt::Key_Enter:
488 Q_EMIT tableClicked();
489 break;
490 case Qt::Key_Control:
491 case Qt::Key_Alt:
492 case Qt::Key_Meta:
493 case Qt::Key_Shift:
494 // Don't beep for modifiers
495 break;
496 default:
497 if (!e->modifiers()) { // hm
498 QApplication::beep();
499 }
500 }
501}
502
503void KDateTable::setFontSize(int size)
504{
505 QFontMetricsF metrics(fontMetrics());
506 QRectF rect;
507 // ----- store rectangles:
508 d->fontsize = size;
509 // ----- find largest day name:
510 d->m_maxCell.setWidth(0);
511 d->m_maxCell.setHeight(0);
512 for (int weekday = 1; weekday <= 7; ++weekday) {
513 rect = metrics.boundingRect(string: locale().dayName(weekday, format: QLocale::ShortFormat));
514 d->m_maxCell.setWidth(qMax(a: d->m_maxCell.width(), b: rect.width()));
515 d->m_maxCell.setHeight(qMax(a: d->m_maxCell.height(), b: rect.height()));
516 }
517 // ----- compare with a real wide number and add some space:
518 rect = metrics.boundingRect(QStringLiteral("88"));
519 d->m_maxCell.setWidth(qMax(a: d->m_maxCell.width() + 2, b: rect.width()));
520 d->m_maxCell.setHeight(qMax(a: d->m_maxCell.height() + 4, b: rect.height()));
521}
522
523void KDateTable::wheelEvent(QWheelEvent *e)
524{
525 setDate(d->m_date.addMonths(months: -(int)(e->angleDelta().y() / 120)));
526 e->accept();
527}
528
529bool KDateTable::event(QEvent *ev)
530{
531 switch (ev->type()) {
532 case QEvent::HoverMove: {
533 QHoverEvent *e = static_cast<QHoverEvent *>(ev);
534 const int row = e->position().y() * d->m_numWeekRows / height();
535 int col;
536 if (layoutDirection() == Qt::RightToLeft) {
537 col = d->m_numDayColumns - (e->position().x() * d->m_numDayColumns / width()) - 1;
538 } else {
539 col = e->position().x() * d->m_numDayColumns / width();
540 }
541
542 const int pos = row < 1 ? -1 : (d->m_numDayColumns * (row - 1)) + col;
543
544 if (pos != d->m_hoveredPos) {
545 d->m_hoveredPos = pos;
546 update();
547 }
548 break;
549 }
550 case QEvent::HoverLeave:
551 if (d->m_hoveredPos != -1) {
552 d->m_hoveredPos = -1;
553 update();
554 }
555 break;
556 default:
557 break;
558 }
559 return QWidget::event(event: ev);
560}
561
562void KDateTable::mousePressEvent(QMouseEvent *e)
563{
564 if (e->type() != QEvent::MouseButtonPress) { // the KDatePicker only reacts on mouse press events:
565 return;
566 }
567
568 if (!isEnabled()) {
569 QApplication::beep();
570 return;
571 }
572
573 int row;
574 int col;
575 int pos;
576
577 QPoint mouseCoord = e->pos();
578 row = mouseCoord.y() * d->m_numWeekRows / height();
579 if (layoutDirection() == Qt::RightToLeft) {
580 col = d->m_numDayColumns - (mouseCoord.x() * d->m_numDayColumns / width()) - 1;
581 } else {
582 col = mouseCoord.x() * d->m_numDayColumns / width();
583 }
584
585 if (row < 1 || col < 0) { // the user clicked on the frame of the table
586 return;
587 }
588
589 // Rows and columns are zero indexed. The (row - 1) below is to avoid counting
590 // the row with the days of the week in the calculation.
591
592 // new position and date
593 pos = (d->m_numDayColumns * (row - 1)) + col;
594 QDate clickedDate = dateFromPos(position: pos);
595
596 // set the new date. If it is in the previous or next month, the month will
597 // automatically be changed, no need to do that manually...
598 // validity checking done inside setDate
599 setDate(clickedDate);
600
601 // This could be optimized to only call update over the regions
602 // of old and new cell, but 99% of times there is also a call to
603 // setDate that already calls update() so no need to optimize that
604 // much here
605 update();
606
607 Q_EMIT tableClicked();
608
609 if (e->button() == Qt::RightButton && d->m_popupMenuEnabled) {
610 QMenu *menu = new QMenu();
611 menu->addSection(text: locale().toString(date: d->m_date));
612 Q_EMIT aboutToShowContextMenu(menu, date: clickedDate);
613 menu->popup(pos: e->globalPosition().toPoint());
614 }
615}
616
617void KDateTable::KDateTablePrivate::setDate(const QDate &date)
618{
619 m_date = date;
620 m_weekDayFirstOfMonth = QDate(date.year(), date.month(), 1).dayOfWeek();
621 m_numDaysThisMonth = m_date.daysInMonth();
622 m_numDayColumns = 7;
623}
624
625bool KDateTable::setDate(const QDate &toDate)
626{
627 if (!toDate.isValid()) {
628 return false;
629 }
630
631 if (toDate == date()) {
632 return true;
633 }
634
635 d->setDate(toDate);
636 Q_EMIT dateChanged(date: date());
637 update();
638
639 return true;
640}
641
642const QDate &KDateTable::date() const
643{
644 return d->m_date;
645}
646
647void KDateTable::focusInEvent(QFocusEvent *e)
648{
649 QWidget::focusInEvent(event: e);
650}
651
652void KDateTable::focusOutEvent(QFocusEvent *e)
653{
654 QWidget::focusOutEvent(event: e);
655}
656
657QSize KDateTable::sizeHint() const
658{
659 if (d->m_maxCell.height() > 0 && d->m_maxCell.width() > 0) {
660 return QSize(qRound(d: d->m_maxCell.width() * d->m_numDayColumns), (qRound(d: d->m_maxCell.height() + 2) * d->m_numWeekRows));
661 } else {
662 // qCDebug(KWidgetsAddonsLog) << "KDateTable::sizeHint: obscure failure - " << endl;
663 return QSize(-1, -1);
664 }
665}
666
667void KDateTable::setPopupMenuEnabled(bool enable)
668{
669 d->m_popupMenuEnabled = enable;
670}
671
672bool KDateTable::popupMenuEnabled() const
673{
674 return d->m_popupMenuEnabled;
675}
676
677void KDateTable::setCustomDatePainting(const QDate &date, const QColor &fgColor, BackgroundMode bgMode, const QColor &bgColor)
678{
679 if (!fgColor.isValid()) {
680 unsetCustomDatePainting(date);
681 return;
682 }
683
684 KDateTablePrivate::DatePaintingMode mode;
685 mode.bgMode = bgMode;
686 mode.fgColor = fgColor;
687 mode.bgColor = bgColor;
688
689 d->m_customPaintingModes.insert(key: date.toJulianDay(), value: mode);
690 d->m_useCustomColors = true;
691 update();
692}
693
694void KDateTable::unsetCustomDatePainting(const QDate &date)
695{
696 d->m_customPaintingModes.remove(key: date.toJulianDay());
697 if (d->m_customPaintingModes.isEmpty()) {
698 d->m_useCustomColors = false;
699 }
700 update();
701}
702
703#include "moc_kdatetable_p.cpp"
704

source code of kwidgetsaddons/src/kdatetable.cpp