diff options
Diffstat (limited to 'src/widgets/qdatetimeedit.cpp')
-rw-r--r-- | src/widgets/qdatetimeedit.cpp | 2842 |
1 files changed, 2842 insertions, 0 deletions
diff --git a/src/widgets/qdatetimeedit.cpp b/src/widgets/qdatetimeedit.cpp new file mode 100644 index 000000000..300db0ee4 --- /dev/null +++ b/src/widgets/qdatetimeedit.cpp @@ -0,0 +1,2842 @@ +/**************************************************************************** +** +** Implementation of date and time edit classes +** +** Created : 001103 +** +** Copyright (C) 2000-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing retquirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "qdatetimeedit.h" + +#ifndef QT_NO_DATETIMEEDIT + +#include "../kernel/qinternal_p.h" +#include "../kernel/qrichtext_p.h" +#include "qrangecontrol.h" +#include "qapplication.h" +#include "qpixmap.h" +#include "qapplication.h" +#include "qvaluelist.h" +#include "qstring.h" +#include "qstyle.h" + +#if defined(Q_WS_WIN) +#include "qt_windows.h" +#endif + +#define TQDATETIMEEDIT_HIDDEN_CHAR '0' + +class Q_EXPORT TQNumberSection +{ +public: + TQNumberSection( int selStart = 0, int selEnd = 0, bool separat = TRUE, int actual = -1 ) + : selstart( selStart ), selend( selEnd ), act( actual ), sep( separat ) + {} + int selectionStart() const { return selstart; } + void setSelectionStart( int s ) { selstart = s; } + int selectionEnd() const { return selend; } + void setSelectionEnd( int s ) { selend = s; } + int width() const { return selend - selstart; } + int index() const { return act; } + bool separator() const { return sep; } + Q_DUMMY_COMPARISON_OPERATOR( TQNumberSection ) +private: + int selstart :12; + int selend :12; + int act :7; + bool sep :1; +}; + +static TQString *lDateSep = 0; +static TQString *lTimeSep = 0; +static bool lAMPM = FALSE; +static TQString *lAM = 0; +static TQString *lPM = 0; +static TQDateEdit::Order lOrder = TQDateEdit::YMD; +static int refcount = 0; + +static void cleanup() +{ + delete lDateSep; + lDateSep = 0; + delete lTimeSep; + lTimeSep = 0; + delete lAM; + lAM = 0; + delete lPM; + lPM = 0; +} + +/*! +\internal +try to get the order of DMY and the date/time separator from the locale settings +*/ +static void readLocaleSettings() +{ + int dpos, mpos, ypos; + cleanup(); + + lDateSep = new TQString(); + lTimeSep = new TQString(); + +#if defined(Q_WS_WIN) + QT_WA( { + TCHAR data[10]; + GetLocaleInfo( LOCALE_USER_DEFAULT, LOCALE_SDATE, data, 10 ); + *lDateSep = TQString::fromUcs2( (ushort*)data ); + GetLocaleInfo( LOCALE_USER_DEFAULT, LOCALE_STIME, data, 10 ); + *lTimeSep = TQString::fromUcs2( (ushort*)data ); + GetLocaleInfo( LOCALE_USER_DEFAULT, LOCALE_ITIME, data, 10 ); + lAMPM = TQString::fromUcs2( (ushort*)data ).toInt()==0; + GetLocaleInfo( LOCALE_USER_DEFAULT, LOCALE_S1159, data, 10 ); + TQString am = TQString::fromUcs2( (ushort*)data ); + if ( !am.isEmpty() ) + lAM = new TQString( am ); + GetLocaleInfo( LOCALE_USER_DEFAULT, LOCALE_S2359, data, 10 ); + TQString pm = TQString::fromUcs2( (ushort*)data ); + if ( !pm.isEmpty() ) + lPM = new TQString( pm ); + } , { + char data[10]; + GetLocaleInfoA( LOCALE_USER_DEFAULT, LOCALE_SDATE, (char*)&data, 10 ); + *lDateSep = TQString::fromLocal8Bit( data ); + GetLocaleInfoA( LOCALE_USER_DEFAULT, LOCALE_STIME, (char*)&data, 10 ); + *lTimeSep = TQString::fromLocal8Bit( data ); + GetLocaleInfoA( LOCALE_USER_DEFAULT, LOCALE_ITIME, (char*)&data, 10 ); + lAMPM = TQString::fromLocal8Bit( data ).toInt()==0; + GetLocaleInfoA( LOCALE_USER_DEFAULT, LOCALE_S1159, (char*)&data, 10 ); + TQString am = TQString::fromLocal8Bit( data ); + if ( !am.isEmpty() ) + lAM = new TQString( am ); + GetLocaleInfoA( LOCALE_USER_DEFAULT, LOCALE_S2359, (char*)&data, 10 ); + TQString pm = TQString::fromLocal8Bit( data ); + if ( !pm.isEmpty() ) + lPM = new TQString( pm ); + } ); +#else + *lDateSep = "-"; + *lTimeSep = ":"; +#endif + TQString d = TQDate( 1999, 11, 22 ).toString( TQt::LocalDate ); + dpos = d.find( "22" ); + mpos = d.find( "11" ); + ypos = d.find( "99" ); + if ( dpos > -1 && mpos > -1 && ypos > -1 ) { + // test for DMY, MDY, YMD, YDM + if ( dpos < mpos && mpos < ypos ) { + lOrder = TQDateEdit::DMY; + } else if ( mpos < dpos && dpos < ypos ) { + lOrder = TQDateEdit::MDY; + } else if ( ypos < mpos && mpos < dpos ) { + lOrder = TQDateEdit::YMD; + } else if ( ypos < dpos && dpos < mpos ) { + lOrder = TQDateEdit::YDM; + } else { + // cannot determine the dateformat - use the default + return; + } + + // this code needs to change if new formats are added + +#ifndef Q_WS_WIN + TQString sep = d.mid( TQMIN( dpos, mpos ) + 2, TQABS( dpos - mpos ) - 2 ); + if ( d.contains( sep ) == 2 ) { + *lDateSep = sep; + } +#endif + } + +#ifndef Q_WS_WIN + TQString t = TQTime( 11, 22, 33 ).toString( TQt::LocalDate ); + dpos = t.find( "11" ); + mpos = t.find( "22" ); + ypos = t.find( "33" ); + // We only allow hhmmss + if ( dpos > -1 && dpos < mpos && mpos < ypos ) { + TQString sep = t.mid( dpos + 2, mpos - dpos - 2 ); + if ( sep == t.mid( mpos + 2, ypos - mpos - 2 ) ) { + *lTimeSep = sep; + } + } +#endif +} + +static TQDateEdit::Order localOrder() { + if ( !lDateSep ) { + readLocaleSettings(); + } + return lOrder; +} + +static TQString localDateSep() { + if ( !lDateSep ) { + readLocaleSettings(); + } + return *lDateSep; +} + +static TQString localTimeSep() { + if ( !lTimeSep ) { + readLocaleSettings(); + } + return *lTimeSep; +} + +class TQDateTimeEditorPrivate +{ +public: + TQDateTimeEditorPrivate() + : frm( TRUE ), + parag( new TQTextParagraph( 0, 0, 0, FALSE ) ), + focusSec(0) + { + parag->formatter()->setWrapEnabled( FALSE ); + cursor = new TQTextCursor( 0 ); + cursor->setParagraph( parag ); + offset = 0; + sep = localDateSep(); + refcount++; + } + ~TQDateTimeEditorPrivate() + { + delete parag; + delete cursor; + if ( !--refcount ) + cleanup(); + } + + void appendSection( const TQNumberSection& sec ) + { + sections.append( sec ); + + } + void clearSections() + { + sections.clear(); + } + void setSectionSelection( int sec, int selstart, int selend ) + { + if ( sec < 0 || sec > (int)sections.count() ) + return; + sections[sec].setSelectionStart( selstart ); + sections[sec].setSelectionEnd( selend ); + } + uint sectionCount() const { return (uint)sections.count(); } + void setSeparator( const TQString& s ) { sep = s; } + TQString separator() const { return sep; } + + void setFrame( bool f ) { frm = f; } + bool frame() const { return frm; } + + int focusSection() const { return focusSec; } + int section( const TQPoint& p ) + { + cursor->place( p + TQPoint( offset, 0 ), parag ); + int idx = cursor->index(); + for ( uint i = 0; i < sections.count(); ++i ) { + if ( idx >= sections[i].selectionStart() && + idx <= sections[i].selectionEnd() ) + return i; + } + return -1; + } + TQNumberSection section( int idx ) const + { + return sections[idx]; + } + bool setFocusSection( int idx ) + { + if ( idx > (int)sections.count()-1 || idx < 0 ) + return FALSE; + if ( idx != focusSec ) { + focusSec = idx; + applyFocusSelection(); + return TRUE; + } + return FALSE; + } + + bool inSectionSelection( int idx ) + { + for ( uint i = 0; i < sections.count(); ++i ) { + if ( idx >= sections[i].selectionStart() && + idx <= sections[i].selectionEnd() ) + return TRUE; + } + return FALSE; + } + + void paint( const TQString& txt, bool focus, TQPainter& p, + const TQColorGroup& cg, const TQRect& rect, TQStyle& style ) + { + int fw = 0; + if ( frm ) + fw = style.pixelMetric(TQStyle::PM_DefaultFrameWidth); + + parag->truncate( 0 ); + parag->append( txt ); + if ( !focus ) + parag->removeSelection( TQTextDocument::Standard ); + else { + applyFocusSelection(); + } + + /* color all TQDATETIMEEDIT_HIDDEN_CHAR chars to background color */ + TQTextFormat *fb = parag->formatCollection()->format( p.font(), + cg.base() ); + TQTextFormat *nf = parag->formatCollection()->format( p.font(), + cg.text() ); + for ( uint i = 0; i < txt.length(); ++i ) { + parag->setFormat( i, 1, nf ); + if ( inSectionSelection( i ) ) + continue; + if ( txt.at(i) == TQDATETIMEEDIT_HIDDEN_CHAR ) + parag->setFormat( i, 1, fb ); + else + parag->setFormat( i, 1, nf ); + } + fb->removeRef(); + nf->removeRef(); + + TQRect r( rect.x(), rect.y(), rect.width() - 2 * ( 2 + fw ), rect.height() ); + parag->pseudoDocument()->docRect = r; + parag->invalidate(0); + parag->format(); + + int xoff = 2 + fw - offset; + int yoff = ( rect.height() - parag->rect().height() + 1 ) / 2; + if ( yoff < 0 ) + yoff = 0; + + p.translate( xoff, yoff ); + parag->paint( p, cg, 0, TRUE ); + if ( frm ) + p.translate( -xoff, -yoff ); + } + + void resize( const TQSize& size ) { sz = size; } + + int mapSection( int sec ) + { + return sections[sec].index(); + } + +protected: + void applyFocusSelection() + { + if ( focusSec > -1 ) { + int selstart = sections[ focusSec ].selectionStart(); + int selend = sections[ focusSec ].selectionEnd(); + parag->setSelection( TQTextDocument::Standard, selstart, selend ); + parag->format(); + if ( parag->at( selstart )->x < offset || + parag->at( selend )->x + parag->string()->width( selend ) > offset + sz.width() ) { + offset = parag->at( selstart )->x; + } + } + } +private: + bool frm; + TQTextParagraph *parag; + TQTextCursor *cursor; + TQSize sz; + int focusSec; + TQValueList< TQNumberSection > sections; + TQString sep; + int offset; +}; + +class TQDateTimeEditor : public TQWidget +{ + Q_OBJECT +public: + TQDateTimeEditor( TQDateTimeEditBase * widget, TQWidget *parent, + const char * name=0 ); + ~TQDateTimeEditor(); + + void setControlWidget( TQDateTimeEditBase * widget ); + TQDateTimeEditBase * controlWidget() const; + + void setSeparator( const TQString& s ); + TQString separator() const; + + int focusSection() const; + bool setFocusSection( int s ); + void appendSection( const TQNumberSection& sec ); + void clearSections(); + void setSectionSelection( int sec, int selstart, int selend ); + bool eventFilter( TQObject *o, TQEvent *e ); + int sectionAt( const TQPoint &p ); + int mapSection( int sec ); + +protected: + void init(); + bool event( TQEvent *e ); + void resizeEvent( TQResizeEvent * ); + void paintEvent( TQPaintEvent * ); + void mousePressEvent( TQMouseEvent *e ); + +private: + TQDateTimeEditBase* cw; + TQDateTimeEditorPrivate* d; +}; + +class TQDateTimeSpinWidget : public TQSpinWidget +{ +public: + TQDateTimeSpinWidget( TQWidget *parent, const char *name ) + : TQSpinWidget( parent, name ) + { + } + + void enabledChange(bool notenabled) + { + TQDateEdit *de = ::qt_cast<TQDateEdit*>(parentWidget()); + if (de && !notenabled) { + setUpEnabled(de->date() < de->maxValue()); + setDownEnabled(de->date() > de->minValue()); + } else { + setUpEnabled(!notenabled); + setDownEnabled(!notenabled); + } + } + +protected: +#ifndef QT_NO_WHEELEVENT + void wheelEvent( TQWheelEvent *e ) + { + TQDateTimeEditor *editor = (TQDateTimeEditor*)editWidget()->qt_cast( "TQDateTimeEditor" ); + Q_ASSERT( editor ); + if ( !editor ) + return; + + int section = editor->sectionAt( e->pos() ); + editor->setFocusSection( section ); + + if ( section == -1 ) + return; + TQSpinWidget::wheelEvent( e ); + } +#endif +}; + +/*! + Constructs an empty datetime editor with parent \a parent and + called \a name. +*/ +TQDateTimeEditor::TQDateTimeEditor( TQDateTimeEditBase * widget, TQWidget *parent, + const char * name ) + : TQWidget( parent, name, WNoAutoErase ) +{ + d = new TQDateTimeEditorPrivate(); + cw = widget; + init(); +} + +/*! + Destroys the object and frees any allocated resources. +*/ + +TQDateTimeEditor::~TQDateTimeEditor() +{ + delete d; +} + +/*! \internal + +*/ + +void TQDateTimeEditor::init() +{ + setBackgroundMode( PaletteBase ); + setFocusSection( -1 ); + installEventFilter( this ); + setFocusPolicy( WheelFocus ); +} + + +/*! \reimp + +*/ + +bool TQDateTimeEditor::event( TQEvent *e ) +{ + if ( e->type() == TQEvent::FocusIn || e->type() == TQEvent::FocusOut ) { + if ( e->type() == TQEvent::FocusOut ) + qApp->sendEvent( cw, e ); + update( rect() ); + } else if ( e->type() == TQEvent::AccelOverride ) { + TQKeyEvent* ke = (TQKeyEvent*) e; + switch ( ke->key() ) { + case Key_Delete: + case Key_Backspace: + case Key_Up: + case Key_Down: + case Key_Left: + case Key_Right: + ke->accept(); + default: + break; + } + } + return TQWidget::event( e ); +} + +/*! \reimp + +*/ + +void TQDateTimeEditor::resizeEvent( TQResizeEvent *e ) +{ + d->resize( e->size() ); + TQWidget::resizeEvent( e ); +} + + +/*! \reimp + +*/ + +void TQDateTimeEditor::paintEvent( TQPaintEvent * ) +{ + TQString txt; + for ( uint i = 0; i < d->sectionCount(); ++i ) { + txt += cw->sectionFormattedText( i ); + if ( i < d->sectionCount()-1 ) { + if ( d->section( i+1 ).separator() ) + txt += d->separator(); + else + txt += " "; + } + } + + TQSharedDoubleBuffer buffer( this ); + const TQBrush &bg = + colorGroup().brush( isEnabled() ? TQColorGroup::Base : TQColorGroup::Background ); + buffer.painter()->fillRect( 0, 0, width(), height(), bg ); + d->paint( txt, hasFocus(), *buffer.painter(), colorGroup(), rect(), + style() ); + buffer.end(); +} + + +/*! + Returns the section index at point \a p. +*/ +int TQDateTimeEditor::sectionAt( const TQPoint &p ) +{ + return d->section( p ); +} + +int TQDateTimeEditor::mapSection( int sec ) +{ + return d->mapSection( sec ); +} + + +/*! \reimp + +*/ + +void TQDateTimeEditor::mousePressEvent( TQMouseEvent *e ) +{ + TQPoint p( e->pos().x(), 0 ); + int sec = sectionAt( p ); + if ( sec != -1 ) { + cw->setFocusSection( sec ); + repaint( rect(), FALSE ); + } +} + +/*! \reimp + +*/ +bool TQDateTimeEditor::eventFilter( TQObject *o, TQEvent *e ) +{ + if ( o == this ) { + if ( e->type() == TQEvent::KeyPress ) { + TQKeyEvent *ke = (TQKeyEvent*)e; + switch ( ke->key() ) { + case Key_Right: + if ( d->focusSection() < (int)d->sectionCount()-1 ) { + if ( cw->setFocusSection( focusSection()+1 ) ) + repaint( rect(), FALSE ); + } + return TRUE; + case Key_Left: + if ( d->focusSection() > 0 ) { + if ( cw->setFocusSection( focusSection()-1 ) ) + repaint( rect(), FALSE ); + } + return TRUE; + case Key_Up: + cw->stepUp(); + return TRUE; + case Key_Down: + cw->stepDown(); + return TRUE; + case Key_Backspace: + if ( ::qt_cast<TQDateEdit*>(cw) ) + ((TQDateEdit*)cw)->removeFirstNumber( d->focusSection() ); + else if ( ::qt_cast<TQTimeEdit*>(cw) ) + ((TQTimeEdit*)cw)->removeFirstNumber( d->focusSection() ); + return TRUE; + case Key_Delete: + cw->removeLastNumber( d->focusSection() ); + return TRUE; + case Key_Tab: + case Key_BackTab: { + if ( ke->state() == TQt::ControlButton ) + return FALSE; + + TQWidget *w = this; + bool hadDateEdit = FALSE; + while ( w ) { + if ( ::qt_cast<TQDateTimeSpinWidget*>(w) && qstrcmp( w->name(), "qt_spin_widget" ) != 0 || + ::qt_cast<TQDateTimeEdit*>(w) ) + break; + hadDateEdit = hadDateEdit || ::qt_cast<TQDateEdit*>(w); + w = w->parentWidget(); + } + + if ( w ) { + if ( !::qt_cast<TQDateTimeEdit*>(w) ) { + w = w->parentWidget(); + } else { + TQDateTimeEdit *ed = (TQDateTimeEdit*)w; + if ( hadDateEdit && ke->key() == Key_Tab ) { + ed->timeEdit()->setFocus(); + return TRUE; + } else if ( !hadDateEdit && ke->key() == Key_BackTab ) { + ed->dateEdit()->setFocus(); + return TRUE; + } else { + while ( w && !::qt_cast<TQDateTimeEdit*>(w) ) + w = w->parentWidget(); + } + } + + qApp->sendEvent( w, e ); + return TRUE; + } + } break; + default: + TQString txt = ke->text().lower(); + if ( !txt.isEmpty() && !separator().isEmpty() && txt[0] == separator()[0] ) { + // do the same thing as KEY_RIGHT when the user presses the separator key + if ( d->focusSection() < 2 ) { + if ( cw->setFocusSection( focusSection()+1 ) ) + repaint( rect(), FALSE ); + } + return TRUE; + } else if ( !txt.isEmpty() && ::qt_cast<TQTimeEdit*>(cw) && focusSection() == (int) d->sectionCount()-1 ) { + // the first character of the AM/PM indicator toggles if the section has focus + TQTimeEdit *te = (TQTimeEdit*)cw; + TQTime time = te->time(); + if ( lAMPM && lAM && lPM && (te->display()&TQTimeEdit::AMPM) ) { + if ( txt[0] == (*lAM).lower()[0] && time.hour() >= 12 ) { + time.setHMS( time.hour()-12, time.minute(), time.second(), time.msec() ); + te->setTime( time ); + } else if ( txt[0] == (*lPM).lower()[0] && time.hour() < 12 ) { + time.setHMS( time.hour()+12, time.minute(), time.second(), time.msec() ); + te->setTime( time ); + } + } + } + + int num = txt[0].digitValue(); + if ( num != -1 ) { + cw->addNumber( d->focusSection(), num ); + return TRUE; + } + } + } + } + return FALSE; +} + + +/*! + Appends the number section \a sec to the editor. +*/ + +void TQDateTimeEditor::appendSection( const TQNumberSection& sec ) +{ + d->appendSection( sec ); +} + +/*! + Removes all sections from the editor. +*/ + +void TQDateTimeEditor::clearSections() +{ + d->clearSections(); +} + +/*! + Sets the selection of \a sec to start at \a selstart and end at \a + selend. +*/ + +void TQDateTimeEditor::setSectionSelection( int sec, int selstart, int selend ) +{ + d->setSectionSelection( sec, selstart, selend ); +} + +/*! + Sets the separator for all numbered sections to \a s. Note that + currently, only the first character of \a s is used. +*/ + +void TQDateTimeEditor::setSeparator( const TQString& s ) +{ + d->setSeparator( s ); + update(); +} + + +/*! + Returns the editor's separator. +*/ + +TQString TQDateTimeEditor::separator() const +{ + return d->separator(); +} + +/*! + Returns the number of the section that has focus. +*/ + +int TQDateTimeEditor::focusSection() const +{ + return d->focusSection(); +} + + +/*! + Sets the focus to section \a sec. If \a sec does not exist, + nothing happens. +*/ + +bool TQDateTimeEditor::setFocusSection( int sec ) +{ + return d->setFocusSection( sec ); +} + +/*! \class TQDateTimeEditBase + \brief The TQDateTimeEditBase class provides an abstraction for date and edit editors. + + Small abstract class that provides some functions that are common + for both TQDateEdit and TQTimeEdit. It is used internally by + TQDateTimeEditor. +*/ + +/*! + \fn TQDateTimeEditBase::TQDateTimeEditBase(TQWidget *, const char*) + \internal +*/ + +/*! + \fn TQDateTimeEditBase::setFocusSection(int) + \internal +*/ + +/*! \fn TQString TQDateTimeEditBase::sectionFormattedText( int sec ) + \internal + + Pure virtual function which returns the formatted text of section \a + sec. + +*/ + +/*! \fn void TQDateTimeEditBase::stepUp() + \internal + + Pure virtual slot which is called whenever the user increases the + number in a section by pressing the widget's arrow buttons or the + keyboard's arrow keys. +*/ + +/*! \fn void TQDateTimeEditBase::stepDown() + \internal + + Pure virtual slot which is called whenever the user decreases the + number in a section by pressing the widget's arrow buttons or the + keyboard's arrow keys. + +*/ + +/*! \fn void TQDateTimeEditBase::addNumber( int sec, int num ) + \internal + + Pure virtual function which is called whenever the user types a number. + \a sec indicates the section where the number should be added. \a + num is the number that was pressed. +*/ + +/*! \fn void TQDateTimeEditBase::removeLastNumber( int sec ) + \internal + + Pure virtual function which is called whenever the user tries to + remove the last number from \a sec by pressing the delete key. +*/ + +//////////////// + +class TQDateEditPrivate +{ +public: + int y; + int m; + int d; + // remebers the last entry for the day. + // if the day is 31 and you cycle through the months, + // the day will be 31 again if you reach a month with 31 days + // otherwise it will be the highest day in the month + int dayCache; + int yearSection; + int monthSection; + int daySection; + TQDateEdit::Order ord; + bool overwrite; + bool adv; + int timerId; + bool typing; + TQDate min; + TQDate max; + bool changed; + TQDateTimeEditor *ed; + TQSpinWidget *controls; +}; + + +/*! + \class TQDateEdit qdatetimeedit.h + \brief The TQDateEdit class provides a date editor. + + \ingroup advanced + \ingroup time + \mainclass + + TQDateEdit allows the user to edit dates by using the keyboard or + the arrow keys to increase/decrease date values. The arrow keys + can be used to move from section to section within the TQDateEdit + box. Dates appear in accordance with the local date/time settings + or in year, month, day order if the system doesn't provide this + information. It is recommended that the TQDateEdit be initialised + with a date, e.g. + + \code + TQDateEdit *dateEdit = new TQDateEdit( TQDate::currentDate(), this ); + dateEdit->setRange( TQDate::currentDate().addDays( -365 ), + TQDate::currentDate().addDays( 365 ) ); + dateEdit->setOrder( TQDateEdit::MDY ); + dateEdit->setAutoAdvance( TRUE ); + \endcode + + Here we've created a new TQDateEdit object initialised 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. If the auto + advance property is TRUE (as we've set it here) when the user + completes a section of the date, e.g. enters two digits for the + month, they are automatically taken to the next section. + + The maximum and minimum values for a date value in the date editor + default to the maximum and minimum values for a TQDate. You can + change this by calling setMinValue(), setMaxValue() or setRange(). + + Terminology: A TQDateEdit widget comprises three 'sections', one + each for the year, month and day. You can change the separator + character using TQDateTimeEditor::setSeparator(), by default the + separator will be taken from the systems settings. If that is + not possible, it defaults to "-". + + \img datetimewidgets.png Date Time Widgets + + \sa TQDate TQTimeEdit TQDateTimeEdit +*/ + +/*! + \enum TQDateEdit::Order + + This enum defines the order in which the sections that comprise a + date appear. + \value MDY month-day-year + \value DMY day-month-year + \value YMD year-month-day (the default) + \value YDM year-day-month (included for completeness; but should + not be used) +*/ + +/*! + \enum TQTimeEdit::Display + + This enum defines the sections that comprise a time + + \value Hours The hours section + \value Minutes The minutes section + \value Seconds The seconds section + \value AMPM The AM/PM section + + The values can be or'ed together to show any combination. +*/ + +/*! + Constructs an empty date editor which is a child of \a parent and + called name \a name. +*/ + +TQDateEdit::TQDateEdit( TQWidget * parent, const char * name ) + : TQDateTimeEditBase( parent, name ) +{ + init(); + updateButtons(); +} + +/*! + \overload + + Constructs a date editor with the initial value \a date, parent \a + parent and called \a name. + + The date editor is initialized with \a date. +*/ + +TQDateEdit::TQDateEdit( const TQDate& date, TQWidget * parent, const char * name ) + : TQDateTimeEditBase( parent, name ) +{ + init(); + setDate( date ); +} + +/*! \internal +*/ +void TQDateEdit::init() +{ + d = new TQDateEditPrivate(); + d->controls = new TQDateTimeSpinWidget( this, qstrcmp( name(), "qt_datetime_dateedit" ) == 0 ? "qt_spin_widget" : "date edit controls" ); + d->ed = new TQDateTimeEditor( this, d->controls, "date editor" ); + d->controls->setEditWidget( d->ed ); + setFocusProxy( d->ed ); + connect( d->controls, SIGNAL( stepUpPressed() ), SLOT( stepUp() ) ); + connect( d->controls, SIGNAL( stepDownPressed() ), SLOT( stepDown() ) ); + connect( this, SIGNAL( valueChanged(const TQDate&) ), + SLOT( updateButtons() ) ); + d->ed->appendSection( TQNumberSection( 0,4 ) ); + d->ed->appendSection( TQNumberSection( 5,7 ) ); + d->ed->appendSection( TQNumberSection( 8,10 ) ); + + d->yearSection = -1; + d->monthSection = -1; + d->daySection = -1; + + d->y = 0; + d->m = 0; + d->d = 0; + d->dayCache = 0; + setOrder( localOrder() ); + setFocusSection( 0 ); + d->overwrite = TRUE; + d->adv = FALSE; + d->timerId = 0; + d->typing = FALSE; + d->min = TQDate( 1752, 9, 14 ); + d->max = TQDate( 8000, 12, 31 ); + d->changed = FALSE; + + setSizePolicy( TQSizePolicy::Minimum, TQSizePolicy::Fixed ); + + refcount++; +} + +/*! + Destroys the object and frees any allocated resources. +*/ + +TQDateEdit::~TQDateEdit() +{ + delete d; + if ( !--refcount ) + cleanup(); +} + +/*! + \property TQDateEdit::minValue + + \brief the editor's minimum value + + Setting the minimum date value is equivalent to calling + TQDateEdit::setRange( \e d, maxValue() ), where \e d is the minimum + date. The default minimum date is 1752-09-14. + + \sa maxValue setRange() +*/ + +TQDate TQDateEdit::minValue() const +{ + return d->min; +} + +/*! + \property TQDateEdit::maxValue + + \brief the editor's maximum value + + Setting the maximum date value for the editor is equivalent to + calling TQDateEdit::setRange( minValue(), \e d ), where \e d is the + maximum date. The default maximum date is 8000-12-31. + + \sa minValue setRange() +*/ + +TQDate TQDateEdit::maxValue() const +{ + return d->max; +} + + +/*! + Sets the valid input range for the editor to be from \a min to \a + max inclusive. If \a min is invalid no minimum date will be set. + Similarly, if \a max is invalid no maximum date will be set. +*/ + +void TQDateEdit::setRange( const TQDate& min, const TQDate& max ) +{ + if ( min.isValid() ) + d->min = min; + if ( max.isValid() ) + d->max = max; +} + +/*! + Sets the separator to \a s. Note that currently only the first + character of \a s is used. +*/ + +void TQDateEdit::setSeparator( const TQString& s ) +{ + d->ed->setSeparator( s ); +} + +/*! + Returns the editor's separator. +*/ + +TQString TQDateEdit::separator() const +{ + return d->ed->separator(); +} + + +/*! + Enables/disables the push buttons according to the min/max date + for this widget. +*/ + +void TQDateEdit::updateButtons() +{ + if ( !isEnabled() ) + return; + + bool upEnabled = date() < maxValue(); + bool downEnabled = date() > minValue(); + + d->controls->setUpEnabled( upEnabled ); + d->controls->setDownEnabled( downEnabled ); +} + +/*! \reimp + */ +void TQDateEdit::resizeEvent( TQResizeEvent * ) +{ + d->controls->resize( width(), height() ); +} + +/*! \reimp + +*/ +TQSize TQDateEdit::sizeHint() const +{ + constPolish(); + TQFontMetrics fm( font() ); + int fw = style().pixelMetric( TQStyle::PM_DefaultFrameWidth, this ); + int h = TQMAX( fm.lineSpacing(), 14 ) + 2; + int w = 2 + fm.width( '9' ) * 8 + fm.width( d->ed->separator() ) * 2 + d->controls->upRect().width() + fw * 4; + + return TQSize( w, TQMAX(h + fw * 2,20) ).expandedTo( TQApplication::globalStrut() ); +} + +/*! \reimp + +*/ +TQSize TQDateEdit::minimumSizeHint() const +{ + return sizeHint(); +} + + +/*! + Returns the formatted number for section \a sec. This will + correspond to either the year, month or day section, depending on + the current display order. + + \sa setOrder() +*/ + +TQString TQDateEdit::sectionFormattedText( int sec ) +{ + TQString txt; + txt = sectionText( sec ); + if ( d->typing && sec == d->ed->focusSection() ) + d->ed->setSectionSelection( sec, sectionOffsetEnd( sec ) - txt.length(), + sectionOffsetEnd( sec ) ); + else + d->ed->setSectionSelection( sec, sectionOffsetEnd( sec ) - sectionLength( sec ), + sectionOffsetEnd( sec ) ); + txt = txt.rightJustify( sectionLength( sec ), TQDATETIMEEDIT_HIDDEN_CHAR ); + return txt; +} + + +/*! + Returns the desired length (number of digits) of section \a sec. + This will correspond to either the year, month or day section, + depending on the current display order. + + \sa setOrder() +*/ + +int TQDateEdit::sectionLength( int sec ) const +{ + int val = 0; + if ( sec == d->yearSection ) { + val = 4; + } else if ( sec == d->monthSection ) { + val = 2; + } else if ( sec == d->daySection ) { + val = 2; + } + return val; +} + +/*! + Returns the text of section \a sec. This will correspond to either + the year, month or day section, depending on the current display + order. + + \sa setOrder() +*/ + +TQString TQDateEdit::sectionText( int sec ) const +{ + int val = 0; + if ( sec == d->yearSection ) { + val = d->y; + } else if ( sec == d->monthSection ) { + val = d->m; + } else if ( sec == d->daySection ) { + val = d->d; + } + return TQString::number( val ); +} + +/*! \internal + + Returns the end of the section offset \a sec. + +*/ + +int TQDateEdit::sectionOffsetEnd( int sec ) const +{ + if ( sec == d->yearSection ) { + switch( d->ord ) { + case DMY: + case MDY: + return sectionOffsetEnd( sec-1) + separator().length() + sectionLength( sec ); + case YMD: + case YDM: + return sectionLength( sec ); + } + } else if ( sec == d->monthSection ) { + switch( d->ord ) { + case DMY: + case YDM: + case YMD: + return sectionOffsetEnd( sec-1) + separator().length() + sectionLength( sec ); + case MDY: + return sectionLength( sec ); + } + } else if ( sec == d->daySection ) { + switch( d->ord ) { + case DMY: + return sectionLength( sec ); + case YMD: + case MDY: + case YDM: + return sectionOffsetEnd( sec-1 ) + separator().length() + sectionLength( sec ); + } + } + return 0; +} + + +/*! + \property TQDateEdit::order + \brief the order in which the year, month and day appear + + The default order is locale dependent. + + \sa Order +*/ + +void TQDateEdit::setOrder( TQDateEdit::Order order ) +{ + d->ord = order; + switch( d->ord ) { + case DMY: + d->yearSection = 2; + d->monthSection = 1; + d->daySection = 0; + break; + case MDY: + d->yearSection = 2; + d->monthSection = 0; + d->daySection = 1; + break; + case YMD: + d->yearSection = 0; + d->monthSection = 1; + d->daySection = 2; + break; + case YDM: + d->yearSection = 0; + d->monthSection = 2; + d->daySection = 1; + break; + } + if ( isVisible() ) + d->ed->repaint( d->ed->rect(), FALSE ); +} + + +TQDateEdit::Order TQDateEdit::order() const +{ + return d->ord; +} + + +/*! \reimp + +*/ +void TQDateEdit::stepUp() +{ + int sec = d->ed->focusSection(); + bool accepted = FALSE; + if ( sec == d->yearSection ) { + if ( !outOfRange( d->y+1, d->m, d->d ) ) { + accepted = TRUE; + setYear( d->y+1 ); + } + } else if ( sec == d->monthSection ) { + if ( !outOfRange( d->y, d->m+1, d->d ) ) { + accepted = TRUE; + setMonth( d->m+1 ); + } + } else if ( sec == d->daySection ) { + if ( !outOfRange( d->y, d->m, d->d+1 ) ) { + accepted = TRUE; + setDay( d->d+1 ); + } + } + if ( accepted ) { + d->changed = FALSE; + emit valueChanged( date() ); + } + d->ed->repaint( d->ed->rect(), FALSE ); +} + + + +/*! \reimp + +*/ + +void TQDateEdit::stepDown() +{ + int sec = d->ed->focusSection(); + bool accepted = FALSE; + if ( sec == d->yearSection ) { + if ( !outOfRange( d->y-1, d->m, d->d ) ) { + accepted = TRUE; + setYear( d->y-1 ); + } + } else if ( sec == d->monthSection ) { + if ( !outOfRange( d->y, d->m-1, d->d ) ) { + accepted = TRUE; + setMonth( d->m-1 ); + } + } else if ( sec == d->daySection ) { + if ( !outOfRange( d->y, d->m, d->d-1 ) ) { + accepted = TRUE; + setDay( d->d-1 ); + } + } + if ( accepted ) { + d->changed = FALSE; + emit valueChanged( date() ); + } + d->ed->repaint( d->ed->rect(), FALSE ); +} + +/*! + Sets the year to \a year, which must be a valid year. The range + currently supported is from 1752 to 8000. + + \sa TQDate +*/ + +void TQDateEdit::setYear( int year ) +{ + if ( year < 1752 ) + year = 1752; + if ( year > 8000 ) + year = 8000; + if ( !outOfRange( year, d->m, d->d ) ) { + d->y = year; + setMonth( d->m ); + int tmp = d->dayCache; + setDay( d->dayCache ); + d->dayCache = tmp; + } +} + + +/*! + Sets the month to \a month, which must be a valid month, i.e. + between 1 and 12. +*/ + +void TQDateEdit::setMonth( int month ) +{ + if ( month < 1 ) + month = 1; + if ( month > 12 ) + month = 12; + if ( !outOfRange( d->y, month, d->d ) ) { + d->m = month; + int tmp = d->dayCache; + setDay( d->dayCache ); + d->dayCache = tmp; + } +} + + +/*! + Sets the day to \a day, which must be a valid day. The function + will ensure that the \a day set is valid for the month and year. +*/ + +void TQDateEdit::setDay( int day ) +{ + if ( day < 1 ) + day = 1; + if ( day > 31 ) + day = 31; + if ( d->m > 0 && d->y > 1752 ) { + while ( !TQDate::isValid( d->y, d->m, day ) ) + --day; + if ( !outOfRange( d->y, d->m, day ) ) + d->d = day; + } else if ( d->m > 0 ) { + if ( day > 0 && day < 32 ) { + if ( !outOfRange( d->y, d->m, day ) ) + d->d = day; + } + } + d->dayCache = d->d; +} + + +/*! + \property TQDateEdit::date + \brief the editor's date value. + + If the date property is not valid, the editor displays all zeroes + and TQDateEdit::date() will return an invalid date. It is strongly + recommended that the editor is given a default date value (e.g. + currentDate()). That way, attempts to set the date property to an + invalid date will fail. + + When changing the date property, if the date is less than + minValue(), or is greater than maxValue(), nothing happens. +*/ + +void TQDateEdit::setDate( const TQDate& date ) +{ + if ( !date.isValid() ) { + d->y = 0; + d->m = 0; + d->d = 0; + d->dayCache = 0; + } else { + if ( date > maxValue() || date < minValue() ) + return; + d->y = date.year(); + d->m = date.month(); + d->d = date.day(); + d->dayCache = d->d; + emit valueChanged( date ); + } + d->changed = FALSE; + d->ed->repaint( d->ed->rect(), FALSE ); +} + +TQDate TQDateEdit::date() const +{ + if ( TQDate::isValid( d->y, d->m, d->d ) ) + return TQDate( d->y, d->m, d->d ); + return TQDate(); +} + +/*! \internal + + Returns TRUE if \a y, \a m, \a d is out of range, otherwise returns + FALSE. + + \sa setRange() + +*/ + +bool TQDateEdit::outOfRange( int y, int m, int d ) const +{ + if ( TQDate::isValid( y, m, d ) ) { + TQDate currentDate( y, m, d ); + if ( currentDate > maxValue() || + currentDate < minValue() ) { + //## outOfRange should set overwrite? + return TRUE; + } + return FALSE; + } + return FALSE; /* assume ok */ +} + +/*! \reimp + +*/ + +void TQDateEdit::addNumber( int sec, int num ) +{ + if ( sec == -1 ) + return; + killTimer( d->timerId ); + bool overwrite = FALSE; + bool accepted = FALSE; + d->typing = TRUE; + TQString txt; + if ( sec == d->yearSection ) { + txt = TQString::number( d->y ); + if ( d->overwrite || txt.length() == 4 ) { + accepted = TRUE; + d->y = num; + } else { + txt += TQString::number( num ); + if ( txt.length() == 4 ) { + int val = txt.toInt(); + if ( val < 1792 ) + d->y = 1792; + else if ( val > 8000 ) + d->y = 8000; + else if ( outOfRange( val, d->m, d->d ) ) + txt = TQString::number( d->y ); + else { + accepted = TRUE; + d->y = val; + } + } else { + accepted = TRUE; + d->y = txt.toInt(); + } + if ( d->adv && txt.length() == 4 ) { + d->ed->setFocusSection( d->ed->focusSection()+1 ); + overwrite = TRUE; + } + } + } else if ( sec == d->monthSection ) { + txt = TQString::number( d->m ); + if ( d->overwrite || txt.length() == 2 ) { + accepted = TRUE; + d->m = num; + } else { + txt += TQString::number( num ); + int temp = txt.toInt(); + if ( temp > 12 ) + temp = num; + if ( outOfRange( d->y, temp, d->d ) ) + txt = TQString::number( d->m ); + else { + accepted = TRUE; + d->m = temp; + } + if ( d->adv && txt.length() == 2 ) { + d->ed->setFocusSection( d->ed->focusSection()+1 ); + overwrite = TRUE; + } + } + } else if ( sec == d->daySection ) { + txt = TQString::number( d->d ); + if ( d->overwrite || txt.length() == 2 ) { + accepted = TRUE; + d->d = num; + d->dayCache = d->d; + } else { + txt += TQString::number( num ); + int temp = txt.toInt(); + if ( temp > 31 ) + temp = num; + if ( outOfRange( d->y, d->m, temp ) ) + txt = TQString::number( d->d ); + else { + accepted = TRUE; + d->d = temp; + d->dayCache = d->d; + } + if ( d->adv && txt.length() == 2 ) { + d->ed->setFocusSection( d->ed->focusSection()+1 ); + overwrite = TRUE; + } + } + } + if ( accepted ) { + d->changed = FALSE; + emit valueChanged( date() ); + } + d->overwrite = overwrite; + d->timerId = startTimer( qApp->doubleClickInterval()*4 ); + d->ed->repaint( d->ed->rect(), FALSE ); +} + + +/*! \reimp + +*/ + +bool TQDateEdit::setFocusSection( int s ) +{ + if ( s != d->ed->focusSection() ) { + killTimer( d->timerId ); + d->overwrite = TRUE; + d->typing = FALSE; + fix(); // will emit valueChanged if necessary + } + return d->ed->setFocusSection( s ); +} + + +/*! + Attempts to fix any invalid date entries. + + The rules applied are as follows: + + \list + \i If the year has four digits it is left unchanged. + \i If the year has two digits, the year will be changed to four + digits in the range current year - 70 to current year + 29. + \i If the year has three digits in the range 100..999, the + current millennium, i.e. 2000, will be added giving a year + in the range 2100..2999. + \i If the day or month is 0 then it will be set to 1 or the + minimum valid day\month in the range. + \endlist + +*/ + +void TQDateEdit::fix() +{ + bool changed = FALSE; + int currentYear = TQDate::currentDate().year(); + int year = d->y; + if ( year < 100 ) { + int currentCentury = currentYear / 100; + year += currentCentury * 100; + if ( currentYear > year ) { + if ( currentYear > year + 70 ) + year += 100; + } else { + if ( year >= currentYear + 30 ) + year -= 100; + } + changed = TRUE; + } else if ( year < 1000 ) { + int currentMillennium = currentYear / 10; + year += currentMillennium * 10; + changed = TRUE; + } else if (d->d == 0) { + d->d = 1; + changed = TRUE; + } else if (d->m == 0) { + d->m = 1; + changed = TRUE; + } + if ( outOfRange( year, d->m, d->d ) ) { + if ( minValue().isValid() && date() < minValue() ) { + d->d = minValue().day(); + d->dayCache = d->d; + d->m = minValue().month(); + d->y = minValue().year(); + } + if ( date() > maxValue() ) { + d->d = maxValue().day(); + d->dayCache = d->d; + d->m = maxValue().month(); + d->y = maxValue().year(); + } + changed = TRUE; + } else if ( changed ) + setYear( year ); + if ( changed ) { + emit valueChanged( date() ); + d->changed = FALSE; + } +} + + +/*! \reimp + +*/ + +bool TQDateEdit::event( TQEvent *e ) +{ + if( e->type() == TQEvent::FocusOut ) { + d->typing = FALSE; + fix(); + // the following can't be done in fix() because fix() called + // from all over the place and it will break the old behaviour + if ( !TQDate::isValid( d->y, d->m, d->d ) ) { + d->dayCache = d->d; + int i = d->d; + for ( ; i > 0; i-- ) { + d->d = i; + if ( TQDate::isValid( d->y, d->m, d->d ) ) + break; + } + d->changed = TRUE; + } + if ( d->changed ) { + emit valueChanged( date() ); + d->changed = FALSE; + } + } else if ( e->type() == TQEvent::LocaleChange ) { + readLocaleSettings(); + d->ed->setSeparator( localDateSep() ); + setOrder( localOrder() ); + } + return TQDateTimeEditBase::event( e ); +} + +/*! + \internal + + Function which is called whenever the user tries to + remove the first number from \a sec by pressing the backspace key. +*/ + +void TQDateEdit::removeFirstNumber( int sec ) +{ + if ( sec == -1 ) + return; + TQString txt; + if ( sec == d->yearSection ) { + txt = TQString::number( d->y ); + txt = txt.mid( 1, txt.length() ) + "0"; + d->y = txt.toInt(); + } else if ( sec == d->monthSection ) { + txt = TQString::number( d->m ); + txt = txt.mid( 1, txt.length() ) + "0"; + d->m = txt.toInt(); + } else if ( sec == d->daySection ) { + txt = TQString::number( d->d ); + txt = txt.mid( 1, txt.length() ) + "0"; + d->d = txt.toInt(); + d->dayCache = d->d; + } + d->ed->repaint( d->ed->rect(), FALSE ); +} + +/*! \reimp + +*/ + +void TQDateEdit::removeLastNumber( int sec ) +{ + if ( sec == -1 ) + return; + TQString txt; + if ( sec == d->yearSection ) { + txt = TQString::number( d->y ); + txt = txt.mid( 0, txt.length()-1 ); + d->y = txt.toInt(); + } else if ( sec == d->monthSection ) { + txt = TQString::number( d->m ); + txt = txt.mid( 0, txt.length()-1 ); + d->m = txt.toInt(); + } else if ( sec == d->daySection ) { + txt = TQString::number( d->d ); + txt = txt.mid( 0, txt.length()-1 ); + d->d = txt.toInt(); + d->dayCache = d->d; + } + d->ed->repaint( d->ed->rect(), FALSE ); +} + +/*! + \property TQDateEdit::autoAdvance + \brief whether the editor automatically advances to the next + section + + If autoAdvance is TRUE, the editor will automatically advance + focus to the next date section if a user has completed a section. + The default is FALSE. +*/ + +void TQDateEdit::setAutoAdvance( bool advance ) +{ + d->adv = advance; +} + + +bool TQDateEdit::autoAdvance() const +{ + return d->adv; +} + +/*! \reimp +*/ + +void TQDateEdit::timerEvent( TQTimerEvent * ) +{ + d->overwrite = TRUE; +} + +/*! + \fn void TQDateEdit::valueChanged( const TQDate& date ) + + This signal is emitted whenever the editor's value changes. The \a + date parameter is the new value. +*/ + +/////////// + +class TQTimeEditPrivate +{ +public: + int h; + int m; + int s; + uint display; + bool adv; + bool overwrite; + int timerId; + bool typing; + TQTime min; + TQTime max; + bool changed; + TQDateTimeEditor *ed; + TQSpinWidget *controls; +}; + +/*! + \class TQTimeEdit qdatetimeedit.h + \brief The TQTimeEdit class provides a time editor. + + \ingroup advanced + \ingroup time + \mainclass + + TQTimeEdit allows the user to edit times by using the keyboard or + the arrow keys to increase/decrease time values. The arrow keys + can be used to move from section to section within the TQTimeEdit + box. The user can automatically be moved to the next section once + they complete a section using setAutoAdvance(). Times appear in + hour, minute, second order. It is recommended that the TQTimeEdit + is initialised with a time, e.g. + \code + TQTime timeNow = TQTime::currentTime(); + TQTimeEdit *timeEdit = new TQTimeEdit( timeNow, this ); + timeEdit->setRange( timeNow, timeNow.addSecs( 60 * 60 ) ); + \endcode + Here we've created a TQTimeEdit widget set to the current time. + We've also set the minimum value to the current time and the + maximum time to one hour from now. + + The maximum and minimum values for a time value in the time editor + default to the maximum and minimum values for a TQTime. You can + change this by calling setMinValue(), setMaxValue() or setRange(). + + Terminology: A TQTimeWidget consists of three sections, one each + for the hour, minute and second. You can change the separator + character using setSeparator(), by default the separator is read + from the system's settings. + + \img datetimewidgets.png Date Time Widgets + + \sa TQTime TQDateEdit TQDateTimeEdit +*/ + + +/*! + Constructs an empty time edit with parent \a parent and called \a + name. +*/ + +TQTimeEdit::TQTimeEdit( TQWidget * parent, const char * name ) + : TQDateTimeEditBase( parent, name ) +{ + init(); +} + +/*! + \overload + + Constructs a time edit with the initial time value, \a time, + parent \a parent and called \a name. +*/ + +TQTimeEdit::TQTimeEdit( const TQTime& time, TQWidget * parent, const char * name ) + : TQDateTimeEditBase( parent, name ) +{ + init(); + setTime( time ); +} + +/*! \internal + */ + +void TQTimeEdit::init() +{ + d = new TQTimeEditPrivate(); + d->controls = new TQDateTimeSpinWidget( this, qstrcmp( name(), "qt_datetime_timeedit" ) == 0 ? "qt_spin_widget" : "time edit controls" ); + d->ed = new TQDateTimeEditor( this, d->controls, "time edit base" ); + d->controls->setEditWidget( d->ed ); + setFocusProxy( d->ed ); + connect( d->controls, SIGNAL( stepUpPressed() ), SLOT( stepUp() ) ); + connect( d->controls, SIGNAL( stepDownPressed() ), SLOT( stepDown() ) ); + + d->ed->appendSection( TQNumberSection( 0,0, TRUE, 0 ) ); + d->ed->appendSection( TQNumberSection( 0,0, TRUE, 1 ) ); + d->ed->appendSection( TQNumberSection( 0,0, TRUE, 2 ) ); + d->ed->setSeparator( localTimeSep() ); + + d->h = 0; + d->m = 0; + d->s = 0; + d->display = Hours | Minutes | Seconds; + if ( lAMPM ) { + d->display |= AMPM; + d->ed->appendSection( TQNumberSection( 0,0, FALSE, 3 ) ); + } + d->adv = FALSE; + d->overwrite = TRUE; + d->timerId = 0; + d->typing = FALSE; + d->min = TQTime( 0, 0, 0 ); + d->max = TQTime( 23, 59, 59 ); + d->changed = FALSE; + + setSizePolicy( TQSizePolicy::Minimum, TQSizePolicy::Fixed ); + + refcount++; +} + +/*! + Destroys the object and frees any allocated resources. +*/ + +TQTimeEdit::~TQTimeEdit() +{ + delete d; + if ( !--refcount ) + cleanup(); +} + +/*! + \property TQTimeEdit::minValue + \brief the minimum time value + + Setting the minimum time value is equivalent to calling + TQTimeEdit::setRange( \e t, maxValue() ), where \e t is the minimum + time. The default minimum time is 00:00:00. + + \sa maxValue setRange() +*/ + +TQTime TQTimeEdit::minValue() const +{ + return d->min; +} + +/*! + \property TQTimeEdit::maxValue + \brief the maximum time value + + Setting the maximum time value is equivalent to calling + TQTimeEdit::setRange( minValue(), \e t ), where \e t is the maximum + time. The default maximum time is 23:59:59. + + \sa minValue setRange() +*/ + +TQTime TQTimeEdit::maxValue() const +{ + return d->max; +} + + +/*! + Sets the valid input range for the editor to be from \a min to \a + max inclusive. If \a min is invalid no minimum time is set. + Similarly, if \a max is invalid no maximum time is set. +*/ + +void TQTimeEdit::setRange( const TQTime& min, const TQTime& max ) +{ + if ( min.isValid() ) + d->min = min; + if ( max.isValid() ) + d->max = max; +} + +/*! + \property TQTimeEdit::display + \brief the sections that are displayed in the time edit + + The value can be any combination of the values in the Display enum. + By default, the widget displays hours, minutes and seconds. +*/ +void TQTimeEdit::setDisplay( uint display ) +{ + if ( d->display == display ) + return; + + d->ed->clearSections(); + d->display = display; + if ( d->display & Hours ) + d->ed->appendSection( TQNumberSection( 0,0, TRUE, 0 ) ); + if ( d->display & Minutes ) + d->ed->appendSection( TQNumberSection( 0,0, TRUE, 1 ) ); + if ( d->display & Seconds ) + d->ed->appendSection( TQNumberSection( 0,0, TRUE, 2 ) ); + if ( d->display & AMPM ) + d->ed->appendSection( TQNumberSection( 0,0, FALSE, 3 ) ); + + d->ed->setFocusSection( 0 ); + d->ed->update(); +} + +uint TQTimeEdit::display() const +{ + return d->display; +} + +/*! + \property TQTimeEdit::time + \brief the editor's time value. + + When changing the time property, if the time is less than + minValue(), or is greater than maxValue(), nothing happens. +*/ + +void TQTimeEdit::setTime( const TQTime& time ) +{ + if ( !time.isValid() ) { + d->h = 0; + d->m = 0; + d->s = 0; + } else { + if ( time > maxValue() || time < minValue() ) + return; + d->h = time.hour(); + d->m = time.minute(); + d->s = time.second(); + emit valueChanged( time ); + } + d->changed = FALSE; + d->ed->repaint( d->ed->rect(), FALSE ); +} + +TQTime TQTimeEdit::time() const +{ + if ( TQTime::isValid( d->h, d->m, d->s ) ) + return TQTime( d->h, d->m, d->s ); + return TQTime(); +} + +/*! + \property TQTimeEdit::autoAdvance + \brief whether the editor automatically advances to the next + section + + If autoAdvance is TRUE, the editor will automatically advance + focus to the next time section if a user has completed a section. + The default is FALSE. +*/ + +void TQTimeEdit::setAutoAdvance( bool advance ) +{ + d->adv = advance; +} + +bool TQTimeEdit::autoAdvance() const +{ + return d->adv; +} + +/*! + Sets the separator to \a s. Note that currently only the first + character of \a s is used. +*/ + +void TQTimeEdit::setSeparator( const TQString& s ) +{ + d->ed->setSeparator( s ); +} + +/*! + Returns the editor's separator. +*/ + +TQString TQTimeEdit::separator() const +{ + return d->ed->separator(); +} + + +/*! + \fn void TQTimeEdit::valueChanged( const TQTime& time ) + + This signal is emitted whenever the editor's value changes. The \a + time parameter is the new value. +*/ + +/*! \reimp + +*/ + +bool TQTimeEdit::event( TQEvent *e ) +{ + if ( e->type() == TQEvent::FocusOut ) { + d->typing = FALSE; + if ( d->changed ) { + emit valueChanged( time() ); + d->changed = FALSE; + } + } else if ( e->type() == TQEvent::LocaleChange ) { + readLocaleSettings(); + d->ed->setSeparator( localTimeSep() ); + } + return TQDateTimeEditBase::event( e ); +} + +/*! \reimp + +*/ + +void TQTimeEdit::timerEvent( TQTimerEvent * ) +{ + d->overwrite = TRUE; +} + + +/*! \reimp + +*/ + +void TQTimeEdit::stepUp() +{ + if (minValue() > maxValue()) { + return; + } + int sec = d->ed->mapSection( d->ed->focusSection() ); + bool accepted = TRUE; + switch( sec ) { + case 0: + do { + d->h = (d->h + 1) % 24; + } while (outOfRange(d->h, d->m, d->s)); + break; + case 1: + do { + d->m = (d->m + 1) % 60; + } while (outOfRange(d->h, d->m, d->s)); + break; + case 2: + do { + d->s = (d->s + 1) % 60; + } while (outOfRange(d->h, d->m, d->s)); + break; + case 3: + if ( d->h < 12 ) + setHour( d->h+12 ); + else + setHour( d->h-12 ); + break; + default: + accepted = FALSE; +#ifdef QT_CHECK_RANGE + qWarning( "TQTimeEdit::stepUp: Focus section out of range!" ); +#endif + break; + } + if ( accepted ) { + d->changed = FALSE; + emit valueChanged( time() ); + } + d->ed->repaint( d->ed->rect(), FALSE ); +} + + +/*! \reimp + +*/ + +void TQTimeEdit::stepDown() +{ + if (minValue() > maxValue()) { + return; + } + + int sec = d->ed->mapSection( d->ed->focusSection() ); + bool accepted = TRUE; + switch( sec ) { + case 0: + do { + if (--d->h < 0) + d->h = 23; + } while (outOfRange(d->h, d->m, d->s)); + break; + case 1: + do { + if (--d->m < 0) + d->m = 59; + } while (outOfRange(d->h, d->m, d->s)); + break; + case 2: + do { + if (--d->s < 0) + d->s = 59; + } while (outOfRange(d->h, d->m, d->s)); + break; + case 3: + if ( d->h > 11 ) + setHour( d->h-12 ); + else + setHour( d->h+12 ); + break; + default: + accepted = FALSE; +#ifdef QT_CHECK_RANGE + qWarning( "TQTimeEdit::stepDown: Focus section out of range!" ); +#endif + break; + } + if ( accepted ) { + d->changed = FALSE; + emit valueChanged( time() ); + } + d->ed->repaint( d->ed->rect(), FALSE ); +} + + +/*! + Returns the formatted number for section \a sec. This will + correspond to either the hour, minute or second section, depending + on \a sec. +*/ + +TQString TQTimeEdit::sectionFormattedText( int sec ) +{ + TQString txt; + txt = sectionText( sec ); + txt = txt.rightJustify( 2, TQDATETIMEEDIT_HIDDEN_CHAR ); + int offset = sec*2+sec*separator().length() + txt.length(); + if ( d->typing && sec == d->ed->focusSection() ) + d->ed->setSectionSelection( sec, offset - txt.length(), offset ); + else + d->ed->setSectionSelection( sec, offset - txt.length(), offset ); + + return txt; +} + + +/*! \reimp + +*/ + +bool TQTimeEdit::setFocusSection( int sec ) +{ + if ( sec != d->ed->focusSection() ) { + killTimer( d->timerId ); + d->overwrite = TRUE; + d->typing = FALSE; + TQString txt = sectionText( sec ); + txt = txt.rightJustify( 2, TQDATETIMEEDIT_HIDDEN_CHAR ); + int offset = sec*2+sec*separator().length() + txt.length(); + d->ed->setSectionSelection( sec, offset - txt.length(), offset ); + if ( d->changed ) { + emit valueChanged( time() ); + d->changed = FALSE; + } + } + return d->ed->setFocusSection( sec ); +} + + +/*! + Sets the hour to \a h, which must be a valid hour, i.e. in the + range 0..24. +*/ + +void TQTimeEdit::setHour( int h ) +{ + if ( h < 0 ) + h = 0; + if ( h > 23 ) + h = 23; + d->h = h; +} + + +/*! + Sets the minute to \a m, which must be a valid minute, i.e. in the + range 0..59. +*/ + +void TQTimeEdit::setMinute( int m ) +{ + if ( m < 0 ) + m = 0; + if ( m > 59 ) + m = 59; + d->m = m; +} + + +/*! + Sets the second to \a s, which must be a valid second, i.e. in the + range 0..59. +*/ + +void TQTimeEdit::setSecond( int s ) +{ + if ( s < 0 ) + s = 0; + if ( s > 59 ) + s = 59; + d->s = s; +} + + +/*! \internal + + Returns the text of section \a sec. + +*/ + +TQString TQTimeEdit::sectionText( int sec ) +{ + sec = d->ed->mapSection( sec ); + + TQString txt; + switch( sec ) { + case 0: + if ( !(d->display & AMPM) || ( d->h < 13 && d->h ) ) { // I wished the day stared at 0:00 for everybody + txt = TQString::number( d->h ); + } else { + if ( d->h ) + txt = TQString::number( d->h - 12 ); + else + txt = "12"; + } + break; + case 1: + txt = TQString::number( d->m ); + break; + case 2: + txt = TQString::number( d->s ); + break; + case 3: + if ( d->h < 12 ) { + if ( lAM ) + txt = *lAM; + else + txt = TQString::fromLatin1( "AM" ); + } else { + if ( lPM ) + txt = *lPM; + else + txt = TQString::fromLatin1( "PM" ); + } + break; + default: + break; + } + return txt; +} + + +/*! \internal + Returns TRUE if \a h, \a m, and \a s are out of range. + */ + +bool TQTimeEdit::outOfRange( int h, int m, int s ) const +{ + if ( TQTime::isValid( h, m, s ) ) { + TQTime currentTime( h, m, s ); + if ( currentTime > maxValue() || + currentTime < minValue() ) + return TRUE; + else + return FALSE; + } + return TRUE; +} + +/*! \reimp + +*/ + +void TQTimeEdit::addNumber( int sec, int num ) +{ + if ( sec == -1 ) + return; + sec = d->ed->mapSection( sec ); + killTimer( d->timerId ); + bool overwrite = FALSE; + bool accepted = FALSE; + d->typing = TRUE; + TQString txt; + + switch( sec ) { + case 0: + txt = ( d->display & AMPM && d->h > 12 ) ? + TQString::number( d->h - 12 ) : TQString::number( d->h ); + + if ( d->overwrite || txt.length() == 2 ) { + if ( d->display & AMPM && num == 0 ) + break; // Don't process 0 in 12 hour clock mode + if ( d->display & AMPM && d->h > 11 ) + num += 12; + if ( !outOfRange( num, d->m, d->s ) ) { + accepted = TRUE; + d->h = num; + } + } else { + txt += TQString::number( num ); + int temp = txt.toInt(); + + if ( d->display & AMPM ) { + if ( temp == 12 ) { + if ( d->h < 12 ) { + temp = 0; + } + accepted = TRUE; + } else if ( outOfRange( temp + 12, d->m, d->s ) ) { + txt = TQString::number( d->h ); + } else { + if ( d->h > 11 ) { + temp += 12; + } + accepted = TRUE; + } + } else if ( !(d->display & AMPM) && outOfRange( temp, d->m, d->s ) ) { + txt = TQString::number( d->h ); + } else { + accepted = TRUE; + } + + if ( accepted ) + d->h = temp; + + if ( d->adv && txt.length() == 2 ) { + setFocusSection( d->ed->focusSection()+1 ); + overwrite = TRUE; + } + } + break; + + case 1: + txt = TQString::number( d->m ); + if ( d->overwrite || txt.length() == 2 ) { + if ( !outOfRange( d->h, num, d->s ) ) { + accepted = TRUE; + d->m = num; + } + } else { + txt += TQString::number( num ); + int temp = txt.toInt(); + if ( temp > 59 ) + temp = num; + if ( outOfRange( d->h, temp, d->s ) ) + txt = TQString::number( d->m ); + else { + accepted = TRUE; + d->m = temp; + } + if ( d->adv && txt.length() == 2 ) { + setFocusSection( d->ed->focusSection()+1 ); + overwrite = TRUE; + } + } + break; + + case 2: + txt = TQString::number( d->s ); + if ( d->overwrite || txt.length() == 2 ) { + if ( !outOfRange( d->h, d->m, num ) ) { + accepted = TRUE; + d->s = num; + } + } else { + txt += TQString::number( num ); + int temp = txt.toInt(); + if ( temp > 59 ) + temp = num; + if ( outOfRange( d->h, d->m, temp ) ) + txt = TQString::number( d->s ); + else { + accepted = TRUE; + d->s = temp; + } + if ( d->adv && txt.length() == 2 ) { + setFocusSection( d->ed->focusSection()+1 ); + overwrite = TRUE; + } + } + break; + + case 3: + break; + + default: + break; + } + d->changed = !accepted; + if ( accepted ) + emit valueChanged( time() ); + d->overwrite = overwrite; + d->timerId = startTimer( qApp->doubleClickInterval()*4 ); + d->ed->repaint( d->ed->rect(), FALSE ); +} + + +/*! + \internal + + Function which is called whenever the user tries to + remove the first number from \a sec by pressing the backspace key. +*/ + +void TQTimeEdit::removeFirstNumber( int sec ) +{ + if ( sec == -1 ) + return; + sec = d->ed->mapSection( sec ); + TQString txt; + switch( sec ) { + case 0: + txt = TQString::number( d->h ); + break; + case 1: + txt = TQString::number( d->m ); + break; + case 2: + txt = TQString::number( d->s ); + break; + } + txt = txt.mid( 1, txt.length() ) + "0"; + switch( sec ) { + case 0: + d->h = txt.toInt(); + break; + case 1: + d->m = txt.toInt(); + break; + case 2: + d->s = txt.toInt(); + break; + } + d->ed->repaint( d->ed->rect(), FALSE ); +} + +/*! \reimp + +*/ +void TQTimeEdit::removeLastNumber( int sec ) +{ + if ( sec == -1 ) + return; + sec = d->ed->mapSection( sec ); + TQString txt; + switch( sec ) { + case 0: + txt = TQString::number( d->h ); + break; + case 1: + txt = TQString::number( d->m ); + break; + case 2: + txt = TQString::number( d->s ); + break; + } + txt = txt.mid( 0, txt.length()-1 ); + switch( sec ) { + case 0: + d->h = txt.toInt(); + break; + case 1: + d->m = txt.toInt(); + break; + case 2: + d->s = txt.toInt(); + break; + } + d->ed->repaint( d->ed->rect(), FALSE ); +} + +/*! \reimp + */ +void TQTimeEdit::resizeEvent( TQResizeEvent * ) +{ + d->controls->resize( width(), height() ); +} + +/*! \reimp +*/ +TQSize TQTimeEdit::sizeHint() const +{ + constPolish(); + TQFontMetrics fm( font() ); + int fw = style().pixelMetric( TQStyle::PM_DefaultFrameWidth, this ); + int h = fm.lineSpacing() + 2; + int w = 2 + fm.width( '9' ) * 6 + fm.width( d->ed->separator() ) * 2 + + d->controls->upRect().width() + fw * 4; + if ( d->display & AMPM ) { + if ( lAM ) + w += fm.width( *lAM ) + 4; + else + w += fm.width( TQString::fromLatin1( "AM" ) ) + 4; + } + + return TQSize( w, TQMAX(h + fw * 2,20) ).expandedTo( TQApplication::globalStrut() ); +} + +/*! \reimp +*/ +TQSize TQTimeEdit::minimumSizeHint() const +{ + return sizeHint(); +} + +/*! + \internal + Enables/disables the push buttons according to the min/max time + for this widget. +*/ + +// ### Remove in 4.0? + +void TQTimeEdit::updateButtons() +{ + if ( !isEnabled() ) + return; + + bool upEnabled = time() < maxValue(); + bool downEnabled = time() > minValue(); + + d->controls->setUpEnabled( upEnabled ); + d->controls->setDownEnabled( downEnabled ); +} + + +class TQDateTimeEditPrivate +{ +public: + bool adv; +}; + +/*! + \class TQDateTimeEdit qdatetimeedit.h + \brief The TQDateTimeEdit class combines a TQDateEdit and TQTimeEdit + widget into a single widget for editing datetimes. + + \ingroup advanced + \ingroup time + \mainclass + + TQDateTimeEdit consists of a TQDateEdit and TQTimeEdit widget placed + side by side and offers the functionality of both. The user can + edit the date and time by using the keyboard or the arrow keys to + increase/decrease date or time values. The Tab key can be used to + move from section to section within the TQDateTimeEdit widget, and + the user can be moved automatically when they complete a section + using setAutoAdvance(). The datetime can be set with + setDateTime(). + + The date format is read from the system's locale settings. It is + set to year, month, day order if that is not possible. See + TQDateEdit::setOrder() to change this. Times appear in the order + hours, minutes, seconds using the 24 hour clock. + + It is recommended that the TQDateTimeEdit is initialised with a + datetime, e.g. + \code + TQDateTimeEdit *dateTimeEdit = new TQDateTimeEdit( TQDateTime::currentDateTime(), this ); + dateTimeEdit->dateEdit()->setRange( TQDateTime::currentDate(), + TQDateTime::currentDate().addDays( 7 ) ); + \endcode + Here we've created a new TQDateTimeEdit set to the current date and + time, and set the date to have a minimum date of now and a maximum + date of a week from now. + + Terminology: A TQDateEdit widget consists of three 'sections', one + each for the year, month and day. Similarly a TQTimeEdit consists + of three sections, one each for the hour, minute and second. The + character that separates each date section is specified with + setDateSeparator(); similarly setTimeSeparator() is used for the + time sections. + + \img datetimewidgets.png Date Time Widgets + + \sa TQDateEdit TQTimeEdit +*/ + +/*! + Constructs an empty datetime edit with parent \a parent and called + \a name. +*/ +TQDateTimeEdit::TQDateTimeEdit( TQWidget * parent, const char * name ) + : TQWidget( parent, name ) +{ + init(); +} + + +/*! + \overload + + Constructs a datetime edit with the initial value \a datetime, + parent \a parent and called \a name. +*/ +TQDateTimeEdit::TQDateTimeEdit( const TQDateTime& datetime, + TQWidget * parent, const char * name ) + : TQWidget( parent, name ) +{ + init(); + setDateTime( datetime ); +} + + + +/*! + Destroys the object and frees any allocated resources. +*/ + +TQDateTimeEdit::~TQDateTimeEdit() +{ + delete d; +} + + +/*! + \reimp + + Intercepts and handles resize events which have special meaning + for the TQDateTimeEdit. +*/ + +void TQDateTimeEdit::resizeEvent( TQResizeEvent * ) +{ + int dw = de->sizeHint().width(); + int tw = te->sizeHint().width(); + int w = width(); + int h = height(); + int extra = w - ( dw + tw ); + + if ( tw + extra < 0 ) { + dw = w; + } else { + dw += 9 * extra / 16; + } + tw = w - dw; + + de->setGeometry( 0, 0, dw, h ); + te->setGeometry( dw, 0, tw, h ); +} + +/*! \reimp +*/ + +TQSize TQDateTimeEdit::minimumSizeHint() const +{ + TQSize dsh = de->minimumSizeHint(); + TQSize tsh = te->minimumSizeHint(); + return TQSize( dsh.width() + tsh.width(), + TQMAX( dsh.height(), tsh.height() ) ); +} + +/*! \internal + */ + +void TQDateTimeEdit::init() +{ + d = new TQDateTimeEditPrivate(); + de = new TQDateEdit( this, "qt_datetime_dateedit" ); + te = new TQTimeEdit( this, "qt_datetime_timeedit" ); + d->adv = FALSE; + connect( de, SIGNAL( valueChanged(const TQDate&) ), + this, SLOT( newValue(const TQDate&) ) ); + connect( te, SIGNAL( valueChanged(const TQTime&) ), + this, SLOT( newValue(const TQTime&) ) ); + setFocusProxy( de ); + setSizePolicy( TQSizePolicy::Minimum, TQSizePolicy::Fixed ); +} + +/*! \reimp + */ + +TQSize TQDateTimeEdit::sizeHint() const +{ + constPolish(); + TQSize dsh = de->sizeHint(); + TQSize tsh = te->sizeHint(); + return TQSize( dsh.width() + tsh.width(), + TQMAX( dsh.height(), tsh.height() ) ); +} + +/*! + \property TQDateTimeEdit::dateTime + \brief the editor's datetime value + + The datetime edit's datetime which may be an invalid datetime. +*/ + +void TQDateTimeEdit::setDateTime( const TQDateTime & dt ) +{ + if ( dt.isValid() ) { + de->setDate( dt.date() ); + te->setTime( dt.time() ); + emit valueChanged( dt ); + } +} + +TQDateTime TQDateTimeEdit::dateTime() const +{ + return TQDateTime( de->date(), te->time() ); +} + +/*! + \fn void TQDateTimeEdit::valueChanged( const TQDateTime& datetime ) + + This signal is emitted every time the date or time changes. The \a + datetime argument is the new datetime. +*/ + + +/*! \internal + + Re-emits the value \a d. + */ + +void TQDateTimeEdit::newValue( const TQDate& ) +{ + TQDateTime dt = dateTime(); + emit valueChanged( dt ); +} + +/*! \internal + \overload + Re-emits the value \a t. + */ + +void TQDateTimeEdit::newValue( const TQTime& ) +{ + TQDateTime dt = dateTime(); + emit valueChanged( dt ); +} + + +/*! + Sets the auto advance property of the editor to \a advance. If set + to TRUE, the editor will automatically advance focus to the next + date or time section if the user has completed a section. +*/ + +void TQDateTimeEdit::setAutoAdvance( bool advance ) +{ + de->setAutoAdvance( advance ); + te->setAutoAdvance( advance ); +} + +/*! + Returns TRUE if auto-advance is enabled, otherwise returns FALSE. + + \sa setAutoAdvance() +*/ + +bool TQDateTimeEdit::autoAdvance() const +{ + return de->autoAdvance(); +} + +/*! + \fn TQDateEdit* TQDateTimeEdit::dateEdit() + + Returns the internal widget used for editing the date part of the + datetime. +*/ + +/*! + \fn TQTimeEdit* TQDateTimeEdit::timeEdit() + + Returns the internal widget used for editing the time part of the + datetime. +*/ + +#include "qdatetimeedit.moc" + +#endif |