Combined platform input contexts in tests into one shared
[profile/ivi/qtdeclarative.git] / tests / auto / qtquick2 / qquicktextinput / tst_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 test suite 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 #include <qtest.h>
42 #include <QtTest/QSignalSpy>
43 #include "../../shared/util.h"
44 #include <private/qinputpanel_p.h>
45 #include <QtDeclarative/qdeclarativeengine.h>
46 #include <QtDeclarative/qdeclarativeexpression.h>
47 #include <QFile>
48 #include <QtQuick/qquickview.h>
49 #include <QtGui/qguiapplication.h>
50 #include <QtGui/qstylehints.h>
51 #include <QInputPanel>
52 #include <private/qquicktextinput_p.h>
53 #include <private/qquicktextinput_p_p.h>
54 #include <QDebug>
55 #include <QDir>
56 #include <QStyle>
57 #include <QtOpenGL/QGLShaderProgram>
58 #include <math.h>
59
60 #ifdef Q_OS_MAC
61 #include <Carbon/Carbon.h>
62 #endif
63
64 #include "qplatformdefs.h"
65 #include "../../shared/platforminputcontext.h"
66
67 Q_DECLARE_METATYPE(QQuickTextInput::SelectionMode)
68 DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD)
69
70 QString createExpectedFileIfNotFound(const QString& filebasename, const QImage& actual)
71 {
72     // XXX This will be replaced by some clever persistent platform image store.
73     QString persistent_dir = QDeclarativeDataTest::instance()->dataDirectory();
74     QString arch = "unknown-architecture"; // QTest needs to help with this.
75
76     QString expectfile = persistent_dir + QDir::separator() + filebasename + "-" + arch + ".png";
77
78     if (!QFile::exists(expectfile)) {
79         actual.save(expectfile);
80         qWarning() << "created" << expectfile;
81     }
82
83     return expectfile;
84 }
85
86 template <typename T> static T evaluate(QObject *scope, const QString &expression)
87 {
88     QDeclarativeExpression expr(qmlContext(scope), scope, expression);
89     T result = expr.evaluate().value<T>();
90     if (expr.hasError())
91         qWarning() << expr.error().toString();
92     return result;
93 }
94
95 typedef QPair<int, QChar> Key;
96
97 class tst_qquicktextinput : public QDeclarativeDataTest
98
99 {
100     Q_OBJECT
101 public:
102     tst_qquicktextinput();
103
104 private slots:
105     void cleanup();
106     void text();
107     void width();
108     void font();
109     void color();
110     void wrap();
111     void selection();
112     void isRightToLeft_data();
113     void isRightToLeft();
114     void moveCursorSelection_data();
115     void moveCursorSelection();
116     void moveCursorSelectionSequence_data();
117     void moveCursorSelectionSequence();
118     void dragMouseSelection();
119     void mouseSelectionMode_data();
120     void mouseSelectionMode();
121     void tripleClickSelectsAll();
122
123     void horizontalAlignment_data();
124     void horizontalAlignment();
125     void horizontalAlignment_RightToLeft();
126     void verticalAlignment();
127
128     void positionAt();
129
130     void maxLength();
131     void masks();
132     void validators();
133     void inputMethods();
134
135     void passwordCharacter();
136     void cursorDelegate();
137     void cursorVisible();
138     void cursorRectangle();
139     void navigation();
140     void navigation_RTL();
141     void copyAndPaste();
142     void copyAndPasteKeySequence();
143     void canPasteEmpty();
144     void canPaste();
145     void readOnly();
146
147     void openInputPanel();
148     void setHAlignClearCache();
149     void focusOutClearSelection();
150
151     void echoMode();
152 #ifdef QT_GUI_PASSWORD_ECHO_DELAY
153     void passwordEchoDelay();
154 #endif
155     void geometrySignals();
156     void testQtQuick11Attributes();
157     void testQtQuick11Attributes_data();
158
159     void preeditAutoScroll();
160     void preeditCursorRectangle();
161     void inputContextMouseHandler();
162     void inputMethodComposing();
163     void cursorRectangleSize();
164
165     void keySequence_data();
166     void keySequence();
167
168     void undo_data();
169     void undo();
170     void redo_data();
171     void redo();
172     void undo_keypressevents_data();
173     void undo_keypressevents();
174
175     void QTBUG_19956();
176     void QTBUG_19956_data();
177     void QTBUG_19956_regexp();
178
179 private:
180     void simulateKey(QQuickView *, int key);
181
182     void simulateKeys(QWindow *window, const QList<Key> &keys);
183     void simulateKeys(QWindow *window, const QKeySequence &sequence);
184
185     QDeclarativeEngine engine;
186     QStringList standard;
187     QStringList colorStrings;
188 };
189
190 typedef QList<int> IntList;
191 Q_DECLARE_METATYPE(IntList)
192
193 typedef QList<Key> KeyList;
194 Q_DECLARE_METATYPE(KeyList)
195
196 void tst_qquicktextinput::simulateKeys(QWindow *window, const QList<Key> &keys)
197 {
198     for (int i = 0; i < keys.count(); ++i) {
199         const int key = keys.at(i).first;
200         const int modifiers = key & Qt::KeyboardModifierMask;
201         const QString text = !keys.at(i).second.isNull() ? QString(keys.at(i).second) : QString();
202
203         QKeyEvent press(QEvent::KeyPress, Qt::Key(key), Qt::KeyboardModifiers(modifiers), text);
204         QKeyEvent release(QEvent::KeyRelease, Qt::Key(key), Qt::KeyboardModifiers(modifiers), text);
205
206         QGuiApplication::sendEvent(window, &press);
207         QGuiApplication::sendEvent(window, &release);
208     }
209 }
210
211 void tst_qquicktextinput::simulateKeys(QWindow *window, const QKeySequence &sequence)
212 {
213     for (uint i = 0; i < sequence.count(); ++i) {
214         const int key = sequence[i];
215         const int modifiers = key & Qt::KeyboardModifierMask;
216
217         QTest::keyClick(window, Qt::Key(key & ~modifiers), Qt::KeyboardModifiers(modifiers));
218     }
219 }
220
221 QList<Key> &operator <<(QList<Key> &keys, const QKeySequence &sequence)
222 {
223     for (uint i = 0; i < sequence.count(); ++i)
224         keys << Key(sequence[i], QChar());
225     return keys;
226 }
227
228 template <int N> QList<Key> &operator <<(QList<Key> &keys, const char (&characters)[N])
229 {
230     for (int i = 0; i < N - 1; ++i) {
231         int key = QTest::asciiToKey(characters[i]);
232         QChar character = QLatin1Char(characters[i]);
233         keys << Key(key, character);
234     }
235     return keys;
236 }
237
238 QList<Key> &operator <<(QList<Key> &keys, Qt::Key key)
239 {
240     keys << Key(key, QChar());
241     return keys;
242 }
243
244 void tst_qquicktextinput::cleanup()
245 {
246     // ensure not even skipped tests with custom input context leave it dangling
247     QInputPanelPrivate *inputPanelPrivate = QInputPanelPrivate::get(qApp->inputPanel());
248     inputPanelPrivate->testContext = 0;
249 }
250
251 tst_qquicktextinput::tst_qquicktextinput()
252 {
253     standard << "the quick brown fox jumped over the lazy dog"
254         << "It's supercalifragisiticexpialidocious!"
255         << "Hello, world!"
256         << "!dlrow ,olleH"
257         << " spacey   text ";
258
259     colorStrings << "aliceblue"
260                  << "antiquewhite"
261                  << "aqua"
262                  << "darkkhaki"
263                  << "darkolivegreen"
264                  << "dimgray"
265                  << "palevioletred"
266                  << "lightsteelblue"
267                  << "#000000"
268                  << "#AAAAAA"
269                  << "#FFFFFF"
270                  << "#2AC05F";
271 }
272
273 void tst_qquicktextinput::text()
274 {
275     {
276         QDeclarativeComponent textinputComponent(&engine);
277         textinputComponent.setData("import QtQuick 2.0\nTextInput {  text: \"\"  }", QUrl());
278         QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
279
280         QVERIFY(textinputObject != 0);
281         QCOMPARE(textinputObject->text(), QString(""));
282
283         delete textinputObject;
284     }
285
286     for (int i = 0; i < standard.size(); i++)
287     {
288         QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + standard.at(i) + "\" }";
289         QDeclarativeComponent textinputComponent(&engine);
290         textinputComponent.setData(componentStr.toLatin1(), QUrl());
291         QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
292
293         QVERIFY(textinputObject != 0);
294         QCOMPARE(textinputObject->text(), standard.at(i));
295
296         delete textinputObject;
297     }
298
299 }
300
301 void tst_qquicktextinput::width()
302 {
303     // uses Font metrics to find the width for standard
304     {
305         QDeclarativeComponent textinputComponent(&engine);
306         textinputComponent.setData("import QtQuick 2.0\nTextInput {  text: \"\" }", QUrl());
307         QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
308
309         QVERIFY(textinputObject != 0);
310         QCOMPARE(textinputObject->width(), 0.0);
311
312         delete textinputObject;
313     }
314
315     bool requiresUnhintedMetrics = !qmlDisableDistanceField();
316
317     for (int i = 0; i < standard.size(); i++)
318     {
319         QString componentStr = "import QtQuick 2.0\nTextInput { text: \"" + standard.at(i) + "\" }";
320         QDeclarativeComponent textinputComponent(&engine);
321         textinputComponent.setData(componentStr.toLatin1(), QUrl());
322         QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
323
324         QString s = standard.at(i);
325         s.replace(QLatin1Char('\n'), QChar::LineSeparator);
326
327         QTextLayout layout(s);
328         layout.setFont(textinputObject->font());
329         layout.setFlags(Qt::TextExpandTabs | Qt::TextShowMnemonic);
330         if (requiresUnhintedMetrics) {
331             QTextOption option;
332             option.setUseDesignMetrics(true);
333             layout.setTextOption(option);
334         }
335
336         layout.beginLayout();
337         forever {
338             QTextLine line = layout.createLine();
339             if (!line.isValid())
340                 break;
341         }
342
343         layout.endLayout();
344
345         qreal metricWidth = ceil(layout.boundingRect().width());
346
347         QVERIFY(textinputObject != 0);
348         int delta = abs(int(int(textinputObject->width()) - metricWidth));
349         QVERIFY(delta <= 3.0); // As best as we can hope for cross-platform.
350
351         delete textinputObject;
352     }
353 }
354
355 void tst_qquicktextinput::font()
356 {
357     //test size, then bold, then italic, then family
358     {
359         QString componentStr = "import QtQuick 2.0\nTextInput {  font.pointSize: 40; text: \"Hello World\" }";
360         QDeclarativeComponent textinputComponent(&engine);
361         textinputComponent.setData(componentStr.toLatin1(), QUrl());
362         QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
363
364         QVERIFY(textinputObject != 0);
365         QCOMPARE(textinputObject->font().pointSize(), 40);
366         QCOMPARE(textinputObject->font().bold(), false);
367         QCOMPARE(textinputObject->font().italic(), false);
368
369         delete textinputObject;
370     }
371
372     {
373         QString componentStr = "import QtQuick 2.0\nTextInput {  font.bold: true; text: \"Hello World\" }";
374         QDeclarativeComponent textinputComponent(&engine);
375         textinputComponent.setData(componentStr.toLatin1(), QUrl());
376         QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
377
378         QVERIFY(textinputObject != 0);
379         QCOMPARE(textinputObject->font().bold(), true);
380         QCOMPARE(textinputObject->font().italic(), false);
381
382         delete textinputObject;
383     }
384
385     {
386         QString componentStr = "import QtQuick 2.0\nTextInput {  font.italic: true; text: \"Hello World\" }";
387         QDeclarativeComponent textinputComponent(&engine);
388         textinputComponent.setData(componentStr.toLatin1(), QUrl());
389         QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
390
391         QVERIFY(textinputObject != 0);
392         QCOMPARE(textinputObject->font().italic(), true);
393         QCOMPARE(textinputObject->font().bold(), false);
394
395         delete textinputObject;
396     }
397
398     {
399         QString componentStr = "import QtQuick 2.0\nTextInput {  font.family: \"Helvetica\"; text: \"Hello World\" }";
400         QDeclarativeComponent textinputComponent(&engine);
401         textinputComponent.setData(componentStr.toLatin1(), QUrl());
402         QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
403
404         QVERIFY(textinputObject != 0);
405         QCOMPARE(textinputObject->font().family(), QString("Helvetica"));
406         QCOMPARE(textinputObject->font().bold(), false);
407         QCOMPARE(textinputObject->font().italic(), false);
408
409         delete textinputObject;
410     }
411
412     {
413         QString componentStr = "import QtQuick 2.0\nTextInput {  font.family: \"\"; text: \"Hello World\" }";
414         QDeclarativeComponent textinputComponent(&engine);
415         textinputComponent.setData(componentStr.toLatin1(), QUrl());
416         QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
417
418         QVERIFY(textinputObject != 0);
419         QCOMPARE(textinputObject->font().family(), QString(""));
420
421         delete textinputObject;
422     }
423 }
424
425 void tst_qquicktextinput::color()
426 {
427     //test color
428     for (int i = 0; i < colorStrings.size(); i++)
429     {
430         QString componentStr = "import QtQuick 2.0\nTextInput {  color: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
431         QDeclarativeComponent textinputComponent(&engine);
432         textinputComponent.setData(componentStr.toLatin1(), QUrl());
433         QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
434         QVERIFY(textinputObject != 0);
435         QCOMPARE(textinputObject->color(), QColor(colorStrings.at(i)));
436
437         delete textinputObject;
438     }
439
440     //test selection color
441     for (int i = 0; i < colorStrings.size(); i++)
442     {
443         QString componentStr = "import QtQuick 2.0\nTextInput {  selectionColor: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
444         QDeclarativeComponent textinputComponent(&engine);
445         textinputComponent.setData(componentStr.toLatin1(), QUrl());
446         QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
447         QVERIFY(textinputObject != 0);
448         QCOMPARE(textinputObject->selectionColor(), QColor(colorStrings.at(i)));
449
450         delete textinputObject;
451     }
452
453     //test selected text color
454     for (int i = 0; i < colorStrings.size(); i++)
455     {
456         QString componentStr = "import QtQuick 2.0\nTextInput {  selectedTextColor: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
457         QDeclarativeComponent textinputComponent(&engine);
458         textinputComponent.setData(componentStr.toLatin1(), QUrl());
459         QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
460         QVERIFY(textinputObject != 0);
461         QCOMPARE(textinputObject->selectedTextColor(), QColor(colorStrings.at(i)));
462
463         delete textinputObject;
464     }
465
466     {
467         QString colorStr = "#AA001234";
468         QColor testColor("#001234");
469         testColor.setAlpha(170);
470
471         QString componentStr = "import QtQuick 2.0\nTextInput {  color: \"" + colorStr + "\"; text: \"Hello World\" }";
472         QDeclarativeComponent textinputComponent(&engine);
473         textinputComponent.setData(componentStr.toLatin1(), QUrl());
474         QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
475
476         QVERIFY(textinputObject != 0);
477         QCOMPARE(textinputObject->color(), testColor);
478
479         delete textinputObject;
480     }
481 }
482
483 void tst_qquicktextinput::wrap()
484 {
485     int textHeight = 0;
486     // for specified width and wrap set true
487     {
488         QDeclarativeComponent textComponent(&engine);
489         textComponent.setData("import QtQuick 2.0\nTextInput { text: \"Hello\"; wrapMode: Text.WrapAnywhere; width: 300 }", QUrl::fromLocalFile(""));
490         QQuickTextInput *textObject = qobject_cast<QQuickTextInput*>(textComponent.create());
491         textHeight = textObject->height();
492
493         QVERIFY(textObject != 0);
494         QVERIFY(textObject->wrapMode() == QQuickTextInput::WrapAnywhere);
495         QCOMPARE(textObject->width(), 300.);
496
497         delete textObject;
498     }
499
500     for (int i = 0; i < standard.count(); i++) {
501         QString componentStr = "import QtQuick 2.0\nTextInput { wrapMode: Text.WrapAnywhere; width: 30; text: \"" + standard.at(i) + "\" }";
502         QDeclarativeComponent textComponent(&engine);
503         textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
504         QQuickTextInput *textObject = qobject_cast<QQuickTextInput*>(textComponent.create());
505
506         QVERIFY(textObject != 0);
507         QCOMPARE(textObject->width(), 30.);
508         QVERIFY(textObject->height() > textHeight);
509
510         int oldHeight = textObject->height();
511         textObject->setWidth(100);
512         QVERIFY(textObject->height() < oldHeight);
513
514         delete textObject;
515     }
516 }
517
518 void tst_qquicktextinput::selection()
519 {
520     QString testStr = standard[0];
521     QString componentStr = "import QtQuick 2.0\nTextInput {  text: \""+ testStr +"\"; }";
522     QDeclarativeComponent textinputComponent(&engine);
523     textinputComponent.setData(componentStr.toLatin1(), QUrl());
524     QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
525     QVERIFY(textinputObject != 0);
526
527
528     //Test selection follows cursor
529     for (int i=0; i<= testStr.size(); i++) {
530         textinputObject->setCursorPosition(i);
531         QCOMPARE(textinputObject->cursorPosition(), i);
532         QCOMPARE(textinputObject->selectionStart(), i);
533         QCOMPARE(textinputObject->selectionEnd(), i);
534         QVERIFY(textinputObject->selectedText().isNull());
535     }
536
537     textinputObject->setCursorPosition(0);
538     QVERIFY(textinputObject->cursorPosition() == 0);
539     QVERIFY(textinputObject->selectionStart() == 0);
540     QVERIFY(textinputObject->selectionEnd() == 0);
541     QVERIFY(textinputObject->selectedText().isNull());
542
543     // Verify invalid positions are ignored.
544     textinputObject->setCursorPosition(-1);
545     QVERIFY(textinputObject->cursorPosition() == 0);
546     QVERIFY(textinputObject->selectionStart() == 0);
547     QVERIFY(textinputObject->selectionEnd() == 0);
548     QVERIFY(textinputObject->selectedText().isNull());
549
550     textinputObject->setCursorPosition(textinputObject->text().count()+1);
551     QVERIFY(textinputObject->cursorPosition() == 0);
552     QVERIFY(textinputObject->selectionStart() == 0);
553     QVERIFY(textinputObject->selectionEnd() == 0);
554     QVERIFY(textinputObject->selectedText().isNull());
555
556     //Test selection
557     for (int i=0; i<= testStr.size(); i++) {
558         textinputObject->select(0,i);
559         QCOMPARE(testStr.mid(0,i), textinputObject->selectedText());
560     }
561     for (int i=0; i<= testStr.size(); i++) {
562         textinputObject->select(i,testStr.size());
563         QCOMPARE(testStr.mid(i,testStr.size()-i), textinputObject->selectedText());
564     }
565
566     textinputObject->setCursorPosition(0);
567     QVERIFY(textinputObject->cursorPosition() == 0);
568     QVERIFY(textinputObject->selectionStart() == 0);
569     QVERIFY(textinputObject->selectionEnd() == 0);
570     QVERIFY(textinputObject->selectedText().isNull());
571
572     //Test Error Ignoring behaviour
573     textinputObject->setCursorPosition(0);
574     QVERIFY(textinputObject->selectedText().isNull());
575     textinputObject->select(-10,0);
576     QVERIFY(textinputObject->selectedText().isNull());
577     textinputObject->select(100,110);
578     QVERIFY(textinputObject->selectedText().isNull());
579     textinputObject->select(0,-10);
580     QVERIFY(textinputObject->selectedText().isNull());
581     textinputObject->select(0,100);
582     QVERIFY(textinputObject->selectedText().isNull());
583     textinputObject->select(0,10);
584     QVERIFY(textinputObject->selectedText().size() == 10);
585     textinputObject->select(-10,10);
586     QVERIFY(textinputObject->selectedText().size() == 10);
587     textinputObject->select(100,101);
588     QVERIFY(textinputObject->selectedText().size() == 10);
589     textinputObject->select(0,-10);
590     QVERIFY(textinputObject->selectedText().size() == 10);
591     textinputObject->select(0,100);
592     QVERIFY(textinputObject->selectedText().size() == 10);
593
594     textinputObject->deselect();
595     QVERIFY(textinputObject->selectedText().isNull());
596     textinputObject->select(0,10);
597     QVERIFY(textinputObject->selectedText().size() == 10);
598     textinputObject->deselect();
599     QVERIFY(textinputObject->selectedText().isNull());
600
601     // test input method selection
602     QSignalSpy selectionSpy(textinputObject, SIGNAL(selectedTextChanged()));
603     textinputObject->setFocus(true);
604     {
605         QList<QInputMethodEvent::Attribute> attributes;
606         attributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 12, 5, QVariant());
607         QInputMethodEvent event("", attributes);
608         QApplication::sendEvent(textinputObject, &event);
609     }
610     QCOMPARE(selectionSpy.count(), 1);
611     QCOMPARE(textinputObject->selectionStart(), 12);
612     QCOMPARE(textinputObject->selectionEnd(), 17);
613
614     delete textinputObject;
615 }
616
617 void tst_qquicktextinput::isRightToLeft_data()
618 {
619     QTest::addColumn<QString>("text");
620     QTest::addColumn<bool>("emptyString");
621     QTest::addColumn<bool>("firstCharacter");
622     QTest::addColumn<bool>("lastCharacter");
623     QTest::addColumn<bool>("middleCharacter");
624     QTest::addColumn<bool>("startString");
625     QTest::addColumn<bool>("midString");
626     QTest::addColumn<bool>("endString");
627
628     const quint16 arabic_str[] = { 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0647};
629     QTest::newRow("Empty") << "" << false << false << false << false << false << false << false;
630     QTest::newRow("Neutral") << "23244242" << false << false << false << false << false << false << false;
631     QTest::newRow("LTR") << "Hello world" << false << false << false << false << false << false << false;
632     QTest::newRow("RTL") << QString::fromUtf16(arabic_str, 11) << false << true << true << true << true << true << true;
633     QTest::newRow("Bidi RTL + LTR + RTL") << QString::fromUtf16(arabic_str, 11) + QString("Hello world") + QString::fromUtf16(arabic_str, 11) << false << true << true << false << true << true << true;
634     QTest::newRow("Bidi LTR + RTL + LTR") << QString("Hello world") + QString::fromUtf16(arabic_str, 11) + QString("Hello world") << false << false << false << true << false << false << false;
635 }
636
637 void tst_qquicktextinput::isRightToLeft()
638 {
639     QFETCH(QString, text);
640     QFETCH(bool, emptyString);
641     QFETCH(bool, firstCharacter);
642     QFETCH(bool, lastCharacter);
643     QFETCH(bool, middleCharacter);
644     QFETCH(bool, startString);
645     QFETCH(bool, midString);
646     QFETCH(bool, endString);
647
648     QQuickTextInput textInput;
649     textInput.setText(text);
650
651     // first test that the right string is delivered to the QString::isRightToLeft()
652     QCOMPARE(textInput.isRightToLeft(0,0), text.mid(0,0).isRightToLeft());
653     QCOMPARE(textInput.isRightToLeft(0,1), text.mid(0,1).isRightToLeft());
654     QCOMPARE(textInput.isRightToLeft(text.count()-2, text.count()-1), text.mid(text.count()-2, text.count()-1).isRightToLeft());
655     QCOMPARE(textInput.isRightToLeft(text.count()/2, text.count()/2 + 1), text.mid(text.count()/2, text.count()/2 + 1).isRightToLeft());
656     QCOMPARE(textInput.isRightToLeft(0,text.count()/4), text.mid(0,text.count()/4).isRightToLeft());
657     QCOMPARE(textInput.isRightToLeft(text.count()/4,3*text.count()/4), text.mid(text.count()/4,3*text.count()/4).isRightToLeft());
658     if (text.isEmpty())
659         QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML TextInput: isRightToLeft(start, end) called with the end property being smaller than the start.");
660     QCOMPARE(textInput.isRightToLeft(3*text.count()/4,text.count()-1), text.mid(3*text.count()/4,text.count()-1).isRightToLeft());
661
662     // then test that the feature actually works
663     QCOMPARE(textInput.isRightToLeft(0,0), emptyString);
664     QCOMPARE(textInput.isRightToLeft(0,1), firstCharacter);
665     QCOMPARE(textInput.isRightToLeft(text.count()-2, text.count()-1), lastCharacter);
666     QCOMPARE(textInput.isRightToLeft(text.count()/2, text.count()/2 + 1), middleCharacter);
667     QCOMPARE(textInput.isRightToLeft(0,text.count()/4), startString);
668     QCOMPARE(textInput.isRightToLeft(text.count()/4,3*text.count()/4), midString);
669     if (text.isEmpty())
670         QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML TextInput: isRightToLeft(start, end) called with the end property being smaller than the start.");
671     QCOMPARE(textInput.isRightToLeft(3*text.count()/4,text.count()-1), endString);
672 }
673
674 void tst_qquicktextinput::moveCursorSelection_data()
675 {
676     QTest::addColumn<QString>("testStr");
677     QTest::addColumn<int>("cursorPosition");
678     QTest::addColumn<int>("movePosition");
679     QTest::addColumn<QQuickTextInput::SelectionMode>("mode");
680     QTest::addColumn<int>("selectionStart");
681     QTest::addColumn<int>("selectionEnd");
682     QTest::addColumn<bool>("reversible");
683
684     // () contains the text selected by the cursor.
685     // <> contains the actual selection.
686
687     QTest::newRow("(t)he|characters")
688             << standard[0] << 0 << 1 << QQuickTextInput::SelectCharacters << 0 << 1 << true;
689     QTest::newRow("do(g)|characters")
690             << standard[0] << 43 << 44 << QQuickTextInput::SelectCharacters << 43 << 44 << true;
691     QTest::newRow("jum(p)ed|characters")
692             << standard[0] << 23 << 24 << QQuickTextInput::SelectCharacters << 23 << 24 << true;
693     QTest::newRow("jumped( )over|characters")
694             << standard[0] << 26 << 27 << QQuickTextInput::SelectCharacters << 26 << 27 << true;
695     QTest::newRow("(the )|characters")
696             << standard[0] << 0 << 4 << QQuickTextInput::SelectCharacters << 0 << 4 << true;
697     QTest::newRow("( dog)|characters")
698             << standard[0] << 40 << 44 << QQuickTextInput::SelectCharacters << 40 << 44 << true;
699     QTest::newRow("( jumped )|characters")
700             << standard[0] << 19 << 27 << QQuickTextInput::SelectCharacters << 19 << 27 << true;
701     QTest::newRow("th(e qu)ick|characters")
702             << standard[0] << 2 << 6 << QQuickTextInput::SelectCharacters << 2 << 6 << true;
703     QTest::newRow("la(zy d)og|characters")
704             << standard[0] << 38 << 42 << QQuickTextInput::SelectCharacters << 38 << 42 << true;
705     QTest::newRow("jum(ped ov)er|characters")
706             << standard[0] << 23 << 29 << QQuickTextInput::SelectCharacters << 23 << 29 << true;
707     QTest::newRow("()the|characters")
708             << standard[0] << 0 << 0 << QQuickTextInput::SelectCharacters << 0 << 0 << true;
709     QTest::newRow("dog()|characters")
710             << standard[0] << 44 << 44 << QQuickTextInput::SelectCharacters << 44 << 44 << true;
711     QTest::newRow("jum()ped|characters")
712             << standard[0] << 23 << 23 << QQuickTextInput::SelectCharacters << 23 << 23 << true;
713
714     QTest::newRow("<(t)he>|words")
715             << standard[0] << 0 << 1 << QQuickTextInput::SelectWords << 0 << 3 << true;
716     QTest::newRow("<do(g)>|words")
717             << standard[0] << 43 << 44 << QQuickTextInput::SelectWords << 41 << 44 << true;
718     QTest::newRow("<jum(p)ed>|words")
719             << standard[0] << 23 << 24 << QQuickTextInput::SelectWords << 20 << 26 << true;
720     QTest::newRow("<jumped( )>over|words,ltr")
721             << standard[0] << 26 << 27 << QQuickTextInput::SelectWords << 20 << 27 << false;
722     QTest::newRow("jumped<( )over>|words,rtl")
723             << standard[0] << 27 << 26 << QQuickTextInput::SelectWords << 26 << 31 << false;
724     QTest::newRow("<(the )>quick|words,ltr")
725             << standard[0] << 0 << 4 << QQuickTextInput::SelectWords << 0 << 4 << false;
726     QTest::newRow("<(the )quick>|words,rtl")
727             << standard[0] << 4 << 0 << QQuickTextInput::SelectWords << 0 << 9 << false;
728     QTest::newRow("<lazy( dog)>|words,ltr")
729             << standard[0] << 40 << 44 << QQuickTextInput::SelectWords << 36 << 44 << false;
730     QTest::newRow("lazy<( dog)>|words,rtl")
731             << standard[0] << 44 << 40 << QQuickTextInput::SelectWords << 40 << 44 << false;
732     QTest::newRow("<fox( jumped )>over|words,ltr")
733             << standard[0] << 19 << 27 << QQuickTextInput::SelectWords << 16 << 27 << false;
734     QTest::newRow("fox<( jumped )over>|words,rtl")
735             << standard[0] << 27 << 19 << QQuickTextInput::SelectWords << 19 << 31 << false;
736     QTest::newRow("<th(e qu)ick>|words")
737             << standard[0] << 2 << 6 << QQuickTextInput::SelectWords << 0 << 9 << true;
738     QTest::newRow("<la(zy d)og|words>")
739             << standard[0] << 38 << 42 << QQuickTextInput::SelectWords << 36 << 44 << true;
740     QTest::newRow("<jum(ped ov)er>|words")
741             << standard[0] << 23 << 29 << QQuickTextInput::SelectWords << 20 << 31 << true;
742     QTest::newRow("<()>the|words")
743             << standard[0] << 0 << 0 << QQuickTextInput::SelectWords << 0 << 0 << true;
744     QTest::newRow("dog<()>|words")
745             << standard[0] << 44 << 44 << QQuickTextInput::SelectWords << 44 << 44 << true;
746     QTest::newRow("jum<()>ped|words")
747             << standard[0] << 23 << 23 << QQuickTextInput::SelectWords << 23 << 23 << true;
748
749     QTest::newRow("Hello<(,)> |words")
750             << standard[2] << 5 << 6 << QQuickTextInput::SelectWords << 5 << 6 << true;
751     QTest::newRow("Hello<(, )>world|words,ltr")
752             << standard[2] << 5 << 7 << QQuickTextInput::SelectWords << 5 << 7 << false;
753     QTest::newRow("Hello<(, )world>|words,rtl")
754             << standard[2] << 7 << 5 << QQuickTextInput::SelectWords << 5 << 12 << false;
755     QTest::newRow("<Hel(lo, )>world|words,ltr")
756             << standard[2] << 3 << 7 << QQuickTextInput::SelectWords << 0 << 7 << false;
757     QTest::newRow("<Hel(lo, )world>|words,rtl")
758             << standard[2] << 7 << 3 << QQuickTextInput::SelectWords << 0 << 12 << false;
759     QTest::newRow("<Hel(lo)>,|words")
760             << standard[2] << 3 << 5 << QQuickTextInput::SelectWords << 0 << 5 << true;
761     QTest::newRow("Hello<()>,|words")
762             << standard[2] << 5 << 5 << QQuickTextInput::SelectWords << 5 << 5 << true;
763     QTest::newRow("Hello,<()>|words")
764             << standard[2] << 6 << 6 << QQuickTextInput::SelectWords << 6 << 6 << true;
765     QTest::newRow("Hello<,( )>world|words,ltr")
766             << standard[2] << 6 << 7 << QQuickTextInput::SelectWords << 5 << 7 << false;
767     QTest::newRow("Hello,<( )world>|words,rtl")
768             << standard[2] << 7 << 6 << QQuickTextInput::SelectWords << 6 << 12 << false;
769     QTest::newRow("Hello<,( world)>|words,ltr")
770             << standard[2] << 6 << 12 << QQuickTextInput::SelectWords << 5 << 12 << false;
771     QTest::newRow("Hello,<( world)>|words,rtl")
772             << standard[2] << 12 << 6 << QQuickTextInput::SelectWords << 6 << 12 << false;
773     QTest::newRow("Hello<,( world!)>|words,ltr")
774             << standard[2] << 6 << 13 << QQuickTextInput::SelectWords << 5 << 13 << false;
775     QTest::newRow("Hello,<( world!)>|words,rtl")
776             << standard[2] << 13 << 6 << QQuickTextInput::SelectWords << 6 << 13 << false;
777     QTest::newRow("Hello<(, world!)>|words")
778             << standard[2] << 5 << 13 << QQuickTextInput::SelectWords << 5 << 13 << true;
779     // Fails due to an issue with QTextBoundaryFinder and punctuation at the end of strings.
780     // QTBUG-11365
781     // QTest::newRow("world<(!)>|words")
782     //         << standard[2] << 12 << 13 << QQuickTextInput::SelectWords << 12 << 13 << true;
783     QTest::newRow("world!<()>)|words")
784             << standard[2] << 13 << 13 << QQuickTextInput::SelectWords << 13 << 13 << true;
785     QTest::newRow("world<()>!)|words")
786             << standard[2] << 12 << 12 << QQuickTextInput::SelectWords << 12 << 12 << true;
787
788     QTest::newRow("<(,)>olleH |words")
789             << standard[3] << 7 << 8 << QQuickTextInput::SelectWords << 7 << 8 << true;
790     QTest::newRow("<dlrow( ,)>olleH|words,ltr")
791             << standard[3] << 6 << 8 << QQuickTextInput::SelectWords << 1 << 8 << false;
792     QTest::newRow("dlrow<( ,)>olleH|words,rtl")
793             << standard[3] << 8 << 6 << QQuickTextInput::SelectWords << 6 << 8 << false;
794     QTest::newRow("<dlrow( ,ol)leH>|words,ltr")
795             << standard[3] << 6 << 10 << QQuickTextInput::SelectWords << 1 << 13 << false;
796     QTest::newRow("dlrow<( ,ol)leH>|words,rtl")
797             << standard[3] << 10 << 6 << QQuickTextInput::SelectWords << 6 << 13 << false;
798     QTest::newRow(",<(ol)leH>,|words")
799             << standard[3] << 8 << 10 << QQuickTextInput::SelectWords << 8 << 13 << true;
800     QTest::newRow(",<()>olleH|words")
801             << standard[3] << 8 << 8 << QQuickTextInput::SelectWords << 8 << 8 << true;
802     QTest::newRow("<()>,olleH|words")
803             << standard[3] << 7 << 7 << QQuickTextInput::SelectWords << 7 << 7 << true;
804     QTest::newRow("<dlrow( )>,olleH|words,ltr")
805             << standard[3] << 6 << 7 << QQuickTextInput::SelectWords << 1 << 7 << false;
806     QTest::newRow("dlrow<( ),>olleH|words,rtl")
807             << standard[3] << 7 << 6 << QQuickTextInput::SelectWords << 6 << 8 << false;
808     QTest::newRow("<(dlrow )>,olleH|words,ltr")
809             << standard[3] << 1 << 7 << QQuickTextInput::SelectWords << 1 << 7 << false;
810     QTest::newRow("<(dlrow ),>olleH|words,rtl")
811             << standard[3] << 7 << 1 << QQuickTextInput::SelectWords << 1 << 8 << false;
812     QTest::newRow("<(!dlrow )>,olleH|words,ltr")
813             << standard[3] << 0 << 7 << QQuickTextInput::SelectWords << 0 << 7 << false;
814     QTest::newRow("<(!dlrow ),>olleH|words,rtl")
815             << standard[3] << 7 << 0 << QQuickTextInput::SelectWords << 0 << 8 << false;
816     QTest::newRow("(!dlrow ,)olleH|words")
817             << standard[3] << 0 << 8 << QQuickTextInput::SelectWords << 0 << 8 << true;
818     QTest::newRow("<(!)>dlrow|words")
819             << standard[3] << 0 << 1 << QQuickTextInput::SelectWords << 0 << 1 << true;
820     QTest::newRow("<()>!dlrow|words")
821             << standard[3] << 0 << 0 << QQuickTextInput::SelectWords << 0 << 0 << true;
822     QTest::newRow("!<()>dlrow|words")
823             << standard[3] << 1 << 1 << QQuickTextInput::SelectWords << 1 << 1 << true;
824
825     QTest::newRow(" <s(pac)ey>   text |words")
826             << standard[4] << 1 << 4 << QQuickTextInput::SelectWords << 1 << 7 << true;
827     QTest::newRow(" spacey   <t(ex)t> |words")
828             << standard[4] << 11 << 13 << QQuickTextInput::SelectWords << 10 << 14 << false; // Should be reversible. QTBUG-11365
829     QTest::newRow("<( )>spacey   text |words|ltr")
830             << standard[4] << 0 << 1 << QQuickTextInput::SelectWords << 0 << 1 << false;
831     QTest::newRow("<( )spacey>   text |words|rtl")
832             << standard[4] << 1 << 0 << QQuickTextInput::SelectWords << 0 << 7 << false;
833     QTest::newRow("spacey   <text( )>|words|ltr")
834             << standard[4] << 14 << 15 << QQuickTextInput::SelectWords << 10 << 15 << false;
835 //    QTBUG-11365
836 //    QTest::newRow("spacey   text<( )>|words|rtl")
837 //            << standard[4] << 15 << 14 << QQuickTextInput::SelectWords << 14 << 15 << false;
838     QTest::newRow("<()> spacey   text |words")
839             << standard[4] << 0 << 0 << QQuickTextInput::SelectWords << 0 << 0 << false;
840     QTest::newRow(" spacey   text <()>|words")
841             << standard[4] << 15 << 15 << QQuickTextInput::SelectWords << 15 << 15 << false;
842 }
843
844 void tst_qquicktextinput::moveCursorSelection()
845 {
846     QFETCH(QString, testStr);
847     QFETCH(int, cursorPosition);
848     QFETCH(int, movePosition);
849     QFETCH(QQuickTextInput::SelectionMode, mode);
850     QFETCH(int, selectionStart);
851     QFETCH(int, selectionEnd);
852     QFETCH(bool, reversible);
853
854     QString componentStr = "import QtQuick 2.0\nTextInput {  text: \""+ testStr +"\"; }";
855     QDeclarativeComponent textinputComponent(&engine);
856     textinputComponent.setData(componentStr.toLatin1(), QUrl());
857     QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
858     QVERIFY(textinputObject != 0);
859
860     textinputObject->setCursorPosition(cursorPosition);
861     textinputObject->moveCursorSelection(movePosition, mode);
862
863     QCOMPARE(textinputObject->selectedText(), testStr.mid(selectionStart, selectionEnd - selectionStart));
864     QCOMPARE(textinputObject->selectionStart(), selectionStart);
865     QCOMPARE(textinputObject->selectionEnd(), selectionEnd);
866
867     if (reversible) {
868         textinputObject->setCursorPosition(movePosition);
869         textinputObject->moveCursorSelection(cursorPosition, mode);
870
871         QCOMPARE(textinputObject->selectedText(), testStr.mid(selectionStart, selectionEnd - selectionStart));
872         QCOMPARE(textinputObject->selectionStart(), selectionStart);
873         QCOMPARE(textinputObject->selectionEnd(), selectionEnd);
874     }
875
876     delete textinputObject;
877 }
878
879 void tst_qquicktextinput::moveCursorSelectionSequence_data()
880 {
881     QTest::addColumn<QString>("testStr");
882     QTest::addColumn<int>("cursorPosition");
883     QTest::addColumn<int>("movePosition1");
884     QTest::addColumn<int>("movePosition2");
885     QTest::addColumn<int>("selection1Start");
886     QTest::addColumn<int>("selection1End");
887     QTest::addColumn<int>("selection2Start");
888     QTest::addColumn<int>("selection2End");
889
890     // () contains the text selected by the cursor.
891     // <> contains the actual selection.
892     // ^ is the revised cursor position.
893     // {} contains the revised selection.
894
895     QTest::newRow("the {<quick( bro)wn> f^ox} jumped|ltr")
896             << standard[0]
897             << 9 << 13 << 17
898             << 4 << 15
899             << 4 << 19;
900     QTest::newRow("the quick<( {bro)wn> f^ox} jumped|rtl")
901             << standard[0]
902             << 13 << 9 << 17
903             << 9 << 15
904             << 10 << 19;
905     QTest::newRow("the {<quick( bro)wn> ^}fox jumped|ltr")
906             << standard[0]
907             << 9 << 13 << 16
908             << 4 << 15
909             << 4 << 16;
910     QTest::newRow("the quick<( {bro)wn> ^}fox jumped|rtl")
911             << standard[0]
912             << 13 << 9 << 16
913             << 9 << 15
914             << 10 << 16;
915     QTest::newRow("the {<quick( bro)wn^>} fox jumped|ltr")
916             << standard[0]
917             << 9 << 13 << 15
918             << 4 << 15
919             << 4 << 15;
920     QTest::newRow("the quick<( {bro)wn^>} f^ox jumped|rtl")
921             << standard[0]
922             << 13 << 9 << 15
923             << 9 << 15
924             << 10 << 15;
925     QTest::newRow("the {<quick() ^}bro)wn> fox|ltr")
926             << standard[0]
927             << 9 << 13 << 10
928             << 4 << 15
929             << 4 << 10;
930     QTest::newRow("the quick<( {^bro)wn>} fox|rtl")
931             << standard[0]
932             << 13 << 9 << 10
933             << 9 << 15
934             << 10 << 15;
935     QTest::newRow("the {<quick^}( bro)wn> fox|ltr")
936             << standard[0]
937             << 9 << 13 << 9
938             << 4 << 15
939             << 4 << 9;
940     QTest::newRow("the quick{<(^ bro)wn>} fox|rtl")
941             << standard[0]
942             << 13 << 9 << 9
943             << 9 << 15
944             << 9 << 15;
945     QTest::newRow("the {<qui^ck}( bro)wn> fox|ltr")
946             << standard[0]
947             << 9 << 13 << 7
948             << 4 << 15
949             << 4 << 9;
950     QTest::newRow("the {<qui^ck}( bro)wn> fox|rtl")
951             << standard[0]
952             << 13 << 9 << 7
953             << 9 << 15
954             << 4 << 15;
955     QTest::newRow("the {<^quick}( bro)wn> fox|ltr")
956             << standard[0]
957             << 9 << 13 << 4
958             << 4 << 15
959             << 4 << 9;
960     QTest::newRow("the {<^quick}( bro)wn> fox|rtl")
961             << standard[0]
962             << 13 << 9 << 4
963             << 9 << 15
964             << 4 << 15;
965     QTest::newRow("the{^ <quick}( bro)wn> fox|ltr")
966             << standard[0]
967             << 9 << 13 << 3
968             << 4 << 15
969             << 3 << 9;
970     QTest::newRow("the{^ <quick}( bro)wn> fox|rtl")
971             << standard[0]
972             << 13 << 9 << 3
973             << 9 << 15
974             << 3 << 15;
975     QTest::newRow("{t^he <quick}( bro)wn> fox|ltr")
976             << standard[0]
977             << 9 << 13 << 1
978             << 4 << 15
979             << 0 << 9;
980     QTest::newRow("{t^he <quick}( bro)wn> fox|rtl")
981             << standard[0]
982             << 13 << 9 << 1
983             << 9 << 15
984             << 0 << 15;
985
986     QTest::newRow("{<He(ll)o>, w^orld}!|ltr")
987             << standard[2]
988             << 2 << 4 << 8
989             << 0 << 5
990             << 0 << 12;
991     QTest::newRow("{<He(ll)o>, w^orld}!|rtl")
992             << standard[2]
993             << 4 << 2 << 8
994             << 0 << 5
995             << 0 << 12;
996
997     QTest::newRow("!{dlro^w ,<o(ll)eH>}|ltr")
998             << standard[3]
999             << 9 << 11 << 5
1000             << 8 << 13
1001             << 1 << 13;
1002     QTest::newRow("!{dlro^w ,<o(ll)eH>}|rtl")
1003             << standard[3]
1004             << 11 << 9 << 5
1005             << 8 << 13
1006             << 1 << 13;
1007
1008     QTest::newRow("{<(^} sp)acey>   text |ltr")
1009             << standard[4]
1010             << 0 << 3 << 0
1011             << 0 << 7
1012             << 0 << 0;
1013     QTest::newRow("{<( ^}sp)acey>   text |ltr")
1014             << standard[4]
1015             << 0 << 3 << 1
1016             << 0 << 7
1017             << 0 << 1;
1018     QTest::newRow("<( {s^p)acey>}   text |rtl")
1019             << standard[4]
1020             << 3 << 0 << 2
1021             << 0 << 7
1022             << 1 << 7;
1023     QTest::newRow("<( {^sp)acey>}   text |rtl")
1024             << standard[4]
1025             << 3 << 0 << 1
1026             << 0 << 7
1027             << 1 << 7;
1028
1029     QTest::newRow(" spacey   <te(xt {^)>}|rtl")
1030             << standard[4]
1031             << 15 << 12 << 15
1032             << 10 << 15
1033             << 15 << 15;
1034 //    QTBUG-11365
1035 //    QTest::newRow(" spacey   <te(xt{^ )>}|rtl")
1036 //            << standard[4]
1037 //            << 15 << 12 << 14
1038 //            << 10 << 15
1039 //            << 14 << 15;
1040     QTest::newRow(" spacey   {<te(x^t} )>|ltr")
1041             << standard[4]
1042             << 12 << 15 << 13
1043             << 10 << 15
1044             << 10 << 14;
1045 //    QTBUG-11365
1046 //    QTest::newRow(" spacey   {<te(xt^} )>|ltr")
1047 //            << standard[4]
1048 //            << 12 << 15 << 14
1049 //            << 10 << 15
1050 //            << 10 << 14;
1051 }
1052
1053 void tst_qquicktextinput::moveCursorSelectionSequence()
1054 {
1055     QFETCH(QString, testStr);
1056     QFETCH(int, cursorPosition);
1057     QFETCH(int, movePosition1);
1058     QFETCH(int, movePosition2);
1059     QFETCH(int, selection1Start);
1060     QFETCH(int, selection1End);
1061     QFETCH(int, selection2Start);
1062     QFETCH(int, selection2End);
1063
1064     QString componentStr = "import QtQuick 2.0\nTextInput {  text: \""+ testStr +"\"; }";
1065     QDeclarativeComponent textinputComponent(&engine);
1066     textinputComponent.setData(componentStr.toLatin1(), QUrl());
1067     QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput*>(textinputComponent.create());
1068     QVERIFY(textinputObject != 0);
1069
1070     textinputObject->setCursorPosition(cursorPosition);
1071
1072     textinputObject->moveCursorSelection(movePosition1, QQuickTextInput::SelectWords);
1073     QCOMPARE(textinputObject->selectedText(), testStr.mid(selection1Start, selection1End - selection1Start));
1074     QCOMPARE(textinputObject->selectionStart(), selection1Start);
1075     QCOMPARE(textinputObject->selectionEnd(), selection1End);
1076
1077     textinputObject->moveCursorSelection(movePosition2, QQuickTextInput::SelectWords);
1078     QCOMPARE(textinputObject->selectedText(), testStr.mid(selection2Start, selection2End - selection2Start));
1079     QCOMPARE(textinputObject->selectionStart(), selection2Start);
1080     QCOMPARE(textinputObject->selectionEnd(), selection2End);
1081
1082     delete textinputObject;
1083 }
1084
1085 void tst_qquicktextinput::dragMouseSelection()
1086 {
1087     QString qmlfile = testFile("mouseselection_true.qml");
1088
1089     QQuickView canvas(QUrl::fromLocalFile(qmlfile));
1090
1091     canvas.show();
1092     canvas.requestActivateWindow();
1093     QTest::qWaitForWindowShown(&canvas);
1094
1095     QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
1096
1097     QVERIFY(canvas.rootObject() != 0);
1098     QQuickTextInput *textInputObject = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1099     QVERIFY(textInputObject != 0);
1100
1101     // press-and-drag-and-release from x1 to x2
1102     int x1 = 10;
1103     int x2 = 70;
1104     int y = textInputObject->height()/2;
1105     QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y));
1106     QTest::mouseMove(&canvas, QPoint(x2, y));
1107     QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y));
1108     QTest::qWait(100);
1109     QString str1;
1110     QVERIFY((str1 = textInputObject->selectedText()).length() > 3);
1111     QVERIFY(str1.length() > 3);
1112
1113     // press and drag the current selection.
1114     x1 = 40;
1115     x2 = 100;
1116     QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y));
1117     QTest::mouseMove(&canvas, QPoint(x2, y));
1118     QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y));
1119     QTest::qWait(300);
1120     QString str2 = textInputObject->selectedText();
1121     QVERIFY(str2.length() > 3);
1122
1123     QVERIFY(str1 != str2);
1124 }
1125
1126 void tst_qquicktextinput::mouseSelectionMode_data()
1127 {
1128     QTest::addColumn<QString>("qmlfile");
1129     QTest::addColumn<bool>("selectWords");
1130
1131     // import installed
1132     QTest::newRow("SelectWords") << testFile("mouseselectionmode_words.qml") << true;
1133     QTest::newRow("SelectCharacters") << testFile("mouseselectionmode_characters.qml") << false;
1134     QTest::newRow("default") << testFile("mouseselectionmode_default.qml") << false;
1135 }
1136
1137 void tst_qquicktextinput::mouseSelectionMode()
1138 {
1139     QFETCH(QString, qmlfile);
1140     QFETCH(bool, selectWords);
1141
1142     QString text = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1143
1144     QQuickView canvas(QUrl::fromLocalFile(qmlfile));
1145
1146     canvas.show();
1147     canvas.requestActivateWindow();
1148     QTest::qWaitForWindowShown(&canvas);
1149     QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
1150
1151     QVERIFY(canvas.rootObject() != 0);
1152     QQuickTextInput *textInputObject = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1153     QVERIFY(textInputObject != 0);
1154
1155     // press-and-drag-and-release from x1 to x2
1156     int x1 = 10;
1157     int x2 = 70;
1158     int y = textInputObject->height()/2;
1159     QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y));
1160     QTest::mouseMove(&canvas, QPoint(x2,y)); // doesn't work
1161     QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y));
1162     QTest::qWait(300);
1163     if (selectWords) {
1164         QTRY_COMPARE(textInputObject->selectedText(), text);
1165     } else {
1166         QTRY_VERIFY(textInputObject->selectedText().length() > 3);
1167         QVERIFY(textInputObject->selectedText() != text);
1168     }
1169 }
1170
1171 void tst_qquicktextinput::horizontalAlignment_data()
1172 {
1173     QTest::addColumn<int>("hAlign");
1174     QTest::addColumn<QString>("expectfile");
1175
1176     QTest::newRow("L") << int(Qt::AlignLeft) << "halign_left";
1177     QTest::newRow("R") << int(Qt::AlignRight) << "halign_right";
1178     QTest::newRow("C") << int(Qt::AlignHCenter) << "halign_center";
1179 }
1180
1181 void tst_qquicktextinput::horizontalAlignment()
1182 {
1183     QSKIP("Image comparison of text is almost guaranteed to fail during development");
1184
1185     QFETCH(int, hAlign);
1186     QFETCH(QString, expectfile);
1187
1188     QQuickView canvas(testFileUrl("horizontalAlignment.qml"));
1189
1190     canvas.show();
1191     canvas.requestActivateWindow();
1192     QTest::qWaitForWindowShown(&canvas);
1193     QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
1194     QObject *ob = canvas.rootObject();
1195     QVERIFY(ob != 0);
1196     ob->setProperty("horizontalAlignment",hAlign);
1197     QImage actual = canvas.grabFrameBuffer();
1198
1199     expectfile = createExpectedFileIfNotFound(expectfile, actual);
1200
1201     QImage expect(expectfile);
1202
1203     QCOMPARE(actual,expect);
1204 }
1205
1206 void tst_qquicktextinput::horizontalAlignment_RightToLeft()
1207 {
1208     QQuickView canvas(testFileUrl("horizontalAlignment_RightToLeft.qml"));
1209     QQuickTextInput *textInput = canvas.rootObject()->findChild<QQuickTextInput*>("text");
1210     QVERIFY(textInput != 0);
1211     canvas.show();
1212
1213     const QString rtlText = textInput->text();
1214
1215     QQuickTextInputPrivate *textInputPrivate = QQuickTextInputPrivate::get(textInput);
1216     QVERIFY(textInputPrivate != 0);
1217     QVERIFY(textInput->boundingRect().left() > canvas.width()/2);
1218
1219     // implicit alignment should follow the reading direction of RTL text
1220     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1221     QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1222     QVERIFY(textInput->boundingRect().left() > canvas.width()/2);
1223
1224     // explicitly left aligned
1225     textInput->setHAlign(QQuickTextInput::AlignLeft);
1226     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
1227     QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1228     QVERIFY(textInput->boundingRect().left() < canvas.width()/2);
1229
1230     // explicitly right aligned
1231     textInput->setHAlign(QQuickTextInput::AlignRight);
1232     QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1233     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1234     QVERIFY(textInput->boundingRect().left() > canvas.width()/2);
1235
1236     // explicitly center aligned
1237     textInput->setHAlign(QQuickTextInput::AlignHCenter);
1238     QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1239     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignHCenter);
1240     QVERIFY(textInput->boundingRect().left() < canvas.width()/2);
1241     QVERIFY(textInput->boundingRect().right() > canvas.width()/2);
1242
1243     // reseted alignment should go back to following the text reading direction
1244     textInput->resetHAlign();
1245     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1246     QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1247     QVERIFY(textInput->boundingRect().left() > canvas.width()/2);
1248
1249     // mirror the text item
1250     QQuickItemPrivate::get(textInput)->setLayoutMirror(true);
1251
1252     // mirrored implicit alignment should continue to follow the reading direction of the text
1253     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1254     QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1255     QVERIFY(textInput->boundingRect().left() > canvas.width()/2);
1256
1257     // explicitly right aligned behaves as left aligned
1258     textInput->setHAlign(QQuickTextInput::AlignRight);
1259     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1260     QCOMPARE(textInput->effectiveHAlign(), QQuickTextInput::AlignLeft);
1261     QVERIFY(textInput->boundingRect().left() < canvas.width()/2);
1262
1263     // mirrored explicitly left aligned behaves as right aligned
1264     textInput->setHAlign(QQuickTextInput::AlignLeft);
1265     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
1266     QCOMPARE(textInput->effectiveHAlign(), QQuickTextInput::AlignRight);
1267     QVERIFY(textInput->boundingRect().left() > canvas.width()/2);
1268
1269     // disable mirroring
1270     QQuickItemPrivate::get(textInput)->setLayoutMirror(false);
1271     QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1272     textInput->resetHAlign();
1273
1274     // English text should be implicitly left aligned
1275     textInput->setText("Hello world!");
1276     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
1277     QVERIFY(textInput->boundingRect().left() < canvas.width()/2);
1278
1279     canvas.requestActivateWindow();
1280     QTest::qWaitForWindowShown(&canvas);
1281     QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
1282
1283     // If there is no commited text, the preedit text should determine the alignment.
1284     textInput->setText(QString());
1285     { QInputMethodEvent ev(rtlText, QList<QInputMethodEvent::Attribute>()); QGuiApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &ev); }
1286     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1287     { QInputMethodEvent ev("Hello world!", QList<QInputMethodEvent::Attribute>()); QGuiApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &ev); }
1288     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignLeft);
1289
1290     // Clear pre-edit text.  TextInput should maybe do this itself on setText, but that may be
1291     // redundant as an actual input method may take care of it.
1292     { QInputMethodEvent ev; QGuiApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &ev); }
1293
1294     // empty text with implicit alignment follows the system locale-based
1295     // keyboard input direction from QGuiApplication::keyboardInputDirection
1296     textInput->setText("");
1297     QCOMPARE(textInput->hAlign(), QGuiApplication::keyboardInputDirection() == Qt::LeftToRight ?
1298                                   QQuickTextInput::AlignLeft : QQuickTextInput::AlignRight);
1299     if (QGuiApplication::keyboardInputDirection() == Qt::LeftToRight)
1300         QVERIFY(textInput->boundingRect().left() < canvas.width()/2);
1301     else
1302         QVERIFY(textInput->boundingRect().left() > canvas.width()/2);
1303     textInput->setHAlign(QQuickTextInput::AlignRight);
1304     QCOMPARE(textInput->hAlign(), QQuickTextInput::AlignRight);
1305     QVERIFY(textInput->boundingRect().left() > canvas.width()/2);
1306
1307     QString componentStr = "import QtQuick 2.0\nTextInput {}";
1308     QDeclarativeComponent textComponent(&engine);
1309     textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1310     QQuickTextInput *textObject = qobject_cast<QQuickTextInput*>(textComponent.create());
1311     QCOMPARE(textObject->hAlign(), QGuiApplication::keyboardInputDirection() == Qt::LeftToRight ?
1312                                   QQuickTextInput::AlignLeft : QQuickTextInput::AlignRight);
1313     delete textObject;
1314 }
1315
1316 void tst_qquicktextinput::verticalAlignment()
1317 {
1318     QQuickView canvas(testFileUrl("horizontalAlignment.qml"));
1319     QQuickTextInput *textInput = canvas.rootObject()->findChild<QQuickTextInput*>("text");
1320     QVERIFY(textInput != 0);
1321     canvas.show();
1322
1323     QQuickTextInputPrivate *textInputPrivate = QQuickTextInputPrivate::get(textInput);
1324     QVERIFY(textInputPrivate != 0);
1325
1326     QCOMPARE(textInput->vAlign(), QQuickTextInput::AlignTop);
1327     QVERIFY(textInput->boundingRect().bottom() < canvas.height() / 2);
1328
1329     // bottom aligned
1330     textInput->setVAlign(QQuickTextInput::AlignBottom);
1331     QCOMPARE(textInput->vAlign(), QQuickTextInput::AlignBottom);
1332     QVERIFY(textInput->boundingRect().top () > canvas.height() / 2);
1333
1334     // explicitly center aligned
1335     textInput->setVAlign(QQuickTextInput::AlignVCenter);
1336     QCOMPARE(textInput->vAlign(), QQuickTextInput::AlignVCenter);
1337     QVERIFY(textInput->boundingRect().top() < canvas.height() / 2);
1338     QVERIFY(textInput->boundingRect().bottom() > canvas.height() / 2);
1339 }
1340
1341 void tst_qquicktextinput::positionAt()
1342 {
1343     QQuickView canvas(testFileUrl("positionAt.qml"));
1344     QVERIFY(canvas.rootObject() != 0);
1345     canvas.show();
1346     canvas.requestActivateWindow();
1347     QTest::qWaitForWindowShown(&canvas);
1348
1349     QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1350     QVERIFY(textinputObject != 0);
1351
1352     // Check autoscrolled...
1353
1354     int pos = evaluate<int>(textinputObject, QString("positionAt(%1)").arg(textinputObject->width()/2));
1355
1356     QTextLayout layout(textinputObject->text());
1357     layout.setFont(textinputObject->font());
1358
1359     if (!qmlDisableDistanceField()) {
1360         QTextOption option;
1361         option.setUseDesignMetrics(true);
1362         layout.setTextOption(option);
1363     }
1364     layout.beginLayout();
1365     QTextLine line = layout.createLine();
1366     layout.endLayout();
1367
1368     int textLeftWidthBegin = floor(line.cursorToX(pos - 1));
1369     int textLeftWidthEnd = ceil(line.cursorToX(pos + 1));
1370     int textWidth = floor(line.horizontalAdvance());
1371
1372     QVERIFY(textLeftWidthBegin <= textWidth - textinputObject->width() / 2);
1373     QVERIFY(textLeftWidthEnd >= textWidth - textinputObject->width() / 2);
1374
1375     int x = textinputObject->positionToRectangle(pos + 1).x() - 1;
1376     QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, 0, TextInput.CursorBetweenCharacters)").arg(x)), pos + 1);
1377     QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, 0, TextInput.CursorOnCharacter)").arg(x)), pos);
1378
1379     // Check without autoscroll...
1380     textinputObject->setAutoScroll(false);
1381     pos = evaluate<int>(textinputObject, QString("positionAt(%1)").arg(textinputObject->width() / 2));
1382
1383     textLeftWidthBegin = floor(line.cursorToX(pos - 1));
1384     textLeftWidthEnd = ceil(line.cursorToX(pos + 1));
1385
1386     QVERIFY(textLeftWidthBegin <= textinputObject->width() / 2);
1387     QVERIFY(textLeftWidthEnd >= textinputObject->width() / 2);
1388
1389     x = textinputObject->positionToRectangle(pos + 1).x() - 1;
1390     QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, 0, TextInput.CursorBetweenCharacters)").arg(x)), pos + 1);
1391     QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, 0, TextInput.CursorOnCharacter)").arg(x)), pos);
1392
1393     const qreal x0 = textinputObject->positionToRectangle(pos).x();
1394     const qreal x1 = textinputObject->positionToRectangle(pos + 1).x();
1395
1396     QString preeditText = textinputObject->text().mid(0, pos);
1397     textinputObject->setText(textinputObject->text().mid(pos));
1398     textinputObject->setCursorPosition(0);
1399
1400     {   QInputMethodEvent inputEvent(preeditText, QList<QInputMethodEvent::Attribute>());
1401         QGuiApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &inputEvent); }
1402
1403     // Check all points within the preedit text return the same position.
1404     QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1)").arg(0)), 0);
1405     QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1)").arg(x0 / 2)), 0);
1406     QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1)").arg(x0)), 0);
1407
1408     // Verify positioning returns to normal after the preedit text.
1409     QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1)").arg(x1)), 1);
1410     QCOMPARE(textinputObject->positionToRectangle(1).x(), x1);
1411
1412     {   QInputMethodEvent inputEvent;
1413         QGuiApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &inputEvent); }
1414
1415     // With wrapping.
1416     textinputObject->setWrapMode(QQuickTextInput::WrapAnywhere);
1417
1418     const qreal y0 = line.height() / 2;
1419     const qreal y1 = line.height() * 3 / 2;
1420
1421     QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, %2)").arg(x0).arg(y0)), pos);
1422     QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, %2)").arg(x1).arg(y0)), pos + 1);
1423
1424     int newLinePos = evaluate<int>(textinputObject, QString("positionAt(%1, %2)").arg(x0).arg(y1));
1425     QVERIFY(newLinePos > pos);
1426     QCOMPARE(evaluate<int>(textinputObject, QString("positionAt(%1, %2)").arg(x1).arg(y1)), newLinePos + 1);
1427 }
1428
1429 void tst_qquicktextinput::maxLength()
1430 {
1431     QQuickView canvas(testFileUrl("maxLength.qml"));
1432     QVERIFY(canvas.rootObject() != 0);
1433     canvas.show();
1434     canvas.requestActivateWindow();
1435     QTest::qWaitForWindowShown(&canvas);
1436
1437     QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1438     QVERIFY(textinputObject != 0);
1439     QVERIFY(textinputObject->text().isEmpty());
1440     QVERIFY(textinputObject->maxLength() == 10);
1441     foreach (const QString &str, standard) {
1442         QVERIFY(textinputObject->text().length() <= 10);
1443         textinputObject->setText(str);
1444         QVERIFY(textinputObject->text().length() <= 10);
1445     }
1446
1447     textinputObject->setText("");
1448     QTRY_VERIFY(textinputObject->hasActiveFocus() == true);
1449     for (int i=0; i<20; i++) {
1450         QTRY_COMPARE(textinputObject->text().length(), qMin(i,10));
1451         //simulateKey(&canvas, Qt::Key_A);
1452         QTest::keyPress(&canvas, Qt::Key_A);
1453         QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1454         QTest::qWait(50);
1455     }
1456 }
1457
1458 void tst_qquicktextinput::masks()
1459 {
1460     //Not a comprehensive test of the possible masks, that's done elsewhere (QLineEdit)
1461     //QString componentStr = "import QtQuick 2.0\nTextInput {  inputMask: 'HHHHhhhh'; }";
1462     QQuickView canvas(testFileUrl("masks.qml"));
1463     canvas.show();
1464     canvas.requestActivateWindow();
1465     QVERIFY(canvas.rootObject() != 0);
1466     QQuickTextInput *textinputObject = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1467     QVERIFY(textinputObject != 0);
1468     QTRY_VERIFY(textinputObject->hasActiveFocus() == true);
1469     QVERIFY(textinputObject->text().length() == 0);
1470     QCOMPARE(textinputObject->inputMask(), QString("HHHHhhhh; "));
1471     for (int i=0; i<10; i++) {
1472         QTRY_COMPARE(qMin(i,8), textinputObject->text().length());
1473         QCOMPARE(i>=4, textinputObject->hasAcceptableInput());
1474         //simulateKey(&canvas, Qt::Key_A);
1475         QTest::keyPress(&canvas, Qt::Key_A);
1476         QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1477         QTest::qWait(50);
1478     }
1479 }
1480
1481 void tst_qquicktextinput::validators()
1482 {
1483     // Note that this test assumes that the validators are working properly
1484     // so you may need to run their tests first. All validators are checked
1485     // here to ensure that their exposure to QML is working.
1486
1487     QQuickView canvas(testFileUrl("validators.qml"));
1488     canvas.show();
1489     canvas.requestActivateWindow();
1490
1491     QVERIFY(canvas.rootObject() != 0);
1492
1493     QQuickTextInput *intInput = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("intInput")));
1494     QVERIFY(intInput);
1495     intInput->setFocus(true);
1496     QTRY_VERIFY(intInput->hasActiveFocus());
1497     QTest::keyPress(&canvas, Qt::Key_1);
1498     QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1499     QTest::qWait(50);
1500     QTRY_COMPARE(intInput->text(), QLatin1String("1"));
1501     QCOMPARE(intInput->hasAcceptableInput(), false);
1502     QTest::keyPress(&canvas, Qt::Key_2);
1503     QTest::keyRelease(&canvas, Qt::Key_2, Qt::NoModifier ,10);
1504     QTest::qWait(50);
1505     QTRY_COMPARE(intInput->text(), QLatin1String("1"));
1506     QCOMPARE(intInput->hasAcceptableInput(), false);
1507     QTest::keyPress(&canvas, Qt::Key_1);
1508     QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1509     QTest::qWait(50);
1510     QCOMPARE(intInput->text(), QLatin1String("11"));
1511     QCOMPARE(intInput->hasAcceptableInput(), true);
1512     QTest::keyPress(&canvas, Qt::Key_0);
1513     QTest::keyRelease(&canvas, Qt::Key_0, Qt::NoModifier ,10);
1514     QTest::qWait(50);
1515     QCOMPARE(intInput->text(), QLatin1String("11"));
1516     QCOMPARE(intInput->hasAcceptableInput(), true);
1517
1518     QQuickTextInput *dblInput = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("dblInput")));
1519     QTRY_VERIFY(dblInput);
1520     dblInput->setFocus(true);
1521     QVERIFY(dblInput->hasActiveFocus() == true);
1522     QTest::keyPress(&canvas, Qt::Key_1);
1523     QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1524     QTest::qWait(50);
1525     QTRY_COMPARE(dblInput->text(), QLatin1String("1"));
1526     QCOMPARE(dblInput->hasAcceptableInput(), false);
1527     QTest::keyPress(&canvas, Qt::Key_2);
1528     QTest::keyRelease(&canvas, Qt::Key_2, Qt::NoModifier ,10);
1529     QTest::qWait(50);
1530     QTRY_COMPARE(dblInput->text(), QLatin1String("12"));
1531     QCOMPARE(dblInput->hasAcceptableInput(), true);
1532     QTest::keyPress(&canvas, Qt::Key_Period);
1533     QTest::keyRelease(&canvas, Qt::Key_Period, Qt::NoModifier ,10);
1534     QTest::qWait(50);
1535     QTRY_COMPARE(dblInput->text(), QLatin1String("12."));
1536     QCOMPARE(dblInput->hasAcceptableInput(), true);
1537     QTest::keyPress(&canvas, Qt::Key_1);
1538     QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1539     QTest::qWait(50);
1540     QTRY_COMPARE(dblInput->text(), QLatin1String("12.1"));
1541     QCOMPARE(dblInput->hasAcceptableInput(), true);
1542     QTest::keyPress(&canvas, Qt::Key_1);
1543     QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1544     QTest::qWait(50);
1545     QTRY_COMPARE(dblInput->text(), QLatin1String("12.11"));
1546     QCOMPARE(dblInput->hasAcceptableInput(), true);
1547     QTest::keyPress(&canvas, Qt::Key_1);
1548     QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1549     QTest::qWait(50);
1550     QTRY_COMPARE(dblInput->text(), QLatin1String("12.11"));
1551     QCOMPARE(dblInput->hasAcceptableInput(), true);
1552
1553     QQuickTextInput *strInput = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("strInput")));
1554     QTRY_VERIFY(strInput);
1555     strInput->setFocus(true);
1556     QVERIFY(strInput->hasActiveFocus() == true);
1557     QTest::keyPress(&canvas, Qt::Key_1);
1558     QTest::keyRelease(&canvas, Qt::Key_1, Qt::NoModifier ,10);
1559     QTest::qWait(50);
1560     QTRY_COMPARE(strInput->text(), QLatin1String(""));
1561     QCOMPARE(strInput->hasAcceptableInput(), false);
1562     QTest::keyPress(&canvas, Qt::Key_A);
1563     QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1564     QTest::qWait(50);
1565     QTRY_COMPARE(strInput->text(), QLatin1String("a"));
1566     QCOMPARE(strInput->hasAcceptableInput(), false);
1567     QTest::keyPress(&canvas, Qt::Key_A);
1568     QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1569     QTest::qWait(50);
1570     QTRY_COMPARE(strInput->text(), QLatin1String("aa"));
1571     QCOMPARE(strInput->hasAcceptableInput(), true);
1572     QTest::keyPress(&canvas, Qt::Key_A);
1573     QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1574     QTest::qWait(50);
1575     QTRY_COMPARE(strInput->text(), QLatin1String("aaa"));
1576     QCOMPARE(strInput->hasAcceptableInput(), true);
1577     QTest::keyPress(&canvas, Qt::Key_A);
1578     QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1579     QTest::qWait(50);
1580     QTRY_COMPARE(strInput->text(), QLatin1String("aaaa"));
1581     QCOMPARE(strInput->hasAcceptableInput(), true);
1582     QTest::keyPress(&canvas, Qt::Key_A);
1583     QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
1584     QTest::qWait(50);
1585     QTRY_COMPARE(strInput->text(), QLatin1String("aaaa"));
1586     QCOMPARE(strInput->hasAcceptableInput(), true);
1587 }
1588
1589 void tst_qquicktextinput::inputMethods()
1590 {
1591     QQuickView canvas(testFileUrl("inputmethods.qml"));
1592     canvas.show();
1593     canvas.requestActivateWindow();
1594     QTest::qWaitForWindowShown(&canvas);
1595
1596     // test input method hints
1597     QVERIFY(canvas.rootObject() != 0);
1598     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(canvas.rootObject());
1599     QVERIFY(input != 0);
1600     QVERIFY(input->inputMethodHints() & Qt::ImhNoPredictiveText);
1601     input->setInputMethodHints(Qt::ImhUppercaseOnly);
1602     QVERIFY(input->inputMethodHints() & Qt::ImhUppercaseOnly);
1603
1604     input->setFocus(true);
1605     QVERIFY(input->hasActiveFocus() == true);
1606     // test that input method event is committed
1607     QInputMethodEvent event;
1608     event.setCommitString( "My ", -12, 0);
1609     QGuiApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &event);
1610     QCOMPARE(input->text(), QString("My Hello world!"));
1611
1612     input->setCursorPosition(2);
1613     event.setCommitString("Your", -2, 2);
1614     QGuiApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &event);
1615     QCOMPARE(input->text(), QString("Your Hello world!"));
1616     QCOMPARE(input->cursorPosition(), 4);
1617
1618     input->setCursorPosition(7);
1619     event.setCommitString("Goodbye", -2, 5);
1620     QGuiApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &event);
1621     QCOMPARE(input->text(), QString("Your Goodbye world!"));
1622     QCOMPARE(input->cursorPosition(), 12);
1623
1624     input->setCursorPosition(8);
1625     event.setCommitString("Our", -8, 4);
1626     QGuiApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &event);
1627     QCOMPARE(input->text(), QString("Our Goodbye world!"));
1628     QCOMPARE(input->cursorPosition(), 7);
1629
1630     // test that basic tentative commit gets to text property on preedit state
1631     input->setText("");
1632     QList<QInputMethodEvent::Attribute> attributes;
1633     QInputMethodEvent preeditEvent("test", attributes);
1634     preeditEvent.setTentativeCommitString("test");
1635     QApplication::sendEvent(input, &preeditEvent);
1636     QCOMPARE(input->text(), QString("test"));
1637
1638     // tentative commit not allowed present in surrounding text
1639     QInputMethodQueryEvent queryEvent(Qt::ImSurroundingText);
1640     QApplication::sendEvent(input, &queryEvent);
1641     QCOMPARE(queryEvent.value(Qt::ImSurroundingText).toString(), QString(""));
1642
1643     // if text with tentative commit does not validate, not allowed to be part of text property
1644     input->setText(""); // ensure input state is reset
1645     QValidator *validator = new QIntValidator(0, 100);
1646     input->setValidator(validator);
1647     QApplication::sendEvent(input, &preeditEvent);
1648     QCOMPARE(input->text(), QString(""));
1649     input->setValidator(0);
1650     delete validator;
1651
1652     // input should reset selection even if replacement parameters are out of bounds
1653     input->setText("text");
1654     input->setCursorPosition(0);
1655     input->moveCursorSelection(input->text().length());
1656     event.setCommitString("replacement", -input->text().length(), input->text().length());
1657     QGuiApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &event);
1658     QCOMPARE(input->selectionStart(), input->selectionEnd());
1659 }
1660
1661 /*
1662 TextInput element should only handle left/right keys until the cursor reaches
1663 the extent of the text, then they should ignore the keys.
1664
1665 */
1666 void tst_qquicktextinput::navigation()
1667 {
1668     QQuickView canvas(testFileUrl("navigation.qml"));
1669     canvas.show();
1670     canvas.requestActivateWindow();
1671
1672     QVERIFY(canvas.rootObject() != 0);
1673
1674     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
1675
1676     QVERIFY(input != 0);
1677     input->setCursorPosition(0);
1678     QTRY_VERIFY(input->hasActiveFocus() == true);
1679     simulateKey(&canvas, Qt::Key_Left);
1680     QVERIFY(input->hasActiveFocus() == false);
1681     simulateKey(&canvas, Qt::Key_Right);
1682     QVERIFY(input->hasActiveFocus() == true);
1683     //QT-2944: If text is selected, ensure we deselect upon cursor motion
1684     input->setCursorPosition(input->text().length());
1685     input->select(0,input->text().length());
1686     QVERIFY(input->selectionStart() != input->selectionEnd());
1687     simulateKey(&canvas, Qt::Key_Right);
1688     QVERIFY(input->selectionStart() == input->selectionEnd());
1689     QVERIFY(input->selectionStart() == input->text().length());
1690     QVERIFY(input->hasActiveFocus() == true);
1691     simulateKey(&canvas, Qt::Key_Right);
1692     QVERIFY(input->hasActiveFocus() == false);
1693     simulateKey(&canvas, Qt::Key_Left);
1694     QVERIFY(input->hasActiveFocus() == true);
1695
1696     // Up and Down should NOT do Home/End, even on Mac OS X (QTBUG-10438).
1697     input->setCursorPosition(2);
1698     QCOMPARE(input->cursorPosition(),2);
1699     simulateKey(&canvas, Qt::Key_Up);
1700     QCOMPARE(input->cursorPosition(),2);
1701     simulateKey(&canvas, Qt::Key_Down);
1702     QCOMPARE(input->cursorPosition(),2);
1703 }
1704
1705 void tst_qquicktextinput::navigation_RTL()
1706 {
1707     QQuickView canvas(testFileUrl("navigation.qml"));
1708     canvas.show();
1709     canvas.requestActivateWindow();
1710
1711     QVERIFY(canvas.rootObject() != 0);
1712
1713     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
1714
1715     QVERIFY(input != 0);
1716     const quint16 arabic_str[] = { 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0647};
1717     input->setText(QString::fromUtf16(arabic_str, 11));
1718
1719     input->setCursorPosition(0);
1720     QTRY_VERIFY(input->hasActiveFocus() == true);
1721
1722     // move off
1723     simulateKey(&canvas, Qt::Key_Right);
1724     QVERIFY(input->hasActiveFocus() == false);
1725
1726     // move back
1727     simulateKey(&canvas, Qt::Key_Left);
1728     QVERIFY(input->hasActiveFocus() == true);
1729
1730     input->setCursorPosition(input->text().length());
1731     QVERIFY(input->hasActiveFocus() == true);
1732
1733     // move off
1734     simulateKey(&canvas, Qt::Key_Left);
1735     QVERIFY(input->hasActiveFocus() == false);
1736
1737     // move back
1738     simulateKey(&canvas, Qt::Key_Right);
1739     QVERIFY(input->hasActiveFocus() == true);
1740 }
1741
1742 void tst_qquicktextinput::copyAndPaste() {
1743 #ifndef QT_NO_CLIPBOARD
1744
1745 #ifdef Q_OS_MAC
1746     {
1747         PasteboardRef pasteboard;
1748         OSStatus status = PasteboardCreate(0, &pasteboard);
1749         if (status == noErr)
1750             CFRelease(pasteboard);
1751         else
1752             QSKIP("This machine doesn't support the clipboard");
1753     }
1754 #endif
1755
1756     QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\" }";
1757     QDeclarativeComponent textInputComponent(&engine);
1758     textInputComponent.setData(componentStr.toLatin1(), QUrl());
1759     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
1760     QVERIFY(textInput != 0);
1761
1762     // copy and paste
1763     QCOMPARE(textInput->text().length(), 12);
1764     textInput->select(0, textInput->text().length());;
1765     textInput->copy();
1766     QCOMPARE(textInput->selectedText(), QString("Hello world!"));
1767     QCOMPARE(textInput->selectedText().length(), 12);
1768     textInput->setCursorPosition(0);
1769     QVERIFY(textInput->canPaste());
1770     textInput->paste();
1771     QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
1772     QCOMPARE(textInput->text().length(), 24);
1773
1774     // can paste
1775     QVERIFY(textInput->canPaste());
1776     textInput->setReadOnly(true);
1777     QVERIFY(!textInput->canPaste());
1778     textInput->setReadOnly(false);
1779     QVERIFY(textInput->canPaste());
1780
1781     // select word
1782     textInput->setCursorPosition(0);
1783     textInput->selectWord();
1784     QCOMPARE(textInput->selectedText(), QString("Hello"));
1785
1786     // select all and cut
1787     textInput->selectAll();
1788     textInput->cut();
1789     QCOMPARE(textInput->text().length(), 0);
1790     textInput->paste();
1791     QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
1792     QCOMPARE(textInput->text().length(), 24);
1793
1794     // clear copy buffer
1795     QClipboard *clipboard = QGuiApplication::clipboard();
1796     QVERIFY(clipboard);
1797     clipboard->clear();
1798     QVERIFY(!textInput->canPaste());
1799
1800     // test that copy functionality is disabled
1801     // when echo mode is set to hide text/password mode
1802     int index = 0;
1803     while (index < 4) {
1804         QQuickTextInput::EchoMode echoMode = QQuickTextInput::EchoMode(index);
1805         textInput->setEchoMode(echoMode);
1806         textInput->setText("My password");
1807         textInput->select(0, textInput->text().length());;
1808         textInput->copy();
1809         if (echoMode == QQuickTextInput::Normal) {
1810             QVERIFY(!clipboard->text().isEmpty());
1811             QCOMPARE(clipboard->text(), QString("My password"));
1812             clipboard->clear();
1813         } else {
1814             QVERIFY(clipboard->text().isEmpty());
1815         }
1816         index++;
1817     }
1818
1819     delete textInput;
1820 #endif
1821 }
1822
1823 void tst_qquicktextinput::copyAndPasteKeySequence() {
1824 #ifndef QT_NO_CLIPBOARD
1825
1826 #ifdef Q_OS_MAC
1827     {
1828         PasteboardRef pasteboard;
1829         OSStatus status = PasteboardCreate(0, &pasteboard);
1830         if (status == noErr)
1831             CFRelease(pasteboard);
1832         else
1833             QSKIP("This machine doesn't support the clipboard");
1834     }
1835 #endif
1836
1837     QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\"; focus: true }";
1838     QDeclarativeComponent textInputComponent(&engine);
1839     textInputComponent.setData(componentStr.toLatin1(), QUrl());
1840     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
1841     QVERIFY(textInput != 0);
1842
1843     QQuickCanvas canvas;
1844     textInput->setParentItem(canvas.rootItem());
1845     canvas.show();
1846     canvas.requestActivateWindow();
1847     QTest::qWaitForWindowShown(&canvas);
1848     QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
1849
1850     // copy and paste
1851     QVERIFY(textInput->hasActiveFocus());
1852     QCOMPARE(textInput->text().length(), 12);
1853     textInput->select(0, textInput->text().length());
1854     simulateKeys(&canvas, QKeySequence::Copy);
1855     QCOMPARE(textInput->selectedText(), QString("Hello world!"));
1856     QCOMPARE(textInput->selectedText().length(), 12);
1857     textInput->setCursorPosition(0);
1858     QVERIFY(textInput->canPaste());
1859     simulateKeys(&canvas, QKeySequence::Paste);
1860     QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
1861     QCOMPARE(textInput->text().length(), 24);
1862
1863     // select all and cut
1864     simulateKeys(&canvas, QKeySequence::SelectAll);
1865     simulateKeys(&canvas, QKeySequence::Cut);
1866     QCOMPARE(textInput->text().length(), 0);
1867     simulateKeys(&canvas, QKeySequence::Paste);
1868     QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
1869     QCOMPARE(textInput->text().length(), 24);
1870
1871     // clear copy buffer
1872     QClipboard *clipboard = QGuiApplication::clipboard();
1873     QVERIFY(clipboard);
1874     clipboard->clear();
1875     QVERIFY(!textInput->canPaste());
1876
1877     // test that copy functionality is disabled
1878     // when echo mode is set to hide text/password mode
1879     int index = 0;
1880     while (index < 4) {
1881         QQuickTextInput::EchoMode echoMode = QQuickTextInput::EchoMode(index);
1882         textInput->setEchoMode(echoMode);
1883         textInput->setText("My password");
1884         textInput->select(0, textInput->text().length());;
1885         simulateKeys(&canvas, QKeySequence::Copy);
1886         if (echoMode == QQuickTextInput::Normal) {
1887             QVERIFY(!clipboard->text().isEmpty());
1888             QCOMPARE(clipboard->text(), QString("My password"));
1889             clipboard->clear();
1890         } else {
1891             QVERIFY(clipboard->text().isEmpty());
1892         }
1893         index++;
1894     }
1895
1896     delete textInput;
1897 #endif
1898 }
1899
1900 void tst_qquicktextinput::canPasteEmpty() {
1901 #ifndef QT_NO_CLIPBOARD
1902
1903     QGuiApplication::clipboard()->clear();
1904
1905     QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\" }";
1906     QDeclarativeComponent textInputComponent(&engine);
1907     textInputComponent.setData(componentStr.toLatin1(), QUrl());
1908     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
1909     QVERIFY(textInput != 0);
1910
1911     bool cp = !textInput->isReadOnly() && QGuiApplication::clipboard()->text().length() != 0;
1912     QCOMPARE(textInput->canPaste(), cp);
1913
1914 #endif
1915 }
1916
1917 void tst_qquicktextinput::canPaste() {
1918 #ifndef QT_NO_CLIPBOARD
1919
1920     QGuiApplication::clipboard()->setText("Some text");
1921
1922     QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\" }";
1923     QDeclarativeComponent textInputComponent(&engine);
1924     textInputComponent.setData(componentStr.toLatin1(), QUrl());
1925     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
1926     QVERIFY(textInput != 0);
1927
1928     bool cp = !textInput->isReadOnly() && QGuiApplication::clipboard()->text().length() != 0;
1929     QCOMPARE(textInput->canPaste(), cp);
1930
1931 #endif
1932 }
1933
1934 void tst_qquicktextinput::passwordCharacter()
1935 {
1936     QString componentStr = "import QtQuick 2.0\nTextInput { text: \"Hello world!\"; font.family: \"Helvetica\"; echoMode: TextInput.Password }";
1937     QDeclarativeComponent textInputComponent(&engine);
1938     textInputComponent.setData(componentStr.toLatin1(), QUrl());
1939     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
1940     QVERIFY(textInput != 0);
1941
1942     textInput->setPasswordCharacter("X");
1943     qreal implicitWidth = textInput->implicitWidth();
1944     textInput->setPasswordCharacter(".");
1945
1946     // QTBUG-12383 content is updated and redrawn
1947     QVERIFY(textInput->implicitWidth() < implicitWidth);
1948
1949     delete textInput;
1950 }
1951
1952 void tst_qquicktextinput::cursorDelegate()
1953 {
1954     QQuickView view(testFileUrl("cursorTest.qml"));
1955     view.show();
1956     view.requestActivateWindow();
1957     QQuickTextInput *textInputObject = view.rootObject()->findChild<QQuickTextInput*>("textInputObject");
1958     QVERIFY(textInputObject != 0);
1959     QVERIFY(textInputObject->findChild<QQuickItem*>("cursorInstance"));
1960     //Test Delegate gets created
1961     textInputObject->setFocus(true);
1962     QQuickItem* delegateObject = textInputObject->findChild<QQuickItem*>("cursorInstance");
1963     QVERIFY(delegateObject);
1964     QCOMPARE(delegateObject->property("localProperty").toString(), QString("Hello"));
1965     //Test Delegate gets moved
1966     for (int i=0; i<= textInputObject->text().length(); i++) {
1967         textInputObject->setCursorPosition(i);
1968         QCOMPARE(textInputObject->cursorRectangle().x(), qRound(delegateObject->x()));
1969         QCOMPARE(textInputObject->cursorRectangle().y(), qRound(delegateObject->y()));
1970     }
1971     textInputObject->setCursorPosition(0);
1972     QCOMPARE(textInputObject->cursorRectangle().x(), qRound(delegateObject->x()));
1973     QCOMPARE(textInputObject->cursorRectangle().y(), qRound(delegateObject->y()));
1974     //Test Delegate gets deleted
1975     textInputObject->setCursorDelegate(0);
1976     QVERIFY(!textInputObject->findChild<QQuickItem*>("cursorInstance"));
1977 }
1978
1979 void tst_qquicktextinput::cursorVisible()
1980 {
1981     QQuickView view(testFileUrl("cursorVisible.qml"));
1982     view.show();
1983     view.requestActivateWindow();
1984     QTest::qWaitForWindowShown(&view);
1985     QTRY_COMPARE(&view, qGuiApp->focusWindow());
1986
1987     QQuickTextInput input;
1988     input.componentComplete();
1989     QSignalSpy spy(&input, SIGNAL(cursorVisibleChanged(bool)));
1990
1991     QCOMPARE(input.isCursorVisible(), false);
1992
1993     input.setCursorVisible(true);
1994     QCOMPARE(input.isCursorVisible(), true);
1995     QCOMPARE(spy.count(), 1);
1996
1997     input.setCursorVisible(false);
1998     QCOMPARE(input.isCursorVisible(), false);
1999     QCOMPARE(spy.count(), 2);
2000
2001     input.setFocus(true);
2002     QCOMPARE(input.isCursorVisible(), false);
2003     QCOMPARE(spy.count(), 2);
2004
2005     input.setParentItem(view.rootObject());
2006     QCOMPARE(input.isCursorVisible(), true);
2007     QCOMPARE(spy.count(), 3);
2008
2009     input.setFocus(false);
2010     QCOMPARE(input.isCursorVisible(), false);
2011     QCOMPARE(spy.count(), 4);
2012
2013     input.setFocus(true);
2014     QCOMPARE(input.isCursorVisible(), true);
2015     QCOMPARE(spy.count(), 5);
2016
2017     QQuickView alternateView;
2018     alternateView.show();
2019     alternateView.requestActivateWindow();
2020     QTest::qWaitForWindowShown(&alternateView);
2021
2022     QCOMPARE(input.isCursorVisible(), false);
2023     QCOMPARE(spy.count(), 6);
2024
2025     view.requestActivateWindow();
2026     QTest::qWaitForWindowShown(&view);
2027     QCOMPARE(input.isCursorVisible(), true);
2028     QCOMPARE(spy.count(), 7);
2029 }
2030
2031 void tst_qquicktextinput::cursorRectangle()
2032 {
2033
2034     QString text = "Hello World!";
2035
2036     QQuickTextInput input;
2037     input.setText(text);
2038     input.componentComplete();
2039
2040     QTextLayout layout(text);
2041     layout.setFont(input.font());
2042     if (!qmlDisableDistanceField()) {
2043         QTextOption option;
2044         option.setUseDesignMetrics(true);
2045         layout.setTextOption(option);
2046     }
2047     layout.beginLayout();
2048     QTextLine line = layout.createLine();
2049     layout.endLayout();
2050
2051     input.setWidth(line.cursorToX(5, QTextLine::Leading));
2052     input.setHeight(qCeil(line.height() * 3 / 2));
2053
2054     QRect r;
2055
2056     // some tolerance for different fonts.
2057 #ifdef Q_OS_LINUX
2058     const int error = 2;
2059 #else
2060     const int error = 5;
2061 #endif
2062
2063     for (int i = 0; i <= 5; ++i) {
2064         input.setCursorPosition(i);
2065         r = input.cursorRectangle();
2066
2067         QVERIFY(r.left() < qCeil(line.cursorToX(i, QTextLine::Trailing)));
2068         QVERIFY(r.right() >= qFloor(line.cursorToX(i , QTextLine::Leading)));
2069         QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRect(), r);
2070     }
2071
2072     // Check the cursor rectangle remains within the input bounding rect when auto scrolling.
2073     QVERIFY(r.left() < input.width());
2074     QVERIFY(r.right() >= input.width() - error);
2075
2076     for (int i = 6; i < text.length(); ++i) {
2077         input.setCursorPosition(i);
2078         QCOMPARE(r, input.cursorRectangle());
2079         QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRect(), r);
2080     }
2081
2082     for (int i = text.length() - 2; i >= 0; --i) {
2083         input.setCursorPosition(i);
2084         r = input.cursorRectangle();
2085         QCOMPARE(r.top(), 0);
2086         QVERIFY(r.right() >= 0);
2087         QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRect(), r);
2088     }
2089
2090     // Check vertical scrolling with word wrap.
2091     input.setWrapMode(QQuickTextInput::WordWrap);
2092     for (int i = 0; i <= 5; ++i) {
2093         input.setCursorPosition(i);
2094         r = input.cursorRectangle();
2095
2096         QVERIFY(r.left() < qCeil(line.cursorToX(i, QTextLine::Trailing)));
2097         QVERIFY(r.right() >= qFloor(line.cursorToX(i , QTextLine::Leading)));
2098         QCOMPARE(r.top(), 0);
2099         QCOMPARE(input.inputMethodQuery(Qt::ImCursorRectangle).toRect(), r);
2100     }
2101
2102     input.setCursorPosition(6);
2103     r = input.cursorRectangle();
2104     QCOMPARE(r.left(), 0);
2105     QVERIFY(r.bottom() >= input.height() - error);
2106
2107     for (int i = 7; i < text.length(); ++i) {
2108         input.setCursorPosition(i);
2109         r = input.cursorRectangle();
2110         QVERIFY(r.bottom() >= input.height() - error);
2111     }
2112
2113     for (int i = text.length() - 2; i >= 6; --i) {
2114         input.setCursorPosition(i);
2115         r = input.cursorRectangle();
2116         QVERIFY(r.bottom() >= input.height() - error);
2117     }
2118
2119     for (int i = 5; i >= 0; --i) {
2120         input.setCursorPosition(i);
2121         r = input.cursorRectangle();
2122         QCOMPARE(r.top(), 0);
2123     }
2124
2125     input.setText("Hi!");
2126     input.setHAlign(QQuickTextInput::AlignRight);
2127     r = input.cursorRectangle();
2128     QVERIFY(r.left() < input.width() + error);
2129     QVERIFY(r.right() >= input.width() - error);
2130 }
2131
2132 void tst_qquicktextinput::readOnly()
2133 {
2134     QQuickView canvas(testFileUrl("readOnly.qml"));
2135     canvas.show();
2136     canvas.requestActivateWindow();
2137
2138     QVERIFY(canvas.rootObject() != 0);
2139
2140     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
2141
2142     QVERIFY(input != 0);
2143     QTRY_VERIFY(input->hasActiveFocus() == true);
2144     QVERIFY(input->isReadOnly() == true);
2145     QString initial = input->text();
2146     for (int k=Qt::Key_0; k<=Qt::Key_Z; k++)
2147         simulateKey(&canvas, k);
2148     simulateKey(&canvas, Qt::Key_Return);
2149     simulateKey(&canvas, Qt::Key_Space);
2150     simulateKey(&canvas, Qt::Key_Escape);
2151     QCOMPARE(input->text(), initial);
2152
2153     input->setCursorPosition(3);
2154     input->setReadOnly(false);
2155     QCOMPARE(input->isReadOnly(), false);
2156     QCOMPARE(input->cursorPosition(), input->text().length());
2157 }
2158
2159 void tst_qquicktextinput::echoMode()
2160 {
2161     QQuickView canvas(testFileUrl("echoMode.qml"));
2162     canvas.show();
2163     canvas.requestActivateWindow();
2164     QTest::qWaitForWindowShown(&canvas);
2165     QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
2166
2167     QVERIFY(canvas.rootObject() != 0);
2168
2169     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
2170
2171     QVERIFY(input != 0);
2172     QTRY_VERIFY(input->hasActiveFocus() == true);
2173     QString initial = input->text();
2174     Qt::InputMethodHints ref;
2175     QCOMPARE(initial, QLatin1String("ABCDefgh"));
2176     QCOMPARE(input->echoMode(), QQuickTextInput::Normal);
2177     QCOMPARE(input->displayText(), input->text());
2178     //Normal
2179     ref &= ~Qt::ImhHiddenText;
2180     ref &= ~(Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText | Qt::ImhSensitiveData);
2181     QCOMPARE(input->inputMethodHints(), ref);
2182     input->setEchoMode(QQuickTextInput::NoEcho);
2183     QCOMPARE(input->text(), initial);
2184     QCOMPARE(input->displayText(), QLatin1String(""));
2185     QCOMPARE(input->passwordCharacter(), QLatin1String("*"));
2186     //NoEcho
2187     ref |= Qt::ImhHiddenText;
2188     ref |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText | Qt::ImhSensitiveData);
2189     QCOMPARE(input->inputMethodHints(), ref);
2190     input->setEchoMode(QQuickTextInput::Password);
2191     //Password
2192     ref |= Qt::ImhHiddenText;
2193     ref |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText | Qt::ImhSensitiveData);
2194     QCOMPARE(input->text(), initial);
2195     QCOMPARE(input->displayText(), QLatin1String("********"));
2196     QCOMPARE(input->inputMethodHints(), ref);
2197     input->setPasswordCharacter(QChar('Q'));
2198     QCOMPARE(input->passwordCharacter(), QLatin1String("Q"));
2199     QCOMPARE(input->text(), initial);
2200     QCOMPARE(input->displayText(), QLatin1String("QQQQQQQQ"));
2201     input->setEchoMode(QQuickTextInput::PasswordEchoOnEdit);
2202     //PasswordEchoOnEdit
2203     ref &= ~Qt::ImhHiddenText;
2204     ref |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText | Qt::ImhSensitiveData);
2205     QCOMPARE(input->inputMethodHints(), ref);
2206     QCOMPARE(input->text(), initial);
2207     QCOMPARE(input->displayText(), QLatin1String("QQQQQQQQ"));
2208     QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), QLatin1String("QQQQQQQQ"));
2209     QTest::keyPress(&canvas, Qt::Key_A);//Clearing previous entry is part of PasswordEchoOnEdit
2210     QTest::keyRelease(&canvas, Qt::Key_A, Qt::NoModifier ,10);
2211     QCOMPARE(input->text(), QLatin1String("a"));
2212     QCOMPARE(input->displayText(), QLatin1String("a"));
2213     QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), QLatin1String("a"));
2214     input->setFocus(false);
2215     QVERIFY(input->hasActiveFocus() == false);
2216     QCOMPARE(input->displayText(), QLatin1String("Q"));
2217     QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), QLatin1String("Q"));
2218     input->setFocus(true);
2219     QVERIFY(input->hasActiveFocus());
2220     QInputMethodEvent inputEvent;
2221     inputEvent.setCommitString(initial);
2222     QGuiApplication::sendEvent(input, &inputEvent);
2223     QCOMPARE(input->text(), initial);
2224     QCOMPARE(input->displayText(), initial);
2225     QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), initial);
2226 }
2227
2228 #ifdef QT_GUI_PASSWORD_ECHO_DELAY
2229 void tst_qquicktextinput::passwordEchoDelay()
2230 {
2231     QQuickView canvas(testFileUrl("echoMode.qml"));
2232     canvas.show();
2233     canvas.requestActivateWindow();
2234     QTest::qWaitForWindowShown(&canvas);
2235     QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
2236
2237     QVERIFY(canvas.rootObject() != 0);
2238
2239     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
2240
2241     QChar fillChar = QLatin1Char('*');
2242
2243     input->setEchoMode(QQuickTextInput::Password);
2244     QCOMPARE(input->displayText(), QString(8, fillChar));
2245     input->setText(QString());
2246     QCOMPARE(input->displayText(), QString());
2247
2248     QTest::keyPress(&canvas, '0');
2249     QTest::keyPress(&canvas, '1');
2250     QTest::keyPress(&canvas, '2');
2251     QCOMPARE(input->displayText(), QString(2, fillChar) + QLatin1Char('2'));
2252     QTest::keyPress(&canvas, '3');
2253     QTest::keyPress(&canvas, '4');
2254     QCOMPARE(input->displayText(), QString(4, fillChar) + QLatin1Char('4'));
2255     QTest::keyPress(&canvas, Qt::Key_Backspace);
2256     QCOMPARE(input->displayText(), QString(4, fillChar));
2257     QTest::keyPress(&canvas, '4');
2258     QCOMPARE(input->displayText(), QString(4, fillChar) + QLatin1Char('4'));
2259     QTest::qWait(QT_GUI_PASSWORD_ECHO_DELAY);
2260     QTRY_COMPARE(input->displayText(), QString(5, fillChar));
2261     QTest::keyPress(&canvas, '5');
2262     QCOMPARE(input->displayText(), QString(5, fillChar) + QLatin1Char('5'));
2263     input->setFocus(false);
2264     QVERIFY(!input->hasFocus());
2265     QCOMPARE(input->displayText(), QString(6, fillChar));
2266     input->setFocus(true);
2267     QTRY_VERIFY(input->hasFocus());
2268     QCOMPARE(input->displayText(), QString(6, fillChar));
2269     QTest::keyPress(&canvas, '6');
2270     QCOMPARE(input->displayText(), QString(6, fillChar) + QLatin1Char('6'));
2271
2272     QInputMethodEvent ev;
2273     ev.setCommitString(QLatin1String("7"));
2274     QGuiApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &ev);
2275     QCOMPARE(input->displayText(), QString(7, fillChar) + QLatin1Char('7'));
2276
2277     input->setCursorPosition(3);
2278     QCOMPARE(input->displayText(), QString(7, fillChar) + QLatin1Char('7'));
2279     QTest::keyPress(&canvas, 'a');
2280     QCOMPARE(input->displayText(), QString(3, fillChar) + QLatin1Char('a') + QString(5, fillChar));
2281     QTest::keyPress(&canvas, Qt::Key_Backspace);
2282     QCOMPARE(input->displayText(), QString(8, fillChar));
2283 }
2284 #endif
2285
2286
2287 void tst_qquicktextinput::simulateKey(QQuickView *view, int key)
2288 {
2289     QKeyEvent press(QKeyEvent::KeyPress, key, 0);
2290     QKeyEvent release(QKeyEvent::KeyRelease, key, 0);
2291
2292     QGuiApplication::sendEvent(view, &press);
2293     QGuiApplication::sendEvent(view, &release);
2294 }
2295
2296
2297 void tst_qquicktextinput::openInputPanel()
2298 {
2299     PlatformInputContext platformInputContext;
2300     QInputPanelPrivate *inputPanelPrivate = QInputPanelPrivate::get(qApp->inputPanel());
2301     inputPanelPrivate->testContext = &platformInputContext;
2302
2303     QQuickView view(testFileUrl("openInputPanel.qml"));
2304     view.show();
2305     view.requestActivateWindow();
2306     QTest::qWaitForWindowShown(&view);
2307     QTRY_COMPARE(&view, qGuiApp->focusWindow());
2308
2309     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
2310     QVERIFY(input);
2311
2312     // check default values
2313     QVERIFY(input->focusOnPress());
2314     QVERIFY(!input->hasActiveFocus());
2315     QCOMPARE(qApp->inputPanel()->inputItem(), static_cast<QObject*>(0));
2316     QCOMPARE(qApp->inputPanel()->visible(), false);
2317
2318     // input panel should open on focus
2319     QPoint centerPoint(view.width()/2, view.height()/2);
2320     Qt::KeyboardModifiers noModifiers = 0;
2321     QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
2322     QGuiApplication::processEvents();
2323     QVERIFY(input->hasActiveFocus());
2324     QCOMPARE(qApp->inputPanel()->inputItem(), input);
2325     QCOMPARE(qApp->inputPanel()->visible(), true);
2326     QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
2327
2328     // input panel should be re-opened when pressing already focused TextInput
2329     qApp->inputPanel()->hide();
2330     QCOMPARE(qApp->inputPanel()->visible(), false);
2331     QVERIFY(input->hasActiveFocus());
2332     QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
2333     QGuiApplication::processEvents();
2334     QCOMPARE(qApp->inputPanel()->visible(), true);
2335     QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
2336
2337     // input panel should stay visible if focus is lost to another text inputor
2338     QSignalSpy inputPanelVisibilitySpy(qApp->inputPanel(), SIGNAL(visibleChanged()));
2339     QQuickTextInput anotherInput;
2340     anotherInput.componentComplete();
2341     anotherInput.setParentItem(view.rootObject());
2342     anotherInput.setFocus(true);
2343     QCOMPARE(qApp->inputPanel()->visible(), true);
2344     QCOMPARE(qApp->inputPanel()->inputItem(), qobject_cast<QObject*>(&anotherInput));
2345     QCOMPARE(inputPanelVisibilitySpy.count(), 0);
2346
2347     anotherInput.setFocus(false);
2348     QCOMPARE(qApp->inputPanel()->inputItem(), static_cast<QObject*>(0));
2349     QCOMPARE(view.activeFocusItem(), view.rootItem());
2350     anotherInput.setFocus(true);
2351
2352     // input item should be null if focus is lost to an item that doesn't accept inputs
2353     QQuickItem item;
2354     item.setParentItem(view.rootObject());
2355     item.setFocus(true);
2356     QCOMPARE(qApp->inputPanel()->inputItem(), static_cast<QObject*>(0));
2357     QCOMPARE(view.activeFocusItem(), &item);
2358
2359     qApp->inputPanel()->hide();
2360
2361     // input panel should not be opened if TextInput is read only
2362     input->setReadOnly(true);
2363     input->setFocus(true);
2364     QCOMPARE(qApp->inputPanel()->visible(), false);
2365     QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
2366     QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
2367     QGuiApplication::processEvents();
2368     QCOMPARE(qApp->inputPanel()->visible(), false);
2369
2370     // input panel should not be opened if focusOnPress is set to false
2371     input->setFocusOnPress(false);
2372     input->setFocus(false);
2373     input->setFocus(true);
2374     QCOMPARE(qApp->inputPanel()->visible(), false);
2375     QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
2376     QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
2377     QCOMPARE(qApp->inputPanel()->visible(), false);
2378
2379     // input panel should open when openSoftwareInputPanel is called
2380     input->openSoftwareInputPanel();
2381     QCOMPARE(qApp->inputPanel()->visible(), true);
2382
2383     // input panel should close when closeSoftwareInputPanel is called
2384     input->closeSoftwareInputPanel();
2385     QCOMPARE(qApp->inputPanel()->visible(), false);
2386 }
2387
2388 class MyTextInput : public QQuickTextInput
2389 {
2390 public:
2391     MyTextInput(QQuickItem *parent = 0) : QQuickTextInput(parent)
2392     {
2393         nbPaint = 0;
2394     }
2395     virtual QSGNode *updatePaintNode(QSGNode *node, UpdatePaintNodeData *data)
2396     {
2397        nbPaint++;
2398        return QQuickTextInput::updatePaintNode(node, data);
2399     }
2400     int nbPaint;
2401 };
2402
2403 void tst_qquicktextinput::setHAlignClearCache()
2404 {
2405     QQuickView view;
2406     MyTextInput input;
2407     input.setText("Hello world");
2408     input.setParentItem(view.rootItem());
2409     view.show();
2410     view.requestActivateWindow();
2411     QTest::qWaitForWindowShown(&view);
2412     QTRY_COMPARE(input.nbPaint, 1);
2413     input.setHAlign(QQuickTextInput::AlignRight);
2414     //Changing the alignment should trigger a repaint
2415     QTRY_COMPARE(input.nbPaint, 2);
2416 }
2417
2418 void tst_qquicktextinput::focusOutClearSelection()
2419 {
2420     QQuickView view;
2421     QQuickTextInput input;
2422     QQuickTextInput input2;
2423     input.setText(QLatin1String("Hello world"));
2424     input.setFocus(true);
2425     input2.setParentItem(view.rootItem());
2426     input.setParentItem(view.rootItem());
2427     input.componentComplete();
2428     input2.componentComplete();
2429     view.show();
2430     view.requestActivateWindow();
2431     QTest::qWaitForWindowShown(&view);
2432     input.select(2,5);
2433     //The selection should work
2434     QTRY_COMPARE(input.selectedText(), QLatin1String("llo"));
2435     input2.setFocus(true);
2436     QGuiApplication::processEvents();
2437     //The input lost the focus selection should be cleared
2438     QTRY_COMPARE(input.selectedText(), QLatin1String(""));
2439 }
2440
2441 void tst_qquicktextinput::geometrySignals()
2442 {
2443     QDeclarativeComponent component(&engine, testFileUrl("geometrySignals.qml"));
2444     QObject *o = component.create();
2445     QVERIFY(o);
2446     QCOMPARE(o->property("bindingWidth").toInt(), 400);
2447     QCOMPARE(o->property("bindingHeight").toInt(), 500);
2448     delete o;
2449 }
2450
2451 void tst_qquicktextinput::testQtQuick11Attributes()
2452 {
2453     QFETCH(QString, code);
2454     QFETCH(QString, warning);
2455     QFETCH(QString, error);
2456
2457     QDeclarativeEngine engine;
2458     QObject *obj;
2459
2460     QDeclarativeComponent valid(&engine);
2461     valid.setData("import QtQuick 2.0; TextInput { " + code.toUtf8() + " }", QUrl(""));
2462     obj = valid.create();
2463     QVERIFY(obj);
2464     QVERIFY(valid.errorString().isEmpty());
2465     delete obj;
2466
2467     QDeclarativeComponent invalid(&engine);
2468     invalid.setData("import QtQuick 1.0; TextInput { " + code.toUtf8() + " }", QUrl(""));
2469     QTest::ignoreMessage(QtWarningMsg, warning.toUtf8());
2470     obj = invalid.create();
2471     QCOMPARE(invalid.errorString(), error);
2472     delete obj;
2473 }
2474
2475 void tst_qquicktextinput::testQtQuick11Attributes_data()
2476 {
2477     QTest::addColumn<QString>("code");
2478     QTest::addColumn<QString>("warning");
2479     QTest::addColumn<QString>("error");
2480
2481     QTest::newRow("canPaste") << "property bool foo: canPaste"
2482         << "<Unknown File>:1: ReferenceError: Can't find variable: canPaste"
2483         << "";
2484
2485     QTest::newRow("moveCursorSelection") << "Component.onCompleted: moveCursorSelection(0, TextEdit.SelectCharacters)"
2486         << "<Unknown File>:1: ReferenceError: Can't find variable: moveCursorSelection"
2487         << "";
2488
2489     QTest::newRow("deselect") << "Component.onCompleted: deselect()"
2490         << "<Unknown File>:1: ReferenceError: Can't find variable: deselect"
2491         << "";
2492 }
2493
2494 static void sendPreeditText(const QString &text, int cursor)
2495 {
2496     QInputMethodEvent event(text, QList<QInputMethodEvent::Attribute>()
2497             << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, cursor, text.length(), QVariant()));
2498     QCoreApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &event);
2499 }
2500
2501 void tst_qquicktextinput::preeditAutoScroll()
2502 {
2503     QString preeditText = "califragisiticexpialidocious!";
2504
2505     QQuickView view(testFileUrl("preeditAutoScroll.qml"));
2506     view.show();
2507     view.requestActivateWindow();
2508     QTest::qWaitForWindowShown(&view);
2509     QTRY_COMPARE(&view, qGuiApp->focusWindow());
2510     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
2511     QVERIFY(input);
2512     QVERIFY(input->hasActiveFocus());
2513
2514     input->setWidth(input->implicitWidth());
2515
2516     QSignalSpy cursorRectangleSpy(input, SIGNAL(cursorRectangleChanged()));
2517     int cursorRectangleChanges = 0;
2518
2519     // test the text is scrolled so the preedit is visible.
2520     sendPreeditText(preeditText.mid(0, 3), 1);
2521     QVERIFY(evaluate<int>(input, QString("positionAt(0)")) != 0);
2522     QVERIFY(input->cursorRectangle().left() < input->boundingRect().width());
2523     QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2524
2525     // test the text is scrolled back when the preedit is removed.
2526     QInputMethodEvent imEvent;
2527     QCoreApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &imEvent);
2528     QCOMPARE(evaluate<int>(input, QString("positionAt(%1)").arg(0)), 0);
2529     QCOMPARE(evaluate<int>(input, QString("positionAt(%1)").arg(input->width())), 5);
2530     QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2531
2532     QTextLayout layout(preeditText);
2533     layout.setFont(input->font());
2534     if (!qmlDisableDistanceField()) {
2535         QTextOption option;
2536         option.setUseDesignMetrics(true);
2537         layout.setTextOption(option);
2538     }
2539     layout.beginLayout();
2540     QTextLine line = layout.createLine();
2541     layout.endLayout();
2542
2543     // test if the preedit is larger than the text input that the
2544     // character preceding the cursor is still visible.
2545     qreal x = input->positionToRectangle(0).x();
2546     for (int i = 0; i < 3; ++i) {
2547         sendPreeditText(preeditText, i + 1);
2548         int width = ceil(line.cursorToX(i, QTextLine::Trailing)) - floor(line.cursorToX(i));
2549         QVERIFY(input->cursorRectangle().right() >= width - 3);
2550         QVERIFY(input->positionToRectangle(0).x() < x);
2551         QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2552         x = input->positionToRectangle(0).x();
2553     }
2554     for (int i = 1; i >= 0; --i) {
2555         sendPreeditText(preeditText, i + 1);
2556         int width = ceil(line.cursorToX(i, QTextLine::Trailing)) - floor(line.cursorToX(i));
2557         QVERIFY(input->cursorRectangle().right() >= width - 3);
2558         QVERIFY(input->positionToRectangle(0).x() > x);
2559         QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2560         x = input->positionToRectangle(0).x();
2561     }
2562
2563     // Test incrementing the preedit cursor doesn't cause further
2564     // scrolling when right most text is visible.
2565     sendPreeditText(preeditText, preeditText.length() - 3);
2566     QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2567     x = input->positionToRectangle(0).x();
2568     for (int i = 2; i >= 0; --i) {
2569         sendPreeditText(preeditText, preeditText.length() - i);
2570         QCOMPARE(input->positionToRectangle(0).x(), x);
2571         QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2572     }
2573     for (int i = 1; i <  3; ++i) {
2574         sendPreeditText(preeditText, preeditText.length() - i);
2575         QCOMPARE(input->positionToRectangle(0).x(), x);
2576         QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2577     }
2578
2579     // Test disabling auto scroll.
2580     QCoreApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &imEvent);
2581
2582     input->setAutoScroll(false);
2583     sendPreeditText(preeditText.mid(0, 3), 1);
2584     QCOMPARE(evaluate<int>(input, QString("positionAt(%1)").arg(0)), 0);
2585     QCOMPARE(evaluate<int>(input, QString("positionAt(%1)").arg(input->width())), 5);
2586 }
2587
2588 void tst_qquicktextinput::preeditCursorRectangle()
2589 {
2590     QString preeditText = "super";
2591
2592     QQuickView view(testFileUrl("inputMethodEvent.qml"));
2593     view.show();
2594     view.requestActivateWindow();
2595     QTest::qWaitForWindowShown(&view);
2596     QTRY_COMPARE(&view, qGuiApp->focusWindow());
2597     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
2598     QVERIFY(input);
2599
2600     QRect currentRect;
2601
2602     QInputMethodQueryEvent query(Qt::ImCursorRectangle);
2603     QCoreApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &query);
2604     QRect previousRect = query.value(Qt::ImCursorRectangle).toRect();
2605
2606     // Verify that the micro focus rect is positioned the same for position 0 as
2607     // it would be if there was no preedit text.
2608     sendPreeditText(preeditText, 0);
2609     QCoreApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &query);
2610     currentRect = query.value(Qt::ImCursorRectangle).toRect();
2611     QCOMPARE(currentRect, previousRect);
2612
2613     QSignalSpy inputSpy(input, SIGNAL(cursorRectangleChanged()));
2614     QSignalSpy panelSpy(qGuiApp->inputPanel(), SIGNAL(cursorRectangleChanged()));
2615
2616     // Verify that the micro focus rect moves to the left as the cursor position
2617     // is incremented.
2618     for (int i = 1; i <= 5; ++i) {
2619         sendPreeditText(preeditText, i);
2620         QCoreApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &query);
2621         currentRect = query.value(Qt::ImCursorRectangle).toRect();
2622         QVERIFY(previousRect.left() < currentRect.left());
2623         QVERIFY(inputSpy.count() > 0); inputSpy.clear();
2624         QVERIFY(panelSpy.count() > 0); panelSpy.clear();
2625         previousRect = currentRect;
2626     }
2627
2628     // Verify that if there is no preedit cursor then the micro focus rect is the
2629     // same as it would be if it were positioned at the end of the preedit text.
2630     sendPreeditText(preeditText, 0);
2631     QInputMethodEvent imEvent(preeditText, QList<QInputMethodEvent::Attribute>());
2632     QCoreApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &imEvent);
2633     QCoreApplication::sendEvent(qGuiApp->inputPanel()->inputItem(), &query);
2634     currentRect = query.value(Qt::ImCursorRectangle).toRect();
2635     QCOMPARE(currentRect, previousRect);
2636     QVERIFY(inputSpy.count() > 0);
2637     QVERIFY(panelSpy.count() > 0);
2638 }
2639
2640 void tst_qquicktextinput::inputContextMouseHandler()
2641 {
2642     PlatformInputContext platformInputContext;
2643     QInputPanelPrivate *inputPanelPrivate = QInputPanelPrivate::get(qApp->inputPanel());
2644     inputPanelPrivate->testContext = &platformInputContext;
2645
2646     QString text = "supercalifragisiticexpialidocious!";
2647     QQuickView view(testFileUrl("inputContext.qml"));
2648     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
2649     QVERIFY(input);
2650
2651     input->setFocus(true);
2652     input->setText("");
2653
2654     view.show();
2655     view.requestActivateWindow();
2656     QTest::qWaitForWindowShown(&view);
2657     QTRY_COMPARE(&view, qGuiApp->focusWindow());
2658
2659     QTextLayout layout(text);
2660     layout.setFont(input->font());
2661     if (!qmlDisableDistanceField()) {
2662         QTextOption option;
2663         option.setUseDesignMetrics(true);
2664         layout.setTextOption(option);
2665     }
2666     layout.beginLayout();
2667     QTextLine line = layout.createLine();
2668     layout.endLayout();
2669
2670     const qreal x = line.cursorToX(2, QTextLine::Leading);
2671     const qreal y = line.height() / 2;
2672     QPoint position = QPointF(x, y).toPoint();
2673
2674     QInputMethodEvent inputEvent(text.mid(0, 5), QList<QInputMethodEvent::Attribute>());
2675     QApplication::sendEvent(input, &inputEvent);
2676
2677     QTest::mousePress(&view, Qt::LeftButton, Qt::NoModifier, position);
2678     QTest::mouseRelease(&view, Qt::LeftButton, Qt::NoModifier, position);
2679     QGuiApplication::processEvents();
2680
2681     QCOMPARE(platformInputContext.m_action, QInputPanel::Click);
2682     QCOMPARE(platformInputContext.m_invokeActionCallCount, 1);
2683     QCOMPARE(platformInputContext.m_cursorPosition, 2);
2684 }
2685
2686 void tst_qquicktextinput::inputMethodComposing()
2687 {
2688     QString text = "supercalifragisiticexpialidocious!";
2689
2690     QQuickView view(testFileUrl("inputContext.qml"));
2691     view.show();
2692     view.requestActivateWindow();
2693     QTest::qWaitForWindowShown(&view);
2694     QTRY_COMPARE(&view, qGuiApp->focusWindow());
2695     QQuickTextInput *input = qobject_cast<QQuickTextInput *>(view.rootObject());
2696     QVERIFY(input);
2697     QSignalSpy spy(input, SIGNAL(inputMethodComposingChanged()));
2698
2699     QCOMPARE(input->isInputMethodComposing(), false);
2700     {
2701         QInputMethodEvent event(text.mid(3), QList<QInputMethodEvent::Attribute>());
2702         QGuiApplication::sendEvent(input, &event);
2703     }
2704     QCOMPARE(input->isInputMethodComposing(), true);
2705     QCOMPARE(spy.count(), 1);
2706
2707     {
2708         QInputMethodEvent event(text.mid(12), QList<QInputMethodEvent::Attribute>());
2709         QGuiApplication::sendEvent(input, &event);
2710     }
2711     QCOMPARE(spy.count(), 1);
2712
2713     {
2714         QInputMethodEvent event;
2715         QGuiApplication::sendEvent(input, &event);
2716     }
2717     QCOMPARE(input->isInputMethodComposing(), false);
2718     QCOMPARE(spy.count(), 2);
2719 }
2720
2721 void tst_qquicktextinput::cursorRectangleSize()
2722 {
2723     QQuickView *canvas = new QQuickView(testFileUrl("positionAt.qml"));
2724     QVERIFY(canvas->rootObject() != 0);
2725     QQuickTextInput *textInput = qobject_cast<QQuickTextInput *>(canvas->rootObject());
2726
2727     // make sure cursor rectangle is not at (0,0)
2728     textInput->setX(10);
2729     textInput->setY(10);
2730     textInput->setCursorPosition(3);
2731     QVERIFY(textInput != 0);
2732     textInput->setFocus(true);
2733     canvas->show();
2734     canvas->requestActivateWindow();
2735     QTest::qWaitForWindowShown(canvas);
2736
2737     QInputMethodQueryEvent event(Qt::ImCursorRectangle);
2738     qApp->sendEvent(qApp->inputPanel()->inputItem(), &event);
2739     QRectF cursorRectFromQuery = event.value(Qt::ImCursorRectangle).toRectF();
2740
2741     QRect cursorRectFromItem = textInput->cursorRectangle();
2742     QRectF cursorRectFromPositionToRectangle = textInput->positionToRectangle(textInput->cursorPosition());
2743
2744     // item and input query cursor rectangles match
2745     QCOMPARE(cursorRectFromItem, cursorRectFromQuery.toRect());
2746
2747     // item cursor rectangle and positionToRectangle calculations match
2748     QCOMPARE(cursorRectFromItem, cursorRectFromPositionToRectangle.toRect());
2749
2750     // item-canvas transform and input item transform match
2751 #ifdef Q_OS_MAC
2752     QEXPECT_FAIL("","QTBUG-22966", Abort);
2753 #endif
2754     QCOMPARE(QQuickItemPrivate::get(textInput)->itemToCanvasTransform(), qApp->inputPanel()->inputItemTransform());
2755
2756     // input panel cursorRectangle property and tranformed item cursor rectangle match
2757     QRectF sceneCursorRect = QQuickItemPrivate::get(textInput)->itemToCanvasTransform().mapRect(cursorRectFromItem);
2758     QCOMPARE(sceneCursorRect, qApp->inputPanel()->cursorRectangle());
2759
2760     delete canvas;
2761 }
2762
2763 void tst_qquicktextinput::tripleClickSelectsAll()
2764 {
2765     QString qmlfile = testFile("positionAt.qml");
2766     QQuickView view(QUrl::fromLocalFile(qmlfile));
2767     view.show();
2768     view.requestActivateWindow();
2769     QTest::qWaitForWindowShown(&view);
2770
2771     QTRY_COMPARE(&view, qGuiApp->focusWindow());
2772
2773     QQuickTextInput* input = qobject_cast<QQuickTextInput*>(view.rootObject());
2774     QVERIFY(input);
2775
2776     QLatin1String hello("Hello world!");
2777     input->setSelectByMouse(true);
2778     input->setText(hello);
2779
2780     // Clicking on the same point inside TextInput three times in a row
2781     // should trigger a triple click, thus selecting all the text.
2782     QPoint pointInside = input->pos().toPoint() + QPoint(2,2);
2783     QTest::mouseDClick(&view, Qt::LeftButton, 0, pointInside);
2784     QTest::mouseClick(&view, Qt::LeftButton, 0, pointInside);
2785     QGuiApplication::processEvents();
2786     QCOMPARE(input->selectedText(), hello);
2787
2788     // Now it simulates user moving the mouse between the second and the third click.
2789     // In this situation, we don't expect a triple click.
2790     QPoint pointInsideButFar = QPoint(input->width(),input->height()) - QPoint(2,2);
2791     QTest::mouseDClick(&view, Qt::LeftButton, 0, pointInside);
2792     QTest::mouseClick(&view, Qt::LeftButton, 0, pointInsideButFar);
2793     QGuiApplication::processEvents();
2794     QVERIFY(input->selectedText().isEmpty());
2795
2796     // And now we press the third click too late, so no triple click event is triggered.
2797     QTest::mouseDClick(&view, Qt::LeftButton, 0, pointInside);
2798     QGuiApplication::processEvents();
2799     QTest::qWait(qApp->styleHints()->mouseDoubleClickInterval() + 1);
2800     QTest::mouseClick(&view, Qt::LeftButton, 0, pointInside);
2801     QGuiApplication::processEvents();
2802     QVERIFY(input->selectedText().isEmpty());
2803 }
2804
2805 void tst_qquicktextinput::QTBUG_19956_data()
2806 {
2807     QTest::addColumn<QString>("url");
2808     QTest::newRow("intvalidator") << "qtbug-19956int.qml";
2809     QTest::newRow("doublevalidator") << "qtbug-19956double.qml";
2810 }
2811
2812 void tst_qquicktextinput::keySequence_data()
2813 {
2814     QTest::addColumn<QString>("text");
2815     QTest::addColumn<QKeySequence>("sequence");
2816     QTest::addColumn<int>("selectionStart");
2817     QTest::addColumn<int>("selectionEnd");
2818     QTest::addColumn<int>("cursorPosition");
2819     QTest::addColumn<QString>("expectedText");
2820     QTest::addColumn<QString>("selectedText");
2821
2822     // standard[0] == "the [4]quick [10]brown [16]fox [20]jumped [27]over [32]the [36]lazy [41]dog"
2823
2824     QTest::newRow("select all")
2825             << standard.at(0) << QKeySequence(QKeySequence::SelectAll) << 0 << 0
2826             << 44 << standard.at(0) << standard.at(0);
2827     QTest::newRow("select end of line")
2828             << standard.at(0) << QKeySequence(QKeySequence::SelectEndOfLine) << 5 << 5
2829             << 44 << standard.at(0) << standard.at(0).mid(5);
2830     QTest::newRow("select end of document") // ### Not handled.
2831             << standard.at(0) << QKeySequence(QKeySequence::SelectEndOfDocument) << 3 << 3
2832             << 3 << standard.at(0) << QString();
2833     QTest::newRow("select end of block")
2834             << standard.at(0) << QKeySequence(QKeySequence::SelectEndOfBlock) << 18 << 18
2835             << 44 << standard.at(0) << standard.at(0).mid(18);
2836     QTest::newRow("delete end of line")
2837             << standard.at(0) << QKeySequence(QKeySequence::DeleteEndOfLine) << 24 << 24
2838             << 24 << standard.at(0).mid(0, 24) << QString();
2839     QTest::newRow("move to start of line")
2840             << standard.at(0) << QKeySequence(QKeySequence::MoveToStartOfLine) << 31 << 31
2841             << 0 << standard.at(0) << QString();
2842     QTest::newRow("move to start of block")
2843             << standard.at(0) << QKeySequence(QKeySequence::MoveToStartOfBlock) << 25 << 25
2844             << 0 << standard.at(0) << QString();
2845     QTest::newRow("move to next char")
2846             << standard.at(0) << QKeySequence(QKeySequence::MoveToNextChar) << 12 << 12
2847             << 13 << standard.at(0) << QString();
2848     QTest::newRow("move to previous char")
2849             << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousChar) << 3 << 3
2850             << 2 << standard.at(0) << QString();
2851     QTest::newRow("select next char")
2852             << standard.at(0) << QKeySequence(QKeySequence::SelectNextChar) << 23 << 23
2853             << 24 << standard.at(0) << standard.at(0).mid(23, 1);
2854     QTest::newRow("select previous char")
2855             << standard.at(0) << QKeySequence(QKeySequence::SelectPreviousChar) << 19 << 19
2856             << 18 << standard.at(0) << standard.at(0).mid(18, 1);
2857     QTest::newRow("move to next word")
2858             << standard.at(0) << QKeySequence(QKeySequence::MoveToNextWord) << 7 << 7
2859             << 10 << standard.at(0) << QString();
2860     QTest::newRow("move to previous word")
2861             << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousWord) << 7 << 7
2862             << 4 << standard.at(0) << QString();
2863     QTest::newRow("select previous word")
2864             << standard.at(0) << QKeySequence(QKeySequence::SelectPreviousWord) << 11 << 11
2865             << 10 << standard.at(0) << standard.at(0).mid(10, 1);
2866     QTest::newRow("delete (selection)")
2867             << standard.at(0) << QKeySequence(QKeySequence::Delete) << 12 << 15
2868             << 12 << (standard.at(0).mid(0, 12) + standard.at(0).mid(15)) << QString();
2869     QTest::newRow("delete (no selection)")
2870             << standard.at(0) << QKeySequence(QKeySequence::Delete) << 15 << 15
2871             << 15 << (standard.at(0).mid(0, 15) + standard.at(0).mid(16)) << QString();
2872     QTest::newRow("delete end of word")
2873             << standard.at(0) << QKeySequence(QKeySequence::DeleteEndOfWord) << 24 << 24
2874             << 24 << (standard.at(0).mid(0, 24) + standard.at(0).mid(27)) << QString();
2875     QTest::newRow("delete start of word")
2876             << standard.at(0) << QKeySequence(QKeySequence::DeleteStartOfWord) << 7 << 7
2877             << 4 << (standard.at(0).mid(0, 4) + standard.at(0).mid(7)) << QString();
2878 }
2879
2880 void tst_qquicktextinput::keySequence()
2881 {
2882     QFETCH(QString, text);
2883     QFETCH(QKeySequence, sequence);
2884     QFETCH(int, selectionStart);
2885     QFETCH(int, selectionEnd);
2886     QFETCH(int, cursorPosition);
2887     QFETCH(QString, expectedText);
2888     QFETCH(QString, selectedText);
2889
2890     if (sequence.isEmpty()) {
2891         QSKIP("Key sequence is undefined");
2892     }
2893
2894     QString componentStr = "import QtQuick 2.0\nTextInput { focus: true; text: \"" + text + "\" }";
2895     QDeclarativeComponent textInputComponent(&engine);
2896     textInputComponent.setData(componentStr.toLatin1(), QUrl());
2897     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
2898     QVERIFY(textInput != 0);
2899
2900     QQuickCanvas canvas;
2901     textInput->setParentItem(canvas.rootItem());
2902     canvas.show();
2903     canvas.requestActivateWindow();
2904     QTest::qWaitForWindowShown(&canvas);
2905     QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
2906
2907     textInput->select(selectionStart, selectionEnd);
2908
2909     simulateKeys(&canvas, sequence);
2910
2911     QCOMPARE(textInput->cursorPosition(), cursorPosition);
2912     QCOMPARE(textInput->text(), expectedText);
2913     QCOMPARE(textInput->selectedText(), selectedText);
2914 }
2915
2916 #define NORMAL 0
2917 #define REPLACE_UNTIL_END 1
2918
2919 void tst_qquicktextinput::undo_data()
2920 {
2921     QTest::addColumn<QStringList>("insertString");
2922     QTest::addColumn<IntList>("insertIndex");
2923     QTest::addColumn<IntList>("insertMode");
2924     QTest::addColumn<QStringList>("expectedString");
2925     QTest::addColumn<bool>("use_keys");
2926
2927     for (int i=0; i<2; i++) {
2928         QString keys_str = "keyboard";
2929         bool use_keys = true;
2930         if (i==0) {
2931             keys_str = "insert";
2932             use_keys = false;
2933         }
2934
2935         {
2936             IntList insertIndex;
2937             IntList insertMode;
2938             QStringList insertString;
2939             QStringList expectedString;
2940
2941             insertIndex << -1;
2942             insertMode << NORMAL;
2943             insertString << "1";
2944
2945             insertIndex << -1;
2946             insertMode << NORMAL;
2947             insertString << "5";
2948
2949             insertIndex << 1;
2950             insertMode << NORMAL;
2951             insertString << "3";
2952
2953             insertIndex << 1;
2954             insertMode << NORMAL;
2955             insertString << "2";
2956
2957             insertIndex << 3;
2958             insertMode << NORMAL;
2959             insertString << "4";
2960
2961             expectedString << "12345";
2962             expectedString << "1235";
2963             expectedString << "135";
2964             expectedString << "15";
2965             expectedString << "";
2966
2967             QTest::newRow(QString(keys_str + "_numbers").toLatin1()) <<
2968                 insertString <<
2969                 insertIndex <<
2970                 insertMode <<
2971                 expectedString <<
2972                 bool(use_keys);
2973         }
2974         {
2975             IntList insertIndex;
2976             IntList insertMode;
2977             QStringList insertString;
2978             QStringList expectedString;
2979
2980             insertIndex << -1;
2981             insertMode << NORMAL;
2982             insertString << "World"; // World
2983
2984             insertIndex << 0;
2985             insertMode << NORMAL;
2986             insertString << "Hello"; // HelloWorld
2987
2988             insertIndex << 0;
2989             insertMode << NORMAL;
2990             insertString << "Well"; // WellHelloWorld
2991
2992             insertIndex << 9;
2993             insertMode << NORMAL;
2994             insertString << "There"; // WellHelloThereWorld;
2995
2996             expectedString << "WellHelloThereWorld";
2997             expectedString << "WellHelloWorld";
2998             expectedString << "HelloWorld";
2999             expectedString << "World";
3000             expectedString << "";
3001
3002             QTest::newRow(QString(keys_str + "_helloworld").toLatin1()) <<
3003                 insertString <<
3004                 insertIndex <<
3005                 insertMode <<
3006                 expectedString <<
3007                 bool(use_keys);
3008         }
3009         {
3010             IntList insertIndex;
3011             IntList insertMode;
3012             QStringList insertString;
3013             QStringList expectedString;
3014
3015             insertIndex << -1;
3016             insertMode << NORMAL;
3017             insertString << "Ensuring";
3018
3019             insertIndex << -1;
3020             insertMode << NORMAL;
3021             insertString << " instan";
3022
3023             insertIndex << 9;
3024             insertMode << NORMAL;
3025             insertString << "an ";
3026
3027             insertIndex << 10;
3028             insertMode << REPLACE_UNTIL_END;
3029             insertString << " unique instance.";
3030
3031             expectedString << "Ensuring a unique instance.";
3032             expectedString << "Ensuring an instan";
3033             expectedString << "Ensuring instan";
3034             expectedString << "";
3035
3036             QTest::newRow(QString(keys_str + "_patterns").toLatin1()) <<
3037                 insertString <<
3038                 insertIndex <<
3039                 insertMode <<
3040                 expectedString <<
3041                 bool(use_keys);
3042         }
3043     }
3044 }
3045
3046 void tst_qquicktextinput::undo()
3047 {
3048     QFETCH(QStringList, insertString);
3049     QFETCH(IntList, insertIndex);
3050     QFETCH(IntList, insertMode);
3051     QFETCH(QStringList, expectedString);
3052
3053     QString componentStr = "import QtQuick 2.0\nTextInput { focus: true }";
3054     QDeclarativeComponent textInputComponent(&engine);
3055     textInputComponent.setData(componentStr.toLatin1(), QUrl());
3056     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
3057     QVERIFY(textInput != 0);
3058
3059     QQuickCanvas canvas;
3060     textInput->setParentItem(canvas.rootItem());
3061     canvas.show();
3062     canvas.requestActivateWindow();
3063     QTest::qWaitForWindowShown(&canvas);
3064     QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
3065
3066     int i;
3067
3068 // STEP 1: First build up an undo history by inserting or typing some strings...
3069     for (i = 0; i < insertString.size(); ++i) {
3070         if (insertIndex[i] > -1)
3071             textInput->setCursorPosition(insertIndex[i]);
3072
3073  // experimental stuff
3074         if (insertMode[i] == REPLACE_UNTIL_END) {
3075             textInput->select(insertIndex[i], insertIndex[i] + 8);
3076
3077             // This is what I actually want...
3078             // QTest::keyClick(testWidget, Qt::Key_End, Qt::ShiftModifier);
3079         }
3080
3081         for (int j = 0; j < insertString.at(i).length(); j++)
3082             QTest::keyClick(&canvas, insertString.at(i).at(j).toLatin1());
3083     }
3084
3085 // STEP 2: Next call undo several times and see if we can restore to the previous state
3086     for (i = 0; i < expectedString.size() - 1; ++i) {
3087         QCOMPARE(textInput->text(), expectedString[i]);
3088         simulateKeys(&canvas, QKeySequence::Undo);
3089     }
3090
3091 // STEP 3: Verify that we have undone everything
3092     QVERIFY(textInput->text().isEmpty());
3093 }
3094
3095 void tst_qquicktextinput::redo_data()
3096 {
3097     QTest::addColumn<QStringList>("insertString");
3098     QTest::addColumn<IntList>("insertIndex");
3099     QTest::addColumn<QStringList>("expectedString");
3100
3101     {
3102         IntList insertIndex;
3103         QStringList insertString;
3104         QStringList expectedString;
3105
3106         insertIndex << -1;
3107         insertString << "World"; // World
3108         insertIndex << 0;
3109         insertString << "Hello"; // HelloWorld
3110         insertIndex << 0;
3111         insertString << "Well"; // WellHelloWorld
3112         insertIndex << 9;
3113         insertString << "There"; // WellHelloThereWorld;
3114
3115         expectedString << "World";
3116         expectedString << "HelloWorld";
3117         expectedString << "WellHelloWorld";
3118         expectedString << "WellHelloThereWorld";
3119
3120         QTest::newRow("Inserts and setting cursor") << insertString << insertIndex << expectedString;
3121     }
3122 }
3123
3124 void tst_qquicktextinput::redo()
3125 {
3126     QFETCH(QStringList, insertString);
3127     QFETCH(IntList, insertIndex);
3128     QFETCH(QStringList, expectedString);
3129
3130     QString componentStr = "import QtQuick 2.0\nTextInput { focus: true }";
3131     QDeclarativeComponent textInputComponent(&engine);
3132     textInputComponent.setData(componentStr.toLatin1(), QUrl());
3133     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
3134     QVERIFY(textInput != 0);
3135
3136     QQuickCanvas canvas;
3137     textInput->setParentItem(canvas.rootItem());
3138     canvas.show();
3139     canvas.requestActivateWindow();
3140     QTest::qWaitForWindowShown(&canvas);
3141     QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
3142
3143     int i;
3144     // inserts the diff strings at diff positions
3145     for (i = 0; i < insertString.size(); ++i) {
3146         if (insertIndex[i] > -1)
3147             textInput->setCursorPosition(insertIndex[i]);
3148         for (int j = 0; j < insertString.at(i).length(); j++)
3149             QTest::keyClick(&canvas, insertString.at(i).at(j).toLatin1());
3150     }
3151
3152     // undo everything
3153     while (!textInput->text().isEmpty())
3154         simulateKeys(&canvas, QKeySequence::Undo);
3155
3156     for (i = 0; i < expectedString.size(); ++i) {
3157         simulateKeys(&canvas, QKeySequence::Redo);
3158         QCOMPARE(textInput->text() , expectedString[i]);
3159     }
3160 }
3161
3162 void tst_qquicktextinput::undo_keypressevents_data()
3163 {
3164     QTest::addColumn<KeyList>("keys");
3165     QTest::addColumn<QStringList>("expectedString");
3166
3167     {
3168         KeyList keys;
3169         QStringList expectedString;
3170
3171         keys << "AFRAID"
3172                 << QKeySequence::MoveToStartOfLine
3173                 << "VERY"
3174                 << Qt::Key_Left
3175                 << Qt::Key_Left
3176                 << Qt::Key_Left
3177                 << Qt::Key_Left
3178                 << "BE"
3179                 << QKeySequence::MoveToEndOfLine
3180                 << "!";
3181
3182         expectedString << "BEVERYAFRAID!";
3183         expectedString << "BEVERYAFRAID";
3184         expectedString << "VERYAFRAID";
3185         expectedString << "AFRAID";
3186
3187         QTest::newRow("Inserts and moving cursor") << keys << expectedString;
3188     } {
3189         KeyList keys;
3190         QStringList expectedString;
3191
3192         // inserting '1234'
3193         keys << "1234" << QKeySequence::MoveToStartOfLine
3194                 // skipping '12'
3195                 << Qt::Key_Right << Qt::Key_Right
3196                 // selecting '34'
3197                 << (Qt::Key_Right | Qt::ShiftModifier) << (Qt::Key_Right | Qt::ShiftModifier)
3198                 // deleting '34'
3199                 << Qt::Key_Delete;
3200
3201         expectedString << "12";
3202         expectedString << "1234";
3203
3204         QTest::newRow("Inserts,moving,selection and delete") << keys << expectedString;
3205     } {
3206         KeyList keys;
3207         QStringList expectedString;
3208
3209         // inserting 'AB12'
3210         keys << "AB12"
3211                 << QKeySequence::MoveToStartOfLine
3212                 // selecting 'AB'
3213                 << (Qt::Key_Right | Qt::ShiftModifier) << (Qt::Key_Right | Qt::ShiftModifier)
3214                 << Qt::Key_Delete
3215                 << QKeySequence::Undo
3216                 << Qt::Key_Right
3217 #ifdef Q_OS_WIN //Mac(?) has a specialcase to handle jumping to the end of a selection
3218                 << Qt::Key_Left
3219 #endif
3220                 << (Qt::Key_Right | Qt::ShiftModifier) << (Qt::Key_Right | Qt::ShiftModifier)
3221                 << Qt::Key_Delete;
3222
3223         expectedString << "AB";
3224         expectedString << "AB12";
3225
3226         QTest::newRow("Inserts,moving,selection, delete and undo") << keys << expectedString;
3227     } {
3228         KeyList keys;
3229         QStringList expectedString;
3230
3231         // inserting 'ABCD'
3232         keys << "abcd"
3233                 //move left two
3234                 << Qt::Key_Left << Qt::Key_Left
3235                 // inserting '1234'
3236                 << "1234"
3237                 // selecting '1234'
3238                 << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier)
3239                 // overwriting '1234' with '5'
3240                 << "5"
3241                 // undoing deletion of 'AB'
3242                 << QKeySequence::Undo
3243                 // overwriting '1234' with '6'
3244                 << "6";
3245
3246         expectedString << "ab6cd";
3247         // for versions previous to 3.2 we overwrite needed two undo operations
3248         expectedString << "ab1234cd";
3249         expectedString << "abcd";
3250
3251         QTest::newRow("Inserts,moving,selection and undo, removing selection") << keys << expectedString;
3252     } {
3253         KeyList keys;
3254         QStringList expectedString;
3255
3256         // inserting 'ABC'
3257         keys << "ABC"
3258                 // removes 'C'
3259                 << Qt::Key_Backspace;
3260
3261         expectedString << "AB";
3262         expectedString << "ABC";
3263
3264         QTest::newRow("Inserts,backspace") << keys << expectedString;
3265     } {
3266         KeyList keys;
3267         QStringList expectedString;
3268
3269         keys << "ABC"
3270                 // removes 'C'
3271                 << Qt::Key_Backspace
3272                 // inserting 'Z'
3273                 << "Z";
3274
3275         expectedString << "ABZ";
3276         expectedString << "AB";
3277         expectedString << "ABC";
3278
3279         QTest::newRow("Inserts,backspace,inserts") << keys << expectedString;
3280     } {
3281         KeyList keys;
3282         QStringList expectedString;
3283
3284         // inserting '123'
3285         keys << "123" << QKeySequence::MoveToStartOfLine
3286             // selecting '123'
3287              << QKeySequence::SelectEndOfLine
3288             // overwriting '123' with 'ABC'
3289              << "ABC";
3290
3291         expectedString << "ABC";
3292         // for versions previous to 3.2 we overwrite needed two undo operations
3293         expectedString << "123";
3294
3295         QTest::newRow("Inserts,moving,selection and overwriting") << keys << expectedString;
3296     }
3297 }
3298
3299 void tst_qquicktextinput::undo_keypressevents()
3300 {
3301     QFETCH(KeyList, keys);
3302     QFETCH(QStringList, expectedString);
3303
3304     QString componentStr = "import QtQuick 2.0\nTextInput { focus: true }";
3305     QDeclarativeComponent textInputComponent(&engine);
3306     textInputComponent.setData(componentStr.toLatin1(), QUrl());
3307     QQuickTextInput *textInput = qobject_cast<QQuickTextInput*>(textInputComponent.create());
3308     QVERIFY(textInput != 0);
3309
3310     QQuickCanvas canvas;
3311     textInput->setParentItem(canvas.rootItem());
3312     canvas.show();
3313     canvas.requestActivateWindow();
3314     QTest::qWaitForWindowShown(&canvas);
3315     QTRY_COMPARE(QGuiApplication::activeWindow(), &canvas);
3316
3317     simulateKeys(&canvas, keys);
3318
3319     for (int i = 0; i < expectedString.size(); ++i) {
3320         QCOMPARE(textInput->text() , expectedString[i]);
3321         simulateKeys(&canvas, QKeySequence::Undo);
3322     }
3323     QVERIFY(textInput->text().isEmpty());
3324 }
3325
3326 void tst_qquicktextinput::QTBUG_19956()
3327 {
3328     QFETCH(QString, url);
3329
3330     QQuickView canvas(testFileUrl(url));
3331     canvas.show();
3332     canvas.requestActivateWindow();
3333     QTest::qWaitForWindowShown(&canvas);
3334     QVERIFY(canvas.rootObject() != 0);
3335     QQuickTextInput *input = qobject_cast<QQuickTextInput*>(canvas.rootObject());
3336     QVERIFY(input);
3337     input->setFocus(true);
3338     QVERIFY(input->hasActiveFocus());
3339
3340     QCOMPARE(canvas.rootObject()->property("topvalue").toInt(), 30);
3341     QCOMPARE(canvas.rootObject()->property("bottomvalue").toInt(), 10);
3342     QCOMPARE(canvas.rootObject()->property("text").toString(), QString("20"));
3343     QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
3344
3345     canvas.rootObject()->setProperty("topvalue", 15);
3346     QCOMPARE(canvas.rootObject()->property("topvalue").toInt(), 15);
3347     QVERIFY(!canvas.rootObject()->property("acceptableInput").toBool());
3348
3349     canvas.rootObject()->setProperty("topvalue", 25);
3350     QCOMPARE(canvas.rootObject()->property("topvalue").toInt(), 25);
3351     QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
3352
3353     canvas.rootObject()->setProperty("bottomvalue", 21);
3354     QCOMPARE(canvas.rootObject()->property("bottomvalue").toInt(), 21);
3355     QVERIFY(!canvas.rootObject()->property("acceptableInput").toBool());
3356
3357     canvas.rootObject()->setProperty("bottomvalue", 10);
3358     QCOMPARE(canvas.rootObject()->property("bottomvalue").toInt(), 10);
3359     QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
3360 }
3361
3362 void tst_qquicktextinput::QTBUG_19956_regexp()
3363 {
3364     QUrl url = testFileUrl("qtbug-19956regexp.qml");
3365
3366     QString warning = url.toString() + ":11: Unable to assign [undefined] to QRegExp";
3367     QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
3368
3369     QQuickView canvas(url);
3370     canvas.show();
3371     canvas.requestActivateWindow();
3372     QTest::qWaitForWindowShown(&canvas);
3373     QVERIFY(canvas.rootObject() != 0);
3374     QQuickTextInput *input = qobject_cast<QQuickTextInput*>(canvas.rootObject());
3375     QVERIFY(input);
3376     input->setFocus(true);
3377     QVERIFY(input->hasActiveFocus());
3378
3379     canvas.rootObject()->setProperty("regexvalue", QRegExp("abc"));
3380     QCOMPARE(canvas.rootObject()->property("regexvalue").toRegExp(), QRegExp("abc"));
3381     QCOMPARE(canvas.rootObject()->property("text").toString(), QString("abc"));
3382     QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
3383
3384     canvas.rootObject()->setProperty("regexvalue", QRegExp("abcd"));
3385     QCOMPARE(canvas.rootObject()->property("regexvalue").toRegExp(), QRegExp("abcd"));
3386     QVERIFY(!canvas.rootObject()->property("acceptableInput").toBool());
3387
3388     canvas.rootObject()->setProperty("regexvalue", QRegExp("abc"));
3389     QCOMPARE(canvas.rootObject()->property("regexvalue").toRegExp(), QRegExp("abc"));
3390     QVERIFY(canvas.rootObject()->property("acceptableInput").toBool());
3391 }
3392
3393 QTEST_MAIN(tst_qquicktextinput)
3394
3395 #include "tst_qquicktextinput.moc"