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