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