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