diff options
Diffstat (limited to 'src/widgets/tqlineedit.cpp')
-rw-r--r-- | src/widgets/tqlineedit.cpp | 2922 |
1 files changed, 2922 insertions, 0 deletions
diff --git a/src/widgets/tqlineedit.cpp b/src/widgets/tqlineedit.cpp new file mode 100644 index 000000000..558069752 --- /dev/null +++ b/src/widgets/tqlineedit.cpp @@ -0,0 +1,2922 @@ +/********************************************************************** +** +** Implementation of TQLineEdit widget class +** +** Created : 941011 +** +** Copyright (C) 1992-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 requirements 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 "tqlineedit.h" +#ifndef TQT_NO_LINEEDIT + +// Keep this position to avoid patch rejection +#ifndef TQT_NO_IM +#include "ntqinputcontext.h" +#endif + +#include "tqpainter.h" +#include "ntqdrawutil.h" +#include "tqfontmetrics.h" +#include "tqpixmap.h" +#include "tqclipboard.h" +#include "ntqapplication.h" +#include "ntqvalidator.h" +#include "tqdragobject.h" +#include "tqtimer.h" +#include "tqpopupmenu.h" +#include "tqstringlist.h" +#include "ntqguardedptr.h" +#include "tqstyle.h" +#include "tqwhatsthis.h" +#include "../kernel/qinternal_p.h" +#include "private/tqtextlayout_p.h" +#include "tqvaluevector.h" +#if defined(Q_OS_LINUX) +#include <sys/mman.h> +#endif +#if defined(QT_ACCESSIBILITY_SUPPORT) +#include "ntqaccessible.h" +#endif + +#ifndef TQT_NO_ACCEL +#include "ntqkeysequence.h" +#define ACCEL_KEY(k) "\t" + TQString(TQKeySequence( TQt::CTRL | TQt::Key_ ## k )) +#else +#define ACCEL_KEY(k) "\t" + TQString("Ctrl+" #k) +#endif + +#if defined(Q_OS_LINUX) +#define LINUX_MEMLOCK_LIMIT_BYTES 16384 +#define LINUX_MEMLOCK_LIMIT_CHARACTERS LINUX_MEMLOCK_LIMIT_BYTES/sizeof(TQChar) +#endif + +#define innerMargin 1 + +struct TQLineEditPrivate : public TQt +{ + TQLineEditPrivate( TQLineEdit *q ) + : q(q), cursor(0), cursorTimer(0), tripleClickTimer(0), frame(1), + cursorVisible(0), separator(0), readOnly(0), modified(0), + direction(TQChar::DirON), dragEnabled(1), alignment(0), + echoMode(0), textDirty(0), selDirty(0), validInput(1), + ascent(0), maxLength(32767), menuId(0), + hscroll(0), validator(0), maskData(0), + undoState(0), selstart(0), selend(0), + imstart(0), imend(0), imselstart(0), imselend(0) +#ifndef TQT_NO_DRAGANDDROP + ,dndTimer(0) +#endif + {} + void init( const TQString&); + + TQLineEdit *q; + TQString text; + int cursor; + int cursorTimer; + TQPoint tripleClick; + int tripleClickTimer; + uint frame : 1; + uint cursorVisible : 1; + uint separator : 1; + uint readOnly : 1; + uint modified : 1; + uint direction : 5; + uint dragEnabled : 1; + uint alignment : 3; + uint echoMode : 2; + uint textDirty : 1; + uint selDirty : 1; + uint validInput : 1; + int ascent; + int maxLength; + int menuId; + int hscroll; + TQChar passwordChar; // obsolete + + void finishChange( int validateFromState = -1, bool setModified = TRUE ); + + const TQValidator* validator; + struct MaskInputData { + enum Casemode { NoCaseMode, Upper, Lower }; + TQChar maskChar; // either the separator char or the inputmask + bool separator; + Casemode caseMode; + }; + TQString inputMask; + TQChar blank; + MaskInputData *maskData; + inline int nextMaskBlank( int pos ) { + int c = findInMask( pos, TRUE, FALSE ); + separator |= ( c != pos ); + return ( c != -1 ? c : maxLength ); + } + inline int prevMaskBlank( int pos ) { + int c = findInMask( pos, FALSE, FALSE ); + separator |= ( c != pos ); + return ( c != -1 ? c : 0 ); + } + + void setCursorVisible( bool visible ); + + + // undo/redo handling + enum CommandType { Separator, Insert, Remove, Delete, RemoveSelection, DeleteSelection }; + struct Command { + inline Command(){} + inline Command( CommandType type, int pos, TQChar c ) + :type(type),c(c),pos(pos){} + uint type : 4; + TQChar c; + int pos; + }; + int undoState; + TQValueVector<Command> history; + void addCommand( const Command& cmd ); + void insert( const TQString& s ); + void del( bool wasBackspace = FALSE ); + void remove( int pos ); + + inline void separate() { separator = TRUE; } + inline void undo( int until = -1 ) { + if ( !isUndoAvailable() ) + return; + deselect(); + while ( undoState && undoState > until ) { + Command& cmd = history[--undoState]; + switch ( cmd.type ) { + case Insert: + text.remove( cmd.pos, 1); + cursor = cmd.pos; + break; + case Remove: + case RemoveSelection: + text.insert( cmd.pos, cmd.c ); + cursor = cmd.pos + 1; + break; + case Delete: + case DeleteSelection: + text.insert( cmd.pos, cmd.c ); + cursor = cmd.pos; + break; + case Separator: + continue; + } + if ( until < 0 && undoState ) { + Command& next = history[undoState-1]; + if ( next.type != cmd.type && next.type < RemoveSelection + && !( cmd.type >= RemoveSelection && next.type != Separator ) ) + break; + } + } + modified = ( undoState != 0 ); + textDirty = TRUE; + } + inline void redo() { + if ( !isRedoAvailable() ) + return; + deselect(); + while ( undoState < (int)history.size() ) { + Command& cmd = history[undoState++]; + switch ( cmd.type ) { + case Insert: + text.insert( cmd.pos, cmd.c ); + cursor = cmd.pos + 1; + break; + case Remove: + case Delete: + case RemoveSelection: + case DeleteSelection: + text.remove( cmd.pos, 1 ); + cursor = cmd.pos; + break; + case Separator: + continue; + } + if ( undoState < (int)history.size() ) { + Command& next = history[undoState]; + if ( next.type != cmd.type && cmd.type < RemoveSelection + && !( next.type >= RemoveSelection && cmd.type != Separator ) ) + break; + } + } + textDirty = TRUE; + } + inline bool isUndoAvailable() const { return !readOnly && undoState; } + inline bool isRedoAvailable() const { return !readOnly && undoState < (int)history.size(); } + + // bidi + inline bool isRightToLeft() const { return direction==TQChar::DirON?text.isRightToLeft():(direction==TQChar::DirR); } + + // selection + int selstart, selend; + inline bool allSelected() const { return !text.isEmpty() && selstart == 0 && selend == (int)text.length(); } + inline bool hasSelectedText() const { return !text.isEmpty() && selend > selstart; } + inline void deselect() { selDirty |= (selend > selstart); selstart = selend = 0; } + void removeSelectedText(); +#ifndef TQT_NO_CLIPBOARD + void copy( bool clipboard = TRUE ) const; +#endif + inline bool inSelection( int x ) const + { if ( selstart >= selend ) return FALSE; + int pos = xToPos( x, TQTextItem::OnCharacters ); return pos >= selstart && pos < selend; } + + // masking + void parseInputMask( const TQString &maskFields ); + bool isValidInput( TQChar key, TQChar mask ) const; + TQString maskString( uint pos, const TQString &str, bool clear = FALSE ) const; + TQString clearString( uint pos, uint len ) const; + TQString stripString( const TQString &str ) const; + int findInMask( int pos, bool forward, bool findSeparator, TQChar searchChar = TQChar() ) const; + + // input methods + int imstart, imend, imselstart, imselend; + bool composeMode() const { return preeditLength(); } + bool hasIMSelection() const { return imSelectionLength(); } + int preeditLength() const { return ( imend - imstart ); } + int imSelectionLength() const { return ( imselend - imselstart ); } + + // complex text layout + TQTextLayout textLayout; + void updateTextLayout(); + void moveCursor( int pos, bool mark = FALSE ); + void setText( const TQString& txt ); + int xToPosInternal( int x, TQTextItem::CursorPosition ) const; + int xToPos( int x, TQTextItem::CursorPosition = TQTextItem::BetweenCharacters ) const; + inline int visualAlignment() const { return alignment ? alignment : int( isRightToLeft() ? AlignRight : AlignLeft ); } + TQRect cursorRect() const; + void updateMicroFocusHint(); + +#ifndef TQT_NO_DRAGANDDROP + // drag and drop + TQPoint dndPos; + int dndTimer; + bool drag(); +#endif +}; + + +/*! + \class TQLineEdit + \brief The TQLineEdit widget is a one-line text editor. + + \ingroup basic + \mainclass + + A line edit allows the user to enter and edit a single line of + plain text with a useful collection of editing functions, + including undo and redo, cut and paste, and drag and drop. + + By changing the echoMode() of a line edit, it can also be used as + a "write-only" field, for inputs such as passwords. + + The length of the text can be constrained to maxLength(). The text + can be arbitrarily constrained using a validator() or an + inputMask(), or both. + + A related class is TQTextEdit which allows multi-line, rich-text + editing. + + You can change the text with setText() or insert(). The text is + retrieved with text(); the displayed text (which may be different, + see \l{EchoMode}) is retrieved with displayText(). Text can be + selected with setSelection() or selectAll(), and the selection can + be cut(), copy()ied and paste()d. The text can be aligned with + setAlignment(). + + When the text changes the textChanged() signal is emitted; when + the Return or Enter key is pressed the returnPressed() signal is + emitted. Note that if there is a validator set on the line edit, + the returnPressed() signal will only be emitted if the validator + returns \c Acceptable. + + By default, TQLineEdits have a frame as specified by the Windows + and Motif style guides; you can turn it off by calling + setFrame(FALSE). + + The default key bindings are described below. The line edit also + provides a context menu (usually invoked by a right mouse click) + that presents some of these editing options. + \target desc + \table + \header \i Keypress \i Action + \row \i Left Arrow \i Moves the cursor one character to the left. + \row \i Shift+Left Arrow \i Moves and selects text one character to the left. + \row \i Right Arrow \i Moves the cursor one character to the right. + \row \i Shift+Right Arrow \i Moves and selects text one character to the right. + \row \i Home \i Moves the cursor to the beginning of the line. + \row \i End \i Moves the cursor to the end of the line. + \row \i Backspace \i Deletes the character to the left of the cursor. + \row \i Ctrl+Backspace \i Deletes the word to the left of the cursor. + \row \i Delete \i Deletes the character to the right of the cursor. + \row \i Ctrl+Delete \i Deletes the word to the right of the cursor. + \row \i Ctrl+A \i Moves the cursor to the beginning of the line. + \row \i Ctrl+B \i Moves the cursor one character to the left. + \row \i Ctrl+C \i Copies the selected text to the clipboard. + (Windows also supports Ctrl+Insert for this operation.) + \row \i Ctrl+D \i Deletes the character to the right of the cursor. + \row \i Ctrl+E \i Moves the cursor to the end of the line. + \row \i Ctrl+F \i Moves the cursor one character to the right. + \row \i Ctrl+H \i Deletes the character to the left of the cursor. + \row \i Ctrl+K \i Deletes to the end of the line. + \row \i Ctrl+V \i Pastes the clipboard text into line edit. + (Windows also supports Shift+Insert for this operation.) + \row \i Ctrl+X \i Deletes the selected text and copies it to the clipboard. + (Windows also supports Shift+Delete for this operation.) + \row \i Ctrl+Z \i Undoes the last operation. + \row \i Ctrl+Y \i Redoes the last undone operation. + \endtable + + Any other key sequence that represents a valid character, will + cause the character to be inserted into the line edit. + + <img src=tqlined-m.png> <img src=tqlined-w.png> + + \sa TQTextEdit TQLabel TQComboBox + \link guibooks.html#fowler GUI Design Handbook: Field, Entry\endlink +*/ + + +/*! + \fn void TQLineEdit::textChanged( const TQString& ) + + This signal is emitted whenever the text changes. The argument is + the new text. +*/ + +/*! + \fn void TQLineEdit::selectionChanged() + + This signal is emitted whenever the selection changes. + + \sa hasSelectedText(), selectedText() +*/ + +/*! + \fn void TQLineEdit::lostFocus() + + This signal is emitted when the line edit has lost focus. + + \sa hasFocus(), TQWidget::focusInEvent(), TQWidget::focusOutEvent() +*/ + + + +/*! + Constructs a line edit with no text. + + The maximum text length is set to 32767 characters. + + The \a parent and \a name arguments are sent to the TQWidget constructor. + + \sa setText(), setMaxLength() +*/ + +TQLineEdit::TQLineEdit( TQWidget* parent, const char* name ) + : TQFrame( parent, name, WNoAutoErase ), d(new TQLineEditPrivate( this )) +{ + d->init( TQString::null ); +} + +/*! + Constructs a line edit containing the text \a contents. + + The cursor position is set to the end of the line and the maximum + text length to 32767 characters. + + The \a parent and \a name arguments are sent to the TQWidget + constructor. + + \sa text(), setMaxLength() +*/ + +TQLineEdit::TQLineEdit( const TQString& contents, TQWidget* parent, const char* name ) + : TQFrame( parent, name, WNoAutoErase ), d(new TQLineEditPrivate( this )) +{ + d->init( contents ); +} + +/*! + Constructs a line edit with an input \a inputMask and the text \a + contents. + + The cursor position is set to the end of the line and the maximum + text length is set to the length of the mask (the number of mask + characters and separators). + + The \a parent and \a name arguments are sent to the TQWidget + constructor. + + \sa setMask() text() +*/ +TQLineEdit::TQLineEdit( const TQString& contents, const TQString &inputMask, TQWidget* parent, const char* name ) + : TQFrame( parent, name, WNoAutoErase ), d(new TQLineEditPrivate( this )) +{ + d->parseInputMask( inputMask ); + if ( d->maskData ) { + TQString ms = d->maskString( 0, contents ); + d->init( ms + d->clearString( ms.length(), d->maxLength - ms.length() ) ); + d->cursor = d->nextMaskBlank( ms.length() ); + } else { + d->init( contents ); + } +} + +/*! + Destroys the line edit. +*/ + +TQLineEdit::~TQLineEdit() +{ + if ((d->echoMode == NoEcho) || (d->echoMode == Password) || (d->echoMode == PasswordThreeStars)) { + d->text.fill(TQChar(0)); +#if defined(Q_OS_LINUX) + munlock(d->text.d->unicode, LINUX_MEMLOCK_LIMIT_BYTES); +#endif + } + delete [] d->maskData; + delete d; +} + + +/*! + \property TQLineEdit::text + \brief the line edit's text + + Note that setting this property clears the selection, clears the + undo/redo history, moves the cursor to the end of the line and + resets the \c modified property to FALSE. The text is not + validated when inserted with setText(). + + The text is truncated to maxLength() length. + + \sa insert() +*/ +TQString TQLineEdit::text() const +{ + TQString res = d->text; + if ( d->maskData ) + res = d->stripString( d->text ); + return ( res.isNull() ? TQString::fromLatin1("") : res ); +} + +void TQLineEdit::setText( const TQString& text) +{ + resetInputContext(); + d->setText( text ); + d->modified = FALSE; + d->finishChange( -1, FALSE ); +} + + +/*! + \property TQLineEdit::displayText + \brief the displayed text + + If \c EchoMode is \c Normal this returns the same as text(); if + \c EchoMode is \c Password it returns a string of asterisks + text().length() characters long, e.g. "******"; if \c EchoMode is + \c NoEcho returns an empty string, "". + + \sa setEchoMode() text() EchoMode +*/ + +TQString TQLineEdit::displayText() const +{ + if ( d->echoMode == NoEcho ) { + return TQString::fromLatin1(""); + } + TQString res = d->text; + if ( d->echoMode == Password ) { + res.fill( passwordChar() ); + } + else if ( d->echoMode == PasswordThreeStars ) { + res.fill( passwordChar(), res.length()*3 ); + } + return ( res.isNull() ? TQString::fromLatin1("") : res ); +} + + +/*! + \property TQLineEdit::maxLength + \brief the maximum permitted length of the text + + If the text is too long, it is truncated at the limit. + + If truncation occurs any selected text will be unselected, the + cursor position is set to 0 and the first part of the string is + shown. + + If the line edit has an input mask, the mask defines the maximum + string length. + + \sa inputMask +*/ + +int TQLineEdit::maxLength() const +{ + return d->maxLength; +} + +void TQLineEdit::setMaxLength( int maxLength ) +{ + if ( d->maskData ) + return; + d->maxLength = maxLength; + setText( d->text ); +} + + + +/*! + \property TQLineEdit::frame + \brief whether the line edit draws itself with a frame + + If enabled (the default) the line edit draws itself inside a + two-pixel frame, otherwise the line edit draws itself without any + frame. +*/ +bool TQLineEdit::frame() const +{ + return frameShape() != NoFrame; +} + + +void TQLineEdit::setFrame( bool enable ) +{ + setFrameStyle( enable ? ( LineEditPanel | Sunken ) : NoFrame ); +} + + +/*! + \enum TQLineEdit::EchoMode + + This enum type describes how a line edit should display its + contents. + + \value Normal Display characters as they are entered. This is the + default. + \value NoEcho Do not display anything. This may be appropriate + for passwords where even the length of the + password should be kept secret. + \value Password Display asterisks instead of the characters + actually entered. + + \sa setEchoMode() echoMode() +*/ + + +/*! + \property TQLineEdit::echoMode + \brief the line edit's echo mode + + The initial setting is \c Normal, but TQLineEdit also supports \c + NoEcho and \c Password modes. + + The widget's display and the ability to copy or drag the text is + affected by this setting. + + \sa EchoMode displayText() +*/ + +TQLineEdit::EchoMode TQLineEdit::echoMode() const +{ + return (EchoMode) d->echoMode; +} + +void TQLineEdit::setEchoMode( EchoMode mode ) +{ + if (mode == (EchoMode)d->echoMode) { + return; + } +#if defined(Q_OS_LINUX) + if (((mode == NoEcho) || (mode == Password) || (mode == PasswordThreeStars)) && ((EchoMode)d->echoMode == Normal)) { + if ((uint)d->maxLength > (LINUX_MEMLOCK_LIMIT_CHARACTERS-1)) { + d->maxLength = LINUX_MEMLOCK_LIMIT_CHARACTERS-1; + } + d->text.reserve(LINUX_MEMLOCK_LIMIT_CHARACTERS); + mlock(d->text.d->unicode, LINUX_MEMLOCK_LIMIT_BYTES); + d->text.setSecurityUnPaged(true); + } + else { + d->text.setSecurityUnPaged(false); + munlock(d->text.d->unicode, LINUX_MEMLOCK_LIMIT_BYTES); + } +#endif + d->echoMode = mode; + d->updateTextLayout(); + setInputMethodEnabled( ( mode == Normal ) || ( mode == Password) ); + update(); +} + + + +/*! + Returns a pointer to the current input validator, or 0 if no + validator has been set. + + \sa setValidator() +*/ + +const TQValidator * TQLineEdit::validator() const +{ + return d->validator; +} + +/*! + Sets this line edit to only accept input that the validator, \a v, + will accept. This allows you to place any arbitrary constraints on + the text which may be entered. + + If \a v == 0, setValidator() removes the current input validator. + The initial setting is to have no input validator (i.e. any input + is accepted up to maxLength()). + + \sa validator() TQIntValidator TQDoubleValidator TQRegExpValidator +*/ + +void TQLineEdit::setValidator( const TQValidator *v ) +{ + if ( d->validator ) + disconnect( (TQObject*)d->validator, TQ_SIGNAL( destroyed() ), + this, TQ_SLOT( clearValidator() ) ); + d->validator = v; + if ( d->validator ) + connect( (TQObject*)d->validator, TQ_SIGNAL( destroyed() ), + this, TQ_SLOT( clearValidator() ) ); +} + + + +/*! + Returns a recommended size for the widget. + + The width returned, in pixels, is usually enough for about 15 to + 20 characters. +*/ + +TQSize TQLineEdit::sizeHint() const +{ + constPolish(); + TQFontMetrics fm( font() ); + int h = TQMAX(fm.lineSpacing(), 14) + 2*innerMargin; + int w = fm.width( 'x' ) * 17; // "some" + int m = frameWidth() * 2; + return (style().sizeFromContents(TQStyle::CT_LineEdit, this, + TQSize( w + m, h + m ). + expandedTo(TQApplication::globalStrut()))); +} + + +/*! + Returns a minimum size for the line edit. + + The width returned is enough for at least one character. +*/ + +TQSize TQLineEdit::minimumSizeHint() const +{ + constPolish(); + TQFontMetrics fm = fontMetrics(); + int h = fm.height() + TQMAX( 2*innerMargin, fm.leading() ); + int w = fm.maxWidth(); + int m = frameWidth() * 2; + return TQSize( w + m, h + m ); +} + + +/*! + \property TQLineEdit::cursorPosition + \brief the current cursor position for this line edit + + Setting the cursor position causes a repaint when appropriate. +*/ + +int TQLineEdit::cursorPosition() const +{ + return d->cursor; +} + + +void TQLineEdit::setCursorPosition( int pos ) +{ + if (pos < 0) + pos = 0; + + if ( pos <= (int) d->text.length() ) + d->moveCursor( pos ); +} + + +/*! \obsolete Use setText(), setCursorPosition() and setSelection() instead. +*/ + +bool TQLineEdit::validateAndSet( const TQString &newText, int newPos, + int newMarkAnchor, int newMarkDrag ) +{ + int priorState = d->undoState; + d->selstart = 0; + d->selend = d->text.length(); + d->removeSelectedText(); + d->insert( newText ); + d->finishChange( priorState ); + if ( d->undoState > priorState ) { + d->cursor = newPos; + d->selstart = TQMIN( newMarkAnchor, newMarkDrag ); + d->selend = TQMAX( newMarkAnchor, newMarkDrag ); + d->updateMicroFocusHint(); + update(); + return TRUE; + } + return FALSE; +} + + +/*! + \property TQLineEdit::alignment + \brief the alignment of the line edit + + Possible Values are \c TQt::AlignAuto, \c TQt::AlignLeft, \c + TQt::AlignRight and \c TQt::AlignHCenter. + + Attempting to set the alignment to an illegal flag combination + does nothing. + + \sa TQt::AlignmentFlags +*/ + +int TQLineEdit::alignment() const +{ + return d->alignment; +} + +void TQLineEdit::setAlignment( int flag ) +{ + d->alignment = flag & 0x7; + update(); +} + + +/*! + Moves the cursor forward \a steps characters. If \a mark is TRUE + each character moved over is added to the selection; if \a mark is + FALSE the selection is cleared. + + \sa cursorBackward() +*/ + +void TQLineEdit::cursorForward( bool mark, int steps ) +{ + int cursor = d->cursor; + if ( steps > 0 ) { + while( steps-- ) + cursor = d->textLayout.nextCursorPosition( cursor ); + } else if ( steps < 0 ) { + while ( steps++ ) + cursor = d->textLayout.previousCursorPosition( cursor ); + } + d->moveCursor( cursor, mark ); +} + + +/*! + Moves the cursor back \a steps characters. If \a mark is TRUE each + character moved over is added to the selection; if \a mark is + FALSE the selection is cleared. + + \sa cursorForward() +*/ +void TQLineEdit::cursorBackward( bool mark, int steps ) +{ + cursorForward( mark, -steps ); +} + +/*! + Moves the cursor one word forward. If \a mark is TRUE, the word is + also selected. + + \sa cursorWordBackward() +*/ +void TQLineEdit::cursorWordForward( bool mark ) +{ + d->moveCursor( d->textLayout.nextCursorPosition(d->cursor, TQTextLayout::SkipWords), mark ); +} + +/*! + Moves the cursor one word backward. If \a mark is TRUE, the word + is also selected. + + \sa cursorWordForward() +*/ + +void TQLineEdit::cursorWordBackward( bool mark ) +{ + d->moveCursor( d->textLayout.previousCursorPosition(d->cursor, TQTextLayout::SkipWords), mark ); +} + + +/*! + If no text is selected, deletes the character to the left of the + text cursor and moves the cursor one position to the left. If any + text is selected, the cursor is moved to the beginning of the + selected text and the selected text is deleted. + + \sa del() +*/ +void TQLineEdit::backspace() +{ + int priorState = d->undoState; + if ( d->hasSelectedText() ) { + d->removeSelectedText(); + } else if ( d->cursor ) { + --d->cursor; + if ( d->maskData ) + d->cursor = d->prevMaskBlank( d->cursor ); + d->del( TRUE ); + } + d->finishChange( priorState ); +} + +/*! + If no text is selected, deletes the character to the right of the + text cursor. If any text is selected, the cursor is moved to the + beginning of the selected text and the selected text is deleted. + + \sa backspace() +*/ + +void TQLineEdit::del() +{ + int priorState = d->undoState; + if ( d->hasSelectedText() ) { + d->removeSelectedText(); + } else { + int n = d->textLayout.nextCursorPosition( d->cursor ) - d->cursor; + while ( n-- ) + d->del(); + } + d->finishChange( priorState ); +} + +/*! + Moves the text cursor to the beginning of the line unless it is + already there. If \a mark is TRUE, text is selected towards the + first position; otherwise, any selected text is unselected if the + cursor is moved. + + \sa end() +*/ + +void TQLineEdit::home( bool mark ) +{ + d->moveCursor( 0, mark ); +} + +/*! + Moves the text cursor to the end of the line unless it is already + there. If \a mark is TRUE, text is selected towards the last + position; otherwise, any selected text is unselected if the cursor + is moved. + + \sa home() +*/ + +void TQLineEdit::end( bool mark ) +{ + d->moveCursor( d->text.length(), mark ); +} + + +/*! + \property TQLineEdit::modified + \brief whether the line edit's contents has been modified by the user + + The modified flag is never read by TQLineEdit; it has a default value + of FALSE and is changed to TRUE whenever the user changes the line + edit's contents. + + This is useful for things that need to provide a default value but + do not start out knowing what the default should be (perhaps it + depends on other fields on the form). Start the line edit without + the best default, and when the default is known, if modified() + returns FALSE (the user hasn't entered any text), insert the + default value. + + Calling clearModified() or setText() resets the modified flag to + FALSE. +*/ + +bool TQLineEdit::isModified() const +{ + return d->modified; +} + +/*! + Resets the modified flag to FALSE. + + \sa isModified() +*/ +void TQLineEdit::clearModified() +{ + d->modified = FALSE; + d->history.clear(); + d->undoState = 0; +} + +/*! + \obsolete + \property TQLineEdit::edited + \brief whether the line edit has been edited. Use modified instead. +*/ +bool TQLineEdit::edited() const { return d->modified; } +void TQLineEdit::setEdited( bool on ) { d->modified = on; } + +/*! + \property TQLineEdit::hasSelectedText + \brief whether there is any text selected + + hasSelectedText() returns TRUE if some or all of the text has been + selected by the user; otherwise returns FALSE. + + \sa selectedText() +*/ + + +bool TQLineEdit::hasSelectedText() const +{ + return d->hasSelectedText(); +} + +/*! + \property TQLineEdit::selectedText + \brief the selected text + + If there is no selected text this property's value is + TQString::null. + + \sa hasSelectedText() +*/ + +TQString TQLineEdit::selectedText() const +{ + if ( d->hasSelectedText() ) + return d->text.mid( d->selstart, d->selend - d->selstart ); + return TQString::null; +} + +/*! + selectionStart() returns the index of the first selected character in the + line edit or -1 if no text is selected. + + \sa selectedText() +*/ + +int TQLineEdit::selectionStart() const +{ + return d->hasSelectedText() ? d->selstart : -1; +} + +/*! \obsolete use selectedText(), selectionStart() */ +bool TQLineEdit::getSelection( int *start, int *end ) +{ + if ( d->hasSelectedText() && start && end ) { + *start = d->selstart; + *end = d->selend; + return TRUE; + } + return FALSE; +} + + +/*! + Selects text from position \a start and for \a length characters. + + Note that this function sets the cursor's position to the end of + the selection regardless of its current position. + + \sa deselect() selectAll() getSelection() cursorForward() cursorBackward() +*/ + +void TQLineEdit::setSelection( int start, int length ) +{ + if ( start < 0 || start > (int)d->text.length() || length < 0 ) { + d->selstart = d->selend = 0; + } else { + d->selstart = start; + d->selend = TQMIN( start + length, (int)d->text.length() ); + d->cursor = d->selend; + } + update(); +} + + +/*! + \property TQLineEdit::undoAvailable + \brief whether undo is available +*/ + +bool TQLineEdit::isUndoAvailable() const +{ + return d->isUndoAvailable(); +} + +/*! + \property TQLineEdit::redoAvailable + \brief whether redo is available +*/ + +bool TQLineEdit::isRedoAvailable() const +{ + return d->isRedoAvailable(); +} + +/*! + \property TQLineEdit::dragEnabled + \brief whether the lineedit starts a drag if the user presses and + moves the mouse on some selected text +*/ + +bool TQLineEdit::dragEnabled() const +{ + return d->dragEnabled; +} + +void TQLineEdit::setDragEnabled( bool b ) +{ + d->dragEnabled = b; +} + +/*! + \property TQLineEdit::acceptableInput + \brief whether the input satisfies the inputMask and the + validator. + + \sa setInputMask(), setValidator() +*/ +bool TQLineEdit::hasAcceptableInput() const +{ +#ifndef TQT_NO_VALIDATOR + TQString text = d->text; + int cursor = d->cursor; + if ( d->validator && d->validator->validate( text, cursor ) != TQValidator::Acceptable ) + return FALSE; +#endif + + if ( !d->maskData ) + return TRUE; + + if ( d->text.length() != (uint)d->maxLength ) + return FALSE; + + for ( uint i=0; i < (uint)d->maxLength; i++) { + if ( d->maskData[i].separator ) { + if ( d->text[(int)i] != d->maskData[i].maskChar ) + return FALSE; + } else { + if ( !d->isValidInput( d->text[(int)i], d->maskData[i].maskChar ) ) + return FALSE; + } + } + return TRUE; +} + + +/*! + \property TQLineEdit::inputMask + \brief The validation input mask + + If no mask is set, inputMask() returns TQString::null. + + Sets the TQLineEdit's validation mask. Validators can be used + instead of, or in conjunction with masks; see setValidator(). + + Unset the mask and return to normal TQLineEdit operation by passing + an empty string ("") or just calling setInputMask() with no + arguments. + + The mask format understands these mask characters: + \table + \header \i Character \i Meaning + \row \i \c A \i ASCII alphabetic character required. A-Z, a-z. + \row \i \c a \i ASCII alphabetic character permitted but not required. + \row \i \c N \i ASCII alphanumeric character required. A-Z, a-z, 0-9. + \row \i \c n \i ASCII alphanumeric character permitted but not required. + \row \i \c X \i Any character required. + \row \i \c x \i Any character permitted but not required. + \row \i \c 9 \i ASCII digit required. 0-9. + \row \i \c 0 \i ASCII digit permitted but not required. + \row \i \c D \i ASCII digit required. 1-9. + \row \i \c d \i ASCII digit permitted but not required (1-9). + \row \i \c # \i ASCII digit or plus/minus sign permitted but not required. + \row \i \c > \i All following alphabetic characters are uppercased. + \row \i \c < \i All following alphabetic characters are lowercased. + \row \i \c ! \i Switch off case conversion. + \row \i <tt>\\</tt> \i Use <tt>\\</tt> to escape the special + characters listed above to use them as + separators. + \endtable + + The mask consists of a string of mask characters and separators, + optionally followed by a semi-colon and the character used for + blanks: the blank characters are always removed from the text + after editing. The default blank character is space. + + Examples: + \table + \header \i Mask \i Notes + \row \i \c 000.000.000.000;_ \i IP address; blanks are \c{_}. + \row \i \c 0000-00-00 \i ISO Date; blanks are \c space + \row \i \c >AAAAA-AAAAA-AAAAA-AAAAA-AAAAA;# \i License number; + blanks are \c - and all (alphabetic) characters are converted to + uppercase. + \endtable + + To get range control (e.g. for an IP address) use masks together + with \link setValidator() validators\endlink. + + \sa maxLength +*/ +TQString TQLineEdit::inputMask() const +{ + return ( d->maskData ? d->inputMask + ';' + d->blank : TQString::null ); +} + +void TQLineEdit::setInputMask( const TQString &inputMask ) +{ + d->parseInputMask( inputMask ); + if ( d->maskData ) + d->moveCursor( d->nextMaskBlank( 0 ) ); +} + +/*! + Selects all the text (i.e. highlights it) and moves the cursor to + the end. This is useful when a default value has been inserted + because if the user types before clicking on the widget, the + selected text will be deleted. + + \sa setSelection() deselect() +*/ + +void TQLineEdit::selectAll() +{ + d->selstart = d->selend = d->cursor = 0; + d->moveCursor( d->text.length(), TRUE ); +} + +/*! + Deselects any selected text. + + \sa setSelection() selectAll() +*/ + +void TQLineEdit::deselect() +{ + d->deselect(); + d->finishChange(); +} + + +/*! + This slot is equivalent to setValidator(0). +*/ + +void TQLineEdit::clearValidator() +{ + setValidator( 0 ); +} + +/*! + Deletes any selected text, inserts \a newText, and validates the + result. If it is valid, it sets it as the new contents of the line + edit. +*/ +void TQLineEdit::insert( const TQString &newText ) +{ +// q->resetInputContext(); //#### FIX ME IN QT + int priorState = d->undoState; + d->removeSelectedText(); + d->insert( newText ); + d->finishChange( priorState ); +} + +/*! + Clears the contents of the line edit. +*/ +void TQLineEdit::clear() +{ + int priorState = d->undoState; + resetInputContext(); + d->selstart = 0; + d->selend = d->text.length(); + d->removeSelectedText(); + d->separate(); + d->finishChange( priorState ); +} + +/*! + Undoes the last operation if undo is \link + TQLineEdit::undoAvailable available\endlink. Deselects any current + selection, and updates the selection start to the current cursor + position. +*/ +void TQLineEdit::undo() +{ + resetInputContext(); + d->undo(); + d->finishChange( -1, FALSE ); +} + +/*! + Redoes the last operation if redo is \link + TQLineEdit::redoAvailable available\endlink. +*/ +void TQLineEdit::redo() +{ + resetInputContext(); + d->redo(); + d->finishChange(); +} + + +/*! + \property TQLineEdit::readOnly + \brief whether the line edit is read only. + + In read-only mode, the user can still copy the text to the + clipboard or drag-and-drop the text (if echoMode() is \c Normal), + but cannot edit it. + + TQLineEdit does not show a cursor in read-only mode. + + \sa setEnabled() +*/ + +bool TQLineEdit::isReadOnly() const +{ + return d->readOnly; +} + +void TQLineEdit::setReadOnly( bool enable ) +{ + d->readOnly = enable; +#ifndef TQT_NO_CURSOR + setCursor( enable ? arrowCursor : ibeamCursor ); +#endif + update(); +} + + +#ifndef TQT_NO_CLIPBOARD +/*! + Copies the selected text to the clipboard and deletes it, if there + is any, and if echoMode() is \c Normal. + + If the current validator disallows deleting the selected text, + cut() will copy without deleting. + + \sa copy() paste() setValidator() +*/ + +void TQLineEdit::cut() +{ + if ( hasSelectedText() ) { + copy(); + del(); + } +} + + +/*! + Copies the selected text to the clipboard, if there is any, and if + echoMode() is \c Normal. + + \sa cut() paste() +*/ + +void TQLineEdit::copy() const +{ + d->copy(); +} + +/*! + Inserts the clipboard's text at the cursor position, deleting any + selected text, providing the line edit is not \link + TQLineEdit::readOnly read-only\endlink. + + If the end result would not be acceptable to the current + \link setValidator() validator\endlink, nothing happens. + + \sa copy() cut() +*/ + +void TQLineEdit::paste() +{ + insert( TQApplication::clipboard()->text( TQClipboard::Clipboard ) ); +} + +void TQLineEditPrivate::copy( bool clipboard ) const +{ + TQString t = q->selectedText(); + if ( !t.isEmpty() && echoMode == TQLineEdit::Normal ) { + q->disconnect( TQApplication::clipboard(), TQ_SIGNAL(selectionChanged()), q, 0); + TQApplication::clipboard()->setText( t, clipboard ? TQClipboard::Clipboard : TQClipboard::Selection ); + q->connect( TQApplication::clipboard(), TQ_SIGNAL(selectionChanged()), + q, TQ_SLOT(clipboardChanged()) ); + } +} + +#endif // !TQT_NO_CLIPBOARD + +/*!\reimp +*/ + +void TQLineEdit::resizeEvent( TQResizeEvent *e ) +{ + TQFrame::resizeEvent( e ); +} + +/*! \reimp +*/ +bool TQLineEdit::event( TQEvent * e ) +{ + if ( e->type() == TQEvent::AccelOverride && !d->readOnly ) { + TQKeyEvent* ke = (TQKeyEvent*) e; + if ( ke->state() == NoButton || ke->state() == ShiftButton + || ke->state() == Keypad ) { + if ( ke->key() < Key_Escape ) { + ke->accept(); + } else { + switch ( ke->key() ) { + case Key_Delete: + case Key_Home: + case Key_End: + case Key_Backspace: + case Key_Left: + case Key_Right: + ke->accept(); + default: + break; + } + } + } else if ( ke->state() & ControlButton ) { + switch ( ke->key() ) { +// Those are too frequently used for application functionality +/* case Key_A: + case Key_B: + case Key_D: + case Key_E: + case Key_F: + case Key_H: + case Key_K: +*/ + case Key_C: + case Key_V: + case Key_X: + case Key_Y: + case Key_Z: + case Key_Left: + case Key_Right: +#if defined (TQ_WS_WIN) + case Key_Insert: + case Key_Delete: +#endif + ke->accept(); + default: + break; + } + } + } else if ( e->type() == TQEvent::Timer ) { + // should be timerEvent, is here for binary compatibility + int timerId = ((TQTimerEvent*)e)->timerId(); + if ( timerId == d->cursorTimer ) { + if(!hasSelectedText() || style().styleHint( TQStyle::SH_BlinkCursorWhenTextSelected )) + d->setCursorVisible( !d->cursorVisible ); +#ifndef TQT_NO_DRAGANDDROP + } else if ( timerId == d->dndTimer ) { + if( !d->drag() ) + return TRUE; +#endif + } else if ( timerId == d->tripleClickTimer ) { + killTimer( d->tripleClickTimer ); + d->tripleClickTimer = 0; + } + } + return TQWidget::event( e ); +} + +/*! \reimp +*/ +void TQLineEdit::mousePressEvent( TQMouseEvent* e ) +{ + if ( sendMouseEventToInputContext( e ) ) + return; + if ( e->button() == RightButton ) + return; + if ( d->tripleClickTimer && ( e->pos() - d->tripleClick ).manhattanLength() < + TQApplication::startDragDistance() ) { + selectAll(); + return; + } + bool mark = e->state() & ShiftButton; + int cursor = d->xToPos( e->pos().x() ); +#ifndef TQT_NO_DRAGANDDROP + if ( !mark && d->dragEnabled && d->echoMode == Normal && + e->button() == LeftButton && d->inSelection( e->pos().x() ) ) { + d->cursor = cursor; + d->updateMicroFocusHint(); + update(); + d->dndPos = e->pos(); + if ( !d->dndTimer ) + d->dndTimer = startTimer( TQApplication::startDragTime() ); + } else +#endif + { + d->moveCursor( cursor, mark ); + } +} + +/*! \reimp +*/ +void TQLineEdit::mouseMoveEvent( TQMouseEvent * e ) +{ + if ( sendMouseEventToInputContext( e ) ) + return; +#ifndef TQT_NO_CURSOR + if ( ( e->state() & MouseButtonMask ) == 0 ) { + if ( !d->readOnly && d->dragEnabled +#ifndef TQT_NO_WHATSTHIS + && !TQWhatsThis::inWhatsThisMode() +#endif + ) + setCursor( ( d->inSelection( e->pos().x() ) ? arrowCursor : ibeamCursor ) ); + } +#endif + + if ( e->state() & LeftButton ) { +#ifndef TQT_NO_DRAGANDDROP + if ( d->dndTimer ) { + if ( ( d->dndPos - e->pos() ).manhattanLength() > TQApplication::startDragDistance() ) + d->drag(); + } else +#endif + { + d->moveCursor( d->xToPos( e->pos().x() ), TRUE ); + } + } +} + +/*! \reimp +*/ +void TQLineEdit::mouseReleaseEvent( TQMouseEvent* e ) +{ + if ( sendMouseEventToInputContext( e ) ) + return; +#ifndef TQT_NO_DRAGANDDROP + if ( e->button() == LeftButton ) { + if ( d->dndTimer ) { + killTimer( d->dndTimer ); + d->dndTimer = 0; + deselect(); + return; + } + } +#endif +#ifndef TQT_NO_CLIPBOARD + if (TQApplication::clipboard()->supportsSelection() ) { + if ( e->button() == LeftButton ) { + d->copy( FALSE ); + } else if ( !d->readOnly && e->button() == MidButton ) { + d->deselect(); + insert( TQApplication::clipboard()->text( TQClipboard::Selection ) ); + } + } +#endif +} + +/*! \reimp +*/ +void TQLineEdit::mouseDoubleClickEvent( TQMouseEvent* e ) +{ + if ( sendMouseEventToInputContext( e ) ) + return; + if ( e->button() == TQt::LeftButton ) { + deselect(); + d->cursor = d->xToPos( e->pos().x() ); + d->cursor = d->textLayout.previousCursorPosition( d->cursor, TQTextLayout::SkipWords ); + // ## text layout should support end of words. + int end = d->textLayout.nextCursorPosition( d->cursor, TQTextLayout::SkipWords ); + while ( end > d->cursor && d->text[end-1].isSpace() ) + --end; + d->moveCursor( end, TRUE ); + d->tripleClickTimer = startTimer( TQApplication::doubleClickInterval() ); + d->tripleClick = e->pos(); + } +} + +/*! + \fn void TQLineEdit::returnPressed() + + This signal is emitted when the Return or Enter key is pressed. + Note that if there is a validator() or inputMask() set on the line + edit, the returnPressed() signal will only be emitted if the input + follows the inputMask() and the validator() returns \c Acceptable. +*/ + +/*! + Converts key press event \a e into a line edit action. + + If Return or Enter is pressed and the current text is valid (or + can be \link TQValidator::fixup() made valid\endlink by the + validator), the signal returnPressed() is emitted. + + The default key bindings are listed in the \link #desc detailed + description.\endlink +*/ + +void TQLineEdit::keyPressEvent( TQKeyEvent * e ) +{ + d->setCursorVisible( TRUE ); + if ( e->key() == Key_Enter || e->key() == Key_Return ) { + const TQValidator * v = d->validator; + if ( hasAcceptableInput() ) { + emit returnPressed(); + } +#ifndef TQT_NO_VALIDATOR + else if ( v && v->validate( d->text, d->cursor ) != TQValidator::Acceptable ) { + TQString vstr = d->text; + v->fixup( vstr ); + if ( vstr != d->text ) { + setText( vstr ); + if ( hasAcceptableInput() ) + emit returnPressed(); + } + } +#endif + e->ignore(); + return; + } + if ( !d->readOnly ) { + TQString t = e->text(); + if ( !t.isEmpty() && (!e->ascii() || e->ascii()>=32) && + e->key() != Key_Delete && + e->key() != Key_Backspace ) { +#ifdef TQ_WS_X11 + extern bool tqt_hebrew_keyboard_hack; + if ( tqt_hebrew_keyboard_hack ) { + // the X11 keyboard layout is broken and does not reverse + // braces correctly. This is a hack to get halfway correct + // behaviour + if ( d->isRightToLeft() ) { + TQChar *c = (TQChar *)t.unicode(); + int l = t.length(); + while( l-- ) { + if ( c->mirrored() ) + *c = c->mirroredChar(); + c++; + } + } + } +#endif + insert( t ); + return; + } + } + bool unknown = FALSE; + if ( e->state() & ControlButton ) { + switch ( e->key() ) { + case Key_A: +#if defined(TQ_WS_X11) + home( e->state() & ShiftButton ); +#else + selectAll(); +#endif + break; + case Key_B: + cursorForward( e->state() & ShiftButton, -1 ); + break; +#ifndef TQT_NO_CLIPBOARD + case Key_C: + copy(); + break; +#endif + case Key_D: + if ( !d->readOnly ) { + del(); + } + break; + case Key_E: + end( e->state() & ShiftButton ); + break; + case Key_F: + cursorForward( e->state() & ShiftButton, 1 ); + break; + case Key_H: + if ( !d->readOnly ) { + backspace(); + } + break; + case Key_K: + if ( !d->readOnly ) { + int priorState = d->undoState; + d->deselect(); + while ( d->cursor < (int) d->text.length() ) + d->del(); + d->finishChange( priorState ); + } + break; +#if defined(TQ_WS_X11) + case Key_U: + if ( !d->readOnly ) + clear(); + break; +#endif +#ifndef TQT_NO_CLIPBOARD + case Key_V: + if ( !d->readOnly ) + paste(); + break; + case Key_X: + if ( !d->readOnly && d->hasSelectedText() && echoMode() == Normal ) { + copy(); + del(); + } + break; +#if defined (TQ_WS_WIN) + case Key_Insert: + copy(); + break; +#endif +#endif + case Key_Delete: + if ( !d->readOnly ) { + cursorWordForward( TRUE ); + del(); + } + break; + case Key_Backspace: + if ( !d->readOnly ) { + cursorWordBackward( TRUE ); + del(); + } + break; + case Key_Right: + case Key_Left: + if ( d->isRightToLeft() == (e->key() == Key_Right) ) { + if (( echoMode() == Normal ) || ( echoMode() == Password )) + cursorWordBackward( e->state() & ShiftButton ); + else + home( e->state() & ShiftButton ); + } else { + if (( echoMode() == Normal ) || ( echoMode() == Password )) + cursorWordForward( e->state() & ShiftButton ); + else + end( e->state() & ShiftButton ); + } + break; + case Key_Z: + if ( !d->readOnly ) { + if(e->state() & ShiftButton) + redo(); + else + undo(); + } + break; + case Key_Y: + if ( !d->readOnly ) + redo(); + break; + default: + unknown = TRUE; + } + } else { // ### check for *no* modifier + switch ( e->key() ) { + case Key_Shift: + // ### TODO + break; + case Key_Left: + case Key_Right: { + int step = (d->isRightToLeft() == (e->key() == Key_Right)) ? -1 : 1; + cursorForward( e->state() & ShiftButton, step ); + } + break; + case Key_Backspace: + if ( !d->readOnly ) { + backspace(); + } + break; + case Key_Home: +#ifdef TQ_WS_MACX + case Key_Up: +#endif + home( e->state() & ShiftButton ); + break; + case Key_End: +#ifdef TQ_WS_MACX + case Key_Down: +#endif + end( e->state() & ShiftButton ); + break; + case Key_Delete: + if ( !d->readOnly ) { +#if defined (TQ_WS_WIN) + if ( e->state() & ShiftButton ) { + cut(); + break; + } +#endif + del(); + } + break; +#if defined (TQ_WS_WIN) + case Key_Insert: + if ( !d->readOnly && e->state() & ShiftButton ) + paste(); + else + unknown = TRUE; + break; +#endif + case Key_F14: // Undo key on Sun keyboards + if ( !d->readOnly ) + undo(); + break; +#ifndef TQT_NO_CLIPBOARD + case Key_F16: // Copy key on Sun keyboards + copy(); + break; + case Key_F18: // Paste key on Sun keyboards + if ( !d->readOnly ) + paste(); + break; + case Key_F20: // Cut key on Sun keyboards + if ( !d->readOnly && hasSelectedText() && echoMode() == Normal ) { + copy(); + del(); + } + break; +#endif + default: + unknown = TRUE; + } + } + if ( e->key() == Key_Direction_L || e->key() == Key_Direction_R ) { + d->direction = (e->key() == Key_Direction_L) ? TQChar::DirL : TQChar::DirR; + d->updateTextLayout(); + update(); + } + + if ( unknown ) + e->ignore(); +} + + +/*! + This function is not intended as polymorphic usage. Just a shared code + fragment that calls TQWidget::sendMouseEventToInputContext() easily for this + class. + */ +bool TQLineEdit::sendMouseEventToInputContext( TQMouseEvent *e ) +{ +#ifndef TQT_NO_IM + if ( d->composeMode() ) { + int cursor = d->xToPosInternal( e->pos().x(), TQTextItem::OnCharacters ); + int mousePos = cursor - d->imstart; + if ( mousePos >= 0 && mousePos < d->preeditLength() ) { + TQWidget::sendMouseEventToInputContext( mousePos, e->type(), + e->button(), e->state() ); + } else if ( e->type() != TQEvent::MouseMove ) { + // send button events on out of preedit + TQWidget::sendMouseEventToInputContext( -1, e->type(), + e->button(), e->state() ); + } + return TRUE; + } +#endif + return FALSE; +} + + +/*! \reimp + */ +void TQLineEdit::imStartEvent( TQIMEvent *e ) +{ + if ( d->readOnly ) { + e->ignore(); + return; + } + d->removeSelectedText(); + d->updateMicroFocusHint(); + d->imstart = d->imend = d->imselstart = d->imselend = d->cursor; +} + +/*! \reimp + */ +void TQLineEdit::imComposeEvent( TQIMEvent *e ) +{ + if ( d->readOnly ) { + e->ignore(); + return; + } + d->text.replace( d->imstart, d->imend - d->imstart, e->text() ); + d->imend = d->imstart + e->text().length(); + d->imselstart = d->imstart + e->cursorPos(); + d->imselend = d->imselstart + e->selectionLength(); + d->cursor = d->imstart + e->cursorPos(); + d->updateTextLayout(); + d->updateMicroFocusHint(); + update(); +} + +/*! \reimp + */ +void TQLineEdit::imEndEvent( TQIMEvent *e ) +{ + if ( d->readOnly) { + e->ignore(); + return; + } + d->text.remove( d->imstart, d->imend - d->imstart ); + d->cursor = d->imselstart = d->imselend = d->imend = d->imstart; + d->textDirty = TRUE; + insert( e->text() ); +} + +/*!\reimp +*/ + +void TQLineEdit::focusInEvent( TQFocusEvent* ) +{ + if ( TQFocusEvent::reason() == TQFocusEvent::Tab || + TQFocusEvent::reason() == TQFocusEvent::Backtab || + TQFocusEvent::reason() == TQFocusEvent::Shortcut ) + d->maskData ? d->moveCursor( d->nextMaskBlank( 0 ) ) : selectAll(); + if ( !d->cursorTimer ) { + int cft = TQApplication::cursorFlashTime(); + d->cursorTimer = cft ? startTimer( cft/2 ) : -1; + } + if( !hasSelectedText() || style().styleHint( TQStyle::SH_BlinkCursorWhenTextSelected ) ) + d->setCursorVisible( TRUE ); + if ( d->hasIMSelection() ) + d->cursor = d->imselstart; + d->updateMicroFocusHint(); +} + +/*!\reimp +*/ + +void TQLineEdit::focusOutEvent( TQFocusEvent* ) +{ + if ( TQFocusEvent::reason() != TQFocusEvent::ActiveWindow && + TQFocusEvent::reason() != TQFocusEvent::Popup ) + deselect(); + d->setCursorVisible( FALSE ); + if ( d->cursorTimer > 0 ) + killTimer( d->cursorTimer ); + d->cursorTimer = 0; + if (TQFocusEvent::reason() != TQFocusEvent::Popup) + emit lostFocus(); +} + +/*!\reimp +*/ +void TQLineEdit::drawContents( TQPainter *p ) +{ + const TQColorGroup& cg = colorGroup(); + TQRect cr = contentsRect(); + TQFontMetrics fm = fontMetrics(); + TQRect lineRect( cr.x() + innerMargin, cr.y() + (cr.height() - fm.height() + 1) / 2, + cr.width() - 2*innerMargin, fm.height() ); + TQBrush bg = TQBrush( paletteBackgroundColor() ); + if ( paletteBackgroundPixmap() ) + bg = TQBrush( cg.background(), *paletteBackgroundPixmap() ); + else if ( !isEnabled() ) + bg = cg.brush( TQColorGroup::Background ); + TQPoint brushOrigin = p->brushOrigin(); + p->save(); + p->setClipRegion( TQRegion(cr) - lineRect ); + p->setBrushOrigin(brushOrigin - backgroundOffset()); + p->fillRect( cr, bg ); + p->restore(); + TQSharedDoubleBuffer buffer( p, lineRect.x(), lineRect.y(), + lineRect.width(), lineRect.height(), + hasFocus() ? TQSharedDoubleBuffer::Force : 0 ); + p = buffer.painter(); + brushOrigin = p->brushOrigin(); + p->setBrushOrigin(brushOrigin - backgroundOffset()); + p->fillRect( lineRect, bg ); + p->setBrushOrigin(brushOrigin); + + // locate cursor position + int cix = 0; + TQTextItem ci = d->textLayout.findItem( d->cursor ); + if ( ci.isValid() ) { + if ( d->cursor != (int)d->text.length() && d->cursor == ci.from() + ci.length() + && ci.isRightToLeft() != d->isRightToLeft() ) + ci = d->textLayout.findItem( d->cursor + 1 ); + cix = ci.x() + ci.cursorToX( d->cursor - ci.from() ); + } + + // horizontal scrolling + int minLB = TQMAX( 0, -fm.minLeftBearing() ); + int minRB = TQMAX( 0, -fm.minRightBearing() ); + int widthUsed = d->textLayout.widthUsed() + 1 + minRB; + if ( (minLB + widthUsed) <= lineRect.width() ) { + switch ( d->visualAlignment() ) { + case AlignRight: + d->hscroll = widthUsed - lineRect.width() + 1; + break; + case AlignHCenter: + d->hscroll = ( widthUsed - lineRect.width() ) / 2; + break; + default: + d->hscroll = 0; + break; + } + d->hscroll -= minLB; + } else if ( cix - d->hscroll >= lineRect.width() ) { + d->hscroll = cix - lineRect.width() + 1; + } else if ( cix - d->hscroll < 0 ) { + d->hscroll = cix; + } else if ( widthUsed - d->hscroll < lineRect.width() ) { + d->hscroll = widthUsed - lineRect.width() + 1; + } else if (d->hscroll < 0) { + d->hscroll = 0; + } + // This updateMicroFocusHint() is corresponding to update() at + // IMCompose event. Although the function is invoked from various + // other points, some situations such as "candidate selection on + // AlignHCenter'ed text" need this invocation because + // updateMicroFocusHint() requires updated contentsRect(), and + // there are no other chances in such situation that invoke the + // function. + d->updateMicroFocusHint(); + // the y offset is there to keep the baseline constant in case we have script changes in the text. + TQPoint topLeft = lineRect.topLeft() - TQPoint(d->hscroll, d->ascent-fm.ascent()); + + // draw text, selections and cursors + p->setPen( cg.text() ); + bool supressCursor = d->readOnly, hasRightToLeft = d->isRightToLeft(); + int textflags = 0; + if ( font().underline() ) + textflags |= TQt::Underline; + if ( font().strikeOut() ) + textflags |= TQt::StrikeOut; + if ( font().overline() ) + textflags |= TQt::Overline; + + for ( int i = 0; i < d->textLayout.numItems(); i++ ) { + TQTextItem ti = d->textLayout.itemAt( i ); + hasRightToLeft |= ti.isRightToLeft(); + int tix = topLeft.x() + ti.x(); + int first = ti.from(); + int last = ti.from() + ti.length() - 1; + + // text and selection + if ( d->selstart < d->selend && (last >= d->selstart && first < d->selend ) ) { + TQRect highlight = TQRect( TQPoint( tix + ti.cursorToX( TQMAX( d->selstart - first, 0 ) ), + lineRect.top() ), + TQPoint( tix + ti.cursorToX( TQMIN( d->selend - first, last - first + 1 ) ) - 1, + lineRect.bottom() ) ).normalize(); + p->save(); + p->setClipRegion( TQRegion( lineRect ) - highlight, TQPainter::CoordPainter ); + p->drawTextItem( topLeft, ti, textflags ); + p->setClipRect( lineRect & highlight, TQPainter::CoordPainter ); + p->fillRect( highlight, cg.highlight() ); + p->setPen( cg.highlightedText() ); + p->drawTextItem( topLeft, ti, textflags ); + p->restore(); + } else { + p->drawTextItem( topLeft, ti, textflags ); + } + + // input method edit area + if ( d->composeMode() && (last >= d->imstart && first < d->imend ) ) { + TQRect highlight = TQRect( TQPoint( tix + ti.cursorToX( TQMAX( d->imstart - first, 0 ) ), lineRect.top() ), + TQPoint( tix + ti.cursorToX( TQMIN( d->imend - first, last - first + 1 ) )-1, lineRect.bottom() ) ).normalize(); + p->save(); + p->setClipRect( lineRect & highlight, TQPainter::CoordPainter ); + + int h1, s1, v1, h2, s2, v2; + cg.color( TQColorGroup::Base ).hsv( &h1, &s1, &v1 ); + cg.color( TQColorGroup::Background ).hsv( &h2, &s2, &v2 ); + TQColor imCol; + imCol.setHsv( h1, s1, ( v1 + v2 ) / 2 ); + p->fillRect( highlight, imCol ); + p->drawTextItem( topLeft, ti, textflags ); + // draw preedit's underline + if (d->imend - d->imstart > 0) { + p->setPen( cg.text() ); + p->drawLine( highlight.bottomLeft(), highlight.bottomRight() ); + } + p->restore(); + } + + // input method selection + if ( d->hasIMSelection() && (last >= d->imselstart && first < d->imselend ) ) { + TQRect highlight = TQRect( TQPoint( tix + ti.cursorToX( TQMAX( d->imselstart - first, 0 ) ), lineRect.top() ), + TQPoint( tix + ti.cursorToX( TQMIN( d->imselend - first, last - first + 1 ) )-1, lineRect.bottom() ) ).normalize(); + p->save(); + p->setClipRect( lineRect & highlight, TQPainter::CoordPainter ); + p->fillRect( highlight, cg.text() ); + p->setPen( paletteBackgroundColor() ); + p->drawTextItem( topLeft, ti, textflags ); + p->restore(); + supressCursor = TRUE; + } + + // overwrite cursor + if ( d->cursorVisible && d->maskData && + d->selend <= d->selstart && (last >= d->cursor && first <= d->cursor ) ) { + TQRect highlight = TQRect( TQPoint( tix + ti.cursorToX( TQMAX( d->cursor - first, 0 ) ), lineRect.top() ), + TQPoint( tix + ti.cursorToX( TQMIN( d->cursor + 1 - first, last - first + 1 ) )-1, lineRect.bottom() ) ).normalize(); + p->save(); + p->setClipRect( lineRect & highlight, TQPainter::CoordPainter ); + p->fillRect( highlight, cg.text() ); + p->setPen( paletteBackgroundColor() ); + p->drawTextItem( topLeft, ti, textflags ); + p->restore(); + supressCursor = TRUE; + } + } + + // draw cursor + // + // Asian users regard IM selection text as cursor on candidate + // selection phase of input method, so ordinary cursor should be + // invisible if IM selection text exists. + if ( d->cursorVisible && !supressCursor && !d->hasIMSelection() && (d->echoMode != PasswordThreeStars) ) { + TQPoint from( topLeft.x() + cix, lineRect.top() ); + TQPoint to = from + TQPoint( 0, lineRect.height() ); + p->drawLine( from, to ); + if ( hasRightToLeft ) { + bool rtl = ci.isValid() ? ci.isRightToLeft() : TRUE; + to = from + TQPoint( (rtl ? -2 : 2), 2 ); + p->drawLine( from, to ); + from.ry() += 4; + p->drawLine( from, to ); + } + } + buffer.end(); +} + + +#ifndef TQT_NO_DRAGANDDROP +/*!\reimp +*/ +void TQLineEdit::dragMoveEvent( TQDragMoveEvent *e ) +{ + if ( !d->readOnly && TQTextDrag::canDecode(e) ) { + e->acceptAction(); + d->cursor = d->xToPos( e->pos().x() ); + d->cursorVisible = TRUE; + update(); + } +} + +/*!\reimp */ +void TQLineEdit::dragEnterEvent( TQDragEnterEvent * e ) +{ + TQLineEdit::dragMoveEvent( e ); +} + +/*!\reimp */ +void TQLineEdit::dragLeaveEvent( TQDragLeaveEvent *) +{ + if ( d->cursorVisible ) { + d->cursorVisible = FALSE; + update(); + } +} + +/*!\reimp */ +void TQLineEdit::dropEvent( TQDropEvent* e ) +{ + TQString str; + // try text/plain + TQCString plain = "plain"; + bool decoded = TQTextDrag::decode(e, str, plain); + // otherwise we'll accept any kind of text (like text/uri-list) + if (! decoded) + decoded = TQTextDrag::decode(e, str); + + if ( decoded && !d->readOnly ) { + if ( e->source() == this && e->action() == TQDropEvent::Copy ) + deselect(); + d->cursor =d->xToPos( e->pos().x() ); + int selStart = d->cursor; + int oldSelStart = d->selstart; + int oldSelEnd = d->selend; + d->cursorVisible = FALSE; + e->acceptAction(); + insert( str ); + if ( e->source() == this ) { + if ( e->action() == TQDropEvent::Move ) { + if ( selStart > oldSelStart && selStart <= oldSelEnd ) + setSelection( oldSelStart, str.length() ); + else if ( selStart > oldSelEnd ) + setSelection( selStart - str.length(), str.length() ); + else + setSelection( selStart, str.length() ); + } else { + setSelection( selStart, str.length() ); + } + } + } else { + e->ignore(); + update(); + } +} + +bool TQLineEditPrivate::drag() +{ + q->killTimer( dndTimer ); + dndTimer = 0; + TQTextDrag *tdo = new TQTextDrag( q->selectedText(), q ); + + TQGuardedPtr<TQLineEdit> gptr = q; + bool r = tdo->drag(); + if ( !gptr ) + return FALSE; + + // ### fix the check TQDragObject::target() != q in TQt4 (should not be needed) + if ( r && !readOnly && TQDragObject::target() != q ) { + int priorState = undoState; + removeSelectedText(); + finishChange( priorState ); + } +#ifndef TQT_NO_CURSOR + q->setCursor( readOnly ? arrowCursor : ibeamCursor ); +#endif + return TRUE; +} + +#endif // TQT_NO_DRAGANDDROP + +enum { IdUndo, IdRedo, IdSep1, IdCut, IdCopy, IdPaste, IdClear, IdSep2, IdSelectAll }; + +/*!\reimp +*/ +void TQLineEdit::contextMenuEvent( TQContextMenuEvent * e ) +{ +#ifndef TQT_NO_POPUPMENU +#ifndef TQT_NO_IM + if ( d->composeMode() ) + return; +#endif + d->separate(); + TQPopupMenu *menu = createPopupMenu(); + if (!menu) + return; + TQGuardedPtr<TQPopupMenu> popup = menu; + TQGuardedPtr<TQLineEdit> that = this; + TQPoint pos = e->reason() == TQContextMenuEvent::Mouse ? e->globalPos() : + mapToGlobal( TQPoint(e->pos().x(), 0) ) + TQPoint( width() / 2, height() / 2 ); + int r = popup->exec( pos ); + delete (TQPopupMenu*)popup; + if ( that && d->menuId ) { + switch ( d->menuId - r ) { + case IdClear: clear(); break; + case IdSelectAll: selectAll(); break; + case IdUndo: undo(); break; + case IdRedo: redo(); break; +#ifndef TQT_NO_CLIPBOARD + case IdCut: cut(); break; + case IdCopy: copy(); break; + case IdPaste: paste(); break; +#endif + default: + ; // nothing selected or lineedit destroyed. Be careful. + } + } +#endif //TQT_NO_POPUPMENU +} + +/*! + This function is called to create the popup menu which is shown + when the user clicks on the line edit with the right mouse button. + If you want to create a custom popup menu, reimplement this + function and return the popup menu you create. The popup menu's + ownership is transferred to the caller. +*/ + +TQPopupMenu *TQLineEdit::createPopupMenu() +{ +#ifndef TQT_NO_POPUPMENU + TQPopupMenu *popup = new TQPopupMenu( this, "qt_edit_menu" ); + int id = d->menuId = popup->insertItem( tr( "&Undo" ) + ACCEL_KEY( Z ) ); + popup->insertItem( tr( "&Redo" ) + ACCEL_KEY( Y ) ); + popup->insertSeparator(); + popup->insertItem( tr( "Cu&t" ) + ACCEL_KEY( X ) ); + popup->insertItem( tr( "&Copy" ) + ACCEL_KEY( C ) ); + popup->insertItem( tr( "&Paste" ) + ACCEL_KEY( V ) ); + popup->insertItem( tr( "Clear" ) ); + popup->insertSeparator(); + popup->insertItem( tr( "Select All" ) +#ifndef TQ_WS_X11 + + ACCEL_KEY( A ) +#endif + ); + +#ifndef TQT_NO_IM + TQInputContext *qic = getInputContext(); + if ( qic ) + qic->addMenusTo( popup ); +#endif + + popup->setItemEnabled( id - IdUndo, d->isUndoAvailable() ); + popup->setItemEnabled( id - IdRedo, d->isRedoAvailable() ); +#ifndef TQT_NO_CLIPBOARD + popup->setItemEnabled( id - IdCut, !d->readOnly && d->hasSelectedText() ); + popup->setItemEnabled( id - IdCopy, d->hasSelectedText() ); + popup->setItemEnabled( id - IdPaste, !d->readOnly && !TQApplication::clipboard()->text().isEmpty() ); +#else + popup->setItemVisible( id - IdCut, FALSE ); + popup->setItemVisible( id - IdCopy, FALSE ); + popup->setItemVisible( id - IdPaste, FALSE ); +#endif + popup->setItemEnabled( id - IdClear, !d->readOnly && !d->text.isEmpty() ); + popup->setItemEnabled( id - IdSelectAll, !d->text.isEmpty() && !d->allSelected() ); + return popup; +#else + return 0; +#endif +} + +/*! \reimp */ +void TQLineEdit::windowActivationChange( bool b ) +{ + //### remove me with WHighlightSelection attribute + if ( palette().active() != palette().inactive() ) + update(); + TQWidget::windowActivationChange( b ); +} + +/*! \reimp */ + +void TQLineEdit::setPalette( const TQPalette & p ) +{ + //### remove me with WHighlightSelection attribute + TQWidget::setPalette( p ); + update(); +} + +/*! \reimp */ +void TQLineEdit::setFont( const TQFont & f ) +{ + TQWidget::setFont( f ); + d->updateTextLayout(); +} + +/*! \obsolete +*/ +int TQLineEdit::characterAt( int xpos, TQChar *chr ) const +{ + int pos = d->xToPos( xpos + contentsRect().x() - d->hscroll + innerMargin ); + if ( chr && pos < (int) d->text.length() ) + *chr = d->text.at( pos ); + return pos; +} + +/*! + \internal + + Sets the password character to \a c. + + \sa passwordChar() +*/ + +void TQLineEdit::setPasswordChar( TQChar c ) +{ + d->passwordChar = c; +} + +/*! + \internal + + Returns the password character. + + \sa setPasswordChar() +*/ +TQChar TQLineEdit::passwordChar() const +{ + return ( d->passwordChar.isNull() ? TQChar( style().styleHint( TQStyle::SH_LineEdit_PasswordCharacter, this ) ) : d->passwordChar ); +} + +void TQLineEdit::clipboardChanged() +{ +} + +void TQLineEditPrivate::init( const TQString& txt ) +{ +#ifndef TQT_NO_CURSOR + q->setCursor( readOnly ? arrowCursor : ibeamCursor ); +#endif + q->setFocusPolicy( TQWidget::StrongFocus ); + q->setInputMethodEnabled( TRUE ); + // Specifies that this widget can use more, but is able to survive on + // less, horizontal space; and is fixed vertically. + q->setSizePolicy( TQSizePolicy( TQSizePolicy::Expanding, TQSizePolicy::Fixed ) ); + q->setBackgroundMode( PaletteBase ); + q->setKeyCompression( TRUE ); + q->setMouseTracking( TRUE ); + q->setAcceptDrops( TRUE ); + q->setFrame( TRUE ); + text = txt; + updateTextLayout(); + cursor = text.length(); +} + +void TQLineEditPrivate::updateTextLayout() +{ + // replace all non-printable characters with spaces (to avoid + // drawing boxes when using fonts that don't have glyphs for such + // characters) + const TQString &displayText = q->displayText(); + TQString str(displayText.unicode(), displayText.length()); + TQChar* uc = (TQChar*)str.unicode(); + for (int i = 0; i < (int)str.length(); ++i) { + if (! uc[i].isPrint()) + uc[i] = TQChar(0x0020); + } + textLayout.setText( str, q->font() ); + textLayout.setDirection((TQChar::Direction)direction); + textLayout.beginLayout(TQTextLayout::SingleLine); + textLayout.beginLine( INT_MAX ); + while ( !textLayout.atEnd() ) + textLayout.addCurrentItem(); + ascent = 0; + textLayout.endLine(0, 0, TQt::AlignLeft|TQt::SingleLine, &ascent); +} + +int TQLineEditPrivate::xToPosInternal( int x, TQTextItem::CursorPosition betweenOrOn ) const +{ + x-= q->contentsRect().x() - hscroll + innerMargin; + for ( int i = 0; i < textLayout.numItems(); ++i ) { + TQTextItem ti = textLayout.itemAt( i ); + TQRect tir = ti.rect(); + if ( x >= tir.left() && x <= tir.right() ) + return ti.xToCursor( x - tir.x(), betweenOrOn ) + ti.from(); + } + return x < 0 ? -1 : text.length(); +} + +int TQLineEditPrivate::xToPos( int x, TQTextItem::CursorPosition betweenOrOn ) const +{ + int pos = xToPosInternal( x, betweenOrOn ); + return ( pos < 0 ) ? 0 : pos; +} + + +TQRect TQLineEditPrivate::cursorRect() const +{ + TQRect cr = q->contentsRect(); + int cix = cr.x() - hscroll + innerMargin; + TQTextItem ci = textLayout.findItem( cursor ); + if ( ci.isValid() ) { + if ( cursor != (int)text.length() && cursor == ci.from() + ci.length() + && ci.isRightToLeft() != isRightToLeft() ) + ci = textLayout.findItem( cursor + 1 ); + cix += ci.x() + ci.cursorToX( cursor - ci.from() ); + } + int ch = q->fontMetrics().height(); + return TQRect( cix-4, cr.y() + ( cr.height() - ch + 1) / 2, 8, ch + 1 ); +} + +void TQLineEditPrivate::updateMicroFocusHint() +{ + // To reduce redundant microfocus update notification, we remember + // the old rect and update the microfocus if actual update is + // required. The rect o is intentionally static because some + // notifyee requires the microfocus information as global update + // rather than per notifyee update to place shared widget around + // microfocus. + static TQRect o; + if ( q->hasFocus() ) { + TQRect r = cursorRect(); + if ( o != r ) { + o = r; + q->setMicroFocusHint( r.x(), r.y(), r.width(), r.height() ); + } + } +} + +void TQLineEditPrivate::moveCursor( int pos, bool mark ) +{ + if ( pos != cursor ) + separate(); + if ( maskData && pos > cursor ) + pos = nextMaskBlank( pos ); + else if ( maskData && pos < cursor ) + pos = prevMaskBlank( pos ); + bool fullUpdate = mark || hasSelectedText(); + if ( mark ) { + int anchor; + if ( selend > selstart && cursor == selstart ) + anchor = selend; + else if ( selend > selstart && cursor == selend ) + anchor = selstart; + else + anchor = cursor; + selstart = TQMIN( anchor, pos ); + selend = TQMAX( anchor, pos ); + } else { + deselect(); + } + if ( fullUpdate ) { + cursor = pos; + q->update(); + } else { + setCursorVisible( FALSE ); + cursor = pos; + setCursorVisible( TRUE ); + } + updateMicroFocusHint(); + if ( mark && !q->style().styleHint( TQStyle::SH_BlinkCursorWhenTextSelected ) ) + setCursorVisible( FALSE ); + if ( mark || selDirty ) { + selDirty = FALSE; + emit q->selectionChanged(); + } +} + +void TQLineEditPrivate::finishChange( int validateFromState, bool setModified ) +{ + bool lineDirty = selDirty; + if ( textDirty ) { + // do validation + bool wasValidInput = validInput; + validInput = TRUE; +#ifndef TQT_NO_VALIDATOR + if ( validator && validateFromState >= 0 ) { + TQString textCopy = text; + int cursorCopy = cursor; + validInput = ( validator->validate( textCopy, cursorCopy ) != TQValidator::Invalid ); + if ( validInput ) { + if ( text != textCopy ) { + q->setText( textCopy ); + cursor = cursorCopy; + return; + } + cursor = cursorCopy; + } + } +#endif + if ( validateFromState >= 0 && wasValidInput && !validInput ) { + undo( validateFromState ); + history.resize( undoState ); + validInput = TRUE; + textDirty = setModified = FALSE; + } + updateTextLayout(); + updateMicroFocusHint(); + lineDirty |= textDirty; + if ( setModified ) + modified = TRUE; + if ( textDirty ) { + textDirty = FALSE; + emit q->textChanged( maskData ? stripString(text) : text ); + } +#if defined(QT_ACCESSIBILITY_SUPPORT) + TQAccessible::updateAccessibility( q, 0, TQAccessible::ValueChanged ); +#endif + } + if ( selDirty ) { + selDirty = FALSE; + emit q->selectionChanged(); + } + if ( lineDirty || !setModified ) + q->update(); +} + +void TQLineEditPrivate::setText( const TQString& txt ) +{ + deselect(); + TQString oldText = text; + if ( maskData ) { + text = maskString( 0, txt, TRUE ); + text += clearString( text.length(), maxLength - text.length() ); + } else { + text = txt.isEmpty() ? txt : txt.left( maxLength ); + } + history.clear(); + undoState = 0; + cursor = text.length(); + textDirty = ( oldText != text ); +} + + +void TQLineEditPrivate::setCursorVisible( bool visible ) +{ + if ( (bool)cursorVisible == visible ) + return; + if ( cursorTimer ) + cursorVisible = visible; + TQRect r = cursorRect(); + if ( maskData || !q->contentsRect().contains( r ) ) + q->update(); + else + q->update( r ); +} + +void TQLineEditPrivate::addCommand( const Command& cmd ) +{ + if ( separator && undoState && history[undoState-1].type != Separator ) { + history.resize( undoState + 2 ); + history[undoState++] = Command( Separator, 0, 0 ); + } else { + history.resize( undoState + 1); + } + separator = FALSE; + history[ undoState++ ] = cmd; +} + +void TQLineEditPrivate::insert( const TQString& s ) +{ + if ( maskData ) { + TQString ms = maskString( cursor, s ); + for ( int i = 0; i < (int) ms.length(); ++i ) { + addCommand ( Command( DeleteSelection, cursor+i, text.at(cursor+i) ) ); + addCommand( Command( Insert, cursor+i, ms.at(i) ) ); + } + text.replace( cursor, ms.length(), ms ); + cursor += ms.length(); + cursor = nextMaskBlank( cursor ); + } else { + int remaining = maxLength - text.length(); + text.insert( cursor, s.left(remaining) ); + for ( int i = 0; i < (int) s.left(remaining).length(); ++i ) + addCommand( Command( Insert, cursor++, s.at(i) ) ); + } + textDirty = TRUE; +} + +void TQLineEditPrivate::del( bool wasBackspace ) +{ + if ( cursor < (int) text.length() ) { + addCommand ( Command( (CommandType)((maskData?2:0)+(wasBackspace?Remove:Delete)), cursor, text.at(cursor) ) ); + if ( maskData ) { + text.replace( cursor, 1, clearString( cursor, 1 ) ); + addCommand( Command( Insert, cursor, text.at( cursor ) ) ); + } else { + text.remove( cursor, 1 ); + } + textDirty = TRUE; + } +} + +void TQLineEditPrivate::removeSelectedText() +{ + if ( selstart < selend && selend <= (int) text.length() ) { + separate(); + int i ; + if ( selstart <= cursor && cursor < selend ) { + // cursor is within the selection. Split up the commands + // to be able to restore the correct cursor position + for ( i = cursor; i >= selstart; --i ) + addCommand ( Command( DeleteSelection, i, text.at(i) ) ); + for ( i = selend - 1; i > cursor; --i ) + addCommand ( Command( DeleteSelection, i - cursor + selstart - 1, text.at(i) ) ); + } else { + for ( i = selend-1; i >= selstart; --i ) + addCommand ( Command( RemoveSelection, i, text.at(i) ) ); + } + if ( maskData ) { + text.replace( selstart, selend - selstart, clearString( selstart, selend - selstart ) ); + for ( int i = 0; i < selend - selstart; ++i ) + addCommand( Command( Insert, selstart + i, text.at( selstart + i ) ) ); + } else { + text.remove( selstart, selend - selstart ); + } + if ( cursor > selstart ) + cursor -= TQMIN( cursor, selend ) - selstart; + deselect(); + textDirty = TRUE; + } +} + +void TQLineEditPrivate::parseInputMask( const TQString &maskFields ) +{ + if ( maskFields.isEmpty() || maskFields.section( ';', 0, 0 ).isEmpty() ) { + if ( maskData ) { + delete [] maskData; + maskData = 0; + maxLength = 32767; + q->setText( TQString::null ); + } + return; + } + + inputMask = maskFields.section( ';', 0, 0 ); + blank = maskFields.section( ';', 1, 1 ).at(0); + if ( blank.isNull() ) + blank = ' '; + + // calculate maxLength / maskData length + maxLength = 0; + TQChar c = 0; + uint i; + for ( i=0; i<inputMask.length(); i++ ) { + c = inputMask.at(i); + if ( i > 0 && inputMask.at( i-1 ) == '\\' ) { + maxLength++; + continue; + } + if ( c != '\\' && c != '!' && + c != '<' && c != '>' && + c != '{' && c != '}' && + c != '[' && c != ']' ) + maxLength++; + } + + delete [] maskData; + maskData = new MaskInputData[ maxLength ]; + + MaskInputData::Casemode m = MaskInputData::NoCaseMode; + c = 0; + bool s; + bool escape = FALSE; + int index = 0; + for ( i = 0; i < inputMask.length(); i++ ) { + c = inputMask.at(i); + if ( escape ) { + s = TRUE; + maskData[ index ].maskChar = c; + maskData[ index ].separator = s; + maskData[ index ].caseMode = m; + index++; + escape = FALSE; + } else if ( c == '<' || c == '>' || c == '!') { + switch ( c ) { + case '<': + m = MaskInputData::Lower; + break; + case '>': + m = MaskInputData::Upper; + break; + case '!': + m = MaskInputData::NoCaseMode; + break; + } + } else if ( c != '{' && c != '}' && c != '[' && c != ']' ) { + switch ( c ) { + case 'A': + case 'a': + case 'N': + case 'n': + case 'X': + case 'x': + case '9': + case '0': + case 'D': + case 'd': + case '#': + s = FALSE; + break; + case '\\': + escape = TRUE; + default: + s = TRUE; + break; + } + + if ( !escape ) { + maskData[ index ].maskChar = c; + maskData[ index ].separator = s; + maskData[ index ].caseMode = m; + index++; + } + } + } + q->setText( TQString::null ); +} + + +/* checks if the key is valid compared to the inputMask */ +bool TQLineEditPrivate::isValidInput( TQChar key, TQChar mask ) const +{ + switch ( mask ) { + case 'A': + if ( key.isLetter() && key != blank ) + return TRUE; + break; + case 'a': + if ( key.isLetter() || key == blank ) + return TRUE; + break; + case 'N': + if ( key.isLetterOrNumber() && key != blank ) + return TRUE; + break; + case 'n': + if ( key.isLetterOrNumber() || key == blank ) + return TRUE; + break; + case 'X': + if ( key.isPrint() && key != blank ) + return TRUE; + break; + case 'x': + if ( key.isPrint() || key == blank ) + return TRUE; + break; + case '9': + if ( key.isNumber() && key != blank ) + return TRUE; + break; + case '0': + if ( key.isNumber() || key == blank ) + return TRUE; + break; + case 'D': + if ( key.isNumber() && key.digitValue() > 0 && key != blank ) + return TRUE; + break; + case 'd': + if ( (key.isNumber() && key.digitValue() > 0) || key == blank ) + return TRUE; + break; + case '#': + if ( key.isNumber() || key == '+' || key == '-' || key == blank ) + return TRUE; + break; + default: + break; + } + return FALSE; +} + +/* + Applies the inputMask on \a str starting from position \a pos in the mask. \a clear + specifies from where characters should be gotten when a separator is met in \a str - TRUE means + that blanks will be used, FALSE that previous input is used. + Calling this when no inputMask is set is undefined. +*/ +TQString TQLineEditPrivate::maskString( uint pos, const TQString &str, bool clear) const +{ + if ( pos >= (uint)maxLength ) + return TQString::fromLatin1(""); + + TQString fill; + fill = clear ? clearString( 0, maxLength ) : text; + + uint strIndex = 0; + TQString s = TQString::fromLatin1(""); + int i = pos; + while ( i < maxLength ) { + if ( strIndex < str.length() ) { + if ( maskData[ i ].separator ) { + s += maskData[ i ].maskChar; + if ( str[(int)strIndex] == maskData[ i ].maskChar ) + strIndex++; + ++i; + } else { + if ( isValidInput( str[(int)strIndex], maskData[ i ].maskChar ) ) { + switch ( maskData[ i ].caseMode ) { + case MaskInputData::Upper: + s += str[(int)strIndex].upper(); + break; + case MaskInputData::Lower: + s += str[(int)strIndex].lower(); + break; + default: + s += str[(int)strIndex]; + } + ++i; + } else { + // search for separator first + int n = findInMask( i, TRUE, TRUE, str[(int)strIndex] ); + if ( n != -1 ) { + if ( str.length() != 1 || i == 0 || (i > 0 && (!maskData[i-1].separator || maskData[i-1].maskChar != str[(int)strIndex])) ) { + s += fill.mid( i, n-i+1 ); + i = n + 1; // update i to find + 1 + } + } else { + // search for valid blank if not + n = findInMask( i, TRUE, FALSE, str[(int)strIndex] ); + if ( n != -1 ) { + s += fill.mid( i, n-i ); + switch ( maskData[ n ].caseMode ) { + case MaskInputData::Upper: + s += str[(int)strIndex].upper(); + break; + case MaskInputData::Lower: + s += str[(int)strIndex].lower(); + break; + default: + s += str[(int)strIndex]; + } + i = n + 1; // updates i to find + 1 + } + } + } + strIndex++; + } + } else + break; + } + + return s; +} + + + +/* + Returns a "cleared" string with only separators and blank chars. + Calling this when no inputMask is set is undefined. +*/ +TQString TQLineEditPrivate::clearString( uint pos, uint len ) const +{ + if ( pos >= (uint)maxLength ) + return TQString::null; + + TQString s; + int end = TQMIN( (uint)maxLength, pos + len ); + for ( int i=pos; i<end; i++ ) + if ( maskData[ i ].separator ) + s += maskData[ i ].maskChar; + else + s += blank; + + return s; +} + +/* + Strips blank parts of the input in a TQLineEdit when an inputMask is set, + separators are still included. Typically "127.0__.0__.1__" becomes "127.0.0.1". +*/ +TQString TQLineEditPrivate::stripString( const TQString &str ) const +{ + if ( !maskData ) + return str; + + TQString s; + int end = TQMIN( maxLength, (int)str.length() ); + for (int i=0; i < end; i++ ) + if ( maskData[ i ].separator ) + s += maskData[ i ].maskChar; + else + if ( str[i] != blank ) + s += str[i]; + + return s; +} + +/* searches forward/backward in maskData for either a separator or a blank */ +int TQLineEditPrivate::findInMask( int pos, bool forward, bool findSeparator, TQChar searchChar ) const +{ + if ( pos >= maxLength || pos < 0 ) + return -1; + + int end = forward ? maxLength : -1; + int step = forward ? 1 : -1; + int i = pos; + + while ( i != end ) { + if ( findSeparator ) { + if ( maskData[ i ].separator && maskData[ i ].maskChar == searchChar ) + return i; + } else { + if ( !maskData[ i ].separator ) { + if ( searchChar.isNull() ) + return i; + else if ( isValidInput( searchChar, maskData[ i ].maskChar ) ) + return i; + } + } + i += step; + } + return -1; +} + + +#endif // TQT_NO_LINEEDIT |