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