Add wrapMode and verticalAlignment properties to TextInput.
[profile/ivi/qtdeclarative.git] / src / quick / items / qquicktextinput.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qquicktextinput_p.h"
43 #include "qquicktextinput_p_p.h"
44 #include "qquickcanvas.h"
45
46 #include <private/qdeclarativeglobal_p.h>
47
48 #include <QtDeclarative/qdeclarativeinfo.h>
49 #include <QtGui/qevent.h>
50 #include <QTextBoundaryFinder>
51 #include "qquicktextnode_p.h"
52 #include <QtQuick/qsgsimplerectnode.h>
53
54 #include <QtGui/qstylehints.h>
55 #include <QtGui/qinputpanel.h>
56
57 #ifndef QT_NO_ACCESSIBILITY
58 #include "qaccessible.h"
59 #endif
60
61 QT_BEGIN_NAMESPACE
62
63 DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD)
64
65 /*!
66     \qmlclass TextInput QQuickTextInput
67     \inqmlmodule QtQuick 2
68     \ingroup qml-basic-visual-elements
69     \brief The TextInput item displays an editable line of text.
70     \inherits Item
71
72     The TextInput element displays a single line of editable plain text.
73
74     TextInput is used to accept a line of text input. Input constraints
75     can be placed on a TextInput item (for example, through a \l validator or \l inputMask),
76     and setting \l echoMode to an appropriate value enables TextInput to be used for
77     a password input field.
78
79     On Mac OS X, the Up/Down key bindings for Home/End are explicitly disabled.
80     If you want such bindings (on any platform), you will need to construct them in QML.
81
82     \sa TextEdit, Text, {declarative/text/textselection}{Text Selection example}
83 */
84 QQuickTextInput::QQuickTextInput(QQuickItem* parent)
85 : QQuickImplicitSizeItem(*(new QQuickTextInputPrivate), parent)
86 {
87     Q_D(QQuickTextInput);
88     d->init();
89 }
90
91 QQuickTextInput::~QQuickTextInput()
92 {
93 }
94
95 void QQuickTextInput::componentComplete()
96 {
97     Q_D(QQuickTextInput);
98
99     QQuickImplicitSizeItem::componentComplete();
100
101     d->updateLayout();
102     updateCursorRectangle();
103     if (d->cursorComponent && d->cursorComponent->isReady())
104         createCursor();
105 }
106
107 /*!
108     \qmlproperty string QtQuick2::TextInput::text
109
110     The text in the TextInput.
111 */
112 QString QQuickTextInput::text() const
113 {
114     Q_D(const QQuickTextInput);
115
116     QString content = d->m_text;
117     if (!d->m_tentativeCommit.isEmpty())
118         content.insert(d->m_cursor, d->m_tentativeCommit);
119     QString res = d->m_maskData ? d->stripString(content) : content;
120     return (res.isNull() ? QString::fromLatin1("") : res);
121 }
122
123 void QQuickTextInput::setText(const QString &s)
124 {
125     Q_D(QQuickTextInput);
126     if (s == text())
127         return;
128     if (d->composeMode())
129         qApp->inputPanel()->reset();
130     d->m_tentativeCommit.clear();
131     d->internalSetText(s, -1, false);
132 }
133
134 QString QQuickTextInputPrivate::realText() const
135 {
136     QString res = m_maskData ? stripString(m_text) : m_text;
137     return (res.isNull() ? QString::fromLatin1("") : res);
138 }
139
140 /*!
141     \qmlproperty string QtQuick2::TextInput::font.family
142
143     Sets the family name of the font.
144
145     The family name is case insensitive and may optionally include a foundry name, e.g. "Helvetica [Cronyx]".
146     If the family is available from more than one foundry and the foundry isn't specified, an arbitrary foundry is chosen.
147     If the family isn't available a family will be set using the font matching algorithm.
148 */
149
150 /*!
151     \qmlproperty bool QtQuick2::TextInput::font.bold
152
153     Sets whether the font weight is bold.
154 */
155
156 /*!
157     \qmlproperty enumeration QtQuick2::TextInput::font.weight
158
159     Sets the font's weight.
160
161     The weight can be one of:
162     \list
163     \o Font.Light
164     \o Font.Normal - the default
165     \o Font.DemiBold
166     \o Font.Bold
167     \o Font.Black
168     \endlist
169
170     \qml
171     TextInput { text: "Hello"; font.weight: Font.DemiBold }
172     \endqml
173 */
174
175 /*!
176     \qmlproperty bool QtQuick2::TextInput::font.italic
177
178     Sets whether the font has an italic style.
179 */
180
181 /*!
182     \qmlproperty bool QtQuick2::TextInput::font.underline
183
184     Sets whether the text is underlined.
185 */
186
187 /*!
188     \qmlproperty bool QtQuick2::TextInput::font.strikeout
189
190     Sets whether the font has a strikeout style.
191 */
192
193 /*!
194     \qmlproperty real QtQuick2::TextInput::font.pointSize
195
196     Sets the font size in points. The point size must be greater than zero.
197 */
198
199 /*!
200     \qmlproperty int QtQuick2::TextInput::font.pixelSize
201
202     Sets the font size in pixels.
203
204     Using this function makes the font device dependent.
205     Use \c pointSize to set the size of the font in a device independent manner.
206 */
207
208 /*!
209     \qmlproperty real QtQuick2::TextInput::font.letterSpacing
210
211     Sets the letter spacing for the font.
212
213     Letter spacing changes the default spacing between individual letters in the font.
214     A positive value increases the letter spacing by the corresponding pixels; a negative value decreases the spacing.
215 */
216
217 /*!
218     \qmlproperty real QtQuick2::TextInput::font.wordSpacing
219
220     Sets the word spacing for the font.
221
222     Word spacing changes the default spacing between individual words.
223     A positive value increases the word spacing by a corresponding amount of pixels,
224     while a negative value decreases the inter-word spacing accordingly.
225 */
226
227 /*!
228     \qmlproperty enumeration QtQuick2::TextInput::font.capitalization
229
230     Sets the capitalization for the text.
231
232     \list
233     \o Font.MixedCase - This is the normal text rendering option where no capitalization change is applied.
234     \o Font.AllUppercase - This alters the text to be rendered in all uppercase type.
235     \o Font.AllLowercase - This alters the text to be rendered in all lowercase type.
236     \o Font.SmallCaps - This alters the text to be rendered in small-caps type.
237     \o Font.Capitalize - This alters the text to be rendered with the first character of each word as an uppercase character.
238     \endlist
239
240     \qml
241     TextInput { text: "Hello"; font.capitalization: Font.AllLowercase }
242     \endqml
243 */
244
245 QFont QQuickTextInput::font() const
246 {
247     Q_D(const QQuickTextInput);
248     return d->sourceFont;
249 }
250
251 void QQuickTextInput::setFont(const QFont &font)
252 {
253     Q_D(QQuickTextInput);
254     if (d->sourceFont == font)
255         return;
256
257     d->sourceFont = font;
258     QFont oldFont = d->font;
259     d->font = font;
260     if (d->font.pointSizeF() != -1) {
261         // 0.5pt resolution
262         qreal size = qRound(d->font.pointSizeF()*2.0);
263         d->font.setPointSizeF(size/2.0);
264     }
265     if (oldFont != d->font) {
266         d->updateLayout();
267         updateCursorRectangle();
268     }
269     emit fontChanged(d->sourceFont);
270 }
271
272 /*!
273     \qmlproperty color QtQuick2::TextInput::color
274
275     The text color.
276 */
277 QColor QQuickTextInput::color() const
278 {
279     Q_D(const QQuickTextInput);
280     return d->color;
281 }
282
283 void QQuickTextInput::setColor(const QColor &c)
284 {
285     Q_D(QQuickTextInput);
286     if (c != d->color) {
287         d->color = c;
288         d->textLayoutDirty = true;
289         update();
290         emit colorChanged(c);
291     }
292 }
293
294
295 /*!
296     \qmlproperty color QtQuick2::TextInput::selectionColor
297
298     The text highlight color, used behind selections.
299 */
300 QColor QQuickTextInput::selectionColor() const
301 {
302     Q_D(const QQuickTextInput);
303     return d->selectionColor;
304 }
305
306 void QQuickTextInput::setSelectionColor(const QColor &color)
307 {
308     Q_D(QQuickTextInput);
309     if (d->selectionColor == color)
310         return;
311
312     d->selectionColor = color;
313     d->m_palette.setColor(QPalette::Highlight, d->selectionColor);
314     if (d->hasSelectedText()) {
315         d->textLayoutDirty = true;
316         update();
317     }
318     emit selectionColorChanged(color);
319 }
320 /*!
321     \qmlproperty color QtQuick2::TextInput::selectedTextColor
322
323     The highlighted text color, used in selections.
324 */
325 QColor QQuickTextInput::selectedTextColor() const
326 {
327     Q_D(const QQuickTextInput);
328     return d->selectedTextColor;
329 }
330
331 void QQuickTextInput::setSelectedTextColor(const QColor &color)
332 {
333     Q_D(QQuickTextInput);
334     if (d->selectedTextColor == color)
335         return;
336
337     d->selectedTextColor = color;
338     d->m_palette.setColor(QPalette::HighlightedText, d->selectedTextColor);
339     if (d->hasSelectedText()) {
340         d->textLayoutDirty = true;
341         update();
342     }
343     emit selectedTextColorChanged(color);
344 }
345
346 /*!
347     \qmlproperty enumeration QtQuick2::TextInput::horizontalAlignment
348     \qmlproperty enumeration QtQuick2::TextInput::effectiveHorizontalAlignment
349     \qmlproperty enumeration QtQuick2::TextInput::verticalAlignment
350
351     Sets the horizontal alignment of the text within the TextInput item's
352     width and height. By default, the text alignment follows the natural alignment
353     of the text, for example text that is read from left to right will be aligned to
354     the left.
355
356     TextInput does not have vertical alignment, as the natural height is
357     exactly the height of the single line of text. If you set the height
358     manually to something larger, TextInput will always be top aligned
359     vertically. You can use anchors to align it however you want within
360     another item.
361
362     The valid values for \c horizontalAlignment are \c TextInput.AlignLeft, \c TextInput.AlignRight and
363     \c TextInput.AlignHCenter.
364
365     Valid values for \c verticalAlignment are \c TextEdit.AlignTop (default),
366     \c TextEdit.AlignBottom \c TextEdit.AlignVCenter.
367
368     When using the attached property LayoutMirroring::enabled to mirror application
369     layouts, the horizontal alignment of text will also be mirrored. However, the property
370     \c horizontalAlignment will remain unchanged. To query the effective horizontal alignment
371     of TextInput, use the read-only property \c effectiveHorizontalAlignment.
372 */
373 QQuickTextInput::HAlignment QQuickTextInput::hAlign() const
374 {
375     Q_D(const QQuickTextInput);
376     return d->hAlign;
377 }
378
379 void QQuickTextInput::setHAlign(HAlignment align)
380 {
381     Q_D(QQuickTextInput);
382     bool forceAlign = d->hAlignImplicit && d->effectiveLayoutMirror;
383     d->hAlignImplicit = false;
384     if (d->setHAlign(align, forceAlign) && isComponentComplete()) {
385         d->updateLayout();
386         updateCursorRectangle();
387     }
388 }
389
390 void QQuickTextInput::resetHAlign()
391 {
392     Q_D(QQuickTextInput);
393     d->hAlignImplicit = true;
394     if (d->determineHorizontalAlignment() && isComponentComplete()) {
395         d->updateLayout();
396         updateCursorRectangle();
397     }
398 }
399
400 QQuickTextInput::HAlignment QQuickTextInput::effectiveHAlign() const
401 {
402     Q_D(const QQuickTextInput);
403     QQuickTextInput::HAlignment effectiveAlignment = d->hAlign;
404     if (!d->hAlignImplicit && d->effectiveLayoutMirror) {
405         switch (d->hAlign) {
406         case QQuickTextInput::AlignLeft:
407             effectiveAlignment = QQuickTextInput::AlignRight;
408             break;
409         case QQuickTextInput::AlignRight:
410             effectiveAlignment = QQuickTextInput::AlignLeft;
411             break;
412         default:
413             break;
414         }
415     }
416     return effectiveAlignment;
417 }
418
419 bool QQuickTextInputPrivate::setHAlign(QQuickTextInput::HAlignment alignment, bool forceAlign)
420 {
421     Q_Q(QQuickTextInput);
422     if ((hAlign != alignment || forceAlign) && alignment <= QQuickTextInput::AlignHCenter) { // justify not supported
423         QQuickTextInput::HAlignment oldEffectiveHAlign = q->effectiveHAlign();
424         hAlign = alignment;
425         emit q->horizontalAlignmentChanged(alignment);
426         if (oldEffectiveHAlign != q->effectiveHAlign())
427             emit q->effectiveHorizontalAlignmentChanged();
428         return true;
429     }
430     return false;
431 }
432
433 bool QQuickTextInputPrivate::determineHorizontalAlignment()
434 {
435     if (hAlignImplicit) {
436         // if no explicit alignment has been set, follow the natural layout direction of the text
437         QString text = q_func()->text();
438         if (text.isEmpty())
439             text = m_textLayout.preeditAreaText();
440         bool isRightToLeft = text.isEmpty() ? QGuiApplication::keyboardInputDirection() == Qt::RightToLeft : text.isRightToLeft();
441         return setHAlign(isRightToLeft ? QQuickTextInput::AlignRight : QQuickTextInput::AlignLeft);
442     }
443     return false;
444 }
445
446 QQuickTextInput::VAlignment QQuickTextInput::vAlign() const
447 {
448     Q_D(const QQuickTextInput);
449     return d->vAlign;
450 }
451
452 void QQuickTextInput::setVAlign(QQuickTextInput::VAlignment alignment)
453 {
454     Q_D(QQuickTextInput);
455     if (alignment == d->vAlign)
456         return;
457     d->vAlign = alignment;
458     emit verticalAlignmentChanged(d->vAlign);
459     if (isComponentComplete()) {
460         updateCursorRectangle();
461     }
462 }
463
464 /*!
465     \qmlproperty enumeration QtQuick2::TextInput::wrapMode
466
467     Set this property to wrap the text to the TextEdit item's width.
468     The text will only wrap if an explicit width has been set.
469
470     \list
471     \o TextInput.NoWrap - no wrapping will be performed. If the text contains insufficient newlines, then implicitWidth will exceed a set width.
472     \o TextInput.WordWrap - wrapping is done on word boundaries only. If a word is too long, implicitWidth will exceed a set width.
473     \o TextInput.WrapAnywhere - wrapping is done at any point on a line, even if it occurs in the middle of a word.
474     \o TextInput.Wrap - if possible, wrapping occurs at a word boundary; otherwise it will occur at the appropriate point on the line, even in the middle of a word.
475     \endlist
476
477     The default is TextInput.NoWrap. If you set a width, consider using TextInput.Wrap.
478 */
479 QQuickTextInput::WrapMode QQuickTextInput::wrapMode() const
480 {
481     Q_D(const QQuickTextInput);
482     return d->wrapMode;
483 }
484
485 void QQuickTextInput::setWrapMode(WrapMode mode)
486 {
487     Q_D(QQuickTextInput);
488     if (mode == d->wrapMode)
489         return;
490     d->wrapMode = mode;
491     d->updateLayout();
492     updateCursorRectangle();
493     emit wrapModeChanged();
494 }
495
496 void QQuickTextInputPrivate::mirrorChange()
497 {
498     Q_Q(QQuickTextInput);
499     if (q->isComponentComplete()) {
500         if (!hAlignImplicit && (hAlign == QQuickTextInput::AlignRight || hAlign == QQuickTextInput::AlignLeft)) {
501             q->updateCursorRectangle();
502             emit q->effectiveHorizontalAlignmentChanged();
503         }
504     }
505 }
506
507 /*!
508     \qmlproperty bool QtQuick2::TextInput::readOnly
509
510     Sets whether user input can modify the contents of the TextInput.
511
512     If readOnly is set to true, then user input will not affect the text
513     property. Any bindings or attempts to set the text property will still
514     work.
515 */
516 bool QQuickTextInput::isReadOnly() const
517 {
518     Q_D(const QQuickTextInput);
519     return d->m_readOnly;
520 }
521
522 void QQuickTextInput::setReadOnly(bool ro)
523 {
524     Q_D(QQuickTextInput);
525     if (d->m_readOnly == ro)
526         return;
527
528     setFlag(QQuickItem::ItemAcceptsInputMethod, !ro);
529     d->m_readOnly = ro;
530     if (!ro)
531         d->setCursorPosition(d->end());
532
533     emit readOnlyChanged(ro);
534 }
535
536 /*!
537     \qmlproperty int QtQuick2::TextInput::maximumLength
538     The maximum permitted length of the text in the TextInput.
539
540     If the text is too long, it is truncated at the limit.
541
542     By default, this property contains a value of 32767.
543 */
544 int QQuickTextInput::maxLength() const
545 {
546     Q_D(const QQuickTextInput);
547     return d->m_maxLength;
548 }
549
550 void QQuickTextInput::setMaxLength(int ml)
551 {
552     Q_D(QQuickTextInput);
553     if (d->m_maxLength == ml || d->m_maskData)
554         return;
555
556     d->m_maxLength = ml;
557     d->internalSetText(d->m_text, -1, false);
558
559     emit maximumLengthChanged(ml);
560 }
561
562 /*!
563     \qmlproperty bool QtQuick2::TextInput::cursorVisible
564     Set to true when the TextInput shows a cursor.
565
566     This property is set and unset when the TextInput gets active focus, so that other
567     properties can be bound to whether the cursor is currently showing. As it
568     gets set and unset automatically, when you set the value yourself you must
569     keep in mind that your value may be overwritten.
570
571     It can be set directly in script, for example if a KeyProxy might
572     forward keys to it and you desire it to look active when this happens
573     (but without actually giving it active focus).
574
575     It should not be set directly on the element, like in the below QML,
576     as the specified value will be overridden an lost on focus changes.
577
578     \code
579     TextInput {
580         text: "Text"
581         cursorVisible: false
582     }
583     \endcode
584
585     In the above snippet the cursor will still become visible when the
586     TextInput gains active focus.
587 */
588 bool QQuickTextInput::isCursorVisible() const
589 {
590     Q_D(const QQuickTextInput);
591     return d->cursorVisible;
592 }
593
594 void QQuickTextInput::setCursorVisible(bool on)
595 {
596     Q_D(QQuickTextInput);
597     if (d->cursorVisible == on)
598         return;
599     d->cursorVisible = on;
600     d->setCursorBlinkPeriod(on ? qApp->styleHints()->cursorFlashTime() : 0);
601     QRect r = cursorRectangle();
602     if (d->inputMask().isEmpty())
603         updateRect(r);
604     else
605         updateRect();
606     emit cursorVisibleChanged(d->cursorVisible);
607 }
608
609 /*!
610     \qmlproperty int QtQuick2::TextInput::cursorPosition
611     The position of the cursor in the TextInput.
612 */
613 int QQuickTextInput::cursorPosition() const
614 {
615     Q_D(const QQuickTextInput);
616     return d->m_cursor;
617 }
618
619 void QQuickTextInput::setCursorPosition(int cp)
620 {
621     Q_D(QQuickTextInput);
622     if (cp < 0 || cp > text().length())
623         return;
624     d->moveCursor(cp);
625 }
626
627 /*!
628   Returns a Rect which encompasses the cursor, but which may be larger than is
629   required. Ignores custom cursor delegates.
630 */
631 QRect QQuickTextInput::cursorRectangle() const
632 {
633     Q_D(const QQuickTextInput);
634
635     int c = d->m_cursor;
636     if (d->m_preeditCursor != -1)
637         c += d->m_preeditCursor;
638     if (d->m_echoMode == NoEcho || !isComponentComplete())
639         c = 0;
640     QTextLine l = d->m_textLayout.lineForTextPosition(c);
641     return QRect(
642             qRound(l.cursorToX(c) - d->hscroll),
643             qRound(l.y() - d->vscroll),
644             d->m_cursorWidth,
645             qCeil(l.height()));
646 }
647
648 /*!
649     \qmlproperty int QtQuick2::TextInput::selectionStart
650
651     The cursor position before the first character in the current selection.
652
653     This property is read-only. To change the selection, use select(start,end),
654     selectAll(), or selectWord().
655
656     \sa selectionEnd, cursorPosition, selectedText
657 */
658 int QQuickTextInput::selectionStart() const
659 {
660     Q_D(const QQuickTextInput);
661     return d->lastSelectionStart;
662 }
663 /*!
664     \qmlproperty int QtQuick2::TextInput::selectionEnd
665
666     The cursor position after the last character in the current selection.
667
668     This property is read-only. To change the selection, use select(start,end),
669     selectAll(), or selectWord().
670
671     \sa selectionStart, cursorPosition, selectedText
672 */
673 int QQuickTextInput::selectionEnd() const
674 {
675     Q_D(const QQuickTextInput);
676     return d->lastSelectionEnd;
677 }
678 /*!
679     \qmlmethod void QtQuick2::TextInput::select(int start, int end)
680
681     Causes the text from \a start to \a end to be selected.
682
683     If either start or end is out of range, the selection is not changed.
684
685     After calling this, selectionStart will become the lesser
686     and selectionEnd will become the greater (regardless of the order passed
687     to this method).
688
689     \sa selectionStart, selectionEnd
690 */
691 void QQuickTextInput::select(int start, int end)
692 {
693     Q_D(QQuickTextInput);
694     if (start < 0 || end < 0 || start > text().length() || end > text().length())
695         return;
696     d->setSelection(start, end-start);
697 }
698
699 /*!
700     \qmlproperty string QtQuick2::TextInput::selectedText
701
702     This read-only property provides the text currently selected in the
703     text input.
704
705     It is equivalent to the following snippet, but is faster and easier
706     to use.
707
708     \js
709     myTextInput.text.toString().substring(myTextInput.selectionStart,
710         myTextInput.selectionEnd);
711     \endjs
712 */
713 QString QQuickTextInput::selectedText() const
714 {
715     Q_D(const QQuickTextInput);
716     return d->selectedText();
717 }
718
719 /*!
720     \qmlproperty bool QtQuick2::TextInput::activeFocusOnPress
721
722     Whether the TextInput should gain active focus on a mouse press. By default this is
723     set to true.
724 */
725 bool QQuickTextInput::focusOnPress() const
726 {
727     Q_D(const QQuickTextInput);
728     return d->focusOnPress;
729 }
730
731 void QQuickTextInput::setFocusOnPress(bool b)
732 {
733     Q_D(QQuickTextInput);
734     if (d->focusOnPress == b)
735         return;
736
737     d->focusOnPress = b;
738
739     emit activeFocusOnPressChanged(d->focusOnPress);
740 }
741 /*!
742     \qmlproperty bool QtQuick2::TextInput::autoScroll
743
744     Whether the TextInput should scroll when the text is longer than the width. By default this is
745     set to true.
746 */
747 bool QQuickTextInput::autoScroll() const
748 {
749     Q_D(const QQuickTextInput);
750     return d->autoScroll;
751 }
752
753 void QQuickTextInput::setAutoScroll(bool b)
754 {
755     Q_D(QQuickTextInput);
756     if (d->autoScroll == b)
757         return;
758
759     d->autoScroll = b;
760     //We need to repaint so that the scrolling is taking into account.
761     updateCursorRectangle();
762     emit autoScrollChanged(d->autoScroll);
763 }
764
765 #ifndef QT_NO_VALIDATOR
766
767 /*!
768     \qmlclass IntValidator QIntValidator
769     \inqmlmodule QtQuick 2
770     \ingroup qml-basic-visual-elements
771
772     This element provides a validator for integer values.
773
774     IntValidator uses the \l {QLocale::setDefault()}{default locale} to interpret the number and
775     will accept locale specific digits, group separators, and positive and negative signs.  In
776     addition, IntValidator is always guaranteed to accept a number formatted according to the "C"
777     locale.
778 */
779 /*!
780     \qmlproperty int QtQuick2::IntValidator::top
781
782     This property holds the validator's highest acceptable value.
783     By default, this property's value is derived from the highest signed integer available (typically 2147483647).
784 */
785 /*!
786     \qmlproperty int QtQuick2::IntValidator::bottom
787
788     This property holds the validator's lowest acceptable value.
789     By default, this property's value is derived from the lowest signed integer available (typically -2147483647).
790 */
791
792 /*!
793     \qmlclass DoubleValidator QDoubleValidator
794     \inqmlmodule QtQuick 2
795     \ingroup qml-basic-visual-elements
796
797     This element provides a validator for non-integer numbers.
798 */
799
800 /*!
801     \qmlproperty real QtQuick2::DoubleValidator::top
802
803     This property holds the validator's maximum acceptable value.
804     By default, this property contains a value of infinity.
805 */
806 /*!
807     \qmlproperty real QtQuick2::DoubleValidator::bottom
808
809     This property holds the validator's minimum acceptable value.
810     By default, this property contains a value of -infinity.
811 */
812 /*!
813     \qmlproperty int QtQuick2::DoubleValidator::decimals
814
815     This property holds the validator's maximum number of digits after the decimal point.
816     By default, this property contains a value of 1000.
817 */
818 /*!
819     \qmlproperty enumeration QtQuick2::DoubleValidator::notation
820     This property holds the notation of how a string can describe a number.
821
822     The possible values for this property are:
823
824     \list
825     \o DoubleValidator.StandardNotation
826     \o DoubleValidator.ScientificNotation (default)
827     \endlist
828
829     If this property is set to DoubleValidator.ScientificNotation, the written number may have an exponent part (e.g. 1.5E-2).
830 */
831
832 /*!
833     \qmlclass RegExpValidator QRegExpValidator
834     \inqmlmodule QtQuick 2
835     \ingroup qml-basic-visual-elements
836
837     This element provides a validator, which counts as valid any string which
838     matches a specified regular expression.
839 */
840 /*!
841    \qmlproperty regExp QtQuick2::RegExpValidator::regExp
842
843    This property holds the regular expression used for validation.
844
845    Note that this property should be a regular expression in JS syntax, e.g /a/ for the regular expression
846    matching "a".
847
848    By default, this property contains a regular expression with the pattern .* that matches any string.
849 */
850
851 /*!
852     \qmlproperty Validator QtQuick2::TextInput::validator
853
854     Allows you to set a validator on the TextInput. When a validator is set
855     the TextInput will only accept input which leaves the text property in
856     an acceptable or intermediate state. The accepted signal will only be sent
857     if the text is in an acceptable state when enter is pressed.
858
859     Currently supported validators are IntValidator, DoubleValidator and
860     RegExpValidator. An example of using validators is shown below, which allows
861     input of integers between 11 and 31 into the text input:
862
863     \code
864     import QtQuick 1.0
865     TextInput{
866         validator: IntValidator{bottom: 11; top: 31;}
867         focus: true
868     }
869     \endcode
870
871     \sa acceptableInput, inputMask
872 */
873
874 QValidator* QQuickTextInput::validator() const
875 {
876     Q_D(const QQuickTextInput);
877     return d->m_validator;
878 }
879
880 void QQuickTextInput::setValidator(QValidator* v)
881 {
882     Q_D(QQuickTextInput);
883     if (d->m_validator == v)
884         return;
885
886     d->m_validator = v;
887     if (!d->hasAcceptableInput(d->m_text)) {
888         d->oldValidity = false;
889         emit acceptableInputChanged();
890     }
891
892     emit validatorChanged();
893 }
894 #endif // QT_NO_VALIDATOR
895
896 /*!
897     \qmlproperty string QtQuick2::TextInput::inputMask
898
899     Allows you to set an input mask on the TextInput, restricting the allowable
900     text inputs. See QLineEdit::inputMask for further details, as the exact
901     same mask strings are used by TextInput.
902
903     \sa acceptableInput, validator
904 */
905 QString QQuickTextInput::inputMask() const
906 {
907     Q_D(const QQuickTextInput);
908     return d->inputMask();
909 }
910
911 void QQuickTextInput::setInputMask(const QString &im)
912 {
913     Q_D(QQuickTextInput);
914     if (d->inputMask() == im)
915         return;
916
917     d->setInputMask(im);
918     emit inputMaskChanged(d->inputMask());
919 }
920
921 /*!
922     \qmlproperty bool QtQuick2::TextInput::acceptableInput
923
924     This property is always true unless a validator or input mask has been set.
925     If a validator or input mask has been set, this property will only be true
926     if the current text is acceptable to the validator or input mask as a final
927     string (not as an intermediate string).
928 */
929 bool QQuickTextInput::hasAcceptableInput() const
930 {
931     Q_D(const QQuickTextInput);
932     return d->hasAcceptableInput(d->m_text);
933 }
934
935 /*!
936     \qmlsignal QtQuick2::TextInput::onAccepted()
937
938     This handler is called when the Return or Enter key is pressed.
939     Note that if there is a \l validator or \l inputMask set on the text
940     input, the handler will only be emitted if the input is in an acceptable
941     state.
942 */
943
944 void QQuickTextInputPrivate::updateInputMethodHints()
945 {
946     Q_Q(QQuickTextInput);
947     Qt::InputMethodHints hints = inputMethodHints;
948     if (m_echoMode == QQuickTextInput::Password || m_echoMode == QQuickTextInput::NoEcho)
949         hints |= Qt::ImhHiddenText;
950     else if (m_echoMode == QQuickTextInput::PasswordEchoOnEdit)
951         hints &= ~Qt::ImhHiddenText;
952     if (m_echoMode != QQuickTextInput::Normal)
953         hints |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText);
954     q->setInputMethodHints(hints);
955 }
956 /*!
957     \qmlproperty enumeration QtQuick2::TextInput::echoMode
958
959     Specifies how the text should be displayed in the TextInput.
960     \list
961     \o TextInput.Normal - Displays the text as it is. (Default)
962     \o TextInput.Password - Displays asterisks instead of characters.
963     \o TextInput.NoEcho - Displays nothing.
964     \o TextInput.PasswordEchoOnEdit - Displays characters as they are entered
965     while editing, otherwise displays asterisks.
966     \endlist
967 */
968 QQuickTextInput::EchoMode QQuickTextInput::echoMode() const
969 {
970     Q_D(const QQuickTextInput);
971     return QQuickTextInput::EchoMode(d->m_echoMode);
972 }
973
974 void QQuickTextInput::setEchoMode(QQuickTextInput::EchoMode echo)
975 {
976     Q_D(QQuickTextInput);
977     if (echoMode() == echo)
978         return;
979     d->m_echoMode = echo;
980     d->m_passwordEchoEditing = false;
981     d->updateInputMethodHints();
982     d->updateDisplayText();
983     updateCursorRectangle();
984
985     emit echoModeChanged(echoMode());
986 }
987
988 Qt::InputMethodHints QQuickTextInput::imHints() const
989 {
990     Q_D(const QQuickTextInput);
991     return d->inputMethodHints;
992 }
993
994 void QQuickTextInput::setIMHints(Qt::InputMethodHints hints)
995 {
996     Q_D(QQuickTextInput);
997     if (d->inputMethodHints == hints)
998         return;
999     d->inputMethodHints = hints;
1000     d->updateInputMethodHints();
1001 }
1002
1003 /*!
1004     \qmlproperty Component QtQuick2::TextInput::cursorDelegate
1005     The delegate for the cursor in the TextInput.
1006
1007     If you set a cursorDelegate for a TextInput, this delegate will be used for
1008     drawing the cursor instead of the standard cursor. An instance of the
1009     delegate will be created and managed by the TextInput when a cursor is
1010     needed, and the x property of delegate instance will be set so as
1011     to be one pixel before the top left of the current character.
1012
1013     Note that the root item of the delegate component must be a QDeclarativeItem or
1014     QDeclarativeItem derived item.
1015 */
1016 QDeclarativeComponent* QQuickTextInput::cursorDelegate() const
1017 {
1018     Q_D(const QQuickTextInput);
1019     return d->cursorComponent;
1020 }
1021
1022 void QQuickTextInput::setCursorDelegate(QDeclarativeComponent* c)
1023 {
1024     Q_D(QQuickTextInput);
1025     if (d->cursorComponent == c)
1026         return;
1027
1028     d->cursorComponent = c;
1029     if (!c) {
1030         //note that the components are owned by something else
1031         delete d->cursorItem;
1032     } else {
1033         d->startCreatingCursor();
1034     }
1035
1036     emit cursorDelegateChanged();
1037 }
1038
1039 void QQuickTextInputPrivate::startCreatingCursor()
1040 {
1041     Q_Q(QQuickTextInput);
1042     if (cursorComponent->isReady()) {
1043         q->createCursor();
1044     } else if (cursorComponent->isLoading()) {
1045         q->connect(cursorComponent, SIGNAL(statusChanged(int)),
1046                 q, SLOT(createCursor()));
1047     } else { // isError
1048         qmlInfo(q, cursorComponent->errors()) << QQuickTextInput::tr("Could not load cursor delegate");
1049     }
1050 }
1051
1052 void QQuickTextInput::createCursor()
1053 {
1054     Q_D(QQuickTextInput);
1055     if (!isComponentComplete())
1056         return;
1057
1058     if (d->cursorComponent->isError()) {
1059         qmlInfo(this, d->cursorComponent->errors()) << tr("Could not load cursor delegate");
1060         return;
1061     }
1062
1063     if (!d->cursorComponent->isReady())
1064         return;
1065
1066     if (d->cursorItem)
1067         delete d->cursorItem;
1068     QDeclarativeContext *creationContext = d->cursorComponent->creationContext();
1069     QObject *object = d->cursorComponent->create(creationContext ? creationContext : qmlContext(this));
1070     d->cursorItem = qobject_cast<QQuickItem*>(object);
1071     if (!d->cursorItem) {
1072         delete object;
1073         qmlInfo(this, d->cursorComponent->errors()) << tr("Could not instantiate cursor delegate");
1074         return;
1075     }
1076
1077     QRectF r = cursorRectangle();
1078
1079     QDeclarative_setParent_noEvent(d->cursorItem, this);
1080     d->cursorItem->setParentItem(this);
1081     d->cursorItem->setPos(r.topLeft());
1082     d->cursorItem->setHeight(r.height());
1083 }
1084
1085 /*!
1086     \qmlmethod rect QtQuick2::TextInput::positionToRectangle(int pos)
1087
1088     This function takes a character position and returns the rectangle that the
1089     cursor would occupy, if it was placed at that character position.
1090
1091     This is similar to setting the cursorPosition, and then querying the cursor
1092     rectangle, but the cursorPosition is not changed.
1093 */
1094 QRectF QQuickTextInput::positionToRectangle(int pos) const
1095 {
1096     Q_D(const QQuickTextInput);
1097     if (pos > d->m_cursor)
1098         pos += d->preeditAreaText().length();
1099     QTextLine l = d->m_textLayout.lineAt(0);
1100     return QRectF(l.cursorToX(pos) - d->hscroll, 0.0, d->m_cursorWidth, l.height());
1101 }
1102
1103 /*!
1104     \qmlmethod int QtQuick2::TextInput::positionAt(real x, real y, CursorPosition position = CursorBetweenCharacters)
1105
1106     This function returns the character position at
1107     x and y pixels from the top left  of the textInput. Position 0 is before the
1108     first character, position 1 is after the first character but before the second,
1109     and so on until position text.length, which is after all characters.
1110
1111     This means that for all x values before the first character this function returns 0,
1112     and for all x values after the last character this function returns text.length.  If
1113     the y value is above the text the position will be that of the nearest character on
1114     the first line line and if it is below the text the position of the nearest character
1115     on the last line will be returned.
1116
1117     The cursor position type specifies how the cursor position should be resolved.
1118
1119     \list
1120     \o TextInput.CursorBetweenCharacters - Returns the position between characters that is nearest x.
1121     \o TextInput.CursorOnCharacter - Returns the position before the character that is nearest x.
1122     \endlist
1123 */
1124
1125 void QQuickTextInput::positionAt(QDeclarativeV8Function *args) const
1126 {
1127     Q_D(const QQuickTextInput);
1128
1129     qreal x = 0;
1130     qreal y = 0;
1131     QTextLine::CursorPosition position = QTextLine::CursorBetweenCharacters;
1132
1133     if (args->Length() < 1)
1134         return;
1135
1136     int i = 0;
1137     v8::Local<v8::Value> arg = (*args)[i];
1138     x = arg->NumberValue();
1139
1140     if (++i < args->Length()) {
1141         arg = (*args)[i];
1142         y = arg->NumberValue();
1143     }
1144
1145     if (++i < args->Length()) {
1146         arg = (*args)[i];
1147         position = QTextLine::CursorPosition(arg->Int32Value());
1148     }
1149
1150     int pos = d->positionAt(x, y, position);
1151     const int cursor = d->m_cursor;
1152     if (pos > cursor) {
1153         const int preeditLength = d->preeditAreaText().length();
1154         pos = pos > cursor + preeditLength
1155                 ? pos - preeditLength
1156                 : cursor;
1157     }
1158     args->returnValue(v8::Int32::New(pos));
1159 }
1160
1161 int QQuickTextInputPrivate::positionAt(int x, int y, QTextLine::CursorPosition position) const
1162 {
1163     x += hscroll;
1164     y += vscroll;
1165     QTextLine line = m_textLayout.lineAt(0);
1166     for (int i = 1; i < m_textLayout.lineCount(); ++i) {
1167         QTextLine nextLine = m_textLayout.lineAt(i);
1168
1169         if (y < (line.rect().bottom() + nextLine.y()) / 2)
1170             break;
1171         line = nextLine;
1172     }
1173     return line.xToCursor(x, position);
1174 }
1175
1176 void QQuickTextInput::keyPressEvent(QKeyEvent* ev)
1177 {
1178     Q_D(QQuickTextInput);
1179     // Don't allow MacOSX up/down support, and we don't allow a completer.
1180     bool ignore = (ev->key() == Qt::Key_Up || ev->key() == Qt::Key_Down) && ev->modifiers() == Qt::NoModifier;
1181     if (!ignore && (d->lastSelectionStart == d->lastSelectionEnd) && (ev->key() == Qt::Key_Right || ev->key() == Qt::Key_Left)) {
1182         // Ignore when moving off the end unless there is a selection,
1183         // because then moving will do something (deselect).
1184         int cursorPosition = d->m_cursor;
1185         if (cursorPosition == 0)
1186             ignore = ev->key() == (d->layoutDirection() == Qt::LeftToRight ? Qt::Key_Left : Qt::Key_Right);
1187         if (cursorPosition == text().length())
1188             ignore = ev->key() == (d->layoutDirection() == Qt::LeftToRight ? Qt::Key_Right : Qt::Key_Left);
1189     }
1190     if (ignore) {
1191         ev->ignore();
1192     } else {
1193         d->processKeyEvent(ev);
1194     }
1195     if (!ev->isAccepted())
1196         QQuickImplicitSizeItem::keyPressEvent(ev);
1197 }
1198
1199 void QQuickTextInput::inputMethodEvent(QInputMethodEvent *ev)
1200 {
1201     Q_D(QQuickTextInput);
1202     const bool wasComposing = d->preeditAreaText().length() > 0;
1203     if (d->m_readOnly) {
1204         ev->ignore();
1205     } else {
1206         d->processInputMethodEvent(ev);
1207     }
1208     if (!ev->isAccepted())
1209         QQuickImplicitSizeItem::inputMethodEvent(ev);
1210
1211     if (wasComposing != (d->m_textLayout.preeditAreaText().length() > 0))
1212         emit inputMethodComposingChanged();
1213 }
1214
1215 void QQuickTextInput::mouseDoubleClickEvent(QMouseEvent *event)
1216 {
1217     Q_D(QQuickTextInput);
1218
1219     if (d->selectByMouse && event->button() == Qt::LeftButton) {
1220         d->commitPreedit();
1221         int cursor = d->positionAt(event->localPos());
1222         d->selectWordAtPos(cursor);
1223         event->setAccepted(true);
1224         if (!d->hasPendingTripleClick()) {
1225             d->tripleClickStartPoint = event->localPos().toPoint();
1226             d->tripleClickTimer.start();
1227         }
1228     } else {
1229         if (d->sendMouseEventToInputContext(event))
1230             return;
1231         QQuickImplicitSizeItem::mouseDoubleClickEvent(event);
1232     }
1233 }
1234
1235 void QQuickTextInput::mousePressEvent(QMouseEvent *event)
1236 {
1237     Q_D(QQuickTextInput);
1238
1239     d->pressPos = event->localPos();
1240
1241     if (d->focusOnPress) {
1242         bool hadActiveFocus = hasActiveFocus();
1243         forceActiveFocus();
1244         // re-open input panel on press if already focused
1245         if (hasActiveFocus() && hadActiveFocus && !d->m_readOnly)
1246             openSoftwareInputPanel();
1247     }
1248     if (d->selectByMouse) {
1249         setKeepMouseGrab(false);
1250         d->selectPressed = true;
1251         QPoint distanceVector = d->pressPos.toPoint() - d->tripleClickStartPoint;
1252         if (d->hasPendingTripleClick()
1253             && distanceVector.manhattanLength() < qApp->styleHints()->startDragDistance()) {
1254             event->setAccepted(true);
1255             selectAll();
1256             return;
1257         }
1258     }
1259
1260     if (d->sendMouseEventToInputContext(event))
1261         return;
1262
1263     bool mark = (event->modifiers() & Qt::ShiftModifier) && d->selectByMouse;
1264     int cursor = d->positionAt(event->localPos());
1265     d->moveCursor(cursor, mark);
1266     event->setAccepted(true);
1267 }
1268
1269 void QQuickTextInput::mouseMoveEvent(QMouseEvent *event)
1270 {
1271     Q_D(QQuickTextInput);
1272
1273     if (d->selectPressed) {
1274         if (qAbs(int(event->localPos().x() - d->pressPos.x())) > qApp->styleHints()->startDragDistance())
1275             setKeepMouseGrab(true);
1276
1277         if (d->composeMode()) {
1278             // start selection
1279             int startPos = d->positionAt(d->pressPos);
1280             int currentPos = d->positionAt(event->localPos());
1281             if (startPos != currentPos)
1282                 d->setSelection(startPos, currentPos - startPos);
1283         } else {
1284             moveCursorSelection(d->positionAt(event->localPos()), d->mouseSelectionMode);
1285         }
1286         event->setAccepted(true);
1287     } else {
1288         QQuickImplicitSizeItem::mouseMoveEvent(event);
1289     }
1290 }
1291
1292 void QQuickTextInput::mouseReleaseEvent(QMouseEvent *event)
1293 {
1294     Q_D(QQuickTextInput);
1295     if (d->sendMouseEventToInputContext(event))
1296         return;
1297     if (d->selectPressed) {
1298         d->selectPressed = false;
1299         setKeepMouseGrab(false);
1300     }
1301 #ifndef QT_NO_CLIPBOARD
1302     if (QGuiApplication::clipboard()->supportsSelection()) {
1303         if (event->button() == Qt::LeftButton) {
1304             d->copy(QClipboard::Selection);
1305         } else if (!d->m_readOnly && event->button() == Qt::MidButton) {
1306             d->deselect();
1307             d->insert(QGuiApplication::clipboard()->text(QClipboard::Selection));
1308         }
1309     }
1310 #endif
1311     if (!event->isAccepted())
1312         QQuickImplicitSizeItem::mouseReleaseEvent(event);
1313 }
1314
1315 bool QQuickTextInputPrivate::sendMouseEventToInputContext(QMouseEvent *event)
1316 {
1317 #if !defined QT_NO_IM
1318     if (composeMode()) {
1319         int tmp_cursor = positionAt(event->localPos());
1320         int mousePos = tmp_cursor - m_cursor;
1321         if (mousePos >= 0 && mousePos <= m_textLayout.preeditAreaText().length()) {
1322             if (event->type() == QEvent::MouseButtonRelease) {
1323                 qApp->inputPanel()->invokeAction(QInputPanel::Click, mousePos);
1324             }
1325             return true;
1326         }
1327     }
1328 #else
1329     Q_UNUSED(event);
1330     Q_UNUSED(eventType)
1331 #endif
1332
1333     return false;
1334 }
1335
1336 void QQuickTextInput::mouseUngrabEvent()
1337 {
1338     Q_D(QQuickTextInput);
1339     d->selectPressed = false;
1340     setKeepMouseGrab(false);
1341 }
1342
1343 bool QQuickTextInput::event(QEvent* ev)
1344 {
1345 #ifndef QT_NO_SHORTCUT
1346     Q_D(QQuickTextInput);
1347     if (ev->type() == QEvent::ShortcutOverride) {
1348         if (d->m_readOnly)
1349             return false;
1350         QKeyEvent* ke = static_cast<QKeyEvent*>(ev);
1351         if (ke == QKeySequence::Copy
1352             || ke == QKeySequence::Paste
1353             || ke == QKeySequence::Cut
1354             || ke == QKeySequence::Redo
1355             || ke == QKeySequence::Undo
1356             || ke == QKeySequence::MoveToNextWord
1357             || ke == QKeySequence::MoveToPreviousWord
1358             || ke == QKeySequence::MoveToStartOfDocument
1359             || ke == QKeySequence::MoveToEndOfDocument
1360             || ke == QKeySequence::SelectNextWord
1361             || ke == QKeySequence::SelectPreviousWord
1362             || ke == QKeySequence::SelectStartOfLine
1363             || ke == QKeySequence::SelectEndOfLine
1364             || ke == QKeySequence::SelectStartOfBlock
1365             || ke == QKeySequence::SelectEndOfBlock
1366             || ke == QKeySequence::SelectStartOfDocument
1367             || ke == QKeySequence::SelectAll
1368             || ke == QKeySequence::SelectEndOfDocument) {
1369             ke->accept();
1370         } else if (ke->modifiers() == Qt::NoModifier || ke->modifiers() == Qt::ShiftModifier
1371                    || ke->modifiers() == Qt::KeypadModifier) {
1372             if (ke->key() < Qt::Key_Escape) {
1373                 ke->accept();
1374                 return true;
1375             } else {
1376                 switch (ke->key()) {
1377                 case Qt::Key_Delete:
1378                 case Qt::Key_Home:
1379                 case Qt::Key_End:
1380                 case Qt::Key_Backspace:
1381                 case Qt::Key_Left:
1382                 case Qt::Key_Right:
1383                     return true;
1384                 default:
1385                     break;
1386                 }
1387             }
1388         }
1389     }
1390 #endif
1391
1392     return QQuickImplicitSizeItem::event(ev);
1393 }
1394
1395 void QQuickTextInput::geometryChanged(const QRectF &newGeometry,
1396                                   const QRectF &oldGeometry)
1397 {
1398     Q_D(QQuickTextInput);
1399     if (newGeometry.width() != oldGeometry.width())
1400         d->updateLayout();
1401     updateCursorRectangle();
1402     QQuickImplicitSizeItem::geometryChanged(newGeometry, oldGeometry);
1403 }
1404
1405 void QQuickTextInputPrivate::updateHorizontalScroll()
1406 {
1407     Q_Q(QQuickTextInput);
1408     QTextLine currentLine = m_textLayout.lineForTextPosition(m_cursor + m_preeditCursor);
1409     const int preeditLength = m_textLayout.preeditAreaText().length();
1410     const int width = q->width();
1411     int widthUsed = currentLine.isValid() ? qRound(currentLine.naturalTextWidth()) : 0;
1412     int previousScroll = hscroll;
1413
1414     if (!autoScroll || widthUsed <=  width || m_echoMode == QQuickTextInput::NoEcho) {
1415         hscroll = 0;
1416     } else {
1417         int cix = qRound(currentLine.cursorToX(m_cursor + preeditLength));
1418         if (cix - hscroll >= width) {
1419             // text doesn't fit, cursor is to the right of br (scroll right)
1420             hscroll = cix - width;
1421         } else if (cix - hscroll < 0 && hscroll < widthUsed) {
1422             // text doesn't fit, cursor is to the left of br (scroll left)
1423             hscroll = cix;
1424         } else if (widthUsed - hscroll < width) {
1425             // text doesn't fit, text document is to the left of br; align
1426             // right
1427             hscroll = widthUsed - width;
1428         }
1429         if (preeditLength > 0) {
1430             // check to ensure long pre-edit text doesn't push the cursor
1431             // off to the left
1432              cix = qRound(currentLine.cursorToX(m_cursor + qMax(0, m_preeditCursor - 1)));
1433              if (cix < hscroll)
1434                  hscroll = cix;
1435         }
1436     }
1437     if (previousScroll != hscroll)
1438         textLayoutDirty = true;
1439 }
1440
1441 void QQuickTextInputPrivate::updateVerticalScroll()
1442 {
1443     Q_Q(QQuickTextInput);
1444     const int preeditLength = m_textLayout.preeditAreaText().length();
1445     const int height = q->height();
1446     int heightUsed = boundingRect.height();
1447     int previousScroll = vscroll;
1448
1449     if (!autoScroll || heightUsed <=  height) {
1450         // text fits in br; use vscroll for alignment
1451         switch (vAlign & ~(Qt::AlignAbsolute|Qt::AlignHorizontal_Mask)) {
1452         case Qt::AlignBottom:
1453             vscroll = heightUsed - height;
1454             break;
1455         case Qt::AlignVCenter:
1456             vscroll = (heightUsed - height) / 2;
1457             break;
1458         default:
1459             // Top
1460             vscroll = 0;
1461             break;
1462         }
1463     } else {
1464         QRectF r = m_textLayout.lineForTextPosition(m_cursor + preeditLength).rect();
1465         int top = qFloor(r.top());
1466         int bottom = qCeil(r.bottom());
1467
1468         if (bottom - vscroll >= height) {
1469             // text doesn't fit, cursor is to the below the br (scroll down)
1470             vscroll = bottom - height;
1471         } else if (top - vscroll < 0 && vscroll < heightUsed) {
1472             // text doesn't fit, cursor is above br (scroll up)
1473             vscroll = top;
1474         } else if (heightUsed - vscroll < height) {
1475             // text doesn't fit, text document is to the left of br; align
1476             // right
1477             vscroll = heightUsed - height;
1478         }
1479         if (preeditLength > 0) {
1480             // check to ensure long pre-edit text doesn't push the cursor
1481             // off the top
1482              top = qRound(m_textLayout.lineForTextPosition(
1483                     m_cursor + qMax(0, m_preeditCursor - 1)).rect().top());
1484              if (top < vscroll)
1485                  vscroll = top;
1486         }
1487     }
1488     if (previousScroll != vscroll)
1489         textLayoutDirty = true;
1490 }
1491
1492 QSGNode *QQuickTextInput::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data)
1493 {
1494     Q_UNUSED(data);
1495     Q_D(QQuickTextInput);
1496
1497     QQuickTextNode *node = static_cast<QQuickTextNode *>(oldNode);
1498     if (node == 0)
1499         node = new QQuickTextNode(QQuickItemPrivate::get(this)->sceneGraphContext());
1500     d->textNode = node;
1501
1502     if (!d->textLayoutDirty) {
1503         QSGSimpleRectNode *cursorNode = node->cursorNode();
1504         if (cursorNode != 0 && !isReadOnly()) {
1505             cursorNode->setRect(cursorRectangle());
1506
1507             if (!d->cursorVisible || (!d->m_blinkStatus && d->m_blinkPeriod > 0)) {
1508                 d->hideCursor();
1509             } else {
1510                 d->showCursor();
1511             }
1512         }
1513     } else {
1514         node->deleteContent();
1515         node->setMatrix(QMatrix4x4());
1516
1517         QPoint offset = QPoint(0,0);
1518         QFontMetrics fm = QFontMetrics(d->font);
1519         if (d->autoScroll) {
1520             // the y offset is there to keep the baseline constant in case we have script changes in the text.
1521             offset = -QPoint(d->hscroll, d->vscroll + d->m_ascent - fm.ascent());
1522         } else {
1523             offset = -QPoint(d->hscroll, d->vscroll);
1524         }
1525
1526         if (!d->m_textLayout.text().isEmpty()) {
1527             node->addTextLayout(offset, &d->m_textLayout, d->color,
1528                                 QQuickText::Normal, QColor(),
1529                                 d->selectionColor, d->selectedTextColor,
1530                                 d->selectionStart(),
1531                                 d->selectionEnd() - 1); // selectionEnd() returns first char after
1532                                                                  // selection
1533         }
1534
1535         if (!isReadOnly() && d->cursorItem == 0) {
1536             node->setCursor(cursorRectangle(), d->color);
1537             if (!d->cursorVisible || (!d->m_blinkStatus && d->m_blinkPeriod > 0)) {
1538                 d->hideCursor();
1539             } else {
1540                 d->showCursor();
1541             }
1542         }
1543
1544         d->textLayoutDirty = false;
1545     }
1546
1547     return node;
1548 }
1549
1550 QVariant QQuickTextInput::inputMethodQuery(Qt::InputMethodQuery property) const
1551 {
1552     Q_D(const QQuickTextInput);
1553     switch (property) {
1554     case Qt::ImEnabled:
1555         return QVariant((bool)(flags() & ItemAcceptsInputMethod));
1556     case Qt::ImHints:
1557         return QVariant((int)inputMethodHints());
1558     case Qt::ImCursorRectangle:
1559         return cursorRectangle();
1560     case Qt::ImFont:
1561         return font();
1562     case Qt::ImCursorPosition:
1563         return QVariant(d->m_cursor);
1564     case Qt::ImSurroundingText:
1565         if (d->m_echoMode == PasswordEchoOnEdit && !d->m_passwordEchoEditing) {
1566             return QVariant(displayText());
1567         } else {
1568             return QVariant(d->realText());
1569         }
1570     case Qt::ImCurrentSelection:
1571         return QVariant(selectedText());
1572     case Qt::ImMaximumTextLength:
1573         return QVariant(maxLength());
1574     case Qt::ImAnchorPosition:
1575         if (d->selectionStart() == d->selectionEnd())
1576             return QVariant(d->m_cursor);
1577         else if (d->selectionStart() == d->m_cursor)
1578             return QVariant(d->selectionEnd());
1579         else
1580             return QVariant(d->selectionStart());
1581     default:
1582         return QVariant();
1583     }
1584 }
1585
1586 /*!
1587     \qmlmethod void QtQuick2::TextInput::deselect()
1588
1589     Removes active text selection.
1590 */
1591 void QQuickTextInput::deselect()
1592 {
1593     Q_D(QQuickTextInput);
1594     d->deselect();
1595 }
1596
1597 /*!
1598     \qmlmethod void QtQuick2::TextInput::selectAll()
1599
1600     Causes all text to be selected.
1601 */
1602 void QQuickTextInput::selectAll()
1603 {
1604     Q_D(QQuickTextInput);
1605     d->setSelection(0, text().length());
1606 }
1607
1608 /*!
1609     \qmlmethod void QtQuick2::TextInput::isRightToLeft(int start, int end)
1610
1611     Returns true if the natural reading direction of the editor text
1612     found between positions \a start and \a end is right to left.
1613 */
1614 bool QQuickTextInput::isRightToLeft(int start, int end)
1615 {
1616     if (start > end) {
1617         qmlInfo(this) << "isRightToLeft(start, end) called with the end property being smaller than the start.";
1618         return false;
1619     } else {
1620         return text().mid(start, end - start).isRightToLeft();
1621     }
1622 }
1623
1624 #ifndef QT_NO_CLIPBOARD
1625 /*!
1626     \qmlmethod QtQuick2::TextInput::cut()
1627
1628     Moves the currently selected text to the system clipboard.
1629 */
1630 void QQuickTextInput::cut()
1631 {
1632     Q_D(QQuickTextInput);
1633     d->copy();
1634     d->del();
1635 }
1636
1637 /*!
1638     \qmlmethod QtQuick2::TextInput::copy()
1639
1640     Copies the currently selected text to the system clipboard.
1641 */
1642 void QQuickTextInput::copy()
1643 {
1644     Q_D(QQuickTextInput);
1645     d->copy();
1646 }
1647
1648 /*!
1649     \qmlmethod QtQuick2::TextInput::paste()
1650
1651     Replaces the currently selected text by the contents of the system clipboard.
1652 */
1653 void QQuickTextInput::paste()
1654 {
1655     Q_D(QQuickTextInput);
1656     if (!d->m_readOnly)
1657         d->paste();
1658 }
1659 #endif // QT_NO_CLIPBOARD
1660
1661 /*!
1662     \qmlmethod void QtQuick2::TextInput::selectWord()
1663
1664     Causes the word closest to the current cursor position to be selected.
1665 */
1666 void QQuickTextInput::selectWord()
1667 {
1668     Q_D(QQuickTextInput);
1669     d->selectWordAtPos(d->m_cursor);
1670 }
1671
1672 /*!
1673     \qmlproperty bool QtQuick2::TextInput::smooth
1674
1675     This property holds whether the text is smoothly scaled or transformed.
1676
1677     Smooth filtering gives better visual quality, but is slower.  If
1678     the item is displayed at its natural size, this property has no visual or
1679     performance effect.
1680
1681     \note Generally scaling artifacts are only visible if the item is stationary on
1682     the screen.  A common pattern when animating an item is to disable smooth
1683     filtering at the beginning of the animation and reenable it at the conclusion.
1684 */
1685
1686 /*!
1687    \qmlproperty string QtQuick2::TextInput::passwordCharacter
1688
1689    This is the character displayed when echoMode is set to Password or
1690    PasswordEchoOnEdit. By default it is an asterisk.
1691
1692    If this property is set to a string with more than one character,
1693    the first character is used. If the string is empty, the value
1694    is ignored and the property is not set.
1695 */
1696 QString QQuickTextInput::passwordCharacter() const
1697 {
1698     Q_D(const QQuickTextInput);
1699     return QString(d->m_passwordCharacter);
1700 }
1701
1702 void QQuickTextInput::setPasswordCharacter(const QString &str)
1703 {
1704     Q_D(QQuickTextInput);
1705     if (str.length() < 1)
1706         return;
1707     d->m_passwordCharacter = str.constData()[0];
1708     if (d->m_echoMode == Password || d->m_echoMode == PasswordEchoOnEdit)
1709         d->updateDisplayText();
1710     emit passwordCharacterChanged();
1711 }
1712
1713 /*!
1714    \qmlproperty string QtQuick2::TextInput::displayText
1715
1716    This is the text displayed in the TextInput.
1717
1718    If \l echoMode is set to TextInput::Normal, this holds the
1719    same value as the TextInput::text property. Otherwise,
1720    this property holds the text visible to the user, while
1721    the \l text property holds the actual entered text.
1722 */
1723 QString QQuickTextInput::displayText() const
1724 {
1725     Q_D(const QQuickTextInput);
1726     return d->m_textLayout.text();
1727 }
1728
1729 /*!
1730     \qmlproperty bool QtQuick2::TextInput::selectByMouse
1731
1732     Defaults to false.
1733
1734     If true, the user can use the mouse to select text in some
1735     platform-specific way. Note that for some platforms this may
1736     not be an appropriate interaction (eg. may conflict with how
1737     the text needs to behave inside a Flickable.
1738 */
1739 bool QQuickTextInput::selectByMouse() const
1740 {
1741     Q_D(const QQuickTextInput);
1742     return d->selectByMouse;
1743 }
1744
1745 void QQuickTextInput::setSelectByMouse(bool on)
1746 {
1747     Q_D(QQuickTextInput);
1748     if (d->selectByMouse != on) {
1749         d->selectByMouse = on;
1750         emit selectByMouseChanged(on);
1751     }
1752 }
1753
1754 /*!
1755     \qmlproperty enum QtQuick2::TextInput::mouseSelectionMode
1756
1757     Specifies how text should be selected using a mouse.
1758
1759     \list
1760     \o TextInput.SelectCharacters - The selection is updated with individual characters. (Default)
1761     \o TextInput.SelectWords - The selection is updated with whole words.
1762     \endlist
1763
1764     This property only applies when \l selectByMouse is true.
1765 */
1766
1767 QQuickTextInput::SelectionMode QQuickTextInput::mouseSelectionMode() const
1768 {
1769     Q_D(const QQuickTextInput);
1770     return d->mouseSelectionMode;
1771 }
1772
1773 void QQuickTextInput::setMouseSelectionMode(SelectionMode mode)
1774 {
1775     Q_D(QQuickTextInput);
1776     if (d->mouseSelectionMode != mode) {
1777         d->mouseSelectionMode = mode;
1778         emit mouseSelectionModeChanged(mode);
1779     }
1780 }
1781
1782 /*!
1783     \qmlproperty bool QtQuick2::TextInput::canPaste
1784
1785     Returns true if the TextInput is writable and the content of the clipboard is
1786     suitable for pasting into the TextEdit.
1787 */
1788 bool QQuickTextInput::canPaste() const
1789 {
1790     Q_D(const QQuickTextInput);
1791     return d->canPaste;
1792 }
1793
1794 void QQuickTextInput::moveCursorSelection(int position)
1795 {
1796     Q_D(QQuickTextInput);
1797     d->moveCursor(position, true);
1798 }
1799
1800 /*!
1801     \qmlmethod void QtQuick2::TextInput::moveCursorSelection(int position, SelectionMode mode = TextInput.SelectCharacters)
1802
1803     Moves the cursor to \a position and updates the selection according to the optional \a mode
1804     parameter.  (To only move the cursor, set the \l cursorPosition property.)
1805
1806     When this method is called it additionally sets either the
1807     selectionStart or the selectionEnd (whichever was at the previous cursor position)
1808     to the specified position. This allows you to easily extend and contract the selected
1809     text range.
1810
1811     The selection mode specifies whether the selection is updated on a per character or a per word
1812     basis.  If not specified the selection mode will default to TextInput.SelectCharacters.
1813
1814     \list
1815     \o TextEdit.SelectCharacters - Sets either the selectionStart or selectionEnd (whichever was at
1816     the previous cursor position) to the specified position.
1817     \o TextEdit.SelectWords - Sets the selectionStart and selectionEnd to include all
1818     words between the specified position and the previous cursor position.  Words partially in the
1819     range are included.
1820     \endlist
1821
1822     For example, take this sequence of calls:
1823
1824     \code
1825         cursorPosition = 5
1826         moveCursorSelection(9, TextInput.SelectCharacters)
1827         moveCursorSelection(7, TextInput.SelectCharacters)
1828     \endcode
1829
1830     This moves the cursor to position 5, extend the selection end from 5 to 9
1831     and then retract the selection end from 9 to 7, leaving the text from position 5 to 7
1832     selected (the 6th and 7th characters).
1833
1834     The same sequence with TextInput.SelectWords will extend the selection start to a word boundary
1835     before or on position 5 and extend the selection end to a word boundary on or past position 9.
1836 */
1837 void QQuickTextInput::moveCursorSelection(int pos, SelectionMode mode)
1838 {
1839     Q_D(QQuickTextInput);
1840
1841     if (mode == SelectCharacters) {
1842         d->moveCursor(pos, true);
1843     } else if (pos != d->m_cursor){
1844         const int cursor = d->m_cursor;
1845         int anchor;
1846         if (!d->hasSelectedText())
1847             anchor = d->m_cursor;
1848         else if (d->selectionStart() == d->m_cursor)
1849             anchor = d->selectionEnd();
1850         else
1851             anchor = d->selectionStart();
1852
1853         if (anchor < pos || (anchor == pos && cursor < pos)) {
1854             const QString text = this->text();
1855             QTextBoundaryFinder finder(QTextBoundaryFinder::Word, text);
1856             finder.setPosition(anchor);
1857
1858             const QTextBoundaryFinder::BoundaryReasons reasons = finder.boundaryReasons();
1859             if (anchor < text.length() && (!(reasons & QTextBoundaryFinder::StartWord)
1860                     || ((reasons & QTextBoundaryFinder::EndWord) && anchor > cursor))) {
1861                 finder.toPreviousBoundary();
1862             }
1863             anchor = finder.position() != -1 ? finder.position() : 0;
1864
1865             finder.setPosition(pos);
1866             if (pos > 0 && !finder.boundaryReasons())
1867                 finder.toNextBoundary();
1868             const int cursor = finder.position() != -1 ? finder.position() : text.length();
1869
1870             d->setSelection(anchor, cursor - anchor);
1871         } else if (anchor > pos || (anchor == pos && cursor > pos)) {
1872             const QString text = this->text();
1873             QTextBoundaryFinder finder(QTextBoundaryFinder::Word, text);
1874             finder.setPosition(anchor);
1875
1876             const QTextBoundaryFinder::BoundaryReasons reasons = finder.boundaryReasons();
1877             if (anchor > 0 && (!(reasons & QTextBoundaryFinder::EndWord)
1878                     || ((reasons & QTextBoundaryFinder::StartWord) && anchor < cursor))) {
1879                 finder.toNextBoundary();
1880             }
1881
1882             anchor = finder.position() != -1 ? finder.position() : text.length();
1883
1884             finder.setPosition(pos);
1885             if (pos < text.length() && !finder.boundaryReasons())
1886                  finder.toPreviousBoundary();
1887             const int cursor = finder.position() != -1 ? finder.position() : 0;
1888
1889             d->setSelection(anchor, cursor - anchor);
1890         }
1891     }
1892 }
1893
1894 /*!
1895     \qmlmethod void QtQuick2::TextInput::openSoftwareInputPanel()
1896
1897     Opens software input panels like virtual keyboards for typing, useful for
1898     customizing when you want the input keyboard to be shown and hidden in
1899     your application.
1900
1901     By default the opening of input panels follows the platform style. Input panels are
1902     always closed if no editor has active focus.
1903
1904     You can disable the automatic behavior by setting the property \c activeFocusOnPress to false
1905     and use functions openSoftwareInputPanel() and closeSoftwareInputPanel() to implement
1906     the behavior you want.
1907
1908     Only relevant on platforms, which provide virtual keyboards.
1909
1910     \qml
1911         import QtQuick 1.0
1912         TextInput {
1913             id: textInput
1914             text: "Hello world!"
1915             activeFocusOnPress: false
1916             MouseArea {
1917                 anchors.fill: parent
1918                 onClicked: {
1919                     if (!textInput.activeFocus) {
1920                         textInput.forceActiveFocus()
1921                         textInput.openSoftwareInputPanel();
1922                     } else {
1923                         textInput.focus = false;
1924                     }
1925                 }
1926                 onPressAndHold: textInput.closeSoftwareInputPanel();
1927             }
1928         }
1929     \endqml
1930 */
1931 void QQuickTextInput::openSoftwareInputPanel()
1932 {
1933     if (qGuiApp)
1934         qGuiApp->inputPanel()->show();
1935 }
1936
1937 /*!
1938     \qmlmethod void QtQuick2::TextInput::closeSoftwareInputPanel()
1939
1940     Closes a software input panel like a virtual keyboard shown on the screen, useful
1941     for customizing when you want the input keyboard to be shown and hidden in
1942     your application.
1943
1944     By default the opening of input panels follows the platform style. Input panels are
1945     always closed if no editor has active focus.
1946
1947     You can disable the automatic behavior by setting the property \c activeFocusOnPress to false
1948     and use functions openSoftwareInputPanel() and closeSoftwareInputPanel() to implement
1949     the behavior you want.
1950
1951     Only relevant on platforms, which provide virtual keyboards.
1952
1953     \qml
1954         import QtQuick 1.0
1955         TextInput {
1956             id: textInput
1957             text: "Hello world!"
1958             activeFocusOnPress: false
1959             MouseArea {
1960                 anchors.fill: parent
1961                 onClicked: {
1962                     if (!textInput.activeFocus) {
1963                         textInput.forceActiveFocus();
1964                         textInput.openSoftwareInputPanel();
1965                     } else {
1966                         textInput.focus = false;
1967                     }
1968                 }
1969                 onPressAndHold: textInput.closeSoftwareInputPanel();
1970             }
1971         }
1972     \endqml
1973 */
1974 void QQuickTextInput::closeSoftwareInputPanel()
1975 {
1976     if (qGuiApp)
1977         qGuiApp->inputPanel()->hide();
1978 }
1979
1980 void QQuickTextInput::focusInEvent(QFocusEvent *event)
1981 {
1982     Q_D(const QQuickTextInput);
1983     if (d->focusOnPress && !d->m_readOnly)
1984         openSoftwareInputPanel();
1985     QQuickImplicitSizeItem::focusInEvent(event);
1986 }
1987
1988 void QQuickTextInput::itemChange(ItemChange change, const ItemChangeData &value)
1989 {
1990     Q_D(QQuickTextInput);
1991     if (change == ItemActiveFocusHasChanged) {
1992         bool hasFocus = value.boolValue;
1993         d->focused = hasFocus;
1994         setCursorVisible(hasFocus); // ### refactor:  && d->canvas && d->canvas->hasFocus()
1995         if (echoMode() == QQuickTextInput::PasswordEchoOnEdit && !hasFocus)
1996             d->updatePasswordEchoEditing(false);//QQuickTextInputPrivate sets it on key events, but doesn't deal with focus events
1997         if (!hasFocus)
1998             d->deselect();
1999     }
2000     QQuickItem::itemChange(change, value);
2001 }
2002
2003 /*!
2004     \qmlproperty bool QtQuick2::TextInput::inputMethodComposing
2005
2006
2007     This property holds whether the TextInput has partial text input from an
2008     input method.
2009
2010     While it is composing an input method may rely on mouse or key events from
2011     the TextInput to edit or commit the partial text.  This property can be
2012     used to determine when to disable events handlers that may interfere with
2013     the correct operation of an input method.
2014 */
2015 bool QQuickTextInput::isInputMethodComposing() const
2016 {
2017     Q_D(const QQuickTextInput);
2018     return d->preeditAreaText().length() > 0;
2019 }
2020
2021 void QQuickTextInputPrivate::init()
2022 {
2023     Q_Q(QQuickTextInput);
2024     q->setSmooth(smooth);
2025     q->setAcceptedMouseButtons(Qt::LeftButton);
2026     q->setFlag(QQuickItem::ItemAcceptsInputMethod);
2027     q->setFlag(QQuickItem::ItemHasContents);
2028 #ifndef QT_NO_CLIPBOARD
2029     q->connect(q, SIGNAL(readOnlyChanged(bool)),
2030             q, SLOT(q_canPasteChanged()));
2031     q->connect(QGuiApplication::clipboard(), SIGNAL(dataChanged()),
2032             q, SLOT(q_canPasteChanged()));
2033     canPaste = !m_readOnly && QGuiApplication::clipboard()->text().length() != 0;
2034 #endif // QT_NO_CLIPBOARD
2035     m_textLayout.beginLayout();
2036     m_textLayout.createLine();
2037     m_textLayout.endLayout();
2038
2039     imHints &= ~Qt::ImhMultiLine;
2040     oldValidity = hasAcceptableInput(m_text);
2041     lastSelectionStart = 0;
2042     lastSelectionEnd = 0;
2043     selectedTextColor = m_palette.color(QPalette::HighlightedText);
2044     selectionColor = m_palette.color(QPalette::Highlight);
2045     determineHorizontalAlignment();
2046
2047     if (!qmlDisableDistanceField()) {
2048         QTextOption option = m_textLayout.textOption();
2049         option.setUseDesignMetrics(true);
2050         m_textLayout.setTextOption(option);
2051     }
2052 }
2053
2054 void QQuickTextInput::updateCursorRectangle()
2055 {
2056     Q_D(QQuickTextInput);
2057     if (!isComponentComplete())
2058         return;
2059
2060     d->updateHorizontalScroll();
2061     d->updateVerticalScroll();
2062     update();
2063     updateMicroFocus();
2064     emit cursorRectangleChanged();
2065     if (d->cursorItem) {
2066         QRectF r = cursorRectangle();
2067         d->cursorItem->setPos(r.topLeft());
2068         d->cursorItem->setHeight(r.height());
2069     }
2070 }
2071
2072 void QQuickTextInput::selectionChanged()
2073 {
2074     Q_D(QQuickTextInput);
2075     updateRect();//TODO: Only update rect in selection
2076     emit selectedTextChanged();
2077
2078     if (d->lastSelectionStart != d->selectionStart()) {
2079         d->lastSelectionStart = d->selectionStart();
2080         if (d->lastSelectionStart == -1)
2081             d->lastSelectionStart = d->m_cursor;
2082         emit selectionStartChanged();
2083     }
2084     if (d->lastSelectionEnd != d->selectionEnd()) {
2085         d->lastSelectionEnd = d->selectionEnd();
2086         if (d->lastSelectionEnd == -1)
2087             d->lastSelectionEnd = d->m_cursor;
2088         emit selectionEndChanged();
2089     }
2090 }
2091
2092 void QQuickTextInputPrivate::showCursor()
2093 {
2094     if (textNode != 0 && textNode->cursorNode() != 0)
2095         textNode->cursorNode()->setColor(color);
2096 }
2097
2098 void QQuickTextInputPrivate::hideCursor()
2099 {
2100     if (textNode != 0 && textNode->cursorNode() != 0)
2101         textNode->cursorNode()->setColor(QColor(0, 0, 0, 0));
2102 }
2103
2104 void QQuickTextInput::updateRect(const QRect &r)
2105 {
2106     Q_D(QQuickTextInput);
2107     if (!isComponentComplete())
2108         return;
2109
2110     if (r.isEmpty()) {
2111         d->textLayoutDirty = true;
2112     }
2113
2114     update();
2115 }
2116
2117 QRectF QQuickTextInput::boundingRect() const
2118 {
2119     Q_D(const QQuickTextInput);
2120
2121     QRectF r = d->boundingRect;
2122     int cursorWidth = d->cursorItem ? d->cursorItem->width() : d->m_cursorWidth;
2123
2124     // Could include font max left/right bearings to either side of rectangle.
2125
2126     r.setRight(r.right() + cursorWidth);
2127     r.translate(-d->hscroll, -d->vscroll);
2128     return r;
2129 }
2130
2131 void QQuickTextInput::q_canPasteChanged()
2132 {
2133     Q_D(QQuickTextInput);
2134     bool old = d->canPaste;
2135 #ifndef QT_NO_CLIPBOARD
2136     d->canPaste = !d->m_readOnly && QGuiApplication::clipboard()->text().length() != 0;
2137 #endif
2138     if (d->canPaste != old)
2139         emit canPasteChanged();
2140 }
2141
2142 // ### these should come from QStyleHints
2143 const int textCursorWidth = 1;
2144 const bool fullWidthSelection = true;
2145
2146 /*!
2147     \internal
2148
2149     Updates the display text based of the current edit text
2150     If the text has changed will emit displayTextChanged()
2151 */
2152 void QQuickTextInputPrivate::updateDisplayText(bool forceUpdate)
2153 {
2154     QString orig = m_textLayout.text();
2155     QString str;
2156     if (m_echoMode == QQuickTextInput::NoEcho)
2157         str = QString::fromLatin1("");
2158     else
2159         str = m_text;
2160
2161     if (m_echoMode == QQuickTextInput::Password
2162             || (m_echoMode == QQuickTextInput::PasswordEchoOnEdit && !m_passwordEchoEditing))
2163         str.fill(m_passwordCharacter);
2164
2165     // replace certain non-printable characters with spaces (to avoid
2166     // drawing boxes when using fonts that don't have glyphs for such
2167     // characters)
2168     QChar* uc = str.data();
2169     for (int i = 0; i < (int)str.length(); ++i) {
2170         if ((uc[i] < 0x20 && uc[i] != 0x09)
2171             || uc[i] == QChar::LineSeparator
2172             || uc[i] == QChar::ParagraphSeparator
2173             || uc[i] == QChar::ObjectReplacementCharacter)
2174             uc[i] = QChar(0x0020);
2175     }
2176
2177     if (str != orig || forceUpdate) {
2178         m_textLayout.setText(str);
2179         updateLayout(); // polish?
2180         emit q_func()->displayTextChanged();
2181     }
2182 }
2183
2184 void QQuickTextInputPrivate::updateLayout()
2185 {
2186     Q_Q(QQuickTextInput);
2187
2188     if (!q->isComponentComplete())
2189         return;
2190
2191     QTextOption option = m_textLayout.textOption();
2192     option.setTextDirection(m_layoutDirection);
2193     option.setFlags(QTextOption::IncludeTrailingSpaces);
2194     option.setWrapMode(QTextOption::WrapMode(wrapMode));
2195     option.setAlignment(Qt::Alignment(q->effectiveHAlign()));
2196     m_textLayout.setTextOption(option);
2197     m_textLayout.setFont(font);
2198
2199     boundingRect = QRectF();
2200     m_textLayout.beginLayout();
2201     QTextLine line = m_textLayout.createLine();
2202     qreal lineWidth = q->widthValid() ? q->width() : INT_MAX;
2203     qreal height = 0;
2204     QTextLine firstLine = line;
2205     do {
2206         line.setLineWidth(lineWidth);
2207         line.setPosition(QPointF(line.position().x(), height));
2208         boundingRect = boundingRect.united(line.naturalTextRect());
2209
2210         height += line.height();
2211         line = m_textLayout.createLine();
2212     } while (line.isValid());
2213     m_textLayout.endLayout();
2214
2215     option.setWrapMode(QTextOption::NoWrap);
2216     m_textLayout.setTextOption(option);
2217
2218     m_ascent = qRound(firstLine.ascent());
2219     textLayoutDirty = true;
2220
2221     q->update();
2222     q->setImplicitSize(qCeil(boundingRect.width()), qCeil(boundingRect.height()));
2223
2224 }
2225
2226 #ifndef QT_NO_CLIPBOARD
2227 /*!
2228     \internal
2229
2230     Copies the currently selected text into the clipboard using the given
2231     \a mode.
2232
2233     \note If the echo mode is set to a mode other than Normal then copy
2234     will not work.  This is to prevent using copy as a method of bypassing
2235     password features of the line control.
2236 */
2237 void QQuickTextInputPrivate::copy(QClipboard::Mode mode) const
2238 {
2239     QString t = selectedText();
2240     if (!t.isEmpty() && m_echoMode == QQuickTextInput::Normal) {
2241         QGuiApplication::clipboard()->setText(t, mode);
2242     }
2243 }
2244
2245 /*!
2246     \internal
2247
2248     Inserts the text stored in the application clipboard into the line
2249     control.
2250
2251     \sa insert()
2252 */
2253 void QQuickTextInputPrivate::paste(QClipboard::Mode clipboardMode)
2254 {
2255     QString clip = QGuiApplication::clipboard()->text(clipboardMode);
2256     if (!clip.isEmpty() || hasSelectedText()) {
2257         separate(); //make it a separate undo/redo command
2258         insert(clip);
2259         separate();
2260     }
2261 }
2262
2263 #endif // !QT_NO_CLIPBOARD
2264
2265 /*!
2266     \internal
2267
2268     Exits preedit mode and commits parts marked as tentative commit
2269 */
2270 void QQuickTextInputPrivate::commitPreedit()
2271 {
2272     if (!composeMode())
2273         return;
2274
2275     qApp->inputPanel()->reset();
2276
2277     if (!m_tentativeCommit.isEmpty()) {
2278         internalInsert(m_tentativeCommit);
2279         m_tentativeCommit.clear();
2280         finishChange(-1, true/*not used, not documented*/, false);
2281     }
2282
2283     m_preeditCursor = 0;
2284     m_textLayout.setPreeditArea(-1, QString());
2285     m_textLayout.clearAdditionalFormats();
2286     updateLayout();
2287 }
2288
2289 /*!
2290     \internal
2291
2292     Handles the behavior for the backspace key or function.
2293     Removes the current selection if there is a selection, otherwise
2294     removes the character prior to the cursor position.
2295
2296     \sa del()
2297 */
2298 void QQuickTextInputPrivate::backspace()
2299 {
2300     int priorState = m_undoState;
2301     if (hasSelectedText()) {
2302         removeSelectedText();
2303     } else if (m_cursor) {
2304             --m_cursor;
2305             if (m_maskData)
2306                 m_cursor = prevMaskBlank(m_cursor);
2307             QChar uc = m_text.at(m_cursor);
2308             if (m_cursor > 0 && uc.unicode() >= 0xdc00 && uc.unicode() < 0xe000) {
2309                 // second half of a surrogate, check if we have the first half as well,
2310                 // if yes delete both at once
2311                 uc = m_text.at(m_cursor - 1);
2312                 if (uc.unicode() >= 0xd800 && uc.unicode() < 0xdc00) {
2313                     internalDelete(true);
2314                     --m_cursor;
2315                 }
2316             }
2317             internalDelete(true);
2318     }
2319     finishChange(priorState);
2320 }
2321
2322 /*!
2323     \internal
2324
2325     Handles the behavior for the delete key or function.
2326     Removes the current selection if there is a selection, otherwise
2327     removes the character after the cursor position.
2328
2329     \sa del()
2330 */
2331 void QQuickTextInputPrivate::del()
2332 {
2333     int priorState = m_undoState;
2334     if (hasSelectedText()) {
2335         removeSelectedText();
2336     } else {
2337         int n = m_textLayout.nextCursorPosition(m_cursor) - m_cursor;
2338         while (n--)
2339             internalDelete();
2340     }
2341     finishChange(priorState);
2342 }
2343
2344 /*!
2345     \internal
2346
2347     Inserts the given \a newText at the current cursor position.
2348     If there is any selected text it is removed prior to insertion of
2349     the new text.
2350 */
2351 void QQuickTextInputPrivate::insert(const QString &newText)
2352 {
2353     int priorState = m_undoState;
2354     removeSelectedText();
2355     internalInsert(newText);
2356     finishChange(priorState);
2357 }
2358
2359 /*!
2360     \internal
2361
2362     Clears the line control text.
2363 */
2364 void QQuickTextInputPrivate::clear()
2365 {
2366     int priorState = m_undoState;
2367     m_selstart = 0;
2368     m_selend = m_text.length();
2369     removeSelectedText();
2370     separate();
2371     finishChange(priorState, /*update*/false, /*edited*/false);
2372 }
2373
2374 /*!
2375     \internal
2376
2377     Sets \a length characters from the given \a start position as selected.
2378     The given \a start position must be within the current text for
2379     the line control.  If \a length characters cannot be selected, then
2380     the selection will extend to the end of the current text.
2381 */
2382 void QQuickTextInputPrivate::setSelection(int start, int length)
2383 {
2384     Q_Q(QQuickTextInput);
2385     commitPreedit();
2386
2387     if (start < 0 || start > (int)m_text.length()){
2388         qWarning("QQuickTextInputPrivate::setSelection: Invalid start position");
2389         return;
2390     }
2391
2392     if (length > 0) {
2393         if (start == m_selstart && start + length == m_selend && m_cursor == m_selend)
2394             return;
2395         m_selstart = start;
2396         m_selend = qMin(start + length, (int)m_text.length());
2397         m_cursor = m_selend;
2398     } else if (length < 0){
2399         if (start == m_selend && start + length == m_selstart && m_cursor == m_selstart)
2400             return;
2401         m_selstart = qMax(start + length, 0);
2402         m_selend = start;
2403         m_cursor = m_selstart;
2404     } else if (m_selstart != m_selend) {
2405         m_selstart = 0;
2406         m_selend = 0;
2407         m_cursor = start;
2408     } else {
2409         m_cursor = start;
2410         emitCursorPositionChanged();
2411         return;
2412     }
2413     emit q->selectionChanged();
2414     emitCursorPositionChanged();
2415 }
2416
2417 /*!
2418     \internal
2419
2420     Initializes the line control with a starting text value of \a txt.
2421 */
2422 void QQuickTextInputPrivate::init(const QString &txt)
2423 {
2424     m_text = txt;
2425
2426     updateDisplayText();
2427     m_cursor = m_text.length();
2428 }
2429
2430 /*!
2431     \internal
2432
2433     Sets the password echo editing to \a editing.  If password echo editing
2434     is true, then the text of the password is displayed even if the echo
2435     mode is set to QLineEdit::PasswordEchoOnEdit.  Password echoing editing
2436     does not affect other echo modes.
2437 */
2438 void QQuickTextInputPrivate::updatePasswordEchoEditing(bool editing)
2439 {
2440     m_passwordEchoEditing = editing;
2441     updateDisplayText();
2442 }
2443
2444 /*!
2445     \internal
2446
2447     Fixes the current text so that it is valid given any set validators.
2448
2449     Returns true if the text was changed.  Otherwise returns false.
2450 */
2451 bool QQuickTextInputPrivate::fixup() // this function assumes that validate currently returns != Acceptable
2452 {
2453 #ifndef QT_NO_VALIDATOR
2454     if (m_validator) {
2455         QString textCopy = m_text;
2456         int cursorCopy = m_cursor;
2457         m_validator->fixup(textCopy);
2458         if (m_validator->validate(textCopy, cursorCopy) == QValidator::Acceptable) {
2459             if (textCopy != m_text || cursorCopy != m_cursor)
2460                 internalSetText(textCopy, cursorCopy);
2461             return true;
2462         }
2463     }
2464 #endif
2465     return false;
2466 }
2467
2468 /*!
2469     \internal
2470
2471     Moves the cursor to the given position \a pos.   If \a mark is true will
2472     adjust the currently selected text.
2473 */
2474 void QQuickTextInputPrivate::moveCursor(int pos, bool mark)
2475 {
2476     Q_Q(QQuickTextInput);
2477     commitPreedit();
2478
2479     if (pos != m_cursor) {
2480         separate();
2481         if (m_maskData)
2482             pos = pos > m_cursor ? nextMaskBlank(pos) : prevMaskBlank(pos);
2483     }
2484     if (mark) {
2485         int anchor;
2486         if (m_selend > m_selstart && m_cursor == m_selstart)
2487             anchor = m_selend;
2488         else if (m_selend > m_selstart && m_cursor == m_selend)
2489             anchor = m_selstart;
2490         else
2491             anchor = m_cursor;
2492         m_selstart = qMin(anchor, pos);
2493         m_selend = qMax(anchor, pos);
2494     } else {
2495         internalDeselect();
2496     }
2497     m_cursor = pos;
2498     if (mark || m_selDirty) {
2499         m_selDirty = false;
2500         emit q->selectionChanged();
2501     }
2502     emitCursorPositionChanged();
2503 }
2504
2505 /*!
2506     \internal
2507
2508     Applies the given input method event \a event to the text of the line
2509     control
2510 */
2511 void QQuickTextInputPrivate::processInputMethodEvent(QInputMethodEvent *event)
2512 {
2513     Q_Q(QQuickTextInput);
2514
2515     int priorState = -1;
2516     bool isGettingInput = !event->commitString().isEmpty()
2517             || event->preeditString() != preeditAreaText()
2518             || event->replacementLength() > 0;
2519     bool cursorPositionChanged = false;
2520     bool selectionChange = false;
2521     m_preeditDirty = event->preeditString() != preeditAreaText();
2522
2523     if (isGettingInput) {
2524         // If any text is being input, remove selected text.
2525         priorState = m_undoState;
2526         if (m_echoMode == QQuickTextInput::PasswordEchoOnEdit && !m_passwordEchoEditing) {
2527             updatePasswordEchoEditing(true);
2528             m_selstart = 0;
2529             m_selend = m_text.length();
2530         }
2531         removeSelectedText();
2532     }
2533
2534     int c = m_cursor; // cursor position after insertion of commit string
2535     if (event->replacementStart() <= 0)
2536         c += event->commitString().length() - qMin(-event->replacementStart(), event->replacementLength());
2537
2538     m_cursor += event->replacementStart();
2539     if (m_cursor < 0)
2540         m_cursor = 0;
2541
2542     // insert commit string
2543     if (event->replacementLength()) {
2544         m_selstart = m_cursor;
2545         m_selend = m_selstart + event->replacementLength();
2546         removeSelectedText();
2547     }
2548     if (!event->commitString().isEmpty()) {
2549         internalInsert(event->commitString());
2550         cursorPositionChanged = true;
2551     }
2552
2553     m_cursor = qBound(0, c, m_text.length());
2554
2555     for (int i = 0; i < event->attributes().size(); ++i) {
2556         const QInputMethodEvent::Attribute &a = event->attributes().at(i);
2557         if (a.type == QInputMethodEvent::Selection) {
2558             m_cursor = qBound(0, a.start + a.length, m_text.length());
2559             if (a.length) {
2560                 m_selstart = qMax(0, qMin(a.start, m_text.length()));
2561                 m_selend = m_cursor;
2562                 if (m_selend < m_selstart) {
2563                     qSwap(m_selstart, m_selend);
2564                 }
2565                 selectionChange = true;
2566             } else {
2567                 m_selstart = m_selend = 0;
2568             }
2569             cursorPositionChanged = true;
2570         }
2571     }
2572 #ifndef QT_NO_IM
2573     m_textLayout.setPreeditArea(m_cursor, event->preeditString());
2574 #endif //QT_NO_IM
2575     const int oldPreeditCursor = m_preeditCursor;
2576     m_preeditCursor = event->preeditString().length();
2577     m_hideCursor = false;
2578     QList<QTextLayout::FormatRange> formats;
2579     for (int i = 0; i < event->attributes().size(); ++i) {
2580         const QInputMethodEvent::Attribute &a = event->attributes().at(i);
2581         if (a.type == QInputMethodEvent::Cursor) {
2582             m_preeditCursor = a.start;
2583             m_hideCursor = !a.length;
2584         } else if (a.type == QInputMethodEvent::TextFormat) {
2585             QTextCharFormat f = qvariant_cast<QTextFormat>(a.value).toCharFormat();
2586             if (f.isValid()) {
2587                 QTextLayout::FormatRange o;
2588                 o.start = a.start + m_cursor;
2589                 o.length = a.length;
2590                 o.format = f;
2591                 formats.append(o);
2592             }
2593         }
2594     }
2595     m_textLayout.setAdditionalFormats(formats);
2596
2597     updateDisplayText(/*force*/ true);
2598     if (cursorPositionChanged)
2599         emitCursorPositionChanged();
2600     else if (m_preeditCursor != oldPreeditCursor)
2601         q->updateCursorRectangle();
2602
2603     bool tentativeCommitChanged = m_tentativeCommit != event->tentativeCommitString();
2604
2605     if (tentativeCommitChanged) {
2606         m_textDirty = true;
2607         m_tentativeCommit = event->tentativeCommitString();
2608     }
2609
2610     if (isGettingInput || tentativeCommitChanged)
2611         finishChange(priorState);
2612
2613     if (selectionChange)
2614         emit q->selectionChanged();
2615 }
2616
2617 /*!
2618     \internal
2619
2620     Sets the selection to cover the word at the given cursor position.
2621     The word boundaries are defined by the behavior of QTextLayout::SkipWords
2622     cursor mode.
2623 */
2624 void QQuickTextInputPrivate::selectWordAtPos(int cursor)
2625 {
2626     int next = cursor + 1;
2627     if (next > end())
2628         --next;
2629     int c = m_textLayout.previousCursorPosition(next, QTextLayout::SkipWords);
2630     moveCursor(c, false);
2631     // ## text layout should support end of words.
2632     int end = m_textLayout.nextCursorPosition(c, QTextLayout::SkipWords);
2633     while (end > cursor && m_text[end-1].isSpace())
2634         --end;
2635     moveCursor(end, true);
2636 }
2637
2638 /*!
2639     \internal
2640
2641     Completes a change to the line control text.  If the change is not valid
2642     will undo the line control state back to the given \a validateFromState.
2643
2644     If \a edited is true and the change is valid, will emit textEdited() in
2645     addition to textChanged().  Otherwise only emits textChanged() on a valid
2646     change.
2647
2648     The \a update value is currently unused.
2649 */
2650 bool QQuickTextInputPrivate::finishChange(int validateFromState, bool update, bool /*edited*/)
2651 {
2652     Q_Q(QQuickTextInput);
2653
2654     Q_UNUSED(update)
2655
2656     if (m_textDirty) {
2657         // do validation
2658         bool wasValidInput = m_validInput;
2659         m_validInput = true;
2660 #ifndef QT_NO_VALIDATOR
2661         if (m_validator) {
2662             QString textCopy = m_text;
2663             int cursorCopy = m_cursor;
2664             m_validInput = (m_validator->validate(textCopy, cursorCopy) != QValidator::Invalid);
2665             if (m_validInput) {
2666                 if (m_text != textCopy) {
2667                     internalSetText(textCopy, cursorCopy);
2668                     return true;
2669                 }
2670                 m_cursor = cursorCopy;
2671
2672                 if (!m_tentativeCommit.isEmpty()) {
2673                     textCopy.insert(m_cursor, m_tentativeCommit);
2674                     bool validInput = m_validator->validate(textCopy, cursorCopy) != QValidator::Invalid;
2675                     if (!validInput)
2676                         m_tentativeCommit.clear();
2677                 }
2678             } else {
2679                 m_tentativeCommit.clear();
2680             }
2681         }
2682 #endif
2683         if (validateFromState >= 0 && wasValidInput && !m_validInput) {
2684             if (m_transactions.count())
2685                 return false;
2686             internalUndo(validateFromState);
2687             m_history.resize(m_undoState);
2688             if (m_modifiedState > m_undoState)
2689                 m_modifiedState = -1;
2690             m_validInput = true;
2691             m_textDirty = false;
2692         }
2693         updateDisplayText();
2694
2695         if (m_textDirty) {
2696             m_textDirty = false;
2697             m_preeditDirty = false;
2698             determineHorizontalAlignment();
2699             emit q->textChanged();
2700         }
2701
2702         if (m_validInput != wasValidInput)
2703             emit q->acceptableInputChanged();
2704     }
2705     if (m_preeditDirty) {
2706         m_preeditDirty = false;
2707         determineHorizontalAlignment();
2708     }
2709     if (m_selDirty) {
2710         m_selDirty = false;
2711         emit q->selectionChanged();
2712     }
2713     emitCursorPositionChanged();
2714     return true;
2715 }
2716
2717 /*!
2718     \internal
2719
2720     An internal function for setting the text of the line control.
2721 */
2722 void QQuickTextInputPrivate::internalSetText(const QString &txt, int pos, bool edited)
2723 {
2724     Q_Q(QQuickTextInput);
2725     internalDeselect();
2726     QString oldText = m_text;
2727     if (m_maskData) {
2728         m_text = maskString(0, txt, true);
2729         m_text += clearString(m_text.length(), m_maxLength - m_text.length());
2730     } else {
2731         m_text = txt.isEmpty() ? txt : txt.left(m_maxLength);
2732     }
2733     m_history.clear();
2734     m_modifiedState =  m_undoState = 0;
2735     m_cursor = (pos < 0 || pos > m_text.length()) ? m_text.length() : pos;
2736     m_textDirty = (oldText != m_text);
2737
2738     bool changed = finishChange(-1, true, edited);
2739 #ifdef QT_NO_ACCESSIBILITY
2740     Q_UNUSED(changed)
2741 #else
2742     if (changed)
2743         QAccessible::updateAccessibility(q, 0, QAccessible::TextUpdated);
2744 #endif
2745 }
2746
2747
2748 /*!
2749     \internal
2750
2751     Adds the given \a command to the undo history
2752     of the line control.  Does not apply the command.
2753 */
2754 void QQuickTextInputPrivate::addCommand(const Command &cmd)
2755 {
2756     if (m_separator && m_undoState && m_history[m_undoState - 1].type != Separator) {
2757         m_history.resize(m_undoState + 2);
2758         m_history[m_undoState++] = Command(Separator, m_cursor, 0, m_selstart, m_selend);
2759     } else {
2760         m_history.resize(m_undoState + 1);
2761     }
2762     m_separator = false;
2763     m_history[m_undoState++] = cmd;
2764 }
2765
2766 /*!
2767     \internal
2768
2769     Inserts the given string \a s into the line
2770     control.
2771
2772     Also adds the appropriate commands into the undo history.
2773     This function does not call finishChange(), and may leave the text
2774     in an invalid state.
2775 */
2776 void QQuickTextInputPrivate::internalInsert(const QString &s)
2777 {
2778     if (hasSelectedText())
2779         addCommand(Command(SetSelection, m_cursor, 0, m_selstart, m_selend));
2780     if (m_maskData) {
2781         QString ms = maskString(m_cursor, s);
2782         for (int i = 0; i < (int) ms.length(); ++i) {
2783             addCommand (Command(DeleteSelection, m_cursor + i, m_text.at(m_cursor + i), -1, -1));
2784             addCommand(Command(Insert, m_cursor + i, ms.at(i), -1, -1));
2785         }
2786         m_text.replace(m_cursor, ms.length(), ms);
2787         m_cursor += ms.length();
2788         m_cursor = nextMaskBlank(m_cursor);
2789         m_textDirty = true;
2790     } else {
2791         int remaining = m_maxLength - m_text.length();
2792         if (remaining != 0) {
2793             m_text.insert(m_cursor, s.left(remaining));
2794             for (int i = 0; i < (int) s.left(remaining).length(); ++i)
2795                addCommand(Command(Insert, m_cursor++, s.at(i), -1, -1));
2796             m_textDirty = true;
2797         }
2798     }
2799 }
2800
2801 /*!
2802     \internal
2803
2804     deletes a single character from the current text.  If \a wasBackspace,
2805     the character prior to the cursor is removed.  Otherwise the character
2806     after the cursor is removed.
2807
2808     Also adds the appropriate commands into the undo history.
2809     This function does not call finishChange(), and may leave the text
2810     in an invalid state.
2811 */
2812 void QQuickTextInputPrivate::internalDelete(bool wasBackspace)
2813 {
2814     if (m_cursor < (int) m_text.length()) {
2815         if (hasSelectedText())
2816             addCommand(Command(SetSelection, m_cursor, 0, m_selstart, m_selend));
2817         addCommand(Command((CommandType)((m_maskData ? 2 : 0) + (wasBackspace ? Remove : Delete)),
2818                    m_cursor, m_text.at(m_cursor), -1, -1));
2819         if (m_maskData) {
2820             m_text.replace(m_cursor, 1, clearString(m_cursor, 1));
2821             addCommand(Command(Insert, m_cursor, m_text.at(m_cursor), -1, -1));
2822         } else {
2823             m_text.remove(m_cursor, 1);
2824         }
2825         m_textDirty = true;
2826     }
2827 }
2828
2829 /*!
2830     \internal
2831
2832     removes the currently selected text from the line control.
2833
2834     Also adds the appropriate commands into the undo history.
2835     This function does not call finishChange(), and may leave the text
2836     in an invalid state.
2837 */
2838 void QQuickTextInputPrivate::removeSelectedText()
2839 {
2840     if (m_selstart < m_selend && m_selend <= (int) m_text.length()) {
2841         separate();
2842         int i ;
2843         addCommand(Command(SetSelection, m_cursor, 0, m_selstart, m_selend));
2844         if (m_selstart <= m_cursor && m_cursor < m_selend) {
2845             // cursor is within the selection. Split up the commands
2846             // to be able to restore the correct cursor position
2847             for (i = m_cursor; i >= m_selstart; --i)
2848                 addCommand (Command(DeleteSelection, i, m_text.at(i), -1, 1));
2849             for (i = m_selend - 1; i > m_cursor; --i)
2850                 addCommand (Command(DeleteSelection, i - m_cursor + m_selstart - 1, m_text.at(i), -1, -1));
2851         } else {
2852             for (i = m_selend-1; i >= m_selstart; --i)
2853                 addCommand (Command(RemoveSelection, i, m_text.at(i), -1, -1));
2854         }
2855         if (m_maskData) {
2856             m_text.replace(m_selstart, m_selend - m_selstart,  clearString(m_selstart, m_selend - m_selstart));
2857             for (int i = 0; i < m_selend - m_selstart; ++i)
2858                 addCommand(Command(Insert, m_selstart + i, m_text.at(m_selstart + i), -1, -1));
2859         } else {
2860             m_text.remove(m_selstart, m_selend - m_selstart);
2861         }
2862         if (m_cursor > m_selstart)
2863             m_cursor -= qMin(m_cursor, m_selend) - m_selstart;
2864         internalDeselect();
2865         m_textDirty = true;
2866     }
2867 }
2868
2869 /*!
2870     \internal
2871
2872     Parses the input mask specified by \a maskFields to generate
2873     the mask data used to handle input masks.
2874 */
2875 void QQuickTextInputPrivate::parseInputMask(const QString &maskFields)
2876 {
2877     int delimiter = maskFields.indexOf(QLatin1Char(';'));
2878     if (maskFields.isEmpty() || delimiter == 0) {
2879         if (m_maskData) {
2880             delete [] m_maskData;
2881             m_maskData = 0;
2882             m_maxLength = 32767;
2883             internalSetText(QString());
2884         }
2885         return;
2886     }
2887
2888     if (delimiter == -1) {
2889         m_blank = QLatin1Char(' ');
2890         m_inputMask = maskFields;
2891     } else {
2892         m_inputMask = maskFields.left(delimiter);
2893         m_blank = (delimiter + 1 < maskFields.length()) ? maskFields[delimiter + 1] : QLatin1Char(' ');
2894     }
2895
2896     // calculate m_maxLength / m_maskData length
2897     m_maxLength = 0;
2898     QChar c = 0;
2899     for (int i=0; i<m_inputMask.length(); i++) {
2900         c = m_inputMask.at(i);
2901         if (i > 0 && m_inputMask.at(i-1) == QLatin1Char('\\')) {
2902             m_maxLength++;
2903             continue;
2904         }
2905         if (c != QLatin1Char('\\') && c != QLatin1Char('!') &&
2906              c != QLatin1Char('<') && c != QLatin1Char('>') &&
2907              c != QLatin1Char('{') && c != QLatin1Char('}') &&
2908              c != QLatin1Char('[') && c != QLatin1Char(']'))
2909             m_maxLength++;
2910     }
2911
2912     delete [] m_maskData;
2913     m_maskData = new MaskInputData[m_maxLength];
2914
2915     MaskInputData::Casemode m = MaskInputData::NoCaseMode;
2916     c = 0;
2917     bool s;
2918     bool escape = false;
2919     int index = 0;
2920     for (int i = 0; i < m_inputMask.length(); i++) {
2921         c = m_inputMask.at(i);
2922         if (escape) {
2923             s = true;
2924             m_maskData[index].maskChar = c;
2925             m_maskData[index].separator = s;
2926             m_maskData[index].caseMode = m;
2927             index++;
2928             escape = false;
2929         } else if (c == QLatin1Char('<')) {
2930                 m = MaskInputData::Lower;
2931         } else if (c == QLatin1Char('>')) {
2932             m = MaskInputData::Upper;
2933         } else if (c == QLatin1Char('!')) {
2934             m = MaskInputData::NoCaseMode;
2935         } else if (c != QLatin1Char('{') && c != QLatin1Char('}') && c != QLatin1Char('[') && c != QLatin1Char(']')) {
2936             switch (c.unicode()) {
2937             case 'A':
2938             case 'a':
2939             case 'N':
2940             case 'n':
2941             case 'X':
2942             case 'x':
2943             case '9':
2944             case '0':
2945             case 'D':
2946             case 'd':
2947             case '#':
2948             case 'H':
2949             case 'h':
2950             case 'B':
2951             case 'b':
2952                 s = false;
2953                 break;
2954             case '\\':
2955                 escape = true;
2956             default:
2957                 s = true;
2958                 break;
2959             }
2960
2961             if (!escape) {
2962                 m_maskData[index].maskChar = c;
2963                 m_maskData[index].separator = s;
2964                 m_maskData[index].caseMode = m;
2965                 index++;
2966             }
2967         }
2968     }
2969     internalSetText(m_text);
2970 }
2971
2972
2973 /*!
2974     \internal
2975
2976     checks if the key is valid compared to the inputMask
2977 */
2978 bool QQuickTextInputPrivate::isValidInput(QChar key, QChar mask) const
2979 {
2980     switch (mask.unicode()) {
2981     case 'A':
2982         if (key.isLetter())
2983             return true;
2984         break;
2985     case 'a':
2986         if (key.isLetter() || key == m_blank)
2987             return true;
2988         break;
2989     case 'N':
2990         if (key.isLetterOrNumber())
2991             return true;
2992         break;
2993     case 'n':
2994         if (key.isLetterOrNumber() || key == m_blank)
2995             return true;
2996         break;
2997     case 'X':
2998         if (key.isPrint())
2999             return true;
3000         break;
3001     case 'x':
3002         if (key.isPrint() || key == m_blank)
3003             return true;
3004         break;
3005     case '9':
3006         if (key.isNumber())
3007             return true;
3008         break;
3009     case '0':
3010         if (key.isNumber() || key == m_blank)
3011             return true;
3012         break;
3013     case 'D':
3014         if (key.isNumber() && key.digitValue() > 0)
3015             return true;
3016         break;
3017     case 'd':
3018         if ((key.isNumber() && key.digitValue() > 0) || key == m_blank)
3019             return true;
3020         break;
3021     case '#':
3022         if (key.isNumber() || key == QLatin1Char('+') || key == QLatin1Char('-') || key == m_blank)
3023             return true;
3024         break;
3025     case 'B':
3026         if (key == QLatin1Char('0') || key == QLatin1Char('1'))
3027             return true;
3028         break;
3029     case 'b':
3030         if (key == QLatin1Char('0') || key == QLatin1Char('1') || key == m_blank)
3031             return true;
3032         break;
3033     case 'H':
3034         if (key.isNumber() || (key >= QLatin1Char('a') && key <= QLatin1Char('f')) || (key >= QLatin1Char('A') && key <= QLatin1Char('F')))
3035             return true;
3036         break;
3037     case 'h':
3038         if (key.isNumber() || (key >= QLatin1Char('a') && key <= QLatin1Char('f')) || (key >= QLatin1Char('A') && key <= QLatin1Char('F')) || key == m_blank)
3039             return true;
3040         break;
3041     default:
3042         break;
3043     }
3044     return false;
3045 }
3046
3047 /*!
3048     \internal
3049
3050     Returns true if the given text \a str is valid for any
3051     validator or input mask set for the line control.
3052
3053     Otherwise returns false
3054 */
3055 bool QQuickTextInputPrivate::hasAcceptableInput(const QString &str) const
3056 {
3057 #ifndef QT_NO_VALIDATOR
3058     QString textCopy = str;
3059     int cursorCopy = m_cursor;
3060     if (m_validator && m_validator->validate(textCopy, cursorCopy)
3061         != QValidator::Acceptable)
3062         return false;
3063 #endif
3064
3065     if (!m_maskData)
3066         return true;
3067
3068     if (str.length() != m_maxLength)
3069         return false;
3070
3071     for (int i=0; i < m_maxLength; ++i) {
3072         if (m_maskData[i].separator) {
3073             if (str.at(i) != m_maskData[i].maskChar)
3074                 return false;
3075         } else {
3076             if (!isValidInput(str.at(i), m_maskData[i].maskChar))
3077                 return false;
3078         }
3079     }
3080     return true;
3081 }
3082
3083 /*!
3084     \internal
3085
3086     Applies the inputMask on \a str starting from position \a pos in the mask. \a clear
3087     specifies from where characters should be gotten when a separator is met in \a str - true means
3088     that blanks will be used, false that previous input is used.
3089     Calling this when no inputMask is set is undefined.
3090 */
3091 QString QQuickTextInputPrivate::maskString(uint pos, const QString &str, bool clear) const
3092 {
3093     if (pos >= (uint)m_maxLength)
3094         return QString::fromLatin1("");
3095
3096     QString fill;
3097     fill = clear ? clearString(0, m_maxLength) : m_text;
3098
3099     int strIndex = 0;
3100     QString s = QString::fromLatin1("");
3101     int i = pos;
3102     while (i < m_maxLength) {
3103         if (strIndex < str.length()) {
3104             if (m_maskData[i].separator) {
3105                 s += m_maskData[i].maskChar;
3106                 if (str[(int)strIndex] == m_maskData[i].maskChar)
3107                     strIndex++;
3108                 ++i;
3109             } else {
3110                 if (isValidInput(str[(int)strIndex], m_maskData[i].maskChar)) {
3111                     switch (m_maskData[i].caseMode) {
3112                     case MaskInputData::Upper:
3113                         s += str[(int)strIndex].toUpper();
3114                         break;
3115                     case MaskInputData::Lower:
3116                         s += str[(int)strIndex].toLower();
3117                         break;
3118                     default:
3119                         s += str[(int)strIndex];
3120                     }
3121                     ++i;
3122                 } else {
3123                     // search for separator first
3124                     int n = findInMask(i, true, true, str[(int)strIndex]);
3125                     if (n != -1) {
3126                         if (str.length() != 1 || i == 0 || (i > 0 && (!m_maskData[i-1].separator || m_maskData[i-1].maskChar != str[(int)strIndex]))) {
3127                             s += fill.mid(i, n-i+1);
3128                             i = n + 1; // update i to find + 1
3129                         }
3130                     } else {
3131                         // search for valid m_blank if not
3132                         n = findInMask(i, true, false, str[(int)strIndex]);
3133                         if (n != -1) {
3134                             s += fill.mid(i, n-i);
3135                             switch (m_maskData[n].caseMode) {
3136                             case MaskInputData::Upper:
3137                                 s += str[(int)strIndex].toUpper();
3138                                 break;
3139                             case MaskInputData::Lower:
3140                                 s += str[(int)strIndex].toLower();
3141                                 break;
3142                             default:
3143                                 s += str[(int)strIndex];
3144                             }
3145                             i = n + 1; // updates i to find + 1
3146                         }
3147                     }
3148                 }
3149                 ++strIndex;
3150             }
3151         } else
3152             break;
3153     }
3154
3155     return s;
3156 }
3157
3158
3159
3160 /*!
3161     \internal
3162
3163     Returns a "cleared" string with only separators and blank chars.
3164     Calling this when no inputMask is set is undefined.
3165 */
3166 QString QQuickTextInputPrivate::clearString(uint pos, uint len) const
3167 {
3168     if (pos >= (uint)m_maxLength)
3169         return QString();
3170
3171     QString s;
3172     int end = qMin((uint)m_maxLength, pos + len);
3173     for (int i = pos; i < end; ++i)
3174         if (m_maskData[i].separator)
3175             s += m_maskData[i].maskChar;
3176         else
3177             s += m_blank;
3178
3179     return s;
3180 }
3181
3182 /*!
3183     \internal
3184
3185     Strips blank parts of the input in a QQuickTextInputPrivate when an inputMask is set,
3186     separators are still included. Typically "127.0__.0__.1__" becomes "127.0.0.1".
3187 */
3188 QString QQuickTextInputPrivate::stripString(const QString &str) const
3189 {
3190     if (!m_maskData)
3191         return str;
3192
3193     QString s;
3194     int end = qMin(m_maxLength, (int)str.length());
3195     for (int i = 0; i < end; ++i)
3196         if (m_maskData[i].separator)
3197             s += m_maskData[i].maskChar;
3198         else
3199             if (str[i] != m_blank)
3200                 s += str[i];
3201
3202     return s;
3203 }
3204
3205 /*!
3206     \internal
3207     searches forward/backward in m_maskData for either a separator or a m_blank
3208 */
3209 int QQuickTextInputPrivate::findInMask(int pos, bool forward, bool findSeparator, QChar searchChar) const
3210 {
3211     if (pos >= m_maxLength || pos < 0)
3212         return -1;
3213
3214     int end = forward ? m_maxLength : -1;
3215     int step = forward ? 1 : -1;
3216     int i = pos;
3217
3218     while (i != end) {
3219         if (findSeparator) {
3220             if (m_maskData[i].separator && m_maskData[i].maskChar == searchChar)
3221                 return i;
3222         } else {
3223             if (!m_maskData[i].separator) {
3224                 if (searchChar.isNull())
3225                     return i;
3226                 else if (isValidInput(searchChar, m_maskData[i].maskChar))
3227                     return i;
3228             }
3229         }
3230         i += step;
3231     }
3232     return -1;
3233 }
3234
3235 void QQuickTextInputPrivate::internalUndo(int until)
3236 {
3237     if (!isUndoAvailable())
3238         return;
3239     internalDeselect();
3240     while (m_undoState && m_undoState > until) {
3241         Command& cmd = m_history[--m_undoState];
3242         switch (cmd.type) {
3243         case Insert:
3244             m_text.remove(cmd.pos, 1);
3245             m_cursor = cmd.pos;
3246             break;
3247         case SetSelection:
3248             m_selstart = cmd.selStart;
3249             m_selend = cmd.selEnd;
3250             m_cursor = cmd.pos;
3251             break;
3252         case Remove:
3253         case RemoveSelection:
3254             m_text.insert(cmd.pos, cmd.uc);
3255             m_cursor = cmd.pos + 1;
3256             break;
3257         case Delete:
3258         case DeleteSelection:
3259             m_text.insert(cmd.pos, cmd.uc);
3260             m_cursor = cmd.pos;
3261             break;
3262         case Separator:
3263             continue;
3264         }
3265         if (until < 0 && m_undoState) {
3266             Command& next = m_history[m_undoState-1];
3267             if (next.type != cmd.type && next.type < RemoveSelection
3268                  && (cmd.type < RemoveSelection || next.type == Separator))
3269                 break;
3270         }
3271     }
3272     m_textDirty = true;
3273     emitCursorPositionChanged();
3274 }
3275
3276 void QQuickTextInputPrivate::internalRedo()
3277 {
3278     if (!isRedoAvailable())
3279         return;
3280     internalDeselect();
3281     while (m_undoState < (int)m_history.size()) {
3282         Command& cmd = m_history[m_undoState++];
3283         switch (cmd.type) {
3284         case Insert:
3285             m_text.insert(cmd.pos, cmd.uc);
3286             m_cursor = cmd.pos + 1;
3287             break;
3288         case SetSelection:
3289             m_selstart = cmd.selStart;
3290             m_selend = cmd.selEnd;
3291             m_cursor = cmd.pos;
3292             break;
3293         case Remove:
3294         case Delete:
3295         case RemoveSelection:
3296         case DeleteSelection:
3297             m_text.remove(cmd.pos, 1);
3298             m_selstart = cmd.selStart;
3299             m_selend = cmd.selEnd;
3300             m_cursor = cmd.pos;
3301             break;
3302         case Separator:
3303             m_selstart = cmd.selStart;
3304             m_selend = cmd.selEnd;
3305             m_cursor = cmd.pos;
3306             break;
3307         }
3308         if (m_undoState < (int)m_history.size()) {
3309             Command& next = m_history[m_undoState];
3310             if (next.type != cmd.type && cmd.type < RemoveSelection && next.type != Separator
3311                  && (next.type < RemoveSelection || cmd.type == Separator))
3312                 break;
3313         }
3314     }
3315     m_textDirty = true;
3316     emitCursorPositionChanged();
3317 }
3318
3319 /*!
3320     \internal
3321
3322     If the current cursor position differs from the last emitted cursor
3323     position, emits cursorPositionChanged().
3324 */
3325 void QQuickTextInputPrivate::emitCursorPositionChanged()
3326 {
3327     Q_Q(QQuickTextInput);
3328     if (m_cursor != m_lastCursorPos) {
3329         m_lastCursorPos = m_cursor;
3330
3331         q->updateCursorRectangle();
3332         emit q->cursorPositionChanged();
3333         // XXX todo - not in 4.8?
3334     #if 0
3335         resetCursorBlinkTimer();
3336     #endif
3337
3338         if (!hasSelectedText()) {
3339             if (lastSelectionStart != m_cursor) {
3340                 lastSelectionStart = m_cursor;
3341                 emit q->selectionStartChanged();
3342             }
3343             if (lastSelectionEnd != m_cursor) {
3344                 lastSelectionEnd = m_cursor;
3345                 emit q->selectionEndChanged();
3346             }
3347         }
3348
3349 #ifndef QT_NO_ACCESSIBILITY
3350         QAccessible::updateAccessibility(q, 0, QAccessible::TextCaretMoved);
3351 #endif
3352     }
3353 }
3354
3355
3356 void QQuickTextInputPrivate::setCursorBlinkPeriod(int msec)
3357 {
3358     Q_Q(QQuickTextInput);
3359     if (msec == m_blinkPeriod)
3360         return;
3361     if (m_blinkTimer) {
3362         q->killTimer(m_blinkTimer);
3363     }
3364     if (msec) {
3365         m_blinkTimer = q->startTimer(msec / 2);
3366         m_blinkStatus = 1;
3367     } else {
3368         m_blinkTimer = 0;
3369         if (m_blinkStatus == 1)
3370             emit q->updateRect(inputMask().isEmpty() ? q->cursorRectangle() : QRect());
3371     }
3372     m_blinkPeriod = msec;
3373 }
3374
3375 void QQuickTextInputPrivate::resetCursorBlinkTimer()
3376 {
3377     Q_Q(QQuickTextInput);
3378     if (m_blinkPeriod == 0 || m_blinkTimer == 0)
3379         return;
3380     q->killTimer(m_blinkTimer);
3381     m_blinkTimer = q->startTimer(m_blinkPeriod / 2);
3382     m_blinkStatus = 1;
3383 }
3384
3385 void QQuickTextInput::timerEvent(QTimerEvent *event)
3386 {
3387     Q_D(QQuickTextInput);
3388     if (event->timerId() == d->m_blinkTimer) {
3389         d->m_blinkStatus = !d->m_blinkStatus;
3390         updateRect(inputMask().isEmpty() ? cursorRectangle() : QRect());
3391     } else if (event->timerId() == d->m_deleteAllTimer) {
3392         killTimer(d->m_deleteAllTimer);
3393         d->m_deleteAllTimer = 0;
3394         d->clear();
3395     }
3396 }
3397
3398 void QQuickTextInputPrivate::processKeyEvent(QKeyEvent* event)
3399 {
3400     Q_Q(QQuickTextInput);
3401     bool inlineCompletionAccepted = false;
3402
3403     if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) {
3404         if (hasAcceptableInput(m_text) || fixup()) {
3405             emit q->accepted();
3406         }
3407         if (inlineCompletionAccepted)
3408             event->accept();
3409         else
3410             event->ignore();
3411         return;
3412     }
3413
3414     if (m_echoMode == QQuickTextInput::PasswordEchoOnEdit
3415         && !m_passwordEchoEditing
3416         && !m_readOnly
3417         && !event->text().isEmpty()
3418         && !(event->modifiers() & Qt::ControlModifier)) {
3419         // Clear the edit and reset to normal echo mode while editing; the
3420         // echo mode switches back when the edit loses focus
3421         // ### resets current content.  dubious code; you can
3422         // navigate with keys up, down, back, and select(?), but if you press
3423         // "left" or "right" it clears?
3424         updatePasswordEchoEditing(true);
3425         clear();
3426     }
3427
3428     bool unknown = false;
3429     bool visual = cursorMoveStyle() == Qt::VisualMoveStyle;
3430
3431     if (false) {
3432     }
3433 #ifndef QT_NO_SHORTCUT
3434     else if (event == QKeySequence::Undo) {
3435         if (!m_readOnly)
3436             undo();
3437     }
3438     else if (event == QKeySequence::Redo) {
3439         if (!m_readOnly)
3440             redo();
3441     }
3442     else if (event == QKeySequence::SelectAll) {
3443         selectAll();
3444     }
3445 #ifndef QT_NO_CLIPBOARD
3446     else if (event == QKeySequence::Copy) {
3447         copy();
3448     }
3449     else if (event == QKeySequence::Paste) {
3450         if (!m_readOnly) {
3451             QClipboard::Mode mode = QClipboard::Clipboard;
3452             paste(mode);
3453         }
3454     }
3455     else if (event == QKeySequence::Cut) {
3456         if (!m_readOnly) {
3457             copy();
3458             del();
3459         }
3460     }
3461     else if (event == QKeySequence::DeleteEndOfLine) {
3462         if (!m_readOnly) {
3463             setSelection(m_cursor, end());
3464             copy();
3465             del();
3466         }
3467     }
3468 #endif //QT_NO_CLIPBOARD
3469     else if (event == QKeySequence::MoveToStartOfLine || event == QKeySequence::MoveToStartOfBlock) {
3470         home(0);
3471     }
3472     else if (event == QKeySequence::MoveToEndOfLine || event == QKeySequence::MoveToEndOfBlock) {
3473         end(0);
3474     }
3475     else if (event == QKeySequence::SelectStartOfLine || event == QKeySequence::SelectStartOfBlock) {
3476         home(1);
3477     }
3478     else if (event == QKeySequence::SelectEndOfLine || event == QKeySequence::SelectEndOfBlock) {
3479         end(1);
3480     }
3481     else if (event == QKeySequence::MoveToNextChar) {
3482         if (hasSelectedText()) {
3483             moveCursor(selectionEnd(), false);
3484         } else {
3485             cursorForward(0, visual ? 1 : (layoutDirection() == Qt::LeftToRight ? 1 : -1));
3486         }
3487     }
3488     else if (event == QKeySequence::SelectNextChar) {
3489         cursorForward(1, visual ? 1 : (layoutDirection() == Qt::LeftToRight ? 1 : -1));
3490     }
3491     else if (event == QKeySequence::MoveToPreviousChar) {
3492         if (hasSelectedText()) {
3493             moveCursor(selectionStart(), false);
3494         } else {
3495             cursorForward(0, visual ? -1 : (layoutDirection() == Qt::LeftToRight ? -1 : 1));
3496         }
3497     }
3498     else if (event == QKeySequence::SelectPreviousChar) {
3499         cursorForward(1, visual ? -1 : (layoutDirection() == Qt::LeftToRight ? -1 : 1));
3500     }
3501     else if (event == QKeySequence::MoveToNextWord) {
3502         if (m_echoMode == QQuickTextInput::Normal)
3503             layoutDirection() == Qt::LeftToRight ? cursorWordForward(0) : cursorWordBackward(0);
3504         else
3505             layoutDirection() == Qt::LeftToRight ? end(0) : home(0);
3506     }
3507     else if (event == QKeySequence::MoveToPreviousWord) {
3508         if (m_echoMode == QQuickTextInput::Normal)
3509             layoutDirection() == Qt::LeftToRight ? cursorWordBackward(0) : cursorWordForward(0);
3510         else if (!m_readOnly) {
3511             layoutDirection() == Qt::LeftToRight ? home(0) : end(0);
3512         }
3513     }
3514     else if (event == QKeySequence::SelectNextWord) {
3515         if (m_echoMode == QQuickTextInput::Normal)
3516             layoutDirection() == Qt::LeftToRight ? cursorWordForward(1) : cursorWordBackward(1);
3517         else
3518             layoutDirection() == Qt::LeftToRight ? end(1) : home(1);
3519     }
3520     else if (event == QKeySequence::SelectPreviousWord) {
3521         if (m_echoMode == QQuickTextInput::Normal)
3522             layoutDirection() == Qt::LeftToRight ? cursorWordBackward(1) : cursorWordForward(1);
3523         else
3524             layoutDirection() == Qt::LeftToRight ? home(1) : end(1);
3525     }
3526     else if (event == QKeySequence::Delete) {
3527         if (!m_readOnly)
3528             del();
3529     }
3530     else if (event == QKeySequence::DeleteEndOfWord) {
3531         if (!m_readOnly) {
3532             cursorWordForward(true);
3533             del();
3534         }
3535     }
3536     else if (event == QKeySequence::DeleteStartOfWord) {
3537         if (!m_readOnly) {
3538             cursorWordBackward(true);
3539             del();
3540         }
3541     }
3542 #endif // QT_NO_SHORTCUT
3543     else {
3544         bool handled = false;
3545         if (event->modifiers() & Qt::ControlModifier) {
3546             switch (event->key()) {
3547             case Qt::Key_Backspace:
3548                 if (!m_readOnly) {
3549                     cursorWordBackward(true);
3550                     del();
3551                 }
3552                 break;
3553             default:
3554                 if (!handled)
3555                     unknown = true;
3556             }
3557         } else { // ### check for *no* modifier
3558             switch (event->key()) {
3559             case Qt::Key_Backspace:
3560                 if (!m_readOnly) {
3561                     backspace();
3562                 }
3563                 break;
3564             default:
3565                 if (!handled)
3566                     unknown = true;
3567             }
3568         }
3569     }
3570
3571     if (event->key() == Qt::Key_Direction_L || event->key() == Qt::Key_Direction_R) {
3572         setLayoutDirection((event->key() == Qt::Key_Direction_L) ? Qt::LeftToRight : Qt::RightToLeft);
3573         unknown = false;
3574     }
3575
3576     if (unknown && !m_readOnly) {
3577         QString t = event->text();
3578         if (!t.isEmpty() && t.at(0).isPrint()) {
3579             insert(t);
3580             event->accept();
3581             return;
3582         }
3583     }
3584
3585     if (unknown)
3586         event->ignore();
3587     else
3588         event->accept();
3589 }
3590
3591
3592 QT_END_NAMESPACE
3593