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 clicked(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#ifdef QT_KEYPAD_NAVIGATION
934#endif
935};
936
937QCalendarModel::QCalendarModel(QObject *parent)
938 : QAbstractTableModel(parent),
939 m_firstColumn(1),
940 m_firstRow(1),
941 m_date(QDate::currentDate()),
942 m_minimumDate(QDate::fromJulianDay(1)),
943 m_maximumDate(9999, 12, 31),
944 m_shownYear(m_date.year(m_calendar)),
945 m_shownMonth(m_date.month(m_calendar)),
946 m_firstDay(QLocale().firstDayOfWeek()),
947 m_horizontalHeaderFormat(QCalendarWidget::ShortDayNames),
948 m_weekNumbersShown(true),
949 m_view(nullptr)
950{
951}
952
954{
955 int col = column - m_firstColumn;
956 if (col < 0 || col > 6)
957 return Qt::Sunday;
958 int day = m_firstDay + col;
959 if (day > 7)
960 day -= 7;
961 return Qt::DayOfWeek(day);
962}
963
964int QCalendarModel::columnForDayOfWeek(Qt::DayOfWeek day) const
965{
966 if (day < 1 || unsigned(day) > unsigned(7))
967 return -1;
968 int column = (int)day - (int)m_firstDay;
969 if (column < 0)
970 column += 7;
971 return column + m_firstColumn;
972}
973
974/*
975This simple algorithm tries to generate a valid date from the month shown.
976Some months don't contain a first day (e.g. Jan of -4713 year,
977so QDate (-4713, 1, 1) would be invalid). In that case we try to generate
978another valid date for that month. Later, returned date's day is the number of cells
979calendar widget will reserve for days before referenceDate. (E.g. if returned date's
980day is 16, that day will be placed in 3rd or 4th row, not in the 1st or 2nd row).
981Depending on referenceData we can change behaviour of Oct 1582. If referenceDate is 1st
982of Oct we render 1 Oct in 1st or 2nd row. If referenceDate is 17 of Oct we show always 16
983dates before 17 of Oct, and since this month contains the hole 5-14 Oct, the first of Oct
984will be rendered in 2nd or 3rd row, showing more dates from previous month.
985*/
987{
988 // TODO: Check this
989 int refDay = 1;
990 while (refDay <= 31) {
991 QDate refDate(m_shownYear, m_shownMonth, refDay, m_calendar);
992 if (refDate.isValid())
993 return refDate;
994 refDay += 1;
995 }
996 return QDate();
997}
998
1000{
1001 return (columnForDayOfWeek(static_cast<Qt::DayOfWeek>(m_calendar.dayOfWeek(date)))
1002 - (date.day(m_calendar) % 7) + 8) % 7;
1003}
1004
1005QDate QCalendarModel::dateForCell(int row, int column) const
1006{
1007 if (row < m_firstRow || row > m_firstRow + RowCount - 1 ||
1008 column < m_firstColumn || column > m_firstColumn + ColumnCount - 1)
1009 return QDate();
1010 const QDate refDate = referenceDate();
1011 if (!refDate.isValid())
1012 return QDate();
1013
1014 const int columnForFirstOfShownMonth = columnForFirstOfMonth(refDate);
1015 if (columnForFirstOfShownMonth - m_firstColumn < MinimumDayOffset)
1016 row -= 1;
1017
1018 const int requestedDay =
1019 7 * (row - m_firstRow) + column - columnForFirstOfShownMonth - refDate.day(m_calendar) + 1;
1020 return refDate.addDays(requestedDay);
1021}
1022
1023void QCalendarModel::cellForDate(QDate date, int *row, int *column) const
1024{
1025 if (!row && !column)
1026 return;
1027
1028 if (row)
1029 *row = -1;
1030 if (column)
1031 *column = -1;
1032
1033 const QDate refDate = referenceDate();
1034 if (!refDate.isValid())
1035 return;
1036
1037 const int columnForFirstOfShownMonth = columnForFirstOfMonth(refDate);
1038 const int requestedPosition = (refDate.daysTo(date) - m_firstColumn +
1039 columnForFirstOfShownMonth + refDate.day(m_calendar) - 1);
1040
1041 int c = requestedPosition % 7;
1042 int r = requestedPosition / 7;
1043 if (c < 0) {
1044 c += 7;
1045 r -= 1;
1046 }
1047
1048 if (columnForFirstOfShownMonth - m_firstColumn < MinimumDayOffset)
1049 r += 1;
1050
1051 if (r < 0 || r > RowCount - 1 || c < 0 || c > ColumnCount - 1)
1052 return;
1053
1054 if (row)
1055 *row = r + m_firstRow;
1056 if (column)
1057 *column = c + m_firstColumn;
1058}
1059
1060QString QCalendarModel::dayName(Qt::DayOfWeek day) const
1061{
1062 switch (m_horizontalHeaderFormat) {
1063 case QCalendarWidget::SingleLetterDayNames: {
1064 QString standaloneDayName = m_view->locale().standaloneDayName(day, QLocale::NarrowFormat);
1065 if (standaloneDayName == m_view->locale().dayName(day, QLocale::NarrowFormat))
1066 return standaloneDayName.left(1);
1067 return standaloneDayName;
1068 }
1069 case QCalendarWidget::ShortDayNames:
1070 return m_view->locale().dayName(day, QLocale::ShortFormat);
1071 case QCalendarWidget::LongDayNames:
1072 return m_view->locale().dayName(day, QLocale::LongFormat);
1073 default:
1074 break;
1075 }
1076 return QString();
1077}
1078
1079QTextCharFormat QCalendarModel::formatForCell(int row, int col) const
1080{
1081 QPalette pal;
1082 QPalette::ColorGroup cg = QPalette::Active;
1083 QTextCharFormat format;
1084
1085 if (m_view) {
1086 pal = m_view->palette();
1087 if (!m_view->isEnabled())
1088 cg = QPalette::Disabled;
1089 else if (!m_view->isActiveWindow())
1090 cg = QPalette::Inactive;
1091 format.setFont(m_view->font());
1092 }
1093
1094 bool header = (m_weekNumbersShown && col == HeaderColumn)
1095 || (m_horizontalHeaderFormat != QCalendarWidget::NoHorizontalHeader && row == HeaderRow);
1096 format.setBackground(pal.brush(cg, header ? QPalette::AlternateBase : QPalette::Base));
1097 format.setForeground(pal.brush(cg, QPalette::Text));
1098 if (header) {
1099 format.merge(m_headerFormat);
1100 }
1101
1102 if (col >= m_firstColumn && col < m_firstColumn + ColumnCount) {
1103 Qt::DayOfWeek dayOfWeek = dayOfWeekForColumn(col);
1104 if (m_dayFormats.contains(dayOfWeek))
1105 format.merge(m_dayFormats.value(dayOfWeek));
1106 }
1107
1108 if (!header) {
1109 QDate date = dateForCell(row, col);
1110 format.merge(m_dateFormats.value(date));
1111 if (date < m_minimumDate || date > m_maximumDate)
1112 format.setBackground(pal.brush(cg, QPalette::Window));
1113 if (m_shownMonth != date.month(m_calendar))
1114 format.setForeground(pal.brush(QPalette::Disabled, QPalette::Text));
1115 }
1116 return format;
1117}
1118
1119QVariant QCalendarModel::data(const QModelIndex &index, int role) const
1120{
1121 if (role == Qt::TextAlignmentRole)
1122 return (int) Qt::AlignCenter;
1123
1124 int row = index.row();
1125 int column = index.column();
1126
1127 if (role == Qt::DisplayRole) {
1128 if (m_weekNumbersShown && column == HeaderColumn
1129 && row >= m_firstRow && row < m_firstRow + RowCount) {
1130 QDate date = dateForCell(row, columnForDayOfWeek(Qt::Monday));
1131 if (date.isValid())
1132 return date.weekNumber();
1133 }
1134 if (m_horizontalHeaderFormat != QCalendarWidget::NoHorizontalHeader && row == HeaderRow
1135 && column >= m_firstColumn && column < m_firstColumn + ColumnCount)
1136 return dayName(dayOfWeekForColumn(column));
1137 QDate date = dateForCell(row, column);
1138 if (date.isValid())
1139 return date.day(m_calendar);
1140 return QString();
1141 }
1142
1143 QTextCharFormat fmt = formatForCell(row, column);
1144 if (role == Qt::BackgroundRole)
1145 return fmt.background().color();
1146 if (role == Qt::ForegroundRole)
1147 return fmt.foreground().color();
1148 if (role == Qt::FontRole)
1149 return fmt.font();
1150 if (role == Qt::ToolTipRole)
1151 return fmt.toolTip();
1152 return QVariant();
1153}
1154
1156{
1157 QDate date = dateForCell(index.row(), index.column());
1158 if (!date.isValid())
1159 return QAbstractTableModel::flags(index);
1160 if (date < m_minimumDate)
1161 return { };
1162 if (date > m_maximumDate)
1163 return { };
1164 return QAbstractTableModel::flags(index);
1165}
1166
1168{
1169 m_date = d;
1170 if (m_date < m_minimumDate)
1171 m_date = m_minimumDate;
1172 else if (m_date > m_maximumDate)
1173 m_date = m_maximumDate;
1174}
1175
1176void QCalendarModel::setCalendar(QCalendar c)
1177{
1178 m_calendar = c;
1179 m_shownYear = m_date.year(c);
1180 m_shownMonth = m_date.month(c);
1183}
1184
1186{
1187 return m_calendar;
1188}
1189
1190void QCalendarModel::showMonth(int year, int month)
1191{
1192 if (m_shownYear == year && m_shownMonth == month)
1193 return;
1194
1195 m_shownYear = year;
1196 m_shownMonth = month;
1197
1199}
1200
1202{
1203 if (!d.isValid() || d == m_minimumDate)
1204 return;
1205
1206 m_minimumDate = d;
1207 if (m_maximumDate < m_minimumDate)
1208 m_maximumDate = m_minimumDate;
1209 if (m_date < m_minimumDate)
1210 m_date = m_minimumDate;
1212}
1213
1215{
1216 if (!d.isValid() || d == m_maximumDate)
1217 return;
1218
1219 m_maximumDate = d;
1220 if (m_minimumDate > m_maximumDate)
1221 m_minimumDate = m_maximumDate;
1222 if (m_date > m_maximumDate)
1223 m_date = m_maximumDate;
1225}
1226
1227void QCalendarModel::setRange(QDate min, QDate max)
1228{
1229 m_minimumDate = min;
1230 m_maximumDate = max;
1231 if (m_minimumDate > m_maximumDate)
1232 qSwap(m_minimumDate, m_maximumDate);
1233 if (m_date < m_minimumDate)
1234 m_date = m_minimumDate;
1235 if (m_date > m_maximumDate)
1236 m_date = m_maximumDate;
1238}
1239
1241{
1242 QModelIndex begin = index(0, 0);
1243 QModelIndex end = index(m_firstRow + RowCount - 1, m_firstColumn + ColumnCount - 1);
1244 emit dataChanged(begin, end);
1245 emit headerDataChanged(Qt::Vertical, 0, m_firstRow + RowCount - 1);
1246 emit headerDataChanged(Qt::Horizontal, 0, m_firstColumn + ColumnCount - 1);
1247}
1248
1249void QCalendarModel::setHorizontalHeaderFormat(QCalendarWidget::HorizontalHeaderFormat format)
1250{
1251 if (m_horizontalHeaderFormat == format)
1252 return;
1253
1254 int oldFormat = m_horizontalHeaderFormat;
1255 m_horizontalHeaderFormat = format;
1256 if (oldFormat == QCalendarWidget::NoHorizontalHeader) {
1257 beginInsertRows(QModelIndex(), 0, 0);
1258 m_firstRow = 1;
1259 endInsertRows();
1260 } else if (m_horizontalHeaderFormat == QCalendarWidget::NoHorizontalHeader) {
1261 beginRemoveRows(QModelIndex(), 0, 0);
1262 m_firstRow = 0;
1263 endRemoveRows();
1264 }
1266}
1267
1268void QCalendarModel::setFirstColumnDay(Qt::DayOfWeek dayOfWeek)
1269{
1270 if (m_firstDay == dayOfWeek)
1271 return;
1272
1273 m_firstDay = dayOfWeek;
1275}
1276
1278{
1279 return m_firstDay;
1280}
1281
1283{
1284 return m_weekNumbersShown;
1285}
1286
1288{
1289 if (m_weekNumbersShown == show)
1290 return;
1291
1292 m_weekNumbersShown = show;
1293 if (show) {
1294 beginInsertColumns(QModelIndex(), 0, 0);
1295 m_firstColumn = 1;
1296 endInsertColumns();
1297 } else {
1298 beginRemoveColumns(QModelIndex(), 0, 0);
1299 m_firstColumn = 0;
1300 endRemoveColumns();
1301 }
1303}
1304
1305QCalendarView::QCalendarView(QWidget *parent)
1306 : QTableView(parent),
1307 readOnly(false),
1308 validDateClicked(false)
1309{
1310 setTabKeyNavigation(false);
1311 setShowGrid(false);
1312 verticalHeader()->setVisible(false);
1313 horizontalHeader()->setVisible(false);
1314 setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1315 setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1316}
1317
1318QModelIndex QCalendarView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers)
1319{
1320 QCalendarModel *calendarModel = qobject_cast<QCalendarModel *>(model());
1321 if (!calendarModel)
1322 return QTableView::moveCursor(cursorAction, modifiers);
1323
1324 QCalendar cal = calendarModel->calendar();
1325
1326 if (readOnly)
1327 return currentIndex();
1328
1329 QModelIndex index = currentIndex();
1330 QDate currentDate = static_cast<QCalendarModel*>(model())->dateForCell(index.row(), index.column());
1331 switch (cursorAction) {
1332 case QAbstractItemView::MoveUp:
1333 currentDate = currentDate.addDays(-7);
1334 break;
1335 case QAbstractItemView::MoveDown:
1336 currentDate = currentDate.addDays(7);
1337 break;
1338 case QAbstractItemView::MoveLeft:
1339 currentDate = currentDate.addDays(isRightToLeft() ? 1 : -1);
1340 break;
1341 case QAbstractItemView::MoveRight:
1342 currentDate = currentDate.addDays(isRightToLeft() ? -1 : 1);
1343 break;
1344 case QAbstractItemView::MoveHome: {
1345 auto parts = cal.partsFromDate(currentDate);
1346 if (parts.isValid()) {
1347 parts.day = 1;
1348 currentDate = cal.dateFromParts(parts);
1349 }
1350 }
1351 break;
1352 case QAbstractItemView::MoveEnd: {
1353 auto parts = cal.partsFromDate(currentDate);
1354 if (parts.isValid()) {
1355 parts.day = cal.daysInMonth(parts.month, parts.year);
1356 currentDate = cal.dateFromParts(parts);
1357 }
1358 }
1359 break;
1360 case QAbstractItemView::MovePageUp:
1361 currentDate = currentDate.addMonths(-1, cal);
1362 break;
1363 case QAbstractItemView::MovePageDown:
1364 currentDate = currentDate.addMonths(1, cal);
1365 break;
1366 case QAbstractItemView::MoveNext:
1367 case QAbstractItemView::MovePrevious:
1368 return currentIndex();
1369 default:
1370 break;
1371 }
1372 emit changeDate(currentDate, true);
1373 return currentIndex();
1374}
1375
1376void QCalendarView::keyPressEvent(QKeyEvent *event)
1377{
1378#ifdef QT_KEYPAD_NAVIGATION
1379 if (event->key() == Qt::Key_Select) {
1381 if (!hasEditFocus()) {
1382 setEditFocus(true);
1383 return;
1384 }
1385 }
1386 } else if (event->key() == Qt::Key_Back) {
1388 if (qobject_cast<QCalendarModel *>(model())) {
1389 emit changeDate(origDate, true); //changes selection back to origDate, but doesn't activate
1390 setEditFocus(false);
1391 return;
1392 }
1393 }
1394 }
1395#endif
1396
1397 if (!readOnly) {
1398 switch (event->key()) {
1399 case Qt::Key_Return:
1400 case Qt::Key_Enter:
1401 case Qt::Key_Select:
1403 return;
1404 default:
1405 break;
1406 }
1407 }
1409}
1410
1411#if QT_CONFIG(wheelevent)
1413{
1414 const int numDegrees = event->angleDelta().y() / 8;
1415 const int numSteps = numDegrees / 15;
1416 const QModelIndex index = currentIndex();
1421}
1422#endif
1423
1424bool QCalendarView::event(QEvent *event)
1425{
1426#ifdef QT_KEYPAD_NAVIGATION
1427 if (event->type() == QEvent::FocusIn) {
1428 if (QCalendarModel *calendarModel = qobject_cast<QCalendarModel *>(model())) {
1429 origDate = calendarModel->m_date;
1430 }
1431 }
1432#endif
1433
1434 return QTableView::event(event);
1435}
1436
1437QDate QCalendarView::handleMouseEvent(QMouseEvent *event)
1438{
1439 QCalendarModel *calendarModel = qobject_cast<QCalendarModel *>(model());
1440 if (!calendarModel)
1441 return QDate();
1442
1443 QPoint pos = event->position().toPoint();
1444 QModelIndex index = indexAt(pos);
1445 QDate date = calendarModel->dateForCell(index.row(), index.column());
1446 if (date.isValid() && date >= calendarModel->m_minimumDate
1447 && date <= calendarModel->m_maximumDate) {
1448 return date;
1449 }
1450 return QDate();
1451}
1452
1453void QCalendarView::mouseDoubleClickEvent(QMouseEvent *event)
1454{
1455 QCalendarModel *calendarModel = qobject_cast<QCalendarModel *>(model());
1456 if (!calendarModel) {
1457 QTableView::mouseDoubleClickEvent(event);
1458 return;
1459 }
1460
1461 if (readOnly)
1462 return;
1463
1464 QDate date = handleMouseEvent(event);
1465 validDateClicked = false;
1466 if (date == calendarModel->m_date &&
1467 !style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, nullptr, this)) {
1468 emit editingFinished();
1469 }
1470}
1471
1472void QCalendarView::mousePressEvent(QMouseEvent *event)
1473{
1474 QCalendarModel *calendarModel = qobject_cast<QCalendarModel *>(model());
1475 if (!calendarModel) {
1476 QTableView::mousePressEvent(event);
1477 return;
1478 }
1479
1480 if (readOnly)
1481 return;
1482
1483 if (event->button() != Qt::LeftButton)
1484 return;
1485
1486 QDate date = handleMouseEvent(event);
1487 if (date.isValid()) {
1488 validDateClicked = true;
1489 int row = -1, col = -1;
1490 static_cast<QCalendarModel *>(model())->cellForDate(date, &row, &col);
1491 if (row != -1 && col != -1) {
1492 selectionModel()->setCurrentIndex(model()->index(row, col), QItemSelectionModel::NoUpdate);
1493 }
1494 } else {
1495 validDateClicked = false;
1496 event->ignore();
1497 }
1498}
1499
1500void QCalendarView::mouseMoveEvent(QMouseEvent *event)
1501{
1502 QCalendarModel *calendarModel = qobject_cast<QCalendarModel *>(model());
1503 if (!calendarModel) {
1504 QTableView::mouseMoveEvent(event);
1505 return;
1506 }
1507
1508 if (readOnly)
1509 return;
1510
1511 if (validDateClicked) {
1512 QDate date = handleMouseEvent(event);
1513 if (date.isValid()) {
1514 int row = -1, col = -1;
1515 static_cast<QCalendarModel *>(model())->cellForDate(date, &row, &col);
1516 if (row != -1 && col != -1) {
1517 selectionModel()->setCurrentIndex(model()->index(row, col), QItemSelectionModel::NoUpdate);
1518 }
1519 }
1520 } else {
1521 event->ignore();
1522 }
1523}
1524
1525void QCalendarView::mouseReleaseEvent(QMouseEvent *event)
1526{
1527 QCalendarModel *calendarModel = qobject_cast<QCalendarModel *>(model());
1528 if (!calendarModel) {
1529 QTableView::mouseReleaseEvent(event);
1530 return;
1531 }
1532
1533 if (event->button() != Qt::LeftButton)
1534 return;
1535
1536 if (readOnly)
1537 return;
1538
1539 if (validDateClicked) {
1540 QDate date = handleMouseEvent(event);
1541 if (date.isValid()) {
1542 emit changeDate(date, true);
1543 emit clicked(date);
1544 if (style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, nullptr, this))
1545 emit editingFinished();
1546 }
1547 validDateClicked = false;
1548 } else {
1549 event->ignore();
1550 }
1551}
1552
1554{
1555 Q_OBJECT
1556public:
1561 const QModelIndex &index) const override;
1562 void paintCell(QPainter *painter, const QRect &rect, QDate date) const;
1563
1564private:
1565 QCalendarWidgetPrivate *calendarWidgetPrivate;
1566 mutable QStyleOptionViewItem storedOption;
1567};
1568
1569//Private tool button class
1571{
1572public:
1573 QCalToolButton(QWidget * parent)
1575 { }
1576protected:
1577 void paintEvent(QPaintEvent *e) override
1578 {
1579 Q_UNUSED(e);
1580
1581 QStylePainter painter(this);
1582 QStyleOptionToolButton opt;
1583 initStyleOption(&opt);
1584
1585 // set the highlight color for button text so it remains legible on top of navBarBackground
1586 if (!opt.state.testFlag(QStyle::State_MouseOver) || isDown())
1587 opt.palette.setColor(QPalette::ButtonText, opt.palette.color(QPalette::HighlightedText));
1588
1589 painter.drawComplexControl(QStyle::CC_ToolButton, opt);
1590 }
1591};
1592
1607
1608} // namespace QtPrivate
1609
1622
1624{
1625 Q_DECLARE_PUBLIC(QCalendarWidget)
1626public:
1628
1629 void showMonth(int year, int month);
1630 void update();
1631 void paintCell(QPainter *painter, const QRect &rect, QDate date) const;
1632
1633 void _q_slotShowDate(QDate date);
1634 void _q_slotChangeDate(QDate date);
1635 void _q_slotChangeDate(QDate date, bool changeMonth);
1637 void _q_monthChanged(QAction*);
1642
1643 void createNavigationBar(QWidget *widget);
1648 void updateCurrentPage(QDate newDate);
1649 inline QDate getCurrentDate();
1650 void setNavigatorEnabled(bool enable);
1651
1658
1663 QMap<int, QAction *> monthToAction;
1668
1670 mutable QSize cachedSizeHint;
1672};
1673
1674void QCalendarDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
1675 const QModelIndex &index) const
1676{
1677 QDate date = calendarWidgetPrivate->m_model->dateForCell(index.row(), index.column());
1678 if (date.isValid()) {
1679 storedOption = option;
1680 QRect rect = option.rect;
1681 calendarWidgetPrivate->paintCell(painter, rect, date);
1682 } else {
1683 QStyledItemDelegate::paint(painter, option, index);
1684 }
1685}
1686
1687void QCalendarDelegate::paintCell(QPainter *painter, const QRect &rect, QDate date) const
1688{
1689 storedOption.rect = rect;
1690 int row = -1;
1691 int col = -1;
1692 calendarWidgetPrivate->m_model->cellForDate(date, &row, &col);
1693 QModelIndex idx = calendarWidgetPrivate->m_model->index(row, col);
1694 QStyledItemDelegate::paint(painter, storedOption, idx);
1695}
1696
1697QCalendarWidgetPrivate::QCalendarWidgetPrivate()
1698 : QWidgetPrivate()
1699{
1700 m_model = nullptr;
1701 m_view = nullptr;
1702 m_delegate = nullptr;
1703 m_selection = nullptr;
1704 m_navigator = nullptr;
1705 m_dateEditEnabled = false;
1706 navBarVisible = true;
1707 oldFocusPolicy = Qt::StrongFocus;
1708}
1709
1711{
1712 Q_Q(QCalendarWidget);
1713
1714 bool navigatorEnabled = (m_navigator->widget() != nullptr);
1715 if (enable == navigatorEnabled)
1716 return;
1717
1718 if (enable) {
1719 m_navigator->setWidget(q);
1720 q->connect(m_navigator, SIGNAL(dateChanged(QDate)),
1721 q, SLOT(_q_slotChangeDate(QDate)));
1722 q->connect(m_navigator, SIGNAL(editingFinished()),
1723 q, SLOT(_q_editingFinished()));
1724 m_view->installEventFilter(m_navigator);
1725 } else {
1726 m_navigator->setWidget(nullptr);
1727 q->disconnect(m_navigator, SIGNAL(dateChanged(QDate)),
1728 q, SLOT(_q_slotChangeDate(QDate)));
1729 q->disconnect(m_navigator, SIGNAL(editingFinished()),
1730 q, SLOT(_q_editingFinished()));
1731 m_view->removeEventFilter(m_navigator);
1732 }
1733}
1734
1736{
1737 Q_Q(QCalendarWidget);
1738 navBarBackground = new QWidget(widget);
1739 navBarBackground->setObjectName("qt_calendar_navigationbar"_L1);
1740 navBarBackground->setAutoFillBackground(true);
1741 navBarBackground->setBackgroundRole(QPalette::Highlight);
1742
1743 prevMonth = new QPrevNextCalButton(navBarBackground);
1744 nextMonth = new QPrevNextCalButton(navBarBackground);
1745 prevMonth->setAutoRaise(true);
1746 nextMonth->setAutoRaise(true);
1747 prevMonth->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum);
1748 nextMonth->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum);
1749 nextMonth->setAutoRaise(true);
1751 prevMonth->setAutoRepeat(true);
1752 nextMonth->setAutoRepeat(true);
1753
1754 monthButton = new QCalToolButton(navBarBackground);
1755 monthButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum);
1756 monthButton->setAutoRaise(true);
1757 monthButton->setPopupMode(QToolButton::InstantPopup);
1758 monthMenu = new QMenu(monthButton);
1759 for (int i = 1, e = m_model->m_calendar.maximumMonthsInYear(); i <= e; i++) {
1760 QString monthName(m_model->monthName(q->locale(), i));
1761 QAction *act = monthMenu->addAction(monthName);
1762 act->setData(i);
1763 monthToAction[i] = act;
1764 }
1765 monthButton->setMenu(monthMenu);
1766 yearButton = new QCalToolButton(navBarBackground);
1767 yearButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum);
1768 yearButton->setAutoRaise(true);
1769 yearEdit = new QSpinBox(navBarBackground);
1770
1771 QFont font = q->font();
1772 font.setBold(true);
1773 monthButton->setFont(font);
1774 yearButton->setFont(font);
1775 yearEdit->setFrame(false);
1776 yearEdit->setMinimum(m_model->m_minimumDate.year(m_model->m_calendar));
1777 yearEdit->setMaximum(m_model->m_maximumDate.year(m_model->m_calendar));
1778 yearEdit->hide();
1779 spaceHolder = new QSpacerItem(0,0);
1780
1781 QHBoxLayout *headerLayout = new QHBoxLayout;
1782 headerLayout->setContentsMargins(QMargins());
1783 headerLayout->setSpacing(0);
1784 headerLayout->addWidget(prevMonth);
1785 headerLayout->insertStretch(headerLayout->count());
1786 headerLayout->addWidget(monthButton);
1787 headerLayout->addItem(spaceHolder);
1788 headerLayout->addWidget(yearButton);
1789 headerLayout->insertStretch(headerLayout->count());
1790 headerLayout->addWidget(nextMonth);
1791 navBarBackground->setLayout(headerLayout);
1792
1793 yearEdit->setFocusPolicy(Qt::StrongFocus);
1794 prevMonth->setFocusPolicy(Qt::NoFocus);
1795 nextMonth->setFocusPolicy(Qt::NoFocus);
1796 yearButton->setFocusPolicy(Qt::NoFocus);
1797 monthButton->setFocusPolicy(Qt::NoFocus);
1798
1799 //set names for the header controls.
1800 prevMonth->setObjectName("qt_calendar_prevmonth"_L1);
1801 nextMonth->setObjectName("qt_calendar_nextmonth"_L1);
1802 monthButton->setObjectName("qt_calendar_monthbutton"_L1);
1803 yearButton->setObjectName("qt_calendar_yearbutton"_L1);
1804 yearEdit->setObjectName("qt_calendar_yearedit"_L1);
1805
1807 showMonth(m_model->m_date.year(m_model->m_calendar), m_model->m_date.month(m_model->m_calendar));
1808}
1809
1811{
1812 Q_Q(QCalendarWidget);
1813 prevMonth->setIcon(q->style()->standardIcon(q->isRightToLeft() ? QStyle::SP_ArrowRight : QStyle::SP_ArrowLeft, nullptr, q));
1814 nextMonth->setIcon(q->style()->standardIcon(q->isRightToLeft() ? QStyle::SP_ArrowLeft : QStyle::SP_ArrowRight, nullptr, q));
1815}
1816
1818{
1819 int maxMonths = m_model->m_calendar.monthsInYear(m_model->m_shownYear);
1820 int beg = 1, end = maxMonths;
1821 bool prevEnabled = true;
1822 bool nextEnabled = true;
1823 QCalendar cal = m_model->calendar();
1824 if (m_model->m_shownYear == m_model->m_minimumDate.year(cal)) {
1825 beg = m_model->m_minimumDate.month(cal);
1826 if (m_model->m_shownMonth == m_model->m_minimumDate.month(cal))
1827 prevEnabled = false;
1828 }
1829 if (m_model->m_shownYear == m_model->m_maximumDate.year(cal)) {
1830 end = m_model->m_maximumDate.month(cal);
1831 if (m_model->m_shownMonth == m_model->m_maximumDate.month(cal))
1832 nextEnabled = false;
1833 }
1834 prevMonth->setEnabled(prevEnabled);
1835 nextMonth->setEnabled(nextEnabled);
1836 for (int i = 1; i <= maxMonths; i++) {
1837 bool monthEnabled = true;
1838 if (i < beg || i > end)
1839 monthEnabled = false;
1840 monthToAction[i]->setEnabled(monthEnabled);
1841 }
1842}
1843
1845{
1846 Q_Q(QCalendarWidget);
1847
1848 for (int i = 1; i <= 12; i++) {
1849 QString monthName(m_model->monthName(q->locale(), i));
1850 monthToAction[i]->setText(monthName);
1851 }
1852}
1853
1855{
1856 Q_Q(QCalendarWidget);
1857 QCalendar cal = m_model->calendar();
1858
1859 QDate newDate = date;
1860 QDate minDate = q->minimumDate();
1861 QDate maxDate = q->maximumDate();
1862 if (minDate.isValid()&& minDate.daysTo(newDate) < 0)
1863 newDate = minDate;
1864 if (maxDate.isValid()&& maxDate.daysTo(newDate) > 0)
1865 newDate = maxDate;
1866 showMonth(newDate.year(cal), newDate.month(cal));
1867 int row = -1, col = -1;
1868 m_model->cellForDate(newDate, &row, &col);
1869 if (row != -1 && col != -1)
1870 {
1871 m_view->selectionModel()->setCurrentIndex(m_model->index(row, col),
1872 QItemSelectionModel::NoUpdate);
1873 }
1874}
1875
1877{
1878 monthButton->setText(act->text());
1879 QDate currentDate = getCurrentDate();
1880 QDate newDate = currentDate.addMonths(act->data().toInt() - currentDate.month(m_model->m_calendar), m_model->m_calendar);
1881 updateCurrentPage(newDate);
1882}
1883
1885{
1886 QModelIndex index = m_view->currentIndex();
1887 return m_model->dateForCell(index.row(), index.column());
1888}
1889
1891{
1892 QDate currentDate = getCurrentDate().addMonths(-1, m_model->m_calendar);
1893 updateCurrentPage(currentDate);
1894}
1895
1897{
1898 QDate currentDate = getCurrentDate().addMonths(1, m_model->m_calendar);
1899 updateCurrentPage(currentDate);
1900}
1901
1903{
1904 Q_Q(QCalendarWidget);
1905 yearEdit->hide();
1906 q->setFocusPolicy(oldFocusPolicy);
1907 qApp->removeEventFilter(q);
1908 spaceHolder->changeSize(0, 0);
1909 yearButton->show();
1910 QDate currentDate = getCurrentDate();
1911 int newYear = q->locale().toInt(yearEdit->text());
1912 currentDate = currentDate.addYears(newYear - currentDate.year(m_model->m_calendar), m_model->m_calendar);
1913 yearButton->setText(q->locale().toString(currentDate, u"yyyy", m_model->m_calendar));
1914 updateCurrentPage(currentDate);
1915}
1916
1918{
1919 Q_Q(QCalendarWidget);
1920 //show the spinbox on top of the button
1921 yearEdit->setGeometry(yearButton->x(), yearButton->y(),
1922 yearEdit->sizeHint().width(), yearButton->height());
1923 spaceHolder->changeSize(yearButton->width(), 0);
1924 yearButton->hide();
1925 oldFocusPolicy = q->focusPolicy();
1926 q->setFocusPolicy(Qt::NoFocus);
1927 yearEdit->show();
1928 qApp->installEventFilter(q);
1929 yearEdit->raise();
1930 yearEdit->selectAll();
1931 yearEdit->setFocus(Qt::MouseFocusReason);
1932}
1933
1934void QCalendarWidgetPrivate::showMonth(int year, int month)
1935{
1936 if (m_model->m_shownYear == year && m_model->m_shownMonth == month)
1937 return;
1938 Q_Q(QCalendarWidget);
1939 m_model->showMonth(year, month);
1941 emit q->currentPageChanged(year, month);
1943 cachedSizeHint = QSize();
1944 update();
1946}
1947
1949{
1950 Q_Q(QCalendarWidget);
1951
1952 QString monthName = m_model->monthName(q->locale(), m_model->m_shownMonth);
1953
1954 monthButton->setText(monthName);
1955 yearEdit->setValue(m_model->m_shownYear);
1956 yearButton->setText(yearEdit->text());
1957}
1958
1960{
1961 QDate currentDate = m_model->m_date;
1962 int row, column;
1963 m_model->cellForDate(currentDate, &row, &column);
1964 QModelIndex idx;
1965 m_selection->clear();
1966 if (row != -1 && column != -1) {
1967 idx = m_model->index(row, column);
1968 m_selection->setCurrentIndex(idx, QItemSelectionModel::SelectCurrent);
1969 }
1970}
1971
1972void QCalendarWidgetPrivate::paintCell(QPainter *painter, const QRect &rect, QDate date) const
1973{
1974 Q_Q(const QCalendarWidget);
1975 q->paintCell(painter, rect, date);
1976}
1977
1979{
1981}
1982
1984{
1985 _q_slotChangeDate(date, true);
1986}
1987
1988void QCalendarWidgetPrivate::_q_slotChangeDate(QDate date, bool changeMonth)
1989{
1990 QDate oldDate = m_model->m_date;
1992 QDate newDate = m_model->m_date;
1993 if (changeMonth)
1994 showMonth(newDate.year(m_model->m_calendar), newDate.month(m_model->m_calendar));
1995 if (oldDate != newDate) {
1996 update();
1997 Q_Q(QCalendarWidget);
1999 emit q->selectionChanged();
2000 }
2001}
2002
2004{
2005 Q_Q(QCalendarWidget);
2006 emit q->activated(m_model->m_date);
2007}
2008
2009/*!
2010 \class QCalendarWidget
2011 \brief The QCalendarWidget class provides a monthly based
2012 calendar widget allowing the user to select a date.
2013 \since 4.2
2014
2015 \ingroup advanced
2016 \inmodule QtWidgets
2017
2018 \image fusion-calendarwidget.png
2019 {Calendar widget showing the month, year, and a selected day}
2020
2021 The widget is initialized with the current month and year, but
2022 QCalendarWidget provides several public slots to change the year
2023 and month that is shown.
2024
2025 By default, today's date is selected, and the user can select a
2026 date using both mouse and keyboard. The currently selected date
2027 can be retrieved using the selectedDate() function. It is
2028 possible to constrain the user selection to a given date range by
2029 setting the minimumDate and maximumDate properties.
2030 Alternatively, both properties can be set in one go using the
2031 setDateRange() convenience slot. Set the \l selectionMode
2032 property to NoSelection to prohibit the user from selecting at
2033 all. Note that a date also can be selected programmatically using
2034 the setSelectedDate() slot.
2035
2036 The currently displayed month and year can be retrieved using the
2037 monthShown() and yearShown() functions, respectively.
2038
2039 A newly created calendar widget uses abbreviated day names, and
2040 both Saturdays and Sundays are marked in red. The calendar grid is
2041 not visible. The week numbers are displayed, and the first column
2042 day is the first day of the week for the calendar's locale.
2043
2044 The notation of the days can be altered to a single letter
2045 abbreviations ("M" for "Monday") by setting the
2046 horizontalHeaderFormat property to
2047 QCalendarWidget::SingleLetterDayNames. Setting the same property
2048 to QCalendarWidget::LongDayNames makes the header display the
2049 complete day names. The week numbers can be removed by setting
2050 the verticalHeaderFormat property to
2051 QCalendarWidget::NoVerticalHeader. The calendar grid can be
2052 turned on by setting the gridVisible property to true using the
2053 setGridVisible() function:
2054
2055 \table
2056 \row \li
2057 \image qcalendarwidget-grid.png
2058 {Calendar widget with a visible grid}
2059 \row \li
2060 \snippet code/src_gui_widgets_qcalendarwidget.cpp 0
2061 \endtable
2062
2063 Finally, the day in the first column can be altered using the
2064 setFirstDayOfWeek() function.
2065
2066 The QCalendarWidget class also provides three signals,
2067 selectionChanged(), activated() and currentPageChanged() making it
2068 possible to respond to user interaction.
2069
2070 The rendering of the headers, weekdays or single days can be
2071 largely customized by setting QTextCharFormat's for some special
2072 weekday, a special date or for the rendering of the headers.
2073
2074 Only a subset of the properties in QTextCharFormat are used by the
2075 calendar widget. Currently, the foreground, background and font
2076 properties are used to determine the rendering of individual cells
2077 in the widget.
2078
2079 \sa QDate, QDateEdit, QTextCharFormat
2080*/
2081
2082/*!
2083 \enum QCalendarWidget::SelectionMode
2084
2085 This enum describes the types of selection offered to the user for
2086 selecting dates in the calendar.
2087
2088 \value NoSelection Dates cannot be selected.
2089 \value SingleSelection Single dates can be selected.
2090
2091 \sa selectionMode
2092*/
2093
2094/*!
2095 Constructs a calendar widget with the given \a parent.
2096
2097 The widget is initialized with the current month and year, and the
2098 currently selected date is today.
2099
2100 \sa setCurrentPage()
2101*/
2102QCalendarWidget::QCalendarWidget(QWidget *parent)
2103 : QWidget(*new QCalendarWidgetPrivate, parent, { })
2104{
2105 Q_D(QCalendarWidget);
2106
2107 setAutoFillBackground(true);
2108 setBackgroundRole(QPalette::Window);
2109
2110 QVBoxLayout *layoutV = new QVBoxLayout(this);
2111 layoutV->setContentsMargins(QMargins());
2112 d->m_model = new QCalendarModel(this);
2113 QTextCharFormat fmt;
2114 fmt.setForeground(QBrush(Qt::red));
2115 d->m_model->m_dayFormats.insert(Qt::Saturday, fmt);
2116 d->m_model->m_dayFormats.insert(Qt::Sunday, fmt);
2117 d->m_view = new QCalendarView(this);
2118 d->m_view->setObjectName("qt_calendar_calendarview"_L1);
2119 d->m_view->setModel(d->m_model);
2120 d->m_model->setView(d->m_view);
2121 d->m_view->setSelectionBehavior(QAbstractItemView::SelectItems);
2122 d->m_view->setSelectionMode(QAbstractItemView::SingleSelection);
2123 d->m_view->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
2124 d->m_view->horizontalHeader()->setSectionsClickable(false);
2125 d->m_view->verticalHeader()->setSectionResizeMode(QHeaderView::Stretch);
2126 d->m_view->verticalHeader()->setSectionsClickable(false);
2127 d->m_selection = d->m_view->selectionModel();
2128 d->createNavigationBar(this);
2129 d->m_view->setFrameStyle(QFrame::NoFrame);
2130 d->m_delegate = new QCalendarDelegate(d, this);
2131 d->m_view->setItemDelegate(d->m_delegate);
2132 d->update();
2133 d->updateNavigationBar();
2134 setFocusPolicy(Qt::StrongFocus);
2135 setFocusProxy(d->m_view);
2136 setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
2137
2138 connect(d->m_view, SIGNAL(showDate(QDate)),
2139 this, SLOT(_q_slotShowDate(QDate)));
2140 connect(d->m_view, SIGNAL(changeDate(QDate,bool)),
2141 this, SLOT(_q_slotChangeDate(QDate,bool)));
2142 connect(d->m_view, SIGNAL(clicked(QDate)),
2143 this, SIGNAL(clicked(QDate)));
2144 connect(d->m_view, SIGNAL(editingFinished()),
2145 this, SLOT(_q_editingFinished()));
2146
2147 connect(d->prevMonth, SIGNAL(clicked(bool)),
2148 this, SLOT(_q_prevMonthClicked()));
2149 connect(d->nextMonth, SIGNAL(clicked(bool)),
2150 this, SLOT(_q_nextMonthClicked()));
2151 connect(d->yearButton, SIGNAL(clicked(bool)),
2152 this, SLOT(_q_yearClicked()));
2153 connect(d->monthMenu, SIGNAL(triggered(QAction*)),
2154 this, SLOT(_q_monthChanged(QAction*)));
2155 connect(d->yearEdit, SIGNAL(editingFinished()),
2156 this, SLOT(_q_yearEditingFinished()));
2157
2158 layoutV->setContentsMargins(QMargins());
2159 layoutV->setSpacing(0);
2160 layoutV->addWidget(d->navBarBackground);
2161 layoutV->addWidget(d->m_view);
2162
2163 d->m_navigator = new QCalendarTextNavigator(this);
2164 setDateEditEnabled(true);
2165}
2166
2167/*!
2168 Destroys the calendar widget.
2169*/
2170QCalendarWidget::~QCalendarWidget()
2171{
2172}
2173
2174/*!
2175 \reimp
2176*/
2177QSize QCalendarWidget::sizeHint() const
2178{
2179 return minimumSizeHint();
2180}
2181
2182/*!
2183 \reimp
2184*/
2185QSize QCalendarWidget::minimumSizeHint() const
2186{
2187 Q_D(const QCalendarWidget);
2188 if (d->cachedSizeHint.isValid())
2189 return d->cachedSizeHint;
2190
2191 ensurePolished();
2192
2193 int w = 0;
2194 int h = 0;
2195
2196 int end = 53;
2197 int rows = 7;
2198 int cols = 8;
2199
2200 QStyleOption option;
2201 option.initFrom(this);
2202 const int marginH = (style()->pixelMetric(QStyle::PM_FocusFrameHMargin, &option, this) + 1) * 2;
2203
2204 if (horizontalHeaderFormat() == QCalendarWidget::NoHorizontalHeader) {
2205 rows = 6;
2206 } else {
2207 for (int i = 1; i <= 7; i++) {
2208 QFontMetrics fm(d->m_model->formatForCell(0, i).font());
2209 w = qMax(w, fm.horizontalAdvance(d->m_model->dayName(d->m_model->dayOfWeekForColumn(i))) + marginH);
2210 h = qMax(h, fm.height());
2211 }
2212 }
2213
2214 if (verticalHeaderFormat() == QCalendarWidget::NoVerticalHeader) {
2215 cols = 7;
2216 } else {
2217 for (int i = 1; i <= 6; i++) {
2218 QFontMetrics fm(d->m_model->formatForCell(i, 0).font());
2219 for (int j = 1; j < end; j++)
2220 w = qMax(w, fm.horizontalAdvance(QString::number(j)) + marginH);
2221 h = qMax(h, fm.height());
2222 }
2223 }
2224
2225 QFontMetrics fm(d->m_model->formatForCell(1, 1).font());
2226 for (int i = 1; i <= end; i++) {
2227 w = qMax(w, fm.horizontalAdvance(QString::number(i)) + marginH);
2228 h = qMax(h, fm.height());
2229 }
2230
2231 if (d->m_view->showGrid()) {
2232 // hardcoded in tableview
2233 w += 1;
2234 h += 1;
2235 }
2236
2237 w += 1; // default column span
2238
2239 h = qMax(h, d->m_view->verticalHeader()->minimumSectionSize());
2240 w = qMax(w, d->m_view->horizontalHeader()->minimumSectionSize());
2241
2242 //add the size of the header.
2243 QSize headerSize(0, 0);
2244 if (d->navBarVisible) {
2245 int headerH = d->navBarBackground->sizeHint().height();
2246 int headerW = 0;
2247
2248 headerW += d->prevMonth->sizeHint().width();
2249 headerW += d->nextMonth->sizeHint().width();
2250
2251 QFontMetrics fm = d->monthButton->fontMetrics();
2252 int monthW = 0;
2253 for (int i = 1; i < 12; i++) {
2254 QString monthName = d->m_model->monthName(locale(), i);
2255 monthW = qMax(monthW, fm.boundingRect(monthName).width());
2256 }
2257 const int buttonDecoMargin = d->monthButton->sizeHint().width() - fm.boundingRect(d->monthButton->text()).width();
2258 headerW += monthW + buttonDecoMargin;
2259
2260 fm = d->yearButton->fontMetrics();
2261 headerW += fm.boundingRect("5555"_L1).width() + buttonDecoMargin;
2262
2263 headerSize = QSize(headerW, headerH);
2264 }
2265 w *= cols;
2266 w = qMax(headerSize.width(), w);
2267 h = (h * rows) + headerSize.height();
2268 QMargins cm = contentsMargins();
2269 w += cm.left() + cm.right();
2270 h += cm.top() + cm.bottom();
2271 d->cachedSizeHint = QSize(w, h);
2272 return d->cachedSizeHint;
2273}
2274
2275/*!
2276 Paints the cell specified by the given \a date, using the given \a painter and \a rect.
2277*/
2278
2279void QCalendarWidget::paintCell(QPainter *painter, const QRect &rect, QDate date) const
2280{
2281 Q_D(const QCalendarWidget);
2282 d->m_delegate->paintCell(painter, rect, date);
2283}
2284
2285/*!
2286 \property QCalendarWidget::selectedDate
2287 \brief the currently selected date.
2288
2289 The selected date must be within the date range specified by the
2290 minimumDate and maximumDate properties. By default, the selected
2291 date is the current date.
2292
2293 \sa setDateRange()
2294*/
2295
2296QDate QCalendarWidget::selectedDate() const
2297{
2298 Q_D(const QCalendarWidget);
2299 return d->m_model->m_date;
2300}
2301
2302void QCalendarWidget::setSelectedDate(QDate date)
2303{
2304 Q_D(QCalendarWidget);
2305 if (d->m_model->m_date == date && date == d->getCurrentDate())
2306 return;
2307
2308 if (!date.isValid())
2309 return;
2310
2311 d->m_model->setDate(date);
2312 d->update();
2313 QDate newDate = d->m_model->m_date;
2314 QCalendar cal = d->m_model->m_calendar;
2315 d->showMonth(newDate.year(cal), newDate.month(cal));
2316 emit selectionChanged();
2317}
2318
2319/*!
2320 Returns the year of the currently displayed month. Months are
2321 numbered from 1 to 12.
2322
2323 \sa monthShown(), setCurrentPage()
2324*/
2325
2326int QCalendarWidget::yearShown() const
2327{
2328 Q_D(const QCalendarWidget);
2329 return d->m_model->m_shownYear;
2330}
2331
2332/*!
2333 Returns the currently displayed month. Months are numbered from 1 to
2334 12.
2335
2336 \sa yearShown(), setCurrentPage()
2337*/
2338
2339int QCalendarWidget::monthShown() const
2340{
2341 Q_D(const QCalendarWidget);
2342 return d->m_model->m_shownMonth;
2343}
2344
2345/*!
2346 Displays the given \a month of the given \a year without changing
2347 the selected date. Use the setSelectedDate() function to alter the
2348 selected date.
2349
2350 The currently displayed month and year can be retrieved using the
2351 monthShown() and yearShown() functions respectively.
2352
2353 \sa yearShown(), monthShown(), showPreviousMonth(), showNextMonth(),
2354 showPreviousYear(), showNextYear()
2355*/
2356
2357void QCalendarWidget::setCurrentPage(int year, int month)
2358{
2359 Q_D(QCalendarWidget);
2360 QDate currentDate = d->getCurrentDate();
2361 QCalendar cal = d->m_model->m_calendar;
2362 int day = currentDate.day(cal);
2363 int daysInMonths = cal.daysInMonth(month, year);
2364 if (day > daysInMonths)
2365 day = daysInMonths;
2366
2367 d->showMonth(year, month);
2368
2369 QDate newDate(year, month, day, d->m_model->m_calendar);
2370 int row = -1, col = -1;
2371 d->m_model->cellForDate(newDate, &row, &col);
2372 if (row != -1 && col != -1) {
2373 d->m_view->selectionModel()->setCurrentIndex(d->m_model->index(row, col),
2374 QItemSelectionModel::NoUpdate);
2375 }
2376}
2377
2378/*!
2379 Shows the next month relative to the currently displayed
2380 month. Note that the selected date is not changed.
2381
2382 \sa showPreviousMonth(), setCurrentPage(), setSelectedDate()
2383*/
2384
2385void QCalendarWidget::showNextMonth()
2386{
2387 Q_D(const QCalendarWidget);
2388 int year = yearShown();
2389 int month = monthShown();
2390 if (month == d->m_model->m_calendar.maximumMonthsInYear()) {
2391 ++year;
2392 month = 1;
2393 } else {
2394 ++month;
2395 }
2396 setCurrentPage(year, month);
2397}
2398
2399/*!
2400 Shows the previous month relative to the currently displayed
2401 month. Note that the selected date is not changed.
2402
2403 \sa showNextMonth(), setCurrentPage(), setSelectedDate()
2404*/
2405
2406void QCalendarWidget::showPreviousMonth()
2407{
2408 Q_D(const QCalendarWidget);
2409
2410 int year = yearShown();
2411 int month = monthShown();
2412 if (month == 1) {
2413 --year;
2414 month = d->m_model->m_calendar.maximumMonthsInYear();
2415 } else {
2416 --month;
2417 }
2418 setCurrentPage(year, month);
2419}
2420
2421/*!
2422 Shows the currently displayed month in the \e next year relative
2423 to the currently displayed year. Note that the selected date is
2424 not changed.
2425
2426 \sa showPreviousYear(), setCurrentPage(), setSelectedDate()
2427*/
2428
2429void QCalendarWidget::showNextYear()
2430{
2431 int year = yearShown();
2432 int month = monthShown();
2433 ++year;
2434 setCurrentPage(year, month);
2435}
2436
2437/*!
2438 Shows the currently displayed month in the \e previous year
2439 relative to the currently displayed year. Note that the selected
2440 date is not changed.
2441
2442 \sa showNextYear(), setCurrentPage(), setSelectedDate()
2443*/
2444
2445void QCalendarWidget::showPreviousYear()
2446{
2447 int year = yearShown();
2448 int month = monthShown();
2449 --year;
2450 setCurrentPage(year, month);
2451}
2452
2453/*!
2454 Shows the month of the selected date.
2455
2456 \sa selectedDate(), setCurrentPage()
2457*/
2458void QCalendarWidget::showSelectedDate()
2459{
2460 Q_D(const QCalendarWidget);
2461
2462 QDate currentDate = selectedDate();
2463 setCurrentPage(currentDate.year(d->m_model->m_calendar), currentDate.month(d->m_model->m_calendar));
2464}
2465
2466/*!
2467 Shows the month of the today's date.
2468
2469 \sa selectedDate(), setCurrentPage()
2470*/
2471void QCalendarWidget::showToday()
2472{
2473 Q_D(const QCalendarWidget);
2474
2475 QDate currentDate = QDate::currentDate();
2476 setCurrentPage(currentDate.year(d->m_model->m_calendar), currentDate.month(d->m_model->m_calendar));
2477}
2478
2479/*!
2480 \property QCalendarWidget::minimumDate
2481 \brief the minimum date of the currently specified date range.
2482
2483 The user will not be able to select a date that is before the
2484 currently set minimum date.
2485
2486 \table
2487 \row
2488 \li \image qcalendarwidget-minimum.png
2489 {Calendar widget with the disabled dates before the minimum date}
2490 \row
2491 \li
2492 \snippet code/src_gui_widgets_qcalendarwidget.cpp 1
2493 \endtable
2494
2495 When setting a minimum date, the maximumDate and selectedDate
2496 properties are adjusted if the selection range becomes invalid. If
2497 the provided date is not a valid QDate object, the
2498 setMinimumDate() function does nothing.
2499
2500 The default minimum date is November 25, 4714 BCE.
2501 You can restore this default by calling clearMinimumDate() (since Qt 6.6).
2502
2503 \sa setDateRange()
2504*/
2505
2506QDate QCalendarWidget::minimumDate() const
2507{
2508 Q_D(const QCalendarWidget);
2509 return d->m_model->m_minimumDate;
2510}
2511
2512void QCalendarWidget::setMinimumDate(QDate date)
2513{
2514 Q_D(QCalendarWidget);
2515 if (!date.isValid() || d->m_model->m_minimumDate == date)
2516 return;
2517
2518 QDate oldDate = d->m_model->m_date;
2519 d->m_model->setMinimumDate(date);
2520 d->yearEdit->setMinimum(d->m_model->m_minimumDate.year(d->m_model->m_calendar));
2521 d->updateMonthMenu();
2522 QDate newDate = d->m_model->m_date;
2523 if (oldDate != newDate) {
2524 d->update();
2525 d->showMonth(newDate.year(d->m_model->m_calendar), newDate.month(d->m_model->m_calendar));
2526 d->m_navigator->setDate(newDate);
2527 emit selectionChanged();
2528 }
2529}
2530
2531void QCalendarWidget::clearMinimumDate()
2532{
2533 setMinimumDate(QDate::fromJulianDay(1));
2534}
2535
2536/*!
2537 \property QCalendarWidget::maximumDate
2538 \brief the maximum date of the currently specified date range.
2539
2540 The user will not be able to select a date which is after the
2541 currently set maximum date.
2542
2543 \table
2544 \row
2545 \li \image qcalendarwidget-maximum.png
2546 {Calendar widget with the disabled dates after the maximum date}
2547 \row
2548 \li
2549 \snippet code/src_gui_widgets_qcalendarwidget.cpp 2
2550 \endtable
2551
2552 When setting a maximum date, the minimumDate and selectedDate
2553 properties are adjusted if the selection range becomes invalid. If
2554 the provided date is not a valid QDate object, the
2555 setMaximumDate() function does nothing.
2556
2557 The default maximum date is December 31, 9999 CE.
2558 You can restore this default by calling clearMaximumDate() (since Qt 6.6).
2559
2560 \sa setDateRange()
2561*/
2562
2563QDate QCalendarWidget::maximumDate() const
2564{
2565 Q_D(const QCalendarWidget);
2566 return d->m_model->m_maximumDate;
2567}
2568
2569void QCalendarWidget::setMaximumDate(QDate date)
2570{
2571 Q_D(QCalendarWidget);
2572 if (!date.isValid() || d->m_model->m_maximumDate == date)
2573 return;
2574
2575 QDate oldDate = d->m_model->m_date;
2576 d->m_model->setMaximumDate(date);
2577 d->yearEdit->setMaximum(d->m_model->m_maximumDate.year(d->m_model->m_calendar));
2578 d->updateMonthMenu();
2579 QDate newDate = d->m_model->m_date;
2580 if (oldDate != newDate) {
2581 d->update();
2582 d->showMonth(newDate.year(d->m_model->m_calendar), newDate.month(d->m_model->m_calendar));
2583 d->m_navigator->setDate(newDate);
2584 emit selectionChanged();
2585 }
2586}
2587
2588void QCalendarWidget::clearMaximumDate()
2589{
2590 setMaximumDate(QDate(9999, 12, 31));
2591}
2592
2593/*!
2594 Defines a date range by setting the minimumDate and maximumDate
2595 properties.
2596
2597 The date range restricts the user selection, i.e. the user can
2598 only select dates within the specified date range. Note that
2599
2600 \snippet code/src_gui_widgets_qcalendarwidget.cpp 3
2601
2602 is analogous to
2603
2604 \snippet code/src_gui_widgets_qcalendarwidget.cpp 4
2605
2606 If either the \a min or \a max parameters are not valid QDate
2607 objects, this function does nothing.
2608
2609 \sa setMinimumDate(), setMaximumDate()
2610*/
2611
2612void QCalendarWidget::setDateRange(QDate min, QDate max)
2613{
2614 Q_D(QCalendarWidget);
2615 if (d->m_model->m_minimumDate == min && d->m_model->m_maximumDate == max)
2616 return;
2617 if (!min.isValid() || !max.isValid())
2618 return;
2619
2620 QDate oldDate = d->m_model->m_date;
2621 d->m_model->setRange(min, max);
2622 d->yearEdit->setMinimum(d->m_model->m_minimumDate.year(d->m_model->m_calendar));
2623 d->yearEdit->setMaximum(d->m_model->m_maximumDate.year(d->m_model->m_calendar));
2624 d->updateMonthMenu();
2625 QDate newDate = d->m_model->m_date;
2626 if (oldDate != newDate) {
2627 d->update();
2628 d->showMonth(newDate.year(d->m_model->m_calendar), newDate.month(d->m_model->m_calendar));
2629 d->m_navigator->setDate(newDate);
2630 emit selectionChanged();
2631 }
2632}
2633
2634
2635/*! \enum QCalendarWidget::HorizontalHeaderFormat
2636
2637 This enum type defines the various formats the horizontal header can display.
2638
2639 \value SingleLetterDayNames The header displays a single letter abbreviation for day names (e.g. M for Monday).
2640 \value ShortDayNames The header displays a short abbreviation for day names (e.g. Mon for Monday).
2641 \value LongDayNames The header displays complete day names (e.g. Monday).
2642 \value NoHorizontalHeader The header is hidden.
2643
2644 \sa horizontalHeaderFormat(), VerticalHeaderFormat
2645*/
2646
2647/*!
2648 \property QCalendarWidget::horizontalHeaderFormat
2649 \brief the format of the horizontal header.
2650
2651 The default value is QCalendarWidget::ShortDayNames.
2652*/
2653
2654void QCalendarWidget::setHorizontalHeaderFormat(QCalendarWidget::HorizontalHeaderFormat format)
2655{
2656 Q_D(QCalendarWidget);
2657 if (d->m_model->m_horizontalHeaderFormat == format)
2658 return;
2659
2660 d->m_model->setHorizontalHeaderFormat(format);
2661 d->cachedSizeHint = QSize();
2662 d->m_view->viewport()->update();
2663 d->m_view->updateGeometry();
2664}
2665
2666QCalendarWidget::HorizontalHeaderFormat QCalendarWidget::horizontalHeaderFormat() const
2667{
2668 Q_D(const QCalendarWidget);
2669 return d->m_model->m_horizontalHeaderFormat;
2670}
2671
2672
2673/*!
2674 \enum QCalendarWidget::VerticalHeaderFormat
2675
2676 This enum type defines the various formats the vertical header can display.
2677
2678 \value ISOWeekNumbers The header displays ISO week numbers as described by \l QDate::weekNumber().
2679 \value NoVerticalHeader The header is hidden.
2680
2681 \sa verticalHeaderFormat(), HorizontalHeaderFormat
2682*/
2683
2684/*!
2685 \property QCalendarWidget::verticalHeaderFormat
2686 \brief the format of the vertical header.
2687
2688 The default value is QCalendarWidget::ISOWeekNumber.
2689*/
2690
2691QCalendarWidget::VerticalHeaderFormat QCalendarWidget::verticalHeaderFormat() const
2692{
2693 Q_D(const QCalendarWidget);
2694 bool shown = d->m_model->weekNumbersShown();
2695 if (shown)
2696 return QCalendarWidget::ISOWeekNumbers;
2697 return QCalendarWidget::NoVerticalHeader;
2698}
2699
2700void QCalendarWidget::setVerticalHeaderFormat(QCalendarWidget::VerticalHeaderFormat format)
2701{
2702 Q_D(QCalendarWidget);
2703 bool show = false;
2704 if (format == QCalendarWidget::ISOWeekNumbers)
2705 show = true;
2706 if (d->m_model->weekNumbersShown() == show)
2707 return;
2708 d->m_model->setWeekNumbersShown(show);
2709 d->cachedSizeHint = QSize();
2710 d->m_view->viewport()->update();
2711 d->m_view->updateGeometry();
2712}
2713
2714/*!
2715 \property QCalendarWidget::gridVisible
2716 \brief whether the table grid is displayed.
2717
2718 \table
2719 \row
2720 \li \inlineimage qcalendarwidget-grid.png
2721 {Calendar widget set to be visible}
2722 \row
2723 \li
2724 \snippet code/src_gui_widgets_qcalendarwidget.cpp 5
2725 \endtable
2726
2727 The default value is false.
2728*/
2729
2730bool QCalendarWidget::isGridVisible() const
2731{
2732 Q_D(const QCalendarWidget);
2733 return d->m_view->showGrid();
2734}
2735
2736/*!
2737 \since 5.14
2738 Report the calendar system in use by this widget.
2739
2740 \sa setCalendar()
2741*/
2742
2743QCalendar QCalendarWidget::calendar() const
2744{
2745 Q_D(const QCalendarWidget);
2746 return d->m_model->m_calendar;
2747}
2748
2749/*!
2750 \since 5.14
2751 Set \a c as the calendar system to be used by this widget.
2752
2753 The widget can use any supported calendar system.
2754 By default, it uses the Gregorian calendar.
2755
2756 \sa calendar()
2757*/
2758
2759void QCalendarWidget::setCalendar(QCalendar c)
2760{
2761 Q_D(QCalendarWidget);
2762 d->m_model->setCalendar(c);
2763 d->updateMonthMenuNames();
2764 d->yearEdit->setMinimum(d->m_model->m_minimumDate.year(d->m_model->m_calendar));
2765 d->yearEdit->setMaximum(d->m_model->m_maximumDate.year(d->m_model->m_calendar));
2766 d->updateNavigationBar();
2767}
2768
2769void QCalendarWidget::setGridVisible(bool show)
2770{
2771 Q_D(QCalendarWidget);
2772 d->m_view->setShowGrid(show);
2773 d->cachedSizeHint = QSize();
2774 d->m_view->viewport()->update();
2775 d->m_view->updateGeometry();
2776}
2777
2778/*!
2779 \property QCalendarWidget::selectionMode
2780 \brief the type of selection the user can make in the calendar
2781
2782 When this property is set to SingleSelection, the user can select a date
2783 within the minimum and maximum allowed dates, using either the mouse or
2784 the keyboard.
2785
2786 When the property is set to NoSelection, the user will be unable to select
2787 dates, but they can still be selected programmatically. Note that the date
2788 that is selected when the property is set to NoSelection will still be
2789 the selected date of the calendar.
2790
2791 The default value is SingleSelection.
2792*/
2793
2794QCalendarWidget::SelectionMode QCalendarWidget::selectionMode() const
2795{
2796 Q_D(const QCalendarWidget);
2797 return d->m_view->readOnly ? QCalendarWidget::NoSelection : QCalendarWidget::SingleSelection;
2798}
2799
2800void QCalendarWidget::setSelectionMode(SelectionMode mode)
2801{
2802 Q_D(QCalendarWidget);
2803 d->m_view->readOnly = (mode == QCalendarWidget::NoSelection);
2804 d->setNavigatorEnabled(isDateEditEnabled() && (selectionMode() != QCalendarWidget::NoSelection));
2805 d->update();
2806}
2807
2808/*!
2809 \property QCalendarWidget::firstDayOfWeek
2810 \brief a value identifying the day displayed in the first column.
2811
2812 By default, the day displayed in the first column
2813 is the first day of the week for the calendar's locale.
2814*/
2815
2816void QCalendarWidget::setFirstDayOfWeek(Qt::DayOfWeek dayOfWeek)
2817{
2818 Q_D(QCalendarWidget);
2819 if ((Qt::DayOfWeek)d->m_model->firstColumnDay() == dayOfWeek)
2820 return;
2821
2822 d->m_model->setFirstColumnDay(dayOfWeek);
2823 d->update();
2824}
2825
2826Qt::DayOfWeek QCalendarWidget::firstDayOfWeek() const
2827{
2828 Q_D(const QCalendarWidget);
2829 return (Qt::DayOfWeek)d->m_model->firstColumnDay();
2830}
2831
2832/*!
2833 Returns the text char format for rendering the header.
2834*/
2835QTextCharFormat QCalendarWidget::headerTextFormat() const
2836{
2837 Q_D(const QCalendarWidget);
2838 return d->m_model->m_headerFormat;
2839}
2840
2841/*!
2842 Sets the text char format for rendering the header to \a format.
2843 If you also set a weekday text format, this format's foreground and
2844 background color will take precedence over the header's format.
2845 The other formatting information will still be decided by
2846 the header's format.
2847*/
2848void QCalendarWidget::setHeaderTextFormat(const QTextCharFormat &format)
2849{
2850 Q_D(QCalendarWidget);
2851 d->m_model->m_headerFormat = format;
2852 d->cachedSizeHint = QSize();
2853 d->m_view->viewport()->update();
2854 d->m_view->updateGeometry();
2855}
2856
2857/*!
2858 Returns the text char format for rendering of day in the week \a dayOfWeek.
2859
2860 \sa headerTextFormat()
2861*/
2862QTextCharFormat QCalendarWidget::weekdayTextFormat(Qt::DayOfWeek dayOfWeek) const
2863{
2864 Q_D(const QCalendarWidget);
2865 return d->m_model->m_dayFormats.value(dayOfWeek);
2866}
2867
2868/*!
2869 Sets the text char format for rendering of day in the week \a dayOfWeek to \a format.
2870 The format will take precedence over the header format in case of foreground
2871 and background color. Other text formatting information is taken from the headers format.
2872
2873 \sa setHeaderTextFormat()
2874*/
2875void QCalendarWidget::setWeekdayTextFormat(Qt::DayOfWeek dayOfWeek, const QTextCharFormat &format)
2876{
2877 Q_D(QCalendarWidget);
2878 d->m_model->m_dayFormats[dayOfWeek] = format;
2879 d->cachedSizeHint = QSize();
2880 d->m_view->viewport()->update();
2881 d->m_view->updateGeometry();
2882}
2883
2884/*!
2885 Returns a QMap from QDate to QTextCharFormat showing all dates
2886 that use a special format that alters their rendering.
2887*/
2888QMap<QDate, QTextCharFormat> QCalendarWidget::dateTextFormat() const
2889{
2890 Q_D(const QCalendarWidget);
2891 return d->m_model->m_dateFormats;
2892}
2893
2894/*!
2895 Returns a QTextCharFormat for \a date. The char format can be
2896 empty if the date is not rendered specially.
2897*/
2898QTextCharFormat QCalendarWidget::dateTextFormat(QDate date) const
2899{
2900 Q_D(const QCalendarWidget);
2901 return d->m_model->m_dateFormats.value(date);
2902}
2903
2904/*!
2905 Sets the format used to render the given \a date to that specified by \a format.
2906
2907 If \a date is null, all date formats are cleared.
2908*/
2909void QCalendarWidget::setDateTextFormat(QDate date, const QTextCharFormat &format)
2910{
2911 Q_D(QCalendarWidget);
2912 if (date.isNull())
2913 d->m_model->m_dateFormats.clear();
2914 else
2915 d->m_model->m_dateFormats[date] = format;
2916 d->m_view->viewport()->update();
2917 d->m_view->updateGeometry();
2918}
2919
2920/*!
2921 \property QCalendarWidget::dateEditEnabled
2922 \brief whether the date edit popup is enabled
2923 \since 4.3
2924
2925 If this property is enabled, pressing a non-modifier key will cause a
2926 date edit to popup if the calendar widget has focus, allowing the user
2927 to specify a date in the form specified by the current locale.
2928
2929 By default, this property is enabled.
2930
2931 The date edit is simpler in appearance than QDateEdit, but allows the
2932 user to navigate between fields using the left and right cursor keys,
2933 increment and decrement individual fields using the up and down cursor
2934 keys, and enter values directly using the number keys.
2935
2936 \sa QCalendarWidget::dateEditAcceptDelay
2937*/
2938bool QCalendarWidget::isDateEditEnabled() const
2939{
2940 Q_D(const QCalendarWidget);
2941 return d->m_dateEditEnabled;
2942}
2943
2944void QCalendarWidget::setDateEditEnabled(bool enable)
2945{
2946 Q_D(QCalendarWidget);
2947 if (isDateEditEnabled() == enable)
2948 return;
2949
2950 d->m_dateEditEnabled = enable;
2951
2952 d->setNavigatorEnabled(enable && (selectionMode() != QCalendarWidget::NoSelection));
2953}
2954
2955/*!
2956 \property QCalendarWidget::dateEditAcceptDelay
2957 \brief the time an inactive date edit is shown before its contents are accepted
2958 \since 4.3
2959
2960 If the calendar widget's \l{dateEditEnabled}{date edit is enabled}, this
2961 property specifies the amount of time (in milliseconds) that the date edit
2962 remains open after the most recent user input. Once this time has elapsed,
2963 the date specified in the date edit is accepted and the popup is closed.
2964
2965 By default, the delay is defined to be 1500 milliseconds (1.5 seconds).
2966*/
2967int QCalendarWidget::dateEditAcceptDelay() const
2968{
2969 Q_D(const QCalendarWidget);
2970 return d->m_navigator->dateEditAcceptDelay();
2971}
2972
2973void QCalendarWidget::setDateEditAcceptDelay(int delay)
2974{
2975 Q_D(QCalendarWidget);
2976 d->m_navigator->setDateEditAcceptDelay(delay);
2977}
2978
2979/*!
2980 \since 4.4
2981
2982 Updates the cell specified by the given \a date unless updates
2983 are disabled or the cell is hidden.
2984
2985 \sa updateCells(), yearShown(), monthShown()
2986*/
2987void QCalendarWidget::updateCell(QDate date)
2988{
2989 if (Q_UNLIKELY(!date.isValid())) {
2990 qWarning("QCalendarWidget::updateCell: Invalid date");
2991 return;
2992 }
2993
2994 if (!isVisible())
2995 return;
2996
2997 Q_D(QCalendarWidget);
2998 int row, column;
2999 d->m_model->cellForDate(date, &row, &column);
3000 if (row == -1 || column == -1)
3001 return;
3002
3003 QModelIndex modelIndex = d->m_model->index(row, column);
3004 if (!modelIndex.isValid())
3005 return;
3006
3007 d->m_view->viewport()->update(d->m_view->visualRect(modelIndex));
3008}
3009
3010/*!
3011 \since 4.4
3012
3013 Updates all visible cells unless updates are disabled.
3014
3015 \sa updateCell()
3016*/
3017void QCalendarWidget::updateCells()
3018{
3019 Q_D(QCalendarWidget);
3020 if (isVisible())
3021 d->m_view->viewport()->update();
3022}
3023
3024/*!
3025 \fn void QCalendarWidget::selectionChanged()
3026
3027 This signal is emitted when the currently selected date is
3028 changed.
3029
3030 The currently selected date can be changed by the user using the
3031 mouse or keyboard, or by the programmer using setSelectedDate().
3032
3033 \sa selectedDate()
3034*/
3035
3036/*!
3037 \fn void QCalendarWidget::currentPageChanged(int year, int month)
3038
3039 This signal is emitted when the currently shown month is changed.
3040 The new \a year and \a month are passed as parameters.
3041
3042 \sa setCurrentPage()
3043*/
3044
3045/*!
3046 \fn void QCalendarWidget::activated(QDate date)
3047
3048 This signal is emitted whenever the user presses the Return or
3049 Enter key or double-clicks a \a date in the calendar
3050 widget.
3051*/
3052
3053/*!
3054 \fn void QCalendarWidget::clicked(QDate date)
3055
3056 This signal is emitted when a mouse button is clicked. The date
3057 the mouse was clicked on is specified by \a date. The signal is
3058 only emitted when clicked on a valid date, e.g., dates are not
3059 outside the minimumDate() and maximumDate(). If the selection mode
3060 is NoSelection, this signal will not be emitted.
3061
3062*/
3063
3064/*!
3065 \property QCalendarWidget::navigationBarVisible
3066 \brief whether the navigation bar is shown or not
3067
3068 \since 4.3
3069
3070 When this property is \c true (the default), the next month,
3071 previous month, month selection, year selection controls are
3072 shown on top.
3073
3074 When the property is set to false, these controls are hidden.
3075*/
3076
3077bool QCalendarWidget::isNavigationBarVisible() const
3078{
3079 Q_D(const QCalendarWidget);
3080 return d->navBarVisible;
3081}
3082
3083
3084void QCalendarWidget::setNavigationBarVisible(bool visible)
3085{
3086 Q_D(QCalendarWidget);
3087 d->navBarVisible = visible;
3088 d->cachedSizeHint = QSize();
3089 d->navBarBackground->setVisible(visible);
3090 updateGeometry();
3091}
3092
3093/*!
3094 \reimp
3095*/
3096bool QCalendarWidget::event(QEvent *event)
3097{
3098 Q_D(QCalendarWidget);
3099 switch (event->type()) {
3100 case QEvent::LayoutDirectionChange:
3101 d->updateButtonIcons();
3102 break;
3103 case QEvent::LocaleChange:
3104 d->m_model->setFirstColumnDay(locale().firstDayOfWeek());
3105 d->cachedSizeHint = QSize();
3106 d->updateMonthMenuNames();
3107 d->updateNavigationBar();
3108 d->m_view->updateGeometry();
3109 // TODO: fix this known bug of calendaring API:
3110 // Changing locale before calendar works, but reverse order causes
3111 // invalid month names (in C Locale apparently).
3112 break;
3113 case QEvent::FontChange:
3114 case QEvent::ApplicationFontChange:
3115 d->cachedSizeHint = QSize();
3116 d->m_view->updateGeometry();
3117 break;
3118 case QEvent::StyleChange:
3119 d->cachedSizeHint = QSize();
3120 d->m_view->updateGeometry();
3121 break;
3122 default:
3123 break;
3124 }
3125 return QWidget::event(event);
3126}
3127
3128/*!
3129 \reimp
3130*/
3131bool QCalendarWidget::eventFilter(QObject *watched, QEvent *event)
3132{
3133 Q_D(QCalendarWidget);
3134 if (event->type() == QEvent::MouseButtonPress && d->yearEdit->hasFocus()) {
3135 // We can get filtered press events that were intended for Qt Virtual Keyboard's
3136 // input panel (QQuickView), so we have to make sure that the window is indeed a QWidget - no static_cast.
3137 // In addition, as we have a event filter on the whole application we first make sure that the top level widget
3138 // of both this and the watched widget are the same to decide if we should finish the year edition.
3139 QWidget *tlw = window();
3140 QWidget *widget = qobject_cast<QWidget *>(watched);
3141 if (!widget || widget->window() != tlw)
3142 return QWidget::eventFilter(watched, event);
3143
3144 QPoint mousePos = widget->mapTo(tlw, static_cast<QMouseEvent *>(event)->position().toPoint());
3145 QRect geom = QRect(d->yearEdit->mapTo(tlw, QPoint(0, 0)), d->yearEdit->size());
3146 if (!geom.contains(mousePos)) {
3147 event->accept();
3148 d->_q_yearEditingFinished();
3149 setFocus();
3150 return true;
3151 }
3152 }
3153 return QWidget::eventFilter(watched, event);
3154}
3155
3156/*!
3157 \reimp
3158*/
3159void QCalendarWidget::mousePressEvent(QMouseEvent *event)
3160{
3161 setAttribute(Qt::WA_NoMouseReplay);
3162 QWidget::mousePressEvent(event);
3163 setFocus();
3164}
3165
3166/*!
3167 \reimp
3168*/
3169void QCalendarWidget::resizeEvent(QResizeEvent * event)
3170{
3171 Q_D(QCalendarWidget);
3172
3173 // XXX Should really use a QWidgetStack for yearEdit and yearButton,
3174 // XXX here we hide the year edit when the layout is likely to break
3175 // XXX the manual positioning of the yearEdit over the yearButton.
3176 if (d->yearEdit->isVisible() && event->size().width() != event->oldSize().width())
3177 d->_q_yearEditingFinished();
3178
3179 QWidget::resizeEvent(event);
3180}
3181
3182/*!
3183 \reimp
3184*/
3185void QCalendarWidget::keyPressEvent(QKeyEvent * event)
3186{
3187#if QT_CONFIG(shortcut)
3188 Q_D(QCalendarWidget);
3189 if (d->yearEdit->isVisible()&& event->matches(QKeySequence::Cancel)) {
3190 d->yearEdit->setValue(yearShown());
3191 d->_q_yearEditingFinished();
3192 return;
3193 }
3194#endif
3195 QWidget::keyPressEvent(event);
3196}
3197
3198QT_END_NAMESPACE
3199
3200#include "qcalendarwidget.moc"
3201#include "moc_qcalendarwidget.cpp"
QCalendarTextNavigator * m_navigator
void setNavigatorEnabled(bool enable)
void paintCell(QPainter *painter, const QRect &rect, QDate date) const
QItemSelectionModel * m_selection
QMap< int, QAction * > monthToAction
void showMonth(int year, int month)
void _q_monthChanged(QAction *)
void _q_slotChangeDate(QDate date, bool changeMonth)
void createNavigationBar(QWidget *widget)
void _q_slotShowDate(QDate date)
QCalendarDelegate * m_delegate
void updateCurrentPage(QDate newDate)
void _q_slotChangeDate(QDate date)
The QCalendar class describes calendar systems.
Definition qcalendar.h:53
Definition qmap.h:189
\inmodule QtCore
constexpr QModelIndex() noexcept
Creates a new empty model index.
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
bool event(QEvent *event) override
void keyPressEvent(QKeyEvent *event) override
void changeDate(QDate date, bool changeMonth)
void setReadOnly(bool enable)
QDate handleMouseEvent(QMouseEvent *event)
QModelIndex moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers) override
Returns a QModelIndex object pointing to the next object in the view, based on the given cursorAction...
void clicked(QDate date)
void mouseDoubleClickEvent(QMouseEvent *event) override
void mouseMoveEvent(QMouseEvent *event) override
virtual QDate applyToDate(QDate date, QCalendar cal) const override
virtual QString text(QDate date, QCalendar cal, int repeat) const override
virtual void setDate(QDate date, QCalendar cal) override
virtual Section handleKey(int key) override
virtual QString text() const override
@ RowCount
@ MinimumDayOffset
@ HeaderRow
@ HeaderColumn
@ ColumnCount
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