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