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