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