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 && !style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick)) {
1466 emit editingFinished();
1467 }
1468}
1469
1470void QCalendarView::mousePressEvent(QMouseEvent *event)
1471{
1472 QCalendarModel *calendarModel = qobject_cast<QCalendarModel *>(model());
1473 if (!calendarModel) {
1474 QTableView::mousePressEvent(event);
1475 return;
1476 }
1477
1478 if (readOnly)
1479 return;
1480
1481 if (event->button() != Qt::LeftButton)
1482 return;
1483
1484 QDate date = handleMouseEvent(event);
1485 if (date.isValid()) {
1486 validDateClicked = true;
1487 int row = -1, col = -1;
1488 static_cast<QCalendarModel *>(model())->cellForDate(date, &row, &col);
1489 if (row != -1 && col != -1) {
1490 selectionModel()->setCurrentIndex(model()->index(row, col), QItemSelectionModel::NoUpdate);
1491 }
1492 } else {
1493 validDateClicked = false;
1494 event->ignore();
1495 }
1496}
1497
1498void QCalendarView::mouseMoveEvent(QMouseEvent *event)
1499{
1500 QCalendarModel *calendarModel = qobject_cast<QCalendarModel *>(model());
1501 if (!calendarModel) {
1502 QTableView::mouseMoveEvent(event);
1503 return;
1504 }
1505
1506 if (readOnly)
1507 return;
1508
1509 if (validDateClicked) {
1510 QDate date = handleMouseEvent(event);
1511 if (date.isValid()) {
1512 int row = -1, col = -1;
1513 static_cast<QCalendarModel *>(model())->cellForDate(date, &row, &col);
1514 if (row != -1 && col != -1) {
1515 selectionModel()->setCurrentIndex(model()->index(row, col), QItemSelectionModel::NoUpdate);
1516 }
1517 }
1518 } else {
1519 event->ignore();
1520 }
1521}
1522
1523void QCalendarView::mouseReleaseEvent(QMouseEvent *event)
1524{
1525 QCalendarModel *calendarModel = qobject_cast<QCalendarModel *>(model());
1526 if (!calendarModel) {
1527 QTableView::mouseReleaseEvent(event);
1528 return;
1529 }
1530
1531 if (event->button() != Qt::LeftButton)
1532 return;
1533
1534 if (readOnly)
1535 return;
1536
1537 if (validDateClicked) {
1538 QDate date = handleMouseEvent(event);
1539 if (date.isValid()) {
1540 emit changeDate(date, true);
1541 emit clicked(date);
1542 if (style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick))
1543 emit editingFinished();
1544 }
1545 validDateClicked = false;
1546 } else {
1547 event->ignore();
1548 }
1549}
1550
1552{
1553 Q_OBJECT
1554public:
1559 const QModelIndex &index) const override;
1560 void paintCell(QPainter *painter, const QRect &rect, QDate date) const;
1561
1562private:
1563 QCalendarWidgetPrivate *calendarWidgetPrivate;
1564 mutable QStyleOptionViewItem storedOption;
1565};
1566
1567//Private tool button class
1569{
1570public:
1571 QCalToolButton(QWidget * parent)
1573 { }
1574protected:
1575 void paintEvent(QPaintEvent *e) override
1576 {
1577 Q_UNUSED(e);
1578
1579 QStyleOptionToolButton opt;
1580 initStyleOption(&opt);
1581
1582 if (opt.state & QStyle::State_MouseOver || isDown()) {
1583 //act as normal button
1584 setPalette(QPalette());
1585 } else {
1586 //set the highlight color for button text
1587 QPalette toolPalette = palette();
1588 toolPalette.setColor(QPalette::ButtonText, toolPalette.color(QPalette::HighlightedText));
1589 setPalette(toolPalette);
1590 }
1591
1592 QToolButton::paintEvent(e);
1593 }
1594};
1595
1610
1611} // namespace QtPrivate
1612
1613using QCalendarDateSectionValidator = QtPrivate::QCalendarDateSectionValidator;
1614using QCalendarDayValidator = QtPrivate::QCalendarDayValidator;
1615using QCalendarMonthValidator = QtPrivate::QCalendarMonthValidator;
1616using QCalendarYearValidator = QtPrivate::QCalendarYearValidator;
1617using QCalendarDateValidator = QtPrivate::QCalendarDateValidator;
1618using QPrevNextCalButton = QtPrivate::QPrevNextCalButton;
1619using QCalendarDelegate = QtPrivate::QCalendarDelegate;
1620using QCalToolButton = QtPrivate::QCalToolButton;
1621using QCalendarDelegate = QtPrivate::QCalendarDelegate;
1622using QCalendarModel = QtPrivate::QCalendarModel;
1623using QCalendarView = QtPrivate::QCalendarView;
1624using QCalendarTextNavigator = QtPrivate::QCalendarTextNavigator;
1625
1627{
1628 Q_DECLARE_PUBLIC(QCalendarWidget)
1629public:
1631
1632 void showMonth(int year, int month);
1633 void update();
1634 void paintCell(QPainter *painter, const QRect &rect, QDate date) const;
1635
1636 void _q_slotShowDate(QDate date);
1637 void _q_slotChangeDate(QDate date);
1638 void _q_slotChangeDate(QDate date, bool changeMonth);
1640 void _q_monthChanged(QAction*);
1645
1646 void createNavigationBar(QWidget *widget);
1651 void updateCurrentPage(QDate newDate);
1652 inline QDate getCurrentDate();
1653 void setNavigatorEnabled(bool enable);
1654
1655 QCalendarModel *m_model;
1656 QCalendarView *m_view;
1657 QCalendarDelegate *m_delegate;
1659 QCalendarTextNavigator *m_navigator;
1661
1664 QCalToolButton *monthButton;
1666 QMap<int, QAction *> monthToAction;
1667 QCalToolButton *yearButton;
1671
1673 mutable QSize cachedSizeHint;
1675};
1676
1677void QCalendarDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
1678 const QModelIndex &index) const
1679{
1680 QDate date = calendarWidgetPrivate->m_model->dateForCell(index.row(), index.column());
1681 if (date.isValid()) {
1682 storedOption = option;
1683 QRect rect = option.rect;
1684 calendarWidgetPrivate->paintCell(painter, rect, date);
1685 } else {
1686 QStyledItemDelegate::paint(painter, option, index);
1687 }
1688}
1689
1690void QCalendarDelegate::paintCell(QPainter *painter, const QRect &rect, QDate date) const
1691{
1692 storedOption.rect = rect;
1693 int row = -1;
1694 int col = -1;
1695 calendarWidgetPrivate->m_model->cellForDate(date, &row, &col);
1696 QModelIndex idx = calendarWidgetPrivate->m_model->index(row, col);
1697 QStyledItemDelegate::paint(painter, storedOption, idx);
1698}
1699
1700QCalendarWidgetPrivate::QCalendarWidgetPrivate()
1701 : QWidgetPrivate()
1702{
1703 m_model = nullptr;
1704 m_view = nullptr;
1705 m_delegate = nullptr;
1706 m_selection = nullptr;
1707 m_navigator = nullptr;
1708 m_dateEditEnabled = false;
1709 navBarVisible = true;
1710 oldFocusPolicy = Qt::StrongFocus;
1711}
1712
1714{
1715 Q_Q(QCalendarWidget);
1716
1717 bool navigatorEnabled = (m_navigator->widget() != nullptr);
1718 if (enable == navigatorEnabled)
1719 return;
1720
1721 if (enable) {
1722 m_navigator->setWidget(q);
1723 q->connect(m_navigator, SIGNAL(dateChanged(QDate)),
1724 q, SLOT(_q_slotChangeDate(QDate)));
1725 q->connect(m_navigator, SIGNAL(editingFinished()),
1726 q, SLOT(_q_editingFinished()));
1727 m_view->installEventFilter(m_navigator);
1728 } else {
1729 m_navigator->setWidget(nullptr);
1730 q->disconnect(m_navigator, SIGNAL(dateChanged(QDate)),
1731 q, SLOT(_q_slotChangeDate(QDate)));
1732 q->disconnect(m_navigator, SIGNAL(editingFinished()),
1733 q, SLOT(_q_editingFinished()));
1734 m_view->removeEventFilter(m_navigator);
1735 }
1736}
1737
1739{
1740 Q_Q(QCalendarWidget);
1741 navBarBackground = new QWidget(widget);
1742 navBarBackground->setObjectName("qt_calendar_navigationbar"_L1);
1743 navBarBackground->setAutoFillBackground(true);
1744 navBarBackground->setBackgroundRole(QPalette::Highlight);
1745
1746 prevMonth = new QPrevNextCalButton(navBarBackground);
1747 nextMonth = new QPrevNextCalButton(navBarBackground);
1748 prevMonth->setAutoRaise(true);
1749 nextMonth->setAutoRaise(true);
1750 prevMonth->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum);
1751 nextMonth->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum);
1752 nextMonth->setAutoRaise(true);
1754 prevMonth->setAutoRepeat(true);
1755 nextMonth->setAutoRepeat(true);
1756
1757 monthButton = new QCalToolButton(navBarBackground);
1758 monthButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum);
1759 monthButton->setAutoRaise(true);
1760 monthButton->setPopupMode(QToolButton::InstantPopup);
1761 monthMenu = new QMenu(monthButton);
1762 for (int i = 1, e = m_model->m_calendar.maximumMonthsInYear(); i <= e; i++) {
1763 QString monthName(m_model->monthName(q->locale(), i));
1764 QAction *act = monthMenu->addAction(monthName);
1765 act->setData(i);
1766 monthToAction[i] = act;
1767 }
1768 monthButton->setMenu(monthMenu);
1769 yearButton = new QCalToolButton(navBarBackground);
1770 yearButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum);
1771 yearButton->setAutoRaise(true);
1772 yearEdit = new QSpinBox(navBarBackground);
1773
1774 QFont font = q->font();
1775 font.setBold(true);
1776 monthButton->setFont(font);
1777 yearButton->setFont(font);
1778 yearEdit->setFrame(false);
1779 yearEdit->setMinimum(m_model->m_minimumDate.year(m_model->m_calendar));
1780 yearEdit->setMaximum(m_model->m_maximumDate.year(m_model->m_calendar));
1781 yearEdit->hide();
1782 spaceHolder = new QSpacerItem(0,0);
1783
1784 QHBoxLayout *headerLayout = new QHBoxLayout;
1785 headerLayout->setContentsMargins(QMargins());
1786 headerLayout->setSpacing(0);
1787 headerLayout->addWidget(prevMonth);
1788 headerLayout->insertStretch(headerLayout->count());
1789 headerLayout->addWidget(monthButton);
1790 headerLayout->addItem(spaceHolder);
1791 headerLayout->addWidget(yearButton);
1792 headerLayout->insertStretch(headerLayout->count());
1793 headerLayout->addWidget(nextMonth);
1794 navBarBackground->setLayout(headerLayout);
1795
1796 yearEdit->setFocusPolicy(Qt::StrongFocus);
1797 prevMonth->setFocusPolicy(Qt::NoFocus);
1798 nextMonth->setFocusPolicy(Qt::NoFocus);
1799 yearButton->setFocusPolicy(Qt::NoFocus);
1800 monthButton->setFocusPolicy(Qt::NoFocus);
1801
1802 //set names for the header controls.
1803 prevMonth->setObjectName("qt_calendar_prevmonth"_L1);
1804 nextMonth->setObjectName("qt_calendar_nextmonth"_L1);
1805 monthButton->setObjectName("qt_calendar_monthbutton"_L1);
1806 yearButton->setObjectName("qt_calendar_yearbutton"_L1);
1807 yearEdit->setObjectName("qt_calendar_yearedit"_L1);
1808
1810 showMonth(m_model->m_date.year(m_model->m_calendar), m_model->m_date.month(m_model->m_calendar));
1811}
1812
1814{
1815 Q_Q(QCalendarWidget);
1816 prevMonth->setIcon(q->style()->standardIcon(q->isRightToLeft() ? QStyle::SP_ArrowRight : QStyle::SP_ArrowLeft, nullptr, q));
1817 nextMonth->setIcon(q->style()->standardIcon(q->isRightToLeft() ? QStyle::SP_ArrowLeft : QStyle::SP_ArrowRight, nullptr, q));
1818}
1819
1821{
1822 int maxMonths = m_model->m_calendar.monthsInYear(m_model->m_shownYear);
1823 int beg = 1, end = maxMonths;
1824 bool prevEnabled = true;
1825 bool nextEnabled = true;
1826 QCalendar cal = m_model->calendar();
1827 if (m_model->m_shownYear == m_model->m_minimumDate.year(cal)) {
1828 beg = m_model->m_minimumDate.month(cal);
1829 if (m_model->m_shownMonth == m_model->m_minimumDate.month(cal))
1830 prevEnabled = false;
1831 }
1832 if (m_model->m_shownYear == m_model->m_maximumDate.year(cal)) {
1833 end = m_model->m_maximumDate.month(cal);
1834 if (m_model->m_shownMonth == m_model->m_maximumDate.month(cal))
1835 nextEnabled = false;
1836 }
1837 prevMonth->setEnabled(prevEnabled);
1838 nextMonth->setEnabled(nextEnabled);
1839 for (int i = 1; i <= maxMonths; i++) {
1840 bool monthEnabled = true;
1841 if (i < beg || i > end)
1842 monthEnabled = false;
1843 monthToAction[i]->setEnabled(monthEnabled);
1844 }
1845}
1846
1848{
1849 Q_Q(QCalendarWidget);
1850
1851 for (int i = 1; i <= 12; i++) {
1852 QString monthName(m_model->monthName(q->locale(), i));
1853 monthToAction[i]->setText(monthName);
1854 }
1855}
1856
1858{
1859 Q_Q(QCalendarWidget);
1860 QCalendar cal = m_model->calendar();
1861
1862 QDate newDate = date;
1863 QDate minDate = q->minimumDate();
1864 QDate maxDate = q->maximumDate();
1865 if (minDate.isValid()&& minDate.daysTo(newDate) < 0)
1866 newDate = minDate;
1867 if (maxDate.isValid()&& maxDate.daysTo(newDate) > 0)
1868 newDate = maxDate;
1869 showMonth(newDate.year(cal), newDate.month(cal));
1870 int row = -1, col = -1;
1871 m_model->cellForDate(newDate, &row, &col);
1872 if (row != -1 && col != -1)
1873 {
1874 m_view->selectionModel()->setCurrentIndex(m_model->index(row, col),
1875 QItemSelectionModel::NoUpdate);
1876 }
1877}
1878
1880{
1881 monthButton->setText(act->text());
1882 QDate currentDate = getCurrentDate();
1883 QDate newDate = currentDate.addMonths(act->data().toInt() - currentDate.month(m_model->m_calendar), m_model->m_calendar);
1884 updateCurrentPage(newDate);
1885}
1886
1888{
1889 QModelIndex index = m_view->currentIndex();
1890 return m_model->dateForCell(index.row(), index.column());
1891}
1892
1894{
1895 QDate currentDate = getCurrentDate().addMonths(-1, m_model->m_calendar);
1896 updateCurrentPage(currentDate);
1897}
1898
1900{
1901 QDate currentDate = getCurrentDate().addMonths(1, m_model->m_calendar);
1902 updateCurrentPage(currentDate);
1903}
1904
1906{
1907 Q_Q(QCalendarWidget);
1908 yearEdit->hide();
1909 q->setFocusPolicy(oldFocusPolicy);
1910 qApp->removeEventFilter(q);
1911 spaceHolder->changeSize(0, 0);
1912 yearButton->show();
1913 QDate currentDate = getCurrentDate();
1914 int newYear = q->locale().toInt(yearEdit->text());
1915 currentDate = currentDate.addYears(newYear - currentDate.year(m_model->m_calendar), m_model->m_calendar);
1916 yearButton->setText(q->locale().toString(currentDate, u"yyyy", m_model->m_calendar));
1917 updateCurrentPage(currentDate);
1918}
1919
1921{
1922 Q_Q(QCalendarWidget);
1923 //show the spinbox on top of the button
1924 yearEdit->setGeometry(yearButton->x(), yearButton->y(),
1925 yearEdit->sizeHint().width(), yearButton->height());
1926 spaceHolder->changeSize(yearButton->width(), 0);
1927 yearButton->hide();
1928 oldFocusPolicy = q->focusPolicy();
1929 q->setFocusPolicy(Qt::NoFocus);
1930 yearEdit->show();
1931 qApp->installEventFilter(q);
1932 yearEdit->raise();
1933 yearEdit->selectAll();
1934 yearEdit->setFocus(Qt::MouseFocusReason);
1935}
1936
1937void QCalendarWidgetPrivate::showMonth(int year, int month)
1938{
1939 if (m_model->m_shownYear == year && m_model->m_shownMonth == month)
1940 return;
1941 Q_Q(QCalendarWidget);
1942 m_model->showMonth(year, month);
1944 emit q->currentPageChanged(year, month);
1946 cachedSizeHint = QSize();
1947 update();
1949}
1950
1952{
1953 Q_Q(QCalendarWidget);
1954
1955 QString monthName = m_model->monthName(q->locale(), m_model->m_shownMonth);
1956
1957 monthButton->setText(monthName);
1958 yearEdit->setValue(m_model->m_shownYear);
1959 yearButton->setText(yearEdit->text());
1960}
1961
1963{
1964 QDate currentDate = m_model->m_date;
1965 int row, column;
1966 m_model->cellForDate(currentDate, &row, &column);
1967 QModelIndex idx;
1968 m_selection->clear();
1969 if (row != -1 && column != -1) {
1970 idx = m_model->index(row, column);
1971 m_selection->setCurrentIndex(idx, QItemSelectionModel::SelectCurrent);
1972 }
1973}
1974
1975void QCalendarWidgetPrivate::paintCell(QPainter *painter, const QRect &rect, QDate date) const
1976{
1977 Q_Q(const QCalendarWidget);
1978 q->paintCell(painter, rect, date);
1979}
1980
1982{
1984}
1985
1987{
1988 _q_slotChangeDate(date, true);
1989}
1990
1991void QCalendarWidgetPrivate::_q_slotChangeDate(QDate date, bool changeMonth)
1992{
1993 QDate oldDate = m_model->m_date;
1995 QDate newDate = m_model->m_date;
1996 if (changeMonth)
1997 showMonth(newDate.year(m_model->m_calendar), newDate.month(m_model->m_calendar));
1998 if (oldDate != newDate) {
1999 update();
2000 Q_Q(QCalendarWidget);
2002 emit q->selectionChanged();
2003 }
2004}
2005
2007{
2008 Q_Q(QCalendarWidget);
2009 emit q->activated(m_model->m_date);
2010}
2011
2012/*!
2013 \class QCalendarWidget
2014 \brief The QCalendarWidget class provides a monthly based
2015 calendar widget allowing the user to select a date.
2016 \since 4.2
2017
2018 \ingroup advanced
2019 \inmodule QtWidgets
2020
2021 \image fusion-calendarwidget.png
2022
2023 The widget is initialized with the current month and year, but
2024 QCalendarWidget provides several public slots to change the year
2025 and month that is shown.
2026
2027 By default, today's date is selected, and the user can select a
2028 date using both mouse and keyboard. The currently selected date
2029 can be retrieved using the selectedDate() function. It is
2030 possible to constrain the user selection to a given date range by
2031 setting the minimumDate and maximumDate properties.
2032 Alternatively, both properties can be set in one go using the
2033 setDateRange() convenience slot. Set the \l selectionMode
2034 property to NoSelection to prohibit the user from selecting at
2035 all. Note that a date also can be selected programmatically using
2036 the setSelectedDate() slot.
2037
2038 The currently displayed month and year can be retrieved using the
2039 monthShown() and yearShown() functions, respectively.
2040
2041 A newly created calendar widget uses abbreviated day names, and
2042 both Saturdays and Sundays are marked in red. The calendar grid is
2043 not visible. The week numbers are displayed, and the first column
2044 day is the first day of the week for the calendar's locale.
2045
2046 The notation of the days can be altered to a single letter
2047 abbreviations ("M" for "Monday") by setting the
2048 horizontalHeaderFormat property to
2049 QCalendarWidget::SingleLetterDayNames. Setting the same property
2050 to QCalendarWidget::LongDayNames makes the header display the
2051 complete day names. The week numbers can be removed by setting
2052 the verticalHeaderFormat property to
2053 QCalendarWidget::NoVerticalHeader. The calendar grid can be
2054 turned on by setting the gridVisible property to true using the
2055 setGridVisible() function:
2056
2057 \table
2058 \row \li
2059 \image qcalendarwidget-grid.png
2060 \row \li
2061 \snippet code/src_gui_widgets_qcalendarwidget.cpp 0
2062 \endtable
2063
2064 Finally, the day in the first column can be altered using the
2065 setFirstDayOfWeek() function.
2066
2067 The QCalendarWidget class also provides three signals,
2068 selectionChanged(), activated() and currentPageChanged() making it
2069 possible to respond to user interaction.
2070
2071 The rendering of the headers, weekdays or single days can be
2072 largely customized by setting QTextCharFormat's for some special
2073 weekday, a special date or for the rendering of the headers.
2074
2075 Only a subset of the properties in QTextCharFormat are used by the
2076 calendar widget. Currently, the foreground, background and font
2077 properties are used to determine the rendering of individual cells
2078 in the widget.
2079
2080 \sa QDate, QDateEdit, QTextCharFormat
2081*/
2082
2083/*!
2084 \enum QCalendarWidget::SelectionMode
2085
2086 This enum describes the types of selection offered to the user for
2087 selecting dates in the calendar.
2088
2089 \value NoSelection Dates cannot be selected.
2090 \value SingleSelection Single dates can be selected.
2091
2092 \sa selectionMode
2093*/
2094
2095/*!
2096 Constructs a calendar widget with the given \a parent.
2097
2098 The widget is initialized with the current month and year, and the
2099 currently selected date is today.
2100
2101 \sa setCurrentPage()
2102*/
2103QCalendarWidget::QCalendarWidget(QWidget *parent)
2104 : QWidget(*new QCalendarWidgetPrivate, parent, { })
2105{
2106 Q_D(QCalendarWidget);
2107
2108 setAutoFillBackground(true);
2109 setBackgroundRole(QPalette::Window);
2110
2111 QVBoxLayout *layoutV = new QVBoxLayout(this);
2112 layoutV->setContentsMargins(QMargins());
2113 d->m_model = new QCalendarModel(this);
2114 QTextCharFormat fmt;
2115 fmt.setForeground(QBrush(Qt::red));
2116 d->m_model->m_dayFormats.insert(Qt::Saturday, fmt);
2117 d->m_model->m_dayFormats.insert(Qt::Sunday, fmt);
2118 d->m_view = new QCalendarView(this);
2119 d->m_view->setObjectName("qt_calendar_calendarview"_L1);
2120 d->m_view->setModel(d->m_model);
2121 d->m_model->setView(d->m_view);
2122 d->m_view->setSelectionBehavior(QAbstractItemView::SelectItems);
2123 d->m_view->setSelectionMode(QAbstractItemView::SingleSelection);
2124 d->m_view->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
2125 d->m_view->horizontalHeader()->setSectionsClickable(false);
2126 d->m_view->verticalHeader()->setSectionResizeMode(QHeaderView::Stretch);
2127 d->m_view->verticalHeader()->setSectionsClickable(false);
2128 d->m_selection = d->m_view->selectionModel();
2129 d->createNavigationBar(this);
2130 d->m_view->setFrameStyle(QFrame::NoFrame);
2131 d->m_delegate = new QCalendarDelegate(d, this);
2132 d->m_view->setItemDelegate(d->m_delegate);
2133 d->update();
2134 d->updateNavigationBar();
2135 setFocusPolicy(Qt::StrongFocus);
2136 setFocusProxy(d->m_view);
2137 setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
2138
2139 connect(d->m_view, SIGNAL(showDate(QDate)),
2140 this, SLOT(_q_slotShowDate(QDate)));
2141 connect(d->m_view, SIGNAL(changeDate(QDate,bool)),
2142 this, SLOT(_q_slotChangeDate(QDate,bool)));
2143 connect(d->m_view, SIGNAL(clicked(QDate)),
2144 this, SIGNAL(clicked(QDate)));
2145 connect(d->m_view, SIGNAL(editingFinished()),
2146 this, SLOT(_q_editingFinished()));
2147
2148 connect(d->prevMonth, SIGNAL(clicked(bool)),
2149 this, SLOT(_q_prevMonthClicked()));
2150 connect(d->nextMonth, SIGNAL(clicked(bool)),
2151 this, SLOT(_q_nextMonthClicked()));
2152 connect(d->yearButton, SIGNAL(clicked(bool)),
2153 this, SLOT(_q_yearClicked()));
2154 connect(d->monthMenu, SIGNAL(triggered(QAction*)),
2155 this, SLOT(_q_monthChanged(QAction*)));
2156 connect(d->yearEdit, SIGNAL(editingFinished()),
2157 this, SLOT(_q_yearEditingFinished()));
2158
2159 layoutV->setContentsMargins(QMargins());
2160 layoutV->setSpacing(0);
2161 layoutV->addWidget(d->navBarBackground);
2162 layoutV->addWidget(d->m_view);
2163
2164 d->m_navigator = new QCalendarTextNavigator(this);
2165 setDateEditEnabled(true);
2166}
2167
2168/*!
2169 Destroys the calendar widget.
2170*/
2171QCalendarWidget::~QCalendarWidget()
2172{
2173}
2174
2175/*!
2176 \reimp
2177*/
2178QSize QCalendarWidget::sizeHint() const
2179{
2180 return minimumSizeHint();
2181}
2182
2183/*!
2184 \reimp
2185*/
2186QSize QCalendarWidget::minimumSizeHint() const
2187{
2188 Q_D(const QCalendarWidget);
2189 if (d->cachedSizeHint.isValid())
2190 return d->cachedSizeHint;
2191
2192 ensurePolished();
2193
2194 int w = 0;
2195 int h = 0;
2196
2197 int end = 53;
2198 int rows = 7;
2199 int cols = 8;
2200
2201 QStyleOption option;
2202 option.initFrom(this);
2203 const int marginH = (style()->pixelMetric(QStyle::PM_FocusFrameHMargin, &option, this) + 1) * 2;
2204
2205 if (horizontalHeaderFormat() == QCalendarWidget::NoHorizontalHeader) {
2206 rows = 6;
2207 } else {
2208 for (int i = 1; i <= 7; i++) {
2209 QFontMetrics fm(d->m_model->formatForCell(0, i).font());
2210 w = qMax(w, fm.horizontalAdvance(d->m_model->dayName(d->m_model->dayOfWeekForColumn(i))) + marginH);
2211 h = qMax(h, fm.height());
2212 }
2213 }
2214
2215 if (verticalHeaderFormat() == QCalendarWidget::NoVerticalHeader) {
2216 cols = 7;
2217 } else {
2218 for (int i = 1; i <= 6; i++) {
2219 QFontMetrics fm(d->m_model->formatForCell(i, 0).font());
2220 for (int j = 1; j < end; j++)
2221 w = qMax(w, fm.horizontalAdvance(QString::number(j)) + marginH);
2222 h = qMax(h, fm.height());
2223 }
2224 }
2225
2226 QFontMetrics fm(d->m_model->formatForCell(1, 1).font());
2227 for (int i = 1; i <= end; i++) {
2228 w = qMax(w, fm.horizontalAdvance(QString::number(i)) + marginH);
2229 h = qMax(h, fm.height());
2230 }
2231
2232 if (d->m_view->showGrid()) {
2233 // hardcoded in tableview
2234 w += 1;
2235 h += 1;
2236 }
2237
2238 w += 1; // default column span
2239
2240 h = qMax(h, d->m_view->verticalHeader()->minimumSectionSize());
2241 w = qMax(w, d->m_view->horizontalHeader()->minimumSectionSize());
2242
2243 //add the size of the header.
2244 QSize headerSize(0, 0);
2245 if (d->navBarVisible) {
2246 int headerH = d->navBarBackground->sizeHint().height();
2247 int headerW = 0;
2248
2249 headerW += d->prevMonth->sizeHint().width();
2250 headerW += d->nextMonth->sizeHint().width();
2251
2252 QFontMetrics fm = d->monthButton->fontMetrics();
2253 int monthW = 0;
2254 for (int i = 1; i < 12; i++) {
2255 QString monthName = d->m_model->monthName(locale(), i);
2256 monthW = qMax(monthW, fm.boundingRect(monthName).width());
2257 }
2258 const int buttonDecoMargin = d->monthButton->sizeHint().width() - fm.boundingRect(d->monthButton->text()).width();
2259 headerW += monthW + buttonDecoMargin;
2260
2261 fm = d->yearButton->fontMetrics();
2262 headerW += fm.boundingRect("5555"_L1).width() + buttonDecoMargin;
2263
2264 headerSize = QSize(headerW, headerH);
2265 }
2266 w *= cols;
2267 w = qMax(headerSize.width(), w);
2268 h = (h * rows) + headerSize.height();
2269 QMargins cm = contentsMargins();
2270 w += cm.left() + cm.right();
2271 h += cm.top() + cm.bottom();
2272 d->cachedSizeHint = QSize(w, h);
2273 return d->cachedSizeHint;
2274}
2275
2276/*!
2277 Paints the cell specified by the given \a date, using the given \a painter and \a rect.
2278*/
2279
2280void QCalendarWidget::paintCell(QPainter *painter, const QRect &rect, QDate date) const
2281{
2282 Q_D(const QCalendarWidget);
2283 d->m_delegate->paintCell(painter, rect, date);
2284}
2285
2286/*!
2287 \property QCalendarWidget::selectedDate
2288 \brief the currently selected date.
2289
2290 The selected date must be within the date range specified by the
2291 minimumDate and maximumDate properties. By default, the selected
2292 date is the current date.
2293
2294 \sa setDateRange()
2295*/
2296
2297QDate QCalendarWidget::selectedDate() const
2298{
2299 Q_D(const QCalendarWidget);
2300 return d->m_model->m_date;
2301}
2302
2303void QCalendarWidget::setSelectedDate(QDate date)
2304{
2305 Q_D(QCalendarWidget);
2306 if (d->m_model->m_date == date && date == d->getCurrentDate())
2307 return;
2308
2309 if (!date.isValid())
2310 return;
2311
2312 d->m_model->setDate(date);
2313 d->update();
2314 QDate newDate = d->m_model->m_date;
2315 QCalendar cal = d->m_model->m_calendar;
2316 d->showMonth(newDate.year(cal), newDate.month(cal));
2317 emit selectionChanged();
2318}
2319
2320/*!
2321 Returns the year of the currently displayed month. Months are
2322 numbered from 1 to 12.
2323
2324 \sa monthShown(), setCurrentPage()
2325*/
2326
2327int QCalendarWidget::yearShown() const
2328{
2329 Q_D(const QCalendarWidget);
2330 return d->m_model->m_shownYear;
2331}
2332
2333/*!
2334 Returns the currently displayed month. Months are numbered from 1 to
2335 12.
2336
2337 \sa yearShown(), setCurrentPage()
2338*/
2339
2340int QCalendarWidget::monthShown() const
2341{
2342 Q_D(const QCalendarWidget);
2343 return d->m_model->m_shownMonth;
2344}
2345
2346/*!
2347 Displays the given \a month of the given \a year without changing
2348 the selected date. Use the setSelectedDate() function to alter the
2349 selected date.
2350
2351 The currently displayed month and year can be retrieved using the
2352 monthShown() and yearShown() functions respectively.
2353
2354 \sa yearShown(), monthShown(), showPreviousMonth(), showNextMonth(),
2355 showPreviousYear(), showNextYear()
2356*/
2357
2358void QCalendarWidget::setCurrentPage(int year, int month)
2359{
2360 Q_D(QCalendarWidget);
2361 QDate currentDate = d->getCurrentDate();
2362 QCalendar cal = d->m_model->m_calendar;
2363 int day = currentDate.day(cal);
2364 int daysInMonths = cal.daysInMonth(month, year);
2365 if (day > daysInMonths)
2366 day = daysInMonths;
2367
2368 d->showMonth(year, month);
2369
2370 QDate newDate(year, month, day, d->m_model->m_calendar);
2371 int row = -1, col = -1;
2372 d->m_model->cellForDate(newDate, &row, &col);
2373 if (row != -1 && col != -1) {
2374 d->m_view->selectionModel()->setCurrentIndex(d->m_model->index(row, col),
2375 QItemSelectionModel::NoUpdate);
2376 }
2377}
2378
2379/*!
2380 Shows the next month relative to the currently displayed
2381 month. Note that the selected date is not changed.
2382
2383 \sa showPreviousMonth(), setCurrentPage(), setSelectedDate()
2384*/
2385
2386void QCalendarWidget::showNextMonth()
2387{
2388 Q_D(const QCalendarWidget);
2389 int year = yearShown();
2390 int month = monthShown();
2391 if (month == d->m_model->m_calendar.maximumMonthsInYear()) {
2392 ++year;
2393 month = 1;
2394 } else {
2395 ++month;
2396 }
2397 setCurrentPage(year, month);
2398}
2399
2400/*!
2401 Shows the previous month relative to the currently displayed
2402 month. Note that the selected date is not changed.
2403
2404 \sa showNextMonth(), setCurrentPage(), setSelectedDate()
2405*/
2406
2407void QCalendarWidget::showPreviousMonth()
2408{
2409 Q_D(const QCalendarWidget);
2410
2411 int year = yearShown();
2412 int month = monthShown();
2413 if (month == 1) {
2414 --year;
2415 month = d->m_model->m_calendar.maximumMonthsInYear();
2416 } else {
2417 --month;
2418 }
2419 setCurrentPage(year, month);
2420}
2421
2422/*!
2423 Shows the currently displayed month in the \e next year relative
2424 to the currently displayed year. Note that the selected date is
2425 not changed.
2426
2427 \sa showPreviousYear(), setCurrentPage(), setSelectedDate()
2428*/
2429
2430void QCalendarWidget::showNextYear()
2431{
2432 int year = yearShown();
2433 int month = monthShown();
2434 ++year;
2435 setCurrentPage(year, month);
2436}
2437
2438/*!
2439 Shows the currently displayed month in the \e previous year
2440 relative to the currently displayed year. Note that the selected
2441 date is not changed.
2442
2443 \sa showNextYear(), setCurrentPage(), setSelectedDate()
2444*/
2445
2446void QCalendarWidget::showPreviousYear()
2447{
2448 int year = yearShown();
2449 int month = monthShown();
2450 --year;
2451 setCurrentPage(year, month);
2452}
2453
2454/*!
2455 Shows the month of the selected date.
2456
2457 \sa selectedDate(), setCurrentPage()
2458*/
2459void QCalendarWidget::showSelectedDate()
2460{
2461 Q_D(const QCalendarWidget);
2462
2463 QDate currentDate = selectedDate();
2464 setCurrentPage(currentDate.year(d->m_model->m_calendar), currentDate.month(d->m_model->m_calendar));
2465}
2466
2467/*!
2468 Shows the month of the today's date.
2469
2470 \sa selectedDate(), setCurrentPage()
2471*/
2472void QCalendarWidget::showToday()
2473{
2474 Q_D(const QCalendarWidget);
2475
2476 QDate currentDate = QDate::currentDate();
2477 setCurrentPage(currentDate.year(d->m_model->m_calendar), currentDate.month(d->m_model->m_calendar));
2478}
2479
2480/*!
2481 \property QCalendarWidget::minimumDate
2482 \brief the minimum date of the currently specified date range.
2483
2484 The user will not be able to select a date that is before the
2485 currently set minimum date.
2486
2487 \table
2488 \row
2489 \li \image qcalendarwidget-minimum.png
2490 \row
2491 \li
2492 \snippet code/src_gui_widgets_qcalendarwidget.cpp 1
2493 \endtable
2494
2495 When setting a minimum date, the maximumDate and selectedDate
2496 properties are adjusted if the selection range becomes invalid. If
2497 the provided date is not a valid QDate object, the
2498 setMinimumDate() function does nothing.
2499
2500 The default minimum date is November 25, 4714 BCE.
2501 You can restore this default by calling clearMinimumDate() (since Qt 6.6).
2502
2503 \sa setDateRange()
2504*/
2505
2506QDate QCalendarWidget::minimumDate() const
2507{
2508 Q_D(const QCalendarWidget);
2509 return d->m_model->m_minimumDate;
2510}
2511
2512void QCalendarWidget::setMinimumDate(QDate date)
2513{
2514 Q_D(QCalendarWidget);
2515 if (!date.isValid() || d->m_model->m_minimumDate == date)
2516 return;
2517
2518 QDate oldDate = d->m_model->m_date;
2519 d->m_model->setMinimumDate(date);
2520 d->yearEdit->setMinimum(d->m_model->m_minimumDate.year(d->m_model->m_calendar));
2521 d->updateMonthMenu();
2522 QDate newDate = d->m_model->m_date;
2523 if (oldDate != newDate) {
2524 d->update();
2525 d->showMonth(newDate.year(d->m_model->m_calendar), newDate.month(d->m_model->m_calendar));
2526 d->m_navigator->setDate(newDate);
2527 emit selectionChanged();
2528 }
2529}
2530
2531void QCalendarWidget::clearMinimumDate()
2532{
2533 setMinimumDate(QDate::fromJulianDay(1));
2534}
2535
2536/*!
2537 \property QCalendarWidget::maximumDate
2538 \brief the maximum date of the currently specified date range.
2539
2540 The user will not be able to select a date which is after the
2541 currently set maximum date.
2542
2543 \table
2544 \row
2545 \li \image qcalendarwidget-maximum.png
2546 \row
2547 \li
2548 \snippet code/src_gui_widgets_qcalendarwidget.cpp 2
2549 \endtable
2550
2551 When setting a maximum date, the minimumDate and selectedDate
2552 properties are adjusted if the selection range becomes invalid. If
2553 the provided date is not a valid QDate object, the
2554 setMaximumDate() function does nothing.
2555
2556 The default maximum date is December 31, 9999 CE.
2557 You can restore this default by calling clearMaximumDate() (since Qt 6.6).
2558
2559 \sa setDateRange()
2560*/
2561
2562QDate QCalendarWidget::maximumDate() const
2563{
2564 Q_D(const QCalendarWidget);
2565 return d->m_model->m_maximumDate;
2566}
2567
2568void QCalendarWidget::setMaximumDate(QDate date)
2569{
2570 Q_D(QCalendarWidget);
2571 if (!date.isValid() || d->m_model->m_maximumDate == date)
2572 return;
2573
2574 QDate oldDate = d->m_model->m_date;
2575 d->m_model->setMaximumDate(date);
2576 d->yearEdit->setMaximum(d->m_model->m_maximumDate.year(d->m_model->m_calendar));
2577 d->updateMonthMenu();
2578 QDate newDate = d->m_model->m_date;
2579 if (oldDate != newDate) {
2580 d->update();
2581 d->showMonth(newDate.year(d->m_model->m_calendar), newDate.month(d->m_model->m_calendar));
2582 d->m_navigator->setDate(newDate);
2583 emit selectionChanged();
2584 }
2585}
2586
2587void QCalendarWidget::clearMaximumDate()
2588{
2589 setMaximumDate(QDate(9999, 12, 31));
2590}
2591
2592/*!
2593 Defines a date range by setting the minimumDate and maximumDate
2594 properties.
2595
2596 The date range restricts the user selection, i.e. the user can
2597 only select dates within the specified date range. Note that
2598
2599 \snippet code/src_gui_widgets_qcalendarwidget.cpp 3
2600
2601 is analogous to
2602
2603 \snippet code/src_gui_widgets_qcalendarwidget.cpp 4
2604
2605 If either the \a min or \a max parameters are not valid QDate
2606 objects, this function does nothing.
2607
2608 \sa setMinimumDate(), setMaximumDate()
2609*/
2610
2611void QCalendarWidget::setDateRange(QDate min, QDate max)
2612{
2613 Q_D(QCalendarWidget);
2614 if (d->m_model->m_minimumDate == min && d->m_model->m_maximumDate == max)
2615 return;
2616 if (!min.isValid() || !max.isValid())
2617 return;
2618
2619 QDate oldDate = d->m_model->m_date;
2620 d->m_model->setRange(min, max);
2621 d->yearEdit->setMinimum(d->m_model->m_minimumDate.year(d->m_model->m_calendar));
2622 d->yearEdit->setMaximum(d->m_model->m_maximumDate.year(d->m_model->m_calendar));
2623 d->updateMonthMenu();
2624 QDate newDate = d->m_model->m_date;
2625 if (oldDate != newDate) {
2626 d->update();
2627 d->showMonth(newDate.year(d->m_model->m_calendar), newDate.month(d->m_model->m_calendar));
2628 d->m_navigator->setDate(newDate);
2629 emit selectionChanged();
2630 }
2631}
2632
2633
2634/*! \enum QCalendarWidget::HorizontalHeaderFormat
2635
2636 This enum type defines the various formats the horizontal header can display.
2637
2638 \value SingleLetterDayNames The header displays a single letter abbreviation for day names (e.g. M for Monday).
2639 \value ShortDayNames The header displays a short abbreviation for day names (e.g. Mon for Monday).
2640 \value LongDayNames The header displays complete day names (e.g. Monday).
2641 \value NoHorizontalHeader The header is hidden.
2642
2643 \sa horizontalHeaderFormat(), VerticalHeaderFormat
2644*/
2645
2646/*!
2647 \property QCalendarWidget::horizontalHeaderFormat
2648 \brief the format of the horizontal header.
2649
2650 The default value is QCalendarWidget::ShortDayNames.
2651*/
2652
2653void QCalendarWidget::setHorizontalHeaderFormat(QCalendarWidget::HorizontalHeaderFormat format)
2654{
2655 Q_D(QCalendarWidget);
2656 if (d->m_model->m_horizontalHeaderFormat == format)
2657 return;
2658
2659 d->m_model->setHorizontalHeaderFormat(format);
2660 d->cachedSizeHint = QSize();
2661 d->m_view->viewport()->update();
2662 d->m_view->updateGeometry();
2663}
2664
2665QCalendarWidget::HorizontalHeaderFormat QCalendarWidget::horizontalHeaderFormat() const
2666{
2667 Q_D(const QCalendarWidget);
2668 return d->m_model->m_horizontalHeaderFormat;
2669}
2670
2671
2672/*!
2673 \enum QCalendarWidget::VerticalHeaderFormat
2674
2675 This enum type defines the various formats the vertical header can display.
2676
2677 \value ISOWeekNumbers The header displays ISO week numbers as described by \l QDate::weekNumber().
2678 \value NoVerticalHeader The header is hidden.
2679
2680 \sa verticalHeaderFormat(), HorizontalHeaderFormat
2681*/
2682
2683/*!
2684 \property QCalendarWidget::verticalHeaderFormat
2685 \brief the format of the vertical header.
2686
2687 The default value is QCalendarWidget::ISOWeekNumber.
2688*/
2689
2690QCalendarWidget::VerticalHeaderFormat QCalendarWidget::verticalHeaderFormat() const
2691{
2692 Q_D(const QCalendarWidget);
2693 bool shown = d->m_model->weekNumbersShown();
2694 if (shown)
2695 return QCalendarWidget::ISOWeekNumbers;
2696 return QCalendarWidget::NoVerticalHeader;
2697}
2698
2699void QCalendarWidget::setVerticalHeaderFormat(QCalendarWidget::VerticalHeaderFormat format)
2700{
2701 Q_D(QCalendarWidget);
2702 bool show = false;
2703 if (format == QCalendarWidget::ISOWeekNumbers)
2704 show = true;
2705 if (d->m_model->weekNumbersShown() == show)
2706 return;
2707 d->m_model->setWeekNumbersShown(show);
2708 d->cachedSizeHint = QSize();
2709 d->m_view->viewport()->update();
2710 d->m_view->updateGeometry();
2711}
2712
2713/*!
2714 \property QCalendarWidget::gridVisible
2715 \brief whether the table grid is displayed.
2716
2717 \table
2718 \row
2719 \li \inlineimage qcalendarwidget-grid.png
2720 \row
2721 \li
2722 \snippet code/src_gui_widgets_qcalendarwidget.cpp 5
2723 \endtable
2724
2725 The default value is false.
2726*/
2727
2728bool QCalendarWidget::isGridVisible() const
2729{
2730 Q_D(const QCalendarWidget);
2731 return d->m_view->showGrid();
2732}
2733
2734/*!
2735 \since 5.14
2736 Report the calendar system in use by this widget.
2737
2738 \sa setCalendar()
2739*/
2740
2741QCalendar QCalendarWidget::calendar() const
2742{
2743 Q_D(const QCalendarWidget);
2744 return d->m_model->m_calendar;
2745}
2746
2747/*!
2748 \since 5.14
2749 Set \a c as the calendar system to be used by this widget.
2750
2751 The widget can use any supported calendar system.
2752 By default, it uses the Gregorian calendar.
2753
2754 \sa calendar()
2755*/
2756
2757void QCalendarWidget::setCalendar(QCalendar c)
2758{
2759 Q_D(QCalendarWidget);
2760 d->m_model->setCalendar(c);
2761 d->updateMonthMenuNames();
2762 d->yearEdit->setMinimum(d->m_model->m_minimumDate.year(d->m_model->m_calendar));
2763 d->yearEdit->setMaximum(d->m_model->m_maximumDate.year(d->m_model->m_calendar));
2764 d->updateNavigationBar();
2765}
2766
2767void QCalendarWidget::setGridVisible(bool show)
2768{
2769 Q_D(QCalendarWidget);
2770 d->m_view->setShowGrid(show);
2771 d->cachedSizeHint = QSize();
2772 d->m_view->viewport()->update();
2773 d->m_view->updateGeometry();
2774}
2775
2776/*!
2777 \property QCalendarWidget::selectionMode
2778 \brief the type of selection the user can make in the calendar
2779
2780 When this property is set to SingleSelection, the user can select a date
2781 within the minimum and maximum allowed dates, using either the mouse or
2782 the keyboard.
2783
2784 When the property is set to NoSelection, the user will be unable to select
2785 dates, but they can still be selected programmatically. Note that the date
2786 that is selected when the property is set to NoSelection will still be
2787 the selected date of the calendar.
2788
2789 The default value is SingleSelection.
2790*/
2791
2792QCalendarWidget::SelectionMode QCalendarWidget::selectionMode() const
2793{
2794 Q_D(const QCalendarWidget);
2795 return d->m_view->readOnly ? QCalendarWidget::NoSelection : QCalendarWidget::SingleSelection;
2796}
2797
2798void QCalendarWidget::setSelectionMode(SelectionMode mode)
2799{
2800 Q_D(QCalendarWidget);
2801 d->m_view->readOnly = (mode == QCalendarWidget::NoSelection);
2802 d->setNavigatorEnabled(isDateEditEnabled() && (selectionMode() != QCalendarWidget::NoSelection));
2803 d->update();
2804}
2805
2806/*!
2807 \property QCalendarWidget::firstDayOfWeek
2808 \brief a value identifying the day displayed in the first column.
2809
2810 By default, the day displayed in the first column
2811 is the first day of the week for the calendar's locale.
2812*/
2813
2814void QCalendarWidget::setFirstDayOfWeek(Qt::DayOfWeek dayOfWeek)
2815{
2816 Q_D(QCalendarWidget);
2817 if ((Qt::DayOfWeek)d->m_model->firstColumnDay() == dayOfWeek)
2818 return;
2819
2820 d->m_model->setFirstColumnDay(dayOfWeek);
2821 d->update();
2822}
2823
2824Qt::DayOfWeek QCalendarWidget::firstDayOfWeek() const
2825{
2826 Q_D(const QCalendarWidget);
2827 return (Qt::DayOfWeek)d->m_model->firstColumnDay();
2828}
2829
2830/*!
2831 Returns the text char format for rendering the header.
2832*/
2833QTextCharFormat QCalendarWidget::headerTextFormat() const
2834{
2835 Q_D(const QCalendarWidget);
2836 return d->m_model->m_headerFormat;
2837}
2838
2839/*!
2840 Sets the text char format for rendering the header to \a format.
2841 If you also set a weekday text format, this format's foreground and
2842 background color will take precedence over the header's format.
2843 The other formatting information will still be decided by
2844 the header's format.
2845*/
2846void QCalendarWidget::setHeaderTextFormat(const QTextCharFormat &format)
2847{
2848 Q_D(QCalendarWidget);
2849 d->m_model->m_headerFormat = format;
2850 d->cachedSizeHint = QSize();
2851 d->m_view->viewport()->update();
2852 d->m_view->updateGeometry();
2853}
2854
2855/*!
2856 Returns the text char format for rendering of day in the week \a dayOfWeek.
2857
2858 \sa headerTextFormat()
2859*/
2860QTextCharFormat QCalendarWidget::weekdayTextFormat(Qt::DayOfWeek dayOfWeek) const
2861{
2862 Q_D(const QCalendarWidget);
2863 return d->m_model->m_dayFormats.value(dayOfWeek);
2864}
2865
2866/*!
2867 Sets the text char format for rendering of day in the week \a dayOfWeek to \a format.
2868 The format will take precedence over the header format in case of foreground
2869 and background color. Other text formatting information is taken from the headers format.
2870
2871 \sa setHeaderTextFormat()
2872*/
2873void QCalendarWidget::setWeekdayTextFormat(Qt::DayOfWeek dayOfWeek, const QTextCharFormat &format)
2874{
2875 Q_D(QCalendarWidget);
2876 d->m_model->m_dayFormats[dayOfWeek] = format;
2877 d->cachedSizeHint = QSize();
2878 d->m_view->viewport()->update();
2879 d->m_view->updateGeometry();
2880}
2881
2882/*!
2883 Returns a QMap from QDate to QTextCharFormat showing all dates
2884 that use a special format that alters their rendering.
2885*/
2886QMap<QDate, QTextCharFormat> QCalendarWidget::dateTextFormat() const
2887{
2888 Q_D(const QCalendarWidget);
2889 return d->m_model->m_dateFormats;
2890}
2891
2892/*!
2893 Returns a QTextCharFormat for \a date. The char format can be
2894 empty if the date is not renderd specially.
2895*/
2896QTextCharFormat QCalendarWidget::dateTextFormat(QDate date) const
2897{
2898 Q_D(const QCalendarWidget);
2899 return d->m_model->m_dateFormats.value(date);
2900}
2901
2902/*!
2903 Sets the format used to render the given \a date to that specified by \a format.
2904
2905 If \a date is null, all date formats are cleared.
2906*/
2907void QCalendarWidget::setDateTextFormat(QDate date, const QTextCharFormat &format)
2908{
2909 Q_D(QCalendarWidget);
2910 if (date.isNull())
2911 d->m_model->m_dateFormats.clear();
2912 else
2913 d->m_model->m_dateFormats[date] = format;
2914 d->m_view->viewport()->update();
2915 d->m_view->updateGeometry();
2916}
2917
2918/*!
2919 \property QCalendarWidget::dateEditEnabled
2920 \brief whether the date edit popup is enabled
2921 \since 4.3
2922
2923 If this property is enabled, pressing a non-modifier key will cause a
2924 date edit to popup if the calendar widget has focus, allowing the user
2925 to specify a date in the form specified by the current locale.
2926
2927 By default, this property is enabled.
2928
2929 The date edit is simpler in appearance than QDateEdit, but allows the
2930 user to navigate between fields using the left and right cursor keys,
2931 increment and decrement individual fields using the up and down cursor
2932 keys, and enter values directly using the number keys.
2933
2934 \sa QCalendarWidget::dateEditAcceptDelay
2935*/
2936bool QCalendarWidget::isDateEditEnabled() const
2937{
2938 Q_D(const QCalendarWidget);
2939 return d->m_dateEditEnabled;
2940}
2941
2942void QCalendarWidget::setDateEditEnabled(bool enable)
2943{
2944 Q_D(QCalendarWidget);
2945 if (isDateEditEnabled() == enable)
2946 return;
2947
2948 d->m_dateEditEnabled = enable;
2949
2950 d->setNavigatorEnabled(enable && (selectionMode() != QCalendarWidget::NoSelection));
2951}
2952
2953/*!
2954 \property QCalendarWidget::dateEditAcceptDelay
2955 \brief the time an inactive date edit is shown before its contents are accepted
2956 \since 4.3
2957
2958 If the calendar widget's \l{dateEditEnabled}{date edit is enabled}, this
2959 property specifies the amount of time (in milliseconds) that the date edit
2960 remains open after the most recent user input. Once this time has elapsed,
2961 the date specified in the date edit is accepted and the popup is closed.
2962
2963 By default, the delay is defined to be 1500 milliseconds (1.5 seconds).
2964*/
2965int QCalendarWidget::dateEditAcceptDelay() const
2966{
2967 Q_D(const QCalendarWidget);
2968 return d->m_navigator->dateEditAcceptDelay();
2969}
2970
2971void QCalendarWidget::setDateEditAcceptDelay(int delay)
2972{
2973 Q_D(QCalendarWidget);
2974 d->m_navigator->setDateEditAcceptDelay(delay);
2975}
2976
2977/*!
2978 \since 4.4
2979
2980 Updates the cell specified by the given \a date unless updates
2981 are disabled or the cell is hidden.
2982
2983 \sa updateCells(), yearShown(), monthShown()
2984*/
2985void QCalendarWidget::updateCell(QDate date)
2986{
2987 if (Q_UNLIKELY(!date.isValid())) {
2988 qWarning("QCalendarWidget::updateCell: Invalid date");
2989 return;
2990 }
2991
2992 if (!isVisible())
2993 return;
2994
2995 Q_D(QCalendarWidget);
2996 int row, column;
2997 d->m_model->cellForDate(date, &row, &column);
2998 if (row == -1 || column == -1)
2999 return;
3000
3001 QModelIndex modelIndex = d->m_model->index(row, column);
3002 if (!modelIndex.isValid())
3003 return;
3004
3005 d->m_view->viewport()->update(d->m_view->visualRect(modelIndex));
3006}
3007
3008/*!
3009 \since 4.4
3010
3011 Updates all visible cells unless updates are disabled.
3012
3013 \sa updateCell()
3014*/
3015void QCalendarWidget::updateCells()
3016{
3017 Q_D(QCalendarWidget);
3018 if (isVisible())
3019 d->m_view->viewport()->update();
3020}
3021
3022/*!
3023 \fn void QCalendarWidget::selectionChanged()
3024
3025 This signal is emitted when the currently selected date is
3026 changed.
3027
3028 The currently selected date can be changed by the user using the
3029 mouse or keyboard, or by the programmer using setSelectedDate().
3030
3031 \sa selectedDate()
3032*/
3033
3034/*!
3035 \fn void QCalendarWidget::currentPageChanged(int year, int month)
3036
3037 This signal is emitted when the currently shown month is changed.
3038 The new \a year and \a month are passed as parameters.
3039
3040 \sa setCurrentPage()
3041*/
3042
3043/*!
3044 \fn void QCalendarWidget::activated(QDate date)
3045
3046 This signal is emitted whenever the user presses the Return or
3047 Enter key or double-clicks a \a date in the calendar
3048 widget.
3049*/
3050
3051/*!
3052 \fn void QCalendarWidget::clicked(QDate date)
3053
3054 This signal is emitted when a mouse button is clicked. The date
3055 the mouse was clicked on is specified by \a date. The signal is
3056 only emitted when clicked on a valid date, e.g., dates are not
3057 outside the minimumDate() and maximumDate(). If the selection mode
3058 is NoSelection, this signal will not be emitted.
3059
3060*/
3061
3062/*!
3063 \property QCalendarWidget::navigationBarVisible
3064 \brief whether the navigation bar is shown or not
3065
3066 \since 4.3
3067
3068 When this property is \c true (the default), the next month,
3069 previous month, month selection, year selection controls are
3070 shown on top.
3071
3072 When the property is set to false, these controls are hidden.
3073*/
3074
3075bool QCalendarWidget::isNavigationBarVisible() const
3076{
3077 Q_D(const QCalendarWidget);
3078 return d->navBarVisible;
3079}
3080
3081
3082void QCalendarWidget::setNavigationBarVisible(bool visible)
3083{
3084 Q_D(QCalendarWidget);
3085 d->navBarVisible = visible;
3086 d->cachedSizeHint = QSize();
3087 d->navBarBackground->setVisible(visible);
3088 updateGeometry();
3089}
3090
3091/*!
3092 \reimp
3093*/
3094bool QCalendarWidget::event(QEvent *event)
3095{
3096 Q_D(QCalendarWidget);
3097 switch (event->type()) {
3098 case QEvent::LayoutDirectionChange:
3099 d->updateButtonIcons();
3100 break;
3101 case QEvent::LocaleChange:
3102 d->m_model->setFirstColumnDay(locale().firstDayOfWeek());
3103 d->cachedSizeHint = QSize();
3104 d->updateMonthMenuNames();
3105 d->updateNavigationBar();
3106 d->m_view->updateGeometry();
3107 // TODO: fix this known bug of calendaring API:
3108 // Changing locale before calendar works, but reverse order causes
3109 // invalid month names (in C Locale apparently).
3110 break;
3111 case QEvent::FontChange:
3112 case QEvent::ApplicationFontChange:
3113 d->cachedSizeHint = QSize();
3114 d->m_view->updateGeometry();
3115 break;
3116 case QEvent::StyleChange:
3117 d->cachedSizeHint = QSize();
3118 d->m_view->updateGeometry();
3119 break;
3120 default:
3121 break;
3122 }
3123 return QWidget::event(event);
3124}
3125
3126/*!
3127 \reimp
3128*/
3129bool QCalendarWidget::eventFilter(QObject *watched, QEvent *event)
3130{
3131 Q_D(QCalendarWidget);
3132 if (event->type() == QEvent::MouseButtonPress && d->yearEdit->hasFocus()) {
3133 // We can get filtered press events that were intended for Qt Virtual Keyboard's
3134 // input panel (QQuickView), so we have to make sure that the window is indeed a QWidget - no static_cast.
3135 // In addition, as we have a event filter on the whole application we first make sure that the top level widget
3136 // of both this and the watched widget are the same to decide if we should finish the year edition.
3137 QWidget *tlw = window();
3138 QWidget *widget = qobject_cast<QWidget *>(watched);
3139 if (!widget || widget->window() != tlw)
3140 return QWidget::eventFilter(watched, event);
3141
3142 QPoint mousePos = widget->mapTo(tlw, static_cast<QMouseEvent *>(event)->position().toPoint());
3143 QRect geom = QRect(d->yearEdit->mapTo(tlw, QPoint(0, 0)), d->yearEdit->size());
3144 if (!geom.contains(mousePos)) {
3145 event->accept();
3146 d->_q_yearEditingFinished();
3147 setFocus();
3148 return true;
3149 }
3150 }
3151 return QWidget::eventFilter(watched, event);
3152}
3153
3154/*!
3155 \reimp
3156*/
3157void QCalendarWidget::mousePressEvent(QMouseEvent *event)
3158{
3159 setAttribute(Qt::WA_NoMouseReplay);
3160 QWidget::mousePressEvent(event);
3161 setFocus();
3162}
3163
3164/*!
3165 \reimp
3166*/
3167void QCalendarWidget::resizeEvent(QResizeEvent * event)
3168{
3169 Q_D(QCalendarWidget);
3170
3171 // XXX Should really use a QWidgetStack for yearEdit and yearButton,
3172 // XXX here we hide the year edit when the layout is likely to break
3173 // XXX the manual positioning of the yearEdit over the yearButton.
3174 if (d->yearEdit->isVisible() && event->size().width() != event->oldSize().width())
3175 d->_q_yearEditingFinished();
3176
3177 QWidget::resizeEvent(event);
3178}
3179
3180/*!
3181 \reimp
3182*/
3183void QCalendarWidget::keyPressEvent(QKeyEvent * event)
3184{
3185#if QT_CONFIG(shortcut)
3186 Q_D(QCalendarWidget);
3187 if (d->yearEdit->isVisible()&& event->matches(QKeySequence::Cancel)) {
3188 d->yearEdit->setValue(yearShown());
3189 d->_q_yearEditingFinished();
3190 return;
3191 }
3192#endif
3193 QWidget::keyPressEvent(event);
3194}
3195
3196QT_END_NAMESPACE
3197
3198#include "qcalendarwidget.moc"
3199#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
\macro QT_NO_KEYWORDS >
Definition qcompare.h:24
static QString formatNumber(int number, int fieldWidth)
Q_DECLARE_TYPEINFO(QtPrivate::SectionToken, Q_PRIMITIVE_TYPE)
@ RowCount
@ MinimumDayOffset
@ HeaderRow
@ HeaderColumn
@ ColumnCount
#define qApp
constexpr SectionToken(QCalendarDateSectionValidator *v, int rep)
QCalendarDateSectionValidator * validator