ddeba45e3bbd0b52f2b74bf2ee0a3c457b9f64d9
[pinentry.git] / qt / secqlineedit.cpp
1 /* secqlineedit.cpp - Secure version of QLineEdit.
2    Copyright (C) 1992-2002 Trolltech AS.  All rights reserved.
3    Copyright (C) 2003 g10 Code GmbH
4
5    The license of the original qlineedit.cpp file from which this file
6    is derived can be found below.  Modified by Marcus Brinkmann
7    <marcus@g10code.de>.  All modifications are licensed as follows, so
8    that the intersection of the two licenses is then the GNU General
9    Public License version 2.
10
11    This program is free software; you can redistribute it and/or
12    modify it under the terms of the GNU General Public License as
13    published by the Free Software Foundation; either version 2 of the
14    License, or (at your option) any later version.
15  
16    This program is distributed in the hope that it will be useful, but
17    WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19    General Public License for more details.
20  
21    You should have received a copy of the GNU General Public License
22    along with this program; if not, write to the Free Software
23    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
24    02111-1307, USA  */
25
26
27 /* Undo/Redo is disabled, because it uses unsecure memory for the
28    history buffer.  */
29 #define SECURE_NO_UNDO  1
30
31
32 /**********************************************************************
33 ** $Id$
34 **
35 ** Implementation of SecQLineEdit widget class
36 **
37 ** Created : 941011
38 **
39 ** Copyright (C) 1992-2002 Trolltech AS.  All rights reserved.
40 **
41 ** This file is part of the widgets module of the Qt GUI Toolkit.
42 **
43 ** This file may be distributed under the terms of the Q Public License
44 ** as defined by Trolltech AS of Norway and appearing in the file
45 ** LICENSE.QPL included in the packaging of this file.
46 **
47 ** This file may be distributed and/or modified under the terms of the
48 ** GNU General Public License version 2 as published by the Free Software
49 ** Foundation and appearing in the file LICENSE.GPL included in the
50 ** packaging of this file.
51 **
52 ** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
53 ** licenses may use this file in accordance with the Qt Commercial License
54 ** Agreement provided with the Software.
55 **
56 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
57 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
58 **
59 ** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
60 **   information about Qt Commercial License Agreements.
61 ** See http://www.trolltech.com/qpl/ for QPL licensing information.
62 ** See http://www.trolltech.com/gpl/ for GPL licensing information.
63 **
64 ** Contact info@trolltech.com if any conditions of this licensing are
65 ** not clear to you.
66 **
67 **********************************************************************/
68
69 #include "secqlineedit.h"
70 #include "qpainter.h"
71 #include "qdrawutil.h"
72 #include "qfontmetrics.h"
73 #include "qpixmap.h"
74 #include "qclipboard.h"
75 #include "qapplication.h"
76 #include "qtimer.h"
77 #include "qpopupmenu.h"
78 #include "qstringlist.h"
79 #include "qguardedptr.h"
80 #include "qstyle.h"
81 #include "qwhatsthis.h"
82 #include "private/qinternal_p.h"
83 #include "private/qtextlayout_p.h"
84 #include "qvaluevector.h"
85 #if defined(QT_ACCESSIBILITY_SUPPORT)
86 #include "qaccessible.h"
87 #endif
88
89 #ifndef QT_NO_ACCEL
90 #include "qkeysequence.h"
91 #define ACCEL_KEY(k) "\t" + QString(QKeySequence( Qt::CTRL | Qt::Key_ ## k ))
92 #else
93 #define ACCEL_KEY(k) "\t" + QString("Ctrl+" #k)
94 #endif
95
96 #define innerMargin 1
97
98 struct SecQLineEditPrivate : public Qt
99 {
100     SecQLineEditPrivate( SecQLineEdit *q )
101         : q(q), cursor(0), cursorTimer(0), tripleClickTimer(0), frame(1),
102           cursorVisible(0), separator(0), readOnly(0), modified(0),
103           direction(QChar::DirON), alignment(0),
104           echoMode(0), textDirty(0), selDirty(0),
105           ascent(0), maxLength(32767), menuId(0),
106           hscroll(0),
107           undoState(0), selstart(0), selend(0),
108           imstart(0), imend(0), imselstart(0), imselend(0)
109         {}
110     void init( const SecQString&);
111
112     SecQLineEdit *q;
113     SecQString text;
114     int cursor;
115     int cursorTimer;
116     QPoint tripleClick;
117     int tripleClickTimer;
118     uint frame : 1;
119     uint cursorVisible : 1;
120     uint separator : 1;
121     uint readOnly : 1;
122     uint modified : 1;
123     uint direction : 5;
124     uint alignment : 3;
125     uint echoMode : 2;
126     uint textDirty : 1;
127     uint selDirty : 1;
128     int ascent;
129     int maxLength;
130     int menuId;
131     int hscroll;
132
133     void finishChange( int validateFromState = -1, bool setModified = TRUE );
134
135     void setCursorVisible( bool visible );
136
137
138     // undo/redo handling
139     enum CommandType { Separator, Insert, Remove, Delete, RemoveSelection, DeleteSelection };
140     struct Command {
141         inline Command(){}
142         inline Command( CommandType type, int pos, QChar c )
143             :type(type),c(c),pos(pos){}
144         uint type : 4;
145         QChar c;
146         int pos;
147     };
148     int undoState;
149     QValueVector<Command> history;
150 #ifndef SECURE_NO_UNDO
151     void addCommand( const Command& cmd );
152 #endif /* SECURE_NO_UNDO */
153
154     void insert( const SecQString& s );
155     void del( bool wasBackspace = FALSE );
156     void remove( int pos );
157
158     inline void separate() { separator = TRUE; }
159 #ifndef SECURE_NO_UNDO
160     inline void undo( int until = -1 ) {
161         if ( !isUndoAvailable() )
162             return;
163         deselect();
164         while ( undoState && undoState > until ) {
165             Command& cmd = history[--undoState];
166             switch ( cmd.type ) {
167             case Insert:
168                 text.remove( cmd.pos, 1);
169                 cursor = cmd.pos;
170                 break;
171             case Remove:
172             case RemoveSelection:
173                 text.insert( cmd.pos, cmd.c );
174                 cursor = cmd.pos + 1;
175                 break;
176             case Delete:
177             case DeleteSelection:
178                 text.insert( cmd.pos, cmd.c );
179                 cursor = cmd.pos;
180                 break;
181             case Separator:
182                 continue;
183             }
184             if ( until < 0 && undoState ) {
185                 Command& next = history[undoState-1];
186                 if ( next.type != cmd.type && next.type < RemoveSelection
187                      && !( cmd.type >= RemoveSelection && next.type != Separator ) )
188                     break;
189             }
190         }
191         modified = ( undoState != 0 );
192         textDirty = TRUE;
193     }
194     inline void redo() {
195         if ( !isRedoAvailable() )
196             return;
197         deselect();
198         while ( undoState < (int)history.size() ) {
199             Command& cmd = history[undoState++];
200             switch ( cmd.type ) {
201             case Insert:
202                 text.insert( cmd.pos, cmd.c );
203                 cursor = cmd.pos + 1;
204                 break;
205             case Remove:
206             case Delete:
207             case RemoveSelection:
208             case DeleteSelection:
209                 text.remove( cmd.pos, 1 );
210                 cursor = cmd.pos;
211                 break;
212             case Separator:
213                 continue;
214             }
215             if ( undoState < (int)history.size() ) {
216                 Command& next = history[undoState];
217                 if ( next.type != cmd.type && cmd.type < RemoveSelection
218                      && !( next.type >= RemoveSelection && cmd.type != Separator ) )
219                     break;
220             }
221         }
222         textDirty = TRUE;
223     }
224 #endif  /* SECURE_NO_UNDO */
225     inline bool isUndoAvailable() const { return !readOnly && undoState; }
226     inline bool isRedoAvailable() const { return !readOnly && undoState < (int)history.size(); }
227
228
229     // bidi
230     inline bool isRightToLeft() const { return direction==QChar::DirON?text.isRightToLeft():(direction==QChar::DirR); }
231
232     // selection
233     int selstart, selend;
234     inline bool allSelected() const { return !text.isEmpty() && selstart == 0 && selend == (int)text.length(); }
235     inline bool hasSelectedText() const { return !text.isEmpty() && selend > selstart; }
236     inline void deselect() { selDirty |= (selend > selstart); selstart = selend = 0; }
237     void removeSelectedText();
238 #ifndef QT_NO_CLIPBOARD
239     void copy( bool clipboard = TRUE ) const;
240 #endif
241     inline bool inSelection( int x ) const
242     { if ( selstart >= selend ) return FALSE;
243     int pos = xToPos( x, QTextItem::OnCharacters );  return pos >= selstart && pos < selend; }
244
245     // input methods
246     int imstart, imend, imselstart, imselend;
247
248     // complex text layout
249     QTextLayout textLayout;
250     void updateTextLayout();
251     void moveCursor( int pos, bool mark = FALSE );
252     void setText( const SecQString& txt );
253     int xToPos( int x, QTextItem::CursorPosition = QTextItem::BetweenCharacters ) const;
254     inline int visualAlignment() const { return alignment ? alignment : int( isRightToLeft() ? AlignRight : AlignLeft ); }
255     QRect cursorRect() const;
256     void updateMicroFocusHint();
257
258 };
259
260
261 /*!
262     \class SecQLineEdit
263     \brief The SecQLineEdit widget is a one-line text editor.
264
265     \ingroup basic
266     \mainclass
267
268     A line edit allows the user to enter and edit a single line of
269     plain text with a useful collection of editing functions,
270     including undo and redo, cut and paste,
271
272     By changing the echoMode() of a line edit, it can also be used as
273     a "write-only" field, for inputs such as passwords.
274
275     The length of the text can be constrained to maxLength().
276
277     A related class is QTextEdit which allows multi-line, rich-text
278     editing.
279
280     You can change the text with setText() or insert(). The text is
281     retrieved with text(); the displayed text (which may be different,
282     see \l{EchoMode}) is retrieved with displayText(). Text can be
283     selected with setSelection() or selectAll(), and the selection can
284     be cut(), copy()ied and paste()d. The text can be aligned with
285     setAlignment().
286
287     When the text changes the textChanged() signal is emitted; when
288     the Return or Enter key is pressed the returnPressed() signal is
289     emitted.
290
291     By default, SecQLineEdits have a frame as specified by the Windows
292     and Motif style guides; you can turn it off by calling
293     setFrame(FALSE).
294
295     The default key bindings are described below.
296     \target desc
297     \table
298     \header \i Keypress \i Action
299     \row \i Left Arrow \i Moves the cursor one character to the left.
300     \row \i Shift+Left Arrow \i Moves and selects text one character to the left.
301     \row \i Right Arrow \i Moves the cursor one character to the right.
302     \row \i Shift+Right Arrow \i Moves and selects text one character to the right.
303     \row \i Home \i Moves the cursor to the beginning of the line.
304     \row \i End \i Moves the cursor to the end of the line.
305     \row \i Backspace \i Deletes the character to the left of the cursor.
306     \row \i Ctrl+Backspace \i Deletes the word to the left of the cursor.
307     \row \i Delete \i Deletes the character to the right of the cursor.
308     \row \i Ctrl+Delete \i Deletes the word to the right of the cursor.
309     \row \i Ctrl+A \i Moves the cursor to the beginning of the line.
310     \row \i Ctrl+B \i Moves the cursor one character to the left.
311     \row \i Ctrl+C \i Copies the selected text to the clipboard.
312                       (Windows also supports Ctrl+Insert for this operation.)
313     \row \i Ctrl+D \i Deletes the character to the right of the cursor.
314     \row \i Ctrl+E \i Moves the cursor to the end of the line.
315     \row \i Ctrl+F \i Moves the cursor one character to the right.
316     \row \i Ctrl+H \i Deletes the character to the left of the cursor.
317     \row \i Ctrl+K \i Deletes to the end of the line.
318     \row \i Ctrl+V \i Pastes the clipboard text into line edit.
319                       (Windows also supports Shift+Insert for this operation.)
320     \row \i Ctrl+X \i Deletes the selected text and copies it to the clipboard.
321                       (Windows also supports Shift+Delete for this operation.)
322     \row \i Ctrl+Z \i Undoes the last operation.
323     \row \i Ctrl+Y \i Redoes the last undone operation.
324     \endtable
325
326     Any other key sequence that represents a valid character, will
327     cause the character to be inserted into the line edit.
328
329     <img src=qlined-m.png> <img src=qlined-w.png>
330
331     \sa QTextEdit QLabel QComboBox
332         \link guibooks.html#fowler GUI Design Handbook: Field, Entry\endlink
333 */
334
335
336 /*!
337     \fn void SecQLineEdit::textChanged( const SecQString& )
338
339     This signal is emitted whenever the text changes. The argument is
340     the new text.
341 */
342
343 /*!
344     \fn void SecQLineEdit::selectionChanged()
345
346     This signal is emitted whenever the selection changes.
347
348     \sa hasSelectedText(), selectedText()
349 */
350
351 /*!
352     \fn void SecQLineEdit::lostFocus()
353
354     This signal is emitted when the line edit has lost focus.
355
356     \sa hasFocus(), QWidget::focusInEvent(), QWidget::focusOutEvent()
357 */
358
359
360
361 /*!
362     Constructs a line edit with no text.
363
364     The maximum text length is set to 32767 characters.
365
366     The \a parent and \a name arguments are sent to the QWidget constructor.
367
368     \sa setText(), setMaxLength()
369 */
370
371 SecQLineEdit::SecQLineEdit( QWidget* parent, const char* name )
372     : QFrame( parent, name, WNoAutoErase ), d(new SecQLineEditPrivate( this ))
373 {
374     d->init( SecQString::null );
375 }
376
377 /*!
378     Constructs a line edit containing the text \a contents.
379
380     The cursor position is set to the end of the line and the maximum
381     text length to 32767 characters.
382
383     The \a parent and \a name arguments are sent to the QWidget
384     constructor.
385
386     \sa text(), setMaxLength()
387 */
388
389 SecQLineEdit::SecQLineEdit( const SecQString& contents, QWidget* parent, const char* name )
390     : QFrame( parent, name, WNoAutoErase ), d(new SecQLineEditPrivate( this ))
391 {
392     d->init( contents );
393 }
394
395 /*!
396     Destroys the line edit.
397 */
398
399 SecQLineEdit::~SecQLineEdit()
400 {
401     delete d;
402 }
403
404
405 /*!
406     \property SecQLineEdit::text
407     \brief the line edit's text
408
409     Setting this property clears the selection, clears the undo/redo
410     history, moves the cursor to the end of the line and resets the
411     \c modified property to FALSE.
412
413     The text is truncated to maxLength() length.
414
415     \sa insert()
416 */
417 SecQString SecQLineEdit::text() const
418 {
419     return ( d->text.isNull() ? SecQString ("") : d->text );
420 }
421
422 void SecQLineEdit::setText( const SecQString& text)
423 {
424     resetInputContext();
425     d->setText( text );
426     d->modified = FALSE;
427     d->finishChange( -1, FALSE );
428 }
429
430
431 /*!
432     \property SecQLineEdit::displayText
433     \brief the displayed text
434
435     If \c EchoMode is \c Normal this returns the same as text(); if
436     \c EchoMode is \c Password it returns a string of asterisks
437     text().length() characters long, e.g. "******"; if \c EchoMode is
438     \c NoEcho returns an empty string, "".
439
440     \sa setEchoMode() text() EchoMode
441 */
442
443 QString SecQLineEdit::displayText() const
444 {
445     if ( d->echoMode == NoEcho )
446         return QString::fromLatin1("");
447
448     QChar pwd_char = QChar (style().styleHint( QStyle::SH_LineEdit_PasswordCharacter, this));
449     QString res;
450     res.fill (pwd_char, d->text.length ());
451     return res;
452 }
453
454
455 /*!
456     \property SecQLineEdit::maxLength
457     \brief the maximum permitted length of the text
458
459     If the text is too long, it is truncated at the limit.
460
461     If truncation occurs any selected text will be unselected, the
462     cursor position is set to 0 and the first part of the string is
463     shown.
464 */
465
466 int SecQLineEdit::maxLength() const
467 {
468     return d->maxLength;
469 }
470
471 void SecQLineEdit::setMaxLength( int maxLength )
472 {
473     d->maxLength = maxLength;
474     setText( d->text );
475 }
476
477
478
479 /*!
480     \property SecQLineEdit::frame
481     \brief whether the line edit draws itself with a frame
482
483     If enabled (the default) the line edit draws itself inside a
484     two-pixel frame, otherwise the line edit draws itself without any
485     frame.
486 */
487 bool SecQLineEdit::frame() const
488 {
489     return frameShape() != NoFrame;
490 }
491
492
493 void SecQLineEdit::setFrame( bool enable )
494 {
495     setFrameStyle( enable ? ( LineEditPanel | Sunken ) : NoFrame  );
496 }
497
498
499 /*!
500     \enum SecQLineEdit::EchoMode
501
502     This enum type describes how a line edit should display its
503     contents.
504
505     \value Normal   Display characters as they are entered. This is the
506                     default.
507     \value NoEcho   Do not display anything. This may be appropriate
508                     for passwords where even the length of the
509                     password should be kept secret.
510     \value Password  Display asterisks instead of the characters
511                     actually entered.
512
513     \sa setEchoMode() echoMode()
514 */
515
516
517 /*!
518     \property SecQLineEdit::echoMode
519     \brief the line edit's echo mode
520
521     The initial setting is \c Normal, but SecQLineEdit also supports \c
522     NoEcho and \c Password modes.
523
524     The widget's display and the ability to copy the text is affected
525     by this setting.
526
527     \sa EchoMode displayText()
528 */
529
530 SecQLineEdit::EchoMode SecQLineEdit::echoMode() const
531 {
532     return (EchoMode) d->echoMode;
533 }
534
535 void SecQLineEdit::setEchoMode( EchoMode mode )
536 {
537     d->echoMode = mode;
538     d->updateTextLayout();
539     update();
540 }
541
542
543
544 /*!
545     Returns a recommended size for the widget.
546
547     The width returned, in pixels, is usually enough for about 15 to
548     20 characters.
549 */
550
551 QSize SecQLineEdit::sizeHint() const
552 {
553     constPolish();
554     QFontMetrics fm( font() );
555     int h = QMAX(fm.lineSpacing(), 14) + 2*innerMargin;
556     int w = fm.width( 'x' ) * 17; // "some"
557     int m = frameWidth() * 2;
558     return (style().sizeFromContents(QStyle::CT_LineEdit, this,
559                                      QSize( w + m, h + m ).
560                                      expandedTo(QApplication::globalStrut())));
561 }
562
563
564 /*!
565     Returns a minimum size for the line edit.
566
567     The width returned is enough for at least one character.
568 */
569
570 QSize SecQLineEdit::minimumSizeHint() const
571 {
572     constPolish();
573     QFontMetrics fm = fontMetrics();
574     int h = fm.height() + QMAX( 2*innerMargin, fm.leading() );
575     int w = fm.maxWidth();
576     int m = frameWidth() * 2;
577     return QSize( w + m, h + m );
578 }
579
580
581 /*!
582     \property SecQLineEdit::cursorPosition
583     \brief the current cursor position for this line edit
584
585     Setting the cursor position causes a repaint when appropriate.
586 */
587
588 int SecQLineEdit::cursorPosition() const
589 {
590     return d->cursor;
591 }
592
593
594 void SecQLineEdit::setCursorPosition( int pos )
595 {
596     if ( pos <= (int) d->text.length() )
597         d->moveCursor( pos );
598 }
599
600
601 /*!
602     \property SecQLineEdit::alignment
603     \brief the alignment of the line edit
604
605     Possible Values are \c Qt::AlignAuto, \c Qt::AlignLeft, \c
606     Qt::AlignRight and \c Qt::AlignHCenter.
607
608     Attempting to set the alignment to an illegal flag combination
609     does nothing.
610
611     \sa Qt::AlignmentFlags
612 */
613
614 int SecQLineEdit::alignment() const
615 {
616     return d->alignment;
617 }
618
619 void SecQLineEdit::setAlignment( int flag )
620 {
621     d->alignment = flag & 0x7;
622     update();
623 }
624
625
626 /*!
627   \obsolete
628   \fn void SecQLineEdit::cursorRight( bool, int )
629
630   Use cursorForward() instead.
631
632   \sa cursorForward()
633 */
634
635 /*!
636   \obsolete
637   \fn void SecQLineEdit::cursorLeft( bool, int )
638   For compatibilty with older applications only. Use cursorBackward()
639   instead.
640   \sa cursorBackward()
641 */
642
643 /*!
644     Moves the cursor forward \a steps characters. If \a mark is TRUE
645     each character moved over is added to the selection; if \a mark is
646     FALSE the selection is cleared.
647
648     \sa cursorBackward()
649 */
650
651 void SecQLineEdit::cursorForward( bool mark, int steps )
652 {
653     int cursor = d->cursor;
654     if ( steps > 0 ) {
655         while( steps-- )
656             cursor = d->textLayout.nextCursorPosition( cursor );
657     } else if ( steps < 0 ) {
658         while ( steps++ )
659             cursor = d->textLayout.previousCursorPosition( cursor );
660     }
661     d->moveCursor( cursor, mark );
662 }
663
664
665 /*!
666     Moves the cursor back \a steps characters. If \a mark is TRUE each
667     character moved over is added to the selection; if \a mark is
668     FALSE the selection is cleared.
669
670     \sa cursorForward()
671 */
672 void SecQLineEdit::cursorBackward( bool mark, int steps )
673 {
674     cursorForward( mark, -steps );
675 }
676
677 /*!
678     Moves the cursor one word forward. If \a mark is TRUE, the word is
679     also selected.
680
681     \sa cursorWordBackward()
682 */
683 void SecQLineEdit::cursorWordForward( bool mark )
684 {
685     d->moveCursor( d->textLayout.nextCursorPosition(d->cursor, QTextLayout::SkipWords), mark );
686 }
687
688 /*!
689     Moves the cursor one word backward. If \a mark is TRUE, the word
690     is also selected.
691
692     \sa cursorWordForward()
693 */
694
695 void SecQLineEdit::cursorWordBackward( bool mark )
696 {
697     d->moveCursor( d->textLayout.previousCursorPosition(d->cursor, QTextLayout::SkipWords), mark );
698 }
699
700
701 /*!
702     If no text is selected, deletes the character to the left of the
703     text cursor and moves the cursor one position to the left. If any
704     text is selected, the cursor is moved to the beginning of the
705     selected text and the selected text is deleted.
706
707     \sa del()
708 */
709 void SecQLineEdit::backspace()
710 {
711     int priorState = d->undoState;
712     if ( d->hasSelectedText() ) {
713         d->removeSelectedText();
714     } else if ( d->cursor ) {
715             --d->cursor;
716             d->del( TRUE );
717     }
718     d->finishChange( priorState );
719 }
720
721 /*!
722     If no text is selected, deletes the character to the right of the
723     text cursor. If any text is selected, the cursor is moved to the
724     beginning of the selected text and the selected text is deleted.
725
726     \sa backspace()
727 */
728
729 void SecQLineEdit::del()
730 {
731     int priorState = d->undoState;
732     if ( d->hasSelectedText() ) {
733         d->removeSelectedText();
734     } else {
735         int n = d->textLayout.nextCursorPosition( d->cursor ) - d->cursor;
736         while ( n-- )
737             d->del();
738     }
739     d->finishChange( priorState );
740 }
741
742 /*!
743     Moves the text cursor to the beginning of the line unless it is
744     already there. If \a mark is TRUE, text is selected towards the
745     first position; otherwise, any selected text is unselected if the
746     cursor is moved.
747
748     \sa end()
749 */
750
751 void SecQLineEdit::home( bool mark )
752 {
753     d->moveCursor( 0, mark );
754 }
755
756 /*!
757     Moves the text cursor to the end of the line unless it is already
758     there. If \a mark is TRUE, text is selected towards the last
759     position; otherwise, any selected text is unselected if the cursor
760     is moved.
761
762     \sa home()
763 */
764
765 void SecQLineEdit::end( bool mark )
766 {
767     d->moveCursor( d->text.length(), mark );
768 }
769
770
771 /*!
772     \property SecQLineEdit::modified
773     \brief whether the line edit's contents has been modified by the user
774
775     The modified flag is never read by SecQLineEdit; it has a default value
776     of FALSE and is changed to TRUE whenever the user changes the line
777     edit's contents.
778
779     This is useful for things that need to provide a default value but
780     do not start out knowing what the default should be (perhaps it
781     depends on other fields on the form). Start the line edit without
782     the best default, and when the default is known, if modified()
783     returns FALSE (the user hasn't entered any text), insert the
784     default value.
785
786     Calling clearModified() or setText() resets the modified flag to
787     FALSE.
788 */
789
790 bool SecQLineEdit::isModified() const
791 {
792     return d->modified;
793 }
794
795 /*!
796     Resets the modified flag to FALSE.
797
798     \sa isModified()
799 */
800 void SecQLineEdit::clearModified()
801 {
802     d->modified = FALSE;
803     d->history.clear();
804     d->undoState = 0;
805 }
806
807 /*!
808   \obsolete
809   \property SecQLineEdit::edited
810   \brief whether the line edit has been edited. Use modified instead.
811 */
812 bool SecQLineEdit::edited() const { return d->modified; }
813 void SecQLineEdit::setEdited( bool on ) { d->modified = on; }
814
815 /*!
816     \obsolete
817     \property SecQLineEdit::hasMarkedText
818     \brief whether part of the text has been selected by the user. Use hasSelectedText instead.
819 */
820
821 /*!
822     \property SecQLineEdit::hasSelectedText
823     \brief whether there is any text selected
824
825     hasSelectedText() returns TRUE if some or all of the text has been
826     selected by the user; otherwise returns FALSE.
827
828     \sa selectedText()
829 */
830
831
832 bool SecQLineEdit::hasSelectedText() const
833 {
834     return d->hasSelectedText();
835 }
836
837 /*!
838   \obsolete
839   \property SecQLineEdit::markedText
840   \brief the text selected by the user. Use selectedText instead.
841 */
842
843 /*!
844     \property SecQLineEdit::selectedText
845     \brief the selected text
846
847     If there is no selected text this property's value is
848     QString::null.
849
850     \sa hasSelectedText()
851 */
852
853 SecQString SecQLineEdit::selectedText() const
854 {
855     if ( d->hasSelectedText() )
856         return d->text.mid( d->selstart, d->selend - d->selstart );
857     return SecQString::null;
858 }
859
860 /*!
861     selectionStart() returns the index of the first selected character in the
862     line edit or -1 if no text is selected.
863
864     \sa selectedText()
865 */
866
867 int SecQLineEdit::selectionStart() const
868 {
869     return d->hasSelectedText() ? d->selstart : -1;
870 }
871
872
873 /*!
874     Selects text from position \a start and for \a length characters.
875
876     \sa deselect() selectAll()
877 */
878
879 void SecQLineEdit::setSelection( int start, int length )
880 {
881     if ( start < 0 || start > (int)d->text.length() || length < 0 ) {
882         d->selstart = d->selend = 0;
883     } else {
884         d->selstart = start;
885         d->selend = QMIN( start + length, (int)d->text.length() );
886         d->cursor = d->selend;
887     }
888     update();
889 }
890
891
892 /*!
893     \property SecQLineEdit::undoAvailable
894     \brief whether undo is available
895 */
896
897 bool SecQLineEdit::isUndoAvailable() const
898 {
899     return d->isUndoAvailable();
900 }
901
902 /*!
903     \property SecQLineEdit::redoAvailable
904     \brief whether redo is available
905 */
906
907 bool SecQLineEdit::isRedoAvailable() const
908 {
909     return d->isRedoAvailable();
910 }
911
912 /*!
913     Selects all the text (i.e. highlights it) and moves the cursor to
914     the end. This is useful when a default value has been inserted
915     because if the user types before clicking on the widget, the
916     selected text will be deleted.
917
918     \sa setSelection() deselect()
919 */
920
921 void SecQLineEdit::selectAll()
922 {
923     d->selstart = d->selend = d->cursor = 0;
924     d->moveCursor( d->text.length(), TRUE );
925 }
926
927 /*!
928     Deselects any selected text.
929
930     \sa setSelection() selectAll()
931 */
932
933 void SecQLineEdit::deselect()
934 {
935     d->deselect();
936     d->finishChange();
937 }
938
939
940 /*!
941     Deletes any selected text, inserts \a newText and sets it as the
942     new contents of the line edit.
943 */
944 void SecQLineEdit::insert( const SecQString &newText )
945 {
946 //     q->resetInputContext(); //#### FIX ME IN QT
947     int priorState = d->undoState;
948     d->removeSelectedText();
949     d->insert( newText );
950     d->finishChange( priorState );
951 }
952
953 /*!
954     Clears the contents of the line edit.
955 */
956 void SecQLineEdit::clear()
957 {
958     int priorState = d->undoState;
959     resetInputContext();
960     d->selstart = 0;
961     d->selend = d->text.length();
962     d->removeSelectedText();
963     d->separate();
964     d->finishChange( priorState );
965 }
966
967 /*!
968     Undoes the last operation if undo is \link
969     SecQLineEdit::undoAvailable available\endlink. Deselects any current
970     selection, and updates the selection start to the current cursor
971     position.
972 */
973 void SecQLineEdit::undo()
974 {
975 #ifndef SECURE_NO_UNDO
976     resetInputContext();
977     d->undo();
978     d->finishChange( -1, FALSE );
979 #endif
980 }
981
982 /*!
983     Redoes the last operation if redo is \link
984     SecQLineEdit::redoAvailable available\endlink.
985 */
986 void SecQLineEdit::redo()
987 {
988 #ifndef SECURE_NO_UNDO
989     resetInputContext();
990     d->redo();
991     d->finishChange();
992 #endif
993 }
994
995
996 /*!
997     \property SecQLineEdit::readOnly
998     \brief whether the line edit is read only.
999
1000     In read-only mode, the user can still copy the text to the
1001     clipboard (if echoMode() is \c Normal), but cannot edit it.
1002
1003     SecQLineEdit does not show a cursor in read-only mode.
1004
1005     \sa setEnabled()
1006 */
1007
1008 bool SecQLineEdit::isReadOnly() const
1009 {
1010     return d->readOnly;
1011 }
1012
1013 void SecQLineEdit::setReadOnly( bool enable )
1014 {
1015     d->readOnly = enable;
1016 #ifndef QT_NO_CURSOR
1017     setCursor( enable ? arrowCursor : ibeamCursor );
1018 #endif
1019     update();
1020 }
1021
1022
1023 #ifndef QT_NO_CLIPBOARD
1024 /*!
1025     Copies the selected text to the clipboard and deletes it, if there
1026     is any, and if echoMode() is \c Normal.
1027
1028     \sa copy() paste() setValidator()
1029 */
1030
1031 void SecQLineEdit::cut()
1032 {
1033     if ( hasSelectedText() ) {
1034         copy();
1035         del();
1036     }
1037 }
1038
1039
1040 /*!
1041     Copies the selected text to the clipboard, if there is any, and if
1042     echoMode() is \c Normal.
1043
1044     \sa cut() paste()
1045 */
1046
1047 void SecQLineEdit::copy() const
1048 {
1049     d->copy();
1050 }
1051
1052 /*!
1053     Inserts the clipboard's text at the cursor position, deleting any
1054     selected text, providing the line edit is not \link
1055     SecQLineEdit::readOnly read-only\endlink.
1056
1057     \sa copy() cut()
1058 */
1059
1060 void SecQLineEdit::paste()
1061 {
1062     d->removeSelectedText();
1063     insert( QApplication::clipboard()->text( QClipboard::Clipboard ) );
1064 }
1065
1066 void SecQLineEditPrivate::copy( bool clipboard ) const
1067 {
1068 #ifndef SECURE
1069     QString t = q->selectedText();
1070     if ( !t.isEmpty() && echoMode == SecQLineEdit::Normal ) {
1071         q->disconnect( QApplication::clipboard(), SIGNAL(selectionChanged()), q, 0);
1072         QApplication::clipboard()->setText( t, clipboard ? QClipboard::Clipboard : QClipboard::Selection );
1073         q->connect( QApplication::clipboard(), SIGNAL(selectionChanged()),
1074                  q, SLOT(clipboardChanged()) );
1075     }
1076 #endif
1077 }
1078
1079 #endif // !QT_NO_CLIPBOARD
1080
1081 /*!\reimp
1082 */
1083
1084 void SecQLineEdit::resizeEvent( QResizeEvent *e )
1085 {
1086     QFrame::resizeEvent( e );
1087 }
1088
1089 /*! \reimp
1090 */
1091 bool SecQLineEdit::event( QEvent * e )
1092 {
1093     if ( e->type() == QEvent::AccelOverride && !d->readOnly ) {
1094         QKeyEvent* ke = (QKeyEvent*) e;
1095         if ( ke->state() == NoButton || ke->state() == ShiftButton
1096              || ke->state() == Keypad ) {
1097             if ( ke->key() < Key_Escape ) {
1098                 ke->accept();
1099             } else if ( ke->state() == NoButton
1100                         || ke->state() == ShiftButton ) {
1101                 switch ( ke->key() ) {
1102                 case Key_Delete:
1103                 case Key_Home:
1104                 case Key_End:
1105                 case Key_Backspace:
1106                 case Key_Left:
1107                 case Key_Right:
1108                     ke->accept();
1109                 default:
1110                     break;
1111                 }
1112             }
1113         } else if ( ke->state() & ControlButton ) {
1114             switch ( ke->key() ) {
1115 // Those are too frequently used for application functionality
1116 /*          case Key_A:
1117             case Key_B:
1118             case Key_D:
1119             case Key_E:
1120             case Key_F:
1121             case Key_H:
1122             case Key_K:
1123 */
1124             case Key_C:
1125             case Key_V:
1126             case Key_X:
1127             case Key_Y:
1128             case Key_Z:
1129             case Key_Left:
1130             case Key_Right:
1131 #if defined (Q_WS_WIN)
1132             case Key_Insert:
1133             case Key_Delete:
1134 #endif
1135                 ke->accept();
1136             default:
1137                 break;
1138             }
1139         }
1140     } else if ( e->type() == QEvent::Timer ) {
1141         // should be timerEvent, is here for binary compatibility
1142         int timerId = ((QTimerEvent*)e)->timerId();
1143         if ( timerId == d->cursorTimer ) {
1144             if(!hasSelectedText() || style().styleHint( QStyle::SH_BlinkCursorWhenTextSelected ))
1145                 d->setCursorVisible( !d->cursorVisible );
1146         } else if ( timerId == d->tripleClickTimer ) {
1147             killTimer( d->tripleClickTimer );
1148             d->tripleClickTimer = 0;
1149         }
1150     }
1151     return QWidget::event( e );
1152 }
1153
1154 /*! \reimp
1155 */
1156 void SecQLineEdit::mousePressEvent( QMouseEvent* e )
1157 {
1158     if ( e->button() == RightButton )
1159         return;
1160     if ( d->tripleClickTimer && ( e->pos() - d->tripleClick ).manhattanLength() <
1161          QApplication::startDragDistance() ) {
1162         selectAll();
1163         return;
1164     }
1165     bool mark = e->state() & ShiftButton;
1166     int cursor = d->xToPos( e->pos().x() );
1167     d->moveCursor( cursor, mark );
1168 }
1169
1170 /*! \reimp
1171 */
1172 void SecQLineEdit::mouseMoveEvent( QMouseEvent * e )
1173 {
1174
1175 #ifndef QT_NO_CURSOR
1176     if ( ( e->state() & MouseButtonMask ) == 0 ) {
1177         if ( !d->readOnly )
1178           setCursor( ( d->inSelection( e->pos().x() ) ? arrowCursor : ibeamCursor ) );
1179     }
1180 #endif
1181
1182     if ( e->state() & LeftButton ) {
1183       d->moveCursor( d->xToPos( e->pos().x() ), TRUE );
1184     }
1185 }
1186
1187 /*! \reimp
1188 */
1189 void SecQLineEdit::mouseReleaseEvent( QMouseEvent* e )
1190 {
1191 #ifndef QT_NO_CLIPBOARD
1192     if (QApplication::clipboard()->supportsSelection() ) {
1193         if ( e->button() == LeftButton ) {
1194             d->copy( FALSE );
1195         } else if ( !d->readOnly && e->button() == MidButton ) {
1196             d->deselect();
1197             insert( QApplication::clipboard()->text( QClipboard::Selection ) );
1198         }
1199     }
1200 #endif
1201 }
1202
1203 /*! \reimp
1204 */
1205 void SecQLineEdit::mouseDoubleClickEvent( QMouseEvent* e )
1206 {
1207     if ( e->button() == Qt::LeftButton ) {
1208         deselect();
1209         d->cursor = d->xToPos( e->pos().x() );
1210         d->cursor = d->textLayout.previousCursorPosition( d->cursor, QTextLayout::SkipWords );
1211         // ## text layout should support end of words.
1212         int end = d->textLayout.nextCursorPosition( d->cursor, QTextLayout::SkipWords );
1213         while ( end > d->cursor && d->text[end-1].isSpace() )
1214             --end;
1215         d->moveCursor( end, TRUE );
1216         d->tripleClickTimer = startTimer( QApplication::doubleClickInterval() );
1217         d->tripleClick = e->pos();
1218     }
1219 }
1220
1221 /*!
1222     \fn void  SecQLineEdit::returnPressed()
1223
1224     This signal is emitted when the Return or Enter key is pressed.
1225 */
1226
1227 /*!
1228     Converts key press event \a e into a line edit action.
1229
1230     If Return or Enter is pressed the signal returnPressed() is
1231     emitted.
1232
1233     The default key bindings are listed in the \link #desc detailed
1234     description.\endlink
1235 */
1236
1237 void SecQLineEdit::keyPressEvent( QKeyEvent * e )
1238 {
1239     d->setCursorVisible( TRUE );
1240     if ( e->key() == Key_Enter || e->key() == Key_Return ) {
1241         emit returnPressed();
1242         e->ignore();
1243         return;
1244     }
1245     if ( !d->readOnly ) {
1246         QString t = e->text();
1247         if ( !t.isEmpty() && (!e->ascii() || e->ascii()>=32) &&
1248              e->key() != Key_Delete &&
1249              e->key() != Key_Backspace ) {
1250 #ifdef Q_WS_X11
1251             extern bool qt_hebrew_keyboard_hack;
1252             if ( qt_hebrew_keyboard_hack ) {
1253                 // the X11 keyboard layout is broken and does not reverse
1254                 // braces correctly. This is a hack to get halfway correct
1255                 // behaviour
1256                 if ( d->isRightToLeft() ) {
1257                     QChar *c = (QChar *)t.unicode();
1258                     int l = t.length();
1259                     while( l-- ) {
1260                         if ( c->mirrored() )
1261                             *c = c->mirroredChar();
1262                         c++;
1263                     }
1264                 }
1265             }
1266 #endif
1267             insert( t );
1268             return;
1269         }
1270     }
1271     bool unknown = FALSE;
1272     if ( e->state() & ControlButton ) {
1273         switch ( e->key() ) {
1274         case Key_A:
1275 #if defined(Q_WS_X11)
1276             home( e->state() & ShiftButton );
1277 #else
1278             selectAll();
1279 #endif
1280             break;
1281         case Key_B:
1282             cursorForward( e->state() & ShiftButton, -1 );
1283             break;
1284 #ifndef QT_NO_CLIPBOARD
1285         case Key_C:
1286             copy();
1287             break;
1288 #endif
1289         case Key_D:
1290             if ( !d->readOnly ) {
1291                 del();
1292             }
1293             break;
1294         case Key_E:
1295             end( e->state() & ShiftButton );
1296             break;
1297         case Key_F:
1298             cursorForward( e->state() & ShiftButton, 1 );
1299             break;
1300         case Key_H:
1301             if ( !d->readOnly ) {
1302                 backspace();
1303             }
1304             break;
1305         case Key_K:
1306             if ( !d->readOnly ) {
1307                 int priorState = d->undoState;
1308                 d->deselect();
1309                 while ( d->cursor < (int) d->text.length() )
1310                     d->del();
1311                 d->finishChange( priorState );
1312             }
1313             break;
1314 #if defined(Q_WS_X11)
1315         case Key_U:
1316             if ( !d->readOnly )
1317                 clear();
1318             break;
1319 #endif
1320 #ifndef QT_NO_CLIPBOARD
1321         case Key_V:
1322             if ( !d->readOnly )
1323                 paste();
1324             break;
1325         case Key_X:
1326             if ( !d->readOnly && d->hasSelectedText() && echoMode() == Normal ) {
1327                 copy();
1328                 del();
1329             }
1330             break;
1331 #if defined (Q_WS_WIN)
1332         case Key_Insert:
1333             copy();
1334             break;
1335 #endif
1336 #endif
1337         case Key_Delete:
1338             if ( !d->readOnly ) {
1339                 cursorWordForward( TRUE );
1340                 del();
1341             }
1342             break;
1343         case Key_Backspace:
1344             if ( !d->readOnly ) {
1345                 cursorWordBackward( TRUE );
1346                 del();
1347             }
1348             break;
1349         case Key_Right:
1350         case Key_Left:
1351             if ( d->isRightToLeft() == (e->key() == Key_Right) ) {
1352                 if ( echoMode() == Normal )
1353                     cursorWordBackward( e->state() & ShiftButton );
1354                 else
1355                     home( e->state() & ShiftButton );
1356             } else {
1357                 if ( echoMode() == Normal )
1358                     cursorWordForward( e->state() & ShiftButton );
1359                 else
1360                     end( e->state() & ShiftButton );
1361             }
1362             break;
1363         case Key_Z:
1364             if ( !d->readOnly ) {
1365                 if(e->state() & ShiftButton)
1366                     redo();
1367                 else
1368                     undo();
1369             }
1370             break;
1371         case Key_Y:
1372             if ( !d->readOnly )
1373                 redo();
1374             break;
1375         default:
1376             unknown = TRUE;
1377         }
1378     } else { // ### check for *no* modifier
1379         switch ( e->key() ) {
1380         case Key_Shift:
1381             // ### TODO
1382             break;
1383         case Key_Left:
1384         case Key_Right: {
1385             int step =  (d->isRightToLeft() == (e->key() == Key_Right)) ? -1 : 1;
1386             cursorForward( e->state() & ShiftButton, step );
1387         }
1388         break;
1389         case Key_Backspace:
1390             if ( !d->readOnly ) {
1391                 backspace();
1392             }
1393             break;
1394         case Key_Home:
1395 #ifdef Q_WS_MACX
1396         case Key_Up:
1397 #endif
1398             home( e->state() & ShiftButton );
1399             break;
1400         case Key_End:
1401 #ifdef Q_WS_MACX
1402         case Key_Down:
1403 #endif
1404             end( e->state() & ShiftButton );
1405             break;
1406         case Key_Delete:
1407             if ( !d->readOnly ) {
1408 #if defined (Q_WS_WIN)
1409                 if ( e->state() & ShiftButton ) {
1410                     cut();
1411                     break;
1412                 }
1413 #endif
1414                 del();
1415             }
1416             break;
1417 #if defined (Q_WS_WIN)
1418         case Key_Insert:
1419             if ( !d->readOnly && e->state() & ShiftButton )
1420                 paste();
1421             else
1422                 unknown = TRUE;
1423             break;
1424 #endif
1425         case Key_F14: // Undo key on Sun keyboards
1426             if ( !d->readOnly )
1427                 undo();
1428             break;
1429 #ifndef QT_NO_CLIPBOARD
1430         case Key_F16: // Copy key on Sun keyboards
1431             copy();
1432             break;
1433         case Key_F18: // Paste key on Sun keyboards
1434             if ( !d->readOnly )
1435                 paste();
1436             break;
1437         case Key_F20: // Cut key on Sun keyboards
1438             if ( !d->readOnly && hasSelectedText() && echoMode() == Normal ) {
1439                 copy();
1440                 del();
1441             }
1442             break;
1443 #endif
1444         default:
1445             unknown = TRUE;
1446         }
1447     }
1448     if ( e->key() == Key_Direction_L )
1449         d->direction = QChar::DirL;
1450     else if ( e->key() == Key_Direction_R )
1451         d->direction = QChar::DirR;
1452
1453     if ( unknown )
1454         e->ignore();
1455 }
1456
1457 /*! \reimp
1458  */
1459 void SecQLineEdit::imStartEvent( QIMEvent *e )
1460 {
1461     if ( d->readOnly ) {
1462         e->ignore();
1463         return;
1464     }
1465     d->removeSelectedText();
1466     d->updateMicroFocusHint();
1467     d->imstart = d->imend = d->imselstart = d->imselend = d->cursor;
1468 }
1469
1470 /*! \reimp
1471  */
1472 void SecQLineEdit::imComposeEvent( QIMEvent *e )
1473 {
1474     if ( d->readOnly ) {
1475         e->ignore();
1476     } else {
1477         d->text.replace( d->imstart, d->imend - d->imstart, e->text() );
1478         d->imend = d->imstart + e->text().length();
1479         d->imselstart = d->imstart + e->cursorPos();
1480         d->imselend = d->imselstart + e->selectionLength();
1481         d->cursor = e->selectionLength() ? d->imend : d->imselend;
1482         d->updateTextLayout();
1483         update();
1484     }
1485 }
1486
1487 /*! \reimp
1488  */
1489 void SecQLineEdit::imEndEvent( QIMEvent *e )
1490 {
1491     if ( d->readOnly ) {
1492         e->ignore();
1493     } else {
1494         d->text.remove( d->imstart, d->imend - d->imstart );
1495         d->cursor = d->imselstart = d->imselend = d->imend = d->imstart;
1496         d->textDirty = TRUE;
1497         insert( e->text() );
1498     }
1499 }
1500
1501 /*!\reimp
1502 */
1503
1504 void SecQLineEdit::focusInEvent( QFocusEvent* e )
1505 {
1506     if ( e->reason() == QFocusEvent::Tab ||
1507          e->reason() == QFocusEvent::Backtab  ||
1508          e->reason() == QFocusEvent::Shortcut )
1509         selectAll();
1510     if ( !d->cursorTimer ) {
1511         int cft = QApplication::cursorFlashTime();
1512         d->cursorTimer = cft ? startTimer( cft/2 ) : -1;
1513     }
1514     d->updateMicroFocusHint();
1515 }
1516
1517 /*!\reimp
1518 */
1519
1520 void SecQLineEdit::focusOutEvent( QFocusEvent* e )
1521 {
1522     if ( e->reason() != QFocusEvent::ActiveWindow &&
1523          e->reason() != QFocusEvent::Popup )
1524         deselect();
1525     d->setCursorVisible( FALSE );
1526     if ( d->cursorTimer > 0 )
1527         killTimer( d->cursorTimer );
1528     d->cursorTimer = 0;
1529     emit lostFocus();
1530 }
1531
1532 /*!\reimp
1533 */
1534 void SecQLineEdit::drawContents( QPainter *p )
1535 {
1536     const QColorGroup& cg = colorGroup();
1537     QRect cr = contentsRect();
1538     QFontMetrics fm = fontMetrics();
1539     QRect lineRect( cr.x() + innerMargin, cr.y() + (cr.height() - fm.height() + 1) / 2,
1540                     cr.width() - 2*innerMargin, fm.height() );
1541     QBrush bg = QBrush( paletteBackgroundColor() );
1542     if ( paletteBackgroundPixmap() )
1543         bg = QBrush( cg.background(), *paletteBackgroundPixmap() );
1544     else if ( !isEnabled() )
1545         bg = cg.brush( QColorGroup::Background );
1546     p->save();
1547     p->setClipRegion( QRegion(cr) - lineRect );
1548     p->fillRect( cr, bg );
1549     p->restore();
1550     QSharedDoubleBuffer buffer( p, lineRect.x(), lineRect.y(),
1551                                 lineRect.width(), lineRect.height(),
1552                                 hasFocus() ? QSharedDoubleBuffer::Force : 0 );
1553     p = buffer.painter();
1554     p->fillRect( lineRect, bg );
1555
1556     // locate cursor position
1557     int cix = 0;
1558     QTextItem ci = d->textLayout.findItem( d->cursor );
1559     if ( ci.isValid() ) {
1560         if ( d->cursor != (int)d->text.length() && d->cursor == ci.from() + ci.length()
1561              && ci.isRightToLeft() != d->isRightToLeft() )
1562             ci = d->textLayout.findItem( d->cursor + 1 );
1563         cix = ci.x() + ci.cursorToX( d->cursor - ci.from() );
1564     }
1565
1566     // horizontal scrolling
1567     int minLB = QMAX( 0, -fm.minLeftBearing() );
1568     int minRB = QMAX( 0, -fm.minRightBearing() );
1569     int widthUsed = d->textLayout.widthUsed() + 1 + minRB;
1570     if ( (minLB + widthUsed) <=  lineRect.width() ) {
1571         switch ( d->visualAlignment() ) {
1572         case AlignRight:
1573             d->hscroll = widthUsed - lineRect.width();
1574             break;
1575         case AlignHCenter:
1576             d->hscroll = ( widthUsed - lineRect.width() ) / 2;
1577             break;
1578         default:
1579             d->hscroll = 0;
1580             break;
1581         }
1582         d->hscroll -= minLB;
1583     } else if ( cix - d->hscroll >= lineRect.width() ) {
1584         d->hscroll = cix - lineRect.width() + 1;
1585     } else if ( cix - d->hscroll < 0 ) {
1586         d->hscroll = cix;
1587     } else if ( widthUsed - d->hscroll < lineRect.width() ) {
1588         d->hscroll = widthUsed - lineRect.width() + 1;
1589     }
1590     // the y offset is there to keep the baseline constant in case we have script changes in the text.
1591     QPoint topLeft = lineRect.topLeft() - QPoint(d->hscroll, d->ascent-fm.ascent());
1592
1593     // draw text, selections and cursors
1594     p->setPen( cg.text() );
1595     bool supressCursor = d->readOnly, hasRightToLeft = d->isRightToLeft();
1596     int textflags = 0;
1597     if ( font().underline() )
1598         textflags |= Qt::Underline;
1599     if ( font().strikeOut() )
1600         textflags |= Qt::StrikeOut;
1601     if ( font().overline() )
1602         textflags |= Qt::Overline;
1603
1604     for ( int i = 0; i < d->textLayout.numItems(); i++ ) {
1605         QTextItem ti = d->textLayout.itemAt( i );
1606         hasRightToLeft |= ti.isRightToLeft();
1607         int tix = topLeft.x() + ti.x();
1608         int first = ti.from();
1609         int last = ti.from() + ti.length() - 1;
1610
1611         // text and selection
1612         if ( d->selstart < d->selend && (last >= d->selstart && first < d->selend ) ) {
1613             QRect highlight = QRect( QPoint( tix + ti.cursorToX( QMAX( d->selstart - first, 0 ) ),
1614                                              lineRect.top() ),
1615                                      QPoint( tix + ti.cursorToX( QMIN( d->selend - first, last - first + 1 ) ) - 1,
1616                                              lineRect.bottom() ) ).normalize();
1617             p->save();
1618             p->setClipRegion( QRegion( lineRect ) - highlight, QPainter::CoordPainter );
1619             p->drawTextItem( topLeft, ti, textflags );
1620             p->setClipRect( lineRect & highlight, QPainter::CoordPainter );
1621             p->fillRect( highlight, cg.highlight() );
1622             p->setPen( cg.highlightedText() );
1623             p->drawTextItem( topLeft, ti, textflags );
1624             p->restore();
1625         } else {
1626             p->drawTextItem( topLeft, ti, textflags );
1627         }
1628
1629         // input method edit area
1630         if ( d->imstart < d->imend && (last >= d->imstart && first < d->imend ) ) {
1631             QRect highlight = QRect( QPoint( tix + ti.cursorToX( QMAX( d->imstart - first, 0 ) ), lineRect.top() ),
1632                               QPoint( tix + ti.cursorToX( QMIN( d->imend - first, last - first + 1 ) )-1, lineRect.bottom() ) ).normalize();
1633             p->save();
1634             p->setClipRect( lineRect & highlight, QPainter::CoordPainter );
1635
1636             int h1, s1, v1, h2, s2, v2;
1637             cg.color( QColorGroup::Base ).hsv( &h1, &s1, &v1 );
1638             cg.color( QColorGroup::Background ).hsv( &h2, &s2, &v2 );
1639             QColor imCol;
1640             imCol.setHsv( h1, s1, ( v1 + v2 ) / 2 );
1641             p->fillRect( highlight, imCol );
1642             p->drawTextItem( topLeft, ti, textflags );
1643             p->restore();
1644         }
1645
1646         // input method selection
1647         if ( d->imselstart < d->imselend && (last >= d->imselstart && first < d->imselend ) ) {
1648             QRect highlight = QRect( QPoint( tix + ti.cursorToX( QMAX( d->imselstart - first, 0 ) ), lineRect.top() ),
1649                               QPoint( tix + ti.cursorToX( QMIN( d->imselend - first, last - first + 1 ) )-1, lineRect.bottom() ) ).normalize();
1650             p->save();
1651             p->setClipRect( lineRect & highlight, QPainter::CoordPainter );
1652             p->fillRect( highlight, cg.text() );
1653             p->setPen( paletteBackgroundColor() );
1654             p->drawTextItem( topLeft, ti, textflags );
1655             p->restore();
1656         }
1657     }
1658
1659     // draw cursor
1660     if ( d->cursorVisible && !supressCursor ) {
1661         QPoint from( topLeft.x() + cix, lineRect.top() );
1662         QPoint to = from + QPoint( 0, lineRect.height() );
1663         p->drawLine( from, to );
1664         if ( hasRightToLeft ) {
1665             to = from + QPoint( (ci.isRightToLeft()?-2:2), 2 );
1666             p->drawLine( from, to );
1667             from.ry() += 4;
1668             p->drawLine( from, to );
1669         }
1670     }
1671     buffer.end();
1672 }
1673
1674
1675 enum { IdUndo, IdRedo, IdSep1, IdCut, IdCopy, IdPaste, IdClear, IdSep2, IdSelectAll };
1676
1677
1678 /*!\reimp
1679 */
1680 void SecQLineEdit::contextMenuEvent( QContextMenuEvent * e )
1681 {
1682 #ifndef QT_NO_POPUPMENU
1683     d->separate();
1684
1685     QGuardedPtr<QPopupMenu> popup = createPopupMenu();
1686     QGuardedPtr<SecQLineEdit> that = this;
1687     QPoint pos = e->reason() == QContextMenuEvent::Mouse ? e->globalPos() :
1688                  mapToGlobal( QPoint(e->pos().x(), 0) ) + QPoint( width() / 2, height() / 2 );
1689     int r = popup->exec( pos );
1690     delete (QPopupMenu*)popup;
1691     if ( that && d->menuId ) {
1692         switch ( d->menuId - r ) {
1693         case IdClear: clear(); break;
1694         case IdSelectAll: selectAll(); break;
1695 #ifndef SECURE_NO_UNDO
1696         case IdUndo: undo(); break;
1697         case IdRedo: redo(); break;
1698 #endif
1699 #ifndef QT_NO_CLIPBOARD
1700         case IdCut: cut(); break;
1701         case IdCopy: copy(); break;
1702         case IdPaste: paste(); break;
1703 #endif
1704         default:
1705             ; // nothing selected or lineedit destroyed. Be careful.
1706         }
1707     }
1708 #endif //QT_NO_POPUPMENU
1709 }
1710
1711 /*!
1712     This function is called to create the popup menu which is shown
1713     when the user clicks on the line edit with the right mouse button.
1714     If you want to create a custom popup menu, reimplement this
1715     function and return the popup menu you create. The popup menu's
1716     ownership is transferred to the caller.
1717 */
1718
1719 QPopupMenu *SecQLineEdit::createPopupMenu()
1720 {
1721 #ifndef QT_NO_POPUPMENU
1722     QPopupMenu *popup = new QPopupMenu( this, "qt_edit_menu" );
1723     int id = d->menuId = popup->insertItem( QString( "&Undo" ) + ACCEL_KEY( Z ) );
1724     popup->insertItem( QString ("&Redo") + ACCEL_KEY( Y ) );
1725     popup->insertSeparator();
1726     popup->insertItem( QString ("Cu&t") + ACCEL_KEY( X ) );
1727     popup->insertItem( QString ("&Copy") + ACCEL_KEY( C ) );
1728     popup->insertItem( QString ("&Paste") + ACCEL_KEY( V ) );
1729     popup->insertItem( QString ("Clear") );
1730     popup->insertSeparator();
1731     popup->insertItem( QString ("Select All")
1732 #ifndef Q_WS_X11
1733     + ACCEL_KEY( A )
1734 #endif
1735         );
1736 #ifndef SECURE_NO_UNDO
1737     popup->setItemEnabled( id - IdUndo, d->isUndoAvailable() );
1738     popup->setItemEnabled( id - IdRedo, d->isRedoAvailable() );
1739 #else
1740     popup->setItemVisible( id - IdUndo, FALSE );
1741     popup->setItemVisible( id - IdRedo, FALSE );
1742 #endif /* SECURE_NO_UNDO */
1743
1744 #ifndef QT_NO_CLIPBOARD
1745     popup->setItemEnabled( id - IdCut, !d->readOnly && d->hasSelectedText() );
1746     popup->setItemEnabled( id - IdCopy, d->hasSelectedText() );
1747     popup->setItemEnabled( id - IdPaste, !d->readOnly && !QApplication::clipboard()->text().isEmpty() );
1748 #else
1749     popup->setItemVisible( id - IdCut, FALSE );
1750     popup->setItemVisible( id - IdCopy, FALSE );
1751     popup->setItemVisible( id - IdPaste, FALSE );
1752 #endif
1753     popup->setItemEnabled( id - IdClear, !d->readOnly && !d->text.isEmpty() );
1754     popup->setItemEnabled( id - IdSelectAll, !d->text.isEmpty() && !d->allSelected() );
1755     return popup;
1756 #else
1757     return 0;
1758 #endif
1759 }
1760
1761
1762 /*! \reimp */
1763 void SecQLineEdit::windowActivationChange( bool b )
1764 {
1765     //### remove me with WHighlightSelection attribute
1766     if ( palette().active() != palette().inactive() )
1767         update();
1768     QWidget::windowActivationChange( b );
1769 }
1770
1771 /*! \reimp */
1772
1773 void SecQLineEdit::setPalette( const QPalette & p )
1774 {
1775     //### remove me with WHighlightSelection attribute
1776     QWidget::setPalette( p );
1777     update();
1778 }
1779
1780 /*!
1781   \obsolete
1782   \fn void SecQLineEdit::repaintArea( int from, int to )
1783   Repaints all characters from \a from to \a to. If cursorPos is
1784   between from and to, ensures that cursorPos is visible.
1785 */
1786
1787 /*! \reimp
1788  */
1789 void SecQLineEdit::setFont( const QFont & f )
1790 {
1791     QWidget::setFont( f );
1792     d->updateTextLayout();
1793 }
1794
1795
1796 void SecQLineEdit::clipboardChanged()
1797 {
1798 }
1799
1800 void SecQLineEditPrivate::init( const SecQString& txt )
1801 {
1802 #ifndef QT_NO_CURSOR
1803     q->setCursor( readOnly ? arrowCursor : ibeamCursor );
1804 #endif
1805     q->setFocusPolicy( QWidget::StrongFocus );
1806     q->setInputMethodEnabled( TRUE );
1807     //   Specifies that this widget can use more, but is able to survive on
1808     //   less, horizontal space; and is fixed vertically.
1809     q->setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ) );
1810     q->setBackgroundMode( PaletteBase );
1811     q->setKeyCompression( TRUE );
1812     q->setMouseTracking( TRUE );
1813     q->setAcceptDrops( TRUE );
1814     q->setFrame( TRUE );
1815     text = txt;
1816     updateTextLayout();
1817     cursor = text.length();
1818 }
1819
1820 void SecQLineEditPrivate::updateTextLayout()
1821 {
1822     // replace all non-printable characters with spaces (to avoid
1823     // drawing boxes when using fonts that don't have glyphs for such
1824     // characters)
1825     const QString &displayText = q->displayText();
1826     QString str(displayText.unicode(), displayText.length());
1827     QChar* uc = (QChar*)str.unicode();
1828     for (int i = 0; i < (int)str.length(); ++i) {
1829         if (! uc[i].isPrint())
1830             uc[i] = QChar(0x0020);
1831     }
1832     textLayout.setText( str, q->font() );
1833     // ### want to do textLayout.setRightToLeft( text.isRightToLeft() );
1834     textLayout.beginLayout( QTextLayout::SingleLine );
1835     textLayout.beginLine( INT_MAX );
1836     while ( !textLayout.atEnd() )
1837         textLayout.addCurrentItem();
1838     ascent = 0;
1839     textLayout.endLine(0, 0, Qt::AlignLeft, &ascent);
1840 }
1841
1842 int SecQLineEditPrivate::xToPos( int x, QTextItem::CursorPosition betweenOrOn ) const
1843 {
1844     x-= q->contentsRect().x() - hscroll + innerMargin;
1845     for ( int i = 0; i < textLayout.numItems(); ++i ) {
1846         QTextItem ti = textLayout.itemAt( i );
1847         QRect tir = ti.rect();
1848         if ( x >= tir.left() && x <= tir.right() )
1849             return ti.xToCursor( x - tir.x(), betweenOrOn ) + ti.from();
1850     }
1851     return x < 0 ? 0 : text.length();
1852 }
1853
1854
1855 QRect SecQLineEditPrivate::cursorRect() const
1856 {
1857     QRect cr = q->contentsRect();
1858     int cix = cr.x() - hscroll + innerMargin;
1859     QTextItem ci = textLayout.findItem( cursor );
1860     if ( ci.isValid() ) {
1861         if ( cursor != (int)text.length() && cursor == ci.from() + ci.length()
1862              && ci.isRightToLeft() != isRightToLeft() )
1863             ci = textLayout.findItem( cursor + 1 );
1864         cix += ci.x() + ci.cursorToX( cursor - ci.from() );
1865     }
1866     int ch = q->fontMetrics().height();
1867     return QRect( cix-4, cr.y() + ( cr.height() -  ch + 1) / 2, 8, ch + 1 );
1868 }
1869
1870 void SecQLineEditPrivate::updateMicroFocusHint()
1871 {
1872     if ( q->hasFocus() ) {
1873         QRect r = cursorRect();
1874         q->setMicroFocusHint( r.x(), r.y(), r.width(), r.height() );
1875     }
1876 }
1877
1878 void SecQLineEditPrivate::moveCursor( int pos, bool mark )
1879 {
1880     if ( pos != cursor )
1881         separate();
1882     bool fullUpdate = mark || hasSelectedText();
1883     if ( mark ) {
1884         int anchor;
1885         if ( selend > selstart && cursor == selstart )
1886             anchor = selend;
1887         else if ( selend > selstart && cursor == selend )
1888             anchor = selstart;
1889         else
1890             anchor = cursor;
1891         selstart = QMIN( anchor, pos );
1892         selend = QMAX( anchor, pos );
1893     } else {
1894         selstart = selend = 0;
1895     }
1896     if ( fullUpdate ) {
1897         cursor = pos;
1898         q->update();
1899     } else {
1900         setCursorVisible( FALSE );
1901         cursor = pos;
1902         setCursorVisible( TRUE );
1903     }
1904     updateMicroFocusHint();
1905     if ( mark ) {
1906         if( !q->style().styleHint( QStyle::SH_BlinkCursorWhenTextSelected ))
1907             setCursorVisible( FALSE );
1908         emit q->selectionChanged();
1909     }
1910 }
1911
1912 void SecQLineEditPrivate::finishChange( int validateFromState, bool setModified )
1913 {
1914     bool lineDirty = selDirty;
1915     if ( textDirty ) {
1916         if ( validateFromState >= 0 ) {
1917 #ifndef SECURE_NO_UNDO
1918             undo( validateFromState );
1919 #endif /* SECURE_NO_UNDO */
1920             history.resize( undoState );
1921             textDirty = setModified = FALSE;
1922         }
1923         updateTextLayout();
1924         updateMicroFocusHint();
1925         lineDirty |= textDirty;
1926         if ( setModified )
1927             modified = TRUE;
1928         if ( textDirty ) {
1929             textDirty = FALSE;
1930             emit q->textChanged( text );
1931         }
1932 #if defined(QT_ACCESSIBILITY_SUPPORT)
1933         QAccessible::updateAccessibility( q, 0, QAccessible::ValueChanged );
1934 #endif
1935     }
1936     if ( selDirty ) {
1937         selDirty = FALSE;
1938         emit q->selectionChanged();
1939     }
1940     if ( lineDirty || !setModified )
1941         q->update();
1942 }
1943
1944 void SecQLineEditPrivate::setText( const SecQString& txt )
1945 {
1946     deselect();
1947     SecQString oldText = text;
1948     text = txt.isEmpty() ? SecQString ("") : txt.left( maxLength );
1949     history.clear();
1950     undoState = 0;
1951     cursor = text.length();
1952     textDirty = 1; // Err on safe side.
1953 }
1954
1955
1956 void SecQLineEditPrivate::setCursorVisible( bool visible )
1957 {
1958     if ( (bool)cursorVisible == visible )
1959         return;
1960     if ( cursorTimer )
1961         cursorVisible = visible;
1962     QRect r = cursorRect();
1963     if ( !q->contentsRect().contains( r ) )
1964         q->update();
1965     else
1966         q->update( r );
1967 }
1968
1969 #ifndef SECURE_NO_UNDO
1970
1971 void SecQLineEditPrivate::addCommand( const Command& cmd )
1972 {
1973     if ( separator && undoState && history[undoState-1].type != Separator ) {
1974         history.resize( undoState + 2 );
1975         history[undoState++] = Command( Separator, 0, 0 );
1976     } else {
1977         history.resize( undoState + 1);
1978     }
1979     separator = FALSE;
1980     history[ undoState++ ] = cmd;
1981 }
1982 #endif /* SECURE_NO_UNDO */
1983
1984 void SecQLineEditPrivate::insert( const SecQString& s )
1985 {
1986   int remaining = maxLength - text.length();
1987   text.insert( cursor, s.left(remaining) );
1988   for ( int i = 0; i < (int) s.left(remaining).length(); ++i )
1989     {
1990 #ifndef SECURE_NO_UNDO
1991       addCommand( Command( Insert, cursor, s.at(i) ) );
1992 #endif /* SECURE_NO_UNDO */
1993       cursor++;
1994     }
1995   textDirty = TRUE;
1996 }
1997
1998 void SecQLineEditPrivate::del( bool wasBackspace )
1999 {
2000     if ( cursor < (int) text.length() ) {
2001 #ifndef SECURE_NO_UNDO
2002         addCommand ( Command( (CommandType)(wasBackspace?Remove:Delete), cursor, text.at(cursor) ) );
2003 #endif /* SECURE_NO_UNDO */
2004         text.remove( cursor, 1 );
2005         textDirty = TRUE;
2006     }
2007 }
2008
2009 void SecQLineEditPrivate::removeSelectedText()
2010 {
2011     if ( selstart < selend && selend <= (int) text.length() ) {
2012         separate();
2013 #ifndef SECURE_NO_UNDO
2014         int i ;
2015         if ( selstart <= cursor && cursor < selend ) {
2016             // cursor is within the selection. Split up the commands
2017             // to be able to restore the correct cursor position
2018             for ( i = cursor; i >= selstart; --i )
2019                 addCommand ( Command( DeleteSelection, i, text.at(i) ) );
2020             for ( i = selend - 1; i > cursor; --i )
2021                 addCommand ( Command( DeleteSelection, i - cursor + selstart - 1, text.at(i) ) );
2022         } else {
2023             for ( i = selend-1; i >= selstart; --i )
2024                 addCommand ( Command( RemoveSelection, i, text.at(i) ) );
2025         }
2026 #endif /* SECURE_NO_UNDO */
2027         text.remove( selstart, selend - selstart );
2028         if ( cursor > selstart )
2029             cursor -= QMIN( cursor, selend ) - selstart;
2030         deselect();
2031         textDirty = TRUE;
2032     }
2033 }