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 | |
24 | class KDateTable::KDateTablePrivate |
25 | { |
26 | public: |
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 ; |
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 | |
98 | KDateTable::KDateTable(const QDate &date, QWidget *parent) |
99 | : QWidget(parent) |
100 | , d(new KDateTablePrivate(this)) |
101 | { |
102 | initWidget(date); |
103 | } |
104 | |
105 | KDateTable::KDateTable(QWidget *parent) |
106 | : QWidget(parent) |
107 | , d(std::make_unique<KDateTablePrivate>(args: this)) |
108 | { |
109 | initWidget(date: QDate::currentDate()); |
110 | } |
111 | |
112 | KDateTable::~KDateTable() |
113 | { |
114 | } |
115 | |
116 | void 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 | |
130 | void 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 | |
181 | int 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 | |
195 | QDate 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 | |
208 | void 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 | |
239 | void 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 | |
417 | void KDateTable::KDateTablePrivate::nextMonth() |
418 | { |
419 | // setDate does validity checking for us |
420 | q->setDate(m_date.addMonths(months: 1)); |
421 | } |
422 | |
423 | void KDateTable::KDateTablePrivate::previousMonth() |
424 | { |
425 | // setDate does validity checking for us |
426 | q->setDate(m_date.addMonths(months: -1)); |
427 | } |
428 | |
429 | void KDateTable::KDateTablePrivate::beginningOfMonth() |
430 | { |
431 | // setDate does validity checking for us |
432 | q->setDate(QDate(m_date.year(), m_date.month(), 1)); |
433 | } |
434 | |
435 | void 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. |
442 | void 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. |
449 | void KDateTable::KDateTablePrivate::endOfWeek() |
450 | { |
451 | // setDate does validity checking for us |
452 | q->setDate(m_date.addDays(days: 7 - m_date.dayOfWeek())); |
453 | } |
454 | |
455 | void 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 | |
503 | void 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 | |
523 | void KDateTable::wheelEvent(QWheelEvent *e) |
524 | { |
525 | setDate(d->m_date.addMonths(months: -(int)(e->angleDelta().y() / 120))); |
526 | e->accept(); |
527 | } |
528 | |
529 | bool 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 | |
562 | void 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 * = 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 | |
617 | void 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 | |
625 | bool 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 | |
642 | const QDate &KDateTable::date() const |
643 | { |
644 | return d->m_date; |
645 | } |
646 | |
647 | void KDateTable::focusInEvent(QFocusEvent *e) |
648 | { |
649 | QWidget::focusInEvent(event: e); |
650 | } |
651 | |
652 | void KDateTable::focusOutEvent(QFocusEvent *e) |
653 | { |
654 | QWidget::focusOutEvent(event: e); |
655 | } |
656 | |
657 | QSize 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 | |
667 | void KDateTable::(bool enable) |
668 | { |
669 | d->m_popupMenuEnabled = enable; |
670 | } |
671 | |
672 | bool KDateTable::() const |
673 | { |
674 | return d->m_popupMenuEnabled; |
675 | } |
676 | |
677 | void 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 | |
694 | void 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 | |