| /**************************************************************************** |
| ** |
| ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). |
| ** All rights reserved. |
| ** Contact: Nokia Corporation (qt-info@nokia.com) |
| ** |
| ** This file is part of the QtGui module of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:LGPL$ |
| ** GNU Lesser General Public License Usage |
| ** This file may be used under the terms of the GNU Lesser General Public |
| ** License version 2.1 as published by the Free Software Foundation and |
| ** appearing in the file LICENSE.LGPL included in the packaging of this |
| ** file. Please review the following information to ensure the GNU Lesser |
| ** General Public License version 2.1 requirements will be met: |
| ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
| ** |
| ** In addition, as a special exception, Nokia gives you certain additional |
| ** rights. These rights are described in the Nokia Qt LGPL Exception |
| ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU General |
| ** Public License version 3.0 as published by the Free Software Foundation |
| ** and appearing in the file LICENSE.GPL included in the packaging of this |
| ** file. Please review the following information to ensure the GNU General |
| ** Public License version 3.0 requirements will be met: |
| ** http://www.gnu.org/copyleft/gpl.html. |
| ** |
| ** Other Usage |
| ** Alternatively, this file may be used in accordance with the terms and |
| ** conditions contained in a signed written agreement between you and Nokia. |
| ** |
| ** |
| ** |
| ** |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include <math.h> |
| #include <private/qdatetimeedit_p.h> |
| #include <qabstractspinbox.h> |
| #include <qapplication.h> |
| #include <qdatetimeedit.h> |
| #include <qdesktopwidget.h> |
| #include <qdebug.h> |
| #include <qevent.h> |
| #include <qlineedit.h> |
| #include <private/qlineedit_p.h> |
| #include <qlocale.h> |
| #include <qpainter.h> |
| #include <qlayout.h> |
| #include <qset.h> |
| #include <qstyle.h> |
| |
| #ifndef QT_NO_DATETIMEEDIT |
| |
| //#define QDATETIMEEDIT_QDTEDEBUG |
| #ifdef QDATETIMEEDIT_QDTEDEBUG |
| # define QDTEDEBUG qDebug() << QString::fromLatin1("%1:%2").arg(__FILE__).arg(__LINE__) |
| # define QDTEDEBUGN qDebug |
| #else |
| # define QDTEDEBUG if (false) qDebug() |
| # define QDTEDEBUGN if (false) qDebug |
| #endif |
| |
| QT_BEGIN_NAMESPACE |
| |
| // --- QDateTimeEdit --- |
| |
| /*! |
| \class QDateTimeEdit |
| \brief The QDateTimeEdit class provides a widget for editing dates and times. |
| |
| \ingroup basicwidgets |
| |
| |
| QDateTimeEdit allows the user to edit dates by using the keyboard or |
| the arrow keys to increase and decrease date and time values. The |
| arrow keys can be used to move from section to section within the |
| QDateTimeEdit box. Dates and times appear in accordance with the |
| format set; see setDisplayFormat(). |
| |
| \snippet doc/src/snippets/code/src_gui_widgets_qdatetimeedit.cpp 0 |
| |
| Here we've created a new QDateTimeEdit object initialized with |
| today's date, and restricted the valid date range to today plus or |
| minus 365 days. We've set the order to month, day, year. |
| |
| The minimum value for QDateTimeEdit is 14 September 1752, |
| and 2 January 4713BC for QDate. You can change this by calling |
| setMinimumDate(), setMaximumDate(), setMinimumTime(), |
| and setMaximumTime(). |
| |
| \section1 Using a Pop-up Calendar Widget |
| |
| QDateTimeEdit can be configured to allow a QCalendarWidget to be used |
| to select dates. This is enabled by setting the calendarPopup property. |
| Additionally, you can supply a custom calendar widget for use as the |
| calendar pop-up by calling the setCalendarWidget() function. The existing |
| calendar widget can be retrieved with calendarWidget(). |
| |
| \table 100% |
| \row \o \inlineimage windowsxp-datetimeedit.png Screenshot of a Windows XP style date time editing widget |
| \o A date time editing widget shown in the \l{Windows XP Style Widget Gallery}{Windows XP widget style}. |
| \row \o \inlineimage macintosh-datetimeedit.png Screenshot of a Macintosh style date time editing widget |
| \o A date time editing widget shown in the \l{Macintosh Style Widget Gallery}{Macintosh widget style}. |
| \row \o \inlineimage plastique-datetimeedit.png Screenshot of a Plastique style date time editing widget |
| \o A date time editing widget shown in the \l{Plastique Style Widget Gallery}{Plastique widget style}. |
| \endtable |
| |
| \sa QDateEdit, QTimeEdit, QDate, QTime |
| */ |
| |
| /*! |
| \enum QDateTimeEdit::Section |
| |
| \value NoSection |
| \value AmPmSection |
| \value MSecSection |
| \value SecondSection |
| \value MinuteSection |
| \value HourSection |
| \value DaySection |
| \value MonthSection |
| \value YearSection |
| \omitvalue DateSections_Mask |
| \omitvalue TimeSections_Mask |
| */ |
| |
| /*! |
| \fn void QDateTimeEdit::dateTimeChanged(const QDateTime &datetime) |
| |
| This signal is emitted whenever the date or time is changed. The |
| new date and time is passed in \a datetime. |
| */ |
| |
| /*! |
| \fn void QDateTimeEdit::timeChanged(const QTime &time) |
| |
| This signal is emitted whenever the time is changed. The new time |
| is passed in \a time. |
| */ |
| |
| /*! |
| \fn void QDateTimeEdit::dateChanged(const QDate &date) |
| |
| This signal is emitted whenever the date is changed. The new date |
| is passed in \a date. |
| */ |
| |
| |
| /*! |
| Constructs an empty date time editor with a \a parent. |
| */ |
| |
| QDateTimeEdit::QDateTimeEdit(QWidget *parent) |
| : QAbstractSpinBox(*new QDateTimeEditPrivate, parent) |
| { |
| Q_D(QDateTimeEdit); |
| d->init(QDateTime(QDATETIMEEDIT_DATE_INITIAL, QDATETIMEEDIT_TIME_MIN)); |
| } |
| |
| /*! |
| Constructs an empty date time editor with a \a parent. The value |
| is set to \a datetime. |
| */ |
| |
| QDateTimeEdit::QDateTimeEdit(const QDateTime &datetime, QWidget *parent) |
| : QAbstractSpinBox(*new QDateTimeEditPrivate, parent) |
| { |
| Q_D(QDateTimeEdit); |
| d->init(datetime.isValid() ? datetime : QDateTime(QDATETIMEEDIT_DATE_INITIAL, |
| QDATETIMEEDIT_TIME_MIN)); |
| } |
| |
| /*! |
| \fn QDateTimeEdit::QDateTimeEdit(const QDate &date, QWidget *parent) |
| |
| Constructs an empty date time editor with a \a parent. |
| The value is set to \a date. |
| */ |
| |
| QDateTimeEdit::QDateTimeEdit(const QDate &date, QWidget *parent) |
| : QAbstractSpinBox(*new QDateTimeEditPrivate, parent) |
| { |
| Q_D(QDateTimeEdit); |
| d->init(date.isValid() ? date : QDATETIMEEDIT_DATE_INITIAL); |
| } |
| |
| /*! |
| \fn QDateTimeEdit::QDateTimeEdit(const QTime &time, QWidget *parent) |
| |
| Constructs an empty date time editor with a \a parent. |
| The value is set to \a time. |
| */ |
| |
| QDateTimeEdit::QDateTimeEdit(const QTime &time, QWidget *parent) |
| : QAbstractSpinBox(*new QDateTimeEditPrivate, parent) |
| { |
| Q_D(QDateTimeEdit); |
| d->init(time.isValid() ? time : QDATETIMEEDIT_TIME_MIN); |
| } |
| |
| /*! |
| \internal |
| */ |
| |
| QDateTimeEdit::QDateTimeEdit(const QVariant &var, QVariant::Type parserType, QWidget *parent) |
| : QAbstractSpinBox(*new QDateTimeEditPrivate, parent) |
| { |
| Q_D(QDateTimeEdit); |
| d->parserType = parserType; |
| d->init(var); |
| } |
| |
| /*! |
| \property QDateTimeEdit::dateTime |
| \brief the QDateTime that is set in the QDateTimeEdit |
| |
| When setting this property the timespec of the QDateTimeEdit remains the same |
| and the timespec of the new QDateTime is ignored. |
| |
| By default, this property contains a date that refers to January 1, |
| 2000 and a time of 00:00:00 and 0 milliseconds. |
| |
| \sa date, time |
| */ |
| |
| QDateTime QDateTimeEdit::dateTime() const |
| { |
| Q_D(const QDateTimeEdit); |
| return d->value.toDateTime(); |
| } |
| |
| void QDateTimeEdit::setDateTime(const QDateTime &datetime) |
| { |
| Q_D(QDateTimeEdit); |
| if (datetime.isValid()) { |
| d->clearCache(); |
| if (!(d->sections & DateSections_Mask)) |
| setDateRange(datetime.date(), datetime.date()); |
| d->setValue(QDateTime(datetime.date(), datetime.time(), d->spec), EmitIfChanged); |
| } |
| } |
| |
| /*! |
| \property QDateTimeEdit::date |
| \brief the QDate that is set in the widget |
| |
| By default, this property contains a date that refers to January 1, 2000. |
| |
| \sa time, dateTime |
| */ |
| |
| /*! |
| Returns the date of the date time edit. |
| */ |
| QDate QDateTimeEdit::date() const |
| { |
| Q_D(const QDateTimeEdit); |
| return d->value.toDate(); |
| } |
| |
| void QDateTimeEdit::setDate(const QDate &date) |
| { |
| Q_D(QDateTimeEdit); |
| if (date.isValid()) { |
| if (!(d->sections & DateSections_Mask)) |
| setDateRange(date, date); |
| |
| d->clearCache(); |
| d->setValue(QDateTime(date, d->value.toTime(), d->spec), EmitIfChanged); |
| d->updateTimeSpec(); |
| } |
| } |
| |
| /*! |
| \property QDateTimeEdit::time |
| \brief the QTime that is set in the widget |
| |
| By default, this property contains a time of 00:00:00 and 0 milliseconds. |
| |
| \sa date, dateTime |
| */ |
| |
| /*! |
| Returns the time of the date time edit. |
| */ |
| QTime QDateTimeEdit::time() const |
| { |
| Q_D(const QDateTimeEdit); |
| return d->value.toTime(); |
| } |
| |
| void QDateTimeEdit::setTime(const QTime &time) |
| { |
| Q_D(QDateTimeEdit); |
| if (time.isValid()) { |
| d->clearCache(); |
| d->setValue(QDateTime(d->value.toDate(), time, d->spec), EmitIfChanged); |
| } |
| } |
| |
| |
| /*! |
| \property QDateTimeEdit::minimumDateTime |
| \since 4.4 |
| |
| \brief the minimum datetime of the date time edit |
| |
| When setting this property the \l maximumDateTime() is adjusted if |
| necessary to ensure that the range remains valid. If the datetime is |
| not a valid QDateTime object, this function does nothing. |
| |
| The default minimumDateTime can be restored with |
| clearMinimumDateTime() |
| |
| By default, this property contains a date that refers to September 14, |
| 1752 and a time of 00:00:00 and 0 milliseconds. |
| |
| \sa maximumDateTime(), minimumTime(), maximumTime(), minimumDate(), |
| maximumDate(), setDateTimeRange(), setDateRange(), setTimeRange(), |
| clearMaximumDateTime(), clearMinimumDate(), |
| clearMaximumDate(), clearMinimumTime(), clearMaximumTime() |
| */ |
| |
| QDateTime QDateTimeEdit::minimumDateTime() const |
| { |
| Q_D(const QDateTimeEdit); |
| return d->minimum.toDateTime(); |
| } |
| |
| void QDateTimeEdit::clearMinimumDateTime() |
| { |
| setMinimumDateTime(QDateTime(QDATETIMEEDIT_COMPAT_DATE_MIN, QDATETIMEEDIT_TIME_MIN)); |
| } |
| |
| void QDateTimeEdit::setMinimumDateTime(const QDateTime &dt) |
| { |
| Q_D(QDateTimeEdit); |
| if (dt.isValid() && dt.date() >= QDATETIMEEDIT_DATE_MIN) { |
| const QDateTime m = dt.toTimeSpec(d->spec); |
| const QDateTime max = d->maximum.toDateTime(); |
| d->setRange(m, (max > m ? max : m)); |
| } |
| } |
| |
| /*! |
| \property QDateTimeEdit::maximumDateTime |
| \since 4.4 |
| |
| \brief the maximum datetime of the date time edit |
| |
| When setting this property the \l minimumDateTime() is adjusted if |
| necessary to ensure that the range remains valid. If the datetime is |
| not a valid QDateTime object, this function does nothing. |
| |
| The default maximumDateTime can be restored with |
| clearMaximumDateTime(). |
| |
| By default, this property contains a date that refers to 31 December, |
| 7999 and a time of 23:59:59 and 999 milliseconds. |
| |
| \sa minimumDateTime(), minimumTime(), maximumTime(), minimumDate(), |
| maximumDate(), setDateTimeRange(), setDateRange(), setTimeRange(), |
| clearMinimumDateTime(), clearMinimumDate(), |
| clearMaximumDate(), clearMinimumTime(), clearMaximumTime() |
| */ |
| |
| QDateTime QDateTimeEdit::maximumDateTime() const |
| { |
| Q_D(const QDateTimeEdit); |
| return d->maximum.toDateTime(); |
| } |
| |
| void QDateTimeEdit::clearMaximumDateTime() |
| { |
| setMaximumDateTime(QDATETIMEEDIT_DATETIME_MAX); |
| } |
| |
| void QDateTimeEdit::setMaximumDateTime(const QDateTime &dt) |
| { |
| Q_D(QDateTimeEdit); |
| if (dt.isValid() && dt.date() <= QDATETIMEEDIT_DATE_MAX) { |
| const QDateTime m = dt.toTimeSpec(d->spec); |
| const QDateTime min = d->minimum.toDateTime(); |
| d->setRange((min < m ? min : m), m); |
| } |
| } |
| |
| |
| /*! |
| Convenience function to set minimum and maximum date time with one |
| function call. |
| \since 4.4 |
| |
| \snippet doc/src/snippets/code/src_gui_widgets_qdatetimeedit.cpp 1 |
| |
| is analogous to: |
| |
| \snippet doc/src/snippets/code/src_gui_widgets_qdatetimeedit.cpp 2 |
| |
| If either \a min or \a max are not valid, this function does |
| nothing. |
| |
| \sa setMinimumDate(), maximumDate(), setMaximumDate(), |
| clearMinimumDate(), setMinimumTime(), maximumTime(), |
| setMaximumTime(), clearMinimumTime(), QDateTime::isValid() |
| */ |
| |
| void QDateTimeEdit::setDateTimeRange(const QDateTime &min, const QDateTime &max) |
| { |
| Q_D(QDateTimeEdit); |
| const QDateTime minimum = min.toTimeSpec(d->spec); |
| QDateTime maximum = max.toTimeSpec(d->spec); |
| if (min > max) |
| maximum = minimum; |
| d->setRange(minimum, maximum); |
| } |
| |
| /*! |
| \property QDateTimeEdit::minimumDate |
| |
| \brief the minimum date of the date time edit |
| |
| When setting this property the \l maximumDate is adjusted if |
| necessary, to ensure that the range remains valid. If the date is |
| not a valid QDate object, this function does nothing. |
| |
| By default, this property contains a date that refers to September 14, 1752. |
| The minimum date must be at least the first day in year 100, otherwise |
| setMinimumDate() has no effect. |
| |
| \sa minimumTime(), maximumTime(), setDateRange() |
| */ |
| |
| QDate QDateTimeEdit::minimumDate() const |
| { |
| Q_D(const QDateTimeEdit); |
| return d->minimum.toDate(); |
| } |
| |
| void QDateTimeEdit::setMinimumDate(const QDate &min) |
| { |
| Q_D(QDateTimeEdit); |
| if (min.isValid() && min >= QDATETIMEEDIT_DATE_MIN) { |
| setMinimumDateTime(QDateTime(min, d->minimum.toTime(), d->spec)); |
| } |
| } |
| |
| void QDateTimeEdit::clearMinimumDate() |
| { |
| setMinimumDate(QDATETIMEEDIT_COMPAT_DATE_MIN); |
| } |
| |
| /*! |
| \property QDateTimeEdit::maximumDate |
| |
| \brief the maximum date of the date time edit |
| |
| When setting this property the \l minimumDate is adjusted if |
| necessary to ensure that the range remains valid. If the date is |
| not a valid QDate object, this function does nothing. |
| |
| By default, this property contains a date that refers to December 31, 7999. |
| |
| \sa minimumDate, minimumTime, maximumTime, setDateRange() |
| */ |
| |
| QDate QDateTimeEdit::maximumDate() const |
| { |
| Q_D(const QDateTimeEdit); |
| return d->maximum.toDate(); |
| } |
| |
| void QDateTimeEdit::setMaximumDate(const QDate &max) |
| { |
| Q_D(QDateTimeEdit); |
| if (max.isValid()) { |
| setMaximumDateTime(QDateTime(max, d->maximum.toTime(), d->spec)); |
| } |
| } |
| |
| void QDateTimeEdit::clearMaximumDate() |
| { |
| setMaximumDate(QDATETIMEEDIT_DATE_MAX); |
| } |
| |
| /*! |
| \property QDateTimeEdit::minimumTime |
| |
| \brief the minimum time of the date time edit |
| |
| When setting this property the \l maximumTime is adjusted if |
| necessary, to ensure that the range remains valid. If the time is |
| not a valid QTime object, this function does nothing. |
| |
| By default, this property contains a time of 00:00:00 and 0 milliseconds. |
| |
| \sa maximumTime, minimumDate, maximumDate, setTimeRange() |
| */ |
| |
| QTime QDateTimeEdit::minimumTime() const |
| { |
| Q_D(const QDateTimeEdit); |
| return d->minimum.toTime(); |
| } |
| |
| void QDateTimeEdit::setMinimumTime(const QTime &min) |
| { |
| Q_D(QDateTimeEdit); |
| if (min.isValid()) { |
| const QDateTime m(d->minimum.toDate(), min, d->spec); |
| setMinimumDateTime(m); |
| } |
| } |
| |
| void QDateTimeEdit::clearMinimumTime() |
| { |
| setMinimumTime(QDATETIMEEDIT_TIME_MIN); |
| } |
| |
| /*! |
| \property QDateTimeEdit::maximumTime |
| |
| \brief the maximum time of the date time edit |
| |
| When setting this property, the \l minimumTime is adjusted if |
| necessary to ensure that the range remains valid. If the time is |
| not a valid QTime object, this function does nothing. |
| |
| By default, this property contains a time of 23:59:59 and 999 milliseconds. |
| |
| \sa minimumTime, minimumDate, maximumDate, setTimeRange() |
| */ |
| QTime QDateTimeEdit::maximumTime() const |
| { |
| Q_D(const QDateTimeEdit); |
| return d->maximum.toTime(); |
| } |
| |
| void QDateTimeEdit::setMaximumTime(const QTime &max) |
| { |
| Q_D(QDateTimeEdit); |
| if (max.isValid()) { |
| const QDateTime m(d->maximum.toDate(), max); |
| setMaximumDateTime(m); |
| } |
| } |
| |
| void QDateTimeEdit::clearMaximumTime() |
| { |
| setMaximumTime(QDATETIMEEDIT_TIME_MAX); |
| } |
| |
| /*! |
| Convenience function to set minimum and maximum date with one |
| function call. |
| |
| \snippet doc/src/snippets/code/src_gui_widgets_qdatetimeedit.cpp 3 |
| |
| is analogous to: |
| |
| \snippet doc/src/snippets/code/src_gui_widgets_qdatetimeedit.cpp 4 |
| |
| If either \a min or \a max are not valid, this function does |
| nothing. |
| |
| \sa setMinimumDate(), maximumDate(), setMaximumDate(), |
| clearMinimumDate(), setMinimumTime(), maximumTime(), |
| setMaximumTime(), clearMinimumTime(), QDate::isValid() |
| */ |
| |
| void QDateTimeEdit::setDateRange(const QDate &min, const QDate &max) |
| { |
| Q_D(QDateTimeEdit); |
| if (min.isValid() && max.isValid()) { |
| setDateTimeRange(QDateTime(min, d->minimum.toTime(), d->spec), |
| QDateTime(max, d->maximum.toTime(), d->spec)); |
| } |
| } |
| |
| /*! |
| Convenience function to set minimum and maximum time with one |
| function call. |
| |
| \snippet doc/src/snippets/code/src_gui_widgets_qdatetimeedit.cpp 5 |
| |
| is analogous to: |
| |
| \snippet doc/src/snippets/code/src_gui_widgets_qdatetimeedit.cpp 6 |
| |
| If either \a min or \a max are not valid, this function does |
| nothing. |
| |
| \sa setMinimumDate(), maximumDate(), setMaximumDate(), |
| clearMinimumDate(), setMinimumTime(), maximumTime(), |
| setMaximumTime(), clearMinimumTime(), QTime::isValid() |
| */ |
| |
| void QDateTimeEdit::setTimeRange(const QTime &min, const QTime &max) |
| { |
| Q_D(QDateTimeEdit); |
| if (min.isValid() && max.isValid()) { |
| setDateTimeRange(QDateTime(d->minimum.toDate(), min, d->spec), |
| QDateTime(d->maximum.toDate(), max, d->spec)); |
| } |
| } |
| |
| /*! |
| \property QDateTimeEdit::displayedSections |
| |
| \brief the currently displayed fields of the date time edit |
| |
| Returns a bit set of the displayed sections for this format. |
| \a setDisplayFormat(), displayFormat() |
| */ |
| |
| QDateTimeEdit::Sections QDateTimeEdit::displayedSections() const |
| { |
| Q_D(const QDateTimeEdit); |
| return d->sections; |
| } |
| |
| /*! |
| \property QDateTimeEdit::currentSection |
| |
| \brief the current section of the spinbox |
| \a setCurrentSection() |
| */ |
| |
| QDateTimeEdit::Section QDateTimeEdit::currentSection() const |
| { |
| Q_D(const QDateTimeEdit); |
| #ifdef QT_KEYPAD_NAVIGATION |
| if (QApplication::keypadNavigationEnabled() && d->focusOnButton) |
| return NoSection; |
| #endif |
| return d->convertToPublic(d->sectionType(d->currentSectionIndex)); |
| } |
| |
| void QDateTimeEdit::setCurrentSection(Section section) |
| { |
| Q_D(QDateTimeEdit); |
| if (section == NoSection || !(section & d->sections)) |
| return; |
| |
| d->updateCache(d->value, d->displayText()); |
| const int size = d->sectionNodes.size(); |
| int index = d->currentSectionIndex + 1; |
| for (int i=0; i<2; ++i) { |
| while (index < size) { |
| if (d->convertToPublic(d->sectionType(index)) == section) { |
| d->edit->setCursorPosition(d->sectionPos(index)); |
| QDTEDEBUG << d->sectionPos(index); |
| return; |
| } |
| ++index; |
| } |
| index = 0; |
| } |
| } |
| |
| /*! |
| \since 4.3 |
| |
| Returns the Section at \a index. |
| |
| If the format is 'yyyy/MM/dd', sectionAt(0) returns YearSection, |
| sectionAt(1) returns MonthSection, and sectionAt(2) returns |
| YearSection, |
| */ |
| |
| QDateTimeEdit::Section QDateTimeEdit::sectionAt(int index) const |
| { |
| Q_D(const QDateTimeEdit); |
| if (index < 0 || index >= d->sectionNodes.size()) |
| return NoSection; |
| return d->convertToPublic(d->sectionType(index)); |
| } |
| |
| /*! |
| \since 4.3 |
| |
| \property QDateTimeEdit::sectionCount |
| |
| \brief the number of sections displayed. |
| If the format is 'yyyy/yy/yyyy', sectionCount returns 3 |
| */ |
| |
| int QDateTimeEdit::sectionCount() const |
| { |
| Q_D(const QDateTimeEdit); |
| return d->sectionNodes.size(); |
| } |
| |
| |
| /*! |
| \since 4.3 |
| |
| \property QDateTimeEdit::currentSectionIndex |
| |
| \brief the current section index of the spinbox |
| |
| If the format is 'yyyy/MM/dd', the displayText is '2001/05/21' and |
| the cursorPosition is 5 currentSectionIndex returns 1. If the |
| cursorPosition is 3 currentSectionIndex is 0 etc. |
| |
| \a setCurrentSection() |
| \sa currentSection() |
| */ |
| |
| int QDateTimeEdit::currentSectionIndex() const |
| { |
| Q_D(const QDateTimeEdit); |
| return d->currentSectionIndex; |
| } |
| |
| void QDateTimeEdit::setCurrentSectionIndex(int index) |
| { |
| Q_D(QDateTimeEdit); |
| if (index < 0 || index >= d->sectionNodes.size()) |
| return; |
| d->edit->setCursorPosition(d->sectionPos(index)); |
| } |
| |
| /*! |
| \since 4.4 |
| |
| \brief Returns the calendar widget for the editor if calendarPopup is |
| set to true and (sections() & DateSections_Mask) != 0. |
| |
| This function creates and returns a calendar widget if none has been set. |
| */ |
| |
| |
| QCalendarWidget *QDateTimeEdit::calendarWidget() const |
| { |
| Q_D(const QDateTimeEdit); |
| if (!d->calendarPopup || !(d->sections & QDateTimeParser::DateSectionMask)) |
| return 0; |
| if (!d->monthCalendar) { |
| const_cast<QDateTimeEditPrivate*>(d)->initCalendarPopup(); |
| } |
| return d->monthCalendar->calendarWidget(); |
| } |
| |
| /*! |
| \since 4.4 |
| |
| Sets the given \a calendarWidget as the widget to be used for the calendar |
| pop-up. The editor does not automatically take ownership of the calendar widget. |
| |
| \note calendarPopup must be set to true before setting the calendar widget. |
| \sa calendarPopup |
| */ |
| void QDateTimeEdit::setCalendarWidget(QCalendarWidget *calendarWidget) |
| { |
| Q_D(QDateTimeEdit); |
| if (!calendarWidget) { |
| qWarning("QDateTimeEdit::setCalendarWidget: Cannot set a null calendar widget"); |
| return; |
| } |
| |
| if (!d->calendarPopup) { |
| qWarning("QDateTimeEdit::setCalendarWidget: calendarPopup is set to false"); |
| return; |
| } |
| |
| if (!(d->display & QDateTimeParser::DateSectionMask)) { |
| qWarning("QDateTimeEdit::setCalendarWidget: no date sections specified"); |
| return; |
| } |
| d->initCalendarPopup(calendarWidget); |
| } |
| |
| |
| /*! |
| \since 4.2 |
| |
| Selects \a section. If \a section doesn't exist in the currently |
| displayed sections this function does nothing. If \a section is |
| NoSection this function will unselect all text in the editor. |
| Otherwise this function will move the cursor and the current section |
| to the selected section. |
| |
| \sa currentSection() |
| */ |
| |
| void QDateTimeEdit::setSelectedSection(Section section) |
| { |
| Q_D(QDateTimeEdit); |
| if (section == NoSection) { |
| d->edit->setSelection(d->edit->cursorPosition(), 0); |
| } else if (section & d->sections) { |
| if (currentSection() != section) |
| setCurrentSection(section); |
| d->setSelected(d->currentSectionIndex); |
| } |
| } |
| |
| |
| |
| /*! |
| \fn QString QDateTimeEdit::sectionText(Section section) const |
| |
| Returns the text from the given \a section. |
| |
| \sa currentSection() |
| */ |
| |
| QString QDateTimeEdit::sectionText(Section section) const |
| { |
| Q_D(const QDateTimeEdit); |
| if (section == QDateTimeEdit::NoSection || !(section & d->sections)) { |
| return QString(); |
| } |
| |
| d->updateCache(d->value, d->displayText()); |
| const int sectionIndex = d->absoluteIndex(section, 0); |
| return d->sectionText(sectionIndex); |
| } |
| |
| /*! |
| \property QDateTimeEdit::displayFormat |
| |
| \brief the format used to display the time/date of the date time edit |
| |
| This format is the same as the one used described in QDateTime::toString() |
| and QDateTime::fromString() |
| |
| Example format strings (assuming that the date is 2nd of July 1969): |
| |
| \table |
| \header \i Format \i Result |
| \row \i dd.MM.yyyy \i 02.07.1969 |
| \row \i MMM d yy \i Jul 2 69 |
| \row \i MMMM d yy \i July 2 69 |
| \endtable |
| |
| Note that if you specify a two digit year, it will be interpreted |
| to be in the century in which the date time edit was initialized. |
| The default century is the 21 (2000-2099). |
| |
| If you specify an invalid format the format will not be set. |
| |
| \sa QDateTime::toString(), displayedSections() |
| */ |
| |
| QString QDateTimeEdit::displayFormat() const |
| { |
| Q_D(const QDateTimeEdit); |
| return isRightToLeft() ? d->unreversedFormat : d->displayFormat; |
| } |
| |
| template<typename C> static inline C reverse(const C &l) |
| { |
| C ret; |
| for (int i=l.size() - 1; i>=0; --i) |
| ret.append(l.at(i)); |
| return ret; |
| } |
| |
| void QDateTimeEdit::setDisplayFormat(const QString &format) |
| { |
| Q_D(QDateTimeEdit); |
| if (d->parseFormat(format)) { |
| d->unreversedFormat.clear(); |
| if (isRightToLeft()) { |
| d->unreversedFormat = format; |
| d->displayFormat.clear(); |
| for (int i=d->sectionNodes.size() - 1; i>=0; --i) { |
| d->displayFormat += d->separators.at(i + 1); |
| d->displayFormat += d->sectionFormat(i); |
| } |
| d->displayFormat += d->separators.at(0); |
| d->separators = reverse(d->separators); |
| d->sectionNodes = reverse(d->sectionNodes); |
| } |
| |
| d->formatExplicitlySet = true; |
| d->sections = d->convertSections(d->display); |
| d->clearCache(); |
| |
| d->currentSectionIndex = qMin(d->currentSectionIndex, d->sectionNodes.size() - 1); |
| const bool timeShown = (d->sections & TimeSections_Mask); |
| const bool dateShown = (d->sections & DateSections_Mask); |
| Q_ASSERT(dateShown || timeShown); |
| if (timeShown && !dateShown) { |
| setDateRange(d->value.toDate(), d->value.toDate()); |
| } else if (dateShown && !timeShown) { |
| setTimeRange(QDATETIMEEDIT_TIME_MIN, QDATETIMEEDIT_TIME_MAX); |
| d->value = QDateTime(d->value.toDate(), QTime(), d->spec); |
| } |
| d->updateEdit(); |
| d->_q_editorCursorPositionChanged(-1, 0); |
| } |
| } |
| |
| /*! |
| \property QDateTimeEdit::calendarPopup |
| \brief the current calendar pop-up showing mode. |
| \since 4.2 |
| |
| The calendar pop-up will be shown upon clicking the arrow button. |
| This property is valid only if there is a valid date display format. |
| |
| \sa setDisplayFormat() |
| */ |
| |
| bool QDateTimeEdit::calendarPopup() const |
| { |
| Q_D(const QDateTimeEdit); |
| return d->calendarPopup; |
| } |
| |
| void QDateTimeEdit::setCalendarPopup(bool enable) |
| { |
| Q_D(QDateTimeEdit); |
| if (enable == d->calendarPopup) |
| return; |
| setAttribute(Qt::WA_MacShowFocusRect, !enable); |
| d->calendarPopup = enable; |
| #ifdef QT_KEYPAD_NAVIGATION |
| if (!enable) |
| d->focusOnButton = false; |
| #endif |
| d->updateEditFieldGeometry(); |
| update(); |
| } |
| |
| /*! |
| \property QDateTimeEdit::timeSpec |
| \brief the current timespec used by the date time edit. |
| \since 4.4 |
| */ |
| |
| Qt::TimeSpec QDateTimeEdit::timeSpec() const |
| { |
| Q_D(const QDateTimeEdit); |
| return d->spec; |
| } |
| |
| void QDateTimeEdit::setTimeSpec(Qt::TimeSpec spec) |
| { |
| Q_D(QDateTimeEdit); |
| if (spec != d->spec) { |
| d->spec = spec; |
| d->updateTimeSpec(); |
| } |
| } |
| |
| /*! |
| \reimp |
| */ |
| |
| QSize QDateTimeEdit::sizeHint() const |
| { |
| Q_D(const QDateTimeEdit); |
| if (d->cachedSizeHint.isEmpty()) { |
| ensurePolished(); |
| |
| const QFontMetrics fm(fontMetrics()); |
| int h = d->edit->sizeHint().height(); |
| int w = 0; |
| QString s; |
| s = d->textFromValue(d->minimum) + QLatin1String(" "); |
| w = qMax<int>(w, fm.width(s)); |
| s = d->textFromValue(d->maximum) + QLatin1String(" "); |
| w = qMax<int>(w, fm.width(s)); |
| if (d->specialValueText.size()) { |
| s = d->specialValueText; |
| w = qMax<int>(w, fm.width(s)); |
| } |
| w += 2; // cursor blinking space |
| |
| QSize hint(w, h); |
| |
| #ifdef Q_WS_MAC |
| if (d->calendarPopupEnabled()) { |
| QStyleOptionComboBox opt; |
| d->cachedSizeHint = style()->sizeFromContents(QStyle::CT_ComboBox, &opt, hint, this); |
| } else { |
| #else |
| { |
| #endif |
| QSize extra(35, 6); |
| QStyleOptionSpinBox opt; |
| initStyleOption(&opt); |
| opt.rect.setSize(hint + extra); |
| extra += hint - style()->subControlRect(QStyle::CC_SpinBox, &opt, |
| QStyle::SC_SpinBoxEditField, this).size(); |
| // get closer to final result by repeating the calculation |
| opt.rect.setSize(hint + extra); |
| extra += hint - style()->subControlRect(QStyle::CC_SpinBox, &opt, |
| QStyle::SC_SpinBoxEditField, this).size(); |
| hint += extra; |
| |
| opt.rect = rect(); |
| d->cachedSizeHint = style()->sizeFromContents(QStyle::CT_SpinBox, &opt, hint, this) |
| .expandedTo(QApplication::globalStrut()); |
| } |
| |
| d->cachedMinimumSizeHint = d->cachedSizeHint; |
| // essentially make minimumSizeHint return the same as sizeHint for datetimeedits |
| } |
| return d->cachedSizeHint; |
| } |
| |
| /*! |
| \reimp |
| */ |
| |
| bool QDateTimeEdit::event(QEvent *event) |
| { |
| Q_D(QDateTimeEdit); |
| switch (event->type()) { |
| case QEvent::ApplicationLayoutDirectionChange: { |
| const bool was = d->formatExplicitlySet; |
| const QString oldFormat = d->displayFormat; |
| d->displayFormat.clear(); |
| setDisplayFormat(oldFormat); |
| d->formatExplicitlySet = was; |
| break; } |
| case QEvent::LocaleChange: |
| d->updateEdit(); |
| break; |
| case QEvent::StyleChange: |
| #ifdef Q_WS_MAC |
| case QEvent::MacSizeChange: |
| #endif |
| d->setLayoutItemMargins(QStyle::SE_DateTimeEditLayoutItem); |
| break; |
| default: |
| break; |
| } |
| return QAbstractSpinBox::event(event); |
| } |
| |
| /*! |
| \reimp |
| */ |
| |
| void QDateTimeEdit::clear() |
| { |
| Q_D(QDateTimeEdit); |
| d->clearSection(d->currentSectionIndex); |
| } |
| /*! |
| \reimp |
| */ |
| |
| void QDateTimeEdit::keyPressEvent(QKeyEvent *event) |
| { |
| Q_D(QDateTimeEdit); |
| int oldCurrent = d->currentSectionIndex; |
| bool select = true; |
| bool inserted = false; |
| |
| switch (event->key()) { |
| #ifdef QT_KEYPAD_NAVIGATION |
| case Qt::Key_NumberSign: //shortcut to popup calendar |
| if (QApplication::keypadNavigationEnabled() && d->calendarPopupEnabled()) { |
| d->initCalendarPopup(); |
| d->positionCalendarPopup(); |
| d->monthCalendar->show(); |
| return; |
| } |
| break; |
| case Qt::Key_Select: |
| if (QApplication::keypadNavigationEnabled()) { |
| if (hasEditFocus()) { |
| if (d->focusOnButton) { |
| d->initCalendarPopup(); |
| d->positionCalendarPopup(); |
| d->monthCalendar->show(); |
| d->focusOnButton = false; |
| return; |
| } |
| setEditFocus(false); |
| selectAll(); |
| } else { |
| setEditFocus(true); |
| |
| //hide cursor |
| d->edit->d_func()->setCursorVisible(false); |
| d->edit->d_func()->control->setCursorBlinkPeriod(0); |
| d->setSelected(0); |
| } |
| } |
| return; |
| #endif |
| case Qt::Key_Enter: |
| case Qt::Key_Return: |
| d->interpret(AlwaysEmit); |
| d->setSelected(d->currentSectionIndex, true); |
| event->ignore(); |
| emit editingFinished(); |
| return; |
| default: |
| #ifdef QT_KEYPAD_NAVIGATION |
| if (QApplication::keypadNavigationEnabled() && !hasEditFocus() |
| && !event->text().isEmpty() && event->text().at(0).isLetterOrNumber()) { |
| setEditFocus(true); |
| |
| //hide cursor |
| d->edit->d_func()->setCursorVisible(false); |
| d->edit->d_func()->control->setCursorBlinkPeriod(0); |
| d->setSelected(0); |
| oldCurrent = 0; |
| } |
| #endif |
| if (!d->isSeparatorKey(event)) { |
| inserted = select = !event->text().isEmpty() && event->text().at(0).isPrint() |
| && !(event->modifiers() & ~(Qt::ShiftModifier|Qt::KeypadModifier)); |
| break; |
| } |
| case Qt::Key_Left: |
| case Qt::Key_Right: |
| if (event->key() == Qt::Key_Left || event->key() == Qt::Key_Right) { |
| if ( |
| #ifdef QT_KEYPAD_NAVIGATION |
| QApplication::keypadNavigationEnabled() && !hasEditFocus() |
| || !QApplication::keypadNavigationEnabled() && |
| #endif |
| !(event->modifiers() & Qt::ControlModifier)) { |
| select = false; |
| break; |
| } |
| #ifdef Q_WS_MAC |
| else |
| #ifdef QT_KEYPAD_NAVIGATION |
| if (!QApplication::keypadNavigationEnabled()) |
| #endif |
| { |
| select = (event->modifiers() & Qt::ShiftModifier); |
| break; |
| } |
| #endif |
| } |
| // else fall through |
| case Qt::Key_Backtab: |
| case Qt::Key_Tab: { |
| event->accept(); |
| if (d->specialValue()) { |
| d->edit->setSelection(d->edit->cursorPosition(), 0); |
| return; |
| } |
| const bool forward = event->key() != Qt::Key_Left && event->key() != Qt::Key_Backtab |
| && (event->key() != Qt::Key_Tab || !(event->modifiers() & Qt::ShiftModifier)); |
| #ifdef QT_KEYPAD_NAVIGATION |
| int newSection = d->nextPrevSection(d->currentSectionIndex, forward); |
| if (QApplication::keypadNavigationEnabled()) { |
| if (d->focusOnButton) { |
| newSection = forward ? 0 : d->sectionNodes.size() - 1; |
| d->focusOnButton = false; |
| update(); |
| } else if (newSection < 0 && select && d->calendarPopupEnabled()) { |
| setSelectedSection(NoSection); |
| d->focusOnButton = true; |
| update(); |
| return; |
| } |
| } |
| // only allow date/time sections to be selected. |
| if (newSection & ~(QDateTimeParser::TimeSectionMask | QDateTimeParser::DateSectionMask)) |
| return; |
| #endif |
| //key tab and backtab will be managed thrgout QWidget::event |
| if (event->key() != Qt::Key_Backtab && event->key() != Qt::Key_Tab) |
| focusNextPrevChild(forward); |
| |
| return; } |
| } |
| QAbstractSpinBox::keyPressEvent(event); |
| if (select && !d->edit->hasSelectedText()) { |
| if (inserted && d->sectionAt(d->edit->cursorPosition()) == QDateTimeParser::NoSectionIndex) { |
| QString str = d->displayText(); |
| int pos = d->edit->cursorPosition(); |
| if (validate(str, pos) == QValidator::Acceptable |
| && (d->sectionNodes.at(oldCurrent).count != 1 |
| || d->sectionMaxSize(oldCurrent) == d->sectionSize(oldCurrent) |
| || d->skipToNextSection(oldCurrent, d->value.toDateTime(), d->sectionText(oldCurrent)))) { |
| QDTEDEBUG << "Setting currentsection to" |
| << d->closestSection(d->edit->cursorPosition(), true) << event->key() |
| << oldCurrent << str; |
| const int tmp = d->closestSection(d->edit->cursorPosition(), true); |
| if (tmp >= 0) |
| d->currentSectionIndex = tmp; |
| } |
| } |
| if (d->currentSectionIndex != oldCurrent) { |
| d->setSelected(d->currentSectionIndex); |
| } |
| } |
| if (d->specialValue()) { |
| d->edit->setSelection(d->edit->cursorPosition(), 0); |
| } |
| } |
| |
| /*! |
| \reimp |
| */ |
| |
| #ifndef QT_NO_WHEELEVENT |
| void QDateTimeEdit::wheelEvent(QWheelEvent *event) |
| { |
| QAbstractSpinBox::wheelEvent(event); |
| } |
| #endif |
| |
| /*! |
| \reimp |
| */ |
| |
| void QDateTimeEdit::focusInEvent(QFocusEvent *event) |
| { |
| Q_D(QDateTimeEdit); |
| QAbstractSpinBox::focusInEvent(event); |
| QString *frm = 0; |
| const int oldPos = d->edit->cursorPosition(); |
| if (!d->formatExplicitlySet) { |
| if (d->displayFormat == d->defaultTimeFormat) { |
| frm = &d->defaultTimeFormat; |
| } else if (d->displayFormat == d->defaultDateFormat) { |
| frm = &d->defaultDateFormat; |
| } else if (d->displayFormat == d->defaultDateTimeFormat) { |
| frm = &d->defaultDateTimeFormat; |
| } |
| |
| if (frm) { |
| d->readLocaleSettings(); |
| if (d->displayFormat != *frm) { |
| setDisplayFormat(*frm); |
| d->formatExplicitlySet = false; |
| d->edit->setCursorPosition(oldPos); |
| } |
| } |
| } |
| const bool oldHasHadFocus = d->hasHadFocus; |
| d->hasHadFocus = true; |
| bool first = true; |
| switch (event->reason()) { |
| case Qt::BacktabFocusReason: |
| first = false; |
| break; |
| case Qt::MouseFocusReason: |
| case Qt::PopupFocusReason: |
| return; |
| case Qt::ActiveWindowFocusReason: |
| if (oldHasHadFocus) |
| return; |
| case Qt::ShortcutFocusReason: |
| case Qt::TabFocusReason: |
| default: |
| break; |
| } |
| if (isRightToLeft()) |
| first = !first; |
| d->updateEdit(); // needed to make it update specialValueText |
| |
| d->setSelected(first ? 0 : d->sectionNodes.size() - 1); |
| } |
| |
| /*! |
| \reimp |
| */ |
| |
| bool QDateTimeEdit::focusNextPrevChild(bool next) |
| { |
| Q_D(QDateTimeEdit); |
| const int newSection = d->nextPrevSection(d->currentSectionIndex, next); |
| switch (d->sectionType(newSection)) { |
| case QDateTimeParser::NoSection: |
| case QDateTimeParser::FirstSection: |
| case QDateTimeParser::LastSection: |
| return QAbstractSpinBox::focusNextPrevChild(next); |
| default: |
| d->edit->deselect(); |
| d->edit->setCursorPosition(d->sectionPos(newSection)); |
| QDTEDEBUG << d->sectionPos(newSection); |
| d->setSelected(newSection, true); |
| return false; |
| } |
| } |
| |
| /*! |
| \reimp |
| */ |
| |
| void QDateTimeEdit::stepBy(int steps) |
| { |
| Q_D(QDateTimeEdit); |
| #ifdef QT_KEYPAD_NAVIGATION |
| // with keypad navigation and not editFocus, left right change the date/time by a fixed amount. |
| if (QApplication::keypadNavigationEnabled() && !hasEditFocus()) { |
| // if date based, shift by day. else shift by 15min |
| if (d->sections & DateSections_Mask) { |
| setDateTime(dateTime().addDays(steps)); |
| } else { |
| int minutes = time().hour()*60 + time().minute(); |
| int blocks = minutes/15; |
| blocks += steps; |
| /* rounding involved */ |
| if (minutes % 15) { |
| if (steps < 0) { |
| blocks += 1; // do one less step; |
| } |
| } |
| |
| minutes = blocks * 15; |
| |
| /* need to take wrapping into account */ |
| if (!d->wrapping) { |
| int max_minutes = d->maximum.toTime().hour()*60 + d->maximum.toTime().minute(); |
| int min_minutes = d->minimum.toTime().hour()*60 + d->minimum.toTime().minute(); |
| |
| if (minutes >= max_minutes) { |
| setTime(maximumTime()); |
| return; |
| } else if (minutes <= min_minutes) { |
| setTime(minimumTime()); |
| return; |
| } |
| } |
| setTime(QTime(minutes/60, minutes%60)); |
| } |
| return; |
| } |
| #endif |
| // don't optimize away steps == 0. This is the only way to select |
| // the currentSection in Qt 4.1.x |
| if (d->specialValue() && displayedSections() != AmPmSection) { |
| for (int i=0; i<d->sectionNodes.size(); ++i) { |
| if (d->sectionType(i) != QDateTimeParser::AmPmSection) { |
| d->currentSectionIndex = i; |
| break; |
| } |
| } |
| } |
| d->setValue(d->stepBy(d->currentSectionIndex, steps, false), EmitIfChanged); |
| d->updateCache(d->value, d->displayText()); |
| |
| d->setSelected(d->currentSectionIndex); |
| d->updateTimeSpec(); |
| } |
| |
| /*! |
| This virtual function is used by the date time edit whenever it |
| needs to display \a dateTime. |
| |
| If you reimplement this, you may also need to reimplement validate(). |
| |
| \sa dateTimeFromText(), validate() |
| */ |
| QString QDateTimeEdit::textFromDateTime(const QDateTime &dateTime) const |
| { |
| Q_D(const QDateTimeEdit); |
| return locale().toString(dateTime, d->displayFormat); |
| } |
| |
| |
| /*! |
| Returns an appropriate datetime for the given \a text. |
| |
| This virtual function is used by the datetime edit whenever it |
| needs to interpret text entered by the user as a value. |
| |
| \sa textFromDateTime(), validate() |
| */ |
| QDateTime QDateTimeEdit::dateTimeFromText(const QString &text) const |
| { |
| Q_D(const QDateTimeEdit); |
| QString copy = text; |
| int pos = d->edit->cursorPosition(); |
| QValidator::State state = QValidator::Acceptable; |
| return d->validateAndInterpret(copy, pos, state); |
| } |
| |
| /*! |
| \reimp |
| */ |
| |
| QValidator::State QDateTimeEdit::validate(QString &text, int &pos) const |
| { |
| Q_D(const QDateTimeEdit); |
| QValidator::State state; |
| d->validateAndInterpret(text, pos, state); |
| return state; |
| } |
| |
| /*! |
| \reimp |
| */ |
| |
| |
| void QDateTimeEdit::fixup(QString &input) const |
| { |
| Q_D(const QDateTimeEdit); |
| QValidator::State state; |
| int copy = d->edit->cursorPosition(); |
| |
| d->validateAndInterpret(input, copy, state, true); |
| } |
| |
| |
| /*! |
| \reimp |
| */ |
| |
| QDateTimeEdit::StepEnabled QDateTimeEdit::stepEnabled() const |
| { |
| Q_D(const QDateTimeEdit); |
| if (d->readOnly) |
| return StepEnabled(0); |
| if (d->specialValue()) { |
| return (d->minimum == d->maximum ? StepEnabled(0) : StepEnabled(StepUpEnabled)); |
| } |
| |
| QAbstractSpinBox::StepEnabled ret = 0; |
| |
| #ifdef QT_KEYPAD_NAVIGATION |
| if (QApplication::keypadNavigationEnabled() && !hasEditFocus()) { |
| if (d->wrapping) |
| return StepEnabled(StepUpEnabled | StepDownEnabled); |
| // 3 cases. date, time, datetime. each case look |
| // at just the relavant component. |
| QVariant max, min, val; |
| if (!(d->sections & DateSections_Mask)) { |
| // time only, no date |
| max = d->maximum.toTime(); |
| min = d->minimum.toTime(); |
| val = d->value.toTime(); |
| } else if (!(d->sections & TimeSections_Mask)) { |
| // date only, no time |
| max = d->maximum.toDate(); |
| min = d->minimum.toDate(); |
| val = d->value.toDate(); |
| } else { |
| // both |
| max = d->maximum; |
| min = d->minimum; |
| val = d->value; |
| } |
| if (val != min) |
| ret |= QAbstractSpinBox::StepDownEnabled; |
| if (val != max) |
| ret |= QAbstractSpinBox::StepUpEnabled; |
| return ret; |
| } |
| #endif |
| switch (d->sectionType(d->currentSectionIndex)) { |
| case QDateTimeParser::NoSection: |
| case QDateTimeParser::FirstSection: |
| case QDateTimeParser::LastSection: return 0; |
| default: break; |
| } |
| if (d->wrapping) |
| return StepEnabled(StepDownEnabled|StepUpEnabled); |
| |
| QVariant v = d->stepBy(d->currentSectionIndex, 1, true); |
| if (v != d->value) { |
| ret |= QAbstractSpinBox::StepUpEnabled; |
| } |
| v = d->stepBy(d->currentSectionIndex, -1, true); |
| if (v != d->value) { |
| ret |= QAbstractSpinBox::StepDownEnabled; |
| } |
| |
| return ret; |
| } |
| |
| |
| /*! |
| \reimp |
| */ |
| |
| void QDateTimeEdit::mousePressEvent(QMouseEvent *event) |
| { |
| Q_D(QDateTimeEdit); |
| if (!d->calendarPopupEnabled()) { |
| QAbstractSpinBox::mousePressEvent(event); |
| return; |
| } |
| d->updateHoverControl(event->pos()); |
| if (d->hoverControl == QStyle::SC_ComboBoxArrow) { |
| event->accept(); |
| if (d->readOnly) { |
| return; |
| } |
| d->updateArrow(QStyle::State_Sunken); |
| d->initCalendarPopup(); |
| d->positionCalendarPopup(); |
| //Show the calendar |
| d->monthCalendar->show(); |
| } else { |
| QAbstractSpinBox::mousePressEvent(event); |
| } |
| } |
| |
| /*! |
| \class QTimeEdit |
| \brief The QTimeEdit class provides a widget for editing times based on |
| the QDateTimeEdit widget. |
| |
| \ingroup basicwidgets |
| |
| |
| Many of the properties and functions provided by QTimeEdit are implemented in |
| QDateTimeEdit. The following properties are most relevant to users of this |
| class: |
| |
| \list |
| \o \l{QDateTimeEdit::time}{time} holds the date displayed by the widget. |
| \o \l{QDateTimeEdit::minimumTime}{minimumTime} defines the minimum (earliest) time |
| that can be set by the user. |
| \o \l{QDateTimeEdit::maximumTime}{maximumTime} defines the maximum (latest) time |
| that can be set by the user. |
| \o \l{QDateTimeEdit::displayFormat}{displayFormat} contains a string that is used |
| to format the time displayed in the widget. |
| \endlist |
| |
| \table 100% |
| \row \o \inlineimage windowsxp-timeedit.png Screenshot of a Windows XP style time editing widget |
| \o A time editing widget shown in the \l{Windows XP Style Widget Gallery}{Windows XP widget style}. |
| \row \o \inlineimage macintosh-timeedit.png Screenshot of a Macintosh style time editing widget |
| \o A time editing widget shown in the \l{Macintosh Style Widget Gallery}{Macintosh widget style}. |
| \row \o \inlineimage plastique-timeedit.png Screenshot of a Plastique style time editing widget |
| \o A time editing widget shown in the \l{Plastique Style Widget Gallery}{Plastique widget style}. |
| \endtable |
| |
| \sa QDateEdit, QDateTimeEdit |
| */ |
| |
| /*! |
| Constructs an empty time editor with a \a parent. |
| */ |
| |
| |
| QTimeEdit::QTimeEdit(QWidget *parent) |
| : QDateTimeEdit(QDATETIMEEDIT_TIME_MIN, QVariant::Time, parent) |
| { |
| } |
| |
| /*! |
| Constructs an empty time editor with a \a parent. The time is set |
| to \a time. |
| */ |
| |
| QTimeEdit::QTimeEdit(const QTime &time, QWidget *parent) |
| : QDateTimeEdit(time, QVariant::Time, parent) |
| { |
| } |
| |
| |
| /*! |
| \class QDateEdit |
| \brief The QDateEdit class provides a widget for editing dates based on |
| the QDateTimeEdit widget. |
| |
| \ingroup basicwidgets |
| |
| |
| Many of the properties and functions provided by QDateEdit are implemented in |
| QDateTimeEdit. The following properties are most relevant to users of this |
| class: |
| |
| \list |
| \o \l{QDateTimeEdit::date}{date} holds the date displayed by the widget. |
| \o \l{QDateTimeEdit::minimumDate}{minimumDate} defines the minimum (earliest) |
| date that can be set by the user. |
| \o \l{QDateTimeEdit::maximumDate}{maximumDate} defines the maximum (latest) date |
| that can be set by the user. |
| \o \l{QDateTimeEdit::displayFormat}{displayFormat} contains a string that is used |
| to format the date displayed in the widget. |
| \endlist |
| |
| \table 100% |
| \row \o \inlineimage windowsxp-dateedit.png Screenshot of a Windows XP style date editing widget |
| \o A date editing widget shown in the \l{Windows XP Style Widget Gallery}{Windows XP widget style}. |
| \row \o \inlineimage macintosh-dateedit.png Screenshot of a Macintosh style date editing widget |
| \o A date editing widget shown in the \l{Macintosh Style Widget Gallery}{Macintosh widget style}. |
| \row \o \inlineimage plastique-dateedit.png Screenshot of a Plastique style date editing widget |
| \o A date editing widget shown in the \l{Plastique Style Widget Gallery}{Plastique widget style}. |
| \endtable |
| |
| \sa QTimeEdit, QDateTimeEdit |
| */ |
| |
| /*! |
| Constructs an empty date editor with a \a parent. |
| */ |
| |
| QDateEdit::QDateEdit(QWidget *parent) |
| : QDateTimeEdit(QDATETIMEEDIT_DATE_INITIAL, QVariant::Date, parent) |
| { |
| } |
| |
| /*! |
| Constructs an empty date editor with a \a parent. The date is set |
| to \a date. |
| */ |
| |
| QDateEdit::QDateEdit(const QDate &date, QWidget *parent) |
| : QDateTimeEdit(date, QVariant::Date, parent) |
| { |
| } |
| |
| |
| // --- QDateTimeEditPrivate --- |
| |
| /*! |
| \internal |
| Constructs a QDateTimeEditPrivate object |
| */ |
| |
| |
| QDateTimeEditPrivate::QDateTimeEditPrivate() |
| : QDateTimeParser(QVariant::DateTime, QDateTimeParser::DateTimeEdit) |
| { |
| hasHadFocus = false; |
| formatExplicitlySet = false; |
| cacheGuard = false; |
| fixday = true; |
| type = QVariant::DateTime; |
| sections = 0; |
| cachedDay = -1; |
| currentSectionIndex = FirstSectionIndex; |
| |
| first.type = FirstSection; |
| last.type = LastSection; |
| none.type = NoSection; |
| first.pos = 0; |
| last.pos = -1; |
| none.pos = -1; |
| sections = 0; |
| calendarPopup = false; |
| minimum = QDATETIMEEDIT_COMPAT_DATETIME_MIN; |
| maximum = QDATETIMEEDIT_DATETIME_MAX; |
| arrowState = QStyle::State_None; |
| monthCalendar = 0; |
| readLocaleSettings(); |
| |
| #ifdef QT_KEYPAD_NAVIGATION |
| focusOnButton = false; |
| #endif |
| } |
| |
| void QDateTimeEditPrivate::updateTimeSpec() |
| { |
| minimum = minimum.toDateTime().toTimeSpec(spec); |
| maximum = maximum.toDateTime().toTimeSpec(spec); |
| value = value.toDateTime().toTimeSpec(spec); |
| } |
| |
| void QDateTimeEditPrivate::updateEdit() |
| { |
| const QString newText = (specialValue() ? specialValueText : textFromValue(value)); |
| if (newText == displayText()) |
| return; |
| int selsize = edit->selectedText().size(); |
| const bool sb = edit->blockSignals(true); |
| |
| edit->setText(newText); |
| |
| if (!specialValue() |
| #ifdef QT_KEYPAD_NAVIGATION |
| && !(QApplication::keypadNavigationEnabled() && !edit->hasEditFocus()) |
| #endif |
| ) { |
| int cursor = sectionPos(currentSectionIndex); |
| QDTEDEBUG << "cursor is " << cursor << currentSectionIndex; |
| cursor = qBound(0, cursor, displayText().size()); |
| QDTEDEBUG << cursor; |
| if (selsize > 0) { |
| edit->setSelection(cursor, selsize); |
| QDTEDEBUG << cursor << selsize; |
| } else { |
| edit->setCursorPosition(cursor); |
| QDTEDEBUG << cursor; |
| |
| } |
| } |
| edit->blockSignals(sb); |
| } |
| |
| |
| /*! |
| \internal |
| |
| Selects the section \a s. If \a forward is false selects backwards. |
| */ |
| |
| void QDateTimeEditPrivate::setSelected(int sectionIndex, bool forward) |
| { |
| if (specialValue() |
| #ifdef QT_KEYPAD_NAVIGATION |
| || (QApplication::keypadNavigationEnabled() && !edit->hasEditFocus()) |
| #endif |
| ) { |
| edit->selectAll(); |
| } else { |
| const SectionNode &node = sectionNode(sectionIndex); |
| if (node.type == NoSection || node.type == LastSection || node.type == FirstSection) |
| return; |
| |
| updateCache(value, displayText()); |
| const int size = sectionSize(sectionIndex); |
| if (forward) { |
| edit->setSelection(sectionPos(node), size); |
| } else { |
| edit->setSelection(sectionPos(node) + size, -size); |
| } |
| } |
| } |
| |
| /*! |
| \internal |
| |
| Returns the section at index \a index or NoSection if there are no sections there. |
| */ |
| |
| int QDateTimeEditPrivate::sectionAt(int pos) const |
| { |
| if (pos < separators.first().size()) { |
| return (pos == 0 ? FirstSectionIndex : NoSectionIndex); |
| } else if (displayText().size() - pos < separators.last().size() + 1) { |
| if (separators.last().size() == 0) { |
| return sectionNodes.count() - 1; |
| } |
| return (pos == displayText().size() ? LastSectionIndex : NoSectionIndex); |
| } |
| updateCache(value, displayText()); |
| |
| for (int i=0; i<sectionNodes.size(); ++i) { |
| const int tmp = sectionPos(i); |
| if (pos < tmp + sectionSize(i)) { |
| return (pos < tmp ? -1 : i); |
| } |
| } |
| return -1; |
| } |
| |
| /*! |
| \internal |
| |
| Returns the closest section of index \a index. Searches forward |
| for a section if \a forward is true. Otherwise searches backwards. |
| */ |
| |
| int QDateTimeEditPrivate::closestSection(int pos, bool forward) const |
| { |
| Q_ASSERT(pos >= 0); |
| if (pos < separators.first().size()) { |
| return forward ? 0 : FirstSectionIndex; |
| } else if (displayText().size() - pos < separators.last().size() + 1) { |
| return forward ? LastSectionIndex : sectionNodes.size() - 1; |
| } |
| updateCache(value, displayText()); |
| for (int i=0; i<sectionNodes.size(); ++i) { |
| const int tmp = sectionPos(sectionNodes.at(i)); |
| if (pos < tmp + sectionSize(i)) { |
| if (pos < tmp && !forward) { |
| return i-1; |
| } |
| return i; |
| } else if (i == sectionNodes.size() - 1 && pos > tmp) { |
| return i; |
| } |
| } |
| qWarning("QDateTimeEdit: Internal Error: closestSection returned NoSection"); |
| return NoSectionIndex; |
| } |
| |
| /*! |
| \internal |
| |
| Returns a copy of the section that is before or after \a current, depending on \a forward. |
| */ |
| |
| int QDateTimeEditPrivate::nextPrevSection(int current, bool forward) const |
| { |
| Q_Q(const QDateTimeEdit); |
| if (q->isRightToLeft()) |
| forward = !forward; |
| |
| switch (current) { |
| case FirstSectionIndex: return forward ? 0 : FirstSectionIndex; |
| case LastSectionIndex: return (forward ? LastSectionIndex : sectionNodes.size() - 1); |
| case NoSectionIndex: return FirstSectionIndex; |
| default: break; |
| } |
| Q_ASSERT(current >= 0 && current < sectionNodes.size()); |
| |
| current += (forward ? 1 : -1); |
| if (current >= sectionNodes.size()) { |
| return LastSectionIndex; |
| } else if (current < 0) { |
| return FirstSectionIndex; |
| } |
| |
| return current; |
| } |
| |
| /*! |
| \internal |
| |
| Clears the text of section \a s. |
| */ |
| |
| void QDateTimeEditPrivate::clearSection(int index) |
| { |
| const QLatin1Char space(' '); |
| int cursorPos = edit->cursorPosition(); |
| bool blocked = edit->blockSignals(true); |
| QString t = edit->text(); |
| const int pos = sectionPos(index); |
| if (pos == -1) { |
| qWarning("QDateTimeEdit: Internal error (%s:%d)", __FILE__, __LINE__); |
| return; |
| } |
| const int size = sectionSize(index); |
| t.replace(pos, size, QString().fill(space, size)); |
| edit->setText(t); |
| edit->setCursorPosition(cursorPos); |
| QDTEDEBUG << cursorPos; |
| |
| edit->blockSignals(blocked); |
| } |
| |
| |
| /*! |
| \internal |
| |
| updates the cached values |
| */ |
| |
| void QDateTimeEditPrivate::updateCache(const QVariant &val, const QString &str) const |
| { |
| if (val != cachedValue || str != cachedText || cacheGuard) { |
| cacheGuard = true; |
| QString copy = str; |
| int unused = edit->cursorPosition(); |
| QValidator::State unusedState; |
| validateAndInterpret(copy, unused, unusedState); |
| cacheGuard = false; |
| } |
| } |
| |
| /*! |
| \internal |
| |
| parses and validates \a input |
| */ |
| |
| QDateTime QDateTimeEditPrivate::validateAndInterpret(QString &input, int &position, |
| QValidator::State &state, bool fixup) const |
| { |
| if (input.isEmpty()) { |
| if (sectionNodes.size() == 1 || !specialValueText.isEmpty()) { |
| state = QValidator::Intermediate; |
| } else { |
| state = QValidator::Invalid; |
| } |
| return getZeroVariant().toDateTime(); |
| } else if (cachedText == input && !fixup) { |
| state = cachedState; |
| return cachedValue.toDateTime(); |
| } else if (!specialValueText.isEmpty()) { |
| bool changeCase = false; |
| const int max = qMin(specialValueText.size(), input.size()); |
| int i; |
| for (i=0; i<max; ++i) { |
| const QChar ic = input.at(i); |
| const QChar sc = specialValueText.at(i); |
| if (ic != sc) { |
| if (sc.toLower() == ic.toLower()) { |
| changeCase = true; |
| } else { |
| break; |
| } |
| } |
| } |
| if (i == max) { |
| state = specialValueText.size() == input.size() ? QValidator::Acceptable : QValidator::Intermediate; |
| if (changeCase) { |
| input = specialValueText.left(max); |
| } |
| return minimum.toDateTime(); |
| } |
| } |
| StateNode tmp = parse(input, position, value.toDateTime(), fixup); |
| input = tmp.input; |
| state = QValidator::State(int(tmp.state)); |
| if (state == QValidator::Acceptable) { |
| if (tmp.conflicts && conflictGuard != tmp.value) { |
| conflictGuard = tmp.value; |
| clearCache(); |
| input = textFromValue(tmp.value); |
| updateCache(tmp.value, input); |
| conflictGuard.clear(); |
| } else { |
| cachedText = input; |
| cachedState = state; |
| cachedValue = tmp.value; |
| } |
| } else { |
| clearCache(); |
| } |
| return (tmp.value.isNull() ? getZeroVariant().toDateTime() : tmp.value); |
| } |
| |
| |
| /*! |
| \internal |
| */ |
| |
| QString QDateTimeEditPrivate::textFromValue(const QVariant &f) const |
| { |
| Q_Q(const QDateTimeEdit); |
| return q->textFromDateTime(f.toDateTime()); |
| } |
| |
| /*! |
| \internal |
| |
| This function's name is slightly confusing; it is not to be confused |
| with QAbstractSpinBox::valueFromText(). |
| */ |
| |
| QVariant QDateTimeEditPrivate::valueFromText(const QString &f) const |
| { |
| Q_Q(const QDateTimeEdit); |
| return q->dateTimeFromText(f).toTimeSpec(spec); |
| } |
| |
| |
| /*! |
| \internal |
| |
| Internal function called by QDateTimeEdit::stepBy(). Also takes a |
| Section for which section to step on and a bool \a test for |
| whether or not to modify the internal cachedDay variable. This is |
| necessary because the function is called from the const function |
| QDateTimeEdit::stepEnabled() as well as QDateTimeEdit::stepBy(). |
| */ |
| |
| QDateTime QDateTimeEditPrivate::stepBy(int sectionIndex, int steps, bool test) const |
| { |
| Q_Q(const QDateTimeEdit); |
| QDateTime v = value.toDateTime(); |
| QString str = displayText(); |
| int pos = edit->cursorPosition(); |
| const SectionNode sn = sectionNode(sectionIndex); |
| |
| int val; |
| // to make sure it behaves reasonably when typing something and then stepping in non-tracking mode |
| if (!test && pendingEmit) { |
| if (q->validate(str, pos) != QValidator::Acceptable) { |
| v = value.toDateTime(); |
| } else { |
| v = q->dateTimeFromText(str); |
| } |
| val = getDigit(v, sectionIndex); |
| } else { |
| val = getDigit(v, sectionIndex); |
| } |
| |
| val += steps; |
| |
| const int min = absoluteMin(sectionIndex); |
| const int max = absoluteMax(sectionIndex, value.toDateTime()); |
| |
| if (val < min) { |
| val = (wrapping ? max - (min - val) + 1 : min); |
| } else if (val > max) { |
| val = (wrapping ? min + val - max - 1 : max); |
| } |
| |
| |
| const int oldDay = v.date().day(); |
| |
| setDigit(v, sectionIndex, val); |
| // if this sets year or month it will make |
| // sure that days are lowered if needed. |
| |
| const QDateTime minimumDateTime = minimum.toDateTime(); |
| const QDateTime maximumDateTime = maximum.toDateTime(); |
| // changing one section should only modify that section, if possible |
| if (sn.type != AmPmSection && (v < minimumDateTime || v > maximumDateTime)) { |
| const int localmin = getDigit(minimumDateTime, sectionIndex); |
| const int localmax = getDigit(maximumDateTime, sectionIndex); |
| |
| if (wrapping) { |
| // just because we hit the roof in one direction, it |
| // doesn't mean that we hit the floor in the other |
| if (steps > 0) { |
| setDigit(v, sectionIndex, min); |
| if (!(sn.type & (DaySection|DayOfWeekSection)) && sections & DateSectionMask) { |
| const int daysInMonth = v.date().daysInMonth(); |
| if (v.date().day() < oldDay && v.date().day() < daysInMonth) { |
| const int adds = qMin(oldDay, daysInMonth); |
| v = v.addDays(adds - v.date().day()); |
| } |
| } |
| |
| if (v < minimumDateTime) { |
| setDigit(v, sectionIndex, localmin); |
| if (v < minimumDateTime) |
| setDigit(v, sectionIndex, localmin + 1); |
| } |
| } else { |
| setDigit(v, sectionIndex, max); |
| if (!(sn.type & (DaySection|DayOfWeekSection)) && sections & DateSectionMask) { |
| const int daysInMonth = v.date().daysInMonth(); |
| if (v.date().day() < oldDay && v.date().day() < daysInMonth) { |
| const int adds = qMin(oldDay, daysInMonth); |
| v = v.addDays(adds - v.date().day()); |
| } |
| } |
| |
| if (v > maximumDateTime) { |
| setDigit(v, sectionIndex, localmax); |
| if (v > maximumDateTime) |
| setDigit(v, sectionIndex, localmax - 1); |
| } |
| } |
| } else { |
| setDigit(v, sectionIndex, (steps > 0 ? localmax : localmin)); |
| } |
| } |
| if (!test && oldDay != v.date().day() && !(sn.type & (DaySection|DayOfWeekSection))) { |
| // this should not happen when called from stepEnabled |
| cachedDay = qMax<int>(oldDay, cachedDay); |
| } |
| |
| if (v < minimumDateTime) { |
| if (wrapping) { |
| QDateTime t = v; |
| setDigit(t, sectionIndex, steps < 0 ? max : min); |
| bool mincmp = (t >= minimumDateTime); |
| bool maxcmp = (t <= maximumDateTime); |
| if (!mincmp || !maxcmp) { |
| setDigit(t, sectionIndex, getDigit(steps < 0 |
| ? maximumDateTime |
| : minimumDateTime, sectionIndex)); |
| mincmp = (t >= minimumDateTime); |
| maxcmp = (t <= maximumDateTime); |
| } |
| if (mincmp && maxcmp) { |
| v = t; |
| } |
| } else { |
| v = value.toDateTime(); |
| } |
| } else if (v > maximumDateTime) { |
| if (wrapping) { |
| QDateTime t = v; |
| setDigit(t, sectionIndex, steps > 0 ? min : max); |
| bool mincmp = (t >= minimumDateTime); |
| bool maxcmp = (t <= maximumDateTime); |
| if (!mincmp || !maxcmp) { |
| setDigit(t, sectionIndex, getDigit(steps > 0 ? |
| minimumDateTime : |
| maximumDateTime, sectionIndex)); |
| mincmp = (t >= minimumDateTime); |
| maxcmp = (t <= maximumDateTime); |
| } |
| if (mincmp && maxcmp) { |
| v = t; |
| } |
| } else { |
| v = value.toDateTime(); |
| } |
| } |
| |
| const QDateTime ret = bound(v, value, steps).toDateTime().toTimeSpec(spec); |
| return ret; |
| } |
| |
| /*! |
| \internal |
| */ |
| |
| void QDateTimeEditPrivate::emitSignals(EmitPolicy ep, const QVariant &old) |
| { |
| Q_Q(QDateTimeEdit); |
| if (ep == NeverEmit) { |
| return; |
| } |
| pendingEmit = false; |
| |
| const bool dodate = value.toDate().isValid() && (sections & DateSectionMask); |
| const bool datechanged = (ep == AlwaysEmit || old.toDate() != value.toDate()); |
| const bool dotime = value.toTime().isValid() && (sections & TimeSectionMask); |
| const bool timechanged = (ep == AlwaysEmit || old.toTime() != value.toTime()); |
| |
| updateCache(value, displayText()); |
| |
| syncCalendarWidget(); |
| if (datechanged || timechanged) |
| emit q->dateTimeChanged(value.toDateTime()); |
| if (dodate && datechanged) |
| emit q->dateChanged(value.toDate()); |
| if (dotime && timechanged) |
| emit q->timeChanged(value.toTime()); |
| |
| } |
| |
| /*! |
| \internal |
| */ |
| |
| void QDateTimeEditPrivate::_q_editorCursorPositionChanged(int oldpos, int newpos) |
| { |
| if (ignoreCursorPositionChanged || specialValue()) |
| return; |
| const QString oldText = displayText(); |
| updateCache(value, oldText); |
| |
| const bool allowChange = !edit->hasSelectedText(); |
| const bool forward = oldpos <= newpos; |
| ignoreCursorPositionChanged = true; |
| int s = sectionAt(newpos); |
| if (s == NoSectionIndex && forward && newpos > 0) { |
| s = sectionAt(newpos - 1); |
| } |
| |
| int c = newpos; |
| |
| const int selstart = edit->selectionStart(); |
| const int selSection = sectionAt(selstart); |
| const int l = selSection != -1 ? sectionSize(selSection) : 0; |
| |
| if (s == NoSectionIndex) { |
| if (l > 0 && selstart == sectionPos(selSection) && edit->selectedText().size() == l) { |
| s = selSection; |
| if (allowChange) |
| setSelected(selSection, true); |
| c = -1; |
| } else { |
| int closest = closestSection(newpos, forward); |
| c = sectionPos(closest) + (forward ? 0 : qMax<int>(0, sectionSize(closest))); |
| |
| if (allowChange) { |
| edit->setCursorPosition(c); |
| QDTEDEBUG << c; |
| } |
| s = closest; |
| } |
| } |
| |
| if (allowChange && currentSectionIndex != s) { |
| interpret(EmitIfChanged); |
| } |
| if (c == -1) { |
| setSelected(s, true); |
| } else if (!edit->hasSelectedText()) { |
| if (oldpos < newpos) { |
| edit->setCursorPosition(displayText().size() - (oldText.size() - c)); |
| } else { |
| edit->setCursorPosition(c); |
| } |
| } |
| |
| QDTEDEBUG << "currentSectionIndex is set to" << sectionName(sectionType(s)) |
| << oldpos << newpos |
| << "was" << sectionName(sectionType(currentSectionIndex)); |
| |
| currentSectionIndex = s; |
| Q_ASSERT_X(currentSectionIndex < sectionNodes.size(), |
| "QDateTimeEditPrivate::_q_editorCursorPositionChanged()", |
| qPrintable(QString::fromAscii("Internal error (%1 %2)"). |
| arg(currentSectionIndex). |
| arg(sectionNodes.size()))); |
| |
| ignoreCursorPositionChanged = false; |
| } |
| |
| /*! |
| \internal |
| |
| Try to get the format from the local settings |
| */ |
| void QDateTimeEditPrivate::readLocaleSettings() |
| { |
| const QLocale loc; |
| defaultTimeFormat = loc.timeFormat(QLocale::ShortFormat); |
| defaultDateFormat = loc.dateFormat(QLocale::ShortFormat); |
| defaultDateTimeFormat = loc.dateTimeFormat(QLocale::ShortFormat); |
| } |
| |
| QDateTimeEdit::Section QDateTimeEditPrivate::convertToPublic(QDateTimeParser::Section s) |
| { |
| switch (s & ~Internal) { |
| case AmPmSection: return QDateTimeEdit::AmPmSection; |
| case MSecSection: return QDateTimeEdit::MSecSection; |
| case SecondSection: return QDateTimeEdit::SecondSection; |
| case MinuteSection: return QDateTimeEdit::MinuteSection; |
| case DayOfWeekSection: |
| case DaySection: return QDateTimeEdit::DaySection; |
| case MonthSection: return QDateTimeEdit::MonthSection; |
| case YearSection2Digits: |
| case YearSection: return QDateTimeEdit::YearSection; |
| case Hour12Section: |
| case Hour24Section: return QDateTimeEdit::HourSection; |
| case FirstSection: |
| case NoSection: |
| case LastSection: break; |
| } |
| return QDateTimeEdit::NoSection; |
| } |
| |
| QDateTimeEdit::Sections QDateTimeEditPrivate::convertSections(QDateTimeParser::Sections s) |
| { |
| QDateTimeEdit::Sections ret = 0; |
| if (s & QDateTimeParser::MSecSection) |
| ret |= QDateTimeEdit::MSecSection; |
| if (s & QDateTimeParser::SecondSection) |
| ret |= QDateTimeEdit::SecondSection; |
| if (s & QDateTimeParser::MinuteSection) |
| ret |= QDateTimeEdit::MinuteSection; |
| if (s & (QDateTimeParser::Hour24Section|QDateTimeParser::Hour12Section)) |
| ret |= QDateTimeEdit::HourSection; |
| if (s & QDateTimeParser::AmPmSection) |
| ret |= QDateTimeEdit::AmPmSection; |
| if (s & (QDateTimeParser::DaySection|QDateTimeParser::DayOfWeekSection)) |
| ret |= QDateTimeEdit::DaySection; |
| if (s & QDateTimeParser::MonthSection) |
| ret |= QDateTimeEdit::MonthSection; |
| if (s & (QDateTimeParser::YearSection|QDateTimeParser::YearSection2Digits)) |
| ret |= QDateTimeEdit::YearSection; |
| |
| return ret; |
| } |
| |
| /*! |
| \reimp |
| */ |
| |
| void QDateTimeEdit::paintEvent(QPaintEvent *event) |
| { |
| Q_D(QDateTimeEdit); |
| if (!d->calendarPopupEnabled()) { |
| QAbstractSpinBox::paintEvent(event); |
| return; |
| } |
| |
| QStyleOptionSpinBox opt; |
| initStyleOption(&opt); |
| |
| QStyleOptionComboBox optCombo; |
| |
| optCombo.init(this); |
| optCombo.editable = true; |
| optCombo.frame = opt.frame; |
| optCombo.subControls = opt.subControls; |
| optCombo.activeSubControls = opt.activeSubControls; |
| optCombo.state = opt.state; |
| if (d->readOnly) { |
| optCombo.state &= ~QStyle::State_Enabled; |
| } |
| |
| QPainter p(this); |
| style()->drawComplexControl(QStyle::CC_ComboBox, &optCombo, &p, this); |
| } |
| |
| QString QDateTimeEditPrivate::getAmPmText(AmPm ap, Case cs) const |
| { |
| if (ap == AmText) { |
| return (cs == UpperCase ? QDateTimeEdit::tr("AM") : QDateTimeEdit::tr("am")); |
| } else { |
| return (cs == UpperCase ? QDateTimeEdit::tr("PM") : QDateTimeEdit::tr("pm")); |
| } |
| } |
| |
| int QDateTimeEditPrivate::absoluteIndex(QDateTimeEdit::Section s, int index) const |
| { |
| for (int i=0; i<sectionNodes.size(); ++i) { |
| if (convertToPublic(sectionNodes.at(i).type) == s && index-- == 0) { |
| return i; |
| } |
| } |
| return NoSectionIndex; |
| } |
| |
| int QDateTimeEditPrivate::absoluteIndex(const SectionNode &s) const |
| { |
| return sectionNodes.indexOf(s); |
| } |
| |
| void QDateTimeEditPrivate::interpret(EmitPolicy ep) |
| { |
| Q_Q(QDateTimeEdit); |
| QString tmp = displayText(); |
| int pos = edit->cursorPosition(); |
| const QValidator::State state = q->validate(tmp, pos); |
| if (state != QValidator::Acceptable |
| && correctionMode == QAbstractSpinBox::CorrectToPreviousValue |
| && (state == QValidator::Invalid || !(fieldInfo(currentSectionIndex) & AllowPartial))) { |
| setValue(value, ep); |
| updateTimeSpec(); |
| } else { |
| QAbstractSpinBoxPrivate::interpret(ep); |
| } |
| } |
| |
| void QDateTimeEditPrivate::clearCache() const |
| { |
| QAbstractSpinBoxPrivate::clearCache(); |
| cachedDay = -1; |
| } |
| |
| /*! |
| Initialize \a option with the values from this QDataTimeEdit. This method |
| is useful for subclasses when they need a QStyleOptionSpinBox, but don't want |
| to fill in all the information themselves. |
| |
| \sa QStyleOption::initFrom() |
| */ |
| void QDateTimeEdit::initStyleOption(QStyleOptionSpinBox *option) const |
| { |
| if (!option) |
| return; |
| |
| Q_D(const QDateTimeEdit); |
| QAbstractSpinBox::initStyleOption(option); |
| if (d->calendarPopupEnabled()) { |
| option->subControls = QStyle::SC_ComboBoxFrame | QStyle::SC_ComboBoxEditField |
| | QStyle::SC_ComboBoxArrow; |
| if (d->arrowState == QStyle::State_Sunken) |
| option->state |= QStyle::State_Sunken; |
| else |
| option->state &= ~QStyle::State_Sunken; |
| } |
| } |
| |
| void QDateTimeEditPrivate::init(const QVariant &var) |
| { |
| Q_Q(QDateTimeEdit); |
| switch (var.type()) { |
| case QVariant::Date: |
| value = QDateTime(var.toDate(), QDATETIMEEDIT_TIME_MIN); |
| q->setDisplayFormat(defaultDateFormat); |
| if (sectionNodes.isEmpty()) // ### safeguard for broken locale |
| q->setDisplayFormat(QLatin1String("dd/MM/yyyy")); |
| break; |
| case QVariant::DateTime: |
| value = var; |
| q->setDisplayFormat(defaultDateTimeFormat); |
| if (sectionNodes.isEmpty()) // ### safeguard for broken locale |
| q->setDisplayFormat(QLatin1String("dd/MM/yyyy hh:mm:ss")); |
| break; |
| case QVariant::Time: |
| value = QDateTime(QDATETIMEEDIT_DATE_INITIAL, var.toTime()); |
| q->setDisplayFormat(defaultTimeFormat); |
| if (sectionNodes.isEmpty()) // ### safeguard for broken locale |
| q->setDisplayFormat(QLatin1String("hh:mm:ss")); |
| break; |
| default: |
| Q_ASSERT_X(0, "QDateTimeEditPrivate::init", "Internal error"); |
| break; |
| } |
| #ifdef QT_KEYPAD_NAVIGATION |
| if (QApplication::keypadNavigationEnabled()) |
| q->setCalendarPopup(true); |
| #endif |
| updateTimeSpec(); |
| q->setInputMethodHints(Qt::ImhPreferNumbers); |
| setLayoutItemMargins(QStyle::SE_DateTimeEditLayoutItem); |
| } |
| |
| void QDateTimeEditPrivate::_q_resetButton() |
| { |
| updateArrow(QStyle::State_None); |
| } |
| |
| void QDateTimeEditPrivate::updateArrow(QStyle::StateFlag state) |
| { |
| Q_Q(QDateTimeEdit); |
| |
| if (arrowState == state) |
| return; |
| arrowState = state; |
| if (arrowState != QStyle::State_None) |
| buttonState |= Mouse; |
| else { |
| buttonState = 0; |
| hoverControl = QStyle::SC_ComboBoxFrame; |
| } |
| q->update(); |
| } |
| |
| /*! |
| \internal |
| Returns the hover control at \a pos. |
| This will update the hoverRect and hoverControl. |
| */ |
| QStyle::SubControl QDateTimeEditPrivate::newHoverControl(const QPoint &pos) |
| { |
| if (!calendarPopupEnabled()) |
| return QAbstractSpinBoxPrivate::newHoverControl(pos); |
| |
| Q_Q(QDateTimeEdit); |
| |
| QStyleOptionComboBox optCombo; |
| optCombo.init(q); |
| optCombo.editable = true; |
| optCombo.subControls = QStyle::SC_All; |
| hoverControl = q->style()->hitTestComplexControl(QStyle::CC_ComboBox, &optCombo, pos, q); |
| return hoverControl; |
| } |
| |
| void QDateTimeEditPrivate::updateEditFieldGeometry() |
| { |
| if (!calendarPopupEnabled()) { |
| QAbstractSpinBoxPrivate::updateEditFieldGeometry(); |
| return; |
| } |
| |
| Q_Q(QDateTimeEdit); |
| |
| QStyleOptionComboBox optCombo; |
| optCombo.init(q); |
| optCombo.editable = true; |
| optCombo.subControls = QStyle::SC_ComboBoxEditField; |
| edit->setGeometry(q->style()->subControlRect(QStyle::CC_ComboBox, &optCombo, |
| QStyle::SC_ComboBoxEditField, q)); |
| } |
| |
| QVariant QDateTimeEditPrivate::getZeroVariant() const |
| { |
| Q_ASSERT(type == QVariant::DateTime); |
| return QDateTime(QDATETIMEEDIT_DATE_INITIAL, QTime(), spec); |
| } |
| |
| void QDateTimeEditPrivate::setRange(const QVariant &min, const QVariant &max) |
| { |
| QAbstractSpinBoxPrivate::setRange(min, max); |
| syncCalendarWidget(); |
| } |
| |
| |
| bool QDateTimeEditPrivate::isSeparatorKey(const QKeyEvent *ke) const |
| { |
| if (!ke->text().isEmpty() && currentSectionIndex + 1 < sectionNodes.size() && currentSectionIndex >= 0) { |
| if (fieldInfo(currentSectionIndex) & Numeric) { |
| if (ke->text().at(0).isNumber()) |
| return false; |
| } else if (ke->text().at(0).isLetterOrNumber()) { |
| return false; |
| } |
| return separators.at(currentSectionIndex + 1).contains(ke->text()); |
| } |
| return false; |
| } |
| |
| void QDateTimeEditPrivate::initCalendarPopup(QCalendarWidget *cw) |
| { |
| Q_Q(QDateTimeEdit); |
| if (!monthCalendar) { |
| monthCalendar = new QCalendarPopup(q, cw); |
| monthCalendar->setObjectName(QLatin1String("qt_datetimedit_calendar")); |
| QObject::connect(monthCalendar, SIGNAL(newDateSelected(QDate)), q, SLOT(setDate(QDate))); |
| QObject::connect(monthCalendar, SIGNAL(hidingCalendar(QDate)), q, SLOT(setDate(QDate))); |
| QObject::connect(monthCalendar, SIGNAL(activated(QDate)), q, SLOT(setDate(QDate))); |
| QObject::connect(monthCalendar, SIGNAL(activated(QDate)), monthCalendar, SLOT(close())); |
| QObject::connect(monthCalendar, SIGNAL(resetButton()), q, SLOT(_q_resetButton())); |
| } else if (cw) { |
| monthCalendar->setCalendarWidget(cw); |
| } |
| syncCalendarWidget(); |
| } |
| |
| void QDateTimeEditPrivate::positionCalendarPopup() |
| { |
| Q_Q(QDateTimeEdit); |
| QPoint pos = (q->layoutDirection() == Qt::RightToLeft) ? q->rect().bottomRight() : q->rect().bottomLeft(); |
| QPoint pos2 = (q->layoutDirection() == Qt::RightToLeft) ? q->rect().topRight() : q->rect().topLeft(); |
| pos = q->mapToGlobal(pos); |
| pos2 = q->mapToGlobal(pos2); |
| QSize size = monthCalendar->sizeHint(); |
| QRect screen = QApplication::desktop()->availableGeometry(pos); |
| //handle popup falling "off screen" |
| if (q->layoutDirection() == Qt::RightToLeft) { |
| pos.setX(pos.x()-size.width()); |
| pos2.setX(pos2.x()-size.width()); |
| if (pos.x() < screen.left()) |
| pos.setX(qMax(pos.x(), screen.left())); |
| else if (pos.x()+size.width() > screen.right()) |
| pos.setX(qMax(pos.x()-size.width(), screen.right()-size.width())); |
| } else { |
| if (pos.x()+size.width() > screen.right()) |
| pos.setX(screen.right()-size.width()); |
| pos.setX(qMax(pos.x(), screen.left())); |
| } |
| if (pos.y() + size.height() > screen.bottom()) |
| pos.setY(pos2.y() - size.height()); |
| else if (pos.y() < screen.top()) |
| pos.setY(screen.top()); |
| if (pos.y() < screen.top()) |
| pos.setY(screen.top()); |
| if (pos.y()+size.height() > screen.bottom()) |
| pos.setY(screen.bottom()-size.height()); |
| monthCalendar->move(pos); |
| } |
| |
| bool QDateTimeEditPrivate::calendarPopupEnabled() const |
| { |
| return (calendarPopup && (sections & (DateSectionMask))); |
| } |
| |
| void QDateTimeEditPrivate::syncCalendarWidget() |
| { |
| Q_Q(QDateTimeEdit); |
| if (monthCalendar) { |
| monthCalendar->setDateRange(q->minimumDate(), q->maximumDate()); |
| monthCalendar->setDate(q->date()); |
| } |
| } |
| |
| QCalendarPopup::QCalendarPopup(QWidget * parent, QCalendarWidget *cw) |
| : QWidget(parent, Qt::Popup), calendar(0) |
| { |
| setAttribute(Qt::WA_WindowPropagation); |
| |
| dateChanged = false; |
| if (!cw) { |
| cw = new QCalendarWidget(this); |
| cw->setVerticalHeaderFormat(QCalendarWidget::NoVerticalHeader); |
| #ifdef QT_KEYPAD_NAVIGATION |
| if (QApplication::keypadNavigationEnabled()) |
| cw->setHorizontalHeaderFormat(QCalendarWidget::SingleLetterDayNames); |
| #endif |
| } |
| setCalendarWidget(cw); |
| } |
| |
| void QCalendarPopup::setCalendarWidget(QCalendarWidget *cw) |
| { |
| Q_ASSERT(cw); |
| QVBoxLayout *widgetLayout = qobject_cast<QVBoxLayout*>(layout()); |
| if (!widgetLayout) { |
| widgetLayout = new QVBoxLayout(this); |
| widgetLayout->setMargin(0); |
| widgetLayout->setSpacing(0); |
| } |
| delete calendar; |
| calendar = cw; |
| widgetLayout->addWidget(calendar); |
| |
| connect(calendar, SIGNAL(activated(QDate)), this, SLOT(dateSelected(QDate))); |
| connect(calendar, SIGNAL(clicked(QDate)), this, SLOT(dateSelected(QDate))); |
| connect(calendar, SIGNAL(selectionChanged()), this, SLOT(dateSelectionChanged())); |
| |
| calendar->setFocus(); |
| } |
| |
| |
| void QCalendarPopup::setDate(const QDate &date) |
| { |
| oldDate = date; |
| calendar->setSelectedDate(date); |
| } |
| |
| void QCalendarPopup::setDateRange(const QDate &min, const QDate &max) |
| { |
| calendar->setMinimumDate(min); |
| calendar->setMaximumDate(max); |
| } |
| |
| void QCalendarPopup::mousePressEvent(QMouseEvent *event) |
| { |
| QDateTimeEdit *dateTime = qobject_cast<QDateTimeEdit *>(parentWidget()); |
| if (dateTime) { |
| QStyleOptionComboBox opt; |
| opt.init(dateTime); |
| QRect arrowRect = dateTime->style()->subControlRect(QStyle::CC_ComboBox, &opt, |
| QStyle::SC_ComboBoxArrow, dateTime); |
| arrowRect.moveTo(dateTime->mapToGlobal(arrowRect .topLeft())); |
| if (arrowRect.contains(event->globalPos()) || rect().contains(event->pos())) |
| setAttribute(Qt::WA_NoMouseReplay); |
| } |
| QWidget::mousePressEvent(event); |
| } |
| |
| void QCalendarPopup::mouseReleaseEvent(QMouseEvent*) |
| { |
| emit resetButton(); |
| } |
| |
| bool QCalendarPopup::event(QEvent *event) |
| { |
| if (event->type() == QEvent::KeyPress) { |
| QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event); |
| if (keyEvent->key()== Qt::Key_Escape) |
| dateChanged = false; |
| } |
| return QWidget::event(event); |
| } |
| |
| void QCalendarPopup::dateSelectionChanged() |
| { |
| dateChanged = true; |
| emit newDateSelected(calendar->selectedDate()); |
| } |
| void QCalendarPopup::dateSelected(const QDate &date) |
| { |
| dateChanged = true; |
| emit activated(date); |
| close(); |
| } |
| |
| void QCalendarPopup::hideEvent(QHideEvent *) |
| { |
| emit resetButton(); |
| if (!dateChanged) |
| emit hidingCalendar(oldDate); |
| } |
| |
| QT_END_NAMESPACE |
| #include "moc_qdatetimeedit.cpp" |
| |
| #endif // QT_NO_DATETIMEEDIT |