Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qcalendarwidget.cpp
Go to the documentation of this file.
1// Copyright (C) 2020 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
5
6#include <qabstractitemmodel.h>
7#include <qstyleditemdelegate.h>
8#include <qdatetime.h>
9#include <qtableview.h>
10#include <qlayout.h>
11#include <qevent.h>
12#include <qtextformat.h>
13#include <qheaderview.h>
14#include <private/qwidget_p.h>
15#include <qpushbutton.h>
16#include <qtoolbutton.h>
17#include <qlabel.h>
18#include <qspinbox.h>
19#include <qmenu.h>
20#include <qapplication.h>
21#include <private/qapplication_p.h>
22#include <qbasictimer.h>
23#include <qstylepainter.h>
24#include <qcalendar.h>
25
26#include <vector>
27
28QT_BEGIN_NAMESPACE
29
30using namespace Qt::StringLiterals;
31
32enum {
38};
39
40static QString formatNumber(int number, int fieldWidth)
41{
42 return QString::number(number).rightJustified(fieldWidth, u'0');
43}
44
45namespace QtPrivate {
46
48{
49public:
50
56
59 virtual Section handleKey(int key) = 0;
60 virtual QDate applyToDate(QDate date, QCalendar cal = QCalendar()) const = 0;
61 virtual void setDate(QDate date, QCalendar cal = QCalendar()) = 0;
62 virtual QString text() const = 0;
63 virtual QString text(QDate date, QCalendar cal, int repeat) const = 0;
64
65 QLocale m_locale;
66
67protected:
68 static QString highlightString(const QString &str, int pos);
69};
70
71QString QCalendarDateSectionValidator::highlightString(const QString &str, int pos)
72{
73 if (pos == 0)
74 return "<b>"_L1 + str + "</b>"_L1;
75 int startPos = str.size() - pos;
76 return QStringView{str}.mid(0, startPos) + "<b>"_L1 + QStringView{str}.mid(startPos, pos) + "</b>"_L1;
77
78}
79
81{
82
83public:
85 virtual Section handleKey(int key) override;
86 virtual QDate applyToDate(QDate date, QCalendar cal) const override;
87 virtual void setDate(QDate date, QCalendar cal) override;
88 virtual QString text() const override;
89 virtual QString text(QDate date, QCalendar cal, int repeat) const override;
90private:
91 int m_pos;
92 int m_day;
93 int m_oldDay;
94};
95
97 : QCalendarDateSectionValidator(), m_pos(0), m_day(1), m_oldDay(1)
98{
99}
100
102{
103 if (key == Qt::Key_Right || key == Qt::Key_Left) {
104 m_pos = 0;
106 } else if (key == Qt::Key_Up) {
107 m_pos = 0;
108 ++m_day;
109 if (m_day > 31)
110 m_day = 1;
112 } else if (key == Qt::Key_Down) {
113 m_pos = 0;
114 --m_day;
115 if (m_day < 1)
116 m_day = 31;
118 } else if (key == Qt::Key_Back || key == Qt::Key_Backspace) {
119 --m_pos;
120 if (m_pos < 0)
121 m_pos = 1;
122
123 if (m_pos == 0)
124 m_day = m_oldDay;
125 else
126 m_day = m_day / 10;
127 //m_day = m_oldDay / 10 * 10 + m_day / 10;
128
129 if (m_pos == 0)
132 }
133 if (key < Qt::Key_0 || key > Qt::Key_9)
135 int pressedKey = key - Qt::Key_0;
136 if (m_pos == 0)
137 m_day = pressedKey;
138 else
139 m_day = m_day % 10 * 10 + pressedKey;
140 if (m_day > 31)
141 m_day = 31;
142 ++m_pos;
143 if (m_pos > 1) {
144 m_pos = 0;
146 }
148}
149
150QDate QCalendarDayValidator::applyToDate(QDate date, QCalendar cal) const
151{
152 auto parts = cal.partsFromDate(date);
153 if (!parts.isValid())
154 return QDate();
155 parts.day = qMin(qMax(1, m_day), cal.daysInMonth(parts.month, parts.year));
156 return cal.dateFromParts(parts);
157}
158
159void QCalendarDayValidator::setDate(QDate date, QCalendar cal)
160{
161 m_day = m_oldDay = date.day(cal);
162 m_pos = 0;
163}
164
166{
167 return highlightString(formatNumber(m_day, 2), m_pos);
168}
169
170QString QCalendarDayValidator::text(QDate date, QCalendar cal, int repeat) const
171{
172 if (repeat <= 1) {
173 return QString::number(date.day(cal));
174 } else if (repeat == 2) {
175 return formatNumber(date.day(cal), 2);
176 } else if (repeat == 3) {
177 return m_locale.dayName(date.dayOfWeek(cal), QLocale::ShortFormat);
178 } else /* repeat >= 4 */ {
179 return m_locale.dayName(date.dayOfWeek(cal), QLocale::LongFormat);
180 }
181}
182
183//////////////////////////////////
184
186{
187
188public:
190 virtual Section handleKey(int key) override;
191 virtual QDate applyToDate(QDate date, QCalendar cal) const override;
192 virtual void setDate(QDate date, QCalendar cal) override;
193 virtual QString text() const override;
194 virtual QString text(QDate date, QCalendar cal, int repeat) const override;
195private:
196 int m_pos;
197 int m_month;
198 int m_oldMonth;
199};
200
202 : QCalendarDateSectionValidator(), m_pos(0), m_month(1), m_oldMonth(1)
203{
204}
205
207{
208 if (key == Qt::Key_Right || key == Qt::Key_Left) {
209 m_pos = 0;
211 } else if (key == Qt::Key_Up) {
212 m_pos = 0;
213 ++m_month;
214 if (m_month > 12)
215 m_month = 1;
217 } else if (key == Qt::Key_Down) {
218 m_pos = 0;
219 --m_month;
220 if (m_month < 1)
221 m_month = 12;
223 } else if (key == Qt::Key_Back || key == Qt::Key_Backspace) {
224 --m_pos;
225 if (m_pos < 0)
226 m_pos = 1;
227
228 if (m_pos == 0)
229 m_month = m_oldMonth;
230 else
231 m_month = m_month / 10;
232 //m_month = m_oldMonth / 10 * 10 + m_month / 10;
233
234 if (m_pos == 0)
237 }
238 if (key < Qt::Key_0 || key > Qt::Key_9)
240 int pressedKey = key - Qt::Key_0;
241 if (m_pos == 0)
242 m_month = pressedKey;
243 else
244 m_month = m_month % 10 * 10 + pressedKey;
245 if (m_month > 12)
246 m_month = 12;
247 ++m_pos;
248 if (m_pos > 1) {
249 m_pos = 0;
251 }
253}
254
255QDate QCalendarMonthValidator::applyToDate(QDate date, QCalendar cal) const
256{
257 auto parts = cal.partsFromDate(date);
258 if (!parts.isValid())
259 return QDate();
260 parts.month = qMin(qMax(1, m_month), cal.monthsInYear(parts.year));
261 parts.day = qMin(parts.day, cal.daysInMonth(m_month, parts.year)); // m_month or parts.month ?
262 return cal.dateFromParts(parts);
263}
264
265void QCalendarMonthValidator::setDate(QDate date, QCalendar cal)
266{
267 m_month = m_oldMonth = date.month(cal);
268 m_pos = 0;
269}
270
272{
273 return highlightString(formatNumber(m_month, 2), m_pos);
274}
275
276QString QCalendarMonthValidator::text(QDate date, QCalendar cal, int repeat) const
277{
278 const auto parts = cal.partsFromDate(date);
279 // Numeric forms:
280 if (repeat <= 1)
281 return QString::number(parts.month);
282 if (repeat == 2)
283 return formatNumber(parts.month, 2);
284 // Text forms:
285 if (repeat == 3)
286 return cal.standaloneMonthName(m_locale, parts.month, parts.year, QLocale::ShortFormat);
287 /* repeat >= 4 */
288 return cal.standaloneMonthName(m_locale, parts.month, parts.year, QLocale::LongFormat);
289}
290
291//////////////////////////////////
292
294{
295
296public:
298 virtual Section handleKey(int key) override;
299 virtual QDate applyToDate(QDate date, QCalendar cal) const override;
300 virtual void setDate(QDate date, QCalendar cal) override;
301 virtual QString text() const override;
302 virtual QString text(QDate date, QCalendar cal, int repeat) const override;
303private:
304 int pow10(int n);
305 int m_pos;
306 int m_year;
307 int m_oldYear;
308};
309
311 : QCalendarDateSectionValidator(), m_pos(0), m_year(2000), m_oldYear(2000)
312{
313 // TODO: What to use (for non-Gregorian calendars) as default year?
314 // Maybe 1360 for Jalali, 1420 for Islamic, etc.
315}
316
317int QCalendarYearValidator::pow10(int n)
318{
319 int power = 1;
320 for (int i = 0; i < n; i++)
321 power *= 10;
322 return power;
323}
324
326{
327 if (key == Qt::Key_Right || key == Qt::Key_Left) {
328 m_pos = 0;
330 } else if (key == Qt::Key_Up) {
331 m_pos = 0;
332 ++m_year;
334 } else if (key == Qt::Key_Down) {
335 m_pos = 0;
336 --m_year;
338 } else if (key == Qt::Key_Back || key == Qt::Key_Backspace) {
339 --m_pos;
340 if (m_pos < 0)
341 m_pos = 3;
342
343 int pow = pow10(m_pos);
344 m_year = m_oldYear / pow * pow + m_year % (pow * 10) / 10;
345
346 if (m_pos == 0)
349 }
350 if (key < Qt::Key_0 || key > Qt::Key_9)
352 int pressedKey = key - Qt::Key_0;
353 int pow = pow10(m_pos);
354 m_year = m_year / (pow * 10) * (pow * 10) + m_year % pow * 10 + pressedKey;
355 ++m_pos;
356 if (m_pos > 3) {
357 m_pos = 0;
359 }
361}
362
363QDate QCalendarYearValidator::applyToDate(QDate date, QCalendar cal) const
364{
365 auto parts = cal.partsFromDate(date);
366 if (!parts.isValid())
367 return QDate();
368 // This widget does not support negative years (some calendars may support)
369 parts.year = qMax(1, m_year);
370 parts.day = qMin(parts.day, cal.daysInMonth(parts.month, parts.year));
371 return cal.dateFromParts(parts);
372}
373
374void QCalendarYearValidator::setDate(QDate date, QCalendar cal)
375{
376 m_year = m_oldYear = date.year(cal);
377 m_pos = 0;
378}
379
381{
382 return highlightString(formatNumber(m_year, 4), m_pos);
383}
384
385QString QCalendarYearValidator::text(QDate date, QCalendar cal, int repeat) const
386{
387 if (repeat < 4)
388 return formatNumber(date.year(cal) % 100, 2);
389 return QString::number(date.year(cal));
390}
391
392///////////////////////////////////
393
401} // namespace QtPrivate
402
404
405namespace QtPrivate {
406
408{
409public:
412
413 void handleKeyEvent(QKeyEvent *keyEvent, QCalendar cal);
414 QString currentText(QCalendar cal) const;
415 QDate currentDate() const { return m_currentDate; }
416 void setFormat(const QString &format);
417 void setInitialDate(QDate date, QCalendar cal);
418
419 void setLocale(const QLocale &locale);
420
421private:
422 void toNextToken();
423 void toPreviousToken();
424 void applyToDate(QCalendar cal);
425
426 int countRepeat(const QString &str, int index) const;
427 void clear();
428
429 QStringList m_separators;
430 std::vector<SectionToken> m_tokens;
431 QCalendarYearValidator m_yearValidator;
432 QCalendarMonthValidator m_monthValidator;
433 QCalendarDayValidator m_dayValidator;
434
435 int m_currentToken;
436
437 QDate m_initialDate;
438 QDate m_currentDate;
439
440 QCalendarDateSectionValidator::Section m_lastSectionMove;
441};
442
450
451void QCalendarDateValidator::setLocale(const QLocale &locale)
452{
453 m_yearValidator.m_locale = locale;
454 m_monthValidator.m_locale = locale;
455 m_dayValidator.m_locale = locale;
456}
457
462
463// from qdatetime.cpp
464int QCalendarDateValidator::countRepeat(const QString &str, int index) const
465{
466 Q_ASSERT(index >= 0 && index < str.size());
467 int count = 1;
468 const QChar ch = str.at(index);
469 while (index + count < str.size() && str.at(index + count) == ch)
470 ++count;
471 return count;
472}
473
474void QCalendarDateValidator::setInitialDate(QDate date, QCalendar cal)
475{
476 m_yearValidator.setDate(date, cal);
477 m_monthValidator.setDate(date, cal);
478 m_dayValidator.setDate(date, cal);
479 m_initialDate = date;
480 m_currentDate = date;
482}
483
484QString QCalendarDateValidator::currentText(QCalendar cal) const
485{
486 QString str;
487 const int numSeps = m_separators.size();
488 const int numTokens = int(m_tokens.size());
489 for (int i = 0; i < numSeps; ++i) {
490 str += m_separators.at(i);
491 if (i < numTokens) {
492 const SectionToken &token = m_tokens[i];
493 if (i == m_currentToken)
494 str += token.validator->text();
495 else
496 str += token.validator->text(m_currentDate, cal, token.repeat);
497 }
498 }
499 return str;
500}
501
502void QCalendarDateValidator::clear()
503{
504 m_tokens.clear();
505 m_separators.clear();
506
507 m_currentToken = -1;
508}
509
510void QCalendarDateValidator::setFormat(const QString &format)
511{
512 clear();
513
514 int pos = 0;
515 const auto quote = u'\'';
516 bool quoting = false;
517 QString separator;
518 while (pos < format.size()) {
519 const QStringView mid = QStringView{format}.mid(pos);
520 int offset = 1;
521
522 if (mid.startsWith(quote)) {
523 quoting = !quoting;
524 } else {
525 const QChar nextChar = format.at(pos);
526 if (quoting) {
527 separator += nextChar;
528 quoting = false;
529 } else {
530 QCalendarDateSectionValidator *validator = nullptr;
531 if (nextChar == u'd') {
532 offset = qMin(4, countRepeat(format, pos));
533 validator = &m_dayValidator;
534 } else if (nextChar == u'M') {
535 offset = qMin(4, countRepeat(format, pos));
536 validator = &m_monthValidator;
537 } else if (nextChar == u'y') {
538 offset = qMin(4, countRepeat(format, pos));
539 validator = &m_yearValidator;
540 } else {
541 separator += nextChar;
542 }
543 if (validator) {
544 m_tokens.push_back(SectionToken(validator, offset));
545 m_separators.append(separator);
546 separator = QString();
547 if (m_currentToken < 0)
548 m_currentToken = int(m_tokens.size()) - 1;
549
550 }
551 }
552 }
553 pos += offset;
554 }
555 m_separators += separator;
556}
557
558void QCalendarDateValidator::applyToDate(QCalendar cal)
559{
560 m_currentDate = m_yearValidator.applyToDate(m_currentDate, cal);
561 m_currentDate = m_monthValidator.applyToDate(m_currentDate, cal);
562 m_currentDate = m_dayValidator.applyToDate(m_currentDate, cal);
563}
564
565void QCalendarDateValidator::toNextToken()
566{
567 if (m_currentToken < 0)
568 return;
569 ++m_currentToken;
570 m_currentToken %= m_tokens.size();
571}
572
573void QCalendarDateValidator::toPreviousToken()
574{
575 if (m_currentToken < 0)
576 return;
577 --m_currentToken;
578 m_currentToken %= m_tokens.size();
579}
580
604
605//////////////////////////////////
606
608{
610public:
612 : QObject(parent), m_dateText(nullptr), m_dateFrame(nullptr), m_dateValidator(nullptr),
613 m_widget(nullptr), m_editDelay(1500), m_date(QDate::currentDate()) {}
614
615 QWidget *widget() const;
616 void setWidget(QWidget *widget);
617
619 void setDateEditAcceptDelay(int delay);
620
621 void setDate(QDate date);
622
623 bool eventFilter(QObject *o, QEvent *e) override;
624 void timerEvent(QTimerEvent *e) override;
625
626signals:
629
630private:
631 void applyDate();
632 void updateDateLabel();
633 void createDateLabel();
634 void removeDateLabel();
635
636 QLabel *m_dateText;
637 QFrame *m_dateFrame;
638 QBasicTimer m_acceptTimer;
639 QCalendarDateValidator *m_dateValidator;
640 QWidget *m_widget;
641 int m_editDelay;
642
643 QDate m_date;
644 const QCalendar m_calendar;
645};
646
647QWidget *QCalendarTextNavigator::widget() const
648{
649 return m_widget;
650}
651
652void QCalendarTextNavigator::setWidget(QWidget *widget)
653{
654 m_widget = widget;
655}
656
658{
659 m_date = date;
660}
661
662void QCalendarTextNavigator::updateDateLabel()
663{
664 if (!m_widget)
665 return;
666
667 m_acceptTimer.start(m_editDelay, this);
668
669 m_dateText->setText(m_dateValidator->currentText(m_calendar));
670
671 QSize s = m_dateFrame->sizeHint();
672 QRect r = m_widget->geometry(); // later, just the table section
673 QRect newRect((r.width() - s.width()) / 2, (r.height() - s.height()) / 2, s.width(), s.height());
674 m_dateFrame->setGeometry(newRect);
675 // need to set palette after geometry update as phonestyle sets transparency
676 // effect in move event.
677 QPalette p = m_dateFrame->palette();
678 p.setBrush(QPalette::Window, m_dateFrame->window()->palette().brush(QPalette::Window));
679 m_dateFrame->setPalette(p);
680
681 m_dateFrame->raise();
682 m_dateFrame->show();
683}
684
685void QCalendarTextNavigator::applyDate()
686{
687 QDate date = m_dateValidator->currentDate();
688 if (m_date == date)
689 return;
690
691 m_date = date;
692 emit dateChanged(date);
693}
694
695void QCalendarTextNavigator::createDateLabel()
696{
697 if (m_dateFrame)
698 return;
699 m_dateFrame = new QFrame(m_widget);
700 QVBoxLayout *vl = new QVBoxLayout;
701 m_dateText = new QLabel;
702 vl->addWidget(m_dateText);
703 m_dateFrame->setLayout(vl);
704 m_dateFrame->setFrameShadow(QFrame::Plain);
705 m_dateFrame->setFrameShape(QFrame::Box);
706 m_dateValidator = new QCalendarDateValidator();
707 m_dateValidator->setLocale(m_widget->locale());
708 m_dateValidator->setFormat(m_widget->locale().dateFormat(QLocale::ShortFormat));
709 m_dateValidator->setInitialDate(m_date, m_calendar);
710
711 m_dateFrame->setAutoFillBackground(true);
712 m_dateFrame->setBackgroundRole(QPalette::Window);
713}
714
715void QCalendarTextNavigator::removeDateLabel()
716{
717 if (!m_dateFrame)
718 return;
719 m_acceptTimer.stop();
720 m_dateFrame->hide();
721 m_dateFrame->deleteLater();
722 delete m_dateValidator;
723 m_dateFrame = nullptr;
724 m_dateText = nullptr;
725 m_dateValidator = nullptr;
726}
727
728bool QCalendarTextNavigator::eventFilter(QObject *o, QEvent *e)
729{
730 if (m_widget) {
731 if (e->type() == QEvent::KeyPress || e->type() == QEvent::KeyRelease) {
732 QKeyEvent *ke = static_cast<QKeyEvent *>(e);
733 if ((ke->text().size() > 0 && ke->text().at(0).isPrint()) || m_dateFrame) {
734 if (ke->key() == Qt::Key_Return || ke->key() == Qt::Key_Enter || ke->key() == Qt::Key_Select) {
735 applyDate();
736 emit editingFinished();
737 removeDateLabel();
738#if QT_CONFIG(shortcut)
739 } else if (ke->matches(QKeySequence::Cancel)) {
740 removeDateLabel();
741#endif
742 } else if (e->type() == QEvent::KeyPress) {
743 createDateLabel();
744 m_dateValidator->handleKeyEvent(ke, m_calendar);
745 updateDateLabel();
746 }
747 ke->accept();
748 return true;
749 }
750 // If we are navigating let the user finish his date in old locate.
751 // If we change our mind and want it to update immediately simply uncomment below
752 /*
753 } else if (e->type() == QEvent::LocaleChange) {
754 if (m_dateValidator) {
755 m_dateValidator->setLocale(m_widget->locale());
756 m_dateValidator->setFormat(m_widget->locale().dateFormat(QLocale::ShortFormat));
757 updateDateLabel();
758 }
759 */
760 }
761 }
762 return QObject::eventFilter(o,e);
763}
764
766{
767 if (e->timerId() == m_acceptTimer.timerId()) {
768 applyDate();
769 removeDateLabel();
770 }
771}
772
774{
775 return m_editDelay;
776}
777
779{
780 m_editDelay = delay;
781}
782
783class QCalendarView;
784
785// a small helper class that replaces a QMap<Qt::DayOfWeek, T>,
786// but requires T to have a member-swap and a default constructor
787// which should be cheap (no memory allocations)
788
789QT_WARNING_PUSH
790QT_WARNING_DISABLE_MSVC(4351) // "new behavior: elements of array ... will be default initialized"
791
792template <typename T>
794 bool contained[7];
795 T data[7];
796
797 static constexpr int day2idx(Qt::DayOfWeek day) noexcept { return int(day) - 1; } // alt: day % 7
798public:
799 constexpr StaticDayOfWeekAssociativeArray() noexcept(noexcept(T()))
800 : contained{}, data{} // arrays require uniform initialization
801 {}
802
803 constexpr bool contains(Qt::DayOfWeek day) const noexcept { return contained[day2idx(day)]; }
804 constexpr const T &value(Qt::DayOfWeek day) const noexcept { return data[day2idx(day)]; }
805
806 constexpr T &operator[](Qt::DayOfWeek day) noexcept
807 {
808 const int idx = day2idx(day);
809 contained[idx] = true;
810 return data[idx];
811 }
812
813 constexpr void insert(Qt::DayOfWeek day, T v) noexcept
814 {
815 operator[](day).swap(v);
816 }
817};
818
820
822{
824public:
826
827 int rowCount(const QModelIndex &parent) const override
828 {
829 if (parent.isValid())
830 return 0;
831 return RowCount + m_firstRow;
832 }
833
834 int columnCount(const QModelIndex &parent) const override
835 {
836 if (parent.isValid())
837 return 0;
838 return ColumnCount + m_firstColumn;
839 }
840
841 QVariant data(const QModelIndex &index, int role) const override;
842 Qt::ItemFlags flags(const QModelIndex &index) const override;
843
844 void showMonth(int year, int month);
845 void setDate(QDate d);
846
847 void setCalendar(QCalendar c);
849
850 void setMinimumDate(QDate date);
851 void setMaximumDate(QDate date);
852
853 void setRange(QDate min, QDate max);
854
855 void setHorizontalHeaderFormat(QCalendarWidget::HorizontalHeaderFormat format);
856
857 void setFirstColumnDay(Qt::DayOfWeek dayOfWeek);
859
860 bool weekNumbersShown() const;
861 void setWeekNumbersShown(bool show);
862
863 QTextCharFormat formatForCell(int row, int col) const;
864 Qt::DayOfWeek dayOfWeekForColumn(int section) const;
865 int columnForDayOfWeek(Qt::DayOfWeek day) const;
866 QDate dateForCell(int row, int column) const;
867 void cellForDate(QDate date, int *row, int *column) const;
868 QString dayName(Qt::DayOfWeek day) const;
869
871 { m_view = view; }
872
874 QDate referenceDate() const;
875 int columnForFirstOfMonth(QDate date) const;
876
877 QString monthName(const QLocale &locale, int month)
878 {
879 return m_calendar.standaloneMonthName(locale, month, m_shownYear, QLocale::LongFormat);
880 }
881
885 QDate m_date;
894 QMap<QDate, QTextCharFormat> m_dateFormats;
895 QTextCharFormat m_headerFormat;
897};
898
900{
902public:
904
905 void internalUpdate() { updateGeometries(); }
906 void setReadOnly(bool enable);
907 virtual void keyboardSearch(const QString &) override {}
908
909signals:
911 void changeDate(QDate date, bool changeMonth);
912 void clicked(QDate date);
914protected:
915 QModelIndex moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers) override;
916 void mouseDoubleClickEvent(QMouseEvent *event) override;
917 void mousePressEvent(QMouseEvent *event) override;
918 void mouseMoveEvent(QMouseEvent *event) override;
919 void mouseReleaseEvent(QMouseEvent *event) override;
920#if QT_CONFIG(wheelevent)
922#endif
923 void keyPressEvent(QKeyEvent *event) override;
924 bool event(QEvent *event) override;
925
926 QDate handleMouseEvent(QMouseEvent *event);
927public:
929private:
930 bool validDateClicked;
931#ifdef QT_KEYPAD_NAVIGATION
933#endif
934};
935
936QCalendarModel::QCalendarModel(QObject *parent)
937 : QAbstractTableModel(parent),
938 m_firstColumn(1),
939 m_firstRow(1),
940 m_date(QDate::currentDate()),
941 m_minimumDate(QDate::fromJulianDay(1)),
942 m_maximumDate(9999, 12, 31),
943 m_shownYear(m_date.year(m_calendar)),
944 m_shownMonth(m_date.month(m_calendar)),
945 m_firstDay(QLocale().firstDayOfWeek()),
946 m_horizontalHeaderFormat(QCalendarWidget::ShortDayNames),
947 m_weekNumbersShown(true),
948 m_view(nullptr)
949{
950}
951
953{
954 int col = column - m_firstColumn;
955 if (col < 0 || col > 6)
956 return Qt::Sunday;
957 int day = m_firstDay + col;
958 if (day > 7)
959 day -= 7;
960 return Qt::DayOfWeek(day);
961}
962
963int QCalendarModel::columnForDayOfWeek(Qt::DayOfWeek day) const
964{
965 if (day < 1 || unsigned(day) > unsigned(7))
966 return -1;
967 int column = (int)day - (int)m_firstDay;
968 if (column < 0)
969 column += 7;
970 return column + m_firstColumn;
971}
972
973/*
974This simple algorithm tries to generate a valid date from the month shown.
975Some months don't contain a first day (e.g. Jan of -4713 year,
976so QDate (-4713, 1, 1) would be invalid). In that case we try to generate
977another valid date for that month. Later, returned date's day is the number of cells
978calendar widget will reserve for days before referenceDate. (E.g. if returned date's
979day is 16, that day will be placed in 3rd or 4th row, not in the 1st or 2nd row).
980Depending on referenceData we can change behaviour of Oct 1582. If referenceDate is 1st
981of Oct we render 1 Oct in 1st or 2nd row. If referenceDate is 17 of Oct we show always 16
982dates before 17 of Oct, and since this month contains the hole 5-14 Oct, the first of Oct
983will be rendered in 2nd or 3rd row, showing more dates from previous month.
984*/
986{
987 // TODO: Check this
988 int refDay = 1;
989 while (refDay <= 31) {
990 QDate refDate(m_shownYear, m_shownMonth, refDay, m_calendar);
991 if (refDate.isValid())
992 return refDate;
993 refDay += 1;
994 }
995 return QDate();
996}
997
999{
1000 return (columnForDayOfWeek(static_cast<Qt::DayOfWeek>(m_calendar.dayOfWeek(date)))
1001 - (date.day(m_calendar) % 7) + 8) % 7;
1002}
1003
1004QDate QCalendarModel::dateForCell(int row, int column) const
1005{
1006 if (row < m_firstRow || row > m_firstRow + RowCount - 1 ||
1007 column < m_firstColumn || column > m_firstColumn + ColumnCount - 1)
1008 return QDate();
1009 const QDate refDate = referenceDate();
1010 if (!refDate.isValid())
1011 return QDate();
1012
1013 const int columnForFirstOfShownMonth = columnForFirstOfMonth(refDate);
1014 if (columnForFirstOfShownMonth - m_firstColumn < MinimumDayOffset)
1015 row -= 1;
1016
1017 const int requestedDay =
1018 7 * (row - m_firstRow) + column - columnForFirstOfShownMonth - refDate.day(m_calendar) + 1;
1019 return refDate.addDays(requestedDay);
1020}
1021
1022void QCalendarModel::cellForDate(QDate date, int *row, int *column) const
1023{
1024 if (!row && !column)
1025 return;
1026
1027 if (row)
1028 *row = -1;
1029 if (column)
1030 *column = -1;
1031
1032 const QDate refDate = referenceDate();
1033 if (!refDate.isValid())
1034 return;
1035
1036 const int columnForFirstOfShownMonth = columnForFirstOfMonth(refDate);
1037 const int requestedPosition = (refDate.daysTo(date) - m_firstColumn +
1038 columnForFirstOfShownMonth + refDate.day(m_calendar) - 1);
1039
1040 int c = requestedPosition % 7;
1041 int r = requestedPosition / 7;
1042 if (c < 0) {
1043 c += 7;
1044 r -= 1;
1045 }
1046
1047 if (columnForFirstOfShownMonth - m_firstColumn < MinimumDayOffset)
1048 r += 1;
1049
1050 if (r < 0 || r > RowCount - 1 || c < 0 || c > ColumnCount - 1)
1051 return;
1052
1053 if (row)
1054 *row = r + m_firstRow;
1055 if (column)
1056 *column = c + m_firstColumn;
1057}
1058
1059QString QCalendarModel::dayName(Qt::DayOfWeek day) const
1060{
1061 switch (m_horizontalHeaderFormat) {
1062 case QCalendarWidget::SingleLetterDayNames: {
1063 QString standaloneDayName = m_view->locale().standaloneDayName(day, QLocale::NarrowFormat);
1064 if (standaloneDayName == m_view->locale().dayName(day, QLocale::NarrowFormat))
1065 return standaloneDayName.left(1);
1066 return standaloneDayName;
1067 }
1068 case QCalendarWidget::ShortDayNames:
1069 return m_view->locale().dayName(day, QLocale::ShortFormat);
1070 case QCalendarWidget::LongDayNames:
1071 return m_view->locale().dayName(day, QLocale::LongFormat);
1072 default:
1073 break;
1074 }
1075 return QString();
1076}
1077
1078QTextCharFormat QCalendarModel::formatForCell(int row, int col) const
1079{
1080 QPalette pal;
1081 QPalette::ColorGroup cg = QPalette::Active;
1082 QTextCharFormat format;
1083
1084 if (m_view) {
1085 pal = m_view->palette();
1086 if (!m_view->isEnabled())
1087 cg = QPalette::Disabled;
1088 else if (!m_view->isActiveWindow())
1089 cg = QPalette::Inactive;
1090 format.setFont(m_view->font());
1091 }
1092
1093 bool header = (m_weekNumbersShown && col == HeaderColumn)
1094 || (m_horizontalHeaderFormat != QCalendarWidget::NoHorizontalHeader && row == HeaderRow);
1095 format.setBackground(pal.brush(cg, header ? QPalette::AlternateBase : QPalette::Base));
1096 format.setForeground(pal.brush(cg, QPalette::Text));
1097 if (header) {
1098 format.merge(m_headerFormat);
1099 }
1100
1101 if (col >= m_firstColumn && col < m_firstColumn + ColumnCount) {
1102 Qt::DayOfWeek dayOfWeek = dayOfWeekForColumn(col);
1103 if (m_dayFormats.contains(dayOfWeek))
1104 format.merge(m_dayFormats.value(dayOfWeek));
1105 }
1106
1107 if (!header) {
1108 QDate date = dateForCell(row, col);
1109 format.merge(m_dateFormats.value(date));
1110 if (date < m_minimumDate || date > m_maximumDate)
1111 format.setBackground(pal.brush(cg, QPalette::Window));
1112 if (m_shownMonth != date.month(m_calendar))
1113 format.setForeground(pal.brush(QPalette::Disabled, QPalette::Text));
1114 }
1115 return format;
1116}
1117
1118QVariant QCalendarModel::data(const QModelIndex &index, int role) const
1119{
1120 if (role == Qt::TextAlignmentRole)
1121 return (int) Qt::AlignCenter;
1122
1123 int row = index.row();
1124 int column = index.column();
1125
1126 if (role == Qt::DisplayRole) {
1127 if (m_weekNumbersShown && column == HeaderColumn
1128 && row >= m_firstRow && row < m_firstRow + RowCount) {
1129 QDate date = dateForCell(row, columnForDayOfWeek(Qt::Monday));
1130 if (date.isValid())
1131 return date.weekNumber();
1132 }
1133 if (m_horizontalHeaderFormat != QCalendarWidget::NoHorizontalHeader && row == HeaderRow
1134 && column >= m_firstColumn && column < m_firstColumn + ColumnCount)
1135 return dayName(dayOfWeekForColumn(column));
1136 QDate date = dateForCell(row, column);
1137 if (date.isValid())
1138 return date.day(m_calendar);
1139 return QString();
1140 }
1141
1142 QTextCharFormat fmt = formatForCell(row, column);
1143 if (role == Qt::BackgroundRole)
1144 return fmt.background().color();
1145 if (role == Qt::ForegroundRole)
1146 return fmt.foreground().color();
1147 if (role == Qt::FontRole)
1148 return fmt.font();
1149 if (role == Qt::ToolTipRole)
1150 return fmt.toolTip();
1151 return QVariant();
1152}
1153
1155{
1156 QDate date = dateForCell(index.row(), index.column());
1157 if (!date.isValid())
1158 return QAbstractTableModel::flags(index);
1159 if (date < m_minimumDate)
1160 return { };
1161 if (date > m_maximumDate)
1162 return { };
1163 return QAbstractTableModel::flags(index);
1164}
1165
1167{
1168 m_date = d;
1169 if (m_date < m_minimumDate)
1170 m_date = m_minimumDate;
1171 else if (m_date > m_maximumDate)
1172 m_date = m_maximumDate;
1173}
1174
1175void QCalendarModel::setCalendar(QCalendar c)
1176{
1177 m_calendar = c;
1178 m_shownYear = m_date.year(c);
1179 m_shownMonth = m_date.month(c);
1182}
1183
1185{
1186 return m_calendar;
1187}
1188
1189void QCalendarModel::showMonth(int year, int month)
1190{
1191 if (m_shownYear == year && m_shownMonth == month)
1192 return;
1193
1194 m_shownYear = year;
1195 m_shownMonth = month;
1196
1198}
1199
1201{
1202 if (!d.isValid() || d == m_minimumDate)
1203 return;
1204
1205 m_minimumDate = d;
1206 if (m_maximumDate < m_minimumDate)
1207 m_maximumDate = m_minimumDate;
1208 if (m_date < m_minimumDate)
1209 m_date = m_minimumDate;
1211}
1212
1214{
1215 if (!d.isValid() || d == m_maximumDate)
1216 return;
1217
1218 m_maximumDate = d;
1219 if (m_minimumDate > m_maximumDate)
1220 m_minimumDate = m_maximumDate;
1221 if (m_date > m_maximumDate)
1222 m_date = m_maximumDate;
1224}
1225
1226void QCalendarModel::setRange(QDate min, QDate max)
1227{
1228 m_minimumDate = min;
1229 m_maximumDate = max;
1230 if (m_minimumDate > m_maximumDate)
1231 qSwap(m_minimumDate, m_maximumDate);
1232 if (m_date < m_minimumDate)
1233 m_date = m_minimumDate;
1234 if (m_date > m_maximumDate)
1235 m_date = m_maximumDate;
1237}
1238
1240{
1241 QModelIndex begin = index(0, 0);
1242 QModelIndex end = index(m_firstRow + RowCount - 1, m_firstColumn + ColumnCount - 1);
1243 emit dataChanged(begin, end);
1244 emit headerDataChanged(Qt::Vertical, 0, m_firstRow + RowCount - 1);
1245 emit headerDataChanged(Qt::Horizontal, 0, m_firstColumn + ColumnCount - 1);
1246}
1247
1248void QCalendarModel::setHorizontalHeaderFormat(QCalendarWidget::HorizontalHeaderFormat format)
1249{
1250 if (m_horizontalHeaderFormat == format)
1251 return;
1252
1253 int oldFormat = m_horizontalHeaderFormat;
1254 m_horizontalHeaderFormat = format;
1255 if (oldFormat == QCalendarWidget::NoHorizontalHeader) {
1256 beginInsertRows(QModelIndex(), 0, 0);
1257 m_firstRow = 1;
1258 endInsertRows();
1259 } else if (m_horizontalHeaderFormat == QCalendarWidget::NoHorizontalHeader) {
1260 beginRemoveRows(QModelIndex(), 0, 0);
1261 m_firstRow = 0;
1262 endRemoveRows();
1263 }
1265}
1266
1267void QCalendarModel::setFirstColumnDay(Qt::DayOfWeek dayOfWeek)
1268{
1269 if (m_firstDay == dayOfWeek)
1270 return;
1271
1272 m_firstDay = dayOfWeek;
1274}
1275
1277{
1278 return m_firstDay;
1279}
1280
1282{
1283 return m_weekNumbersShown;
1284}
1285
1287{
1288 if (m_weekNumbersShown == show)
1289 return;
1290
1291 m_weekNumbersShown = show;
1292 if (show) {
1293 beginInsertColumns(QModelIndex(), 0, 0);
1294 m_firstColumn = 1;
1295 endInsertColumns();
1296 } else {
1297 beginRemoveColumns(QModelIndex(), 0, 0);
1298 m_firstColumn = 0;
1299 endRemoveColumns();
1300 }
1302}
1303
1304QCalendarView::QCalendarView(QWidget *parent)
1305 : QTableView(parent),
1306 readOnly(false),
1307 validDateClicked(false)
1308{
1309 setTabKeyNavigation(false);
1310 setShowGrid(false);
1311 verticalHeader()->setVisible(false);
1312 horizontalHeader()->setVisible(false);
1313 setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1314 setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1315}
1316
1317QModelIndex QCalendarView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers)
1318{
1319 QCalendarModel *calendarModel = qobject_cast<QCalendarModel *>(model());
1320 if (!calendarModel)
1321 return QTableView::moveCursor(cursorAction, modifiers);
1322
1323 QCalendar cal = calendarModel->calendar();
1324
1325 if (readOnly)
1326 return currentIndex();
1327
1328 QModelIndex index = currentIndex();
1329 QDate currentDate = static_cast<QCalendarModel*>(model())->dateForCell(index.row(), index.column());
1330 switch (cursorAction) {
1331 case QAbstractItemView::MoveUp:
1332 currentDate = currentDate.addDays(-7);
1333 break;
1334 case QAbstractItemView::MoveDown:
1335 currentDate = currentDate.addDays(7);
1336 break;
1337 case QAbstractItemView::MoveLeft:
1338 currentDate = currentDate.addDays(isRightToLeft() ? 1 : -1);
1339 break;
1340 case QAbstractItemView::MoveRight:
1341 currentDate = currentDate.addDays(isRightToLeft() ? -1 : 1);
1342 break;
1343 case QAbstractItemView::MoveHome: {
1344 auto parts = cal.partsFromDate(currentDate);
1345 if (parts.isValid()) {
1346 parts.day = 1;
1347 currentDate = cal.dateFromParts(parts);
1348 }
1349 }
1350 break;
1351 case QAbstractItemView::MoveEnd: {
1352 auto parts = cal.partsFromDate(currentDate);
1353 if (parts.isValid()) {
1354 parts.day = cal.daysInMonth(parts.month, parts.year);
1355 currentDate = cal.dateFromParts(parts);
1356 }
1357 }
1358 break;
1359 case QAbstractItemView::MovePageUp:
1360 currentDate = currentDate.addMonths(-1, cal);
1361 break;
1362 case QAbstractItemView::MovePageDown:
1363 currentDate = currentDate.addMonths(1, cal);
1364 break;
1365 case QAbstractItemView::MoveNext:
1366 case QAbstractItemView::MovePrevious:
1367 return currentIndex();
1368 default:
1369 break;
1370 }
1371 emit changeDate(currentDate, true);
1372 return currentIndex();
1373}
1374
1375void QCalendarView::keyPressEvent(QKeyEvent *event)
1376{
1377#ifdef QT_KEYPAD_NAVIGATION
1378 if (event->key() == Qt::Key_Select) {
1380 if (!hasEditFocus()) {
1381 setEditFocus(true);
1382 return;
1383 }
1384 }
1385 } else if (event->key() == Qt::Key_Back) {
1387 if (qobject_cast<QCalendarModel *>(model())) {
1388 emit changeDate(origDate, true); //changes selection back to origDate, but doesn't activate
1389 setEditFocus(false);
1390 return;
1391 }
1392 }
1393 }
1394#endif
1395
1396 if (!readOnly) {
1397 switch (event->key()) {
1398 case Qt::Key_Return:
1399 case Qt::Key_Enter:
1400 case Qt::Key_Select:
1402 return;
1403 default:
1404 break;
1405 }
1406 }
1408}
1409
1410#if QT_CONFIG(wheelevent)
1412{
1413 const int numDegrees = event->angleDelta().y() / 8;
1414 const int numSteps = numDegrees / 15;
1415 const QModelIndex index = currentIndex();
1420}
1421#endif
1422
1423bool QCalendarView::event(QEvent *event)
1424{
1425#ifdef QT_KEYPAD_NAVIGATION
1426 if (event->type() == QEvent::FocusIn) {
1427 if (QCalendarModel *calendarModel = qobject_cast<QCalendarModel *>(model())) {
1428 origDate = calendarModel->m_date;
1429 }
1430 }
1431#endif
1432
1433 return QTableView::event(event);
1434}
1435
1436QDate QCalendarView::handleMouseEvent(QMouseEvent *event)
1437{
1438 QCalendarModel *calendarModel = qobject_cast<QCalendarModel *>(model());
1439 if (!calendarModel)
1440 return QDate();
1441
1442 QPoint pos = event->position().toPoint();
1443 QModelIndex index = indexAt(pos);
1444 QDate date = calendarModel->dateForCell(index.row(), index.column());
1445 if (date.isValid() && date >= calendarModel->m_minimumDate
1446 && date <= calendarModel->m_maximumDate) {
1447 return date;
1448 }
1449 return QDate();
1450}
1451
1452void QCalendarView::mouseDoubleClickEvent(QMouseEvent *event)
1453{
1454 QCalendarModel *calendarModel = qobject_cast<QCalendarModel *>(model());
1455 if (!calendarModel) {
1456 QTableView::mouseDoubleClickEvent(event);
1457 return;
1458 }
1459
1460 if (readOnly)
1461 return;
1462
1463 QDate date = handleMouseEvent(event);
1464 validDateClicked = false;
1465 if (date == calendarModel->m_date &&
1466 !style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, nullptr, this)) {
1467 emit editingFinished();
1468 }
1469}
1470
1471void QCalendarView::mousePressEvent(QMouseEvent *event)
1472{
1473 QCalendarModel *calendarModel = qobject_cast<QCalendarModel *>(model());
1474 if (!calendarModel) {
1475 QTableView::mousePressEvent(event);
1476 return;
1477 }
1478
1479 if (readOnly)
1480 return;
1481
1482 if (event->button() != Qt::LeftButton)
1483 return;
1484
1485 QDate date = handleMouseEvent(event);
1486 if (date.isValid()) {
1487 validDateClicked = true;
1488 int row = -1, col = -1;
1489 static_cast<QCalendarModel *>(model())->cellForDate(date, &row, &col);
1490 if (row != -1 && col != -1) {
1491 selectionModel()->setCurrentIndex(model()->index(row, col), QItemSelectionModel::NoUpdate);
1492 }
1493 } else {
1494 validDateClicked = false;
1495 event->ignore();
1496 }
1497}
1498
1499void QCalendarView::mouseMoveEvent(QMouseEvent *event)
1500{
1501 QCalendarModel *calendarModel = qobject_cast<QCalendarModel *>(model());
1502 if (!calendarModel) {
1503 QTableView::mouseMoveEvent(event);
1504 return;
1505 }
1506
1507 if (readOnly)
1508 return;
1509
1510 if (validDateClicked) {
1511 QDate date = handleMouseEvent(event);
1512 if (date.isValid()) {
1513 int row = -1, col = -1;
1514 static_cast<QCalendarModel *>(model())->cellForDate(date, &row, &col);
1515 if (row != -1 && col != -1) {
1516 selectionModel()->setCurrentIndex(model()->index(row, col), QItemSelectionModel::NoUpdate);
1517 }
1518 }
1519 } else {
1520 event->ignore();
1521 }
1522}
1523
1524void QCalendarView::mouseReleaseEvent(QMouseEvent *event)
1525{
1526 QCalendarModel *calendarModel = qobject_cast<QCalendarModel *>(model());
1527 if (!calendarModel) {
1528 QTableView::mouseReleaseEvent(event);
1529 return;
1530 }
1531
1532 if (event->button() != Qt::LeftButton)
1533 return;
1534
1535 if (readOnly)
1536 return;
1537
1538 if (validDateClicked) {
1539 QDate date = handleMouseEvent(event);
1540 if (date.isValid()) {
1541 emit changeDate(date, true);
1542 emit clicked(date);
1543 if (style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, nullptr, this))
1544 emit editingFinished();
1545 }
1546 validDateClicked = false;
1547 } else {
1548 event->ignore();
1549 }
1550}
1551
1553{
1554 Q_OBJECT
1555public:
1560 const QModelIndex &index) const override;
1561 void paintCell(QPainter *painter, const QRect &rect, QDate date) const;
1562
1563private:
1564 QCalendarWidgetPrivate *calendarWidgetPrivate;
1565 mutable QStyleOptionViewItem storedOption;
1566};
1567
1568//Private tool button class
1570{
1571public:
1572 QCalToolButton(QWidget * parent)
1574 { }
1575protected:
1576 void paintEvent(QPaintEvent *e) override
1577 {
1578 Q_UNUSED(e);
1579
1580 QStyleOptionToolButton opt;
1581 initStyleOption(&opt);
1582
1583 if (opt.state & QStyle::State_MouseOver || isDown()) {
1584 //act as normal button
1585 setPalette(QPalette());
1586 } else {
1587 //set the highlight color for button text
1588 QPalette toolPalette = palette();
1589 toolPalette.setColor(QPalette::ButtonText, toolPalette.color(QPalette::HighlightedText));
1590 setPalette(toolPalette);
1591 }
1592
1593 QToolButton::paintEvent(e);
1594 }
1595};
1596
1611
1612} // namespace QtPrivate
1613
1614using QCalendarDateSectionValidator = QtPrivate::QCalendarDateSectionValidator;
1615using QCalendarDayValidator = QtPrivate::QCalendarDayValidator;
1616using QCalendarMonthValidator = QtPrivate::QCalendarMonthValidator;
1617using QCalendarYearValidator = QtPrivate::QCalendarYearValidator;
1618using QCalendarDateValidator = QtPrivate::QCalendarDateValidator;
1619using QPrevNextCalButton = QtPrivate::QPrevNextCalButton;
1620using QCalendarDelegate = QtPrivate::QCalendarDelegate;
1621using QCalToolButton = QtPrivate::QCalToolButton;
1622using QCalendarDelegate = QtPrivate::QCalendarDelegate;
1623using QCalendarModel = QtPrivate::QCalendarModel;
1624using QCalendarView = QtPrivate::QCalendarView;
1625using QCalendarTextNavigator = QtPrivate::QCalendarTextNavigator;
1626
1628{
1629 Q_DECLARE_PUBLIC(QCalendarWidget)
1630public:
1632
1633 void showMonth(int year, int month);
1634 void update();
1635 void paintCell(QPainter *painter, const QRect &rect, QDate date) const;
1636
1637 void _q_slotShowDate(QDate date);
1638 void _q_slotChangeDate(QDate date);
1639 void _q_slotChangeDate(QDate date, bool changeMonth);
1641 void _q_monthChanged(QAction*);
1646
1647 void createNavigationBar(QWidget *widget);
1652 void updateCurrentPage(QDate newDate);
1653 inline QDate getCurrentDate();
1654 void setNavigatorEnabled(bool enable);
1655
1656 QCalendarModel *m_model;
1657 QCalendarView *m_view;
1658 QCalendarDelegate *m_delegate;
1660 QCalendarTextNavigator *m_navigator;
1662
1665 QCalToolButton *monthButton;
1667 QMap<int, QAction *> monthToAction;
1668 QCalToolButton *yearButton;
1672
1674 mutable QSize cachedSizeHint;
1676};
1677
1678void QCalendarDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
1679 const QModelIndex &index) const
1680{
1681 QDate date = calendarWidgetPrivate->m_model->dateForCell(index.row(), index.column());
1682 if (date.isValid()) {
1683 storedOption = option;
1684 QRect rect = option.rect;
1685 calendarWidgetPrivate->paintCell(painter, rect, date);
1686 } else {
1687 QStyledItemDelegate::paint(painter, option, index);
1688 }
1689}
1690
1691void QCalendarDelegate::paintCell(QPainter *painter, const QRect &rect, QDate date) const
1692{
1693 storedOption.rect = rect;
1694 int row = -1;
1695 int col = -1;
1696 calendarWidgetPrivate->m_model->cellForDate(date, &row, &col);
1697 QModelIndex idx = calendarWidgetPrivate->m_model->index(row, col);
1698 QStyledItemDelegate::paint(painter, storedOption, idx);
1699}
1700
1701QCalendarWidgetPrivate::QCalendarWidgetPrivate()
1702 : QWidgetPrivate()
1703{
1704 m_model = nullptr;
1705 m_view = nullptr;
1706 m_delegate = nullptr;
1707 m_selection = nullptr;
1708 m_navigator = nullptr;
1709 m_dateEditEnabled = false;
1710 navBarVisible = true;
1711 oldFocusPolicy = Qt::StrongFocus;
1712}
1713
1715{
1716 Q_Q(QCalendarWidget);
1717
1718 bool navigatorEnabled = (m_navigator->widget() != nullptr);
1719 if (enable == navigatorEnabled)
1720 return;
1721
1722 if (enable) {
1723 m_navigator->setWidget(q);
1724 q->connect(m_navigator, SIGNAL(dateChanged(QDate)),
1725 q, SLOT(_q_slotChangeDate(QDate)));
1726 q->connect(m_navigator, SIGNAL(editingFinished()),
1727 q, SLOT(_q_editingFinished()));
1728 m_view->installEventFilter(m_navigator);
1729 } else {
1730 m_navigator->setWidget(nullptr);
1731 q->disconnect(m_navigator, SIGNAL(dateChanged(QDate)),
1732 q, SLOT(_q_slotChangeDate(QDate)));
1733 q->disconnect(m_navigator, SIGNAL(editingFinished()),
1734 q, SLOT(_q_editingFinished()));
1735 m_view->removeEventFilter(m_navigator);
1736 }
1737}
1738
1740{
1741 Q_Q(QCalendarWidget);
1742 navBarBackground = new QWidget(widget);
1743 navBarBackground->setObjectName("qt_calendar_navigationbar"_L1);
1744 navBarBackground->setAutoFillBackground(true);
1745 navBarBackground->setBackgroundRole(QPalette::Highlight);
1746
1747 prevMonth = new QPrevNextCalButton(navBarBackground);
1748 nextMonth = new QPrevNextCalButton(navBarBackground);
1749 prevMonth->setAutoRaise(true);
1750 nextMonth->setAutoRaise(true);
1751 prevMonth->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum);
1752 nextMonth->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum);
1753 nextMonth->setAutoRaise(true);
1755 prevMonth->setAutoRepeat(true);
1756 nextMonth->setAutoRepeat(true);
1757
1758 monthButton = new QCalToolButton(navBarBackground);
1759 monthButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum);
1760 monthButton->setAutoRaise(true);
1761 monthButton->setPopupMode(QToolButton::InstantPopup);
1762 monthMenu = new QMenu(monthButton);
1763 for (int i = 1, e = m_model->m_calendar.maximumMonthsInYear(); i <= e; i++) {
1764 QString monthName(m_model->monthName(q->locale(), i));
1765 QAction *act = monthMenu->addAction(monthName);
1766 act->setData(i);
1767 monthToAction[i] = act;
1768 }
1769 monthButton->setMenu(monthMenu);
1770 yearButton = new QCalToolButton(navBarBackground);
1771 yearButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum);
1772 yearButton->setAutoRaise(true);
1773 yearEdit = new QSpinBox(navBarBackground);
1774
1775 QFont font = q->font();
1776 font.setBold(true);
1777 monthButton->setFont(font);
1778 yearButton->setFont(font);
1779 yearEdit->setFrame(false);
1780 yearEdit->setMinimum(m_model->m_minimumDate.year(m_model->m_calendar));
1781 yearEdit->setMaximum(m_model->m_maximumDate.year(m_model->m_calendar));
1782 yearEdit->hide();
1783 spaceHolder = new QSpacerItem(0,0);
1784
1785 QHBoxLayout *headerLayout = new QHBoxLayout;
1786 headerLayout->setContentsMargins(QMargins());
1787 headerLayout->setSpacing(0);
1788 headerLayout->addWidget(prevMonth);
1789 headerLayout->insertStretch(headerLayout->count());
1790 headerLayout->addWidget(monthButton);
1791 headerLayout->addItem(spaceHolder);
1792 headerLayout->addWidget(yearButton);
1793 headerLayout->insertStretch(headerLayout->count());
1794 headerLayout->addWidget(nextMonth);
1795 navBarBackground->setLayout(headerLayout);
1796
1797 yearEdit->setFocusPolicy(Qt::StrongFocus);
1798 prevMonth->setFocusPolicy(Qt::NoFocus);
1799 nextMonth->setFocusPolicy(Qt::NoFocus);
1800 yearButton->setFocusPolicy(Qt::NoFocus);
1801 monthButton->setFocusPolicy(Qt::NoFocus);
1802
1803 //set names for the header controls.
1804 prevMonth->setObjectName("qt_calendar_prevmonth"_L1);
1805 nextMonth->setObjectName("qt_calendar_nextmonth"_L1);
1806 monthButton->setObjectName("qt_calendar_monthbutton"_L1);
1807 yearButton->setObjectName("qt_calendar_yearbutton"_L1);
1808 yearEdit->setObjectName("qt_calendar_yearedit"_L1);
1809
1811 showMonth(m_model->m_date.year(m_model->m_calendar), m_model->m_date.month(m_model->m_calendar));
1812}
1813
1815{
1816 Q_Q(QCalendarWidget);
1817 prevMonth->setIcon(q->style()->standardIcon(q->isRightToLeft() ? QStyle::SP_ArrowRight : QStyle::SP_ArrowLeft, nullptr, q));
1818 nextMonth->setIcon(q->style()->standardIcon(q->isRightToLeft() ? QStyle::SP_ArrowLeft : QStyle::SP_ArrowRight, nullptr, q));
1819}
1820
1822{
1823 int maxMonths = m_model->m_calendar.monthsInYear(m_model->m_shownYear);
1824 int beg = 1, end = maxMonths;
1825 bool prevEnabled = true;
1826 bool nextEnabled = true;
1827 QCalendar cal = m_model->calendar();
1828 if (m_model->m_shownYear == m_model->m_minimumDate.year(cal)) {
1829 beg = m_model->m_minimumDate.month(cal);
1830 if (m_model->m_shownMonth == m_model->m_minimumDate.month(cal))
1831 prevEnabled = false;
1832 }
1833 if (m_model->m_shownYear == m_model->m_maximumDate.year(cal)) {
1834 end = m_model->m_maximumDate.month(cal);
1835 if (m_model->m_shownMonth == m_model->m_maximumDate.month(cal))
1836 nextEnabled = false;
1837 }
1838 prevMonth->setEnabled(prevEnabled);
1839 nextMonth->setEnabled(nextEnabled);
1840 for (int i = 1; i <= maxMonths; i++) {
1841 bool monthEnabled = true;
1842 if (i < beg || i > end)
1843 monthEnabled = false;
1844 monthToAction[i]->setEnabled(monthEnabled);
1845 }
1846}
1847
1849{
1850 Q_Q(QCalendarWidget);
1851
1852 for (int i = 1; i <= 12; i++) {
1853 QString monthName(m_model->monthName(q->locale(), i));
1854 monthToAction[i]->setText(monthName);
1855 }
1856}
1857
1859{
1860 Q_Q(QCalendarWidget);
1861 QCalendar cal = m_model->calendar();
1862
1863 QDate newDate = date;
1864 QDate minDate = q->minimumDate();
1865 QDate maxDate = q->maximumDate();
1866 if (minDate.isValid()&& minDate.daysTo(newDate) < 0)
1867 newDate = minDate;
1868 if (maxDate.isValid()&& maxDate.daysTo(newDate) > 0)
1869 newDate = maxDate;
1870 showMonth(newDate.year(cal), newDate.month(cal));
1871 int row = -1, col = -1;
1872 m_model->cellForDate(newDate, &row, &col);
1873 if (row != -1 && col != -1)
1874 {
1875 m_view->selectionModel()->setCurrentIndex(m_model->index(row, col),
1876 QItemSelectionModel::NoUpdate);
1877 }
1878}
1879
1881{
1882 monthButton->setText(act->text());
1883 QDate currentDate = getCurrentDate();
1884 QDate newDate = currentDate.addMonths(act->data().toInt() - currentDate.month(m_model->m_calendar), m_model->m_calendar);
1885 updateCurrentPage(newDate);
1886}
1887
1889{
1890 QModelIndex index = m_view->currentIndex();
1891 return m_model->dateForCell(index.row(), index.column());
1892}
1893
1895{
1896 QDate currentDate = getCurrentDate().addMonths(-1, m_model->m_calendar);
1897 updateCurrentPage(currentDate);
1898}
1899
1901{
1902 QDate currentDate = getCurrentDate().addMonths(1, m_model->m_calendar);
1903 updateCurrentPage(currentDate);
1904}
1905
1907{
1908 Q_Q(QCalendarWidget);
1909 yearEdit->hide();
1910 q->setFocusPolicy(oldFocusPolicy);
1911 qApp->removeEventFilter(q);
1912 spaceHolder->changeSize(0, 0);
1913 yearButton->show();
1914 QDate currentDate = getCurrentDate();
1915 int newYear = q->locale().toInt(yearEdit->text());
1916 currentDate = currentDate.addYears(newYear - currentDate.year(m_model->m_calendar), m_model->m_calendar);
1917 yearButton->setText(q->locale().toString(currentDate, u"yyyy", m_model->m_calendar));
1918 updateCurrentPage(currentDate);
1919}
1920
1922{
1923 Q_Q(QCalendarWidget);
1924 //show the spinbox on top of the button
1925 yearEdit->setGeometry(yearButton->x(), yearButton->y(),
1926 yearEdit->sizeHint().width(), yearButton->height());
1927 spaceHolder->changeSize(yearButton->width(), 0);
1928 yearButton->hide();
1929 oldFocusPolicy = q->focusPolicy();
1930 q->setFocusPolicy(Qt::NoFocus);
1931 yearEdit->show();
1932 qApp->installEventFilter(q);
1933 yearEdit->raise();
1934 yearEdit->selectAll();
1935 yearEdit->setFocus(Qt::MouseFocusReason);
1936}
1937
1938void QCalendarWidgetPrivate::showMonth(int year, int month)
1939{
1940 if (m_model->m_shownYear == year && m_model->m_shownMonth == month)
1941 return;
1942 Q_Q(QCalendarWidget);
1943 m_model->showMonth(year, month);
1945 emit q->currentPageChanged(year, month);
1947 cachedSizeHint = QSize();
1948 update();
1950}
1951
1953{
1954 Q_Q(QCalendarWidget);
1955
1956 QString monthName = m_model->monthName(q->locale(), m_model->m_shownMonth);
1957
1958 monthButton->setText(monthName);
1959 yearEdit->setValue(m_model->m_shownYear);
1960 yearButton->setText(yearEdit->text());
1961}
1962
1964{
1965 QDate currentDate = m_model->m_date;
1966 int row, column;
1967 m_model->cellForDate(currentDate, &row, &column);
1968 QModelIndex idx;
1969 m_selection->clear();
1970 if (row != -1 && column != -1) {
1971 idx = m_model->index(row, column);
1972 m_selection->setCurrentIndex(idx, QItemSelectionModel::SelectCurrent);
1973 }
1974}
1975
1976void QCalendarWidgetPrivate::paintCell(QPainter *painter, const QRect &rect, QDate date) const
1977{
1978 Q_Q(const QCalendarWidget);
1979 q->paintCell(painter, rect, date);
1980}
1981
1983{
1985}
1986
1988{
1989 _q_slotChangeDate(date, true);
1990}
1991
1992void QCalendarWidgetPrivate::_q_slotChangeDate(QDate date, bool changeMonth)
1993{
1994 QDate oldDate = m_model->m_date;
1996 QDate newDate = m_model->m_date;
1997 if (changeMonth)
1998 showMonth(newDate.year(m_model->m_calendar), newDate.month(m_model->m_calendar));
1999 if (oldDate != newDate) {
2000 update();
2001 Q_Q(QCalendarWidget);
2003 emit q->selectionChanged();
2004 }
2005}
2006
2008{
2009 Q_Q(QCalendarWidget);
2010 emit q->activated(m_model->m_date);
2011}
2012
2013/*!
2014 \class QCalendarWidget
2015 \brief The QCalendarWidget class provides a monthly based
2016 calendar widget allowing the user to select a date.
2017 \since 4.2
2018
2019 \ingroup advanced
2020 \inmodule QtWidgets
2021
2022 \image fusion-calendarwidget.png
2023
2024 The widget is initialized with the current month and year, but
2025 QCalendarWidget provides several public slots to change the year
2026 and month that is shown.
2027
2028 By default, today's date is selected, and the user can select a
2029 date using both mouse and keyboard. The currently selected date
2030 can be retrieved using the selectedDate() function. It is
2031 possible to constrain the user selection to a given date range by
2032 setting the minimumDate and maximumDate properties.
2033 Alternatively, both properties can be set in one go using the
2034 setDateRange() convenience slot. Set the \l selectionMode
2035 property to NoSelection to prohibit the user from selecting at
2036 all. Note that a date also can be selected programmatically using
2037 the setSelectedDate() slot.
2038
2039 The currently displayed month and year can be retrieved using the
2040 monthShown() and yearShown() functions, respectively.
2041
2042 A newly created calendar widget uses abbreviated day names, and
2043 both Saturdays and Sundays are marked in red. The calendar grid is
2044 not visible. The week numbers are displayed, and the first column
2045 day is the first day of the week for the calendar's locale.
2046
2047 The notation of the days can be altered to a single letter
2048 abbreviations ("M" for "Monday") by setting the
2049 horizontalHeaderFormat property to
2050 QCalendarWidget::SingleLetterDayNames. Setting the same property
2051 to QCalendarWidget::LongDayNames makes the header display the
2052 complete day names. The week numbers can be removed by setting
2053 the verticalHeaderFormat property to
2054 QCalendarWidget::NoVerticalHeader. The calendar grid can be
2055 turned on by setting the gridVisible property to true using the
2056 setGridVisible() function:
2057
2058 \table
2059 \row \li
2060 \image qcalendarwidget-grid.png
2061 \row \li
2062 \snippet code/src_gui_widgets_qcalendarwidget.cpp 0
2063 \endtable
2064
2065 Finally, the day in the first column can be altered using the
2066 setFirstDayOfWeek() function.
2067
2068 The QCalendarWidget class also provides three signals,
2069 selectionChanged(), activated() and currentPageChanged() making it
2070 possible to respond to user interaction.
2071
2072 The rendering of the headers, weekdays or single days can be
2073 largely customized by setting QTextCharFormat's for some special
2074 weekday, a special date or for the rendering of the headers.
2075
2076 Only a subset of the properties in QTextCharFormat are used by the
2077 calendar widget. Currently, the foreground, background and font
2078 properties are used to determine the rendering of individual cells
2079 in the widget.
2080
2081 \sa QDate, QDateEdit, QTextCharFormat
2082*/
2083
2084/*!
2085 \enum QCalendarWidget::SelectionMode
2086
2087 This enum describes the types of selection offered to the user for
2088 selecting dates in the calendar.
2089
2090 \value NoSelection Dates cannot be selected.
2091 \value SingleSelection Single dates can be selected.
2092
2093 \sa selectionMode
2094*/
2095
2096/*!
2097 Constructs a calendar widget with the given \a parent.
2098
2099 The widget is initialized with the current month and year, and the
2100 currently selected date is today.
2101
2102 \sa setCurrentPage()
2103*/
2104QCalendarWidget::QCalendarWidget(QWidget *parent)
2105 : QWidget(*new QCalendarWidgetPrivate, parent, { })
2106{
2107 Q_D(QCalendarWidget);
2108
2109 setAutoFillBackground(true);
2110 setBackgroundRole(QPalette::Window);
2111
2112 QVBoxLayout *layoutV = new QVBoxLayout(this);
2113 layoutV->setContentsMargins(QMargins());
2114 d->m_model = new QCalendarModel(this);
2115 QTextCharFormat fmt;
2116 fmt.setForeground(QBrush(Qt::red));
2117 d->m_model->m_dayFormats.insert(Qt::Saturday, fmt);
2118 d->m_model->m_dayFormats.insert(Qt::Sunday, fmt);
2119 d->m_view = new QCalendarView(this);
2120 d->m_view->setObjectName("qt_calendar_calendarview"_L1);
2121 d->m_view->setModel(d->m_model);
2122 d->m_model->setView(d->m_view);
2123 d->m_view->setSelectionBehavior(QAbstractItemView::SelectItems);
2124 d->m_view->setSelectionMode(QAbstractItemView::SingleSelection);
2125 d->m_view->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
2126 d->m_view->horizontalHeader()->setSectionsClickable(false);
2127 d->m_view->verticalHeader()->setSectionResizeMode(QHeaderView::Stretch);
2128 d->m_view->verticalHeader()->setSectionsClickable(false);
2129 d->m_selection = d->m_view->selectionModel();
2130 d->createNavigationBar(this);
2131 d->m_view->setFrameStyle(QFrame::NoFrame);
2132 d->m_delegate = new QCalendarDelegate(d, this);
2133 d->m_view->setItemDelegate(d->m_delegate);
2134 d->update();
2135 d->updateNavigationBar();
2136 setFocusPolicy(Qt::StrongFocus);
2137 setFocusProxy(d->m_view);
2138 setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
2139
2140 connect(d->m_view, SIGNAL(showDate(QDate)),
2141 this, SLOT(_q_slotShowDate(QDate)));
2142 connect(d->m_view, SIGNAL(changeDate(QDate,bool)),
2143 this, SLOT(_q_slotChangeDate(QDate,bool)));
2144 connect(d->m_view, SIGNAL(clicked(QDate)),
2145 this, SIGNAL(clicked(QDate)));
2146 connect(d->m_view, SIGNAL(editingFinished()),
2147 this, SLOT(_q_editingFinished()));
2148
2149 connect(d->prevMonth, SIGNAL(clicked(bool)),
2150 this, SLOT(_q_prevMonthClicked()));
2151 connect(d->nextMonth, SIGNAL(clicked(bool)),
2152 this, SLOT(_q_nextMonthClicked()));
2153 connect(d->yearButton, SIGNAL(clicked(bool)),
2154 this, SLOT(_q_yearClicked()));
2155 connect(d->monthMenu, SIGNAL(triggered(QAction*)),
2156 this, SLOT(_q_monthChanged(QAction*)));
2157 connect(d->yearEdit, SIGNAL(editingFinished()),
2158 this, SLOT(_q_yearEditingFinished()));
2159
2160 layoutV->setContentsMargins(QMargins());
2161 layoutV->setSpacing(0);
2162 layoutV->addWidget(d->navBarBackground);
2163 layoutV->addWidget(d->m_view);
2164
2165 d->m_navigator = new QCalendarTextNavigator(this);
2166 setDateEditEnabled(true);
2167}
2168
2169/*!
2170 Destroys the calendar widget.
2171*/
2172QCalendarWidget::~QCalendarWidget()
2173{
2174}
2175
2176/*!
2177 \reimp
2178*/
2179QSize QCalendarWidget::sizeHint() const
2180{
2181 return minimumSizeHint();
2182}
2183
2184/*!
2185 \reimp
2186*/
2187QSize QCalendarWidget::minimumSizeHint() const
2188{
2189 Q_D(const QCalendarWidget);
2190 if (d->cachedSizeHint.isValid())
2191 return d->cachedSizeHint;
2192
2193 ensurePolished();
2194
2195 int w = 0;
2196 int h = 0;
2197
2198 int end = 53;
2199 int rows = 7;
2200 int cols = 8;
2201
2202 QStyleOption option;
2203 option.initFrom(this);
2204 const int marginH = (style()->pixelMetric(QStyle::PM_FocusFrameHMargin, &option, this) + 1) * 2;
2205
2206 if (horizontalHeaderFormat() == QCalendarWidget::NoHorizontalHeader) {
2207 rows = 6;
2208 } else {
2209 for (int i = 1; i <= 7; i++) {
2210 QFontMetrics fm(d->m_model->formatForCell(0, i).font());
2211 w = qMax(w, fm.horizontalAdvance(d->m_model->dayName(d->m_model->dayOfWeekForColumn(i))) + marginH);
2212 h = qMax(h, fm.height());
2213 }
2214 }
2215
2216 if (verticalHeaderFormat() == QCalendarWidget::NoVerticalHeader) {
2217 cols = 7;
2218 } else {
2219 for (int i = 1; i <= 6; i++) {
2220 QFontMetrics fm(d->m_model->formatForCell(i, 0).font());
2221 for (int j = 1; j < end; j++)
2222 w = qMax(w, fm.horizontalAdvance(QString::number(j)) + marginH);
2223 h = qMax(h, fm.height());
2224 }
2225 }
2226
2227 QFontMetrics fm(d->m_model->formatForCell(1, 1).font());
2228 for (int i = 1; i <= end; i++) {
2229 w = qMax(w, fm.horizontalAdvance(QString::number(i)) + marginH);
2230 h = qMax(h, fm.height());
2231 }
2232
2233 if (d->m_view->showGrid()) {
2234 // hardcoded in tableview
2235 w += 1;
2236 h += 1;
2237 }
2238
2239 w += 1; // default column span
2240
2241 h = qMax(h, d->m_view->verticalHeader()->minimumSectionSize());
2242 w = qMax(w, d->m_view->horizontalHeader()->minimumSectionSize());
2243
2244 //add the size of the header.
2245 QSize headerSize(0, 0);
2246 if (d->navBarVisible) {
2247 int headerH = d->navBarBackground->sizeHint().height();
2248 int headerW = 0;
2249
2250 headerW += d->prevMonth->sizeHint().width();
2251 headerW += d->nextMonth->sizeHint().width();
2252
2253 QFontMetrics fm = d->monthButton->fontMetrics();
2254 int monthW = 0;
2255 for (int i = 1; i < 12; i++) {
2256 QString monthName = d->m_model->monthName(locale(), i);
2257 monthW = qMax(monthW, fm.boundingRect(monthName).width());
2258 }
2259 const int buttonDecoMargin = d->monthButton->sizeHint().width() - fm.boundingRect(d->monthButton->text()).width();
2260 headerW += monthW + buttonDecoMargin;
2261
2262 fm = d->yearButton->fontMetrics();
2263 headerW += fm.boundingRect("5555"_L1).width() + buttonDecoMargin;
2264
2265 headerSize = QSize(headerW, headerH);
2266 }
2267 w *= cols;
2268 w = qMax(headerSize.width(), w);
2269 h = (h * rows) + headerSize.height();
2270 QMargins cm = contentsMargins();
2271 w += cm.left() + cm.right();
2272 h += cm.top() + cm.bottom();
2273 d->cachedSizeHint = QSize(w, h);
2274 return d->cachedSizeHint;
2275}
2276
2277/*!
2278 Paints the cell specified by the given \a date, using the given \a painter and \a rect.
2279*/
2280
2281void QCalendarWidget::paintCell(QPainter *painter, const QRect &rect, QDate date) const
2282{
2283 Q_D(const QCalendarWidget);
2284 d->m_delegate->paintCell(painter, rect, date);
2285}
2286
2287/*!
2288 \property QCalendarWidget::selectedDate
2289 \brief the currently selected date.
2290
2291 The selected date must be within the date range specified by the
2292 minimumDate and maximumDate properties. By default, the selected
2293 date is the current date.
2294
2295 \sa setDateRange()
2296*/
2297
2298QDate QCalendarWidget::selectedDate() const
2299{
2300 Q_D(const QCalendarWidget);
2301 return d->m_model->m_date;
2302}
2303
2304void QCalendarWidget::setSelectedDate(QDate date)
2305{
2306 Q_D(QCalendarWidget);
2307 if (d->m_model->m_date == date && date == d->getCurrentDate())
2308 return;
2309
2310 if (!date.isValid())
2311 return;
2312
2313 d->m_model->setDate(date);
2314 d->update();
2315 QDate newDate = d->m_model->m_date;
2316 QCalendar cal = d->m_model->m_calendar;
2317 d->showMonth(newDate.year(cal), newDate.month(cal));
2318 emit selectionChanged();
2319}
2320
2321/*!
2322 Returns the year of the currently displayed month. Months are
2323 numbered from 1 to 12.
2324
2325 \sa monthShown(), setCurrentPage()
2326*/
2327
2328int QCalendarWidget::yearShown() const
2329{
2330 Q_D(const QCalendarWidget);
2331 return d->m_model->m_shownYear;
2332}
2333
2334/*!
2335 Returns the currently displayed month. Months are numbered from 1 to
2336 12.
2337
2338 \sa yearShown(), setCurrentPage()
2339*/
2340
2341int QCalendarWidget::monthShown() const
2342{
2343 Q_D(const QCalendarWidget);
2344 return d->m_model->m_shownMonth;
2345}
2346
2347/*!
2348 Displays the given \a month of the given \a year without changing
2349 the selected date. Use the setSelectedDate() function to alter the
2350 selected date.
2351
2352 The currently displayed month and year can be retrieved using the
2353 monthShown() and yearShown() functions respectively.
2354
2355 \sa yearShown(), monthShown(), showPreviousMonth(), showNextMonth(),
2356 showPreviousYear(), showNextYear()
2357*/
2358
2359void QCalendarWidget::setCurrentPage(int year, int month)
2360{
2361 Q_D(QCalendarWidget);
2362 QDate currentDate = d->getCurrentDate();
2363 QCalendar cal = d->m_model->m_calendar;
2364 int day = currentDate.day(cal);
2365 int daysInMonths = cal.daysInMonth(month, year);
2366 if (day > daysInMonths)
2367 day = daysInMonths;
2368
2369 d->showMonth(year, month);
2370
2371 QDate newDate(year, month, day, d->m_model->m_calendar);
2372 int row = -1, col = -1;
2373 d->m_model->cellForDate(newDate, &row, &col);
2374 if (row != -1 && col != -1) {
2375 d->m_view->selectionModel()->setCurrentIndex(d->m_model->index(row, col),
2376 QItemSelectionModel::NoUpdate);
2377 }
2378}
2379
2380/*!
2381 Shows the next month relative to the currently displayed
2382 month. Note that the selected date is not changed.
2383
2384 \sa showPreviousMonth(), setCurrentPage(), setSelectedDate()
2385*/
2386
2387void QCalendarWidget::showNextMonth()
2388{
2389 Q_D(const QCalendarWidget);
2390 int year = yearShown();
2391 int month = monthShown();
2392 if (month == d->m_model->m_calendar.maximumMonthsInYear()) {
2393 ++year;
2394 month = 1;
2395 } else {
2396 ++month;
2397 }
2398 setCurrentPage(year, month);
2399}
2400
2401/*!
2402 Shows the previous month relative to the currently displayed
2403 month. Note that the selected date is not changed.
2404
2405 \sa showNextMonth(), setCurrentPage(), setSelectedDate()
2406*/
2407
2408void QCalendarWidget::showPreviousMonth()
2409{
2410 Q_D(const QCalendarWidget);
2411
2412 int year = yearShown();
2413 int month = monthShown();
2414 if (month == 1) {
2415 --year;
2416 month = d->m_model->m_calendar.maximumMonthsInYear();
2417 } else {
2418 --month;
2419 }
2420 setCurrentPage(year, month);
2421}
2422
2423/*!
2424 Shows the currently displayed month in the \e next year relative
2425 to the currently displayed year. Note that the selected date is
2426 not changed.
2427
2428 \sa showPreviousYear(), setCurrentPage(), setSelectedDate()
2429*/
2430
2431void QCalendarWidget::showNextYear()
2432{
2433 int year = yearShown();
2434 int month = monthShown();
2435 ++year;
2436 setCurrentPage(year, month);
2437}
2438
2439/*!
2440 Shows the currently displayed month in the \e previous year
2441 relative to the currently displayed year. Note that the selected
2442 date is not changed.
2443
2444 \sa showNextYear(), setCurrentPage(), setSelectedDate()
2445*/
2446
2447void QCalendarWidget::showPreviousYear()
2448{
2449 int year = yearShown();
2450 int month = monthShown();
2451 --year;
2452 setCurrentPage(year, month);
2453}
2454
2455/*!
2456 Shows the month of the selected date.
2457
2458 \sa selectedDate(), setCurrentPage()
2459*/
2460void QCalendarWidget::showSelectedDate()
2461{
2462 Q_D(const QCalendarWidget);
2463
2464 QDate currentDate = selectedDate();
2465 setCurrentPage(currentDate.year(d->m_model->m_calendar), currentDate.month(d->m_model->m_calendar));
2466}
2467
2468/*!
2469 Shows the month of the today's date.
2470
2471 \sa selectedDate(), setCurrentPage()
2472*/
2473void QCalendarWidget::showToday()
2474{
2475 Q_D(const QCalendarWidget);
2476
2477 QDate currentDate = QDate::currentDate();
2478 setCurrentPage(currentDate.year(d->m_model->m_calendar), currentDate.month(d->m_model->m_calendar));
2479}
2480
2481/*!
2482 \property QCalendarWidget::minimumDate
2483 \brief the minimum date of the currently specified date range.
2484
2485 The user will not be able to select a date that is before the
2486 currently set minimum date.
2487
2488 \table
2489 \row
2490 \li \image qcalendarwidget-minimum.png
2491 \row
2492 \li
2493 \snippet code/src_gui_widgets_qcalendarwidget.cpp 1
2494 \endtable
2495
2496 When setting a minimum date, the maximumDate and selectedDate
2497 properties are adjusted if the selection range becomes invalid. If
2498 the provided date is not a valid QDate object, the
2499 setMinimumDate() function does nothing.
2500
2501 The default minimum date is November 25, 4714 BCE.
2502 You can restore this default by calling clearMinimumDate() (since Qt 6.6).
2503
2504 \sa setDateRange()
2505*/
2506
2507QDate QCalendarWidget::minimumDate() const
2508{
2509 Q_D(const QCalendarWidget);
2510 return d->m_model->m_minimumDate;
2511}
2512
2513void QCalendarWidget::setMinimumDate(QDate date)
2514{
2515 Q_D(QCalendarWidget);
2516 if (!date.isValid() || d->m_model->m_minimumDate == date)
2517 return;
2518
2519 QDate oldDate = d->m_model->m_date;
2520 d->m_model->setMinimumDate(date);
2521 d->yearEdit->setMinimum(d->m_model->m_minimumDate.year(d->m_model->m_calendar));
2522 d->updateMonthMenu();
2523 QDate newDate = d->m_model->m_date;
2524 if (oldDate != newDate) {
2525 d->update();
2526 d->showMonth(newDate.year(d->m_model->m_calendar), newDate.month(d->m_model->m_calendar));
2527 d->m_navigator->setDate(newDate);
2528 emit selectionChanged();
2529 }
2530}
2531
2532void QCalendarWidget::clearMinimumDate()
2533{
2534 setMinimumDate(QDate::fromJulianDay(1));
2535}
2536
2537/*!
2538 \property QCalendarWidget::maximumDate
2539 \brief the maximum date of the currently specified date range.
2540
2541 The user will not be able to select a date which is after the
2542 currently set maximum date.
2543
2544 \table
2545 \row
2546 \li \image qcalendarwidget-maximum.png
2547 \row
2548 \li
2549 \snippet code/src_gui_widgets_qcalendarwidget.cpp 2
2550 \endtable
2551
2552 When setting a maximum date, the minimumDate and selectedDate
2553 properties are adjusted if the selection range becomes invalid. If
2554 the provided date is not a valid QDate object, the
2555 setMaximumDate() function does nothing.
2556
2557 The default maximum date is December 31, 9999 CE.
2558 You can restore this default by calling clearMaximumDate() (since Qt 6.6).
2559
2560 \sa setDateRange()
2561*/
2562
2563QDate QCalendarWidget::maximumDate() const
2564{
2565 Q_D(const QCalendarWidget);
2566 return d->m_model->m_maximumDate;
2567}
2568
2569void QCalendarWidget::setMaximumDate(QDate date)
2570{
2571 Q_D(QCalendarWidget);
2572 if (!date.isValid() || d->m_model->m_maximumDate == date)
2573 return;
2574
2575 QDate oldDate = d->m_model->m_date;
2576 d->m_model->setMaximumDate(date);
2577 d->yearEdit->setMaximum(d->m_model->m_maximumDate.year(d->m_model->m_calendar));
2578 d->updateMonthMenu();
2579 QDate newDate = d->m_model->m_date;
2580 if (oldDate != newDate) {
2581 d->update();
2582 d->showMonth(newDate.year(d->m_model->m_calendar), newDate.month(d->m_model->m_calendar));
2583 d->m_navigator->setDate(newDate);
2584 emit selectionChanged();
2585 }
2586}
2587
2588void QCalendarWidget::clearMaximumDate()
2589{
2590 setMaximumDate(QDate(9999, 12, 31));
2591}
2592
2593/*!
2594 Defines a date range by setting the minimumDate and maximumDate
2595 properties.
2596
2597 The date range restricts the user selection, i.e. the user can
2598 only select dates within the specified date range. Note that
2599
2600 \snippet code/src_gui_widgets_qcalendarwidget.cpp 3
2601
2602 is analogous to
2603
2604 \snippet code/src_gui_widgets_qcalendarwidget.cpp 4
2605
2606 If either the \a min or \a max parameters are not valid QDate
2607 objects, this function does nothing.
2608
2609 \sa setMinimumDate(), setMaximumDate()
2610*/
2611
2612void QCalendarWidget::setDateRange(QDate min, QDate max)
2613{
2614 Q_D(QCalendarWidget);
2615 if (d->m_model->m_minimumDate == min && d->m_model->m_maximumDate == max)
2616 return;
2617 if (!min.isValid() || !max.isValid())
2618 return;
2619
2620 QDate oldDate = d->m_model->m_date;
2621 d->m_model->setRange(min, max);
2622 d->yearEdit->setMinimum(d->m_model->m_minimumDate.year(d->m_model->m_calendar));
2623 d->yearEdit->setMaximum(d->m_model->m_maximumDate.year(d->m_model->m_calendar));
2624 d->updateMonthMenu();
2625 QDate newDate = d->m_model->m_date;
2626 if (oldDate != newDate) {
2627 d->update();
2628 d->showMonth(newDate.year(d->m_model->m_calendar), newDate.month(d->m_model->m_calendar));
2629 d->m_navigator->setDate(newDate);
2630 emit selectionChanged();
2631 }
2632}
2633
2634
2635/*! \enum QCalendarWidget::HorizontalHeaderFormat
2636
2637 This enum type defines the various formats the horizontal header can display.
2638
2639 \value SingleLetterDayNames The header displays a single letter abbreviation for day names (e.g. M for Monday).
2640 \value ShortDayNames The header displays a short abbreviation for day names (e.g. Mon for Monday).
2641 \value LongDayNames The header displays complete day names (e.g. Monday).
2642 \value NoHorizontalHeader The header is hidden.
2643
2644 \sa horizontalHeaderFormat(), VerticalHeaderFormat
2645*/
2646
2647/*!
2648 \property QCalendarWidget::horizontalHeaderFormat
2649 \brief the format of the horizontal header.
2650
2651 The default value is QCalendarWidget::ShortDayNames.
2652*/
2653
2654void QCalendarWidget::setHorizontalHeaderFormat(QCalendarWidget::HorizontalHeaderFormat format)
2655{
2656 Q_D(QCalendarWidget);
2657 if (d->m_model->m_horizontalHeaderFormat == format)
2658 return;
2659
2660 d->m_model->setHorizontalHeaderFormat(format);
2661 d->cachedSizeHint = QSize();
2662 d->m_view->viewport()->update();
2663 d->m_view->updateGeometry();
2664}
2665
2666QCalendarWidget::HorizontalHeaderFormat QCalendarWidget::horizontalHeaderFormat() const
2667{
2668 Q_D(const QCalendarWidget);
2669 return d->m_model->m_horizontalHeaderFormat;
2670}
2671
2672
2673/*!
2674 \enum QCalendarWidget::VerticalHeaderFormat
2675
2676 This enum type defines the various formats the vertical header can display.
2677
2678 \value ISOWeekNumbers The header displays ISO week numbers as described by \l QDate::weekNumber().
2679 \value NoVerticalHeader The header is hidden.
2680
2681 \sa verticalHeaderFormat(), HorizontalHeaderFormat
2682*/
2683
2684/*!
2685 \property QCalendarWidget::verticalHeaderFormat
2686 \brief the format of the vertical header.
2687
2688 The default value is QCalendarWidget::ISOWeekNumber.
2689*/
2690
2691QCalendarWidget::VerticalHeaderFormat QCalendarWidget::verticalHeaderFormat() const
2692{
2693 Q_D(const QCalendarWidget);
2694 bool shown = d->m_model->weekNumbersShown();
2695 if (shown)
2696 return QCalendarWidget::ISOWeekNumbers;
2697 return QCalendarWidget::NoVerticalHeader;
2698}
2699
2700void QCalendarWidget::setVerticalHeaderFormat(QCalendarWidget::VerticalHeaderFormat format)
2701{
2702 Q_D(QCalendarWidget);
2703 bool show = false;
2704 if (format == QCalendarWidget::ISOWeekNumbers)
2705 show = true;
2706 if (d->m_model->weekNumbersShown() == show)
2707 return;
2708 d->m_model->setWeekNumbersShown(show);
2709 d->cachedSizeHint = QSize();
2710 d->m_view->viewport()->update();
2711 d->m_view->updateGeometry();
2712}
2713
2714/*!
2715 \property QCalendarWidget::gridVisible
2716 \brief whether the table grid is displayed.
2717
2718 \table
2719 \row
2720 \li \inlineimage qcalendarwidget-grid.png
2721 \row
2722 \li
2723 \snippet code/src_gui_widgets_qcalendarwidget.cpp 5
2724 \endtable
2725
2726 The default value is false.
2727*/
2728
2729bool QCalendarWidget::isGridVisible() const
2730{
2731 Q_D(const QCalendarWidget);
2732 return d->m_view->showGrid();
2733}
2734
2735/*!
2736 \since 5.14
2737 Report the calendar system in use by this widget.
2738
2739 \sa setCalendar()
2740*/
2741
2742QCalendar QCalendarWidget::calendar() const
2743{
2744 Q_D(const QCalendarWidget);
2745 return d->m_model->m_calendar;
2746}
2747
2748/*!
2749 \since 5.14
2750 Set \a c as the calendar system to be used by this widget.
2751
2752 The widget can use any supported calendar system.
2753 By default, it uses the Gregorian calendar.
2754
2755 \sa calendar()
2756*/
2757
2758void QCalendarWidget::setCalendar(QCalendar c)
2759{
2760 Q_D(QCalendarWidget);
2761 d->m_model->setCalendar(c);
2762 d->updateMonthMenuNames();
2763 d->yearEdit->setMinimum(d->m_model->m_minimumDate.year(d->m_model->m_calendar));
2764 d->yearEdit->setMaximum(d->m_model->m_maximumDate.year(d->m_model->m_calendar));
2765 d->updateNavigationBar();
2766}
2767
2768void QCalendarWidget::setGridVisible(bool show)
2769{
2770 Q_D(QCalendarWidget);
2771 d->m_view->setShowGrid(show);
2772 d->cachedSizeHint = QSize();
2773 d->m_view->viewport()->update();
2774 d->m_view->updateGeometry();
2775}
2776
2777/*!
2778 \property QCalendarWidget::selectionMode
2779 \brief the type of selection the user can make in the calendar
2780
2781 When this property is set to SingleSelection, the user can select a date
2782 within the minimum and maximum allowed dates, using either the mouse or
2783 the keyboard.
2784
2785 When the property is set to NoSelection, the user will be unable to select
2786 dates, but they can still be selected programmatically. Note that the date
2787 that is selected when the property is set to NoSelection will still be
2788 the selected date of the calendar.
2789
2790 The default value is SingleSelection.
2791*/
2792
2793QCalendarWidget::SelectionMode QCalendarWidget::selectionMode() const
2794{
2795 Q_D(const QCalendarWidget);
2796 return d->m_view->readOnly ? QCalendarWidget::NoSelection : QCalendarWidget::SingleSelection;
2797}
2798
2799void QCalendarWidget::setSelectionMode(SelectionMode mode)
2800{
2801 Q_D(QCalendarWidget);
2802 d->m_view->readOnly = (mode == QCalendarWidget::NoSelection);
2803 d->setNavigatorEnabled(isDateEditEnabled() && (selectionMode() != QCalendarWidget::NoSelection));
2804 d->update();
2805}
2806
2807/*!
2808 \property QCalendarWidget::firstDayOfWeek
2809 \brief a value identifying the day displayed in the first column.
2810
2811 By default, the day displayed in the first column
2812 is the first day of the week for the calendar's locale.
2813*/
2814
2815void QCalendarWidget::setFirstDayOfWeek(Qt::DayOfWeek dayOfWeek)
2816{
2817 Q_D(QCalendarWidget);
2818 if ((Qt::DayOfWeek)d->m_model->firstColumnDay() == dayOfWeek)
2819 return;
2820
2821 d->m_model->setFirstColumnDay(dayOfWeek);
2822 d->update();
2823}
2824
2825Qt::DayOfWeek QCalendarWidget::firstDayOfWeek() const
2826{
2827 Q_D(const QCalendarWidget);
2828 return (Qt::DayOfWeek)d->m_model->firstColumnDay();
2829}
2830
2831/*!
2832 Returns the text char format for rendering the header.
2833*/
2834QTextCharFormat QCalendarWidget::headerTextFormat() const
2835{
2836 Q_D(const QCalendarWidget);
2837 return d->m_model->m_headerFormat;
2838}
2839
2840/*!
2841 Sets the text char format for rendering the header to \a format.
2842 If you also set a weekday text format, this format's foreground and
2843 background color will take precedence over the header's format.
2844 The other formatting information will still be decided by
2845 the header's format.
2846*/
2847void QCalendarWidget::setHeaderTextFormat(const QTextCharFormat &format)
2848{
2849 Q_D(QCalendarWidget);
2850 d->m_model->m_headerFormat = format;
2851 d->cachedSizeHint = QSize();
2852 d->m_view->viewport()->update();
2853 d->m_view->updateGeometry();
2854}
2855
2856/*!
2857 Returns the text char format for rendering of day in the week \a dayOfWeek.
2858
2859 \sa headerTextFormat()
2860*/
2861QTextCharFormat QCalendarWidget::weekdayTextFormat(Qt::DayOfWeek dayOfWeek) const
2862{
2863 Q_D(const QCalendarWidget);
2864 return d->m_model->m_dayFormats.value(dayOfWeek);
2865}
2866
2867/*!
2868 Sets the text char format for rendering of day in the week \a dayOfWeek to \a format.
2869 The format will take precedence over the header format in case of foreground
2870 and background color. Other text formatting information is taken from the headers format.
2871
2872 \sa setHeaderTextFormat()
2873*/
2874void QCalendarWidget::setWeekdayTextFormat(Qt::DayOfWeek dayOfWeek, const QTextCharFormat &format)
2875{
2876 Q_D(QCalendarWidget);
2877 d->m_model->m_dayFormats[dayOfWeek] = format;
2878 d->cachedSizeHint = QSize();
2879 d->m_view->viewport()->update();
2880 d->m_view->updateGeometry();
2881}
2882
2883/*!
2884 Returns a QMap from QDate to QTextCharFormat showing all dates
2885 that use a special format that alters their rendering.
2886*/
2887QMap<QDate, QTextCharFormat> QCalendarWidget::dateTextFormat() const
2888{
2889 Q_D(const QCalendarWidget);
2890 return d->m_model->m_dateFormats;
2891}
2892
2893/*!
2894 Returns a QTextCharFormat for \a date. The char format can be
2895 empty if the date is not renderd specially.
2896*/
2897QTextCharFormat QCalendarWidget::dateTextFormat(QDate date) const
2898{
2899 Q_D(const QCalendarWidget);
2900 return d->m_model->m_dateFormats.value(date);
2901}
2902
2903/*!
2904 Sets the format used to render the given \a date to that specified by \a format.
2905
2906 If \a date is null, all date formats are cleared.
2907*/
2908void QCalendarWidget::setDateTextFormat(QDate date, const QTextCharFormat &format)
2909{
2910 Q_D(QCalendarWidget);
2911 if (date.isNull())
2912 d->m_model->m_dateFormats.clear();
2913 else
2914 d->m_model->m_dateFormats[date] = format;
2915 d->m_view->viewport()->update();
2916 d->m_view->updateGeometry();
2917}
2918
2919/*!
2920 \property QCalendarWidget::dateEditEnabled
2921 \brief whether the date edit popup is enabled
2922 \since 4.3
2923
2924 If this property is enabled, pressing a non-modifier key will cause a
2925 date edit to popup if the calendar widget has focus, allowing the user
2926 to specify a date in the form specified by the current locale.
2927
2928 By default, this property is enabled.
2929
2930 The date edit is simpler in appearance than QDateEdit, but allows the
2931 user to navigate between fields using the left and right cursor keys,
2932 increment and decrement individual fields using the up and down cursor
2933 keys, and enter values directly using the number keys.
2934
2935 \sa QCalendarWidget::dateEditAcceptDelay
2936*/
2937bool QCalendarWidget::isDateEditEnabled() const
2938{
2939 Q_D(const QCalendarWidget);
2940 return d->m_dateEditEnabled;
2941}
2942
2943void QCalendarWidget::setDateEditEnabled(bool enable)
2944{
2945 Q_D(QCalendarWidget);
2946 if (isDateEditEnabled() == enable)
2947 return;
2948
2949 d->m_dateEditEnabled = enable;
2950
2951 d->setNavigatorEnabled(enable && (selectionMode() != QCalendarWidget::NoSelection));
2952}
2953
2954/*!
2955 \property QCalendarWidget::dateEditAcceptDelay
2956 \brief the time an inactive date edit is shown before its contents are accepted
2957 \since 4.3
2958
2959 If the calendar widget's \l{dateEditEnabled}{date edit is enabled}, this
2960 property specifies the amount of time (in milliseconds) that the date edit
2961 remains open after the most recent user input. Once this time has elapsed,
2962 the date specified in the date edit is accepted and the popup is closed.
2963
2964 By default, the delay is defined to be 1500 milliseconds (1.5 seconds).
2965*/
2966int QCalendarWidget::dateEditAcceptDelay() const
2967{
2968 Q_D(const QCalendarWidget);
2969 return d->m_navigator->dateEditAcceptDelay();
2970}
2971
2972void QCalendarWidget::setDateEditAcceptDelay(int delay)
2973{
2974 Q_D(QCalendarWidget);
2975 d->m_navigator->setDateEditAcceptDelay(delay);
2976}
2977
2978/*!
2979 \since 4.4
2980
2981 Updates the cell specified by the given \a date unless updates
2982 are disabled or the cell is hidden.
2983
2984 \sa updateCells(), yearShown(), monthShown()
2985*/
2986void QCalendarWidget::updateCell(QDate date)
2987{
2988 if (Q_UNLIKELY(!date.isValid())) {
2989 qWarning("QCalendarWidget::updateCell: Invalid date");
2990 return;
2991 }
2992
2993 if (!isVisible())
2994 return;
2995
2996 Q_D(QCalendarWidget);
2997 int row, column;
2998 d->m_model->cellForDate(date, &row, &column);
2999 if (row == -1 || column == -1)
3000 return;
3001
3002 QModelIndex modelIndex = d->m_model->index(row, column);
3003 if (!modelIndex.isValid())
3004 return;
3005
3006 d->m_view->viewport()->update(d->m_view->visualRect(modelIndex));
3007}
3008
3009/*!
3010 \since 4.4
3011
3012 Updates all visible cells unless updates are disabled.
3013
3014 \sa updateCell()
3015*/
3016void QCalendarWidget::updateCells()
3017{
3018 Q_D(QCalendarWidget);
3019 if (isVisible())
3020 d->m_view->viewport()->update();
3021}
3022
3023/*!
3024 \fn void QCalendarWidget::selectionChanged()
3025
3026 This signal is emitted when the currently selected date is
3027 changed.
3028
3029 The currently selected date can be changed by the user using the
3030 mouse or keyboard, or by the programmer using setSelectedDate().
3031
3032 \sa selectedDate()
3033*/
3034
3035/*!
3036 \fn void QCalendarWidget::currentPageChanged(int year, int month)
3037
3038 This signal is emitted when the currently shown month is changed.
3039 The new \a year and \a month are passed as parameters.
3040
3041 \sa setCurrentPage()
3042*/
3043
3044/*!
3045 \fn void QCalendarWidget::activated(QDate date)
3046
3047 This signal is emitted whenever the user presses the Return or
3048 Enter key or double-clicks a \a date in the calendar
3049 widget.
3050*/
3051
3052/*!
3053 \fn void QCalendarWidget::clicked(QDate date)
3054
3055 This signal is emitted when a mouse button is clicked. The date
3056 the mouse was clicked on is specified by \a date. The signal is
3057 only emitted when clicked on a valid date, e.g., dates are not
3058 outside the minimumDate() and maximumDate(). If the selection mode
3059 is NoSelection, this signal will not be emitted.
3060
3061*/
3062
3063/*!
3064 \property QCalendarWidget::navigationBarVisible
3065 \brief whether the navigation bar is shown or not
3066
3067 \since 4.3
3068
3069 When this property is \c true (the default), the next month,
3070 previous month, month selection, year selection controls are
3071 shown on top.
3072
3073 When the property is set to false, these controls are hidden.
3074*/
3075
3076bool QCalendarWidget::isNavigationBarVisible() const
3077{
3078 Q_D(const QCalendarWidget);
3079 return d->navBarVisible;
3080}
3081
3082
3083void QCalendarWidget::setNavigationBarVisible(bool visible)
3084{
3085 Q_D(QCalendarWidget);
3086 d->navBarVisible = visible;
3087 d->cachedSizeHint = QSize();
3088 d->navBarBackground->setVisible(visible);
3089 updateGeometry();
3090}
3091
3092/*!
3093 \reimp
3094*/
3095bool QCalendarWidget::event(QEvent *event)
3096{
3097 Q_D(QCalendarWidget);
3098 switch (event->type()) {
3099 case QEvent::LayoutDirectionChange:
3100 d->updateButtonIcons();
3101 break;
3102 case QEvent::LocaleChange:
3103 d->m_model->setFirstColumnDay(locale().firstDayOfWeek());
3104 d->cachedSizeHint = QSize();
3105 d->updateMonthMenuNames();
3106 d->updateNavigationBar();
3107 d->m_view->updateGeometry();
3108 // TODO: fix this known bug of calendaring API:
3109 // Changing locale before calendar works, but reverse order causes
3110 // invalid month names (in C Locale apparently).
3111 break;
3112 case QEvent::FontChange:
3113 case QEvent::ApplicationFontChange:
3114 d->cachedSizeHint = QSize();
3115 d->m_view->updateGeometry();
3116 break;
3117 case QEvent::StyleChange:
3118 d->cachedSizeHint = QSize();
3119 d->m_view->updateGeometry();
3120 break;
3121 default:
3122 break;
3123 }
3124 return QWidget::event(event);
3125}
3126
3127/*!
3128 \reimp
3129*/
3130bool QCalendarWidget::eventFilter(QObject *watched, QEvent *event)
3131{
3132 Q_D(QCalendarWidget);
3133 if (event->type() == QEvent::MouseButtonPress && d->yearEdit->hasFocus()) {
3134 // We can get filtered press events that were intended for Qt Virtual Keyboard's
3135 // input panel (QQuickView), so we have to make sure that the window is indeed a QWidget - no static_cast.
3136 // In addition, as we have a event filter on the whole application we first make sure that the top level widget
3137 // of both this and the watched widget are the same to decide if we should finish the year edition.
3138 QWidget *tlw = window();
3139 QWidget *widget = qobject_cast<QWidget *>(watched);
3140 if (!widget || widget->window() != tlw)
3141 return QWidget::eventFilter(watched, event);
3142
3143 QPoint mousePos = widget->mapTo(tlw, static_cast<QMouseEvent *>(event)->position().toPoint());
3144 QRect geom = QRect(d->yearEdit->mapTo(tlw, QPoint(0, 0)), d->yearEdit->size());
3145 if (!geom.contains(mousePos)) {
3146 event->accept();
3147 d->_q_yearEditingFinished();
3148 setFocus();
3149 return true;
3150 }
3151 }
3152 return QWidget::eventFilter(watched, event);
3153}
3154
3155/*!
3156 \reimp
3157*/
3158void QCalendarWidget::mousePressEvent(QMouseEvent *event)
3159{
3160 setAttribute(Qt::WA_NoMouseReplay);
3161 QWidget::mousePressEvent(event);
3162 setFocus();
3163}
3164
3165/*!
3166 \reimp
3167*/
3168void QCalendarWidget::resizeEvent(QResizeEvent * event)
3169{
3170 Q_D(QCalendarWidget);
3171
3172 // XXX Should really use a QWidgetStack for yearEdit and yearButton,
3173 // XXX here we hide the year edit when the layout is likely to break
3174 // XXX the manual positioning of the yearEdit over the yearButton.
3175 if (d->yearEdit->isVisible() && event->size().width() != event->oldSize().width())
3176 d->_q_yearEditingFinished();
3177
3178 QWidget::resizeEvent(event);
3179}
3180
3181/*!
3182 \reimp
3183*/
3184void QCalendarWidget::keyPressEvent(QKeyEvent * event)
3185{
3186#if QT_CONFIG(shortcut)
3187 Q_D(QCalendarWidget);
3188 if (d->yearEdit->isVisible()&& event->matches(QKeySequence::Cancel)) {
3189 d->yearEdit->setValue(yearShown());
3190 d->_q_yearEditingFinished();
3191 return;
3192 }
3193#endif
3194 QWidget::keyPressEvent(event);
3195}
3196
3197QT_END_NAMESPACE
3198
3199#include "qcalendarwidget.moc"
3200#include "moc_qcalendarwidget.cpp"
QCalendarTextNavigator * m_navigator
void setNavigatorEnabled(bool enable)
void paintCell(QPainter *painter, const QRect &rect, QDate date) const
QItemSelectionModel * m_selection
QMap< int, QAction * > monthToAction
void showMonth(int year, int month)
void _q_monthChanged(QAction *)
void _q_slotChangeDate(QDate date, bool changeMonth)
void createNavigationBar(QWidget *widget)
void _q_slotShowDate(QDate date)
QCalendarDelegate * m_delegate
void updateCurrentPage(QDate newDate)
void _q_slotChangeDate(QDate date)
The QCalendar class describes calendar systems.
Definition qcalendar.h:53
Definition qmap.h:189
\inmodule QtCore
constexpr QModelIndex() noexcept
Creates a new empty model index.
void paintEvent(QPaintEvent *e) override
\reimp
virtual Section handleKey(int key)=0
virtual void setDate(QDate date, QCalendar cal=QCalendar())=0
virtual QDate applyToDate(QDate date, QCalendar cal=QCalendar()) const =0
virtual QString text(QDate date, QCalendar cal, int repeat) const =0
static QString highlightString(const QString &str, int pos)
virtual QString text() const =0
void setLocale(const QLocale &locale)
void setInitialDate(QDate date, QCalendar cal)
void handleKeyEvent(QKeyEvent *keyEvent, QCalendar cal)
void setFormat(const QString &format)
QString currentText(QCalendar cal) const
virtual QDate applyToDate(QDate date, QCalendar cal) const override
virtual Section handleKey(int key) override
virtual void setDate(QDate date, QCalendar cal) override
virtual QString text() const override
virtual QString text(QDate date, QCalendar cal, int repeat) const override
void paintCell(QPainter *painter, const QRect &rect, QDate date) const
QString dayName(Qt::DayOfWeek day) const
void showMonth(int year, int month)
QTextCharFormat formatForCell(int row, int col) const
int columnCount(const QModelIndex &parent) const override
Returns the number of columns for the children of the given parent.
QString monthName(const QLocale &locale, int month)
void setFirstColumnDay(Qt::DayOfWeek dayOfWeek)
int columnForDayOfWeek(Qt::DayOfWeek day) const
Qt::ItemFlags flags(const QModelIndex &index) const override
\reimp
StaticDayOfWeekAssociativeArray< QTextCharFormat > m_dayFormats
int columnForFirstOfMonth(QDate date) const
void cellForDate(QDate date, int *row, int *column) const
QVariant data(const QModelIndex &index, int role) const override
Returns the data stored under the given role for the item referred to by the index.
Qt::DayOfWeek firstColumnDay() const
void setHorizontalHeaderFormat(QCalendarWidget::HorizontalHeaderFormat format)
Qt::DayOfWeek dayOfWeekForColumn(int section) const
void setView(QCalendarView *view)
int rowCount(const QModelIndex &parent) const override
Returns the number of rows under the given parent.
void setRange(QDate min, QDate max)
QMap< QDate, QTextCharFormat > m_dateFormats
QDate dateForCell(int row, int column) const
virtual QDate applyToDate(QDate date, QCalendar cal) const override
virtual Section handleKey(int key) override
virtual QString text(QDate date, QCalendar cal, int repeat) const override
virtual void setDate(QDate date, QCalendar cal) override
virtual QString text() const override
void timerEvent(QTimerEvent *e) override
This event handler can be reimplemented in a subclass to receive timer events for the object.
bool eventFilter(QObject *o, QEvent *e) override
Filters events if this object has been installed as an event filter for the watched object.
virtual void keyboardSearch(const QString &) override
Moves to and selects the item best matching the string search.
void mouseReleaseEvent(QMouseEvent *event) override
void mousePressEvent(QMouseEvent *event) override
bool event(QEvent *event) override
void keyPressEvent(QKeyEvent *event) override
void changeDate(QDate date, bool changeMonth)
void setReadOnly(bool enable)
QDate handleMouseEvent(QMouseEvent *event)
QModelIndex moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers) override
Returns a QModelIndex object pointing to the next object in the view, based on the given cursorAction...
void clicked(QDate date)
void mouseDoubleClickEvent(QMouseEvent *event) override
void mouseMoveEvent(QMouseEvent *event) override
virtual QDate applyToDate(QDate date, QCalendar cal) const override
virtual QString text(QDate date, QCalendar cal, int repeat) const override
virtual void setDate(QDate date, QCalendar cal) override
virtual Section handleKey(int key) override
virtual QString text() const override
@ RowCount
@ MinimumDayOffset
@ HeaderRow
@ HeaderColumn
@ ColumnCount
static QString formatNumber(int number, int fieldWidth)
Q_DECLARE_TYPEINFO(QtPrivate::SectionToken, Q_PRIMITIVE_TYPE)
#define qApp
constexpr SectionToken(QCalendarDateSectionValidator *v, int rep)
QCalendarDateSectionValidator * validator