ffff922e5219ff2976da8c80888fe71fe6a8cb85
[profile/ivi/qtdeclarative.git] / tests / auto / quick / qquicktextedit / tst_qquicktextedit.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the test suite of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 #include <qtest.h>
42 #include <QtTest/QSignalSpy>
43 #include "../../shared/testhttpserver.h"
44 #include <math.h>
45 #include <QFile>
46 #include <QTextDocument>
47 #include <QtQml/qqmlengine.h>
48 #include <QtQml/qqmlcontext.h>
49 #include <QtQml/qqmlexpression.h>
50 #include <QtQml/qqmlcomponent.h>
51 #include <QtGui/qguiapplication.h>
52 #include <private/qquicktextedit_p.h>
53 #include <private/qquicktextedit_p_p.h>
54 #include <private/qquicktext_p_p.h>
55 #include <QFontMetrics>
56 #include <QtQuick/QQuickView>
57 #include <QDir>
58 #include <QInputMethod>
59 #include <QClipboard>
60 #include <QMimeData>
61 #include <private/qquicktextcontrol_p.h>
62 #include "../../shared/util.h"
63 #include "../../shared/platforminputcontext.h"
64 #include <private/qinputmethod_p.h>
65 #include <QtGui/qstylehints.h>
66
67 #ifdef Q_OS_MAC
68 #include <Carbon/Carbon.h>
69 #endif
70
71 #define SERVER_PORT 42332
72 #define SERVER_ADDR "http://localhost:42332"
73
74 Q_DECLARE_METATYPE(QQuickTextEdit::SelectionMode)
75 Q_DECLARE_METATYPE(Qt::Key)
76 DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD)
77
78 QString createExpectedFileIfNotFound(const QString& filebasename, const QImage& actual)
79 {
80     // XXX This will be replaced by some clever persistent platform image store.
81     QString persistent_dir = QQmlDataTest::instance()->dataDirectory();
82     QString arch = "unknown-architecture"; // QTest needs to help with this.
83
84     QString expectfile = persistent_dir + QDir::separator() + filebasename + "-" + arch + ".png";
85
86     if (!QFile::exists(expectfile)) {
87         actual.save(expectfile);
88         qWarning() << "created" << expectfile;
89     }
90
91     return expectfile;
92 }
93
94 typedef QPair<int, QChar> Key;
95
96 class tst_qquicktextedit : public QQmlDataTest
97
98 {
99     Q_OBJECT
100 public:
101     tst_qquicktextedit();
102
103 private slots:
104     void cleanup();
105     void text();
106     void width();
107     void wrap();
108     void textFormat();
109     void alignments();
110     void alignments_data();
111
112     // ### these tests may be trivial
113     void hAlign();
114     void hAlign_RightToLeft();
115     void hAlignVisual();
116     void vAlign();
117     void font();
118     void color();
119     void textMargin();
120     void persistentSelection();
121     void focusOnPress();
122     void selection();
123     void isRightToLeft_data();
124     void isRightToLeft();
125     void keySelection();
126     void moveCursorSelection_data();
127     void moveCursorSelection();
128     void moveCursorSelectionSequence_data();
129     void moveCursorSelectionSequence();
130     void mouseSelection_data();
131     void mouseSelection();
132     void mouseSelectionMode_data();
133     void mouseSelectionMode();
134     void dragMouseSelection();
135     void inputMethodHints();
136
137     void positionAt_data();
138     void positionAt();
139
140     void linkActivated();
141
142     void cursorDelegate_data();
143     void cursorDelegate();
144     void remoteCursorDelegate();
145     void cursorVisible();
146     void delegateLoading_data();
147     void delegateLoading();
148     void navigation();
149     void readOnly();
150     void copyAndPaste();
151     void canPaste();
152     void canPasteEmpty();
153     void textInput();
154     void inputMethodUpdate();
155     void openInputPanel();
156     void geometrySignals();
157     void pastingRichText_QTBUG_14003();
158     void implicitSize_data();
159     void implicitSize();
160     void contentSize();
161     void boundingRect();
162     void clipRect();
163     void implicitSizeBinding_data();
164     void implicitSizeBinding();
165
166     void preeditCursorRectangle();
167     void inputMethodComposing();
168     void cursorRectangleSize();
169
170     void getText_data();
171     void getText();
172     void getFormattedText_data();
173     void getFormattedText();
174     void insert_data();
175     void insert();
176     void remove_data();
177     void remove();
178
179     void keySequence_data();
180     void keySequence();
181
182     void undo_data();
183     void undo();
184     void redo_data();
185     void redo();
186     void undo_keypressevents_data();
187     void undo_keypressevents();
188
189     void baseUrl();
190     void embeddedImages();
191     void embeddedImages_data();
192
193     void emptytags_QTBUG_22058();
194
195 private:
196     void simulateKeys(QWindow *window, const QList<Key> &keys);
197     void simulateKeys(QWindow *window, const QKeySequence &sequence);
198
199     void simulateKey(QWindow *, int key, Qt::KeyboardModifiers modifiers = 0);
200
201     QStringList standard;
202     QStringList richText;
203
204     QStringList hAlignmentStrings;
205     QStringList vAlignmentStrings;
206
207     QList<Qt::Alignment> vAlignments;
208     QList<Qt::Alignment> hAlignments;
209
210     QStringList colorStrings;
211
212     QQmlEngine engine;
213 };
214
215 typedef QList<int> IntList;
216 Q_DECLARE_METATYPE(IntList)
217
218 typedef QList<Key> KeyList;
219 Q_DECLARE_METATYPE(KeyList)
220
221 Q_DECLARE_METATYPE(QQuickTextEdit::HAlignment)
222 Q_DECLARE_METATYPE(QQuickTextEdit::VAlignment)
223 Q_DECLARE_METATYPE(QQuickTextEdit::TextFormat)
224
225 void tst_qquicktextedit::simulateKeys(QWindow *window, const QList<Key> &keys)
226 {
227     for (int i = 0; i < keys.count(); ++i) {
228         const int key = keys.at(i).first;
229         const int modifiers = key & Qt::KeyboardModifierMask;
230         const QString text = !keys.at(i).second.isNull() ? QString(keys.at(i).second) : QString();
231
232         QKeyEvent press(QEvent::KeyPress, Qt::Key(key), Qt::KeyboardModifiers(modifiers), text);
233         QKeyEvent release(QEvent::KeyRelease, Qt::Key(key), Qt::KeyboardModifiers(modifiers), text);
234
235         QGuiApplication::sendEvent(window, &press);
236         QGuiApplication::sendEvent(window, &release);
237     }
238 }
239
240 void tst_qquicktextedit::simulateKeys(QWindow *window, const QKeySequence &sequence)
241 {
242     for (int i = 0; i < sequence.count(); ++i) {
243         const int key = sequence[i];
244         const int modifiers = key & Qt::KeyboardModifierMask;
245
246         QTest::keyClick(window, Qt::Key(key & ~modifiers), Qt::KeyboardModifiers(modifiers));
247     }
248 }
249
250 QList<Key> &operator <<(QList<Key> &keys, const QKeySequence &sequence)
251 {
252     for (int i = 0; i < sequence.count(); ++i)
253         keys << Key(sequence[i], QChar());
254     return keys;
255 }
256
257 template <int N> QList<Key> &operator <<(QList<Key> &keys, const char (&characters)[N])
258 {
259     for (int i = 0; i < N - 1; ++i) {
260         int key = QTest::asciiToKey(characters[i]);
261         QChar character = QLatin1Char(characters[i]);
262         keys << Key(key, character);
263     }
264     return keys;
265 }
266
267 QList<Key> &operator <<(QList<Key> &keys, Qt::Key key)
268 {
269     keys << Key(key, QChar());
270     return keys;
271 }
272
273 tst_qquicktextedit::tst_qquicktextedit()
274 {
275     standard << "the quick brown fox jumped over the lazy dog"
276              << "the quick brown fox\n jumped over the lazy dog"
277              << "Hello, world!"
278              << "!dlrow ,olleH";
279
280     richText << "<i>the <b>quick</b> brown <a href=\\\"http://www.google.com\\\">fox</a> jumped over the <b>lazy</b> dog</i>"
281              << "<i>the <b>quick</b> brown <a href=\\\"http://www.google.com\\\">fox</a><br>jumped over the <b>lazy</b> dog</i>";
282
283     hAlignmentStrings << "AlignLeft"
284                       << "AlignRight"
285                       << "AlignHCenter";
286
287     vAlignmentStrings << "AlignTop"
288                       << "AlignBottom"
289                       << "AlignVCenter";
290
291     hAlignments << Qt::AlignLeft
292                 << Qt::AlignRight
293                 << Qt::AlignHCenter;
294
295     vAlignments << Qt::AlignTop
296                 << Qt::AlignBottom
297                 << Qt::AlignVCenter;
298
299     colorStrings << "aliceblue"
300                  << "antiquewhite"
301                  << "aqua"
302                  << "darkkhaki"
303                  << "darkolivegreen"
304                  << "dimgray"
305                  << "palevioletred"
306                  << "lightsteelblue"
307                  << "#000000"
308                  << "#AAAAAA"
309                  << "#FFFFFF"
310                  << "#2AC05F";
311                  //
312                  // need a different test to do alpha channel test
313                  // << "#AA0011DD"
314                  // << "#00F16B11";
315                  //
316 }
317
318 void tst_qquicktextedit::cleanup()
319 {
320     // ensure not even skipped tests with custom input context leave it dangling
321     QInputMethodPrivate *inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod());
322     inputMethodPrivate->testContext = 0;
323 }
324
325 void tst_qquicktextedit::text()
326 {
327     {
328         QQmlComponent texteditComponent(&engine);
329         texteditComponent.setData("import QtQuick 2.0\nTextEdit {  text: \"\"  }", QUrl());
330         QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
331
332         QVERIFY(textEditObject != 0);
333         QCOMPARE(textEditObject->text(), QString(""));
334         QCOMPARE(textEditObject->length(), 0);
335     }
336
337     for (int i = 0; i < standard.size(); i++)
338     {
339         QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"" + standard.at(i) + "\" }";
340         QQmlComponent texteditComponent(&engine);
341         texteditComponent.setData(componentStr.toLatin1(), QUrl());
342         QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
343
344         QVERIFY(textEditObject != 0);
345         QCOMPARE(textEditObject->text(), standard.at(i));
346         QCOMPARE(textEditObject->length(), standard.at(i).length());
347     }
348
349     for (int i = 0; i < richText.size(); i++)
350     {
351         QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"" + richText.at(i) + "\" }";
352         QQmlComponent texteditComponent(&engine);
353         texteditComponent.setData(componentStr.toLatin1(), QUrl());
354
355         QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
356
357         QVERIFY(textEditObject != 0);
358
359         QString expected = richText.at(i);
360         expected.replace(QRegExp("\\\\(.)"),"\\1");
361         QCOMPARE(textEditObject->text(), expected);
362         QCOMPARE(textEditObject->length(), expected.length());
363     }
364
365     for (int i = 0; i < standard.size(); i++)
366     {
367         QString componentStr = "import QtQuick 2.0\nTextEdit { textFormat: TextEdit.RichText; text: \"" + standard.at(i) + "\" }";
368         QQmlComponent texteditComponent(&engine);
369         texteditComponent.setData(componentStr.toLatin1(), QUrl());
370         QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
371
372         QVERIFY(textEditObject != 0);
373
374         QString actual = textEditObject->text();
375         QString expected = standard.at(i);
376         actual.remove(QRegExp(".*<body[^>]*>"));
377         actual.remove(QRegExp("(<[^>]*>)+"));
378         expected.remove("\n");
379         QCOMPARE(actual.simplified(), expected);
380         QCOMPARE(textEditObject->length(), expected.length());
381     }
382
383     for (int i = 0; i < richText.size(); i++)
384     {
385         QString componentStr = "import QtQuick 2.0\nTextEdit { textFormat: TextEdit.RichText; text: \"" + richText.at(i) + "\" }";
386         QQmlComponent texteditComponent(&engine);
387         texteditComponent.setData(componentStr.toLatin1(), QUrl());
388         QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
389
390         QVERIFY(textEditObject != 0);
391         QString actual = textEditObject->text();
392         QString expected = richText.at(i);
393         actual.replace(QRegExp(".*<body[^>]*>"),"");
394         actual.replace(QRegExp("(<[^>]*>)+"),"<>");
395         expected.replace(QRegExp("(<[^>]*>)+"),"<>");
396         QCOMPARE(actual.simplified(),expected.simplified());
397
398         expected.replace("<>", " ");
399         QCOMPARE(textEditObject->length(), expected.simplified().length());
400     }
401
402     for (int i = 0; i < standard.size(); i++)
403     {
404         QString componentStr = "import QtQuick 2.0\nTextEdit { textFormat: TextEdit.AutoText; text: \"" + standard.at(i) + "\" }";
405         QQmlComponent texteditComponent(&engine);
406         texteditComponent.setData(componentStr.toLatin1(), QUrl());
407         QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
408
409         QVERIFY(textEditObject != 0);
410         QCOMPARE(textEditObject->text(), standard.at(i));
411         QCOMPARE(textEditObject->length(), standard.at(i).length());
412     }
413
414     for (int i = 0; i < richText.size(); i++)
415     {
416         QString componentStr = "import QtQuick 2.0\nTextEdit { textFormat: TextEdit.AutoText; text: \"" + richText.at(i) + "\" }";
417         QQmlComponent texteditComponent(&engine);
418         texteditComponent.setData(componentStr.toLatin1(), QUrl());
419         QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
420
421         QVERIFY(textEditObject != 0);
422         QString actual = textEditObject->text();
423         QString expected = richText.at(i);
424         actual.replace(QRegExp(".*<body[^>]*>"),"");
425         actual.replace(QRegExp("(<[^>]*>)+"),"<>");
426         expected.replace(QRegExp("(<[^>]*>)+"),"<>");
427         QCOMPARE(actual.simplified(),expected.simplified());
428
429         expected.replace("<>", " ");
430         QCOMPARE(textEditObject->length(), expected.simplified().length());
431     }
432 }
433
434 void tst_qquicktextedit::width()
435 {
436     // uses Font metrics to find the width for standard and document to find the width for rich
437     {
438         QQmlComponent texteditComponent(&engine);
439         texteditComponent.setData("import QtQuick 2.0\nTextEdit {  text: \"\" }", QUrl());
440         QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
441
442         QVERIFY(textEditObject != 0);
443         QCOMPARE(textEditObject->width(), 0.0);
444     }
445
446     bool requiresUnhintedMetrics = !qmlDisableDistanceField();
447
448     for (int i = 0; i < standard.size(); i++)
449     {
450         QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"" + standard.at(i) + "\" }";
451         QQmlComponent texteditComponent(&engine);
452         texteditComponent.setData(componentStr.toLatin1(), QUrl());
453         QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
454
455         QString s = standard.at(i);
456         s.replace(QLatin1Char('\n'), QChar::LineSeparator);
457
458         QTextLayout layout(s);
459         layout.setFont(textEditObject->font());
460         layout.setFlags(Qt::TextExpandTabs | Qt::TextShowMnemonic);
461         if (requiresUnhintedMetrics) {
462             QTextOption option;
463             option.setUseDesignMetrics(true);
464             layout.setTextOption(option);
465         }
466
467         layout.beginLayout();
468         forever {
469             QTextLine line = layout.createLine();
470             if (!line.isValid())
471                 break;
472         }
473
474         layout.endLayout();
475
476         qreal metricWidth = layout.boundingRect().width();
477
478         QVERIFY(textEditObject != 0);
479         QCOMPARE(textEditObject->width(), metricWidth);
480     }
481
482     for (int i = 0; i < richText.size(); i++)
483     {
484         QTextDocument document;
485         document.setHtml(richText.at(i));
486         document.setDocumentMargin(0);
487         if (requiresUnhintedMetrics)
488             document.setUseDesignMetrics(true);
489
490         qreal documentWidth = document.idealWidth();
491
492         QString componentStr = "import QtQuick 2.0\nTextEdit { textFormat: TextEdit.RichText; text: \"" + richText.at(i) + "\" }";
493         QQmlComponent texteditComponent(&engine);
494         texteditComponent.setData(componentStr.toLatin1(), QUrl());
495         QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
496
497         QVERIFY(textEditObject != 0);
498         QCOMPARE(textEditObject->width(), documentWidth);
499     }
500 }
501
502 void tst_qquicktextedit::wrap()
503 {
504     // for specified width and wrap set true
505     {
506         QQmlComponent texteditComponent(&engine);
507         texteditComponent.setData("import QtQuick 2.0\nTextEdit {  text: \"\"; wrapMode: TextEdit.WordWrap; width: 300 }", QUrl());
508         QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
509
510         QVERIFY(textEditObject != 0);
511         QCOMPARE(textEditObject->width(), 300.);
512     }
513
514     for (int i = 0; i < standard.size(); i++)
515     {
516         QString componentStr = "import QtQuick 2.0\nTextEdit {  wrapMode: TextEdit.WordWrap; width: 300; text: \"" + standard.at(i) + "\" }";
517         QQmlComponent texteditComponent(&engine);
518         texteditComponent.setData(componentStr.toLatin1(), QUrl());
519         QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
520
521         QVERIFY(textEditObject != 0);
522         QCOMPARE(textEditObject->width(), 300.);
523     }
524
525     for (int i = 0; i < richText.size(); i++)
526     {
527         QString componentStr = "import QtQuick 2.0\nTextEdit {  wrapMode: TextEdit.WordWrap; width: 300; text: \"" + richText.at(i) + "\" }";
528         QQmlComponent texteditComponent(&engine);
529         texteditComponent.setData(componentStr.toLatin1(), QUrl());
530         QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
531
532         QVERIFY(textEditObject != 0);
533         QCOMPARE(textEditObject->width(), 300.);
534     }
535
536 }
537
538 void tst_qquicktextedit::textFormat()
539 {
540     {
541         QQmlComponent textComponent(&engine);
542         textComponent.setData("import QtQuick 2.0\nTextEdit { text: \"Hello\"; textFormat: Text.RichText }", QUrl::fromLocalFile(""));
543         QQuickTextEdit *textObject = qobject_cast<QQuickTextEdit*>(textComponent.create());
544
545         QVERIFY(textObject != 0);
546         QVERIFY(textObject->textFormat() == QQuickTextEdit::RichText);
547     }
548     {
549         QQmlComponent textComponent(&engine);
550         textComponent.setData("import QtQuick 2.0\nTextEdit { text: \"<b>Hello</b>\"; textFormat: Text.PlainText }", QUrl::fromLocalFile(""));
551         QQuickTextEdit *textObject = qobject_cast<QQuickTextEdit*>(textComponent.create());
552
553         QVERIFY(textObject != 0);
554         QVERIFY(textObject->textFormat() == QQuickTextEdit::PlainText);
555     }
556 }
557
558 void tst_qquicktextedit::alignments_data()
559 {
560     QTest::addColumn<int>("hAlign");
561     QTest::addColumn<int>("vAlign");
562     QTest::addColumn<QString>("expectfile");
563
564     QTest::newRow("LT") << int(Qt::AlignLeft) << int(Qt::AlignTop) << "alignments_lt";
565     QTest::newRow("RT") << int(Qt::AlignRight) << int(Qt::AlignTop) << "alignments_rt";
566     QTest::newRow("CT") << int(Qt::AlignHCenter) << int(Qt::AlignTop) << "alignments_ct";
567
568     QTest::newRow("LB") << int(Qt::AlignLeft) << int(Qt::AlignBottom) << "alignments_lb";
569     QTest::newRow("RB") << int(Qt::AlignRight) << int(Qt::AlignBottom) << "alignments_rb";
570     QTest::newRow("CB") << int(Qt::AlignHCenter) << int(Qt::AlignBottom) << "alignments_cb";
571
572     QTest::newRow("LC") << int(Qt::AlignLeft) << int(Qt::AlignVCenter) << "alignments_lc";
573     QTest::newRow("RC") << int(Qt::AlignRight) << int(Qt::AlignVCenter) << "alignments_rc";
574     QTest::newRow("CC") << int(Qt::AlignHCenter) << int(Qt::AlignVCenter) << "alignments_cc";
575 }
576
577
578 void tst_qquicktextedit::alignments()
579 {
580     QSKIP("Image comparison of text is almost guaranteed to fail during development");
581
582     QFETCH(int, hAlign);
583     QFETCH(int, vAlign);
584     QFETCH(QString, expectfile);
585
586     QQuickView canvas(testFileUrl("alignments.qml"));
587
588     canvas.show();
589     QTest::qWaitForWindowShown(&canvas);
590
591     QObject *ob = canvas.rootObject();
592     QVERIFY(ob != 0);
593     ob->setProperty("horizontalAlignment",hAlign);
594     ob->setProperty("verticalAlignment",vAlign);
595     QTRY_COMPARE(ob->property("running").toBool(),false);
596     QImage actual = canvas.grabFrameBuffer();
597
598     expectfile = createExpectedFileIfNotFound(expectfile, actual);
599
600     QImage expect(expectfile);
601
602     QCOMPARE(actual,expect);
603 }
604
605
606 //the alignment tests may be trivial o.oa
607 void tst_qquicktextedit::hAlign()
608 {
609     //test one align each, and then test if two align fails.
610
611     for (int i = 0; i < standard.size(); i++)
612     {
613         for (int j=0; j < hAlignmentStrings.size(); j++)
614         {
615             QString componentStr = "import QtQuick 2.0\nTextEdit {  horizontalAlignment: \"" + hAlignmentStrings.at(j) + "\"; text: \"" + standard.at(i) + "\" }";
616             QQmlComponent texteditComponent(&engine);
617             texteditComponent.setData(componentStr.toLatin1(), QUrl());
618             QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
619
620             QVERIFY(textEditObject != 0);
621             QCOMPARE((int)textEditObject->hAlign(), (int)hAlignments.at(j));
622         }
623     }
624
625     for (int i = 0; i < richText.size(); i++)
626     {
627         for (int j=0; j < hAlignmentStrings.size(); j++)
628         {
629             QString componentStr = "import QtQuick 2.0\nTextEdit {  horizontalAlignment: \"" + hAlignmentStrings.at(j) + "\"; text: \"" + richText.at(i) + "\" }";
630             QQmlComponent texteditComponent(&engine);
631             texteditComponent.setData(componentStr.toLatin1(), QUrl());
632             QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
633
634             QVERIFY(textEditObject != 0);
635             QCOMPARE((int)textEditObject->hAlign(), (int)hAlignments.at(j));
636         }
637     }
638
639 }
640
641 void tst_qquicktextedit::hAlign_RightToLeft()
642 {
643     PlatformInputContext platformInputContext;
644     QInputMethodPrivate *inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod());
645     inputMethodPrivate->testContext = &platformInputContext;
646
647     QQuickView canvas(testFileUrl("horizontalAlignment_RightToLeft.qml"));
648     QQuickTextEdit *textEdit = canvas.rootObject()->findChild<QQuickTextEdit*>("text");
649     QVERIFY(textEdit != 0);
650     canvas.show();
651
652     const QString rtlText = textEdit->text();
653
654     // implicit alignment should follow the reading direction of text
655     QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignRight);
656     QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2);
657
658     // explicitly left aligned
659     textEdit->setHAlign(QQuickTextEdit::AlignLeft);
660     QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignLeft);
661     QVERIFY(textEdit->positionToRectangle(0).x() < canvas.width()/2);
662
663     // explicitly right aligned
664     textEdit->setHAlign(QQuickTextEdit::AlignRight);
665     QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignRight);
666     QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2);
667
668     QString textString = textEdit->text();
669     textEdit->setText(QString("<i>") + textString + QString("</i>"));
670     textEdit->resetHAlign();
671
672     // implicitly aligned rich text should follow the reading direction of RTL text
673     QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignRight);
674     QCOMPARE(textEdit->effectiveHAlign(), textEdit->hAlign());
675     QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2);
676
677     // explicitly left aligned rich text
678     textEdit->setHAlign(QQuickTextEdit::AlignLeft);
679     QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignLeft);
680     QCOMPARE(textEdit->effectiveHAlign(), textEdit->hAlign());
681     QVERIFY(textEdit->positionToRectangle(0).x() < canvas.width()/2);
682
683     // explicitly right aligned rich text
684     textEdit->setHAlign(QQuickTextEdit::AlignRight);
685     QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignRight);
686     QCOMPARE(textEdit->effectiveHAlign(), textEdit->hAlign());
687     QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2);
688
689     textEdit->setText(textString);
690
691     // explicitly center aligned
692     textEdit->setHAlign(QQuickTextEdit::AlignHCenter);
693     QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignHCenter);
694     QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2);
695
696     // reseted alignment should go back to following the text reading direction
697     textEdit->resetHAlign();
698     QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignRight);
699     QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2);
700
701     // mirror the text item
702     QQuickItemPrivate::get(textEdit)->setLayoutMirror(true);
703
704     // mirrored implicit alignment should continue to follow the reading direction of the text
705     QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignRight);
706     QCOMPARE(textEdit->effectiveHAlign(), QQuickTextEdit::AlignRight);
707     QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2);
708
709     // mirrored explicitly right aligned behaves as left aligned
710     textEdit->setHAlign(QQuickTextEdit::AlignRight);
711     QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignRight);
712     QCOMPARE(textEdit->effectiveHAlign(), QQuickTextEdit::AlignLeft);
713     QVERIFY(textEdit->positionToRectangle(0).x() < canvas.width()/2);
714
715     // mirrored explicitly left aligned behaves as right aligned
716     textEdit->setHAlign(QQuickTextEdit::AlignLeft);
717     QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignLeft);
718     QCOMPARE(textEdit->effectiveHAlign(), QQuickTextEdit::AlignRight);
719     QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2);
720
721     // disable mirroring
722     QQuickItemPrivate::get(textEdit)->setLayoutMirror(false);
723     textEdit->resetHAlign();
724
725     // English text should be implicitly left aligned
726     textEdit->setText("Hello world!");
727     QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignLeft);
728     QVERIFY(textEdit->positionToRectangle(0).x() < canvas.width()/2);
729
730     canvas.requestActivateWindow();
731     QTest::qWaitForWindowActive(&canvas);
732     QVERIFY(textEdit->hasActiveFocus());
733
734     textEdit->setText(QString());
735     { QInputMethodEvent ev(rtlText, QList<QInputMethodEvent::Attribute>()); QGuiApplication::sendEvent(textEdit, &ev); }
736     QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignRight);
737     { QInputMethodEvent ev("Hello world!", QList<QInputMethodEvent::Attribute>()); QGuiApplication::sendEvent(textEdit, &ev); }
738     QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignLeft);
739
740     // Clear pre-edit text.  TextEdit should maybe do this itself on setText, but that may be
741     // redundant as an actual input method may take care of it.
742     { QInputMethodEvent ev; QGuiApplication::sendEvent(textEdit, &ev); }
743
744     // empty text with implicit alignment follows the system locale-based
745     // keyboard input direction from qApp->inputMethod()->inputDirection
746     textEdit->setText("");
747     platformInputContext.setInputDirection(Qt::LeftToRight);
748     QVERIFY(qApp->inputMethod()->inputDirection() == Qt::LeftToRight);
749     QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignLeft);
750     QVERIFY(textEdit->positionToRectangle(0).x() < canvas.width()/2);
751
752     QSignalSpy cursorRectangleSpy(textEdit, SIGNAL(cursorRectangleChanged()));
753
754     platformInputContext.setInputDirection(Qt::RightToLeft);
755     QCOMPARE(cursorRectangleSpy.count(), 1);
756     QVERIFY(qApp->inputMethod()->inputDirection() == Qt::RightToLeft);
757     QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignRight);
758     QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2);
759
760     // neutral text follows also input method direction
761     textEdit->resetHAlign();
762     textEdit->setText(" ()((=<>");
763     platformInputContext.setInputDirection(Qt::LeftToRight);
764     QCOMPARE(textEdit->effectiveHAlign(), QQuickTextEdit::AlignLeft);
765     QVERIFY(textEdit->cursorRectangle().left() < canvas.width()/2);
766     platformInputContext.setInputDirection(Qt::RightToLeft);
767     QCOMPARE(textEdit->effectiveHAlign(), QQuickTextEdit::AlignRight);
768     QVERIFY(textEdit->cursorRectangle().left() > canvas.width()/2);
769
770     // set input direction while having content
771     platformInputContext.setInputDirection(Qt::LeftToRight);
772     textEdit->setText("a");
773     textEdit->setCursorPosition(1);
774     platformInputContext.setInputDirection(Qt::RightToLeft);
775     QTest::keyClick(&canvas, Qt::Key_Backspace);
776     QVERIFY(textEdit->text().isEmpty());
777     QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignRight);
778     QVERIFY(textEdit->cursorRectangle().left() > canvas.width()/2);
779
780     // input direction changed while not having focus
781     platformInputContext.setInputDirection(Qt::LeftToRight);
782     textEdit->setFocus(false);
783     platformInputContext.setInputDirection(Qt::RightToLeft);
784     textEdit->setFocus(true);
785     QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignRight);
786     QVERIFY(textEdit->cursorRectangle().left() > canvas.width()/2);
787
788     textEdit->setHAlign(QQuickTextEdit::AlignRight);
789     QCOMPARE(textEdit->hAlign(), QQuickTextEdit::AlignRight);
790     QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2);
791
792     // make sure editor doesn't rely on input for updating size
793     QQuickTextEdit *emptyEdit = canvas.rootObject()->findChild<QQuickTextEdit*>("emptyTextEdit");
794     QVERIFY(emptyEdit != 0);
795     platformInputContext.setInputDirection(Qt::RightToLeft);
796     emptyEdit->setFocus(true);
797     QCOMPARE(emptyEdit->hAlign(), QQuickTextEdit::AlignRight);
798     QVERIFY(emptyEdit->cursorRectangle().left() > canvas.width()/2);
799 }
800
801
802 static int numberOfNonWhitePixels(int fromX, int toX, const QImage &image)
803 {
804     int pixels = 0;
805     for (int x = fromX; x < toX; ++x) {
806         for (int y = 0; y < image.height(); ++y) {
807             if (image.pixel(x, y) != qRgb(255, 255, 255))
808                 pixels++;
809         }
810     }
811     return pixels;
812 }
813
814 void tst_qquicktextedit::hAlignVisual()
815 {
816     QQuickView view(testFileUrl("hAlignVisual.qml"));
817     view.show();
818     QTest::qWaitForWindowShown(&view);
819
820     QQuickText *text = view.rootObject()->findChild<QQuickText*>("textItem");
821     QVERIFY(text != 0);
822     {
823         // Left Align
824         QImage image = view.grabFrameBuffer();
825         int left = numberOfNonWhitePixels(0, image.width() / 3, image);
826         int mid = numberOfNonWhitePixels(image.width() / 3, 2 * image.width() / 3, image);
827         int right = numberOfNonWhitePixels( 2 * image.width() / 3, image.width(), image);
828         QVERIFY(left > mid);
829         QVERIFY(mid > right);
830     }
831     {
832         // HCenter Align
833         text->setHAlign(QQuickText::AlignHCenter);
834         QImage image = view.grabFrameBuffer();
835         int left = numberOfNonWhitePixels(0, image.width() / 3, image);
836         int mid = numberOfNonWhitePixels(image.width() / 3, 2 * image.width() / 3, image);
837         int right = numberOfNonWhitePixels( 2 * image.width() / 3, image.width(), image);
838         QVERIFY(left < mid);
839         QVERIFY(mid > right);
840     }
841     {
842         // Right Align
843         text->setHAlign(QQuickText::AlignRight);
844         QImage image = view.grabFrameBuffer();
845         int left = numberOfNonWhitePixels(0, image.width() / 3, image);
846         int mid = numberOfNonWhitePixels(image.width() / 3, 2 * image.width() / 3, image);
847         int right = numberOfNonWhitePixels( 2 * image.width() / 3, image.width(), image);
848         QVERIFY(left < mid);
849         QVERIFY(mid < right);
850     }
851
852     text->setWidth(200);
853
854     {
855         // Left Align
856         QImage image = view.grabFrameBuffer();
857         int x = qCeil(text->implicitWidth());
858         int left = numberOfNonWhitePixels(0, x, image);
859         int right = numberOfNonWhitePixels(x, image.width() - x, image);
860         QVERIFY(left > 0);
861         QVERIFY(right == 0);
862     }
863     {
864         // HCenter Align
865         text->setHAlign(QQuickText::AlignHCenter);
866         QImage image = view.grabFrameBuffer();
867         int x1 = qFloor(image.width() - text->implicitWidth()) / 2;
868         int x2 = image.width() - x1;
869         int left = numberOfNonWhitePixels(0, x1, image);
870         int mid = numberOfNonWhitePixels(x1, x2 - x1, image);
871         int right = numberOfNonWhitePixels(x2, image.width() - x2, image);
872         QVERIFY(left == 0);
873         QVERIFY(mid > 0);
874         QVERIFY(right == 0);
875     }
876     {
877         // Right Align
878         text->setHAlign(QQuickText::AlignRight);
879         QImage image = view.grabFrameBuffer();
880         int x = image.width() - qCeil(text->implicitWidth());
881         int left = numberOfNonWhitePixels(0, x, image);
882         int right = numberOfNonWhitePixels(x, image.width() - x, image);
883         QVERIFY(left == 0);
884         QVERIFY(right > 0);
885     }
886 }
887
888 void tst_qquicktextedit::vAlign()
889 {
890     //test one align each, and then test if two align fails.
891
892     for (int i = 0; i < standard.size(); i++)
893     {
894         for (int j=0; j < vAlignmentStrings.size(); j++)
895         {
896             QString componentStr = "import QtQuick 2.0\nTextEdit {  verticalAlignment: \"" + vAlignmentStrings.at(j) + "\"; text: \"" + standard.at(i) + "\" }";
897             QQmlComponent texteditComponent(&engine);
898             texteditComponent.setData(componentStr.toLatin1(), QUrl());
899             QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
900
901             QVERIFY(textEditObject != 0);
902             QCOMPARE((int)textEditObject->vAlign(), (int)vAlignments.at(j));
903         }
904     }
905
906     for (int i = 0; i < richText.size(); i++)
907     {
908         for (int j=0; j < vAlignmentStrings.size(); j++)
909         {
910             QString componentStr = "import QtQuick 2.0\nTextEdit {  verticalAlignment: \"" + vAlignmentStrings.at(j) + "\"; text: \"" + richText.at(i) + "\" }";
911             QQmlComponent texteditComponent(&engine);
912             texteditComponent.setData(componentStr.toLatin1(), QUrl());
913             QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
914
915             QVERIFY(textEditObject != 0);
916             QCOMPARE((int)textEditObject->vAlign(), (int)vAlignments.at(j));
917         }
918     }
919
920     QQmlComponent texteditComponent(&engine);
921     texteditComponent.setData(
922                 "import QtQuick 2.0\n"
923                 "TextEdit { width: 100; height: 100; text: \"Hello World\" }", QUrl());
924     QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
925
926     QVERIFY(textEditObject != 0);
927
928     QCOMPARE(textEditObject->vAlign(), QQuickTextEdit::AlignTop);
929     QVERIFY(textEditObject->cursorRectangle().bottom() < 50);
930     QVERIFY(textEditObject->positionToRectangle(0).bottom() < 50);
931
932     // bottom aligned
933     textEditObject->setVAlign(QQuickTextEdit::AlignBottom);
934     QCOMPARE(textEditObject->vAlign(), QQuickTextEdit::AlignBottom);
935     QVERIFY(textEditObject->cursorRectangle().top() > 50);
936     QVERIFY(textEditObject->positionToRectangle(0).top() > 50);
937
938     // explicitly center aligned
939     textEditObject->setVAlign(QQuickTextEdit::AlignVCenter);
940     QCOMPARE(textEditObject->vAlign(), QQuickTextEdit::AlignVCenter);
941     QVERIFY(textEditObject->cursorRectangle().top() < 50);
942     QVERIFY(textEditObject->cursorRectangle().bottom() > 50);
943     QVERIFY(textEditObject->positionToRectangle(0).top() < 50);
944     QVERIFY(textEditObject->positionToRectangle(0).bottom() > 50);
945 }
946
947 void tst_qquicktextedit::font()
948 {
949     //test size, then bold, then italic, then family
950     {
951         QString componentStr = "import QtQuick 2.0\nTextEdit {  font.pointSize: 40; text: \"Hello World\" }";
952         QQmlComponent texteditComponent(&engine);
953         texteditComponent.setData(componentStr.toLatin1(), QUrl());
954         QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
955
956         QVERIFY(textEditObject != 0);
957         QCOMPARE(textEditObject->font().pointSize(), 40);
958         QCOMPARE(textEditObject->font().bold(), false);
959         QCOMPARE(textEditObject->font().italic(), false);
960     }
961
962     {
963         QString componentStr = "import QtQuick 2.0\nTextEdit {  font.bold: true; text: \"Hello World\" }";
964         QQmlComponent texteditComponent(&engine);
965         texteditComponent.setData(componentStr.toLatin1(), QUrl());
966         QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
967
968         QVERIFY(textEditObject != 0);
969         QCOMPARE(textEditObject->font().bold(), true);
970         QCOMPARE(textEditObject->font().italic(), false);
971     }
972
973     {
974         QString componentStr = "import QtQuick 2.0\nTextEdit {  font.italic: true; text: \"Hello World\" }";
975         QQmlComponent texteditComponent(&engine);
976         texteditComponent.setData(componentStr.toLatin1(), QUrl());
977         QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
978
979         QVERIFY(textEditObject != 0);
980         QCOMPARE(textEditObject->font().italic(), true);
981         QCOMPARE(textEditObject->font().bold(), false);
982     }
983
984     {
985         QString componentStr = "import QtQuick 2.0\nTextEdit {  font.family: \"Helvetica\"; text: \"Hello World\" }";
986         QQmlComponent texteditComponent(&engine);
987         texteditComponent.setData(componentStr.toLatin1(), QUrl());
988         QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
989
990         QVERIFY(textEditObject != 0);
991         QCOMPARE(textEditObject->font().family(), QString("Helvetica"));
992         QCOMPARE(textEditObject->font().bold(), false);
993         QCOMPARE(textEditObject->font().italic(), false);
994     }
995
996     {
997         QString componentStr = "import QtQuick 2.0\nTextEdit {  font.family: \"\"; text: \"Hello World\" }";
998         QQmlComponent texteditComponent(&engine);
999         texteditComponent.setData(componentStr.toLatin1(), QUrl());
1000         QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
1001
1002         QVERIFY(textEditObject != 0);
1003         QCOMPARE(textEditObject->font().family(), QString(""));
1004     }
1005 }
1006
1007 void tst_qquicktextedit::color()
1008 {
1009     //test initial color
1010     {
1011         QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"Hello World\" }";
1012         QQmlComponent texteditComponent(&engine);
1013         texteditComponent.setData(componentStr.toLatin1(), QUrl());
1014         QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
1015
1016         QQuickTextEditPrivate *textEditPrivate = static_cast<QQuickTextEditPrivate*>(QQuickItemPrivate::get(textEditObject));
1017
1018         QVERIFY(textEditObject);
1019         QVERIFY(textEditPrivate);
1020         QVERIFY(textEditPrivate->control);
1021         QCOMPARE(textEditPrivate->color, QColor("black"));
1022     }
1023     //test normal
1024     for (int i = 0; i < colorStrings.size(); i++)
1025     {
1026         QString componentStr = "import QtQuick 2.0\nTextEdit {  color: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
1027         QQmlComponent texteditComponent(&engine);
1028         texteditComponent.setData(componentStr.toLatin1(), QUrl());
1029         QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
1030         //qDebug() << "textEditObject: " << textEditObject->color() << "vs. " << QColor(colorStrings.at(i));
1031         QVERIFY(textEditObject != 0);
1032         QCOMPARE(textEditObject->color(), QColor(colorStrings.at(i)));
1033     }
1034
1035     //test selection
1036     for (int i = 0; i < colorStrings.size(); i++)
1037     {
1038         QString componentStr = "import QtQuick 2.0\nTextEdit {  selectionColor: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
1039         QQmlComponent texteditComponent(&engine);
1040         texteditComponent.setData(componentStr.toLatin1(), QUrl());
1041         QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
1042         QVERIFY(textEditObject != 0);
1043         QCOMPARE(textEditObject->selectionColor(), QColor(colorStrings.at(i)));
1044     }
1045
1046     //test selected text
1047     for (int i = 0; i < colorStrings.size(); i++)
1048     {
1049         QString componentStr = "import QtQuick 2.0\nTextEdit {  selectedTextColor: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
1050         QQmlComponent texteditComponent(&engine);
1051         texteditComponent.setData(componentStr.toLatin1(), QUrl());
1052         QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
1053         QVERIFY(textEditObject != 0);
1054         QCOMPARE(textEditObject->selectedTextColor(), QColor(colorStrings.at(i)));
1055     }
1056
1057     {
1058         QString colorStr = "#AA001234";
1059         QColor testColor("#001234");
1060         testColor.setAlpha(170);
1061
1062         QString componentStr = "import QtQuick 2.0\nTextEdit {  color: \"" + colorStr + "\"; text: \"Hello World\" }";
1063         QQmlComponent texteditComponent(&engine);
1064         texteditComponent.setData(componentStr.toLatin1(), QUrl());
1065         QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
1066
1067         QVERIFY(textEditObject != 0);
1068         QCOMPARE(textEditObject->color(), testColor);
1069     }
1070 }
1071
1072 void tst_qquicktextedit::textMargin()
1073 {
1074     for (qreal i=0; i<=10; i+=0.3) {
1075         QString componentStr = "import QtQuick 2.0\nTextEdit {  textMargin: " + QString::number(i) + "; text: \"Hello World\" }";
1076         QQmlComponent texteditComponent(&engine);
1077         texteditComponent.setData(componentStr.toLatin1(), QUrl());
1078         QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
1079         QVERIFY(textEditObject != 0);
1080         QCOMPARE(textEditObject->textMargin(), i);
1081     }
1082 }
1083
1084 void tst_qquicktextedit::persistentSelection()
1085 {
1086     QQuickView canvas(testFileUrl("persistentSelection.qml"));
1087     canvas.show();
1088     canvas.requestActivateWindow();
1089     QTest::qWaitForWindowActive(&canvas);
1090
1091     QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(canvas.rootObject());
1092     QVERIFY(edit);
1093     QVERIFY(edit->hasActiveFocus());
1094
1095     QSignalSpy spy(edit, SIGNAL(persistentSelectionChanged(bool)));
1096
1097     QCOMPARE(edit->persistentSelection(), false);
1098
1099     edit->setPersistentSelection(false);
1100     QCOMPARE(edit->persistentSelection(), false);
1101     QCOMPARE(spy.count(), 0);
1102
1103     edit->select(1, 4);
1104     QCOMPARE(edit->property("selected").toString(), QLatin1String("ell"));
1105
1106     edit->setFocus(false);
1107     QCOMPARE(edit->property("selected").toString(), QString());
1108
1109     edit->setFocus(true);
1110     QCOMPARE(edit->property("selected").toString(), QString());
1111
1112     edit->setPersistentSelection(true);
1113     QCOMPARE(edit->persistentSelection(), true);
1114     QCOMPARE(spy.count(), 1);
1115
1116     edit->select(1, 4);
1117     QCOMPARE(edit->property("selected").toString(), QLatin1String("ell"));
1118
1119     edit->setFocus(false);
1120     QCOMPARE(edit->property("selected").toString(), QLatin1String("ell"));
1121
1122     edit->setFocus(true);
1123     QCOMPARE(edit->property("selected").toString(), QLatin1String("ell"));
1124
1125 }
1126
1127 void tst_qquicktextedit::focusOnPress()
1128 {
1129     QString componentStr =
1130             "import QtQuick 2.0\n"
1131             "TextEdit {\n"
1132                 "property bool selectOnFocus: false\n"
1133                 "width: 100; height: 50\n"
1134                 "activeFocusOnPress: true\n"
1135                 "text: \"Hello World\"\n"
1136                 "onFocusChanged: { if (focus && selectOnFocus) selectAll() }"
1137             " }";
1138     QQmlComponent texteditComponent(&engine);
1139     texteditComponent.setData(componentStr.toLatin1(), QUrl());
1140     QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
1141     QVERIFY(textEditObject != 0);
1142     QCOMPARE(textEditObject->focusOnPress(), true);
1143     QCOMPARE(textEditObject->hasFocus(), false);
1144
1145     QSignalSpy focusSpy(textEditObject, SIGNAL(focusChanged(bool)));
1146     QSignalSpy activeFocusSpy(textEditObject, SIGNAL(focusChanged(bool)));
1147     QSignalSpy activeFocusOnPressSpy(textEditObject, SIGNAL(activeFocusOnPressChanged(bool)));
1148
1149     textEditObject->setFocusOnPress(true);
1150     QCOMPARE(textEditObject->focusOnPress(), true);
1151     QCOMPARE(activeFocusOnPressSpy.count(), 0);
1152
1153     QQuickCanvas canvas;
1154     canvas.resize(100, 50);
1155     textEditObject->setParentItem(canvas.rootItem());
1156     canvas.show();
1157     canvas.requestActivateWindow();
1158     QTest::qWaitForWindowActive(&canvas);
1159
1160     QCOMPARE(textEditObject->hasFocus(), false);
1161     QCOMPARE(textEditObject->hasActiveFocus(), false);
1162
1163     QPoint centerPoint(canvas.width()/2, canvas.height()/2);
1164     Qt::KeyboardModifiers noModifiers = 0;
1165     QTest::mousePress(&canvas, Qt::LeftButton, noModifiers, centerPoint);
1166     QGuiApplication::processEvents();
1167     QCOMPARE(textEditObject->hasFocus(), true);
1168     QCOMPARE(textEditObject->hasActiveFocus(), true);
1169     QCOMPARE(focusSpy.count(), 1);
1170     QCOMPARE(activeFocusSpy.count(), 1);
1171     QCOMPARE(textEditObject->selectedText(), QString());
1172     QTest::mouseRelease(&canvas, Qt::LeftButton, noModifiers, centerPoint);
1173
1174     textEditObject->setFocusOnPress(false);
1175     QCOMPARE(textEditObject->focusOnPress(), false);
1176     QCOMPARE(activeFocusOnPressSpy.count(), 1);
1177
1178     textEditObject->setFocus(false);
1179     QCOMPARE(textEditObject->hasFocus(), false);
1180     QCOMPARE(textEditObject->hasActiveFocus(), false);
1181     QCOMPARE(focusSpy.count(), 2);
1182     QCOMPARE(activeFocusSpy.count(), 2);
1183
1184     // Wait for double click timeout to expire before clicking again.
1185     QTest::qWait(400);
1186     QTest::mousePress(&canvas, Qt::LeftButton, noModifiers, centerPoint);
1187     QGuiApplication::processEvents();
1188     QCOMPARE(textEditObject->hasFocus(), false);
1189     QCOMPARE(textEditObject->hasActiveFocus(), false);
1190     QCOMPARE(focusSpy.count(), 2);
1191     QCOMPARE(activeFocusSpy.count(), 2);
1192     QTest::mouseRelease(&canvas, Qt::LeftButton, noModifiers, centerPoint);
1193
1194     textEditObject->setFocusOnPress(true);
1195     QCOMPARE(textEditObject->focusOnPress(), true);
1196     QCOMPARE(activeFocusOnPressSpy.count(), 2);
1197
1198     // Test a selection made in the on(Active)FocusChanged handler isn't overwritten.
1199     textEditObject->setProperty("selectOnFocus", true);
1200
1201     QTest::qWait(400);
1202     QTest::mousePress(&canvas, Qt::LeftButton, noModifiers, centerPoint);
1203     QGuiApplication::processEvents();
1204     QCOMPARE(textEditObject->hasFocus(), true);
1205     QCOMPARE(textEditObject->hasActiveFocus(), true);
1206     QCOMPARE(focusSpy.count(), 3);
1207     QCOMPARE(activeFocusSpy.count(), 3);
1208     QCOMPARE(textEditObject->selectedText(), textEditObject->text());
1209     QTest::mouseRelease(&canvas, Qt::LeftButton, noModifiers, centerPoint);
1210 }
1211
1212 void tst_qquicktextedit::selection()
1213 {
1214     QString testStr = standard[0];//TODO: What should happen for multiline/rich text?
1215     QString componentStr = "import QtQuick 2.0\nTextEdit {  text: \""+ testStr +"\"; }";
1216     QQmlComponent texteditComponent(&engine);
1217     texteditComponent.setData(componentStr.toLatin1(), QUrl());
1218     QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
1219     QVERIFY(textEditObject != 0);
1220
1221
1222     //Test selection follows cursor
1223     for (int i=0; i<= testStr.size(); i++) {
1224         textEditObject->setCursorPosition(i);
1225         QCOMPARE(textEditObject->cursorPosition(), i);
1226         QCOMPARE(textEditObject->selectionStart(), i);
1227         QCOMPARE(textEditObject->selectionEnd(), i);
1228         QVERIFY(textEditObject->selectedText().isNull());
1229     }
1230
1231     textEditObject->setCursorPosition(0);
1232     QVERIFY(textEditObject->cursorPosition() == 0);
1233     QVERIFY(textEditObject->selectionStart() == 0);
1234     QVERIFY(textEditObject->selectionEnd() == 0);
1235     QVERIFY(textEditObject->selectedText().isNull());
1236
1237     // Verify invalid positions are ignored.
1238     textEditObject->setCursorPosition(-1);
1239     QVERIFY(textEditObject->cursorPosition() == 0);
1240     QVERIFY(textEditObject->selectionStart() == 0);
1241     QVERIFY(textEditObject->selectionEnd() == 0);
1242     QVERIFY(textEditObject->selectedText().isNull());
1243
1244     textEditObject->setCursorPosition(textEditObject->text().count()+1);
1245     QVERIFY(textEditObject->cursorPosition() == 0);
1246     QVERIFY(textEditObject->selectionStart() == 0);
1247     QVERIFY(textEditObject->selectionEnd() == 0);
1248     QVERIFY(textEditObject->selectedText().isNull());
1249
1250     //Test selection
1251     for (int i=0; i<= testStr.size(); i++) {
1252         textEditObject->select(0,i);
1253         QCOMPARE(testStr.mid(0,i), textEditObject->selectedText());
1254     }
1255     for (int i=0; i<= testStr.size(); i++) {
1256         textEditObject->select(i,testStr.size());
1257         QCOMPARE(testStr.mid(i,testStr.size()-i), textEditObject->selectedText());
1258     }
1259
1260     textEditObject->setCursorPosition(0);
1261     QVERIFY(textEditObject->cursorPosition() == 0);
1262     QVERIFY(textEditObject->selectionStart() == 0);
1263     QVERIFY(textEditObject->selectionEnd() == 0);
1264     QVERIFY(textEditObject->selectedText().isNull());
1265
1266     //Test Error Ignoring behaviour
1267     textEditObject->setCursorPosition(0);
1268     QVERIFY(textEditObject->selectedText().isNull());
1269     textEditObject->select(-10,0);
1270     QVERIFY(textEditObject->selectedText().isNull());
1271     textEditObject->select(100,101);
1272     QVERIFY(textEditObject->selectedText().isNull());
1273     textEditObject->select(0,-10);
1274     QVERIFY(textEditObject->selectedText().isNull());
1275     textEditObject->select(0,100);
1276     QVERIFY(textEditObject->selectedText().isNull());
1277     textEditObject->select(0,10);
1278     QVERIFY(textEditObject->selectedText().size() == 10);
1279     textEditObject->select(-10,0);
1280     QVERIFY(textEditObject->selectedText().size() == 10);
1281     textEditObject->select(100,101);
1282     QVERIFY(textEditObject->selectedText().size() == 10);
1283     textEditObject->select(0,-10);
1284     QVERIFY(textEditObject->selectedText().size() == 10);
1285     textEditObject->select(0,100);
1286     QVERIFY(textEditObject->selectedText().size() == 10);
1287
1288     textEditObject->deselect();
1289     QVERIFY(textEditObject->selectedText().isNull());
1290     textEditObject->select(0,10);
1291     QVERIFY(textEditObject->selectedText().size() == 10);
1292     textEditObject->deselect();
1293     QVERIFY(textEditObject->selectedText().isNull());
1294 }
1295
1296 void tst_qquicktextedit::isRightToLeft_data()
1297 {
1298     QTest::addColumn<QString>("text");
1299     QTest::addColumn<bool>("emptyString");
1300     QTest::addColumn<bool>("firstCharacter");
1301     QTest::addColumn<bool>("lastCharacter");
1302     QTest::addColumn<bool>("middleCharacter");
1303     QTest::addColumn<bool>("startString");
1304     QTest::addColumn<bool>("midString");
1305     QTest::addColumn<bool>("endString");
1306
1307     const quint16 arabic_str[] = { 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0647};
1308     QTest::newRow("Empty") << "" << false << false << false << false << false << false << false;
1309     QTest::newRow("Neutral") << "23244242" << false << false << false << false << false << false << false;
1310     QTest::newRow("LTR") << "Hello world" << false << false << false << false << false << false << false;
1311     QTest::newRow("RTL") << QString::fromUtf16(arabic_str, 11) << false << true << true << true << true << true << true;
1312     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;
1313     QTest::newRow("Bidi LTR + RTL + LTR") << QString("Hello world") + QString::fromUtf16(arabic_str, 11) + QString("Hello world") << false << false << false << true << false << false << false;
1314 }
1315
1316 void tst_qquicktextedit::isRightToLeft()
1317 {
1318     QFETCH(QString, text);
1319     QFETCH(bool, emptyString);
1320     QFETCH(bool, firstCharacter);
1321     QFETCH(bool, lastCharacter);
1322     QFETCH(bool, middleCharacter);
1323     QFETCH(bool, startString);
1324     QFETCH(bool, midString);
1325     QFETCH(bool, endString);
1326
1327     QQuickTextEdit textEdit;
1328     textEdit.setText(text);
1329
1330     // first test that the right string is delivered to the QString::isRightToLeft()
1331     QCOMPARE(textEdit.isRightToLeft(0,0), text.mid(0,0).isRightToLeft());
1332     QCOMPARE(textEdit.isRightToLeft(0,1), text.mid(0,1).isRightToLeft());
1333     QCOMPARE(textEdit.isRightToLeft(text.count()-2, text.count()-1), text.mid(text.count()-2, text.count()-1).isRightToLeft());
1334     QCOMPARE(textEdit.isRightToLeft(text.count()/2, text.count()/2 + 1), text.mid(text.count()/2, text.count()/2 + 1).isRightToLeft());
1335     QCOMPARE(textEdit.isRightToLeft(0,text.count()/4), text.mid(0,text.count()/4).isRightToLeft());
1336     QCOMPARE(textEdit.isRightToLeft(text.count()/4,3*text.count()/4), text.mid(text.count()/4,3*text.count()/4).isRightToLeft());
1337     if (text.isEmpty())
1338         QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML TextEdit: isRightToLeft(start, end) called with the end property being smaller than the start.");
1339     QCOMPARE(textEdit.isRightToLeft(3*text.count()/4,text.count()-1), text.mid(3*text.count()/4,text.count()-1).isRightToLeft());
1340
1341     // then test that the feature actually works
1342     QCOMPARE(textEdit.isRightToLeft(0,0), emptyString);
1343     QCOMPARE(textEdit.isRightToLeft(0,1), firstCharacter);
1344     QCOMPARE(textEdit.isRightToLeft(text.count()-2, text.count()-1), lastCharacter);
1345     QCOMPARE(textEdit.isRightToLeft(text.count()/2, text.count()/2 + 1), middleCharacter);
1346     QCOMPARE(textEdit.isRightToLeft(0,text.count()/4), startString);
1347     QCOMPARE(textEdit.isRightToLeft(text.count()/4,3*text.count()/4), midString);
1348     if (text.isEmpty())
1349         QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML TextEdit: isRightToLeft(start, end) called with the end property being smaller than the start.");
1350     QCOMPARE(textEdit.isRightToLeft(3*text.count()/4,text.count()-1), endString);
1351 }
1352
1353 void tst_qquicktextedit::keySelection()
1354 {
1355     QQuickView canvas(testFileUrl("navigation.qml"));
1356     canvas.show();
1357     canvas.requestActivateWindow();
1358     QTest::qWaitForWindowActive(&canvas);
1359
1360     QVERIFY(canvas.rootObject() != 0);
1361
1362     QQuickTextEdit *input = qobject_cast<QQuickTextEdit *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
1363
1364     QVERIFY(input != 0);
1365     QVERIFY(input->hasActiveFocus());
1366
1367     QSignalSpy spy(input, SIGNAL(selectedTextChanged()));
1368
1369     simulateKey(&canvas, Qt::Key_Right, Qt::ShiftModifier);
1370     QVERIFY(input->hasActiveFocus() == true);
1371     QCOMPARE(input->selectedText(), QString("a"));
1372     QCOMPARE(spy.count(), 1);
1373     simulateKey(&canvas, Qt::Key_Right);
1374     QVERIFY(input->hasActiveFocus() == true);
1375     QCOMPARE(input->selectedText(), QString());
1376     QCOMPARE(spy.count(), 2);
1377     simulateKey(&canvas, Qt::Key_Right);
1378     QVERIFY(input->hasActiveFocus() == false);
1379     QCOMPARE(input->selectedText(), QString());
1380     QCOMPARE(spy.count(), 2);
1381
1382     simulateKey(&canvas, Qt::Key_Left);
1383     QVERIFY(input->hasActiveFocus() == true);
1384     QCOMPARE(spy.count(), 2);
1385     simulateKey(&canvas, Qt::Key_Left, Qt::ShiftModifier);
1386     QVERIFY(input->hasActiveFocus() == true);
1387     QCOMPARE(input->selectedText(), QString("a"));
1388     QCOMPARE(spy.count(), 3);
1389     simulateKey(&canvas, Qt::Key_Left);
1390     QVERIFY(input->hasActiveFocus() == true);
1391     QCOMPARE(input->selectedText(), QString());
1392     QCOMPARE(spy.count(), 4);
1393     simulateKey(&canvas, Qt::Key_Left);
1394     QVERIFY(input->hasActiveFocus() == false);
1395     QCOMPARE(input->selectedText(), QString());
1396     QCOMPARE(spy.count(), 4);
1397 }
1398
1399 void tst_qquicktextedit::moveCursorSelection_data()
1400 {
1401     QTest::addColumn<QString>("testStr");
1402     QTest::addColumn<int>("cursorPosition");
1403     QTest::addColumn<int>("movePosition");
1404     QTest::addColumn<QQuickTextEdit::SelectionMode>("mode");
1405     QTest::addColumn<int>("selectionStart");
1406     QTest::addColumn<int>("selectionEnd");
1407     QTest::addColumn<bool>("reversible");
1408
1409     QTest::newRow("(t)he|characters")
1410             << standard[0] << 0 << 1 << QQuickTextEdit::SelectCharacters << 0 << 1 << true;
1411     QTest::newRow("do(g)|characters")
1412             << standard[0] << 43 << 44 << QQuickTextEdit::SelectCharacters << 43 << 44 << true;
1413     QTest::newRow("jum(p)ed|characters")
1414             << standard[0] << 23 << 24 << QQuickTextEdit::SelectCharacters << 23 << 24 << true;
1415     QTest::newRow("jumped( )over|characters")
1416             << standard[0] << 26 << 27 << QQuickTextEdit::SelectCharacters << 26 << 27 << true;
1417     QTest::newRow("(the )|characters")
1418             << standard[0] << 0 << 4 << QQuickTextEdit::SelectCharacters << 0 << 4 << true;
1419     QTest::newRow("( dog)|characters")
1420             << standard[0] << 40 << 44 << QQuickTextEdit::SelectCharacters << 40 << 44 << true;
1421     QTest::newRow("( jumped )|characters")
1422             << standard[0] << 19 << 27 << QQuickTextEdit::SelectCharacters << 19 << 27 << true;
1423     QTest::newRow("th(e qu)ick|characters")
1424             << standard[0] << 2 << 6 << QQuickTextEdit::SelectCharacters << 2 << 6 << true;
1425     QTest::newRow("la(zy d)og|characters")
1426             << standard[0] << 38 << 42 << QQuickTextEdit::SelectCharacters << 38 << 42 << true;
1427     QTest::newRow("jum(ped ov)er|characters")
1428             << standard[0] << 23 << 29 << QQuickTextEdit::SelectCharacters << 23 << 29 << true;
1429     QTest::newRow("()the|characters")
1430             << standard[0] << 0 << 0 << QQuickTextEdit::SelectCharacters << 0 << 0 << true;
1431     QTest::newRow("dog()|characters")
1432             << standard[0] << 44 << 44 << QQuickTextEdit::SelectCharacters << 44 << 44 << true;
1433     QTest::newRow("jum()ped|characters")
1434             << standard[0] << 23 << 23 << QQuickTextEdit::SelectCharacters << 23 << 23 << true;
1435
1436     QTest::newRow("<(t)he>|words")
1437             << standard[0] << 0 << 1 << QQuickTextEdit::SelectWords << 0 << 3 << true;
1438     QTest::newRow("<do(g)>|words")
1439             << standard[0] << 43 << 44 << QQuickTextEdit::SelectWords << 41 << 44 << true;
1440     QTest::newRow("<jum(p)ed>|words")
1441             << standard[0] << 23 << 24 << QQuickTextEdit::SelectWords << 20 << 26 << true;
1442     QTest::newRow("<jumped( )>over|words")
1443             << standard[0] << 26 << 27 << QQuickTextEdit::SelectWords << 20 << 27 << false;
1444     QTest::newRow("jumped<( )over>|words,reversed")
1445             << standard[0] << 27 << 26 << QQuickTextEdit::SelectWords << 26 << 31 << false;
1446     QTest::newRow("<(the )>quick|words")
1447             << standard[0] << 0 << 4 << QQuickTextEdit::SelectWords << 0 << 4 << false;
1448     QTest::newRow("<(the )quick>|words,reversed")
1449             << standard[0] << 4 << 0 << QQuickTextEdit::SelectWords << 0 << 9 << false;
1450     QTest::newRow("<lazy( dog)>|words")
1451             << standard[0] << 40 << 44 << QQuickTextEdit::SelectWords << 36 << 44 << false;
1452     QTest::newRow("lazy<( dog)>|words,reversed")
1453             << standard[0] << 44 << 40 << QQuickTextEdit::SelectWords << 40 << 44 << false;
1454     QTest::newRow("<fox( jumped )>over|words")
1455             << standard[0] << 19 << 27 << QQuickTextEdit::SelectWords << 16 << 27 << false;
1456     QTest::newRow("fox<( jumped )over>|words,reversed")
1457             << standard[0] << 27 << 19 << QQuickTextEdit::SelectWords << 19 << 31 << false;
1458     QTest::newRow("<th(e qu)ick>|words")
1459             << standard[0] << 2 << 6 << QQuickTextEdit::SelectWords << 0 << 9 << true;
1460     QTest::newRow("<la(zy d)og|words>")
1461             << standard[0] << 38 << 42 << QQuickTextEdit::SelectWords << 36 << 44 << true;
1462     QTest::newRow("<jum(ped ov)er>|words")
1463             << standard[0] << 23 << 29 << QQuickTextEdit::SelectWords << 20 << 31 << true;
1464     QTest::newRow("<()>the|words")
1465             << standard[0] << 0 << 0 << QQuickTextEdit::SelectWords << 0 << 0 << true;
1466     QTest::newRow("dog<()>|words")
1467             << standard[0] << 44 << 44 << QQuickTextEdit::SelectWords << 44 << 44 << true;
1468     QTest::newRow("jum<()>ped|words")
1469             << standard[0] << 23 << 23 << QQuickTextEdit::SelectWords << 23 << 23 << true;
1470
1471     QTest::newRow("Hello<(,)> |words")
1472             << standard[2] << 5 << 6 << QQuickTextEdit::SelectWords << 5 << 6 << true;
1473     QTest::newRow("Hello<(, )>world|words")
1474             << standard[2] << 5 << 7 << QQuickTextEdit::SelectWords << 5 << 7 << false;
1475     QTest::newRow("Hello<(, )world>|words,reversed")
1476             << standard[2] << 7 << 5 << QQuickTextEdit::SelectWords << 5 << 12 << false;
1477     QTest::newRow("<Hel(lo, )>world|words")
1478             << standard[2] << 3 << 7 << QQuickTextEdit::SelectWords << 0 << 7 << false;
1479     QTest::newRow("<Hel(lo, )world>|words,reversed")
1480             << standard[2] << 7 << 3 << QQuickTextEdit::SelectWords << 0 << 12 << false;
1481     QTest::newRow("<Hel(lo)>,|words")
1482             << standard[2] << 3 << 5 << QQuickTextEdit::SelectWords << 0 << 5 << true;
1483     QTest::newRow("Hello<()>,|words")
1484             << standard[2] << 5 << 5 << QQuickTextEdit::SelectWords << 5 << 5 << true;
1485     QTest::newRow("Hello,<()>|words")
1486             << standard[2] << 6 << 6 << QQuickTextEdit::SelectWords << 6 << 6 << true;
1487     QTest::newRow("Hello<,( )>world|words")
1488             << standard[2] << 6 << 7 << QQuickTextEdit::SelectWords << 5 << 7 << false;
1489     QTest::newRow("Hello,<( )world>|words,reversed")
1490             << standard[2] << 7 << 6 << QQuickTextEdit::SelectWords << 6 << 12 << false;
1491     QTest::newRow("Hello<,( world)>|words")
1492             << standard[2] << 6 << 12 << QQuickTextEdit::SelectWords << 5 << 12 << false;
1493     QTest::newRow("Hello,<( world)>|words,reversed")
1494             << standard[2] << 12 << 6 << QQuickTextEdit::SelectWords << 6 << 12 << false;
1495     QTest::newRow("Hello<,( world!)>|words")
1496             << standard[2] << 6 << 13 << QQuickTextEdit::SelectWords << 5 << 13 << false;
1497     QTest::newRow("Hello,<( world!)>|words,reversed")
1498             << standard[2] << 13 << 6 << QQuickTextEdit::SelectWords << 6 << 13 << false;
1499     QTest::newRow("Hello<(, world!)>|words")
1500             << standard[2] << 5 << 13 << QQuickTextEdit::SelectWords << 5 << 13 << true;
1501     QTest::newRow("world<(!)>|words")
1502             << standard[2] << 12 << 13 << QQuickTextEdit::SelectWords << 12 << 13 << true;
1503     QTest::newRow("world!<()>)|words")
1504             << standard[2] << 13 << 13 << QQuickTextEdit::SelectWords << 13 << 13 << true;
1505     QTest::newRow("world<()>!)|words")
1506             << standard[2] << 12 << 12 << QQuickTextEdit::SelectWords << 12 << 12 << true;
1507
1508     QTest::newRow("<(,)>olleH |words")
1509             << standard[3] << 7 << 8 << QQuickTextEdit::SelectWords << 7 << 8 << true;
1510     QTest::newRow("<dlrow( ,)>olleH|words")
1511             << standard[3] << 6 << 8 << QQuickTextEdit::SelectWords << 1 << 8 << false;
1512     QTest::newRow("dlrow<( ,)>olleH|words,reversed")
1513             << standard[3] << 8 << 6 << QQuickTextEdit::SelectWords << 6 << 8 << false;
1514     QTest::newRow("<dlrow( ,ol)leH>|words")
1515             << standard[3] << 6 << 10 << QQuickTextEdit::SelectWords << 1 << 13 << false;
1516     QTest::newRow("dlrow<( ,ol)leH>|words,reversed")
1517             << standard[3] << 10 << 6 << QQuickTextEdit::SelectWords << 6 << 13 << false;
1518     QTest::newRow(",<(ol)leH>,|words")
1519             << standard[3] << 8 << 10 << QQuickTextEdit::SelectWords << 8 << 13 << true;
1520     QTest::newRow(",<()>olleH|words")
1521             << standard[3] << 8 << 8 << QQuickTextEdit::SelectWords << 8 << 8 << true;
1522     QTest::newRow("<()>,olleH|words")
1523             << standard[3] << 7 << 7 << QQuickTextEdit::SelectWords << 7 << 7 << true;
1524     QTest::newRow("<dlrow( )>,olleH|words")
1525             << standard[3] << 6 << 7 << QQuickTextEdit::SelectWords << 1 << 7 << false;
1526     QTest::newRow("dlrow<( ),>olleH|words,reversed")
1527             << standard[3] << 7 << 6 << QQuickTextEdit::SelectWords << 6 << 8 << false;
1528     QTest::newRow("<(dlrow )>,olleH|words")
1529             << standard[3] << 1 << 7 << QQuickTextEdit::SelectWords << 1 << 7 << false;
1530     QTest::newRow("<(dlrow ),>olleH|words,reversed")
1531             << standard[3] << 7 << 1 << QQuickTextEdit::SelectWords << 1 << 8 << false;
1532     QTest::newRow("<(!dlrow )>,olleH|words")
1533             << standard[3] << 0 << 7 << QQuickTextEdit::SelectWords << 0 << 7 << false;
1534     QTest::newRow("<(!dlrow ),>olleH|words,reversed")
1535             << standard[3] << 7 << 0 << QQuickTextEdit::SelectWords << 0 << 8 << false;
1536     QTest::newRow("(!dlrow ,)olleH|words")
1537             << standard[3] << 0 << 8 << QQuickTextEdit::SelectWords << 0 << 8 << true;
1538     QTest::newRow("<(!)>dlrow|words")
1539             << standard[3] << 0 << 1 << QQuickTextEdit::SelectWords << 0 << 1 << true;
1540     QTest::newRow("<()>!dlrow|words")
1541             << standard[3] << 0 << 0 << QQuickTextEdit::SelectWords << 0 << 0 << true;
1542     QTest::newRow("!<()>dlrow|words")
1543             << standard[3] << 1 << 1 << QQuickTextEdit::SelectWords << 1 << 1 << true;
1544 }
1545
1546 void tst_qquicktextedit::moveCursorSelection()
1547 {
1548     QFETCH(QString, testStr);
1549     QFETCH(int, cursorPosition);
1550     QFETCH(int, movePosition);
1551     QFETCH(QQuickTextEdit::SelectionMode, mode);
1552     QFETCH(int, selectionStart);
1553     QFETCH(int, selectionEnd);
1554     QFETCH(bool, reversible);
1555
1556     QString componentStr = "import QtQuick 2.0\nTextEdit {  text: \""+ testStr +"\"; }";
1557     QQmlComponent textinputComponent(&engine);
1558     textinputComponent.setData(componentStr.toLatin1(), QUrl());
1559     QQuickTextEdit *texteditObject = qobject_cast<QQuickTextEdit*>(textinputComponent.create());
1560     QVERIFY(texteditObject != 0);
1561
1562     texteditObject->setCursorPosition(cursorPosition);
1563     texteditObject->moveCursorSelection(movePosition, mode);
1564
1565     QCOMPARE(texteditObject->selectedText(), testStr.mid(selectionStart, selectionEnd - selectionStart));
1566     QCOMPARE(texteditObject->selectionStart(), selectionStart);
1567     QCOMPARE(texteditObject->selectionEnd(), selectionEnd);
1568
1569     if (reversible) {
1570         texteditObject->setCursorPosition(movePosition);
1571         texteditObject->moveCursorSelection(cursorPosition, mode);
1572
1573         QCOMPARE(texteditObject->selectedText(), testStr.mid(selectionStart, selectionEnd - selectionStart));
1574         QCOMPARE(texteditObject->selectionStart(), selectionStart);
1575         QCOMPARE(texteditObject->selectionEnd(), selectionEnd);
1576     }
1577 }
1578
1579 void tst_qquicktextedit::moveCursorSelectionSequence_data()
1580 {
1581     QTest::addColumn<QString>("testStr");
1582     QTest::addColumn<int>("cursorPosition");
1583     QTest::addColumn<int>("movePosition1");
1584     QTest::addColumn<int>("movePosition2");
1585     QTest::addColumn<int>("selection1Start");
1586     QTest::addColumn<int>("selection1End");
1587     QTest::addColumn<int>("selection2Start");
1588     QTest::addColumn<int>("selection2End");
1589
1590     QTest::newRow("the {<quick( bro)wn> f^ox} jumped|ltr")
1591             << standard[0]
1592             << 9 << 13 << 17
1593             << 4 << 15
1594             << 4 << 19;
1595     QTest::newRow("the quick<( {bro)wn> f^ox} jumped|rtl")
1596             << standard[0]
1597             << 13 << 9 << 17
1598             << 9 << 15
1599             << 10 << 19;
1600     QTest::newRow("the {<quick( bro)wn> ^}fox jumped|ltr")
1601             << standard[0]
1602             << 9 << 13 << 16
1603             << 4 << 15
1604             << 4 << 16;
1605     QTest::newRow("the quick<( {bro)wn> ^}fox jumped|rtl")
1606             << standard[0]
1607             << 13 << 9 << 16
1608             << 9 << 15
1609             << 10 << 16;
1610     QTest::newRow("the {<quick( bro)wn^>} fox jumped|ltr")
1611             << standard[0]
1612             << 9 << 13 << 15
1613             << 4 << 15
1614             << 4 << 15;
1615     QTest::newRow("the quick<( {bro)wn^>} f^ox jumped|rtl")
1616             << standard[0]
1617             << 13 << 9 << 15
1618             << 9 << 15
1619             << 10 << 15;
1620     QTest::newRow("the {<quick() ^}bro)wn> fox|ltr")
1621             << standard[0]
1622             << 9 << 13 << 10
1623             << 4 << 15
1624             << 4 << 10;
1625     QTest::newRow("the quick<(^ {^bro)wn>} fox|rtl")
1626             << standard[0]
1627             << 13 << 9 << 10
1628             << 9 << 15
1629             << 10 << 15;
1630     QTest::newRow("the {<quick^}( bro)wn> fox|ltr")
1631             << standard[0]
1632             << 9 << 13 << 9
1633             << 4 << 15
1634             << 4 << 9;
1635     QTest::newRow("the quick{<(^ bro)wn>} fox|rtl")
1636             << standard[0]
1637             << 13 << 9 << 9
1638             << 9 << 15
1639             << 9 << 15;
1640     QTest::newRow("the {<qui^ck}( bro)wn> fox|ltr")
1641             << standard[0]
1642             << 9 << 13 << 7
1643             << 4 << 15
1644             << 4 << 9;
1645     QTest::newRow("the {<qui^ck}( bro)wn> fox|rtl")
1646             << standard[0]
1647             << 13 << 9 << 7
1648             << 9 << 15
1649             << 4 << 15;
1650     QTest::newRow("the {<^quick}( bro)wn> fox|ltr")
1651             << standard[0]
1652             << 9 << 13 << 4
1653             << 4 << 15
1654             << 4 << 9;
1655     QTest::newRow("the {<^quick}( bro)wn> fox|rtl")
1656             << standard[0]
1657             << 13 << 9 << 4
1658             << 9 << 15
1659             << 4 << 15;
1660     QTest::newRow("the{^ <quick}( bro)wn> fox|ltr")
1661             << standard[0]
1662             << 9 << 13 << 3
1663             << 4 << 15
1664             << 3 << 9;
1665     QTest::newRow("the{^ <quick}( bro)wn> fox|rtl")
1666             << standard[0]
1667             << 13 << 9 << 3
1668             << 9 << 15
1669             << 3 << 15;
1670     QTest::newRow("{t^he <quick}( bro)wn> fox|ltr")
1671             << standard[0]
1672             << 9 << 13 << 1
1673             << 4 << 15
1674             << 0 << 9;
1675     QTest::newRow("{t^he <quick}( bro)wn> fox|rtl")
1676             << standard[0]
1677             << 13 << 9 << 1
1678             << 9 << 15
1679             << 0 << 15;
1680
1681     QTest::newRow("{<He(ll)o>, w^orld}!|ltr")
1682             << standard[2]
1683             << 2 << 4 << 8
1684             << 0 << 5
1685             << 0 << 12;
1686     QTest::newRow("{<He(ll)o>, w^orld}!|rtl")
1687             << standard[2]
1688             << 4 << 2 << 8
1689             << 0 << 5
1690             << 0 << 12;
1691
1692     QTest::newRow("!{dlro^w ,<o(ll)eH>}|ltr")
1693             << standard[3]
1694             << 9 << 11 << 5
1695             << 8 << 13
1696             << 1 << 13;
1697     QTest::newRow("!{dlro^w ,<o(ll)eH>}|rtl")
1698             << standard[3]
1699             << 11 << 9 << 5
1700             << 8 << 13
1701             << 1 << 13;
1702 }
1703
1704 void tst_qquicktextedit::moveCursorSelectionSequence()
1705 {
1706     QFETCH(QString, testStr);
1707     QFETCH(int, cursorPosition);
1708     QFETCH(int, movePosition1);
1709     QFETCH(int, movePosition2);
1710     QFETCH(int, selection1Start);
1711     QFETCH(int, selection1End);
1712     QFETCH(int, selection2Start);
1713     QFETCH(int, selection2End);
1714
1715     QString componentStr = "import QtQuick 2.0\nTextEdit {  text: \""+ testStr +"\"; }";
1716     QQmlComponent texteditComponent(&engine);
1717     texteditComponent.setData(componentStr.toLatin1(), QUrl());
1718     QQuickTextEdit *texteditObject = qobject_cast<QQuickTextEdit*>(texteditComponent.create());
1719     QVERIFY(texteditObject != 0);
1720
1721     texteditObject->setCursorPosition(cursorPosition);
1722
1723     texteditObject->moveCursorSelection(movePosition1, QQuickTextEdit::SelectWords);
1724     QCOMPARE(texteditObject->selectedText(), testStr.mid(selection1Start, selection1End - selection1Start));
1725     QCOMPARE(texteditObject->selectionStart(), selection1Start);
1726     QCOMPARE(texteditObject->selectionEnd(), selection1End);
1727
1728     texteditObject->moveCursorSelection(movePosition2, QQuickTextEdit::SelectWords);
1729     QCOMPARE(texteditObject->selectedText(), testStr.mid(selection2Start, selection2End - selection2Start));
1730     QCOMPARE(texteditObject->selectionStart(), selection2Start);
1731     QCOMPARE(texteditObject->selectionEnd(), selection2End);
1732 }
1733
1734
1735 void tst_qquicktextedit::mouseSelection_data()
1736 {
1737     QTest::addColumn<QString>("qmlfile");
1738     QTest::addColumn<int>("from");
1739     QTest::addColumn<int>("to");
1740     QTest::addColumn<QString>("selectedText");
1741     QTest::addColumn<bool>("focus");
1742     QTest::addColumn<bool>("focusOnPress");
1743     QTest::addColumn<int>("clicks");
1744
1745     // import installed
1746     QTest::newRow("on") << testFile("mouseselection_true.qml") << 4 << 9 << "45678" << true << true << 1;
1747     QTest::newRow("off") << testFile("mouseselection_false.qml") << 4 << 9 << QString() << true << true << 1;
1748     QTest::newRow("default") << testFile("mouseselection_default.qml") << 4 << 9 << QString() << true << true << 1;
1749     QTest::newRow("off word selection") << testFile("mouseselection_false_words.qml") << 4 << 9 << QString() << true << true << 1;
1750     QTest::newRow("on word selection (4,9)") << testFile("mouseselection_true_words.qml") << 4 << 9 << "0123456789" << true << true << 1;
1751
1752     QTest::newRow("on unfocused") << testFile("mouseselection_true.qml") << 4 << 9 << "45678" << false << false << 1;
1753     QTest::newRow("on word selection (4,9) unfocused") << testFile("mouseselection_true_words.qml") << 4 << 9 << "0123456789" << false << false << 1;
1754
1755     QTest::newRow("on focus on press") << testFile("mouseselection_true.qml") << 4 << 9 << "45678" << false << true << 1;
1756     QTest::newRow("on word selection (4,9) focus on press") << testFile("mouseselection_true_words.qml") << 4 << 9 << "0123456789" << false << true << 1;
1757
1758     QTest::newRow("on word selection (2,13)") << testFile("mouseselection_true_words.qml") << 2 << 13 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << 1;
1759     QTest::newRow("on word selection (2,30)") << testFile("mouseselection_true_words.qml") << 2 << 30 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << 1;
1760     QTest::newRow("on word selection (9,13)") << testFile("mouseselection_true_words.qml") << 9 << 13 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << 1;
1761     QTest::newRow("on word selection (9,30)") << testFile("mouseselection_true_words.qml") << 9 << 30 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << 1;
1762     QTest::newRow("on word selection (13,2)") << testFile("mouseselection_true_words.qml") << 13 << 2 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << 1;
1763     QTest::newRow("on word selection (20,2)") << testFile("mouseselection_true_words.qml") << 20 << 2 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << 1;
1764     QTest::newRow("on word selection (12,9)") << testFile("mouseselection_true_words.qml") << 12 << 9 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << 1;
1765     QTest::newRow("on word selection (30,9)") << testFile("mouseselection_true_words.qml") << 30 << 9 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << 1;
1766
1767     QTest::newRow("on double click (4,9)") << testFile("mouseselection_true.qml") << 4 << 9 << "0123456789" << true << true << 2;
1768     QTest::newRow("on double click (2,13)") << testFile("mouseselection_true.qml") << 2 << 13 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << 2;
1769     QTest::newRow("on double click (2,30)") << testFile("mouseselection_true.qml") << 2 << 30 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << 2;
1770     QTest::newRow("on double click (9,13)") << testFile("mouseselection_true.qml") << 9 << 13 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << 2;
1771     QTest::newRow("on double click (9,30)") << testFile("mouseselection_true.qml") << 9 << 30 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << 2;
1772     QTest::newRow("on double click (13,2)") << testFile("mouseselection_true.qml") << 13 << 2 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << 2;
1773     QTest::newRow("on double click (20,2)") << testFile("mouseselection_true.qml") << 20 << 2 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << 2;
1774     QTest::newRow("on double click (12,9)") << testFile("mouseselection_true.qml") << 12 << 9 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << 2;
1775     QTest::newRow("on double click (30,9)") << testFile("mouseselection_true.qml") << 30 << 9 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ" << true << true << 2;
1776
1777     QTest::newRow("on triple click (4,9)") << testFile("mouseselection_true.qml") << 4 << 9 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ\n" << true << true << 3;
1778     QTest::newRow("on triple click (2,13)") << testFile("mouseselection_true.qml") << 2 << 13 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ\n" << true << true << 3;
1779     QTest::newRow("on triple click (2,30)") << testFile("mouseselection_true.qml") << 2 << 30 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ\n" << true << true << 3;
1780     QTest::newRow("on triple click (9,13)") << testFile("mouseselection_true.qml") << 9 << 13 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ\n" << true << true << 3;
1781     QTest::newRow("on triple click (9,30)") << testFile("mouseselection_true.qml") << 9 << 30 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ\n" << true << true << 3;
1782     QTest::newRow("on triple click (13,2)") << testFile("mouseselection_true.qml") << 13 << 2 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ\n" << true << true << 3;
1783     QTest::newRow("on triple click (20,2)") << testFile("mouseselection_true.qml") << 20 << 2 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ\n" << true << true << 3;
1784     QTest::newRow("on triple click (12,9)") << testFile("mouseselection_true.qml") << 12 << 9 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ\n" << true << true << 3;
1785     QTest::newRow("on triple click (30,9)") << testFile("mouseselection_true.qml") << 30 << 9 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ\n" << true << true << 3;
1786
1787     QTest::newRow("on triple click (2,40)") << testFile("mouseselection_true.qml") << 2 << 40 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ\n9876543210\n" << true << true << 3;
1788     QTest::newRow("on triple click (2,50)") << testFile("mouseselection_true.qml") << 2 << 50 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ\n9876543210\n\nZXYWVUTSRQPON MLKJIHGFEDCBA" << true << true << 3;
1789     QTest::newRow("on triple click (25,40)") << testFile("mouseselection_true.qml") << 25 << 40 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ\n9876543210\n" << true << true << 3;
1790     QTest::newRow("on triple click (25,50)") << testFile("mouseselection_true.qml") << 25 << 50 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ\n9876543210\n\nZXYWVUTSRQPON MLKJIHGFEDCBA" << true << true << 3;
1791     QTest::newRow("on triple click (40,25)") << testFile("mouseselection_true.qml") << 40 << 25 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ\n9876543210\n" << true << true << 3;
1792     QTest::newRow("on triple click (40,50)") << testFile("mouseselection_true.qml") << 40 << 50 << "9876543210\n\nZXYWVUTSRQPON MLKJIHGFEDCBA" << true << true << 3;
1793     QTest::newRow("on triple click (50,25)") << testFile("mouseselection_true.qml") << 50 << 25 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ\n9876543210\n\nZXYWVUTSRQPON MLKJIHGFEDCBA" << true << true << 3;
1794     QTest::newRow("on triple click (50,40)") << testFile("mouseselection_true.qml") << 50 << 40 << "9876543210\n\nZXYWVUTSRQPON MLKJIHGFEDCBA" << true << true << 3;
1795
1796     QTest::newRow("on tr align") << testFile("mouseselection_align_tr.qml") << 4 << 9 << "45678" << true << true << 1;
1797     QTest::newRow("on center align") << testFile("mouseselection_align_center.qml") << 4 << 9 << "45678" << true << true << 1;
1798     QTest::newRow("on bl align") << testFile("mouseselection_align_bl.qml") << 4 << 9 << "45678" << true << true << 1;
1799 }
1800
1801 void tst_qquicktextedit::mouseSelection()
1802 {
1803     QFETCH(QString, qmlfile);
1804     QFETCH(int, from);
1805     QFETCH(int, to);
1806     QFETCH(QString, selectedText);
1807     QFETCH(bool, focus);
1808     QFETCH(bool, focusOnPress);
1809     QFETCH(int, clicks);
1810
1811     QQuickView canvas(QUrl::fromLocalFile(qmlfile));
1812
1813     canvas.show();
1814     canvas.requestActivateWindow();
1815     QTest::qWaitForWindowActive(&canvas);
1816
1817     QVERIFY(canvas.rootObject() != 0);
1818     QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit *>(canvas.rootObject());
1819     QVERIFY(textEditObject != 0);
1820
1821     textEditObject->setFocus(focus);
1822     textEditObject->setFocusOnPress(focusOnPress);
1823
1824     // press-and-drag-and-release from x1 to x2
1825     QPoint p1 = textEditObject->positionToRectangle(from).center().toPoint();
1826     QPoint p2 = textEditObject->positionToRectangle(to).center().toPoint();
1827     if (clicks == 2)
1828         QTest::mouseClick(&canvas, Qt::LeftButton, Qt::NoModifier, p1);
1829     else if (clicks == 3)
1830         QTest::mouseDClick(&canvas, Qt::LeftButton, Qt::NoModifier, p1);
1831     QTest::mousePress(&canvas, Qt::LeftButton, Qt::NoModifier, p1);
1832     QTest::mouseMove(&canvas, p2);
1833     QTest::mouseRelease(&canvas, Qt::LeftButton, Qt::NoModifier, p2);
1834     QTRY_COMPARE(textEditObject->selectedText(), selectedText);
1835
1836     // Clicking and shift to clicking between the same points should select the same text.
1837     textEditObject->setCursorPosition(0);
1838     if (clicks > 1)
1839         QTest::mouseDClick(&canvas, Qt::LeftButton, Qt::NoModifier, p1);
1840     if (clicks != 2)
1841         QTest::mouseClick(&canvas, Qt::LeftButton, Qt::NoModifier, p1);
1842     QTest::mouseClick(&canvas, Qt::LeftButton, Qt::ShiftModifier, p2);
1843     QTRY_COMPARE(textEditObject->selectedText(), selectedText);
1844
1845     // ### This is to prevent double click detection from carrying over to the next test.
1846     QTest::qWait(QGuiApplication::styleHints()->mouseDoubleClickInterval() + 10);
1847 }
1848
1849 void tst_qquicktextedit::dragMouseSelection()
1850 {
1851     QString qmlfile = testFile("mouseselection_true.qml");
1852
1853     QQuickView canvas(QUrl::fromLocalFile(qmlfile));
1854
1855     canvas.show();
1856     canvas.requestActivateWindow();
1857     QTest::qWaitForWindowActive(&canvas);
1858
1859     QVERIFY(canvas.rootObject() != 0);
1860     QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit *>(canvas.rootObject());
1861     QVERIFY(textEditObject != 0);
1862
1863     // press-and-drag-and-release from x1 to x2
1864     int x1 = 10;
1865     int x2 = 70;
1866     int y = QFontMetrics(textEditObject->font()).height() / 2;
1867     QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y));
1868     QTest::mouseMove(&canvas, QPoint(x2, y));
1869     QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y));
1870     QTest::qWait(300);
1871     QString str1;
1872     QTRY_VERIFY((str1 = textEditObject->selectedText()).length() > 3);
1873
1874     // press and drag the current selection.
1875     x1 = 40;
1876     x2 = 100;
1877     QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y));
1878     QTest::mouseMove(&canvas, QPoint(x2, y));
1879     QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y));
1880     QTest::qWait(300);
1881     QString str2;
1882     QTRY_VERIFY((str2 = textEditObject->selectedText()).length() > 3);
1883
1884     QVERIFY(str1 != str2); // Verify the second press and drag is a new selection and not the first moved.
1885
1886 }
1887
1888 void tst_qquicktextedit::mouseSelectionMode_data()
1889 {
1890     QTest::addColumn<QString>("qmlfile");
1891     QTest::addColumn<bool>("selectWords");
1892
1893     // import installed
1894     QTest::newRow("SelectWords") << testFile("mouseselectionmode_words.qml") << true;
1895     QTest::newRow("SelectCharacters") << testFile("mouseselectionmode_characters.qml") << false;
1896     QTest::newRow("default") << testFile("mouseselectionmode_default.qml") << false;
1897 }
1898
1899 void tst_qquicktextedit::mouseSelectionMode()
1900 {
1901     QFETCH(QString, qmlfile);
1902     QFETCH(bool, selectWords);
1903
1904     QString text = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1905
1906     QQuickView canvas(QUrl::fromLocalFile(qmlfile));
1907
1908     canvas.show();
1909     canvas.requestActivateWindow();
1910     QTest::qWaitForWindowActive(&canvas);
1911
1912     QVERIFY(canvas.rootObject() != 0);
1913     QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit *>(canvas.rootObject());
1914     QVERIFY(textEditObject != 0);
1915
1916     // press-and-drag-and-release from x1 to x2
1917     int x1 = 10;
1918     int x2 = 70;
1919     int y = textEditObject->height()/2;
1920     QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y));
1921     QTest::mouseMove(&canvas, QPoint(x2, y));
1922     QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y));
1923     QString str = textEditObject->selectedText();
1924     if (selectWords) {
1925         QTRY_COMPARE(textEditObject->selectedText(), text);
1926     } else {
1927         QTRY_VERIFY(textEditObject->selectedText().length() > 3);
1928         QVERIFY(str != text);
1929     }
1930 }
1931
1932 void tst_qquicktextedit::inputMethodHints()
1933 {
1934     QQuickView canvas(testFileUrl("inputmethodhints.qml"));
1935     canvas.show();
1936     canvas.requestActivateWindow();
1937
1938     QVERIFY(canvas.rootObject() != 0);
1939     QQuickTextEdit *textEditObject = qobject_cast<QQuickTextEdit *>(canvas.rootObject());
1940     QVERIFY(textEditObject != 0);
1941     QVERIFY(textEditObject->inputMethodHints() & Qt::ImhNoPredictiveText);
1942     QSignalSpy inputMethodHintSpy(textEditObject, SIGNAL(inputMethodHintsChanged()));
1943     textEditObject->setInputMethodHints(Qt::ImhUppercaseOnly);
1944     QVERIFY(textEditObject->inputMethodHints() & Qt::ImhUppercaseOnly);
1945     QCOMPARE(inputMethodHintSpy.count(), 1);
1946     textEditObject->setInputMethodHints(Qt::ImhUppercaseOnly);
1947     QCOMPARE(inputMethodHintSpy.count(), 1);
1948
1949     QQuickTextEdit plainTextEdit;
1950     QCOMPARE(plainTextEdit.inputMethodHints(), Qt::ImhNone);
1951 }
1952
1953 void tst_qquicktextedit::positionAt_data()
1954 {
1955     QTest::addColumn<QQuickTextEdit::HAlignment>("horizontalAlignment");
1956     QTest::addColumn<QQuickTextEdit::VAlignment>("verticalAlignment");
1957
1958     QTest::newRow("top-left") << QQuickTextEdit::AlignLeft << QQuickTextEdit::AlignTop;
1959     QTest::newRow("bottom-left") << QQuickTextEdit::AlignLeft << QQuickTextEdit::AlignBottom;
1960     QTest::newRow("center-left") << QQuickTextEdit::AlignLeft << QQuickTextEdit::AlignVCenter;
1961
1962     QTest::newRow("top-right") << QQuickTextEdit::AlignRight << QQuickTextEdit::AlignTop;
1963     QTest::newRow("top-center") << QQuickTextEdit::AlignHCenter << QQuickTextEdit::AlignTop;
1964
1965     QTest::newRow("center") << QQuickTextEdit::AlignHCenter << QQuickTextEdit::AlignVCenter;
1966 }
1967
1968 void tst_qquicktextedit::positionAt()
1969 {
1970     QFETCH(QQuickTextEdit::HAlignment, horizontalAlignment);
1971     QFETCH(QQuickTextEdit::VAlignment, verticalAlignment);
1972
1973     QQuickView canvas(testFileUrl("positionAt.qml"));
1974     QVERIFY(canvas.rootObject() != 0);
1975     canvas.show();
1976     canvas.requestActivateWindow();
1977     QTest::qWaitForWindowActive(&canvas);
1978
1979     QQuickTextEdit *texteditObject = qobject_cast<QQuickTextEdit *>(canvas.rootObject());
1980     QVERIFY(texteditObject != 0);
1981     texteditObject->setHAlign(horizontalAlignment);
1982     texteditObject->setVAlign(verticalAlignment);
1983
1984     QTextLayout layout(texteditObject->text().replace(QLatin1Char('\n'), QChar::LineSeparator));
1985     layout.setFont(texteditObject->font());
1986
1987     if (!qmlDisableDistanceField()) {
1988         QTextOption option;
1989         option.setUseDesignMetrics(true);
1990         layout.setTextOption(option);
1991     }
1992
1993     layout.beginLayout();
1994     QTextLine line = layout.createLine();
1995     line.setLineWidth(texteditObject->width());
1996     QTextLine secondLine = layout.createLine();
1997     secondLine.setLineWidth(texteditObject->width());
1998     layout.endLayout();
1999
2000     qreal y0;
2001     qreal y1;
2002
2003     switch (verticalAlignment) {
2004     case QQuickTextEdit::AlignTop:
2005         y0 = line.height() / 2;
2006         y1 = line.height() * 3 / 2;
2007         break;
2008     case QQuickTextEdit::AlignVCenter:
2009         y0 = (texteditObject->height() - line.height()) / 2;
2010         y1 = (texteditObject->height() + line.height()) / 2;
2011         break;
2012     case QQuickTextEdit::AlignBottom:
2013         y0 = texteditObject->height() - line.height() * 3 / 2;
2014         y1 = texteditObject->height() - line.height() / 2;
2015         break;
2016     }
2017
2018     qreal xoff;
2019     switch (horizontalAlignment) {
2020     case QQuickTextEdit::AlignLeft:
2021         xoff = 0;
2022         break;
2023     case QQuickTextEdit::AlignHCenter:
2024         xoff = (texteditObject->width() - secondLine.naturalTextWidth()) / 2;
2025         break;
2026     case QQuickTextEdit::AlignRight:
2027         xoff = texteditObject->width() - secondLine.naturalTextWidth();
2028         break;
2029     }
2030     int pos = texteditObject->positionAt(texteditObject->width()/2, y0);
2031
2032     int widthBegin = floor(xoff + line.cursorToX(pos - 1));
2033     int widthEnd = ceil(xoff + line.cursorToX(pos + 1));
2034
2035     QVERIFY(widthBegin <= texteditObject->width() / 2);
2036     QVERIFY(widthEnd >= texteditObject->width() / 2);
2037
2038     const qreal x0 = texteditObject->positionToRectangle(pos).x();
2039     const qreal x1 = texteditObject->positionToRectangle(pos + 1).x();
2040
2041     QString preeditText = texteditObject->text().mid(0, pos);
2042     texteditObject->setText(texteditObject->text().mid(pos));
2043     texteditObject->setCursorPosition(0);
2044
2045     QInputMethodEvent inputEvent(preeditText, QList<QInputMethodEvent::Attribute>());
2046     QGuiApplication::sendEvent(texteditObject, &inputEvent);
2047
2048     // Check all points within the preedit text return the same position.
2049     QCOMPARE(texteditObject->positionAt(0, y0), 0);
2050     QCOMPARE(texteditObject->positionAt(x0 / 2, y0), 0);
2051     QCOMPARE(texteditObject->positionAt(x0, y0), 0);
2052
2053     // Verify positioning returns to normal after the preedit text.
2054     QCOMPARE(texteditObject->positionAt(x1, y0), 1);
2055     QCOMPARE(texteditObject->positionToRectangle(1).x(), x1);
2056
2057     QVERIFY(texteditObject->positionAt(x0 / 2, y1) > 0);
2058 }
2059
2060 void tst_qquicktextedit::linkActivated()
2061 {
2062     QQuickView canvas(testFileUrl("linkActivated.qml"));
2063     QVERIFY(canvas.rootObject() != 0);
2064     canvas.show();
2065     canvas.requestActivateWindow();
2066     QTest::qWaitForWindowActive(&canvas);
2067
2068     QQuickTextEdit *texteditObject = qobject_cast<QQuickTextEdit *>(canvas.rootObject());
2069     QVERIFY(texteditObject != 0);
2070
2071     QSignalSpy spy(texteditObject, SIGNAL(linkActivated(QString)));
2072
2073     const QString link("http://example.com/");
2074
2075     const QPointF linkPos = texteditObject->positionToRectangle(7).center();
2076     const QPointF textPos = texteditObject->positionToRectangle(2).center();
2077
2078     QTest::mouseClick(&canvas, Qt::LeftButton, 0, linkPos.toPoint());
2079     QTRY_COMPARE(spy.count(), 1);
2080     QCOMPARE(spy.last()[0].toString(), link);
2081
2082     QTest::mouseClick(&canvas, Qt::LeftButton, 0, textPos.toPoint());
2083     QTest::qWait(50);
2084     QCOMPARE(spy.count(), 1);
2085
2086     texteditObject->setReadOnly(true);
2087
2088     QTest::mouseClick(&canvas, Qt::LeftButton, 0, linkPos.toPoint());
2089     QTRY_COMPARE(spy.count(), 2);
2090     QCOMPARE(spy.last()[0].toString(), link);
2091
2092     QTest::mouseClick(&canvas, Qt::LeftButton, 0, textPos.toPoint());
2093     QTest::qWait(50);
2094     QCOMPARE(spy.count(), 2);
2095 }
2096
2097 void tst_qquicktextedit::cursorDelegate_data()
2098 {
2099     QTest::addColumn<QUrl>("source");
2100     QTest::newRow("out of line") << testFileUrl("cursorTest.qml");
2101     QTest::newRow("in line") << testFileUrl("cursorTestInline.qml");
2102     QTest::newRow("external") << testFileUrl("cursorTestExternal.qml");
2103 }
2104
2105 void tst_qquicktextedit::cursorDelegate()
2106 {
2107     QFETCH(QUrl, source);
2108     QQuickView view(source);
2109     view.show();
2110     view.requestActivateWindow();
2111     QQuickTextEdit *textEditObject = view.rootObject()->findChild<QQuickTextEdit*>("textEditObject");
2112     QVERIFY(textEditObject != 0);
2113     // Delegate creation is deferred until focus in or cursor visiblity is forced.
2114     QVERIFY(!textEditObject->findChild<QQuickItem*>("cursorInstance"));
2115     QVERIFY(!textEditObject->isCursorVisible());
2116     //Test Delegate gets created
2117     textEditObject->setFocus(true);
2118     QVERIFY(textEditObject->isCursorVisible());
2119     QQuickItem* delegateObject = textEditObject->findChild<QQuickItem*>("cursorInstance");
2120     QVERIFY(delegateObject);
2121     QCOMPARE(delegateObject->property("localProperty").toString(), QString("Hello"));
2122     //Test Delegate gets moved
2123     for (int i=0; i<= textEditObject->text().length(); i++) {
2124         textEditObject->setCursorPosition(i);
2125         QCOMPARE(textEditObject->cursorRectangle().x(), delegateObject->x());
2126         QCOMPARE(textEditObject->cursorRectangle().y(), delegateObject->y());
2127     }
2128
2129     // Test delegate gets moved on mouse press.
2130     textEditObject->setSelectByMouse(true);
2131     textEditObject->setCursorPosition(0);
2132     const QPoint point1 = textEditObject->positionToRectangle(5).center().toPoint();
2133     QTest::qWait(400);  //ensure this isn't treated as a double-click
2134     QTest::mouseClick(&view, Qt::LeftButton, 0, point1);
2135     QTest::qWait(50);
2136     QTRY_VERIFY(textEditObject->cursorPosition() != 0);
2137     QCOMPARE(textEditObject->cursorRectangle().x(), delegateObject->x());
2138     QCOMPARE(textEditObject->cursorRectangle().y(), delegateObject->y());
2139
2140     // Test delegate gets moved on mouse drag
2141     textEditObject->setCursorPosition(0);
2142     const QPoint point2 = textEditObject->positionToRectangle(10).center().toPoint();
2143     QTest::qWait(400);  //ensure this isn't treated as a double-click
2144     QTest::mousePress(&view, Qt::LeftButton, 0, point1);
2145     QMouseEvent mv(QEvent::MouseMove, point2, Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
2146     QGuiApplication::sendEvent(&view, &mv);
2147     QTest::mouseRelease(&view, Qt::LeftButton, 0, point2);
2148     QTest::qWait(50);
2149     QTRY_COMPARE(textEditObject->cursorRectangle().x(), delegateObject->x());
2150     QCOMPARE(textEditObject->cursorRectangle().y(), delegateObject->y());
2151
2152     textEditObject->setReadOnly(true);
2153     textEditObject->setCursorPosition(0);
2154     QTest::qWait(400);  //ensure this isn't treated as a double-click
2155     QTest::mouseClick(&view, Qt::LeftButton, 0, textEditObject->positionToRectangle(5).center().toPoint());
2156     QTest::qWait(50);
2157     QTRY_VERIFY(textEditObject->cursorPosition() != 0);
2158     QCOMPARE(textEditObject->cursorRectangle().x(), delegateObject->x());
2159     QCOMPARE(textEditObject->cursorRectangle().y(), delegateObject->y());
2160
2161     textEditObject->setCursorPosition(0);
2162     QTest::qWait(400);  //ensure this isn't treated as a double-click
2163     QTest::mouseClick(&view, Qt::LeftButton, 0, textEditObject->positionToRectangle(5).center().toPoint());
2164     QTest::qWait(50);
2165     QTRY_VERIFY(textEditObject->cursorPosition() != 0);
2166     QCOMPARE(textEditObject->cursorRectangle().x(), delegateObject->x());
2167     QCOMPARE(textEditObject->cursorRectangle().y(), delegateObject->y());
2168
2169     textEditObject->setCursorPosition(0);
2170     QCOMPARE(textEditObject->cursorRectangle().x(), delegateObject->x());
2171     QCOMPARE(textEditObject->cursorRectangle().y(), delegateObject->y());
2172
2173     textEditObject->setReadOnly(false);
2174
2175     // Delegate moved when text is entered
2176     textEditObject->setText(QString());
2177     for (int i = 0; i < 20; ++i) {
2178         QTest::keyClick(&view, Qt::Key_A);
2179         QCOMPARE(textEditObject->cursorRectangle().x(), delegateObject->x());
2180         QCOMPARE(textEditObject->cursorRectangle().y(), delegateObject->y());
2181     }
2182
2183     // Delegate moved when text is entered by im.
2184     textEditObject->setText(QString());
2185     for (int i = 0; i < 20; ++i) {
2186         QInputMethodEvent event;
2187         event.setCommitString("a");
2188         QGuiApplication::sendEvent(textEditObject, &event);
2189         QCOMPARE(textEditObject->cursorRectangle().x(), delegateObject->x());
2190         QCOMPARE(textEditObject->cursorRectangle().y(), delegateObject->y());
2191     }
2192     // Delegate moved when text is removed by im.
2193     for (int i = 19; i > 1; --i) {
2194         QInputMethodEvent event;
2195         event.setCommitString(QString(), -1, 1);
2196         QGuiApplication::sendEvent(textEditObject, &event);
2197         QCOMPARE(textEditObject->cursorRectangle().x(), delegateObject->x());
2198         QCOMPARE(textEditObject->cursorRectangle().y(), delegateObject->y());
2199     }
2200     {   // Delegate moved the text is changed in place by im.
2201         QInputMethodEvent event;
2202         event.setCommitString("i", -1, 1);
2203         QGuiApplication::sendEvent(textEditObject, &event);
2204         QCOMPARE(textEditObject->cursorRectangle().x(), delegateObject->x());
2205         QCOMPARE(textEditObject->cursorRectangle().y(), delegateObject->y());
2206     }
2207
2208     //Test Delegate gets deleted
2209     textEditObject->setCursorDelegate(0);
2210     QVERIFY(!textEditObject->findChild<QQuickItem*>("cursorInstance"));
2211 }
2212
2213 void tst_qquicktextedit::remoteCursorDelegate()
2214 {
2215     TestHTTPServer server(SERVER_PORT);
2216     server.serveDirectory(dataDirectory());
2217
2218     QQuickView view;
2219
2220     QQmlComponent component(view.engine(), QUrl(SERVER_ADDR "/RemoteCursor.qml"));
2221
2222     view.rootContext()->setContextProperty("contextDelegate", &component);
2223     view.setSource(testFileUrl("cursorTestRemote.qml"));
2224     view.show();
2225     view.requestActivateWindow();
2226     QQuickTextEdit *textEditObject = view.rootObject()->findChild<QQuickTextEdit*>("textEditObject");
2227     QVERIFY(textEditObject != 0);
2228
2229     // Delegate is created on demand, and so won't be available immediately.  Focus in or
2230     // setCursorVisible(true) will trigger creation.
2231     QTRY_VERIFY(!textEditObject->findChild<QQuickItem*>("cursorInstance"));
2232     QVERIFY(!textEditObject->isCursorVisible());
2233
2234     textEditObject->setFocus(true);
2235     QVERIFY(textEditObject->isCursorVisible());
2236
2237     QCOMPARE(component.status(), QQmlComponent::Loading);
2238     QVERIFY(!textEditObject->findChild<QQuickItem*>("cursorInstance"));
2239
2240     // Wait for component to load.
2241     QTRY_COMPARE(component.status(), QQmlComponent::Ready);
2242     QVERIFY(textEditObject->findChild<QQuickItem*>("cursorInstance"));
2243 }
2244
2245 void tst_qquicktextedit::cursorVisible()
2246 {
2247     QQuickTextEdit edit;
2248     edit.componentComplete();
2249     QSignalSpy spy(&edit, SIGNAL(cursorVisibleChanged(bool)));
2250
2251     QQuickView view(testFileUrl("cursorVisible.qml"));
2252     view.show();
2253     view.requestActivateWindow();
2254     QTest::qWaitForWindowActive(&view);
2255     QCOMPARE(&view, qGuiApp->focusWindow());
2256
2257     QCOMPARE(edit.isCursorVisible(), false);
2258
2259     edit.setCursorVisible(true);
2260     QCOMPARE(edit.isCursorVisible(), true);
2261     QCOMPARE(spy.count(), 1);
2262
2263     edit.setCursorVisible(false);
2264     QCOMPARE(edit.isCursorVisible(), false);
2265     QCOMPARE(spy.count(), 2);
2266
2267     edit.setFocus(true);
2268     QCOMPARE(edit.isCursorVisible(), false);
2269     QCOMPARE(spy.count(), 2);
2270
2271     edit.setParentItem(view.rootObject());
2272     QCOMPARE(edit.isCursorVisible(), true);
2273     QCOMPARE(spy.count(), 3);
2274
2275     edit.setFocus(false);
2276     QCOMPARE(edit.isCursorVisible(), false);
2277     QCOMPARE(spy.count(), 4);
2278
2279     edit.setFocus(true);
2280     QCOMPARE(edit.isCursorVisible(), true);
2281     QCOMPARE(spy.count(), 5);
2282
2283     QWindow alternateView;
2284     alternateView.show();
2285     alternateView.requestActivateWindow();
2286     QTest::qWaitForWindowActive(&alternateView);
2287
2288     QCOMPARE(edit.isCursorVisible(), false);
2289     QCOMPARE(spy.count(), 6);
2290
2291     view.requestActivateWindow();
2292     QTest::qWaitForWindowActive(&view);
2293     QCOMPARE(edit.isCursorVisible(), true);
2294     QCOMPARE(spy.count(), 7);
2295
2296     {   // Cursor attribute with 0 length hides cursor.
2297         QInputMethodEvent ev(QString(), QList<QInputMethodEvent::Attribute>()
2298                 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 0, QVariant()));
2299         QCoreApplication::sendEvent(&edit, &ev);
2300     }
2301     QCOMPARE(edit.isCursorVisible(), false);
2302     QCOMPARE(spy.count(), 8);
2303
2304     {   // Cursor attribute with non zero length shows cursor.
2305         QInputMethodEvent ev(QString(), QList<QInputMethodEvent::Attribute>()
2306                 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 1, QVariant()));
2307         QCoreApplication::sendEvent(&edit, &ev);
2308     }
2309     QCOMPARE(edit.isCursorVisible(), true);
2310     QCOMPARE(spy.count(), 9);
2311
2312
2313     {   // If the cursor is hidden by the input method and the text is changed it should be visible again.
2314         QInputMethodEvent ev(QString(), QList<QInputMethodEvent::Attribute>()
2315                 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 0, QVariant()));
2316         QCoreApplication::sendEvent(&edit, &ev);
2317     }
2318     QCOMPARE(edit.isCursorVisible(), false);
2319     QCOMPARE(spy.count(), 10);
2320
2321     edit.setText("something");
2322     QCOMPARE(edit.isCursorVisible(), true);
2323     QCOMPARE(spy.count(), 11);
2324
2325     {   // If the cursor is hidden by the input method and the cursor position is changed it should be visible again.
2326         QInputMethodEvent ev(QString(), QList<QInputMethodEvent::Attribute>()
2327                 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 0, QVariant()));
2328         QCoreApplication::sendEvent(&edit, &ev);
2329     }
2330     QCOMPARE(edit.isCursorVisible(), false);
2331     QCOMPARE(spy.count(), 12);
2332
2333     edit.setCursorPosition(5);
2334     QCOMPARE(edit.isCursorVisible(), true);
2335     QCOMPARE(spy.count(), 13);
2336 }
2337
2338 void tst_qquicktextedit::delegateLoading_data()
2339 {
2340     QTest::addColumn<QString>("qmlfile");
2341     QTest::addColumn<QString>("error");
2342
2343     // import installed
2344     QTest::newRow("pass") << "cursorHttpTestPass.qml" << "";
2345     QTest::newRow("fail1") << "cursorHttpTestFail1.qml" << "http://localhost:42332/FailItem.qml: Remote host closed the connection ";
2346     QTest::newRow("fail2") << "cursorHttpTestFail2.qml" << "http://localhost:42332/ErrItem.qml:4:5: Fungus is not a type ";
2347 }
2348
2349 void tst_qquicktextedit::delegateLoading()
2350 {
2351     QFETCH(QString, qmlfile);
2352     QFETCH(QString, error);
2353
2354     TestHTTPServer server(SERVER_PORT);
2355     server.serveDirectory(testFile("httpfail"), TestHTTPServer::Disconnect);
2356     server.serveDirectory(testFile("httpslow"), TestHTTPServer::Delay);
2357     server.serveDirectory(testFile("http"));
2358
2359     QQuickView view(QUrl(QLatin1String(SERVER_ADDR "/") + qmlfile));
2360     view.show();
2361     view.requestActivateWindow();
2362
2363     if (!error.isEmpty()) {
2364         QTest::ignoreMessage(QtWarningMsg, error.toUtf8());
2365         QTRY_VERIFY(view.status()==QQuickView::Error);
2366         QTRY_VERIFY(!view.rootObject()); // there is fail item inside this test
2367     } else {
2368         QTRY_VERIFY(view.rootObject());//Wait for loading to finish.
2369         QQuickTextEdit *textEditObject = view.rootObject()->findChild<QQuickTextEdit*>("textEditObject");
2370         //    view.rootObject()->dumpObjectTree();
2371         QVERIFY(textEditObject != 0);
2372         textEditObject->setFocus(true);
2373         QQuickItem *delegate;
2374         delegate = view.rootObject()->findChild<QQuickItem*>("delegateOkay");
2375         QVERIFY(delegate);
2376         delegate = view.rootObject()->findChild<QQuickItem*>("delegateSlow");
2377         QVERIFY(delegate);
2378
2379         delete delegate;
2380     }
2381
2382
2383     //A test should be added here with a component which is ready but component.create() returns null
2384     //Not sure how to accomplish this with QQuickTextEdits cursor delegate
2385     //###This was only needed for code coverage, and could be a case of overzealous defensive programming
2386     //delegate = view.rootObject()->findChild<QQuickItem*>("delegateErrorB");
2387     //QVERIFY(!delegate);
2388 }
2389
2390 /*
2391 TextEdit element should only handle left/right keys until the cursor reaches
2392 the extent of the text, then they should ignore the keys.
2393 */
2394 void tst_qquicktextedit::navigation()
2395 {
2396     QQuickView canvas(testFileUrl("navigation.qml"));
2397     canvas.show();
2398     canvas.requestActivateWindow();
2399
2400     QVERIFY(canvas.rootObject() != 0);
2401
2402     QQuickTextEdit *input = qobject_cast<QQuickTextEdit *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
2403
2404     QVERIFY(input != 0);
2405     QTRY_VERIFY(input->hasActiveFocus() == true);
2406     simulateKey(&canvas, Qt::Key_Left);
2407     QVERIFY(input->hasActiveFocus() == false);
2408     simulateKey(&canvas, Qt::Key_Right);
2409     QVERIFY(input->hasActiveFocus() == true);
2410     simulateKey(&canvas, Qt::Key_Right);
2411     QVERIFY(input->hasActiveFocus() == true);
2412     simulateKey(&canvas, Qt::Key_Right);
2413     QVERIFY(input->hasActiveFocus() == false);
2414     simulateKey(&canvas, Qt::Key_Left);
2415     QVERIFY(input->hasActiveFocus() == true);
2416
2417     // Test left and right navigation works if the TextEdit is empty (QTBUG-25447).
2418     input->setText(QString());
2419     QCOMPARE(input->cursorPosition(), 0);
2420     simulateKey(&canvas, Qt::Key_Right);
2421     QCOMPARE(input->hasActiveFocus(), false);
2422     simulateKey(&canvas, Qt::Key_Left);
2423     QCOMPARE(input->hasActiveFocus(), true);
2424     simulateKey(&canvas, Qt::Key_Left);
2425     QCOMPARE(input->hasActiveFocus(), false);
2426 }
2427
2428 void tst_qquicktextedit::copyAndPaste() {
2429 #ifndef QT_NO_CLIPBOARD
2430
2431 #ifdef Q_OS_MAC
2432     {
2433         PasteboardRef pasteboard;
2434         OSStatus status = PasteboardCreate(0, &pasteboard);
2435         if (status == noErr)
2436             CFRelease(pasteboard);
2437         else
2438             QSKIP("This machine doesn't support the clipboard");
2439     }
2440 #endif
2441
2442     QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"Hello world!\" }";
2443     QQmlComponent textEditComponent(&engine);
2444     textEditComponent.setData(componentStr.toLatin1(), QUrl());
2445     QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(textEditComponent.create());
2446     QVERIFY(textEdit != 0);
2447
2448     // copy and paste
2449     QCOMPARE(textEdit->text().length(), 12);
2450     textEdit->select(0, textEdit->text().length());;
2451     textEdit->copy();
2452     QCOMPARE(textEdit->selectedText(), QString("Hello world!"));
2453     QCOMPARE(textEdit->selectedText().length(), 12);
2454     textEdit->setCursorPosition(0);
2455     QVERIFY(textEdit->canPaste());
2456     textEdit->paste();
2457     QCOMPARE(textEdit->text(), QString("Hello world!Hello world!"));
2458     QCOMPARE(textEdit->text().length(), 24);
2459
2460     // canPaste
2461     QVERIFY(textEdit->canPaste());
2462     textEdit->setReadOnly(true);
2463     QVERIFY(!textEdit->canPaste());
2464     textEdit->setReadOnly(false);
2465     QVERIFY(textEdit->canPaste());
2466
2467     // QTBUG-12339
2468     // test that document and internal text attribute are in sync
2469     QQuickItemPrivate* pri = QQuickItemPrivate::get(textEdit);
2470     QQuickTextEditPrivate *editPrivate = static_cast<QQuickTextEditPrivate*>(pri);
2471     QCOMPARE(textEdit->text(), editPrivate->text);
2472
2473     // select word
2474     textEdit->setCursorPosition(0);
2475     textEdit->selectWord();
2476     QCOMPARE(textEdit->selectedText(), QString("Hello"));
2477
2478     // select all and cut
2479     textEdit->selectAll();
2480     textEdit->cut();
2481     QCOMPARE(textEdit->text().length(), 0);
2482     textEdit->paste();
2483     QCOMPARE(textEdit->text(), QString("Hello world!Hello world!"));
2484     QCOMPARE(textEdit->text().length(), 24);
2485 #endif
2486 }
2487
2488 void tst_qquicktextedit::canPaste() {
2489 #ifndef QT_NO_CLIPBOARD
2490
2491     QGuiApplication::clipboard()->setText("Some text");
2492
2493     QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"Hello world!\" }";
2494     QQmlComponent textEditComponent(&engine);
2495     textEditComponent.setData(componentStr.toLatin1(), QUrl());
2496     QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(textEditComponent.create());
2497     QVERIFY(textEdit != 0);
2498
2499     // check initial value - QTBUG-17765
2500     QTextDocument document;
2501     QQuickTextControl tc(&document);
2502     QCOMPARE(textEdit->canPaste(), tc.canPaste());
2503
2504 #endif
2505 }
2506
2507 void tst_qquicktextedit::canPasteEmpty() {
2508 #ifndef QT_NO_CLIPBOARD
2509
2510     QGuiApplication::clipboard()->clear();
2511
2512     QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"Hello world!\" }";
2513     QQmlComponent textEditComponent(&engine);
2514     textEditComponent.setData(componentStr.toLatin1(), QUrl());
2515     QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(textEditComponent.create());
2516     QVERIFY(textEdit != 0);
2517
2518     // check initial value - QTBUG-17765
2519     QTextDocument document;
2520     QQuickTextControl tc(&document);
2521     QCOMPARE(textEdit->canPaste(), tc.canPaste());
2522
2523 #endif
2524 }
2525
2526 void tst_qquicktextedit::readOnly()
2527 {
2528     QQuickView canvas(testFileUrl("readOnly.qml"));
2529     canvas.show();
2530     canvas.requestActivateWindow();
2531
2532     QVERIFY(canvas.rootObject() != 0);
2533
2534     QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
2535
2536     QVERIFY(edit != 0);
2537     QTRY_VERIFY(edit->hasActiveFocus() == true);
2538     QVERIFY(edit->isReadOnly() == true);
2539     QString initial = edit->text();
2540     for (int k=Qt::Key_0; k<=Qt::Key_Z; k++)
2541         simulateKey(&canvas, k);
2542     simulateKey(&canvas, Qt::Key_Return);
2543     simulateKey(&canvas, Qt::Key_Space);
2544     simulateKey(&canvas, Qt::Key_Escape);
2545     QCOMPARE(edit->text(), initial);
2546
2547     edit->setCursorPosition(3);
2548     edit->setReadOnly(false);
2549     QCOMPARE(edit->isReadOnly(), false);
2550     QCOMPARE(edit->cursorPosition(), edit->text().length());
2551 }
2552
2553 void tst_qquicktextedit::simulateKey(QWindow *view, int key, Qt::KeyboardModifiers modifiers)
2554 {
2555     QKeyEvent press(QKeyEvent::KeyPress, key, modifiers);
2556     QKeyEvent release(QKeyEvent::KeyRelease, key, modifiers);
2557
2558     QGuiApplication::sendEvent(view, &press);
2559     QGuiApplication::sendEvent(view, &release);
2560 }
2561
2562 void tst_qquicktextedit::textInput()
2563 {
2564     QQuickView view(testFileUrl("inputMethodEvent.qml"));
2565     view.show();
2566     view.requestActivateWindow();
2567     QTest::qWaitForWindowActive(&view);
2568     QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(view.rootObject());
2569     QVERIFY(edit);
2570     QVERIFY(edit->hasActiveFocus() == true);
2571
2572     // test that input method event is committed and change signal is emitted
2573     QSignalSpy spy(edit, SIGNAL(textChanged()));
2574     QInputMethodEvent event;
2575     event.setCommitString( "Hello world!", 0, 0);
2576     QGuiApplication::sendEvent(edit, &event);
2577     QCOMPARE(edit->text(), QString("Hello world!"));
2578     QCOMPARE(spy.count(), 1);
2579
2580     // QTBUG-12339
2581     // test that document and internal text attribute are in sync
2582     QQuickTextEditPrivate *editPrivate = static_cast<QQuickTextEditPrivate*>(QQuickItemPrivate::get(edit));
2583     QCOMPARE(editPrivate->text, QString("Hello world!"));
2584
2585     QInputMethodQueryEvent queryEvent(Qt::ImEnabled);
2586     QGuiApplication::sendEvent(edit, &queryEvent);
2587     QCOMPARE(queryEvent.value(Qt::ImEnabled).toBool(), true);
2588
2589     edit->setReadOnly(true);
2590     QGuiApplication::sendEvent(edit, &queryEvent);
2591     QCOMPARE(queryEvent.value(Qt::ImEnabled).toBool(), false);
2592 }
2593
2594 void tst_qquicktextedit::inputMethodUpdate()
2595 {
2596     PlatformInputContext platformInputContext;
2597     QInputMethodPrivate *inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod());
2598     inputMethodPrivate->testContext = &platformInputContext;
2599
2600     QQuickView view(testFileUrl("inputMethodEvent.qml"));
2601     view.show();
2602     view.requestActivateWindow();
2603     QTest::qWaitForWindowActive(&view);
2604     QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(view.rootObject());
2605     QVERIFY(edit);
2606     QVERIFY(edit->hasActiveFocus() == true);
2607
2608     // text change even without cursor position change needs to trigger update
2609     edit->setText("test");
2610     platformInputContext.clear();
2611     edit->setText("xxxx");
2612     QVERIFY(platformInputContext.m_updateCallCount > 0);
2613
2614     // input method event replacing text
2615     platformInputContext.clear();
2616     {
2617         QInputMethodEvent inputMethodEvent;
2618         inputMethodEvent.setCommitString("y", -1, 1);
2619         QGuiApplication::sendEvent(edit, &inputMethodEvent);
2620     }
2621     QVERIFY(platformInputContext.m_updateCallCount > 0);
2622
2623     // input method changing selection
2624     platformInputContext.clear();
2625     {
2626         QList<QInputMethodEvent::Attribute> attributes;
2627         attributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 0, 2, QVariant());
2628         QInputMethodEvent inputMethodEvent("", attributes);
2629         QGuiApplication::sendEvent(edit, &inputMethodEvent);
2630     }
2631     QVERIFY(edit->selectionStart() != edit->selectionEnd());
2632     QVERIFY(platformInputContext.m_updateCallCount > 0);
2633
2634     // programmatical selections trigger update
2635     platformInputContext.clear();
2636     edit->selectAll();
2637     QCOMPARE(platformInputContext.m_updateCallCount, 1);
2638
2639     // font changes
2640     platformInputContext.clear();
2641     QFont font = edit->font();
2642     font.setBold(!font.bold());
2643     edit->setFont(font);
2644     QVERIFY(platformInputContext.m_updateCallCount > 0);
2645
2646     // normal input
2647     platformInputContext.clear();
2648     {
2649         QInputMethodEvent inputMethodEvent;
2650         inputMethodEvent.setCommitString("y");
2651         QGuiApplication::sendEvent(edit, &inputMethodEvent);
2652     }
2653     QVERIFY(platformInputContext.m_updateCallCount > 0);
2654
2655     // changing cursor position
2656     platformInputContext.clear();
2657     edit->setCursorPosition(0);
2658     QVERIFY(platformInputContext.m_updateCallCount > 0);
2659
2660     // continuing with selection
2661     platformInputContext.clear();
2662     edit->moveCursorSelection(1);
2663     QVERIFY(platformInputContext.m_updateCallCount > 0);
2664
2665     // read only disabled input method
2666     platformInputContext.clear();
2667     edit->setReadOnly(true);
2668     QVERIFY(platformInputContext.m_updateCallCount > 0);
2669     edit->setReadOnly(false);
2670
2671     // no updates while no focus
2672     edit->setFocus(false);
2673     platformInputContext.clear();
2674     edit->setText("Foo");
2675     QCOMPARE(platformInputContext.m_updateCallCount, 0);
2676     edit->setCursorPosition(1);
2677     QCOMPARE(platformInputContext.m_updateCallCount, 0);
2678     edit->selectAll();
2679     QCOMPARE(platformInputContext.m_updateCallCount, 0);
2680     edit->setReadOnly(true);
2681     QCOMPARE(platformInputContext.m_updateCallCount, 0);
2682 }
2683
2684 void tst_qquicktextedit::openInputPanel()
2685 {
2686     PlatformInputContext platformInputContext;
2687     QInputMethodPrivate *inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod());
2688     inputMethodPrivate->testContext = &platformInputContext;
2689
2690     QQuickView view(testFileUrl("openInputPanel.qml"));
2691     view.show();
2692     view.requestActivateWindow();
2693     QTest::qWaitForWindowActive(&view);
2694
2695     QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(view.rootObject());
2696     QVERIFY(edit);
2697
2698     // check default values
2699     QVERIFY(edit->focusOnPress());
2700     QVERIFY(!edit->hasActiveFocus());
2701     QVERIFY(qApp->focusObject() != edit);
2702
2703     QCOMPARE(qApp->inputMethod()->isVisible(), false);
2704
2705     // input panel should open on focus
2706     QPoint centerPoint(view.width()/2, view.height()/2);
2707     Qt::KeyboardModifiers noModifiers = 0;
2708     QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
2709     QGuiApplication::processEvents();
2710     QVERIFY(edit->hasActiveFocus());
2711     QCOMPARE(qApp->focusObject(), edit);
2712     QCOMPARE(qApp->inputMethod()->isVisible(), true);
2713     QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
2714
2715     // input panel should be re-opened when pressing already focused TextEdit
2716     qApp->inputMethod()->hide();
2717     QCOMPARE(qApp->inputMethod()->isVisible(), false);
2718     QVERIFY(edit->hasActiveFocus());
2719     QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
2720     QGuiApplication::processEvents();
2721     QCOMPARE(qApp->inputMethod()->isVisible(), true);
2722     QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
2723
2724     // input panel should stay visible if focus is lost to another text editor
2725     QSignalSpy inputPanelVisibilitySpy(qApp->inputMethod(), SIGNAL(visibleChanged()));
2726     QQuickTextEdit anotherEdit;
2727     anotherEdit.setParentItem(view.rootObject());
2728     anotherEdit.setFocus(true);
2729     QCOMPARE(qApp->inputMethod()->isVisible(), true);
2730     QCOMPARE(qApp->focusObject(), qobject_cast<QObject*>(&anotherEdit));
2731     QCOMPARE(inputPanelVisibilitySpy.count(), 0);
2732
2733     anotherEdit.setFocus(false);
2734     QVERIFY(qApp->focusObject() != &anotherEdit);
2735     QCOMPARE(view.activeFocusItem(), view.rootItem());
2736     anotherEdit.setFocus(true);
2737
2738     qApp->inputMethod()->hide();
2739
2740     // input panel should not be opened if TextEdit is read only
2741     edit->setReadOnly(true);
2742     edit->setFocus(true);
2743     QCOMPARE(qApp->inputMethod()->isVisible(), false);
2744     QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
2745     QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
2746     QGuiApplication::processEvents();
2747     QCOMPARE(qApp->inputMethod()->isVisible(), false);
2748
2749     // input panel should not be opened if focusOnPress is set to false
2750     edit->setFocusOnPress(false);
2751     edit->setFocus(false);
2752     edit->setFocus(true);
2753     QCOMPARE(qApp->inputMethod()->isVisible(), false);
2754     QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
2755     QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
2756     QCOMPARE(qApp->inputMethod()->isVisible(), false);
2757
2758     inputMethodPrivate->testContext = 0;
2759 }
2760
2761 void tst_qquicktextedit::geometrySignals()
2762 {
2763     QQmlComponent component(&engine, testFileUrl("geometrySignals.qml"));
2764     QObject *o = component.create();
2765     QVERIFY(o);
2766     QCOMPARE(o->property("bindingWidth").toInt(), 400);
2767     QCOMPARE(o->property("bindingHeight").toInt(), 500);
2768     delete o;
2769 }
2770
2771 void tst_qquicktextedit::pastingRichText_QTBUG_14003()
2772 {
2773 #ifndef QT_NO_CLIPBOARD
2774     QString componentStr = "import QtQuick 2.0\nTextEdit { textFormat: TextEdit.PlainText }";
2775     QQmlComponent component(&engine);
2776     component.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
2777     QQuickTextEdit *obj = qobject_cast<QQuickTextEdit*>(component.create());
2778
2779     QTRY_VERIFY(obj != 0);
2780     QTRY_VERIFY(obj->textFormat() == QQuickTextEdit::PlainText);
2781
2782     QMimeData *mData = new QMimeData;
2783     mData->setHtml("<font color=\"red\">Hello</font>");
2784     QGuiApplication::clipboard()->setMimeData(mData);
2785
2786     obj->paste();
2787     QTRY_VERIFY(obj->text() == "");
2788     QTRY_VERIFY(obj->textFormat() == QQuickTextEdit::PlainText);
2789 #endif
2790 }
2791
2792 void tst_qquicktextedit::implicitSize_data()
2793 {
2794     QTest::addColumn<QString>("text");
2795     QTest::addColumn<QString>("wrap");
2796     QTest::addColumn<QString>("format");
2797     QTest::newRow("plain") << "The quick red fox jumped over the lazy brown dog" << "TextEdit.NoWrap" << "TextEdit.PlainText";
2798     QTest::newRow("richtext") << "<b>The quick red fox jumped over the lazy brown dog</b>" << "TextEdit.NoWrap" << "TextEdit.RichText";
2799     QTest::newRow("plain_wrap") << "The quick red fox jumped over the lazy brown dog" << "TextEdit.Wrap" << "TextEdit.PlainText";
2800     QTest::newRow("richtext_wrap") << "<b>The quick red fox jumped over the lazy brown dog</b>" << "TextEdit.Wrap" << "TextEdit.RichText";
2801 }
2802
2803 void tst_qquicktextedit::implicitSize()
2804 {
2805     QFETCH(QString, text);
2806     QFETCH(QString, wrap);
2807     QFETCH(QString, format);
2808     QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"" + text + "\"; width: 50; wrapMode: " + wrap + "; textFormat: " + format + " }";
2809     QQmlComponent textComponent(&engine);
2810     textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
2811     QQuickTextEdit *textObject = qobject_cast<QQuickTextEdit*>(textComponent.create());
2812
2813     QVERIFY(textObject->width() < textObject->implicitWidth());
2814     QVERIFY(textObject->height() == textObject->implicitHeight());
2815
2816     textObject->resetWidth();
2817     QVERIFY(textObject->width() == textObject->implicitWidth());
2818     QVERIFY(textObject->height() == textObject->implicitHeight());
2819 }
2820
2821 void tst_qquicktextedit::contentSize()
2822 {
2823     QString componentStr = "import QtQuick 2.0\nTextEdit { width: 75; height: 16; font.pixelSize: 10 }";
2824     QQmlComponent textComponent(&engine);
2825     textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
2826     QScopedPointer<QObject> object(textComponent.create());
2827     QQuickTextEdit *textObject = qobject_cast<QQuickTextEdit *>(object.data());
2828
2829     QSignalSpy spy(textObject, SIGNAL(contentSizeChanged()));
2830
2831     textObject->setText("The quick red fox jumped over the lazy brown dog");
2832
2833     QVERIFY(textObject->contentWidth() > textObject->width());
2834     QVERIFY(textObject->contentHeight() < textObject->height());
2835     QCOMPARE(spy.count(), 1);
2836
2837     textObject->setWrapMode(QQuickTextEdit::WordWrap);
2838     QVERIFY(textObject->contentWidth() <= textObject->width());
2839     QVERIFY(textObject->contentHeight() > textObject->height());
2840     QCOMPARE(spy.count(), 2);
2841
2842     textObject->setText("The quickredfoxjumpedoverthe lazy brown dog");
2843
2844     QVERIFY(textObject->contentWidth() > textObject->width());
2845     QVERIFY(textObject->contentHeight() > textObject->height());
2846     QCOMPARE(spy.count(), 3);
2847 }
2848
2849 void tst_qquicktextedit::implicitSizeBinding_data()
2850 {
2851     implicitSize_data();
2852 }
2853
2854 void tst_qquicktextedit::implicitSizeBinding()
2855 {
2856     QFETCH(QString, text);
2857     QFETCH(QString, wrap);
2858     QFETCH(QString, format);
2859     QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"" + text + "\"; width: implicitWidth; height: implicitHeight; wrapMode: " + wrap + "; textFormat: " + format + " }";
2860     QQmlComponent textComponent(&engine);
2861     textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
2862     QScopedPointer<QObject> object(textComponent.create());
2863     QQuickTextEdit *textObject = qobject_cast<QQuickTextEdit *>(object.data());
2864
2865     QCOMPARE(textObject->width(), textObject->implicitWidth());
2866     QCOMPARE(textObject->height(), textObject->implicitHeight());
2867
2868     textObject->resetWidth();
2869     QCOMPARE(textObject->width(), textObject->implicitWidth());
2870     QCOMPARE(textObject->height(), textObject->implicitHeight());
2871
2872     textObject->resetHeight();
2873     QCOMPARE(textObject->width(), textObject->implicitWidth());
2874     QCOMPARE(textObject->height(), textObject->implicitHeight());
2875 }
2876
2877 void tst_qquicktextedit::clipRect()
2878 {
2879     QQmlComponent component(&engine);
2880     component.setData("import QtQuick 2.0\n TextEdit {}", QUrl());
2881     QScopedPointer<QObject> object(component.create());
2882     QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(object.data());
2883     QVERIFY(edit);
2884
2885     QCOMPARE(edit->clipRect().x(), qreal(0));
2886     QCOMPARE(edit->clipRect().y(), qreal(0));
2887
2888     QCOMPARE(edit->clipRect().width(), edit->width() + edit->cursorRectangle().width());
2889     QCOMPARE(edit->clipRect().height(), edit->height());
2890
2891     edit->setText("Hello World");
2892     QCOMPARE(edit->clipRect().x(), qreal(0));
2893     QCOMPARE(edit->clipRect().y(), qreal(0));
2894     // XXX: TextEdit allows an extra 3 pixels boundary for the cursor beyond it's width for non
2895     // empty text. TextInput doesn't.
2896     QCOMPARE(edit->clipRect().width(), edit->width() + edit->cursorRectangle().width() + 3);
2897     QCOMPARE(edit->clipRect().height(), edit->height());
2898
2899     // clip rect shouldn't exceed the size of the item, expect for the cursor width;
2900     edit->setWidth(edit->width() / 2);
2901     QCOMPARE(edit->clipRect().x(), qreal(0));
2902     QCOMPARE(edit->clipRect().y(), qreal(0));
2903     QCOMPARE(edit->clipRect().width(), edit->width() + edit->cursorRectangle().width() + 3);
2904     QCOMPARE(edit->clipRect().height(), edit->height());
2905
2906     edit->setHeight(edit->height() * 2);
2907     QCOMPARE(edit->clipRect().x(), qreal(0));
2908     QCOMPARE(edit->clipRect().y(), qreal(0));
2909     QCOMPARE(edit->clipRect().width(), edit->width() + edit->cursorRectangle().width() + 3);
2910     QCOMPARE(edit->clipRect().height(), edit->height());
2911
2912     QQmlComponent cursorComponent(&engine);
2913     cursorComponent.setData("import QtQuick 2.0\nRectangle { height: 20; width: 8 }", QUrl());
2914
2915     edit->setCursorDelegate(&cursorComponent);
2916     edit->setCursorVisible(true);
2917
2918     // If a cursor delegate is used it's size should determine the excess width.
2919     QCOMPARE(edit->clipRect().x(), qreal(0));
2920     QCOMPARE(edit->clipRect().y(), qreal(0));
2921     QCOMPARE(edit->clipRect().width(), edit->width() + 8 + 3);
2922     QCOMPARE(edit->clipRect().height(), edit->height());
2923
2924     // Alignment and wrapping don't affect the clip rect.
2925     edit->setHAlign(QQuickTextEdit::AlignRight);
2926     QCOMPARE(edit->clipRect().x(), qreal(0));
2927     QCOMPARE(edit->clipRect().y(), qreal(0));
2928     QCOMPARE(edit->clipRect().width(), edit->width() + 8 + 3);
2929     QCOMPARE(edit->clipRect().height(), edit->height());
2930
2931     edit->setWrapMode(QQuickTextEdit::Wrap);
2932     QCOMPARE(edit->clipRect().x(), qreal(0));
2933     QCOMPARE(edit->clipRect().y(), qreal(0));
2934     QCOMPARE(edit->clipRect().width(), edit->width() + 8 + 3);
2935     QCOMPARE(edit->clipRect().height(), edit->height());
2936
2937     edit->setVAlign(QQuickTextEdit::AlignBottom);
2938     QCOMPARE(edit->clipRect().x(), qreal(0));
2939     QCOMPARE(edit->clipRect().y(), qreal(0));
2940     QCOMPARE(edit->clipRect().width(), edit->width() + 8 + 3);
2941     QCOMPARE(edit->clipRect().height(), edit->height());
2942 }
2943
2944 void tst_qquicktextedit::boundingRect()
2945 {
2946     QQmlComponent component(&engine);
2947     component.setData("import QtQuick 2.0\n TextEdit {}", QUrl());
2948     QScopedPointer<QObject> object(component.create());
2949     QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(object.data());
2950     QVERIFY(edit);
2951
2952     QTextLayout layout;
2953     layout.setFont(edit->font());
2954
2955     if (!qmlDisableDistanceField()) {
2956         QTextOption option;
2957         option.setUseDesignMetrics(true);
2958         layout.setTextOption(option);
2959     }
2960     layout.beginLayout();
2961     QTextLine line = layout.createLine();
2962     layout.endLayout();
2963
2964     QCOMPARE(edit->boundingRect().x(), qreal(0));
2965     QCOMPARE(edit->boundingRect().y(), qreal(0));
2966     QCOMPARE(edit->boundingRect().width(), edit->cursorRectangle().width());
2967     QCOMPARE(edit->boundingRect().height(), line.height());
2968
2969     edit->setText("Hello World");
2970
2971     layout.setText(edit->text());
2972     layout.beginLayout();
2973     line = layout.createLine();
2974     layout.endLayout();
2975
2976     QCOMPARE(edit->boundingRect().x(), qreal(0));
2977     QCOMPARE(edit->boundingRect().y(), qreal(0));
2978     QCOMPARE(edit->boundingRect().width(), line.naturalTextWidth() + edit->cursorRectangle().width() + 3);
2979     QCOMPARE(edit->boundingRect().height(), line.height());
2980
2981     // the size of the bounding rect shouldn't be bounded by the size of item.
2982     edit->setWidth(edit->width() / 2);
2983     QCOMPARE(edit->boundingRect().x(), qreal(0));
2984     QCOMPARE(edit->boundingRect().y(), qreal(0));
2985     QCOMPARE(edit->boundingRect().width(), line.naturalTextWidth() + edit->cursorRectangle().width() + 3);
2986     QCOMPARE(edit->boundingRect().height(), line.height());
2987
2988     edit->setHeight(edit->height() * 2);
2989     QCOMPARE(edit->boundingRect().x(), qreal(0));
2990     QCOMPARE(edit->boundingRect().y(), qreal(0));
2991     QCOMPARE(edit->boundingRect().width(), line.naturalTextWidth() + edit->cursorRectangle().width() + 3);
2992     QCOMPARE(edit->boundingRect().height(), line.height());
2993
2994     QQmlComponent cursorComponent(&engine);
2995     cursorComponent.setData("import QtQuick 2.0\nRectangle { height: 20; width: 8 }", QUrl());
2996
2997     edit->setCursorDelegate(&cursorComponent);
2998     edit->setCursorVisible(true);
2999
3000     // Don't include the size of a cursor delegate as it has its own bounding rect.
3001     QCOMPARE(edit->boundingRect().x(), qreal(0));
3002     QCOMPARE(edit->boundingRect().y(), qreal(0));
3003     QCOMPARE(edit->boundingRect().width(), line.naturalTextWidth());
3004     QCOMPARE(edit->boundingRect().height(), line.height());
3005
3006     edit->setHAlign(QQuickTextEdit::AlignRight);
3007     QCOMPARE(edit->boundingRect().x(), edit->width() - line.naturalTextWidth());
3008     QCOMPARE(edit->boundingRect().y(), qreal(0));
3009     QCOMPARE(edit->boundingRect().width(), line.naturalTextWidth());
3010     QCOMPARE(edit->boundingRect().height(), line.height());
3011
3012     edit->setWrapMode(QQuickTextEdit::Wrap);
3013     QCOMPARE(edit->boundingRect().right(), edit->width());
3014     QCOMPARE(edit->boundingRect().y(), qreal(0));
3015     QVERIFY(edit->boundingRect().width() < line.naturalTextWidth());
3016     QVERIFY(edit->boundingRect().height() > line.height());
3017
3018     edit->setVAlign(QQuickTextEdit::AlignBottom);
3019     QCOMPARE(edit->boundingRect().right(), edit->width());
3020     QCOMPARE(edit->boundingRect().bottom(), edit->height());
3021     QVERIFY(edit->boundingRect().width() < line.naturalTextWidth());
3022     QVERIFY(edit->boundingRect().height() > line.height());
3023 }
3024
3025 void tst_qquicktextedit::preeditCursorRectangle()
3026 {
3027     QString preeditText = "super";
3028
3029     QQuickView view(testFileUrl("inputMethodEvent.qml"));
3030     view.show();
3031     view.requestActivateWindow();
3032     QTest::qWaitForWindowActive(&view);
3033
3034     QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(view.rootObject());
3035     QVERIFY(edit);
3036
3037     QQuickItem *cursor = edit->findChild<QQuickItem *>("cursor");
3038     QVERIFY(cursor);
3039
3040     QSignalSpy editSpy(edit, SIGNAL(cursorRectangleChanged()));
3041     QSignalSpy panelSpy(qGuiApp->inputMethod(), SIGNAL(cursorRectangleChanged()));
3042
3043     QRectF currentRect;
3044
3045     QCOMPARE(QGuiApplication::focusObject(), static_cast<QObject *>(edit));
3046     QInputMethodQueryEvent query(Qt::ImCursorRectangle);
3047     QCoreApplication::sendEvent(edit, &query);
3048     QRectF previousRect = query.value(Qt::ImCursorRectangle).toRectF();
3049
3050     // Verify that the micro focus rect is positioned the same for position 0 as
3051     // it would be if there was no preedit text.
3052     QInputMethodEvent imEvent(preeditText, QList<QInputMethodEvent::Attribute>()
3053             << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, preeditText.length(), QVariant()));
3054     QCoreApplication::sendEvent(edit, &imEvent);
3055     QCoreApplication::sendEvent(edit, &query);
3056     currentRect = query.value(Qt::ImCursorRectangle).toRectF();
3057     QCOMPARE(edit->cursorRectangle(), currentRect);
3058     QCOMPARE(cursor->pos(), currentRect.topLeft());
3059     QCOMPARE(currentRect, previousRect);
3060
3061     // Verify that the micro focus rect moves to the left as the cursor position
3062     // is incremented.
3063     editSpy.clear();
3064     panelSpy.clear();
3065     for (int i = 1; i <= 5; ++i) {
3066         QInputMethodEvent imEvent(preeditText, QList<QInputMethodEvent::Attribute>()
3067                 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, i, preeditText.length(), QVariant()));
3068         QCoreApplication::sendEvent(edit, &imEvent);
3069         QCoreApplication::sendEvent(edit, &query);
3070         currentRect = query.value(Qt::ImCursorRectangle).toRectF();
3071         QCOMPARE(edit->cursorRectangle(), currentRect);
3072         QCOMPARE(cursor->pos(), currentRect.topLeft());
3073         QVERIFY(previousRect.left() < currentRect.left());
3074         QCOMPARE(editSpy.count(), 1); editSpy.clear();
3075         QCOMPARE(panelSpy.count(), 1); panelSpy.clear();
3076         previousRect = currentRect;
3077     }
3078
3079     // Verify that if the cursor rectangle is updated if the pre-edit text changes
3080     // but the (non-zero) cursor position is the same.
3081     editSpy.clear();
3082     panelSpy.clear();
3083     {   QInputMethodEvent imEvent("wwwww", QList<QInputMethodEvent::Attribute>()
3084                 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 5, 1, QVariant()));
3085         QCoreApplication::sendEvent(edit, &imEvent); }
3086     QCoreApplication::sendEvent(edit, &query);
3087     currentRect = query.value(Qt::ImCursorRectangle).toRectF();
3088     QCOMPARE(edit->cursorRectangle(), currentRect);
3089     QCOMPARE(cursor->pos(), currentRect.topLeft());
3090     QCOMPARE(editSpy.count(), 1);
3091     QCOMPARE(panelSpy.count(), 1);
3092
3093     // Verify that if there is no preedit cursor then the micro focus rect is the
3094     // same as it would be if it were positioned at the end of the preedit text.
3095     editSpy.clear();
3096     panelSpy.clear();
3097     {   QInputMethodEvent imEvent(preeditText, QList<QInputMethodEvent::Attribute>());
3098         QCoreApplication::sendEvent(edit, &imEvent); }
3099     QCoreApplication::sendEvent(edit, &query);
3100     currentRect = query.value(Qt::ImCursorRectangle).toRectF();
3101     QCOMPARE(edit->cursorRectangle(), currentRect);
3102     QCOMPARE(cursor->pos(), currentRect.topLeft());
3103     QCOMPARE(currentRect, previousRect);
3104     QCOMPARE(editSpy.count(), 1);
3105     QCOMPARE(panelSpy.count(), 1);
3106 }
3107
3108 void tst_qquicktextedit::inputMethodComposing()
3109 {
3110     QString text = "supercalifragisiticexpialidocious!";
3111
3112     QQuickView view(testFileUrl("inputContext.qml"));
3113     view.show();
3114     view.requestActivateWindow();
3115     QTest::qWaitForWindowActive(&view);
3116
3117     QQuickTextEdit *edit = qobject_cast<QQuickTextEdit *>(view.rootObject());
3118     QVERIFY(edit);
3119     QCOMPARE(QGuiApplication::focusObject(), static_cast<QObject *>(edit));
3120
3121     QSignalSpy spy(edit, SIGNAL(inputMethodComposingChanged()));
3122     edit->setCursorPosition(12);
3123
3124     QCOMPARE(edit->isInputMethodComposing(), false);
3125
3126     {
3127         QInputMethodEvent event(text.mid(3), QList<QInputMethodEvent::Attribute>());
3128         QGuiApplication::sendEvent(edit, &event);
3129     }
3130
3131     QCOMPARE(edit->isInputMethodComposing(), true);
3132     QCOMPARE(spy.count(), 1);
3133
3134     {
3135         QInputMethodEvent event(text.mid(12), QList<QInputMethodEvent::Attribute>());
3136         QGuiApplication::sendEvent(edit, &event);
3137     }
3138     QCOMPARE(spy.count(), 1);
3139
3140     {
3141         QInputMethodEvent event;
3142         QGuiApplication::sendEvent(edit, &event);
3143     }
3144     QCOMPARE(edit->isInputMethodComposing(), false);
3145     QCOMPARE(spy.count(), 2);
3146
3147     // Changing the text while not composing doesn't alter the composing state.
3148     edit->setText(text.mid(0, 16));
3149     QCOMPARE(edit->isInputMethodComposing(), false);
3150     QCOMPARE(spy.count(), 2);
3151
3152     {
3153         QInputMethodEvent event(text.mid(16), QList<QInputMethodEvent::Attribute>());
3154         QGuiApplication::sendEvent(edit, &event);
3155     }
3156     QCOMPARE(edit->isInputMethodComposing(), true);
3157     QCOMPARE(spy.count(), 3);
3158
3159     // Changing the text while composing cancels composition.
3160     edit->setText(text.mid(0, 12));
3161     QCOMPARE(edit->isInputMethodComposing(), false);
3162     QCOMPARE(spy.count(), 4);
3163
3164     {   // Preedit cursor positioned outside (empty) preedit; composing.
3165         QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>()
3166                 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, -2, 1, QVariant()));
3167         QGuiApplication::sendEvent(edit, &event);
3168     }
3169     QCOMPARE(edit->isInputMethodComposing(), true);
3170     QCOMPARE(spy.count(), 5);
3171
3172     {   // Cursor hidden; composing
3173         QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>()
3174                 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 0, QVariant()));
3175         QGuiApplication::sendEvent(edit, &event);
3176     }
3177     QCOMPARE(edit->isInputMethodComposing(), true);
3178     QCOMPARE(spy.count(), 5);
3179
3180     {   // Default cursor attributes; composing.
3181         QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>()
3182                 << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, 0, 1, QVariant()));
3183         QGuiApplication::sendEvent(edit, &event);
3184     }
3185     QCOMPARE(edit->isInputMethodComposing(), true);
3186     QCOMPARE(spy.count(), 5);
3187
3188     {   // Selections are persisted: not composing
3189         QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>()
3190                 << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 2, 4, QVariant()));
3191         QGuiApplication::sendEvent(edit, &event);
3192     }
3193     QCOMPARE(edit->isInputMethodComposing(), false);
3194     QCOMPARE(spy.count(), 6);
3195
3196     edit->setCursorPosition(0);
3197
3198     {   // Formatting applied; composing.
3199         QTextCharFormat format;
3200         format.setUnderlineStyle(QTextCharFormat::SingleUnderline);
3201         QInputMethodEvent event(QString(), QList<QInputMethodEvent::Attribute>()
3202                 << QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, 2, 4, format));
3203         QGuiApplication::sendEvent(edit, &event);
3204     }
3205     QCOMPARE(edit->isInputMethodComposing(), true);
3206     QCOMPARE(spy.count(), 7);
3207
3208     {
3209         QInputMethodEvent event;
3210         QGuiApplication::sendEvent(edit, &event);
3211     }
3212     QCOMPARE(edit->isInputMethodComposing(), false);
3213     QCOMPARE(spy.count(), 8);
3214 }
3215
3216 void tst_qquicktextedit::cursorRectangleSize()
3217 {
3218     QQuickView *canvas = new QQuickView(testFileUrl("positionAt.qml"));
3219     QVERIFY(canvas->rootObject() != 0);
3220     QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit *>(canvas->rootObject());
3221
3222     // make sure cursor rectangle is not at (0,0)
3223     textEdit->setX(10);
3224     textEdit->setY(10);
3225     textEdit->setCursorPosition(3);
3226     QVERIFY(textEdit != 0);
3227     textEdit->setFocus(true);
3228     canvas->show();
3229     canvas->requestActivateWindow();
3230     QTest::qWaitForWindowActive(canvas);
3231
3232     QInputMethodQueryEvent event(Qt::ImCursorRectangle);
3233     qApp->sendEvent(textEdit, &event);
3234     QRectF cursorRectFromQuery = event.value(Qt::ImCursorRectangle).toRectF();
3235
3236     QRectF cursorRectFromItem = textEdit->cursorRectangle();
3237     QRectF cursorRectFromPositionToRectangle = textEdit->positionToRectangle(textEdit->cursorPosition());
3238
3239     // item and input query cursor rectangles match
3240     QCOMPARE(cursorRectFromItem, cursorRectFromQuery);
3241
3242     // item cursor rectangle and positionToRectangle calculations match
3243     QCOMPARE(cursorRectFromItem, cursorRectFromPositionToRectangle);
3244
3245     // item-canvas transform and input item transform match
3246     QCOMPARE(QQuickItemPrivate::get(textEdit)->itemToCanvasTransform(), qApp->inputMethod()->inputItemTransform());
3247
3248     // input panel cursorRectangle property and tranformed item cursor rectangle match
3249     QRectF sceneCursorRect = QQuickItemPrivate::get(textEdit)->itemToCanvasTransform().mapRect(cursorRectFromItem);
3250     QCOMPARE(sceneCursorRect, qApp->inputMethod()->cursorRectangle());
3251
3252     delete canvas;
3253 }
3254
3255 void tst_qquicktextedit::getText_data()
3256 {
3257     QTest::addColumn<QString>("text");
3258     QTest::addColumn<int>("start");
3259     QTest::addColumn<int>("end");
3260     QTest::addColumn<QString>("expectedText");
3261
3262     const QString richBoldText = QStringLiteral("This is some <b>bold</b> text");
3263     const QString plainBoldText = QStringLiteral("This is some bold text");
3264     const QString richBoldTextLB = QStringLiteral("This is some<br/><b>bold</b> text");
3265     const QString plainBoldTextLB = QString(QStringLiteral("This is some\nbold text")).replace(QLatin1Char('\n'), QChar(QChar::LineSeparator));
3266
3267     QTest::newRow("all plain text")
3268             << standard.at(0)
3269             << 0 << standard.at(0).length()
3270             << standard.at(0);
3271
3272     QTest::newRow("plain text sub string")
3273             << standard.at(0)
3274             << 0 << 12
3275             << standard.at(0).mid(0, 12);
3276
3277     QTest::newRow("plain text sub string reversed")
3278             << standard.at(0)
3279             << 12 << 0
3280             << standard.at(0).mid(0, 12);
3281
3282     QTest::newRow("plain text cropped beginning")
3283             << standard.at(0)
3284             << -3 << 4
3285             << standard.at(0).mid(0, 4);
3286
3287     QTest::newRow("plain text cropped end")
3288             << standard.at(0)
3289             << 23 << standard.at(0).length() + 8
3290             << standard.at(0).mid(23);
3291
3292     QTest::newRow("plain text cropped beginning and end")
3293             << standard.at(0)
3294             << -9 << standard.at(0).length() + 4
3295             << standard.at(0);
3296
3297     QTest::newRow("all rich text")
3298             << richBoldText
3299             << 0 << plainBoldText.length()
3300             << plainBoldText;
3301
3302     QTest::newRow("rich text sub string")
3303             << richBoldText
3304             << 14 << 21
3305             << plainBoldText.mid(14, 7);
3306
3307     // Line break.
3308     QTest::newRow("all plain text (line break)")
3309             << standard.at(1)
3310             << 0 << standard.at(1).length()
3311             << standard.at(1);
3312
3313     QTest::newRow("plain text sub string (line break)")
3314             << standard.at(1)
3315             << 0 << 12
3316             << standard.at(1).mid(0, 12);
3317
3318     QTest::newRow("plain text sub string reversed (line break)")
3319             << standard.at(1)
3320             << 12 << 0
3321             << standard.at(1).mid(0, 12);
3322
3323     QTest::newRow("plain text cropped beginning (line break)")
3324             << standard.at(1)
3325             << -3 << 4
3326             << standard.at(1).mid(0, 4);
3327
3328     QTest::newRow("plain text cropped end (line break)")
3329             << standard.at(1)
3330             << 23 << standard.at(1).length() + 8
3331             << standard.at(1).mid(23);
3332
3333     QTest::newRow("plain text cropped beginning and end (line break)")
3334             << standard.at(1)
3335             << -9 << standard.at(1).length() + 4
3336             << standard.at(1);
3337
3338     QTest::newRow("all rich text (line break)")
3339             << richBoldTextLB
3340             << 0 << plainBoldTextLB.length()
3341             << plainBoldTextLB;
3342
3343     QTest::newRow("rich text sub string (line break)")
3344             << richBoldTextLB
3345             << 14 << 21
3346             << plainBoldTextLB.mid(14, 7);
3347 }
3348
3349 void tst_qquicktextedit::getText()
3350 {
3351     QFETCH(QString, text);
3352     QFETCH(int, start);
3353     QFETCH(int, end);
3354     QFETCH(QString, expectedText);
3355
3356     QString componentStr = "import QtQuick 2.0\nTextEdit { textFormat: TextEdit.AutoText; text: \"" + text + "\" }";
3357     QQmlComponent textEditComponent(&engine);
3358     textEditComponent.setData(componentStr.toLatin1(), QUrl());
3359     QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(textEditComponent.create());
3360     QVERIFY(textEdit != 0);
3361
3362     QCOMPARE(textEdit->getText(start, end), expectedText);
3363 }
3364
3365 void tst_qquicktextedit::getFormattedText_data()
3366 {
3367     QTest::addColumn<QString>("text");
3368     QTest::addColumn<QQuickTextEdit::TextFormat>("textFormat");
3369     QTest::addColumn<int>("start");
3370     QTest::addColumn<int>("end");
3371     QTest::addColumn<QString>("expectedText");
3372
3373     const QString richBoldText = QStringLiteral("This is some <b>bold</b> text");
3374     const QString plainBoldText = QStringLiteral("This is some bold text");
3375
3376     QTest::newRow("all plain text")
3377             << standard.at(0)
3378             << QQuickTextEdit::PlainText
3379             << 0 << standard.at(0).length()
3380             << standard.at(0);
3381
3382     QTest::newRow("plain text sub string")
3383             << standard.at(0)
3384             << QQuickTextEdit::PlainText
3385             << 0 << 12
3386             << standard.at(0).mid(0, 12);
3387
3388     QTest::newRow("plain text sub string reversed")
3389             << standard.at(0)
3390             << QQuickTextEdit::PlainText
3391             << 12 << 0
3392             << standard.at(0).mid(0, 12);
3393
3394     QTest::newRow("plain text cropped beginning")
3395             << standard.at(0)
3396             << QQuickTextEdit::PlainText
3397             << -3 << 4
3398             << standard.at(0).mid(0, 4);
3399
3400     QTest::newRow("plain text cropped end")
3401             << standard.at(0)
3402             << QQuickTextEdit::PlainText
3403             << 23 << standard.at(0).length() + 8
3404             << standard.at(0).mid(23);
3405
3406     QTest::newRow("plain text cropped beginning and end")
3407             << standard.at(0)
3408             << QQuickTextEdit::PlainText
3409             << -9 << standard.at(0).length() + 4
3410             << standard.at(0);
3411
3412     QTest::newRow("all rich (Auto) text")
3413             << richBoldText
3414             << QQuickTextEdit::AutoText
3415             << 0 << plainBoldText.length()
3416             << QString("This is some \\<.*\\>bold\\</.*\\> text");
3417
3418     QTest::newRow("all rich (Rich) text")
3419             << richBoldText
3420             << QQuickTextEdit::RichText
3421             << 0 << plainBoldText.length()
3422             << QString("This is some \\<.*\\>bold\\</.*\\> text");
3423
3424     QTest::newRow("all rich (Plain) text")
3425             << richBoldText
3426             << QQuickTextEdit::PlainText
3427             << 0 << richBoldText.length()
3428             << richBoldText;
3429
3430     QTest::newRow("rich (Auto) text sub string")
3431             << richBoldText
3432             << QQuickTextEdit::AutoText
3433             << 14 << 21
3434             << QString("\\<.*\\>old\\</.*\\> tex");
3435
3436     QTest::newRow("rich (Rich) text sub string")
3437             << richBoldText
3438             << QQuickTextEdit::RichText
3439             << 14 << 21
3440             << QString("\\<.*\\>old\\</.*\\> tex");
3441
3442     QTest::newRow("rich (Plain) text sub string")
3443             << richBoldText
3444             << QQuickTextEdit::PlainText
3445             << 17 << 27
3446             << richBoldText.mid(17, 10);
3447 }
3448
3449 void tst_qquicktextedit::getFormattedText()
3450 {
3451     QFETCH(QString, text);
3452     QFETCH(QQuickTextEdit::TextFormat, textFormat);
3453     QFETCH(int, start);
3454     QFETCH(int, end);
3455     QFETCH(QString, expectedText);
3456
3457     QString componentStr = "import QtQuick 2.0\nTextEdit {}";
3458     QQmlComponent textEditComponent(&engine);
3459     textEditComponent.setData(componentStr.toLatin1(), QUrl());
3460     QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(textEditComponent.create());
3461     QVERIFY(textEdit != 0);
3462
3463     textEdit->setTextFormat(textFormat);
3464     textEdit->setText(text);
3465
3466     if (textFormat == QQuickTextEdit::RichText
3467             || (textFormat == QQuickTextEdit::AutoText && Qt::mightBeRichText(text))) {
3468         QVERIFY(textEdit->getFormattedText(start, end).contains(QRegExp(expectedText)));
3469     } else {
3470         QCOMPARE(textEdit->getFormattedText(start, end), expectedText);
3471     }
3472 }
3473
3474 void tst_qquicktextedit::insert_data()
3475 {
3476     QTest::addColumn<QString>("text");
3477     QTest::addColumn<QQuickTextEdit::TextFormat>("textFormat");
3478     QTest::addColumn<int>("selectionStart");
3479     QTest::addColumn<int>("selectionEnd");
3480     QTest::addColumn<int>("insertPosition");
3481     QTest::addColumn<QString>("insertText");
3482     QTest::addColumn<QString>("expectedText");
3483     QTest::addColumn<int>("expectedSelectionStart");
3484     QTest::addColumn<int>("expectedSelectionEnd");
3485     QTest::addColumn<int>("expectedCursorPosition");
3486     QTest::addColumn<bool>("selectionChanged");
3487     QTest::addColumn<bool>("cursorPositionChanged");
3488
3489     QTest::newRow("at cursor position (beginning)")
3490             << standard.at(0) << QQuickTextEdit::PlainText
3491             << 0 << 0 << 0
3492             << QString("Hello")
3493             << QString("Hello") + standard.at(0)
3494             << 5 << 5 << 5
3495             << false << true;
3496
3497     QTest::newRow("at cursor position (end)")
3498             << standard.at(0) << QQuickTextEdit::PlainText
3499             << standard.at(0).length() << standard.at(0).length() << standard.at(0).length()
3500             << QString("Hello")
3501             << standard.at(0) + QString("Hello")
3502             << standard.at(0).length() + 5 << standard.at(0).length() + 5 << standard.at(0).length() + 5
3503             << false << true;
3504
3505     QTest::newRow("at cursor position (middle)")
3506             << standard.at(0) << QQuickTextEdit::PlainText
3507             << 18 << 18 << 18
3508             << QString("Hello")
3509             << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
3510             << 23 << 23 << 23
3511             << false << true;
3512
3513     QTest::newRow("after cursor position (beginning)")
3514             << standard.at(0) << QQuickTextEdit::PlainText
3515             << 0 << 0 << 18
3516             << QString("Hello")
3517             << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
3518             << 0 << 0 << 0
3519             << false << false;
3520
3521     QTest::newRow("before cursor position (end)")
3522             << standard.at(0) << QQuickTextEdit::PlainText
3523             << standard.at(0).length() << standard.at(0).length() << 18
3524             << QString("Hello")
3525             << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
3526             << standard.at(0).length() + 5 << standard.at(0).length() + 5 << standard.at(0).length() + 5
3527             << false << true;
3528
3529     QTest::newRow("before cursor position (middle)")
3530             << standard.at(0) << QQuickTextEdit::PlainText
3531             << 18 << 18 << 0
3532             << QString("Hello")
3533             << QString("Hello") + standard.at(0)
3534             << 23 << 23 << 23
3535             << false << true;
3536
3537     QTest::newRow("after cursor position (middle)")
3538             << standard.at(0) << QQuickTextEdit::PlainText
3539             << 18 << 18 << standard.at(0).length()
3540             << QString("Hello")
3541             << standard.at(0) + QString("Hello")
3542             << 18 << 18 << 18
3543             << false << false;
3544
3545     QTest::newRow("before selection")
3546             << standard.at(0) << QQuickTextEdit::PlainText
3547             << 14 << 19 << 0
3548             << QString("Hello")
3549             << QString("Hello") + standard.at(0)
3550             << 19 << 24 << 24
3551             << false << true;
3552
3553     QTest::newRow("before reversed selection")
3554             << standard.at(0) << QQuickTextEdit::PlainText
3555             << 19 << 14 << 0
3556             << QString("Hello")
3557             << QString("Hello") + standard.at(0)
3558             << 19 << 24 << 19
3559             << false << true;
3560
3561     QTest::newRow("after selection")
3562             << standard.at(0) << QQuickTextEdit::PlainText
3563             << 14 << 19 << standard.at(0).length()
3564             << QString("Hello")
3565             << standard.at(0) + QString("Hello")
3566             << 14 << 19 << 19
3567             << false << false;
3568
3569     QTest::newRow("after reversed selection")
3570             << standard.at(0) << QQuickTextEdit::PlainText
3571             << 19 << 14 << standard.at(0).length()
3572             << QString("Hello")
3573             << standard.at(0) + QString("Hello")
3574             << 14 << 19 << 14
3575             << false << false;
3576
3577     QTest::newRow("into selection")
3578             << standard.at(0) << QQuickTextEdit::PlainText
3579             << 14 << 19 << 18
3580             << QString("Hello")
3581             << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
3582             << 14 << 24 << 24
3583             << true << true;
3584
3585     QTest::newRow("into reversed selection")
3586             << standard.at(0) << QQuickTextEdit::PlainText
3587             << 19 << 14 << 18
3588             << QString("Hello")
3589             << standard.at(0).mid(0, 18) + QString("Hello") + standard.at(0).mid(18)
3590             << 14 << 24 << 14
3591             << true << false;
3592
3593     QTest::newRow("rich text into plain text")
3594             << standard.at(0) << QQuickTextEdit::PlainText
3595             << 0 << 0 << 0
3596             << QString("<b>Hello</b>")
3597             << QString("<b>Hello</b>") + standard.at(0)
3598             << 12 << 12 << 12
3599             << false << true;
3600
3601     QTest::newRow("rich text into rich text")
3602             << standard.at(0) << QQuickTextEdit::RichText
3603             << 0 << 0 << 0
3604             << QString("<b>Hello</b>")
3605             << QString("Hello") + standard.at(0)
3606             << 5 << 5 << 5
3607             << false << true;
3608
3609     QTest::newRow("rich text into auto text")
3610             << standard.at(0) << QQuickTextEdit::AutoText
3611             << 0 << 0 << 0
3612             << QString("<b>Hello</b>")
3613             << QString("Hello") + standard.at(0)
3614             << 5 << 5 << 5
3615             << false << true;
3616
3617     QTest::newRow("before start")
3618             << standard.at(0) << QQuickTextEdit::PlainText
3619             << 0 << 0 << -3
3620             << QString("Hello")
3621             << standard.at(0)
3622             << 0 << 0 << 0
3623             << false << false;
3624
3625     QTest::newRow("past end")
3626             << standard.at(0) << QQuickTextEdit::PlainText
3627             << 0 << 0 << standard.at(0).length() + 3
3628             << QString("Hello")
3629             << standard.at(0)
3630             << 0 << 0 << 0
3631             << false << false;
3632 }
3633
3634 void tst_qquicktextedit::insert()
3635 {
3636     QFETCH(QString, text);
3637     QFETCH(QQuickTextEdit::TextFormat, textFormat);
3638     QFETCH(int, selectionStart);
3639     QFETCH(int, selectionEnd);
3640     QFETCH(int, insertPosition);
3641     QFETCH(QString, insertText);
3642     QFETCH(QString, expectedText);
3643     QFETCH(int, expectedSelectionStart);
3644     QFETCH(int, expectedSelectionEnd);
3645     QFETCH(int, expectedCursorPosition);
3646     QFETCH(bool, selectionChanged);
3647     QFETCH(bool, cursorPositionChanged);
3648
3649     QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"" + text + "\" }";
3650     QQmlComponent textEditComponent(&engine);
3651     textEditComponent.setData(componentStr.toLatin1(), QUrl());
3652     QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(textEditComponent.create());
3653     QVERIFY(textEdit != 0);
3654
3655     textEdit->setTextFormat(textFormat);
3656     textEdit->select(selectionStart, selectionEnd);
3657
3658     QSignalSpy selectionSpy(textEdit, SIGNAL(selectedTextChanged()));
3659     QSignalSpy selectionStartSpy(textEdit, SIGNAL(selectionStartChanged()));
3660     QSignalSpy selectionEndSpy(textEdit, SIGNAL(selectionEndChanged()));
3661     QSignalSpy textSpy(textEdit, SIGNAL(textChanged()));
3662     QSignalSpy cursorPositionSpy(textEdit, SIGNAL(cursorPositionChanged()));
3663
3664     textEdit->insert(insertPosition, insertText);
3665
3666     if (textFormat == QQuickTextEdit::RichText || (textFormat == QQuickTextEdit::AutoText && (
3667             Qt::mightBeRichText(text) || Qt::mightBeRichText(insertText)))) {
3668         QCOMPARE(textEdit->getText(0, expectedText.length()), expectedText);
3669     } else {
3670         QCOMPARE(textEdit->text(), expectedText);
3671
3672     }
3673     QCOMPARE(textEdit->length(), expectedText.length());
3674
3675     QCOMPARE(textEdit->selectionStart(), expectedSelectionStart);
3676     QCOMPARE(textEdit->selectionEnd(), expectedSelectionEnd);
3677     QCOMPARE(textEdit->cursorPosition(), expectedCursorPosition);
3678
3679     if (selectionStart > selectionEnd)
3680         qSwap(selectionStart, selectionEnd);
3681
3682     QEXPECT_FAIL("into selection", "selectedTextChanged signal isn't emitted on edits within selection", Continue);
3683     QEXPECT_FAIL("into reversed selection", "selectedTextChanged signal isn't emitted on edits within selection", Continue);
3684     QCOMPARE(selectionSpy.count() > 0, selectionChanged);
3685     QCOMPARE(selectionStartSpy.count() > 0, selectionStart != expectedSelectionStart);
3686     QEXPECT_FAIL("into reversed selection", "selectionEndChanged signal not emitted", Continue);
3687     QCOMPARE(selectionEndSpy.count() > 0, selectionEnd != expectedSelectionEnd);
3688     QCOMPARE(textSpy.count() > 0, text != expectedText);
3689     QCOMPARE(cursorPositionSpy.count() > 0, cursorPositionChanged);
3690 }
3691
3692 void tst_qquicktextedit::remove_data()
3693 {
3694     QTest::addColumn<QString>("text");
3695     QTest::addColumn<QQuickTextEdit::TextFormat>("textFormat");
3696     QTest::addColumn<int>("selectionStart");
3697     QTest::addColumn<int>("selectionEnd");
3698     QTest::addColumn<int>("removeStart");
3699     QTest::addColumn<int>("removeEnd");
3700     QTest::addColumn<QString>("expectedText");
3701     QTest::addColumn<int>("expectedSelectionStart");
3702     QTest::addColumn<int>("expectedSelectionEnd");
3703     QTest::addColumn<int>("expectedCursorPosition");
3704     QTest::addColumn<bool>("selectionChanged");
3705     QTest::addColumn<bool>("cursorPositionChanged");
3706
3707     const QString richBoldText = QStringLiteral("This is some <b>bold</b> text");
3708     const QString plainBoldText = QStringLiteral("This is some bold text");
3709
3710     QTest::newRow("from cursor position (beginning)")
3711             << standard.at(0) << QQuickTextEdit::PlainText
3712             << 0 << 0
3713             << 0 << 5
3714             << standard.at(0).mid(5)
3715             << 0 << 0 << 0
3716             << false << false;
3717
3718     QTest::newRow("to cursor position (beginning)")
3719             << standard.at(0) << QQuickTextEdit::PlainText
3720             << 0 << 0
3721             << 5 << 0
3722             << standard.at(0).mid(5)
3723             << 0 << 0 << 0
3724             << false << false;
3725
3726     QTest::newRow("to cursor position (end)")
3727             << standard.at(0) << QQuickTextEdit::PlainText
3728             << standard.at(0).length() << standard.at(0).length()
3729             << standard.at(0).length() << standard.at(0).length() - 5
3730             << standard.at(0).mid(0, standard.at(0).length() - 5)
3731             << standard.at(0).length() - 5 << standard.at(0).length() - 5 << standard.at(0).length() - 5
3732             << false << true;
3733
3734     QTest::newRow("to cursor position (end)")
3735             << standard.at(0) << QQuickTextEdit::PlainText
3736             << standard.at(0).length() << standard.at(0).length()
3737             << standard.at(0).length() - 5 << standard.at(0).length()
3738             << standard.at(0).mid(0, standard.at(0).length() - 5)
3739             << standard.at(0).length() - 5 << standard.at(0).length() - 5 << standard.at(0).length() - 5
3740             << false << true;
3741
3742     QTest::newRow("from cursor position (middle)")
3743             << standard.at(0) << QQuickTextEdit::PlainText
3744             << 18 << 18
3745             << 18 << 23
3746             << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
3747             << 18 << 18 << 18
3748             << false << false;
3749
3750     QTest::newRow("to cursor position (middle)")
3751             << standard.at(0) << QQuickTextEdit::PlainText
3752             << 23 << 23
3753             << 18 << 23
3754             << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
3755             << 18 << 18 << 18
3756             << false << true;
3757
3758     QTest::newRow("after cursor position (beginning)")
3759             << standard.at(0) << QQuickTextEdit::PlainText
3760             << 0 << 0
3761             << 18 << 23
3762             << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
3763             << 0 << 0 << 0
3764             << false << false;
3765
3766     QTest::newRow("before cursor position (end)")
3767             << standard.at(0) << QQuickTextEdit::PlainText
3768             << standard.at(0).length() << standard.at(0).length()
3769             << 18 << 23
3770             << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
3771             << standard.at(0).length() - 5 << standard.at(0).length() - 5 << standard.at(0).length() - 5
3772             << false << true;
3773
3774     QTest::newRow("before cursor position (middle)")
3775             << standard.at(0) << QQuickTextEdit::PlainText
3776             << 23 << 23
3777             << 0 << 5
3778             << standard.at(0).mid(5)
3779             << 18 << 18 << 18
3780             << false << true;
3781
3782     QTest::newRow("after cursor position (middle)")
3783             << standard.at(0) << QQuickTextEdit::PlainText
3784             << 18 << 18
3785             << 18 << 23
3786             << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
3787             << 18 << 18 << 18
3788             << false << false;
3789
3790     QTest::newRow("before selection")
3791             << standard.at(0) << QQuickTextEdit::PlainText
3792             << 14 << 19
3793             << 0 << 5
3794             << standard.at(0).mid(5)
3795             << 9 << 14 << 14
3796             << false << true;
3797
3798     QTest::newRow("before reversed selection")
3799             << standard.at(0) << QQuickTextEdit::PlainText
3800             << 19 << 14
3801             << 0 << 5
3802             << standard.at(0).mid(5)
3803             << 9 << 14 << 9
3804             << false << true;
3805
3806     QTest::newRow("after selection")
3807             << standard.at(0) << QQuickTextEdit::PlainText
3808             << 14 << 19
3809             << standard.at(0).length() - 5 << standard.at(0).length()
3810             << standard.at(0).mid(0, standard.at(0).length() - 5)
3811             << 14 << 19 << 19
3812             << false << false;
3813
3814     QTest::newRow("after reversed selection")
3815             << standard.at(0) << QQuickTextEdit::PlainText
3816             << 19 << 14
3817             << standard.at(0).length() - 5 << standard.at(0).length()
3818             << standard.at(0).mid(0, standard.at(0).length() - 5)
3819             << 14 << 19 << 14
3820             << false << false;
3821
3822     QTest::newRow("from selection")
3823             << standard.at(0) << QQuickTextEdit::PlainText
3824             << 14 << 24
3825             << 18 << 23
3826             << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
3827             << 14 << 19 << 19
3828             << true << true;
3829
3830     QTest::newRow("from reversed selection")
3831             << standard.at(0) << QQuickTextEdit::PlainText
3832             << 24 << 14
3833             << 18 << 23
3834             << standard.at(0).mid(0, 18) + standard.at(0).mid(23)
3835             << 14 << 19 << 14
3836             << true << false;
3837
3838     QTest::newRow("plain text cropped beginning")
3839             << standard.at(0) << QQuickTextEdit::PlainText
3840             << 0 << 0
3841             << -3 << 4
3842             << standard.at(0).mid(4)
3843             << 0 << 0 << 0
3844             << false << false;
3845
3846     QTest::newRow("plain text cropped end")
3847             << standard.at(0) << QQuickTextEdit::PlainText
3848             << 0 << 0
3849             << 23 << standard.at(0).length() + 8
3850             << standard.at(0).mid(0, 23)
3851             << 0 << 0 << 0
3852             << false << false;
3853
3854     QTest::newRow("plain text cropped beginning and end")
3855             << standard.at(0) << QQuickTextEdit::PlainText
3856             << 0 << 0
3857             << -9 << standard.at(0).length() + 4
3858             << QString()
3859             << 0 << 0 << 0
3860             << false << false;
3861
3862     QTest::newRow("all rich text")
3863             << richBoldText << QQuickTextEdit::RichText
3864             << 0 << 0
3865             << 0 << plainBoldText.length()
3866             << QString()
3867             << 0 << 0 << 0
3868             << false << false;
3869
3870     QTest::newRow("rick text sub string")
3871             << richBoldText << QQuickTextEdit::RichText
3872             << 0 << 0
3873             << 14 << 21
3874             << plainBoldText.mid(0, 14) + plainBoldText.mid(21)
3875             << 0 << 0 << 0
3876             << false << false;
3877 }
3878
3879 void tst_qquicktextedit::remove()
3880 {
3881     QFETCH(QString, text);
3882     QFETCH(QQuickTextEdit::TextFormat, textFormat);
3883     QFETCH(int, selectionStart);
3884     QFETCH(int, selectionEnd);
3885     QFETCH(int, removeStart);
3886     QFETCH(int, removeEnd);
3887     QFETCH(QString, expectedText);
3888     QFETCH(int, expectedSelectionStart);
3889     QFETCH(int, expectedSelectionEnd);
3890     QFETCH(int, expectedCursorPosition);
3891     QFETCH(bool, selectionChanged);
3892     QFETCH(bool, cursorPositionChanged);
3893
3894     QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"" + text + "\" }";
3895     QQmlComponent textEditComponent(&engine);
3896     textEditComponent.setData(componentStr.toLatin1(), QUrl());
3897     QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(textEditComponent.create());
3898     QVERIFY(textEdit != 0);
3899
3900     textEdit->setTextFormat(textFormat);
3901     textEdit->select(selectionStart, selectionEnd);
3902
3903     QSignalSpy selectionSpy(textEdit, SIGNAL(selectedTextChanged()));
3904     QSignalSpy selectionStartSpy(textEdit, SIGNAL(selectionStartChanged()));
3905     QSignalSpy selectionEndSpy(textEdit, SIGNAL(selectionEndChanged()));
3906     QSignalSpy textSpy(textEdit, SIGNAL(textChanged()));
3907     QSignalSpy cursorPositionSpy(textEdit, SIGNAL(cursorPositionChanged()));
3908
3909     textEdit->remove(removeStart, removeEnd);
3910
3911     if (textFormat == QQuickTextEdit::RichText
3912             || (textFormat == QQuickTextEdit::AutoText && Qt::mightBeRichText(text))) {
3913         QCOMPARE(textEdit->getText(0, expectedText.length()), expectedText);
3914     } else {
3915         QCOMPARE(textEdit->text(), expectedText);
3916     }
3917     QCOMPARE(textEdit->length(), expectedText.length());
3918
3919     if (selectionStart > selectionEnd)  //
3920         qSwap(selectionStart, selectionEnd);
3921
3922     QCOMPARE(textEdit->selectionStart(), expectedSelectionStart);
3923     QCOMPARE(textEdit->selectionEnd(), expectedSelectionEnd);
3924     QCOMPARE(textEdit->cursorPosition(), expectedCursorPosition);
3925
3926     QEXPECT_FAIL("from selection", "selectedTextChanged signal isn't emitted on edits within selection", Continue);
3927     QEXPECT_FAIL("from reversed selection", "selectedTextChanged signal isn't emitted on edits within selection", Continue);
3928     QCOMPARE(selectionSpy.count() > 0, selectionChanged);
3929     QCOMPARE(selectionStartSpy.count() > 0, selectionStart != expectedSelectionStart);
3930     QEXPECT_FAIL("from reversed selection", "selectionEndChanged signal not emitted", Continue);
3931     QCOMPARE(selectionEndSpy.count() > 0, selectionEnd != expectedSelectionEnd);
3932     QCOMPARE(textSpy.count() > 0, text != expectedText);
3933
3934
3935     if (cursorPositionChanged)  //
3936         QVERIFY(cursorPositionSpy.count() > 0);
3937 }
3938
3939
3940 void tst_qquicktextedit::keySequence_data()
3941 {
3942     QTest::addColumn<QString>("text");
3943     QTest::addColumn<QKeySequence>("sequence");
3944     QTest::addColumn<int>("selectionStart");
3945     QTest::addColumn<int>("selectionEnd");
3946     QTest::addColumn<int>("cursorPosition");
3947     QTest::addColumn<QString>("expectedText");
3948     QTest::addColumn<QString>("selectedText");
3949     QTest::addColumn<Qt::Key>("layoutDirection");
3950
3951     // standard[0] == "the [4]quick [10]brown [16]fox [20]jumped [27]over [32]the [36]lazy [41]dog"
3952
3953     QTest::newRow("select all")
3954             << standard.at(0) << QKeySequence(QKeySequence::SelectAll) << 0 << 0
3955             << 44 << standard.at(0) << standard.at(0)
3956             << Qt::Key_Direction_L;
3957     QTest::newRow("select start of line")
3958             << standard.at(0) << QKeySequence(QKeySequence::SelectStartOfLine) << 5 << 5
3959             << 0 << standard.at(0) << standard.at(0).mid(0, 5)
3960             << Qt::Key_Direction_L;
3961     QTest::newRow("select start of block")
3962             << standard.at(0) << QKeySequence(QKeySequence::SelectStartOfBlock) << 5 << 5
3963             << 0 << standard.at(0) << standard.at(0).mid(0, 5)
3964             << Qt::Key_Direction_L;
3965     QTest::newRow("select end of line")
3966             << standard.at(0) << QKeySequence(QKeySequence::SelectEndOfLine) << 5 << 5
3967             << 44 << standard.at(0) << standard.at(0).mid(5)
3968             << Qt::Key_Direction_L;
3969     QTest::newRow("select end of document")
3970             << standard.at(0) << QKeySequence(QKeySequence::SelectEndOfDocument) << 3 << 3
3971             << 44 << standard.at(0) << standard.at(0).mid(3)
3972             << Qt::Key_Direction_L;
3973     QTest::newRow("select end of block")
3974             << standard.at(0) << QKeySequence(QKeySequence::SelectEndOfBlock) << 18 << 18
3975             << 44 << standard.at(0) << standard.at(0).mid(18)
3976             << Qt::Key_Direction_L;
3977     QTest::newRow("delete end of line")
3978             << standard.at(0) << QKeySequence(QKeySequence::DeleteEndOfLine) << 24 << 24
3979             << 24 << standard.at(0).mid(0, 24) << QString()
3980             << Qt::Key_Direction_L;
3981     QTest::newRow("move to start of line")
3982             << standard.at(0) << QKeySequence(QKeySequence::MoveToStartOfLine) << 31 << 31
3983             << 0 << standard.at(0) << QString()
3984             << Qt::Key_Direction_L;
3985     QTest::newRow("move to start of block")
3986             << standard.at(0) << QKeySequence(QKeySequence::MoveToStartOfBlock) << 25 << 25
3987             << 0 << standard.at(0) << QString()
3988             << Qt::Key_Direction_L;
3989     QTest::newRow("move to next char")
3990             << standard.at(0) << QKeySequence(QKeySequence::MoveToNextChar) << 12 << 12
3991             << 13 << standard.at(0) << QString()
3992             << Qt::Key_Direction_L;
3993     QTest::newRow("move to previous char (ltr)")
3994             << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousChar) << 3 << 3
3995             << 2 << standard.at(0) << QString()
3996             << Qt::Key_Direction_L;
3997     QTest::newRow("move to previous char (rtl)")
3998             << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousChar) << 3 << 3
3999             << 4 << standard.at(0) << QString()
4000             << Qt::Key_Direction_R;
4001     QTest::newRow("move to previous char with selection")
4002             << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousChar) << 3 << 7
4003             << 3 << standard.at(0) << QString()
4004             << Qt::Key_Direction_L;
4005     QTest::newRow("select next char (ltr)")
4006             << standard.at(0) << QKeySequence(QKeySequence::SelectNextChar) << 23 << 23
4007             << 24 << standard.at(0) << standard.at(0).mid(23, 1)
4008             << Qt::Key_Direction_L;
4009     QTest::newRow("select next char (rtl)")
4010             << standard.at(0) << QKeySequence(QKeySequence::SelectNextChar) << 23 << 23
4011             << 22 << standard.at(0) << standard.at(0).mid(22, 1)
4012             << Qt::Key_Direction_R;
4013     QTest::newRow("select previous char (ltr)")
4014             << standard.at(0) << QKeySequence(QKeySequence::SelectPreviousChar) << 19 << 19
4015             << 18 << standard.at(0) << standard.at(0).mid(18, 1)
4016             << Qt::Key_Direction_L;
4017     QTest::newRow("select previous char (rtl)")
4018             << standard.at(0) << QKeySequence(QKeySequence::SelectPreviousChar) << 19 << 19
4019             << 20 << standard.at(0) << standard.at(0).mid(19, 1)
4020             << Qt::Key_Direction_R;
4021     QTest::newRow("move to next word (ltr)")
4022             << standard.at(0) << QKeySequence(QKeySequence::MoveToNextWord) << 7 << 7
4023             << 10 << standard.at(0) << QString()
4024             << Qt::Key_Direction_L;
4025     QTest::newRow("move to next word (rtl)")
4026             << standard.at(0) << QKeySequence(QKeySequence::MoveToNextWord) << 7 << 7
4027             << 4 << standard.at(0) << QString()
4028             << Qt::Key_Direction_R;
4029     QTest::newRow("move to previous word (ltr)")
4030             << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousWord) << 7 << 7
4031             << 4 << standard.at(0) << QString()
4032             << Qt::Key_Direction_L;
4033     QTest::newRow("move to previous word (rlt)")
4034             << standard.at(0) << QKeySequence(QKeySequence::MoveToPreviousWord) << 7 << 7
4035             << 10 << standard.at(0) << QString()
4036             << Qt::Key_Direction_R;
4037     QTest::newRow("select next word")
4038             << standard.at(0) << QKeySequence(QKeySequence::SelectNextWord) << 11 << 11
4039             << 16 << standard.at(0) << standard.at(0).mid(11, 5)
4040             << Qt::Key_Direction_L;
4041     QTest::newRow("select previous word")
4042             << standard.at(0) << QKeySequence(QKeySequence::SelectPreviousWord) << 11 << 11
4043             << 10 << standard.at(0) << standard.at(0).mid(10, 1)
4044             << Qt::Key_Direction_L;
4045     QTest::newRow("delete (selection)")
4046             << standard.at(0) << QKeySequence(QKeySequence::Delete) << 12 << 15
4047             << 12 << (standard.at(0).mid(0, 12) + standard.at(0).mid(15)) << QString()
4048             << Qt::Key_Direction_L;
4049     QTest::newRow("delete (no selection)")
4050             << standard.at(0) << QKeySequence(QKeySequence::Delete) << 15 << 15
4051             << 15 << (standard.at(0).mid(0, 15) + standard.at(0).mid(16)) << QString()
4052             << Qt::Key_Direction_L;
4053     QTest::newRow("delete end of word")
4054             << standard.at(0) << QKeySequence(QKeySequence::DeleteEndOfWord) << 24 << 24
4055             << 24 << (standard.at(0).mid(0, 24) + standard.at(0).mid(27)) << QString()
4056             << Qt::Key_Direction_L;
4057     QTest::newRow("delete start of word")
4058             << standard.at(0) << QKeySequence(QKeySequence::DeleteStartOfWord) << 7 << 7
4059             << 4 << (standard.at(0).mid(0, 4) + standard.at(0).mid(7)) << QString()
4060             << Qt::Key_Direction_L;
4061 }
4062
4063 void tst_qquicktextedit::keySequence()
4064 {
4065     QFETCH(QString, text);
4066     QFETCH(QKeySequence, sequence);
4067     QFETCH(int, selectionStart);
4068     QFETCH(int, selectionEnd);
4069     QFETCH(int, cursorPosition);
4070     QFETCH(QString, expectedText);
4071     QFETCH(QString, selectedText);
4072     QFETCH(Qt::Key, layoutDirection);
4073
4074     if (sequence.isEmpty()) {
4075         QSKIP("Key sequence is undefined");
4076     }
4077
4078     QString componentStr = "import QtQuick 2.0\nTextEdit { focus: true; text: \"" + text + "\" }";
4079     QQmlComponent textEditComponent(&engine);
4080     textEditComponent.setData(componentStr.toLatin1(), QUrl());
4081     QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(textEditComponent.create());
4082     QVERIFY(textEdit != 0);
4083
4084     QQuickCanvas canvas;
4085     textEdit->setParentItem(canvas.rootItem());
4086     canvas.show();
4087     canvas.requestActivateWindow();
4088     QTest::qWaitForWindowActive(&canvas);
4089
4090     QVERIFY(textEdit->hasActiveFocus());
4091
4092     simulateKey(&canvas, layoutDirection);
4093
4094     textEdit->select(selectionStart, selectionEnd);
4095
4096     simulateKeys(&canvas, sequence);
4097
4098     QCOMPARE(textEdit->cursorPosition(), cursorPosition);
4099     QCOMPARE(textEdit->text(), expectedText);
4100     QCOMPARE(textEdit->selectedText(), selectedText);
4101 }
4102
4103 #define NORMAL 0
4104 #define REPLACE_UNTIL_END 1
4105
4106 void tst_qquicktextedit::undo_data()
4107 {
4108     QTest::addColumn<QStringList>("insertString");
4109     QTest::addColumn<IntList>("insertIndex");
4110     QTest::addColumn<IntList>("insertMode");
4111     QTest::addColumn<QStringList>("expectedString");
4112     QTest::addColumn<bool>("use_keys");
4113
4114     for (int i=0; i<2; i++) {
4115         QString keys_str = "keyboard";
4116         bool use_keys = true;
4117         if (i==0) {
4118             keys_str = "insert";
4119             use_keys = false;
4120         }
4121
4122         {
4123             IntList insertIndex;
4124             IntList insertMode;
4125             QStringList insertString;
4126             QStringList expectedString;
4127
4128             insertIndex << -1;
4129             insertMode << NORMAL;
4130             insertString << "1";
4131
4132             insertIndex << -1;
4133             insertMode << NORMAL;
4134             insertString << "5";
4135
4136             insertIndex << 1;
4137             insertMode << NORMAL;
4138             insertString << "3";
4139
4140             insertIndex << 1;
4141             insertMode << NORMAL;
4142             insertString << "2";
4143
4144             insertIndex << 3;
4145             insertMode << NORMAL;
4146             insertString << "4";
4147
4148             expectedString << "12345";
4149             expectedString << "1235";
4150             expectedString << "135";
4151             expectedString << "15";
4152             expectedString << "";
4153
4154             QTest::newRow(QString(keys_str + "_numbers").toLatin1()) <<
4155                 insertString <<
4156                 insertIndex <<
4157                 insertMode <<
4158                 expectedString <<
4159                 bool(use_keys);
4160         }
4161         {
4162             IntList insertIndex;
4163             IntList insertMode;
4164             QStringList insertString;
4165             QStringList expectedString;
4166
4167             insertIndex << -1;
4168             insertMode << NORMAL;
4169             insertString << "World"; // World
4170
4171             insertIndex << 0;
4172             insertMode << NORMAL;
4173             insertString << "Hello"; // HelloWorld
4174
4175             insertIndex << 0;
4176             insertMode << NORMAL;
4177             insertString << "Well"; // WellHelloWorld
4178
4179             insertIndex << 9;
4180             insertMode << NORMAL;
4181             insertString << "There"; // WellHelloThereWorld;
4182
4183             expectedString << "WellHelloThereWorld";
4184             expectedString << "WellHelloWorld";
4185             expectedString << "HelloWorld";
4186             expectedString << "World";
4187             expectedString << "";
4188
4189             QTest::newRow(QString(keys_str + "_helloworld").toLatin1()) <<
4190                 insertString <<
4191                 insertIndex <<
4192                 insertMode <<
4193                 expectedString <<
4194                 bool(use_keys);
4195         }
4196         {
4197             IntList insertIndex;
4198             IntList insertMode;
4199             QStringList insertString;
4200             QStringList expectedString;
4201
4202             insertIndex << -1;
4203             insertMode << NORMAL;
4204             insertString << "Ensuring";
4205
4206             insertIndex << -1;
4207             insertMode << NORMAL;
4208             insertString << " instan";
4209
4210             insertIndex << 9;
4211             insertMode << NORMAL;
4212             insertString << "an ";
4213
4214             insertIndex << 10;
4215             insertMode << REPLACE_UNTIL_END;
4216             insertString << " unique instance.";
4217
4218             expectedString << "Ensuring a unique instance.";
4219             expectedString << "Ensuring a ";    // ### Not present in TextEdit.
4220             expectedString << "Ensuring an instan";
4221             expectedString << "Ensuring instan";
4222             expectedString << "";
4223
4224             QTest::newRow(QString(keys_str + "_patterns").toLatin1()) <<
4225                 insertString <<
4226                 insertIndex <<
4227                 insertMode <<
4228                 expectedString <<
4229                 bool(use_keys);
4230         }
4231     }
4232 }
4233
4234 void tst_qquicktextedit::undo()
4235 {
4236     QFETCH(QStringList, insertString);
4237     QFETCH(IntList, insertIndex);
4238     QFETCH(IntList, insertMode);
4239     QFETCH(QStringList, expectedString);
4240
4241     QString componentStr = "import QtQuick 2.0\nTextEdit { focus: true }";
4242     QQmlComponent textEditComponent(&engine);
4243     textEditComponent.setData(componentStr.toLatin1(), QUrl());
4244     QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(textEditComponent.create());
4245     QVERIFY(textEdit != 0);
4246
4247     QQuickCanvas canvas;
4248     textEdit->setParentItem(canvas.rootItem());
4249     canvas.show();
4250     canvas.requestActivateWindow();
4251     QTest::qWaitForWindowActive(&canvas);
4252
4253     QVERIFY(textEdit->hasActiveFocus());
4254     QVERIFY(!textEdit->canUndo());
4255
4256     QSignalSpy spy(textEdit, SIGNAL(canUndoChanged()));
4257
4258     int i;
4259
4260 // STEP 1: First build up an undo history by inserting or typing some strings...
4261     for (i = 0; i < insertString.size(); ++i) {
4262         if (insertIndex[i] > -1)
4263             textEdit->setCursorPosition(insertIndex[i]);
4264
4265  // experimental stuff
4266         if (insertMode[i] == REPLACE_UNTIL_END) {
4267             textEdit->select(insertIndex[i], insertIndex[i] + 8);
4268
4269             // This is what I actually want...
4270             // QTest::keyClick(testWidget, Qt::Key_End, Qt::ShiftModifier);
4271         }
4272
4273         for (int j = 0; j < insertString.at(i).length(); j++)
4274             QTest::keyClick(&canvas, insertString.at(i).at(j).toLatin1());
4275     }
4276
4277     QCOMPARE(spy.count(), 1);
4278
4279 // STEP 2: Next call undo several times and see if we can restore to the previous state
4280     for (i = 0; i < expectedString.size() - 1; ++i) {
4281         QCOMPARE(textEdit->text(), expectedString[i]);
4282         QVERIFY(textEdit->canUndo());
4283         textEdit->undo();
4284     }
4285
4286 // STEP 3: Verify that we have undone everything
4287     QVERIFY(textEdit->text().isEmpty());
4288     QVERIFY(!textEdit->canUndo());
4289     QCOMPARE(spy.count(), 2);
4290 }
4291
4292 void tst_qquicktextedit::redo_data()
4293 {
4294     QTest::addColumn<QStringList>("insertString");
4295     QTest::addColumn<IntList>("insertIndex");
4296     QTest::addColumn<QStringList>("expectedString");
4297
4298     {
4299         IntList insertIndex;
4300         QStringList insertString;
4301         QStringList expectedString;
4302
4303         insertIndex << -1;
4304         insertString << "World"; // World
4305         insertIndex << 0;
4306         insertString << "Hello"; // HelloWorld
4307         insertIndex << 0;
4308         insertString << "Well"; // WellHelloWorld
4309         insertIndex << 9;
4310         insertString << "There"; // WellHelloThereWorld;
4311
4312         expectedString << "World";
4313         expectedString << "HelloWorld";
4314         expectedString << "WellHelloWorld";
4315         expectedString << "WellHelloThereWorld";
4316
4317         QTest::newRow("Inserts and setting cursor") << insertString << insertIndex << expectedString;
4318     }
4319 }
4320
4321 void tst_qquicktextedit::redo()
4322 {
4323     QFETCH(QStringList, insertString);
4324     QFETCH(IntList, insertIndex);
4325     QFETCH(QStringList, expectedString);
4326
4327     QString componentStr = "import QtQuick 2.0\nTextEdit { focus: true }";
4328     QQmlComponent textEditComponent(&engine);
4329     textEditComponent.setData(componentStr.toLatin1(), QUrl());
4330     QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(textEditComponent.create());
4331     QVERIFY(textEdit != 0);
4332
4333     QQuickCanvas canvas;
4334     textEdit->setParentItem(canvas.rootItem());
4335     canvas.show();
4336     canvas.requestActivateWindow();
4337     QTest::qWaitForWindowActive(&canvas);
4338     QVERIFY(textEdit->hasActiveFocus());
4339
4340     QVERIFY(!textEdit->canUndo());
4341     QVERIFY(!textEdit->canRedo());
4342
4343     QSignalSpy spy(textEdit, SIGNAL(canRedoChanged()));
4344
4345     int i;
4346     // inserts the diff strings at diff positions
4347     for (i = 0; i < insertString.size(); ++i) {
4348         if (insertIndex[i] > -1)
4349             textEdit->setCursorPosition(insertIndex[i]);
4350         for (int j = 0; j < insertString.at(i).length(); j++)
4351             QTest::keyClick(&canvas, insertString.at(i).at(j).toLatin1());
4352         QVERIFY(textEdit->canUndo());
4353         QVERIFY(!textEdit->canRedo());
4354     }
4355
4356     QCOMPARE(spy.count(), 0);
4357
4358     // undo everything
4359     while (!textEdit->text().isEmpty()) {
4360         QVERIFY(textEdit->canUndo());
4361         textEdit->undo();
4362         QVERIFY(textEdit->canRedo());
4363     }
4364
4365     QCOMPARE(spy.count(), 1);
4366
4367     for (i = 0; i < expectedString.size(); ++i) {
4368         QVERIFY(textEdit->canRedo());
4369         textEdit->redo();
4370         QCOMPARE(textEdit->text() , expectedString[i]);
4371         QVERIFY(textEdit->canUndo());
4372     }
4373     QVERIFY(!textEdit->canRedo());
4374     QCOMPARE(spy.count(), 2);
4375 }
4376
4377 void tst_qquicktextedit::undo_keypressevents_data()
4378 {
4379     QTest::addColumn<KeyList>("keys");
4380     QTest::addColumn<QStringList>("expectedString");
4381
4382     {
4383         KeyList keys;
4384         QStringList expectedString;
4385
4386         keys << "AFRAID"
4387                 << Qt::Key_Home
4388                 << "VERY"
4389                 << Qt::Key_Left
4390                 << Qt::Key_Left
4391                 << Qt::Key_Left
4392                 << Qt::Key_Left
4393                 << "BE"
4394                 << Qt::Key_End
4395                 << "!";
4396
4397         expectedString << "BEVERYAFRAID!";
4398         expectedString << "BEVERYAFRAID";
4399         expectedString << "VERYAFRAID";
4400         expectedString << "AFRAID";
4401
4402         QTest::newRow("Inserts and moving cursor") << keys << expectedString;
4403     } {
4404         KeyList keys;
4405         QStringList expectedString;
4406
4407         // inserting '1234'
4408         keys << "1234" << Qt::Key_Home
4409                 // skipping '12'
4410                 << Qt::Key_Right << Qt::Key_Right
4411                 // selecting '34'
4412                 << (Qt::Key_Right | Qt::ShiftModifier) << (Qt::Key_Right | Qt::ShiftModifier)
4413                 // deleting '34'
4414                 << Qt::Key_Delete;
4415
4416         expectedString << "12";
4417         expectedString << "1234";
4418
4419         QTest::newRow("Inserts,moving,selection and delete") << keys << expectedString;
4420     } {
4421         KeyList keys;
4422         QStringList expectedString;
4423
4424         // inserting 'AB12'
4425         keys << "AB12"
4426                 << Qt::Key_Home
4427                 // selecting 'AB'
4428                 << (Qt::Key_Right | Qt::ShiftModifier) << (Qt::Key_Right | Qt::ShiftModifier)
4429                 << Qt::Key_Delete
4430                 << QKeySequence::Undo
4431                 // ### Text is selected in text input
4432 //                << Qt::Key_Right
4433                 << (Qt::Key_Right | Qt::ShiftModifier) << (Qt::Key_Right | Qt::ShiftModifier)
4434                 << Qt::Key_Delete;
4435
4436         expectedString << "AB";
4437         expectedString << "AB12";
4438
4439         QTest::newRow("Inserts,moving,selection, delete and undo") << keys << expectedString;
4440     } {
4441         KeyList keys;
4442         QStringList expectedString;
4443
4444         // inserting 'ABCD'
4445         keys << "abcd"
4446                 //move left two
4447                 << Qt::Key_Left << Qt::Key_Left
4448                 // inserting '1234'
4449                 << "1234"
4450                 // selecting '1234'
4451                 << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier)
4452                 // overwriting '1234' with '5'
4453                 << "5"
4454                 // undoing deletion of 'AB'
4455                 << QKeySequence::Undo
4456                 // ### Text is selected in text input
4457                 << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier) << (Qt::Key_Left | Qt::ShiftModifier)
4458                 // overwriting '1234' with '6'
4459                 << "6";
4460
4461         expectedString << "ab6cd";
4462         // for versions previous to 3.2 we overwrite needed two undo operations
4463         expectedString << "ab1234cd";
4464         expectedString << "abcd";
4465
4466         QTest::newRow("Inserts,moving,selection and undo, removing selection") << keys << expectedString;
4467     } {
4468         KeyList keys;
4469         QStringList expectedString;
4470
4471         // inserting 'ABC'
4472         keys << "ABC"
4473                 // removes 'C'
4474                 << Qt::Key_Backspace;
4475
4476         expectedString << "AB";
4477         expectedString << "ABC";
4478
4479         QTest::newRow("Inserts,backspace") << keys << expectedString;
4480     } {
4481         KeyList keys;
4482         QStringList expectedString;
4483
4484         keys << "ABC"
4485                 // removes 'C'
4486                 << Qt::Key_Backspace
4487                 // inserting 'Z'
4488                 << "Z";
4489
4490         expectedString << "ABZ";
4491         expectedString << "AB";
4492         expectedString << "ABC";
4493
4494         QTest::newRow("Inserts,backspace,inserts") << keys << expectedString;
4495     } {
4496         KeyList keys;
4497         QStringList expectedString;
4498
4499         // inserting '123'
4500         keys << "123" << Qt::Key_Home
4501             // selecting '123'
4502              << (Qt::Key_End | Qt::ShiftModifier)
4503             // overwriting '123' with 'ABC'
4504              << "ABC";
4505
4506         expectedString << "ABC";
4507         // ### One operation in TextEdit.
4508         expectedString << "A";
4509         expectedString << "123";
4510
4511         QTest::newRow("Inserts,moving,selection and overwriting") << keys << expectedString;
4512     }
4513 }
4514
4515 void tst_qquicktextedit::undo_keypressevents()
4516 {
4517     QFETCH(KeyList, keys);
4518     QFETCH(QStringList, expectedString);
4519
4520     QString componentStr = "import QtQuick 2.0\nTextEdit { focus: true }";
4521     QQmlComponent textEditComponent(&engine);
4522     textEditComponent.setData(componentStr.toLatin1(), QUrl());
4523     QQuickTextEdit *textEdit = qobject_cast<QQuickTextEdit*>(textEditComponent.create());
4524     QVERIFY(textEdit != 0);
4525
4526     QQuickCanvas canvas;
4527     textEdit->setParentItem(canvas.rootItem());
4528     canvas.show();
4529     canvas.requestActivateWindow();
4530     QTest::qWaitForWindowActive(&canvas);
4531     QVERIFY(textEdit->hasActiveFocus());
4532
4533     simulateKeys(&canvas, keys);
4534
4535     for (int i = 0; i < expectedString.size(); ++i) {
4536         QCOMPARE(textEdit->text() , expectedString[i]);
4537         textEdit->undo();
4538     }
4539     QVERIFY(textEdit->text().isEmpty());
4540 }
4541
4542 void tst_qquicktextedit::baseUrl()
4543 {
4544     QUrl localUrl("file:///tests/text.qml");
4545     QUrl remoteUrl("http://qt.nokia.com/test.qml");
4546
4547     QQmlComponent textComponent(&engine);
4548     textComponent.setData("import QtQuick 2.0\n TextEdit {}", localUrl);
4549     QQuickTextEdit *textObject = qobject_cast<QQuickTextEdit *>(textComponent.create());
4550
4551     QCOMPARE(textObject->baseUrl(), localUrl);
4552
4553     QSignalSpy spy(textObject, SIGNAL(baseUrlChanged()));
4554
4555     textObject->setBaseUrl(localUrl);
4556     QCOMPARE(textObject->baseUrl(), localUrl);
4557     QCOMPARE(spy.count(), 0);
4558
4559     textObject->setBaseUrl(remoteUrl);
4560     QCOMPARE(textObject->baseUrl(), remoteUrl);
4561     QCOMPARE(spy.count(), 1);
4562
4563     textObject->resetBaseUrl();
4564     QCOMPARE(textObject->baseUrl(), localUrl);
4565     QCOMPARE(spy.count(), 2);
4566 }
4567
4568 void tst_qquicktextedit::embeddedImages_data()
4569 {
4570     QTest::addColumn<QUrl>("qmlfile");
4571     QTest::addColumn<QString>("error");
4572     QTest::newRow("local") << testFileUrl("embeddedImagesLocal.qml") << "";
4573     QTest::newRow("local-error") << testFileUrl("embeddedImagesLocalError.qml")
4574         << testFileUrl("embeddedImagesLocalError.qml").toString()+":3:1: QML TextEdit: Cannot open: " + testFileUrl("http/notexists.png").toString();
4575     QTest::newRow("local") << testFileUrl("embeddedImagesLocalRelative.qml") << "";
4576     QTest::newRow("remote") << testFileUrl("embeddedImagesRemote.qml") << "";
4577     QTest::newRow("remote-error") << testFileUrl("embeddedImagesRemoteError.qml")
4578         << testFileUrl("embeddedImagesRemoteError.qml").toString()+":3:1: QML TextEdit: Error downloading http://127.0.0.1:42332/notexists.png - server replied: Not found";
4579     QTest::newRow("remote") << testFileUrl("embeddedImagesRemoteRelative.qml") << "";
4580 }
4581
4582 void tst_qquicktextedit::embeddedImages()
4583 {
4584     QFETCH(QUrl, qmlfile);
4585     QFETCH(QString, error);
4586
4587     TestHTTPServer server(SERVER_PORT);
4588     server.serveDirectory(testFile("http"));
4589
4590     if (!error.isEmpty())
4591         QTest::ignoreMessage(QtWarningMsg, error.toLatin1());
4592
4593     QQmlComponent textComponent(&engine, qmlfile);
4594     QQuickTextEdit *textObject = qobject_cast<QQuickTextEdit*>(textComponent.create());
4595
4596     QVERIFY(textObject != 0);
4597     QTRY_COMPARE(QQuickTextEditPrivate::get(textObject)->document->resourcesLoading(), 0);
4598
4599     QPixmap pm(testFile("http/exists.png"));
4600     if (error.isEmpty()) {
4601         QCOMPARE(textObject->width(), double(pm.width()));
4602         QCOMPARE(textObject->height(), double(pm.height()));
4603     } else {
4604         QVERIFY(16 != pm.width()); // check test is effective
4605         QCOMPARE(textObject->width(), 16.0); // default size of QTextDocument broken image icon
4606         QCOMPARE(textObject->height(), 16.0);
4607     }
4608
4609     delete textObject;
4610 }
4611
4612 void tst_qquicktextedit::emptytags_QTBUG_22058()
4613 {
4614     QQuickView canvas(testFileUrl("qtbug-22058.qml"));
4615     QVERIFY(canvas.rootObject() != 0);
4616
4617     canvas.show();
4618     canvas.requestActivateWindow();
4619     QTest::qWaitForWindowActive(&canvas);
4620     QQuickTextEdit *input = qobject_cast<QQuickTextEdit *>(qvariant_cast<QObject *>(canvas.rootObject()->property("inputField")));
4621     QVERIFY(input->hasActiveFocus());
4622
4623     QInputMethodEvent event("", QList<QInputMethodEvent::Attribute>());
4624     event.setCommitString("<b>Bold<");
4625     QGuiApplication::sendEvent(input, &event);
4626     QCOMPARE(input->text(), QString("<b>Bold<"));
4627     event.setCommitString(">");
4628     QGuiApplication::sendEvent(input, &event);
4629     QCOMPARE(input->text(), QString("<b>Bold<>"));
4630 }
4631
4632 QTEST_MAIN(tst_qquicktextedit)
4633
4634 #include "tst_qquicktextedit.moc"