partial build fix of qtquick1 autotests
[profile/ivi/qtdeclarative.git] / tests / auto / qtquick1 / qdeclarativetextinput / tst_qdeclarativetextinput.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 <QtDeclarative/qdeclarativeengine.h>
45 #include <QFile>
46 #include <QtQuick1/qdeclarativeview.h>
47 #include <QtQuick1/private/qdeclarativetextinput_p.h>
48 #include <QtQuick1/private/qdeclarativetextinput_p_p.h>
49 #include <QDebug>
50 #include <QDir>
51 #include <QStyle>
52 #include <QInputContext>
53 #include <QtWidgets/5.0.0/QtWidgets/private/qapplication_p.h>
54
55 #ifdef Q_OS_SYMBIAN
56 // In Symbian OS test data is located in applications private dir
57 #define SRCDIR "."
58 #endif
59
60 Q_DECLARE_METATYPE(QDeclarative1TextInput::SelectionMode)
61
62 QString createExpectedFileIfNotFound(const QString& filebasename, const QImage& actual)
63 {
64     // XXX This will be replaced by some clever persistent platform image store.
65     QString persistent_dir = SRCDIR "/data";
66     QString arch = "unknown-architecture"; // QTest needs to help with this.
67
68     QString expectfile = persistent_dir + QDir::separator() + filebasename + "-" + arch + ".png";
69
70     if (!QFile::exists(expectfile)) {
71         actual.save(expectfile);
72         qWarning() << "created" << expectfile;
73     }
74
75     return expectfile;
76 }
77
78 class tst_qdeclarativetextinput : public QObject
79
80 {
81     Q_OBJECT
82 public:
83     tst_qdeclarativetextinput();
84
85 private slots:
86
87     void text();
88     void width();
89     void font();
90     void color();
91     void selection();
92     void isRightToLeft_data();
93     void isRightToLeft();
94     void moveCursorSelection_data();
95     void moveCursorSelection();
96     void moveCursorSelectionSequence_data();
97     void moveCursorSelectionSequence();
98     void mouseSelection_data();
99     void mouseSelection();
100     void deferEnableSelectByMouse_data();
101     void deferEnableSelectByMouse();
102     void deferDisableSelectByMouse_data();
103     void deferDisableSelectByMouse();
104     void dragMouseSelection();
105     void mouseSelectionMode_data();
106     void mouseSelectionMode();
107
108     void horizontalAlignment_data();
109     void horizontalAlignment();
110     void horizontalAlignment_RightToLeft();
111
112     void positionAt();
113
114     void maxLength();
115     void masks();
116     void validators();
117     void inputMethods();
118
119     void passwordCharacter();
120     void cursorDelegate();
121     void cursorVisible();
122     void cursorRectangle();
123     void navigation();
124     void navigation_RTL();
125     void copyAndPaste();
126     void canPasteEmpty();
127     void canPaste();
128     void readOnly();
129
130     void openInputPanelOnClick();
131     void openInputPanelOnFocus();
132     void setHAlignClearCache();
133     void focusOutClearSelection();
134
135     void echoMode();
136     void geometrySignals();
137     void testQtQuick11Attributes();
138     void testQtQuick11Attributes_data();
139
140     void preeditAutoScroll();
141     void preeditMicroFocus();
142     void inputContextMouseHandler();
143     void inputMethodComposing();
144     void cursorRectangleSize();
145
146 private:
147     void simulateKey(QDeclarativeView *, int key);
148     QDeclarativeView *createView(const QString &filename);
149
150     QDeclarativeEngine engine;
151     QStringList standard;
152     QStringList colorStrings;
153 };
154
155 tst_qdeclarativetextinput::tst_qdeclarativetextinput()
156 {
157     standard << "the quick brown fox jumped over the lazy dog"
158         << "It's supercalifragisiticexpialidocious!"
159         << "Hello, world!"
160         << "!dlrow ,olleH"
161         << " spacey   text ";
162
163     colorStrings << "aliceblue"
164                  << "antiquewhite"
165                  << "aqua"
166                  << "darkkhaki"
167                  << "darkolivegreen"
168                  << "dimgray"
169                  << "palevioletred"
170                  << "lightsteelblue"
171                  << "#000000"
172                  << "#AAAAAA"
173                  << "#FFFFFF"
174                  << "#2AC05F";
175 }
176
177 void tst_qdeclarativetextinput::text()
178 {
179     {
180         QDeclarativeComponent textinputComponent(&engine);
181         textinputComponent.setData("import QtQuick 1.0\nTextInput {  text: \"\"  }", QUrl());
182         QDeclarative1TextInput *textinputObject = qobject_cast<QDeclarative1TextInput*>(textinputComponent.create());
183
184         QVERIFY(textinputObject != 0);
185         QCOMPARE(textinputObject->text(), QString(""));
186
187         delete textinputObject;
188     }
189
190     for (int i = 0; i < standard.size(); i++)
191     {
192         QString componentStr = "import QtQuick 1.0\nTextInput { text: \"" + standard.at(i) + "\" }";
193         QDeclarativeComponent textinputComponent(&engine);
194         textinputComponent.setData(componentStr.toLatin1(), QUrl());
195         QDeclarative1TextInput *textinputObject = qobject_cast<QDeclarative1TextInput*>(textinputComponent.create());
196
197         QVERIFY(textinputObject != 0);
198         QCOMPARE(textinputObject->text(), standard.at(i));
199
200         delete textinputObject;
201     }
202
203 }
204
205 void tst_qdeclarativetextinput::width()
206 {
207     // uses Font metrics to find the width for standard
208     {
209         QDeclarativeComponent textinputComponent(&engine);
210         textinputComponent.setData("import QtQuick 1.0\nTextInput {  text: \"\" }", QUrl());
211         QDeclarative1TextInput *textinputObject = qobject_cast<QDeclarative1TextInput*>(textinputComponent.create());
212
213         QVERIFY(textinputObject != 0);
214         QCOMPARE(textinputObject->width(), 0.0);
215
216         delete textinputObject;
217     }
218
219     for (int i = 0; i < standard.size(); i++)
220     {
221         QFont f;
222         QFontMetricsF fm(f);
223         qreal metricWidth = fm.width(standard.at(i));
224
225         QString componentStr = "import QtQuick 1.0\nTextInput { text: \"" + standard.at(i) + "\" }";
226         QDeclarativeComponent textinputComponent(&engine);
227         textinputComponent.setData(componentStr.toLatin1(), QUrl());
228         QDeclarative1TextInput *textinputObject = qobject_cast<QDeclarative1TextInput*>(textinputComponent.create());
229
230         QVERIFY(textinputObject != 0);
231         int delta = abs(int(int(textinputObject->width()) - metricWidth));
232         QVERIFY(delta <= 3.0); // As best as we can hope for cross-platform.
233
234         delete textinputObject;
235     }
236 }
237
238 void tst_qdeclarativetextinput::font()
239 {
240     //test size, then bold, then italic, then family
241     { 
242         QString componentStr = "import QtQuick 1.0\nTextInput {  font.pointSize: 40; text: \"Hello World\" }";
243         QDeclarativeComponent textinputComponent(&engine);
244         textinputComponent.setData(componentStr.toLatin1(), QUrl());
245         QDeclarative1TextInput *textinputObject = qobject_cast<QDeclarative1TextInput*>(textinputComponent.create());
246
247         QVERIFY(textinputObject != 0);
248         QCOMPARE(textinputObject->font().pointSize(), 40);
249         QCOMPARE(textinputObject->font().bold(), false);
250         QCOMPARE(textinputObject->font().italic(), false);
251
252         delete textinputObject;
253     }
254
255     { 
256         QString componentStr = "import QtQuick 1.0\nTextInput {  font.bold: true; text: \"Hello World\" }";
257         QDeclarativeComponent textinputComponent(&engine);
258         textinputComponent.setData(componentStr.toLatin1(), QUrl());
259         QDeclarative1TextInput *textinputObject = qobject_cast<QDeclarative1TextInput*>(textinputComponent.create());
260
261         QVERIFY(textinputObject != 0);
262         QCOMPARE(textinputObject->font().bold(), true);
263         QCOMPARE(textinputObject->font().italic(), false);
264
265         delete textinputObject;
266     }
267
268     { 
269         QString componentStr = "import QtQuick 1.0\nTextInput {  font.italic: true; text: \"Hello World\" }";
270         QDeclarativeComponent textinputComponent(&engine);
271         textinputComponent.setData(componentStr.toLatin1(), QUrl());
272         QDeclarative1TextInput *textinputObject = qobject_cast<QDeclarative1TextInput*>(textinputComponent.create());
273
274         QVERIFY(textinputObject != 0);
275         QCOMPARE(textinputObject->font().italic(), true);
276         QCOMPARE(textinputObject->font().bold(), false);
277
278         delete textinputObject;
279     }
280  
281     { 
282         QString componentStr = "import QtQuick 1.0\nTextInput {  font.family: \"Helvetica\"; text: \"Hello World\" }";
283         QDeclarativeComponent textinputComponent(&engine);
284         textinputComponent.setData(componentStr.toLatin1(), QUrl());
285         QDeclarative1TextInput *textinputObject = qobject_cast<QDeclarative1TextInput*>(textinputComponent.create());
286
287         QVERIFY(textinputObject != 0);
288         QCOMPARE(textinputObject->font().family(), QString("Helvetica"));
289         QCOMPARE(textinputObject->font().bold(), false);
290         QCOMPARE(textinputObject->font().italic(), false);
291
292         delete textinputObject;
293     }
294
295     { 
296         QString componentStr = "import QtQuick 1.0\nTextInput {  font.family: \"\"; text: \"Hello World\" }";
297         QDeclarativeComponent textinputComponent(&engine);
298         textinputComponent.setData(componentStr.toLatin1(), QUrl());
299         QDeclarative1TextInput *textinputObject = qobject_cast<QDeclarative1TextInput*>(textinputComponent.create());
300
301         QVERIFY(textinputObject != 0);
302         QCOMPARE(textinputObject->font().family(), QString(""));
303
304         delete textinputObject;
305     }
306 }
307
308 void tst_qdeclarativetextinput::color()
309 {
310     //test color
311     for (int i = 0; i < colorStrings.size(); i++)
312     { 
313         QString componentStr = "import QtQuick 1.0\nTextInput {  color: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
314         QDeclarativeComponent textinputComponent(&engine);
315         textinputComponent.setData(componentStr.toLatin1(), QUrl());
316         QDeclarative1TextInput *textinputObject = qobject_cast<QDeclarative1TextInput*>(textinputComponent.create());
317         QVERIFY(textinputObject != 0);
318         QCOMPARE(textinputObject->color(), QColor(colorStrings.at(i)));
319
320         delete textinputObject;
321     }
322
323     //test selection color
324     for (int i = 0; i < colorStrings.size(); i++)
325     {
326         QString componentStr = "import QtQuick 1.0\nTextInput {  selectionColor: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
327         QDeclarativeComponent textinputComponent(&engine);
328         textinputComponent.setData(componentStr.toLatin1(), QUrl());
329         QDeclarative1TextInput *textinputObject = qobject_cast<QDeclarative1TextInput*>(textinputComponent.create());
330         QVERIFY(textinputObject != 0);
331         QCOMPARE(textinputObject->selectionColor(), QColor(colorStrings.at(i)));
332
333         delete textinputObject;
334     }
335
336     //test selected text color
337     for (int i = 0; i < colorStrings.size(); i++)
338     { 
339         QString componentStr = "import QtQuick 1.0\nTextInput {  selectedTextColor: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
340         QDeclarativeComponent textinputComponent(&engine);
341         textinputComponent.setData(componentStr.toLatin1(), QUrl());
342         QDeclarative1TextInput *textinputObject = qobject_cast<QDeclarative1TextInput*>(textinputComponent.create());
343         QVERIFY(textinputObject != 0);
344         QCOMPARE(textinputObject->selectedTextColor(), QColor(colorStrings.at(i)));
345
346         delete textinputObject;
347     }
348
349     {
350         QString colorStr = "#AA001234";
351         QColor testColor("#001234");
352         testColor.setAlpha(170);
353
354         QString componentStr = "import QtQuick 1.0\nTextInput {  color: \"" + colorStr + "\"; text: \"Hello World\" }";
355         QDeclarativeComponent textinputComponent(&engine);
356         textinputComponent.setData(componentStr.toLatin1(), QUrl());
357         QDeclarative1TextInput *textinputObject = qobject_cast<QDeclarative1TextInput*>(textinputComponent.create());
358
359         QVERIFY(textinputObject != 0);
360         QCOMPARE(textinputObject->color(), testColor);
361
362         delete textinputObject;
363     }
364 }
365
366 void tst_qdeclarativetextinput::selection()
367 {
368     QString testStr = standard[0];
369     QString componentStr = "import QtQuick 1.0\nTextInput {  text: \""+ testStr +"\"; }";
370     QDeclarativeComponent textinputComponent(&engine);
371     textinputComponent.setData(componentStr.toLatin1(), QUrl());
372     QDeclarative1TextInput *textinputObject = qobject_cast<QDeclarative1TextInput*>(textinputComponent.create());
373     QVERIFY(textinputObject != 0);
374
375
376     //Test selection follows cursor
377     for(int i=0; i<= testStr.size(); i++) {
378         textinputObject->setCursorPosition(i);
379         QCOMPARE(textinputObject->cursorPosition(), i);
380         QCOMPARE(textinputObject->selectionStart(), i);
381         QCOMPARE(textinputObject->selectionEnd(), i);
382         QVERIFY(textinputObject->selectedText().isNull());
383     }
384     //Test cursor follows selection
385     for(int i=0; i<= testStr.size(); i++) {
386         textinputObject->select(i,i);
387         QCOMPARE(textinputObject->cursorPosition(), i);
388         QCOMPARE(textinputObject->selectionStart(), i);
389         QCOMPARE(textinputObject->selectionEnd(), i);
390     }
391
392     textinputObject->setCursorPosition(0);
393     QVERIFY(textinputObject->cursorPosition() == 0);
394     QVERIFY(textinputObject->selectionStart() == 0);
395     QVERIFY(textinputObject->selectionEnd() == 0);
396     QVERIFY(textinputObject->selectedText().isNull());
397
398     // Verify invalid positions are ignored.
399     textinputObject->setCursorPosition(-1);
400     QVERIFY(textinputObject->cursorPosition() == 0);
401     QVERIFY(textinputObject->selectionStart() == 0);
402     QVERIFY(textinputObject->selectionEnd() == 0);
403     QVERIFY(textinputObject->selectedText().isNull());
404
405     textinputObject->setCursorPosition(textinputObject->text().count()+1);
406     QVERIFY(textinputObject->cursorPosition() == 0);
407     QVERIFY(textinputObject->selectionStart() == 0);
408     QVERIFY(textinputObject->selectionEnd() == 0);
409     QVERIFY(textinputObject->selectedText().isNull());
410
411     //Test selection
412     for(int i=0; i<= testStr.size(); i++) {
413         textinputObject->select(0,i);
414         QCOMPARE(testStr.mid(0,i), textinputObject->selectedText());
415         QCOMPARE(textinputObject->cursorPosition(), i);
416     }
417     for(int i=0; i<= testStr.size(); i++) {
418         textinputObject->select(i,testStr.size());
419         QCOMPARE(testStr.mid(i,testStr.size()-i), textinputObject->selectedText());
420         QCOMPARE(textinputObject->cursorPosition(), testStr.size());
421     }
422
423     textinputObject->setCursorPosition(0);
424     QVERIFY(textinputObject->cursorPosition() == 0);
425     QVERIFY(textinputObject->selectionStart() == 0);
426     QVERIFY(textinputObject->selectionEnd() == 0);
427     QVERIFY(textinputObject->selectedText().isNull());
428
429     //Test Error Ignoring behaviour
430     textinputObject->setCursorPosition(0);
431     QVERIFY(textinputObject->selectedText().isNull());
432     textinputObject->select(-10,0);
433     QVERIFY(textinputObject->selectedText().isNull());
434     textinputObject->select(100,110);
435     QVERIFY(textinputObject->selectedText().isNull());
436     textinputObject->select(0,-10);
437     QVERIFY(textinputObject->selectedText().isNull());
438     textinputObject->select(0,100);
439     QVERIFY(textinputObject->selectedText().isNull());
440     textinputObject->select(0,10);
441     QVERIFY(textinputObject->selectedText().size() == 10);
442     textinputObject->select(-10,10);
443     QVERIFY(textinputObject->selectedText().size() == 10);
444     textinputObject->select(100,101);
445     QVERIFY(textinputObject->selectedText().size() == 10);
446     textinputObject->select(0,-10);
447     QVERIFY(textinputObject->selectedText().size() == 10);
448     textinputObject->select(0,100);
449     QVERIFY(textinputObject->selectedText().size() == 10);
450
451     textinputObject->deselect();
452     QVERIFY(textinputObject->selectedText().isNull());
453     textinputObject->select(0,10);
454     QVERIFY(textinputObject->selectedText().size() == 10);
455     textinputObject->deselect();
456     QVERIFY(textinputObject->selectedText().isNull());
457
458     delete textinputObject;
459 }
460
461 void tst_qdeclarativetextinput::isRightToLeft_data()
462 {
463     QTest::addColumn<QString>("text");
464     QTest::addColumn<bool>("emptyString");
465     QTest::addColumn<bool>("firstCharacter");
466     QTest::addColumn<bool>("lastCharacter");
467     QTest::addColumn<bool>("middleCharacter");
468     QTest::addColumn<bool>("startString");
469     QTest::addColumn<bool>("midString");
470     QTest::addColumn<bool>("endString");
471
472     const quint16 arabic_str[] = { 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0647};
473     QTest::newRow("Empty") << "" << false << false << false << false << false << false << false;
474     QTest::newRow("Neutral") << "23244242" << false << false << false << false << false << false << false;
475     QTest::newRow("LTR") << "Hello world" << false << false << false << false << false << false << false;
476     QTest::newRow("RTL") << QString::fromUtf16(arabic_str, 11) << false << true << true << true << true << true << true;
477     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;
478     QTest::newRow("Bidi LTR + RTL + LTR") << QString("Hello world") + QString::fromUtf16(arabic_str, 11) + QString("Hello world") << false << false << false << true << false << false << false;
479 }
480
481 void tst_qdeclarativetextinput::isRightToLeft()
482 {
483     QFETCH(QString, text);
484     QFETCH(bool, emptyString);
485     QFETCH(bool, firstCharacter);
486     QFETCH(bool, lastCharacter);
487     QFETCH(bool, middleCharacter);
488     QFETCH(bool, startString);
489     QFETCH(bool, midString);
490     QFETCH(bool, endString);
491
492     QDeclarative1TextInput textInput;
493     textInput.setText(text);
494
495     // first test that the right string is delivered to the QString::isRightToLeft()
496     QCOMPARE(textInput.isRightToLeft(0,0), text.mid(0,0).isRightToLeft());
497     QCOMPARE(textInput.isRightToLeft(0,1), text.mid(0,1).isRightToLeft());
498     QCOMPARE(textInput.isRightToLeft(text.count()-2, text.count()-1), text.mid(text.count()-2, text.count()-1).isRightToLeft());
499     QCOMPARE(textInput.isRightToLeft(text.count()/2, text.count()/2 + 1), text.mid(text.count()/2, text.count()/2 + 1).isRightToLeft());
500     QCOMPARE(textInput.isRightToLeft(0,text.count()/4), text.mid(0,text.count()/4).isRightToLeft());
501     QCOMPARE(textInput.isRightToLeft(text.count()/4,3*text.count()/4), text.mid(text.count()/4,3*text.count()/4).isRightToLeft());
502     if (text.isEmpty())
503         QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML TextInput: isRightToLeft(start, end) called with the end property being smaller than the start.");
504     QCOMPARE(textInput.isRightToLeft(3*text.count()/4,text.count()-1), text.mid(3*text.count()/4,text.count()-1).isRightToLeft());
505
506     // then test that the feature actually works
507     QCOMPARE(textInput.isRightToLeft(0,0), emptyString);
508     QCOMPARE(textInput.isRightToLeft(0,1), firstCharacter);
509     QCOMPARE(textInput.isRightToLeft(text.count()-2, text.count()-1), lastCharacter);
510     QCOMPARE(textInput.isRightToLeft(text.count()/2, text.count()/2 + 1), middleCharacter);
511     QCOMPARE(textInput.isRightToLeft(0,text.count()/4), startString);
512     QCOMPARE(textInput.isRightToLeft(text.count()/4,3*text.count()/4), midString);
513     if (text.isEmpty())
514         QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML TextInput: isRightToLeft(start, end) called with the end property being smaller than the start.");
515     QCOMPARE(textInput.isRightToLeft(3*text.count()/4,text.count()-1), endString);
516 }
517
518 void tst_qdeclarativetextinput::moveCursorSelection_data()
519 {
520     QTest::addColumn<QString>("testStr");
521     QTest::addColumn<int>("cursorPosition");
522     QTest::addColumn<int>("movePosition");
523     QTest::addColumn<QDeclarative1TextInput::SelectionMode>("mode");
524     QTest::addColumn<int>("selectionStart");
525     QTest::addColumn<int>("selectionEnd");
526     QTest::addColumn<bool>("reversible");
527
528     // () contains the text selected by the cursor.
529     // <> contains the actual selection.
530
531     QTest::newRow("(t)he|characters")
532             << standard[0] << 0 << 1 << QDeclarative1TextInput::SelectCharacters << 0 << 1 << true;
533     QTest::newRow("do(g)|characters")
534             << standard[0] << 43 << 44 << QDeclarative1TextInput::SelectCharacters << 43 << 44 << true;
535     QTest::newRow("jum(p)ed|characters")
536             << standard[0] << 23 << 24 << QDeclarative1TextInput::SelectCharacters << 23 << 24 << true;
537     QTest::newRow("jumped( )over|characters")
538             << standard[0] << 26 << 27 << QDeclarative1TextInput::SelectCharacters << 26 << 27 << true;
539     QTest::newRow("(the )|characters")
540             << standard[0] << 0 << 4 << QDeclarative1TextInput::SelectCharacters << 0 << 4 << true;
541     QTest::newRow("( dog)|characters")
542             << standard[0] << 40 << 44 << QDeclarative1TextInput::SelectCharacters << 40 << 44 << true;
543     QTest::newRow("( jumped )|characters")
544             << standard[0] << 19 << 27 << QDeclarative1TextInput::SelectCharacters << 19 << 27 << true;
545     QTest::newRow("th(e qu)ick|characters")
546             << standard[0] << 2 << 6 << QDeclarative1TextInput::SelectCharacters << 2 << 6 << true;
547     QTest::newRow("la(zy d)og|characters")
548             << standard[0] << 38 << 42 << QDeclarative1TextInput::SelectCharacters << 38 << 42 << true;
549     QTest::newRow("jum(ped ov)er|characters")
550             << standard[0] << 23 << 29 << QDeclarative1TextInput::SelectCharacters << 23 << 29 << true;
551     QTest::newRow("()the|characters")
552             << standard[0] << 0 << 0 << QDeclarative1TextInput::SelectCharacters << 0 << 0 << true;
553     QTest::newRow("dog()|characters")
554             << standard[0] << 44 << 44 << QDeclarative1TextInput::SelectCharacters << 44 << 44 << true;
555     QTest::newRow("jum()ped|characters")
556             << standard[0] << 23 << 23 << QDeclarative1TextInput::SelectCharacters << 23 << 23 << true;
557
558     QTest::newRow("<(t)he>|words")
559             << standard[0] << 0 << 1 << QDeclarative1TextInput::SelectWords << 0 << 3 << true;
560     QTest::newRow("<do(g)>|words")
561             << standard[0] << 43 << 44 << QDeclarative1TextInput::SelectWords << 41 << 44 << true;
562     QTest::newRow("<jum(p)ed>|words")
563             << standard[0] << 23 << 24 << QDeclarative1TextInput::SelectWords << 20 << 26 << true;
564     QTest::newRow("<jumped( )>over|words,ltr")
565             << standard[0] << 26 << 27 << QDeclarative1TextInput::SelectWords << 20 << 27 << false;
566     QTest::newRow("jumped<( )over>|words,rtl")
567             << standard[0] << 27 << 26 << QDeclarative1TextInput::SelectWords << 26 << 31 << false;
568     QTest::newRow("<(the )>quick|words,ltr")
569             << standard[0] << 0 << 4 << QDeclarative1TextInput::SelectWords << 0 << 4 << false;
570     QTest::newRow("<(the )quick>|words,rtl")
571             << standard[0] << 4 << 0 << QDeclarative1TextInput::SelectWords << 0 << 9 << false;
572     QTest::newRow("<lazy( dog)>|words,ltr")
573             << standard[0] << 40 << 44 << QDeclarative1TextInput::SelectWords << 36 << 44 << false;
574     QTest::newRow("lazy<( dog)>|words,rtl")
575             << standard[0] << 44 << 40 << QDeclarative1TextInput::SelectWords << 40 << 44 << false;
576     QTest::newRow("<fox( jumped )>over|words,ltr")
577             << standard[0] << 19 << 27 << QDeclarative1TextInput::SelectWords << 16 << 27 << false;
578     QTest::newRow("fox<( jumped )over>|words,rtl")
579             << standard[0] << 27 << 19 << QDeclarative1TextInput::SelectWords << 19 << 31 << false;
580     QTest::newRow("<th(e qu)ick>|words")
581             << standard[0] << 2 << 6 << QDeclarative1TextInput::SelectWords << 0 << 9 << true;
582     QTest::newRow("<la(zy d)og|words>")
583             << standard[0] << 38 << 42 << QDeclarative1TextInput::SelectWords << 36 << 44 << true;
584     QTest::newRow("<jum(ped ov)er>|words")
585             << standard[0] << 23 << 29 << QDeclarative1TextInput::SelectWords << 20 << 31 << true;
586     QTest::newRow("<()>the|words")
587             << standard[0] << 0 << 0 << QDeclarative1TextInput::SelectWords << 0 << 0 << true;
588     QTest::newRow("dog<()>|words")
589             << standard[0] << 44 << 44 << QDeclarative1TextInput::SelectWords << 44 << 44 << true;
590     QTest::newRow("jum<()>ped|words")
591             << standard[0] << 23 << 23 << QDeclarative1TextInput::SelectWords << 23 << 23 << true;
592
593     QTest::newRow("Hello<(,)> |words")
594             << standard[2] << 5 << 6 << QDeclarative1TextInput::SelectWords << 5 << 6 << true;
595     QTest::newRow("Hello<(, )>world|words,ltr")
596             << standard[2] << 5 << 7 << QDeclarative1TextInput::SelectWords << 5 << 7 << false;
597     QTest::newRow("Hello<(, )world>|words,rtl")
598             << standard[2] << 7 << 5 << QDeclarative1TextInput::SelectWords << 5 << 12 << false;
599     QTest::newRow("<Hel(lo, )>world|words,ltr")
600             << standard[2] << 3 << 7 << QDeclarative1TextInput::SelectWords << 0 << 7 << false;
601     QTest::newRow("<Hel(lo, )world>|words,rtl")
602             << standard[2] << 7 << 3 << QDeclarative1TextInput::SelectWords << 0 << 12 << false;
603     QTest::newRow("<Hel(lo)>,|words")
604             << standard[2] << 3 << 5 << QDeclarative1TextInput::SelectWords << 0 << 5 << true;
605     QTest::newRow("Hello<()>,|words")
606             << standard[2] << 5 << 5 << QDeclarative1TextInput::SelectWords << 5 << 5 << true;
607     QTest::newRow("Hello,<()>|words")
608             << standard[2] << 6 << 6 << QDeclarative1TextInput::SelectWords << 6 << 6 << true;
609     QTest::newRow("Hello<,( )>world|words,ltr")
610             << standard[2] << 6 << 7 << QDeclarative1TextInput::SelectWords << 5 << 7 << false;
611     QTest::newRow("Hello,<( )world>|words,rtl")
612             << standard[2] << 7 << 6 << QDeclarative1TextInput::SelectWords << 6 << 12 << false;
613     QTest::newRow("Hello<,( world)>|words,ltr")
614             << standard[2] << 6 << 12 << QDeclarative1TextInput::SelectWords << 5 << 12 << false;
615     QTest::newRow("Hello,<( world)>|words,rtl")
616             << standard[2] << 12 << 6 << QDeclarative1TextInput::SelectWords << 6 << 12 << false;
617     QTest::newRow("Hello<,( world!)>|words,ltr")
618             << standard[2] << 6 << 13 << QDeclarative1TextInput::SelectWords << 5 << 13 << false;
619     QTest::newRow("Hello,<( world!)>|words,rtl")
620             << standard[2] << 13 << 6 << QDeclarative1TextInput::SelectWords << 6 << 13 << false;
621     QTest::newRow("Hello<(, world!)>|words")
622             << standard[2] << 5 << 13 << QDeclarative1TextInput::SelectWords << 5 << 13 << true;
623      QTest::newRow("world<(!)>|words")
624              << standard[2] << 12 << 13 << QDeclarative1TextInput::SelectWords << 12 << 13 << true;
625     QTest::newRow("world!<()>)|words")
626             << standard[2] << 13 << 13 << QDeclarative1TextInput::SelectWords << 13 << 13 << true;
627     QTest::newRow("world<()>!)|words")
628             << standard[2] << 12 << 12 << QDeclarative1TextInput::SelectWords << 12 << 12 << true;
629
630     QTest::newRow("<(,)>olleH |words")
631             << standard[3] << 7 << 8 << QDeclarative1TextInput::SelectWords << 7 << 8 << true;
632     QTest::newRow("<dlrow( ,)>olleH|words,ltr")
633             << standard[3] << 6 << 8 << QDeclarative1TextInput::SelectWords << 1 << 8 << false;
634     QTest::newRow("dlrow<( ,)>olleH|words,rtl")
635             << standard[3] << 8 << 6 << QDeclarative1TextInput::SelectWords << 6 << 8 << false;
636     QTest::newRow("<dlrow( ,ol)leH>|words,ltr")
637             << standard[3] << 6 << 10 << QDeclarative1TextInput::SelectWords << 1 << 13 << false;
638     QTest::newRow("dlrow<( ,ol)leH>|words,rtl")
639             << standard[3] << 10 << 6 << QDeclarative1TextInput::SelectWords << 6 << 13 << false;
640     QTest::newRow(",<(ol)leH>,|words")
641             << standard[3] << 8 << 10 << QDeclarative1TextInput::SelectWords << 8 << 13 << true;
642     QTest::newRow(",<()>olleH|words")
643             << standard[3] << 8 << 8 << QDeclarative1TextInput::SelectWords << 8 << 8 << true;
644     QTest::newRow("<()>,olleH|words")
645             << standard[3] << 7 << 7 << QDeclarative1TextInput::SelectWords << 7 << 7 << true;
646     QTest::newRow("<dlrow( )>,olleH|words,ltr")
647             << standard[3] << 6 << 7 << QDeclarative1TextInput::SelectWords << 1 << 7 << false;
648     QTest::newRow("dlrow<( ),>olleH|words,rtl")
649             << standard[3] << 7 << 6 << QDeclarative1TextInput::SelectWords << 6 << 8 << false;
650     QTest::newRow("<(dlrow )>,olleH|words,ltr")
651             << standard[3] << 1 << 7 << QDeclarative1TextInput::SelectWords << 1 << 7 << false;
652     QTest::newRow("<(dlrow ),>olleH|words,rtl")
653             << standard[3] << 7 << 1 << QDeclarative1TextInput::SelectWords << 1 << 8 << false;
654     QTest::newRow("<(!dlrow )>,olleH|words,ltr")
655             << standard[3] << 0 << 7 << QDeclarative1TextInput::SelectWords << 0 << 7 << false;
656     QTest::newRow("<(!dlrow ),>olleH|words,rtl")
657             << standard[3] << 7 << 0 << QDeclarative1TextInput::SelectWords << 0 << 8 << false;
658     QTest::newRow("(!dlrow ,)olleH|words")
659             << standard[3] << 0 << 8 << QDeclarative1TextInput::SelectWords << 0 << 8 << true;
660     QTest::newRow("<(!)>dlrow|words")
661             << standard[3] << 0 << 1 << QDeclarative1TextInput::SelectWords << 0 << 1 << true;
662     QTest::newRow("<()>!dlrow|words")
663             << standard[3] << 0 << 0 << QDeclarative1TextInput::SelectWords << 0 << 0 << true;
664     QTest::newRow("!<()>dlrow|words")
665             << standard[3] << 1 << 1 << QDeclarative1TextInput::SelectWords << 1 << 1 << true;
666
667     QTest::newRow(" <s(pac)ey>   text |words")
668             << standard[4] << 1 << 4 << QDeclarative1TextInput::SelectWords << 1 << 7 << true;
669     QTest::newRow(" spacey   <t(ex)t> |words")
670             << standard[4] << 11 << 13 << QDeclarative1TextInput::SelectWords << 10 << 14 << true;
671     QTest::newRow("<( )>spacey   text |words|ltr")
672             << standard[4] << 0 << 1 << QDeclarative1TextInput::SelectWords << 0 << 1 << false;
673     QTest::newRow("<( )spacey>   text |words|rtl")
674             << standard[4] << 1 << 0 << QDeclarative1TextInput::SelectWords << 0 << 7 << false;
675     QTest::newRow("spacey   <text( )>|words|ltr")
676             << standard[4] << 14 << 15 << QDeclarative1TextInput::SelectWords << 10 << 15 << false;
677     QTest::newRow("spacey   text<( )>|words|rtl")
678             << standard[4] << 15 << 14 << QDeclarative1TextInput::SelectWords << 14 << 15 << false;
679     QTest::newRow("<()> spacey   text |words")
680             << standard[4] << 0 << 0 << QDeclarative1TextInput::SelectWords << 0 << 0 << false;
681     QTest::newRow(" spacey   text <()>|words")
682             << standard[4] << 15 << 15 << QDeclarative1TextInput::SelectWords << 15 << 15 << false;
683 }
684
685 void tst_qdeclarativetextinput::moveCursorSelection()
686 {
687     QFETCH(QString, testStr);
688     QFETCH(int, cursorPosition);
689     QFETCH(int, movePosition);
690     QFETCH(QDeclarative1TextInput::SelectionMode, mode);
691     QFETCH(int, selectionStart);
692     QFETCH(int, selectionEnd);
693     QFETCH(bool, reversible);
694
695     QString componentStr = "import QtQuick 1.1\nTextInput {  text: \""+ testStr +"\"; }";
696     QDeclarativeComponent textinputComponent(&engine);
697     textinputComponent.setData(componentStr.toLatin1(), QUrl());
698     QDeclarative1TextInput *textinputObject = qobject_cast<QDeclarative1TextInput*>(textinputComponent.create());
699     QVERIFY(textinputObject != 0);
700
701     textinputObject->setCursorPosition(cursorPosition);
702     textinputObject->moveCursorSelection(movePosition, mode);
703
704     QCOMPARE(textinputObject->selectedText(), testStr.mid(selectionStart, selectionEnd - selectionStart));
705     QCOMPARE(textinputObject->selectionStart(), selectionStart);
706     QCOMPARE(textinputObject->selectionEnd(), selectionEnd);
707
708     if (reversible) {
709         textinputObject->setCursorPosition(movePosition);
710         textinputObject->moveCursorSelection(cursorPosition, mode);
711
712         QCOMPARE(textinputObject->selectedText(), testStr.mid(selectionStart, selectionEnd - selectionStart));
713         QCOMPARE(textinputObject->selectionStart(), selectionStart);
714         QCOMPARE(textinputObject->selectionEnd(), selectionEnd);
715     }
716
717     delete textinputObject;
718 }
719
720 void tst_qdeclarativetextinput::moveCursorSelectionSequence_data()
721 {
722     QTest::addColumn<QString>("testStr");
723     QTest::addColumn<int>("cursorPosition");
724     QTest::addColumn<int>("movePosition1");
725     QTest::addColumn<int>("movePosition2");
726     QTest::addColumn<int>("selection1Start");
727     QTest::addColumn<int>("selection1End");
728     QTest::addColumn<int>("selection2Start");
729     QTest::addColumn<int>("selection2End");
730
731     // () contains the text selected by the cursor.
732     // <> contains the actual selection.
733     // ^ is the revised cursor position.
734     // {} contains the revised selection.
735
736     QTest::newRow("the {<quick( bro)wn> f^ox} jumped|ltr")
737             << standard[0]
738             << 9 << 13 << 17
739             << 4 << 15
740             << 4 << 19;
741     QTest::newRow("the quick<( {bro)wn> f^ox} jumped|rtl")
742             << standard[0]
743             << 13 << 9 << 17
744             << 9 << 15
745             << 10 << 19;
746     QTest::newRow("the {<quick( bro)wn> ^}fox jumped|ltr")
747             << standard[0]
748             << 9 << 13 << 16
749             << 4 << 15
750             << 4 << 16;
751     QTest::newRow("the quick<( {bro)wn> ^}fox jumped|rtl")
752             << standard[0]
753             << 13 << 9 << 16
754             << 9 << 15
755             << 10 << 16;
756     QTest::newRow("the {<quick( bro)wn^>} fox jumped|ltr")
757             << standard[0]
758             << 9 << 13 << 15
759             << 4 << 15
760             << 4 << 15;
761     QTest::newRow("the quick<( {bro)wn^>} f^ox jumped|rtl")
762             << standard[0]
763             << 13 << 9 << 15
764             << 9 << 15
765             << 10 << 15;
766     QTest::newRow("the {<quick() ^}bro)wn> fox|ltr")
767             << standard[0]
768             << 9 << 13 << 10
769             << 4 << 15
770             << 4 << 10;
771     QTest::newRow("the quick<( {^bro)wn>} fox|rtl")
772             << standard[0]
773             << 13 << 9 << 10
774             << 9 << 15
775             << 10 << 15;
776     QTest::newRow("the {<quick^}( bro)wn> fox|ltr")
777             << standard[0]
778             << 9 << 13 << 9
779             << 4 << 15
780             << 4 << 9;
781     QTest::newRow("the quick{<(^ bro)wn>} fox|rtl")
782             << standard[0]
783             << 13 << 9 << 9
784             << 9 << 15
785             << 9 << 15;
786     QTest::newRow("the {<qui^ck}( bro)wn> fox|ltr")
787             << standard[0]
788             << 9 << 13 << 7
789             << 4 << 15
790             << 4 << 9;
791     QTest::newRow("the {<qui^ck}( bro)wn> fox|rtl")
792             << standard[0]
793             << 13 << 9 << 7
794             << 9 << 15
795             << 4 << 15;
796     QTest::newRow("the {<^quick}( bro)wn> fox|ltr")
797             << standard[0]
798             << 9 << 13 << 4
799             << 4 << 15
800             << 4 << 9;
801     QTest::newRow("the {<^quick}( bro)wn> fox|rtl")
802             << standard[0]
803             << 13 << 9 << 4
804             << 9 << 15
805             << 4 << 15;
806     QTest::newRow("the{^ <quick}( bro)wn> fox|ltr")
807             << standard[0]
808             << 9 << 13 << 3
809             << 4 << 15
810             << 3 << 9;
811     QTest::newRow("the{^ <quick}( bro)wn> fox|rtl")
812             << standard[0]
813             << 13 << 9 << 3
814             << 9 << 15
815             << 3 << 15;
816     QTest::newRow("{t^he <quick}( bro)wn> fox|ltr")
817             << standard[0]
818             << 9 << 13 << 1
819             << 4 << 15
820             << 0 << 9;
821     QTest::newRow("{t^he <quick}( bro)wn> fox|rtl")
822             << standard[0]
823             << 13 << 9 << 1
824             << 9 << 15
825             << 0 << 15;
826
827     QTest::newRow("{<He(ll)o>, w^orld}!|ltr")
828             << standard[2]
829             << 2 << 4 << 8
830             << 0 << 5
831             << 0 << 12;
832     QTest::newRow("{<He(ll)o>, w^orld}!|rtl")
833             << standard[2]
834             << 4 << 2 << 8
835             << 0 << 5
836             << 0 << 12;
837
838     QTest::newRow("!{dlro^w ,<o(ll)eH>}|ltr")
839             << standard[3]
840             << 9 << 11 << 5
841             << 8 << 13
842             << 1 << 13;
843     QTest::newRow("!{dlro^w ,<o(ll)eH>}|rtl")
844             << standard[3]
845             << 11 << 9 << 5
846             << 8 << 13
847             << 1 << 13;
848
849     QTest::newRow("{<(^} sp)acey>   text |ltr")
850             << standard[4]
851             << 0 << 3 << 0
852             << 0 << 7
853             << 0 << 0;
854     QTest::newRow("{<( ^}sp)acey>   text |ltr")
855             << standard[4]
856             << 0 << 3 << 1
857             << 0 << 7
858             << 0 << 1;
859     QTest::newRow("<( {s^p)acey>}   text |rtl")
860             << standard[4]
861             << 3 << 0 << 2
862             << 0 << 7
863             << 1 << 7;
864     QTest::newRow("<( {^sp)acey>}   text |rtl")
865             << standard[4]
866             << 3 << 0 << 1
867             << 0 << 7
868             << 1 << 7;
869
870     QTest::newRow(" spacey   <te(xt {^)>}|rtl")
871             << standard[4]
872             << 15 << 12 << 15
873             << 10 << 15
874             << 15 << 15;
875     QTest::newRow(" spacey   <te(xt{^ )>}|rtl")
876             << standard[4]
877             << 15 << 12 << 14
878             << 10 << 15
879             << 14 << 15;
880     QTest::newRow(" spacey   {<te(x^t} )>|ltr")
881             << standard[4]
882             << 12 << 15 << 13
883             << 10 << 15
884             << 10 << 14;
885     QTest::newRow(" spacey   {<te(xt^} )>|ltr")
886             << standard[4]
887             << 12 << 15 << 14
888             << 10 << 15
889             << 10 << 14;
890 }
891
892 void tst_qdeclarativetextinput::moveCursorSelectionSequence()
893 {
894     QFETCH(QString, testStr);
895     QFETCH(int, cursorPosition);
896     QFETCH(int, movePosition1);
897     QFETCH(int, movePosition2);
898     QFETCH(int, selection1Start);
899     QFETCH(int, selection1End);
900     QFETCH(int, selection2Start);
901     QFETCH(int, selection2End);
902
903     QString componentStr = "import QtQuick 1.1\nTextInput {  text: \""+ testStr +"\"; }";
904     QDeclarativeComponent textinputComponent(&engine);
905     textinputComponent.setData(componentStr.toLatin1(), QUrl());
906     QDeclarative1TextInput *textinputObject = qobject_cast<QDeclarative1TextInput*>(textinputComponent.create());
907     QVERIFY(textinputObject != 0);
908
909     textinputObject->setCursorPosition(cursorPosition);
910
911     textinputObject->moveCursorSelection(movePosition1, QDeclarative1TextInput::SelectWords);
912     QCOMPARE(textinputObject->selectedText(), testStr.mid(selection1Start, selection1End - selection1Start));
913     QCOMPARE(textinputObject->selectionStart(), selection1Start);
914     QCOMPARE(textinputObject->selectionEnd(), selection1End);
915
916     textinputObject->moveCursorSelection(movePosition2, QDeclarative1TextInput::SelectWords);
917     QCOMPARE(textinputObject->selectedText(), testStr.mid(selection2Start, selection2End - selection2Start));
918     QCOMPARE(textinputObject->selectionStart(), selection2Start);
919     QCOMPARE(textinputObject->selectionEnd(), selection2End);
920
921     delete textinputObject;
922 }
923
924 void tst_qdeclarativetextinput::mouseSelection_data()
925 {
926     QTest::addColumn<QString>("qmlfile");
927     QTest::addColumn<bool>("expectSelection");
928
929     // import installed
930     QTest::newRow("on") << SRCDIR "/data/mouseselection_true.qml" << true;
931     QTest::newRow("off") << SRCDIR "/data/mouseselection_false.qml" << false;
932     QTest::newRow("default") << SRCDIR "/data/mouseselection_default.qml" << false;
933     QTest::newRow("on word selection") << SRCDIR "/data/mouseselection_true_words.qml" << true;
934     QTest::newRow("off word selection") << SRCDIR "/data/mouseselection_false_words.qml" << false;
935     QTest::newRow("on read only") << SRCDIR "/data/mouseselection_true_readonly.qml" << true;
936     QTest::newRow("off read only") << SRCDIR "/data/mouseselection_false_readonly.qml" << false;
937 }
938
939 void tst_qdeclarativetextinput::mouseSelection()
940 {
941     QFETCH(QString, qmlfile);
942     QFETCH(bool, expectSelection);
943
944     QDeclarativeView *canvas = createView(qmlfile);
945
946     canvas->show();
947     QApplication::setActiveWindow(canvas);
948     QTest::qWaitForWindowShown(canvas);
949     QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(canvas));
950
951     QVERIFY(canvas->rootObject() != 0);
952     QDeclarative1TextInput *textInputObject = qobject_cast<QDeclarative1TextInput *>(canvas->rootObject());
953     QVERIFY(textInputObject != 0);
954
955     // press-and-drag-and-release from x1 to x2
956     int x1 = 10;
957     int x2 = 70;
958     int y = textInputObject->height()/2;
959     QTest::mousePress(canvas->viewport(), Qt::LeftButton, 0, canvas->mapFromScene(QPoint(x1,y)));
960     //QTest::mouseMove(canvas->viewport(), canvas->mapFromScene(QPoint(x2,y))); // doesn't work
961     QMouseEvent mv(QEvent::MouseMove, canvas->mapFromScene(QPoint(x2,y)), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
962     QApplication::sendEvent(canvas->viewport(), &mv);
963     QTest::mouseRelease(canvas->viewport(), Qt::LeftButton, 0, canvas->mapFromScene(QPoint(x2,y)));
964     QString str = textInputObject->selectedText();
965     if (expectSelection)
966         QVERIFY(str.length() > 3); // don't reallly care *what* was selected (and it's too sensitive to platform)
967     else
968         QVERIFY(str.isEmpty());
969
970     // Clicking and shift to clicking between the same points should select the same text.
971     textInputObject->setCursorPosition(0);
972     QTest::mouseClick(canvas->viewport(), Qt::LeftButton, Qt::NoModifier, canvas->mapFromScene(QPoint(x1,y)));
973     QTest::mouseClick(canvas->viewport(), Qt::LeftButton, Qt::ShiftModifier, canvas->mapFromScene(QPoint(x2,y)));
974     QCOMPARE(textInputObject->selectedText(), str);
975
976     delete canvas;
977 }
978
979 void tst_qdeclarativetextinput::deferEnableSelectByMouse_data()
980 {
981     QTest::addColumn<QString>("qmlfile");
982
983     QTest::newRow("writable") << SRCDIR "/data/mouseselection_false.qml";
984     QTest::newRow("read only") << SRCDIR "/data/mouseselection_false_readonly.qml";
985 }
986
987 void tst_qdeclarativetextinput::deferEnableSelectByMouse()
988 {
989     // Verify text isn't selected if selectByMouse is enabled after the mouse button has been pressed.
990     QFETCH(QString, qmlfile);
991
992     QDeclarativeView *canvas = createView(qmlfile);
993
994     canvas->show();
995     QApplication::setActiveWindow(canvas);
996     QTest::qWaitForWindowShown(canvas);
997     QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(canvas));
998
999     QVERIFY(canvas->rootObject() != 0);
1000     QDeclarative1TextInput *textInputObject = qobject_cast<QDeclarative1TextInput *>(canvas->rootObject());
1001     QVERIFY(textInputObject != 0);
1002
1003     // press-and-drag-and-release from x1 to x2
1004     int x1 = 10;
1005     int x2 = 70;
1006     int y = textInputObject->height()/2;
1007
1008     QTest::mousePress(canvas->viewport(), Qt::LeftButton, 0, canvas->mapFromScene(QPoint(x1,y)));
1009     textInputObject->setSelectByMouse(true);
1010     //QTest::mouseMove(canvas->viewport(), canvas->mapFromScene(QPoint(x2,y))); // doesn't work
1011     QMouseEvent mv(QEvent::MouseMove, canvas->mapFromScene(QPoint(x2,y)), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
1012     QApplication::sendEvent(canvas->viewport(), &mv);
1013     QTest::mouseRelease(canvas->viewport(), Qt::LeftButton, 0, canvas->mapFromScene(QPoint(x2,y)));
1014     QVERIFY(textInputObject->selectedText().isEmpty());
1015
1016     delete canvas;
1017 }
1018
1019 void tst_qdeclarativetextinput::deferDisableSelectByMouse_data()
1020 {
1021     QTest::addColumn<QString>("qmlfile");
1022
1023     QTest::newRow("writable") << SRCDIR "/data/mouseselection_true.qml";
1024     QTest::newRow("read only") << SRCDIR "/data/mouseselection_true_readonly.qml";
1025 }
1026
1027 void tst_qdeclarativetextinput::deferDisableSelectByMouse()
1028 {
1029     // Verify text isn't selected if selectByMouse is enabled after the mouse button has been pressed.
1030     QFETCH(QString, qmlfile);
1031
1032     QDeclarativeView *canvas = createView(qmlfile);
1033
1034     canvas->show();
1035     QApplication::setActiveWindow(canvas);
1036     QTest::qWaitForWindowShown(canvas);
1037     QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(canvas));
1038
1039     QVERIFY(canvas->rootObject() != 0);
1040     QDeclarative1TextInput *textInputObject = qobject_cast<QDeclarative1TextInput *>(canvas->rootObject());
1041     QVERIFY(textInputObject != 0);
1042
1043     // press-and-drag-and-release from x1 to x2
1044     int x1 = 10;
1045     int x2 = 70;
1046     int y = textInputObject->height()/2;
1047
1048     QTest::mousePress(canvas->viewport(), Qt::LeftButton, 0, canvas->mapFromScene(QPoint(x1,y)));
1049     textInputObject->setSelectByMouse(false);
1050     //QTest::mouseMove(canvas->viewport(), canvas->mapFromScene(QPoint(x2,y))); // doesn't work
1051     QMouseEvent mv(QEvent::MouseMove, canvas->mapFromScene(QPoint(x2,y)), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
1052     QApplication::sendEvent(canvas->viewport(), &mv);
1053     QTest::mouseRelease(canvas->viewport(), Qt::LeftButton, 0, canvas->mapFromScene(QPoint(x2,y)));
1054     QVERIFY(textInputObject->selectedText().length() > 3);
1055
1056     delete canvas;
1057 }
1058
1059 void tst_qdeclarativetextinput::dragMouseSelection()
1060 {
1061     QString qmlfile = SRCDIR "/data/mouseselection_true.qml";
1062
1063     QDeclarativeView *canvas = createView(qmlfile);
1064
1065     canvas->show();
1066     QApplication::setActiveWindow(canvas);
1067     QTest::qWaitForWindowShown(canvas);
1068     QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(canvas));
1069
1070     QVERIFY(canvas->rootObject() != 0);
1071     QDeclarative1TextInput *textInputObject = qobject_cast<QDeclarative1TextInput *>(canvas->rootObject());
1072     QVERIFY(textInputObject != 0);
1073
1074     // press-and-drag-and-release from x1 to x2
1075     int x1 = 10;
1076     int x2 = 70;
1077     int y = textInputObject->height()/2;
1078     QTest::mousePress(canvas->viewport(), Qt::LeftButton, 0, canvas->mapFromScene(QPoint(x1,y)));
1079     {
1080         QMouseEvent mv(QEvent::MouseMove, canvas->mapFromScene(QPoint(x2,y)), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
1081         QApplication::sendEvent(canvas->viewport(), &mv);
1082     }
1083     QTest::mouseRelease(canvas->viewport(), Qt::LeftButton, 0, canvas->mapFromScene(QPoint(x2,y)));
1084
1085     QString str1 = textInputObject->selectedText();
1086     QVERIFY(str1.length() > 3);
1087
1088     // press and drag the current selection.
1089     x1 = 40;
1090     x2 = 100;
1091     QTest::mousePress(canvas->viewport(), Qt::LeftButton, 0, canvas->mapFromScene(QPoint(x1,y)));
1092     {
1093         QMouseEvent mv(QEvent::MouseMove, canvas->mapFromScene(QPoint(x2,y)), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
1094         QApplication::sendEvent(canvas->viewport(), &mv);
1095     }
1096         QTest::mouseRelease(canvas->viewport(), Qt::LeftButton, 0, canvas->mapFromScene(QPoint(x2,y)));
1097     QString str2 = textInputObject->selectedText();
1098     QVERIFY(str2.length() > 3);
1099
1100     QVERIFY(str1 != str2); // Verify the second press and drag is a new selection and doesn't not the first moved.
1101     delete canvas;
1102 }
1103
1104 void tst_qdeclarativetextinput::mouseSelectionMode_data()
1105 {
1106     QTest::addColumn<QString>("qmlfile");
1107     QTest::addColumn<bool>("selectWords");
1108
1109     // import installed
1110     QTest::newRow("SelectWords") << SRCDIR "/data/mouseselectionmode_words.qml" << true;
1111     QTest::newRow("SelectCharacters") << SRCDIR "/data/mouseselectionmode_characters.qml" << false;
1112     QTest::newRow("default") << SRCDIR "/data/mouseselectionmode_default.qml" << false;
1113 }
1114
1115 void tst_qdeclarativetextinput::mouseSelectionMode()
1116 {
1117     QFETCH(QString, qmlfile);
1118     QFETCH(bool, selectWords);
1119
1120     QString text = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1121
1122     QDeclarativeView *canvas = createView(qmlfile);
1123
1124     canvas->show();
1125     QApplication::setActiveWindow(canvas);
1126     QTest::qWaitForWindowShown(canvas);
1127     QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(canvas));
1128
1129     QVERIFY(canvas->rootObject() != 0);
1130     QDeclarative1TextInput *textInputObject = qobject_cast<QDeclarative1TextInput *>(canvas->rootObject());
1131     QVERIFY(textInputObject != 0);
1132
1133     // press-and-drag-and-release from x1 to x2
1134     int x1 = 10;
1135     int x2 = 70;
1136     int y = textInputObject->height()/2;
1137     QTest::mousePress(canvas->viewport(), Qt::LeftButton, 0, canvas->mapFromScene(QPoint(x1,y)));
1138     //QTest::mouseMove(canvas->viewport(), canvas->mapFromScene(QPoint(x2,y))); // doesn't work
1139     QMouseEvent mv(QEvent::MouseMove, canvas->mapFromScene(QPoint(x2,y)), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
1140     QApplication::sendEvent(canvas->viewport(), &mv);
1141     QTest::mouseRelease(canvas->viewport(), Qt::LeftButton, 0, canvas->mapFromScene(QPoint(x2,y)));
1142     QString str = textInputObject->selectedText();
1143     if (selectWords) {
1144         QCOMPARE(str, text);
1145     } else {
1146         QVERIFY(str.length() > 3);
1147         QVERIFY(str != text);
1148     }
1149
1150     delete canvas;
1151 }
1152
1153 void tst_qdeclarativetextinput::horizontalAlignment_data()
1154 {
1155     QTest::addColumn<int>("hAlign");
1156     QTest::addColumn<QString>("expectfile");
1157
1158     QTest::newRow("L") << int(Qt::AlignLeft) << "halign_left";
1159     QTest::newRow("R") << int(Qt::AlignRight) << "halign_right";
1160     QTest::newRow("C") << int(Qt::AlignHCenter) << "halign_center";
1161 }
1162
1163 void tst_qdeclarativetextinput::horizontalAlignment()
1164 {
1165     QFETCH(int, hAlign);
1166     QFETCH(QString, expectfile);
1167
1168     QDeclarativeView *canvas = createView(SRCDIR "/data/horizontalAlignment.qml");
1169
1170     canvas->show();
1171     QApplication::setActiveWindow(canvas);
1172     QTest::qWaitForWindowShown(canvas);
1173     QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(canvas));
1174     QObject *ob = canvas->rootObject();
1175     QVERIFY(ob != 0);
1176     ob->setProperty("horizontalAlignment",hAlign);
1177     QImage actual(canvas->width(), canvas->height(), QImage::Format_RGB32);
1178     actual.fill(qRgb(255,255,255));
1179     {
1180         QPainter p(&actual);
1181         canvas->render(&p);
1182     }
1183
1184     expectfile = createExpectedFileIfNotFound(expectfile, actual);
1185
1186     QImage expect(expectfile);
1187
1188     QCOMPARE(actual,expect);
1189
1190     delete canvas;
1191 }
1192
1193 void tst_qdeclarativetextinput::horizontalAlignment_RightToLeft()
1194 {
1195     QDeclarativeView *canvas = createView(SRCDIR "/data/horizontalAlignment_RightToLeft.qml");
1196     QDeclarative1TextInput *textInput = canvas->rootObject()->findChild<QDeclarative1TextInput*>("text");
1197     QVERIFY(textInput != 0);
1198     canvas->show();
1199
1200     QDeclarative1TextInputPrivate *textInputPrivate = QDeclarative1TextInputPrivate::get(textInput);
1201     QVERIFY(textInputPrivate != 0);
1202     QVERIFY(-textInputPrivate->hscroll > canvas->width()/2);
1203
1204     // implicit alignment should follow the reading direction of RTL text
1205     QCOMPARE(textInput->hAlign(), QDeclarative1TextInput::AlignRight);
1206     QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1207     QVERIFY(-textInputPrivate->hscroll > canvas->width()/2);
1208
1209     // explicitly left aligned
1210     textInput->setHAlign(QDeclarative1TextInput::AlignLeft);
1211     QCOMPARE(textInput->hAlign(), QDeclarative1TextInput::AlignLeft);
1212     QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1213     QVERIFY(-textInputPrivate->hscroll < canvas->width()/2);
1214
1215     // explicitly right aligned
1216     textInput->setHAlign(QDeclarative1TextInput::AlignRight);
1217     QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1218     QCOMPARE(textInput->hAlign(), QDeclarative1TextInput::AlignRight);
1219     QVERIFY(-textInputPrivate->hscroll > canvas->width()/2);
1220
1221     // explicitly center aligned
1222     textInput->setHAlign(QDeclarative1TextInput::AlignHCenter);
1223     QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1224     QCOMPARE(textInput->hAlign(), QDeclarative1TextInput::AlignHCenter);
1225     QVERIFY(-textInputPrivate->hscroll < canvas->width()/2);
1226     QVERIFY(-textInputPrivate->hscroll + textInputPrivate->width() > canvas->width()/2);
1227
1228     // reseted alignment should go back to following the text reading direction
1229     textInput->resetHAlign();
1230     QCOMPARE(textInput->hAlign(), QDeclarative1TextInput::AlignRight);
1231     QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1232     QVERIFY(-textInputPrivate->hscroll > canvas->width()/2);
1233
1234     // mirror the text item
1235     QDeclarativeItemPrivate::get(textInput)->setLayoutMirror(true);
1236
1237     // mirrored implicit alignment should continue to follow the reading direction of the text
1238     QCOMPARE(textInput->hAlign(), QDeclarative1TextInput::AlignRight);
1239     QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1240     QVERIFY(-textInputPrivate->hscroll > canvas->width()/2);
1241
1242     // explicitly right aligned behaves as left aligned
1243     textInput->setHAlign(QDeclarative1TextInput::AlignRight);
1244     QCOMPARE(textInput->hAlign(), QDeclarative1TextInput::AlignRight);
1245     QCOMPARE(textInput->effectiveHAlign(), QDeclarative1TextInput::AlignLeft);
1246     QVERIFY(-textInputPrivate->hscroll < canvas->width()/2);
1247
1248     // mirrored explicitly left aligned behaves as right aligned
1249     textInput->setHAlign(QDeclarative1TextInput::AlignLeft);
1250     QCOMPARE(textInput->hAlign(), QDeclarative1TextInput::AlignLeft);
1251     QCOMPARE(textInput->effectiveHAlign(), QDeclarative1TextInput::AlignRight);
1252     QVERIFY(-textInputPrivate->hscroll > canvas->width()/2);
1253
1254     // disable mirroring
1255     QDeclarativeItemPrivate::get(textInput)->setLayoutMirror(false);
1256     QCOMPARE(textInput->effectiveHAlign(), textInput->hAlign());
1257     textInput->resetHAlign();
1258
1259     // English text should be implicitly left aligned
1260     textInput->setText("Hello world!");
1261     QCOMPARE(textInput->hAlign(), QDeclarative1TextInput::AlignLeft);
1262     QVERIFY(-textInputPrivate->hscroll < canvas->width()/2);
1263
1264 #ifndef Q_OS_MAC    // QTBUG-18040
1265     // empty text with implicit alignment follows the system locale-based
1266     // keyboard input direction from QApplication::keyboardInputDirection
1267     textInput->setText("");
1268     QCOMPARE(textInput->hAlign(), QApplication::keyboardInputDirection() == Qt::LeftToRight ?
1269                                   QDeclarative1TextInput::AlignLeft : QDeclarative1TextInput::AlignRight);
1270     if (QApplication::keyboardInputDirection() == Qt::LeftToRight)
1271         QVERIFY(-textInputPrivate->hscroll < canvas->width()/2);
1272     else
1273         QVERIFY(-textInputPrivate->hscroll > canvas->width()/2);
1274     textInput->setHAlign(QDeclarative1TextInput::AlignRight);
1275     QCOMPARE(textInput->hAlign(), QDeclarative1TextInput::AlignRight);
1276     QVERIFY(-textInputPrivate->hscroll > canvas->width()/2);
1277 #endif
1278
1279     delete canvas;
1280
1281 #ifndef Q_OS_MAC    // QTBUG-18040
1282     // alignment of TextInput with no text set to it
1283     QString componentStr = "import QtQuick 1.0\nTextInput {}";
1284     QDeclarativeComponent textComponent(&engine);
1285     textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
1286     QDeclarative1TextInput *textObject = qobject_cast<QDeclarative1TextInput*>(textComponent.create());
1287     QCOMPARE(textObject->hAlign(), QApplication::keyboardInputDirection() == Qt::LeftToRight ?
1288                                   QDeclarative1TextInput::AlignLeft : QDeclarative1TextInput::AlignRight);
1289     delete textObject;
1290 #endif
1291 }
1292
1293 void tst_qdeclarativetextinput::positionAt()
1294 {
1295     QDeclarativeView *canvas = createView(SRCDIR "/data/positionAt.qml");
1296     QVERIFY(canvas->rootObject() != 0);
1297     canvas->show();
1298     canvas->setFocus();
1299     QApplication::setActiveWindow(canvas);
1300     QTest::qWaitForWindowShown(canvas);
1301
1302     QDeclarative1TextInput *textinputObject = qobject_cast<QDeclarative1TextInput *>(canvas->rootObject());
1303     QVERIFY(textinputObject != 0);
1304
1305     // Check autoscrolled...
1306     QFontMetrics fm(textinputObject->font());
1307
1308     int pos = textinputObject->positionAt(textinputObject->width()/2);
1309     int diff = abs(int(fm.width(textinputObject->text()) - (fm.width(textinputObject->text().left(pos))+textinputObject->width()/2)));
1310
1311     // some tollerance for different fonts.
1312 #ifdef Q_OS_LINUX
1313     QVERIFY(diff < 2);
1314 #else
1315     QVERIFY(diff < 5);
1316 #endif
1317
1318     int x = textinputObject->positionToRectangle(pos + 1).x() - 1;
1319     QCOMPARE(textinputObject->positionAt(x, QDeclarative1TextInput::CursorBetweenCharacters), pos + 1);
1320     QCOMPARE(textinputObject->positionAt(x, QDeclarative1TextInput::CursorOnCharacter), pos);
1321
1322     // Check without autoscroll...
1323     textinputObject->setAutoScroll(false);
1324     pos = textinputObject->positionAt(textinputObject->width()/2);
1325     diff = abs(int(fm.width(textinputObject->text().left(pos))-textinputObject->width()/2));
1326
1327 #ifdef Q_WS_QPA
1328     QEXPECT_FAIL("", "QTBUG-21017 fails", Continue);
1329 #endif
1330     // some tollerance for different fonts.
1331 #ifdef Q_OS_LINUX
1332     QVERIFY(diff < 2);
1333 #else
1334     QVERIFY(diff < 5);
1335 #endif
1336
1337     x = textinputObject->positionToRectangle(pos + 1).x() - 1;
1338     QCOMPARE(textinputObject->positionAt(x, QDeclarative1TextInput::CursorBetweenCharacters), pos + 1);
1339     QCOMPARE(textinputObject->positionAt(x, QDeclarative1TextInput::CursorOnCharacter), pos);
1340
1341     const qreal x0 = textinputObject->positionToRectangle(pos).x();
1342     const qreal x1 = textinputObject->positionToRectangle(pos + 1).x();
1343
1344     QString preeditText = textinputObject->text().mid(0, pos);
1345     textinputObject->setText(textinputObject->text().mid(pos));
1346     textinputObject->setCursorPosition(0);
1347
1348     QInputMethodEvent inputEvent(preeditText, QList<QInputMethodEvent::Attribute>());
1349     QApplication::sendEvent(canvas, &inputEvent);
1350
1351     // Check all points within the preedit text return the same position.
1352     QCOMPARE(textinputObject->positionAt(0), 0);
1353     QCOMPARE(textinputObject->positionAt(x0 / 2), 0);
1354     QCOMPARE(textinputObject->positionAt(x0), 0);
1355
1356     // Verify positioning returns to normal after the preedit text.
1357     QCOMPARE(textinputObject->positionAt(x1), 1);
1358     QCOMPARE(textinputObject->positionToRectangle(1).x(), x1);
1359
1360     delete canvas;
1361 }
1362
1363 void tst_qdeclarativetextinput::maxLength()
1364 {
1365     QDeclarativeView *canvas = createView(SRCDIR "/data/maxLength.qml");
1366     QVERIFY(canvas->rootObject() != 0);
1367     canvas->show();
1368     canvas->setFocus();
1369     QApplication::setActiveWindow(canvas);
1370     QTest::qWaitForWindowShown(canvas);
1371
1372     QDeclarative1TextInput *textinputObject = qobject_cast<QDeclarative1TextInput *>(canvas->rootObject());
1373     QVERIFY(textinputObject != 0);
1374     QVERIFY(textinputObject->text().isEmpty());
1375     QVERIFY(textinputObject->maxLength() == 10);
1376     foreach(const QString &str, standard){
1377         QVERIFY(textinputObject->text().length() <= 10);
1378         textinputObject->setText(str);
1379         QVERIFY(textinputObject->text().length() <= 10);
1380     }
1381
1382     textinputObject->setText("");
1383     QTRY_VERIFY(textinputObject->hasActiveFocus() == true);
1384     for(int i=0; i<20; i++){
1385         QCOMPARE(textinputObject->text().length(), qMin(i,10));
1386         //simulateKey(canvas, Qt::Key_A);
1387         QTest::keyPress(canvas, Qt::Key_A);
1388         QTest::keyRelease(canvas, Qt::Key_A, Qt::NoModifier ,10);
1389     }
1390
1391     delete canvas;
1392 }
1393
1394 void tst_qdeclarativetextinput::masks()
1395 {
1396     //Not a comprehensive test of the possible masks, that's done elsewhere (QLineEdit)
1397     //QString componentStr = "import QtQuick 1.0\nTextInput {  inputMask: 'HHHHhhhh'; }";
1398     QDeclarativeView *canvas = createView(SRCDIR "/data/masks.qml");
1399     canvas->show();
1400     canvas->setFocus();
1401     QVERIFY(canvas->rootObject() != 0);
1402     QDeclarative1TextInput *textinputObject = qobject_cast<QDeclarative1TextInput *>(canvas->rootObject());
1403     QVERIFY(textinputObject != 0);
1404     QTRY_VERIFY(textinputObject->hasActiveFocus() == true);
1405     QVERIFY(textinputObject->text().length() == 0);
1406     QCOMPARE(textinputObject->inputMask(), QString("HHHHhhhh; "));
1407     for(int i=0; i<10; i++){
1408         QCOMPARE(qMin(i,8), textinputObject->text().length());
1409         QCOMPARE(i>=4, textinputObject->hasAcceptableInput());
1410         //simulateKey(canvas, Qt::Key_A);
1411         QTest::keyPress(canvas, Qt::Key_A);
1412         QTest::keyRelease(canvas, Qt::Key_A, Qt::NoModifier ,10);
1413     }
1414
1415     delete canvas;
1416 }
1417
1418 void tst_qdeclarativetextinput::validators()
1419 {
1420     // Note that this test assumes that the validators are working properly
1421     // so you may need to run their tests first. All validators are checked
1422     // here to ensure that their exposure to QML is working.
1423
1424     QDeclarativeView *canvas = createView(SRCDIR "/data/validators.qml");
1425     canvas->show();
1426     canvas->setFocus();
1427
1428     QVERIFY(canvas->rootObject() != 0);
1429
1430     QDeclarative1TextInput *intInput = qobject_cast<QDeclarative1TextInput *>(qvariant_cast<QObject *>(canvas->rootObject()->property("intInput")));
1431     QVERIFY(intInput);
1432     intInput->setFocus(true);
1433     QTRY_VERIFY(intInput->hasActiveFocus());
1434     QTest::keyPress(canvas, Qt::Key_1);
1435     QTest::keyRelease(canvas, Qt::Key_1, Qt::NoModifier ,10);
1436     QCOMPARE(intInput->text(), QLatin1String("1"));
1437     QCOMPARE(intInput->hasAcceptableInput(), false);
1438     QTest::keyPress(canvas, Qt::Key_2);
1439     QTest::keyRelease(canvas, Qt::Key_2, Qt::NoModifier ,10);
1440     QCOMPARE(intInput->text(), QLatin1String("1"));
1441     QCOMPARE(intInput->hasAcceptableInput(), false);
1442     QTest::keyPress(canvas, Qt::Key_1);
1443     QTest::keyRelease(canvas, Qt::Key_1, Qt::NoModifier ,10);
1444     QCOMPARE(intInput->text(), QLatin1String("11"));
1445     QCOMPARE(intInput->hasAcceptableInput(), true);
1446     QTest::keyPress(canvas, Qt::Key_0);
1447     QTest::keyRelease(canvas, Qt::Key_0, Qt::NoModifier ,10);
1448     QCOMPARE(intInput->text(), QLatin1String("11"));
1449     QCOMPARE(intInput->hasAcceptableInput(), true);
1450
1451     QDeclarative1TextInput *dblInput = qobject_cast<QDeclarative1TextInput *>(qvariant_cast<QObject *>(canvas->rootObject()->property("dblInput")));
1452     QTRY_VERIFY(dblInput);
1453     dblInput->setFocus(true);
1454     QVERIFY(dblInput->hasActiveFocus() == true);
1455     QTest::keyPress(canvas, Qt::Key_1);
1456     QTest::keyRelease(canvas, Qt::Key_1, Qt::NoModifier ,10);
1457     QCOMPARE(dblInput->text(), QLatin1String("1"));
1458     QCOMPARE(dblInput->hasAcceptableInput(), false);
1459     QTest::keyPress(canvas, Qt::Key_2);
1460     QTest::keyRelease(canvas, Qt::Key_2, Qt::NoModifier ,10);
1461     QCOMPARE(dblInput->text(), QLatin1String("12"));
1462     QCOMPARE(dblInput->hasAcceptableInput(), true);
1463     QTest::keyPress(canvas, Qt::Key_Period);
1464     QTest::keyRelease(canvas, Qt::Key_Period, Qt::NoModifier ,10);
1465     QCOMPARE(dblInput->text(), QLatin1String("12."));
1466     QCOMPARE(dblInput->hasAcceptableInput(), true);
1467     QTest::keyPress(canvas, Qt::Key_1);
1468     QTest::keyRelease(canvas, Qt::Key_1, Qt::NoModifier ,10);
1469     QCOMPARE(dblInput->text(), QLatin1String("12.1"));
1470     QCOMPARE(dblInput->hasAcceptableInput(), true);
1471     QTest::keyPress(canvas, Qt::Key_1);
1472     QTest::keyRelease(canvas, Qt::Key_1, Qt::NoModifier ,10);
1473     QCOMPARE(dblInput->text(), QLatin1String("12.11"));
1474     QCOMPARE(dblInput->hasAcceptableInput(), true);
1475     QTest::keyPress(canvas, Qt::Key_1);
1476     QTest::keyRelease(canvas, Qt::Key_1, Qt::NoModifier ,10);
1477     QCOMPARE(dblInput->text(), QLatin1String("12.11"));
1478     QCOMPARE(dblInput->hasAcceptableInput(), true);
1479
1480     QDeclarative1TextInput *strInput = qobject_cast<QDeclarative1TextInput *>(qvariant_cast<QObject *>(canvas->rootObject()->property("strInput")));
1481     QTRY_VERIFY(strInput);
1482     strInput->setFocus(true);
1483     QVERIFY(strInput->hasActiveFocus() == true);
1484     QTest::keyPress(canvas, Qt::Key_1);
1485     QTest::keyRelease(canvas, Qt::Key_1, Qt::NoModifier ,10);
1486     QCOMPARE(strInput->text(), QLatin1String(""));
1487     QCOMPARE(strInput->hasAcceptableInput(), false);
1488     QTest::keyPress(canvas, Qt::Key_A);
1489     QTest::keyRelease(canvas, Qt::Key_A, Qt::NoModifier ,10);
1490     QCOMPARE(strInput->text(), QLatin1String("a"));
1491     QCOMPARE(strInput->hasAcceptableInput(), false);
1492     QTest::keyPress(canvas, Qt::Key_A);
1493     QTest::keyRelease(canvas, Qt::Key_A, Qt::NoModifier ,10);
1494     QCOMPARE(strInput->text(), QLatin1String("aa"));
1495     QCOMPARE(strInput->hasAcceptableInput(), true);
1496     QTest::keyPress(canvas, Qt::Key_A);
1497     QTest::keyRelease(canvas, Qt::Key_A, Qt::NoModifier ,10);
1498     QCOMPARE(strInput->text(), QLatin1String("aaa"));
1499     QCOMPARE(strInput->hasAcceptableInput(), true);
1500     QTest::keyPress(canvas, Qt::Key_A);
1501     QTest::keyRelease(canvas, Qt::Key_A, Qt::NoModifier ,10);
1502     QCOMPARE(strInput->text(), QLatin1String("aaaa"));
1503     QCOMPARE(strInput->hasAcceptableInput(), true);
1504     QTest::keyPress(canvas, Qt::Key_A);
1505     QTest::keyRelease(canvas, Qt::Key_A, Qt::NoModifier ,10);
1506     QCOMPARE(strInput->text(), QLatin1String("aaaa"));
1507     QCOMPARE(strInput->hasAcceptableInput(), true);
1508
1509     delete canvas;
1510 }
1511
1512 void tst_qdeclarativetextinput::inputMethods()
1513 {
1514     QDeclarativeView *canvas = createView(SRCDIR "/data/inputmethods.qml");
1515     canvas->show();
1516     canvas->setFocus();
1517     QApplication::setActiveWindow(canvas);
1518     QTest::qWaitForWindowShown(canvas);
1519
1520     // test input method hints
1521     QVERIFY(canvas->rootObject() != 0);
1522     QDeclarative1TextInput *input = qobject_cast<QDeclarative1TextInput *>(canvas->rootObject());
1523     QVERIFY(input != 0);
1524     QVERIFY(input->imHints() & Qt::ImhNoPredictiveText);
1525     QVERIFY(input->inputMethodHints() & Qt::ImhNoPredictiveText);
1526     input->setIMHints(Qt::ImhUppercaseOnly);
1527     QVERIFY(input->imHints() & Qt::ImhUppercaseOnly);
1528     QVERIFY(input->inputMethodHints() & Qt::ImhUppercaseOnly);
1529
1530     QVERIFY(canvas->rootObject() != 0);
1531
1532     input->setFocus(true);
1533     QVERIFY(input->hasActiveFocus() == true);
1534     // test that input method event is committed
1535     QInputMethodEvent event;
1536     event.setCommitString( "My ", -12, 0);
1537     QApplication::sendEvent(canvas, &event);
1538     QCOMPARE(input->text(), QString("My Hello world!"));
1539
1540     input->setCursorPosition(2);
1541     event.setCommitString("Your", -2, 2);
1542     QApplication::sendEvent(canvas, &event);
1543     QCOMPARE(input->text(), QString("Your Hello world!"));
1544     QCOMPARE(input->cursorPosition(), 4);
1545
1546     input->setCursorPosition(7);
1547     event.setCommitString("Goodbye", -2, 5);
1548     QApplication::sendEvent(canvas, &event);
1549     QCOMPARE(input->text(), QString("Your Goodbye world!"));
1550     QCOMPARE(input->cursorPosition(), 12);
1551
1552     input->setCursorPosition(8);
1553     event.setCommitString("Our", -8, 4);
1554     QApplication::sendEvent(canvas, &event);
1555     QCOMPARE(input->text(), QString("Our Goodbye world!"));
1556     QCOMPARE(input->cursorPosition(), 7);
1557
1558     delete canvas;
1559 }
1560
1561 /*
1562 TextInput element should only handle left/right keys until the cursor reaches
1563 the extent of the text, then they should ignore the keys.
1564
1565 */
1566 void tst_qdeclarativetextinput::navigation()
1567 {
1568     QDeclarativeView *canvas = createView(SRCDIR "/data/navigation.qml");
1569     canvas->show();
1570     canvas->setFocus();
1571
1572     QVERIFY(canvas->rootObject() != 0);
1573
1574     QDeclarative1TextInput *input = qobject_cast<QDeclarative1TextInput *>(qvariant_cast<QObject *>(canvas->rootObject()->property("myInput")));
1575
1576     QVERIFY(input != 0);
1577     input->setCursorPosition(0);
1578     QTRY_VERIFY(input->hasActiveFocus() == true);
1579     simulateKey(canvas, Qt::Key_Left);
1580     QVERIFY(input->hasActiveFocus() == false);
1581     simulateKey(canvas, Qt::Key_Right);
1582     QVERIFY(input->hasActiveFocus() == true);
1583     //QT-2944: If text is selected, ensure we deselect upon cursor motion
1584     input->setCursorPosition(input->text().length());
1585     input->select(0,input->text().length());
1586     QVERIFY(input->selectionStart() != input->selectionEnd());
1587     simulateKey(canvas, Qt::Key_Right);
1588     QVERIFY(input->selectionStart() == input->selectionEnd());
1589     QVERIFY(input->selectionStart() == input->text().length());
1590     QVERIFY(input->hasActiveFocus() == true);
1591     simulateKey(canvas, Qt::Key_Right);
1592     QVERIFY(input->hasActiveFocus() == false);
1593     simulateKey(canvas, Qt::Key_Left);
1594     QVERIFY(input->hasActiveFocus() == true);
1595
1596     // Up and Down should NOT do Home/End, even on Mac OS X (QTBUG-10438).
1597     input->setCursorPosition(2);
1598     QCOMPARE(input->cursorPosition(),2);
1599     simulateKey(canvas, Qt::Key_Up);
1600     QCOMPARE(input->cursorPosition(),2);
1601     simulateKey(canvas, Qt::Key_Down);
1602     QCOMPARE(input->cursorPosition(),2);
1603
1604     delete canvas;
1605 }
1606
1607 void tst_qdeclarativetextinput::navigation_RTL()
1608 {
1609     QDeclarativeView *canvas = createView(SRCDIR "/data/navigation.qml");
1610     canvas->show();
1611     canvas->setFocus();
1612
1613     QVERIFY(canvas->rootObject() != 0);
1614
1615     QDeclarative1TextInput *input = qobject_cast<QDeclarative1TextInput *>(qvariant_cast<QObject *>(canvas->rootObject()->property("myInput")));
1616
1617     QVERIFY(input != 0);
1618     const quint16 arabic_str[] = { 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0647};
1619     input->setText(QString::fromUtf16(arabic_str, 11));
1620
1621     input->setCursorPosition(0);
1622     QTRY_VERIFY(input->hasActiveFocus() == true);
1623
1624     // move off
1625     simulateKey(canvas, Qt::Key_Right);
1626     QVERIFY(input->hasActiveFocus() == false);
1627
1628     // move back
1629     simulateKey(canvas, Qt::Key_Left);
1630     QVERIFY(input->hasActiveFocus() == true);
1631
1632     input->setCursorPosition(input->text().length());
1633     QVERIFY(input->hasActiveFocus() == true);
1634
1635     // move off
1636     simulateKey(canvas, Qt::Key_Left);
1637     QVERIFY(input->hasActiveFocus() == false);
1638
1639     // move back
1640     simulateKey(canvas, Qt::Key_Right);
1641     QVERIFY(input->hasActiveFocus() == true);
1642
1643     delete canvas;
1644 }
1645
1646 void tst_qdeclarativetextinput::copyAndPaste() {
1647 #ifndef QT_NO_CLIPBOARD
1648
1649 #ifdef Q_WS_MAC
1650     {
1651         PasteboardRef pasteboard;
1652         OSStatus status = PasteboardCreate(0, &pasteboard);
1653         if (status == noErr)
1654             CFRelease(pasteboard);
1655         else
1656             QSKIP("This machine doesn't support the clipboard", SkipAll);
1657     }
1658 #endif
1659
1660     QString componentStr = "import QtQuick 1.0\nTextInput { text: \"Hello world!\" }";
1661     QDeclarativeComponent textInputComponent(&engine);
1662     textInputComponent.setData(componentStr.toLatin1(), QUrl());
1663     QDeclarative1TextInput *textInput = qobject_cast<QDeclarative1TextInput*>(textInputComponent.create());
1664     QVERIFY(textInput != 0);
1665
1666     // copy and paste
1667     QCOMPARE(textInput->text().length(), 12);
1668     textInput->select(0, textInput->text().length());;
1669     textInput->copy();
1670     QCOMPARE(textInput->selectedText(), QString("Hello world!"));
1671     QCOMPARE(textInput->selectedText().length(), 12);
1672     textInput->setCursorPosition(0);
1673     QVERIFY(textInput->canPaste());
1674     textInput->paste();
1675     QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
1676     QCOMPARE(textInput->text().length(), 24);
1677
1678     // can paste
1679     QVERIFY(textInput->canPaste());
1680     textInput->setReadOnly(true);
1681     QVERIFY(!textInput->canPaste());
1682     textInput->setReadOnly(false);
1683     QVERIFY(textInput->canPaste());
1684
1685     // select word
1686     textInput->setCursorPosition(0);
1687     textInput->selectWord();
1688     QCOMPARE(textInput->selectedText(), QString("Hello"));
1689
1690     // select all and cut
1691     textInput->selectAll();
1692     textInput->cut();
1693     QCOMPARE(textInput->text().length(), 0);
1694     textInput->paste();
1695     QCOMPARE(textInput->text(), QString("Hello world!Hello world!"));
1696     QCOMPARE(textInput->text().length(), 24);
1697
1698     // clear copy buffer
1699     QClipboard *clipboard = QApplication::clipboard();
1700     QVERIFY(clipboard);
1701     clipboard->clear();
1702     QVERIFY(!textInput->canPaste());
1703
1704     // test that copy functionality is disabled
1705     // when echo mode is set to hide text/password mode
1706     int index = 0;
1707     while (index < 4) {
1708         QDeclarative1TextInput::EchoMode echoMode = QDeclarative1TextInput::EchoMode(index);
1709         textInput->setEchoMode(echoMode);
1710         textInput->setText("My password");
1711         textInput->select(0, textInput->text().length());;
1712         textInput->copy();
1713         if (echoMode == QDeclarative1TextInput::Normal) {
1714             QVERIFY(!clipboard->text().isEmpty());
1715             QCOMPARE(clipboard->text(), QString("My password"));
1716             clipboard->clear();
1717         } else {
1718             QVERIFY(clipboard->text().isEmpty());
1719         }
1720         index++;
1721     }
1722
1723     delete textInput;
1724 #endif
1725 }
1726
1727 void tst_qdeclarativetextinput::canPasteEmpty() {
1728 #ifndef QT_NO_CLIPBOARD
1729
1730     QApplication::clipboard()->clear();
1731
1732     QString componentStr = "import QtQuick 1.0\nTextInput { text: \"Hello world!\" }";
1733     QDeclarativeComponent textInputComponent(&engine);
1734     textInputComponent.setData(componentStr.toLatin1(), QUrl());
1735     QDeclarative1TextInput *textInput = qobject_cast<QDeclarative1TextInput*>(textInputComponent.create());
1736     QVERIFY(textInput != 0);
1737
1738     QLineControl lc;
1739     bool cp = !lc.isReadOnly() && QApplication::clipboard()->text().length() != 0;
1740     QCOMPARE(textInput->canPaste(), cp);
1741
1742 #endif
1743 }
1744
1745 void tst_qdeclarativetextinput::canPaste() {
1746 #ifndef QT_NO_CLIPBOARD
1747
1748     QApplication::clipboard()->setText("Some text");
1749
1750     QString componentStr = "import QtQuick 1.0\nTextInput { text: \"Hello world!\" }";
1751     QDeclarativeComponent textInputComponent(&engine);
1752     textInputComponent.setData(componentStr.toLatin1(), QUrl());
1753     QDeclarative1TextInput *textInput = qobject_cast<QDeclarative1TextInput*>(textInputComponent.create());
1754     QVERIFY(textInput != 0);
1755
1756     QLineControl lc;
1757     bool cp = !lc.isReadOnly() && QApplication::clipboard()->text().length() != 0;
1758     QCOMPARE(textInput->canPaste(), cp);
1759
1760 #endif
1761 }
1762
1763 void tst_qdeclarativetextinput::passwordCharacter()
1764 {
1765     QString componentStr = "import QtQuick 1.0\nTextInput { text: \"Hello world!\"; font.family: \"Helvetica\"; echoMode: TextInput.Password }";
1766     QDeclarativeComponent textInputComponent(&engine);
1767     textInputComponent.setData(componentStr.toLatin1(), QUrl());
1768     QDeclarative1TextInput *textInput = qobject_cast<QDeclarative1TextInput*>(textInputComponent.create());
1769     QVERIFY(textInput != 0);
1770
1771     textInput->setPasswordCharacter("X");
1772     QSize contentsSize = textInput->contentsSize();
1773     textInput->setPasswordCharacter(".");
1774     // QTBUG-12383 content is updated and redrawn
1775     QVERIFY(contentsSize != textInput->contentsSize());
1776
1777     delete textInput;
1778 }
1779
1780 void tst_qdeclarativetextinput::cursorDelegate()
1781 {
1782     QDeclarativeView* view = createView(SRCDIR "/data/cursorTest.qml");
1783     view->show();
1784     view->setFocus();
1785     QDeclarative1TextInput *textInputObject = view->rootObject()->findChild<QDeclarative1TextInput*>("textInputObject");
1786     QVERIFY(textInputObject != 0);
1787     QVERIFY(textInputObject->findChild<QDeclarativeItem*>("cursorInstance"));
1788     //Test Delegate gets created
1789     textInputObject->setFocus(true);
1790     QDeclarativeItem* delegateObject = textInputObject->findChild<QDeclarativeItem*>("cursorInstance");
1791     QVERIFY(delegateObject);
1792     //Test Delegate gets moved
1793     for(int i=0; i<= textInputObject->text().length(); i++){
1794         textInputObject->setCursorPosition(i);
1795         QCOMPARE(textInputObject->cursorRectangle().x(), qRound(delegateObject->x()));
1796         QCOMPARE(textInputObject->cursorRectangle().y(), qRound(delegateObject->y()));
1797     }
1798     const QString preedit = "preedit";
1799     for (int i = 0; i <= preedit.length(); i++) {
1800         QInputMethodEvent event(preedit, QList<QInputMethodEvent::Attribute>()
1801                 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, i, 1, QVariant()));
1802         QApplication::sendEvent(view, &event);
1803
1804         QCOMPARE(textInputObject->cursorRectangle().x(), qRound(delegateObject->x()));
1805         QCOMPARE(textInputObject->cursorRectangle().y(), qRound(delegateObject->y()));
1806     }
1807     textInputObject->setCursorPosition(0);
1808     QCOMPARE(textInputObject->cursorRectangle().x(), qRound(delegateObject->x()));
1809     QCOMPARE(textInputObject->cursorRectangle().y(), qRound(delegateObject->y()));
1810     //Test Delegate gets deleted
1811     textInputObject->setCursorDelegate(0);
1812     QVERIFY(!textInputObject->findChild<QDeclarativeItem*>("cursorInstance"));
1813
1814     delete view;
1815 }
1816
1817 void tst_qdeclarativetextinput::cursorVisible()
1818 {
1819     QGraphicsScene scene;
1820     QGraphicsView view(&scene);
1821     view.show();
1822     QApplication::setActiveWindow(&view);
1823     QTest::qWaitForWindowShown(&view);
1824     QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&view));
1825
1826     QDeclarative1TextInput input;
1827     QSignalSpy spy(&input, SIGNAL(cursorVisibleChanged(bool)));
1828
1829     QCOMPARE(input.isCursorVisible(), false);
1830
1831     input.setCursorVisible(true);
1832     QCOMPARE(input.isCursorVisible(), true);
1833     QCOMPARE(spy.count(), 1);
1834
1835     input.setCursorVisible(false);
1836     QCOMPARE(input.isCursorVisible(), false);
1837     QCOMPARE(spy.count(), 2);
1838
1839     input.setFocus(true);
1840     QCOMPARE(input.isCursorVisible(), false);
1841     QCOMPARE(spy.count(), 2);
1842
1843     scene.addItem(&input);
1844     QCOMPARE(input.isCursorVisible(), true);
1845     QCOMPARE(spy.count(), 3);
1846
1847     input.setFocus(false);
1848     QCOMPARE(input.isCursorVisible(), false);
1849     QCOMPARE(spy.count(), 4);
1850
1851     input.setFocus(true);
1852     QCOMPARE(input.isCursorVisible(), true);
1853     QCOMPARE(spy.count(), 5);
1854
1855     scene.clearFocus();
1856     QCOMPARE(input.isCursorVisible(), false);
1857     QCOMPARE(spy.count(), 6);
1858
1859     scene.setFocus();
1860     QCOMPARE(input.isCursorVisible(), true);
1861     QCOMPARE(spy.count(), 7);
1862
1863     view.clearFocus();
1864     QCOMPARE(input.isCursorVisible(), false);
1865     QCOMPARE(spy.count(), 8);
1866
1867     view.setFocus();
1868     QCOMPARE(input.isCursorVisible(), true);
1869     QCOMPARE(spy.count(), 9);
1870
1871     // on mac, setActiveWindow(0) on mac does not deactivate the current application
1872     // (you have to switch to a different app or hide the current app to trigger this)
1873 #if !defined(Q_WS_MAC)
1874     QApplication::setActiveWindow(0);
1875     QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(0));
1876     QCOMPARE(input.isCursorVisible(), false);
1877     QCOMPARE(spy.count(), 10);
1878
1879     QApplication::setActiveWindow(&view);
1880     QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&view));
1881     QCOMPARE(input.isCursorVisible(), true);
1882     QCOMPARE(spy.count(), 11);
1883 #endif
1884 }
1885
1886 void tst_qdeclarativetextinput::cursorRectangle()
1887 {
1888     QString text = "Hello World!";
1889
1890     QDeclarative1TextInput input;
1891     input.setText(text);
1892     QFontMetricsF fm(input.font());
1893     input.setWidth(fm.width(text.mid(0, 5)));
1894
1895     QRect r;
1896
1897     // some tolerance for different fonts.
1898 #ifdef Q_OS_LINUX
1899     const int error = 2;
1900 #else
1901     const int error = 5;
1902 #endif
1903
1904     for (int i = 0; i <= 5; ++i) {
1905         input.setCursorPosition(i);
1906         r = input.cursorRectangle();
1907         int textWidth = fm.width(text.mid(0, i));
1908
1909         QVERIFY(r.left() < textWidth + error);
1910         QVERIFY(r.right() > textWidth - error);
1911         QCOMPARE(input.inputMethodQuery(Qt::ImMicroFocus).toRect(), r);
1912     }
1913
1914     // Check the cursor rectangle remains within the input bounding rect when auto scrolling.
1915     QVERIFY(r.left() < input.boundingRect().width());
1916     QVERIFY(r.right() >= input.width() - error);
1917
1918     for (int i = 6; i < text.length(); ++i) {
1919         input.setCursorPosition(i);
1920         QCOMPARE(r, input.cursorRectangle());
1921         QCOMPARE(input.inputMethodQuery(Qt::ImMicroFocus).toRect(), r);
1922     }
1923
1924     for (int i = text.length() - 2; i >= 0; --i) {
1925         input.setCursorPosition(i);
1926         r = input.cursorRectangle();
1927         QVERIFY(r.right() >= 0);
1928         QCOMPARE(input.inputMethodQuery(Qt::ImMicroFocus).toRect(), r);
1929     }
1930 }
1931
1932 void tst_qdeclarativetextinput::readOnly()
1933 {
1934     QDeclarativeView *canvas = createView(SRCDIR "/data/readOnly.qml");
1935     canvas->show();
1936     canvas->setFocus();
1937
1938     QVERIFY(canvas->rootObject() != 0);
1939
1940     QDeclarative1TextInput *input = qobject_cast<QDeclarative1TextInput *>(qvariant_cast<QObject *>(canvas->rootObject()->property("myInput")));
1941
1942     QVERIFY(input != 0);
1943     QTRY_VERIFY(input->hasActiveFocus() == true);
1944     QVERIFY(input->isReadOnly() == true);
1945     QString initial = input->text();
1946     for(int k=Qt::Key_0; k<=Qt::Key_Z; k++)
1947         simulateKey(canvas, k);
1948     simulateKey(canvas, Qt::Key_Return);
1949     simulateKey(canvas, Qt::Key_Space);
1950     simulateKey(canvas, Qt::Key_Escape);
1951     QCOMPARE(input->text(), initial);
1952
1953     delete canvas;
1954 }
1955
1956 void tst_qdeclarativetextinput::echoMode()
1957 {
1958     QDeclarativeView *canvas = createView(SRCDIR "/data/echoMode.qml");
1959     canvas->show();
1960     canvas->setFocus();
1961     QApplication::setActiveWindow(canvas);
1962     QTest::qWaitForWindowShown(canvas);
1963     QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(canvas));
1964
1965     QVERIFY(canvas->rootObject() != 0);
1966
1967     QDeclarative1TextInput *input = qobject_cast<QDeclarative1TextInput *>(qvariant_cast<QObject *>(canvas->rootObject()->property("myInput")));
1968
1969     QVERIFY(input != 0);
1970     QTRY_VERIFY(input->hasActiveFocus() == true);
1971     QString initial = input->text();
1972     Qt::InputMethodHints ref;
1973     QCOMPARE(initial, QLatin1String("ABCDefgh"));
1974     QCOMPARE(input->echoMode(), QDeclarative1TextInput::Normal);
1975     QCOMPARE(input->displayText(), input->text());
1976     //Normal
1977     ref &= ~Qt::ImhHiddenText;
1978     ref &= ~(Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText);
1979     QCOMPARE(input->inputMethodHints(), ref);
1980     QCOMPARE(input->imHints(), Qt::ImhNone);
1981     input->setEchoMode(QDeclarative1TextInput::NoEcho);
1982     QCOMPARE(input->text(), initial);
1983     QCOMPARE(input->displayText(), QLatin1String(""));
1984     QCOMPARE(input->passwordCharacter(), QLatin1String("*"));
1985     //NoEcho
1986     ref |= Qt::ImhHiddenText;
1987     ref |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText);
1988     QCOMPARE(input->inputMethodHints(), ref);
1989     QCOMPARE(input->imHints(), Qt::ImhNone);
1990     input->setEchoMode(QDeclarative1TextInput::Password);
1991     //Password
1992     ref |= Qt::ImhHiddenText;
1993     ref |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText);
1994     QCOMPARE(input->text(), initial);
1995     QCOMPARE(input->displayText(), QLatin1String("********"));
1996     QCOMPARE(input->inputMethodHints(), ref);
1997     QCOMPARE(input->imHints(), Qt::ImhNone);
1998     input->setPasswordCharacter(QChar('Q'));
1999     QCOMPARE(input->passwordCharacter(), QLatin1String("Q"));
2000     QCOMPARE(input->text(), initial);
2001     QCOMPARE(input->displayText(), QLatin1String("QQQQQQQQ"));
2002     input->setEchoMode(QDeclarative1TextInput::PasswordEchoOnEdit);
2003     //PasswordEchoOnEdit
2004     ref &= ~Qt::ImhHiddenText;
2005     ref |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText);
2006     QCOMPARE(input->inputMethodHints(), ref);
2007     QCOMPARE(input->imHints(), Qt::ImhNone);
2008     QCOMPARE(input->text(), initial);
2009     QCOMPARE(input->displayText(), QLatin1String("QQQQQQQQ"));
2010     QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), QLatin1String("QQQQQQQQ"));
2011     QTest::keyPress(canvas, Qt::Key_A);//Clearing previous entry is part of PasswordEchoOnEdit
2012     QTest::keyRelease(canvas, Qt::Key_A, Qt::NoModifier ,10);
2013     QCOMPARE(input->text(), QLatin1String("a"));
2014     QCOMPARE(input->displayText(), QLatin1String("a"));
2015     QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), QLatin1String("a"));
2016     input->setFocus(false);
2017     QVERIFY(input->hasActiveFocus() == false);
2018     QCOMPARE(input->displayText(), QLatin1String("Q"));
2019     QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), QLatin1String("Q"));
2020     input->setFocus(true);
2021     QInputMethodEvent inputEvent;
2022     inputEvent.setCommitString(initial);
2023     QApplication::sendEvent(canvas, &inputEvent);
2024     QCOMPARE(input->text(), initial);
2025     QCOMPARE(input->displayText(), initial);
2026     QCOMPARE(input->inputMethodQuery(Qt::ImSurroundingText).toString(), initial);
2027
2028     // Test echo mode doesn't override imHints.
2029     input->setIMHints(Qt::ImhHiddenText | Qt::ImhDialableCharactersOnly);
2030     ref |=  Qt::ImhDialableCharactersOnly;
2031     //Normal
2032     input->setEchoMode(QDeclarative1TextInput::Normal);
2033     ref |= Qt::ImhHiddenText;
2034     ref &= ~(Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText);
2035     QCOMPARE(input->inputMethodHints(), ref);
2036     QCOMPARE(input->imHints(), Qt::ImhHiddenText | Qt::ImhDialableCharactersOnly);
2037     //NoEcho
2038     input->setEchoMode(QDeclarative1TextInput::NoEcho);
2039     ref |= Qt::ImhHiddenText;
2040     ref |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText);
2041     QCOMPARE(input->inputMethodHints(), ref);
2042     QCOMPARE(input->imHints(), Qt::ImhHiddenText | Qt::ImhDialableCharactersOnly);
2043     //Password
2044     input->setEchoMode(QDeclarative1TextInput::Password);
2045     ref |= Qt::ImhHiddenText;
2046     ref |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText);
2047     QCOMPARE(input->inputMethodHints(), ref);
2048     QCOMPARE(input->imHints(), Qt::ImhHiddenText | Qt::ImhDialableCharactersOnly);
2049     //PasswordEchoOnEdit
2050     input->setEchoMode(QDeclarative1TextInput::PasswordEchoOnEdit);
2051     ref &= ~Qt::ImhHiddenText;
2052     ref |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText);
2053     QCOMPARE(input->inputMethodHints(), ref);
2054     QCOMPARE(input->imHints(), Qt::ImhHiddenText | Qt::ImhDialableCharactersOnly);
2055     //Normal
2056     input->setEchoMode(QDeclarative1TextInput::Normal);
2057     ref |= Qt::ImhHiddenText;
2058     ref &= ~(Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText);
2059     QCOMPARE(input->inputMethodHints(), ref);
2060     QCOMPARE(input->imHints(), Qt::ImhHiddenText | Qt::ImhDialableCharactersOnly);
2061
2062     delete canvas;
2063 }
2064
2065 void tst_qdeclarativetextinput::simulateKey(QDeclarativeView *view, int key)
2066 {
2067     QKeyEvent press(QKeyEvent::KeyPress, key, 0);
2068     QKeyEvent release(QKeyEvent::KeyRelease, key, 0);
2069
2070     QApplication::sendEvent(view, &press);
2071     QApplication::sendEvent(view, &release);
2072 }
2073
2074 QDeclarativeView *tst_qdeclarativetextinput::createView(const QString &filename)
2075 {
2076     QDeclarativeView *canvas = new QDeclarativeView(0);
2077
2078     canvas->setSource(QUrl::fromLocalFile(filename));
2079
2080     return canvas;
2081 }
2082 class MyInputContext : public QInputContext
2083 {
2084 public:
2085     MyInputContext() : openInputPanelReceived(false), closeInputPanelReceived(false), updateReceived(false), eventType(QEvent::None) {}
2086     ~MyInputContext() {}
2087
2088     QString identifierName() { return QString(); }
2089     QString language() { return QString(); }
2090
2091     void reset() {}
2092
2093     bool isComposing() const { return false; }
2094
2095     bool filterEvent( const QEvent *event )
2096     {
2097         if (event->type() == QEvent::RequestSoftwareInputPanel)
2098             openInputPanelReceived = true;
2099         if (event->type() == QEvent::CloseSoftwareInputPanel)
2100             closeInputPanelReceived = true;
2101         return QInputContext::filterEvent(event);
2102     }
2103
2104     void update() { updateReceived = true; }
2105
2106     void mouseHandler(int x, QMouseEvent *event)
2107     {
2108         cursor = x;
2109         eventType = event->type();
2110         eventPosition = event->pos();
2111         eventGlobalPosition = event->globalPos();
2112         eventButton = event->button();
2113         eventButtons = event->buttons();
2114         eventModifiers = event->modifiers();
2115     }
2116
2117     void sendPreeditText(const QString &text, int cursor)
2118     {
2119         QList<QInputMethodEvent::Attribute> attributes;
2120         attributes.append(QInputMethodEvent::Attribute(
2121                 QInputMethodEvent::Cursor, cursor, text.length(), QVariant()));
2122
2123         QInputMethodEvent event(text, attributes);
2124         sendEvent(event);
2125     }
2126
2127     bool openInputPanelReceived;
2128     bool closeInputPanelReceived;
2129     bool updateReceived;
2130     int cursor;
2131     QEvent::Type eventType;
2132     QPoint eventPosition;
2133     QPoint eventGlobalPosition;
2134     Qt::MouseButton eventButton;
2135     Qt::MouseButtons eventButtons;
2136     Qt::KeyboardModifiers eventModifiers;
2137 };
2138
2139 void tst_qdeclarativetextinput::openInputPanelOnClick()
2140 {
2141     QGraphicsScene scene;
2142     QGraphicsView view(&scene);
2143     MyInputContext ic;
2144     view.setInputContext(&ic);
2145     QDeclarative1TextInput input;
2146     QSignalSpy focusOnPressSpy(&input, SIGNAL(activeFocusOnPressChanged(bool)));
2147     input.setText("Hello world");
2148     input.setPos(0, 0);
2149     scene.addItem(&input);
2150     view.show();
2151     qApp->setAutoSipEnabled(true);
2152     QApplication::setActiveWindow(&view);
2153     QTest::qWaitForWindowShown(&view);
2154     QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&view));
2155
2156     QDeclarativeItemPrivate* pri = QDeclarativeItemPrivate::get(&input);
2157     QDeclarative1TextInputPrivate *inputPrivate = static_cast<QDeclarative1TextInputPrivate*>(pri);
2158
2159     // input panel on click
2160     inputPrivate->showInputPanelOnFocus = false;
2161
2162     QStyle::RequestSoftwareInputPanel behavior = QStyle::RequestSoftwareInputPanel(
2163             view.style()->styleHint(QStyle::SH_RequestSoftwareInputPanel));
2164     QTest::mouseClick(view.viewport(), Qt::LeftButton, 0, view.mapFromScene(input.scenePos()));
2165     QApplication::processEvents();
2166     if (behavior == QStyle::RSIP_OnMouseClickAndAlreadyFocused) {
2167         QCOMPARE(ic.openInputPanelReceived, false);
2168         QTest::mouseClick(view.viewport(), Qt::LeftButton, 0, view.mapFromScene(input.scenePos()));
2169         QApplication::processEvents();
2170         QCOMPARE(ic.openInputPanelReceived, true);
2171     } else if (behavior == QStyle::RSIP_OnMouseClick) {
2172         QCOMPARE(ic.openInputPanelReceived, true);
2173     }
2174     ic.openInputPanelReceived = false;
2175
2176     // focus should not cause input panels to open or close
2177     input.setFocus(false);
2178     input.setFocus(true);
2179     input.setFocus(false);
2180     input.setFocus(true);
2181     input.setFocus(false);
2182     QCOMPARE(ic.openInputPanelReceived, false);
2183     QCOMPARE(ic.closeInputPanelReceived, false);
2184 }
2185
2186 void tst_qdeclarativetextinput::openInputPanelOnFocus()
2187 {
2188     QGraphicsScene scene;
2189     QGraphicsView view(&scene);
2190     MyInputContext ic;
2191     view.setInputContext(&ic);
2192     QDeclarative1TextInput input;
2193     QSignalSpy focusOnPressSpy(&input, SIGNAL(activeFocusOnPressChanged(bool)));
2194     input.setText("Hello world");
2195     input.setPos(0, 0);
2196     scene.addItem(&input);
2197     view.show();
2198     qApp->setAutoSipEnabled(true);
2199     QApplication::setActiveWindow(&view);
2200     QTest::qWaitForWindowShown(&view);
2201     QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&view));
2202
2203     QDeclarativeItemPrivate* pri = QDeclarativeItemPrivate::get(&input);
2204     QDeclarative1TextInputPrivate *inputPrivate = static_cast<QDeclarative1TextInputPrivate*>(pri);
2205     inputPrivate->showInputPanelOnFocus = true;
2206
2207     // test default values
2208     QVERIFY(input.focusOnPress());
2209     QCOMPARE(ic.openInputPanelReceived, false);
2210     QCOMPARE(ic.closeInputPanelReceived, false);
2211
2212     // focus on press, input panel on focus
2213     QTest::mousePress(view.viewport(), Qt::LeftButton, 0, view.mapFromScene(input.scenePos()));
2214     QApplication::processEvents();
2215     QVERIFY(input.hasActiveFocus());
2216     QCOMPARE(ic.openInputPanelReceived, true);
2217     ic.openInputPanelReceived = false;
2218
2219     // no events on release
2220     QTest::mouseRelease(view.viewport(), Qt::LeftButton, 0, view.mapFromScene(input.scenePos()));
2221     QCOMPARE(ic.openInputPanelReceived, false);
2222     ic.openInputPanelReceived = false;
2223
2224     // if already focused, input panel can be opened on press
2225     QVERIFY(input.hasActiveFocus());
2226     QTest::mousePress(view.viewport(), Qt::LeftButton, 0, view.mapFromScene(input.scenePos()));
2227     QApplication::processEvents();
2228     QCOMPARE(ic.openInputPanelReceived, true);
2229     ic.openInputPanelReceived = false;
2230
2231     // input method should stay enabled if focus
2232     // is lost to an item that also accepts inputs
2233     QDeclarative1TextInput anotherInput;
2234     scene.addItem(&anotherInput);
2235     anotherInput.setFocus(true);
2236     QApplication::processEvents();
2237     QCOMPARE(ic.openInputPanelReceived, true);
2238     ic.openInputPanelReceived = false;
2239     QCOMPARE(view.inputContext(), (QInputContext*)&ic);
2240     QVERIFY(view.testAttribute(Qt::WA_InputMethodEnabled));
2241
2242     // input method should be disabled if focus
2243     // is lost to an item that doesn't accept inputs
2244     QDeclarativeItem item;
2245     scene.addItem(&item);
2246     item.setFocus(true);
2247     QApplication::processEvents();
2248     QCOMPARE(ic.openInputPanelReceived, false);
2249     QVERIFY(view.inputContext() == 0);
2250     QVERIFY(!view.testAttribute(Qt::WA_InputMethodEnabled));
2251
2252     // no automatic input panel events should
2253     // be sent if activeFocusOnPress is false
2254     input.setFocusOnPress(false);
2255     QCOMPARE(focusOnPressSpy.count(),1);
2256     input.setFocusOnPress(false);
2257     QCOMPARE(focusOnPressSpy.count(),1);
2258     input.setFocus(false);
2259     input.setFocus(true);
2260     QTest::mousePress(view.viewport(), Qt::LeftButton, 0, view.mapFromScene(input.scenePos()));
2261     QTest::mouseRelease(view.viewport(), Qt::LeftButton, 0, view.mapFromScene(input.scenePos()));
2262     QApplication::processEvents();
2263     QCOMPARE(ic.openInputPanelReceived, false);
2264     QCOMPARE(ic.closeInputPanelReceived, false);
2265
2266     // one show input panel event should
2267     // be set when openSoftwareInputPanel is called
2268     input.openSoftwareInputPanel();
2269     QCOMPARE(ic.openInputPanelReceived, true);
2270     QCOMPARE(ic.closeInputPanelReceived, false);
2271     ic.openInputPanelReceived = false;
2272
2273     // one close input panel event should
2274     // be sent when closeSoftwareInputPanel is called
2275     input.closeSoftwareInputPanel();
2276     QCOMPARE(ic.openInputPanelReceived, false);
2277     QCOMPARE(ic.closeInputPanelReceived, true);
2278     ic.closeInputPanelReceived = false;
2279
2280     // set activeFocusOnPress back to true
2281     input.setFocusOnPress(true);
2282     QCOMPARE(focusOnPressSpy.count(),2);
2283     input.setFocusOnPress(true);
2284     QCOMPARE(focusOnPressSpy.count(),2);
2285     input.setFocus(false);
2286     QApplication::processEvents();
2287     QCOMPARE(ic.openInputPanelReceived, false);
2288     QCOMPARE(ic.closeInputPanelReceived, false);
2289     ic.closeInputPanelReceived = false;
2290
2291     // input panel should not re-open
2292     // if focus has already been set
2293     input.setFocus(true);
2294     QCOMPARE(ic.openInputPanelReceived, true);
2295     ic.openInputPanelReceived = false;
2296     input.setFocus(true);
2297     QCOMPARE(ic.openInputPanelReceived, false);
2298
2299     // input method should be disabled
2300     // if TextInput loses focus
2301     input.setFocus(false);
2302     QApplication::processEvents();
2303     QVERIFY(view.inputContext() == 0);
2304     QVERIFY(!view.testAttribute(Qt::WA_InputMethodEnabled));
2305
2306     // input method should not be enabled
2307     // if TextEdit is read only.
2308     input.setReadOnly(true);
2309     ic.openInputPanelReceived = false;
2310     input.setFocus(true);
2311     QApplication::processEvents();
2312     QCOMPARE(ic.openInputPanelReceived, false);
2313     QVERIFY(view.inputContext() == 0);
2314     QVERIFY(!view.testAttribute(Qt::WA_InputMethodEnabled));
2315 }
2316
2317 class MyTextInput : public QDeclarative1TextInput
2318 {
2319 public:
2320     MyTextInput(QDeclarativeItem *parent = 0) : QDeclarative1TextInput(parent)
2321     {
2322         nbPaint = 0;
2323     }
2324     void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
2325     {
2326        nbPaint++;
2327        QDeclarative1TextInput::paint(painter, option, widget);
2328     }
2329     int nbPaint;
2330 };
2331
2332 void tst_qdeclarativetextinput::setHAlignClearCache()
2333 {
2334     QGraphicsScene scene;
2335     QGraphicsView view(&scene);
2336     MyTextInput input;
2337     input.setText("Hello world");
2338     scene.addItem(&input);
2339     view.show();
2340     QApplication::setActiveWindow(&view);
2341     QTest::qWaitForWindowShown(&view);
2342 #ifdef Q_WS_QPA
2343     QEXPECT_FAIL("", "QTBUG-21017 fails", Abort);
2344 #endif
2345     QTRY_COMPARE(input.nbPaint, 1);
2346     input.setHAlign(QDeclarative1TextInput::AlignRight);
2347     QApplication::processEvents();
2348     //Changing the alignment should trigger a repaint
2349     QCOMPARE(input.nbPaint, 2);
2350 }
2351
2352 void tst_qdeclarativetextinput::focusOutClearSelection()
2353 {
2354     QGraphicsScene scene;
2355     QGraphicsView view(&scene);
2356     QDeclarative1TextInput input;
2357     QDeclarative1TextInput input2;
2358     input.setText(QLatin1String("Hello world"));
2359     input.setFocus(true);
2360     scene.addItem(&input2);
2361     scene.addItem(&input);
2362     view.show();
2363     QApplication::setActiveWindow(&view);
2364     QTest::qWaitForWindowShown(&view);
2365     input.select(2,5);
2366     //The selection should work
2367     QTRY_COMPARE(input.selectedText(), QLatin1String("llo"));
2368     input2.setFocus(true);
2369     QApplication::processEvents();
2370     //The input lost the focus selection should be cleared
2371     QTRY_COMPARE(input.selectedText(), QLatin1String(""));
2372 }
2373
2374 void tst_qdeclarativetextinput::geometrySignals()
2375 {
2376     QDeclarativeComponent component(&engine, SRCDIR "/data/geometrySignals.qml");
2377     QObject *o = component.create();
2378     QVERIFY(o);
2379     QCOMPARE(o->property("bindingWidth").toInt(), 400);
2380     QCOMPARE(o->property("bindingHeight").toInt(), 500);
2381     delete o;
2382 }
2383
2384 void tst_qdeclarativetextinput::testQtQuick11Attributes()
2385 {
2386     QFETCH(QString, code);
2387     QFETCH(QString, warning);
2388     QFETCH(QString, error);
2389
2390     QDeclarativeEngine engine;
2391     QObject *obj;
2392
2393     QDeclarativeComponent valid(&engine);
2394     valid.setData("import QtQuick 1.1; TextInput { " + code.toUtf8() + " }", QUrl(""));
2395     obj = valid.create();
2396     QVERIFY(obj);
2397     QVERIFY(valid.errorString().isEmpty());
2398     delete obj;
2399
2400     QDeclarativeComponent invalid(&engine);
2401     invalid.setData("import QtQuick 1.0; TextInput { " + code.toUtf8() + " }", QUrl(""));
2402     QTest::ignoreMessage(QtWarningMsg, warning.toUtf8());
2403     obj = invalid.create();
2404     QCOMPARE(invalid.errorString(), error);
2405     delete obj;
2406 }
2407
2408 void tst_qdeclarativetextinput::testQtQuick11Attributes_data()
2409 {
2410     QTest::addColumn<QString>("code");
2411     QTest::addColumn<QString>("warning");
2412     QTest::addColumn<QString>("error");
2413
2414     QTest::newRow("canPaste") << "property bool foo: canPaste"
2415         << "<Unknown File>:1: ReferenceError: Can't find variable: canPaste"
2416         << "";
2417
2418     QTest::newRow("moveCursorSelection") << "Component.onCompleted: moveCursorSelection(0, TextEdit.SelectCharacters)"
2419         << "<Unknown File>:1: ReferenceError: Can't find variable: moveCursorSelection"
2420         << "";
2421
2422     QTest::newRow("deselect") << "Component.onCompleted: deselect()"
2423         << "<Unknown File>:1: ReferenceError: Can't find variable: deselect"
2424         << "";
2425 }
2426
2427 void tst_qdeclarativetextinput::preeditAutoScroll()
2428 {
2429     QString committedText = "super";
2430     QString preeditText = "califragisiticexpialidocious!";
2431
2432     QGraphicsScene scene;
2433     QGraphicsView view(&scene);
2434     MyInputContext ic;
2435     view.setInputContext(&ic);
2436     QDeclarative1TextInput input;
2437     QFontMetricsF fm(input.font());
2438     input.setWidth(fm.width(committedText));
2439     input.setText(committedText);
2440     input.setPos(0, 0);
2441     input.setFocus(true);
2442     scene.addItem(&input);
2443     view.show();
2444     QApplication::setActiveWindow(&view);
2445     QTest::qWaitForWindowShown(&view);
2446     QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&view));
2447
2448     QSignalSpy cursorRectangleSpy(&input, SIGNAL(cursorRectangleChanged()));
2449     int cursorRectangleChanges = 0;
2450
2451     // test the text is scrolled so the preedit is visible.
2452     ic.sendPreeditText(preeditText.mid(0, 3), 1);
2453 #ifdef Q_WS_QPA
2454     QEXPECT_FAIL("", "QTBUG-21017 fails", Abort);
2455 #endif
2456     QVERIFY(input.positionAt(0) != 0);
2457     QVERIFY(input.cursorRectangle().left() < input.boundingRect().width());
2458     QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2459
2460     // test the text is scrolled back when the preedit is removed.
2461     ic.sendEvent(QInputMethodEvent());
2462     QCOMPARE(input.positionAt(0), 0);
2463     QCOMPARE(input.positionAt(input.width()), 5);
2464     QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2465
2466     // some tolerance for different fonts.
2467 #ifdef Q_OS_LINUX
2468     const int error = 2;
2469 #else
2470     const int error = 5;
2471 #endif
2472
2473     // test if the preedit is larger than the text input that the
2474     // character preceding the cursor is still visible.
2475     qreal x = input.positionToRectangle(0).x();
2476     for (int i = 0; i < 3; ++i) {
2477         ic.sendPreeditText(preeditText, i + 1);
2478         QVERIFY(input.cursorRectangle().right() >= fm.width(preeditText.at(i)) - error);
2479         QVERIFY(input.positionToRectangle(0).x() < x);
2480         QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2481         x = input.positionToRectangle(0).x();
2482     }
2483     for (int i = 1; i >= 0; --i) {
2484         ic.sendPreeditText(preeditText, i + 1);
2485         QVERIFY(input.cursorRectangle().right() >= fm.width(preeditText.at(i)) - error);
2486         QVERIFY(input.positionToRectangle(0).x() > x);
2487         QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2488         x = input.positionToRectangle(0).x();
2489     }
2490
2491     // Test incrementing the preedit cursor doesn't cause further
2492     // scrolling when right most text is visible.
2493     ic.sendPreeditText(preeditText, preeditText.length() - 3);
2494     QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2495     x = input.positionToRectangle(0).x();
2496     for (int i = 2; i >= 0; --i) {
2497         ic.sendPreeditText(preeditText, preeditText.length() - i);
2498         QCOMPARE(input.positionToRectangle(0).x(), x);
2499         QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2500     }
2501     for (int i = 1; i <  3; ++i) {
2502         ic.sendPreeditText(preeditText, preeditText.length() - i);
2503         QCOMPARE(input.positionToRectangle(0).x(), x);
2504         QCOMPARE(cursorRectangleSpy.count(), ++cursorRectangleChanges);
2505     }
2506
2507     // Test disabling auto scroll.
2508     ic.sendEvent(QInputMethodEvent());
2509
2510     input.setAutoScroll(false);
2511     ic.sendPreeditText(preeditText.mid(0, 3), 1);
2512     QCOMPARE(input.positionAt(0), 0);
2513     QCOMPARE(input.positionAt(input.width()), 5);
2514
2515     ic.sendEvent(QInputMethodEvent());
2516     input.setAutoScroll(true);
2517     // Test committing pre-edit text at the start of the string. QTBUG-18789
2518     input.setCursorPosition(0);
2519     ic.sendPreeditText(input.text(), 5);
2520     QCOMPARE(input.positionAt(0), 0);
2521
2522     QInputMethodEvent event;
2523     event.setCommitString(input.text());
2524     ic.sendEvent(event);
2525
2526     QCOMPARE(input.positionAt(0), 0);
2527     QCOMPARE(input.positionAt(input.width()), 5);
2528 }
2529
2530 void tst_qdeclarativetextinput::preeditMicroFocus()
2531 {
2532     QString preeditText = "super";
2533
2534     QGraphicsScene scene;
2535     QGraphicsView view(&scene);
2536     MyInputContext ic;
2537     view.setInputContext(&ic);
2538     QDeclarative1TextInput input;
2539     input.setPos(0, 0);
2540     input.setAutoScroll(false);
2541     input.setFocus(true);
2542     scene.addItem(&input);
2543     view.show();
2544     QApplication::setActiveWindow(&view);
2545     QTest::qWaitForWindowShown(&view);
2546     QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&view));
2547
2548     QRect currentRect;
2549     QRect previousRect = input.inputMethodQuery(Qt::ImMicroFocus).toRect();
2550
2551     // Verify that the micro focus rect is positioned the same for position 0 as
2552     // it would be if there was no preedit text.
2553     ic.updateReceived = false;
2554     ic.sendPreeditText(preeditText, 0);
2555     currentRect = input.inputMethodQuery(Qt::ImMicroFocus).toRect();
2556     QCOMPARE(currentRect, previousRect);
2557 #if defined(Q_WS_X11) || defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN)
2558     QCOMPARE(ic.updateReceived, true);
2559 #endif
2560
2561     // Verify that the micro focus rect moves to the left as the cursor position
2562     // is incremented.
2563     for (int i = 1; i <= 5; ++i) {
2564         ic.updateReceived = false;
2565         ic.sendPreeditText(preeditText, i);
2566         currentRect = input.inputMethodQuery(Qt::ImMicroFocus).toRect();
2567         QVERIFY(previousRect.left() < currentRect.left());
2568 #if defined(Q_WS_X11) || defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN)
2569         QCOMPARE(ic.updateReceived, true);
2570 #endif
2571         previousRect = currentRect;
2572     }
2573
2574     // Verify that if there is no preedit cursor then the micro focus rect is the
2575     // same as it would be if it were positioned at the end of the preedit text.
2576     ic.sendPreeditText(preeditText, 0);
2577     ic.updateReceived = false;
2578     ic.sendEvent(QInputMethodEvent(preeditText, QList<QInputMethodEvent::Attribute>()));
2579     currentRect = input.inputMethodQuery(Qt::ImMicroFocus).toRect();
2580     QCOMPARE(currentRect, previousRect);
2581 #if defined(Q_WS_X11) || defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN)
2582     QCOMPARE(ic.updateReceived, true);
2583 #endif
2584 }
2585
2586 void tst_qdeclarativetextinput::inputContextMouseHandler()
2587 {
2588     QString text = "supercalifragisiticexpialidocious!";
2589
2590     QGraphicsScene scene;
2591     QGraphicsView view(&scene);
2592     MyInputContext ic;
2593     view.setInputContext(&ic);
2594     QDeclarative1TextInput input;
2595     input.setWidth(200);
2596     input.setText(text.mid(0, 12));
2597     input.setCursorPosition(12);
2598     input.setPos(0, 0);
2599     input.setFocus(true);
2600     scene.addItem(&input);
2601     view.show();
2602     QApplication::setActiveWindow(&view);
2603     QTest::qWaitForWindowShown(&view);
2604     QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&view));
2605
2606     QFontMetricsF fm(input.font());
2607     const qreal y = fm.height() / 2;
2608
2609     QPoint position2 = view.mapFromScene(input.mapToScene(QPointF(fm.width(text.mid(0, 2)), y)));
2610     QPoint position8 = view.mapFromScene(input.mapToScene(QPointF(fm.width(text.mid(0, 8)), y)));
2611     QPoint position20 = view.mapFromScene(input.mapToScene(QPointF(fm.width(text.mid(0, 20)), y)));
2612     QPoint position27 = view.mapFromScene(input.mapToScene(QPointF(fm.width(text.mid(0, 27)), y)));
2613     QPoint globalPosition2 = view.viewport()->mapToGlobal(position2);
2614     QPoint globalposition8 = view.viewport()->mapToGlobal(position8);
2615     QPoint globalposition20 = view.viewport()->mapToGlobal(position20);
2616     QPoint globalposition27 = view.viewport()->mapToGlobal(position27);
2617
2618     ic.sendEvent(QInputMethodEvent(text.mid(12), QList<QInputMethodEvent::Attribute>()));
2619
2620     QTest::mouseDClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, position2);
2621     QCOMPARE(ic.eventType, QEvent::MouseButtonDblClick);
2622     QCOMPARE(ic.eventPosition, position2);
2623     QCOMPARE(ic.eventGlobalPosition, globalPosition2);
2624     QCOMPARE(ic.eventButton, Qt::LeftButton);
2625     QCOMPARE(ic.eventModifiers, Qt::NoModifier);
2626     QVERIFY(ic.cursor < 0);
2627     ic.eventType = QEvent::None;
2628
2629     QTest::mousePress(view.viewport(), Qt::LeftButton, Qt::NoModifier, position2);
2630     QCOMPARE(ic.eventType, QEvent::MouseButtonPress);
2631     QCOMPARE(ic.eventPosition, position2);
2632     QCOMPARE(ic.eventGlobalPosition, globalPosition2);
2633     QCOMPARE(ic.eventButton, Qt::LeftButton);
2634     QCOMPARE(ic.eventModifiers, Qt::NoModifier);
2635     QVERIFY(ic.cursor < 0);
2636     ic.eventType = QEvent::None;
2637
2638     {   QMouseEvent mv(QEvent::MouseMove, position8, globalposition8, Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
2639         QApplication::sendEvent(view.viewport(), &mv); }
2640     QCOMPARE(ic.eventType, QEvent::None);
2641
2642     {   QMouseEvent mv(QEvent::MouseMove, position27, globalposition27, Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
2643         QApplication::sendEvent(view.viewport(), &mv); }
2644     QCOMPARE(ic.eventType, QEvent::MouseMove);
2645     QCOMPARE(ic.eventPosition, position27);
2646     QCOMPARE(ic.eventGlobalPosition, globalposition27);
2647     QCOMPARE(ic.eventButton, Qt::LeftButton);
2648     QCOMPARE(ic.eventModifiers, Qt::NoModifier);
2649     QVERIFY(ic.cursor >= 14 && ic.cursor <= 16);    // 15 is expected but some platforms may be off by one.
2650     ic.eventType = QEvent::None;
2651
2652     QTest::mouseRelease(view.viewport(), Qt::LeftButton, Qt::NoModifier, position27);
2653     QCOMPARE(ic.eventType, QEvent::MouseButtonRelease);
2654     QCOMPARE(ic.eventPosition, position27);
2655     QCOMPARE(ic.eventGlobalPosition, globalposition27);
2656     QCOMPARE(ic.eventButton, Qt::LeftButton);
2657     QCOMPARE(ic.eventModifiers, Qt::NoModifier);
2658     QVERIFY(ic.cursor >= 14 && ic.cursor <= 16);
2659     ic.eventType = QEvent::None;
2660
2661     // And in the other direction.
2662     QTest::mouseDClick(view.viewport(), Qt::LeftButton, Qt::ControlModifier, position27);
2663     QCOMPARE(ic.eventType, QEvent::MouseButtonDblClick);
2664     QCOMPARE(ic.eventPosition, position27);
2665     QCOMPARE(ic.eventGlobalPosition, globalposition27);
2666     QCOMPARE(ic.eventButton, Qt::LeftButton);
2667     QCOMPARE(ic.eventModifiers, Qt::ControlModifier);
2668     QVERIFY(ic.cursor >= 14 && ic.cursor <= 16);
2669     ic.eventType = QEvent::None;
2670
2671     QTest::mousePress(view.viewport(), Qt::RightButton, Qt::ControlModifier, position27);
2672     QCOMPARE(ic.eventType, QEvent::MouseButtonPress);
2673     QCOMPARE(ic.eventPosition, position27);
2674     QCOMPARE(ic.eventGlobalPosition, globalposition27);
2675     QCOMPARE(ic.eventButton, Qt::RightButton);
2676     QCOMPARE(ic.eventModifiers, Qt::ControlModifier);
2677     QVERIFY(ic.cursor >= 14 && ic.cursor <= 16);
2678     ic.eventType = QEvent::None;
2679
2680     {   QMouseEvent mv(QEvent::MouseMove, position20, globalposition20, Qt::RightButton, Qt::RightButton,Qt::ControlModifier);
2681         QApplication::sendEvent(view.viewport(), &mv); }
2682     QCOMPARE(ic.eventType, QEvent::MouseMove);
2683     QCOMPARE(ic.eventPosition, position20);
2684     QCOMPARE(ic.eventGlobalPosition, globalposition20);
2685     QCOMPARE(ic.eventButton, Qt::RightButton);
2686     QCOMPARE(ic.eventModifiers, Qt::ControlModifier);
2687     QVERIFY(ic.cursor >= 7 && ic.cursor <= 9);
2688     ic.eventType = QEvent::None;
2689
2690     {   QMouseEvent mv(QEvent::MouseMove, position2, globalPosition2, Qt::RightButton, Qt::RightButton,Qt::ControlModifier);
2691         QApplication::sendEvent(view.viewport(), &mv); }
2692     QCOMPARE(ic.eventType, QEvent::None);
2693
2694     QTest::mouseRelease(view.viewport(), Qt::RightButton, Qt::ControlModifier, position2);
2695     QCOMPARE(ic.eventType, QEvent::MouseButtonRelease);
2696     QCOMPARE(ic.eventPosition, position2);
2697     QCOMPARE(ic.eventGlobalPosition, globalPosition2);
2698     QCOMPARE(ic.eventButton, Qt::RightButton);
2699     QCOMPARE(ic.eventModifiers, Qt::ControlModifier);
2700     QVERIFY(ic.cursor < 0);
2701     ic.eventType = QEvent::None;
2702 }
2703
2704 void tst_qdeclarativetextinput::inputMethodComposing()
2705 {
2706     QString text = "supercalifragisiticexpialidocious!";
2707
2708     QGraphicsScene scene;
2709     QGraphicsView view(&scene);
2710     MyInputContext ic;
2711     view.setInputContext(&ic);
2712     QDeclarative1TextInput input;
2713     input.setWidth(200);
2714     input.setText(text.mid(0, 12));
2715     input.setCursorPosition(12);
2716     input.setPos(0, 0);
2717     input.setFocus(true);
2718     scene.addItem(&input);
2719     view.show();
2720     QApplication::setActiveWindow(&view);
2721     QTest::qWaitForWindowShown(&view);
2722     QTRY_COMPARE(QApplication::activeWindow(), static_cast<QWidget *>(&view));
2723
2724     QSignalSpy spy(&input, SIGNAL(inputMethodComposingChanged()));
2725
2726     QCOMPARE(input.isInputMethodComposing(), false);
2727
2728     ic.sendEvent(QInputMethodEvent(text.mid(3), QList<QInputMethodEvent::Attribute>()));
2729     QCOMPARE(input.isInputMethodComposing(), true);
2730     QCOMPARE(spy.count(), 1);
2731
2732     ic.sendEvent(QInputMethodEvent(text.mid(12), QList<QInputMethodEvent::Attribute>()));
2733     QCOMPARE(input.isInputMethodComposing(), true);
2734     QCOMPARE(spy.count(), 1);
2735
2736     ic.sendEvent(QInputMethodEvent());
2737     QCOMPARE(input.isInputMethodComposing(), false);
2738     QCOMPARE(spy.count(), 2);
2739 }
2740
2741 void tst_qdeclarativetextinput::cursorRectangleSize()
2742 {
2743     QDeclarativeView *canvas = createView(SRCDIR "/data/positionAt.qml");
2744     QVERIFY(canvas->rootObject() != 0);
2745     canvas->show();
2746     canvas->setFocus();
2747     QApplication::setActiveWindow(canvas);
2748     QTest::qWaitForWindowShown(canvas);
2749
2750     QDeclarative1TextInput *textInput = qobject_cast<QDeclarative1TextInput *>(canvas->rootObject());
2751     QVERIFY(textInput != 0);
2752     textInput->setFocus(Qt::OtherFocusReason);
2753     QRectF cursorRect = textInput->positionToRectangle(textInput->cursorPosition());
2754     QRectF microFocusFromScene = canvas->scene()->inputMethodQuery(Qt::ImMicroFocus).toRectF();
2755     QRectF microFocusFromApp= QApplication::focusWidget()->inputMethodQuery(Qt::ImMicroFocus).toRectF();
2756
2757     QCOMPARE(microFocusFromScene.size(), cursorRect.size());
2758     QCOMPARE(microFocusFromApp.size(), cursorRect.size());
2759 }
2760
2761 QTEST_MAIN(tst_qdeclarativetextinput)
2762
2763 #include "tst_qdeclarativetextinput.moc"