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