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