945b2d2c45adfd6c7c0bdcd992fab55296beb60b
[profile/ivi/qtdeclarative.git] / tests / auto / declarative / qsgtextedit / tst_qsgtextedit.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the test suite of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 #include <qtest.h>
42 #include <QtTest/QSignalSpy>
43 #include "../../../shared/util.h"
44 #include "../shared/testhttpserver.h"
45 #include <math.h>
46 #include <QFile>
47 #include <QTextDocument>
48 #include <QtDeclarative/qdeclarativeengine.h>
49 #include <QtDeclarative/qdeclarativecontext.h>
50 #include <QtDeclarative/qdeclarativeexpression.h>
51 #include <QtDeclarative/qdeclarativecomponent.h>
52 #include <QtGui/qguiapplication.h>
53 #include <private/qsgtextedit_p.h>
54 #include <private/qsgtextedit_p_p.h>
55 #include <private/qsgdistancefieldglyphcache_p.h>
56 #include <QFontMetrics>
57 #include <QSGView>
58 #include <QDir>
59 #include <QStyle>
60 #include <QInputContext>
61 #include <QInputPanel>
62 #include <QClipboard>
63 #include <QMimeData>
64 #include <private/qtextcontrol_p.h>
65 #include "../shared/util.h"
66
67 #ifdef Q_WS_MAC
68 #include <Carbon/Carbon.h>
69 #endif
70
71 #define QTBUG_21691
72 #define QTBUG_21691_MESSAGE "QTBUG-21691: The test needs to be rewritten to not use QInputContext"
73
74 Q_DECLARE_METATYPE(QSGTextEdit::SelectionMode)
75 DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD)
76
77 QString createExpectedFileIfNotFound(const QString& filebasename, const QImage& actual)
78 {
79     // XXX This will be replaced by some clever persistent platform image store.
80     QString persistent_dir = TESTDATA("");
81     QString arch = "unknown-architecture"; // QTest needs to help with this.
82
83     QString expectfile = persistent_dir + QDir::separator() + filebasename + "-" + arch + ".png";
84
85     if (!QFile::exists(expectfile)) {
86         actual.save(expectfile);
87         qWarning() << "created" << expectfile;
88     }
89
90     return expectfile;
91 }
92
93
94 class tst_qsgtextedit : public QObject
95
96 {
97     Q_OBJECT
98 public:
99     tst_qsgtextedit();
100
101 private slots:
102     void initTestCase();
103     void cleanupTestCase();
104     void text();
105     void width();
106     void wrap();
107     void textFormat();
108     void alignments();
109     void alignments_data();
110
111     // ### these tests may be trivial
112     void hAlign();
113     void hAlign_RightToLeft();
114     void vAlign();
115     void font();
116     void color();
117     void textMargin();
118     void persistentSelection();
119     void focusOnPress();
120     void selection();
121     void isRightToLeft_data();
122     void isRightToLeft();
123     void keySelection();
124     void moveCursorSelection_data();
125     void moveCursorSelection();
126     void moveCursorSelectionSequence_data();
127     void moveCursorSelectionSequence();
128     void mouseSelection_data();
129     void mouseSelection();
130     void mouseSelectionMode_data();
131     void mouseSelectionMode();
132     void dragMouseSelection();
133     void inputMethodHints();
134
135     void positionAt();
136
137     void cursorDelegate();
138     void cursorVisible();
139     void delegateLoading_data();
140     void delegateLoading();
141     void navigation();
142     void readOnly();
143     void copyAndPaste();
144     void canPaste();
145     void canPasteEmpty();
146     void textInput();
147     void openInputPanel();
148     void geometrySignals();
149     void pastingRichText_QTBUG_14003();
150     void implicitSize_data();
151     void implicitSize();
152     void testQtQuick11Attributes();
153     void testQtQuick11Attributes_data();
154
155     void preeditMicroFocus();
156     void inputContextMouseHandler();
157     void inputMethodComposing();
158     void cursorRectangleSize();
159
160 private:
161     void simulateKey(QSGView *, int key, Qt::KeyboardModifiers modifiers = 0);
162
163     QStringList standard;
164     QStringList richText;
165
166     QStringList hAlignmentStrings;
167     QStringList vAlignmentStrings;
168
169     QList<Qt::Alignment> vAlignments;
170     QList<Qt::Alignment> hAlignments;
171
172     QStringList colorStrings;
173
174     QDeclarativeEngine engine;
175 };
176 void tst_qsgtextedit::initTestCase()
177 {
178 }
179
180 void tst_qsgtextedit::cleanupTestCase()
181 {
182
183 }
184 tst_qsgtextedit::tst_qsgtextedit()
185 {
186     standard << "the quick brown fox jumped over the lazy dog"
187              << "the quick brown fox\n jumped over the lazy dog"
188              << "Hello, world!"
189              << "!dlrow ,olleH";
190
191     richText << "<i>the <b>quick</b> brown <a href=\\\"http://www.google.com\\\">fox</a> jumped over the <b>lazy</b> dog</i>"
192              << "<i>the <b>quick</b> brown <a href=\\\"http://www.google.com\\\">fox</a><br>jumped over the <b>lazy</b> dog</i>";
193
194     hAlignmentStrings << "AlignLeft"
195                       << "AlignRight"
196                       << "AlignHCenter";
197
198     vAlignmentStrings << "AlignTop"
199                       << "AlignBottom"
200                       << "AlignVCenter";
201
202     hAlignments << Qt::AlignLeft
203                 << Qt::AlignRight
204                 << Qt::AlignHCenter;
205
206     vAlignments << Qt::AlignTop
207                 << Qt::AlignBottom
208                 << Qt::AlignVCenter;
209
210     colorStrings << "aliceblue"
211                  << "antiquewhite"
212                  << "aqua"
213                  << "darkkhaki"
214                  << "darkolivegreen"
215                  << "dimgray"
216                  << "palevioletred"
217                  << "lightsteelblue"
218                  << "#000000"
219                  << "#AAAAAA"
220                  << "#FFFFFF"
221                  << "#2AC05F";
222                  //
223                  // need a different test to do alpha channel test
224                  // << "#AA0011DD"
225                  // << "#00F16B11";
226                  //
227 }
228
229 void tst_qsgtextedit::text()
230 {
231     {
232         QDeclarativeComponent texteditComponent(&engine);
233         texteditComponent.setData("import QtQuick 2.0\nTextEdit {  text: \"\"  }", QUrl());
234         QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
235
236         QVERIFY(textEditObject != 0);
237         QCOMPARE(textEditObject->text(), QString(""));
238     }
239
240     for (int i = 0; i < standard.size(); i++)
241     {
242         QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"" + standard.at(i) + "\" }";
243         QDeclarativeComponent texteditComponent(&engine);
244         texteditComponent.setData(componentStr.toLatin1(), QUrl());
245         QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
246
247         QVERIFY(textEditObject != 0);
248         QCOMPARE(textEditObject->text(), standard.at(i));
249     }
250
251     for (int i = 0; i < richText.size(); i++)
252     {
253         QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"" + richText.at(i) + "\" }";
254         QDeclarativeComponent texteditComponent(&engine);
255         texteditComponent.setData(componentStr.toLatin1(), QUrl());
256         QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
257
258         QVERIFY(textEditObject != 0);
259         QString actual = textEditObject->text();
260         QString expected = richText.at(i);
261         actual.replace(QRegExp(".*<body[^>]*>"),"");
262         actual.replace(QRegExp("(<[^>]*>)+"),"<>");
263         expected.replace(QRegExp("(<[^>]*>)+"),"<>");
264         QCOMPARE(actual.simplified(),expected.simplified());
265     }
266 }
267
268 void tst_qsgtextedit::width()
269 {
270     // uses Font metrics to find the width for standard and document to find the width for rich
271     {
272         QDeclarativeComponent texteditComponent(&engine);
273         texteditComponent.setData("import QtQuick 2.0\nTextEdit {  text: \"\" }", QUrl());
274         QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
275
276         QVERIFY(textEditObject != 0);
277         QCOMPARE(textEditObject->width(), 0.0);
278     }
279
280     bool requiresUnhintedMetrics = !qmlDisableDistanceField();
281
282     for (int i = 0; i < standard.size(); i++)
283     {
284         QFont f;
285         qreal metricWidth = 0.0;
286
287         if (requiresUnhintedMetrics) {
288             QString s = standard.at(i);
289             s.replace(QLatin1Char('\n'), QChar::LineSeparator);
290
291             QTextLayout layout(s);
292             layout.setFlags(Qt::TextExpandTabs | Qt::TextShowMnemonic);
293             {
294                 QTextOption option;
295                 option.setUseDesignMetrics(true);
296                 layout.setTextOption(option);
297             }
298
299             layout.beginLayout();
300             forever {
301                 QTextLine line = layout.createLine();
302                 if (!line.isValid())
303                     break;
304             }
305
306             layout.endLayout();
307
308             metricWidth = ceil(layout.boundingRect().width());
309         } else {
310             QFontMetricsF fm(f);
311             metricWidth = fm.size(Qt::TextExpandTabs | Qt::TextShowMnemonic, standard.at(i)).width();
312             metricWidth = ceil(metricWidth);
313         }
314
315         QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"" + standard.at(i) + "\" }";
316         QDeclarativeComponent texteditComponent(&engine);
317         texteditComponent.setData(componentStr.toLatin1(), QUrl());
318         QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
319
320         QVERIFY(textEditObject != 0);
321         QCOMPARE(textEditObject->width(), qreal(metricWidth));
322     }
323
324     for (int i = 0; i < richText.size(); i++)
325     {
326         QTextDocument document;
327         document.setHtml(richText.at(i));
328         document.setDocumentMargin(0);
329         if (requiresUnhintedMetrics)
330             document.setUseDesignMetrics(true);
331
332         int documentWidth = ceil(document.idealWidth());
333
334         QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"" + richText.at(i) + "\" }";
335         QDeclarativeComponent texteditComponent(&engine);
336         texteditComponent.setData(componentStr.toLatin1(), QUrl());
337         QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
338
339         QVERIFY(textEditObject != 0);
340         QCOMPARE(textEditObject->width(), qreal(documentWidth));
341     }
342 }
343
344 void tst_qsgtextedit::wrap()
345 {
346     // for specified width and wrap set true
347     {
348         QDeclarativeComponent texteditComponent(&engine);
349         texteditComponent.setData("import QtQuick 2.0\nTextEdit {  text: \"\"; wrapMode: TextEdit.WordWrap; width: 300 }", QUrl());
350         QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
351
352         QVERIFY(textEditObject != 0);
353         QCOMPARE(textEditObject->width(), 300.);
354     }
355
356     for (int i = 0; i < standard.size(); i++)
357     {
358         QString componentStr = "import QtQuick 2.0\nTextEdit {  wrapMode: TextEdit.WordWrap; width: 300; text: \"" + standard.at(i) + "\" }";
359         QDeclarativeComponent texteditComponent(&engine);
360         texteditComponent.setData(componentStr.toLatin1(), QUrl());
361         QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
362
363         QVERIFY(textEditObject != 0);
364         QCOMPARE(textEditObject->width(), 300.);
365     }
366
367     for (int i = 0; i < richText.size(); i++)
368     {
369         QString componentStr = "import QtQuick 2.0\nTextEdit {  wrapMode: TextEdit.WordWrap; width: 300; text: \"" + richText.at(i) + "\" }";
370         QDeclarativeComponent texteditComponent(&engine);
371         texteditComponent.setData(componentStr.toLatin1(), QUrl());
372         QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
373
374         QVERIFY(textEditObject != 0);
375         QCOMPARE(textEditObject->width(), 300.);
376     }
377
378 }
379
380 void tst_qsgtextedit::textFormat()
381 {
382     {
383         QDeclarativeComponent textComponent(&engine);
384         textComponent.setData("import QtQuick 2.0\nTextEdit { text: \"Hello\"; textFormat: Text.RichText }", QUrl::fromLocalFile(""));
385         QSGTextEdit *textObject = qobject_cast<QSGTextEdit*>(textComponent.create());
386
387         QVERIFY(textObject != 0);
388         QVERIFY(textObject->textFormat() == QSGTextEdit::RichText);
389     }
390     {
391         QDeclarativeComponent textComponent(&engine);
392         textComponent.setData("import QtQuick 2.0\nTextEdit { text: \"<b>Hello</b>\"; textFormat: Text.PlainText }", QUrl::fromLocalFile(""));
393         QSGTextEdit *textObject = qobject_cast<QSGTextEdit*>(textComponent.create());
394
395         QVERIFY(textObject != 0);
396         QVERIFY(textObject->textFormat() == QSGTextEdit::PlainText);
397     }
398 }
399
400 void tst_qsgtextedit::alignments_data()
401 {
402     QTest::addColumn<int>("hAlign");
403     QTest::addColumn<int>("vAlign");
404     QTest::addColumn<QString>("expectfile");
405
406     QTest::newRow("LT") << int(Qt::AlignLeft) << int(Qt::AlignTop) << "alignments_lt";
407     QTest::newRow("RT") << int(Qt::AlignRight) << int(Qt::AlignTop) << "alignments_rt";
408     QTest::newRow("CT") << int(Qt::AlignHCenter) << int(Qt::AlignTop) << "alignments_ct";
409
410     QTest::newRow("LB") << int(Qt::AlignLeft) << int(Qt::AlignBottom) << "alignments_lb";
411     QTest::newRow("RB") << int(Qt::AlignRight) << int(Qt::AlignBottom) << "alignments_rb";
412     QTest::newRow("CB") << int(Qt::AlignHCenter) << int(Qt::AlignBottom) << "alignments_cb";
413
414     QTest::newRow("LC") << int(Qt::AlignLeft) << int(Qt::AlignVCenter) << "alignments_lc";
415     QTest::newRow("RC") << int(Qt::AlignRight) << int(Qt::AlignVCenter) << "alignments_rc";
416     QTest::newRow("CC") << int(Qt::AlignHCenter) << int(Qt::AlignVCenter) << "alignments_cc";
417 }
418
419
420 void tst_qsgtextedit::alignments()
421 {
422     QSKIP("Image comparison of text is almost guaranteed to fail during development", SkipAll);
423
424     QFETCH(int, hAlign);
425     QFETCH(int, vAlign);
426     QFETCH(QString, expectfile);
427
428     QSGView canvas(QUrl::fromLocalFile(TESTDATA("alignments.qml")));
429
430     canvas.show();
431     canvas.requestActivateWindow();
432     QTest::qWaitForWindowShown(&canvas);
433     QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
434
435     QObject *ob = canvas.rootObject();
436     QVERIFY(ob != 0);
437     ob->setProperty("horizontalAlignment",hAlign);
438     ob->setProperty("verticalAlignment",vAlign);
439     QTRY_COMPARE(ob->property("running").toBool(),false);
440     QImage actual = canvas.grabFrameBuffer();
441
442     expectfile = createExpectedFileIfNotFound(expectfile, actual);
443
444     QImage expect(expectfile);
445
446     QCOMPARE(actual,expect);
447 }
448
449
450 //the alignment tests may be trivial o.oa
451 void tst_qsgtextedit::hAlign()
452 {
453     //test one align each, and then test if two align fails.
454
455     for (int i = 0; i < standard.size(); i++)
456     {
457         for (int j=0; j < hAlignmentStrings.size(); j++)
458         {
459             QString componentStr = "import QtQuick 2.0\nTextEdit {  horizontalAlignment: \"" + hAlignmentStrings.at(j) + "\"; text: \"" + standard.at(i) + "\" }";
460             QDeclarativeComponent texteditComponent(&engine);
461             texteditComponent.setData(componentStr.toLatin1(), QUrl());
462             QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
463
464             QVERIFY(textEditObject != 0);
465             QCOMPARE((int)textEditObject->hAlign(), (int)hAlignments.at(j));
466         }
467     }
468
469     for (int i = 0; i < richText.size(); i++)
470     {
471         for (int j=0; j < hAlignmentStrings.size(); j++)
472         {
473             QString componentStr = "import QtQuick 2.0\nTextEdit {  horizontalAlignment: \"" + hAlignmentStrings.at(j) + "\"; text: \"" + richText.at(i) + "\" }";
474             QDeclarativeComponent texteditComponent(&engine);
475             texteditComponent.setData(componentStr.toLatin1(), QUrl());
476             QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
477
478             QVERIFY(textEditObject != 0);
479             QCOMPARE((int)textEditObject->hAlign(), (int)hAlignments.at(j));
480         }
481     }
482
483 }
484
485 void tst_qsgtextedit::hAlign_RightToLeft()
486 {
487     QSGView canvas(QUrl::fromLocalFile(TESTDATA("horizontalAlignment_RightToLeft.qml")));
488     QSGTextEdit *textEdit = canvas.rootObject()->findChild<QSGTextEdit*>("text");
489     QVERIFY(textEdit != 0);
490     canvas.show();
491
492     const QString rtlText = textEdit->text();
493
494     // implicit alignment should follow the reading direction of text
495     QCOMPARE(textEdit->hAlign(), QSGTextEdit::AlignRight);
496     QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2);
497
498     // explicitly left aligned
499     textEdit->setHAlign(QSGTextEdit::AlignLeft);
500     QCOMPARE(textEdit->hAlign(), QSGTextEdit::AlignLeft);
501     QVERIFY(textEdit->positionToRectangle(0).x() < canvas.width()/2);
502
503     // explicitly right aligned
504     textEdit->setHAlign(QSGTextEdit::AlignRight);
505     QCOMPARE(textEdit->hAlign(), QSGTextEdit::AlignRight);
506     QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2);
507
508     QString textString = textEdit->text();
509     textEdit->setText(QString("<i>") + textString + QString("</i>"));
510     textEdit->resetHAlign();
511
512     // implicitly aligned rich text should follow the reading direction of RTL text
513     QCOMPARE(textEdit->hAlign(), QSGTextEdit::AlignRight);
514     QCOMPARE(textEdit->effectiveHAlign(), textEdit->hAlign());
515     QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2);
516
517     // explicitly left aligned rich text
518     textEdit->setHAlign(QSGTextEdit::AlignLeft);
519     QCOMPARE(textEdit->hAlign(), QSGTextEdit::AlignLeft);
520     QCOMPARE(textEdit->effectiveHAlign(), textEdit->hAlign());
521     QVERIFY(textEdit->positionToRectangle(0).x() < canvas.width()/2);
522
523     // explicitly right aligned rich text
524     textEdit->setHAlign(QSGTextEdit::AlignRight);
525     QCOMPARE(textEdit->hAlign(), QSGTextEdit::AlignRight);
526     QCOMPARE(textEdit->effectiveHAlign(), textEdit->hAlign());
527     QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2);
528
529     textEdit->setText(textString);
530
531     // explicitly center aligned
532     textEdit->setHAlign(QSGTextEdit::AlignHCenter);
533     QCOMPARE(textEdit->hAlign(), QSGTextEdit::AlignHCenter);
534     QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2);
535
536     // reseted alignment should go back to following the text reading direction
537     textEdit->resetHAlign();
538     QCOMPARE(textEdit->hAlign(), QSGTextEdit::AlignRight);
539     QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2);
540
541     // mirror the text item
542     QSGItemPrivate::get(textEdit)->setLayoutMirror(true);
543
544     // mirrored implicit alignment should continue to follow the reading direction of the text
545     QCOMPARE(textEdit->hAlign(), QSGTextEdit::AlignRight);
546     QCOMPARE(textEdit->effectiveHAlign(), QSGTextEdit::AlignRight);
547     QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2);
548
549     // mirrored explicitly right aligned behaves as left aligned
550     textEdit->setHAlign(QSGTextEdit::AlignRight);
551     QCOMPARE(textEdit->hAlign(), QSGTextEdit::AlignRight);
552     QCOMPARE(textEdit->effectiveHAlign(), QSGTextEdit::AlignLeft);
553     QVERIFY(textEdit->positionToRectangle(0).x() < canvas.width()/2);
554
555     // mirrored explicitly left aligned behaves as right aligned
556     textEdit->setHAlign(QSGTextEdit::AlignLeft);
557     QCOMPARE(textEdit->hAlign(), QSGTextEdit::AlignLeft);
558     QCOMPARE(textEdit->effectiveHAlign(), QSGTextEdit::AlignRight);
559     QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2);
560
561     // disable mirroring
562     QSGItemPrivate::get(textEdit)->setLayoutMirror(false);
563     textEdit->resetHAlign();
564
565     // English text should be implicitly left aligned
566     textEdit->setText("Hello world!");
567     QCOMPARE(textEdit->hAlign(), QSGTextEdit::AlignLeft);
568     QVERIFY(textEdit->positionToRectangle(0).x() < canvas.width()/2);
569
570     canvas.requestActivateWindow();
571     QTest::qWaitForWindowShown(&canvas);
572     QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
573
574     textEdit->setText(QString());
575     { QInputMethodEvent ev(rtlText, QList<QInputMethodEvent::Attribute>()); QGuiApplication::sendEvent(&canvas, &ev); }
576     QEXPECT_FAIL("", "QTBUG-21691", Abort);
577     QCOMPARE(textEdit->hAlign(), QSGTextEdit::AlignRight);
578     { QInputMethodEvent ev("Hello world!", QList<QInputMethodEvent::Attribute>()); QGuiApplication::sendEvent(&canvas, &ev); }
579     QCOMPARE(textEdit->hAlign(), QSGTextEdit::AlignLeft);
580
581 #ifndef Q_OS_MAC    // QTBUG-18040
582     // empty text with implicit alignment follows the system locale-based
583     // keyboard input direction from QGuiApplication::keyboardInputDirection
584     textEdit->setText("");
585     QCOMPARE(textEdit->hAlign(), QGuiApplication::keyboardInputDirection() == Qt::LeftToRight ?
586                                   QSGTextEdit::AlignLeft : QSGTextEdit::AlignRight);
587     if (QGuiApplication::keyboardInputDirection() == Qt::LeftToRight)
588         QVERIFY(textEdit->positionToRectangle(0).x() < canvas.width()/2);
589     else
590         QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2);
591     textEdit->setHAlign(QSGTextEdit::AlignRight);
592     QCOMPARE(textEdit->hAlign(), QSGTextEdit::AlignRight);
593     QVERIFY(textEdit->positionToRectangle(0).x() > canvas.width()/2);
594 #endif
595
596 #ifndef Q_OS_MAC    // QTBUG-18040
597     // alignment of TextEdit with no text set to it
598     QString componentStr = "import QtQuick 2.0\nTextEdit {}";
599     QDeclarativeComponent textComponent(&engine);
600     textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
601     QSGTextEdit *textObject = qobject_cast<QSGTextEdit*>(textComponent.create());
602     QCOMPARE(textObject->hAlign(), QGuiApplication::keyboardInputDirection() == Qt::LeftToRight ?
603                                   QSGTextEdit::AlignLeft : QSGTextEdit::AlignRight);
604     delete textObject;
605 #endif
606 }
607
608 void tst_qsgtextedit::vAlign()
609 {
610     //test one align each, and then test if two align fails.
611
612     for (int i = 0; i < standard.size(); i++)
613     {
614         for (int j=0; j < vAlignmentStrings.size(); j++)
615         {
616             QString componentStr = "import QtQuick 2.0\nTextEdit {  verticalAlignment: \"" + vAlignmentStrings.at(j) + "\"; text: \"" + standard.at(i) + "\" }";
617             QDeclarativeComponent texteditComponent(&engine);
618             texteditComponent.setData(componentStr.toLatin1(), QUrl());
619             QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
620
621             QVERIFY(textEditObject != 0);
622             QCOMPARE((int)textEditObject->vAlign(), (int)vAlignments.at(j));
623         }
624     }
625
626     for (int i = 0; i < richText.size(); i++)
627     {
628         for (int j=0; j < vAlignmentStrings.size(); j++)
629         {
630             QString componentStr = "import QtQuick 2.0\nTextEdit {  verticalAlignment: \"" + vAlignmentStrings.at(j) + "\"; text: \"" + richText.at(i) + "\" }";
631             QDeclarativeComponent texteditComponent(&engine);
632             texteditComponent.setData(componentStr.toLatin1(), QUrl());
633             QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
634
635             QVERIFY(textEditObject != 0);
636             QCOMPARE((int)textEditObject->vAlign(), (int)vAlignments.at(j));
637         }
638     }
639
640 }
641
642 void tst_qsgtextedit::font()
643 {
644     //test size, then bold, then italic, then family
645     {
646         QString componentStr = "import QtQuick 2.0\nTextEdit {  font.pointSize: 40; text: \"Hello World\" }";
647         QDeclarativeComponent texteditComponent(&engine);
648         texteditComponent.setData(componentStr.toLatin1(), QUrl());
649         QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
650
651         QVERIFY(textEditObject != 0);
652         QCOMPARE(textEditObject->font().pointSize(), 40);
653         QCOMPARE(textEditObject->font().bold(), false);
654         QCOMPARE(textEditObject->font().italic(), false);
655     }
656
657     {
658         QString componentStr = "import QtQuick 2.0\nTextEdit {  font.bold: true; text: \"Hello World\" }";
659         QDeclarativeComponent texteditComponent(&engine);
660         texteditComponent.setData(componentStr.toLatin1(), QUrl());
661         QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
662
663         QVERIFY(textEditObject != 0);
664         QCOMPARE(textEditObject->font().bold(), true);
665         QCOMPARE(textEditObject->font().italic(), false);
666     }
667
668     {
669         QString componentStr = "import QtQuick 2.0\nTextEdit {  font.italic: true; text: \"Hello World\" }";
670         QDeclarativeComponent texteditComponent(&engine);
671         texteditComponent.setData(componentStr.toLatin1(), QUrl());
672         QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
673
674         QVERIFY(textEditObject != 0);
675         QCOMPARE(textEditObject->font().italic(), true);
676         QCOMPARE(textEditObject->font().bold(), false);
677     }
678  
679     {
680         QString componentStr = "import QtQuick 2.0\nTextEdit {  font.family: \"Helvetica\"; text: \"Hello World\" }";
681         QDeclarativeComponent texteditComponent(&engine);
682         texteditComponent.setData(componentStr.toLatin1(), QUrl());
683         QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
684
685         QVERIFY(textEditObject != 0);
686         QCOMPARE(textEditObject->font().family(), QString("Helvetica"));
687         QCOMPARE(textEditObject->font().bold(), false);
688         QCOMPARE(textEditObject->font().italic(), false);
689     }
690
691     {
692         QString componentStr = "import QtQuick 2.0\nTextEdit {  font.family: \"\"; text: \"Hello World\" }";
693         QDeclarativeComponent texteditComponent(&engine);
694         texteditComponent.setData(componentStr.toLatin1(), QUrl());
695         QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
696
697         QVERIFY(textEditObject != 0);
698         QCOMPARE(textEditObject->font().family(), QString(""));
699     }
700 }
701
702 void tst_qsgtextedit::color()
703 {
704     //test initial color
705     {
706         QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"Hello World\" }";
707         QDeclarativeComponent texteditComponent(&engine);
708         texteditComponent.setData(componentStr.toLatin1(), QUrl());
709         QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
710
711         QSGTextEditPrivate *textEditPrivate = static_cast<QSGTextEditPrivate*>(QSGItemPrivate::get(textEditObject));
712
713         QVERIFY(textEditObject);
714         QVERIFY(textEditPrivate);
715         QVERIFY(textEditPrivate->control);
716
717         QPalette pal = textEditPrivate->control->palette();
718         QCOMPARE(textEditPrivate->color, QColor("black"));
719         QCOMPARE(textEditPrivate->color, pal.color(QPalette::Text));
720     }
721     //test normal
722     for (int i = 0; i < colorStrings.size(); i++)
723     {
724         QString componentStr = "import QtQuick 2.0\nTextEdit {  color: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
725         QDeclarativeComponent texteditComponent(&engine);
726         texteditComponent.setData(componentStr.toLatin1(), QUrl());
727         QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
728         //qDebug() << "textEditObject: " << textEditObject->color() << "vs. " << QColor(colorStrings.at(i));
729         QVERIFY(textEditObject != 0);
730         QCOMPARE(textEditObject->color(), QColor(colorStrings.at(i)));
731     }
732
733     //test selection
734     for (int i = 0; i < colorStrings.size(); i++)
735     {
736         QString componentStr = "import QtQuick 2.0\nTextEdit {  selectionColor: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
737         QDeclarativeComponent texteditComponent(&engine);
738         texteditComponent.setData(componentStr.toLatin1(), QUrl());
739         QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
740         QVERIFY(textEditObject != 0);
741         QCOMPARE(textEditObject->selectionColor(), QColor(colorStrings.at(i)));
742     }
743
744     //test selected text
745     for (int i = 0; i < colorStrings.size(); i++)
746     {
747         QString componentStr = "import QtQuick 2.0\nTextEdit {  selectedTextColor: \"" + colorStrings.at(i) + "\"; text: \"Hello World\" }";
748         QDeclarativeComponent texteditComponent(&engine);
749         texteditComponent.setData(componentStr.toLatin1(), QUrl());
750         QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
751         QVERIFY(textEditObject != 0);
752         QCOMPARE(textEditObject->selectedTextColor(), QColor(colorStrings.at(i)));
753     }
754
755     {
756         QString colorStr = "#AA001234";
757         QColor testColor("#001234");
758         testColor.setAlpha(170);
759
760         QString componentStr = "import QtQuick 2.0\nTextEdit {  color: \"" + colorStr + "\"; text: \"Hello World\" }";
761         QDeclarativeComponent texteditComponent(&engine);
762         texteditComponent.setData(componentStr.toLatin1(), QUrl());
763         QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
764
765         QVERIFY(textEditObject != 0);
766         QCOMPARE(textEditObject->color(), testColor);
767     }
768 }
769
770 void tst_qsgtextedit::textMargin()
771 {
772     for (qreal i=0; i<=10; i+=0.3) {
773         QString componentStr = "import QtQuick 2.0\nTextEdit {  textMargin: " + QString::number(i) + "; text: \"Hello World\" }";
774         QDeclarativeComponent texteditComponent(&engine);
775         texteditComponent.setData(componentStr.toLatin1(), QUrl());
776         QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
777         QVERIFY(textEditObject != 0);
778         QCOMPARE(textEditObject->textMargin(), i);
779     }
780 }
781
782 void tst_qsgtextedit::persistentSelection()
783 {
784     {
785         QString componentStr = "import QtQuick 2.0\nTextEdit {  persistentSelection: true; text: \"Hello World\" }";
786         QDeclarativeComponent texteditComponent(&engine);
787         texteditComponent.setData(componentStr.toLatin1(), QUrl());
788         QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
789         QVERIFY(textEditObject != 0);
790         QCOMPARE(textEditObject->persistentSelection(), true);
791     }
792
793     {
794         QString componentStr = "import QtQuick 2.0\nTextEdit {  persistentSelection: false; text: \"Hello World\" }";
795         QDeclarativeComponent texteditComponent(&engine);
796         texteditComponent.setData(componentStr.toLatin1(), QUrl());
797         QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
798         QVERIFY(textEditObject != 0);
799         QCOMPARE(textEditObject->persistentSelection(), false);
800     }
801 }
802
803 void tst_qsgtextedit::focusOnPress()
804 {
805     {
806         QString componentStr = "import QtQuick 2.0\nTextEdit {  activeFocusOnPress: true; text: \"Hello World\" }";
807         QDeclarativeComponent texteditComponent(&engine);
808         texteditComponent.setData(componentStr.toLatin1(), QUrl());
809         QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
810         QVERIFY(textEditObject != 0);
811         QCOMPARE(textEditObject->focusOnPress(), true);
812     }
813
814     {
815         QString componentStr = "import QtQuick 2.0\nTextEdit {  activeFocusOnPress: false; text: \"Hello World\" }";
816         QDeclarativeComponent texteditComponent(&engine);
817         texteditComponent.setData(componentStr.toLatin1(), QUrl());
818         QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
819         QVERIFY(textEditObject != 0);
820         QCOMPARE(textEditObject->focusOnPress(), false);
821     }
822 }
823
824 void tst_qsgtextedit::selection()
825 {
826     QString testStr = standard[0];//TODO: What should happen for multiline/rich text?
827     QString componentStr = "import QtQuick 2.0\nTextEdit {  text: \""+ testStr +"\"; }";
828     QDeclarativeComponent texteditComponent(&engine);
829     texteditComponent.setData(componentStr.toLatin1(), QUrl());
830     QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
831     QVERIFY(textEditObject != 0);
832
833
834     //Test selection follows cursor
835     for (int i=0; i<= testStr.size(); i++) {
836         textEditObject->setCursorPosition(i);
837         QCOMPARE(textEditObject->cursorPosition(), i);
838         QCOMPARE(textEditObject->selectionStart(), i);
839         QCOMPARE(textEditObject->selectionEnd(), i);
840         QVERIFY(textEditObject->selectedText().isNull());
841     }
842
843     textEditObject->setCursorPosition(0);
844     QVERIFY(textEditObject->cursorPosition() == 0);
845     QVERIFY(textEditObject->selectionStart() == 0);
846     QVERIFY(textEditObject->selectionEnd() == 0);
847     QVERIFY(textEditObject->selectedText().isNull());
848
849     // Verify invalid positions are ignored.
850     textEditObject->setCursorPosition(-1);
851     QVERIFY(textEditObject->cursorPosition() == 0);
852     QVERIFY(textEditObject->selectionStart() == 0);
853     QVERIFY(textEditObject->selectionEnd() == 0);
854     QVERIFY(textEditObject->selectedText().isNull());
855
856     textEditObject->setCursorPosition(textEditObject->text().count()+1);
857     QVERIFY(textEditObject->cursorPosition() == 0);
858     QVERIFY(textEditObject->selectionStart() == 0);
859     QVERIFY(textEditObject->selectionEnd() == 0);
860     QVERIFY(textEditObject->selectedText().isNull());
861
862     //Test selection
863     for (int i=0; i<= testStr.size(); i++) {
864         textEditObject->select(0,i);
865         QCOMPARE(testStr.mid(0,i), textEditObject->selectedText());
866     }
867     for (int i=0; i<= testStr.size(); i++) {
868         textEditObject->select(i,testStr.size());
869         QCOMPARE(testStr.mid(i,testStr.size()-i), textEditObject->selectedText());
870     }
871
872     textEditObject->setCursorPosition(0);
873     QVERIFY(textEditObject->cursorPosition() == 0);
874     QVERIFY(textEditObject->selectionStart() == 0);
875     QVERIFY(textEditObject->selectionEnd() == 0);
876     QVERIFY(textEditObject->selectedText().isNull());
877
878     //Test Error Ignoring behaviour
879     textEditObject->setCursorPosition(0);
880     QVERIFY(textEditObject->selectedText().isNull());
881     textEditObject->select(-10,0);
882     QVERIFY(textEditObject->selectedText().isNull());
883     textEditObject->select(100,101);
884     QVERIFY(textEditObject->selectedText().isNull());
885     textEditObject->select(0,-10);
886     QVERIFY(textEditObject->selectedText().isNull());
887     textEditObject->select(0,100);
888     QVERIFY(textEditObject->selectedText().isNull());
889     textEditObject->select(0,10);
890     QVERIFY(textEditObject->selectedText().size() == 10);
891     textEditObject->select(-10,0);
892     QVERIFY(textEditObject->selectedText().size() == 10);
893     textEditObject->select(100,101);
894     QVERIFY(textEditObject->selectedText().size() == 10);
895     textEditObject->select(0,-10);
896     QVERIFY(textEditObject->selectedText().size() == 10);
897     textEditObject->select(0,100);
898     QVERIFY(textEditObject->selectedText().size() == 10);
899
900     textEditObject->deselect();
901     QVERIFY(textEditObject->selectedText().isNull());
902     textEditObject->select(0,10);
903     QVERIFY(textEditObject->selectedText().size() == 10);
904     textEditObject->deselect();
905     QVERIFY(textEditObject->selectedText().isNull());
906 }
907
908 void tst_qsgtextedit::isRightToLeft_data()
909 {
910     QTest::addColumn<QString>("text");
911     QTest::addColumn<bool>("emptyString");
912     QTest::addColumn<bool>("firstCharacter");
913     QTest::addColumn<bool>("lastCharacter");
914     QTest::addColumn<bool>("middleCharacter");
915     QTest::addColumn<bool>("startString");
916     QTest::addColumn<bool>("midString");
917     QTest::addColumn<bool>("endString");
918
919     const quint16 arabic_str[] = { 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0638, 0x0643, 0x00646, 0x0647, 0x0633, 0x0647};
920     QTest::newRow("Empty") << "" << false << false << false << false << false << false << false;
921     QTest::newRow("Neutral") << "23244242" << false << false << false << false << false << false << false;
922     QTest::newRow("LTR") << "Hello world" << false << false << false << false << false << false << false;
923     QTest::newRow("RTL") << QString::fromUtf16(arabic_str, 11) << false << true << true << true << true << true << true;
924     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;
925     QTest::newRow("Bidi LTR + RTL + LTR") << QString("Hello world") + QString::fromUtf16(arabic_str, 11) + QString("Hello world") << false << false << false << true << false << false << false;
926 }
927
928 void tst_qsgtextedit::isRightToLeft()
929 {
930     QFETCH(QString, text);
931     QFETCH(bool, emptyString);
932     QFETCH(bool, firstCharacter);
933     QFETCH(bool, lastCharacter);
934     QFETCH(bool, middleCharacter);
935     QFETCH(bool, startString);
936     QFETCH(bool, midString);
937     QFETCH(bool, endString);
938
939     QSGTextEdit textEdit;
940     textEdit.setText(text);
941
942     // first test that the right string is delivered to the QString::isRightToLeft()
943     QCOMPARE(textEdit.isRightToLeft(0,0), text.mid(0,0).isRightToLeft());
944     QCOMPARE(textEdit.isRightToLeft(0,1), text.mid(0,1).isRightToLeft());
945     QCOMPARE(textEdit.isRightToLeft(text.count()-2, text.count()-1), text.mid(text.count()-2, text.count()-1).isRightToLeft());
946     QCOMPARE(textEdit.isRightToLeft(text.count()/2, text.count()/2 + 1), text.mid(text.count()/2, text.count()/2 + 1).isRightToLeft());
947     QCOMPARE(textEdit.isRightToLeft(0,text.count()/4), text.mid(0,text.count()/4).isRightToLeft());
948     QCOMPARE(textEdit.isRightToLeft(text.count()/4,3*text.count()/4), text.mid(text.count()/4,3*text.count()/4).isRightToLeft());
949     if (text.isEmpty())
950         QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML TextEdit: isRightToLeft(start, end) called with the end property being smaller than the start.");
951     QCOMPARE(textEdit.isRightToLeft(3*text.count()/4,text.count()-1), text.mid(3*text.count()/4,text.count()-1).isRightToLeft());
952
953     // then test that the feature actually works
954     QCOMPARE(textEdit.isRightToLeft(0,0), emptyString);
955     QCOMPARE(textEdit.isRightToLeft(0,1), firstCharacter);
956     QCOMPARE(textEdit.isRightToLeft(text.count()-2, text.count()-1), lastCharacter);
957     QCOMPARE(textEdit.isRightToLeft(text.count()/2, text.count()/2 + 1), middleCharacter);
958     QCOMPARE(textEdit.isRightToLeft(0,text.count()/4), startString);
959     QCOMPARE(textEdit.isRightToLeft(text.count()/4,3*text.count()/4), midString);
960     if (text.isEmpty())
961         QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML TextEdit: isRightToLeft(start, end) called with the end property being smaller than the start.");
962     QCOMPARE(textEdit.isRightToLeft(3*text.count()/4,text.count()-1), endString);
963 }
964
965 void tst_qsgtextedit::keySelection()
966 {
967     QSGView canvas(QUrl::fromLocalFile(TESTDATA("navigation.qml")));
968     canvas.show();
969     canvas.requestActivateWindow();
970     QTest::qWaitForWindowShown(&canvas);
971     QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
972     canvas.requestActivateWindow();
973
974     QVERIFY(canvas.rootObject() != 0);
975
976     QSGTextEdit *input = qobject_cast<QSGTextEdit *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
977
978     QVERIFY(input != 0);
979     QTRY_VERIFY(input->hasActiveFocus() == true);
980
981     QSignalSpy spy(input, SIGNAL(selectionChanged()));
982
983     simulateKey(&canvas, Qt::Key_Right, Qt::ShiftModifier);
984     QVERIFY(input->hasActiveFocus() == true);
985     QCOMPARE(input->selectedText(), QString("a"));
986     QCOMPARE(spy.count(), 1);
987     simulateKey(&canvas, Qt::Key_Right);
988     QVERIFY(input->hasActiveFocus() == true);
989     QCOMPARE(input->selectedText(), QString());
990     QCOMPARE(spy.count(), 2);
991     simulateKey(&canvas, Qt::Key_Right);
992     QVERIFY(input->hasActiveFocus() == false);
993     QCOMPARE(input->selectedText(), QString());
994     QCOMPARE(spy.count(), 2);
995
996     simulateKey(&canvas, Qt::Key_Left);
997     QVERIFY(input->hasActiveFocus() == true);
998     QCOMPARE(spy.count(), 2);
999     simulateKey(&canvas, Qt::Key_Left, Qt::ShiftModifier);
1000     QVERIFY(input->hasActiveFocus() == true);
1001     QCOMPARE(input->selectedText(), QString("a"));
1002     QCOMPARE(spy.count(), 3);
1003     simulateKey(&canvas, Qt::Key_Left);
1004     QVERIFY(input->hasActiveFocus() == true);
1005     QCOMPARE(input->selectedText(), QString());
1006     QCOMPARE(spy.count(), 4);
1007     simulateKey(&canvas, Qt::Key_Left);
1008     QVERIFY(input->hasActiveFocus() == false);
1009     QCOMPARE(input->selectedText(), QString());
1010     QCOMPARE(spy.count(), 4);
1011 }
1012
1013 void tst_qsgtextedit::moveCursorSelection_data()
1014 {
1015     QTest::addColumn<QString>("testStr");
1016     QTest::addColumn<int>("cursorPosition");
1017     QTest::addColumn<int>("movePosition");
1018     QTest::addColumn<QSGTextEdit::SelectionMode>("mode");
1019     QTest::addColumn<int>("selectionStart");
1020     QTest::addColumn<int>("selectionEnd");
1021     QTest::addColumn<bool>("reversible");
1022
1023     QTest::newRow("(t)he|characters")
1024             << standard[0] << 0 << 1 << QSGTextEdit::SelectCharacters << 0 << 1 << true;
1025     QTest::newRow("do(g)|characters")
1026             << standard[0] << 43 << 44 << QSGTextEdit::SelectCharacters << 43 << 44 << true;
1027     QTest::newRow("jum(p)ed|characters")
1028             << standard[0] << 23 << 24 << QSGTextEdit::SelectCharacters << 23 << 24 << true;
1029     QTest::newRow("jumped( )over|characters")
1030             << standard[0] << 26 << 27 << QSGTextEdit::SelectCharacters << 26 << 27 << true;
1031     QTest::newRow("(the )|characters")
1032             << standard[0] << 0 << 4 << QSGTextEdit::SelectCharacters << 0 << 4 << true;
1033     QTest::newRow("( dog)|characters")
1034             << standard[0] << 40 << 44 << QSGTextEdit::SelectCharacters << 40 << 44 << true;
1035     QTest::newRow("( jumped )|characters")
1036             << standard[0] << 19 << 27 << QSGTextEdit::SelectCharacters << 19 << 27 << true;
1037     QTest::newRow("th(e qu)ick|characters")
1038             << standard[0] << 2 << 6 << QSGTextEdit::SelectCharacters << 2 << 6 << true;
1039     QTest::newRow("la(zy d)og|characters")
1040             << standard[0] << 38 << 42 << QSGTextEdit::SelectCharacters << 38 << 42 << true;
1041     QTest::newRow("jum(ped ov)er|characters")
1042             << standard[0] << 23 << 29 << QSGTextEdit::SelectCharacters << 23 << 29 << true;
1043     QTest::newRow("()the|characters")
1044             << standard[0] << 0 << 0 << QSGTextEdit::SelectCharacters << 0 << 0 << true;
1045     QTest::newRow("dog()|characters")
1046             << standard[0] << 44 << 44 << QSGTextEdit::SelectCharacters << 44 << 44 << true;
1047     QTest::newRow("jum()ped|characters")
1048             << standard[0] << 23 << 23 << QSGTextEdit::SelectCharacters << 23 << 23 << true;
1049
1050     QTest::newRow("<(t)he>|words")
1051             << standard[0] << 0 << 1 << QSGTextEdit::SelectWords << 0 << 3 << true;
1052     QTest::newRow("<do(g)>|words")
1053             << standard[0] << 43 << 44 << QSGTextEdit::SelectWords << 41 << 44 << true;
1054     QTest::newRow("<jum(p)ed>|words")
1055             << standard[0] << 23 << 24 << QSGTextEdit::SelectWords << 20 << 26 << true;
1056     QTest::newRow("<jumped( )>over|words")
1057             << standard[0] << 26 << 27 << QSGTextEdit::SelectWords << 20 << 27 << false;
1058     QTest::newRow("jumped<( )over>|words,reversed")
1059             << standard[0] << 27 << 26 << QSGTextEdit::SelectWords << 26 << 31 << false;
1060     QTest::newRow("<(the )>quick|words")
1061             << standard[0] << 0 << 4 << QSGTextEdit::SelectWords << 0 << 4 << false;
1062     QTest::newRow("<(the )quick>|words,reversed")
1063             << standard[0] << 4 << 0 << QSGTextEdit::SelectWords << 0 << 9 << false;
1064     QTest::newRow("<lazy( dog)>|words")
1065             << standard[0] << 40 << 44 << QSGTextEdit::SelectWords << 36 << 44 << false;
1066     QTest::newRow("lazy<( dog)>|words,reversed")
1067             << standard[0] << 44 << 40 << QSGTextEdit::SelectWords << 40 << 44 << false;
1068     QTest::newRow("<fox( jumped )>over|words")
1069             << standard[0] << 19 << 27 << QSGTextEdit::SelectWords << 16 << 27 << false;
1070     QTest::newRow("fox<( jumped )over>|words,reversed")
1071             << standard[0] << 27 << 19 << QSGTextEdit::SelectWords << 19 << 31 << false;
1072     QTest::newRow("<th(e qu)ick>|words")
1073             << standard[0] << 2 << 6 << QSGTextEdit::SelectWords << 0 << 9 << true;
1074     QTest::newRow("<la(zy d)og|words>")
1075             << standard[0] << 38 << 42 << QSGTextEdit::SelectWords << 36 << 44 << true;
1076     QTest::newRow("<jum(ped ov)er>|words")
1077             << standard[0] << 23 << 29 << QSGTextEdit::SelectWords << 20 << 31 << true;
1078     QTest::newRow("<()>the|words")
1079             << standard[0] << 0 << 0 << QSGTextEdit::SelectWords << 0 << 0 << true;
1080     QTest::newRow("dog<()>|words")
1081             << standard[0] << 44 << 44 << QSGTextEdit::SelectWords << 44 << 44 << true;
1082     QTest::newRow("jum<()>ped|words")
1083             << standard[0] << 23 << 23 << QSGTextEdit::SelectWords << 23 << 23 << true;
1084
1085     QTest::newRow("Hello<(,)> |words")
1086             << standard[2] << 5 << 6 << QSGTextEdit::SelectWords << 5 << 6 << true;
1087     QTest::newRow("Hello<(, )>world|words")
1088             << standard[2] << 5 << 7 << QSGTextEdit::SelectWords << 5 << 7 << false;
1089     QTest::newRow("Hello<(, )world>|words,reversed")
1090             << standard[2] << 7 << 5 << QSGTextEdit::SelectWords << 5 << 12 << false;
1091     QTest::newRow("<Hel(lo, )>world|words")
1092             << standard[2] << 3 << 7 << QSGTextEdit::SelectWords << 0 << 7 << false;
1093     QTest::newRow("<Hel(lo, )world>|words,reversed")
1094             << standard[2] << 7 << 3 << QSGTextEdit::SelectWords << 0 << 12 << false;
1095     QTest::newRow("<Hel(lo)>,|words")
1096             << standard[2] << 3 << 5 << QSGTextEdit::SelectWords << 0 << 5 << true;
1097     QTest::newRow("Hello<()>,|words")
1098             << standard[2] << 5 << 5 << QSGTextEdit::SelectWords << 5 << 5 << true;
1099     QTest::newRow("Hello,<()>|words")
1100             << standard[2] << 6 << 6 << QSGTextEdit::SelectWords << 6 << 6 << true;
1101     QTest::newRow("Hello<,( )>world|words")
1102             << standard[2] << 6 << 7 << QSGTextEdit::SelectWords << 5 << 7 << false;
1103     QTest::newRow("Hello,<( )world>|words,reversed")
1104             << standard[2] << 7 << 6 << QSGTextEdit::SelectWords << 6 << 12 << false;
1105     QTest::newRow("Hello<,( world)>|words")
1106             << standard[2] << 6 << 12 << QSGTextEdit::SelectWords << 5 << 12 << false;
1107     QTest::newRow("Hello,<( world)>|words,reversed")
1108             << standard[2] << 12 << 6 << QSGTextEdit::SelectWords << 6 << 12 << false;
1109     QTest::newRow("Hello<,( world!)>|words")
1110             << standard[2] << 6 << 13 << QSGTextEdit::SelectWords << 5 << 13 << false;
1111     QTest::newRow("Hello,<( world!)>|words,reversed")
1112             << standard[2] << 13 << 6 << QSGTextEdit::SelectWords << 6 << 13 << false;
1113     QTest::newRow("Hello<(, world!)>|words")
1114             << standard[2] << 5 << 13 << QSGTextEdit::SelectWords << 5 << 13 << true;
1115     QTest::newRow("world<(!)>|words")
1116             << standard[2] << 12 << 13 << QSGTextEdit::SelectWords << 12 << 13 << true;
1117     QTest::newRow("world!<()>)|words")
1118             << standard[2] << 13 << 13 << QSGTextEdit::SelectWords << 13 << 13 << true;
1119     QTest::newRow("world<()>!)|words")
1120             << standard[2] << 12 << 12 << QSGTextEdit::SelectWords << 12 << 12 << true;
1121
1122     QTest::newRow("<(,)>olleH |words")
1123             << standard[3] << 7 << 8 << QSGTextEdit::SelectWords << 7 << 8 << true;
1124     QTest::newRow("<dlrow( ,)>olleH|words")
1125             << standard[3] << 6 << 8 << QSGTextEdit::SelectWords << 1 << 8 << false;
1126     QTest::newRow("dlrow<( ,)>olleH|words,reversed")
1127             << standard[3] << 8 << 6 << QSGTextEdit::SelectWords << 6 << 8 << false;
1128     QTest::newRow("<dlrow( ,ol)leH>|words")
1129             << standard[3] << 6 << 10 << QSGTextEdit::SelectWords << 1 << 13 << false;
1130     QTest::newRow("dlrow<( ,ol)leH>|words,reversed")
1131             << standard[3] << 10 << 6 << QSGTextEdit::SelectWords << 6 << 13 << false;
1132     QTest::newRow(",<(ol)leH>,|words")
1133             << standard[3] << 8 << 10 << QSGTextEdit::SelectWords << 8 << 13 << true;
1134     QTest::newRow(",<()>olleH|words")
1135             << standard[3] << 8 << 8 << QSGTextEdit::SelectWords << 8 << 8 << true;
1136     QTest::newRow("<()>,olleH|words")
1137             << standard[3] << 7 << 7 << QSGTextEdit::SelectWords << 7 << 7 << true;
1138     QTest::newRow("<dlrow( )>,olleH|words")
1139             << standard[3] << 6 << 7 << QSGTextEdit::SelectWords << 1 << 7 << false;
1140     QTest::newRow("dlrow<( ),>olleH|words,reversed")
1141             << standard[3] << 7 << 6 << QSGTextEdit::SelectWords << 6 << 8 << false;
1142     QTest::newRow("<(dlrow )>,olleH|words")
1143             << standard[3] << 1 << 7 << QSGTextEdit::SelectWords << 1 << 7 << false;
1144     QTest::newRow("<(dlrow ),>olleH|words,reversed")
1145             << standard[3] << 7 << 1 << QSGTextEdit::SelectWords << 1 << 8 << false;
1146     QTest::newRow("<(!dlrow )>,olleH|words")
1147             << standard[3] << 0 << 7 << QSGTextEdit::SelectWords << 0 << 7 << false;
1148     QTest::newRow("<(!dlrow ),>olleH|words,reversed")
1149             << standard[3] << 7 << 0 << QSGTextEdit::SelectWords << 0 << 8 << false;
1150     QTest::newRow("(!dlrow ,)olleH|words")
1151             << standard[3] << 0 << 8 << QSGTextEdit::SelectWords << 0 << 8 << true;
1152     QTest::newRow("<(!)>dlrow|words")
1153             << standard[3] << 0 << 1 << QSGTextEdit::SelectWords << 0 << 1 << true;
1154     QTest::newRow("<()>!dlrow|words")
1155             << standard[3] << 0 << 0 << QSGTextEdit::SelectWords << 0 << 0 << true;
1156     QTest::newRow("!<()>dlrow|words")
1157             << standard[3] << 1 << 1 << QSGTextEdit::SelectWords << 1 << 1 << true;
1158 }
1159
1160 void tst_qsgtextedit::moveCursorSelection()
1161 {
1162     QFETCH(QString, testStr);
1163     QFETCH(int, cursorPosition);
1164     QFETCH(int, movePosition);
1165     QFETCH(QSGTextEdit::SelectionMode, mode);
1166     QFETCH(int, selectionStart);
1167     QFETCH(int, selectionEnd);
1168     QFETCH(bool, reversible);
1169
1170     QString componentStr = "import QtQuick 2.0\nTextEdit {  text: \""+ testStr +"\"; }";
1171     QDeclarativeComponent textinputComponent(&engine);
1172     textinputComponent.setData(componentStr.toLatin1(), QUrl());
1173     QSGTextEdit *texteditObject = qobject_cast<QSGTextEdit*>(textinputComponent.create());
1174     QVERIFY(texteditObject != 0);
1175
1176     texteditObject->setCursorPosition(cursorPosition);
1177     texteditObject->moveCursorSelection(movePosition, mode);
1178
1179     QCOMPARE(texteditObject->selectedText(), testStr.mid(selectionStart, selectionEnd - selectionStart));
1180     QCOMPARE(texteditObject->selectionStart(), selectionStart);
1181     QCOMPARE(texteditObject->selectionEnd(), selectionEnd);
1182
1183     if (reversible) {
1184         texteditObject->setCursorPosition(movePosition);
1185         texteditObject->moveCursorSelection(cursorPosition, mode);
1186
1187         QCOMPARE(texteditObject->selectedText(), testStr.mid(selectionStart, selectionEnd - selectionStart));
1188         QCOMPARE(texteditObject->selectionStart(), selectionStart);
1189         QCOMPARE(texteditObject->selectionEnd(), selectionEnd);
1190     }
1191 }
1192
1193 void tst_qsgtextedit::moveCursorSelectionSequence_data()
1194 {
1195     QTest::addColumn<QString>("testStr");
1196     QTest::addColumn<int>("cursorPosition");
1197     QTest::addColumn<int>("movePosition1");
1198     QTest::addColumn<int>("movePosition2");
1199     QTest::addColumn<int>("selection1Start");
1200     QTest::addColumn<int>("selection1End");
1201     QTest::addColumn<int>("selection2Start");
1202     QTest::addColumn<int>("selection2End");
1203
1204     QTest::newRow("the {<quick( bro)wn> f^ox} jumped|ltr")
1205             << standard[0]
1206             << 9 << 13 << 17
1207             << 4 << 15
1208             << 4 << 19;
1209     QTest::newRow("the quick<( {bro)wn> f^ox} jumped|rtl")
1210             << standard[0]
1211             << 13 << 9 << 17
1212             << 9 << 15
1213             << 10 << 19;
1214     QTest::newRow("the {<quick( bro)wn> ^}fox jumped|ltr")
1215             << standard[0]
1216             << 9 << 13 << 16
1217             << 4 << 15
1218             << 4 << 16;
1219     QTest::newRow("the quick<( {bro)wn> ^}fox jumped|rtl")
1220             << standard[0]
1221             << 13 << 9 << 16
1222             << 9 << 15
1223             << 10 << 16;
1224     QTest::newRow("the {<quick( bro)wn^>} fox jumped|ltr")
1225             << standard[0]
1226             << 9 << 13 << 15
1227             << 4 << 15
1228             << 4 << 15;
1229     QTest::newRow("the quick<( {bro)wn^>} f^ox jumped|rtl")
1230             << standard[0]
1231             << 13 << 9 << 15
1232             << 9 << 15
1233             << 10 << 15;
1234     QTest::newRow("the {<quick() ^}bro)wn> fox|ltr")
1235             << standard[0]
1236             << 9 << 13 << 10
1237             << 4 << 15
1238             << 4 << 10;
1239     QTest::newRow("the quick<(^ {^bro)wn>} fox|rtl")
1240             << standard[0]
1241             << 13 << 9 << 10
1242             << 9 << 15
1243             << 10 << 15;
1244     QTest::newRow("the {<quick^}( bro)wn> fox|ltr")
1245             << standard[0]
1246             << 9 << 13 << 9
1247             << 4 << 15
1248             << 4 << 9;
1249     QTest::newRow("the quick{<(^ bro)wn>} fox|rtl")
1250             << standard[0]
1251             << 13 << 9 << 9
1252             << 9 << 15
1253             << 9 << 15;
1254     QTest::newRow("the {<qui^ck}( bro)wn> fox|ltr")
1255             << standard[0]
1256             << 9 << 13 << 7
1257             << 4 << 15
1258             << 4 << 9;
1259     QTest::newRow("the {<qui^ck}( bro)wn> fox|rtl")
1260             << standard[0]
1261             << 13 << 9 << 7
1262             << 9 << 15
1263             << 4 << 15;
1264     QTest::newRow("the {<^quick}( bro)wn> fox|ltr")
1265             << standard[0]
1266             << 9 << 13 << 4
1267             << 4 << 15
1268             << 4 << 9;
1269     QTest::newRow("the {<^quick}( bro)wn> fox|rtl")
1270             << standard[0]
1271             << 13 << 9 << 4
1272             << 9 << 15
1273             << 4 << 15;
1274     QTest::newRow("the{^ <quick}( bro)wn> fox|ltr")
1275             << standard[0]
1276             << 9 << 13 << 3
1277             << 4 << 15
1278             << 3 << 9;
1279     QTest::newRow("the{^ <quick}( bro)wn> fox|rtl")
1280             << standard[0]
1281             << 13 << 9 << 3
1282             << 9 << 15
1283             << 3 << 15;
1284     QTest::newRow("{t^he <quick}( bro)wn> fox|ltr")
1285             << standard[0]
1286             << 9 << 13 << 1
1287             << 4 << 15
1288             << 0 << 9;
1289     QTest::newRow("{t^he <quick}( bro)wn> fox|rtl")
1290             << standard[0]
1291             << 13 << 9 << 1
1292             << 9 << 15
1293             << 0 << 15;
1294
1295     QTest::newRow("{<He(ll)o>, w^orld}!|ltr")
1296             << standard[2]
1297             << 2 << 4 << 8
1298             << 0 << 5
1299             << 0 << 12;
1300     QTest::newRow("{<He(ll)o>, w^orld}!|rtl")
1301             << standard[2]
1302             << 4 << 2 << 8
1303             << 0 << 5
1304             << 0 << 12;
1305
1306     QTest::newRow("!{dlro^w ,<o(ll)eH>}|ltr")
1307             << standard[3]
1308             << 9 << 11 << 5
1309             << 8 << 13
1310             << 1 << 13;
1311     QTest::newRow("!{dlro^w ,<o(ll)eH>}|rtl")
1312             << standard[3]
1313             << 11 << 9 << 5
1314             << 8 << 13
1315             << 1 << 13;
1316 }
1317
1318 void tst_qsgtextedit::moveCursorSelectionSequence()
1319 {
1320     QFETCH(QString, testStr);
1321     QFETCH(int, cursorPosition);
1322     QFETCH(int, movePosition1);
1323     QFETCH(int, movePosition2);
1324     QFETCH(int, selection1Start);
1325     QFETCH(int, selection1End);
1326     QFETCH(int, selection2Start);
1327     QFETCH(int, selection2End);
1328
1329     QString componentStr = "import QtQuick 2.0\nTextEdit {  text: \""+ testStr +"\"; }";
1330     QDeclarativeComponent texteditComponent(&engine);
1331     texteditComponent.setData(componentStr.toLatin1(), QUrl());
1332     QSGTextEdit *texteditObject = qobject_cast<QSGTextEdit*>(texteditComponent.create());
1333     QVERIFY(texteditObject != 0);
1334
1335     texteditObject->setCursorPosition(cursorPosition);
1336
1337     texteditObject->moveCursorSelection(movePosition1, QSGTextEdit::SelectWords);
1338     QCOMPARE(texteditObject->selectedText(), testStr.mid(selection1Start, selection1End - selection1Start));
1339     QCOMPARE(texteditObject->selectionStart(), selection1Start);
1340     QCOMPARE(texteditObject->selectionEnd(), selection1End);
1341
1342     texteditObject->moveCursorSelection(movePosition2, QSGTextEdit::SelectWords);
1343     QCOMPARE(texteditObject->selectedText(), testStr.mid(selection2Start, selection2End - selection2Start));
1344     QCOMPARE(texteditObject->selectionStart(), selection2Start);
1345     QCOMPARE(texteditObject->selectionEnd(), selection2End);
1346 }
1347
1348
1349 void tst_qsgtextedit::mouseSelection_data()
1350 {
1351     QTest::addColumn<QString>("qmlfile");
1352     QTest::addColumn<int>("from");
1353     QTest::addColumn<int>("to");
1354     QTest::addColumn<QString>("selectedText");
1355
1356     // import installed
1357     QTest::newRow("on") << TESTDATA("mouseselection_true.qml") << 4 << 9 << "45678";
1358     QTest::newRow("off") << TESTDATA("mouseselection_false.qml") << 4 << 9 << QString();
1359     QTest::newRow("default") << TESTDATA("mouseselection_default.qml") << 4 << 9 << QString();
1360     QTest::newRow("off word selection") << TESTDATA("mouseselection_false_words.qml") << 4 << 9 << QString();
1361     QTest::newRow("on word selection (4,9)") << TESTDATA("mouseselection_true_words.qml") << 4 << 9 << "0123456789";
1362     QTest::newRow("on word selection (2,13)") << TESTDATA("mouseselection_true_words.qml") << 2 << 13 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1363     QTest::newRow("on word selection (2,30)") << TESTDATA("mouseselection_true_words.qml") << 2 << 30 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1364     QTest::newRow("on word selection (9,13)") << TESTDATA("mouseselection_true_words.qml") << 9 << 13 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1365     QTest::newRow("on word selection (9,30)") << TESTDATA("mouseselection_true_words.qml") << 9 << 30 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1366     QTest::newRow("on word selection (13,2)") << TESTDATA("mouseselection_true_words.qml") << 13 << 2 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1367     QTest::newRow("on word selection (20,2)") << TESTDATA("mouseselection_true_words.qml") << 20 << 2 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1368     QTest::newRow("on word selection (12,9)") << TESTDATA("mouseselection_true_words.qml") << 12 << 9 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1369     QTest::newRow("on word selection (30,9)") << TESTDATA("mouseselection_true_words.qml") << 30 << 9 << "0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1370 }
1371
1372 void tst_qsgtextedit::mouseSelection()
1373 {
1374     QFETCH(QString, qmlfile);
1375     QFETCH(int, from);
1376     QFETCH(int, to);
1377     QFETCH(QString, selectedText);
1378
1379     QSGView canvas(QUrl::fromLocalFile(qmlfile));
1380
1381     canvas.show();
1382     canvas.requestActivateWindow();
1383     QTest::qWaitForWindowShown(&canvas);
1384     QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
1385
1386     QVERIFY(canvas.rootObject() != 0);
1387     QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit *>(canvas.rootObject());
1388     QVERIFY(textEditObject != 0);
1389
1390     // press-and-drag-and-release from x1 to x2
1391     QPoint p1 = textEditObject->positionToRectangle(from).center().toPoint();
1392     QPoint p2 = textEditObject->positionToRectangle(to).center().toPoint();
1393     QTest::mousePress(&canvas, Qt::LeftButton, 0, p1);
1394     QTest::mouseMove(&canvas, p2);
1395     QTest::mouseRelease(&canvas, Qt::LeftButton, 0, p2);
1396     QTest::qWait(50);
1397     QTRY_COMPARE(textEditObject->selectedText(), selectedText);
1398
1399     // Clicking and shift to clicking between the same points should select the same text.
1400     textEditObject->setCursorPosition(0);
1401     QTest::mouseClick(&canvas, Qt::LeftButton, Qt::NoModifier, p1);
1402     QTest::mouseClick(&canvas, Qt::LeftButton, Qt::ShiftModifier, p2);
1403     QTest::qWait(50);
1404     if (!selectedText.isEmpty())
1405         QEXPECT_FAIL("", "QTBUG-21743", Continue);
1406     QTRY_COMPARE(textEditObject->selectedText(), selectedText);
1407 }
1408
1409 void tst_qsgtextedit::dragMouseSelection()
1410 {
1411     QString qmlfile = TESTDATA("mouseselection_true.qml");
1412
1413     QSGView canvas(QUrl::fromLocalFile(qmlfile));
1414
1415     canvas.show();
1416     canvas.requestActivateWindow();
1417     QTest::qWaitForWindowShown(&canvas);
1418     QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
1419
1420     QVERIFY(canvas.rootObject() != 0);
1421     QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit *>(canvas.rootObject());
1422     QVERIFY(textEditObject != 0);
1423
1424     // press-and-drag-and-release from x1 to x2
1425     int x1 = 10;
1426     int x2 = 70;
1427     int y = textEditObject->height()/2;
1428     QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y));
1429     QTest::mouseMove(&canvas, QPoint(x2, y));
1430     QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y));
1431     QTest::qWait(300);
1432     QString str1;
1433     QTRY_VERIFY((str1 = textEditObject->selectedText()).length() > 3);
1434
1435     // press and drag the current selection.
1436     x1 = 40;
1437     x2 = 100;
1438     QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y));
1439     QTest::mouseMove(&canvas, QPoint(x2, y));
1440     QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y));
1441     QTest::qWait(300);
1442     QString str2;
1443     QTRY_VERIFY((str2 = textEditObject->selectedText()).length() > 3);
1444
1445     QVERIFY(str1 != str2); // Verify the second press and drag is a new selection and not the first moved.
1446 }
1447
1448 void tst_qsgtextedit::mouseSelectionMode_data()
1449 {
1450     QTest::addColumn<QString>("qmlfile");
1451     QTest::addColumn<bool>("selectWords");
1452
1453     // import installed
1454     QTest::newRow("SelectWords") << TESTDATA("mouseselectionmode_words.qml") << true;
1455     QTest::newRow("SelectCharacters") << TESTDATA("mouseselectionmode_characters.qml") << false;
1456     QTest::newRow("default") << TESTDATA("mouseselectionmode_default.qml") << false;
1457 }
1458
1459 void tst_qsgtextedit::mouseSelectionMode()
1460 {
1461     QFETCH(QString, qmlfile);
1462     QFETCH(bool, selectWords);
1463
1464     QString text = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1465
1466     QSGView canvas(QUrl::fromLocalFile(qmlfile));
1467
1468     canvas.show();
1469     canvas.requestActivateWindow();
1470     QTest::qWaitForWindowShown(&canvas);
1471     QTRY_COMPARE(&canvas, qGuiApp->focusWindow());
1472
1473     QVERIFY(canvas.rootObject() != 0);
1474     QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit *>(canvas.rootObject());
1475     QVERIFY(textEditObject != 0);
1476
1477     // press-and-drag-and-release from x1 to x2
1478     int x1 = 10;
1479     int x2 = 70;
1480     int y = textEditObject->height()/2;
1481     QTest::mousePress(&canvas, Qt::LeftButton, 0, QPoint(x1,y));
1482     QTest::mouseMove(&canvas, QPoint(x2, y));
1483     //QTest::mouseMove(canvas, QPoint(x2,y)); // doesn't work
1484 //    QMouseEvent mv(QEvent::MouseMove, QPoint(x2,y), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
1485 //    QGuiApplication::sendEvent(&canvas, &mv);
1486     QTest::mouseRelease(&canvas, Qt::LeftButton, 0, QPoint(x2,y));
1487     QString str = textEditObject->selectedText();
1488     if (selectWords) {
1489         QTRY_COMPARE(textEditObject->selectedText(), text);
1490     } else {
1491         QTRY_VERIFY(textEditObject->selectedText().length() > 3);
1492         QVERIFY(str != text);
1493     }
1494 }
1495
1496 void tst_qsgtextedit::inputMethodHints()
1497 {
1498     QSGView canvas(QUrl::fromLocalFile(TESTDATA("inputmethodhints.qml")));
1499     canvas.show();
1500     canvas.requestActivateWindow();
1501
1502     QVERIFY(canvas.rootObject() != 0);
1503     QSGTextEdit *textEditObject = qobject_cast<QSGTextEdit *>(canvas.rootObject());
1504     QVERIFY(textEditObject != 0);
1505     QVERIFY(textEditObject->inputMethodHints() & Qt::ImhNoPredictiveText);
1506     textEditObject->setInputMethodHints(Qt::ImhUppercaseOnly);
1507     QVERIFY(textEditObject->inputMethodHints() & Qt::ImhUppercaseOnly);
1508 }
1509
1510 void tst_qsgtextedit::positionAt()
1511 {
1512     QSGView canvas(QUrl::fromLocalFile(TESTDATA("positionAt.qml")));
1513     QVERIFY(canvas.rootObject() != 0);
1514     canvas.show();
1515     canvas.requestActivateWindow();
1516     canvas.requestActivateWindow();
1517     QTest::qWaitForWindowShown(&canvas);
1518
1519     QSGTextEdit *texteditObject = qobject_cast<QSGTextEdit *>(canvas.rootObject());
1520     QVERIFY(texteditObject != 0);
1521
1522     QFontMetrics fm(texteditObject->font());
1523     const int y0 = fm.height() / 2;
1524     const int y1 = fm.height() * 3 / 2;
1525
1526     int pos = texteditObject->positionAt(texteditObject->width()/2, y0);
1527     int width = 0;
1528     if (!qmlDisableDistanceField()) {
1529         QTextLayout layout(texteditObject->text().left(pos));
1530
1531         {
1532             QTextOption option;
1533             option.setUseDesignMetrics(true);
1534             layout.setTextOption(option);
1535         }
1536
1537         layout.beginLayout();
1538         QTextLine line = layout.createLine();
1539         layout.endLayout();
1540
1541         width = ceil(line.horizontalAdvance());
1542
1543     } else {
1544         width = fm.width(texteditObject->text().left(pos));
1545     }
1546
1547
1548     int diff = abs(int(width-texteditObject->width()/2));
1549
1550     QEXPECT_FAIL("", "QTBUG-21689", Abort);
1551     // some tollerance for different fonts.
1552 #ifdef Q_OS_LINUX
1553     QVERIFY(diff < 2);
1554 #else
1555     QVERIFY(diff < 5);
1556 #endif
1557
1558     const qreal x0 = texteditObject->positionToRectangle(pos).x();
1559     const qreal x1 = texteditObject->positionToRectangle(pos + 1).x();
1560
1561     QString preeditText = texteditObject->text().mid(0, pos);
1562     texteditObject->setText(texteditObject->text().mid(pos));
1563     texteditObject->setCursorPosition(0);
1564
1565     QInputMethodEvent inputEvent(preeditText, QList<QInputMethodEvent::Attribute>());
1566     QGuiApplication::sendEvent(&canvas, &inputEvent);
1567
1568     // Check all points within the preedit text return the same position.
1569     QCOMPARE(texteditObject->positionAt(0, y0), 0);
1570     QCOMPARE(texteditObject->positionAt(x0 / 2, y0), 0);
1571     QCOMPARE(texteditObject->positionAt(x0, y0), 0);
1572
1573     // Verify positioning returns to normal after the preedit text.
1574     QCOMPARE(texteditObject->positionAt(x1, y0), 1);
1575     QCOMPARE(texteditObject->positionToRectangle(1).x(), x1);
1576
1577     QVERIFY(texteditObject->positionAt(x0 / 2, y1) > 0);
1578 }
1579
1580 void tst_qsgtextedit::cursorDelegate()
1581 {
1582     QSGView view(QUrl::fromLocalFile(TESTDATA("cursorTest.qml")));
1583     view.show();
1584     view.requestActivateWindow();
1585     QSGTextEdit *textEditObject = view.rootObject()->findChild<QSGTextEdit*>("textEditObject");
1586     QVERIFY(textEditObject != 0);
1587     QVERIFY(textEditObject->findChild<QSGItem*>("cursorInstance"));
1588     //Test Delegate gets created
1589     textEditObject->setFocus(true);
1590     QSGItem* delegateObject = textEditObject->findChild<QSGItem*>("cursorInstance");
1591     QVERIFY(delegateObject);
1592     QCOMPARE(delegateObject->property("localProperty").toString(), QString("Hello"));
1593     //Test Delegate gets moved
1594     for (int i=0; i<= textEditObject->text().length(); i++) {
1595         textEditObject->setCursorPosition(i);
1596         QCOMPARE(textEditObject->cursorRectangle().x(), qRound(delegateObject->x()));
1597         QCOMPARE(textEditObject->cursorRectangle().y(), qRound(delegateObject->y()));
1598     }
1599     // Clear preedit text;
1600     QInputMethodEvent event;
1601     QGuiApplication::sendEvent(&view, &event);
1602
1603
1604     // Test delegate gets moved on mouse press.
1605     textEditObject->setSelectByMouse(true);
1606     textEditObject->setCursorPosition(0);
1607     const QPoint point1 = textEditObject->positionToRectangle(5).center().toPoint();
1608     QTest::mouseClick(&view, Qt::LeftButton, 0, point1);
1609     QTest::qWait(50);
1610     QTRY_VERIFY(textEditObject->cursorPosition() != 0);
1611     QCOMPARE(textEditObject->cursorRectangle().x(), qRound(delegateObject->x()));
1612     QCOMPARE(textEditObject->cursorRectangle().y(), qRound(delegateObject->y()));
1613
1614     // Test delegate gets moved on mouse drag
1615     textEditObject->setCursorPosition(0);
1616     const QPoint point2 = textEditObject->positionToRectangle(10).center().toPoint();
1617     QTest::mousePress(&view, Qt::LeftButton, 0, point1);
1618     QMouseEvent mv(QEvent::MouseMove, point2, Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
1619     QGuiApplication::sendEvent(&view, &mv);
1620     QTest::mouseRelease(&view, Qt::LeftButton, 0, point2);
1621     QTest::qWait(50);
1622     QTRY_COMPARE(textEditObject->cursorRectangle().x(), qRound(delegateObject->x()));
1623     QCOMPARE(textEditObject->cursorRectangle().y(), qRound(delegateObject->y()));
1624
1625     textEditObject->setReadOnly(true);
1626     textEditObject->setCursorPosition(0);
1627     QTest::mouseClick(&view, Qt::LeftButton, 0, textEditObject->positionToRectangle(5).center().toPoint());
1628     QTest::qWait(50);
1629     QTRY_VERIFY(textEditObject->cursorPosition() != 0);
1630     QCOMPARE(textEditObject->cursorRectangle().x(), qRound(delegateObject->x()));
1631     QCOMPARE(textEditObject->cursorRectangle().y(), qRound(delegateObject->y()));
1632
1633     textEditObject->setCursorPosition(0);
1634     QTest::mouseClick(&view, Qt::LeftButton, 0, textEditObject->positionToRectangle(5).center().toPoint());
1635     QTest::qWait(50);
1636     QTRY_VERIFY(textEditObject->cursorPosition() != 0);
1637     QCOMPARE(textEditObject->cursorRectangle().x(), qRound(delegateObject->x()));
1638     QCOMPARE(textEditObject->cursorRectangle().y(), qRound(delegateObject->y()));
1639
1640     textEditObject->setCursorPosition(0);
1641     QCOMPARE(textEditObject->cursorRectangle().x(), qRound(delegateObject->x()));
1642     QCOMPARE(textEditObject->cursorRectangle().y(), qRound(delegateObject->y()));
1643     //Test Delegate gets deleted
1644     textEditObject->setCursorDelegate(0);
1645     QVERIFY(!textEditObject->findChild<QSGItem*>("cursorInstance"));
1646 }
1647
1648 void tst_qsgtextedit::cursorVisible()
1649 {
1650     QSGView view(QUrl::fromLocalFile(TESTDATA("cursorVisible.qml")));
1651     view.show();
1652     view.requestActivateWindow();
1653     QTest::qWaitForWindowShown(&view);
1654     QTRY_COMPARE(&view, qGuiApp->focusWindow());
1655
1656     QSGTextEdit edit;
1657     QSignalSpy spy(&edit, SIGNAL(cursorVisibleChanged(bool)));
1658
1659     QCOMPARE(edit.isCursorVisible(), false);
1660
1661     edit.setCursorVisible(true);
1662     QCOMPARE(edit.isCursorVisible(), true);
1663     QCOMPARE(spy.count(), 1);
1664
1665     edit.setCursorVisible(false);
1666     QCOMPARE(edit.isCursorVisible(), false);
1667     QCOMPARE(spy.count(), 2);
1668
1669     edit.setFocus(true);
1670     QCOMPARE(edit.isCursorVisible(), false);
1671     QCOMPARE(spy.count(), 2);
1672
1673     edit.setParentItem(view.rootObject());
1674     QCOMPARE(edit.isCursorVisible(), true);
1675     QCOMPARE(spy.count(), 3);
1676
1677     edit.setFocus(false);
1678     QCOMPARE(edit.isCursorVisible(), false);
1679     QCOMPARE(spy.count(), 4);
1680
1681     edit.setFocus(true);
1682     QCOMPARE(edit.isCursorVisible(), true);
1683     QCOMPARE(spy.count(), 5);
1684
1685     QEXPECT_FAIL("", "Most likely a side-effect of QTBUG-21489", Abort);
1686     view.setWindowState(Qt::WindowNoState);
1687     QCOMPARE(edit.isCursorVisible(), false);
1688     QCOMPARE(spy.count(), 6);
1689
1690     view.requestActivateWindow();
1691     QCOMPARE(edit.isCursorVisible(), true);
1692     QCOMPARE(spy.count(), 7);
1693
1694     // on mac, setActiveWindow(0) on mac does not deactivate the current application
1695     // (you have to switch to a different app or hide the current app to trigger this)
1696 #if !defined(Q_WS_MAC)
1697     // on mac, setActiveWindow(0) on mac does not deactivate the current application
1698     // (you have to switch to a different app or hide the current app to trigger this)
1699 //    QApplication::setActiveWindow(0);
1700 //    QTRY_COMPARE(QApplication::focusWindow(), static_cast<QWidget *>(0));
1701 //    QCOMPARE(edit.isCursorVisible(), false);
1702 //    QCOMPARE(spy.count(), 8);
1703
1704 //    view.requestActivateWindow();
1705 //    QTest::qWaitForWindowShown(&view);
1706 //    QTRY_COMPARE(view.windowState(), Qt::WindowActive);
1707 //    QCOMPARE(edit.isCursorVisible(), true);
1708 //    QCOMPARE(spy.count(), 9);
1709 #endif
1710 }
1711
1712 void tst_qsgtextedit::delegateLoading_data()
1713 {
1714     QTest::addColumn<QString>("qmlfile");
1715     QTest::addColumn<QString>("error");
1716
1717     // import installed
1718     QTest::newRow("pass") << "cursorHttpTestPass.qml" << "";
1719     QTest::newRow("fail1") << "cursorHttpTestFail1.qml" << "http://localhost:42332/FailItem.qml: Remote host closed the connection ";
1720     QTest::newRow("fail2") << "cursorHttpTestFail2.qml" << "http://localhost:42332/ErrItem.qml:4:5: Fungus is not a type ";
1721 }
1722
1723 void tst_qsgtextedit::delegateLoading()
1724 {
1725     QFETCH(QString, qmlfile);
1726     QFETCH(QString, error);
1727
1728     TestHTTPServer server(42332);
1729     server.serveDirectory(TESTDATA("httpfail"), TestHTTPServer::Disconnect);
1730     server.serveDirectory(TESTDATA("httpslow"), TestHTTPServer::Delay);
1731     server.serveDirectory(TESTDATA("http"));
1732
1733     QSGView view(QUrl(QLatin1String("http://localhost:42332/") + qmlfile));
1734     view.show();
1735     view.requestActivateWindow();
1736
1737     if (!error.isEmpty()) {
1738         QTest::ignoreMessage(QtWarningMsg, error.toUtf8());
1739         QTRY_VERIFY(view.status()==QSGView::Error);
1740         QTRY_VERIFY(!view.rootObject()); // there is fail item inside this test
1741     } else {
1742         QTRY_VERIFY(view.rootObject());//Wait for loading to finish.
1743         QSGTextEdit *textEditObject = view.rootObject()->findChild<QSGTextEdit*>("textEditObject");
1744         //    view.rootObject()->dumpObjectTree();
1745         QVERIFY(textEditObject != 0);
1746         textEditObject->setFocus(true);
1747         QSGItem *delegate;
1748         delegate = view.rootObject()->findChild<QSGItem*>("delegateOkay");
1749         QVERIFY(delegate);
1750         delegate = view.rootObject()->findChild<QSGItem*>("delegateSlow");
1751         QVERIFY(delegate);
1752
1753         delete delegate;
1754     }
1755
1756
1757     //A test should be added here with a component which is ready but component.create() returns null
1758     //Not sure how to accomplish this with QSGTextEdits cursor delegate
1759     //###This was only needed for code coverage, and could be a case of overzealous defensive programming
1760     //delegate = view.rootObject()->findChild<QSGItem*>("delegateErrorB");
1761     //QVERIFY(!delegate);
1762 }
1763
1764 /*
1765 TextEdit element should only handle left/right keys until the cursor reaches
1766 the extent of the text, then they should ignore the keys.
1767 */
1768 void tst_qsgtextedit::navigation()
1769 {
1770     QSGView canvas(QUrl::fromLocalFile(TESTDATA("navigation.qml")));
1771     canvas.show();
1772     canvas.requestActivateWindow();
1773
1774     QVERIFY(canvas.rootObject() != 0);
1775
1776     QSGItem *input = qobject_cast<QSGItem *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
1777
1778     QVERIFY(input != 0);
1779     QTRY_VERIFY(input->hasActiveFocus() == true);
1780     simulateKey(&canvas, Qt::Key_Left);
1781     QVERIFY(input->hasActiveFocus() == false);
1782     simulateKey(&canvas, Qt::Key_Right);
1783     QVERIFY(input->hasActiveFocus() == true);
1784     simulateKey(&canvas, Qt::Key_Right);
1785     QVERIFY(input->hasActiveFocus() == true);
1786     simulateKey(&canvas, Qt::Key_Right);
1787     QVERIFY(input->hasActiveFocus() == false);
1788     simulateKey(&canvas, Qt::Key_Left);
1789     QVERIFY(input->hasActiveFocus() == true);
1790 }
1791
1792 void tst_qsgtextedit::copyAndPaste() {
1793 #ifndef QT_NO_CLIPBOARD
1794
1795 #ifdef Q_WS_MAC
1796     {
1797         PasteboardRef pasteboard;
1798         OSStatus status = PasteboardCreate(0, &pasteboard);
1799         if (status == noErr)
1800             CFRelease(pasteboard);
1801         else
1802             QSKIP("This machine doesn't support the clipboard", SkipAll);
1803     }
1804 #endif
1805
1806     QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"Hello world!\" }";
1807     QDeclarativeComponent textEditComponent(&engine);
1808     textEditComponent.setData(componentStr.toLatin1(), QUrl());
1809     QSGTextEdit *textEdit = qobject_cast<QSGTextEdit*>(textEditComponent.create());
1810     QVERIFY(textEdit != 0);
1811
1812     // copy and paste
1813     QCOMPARE(textEdit->text().length(), 12);
1814     textEdit->select(0, textEdit->text().length());;
1815     textEdit->copy();
1816     QCOMPARE(textEdit->selectedText(), QString("Hello world!"));
1817     QCOMPARE(textEdit->selectedText().length(), 12);
1818     textEdit->setCursorPosition(0);
1819     QVERIFY(textEdit->canPaste());
1820     textEdit->paste();
1821     QCOMPARE(textEdit->text(), QString("Hello world!Hello world!"));
1822     QCOMPARE(textEdit->text().length(), 24);
1823
1824     // canPaste
1825     QVERIFY(textEdit->canPaste());
1826     textEdit->setReadOnly(true);
1827     QVERIFY(!textEdit->canPaste());
1828     textEdit->setReadOnly(false);
1829     QVERIFY(textEdit->canPaste());
1830
1831     // QTBUG-12339
1832     // test that document and internal text attribute are in sync
1833     QSGItemPrivate* pri = QSGItemPrivate::get(textEdit);
1834     QSGTextEditPrivate *editPrivate = static_cast<QSGTextEditPrivate*>(pri);
1835     QCOMPARE(textEdit->text(), editPrivate->text);
1836
1837     // select word
1838     textEdit->setCursorPosition(0);
1839     textEdit->selectWord();
1840     QCOMPARE(textEdit->selectedText(), QString("Hello"));
1841
1842     // select all and cut
1843     textEdit->selectAll();
1844     textEdit->cut();
1845     QCOMPARE(textEdit->text().length(), 0);
1846     textEdit->paste();
1847     QCOMPARE(textEdit->text(), QString("Hello world!Hello world!"));
1848     QCOMPARE(textEdit->text().length(), 24);
1849 #endif
1850 }
1851
1852 void tst_qsgtextedit::canPaste() {
1853 #ifndef QT_NO_CLIPBOARD
1854
1855     QGuiApplication::clipboard()->setText("Some text");
1856
1857     QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"Hello world!\" }";
1858     QDeclarativeComponent textEditComponent(&engine);
1859     textEditComponent.setData(componentStr.toLatin1(), QUrl());
1860     QSGTextEdit *textEdit = qobject_cast<QSGTextEdit*>(textEditComponent.create());
1861     QVERIFY(textEdit != 0);
1862
1863     // check initial value - QTBUG-17765
1864     QTextControl tc;
1865     QCOMPARE(textEdit->canPaste(), tc.canPaste());
1866
1867 #endif
1868 }
1869
1870 void tst_qsgtextedit::canPasteEmpty() {
1871 #ifndef QT_NO_CLIPBOARD
1872
1873     QGuiApplication::clipboard()->clear();
1874
1875     QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"Hello world!\" }";
1876     QDeclarativeComponent textEditComponent(&engine);
1877     textEditComponent.setData(componentStr.toLatin1(), QUrl());
1878     QSGTextEdit *textEdit = qobject_cast<QSGTextEdit*>(textEditComponent.create());
1879     QVERIFY(textEdit != 0);
1880
1881     // check initial value - QTBUG-17765
1882     QTextControl tc;
1883     QCOMPARE(textEdit->canPaste(), tc.canPaste());
1884
1885 #endif
1886 }
1887
1888 void tst_qsgtextedit::readOnly()
1889 {
1890     QSGView canvas(QUrl::fromLocalFile(TESTDATA("readOnly.qml")));
1891     canvas.show();
1892     canvas.requestActivateWindow();
1893
1894     QVERIFY(canvas.rootObject() != 0);
1895
1896     QSGTextEdit *edit = qobject_cast<QSGTextEdit *>(qvariant_cast<QObject *>(canvas.rootObject()->property("myInput")));
1897
1898     QVERIFY(edit != 0);
1899     QTRY_VERIFY(edit->hasActiveFocus() == true);
1900     QVERIFY(edit->isReadOnly() == true);
1901     QString initial = edit->text();
1902     for (int k=Qt::Key_0; k<=Qt::Key_Z; k++)
1903         simulateKey(&canvas, k);
1904     simulateKey(&canvas, Qt::Key_Return);
1905     simulateKey(&canvas, Qt::Key_Space);
1906     simulateKey(&canvas, Qt::Key_Escape);
1907     QCOMPARE(edit->text(), initial);
1908
1909     edit->setCursorPosition(3);
1910     edit->setReadOnly(false);
1911     QCOMPARE(edit->isReadOnly(), false);
1912     QCOMPARE(edit->cursorPosition(), edit->text().length());
1913 }
1914
1915 void tst_qsgtextedit::simulateKey(QSGView *view, int key, Qt::KeyboardModifiers modifiers)
1916 {
1917     QKeyEvent press(QKeyEvent::KeyPress, key, modifiers);
1918     QKeyEvent release(QKeyEvent::KeyRelease, key, modifiers);
1919
1920     QGuiApplication::sendEvent(view, &press);
1921     QGuiApplication::sendEvent(view, &release);
1922 }
1923
1924
1925 #ifndef QTBUG_21691
1926 class MyInputContext : public QInputContext
1927 {
1928 public:
1929     MyInputContext() : updateReceived(false), eventType(QEvent::None) {}
1930     ~MyInputContext() {}
1931
1932     QString identifierName() { return QString(); }
1933     QString language() { return QString(); }
1934
1935     void reset() {}
1936
1937     bool isComposing() const { return false; }
1938
1939     void update() { updateReceived = true; }
1940
1941     void sendPreeditText(const QString &text, int cursor)
1942     {
1943         QList<QInputMethodEvent::Attribute> attributes;
1944         attributes.append(QInputMethodEvent::Attribute(
1945                 QInputMethodEvent::Cursor, cursor, text.length(), QVariant()));
1946
1947         QInputMethodEvent event(text, attributes);
1948         sendEvent(event);
1949     }
1950
1951     void mouseHandler(int x, QMouseEvent *event)
1952     {
1953         cursor = x;
1954         eventType = event->type();
1955         eventPosition = event->pos();
1956         eventGlobalPosition = event->globalPos();
1957         eventButton = event->button();
1958         eventButtons = event->buttons();
1959         eventModifiers = event->modifiers();
1960     }
1961
1962     bool updateReceived;
1963     int cursor;
1964     QEvent::Type eventType;
1965     QPoint eventPosition;
1966     QPoint eventGlobalPosition;
1967     Qt::MouseButton eventButton;
1968     Qt::MouseButtons eventButtons;
1969     Qt::KeyboardModifiers eventModifiers;
1970 };
1971 #endif
1972
1973 void tst_qsgtextedit::textInput()
1974 {
1975     QSGView view(QUrl::fromLocalFile(TESTDATA("inputMethodEvent.qml")));
1976     view.show();
1977     view.requestActivateWindow();
1978     QTest::qWaitForWindowShown(&view);
1979     QTRY_COMPARE(&view, qGuiApp->focusWindow());
1980     QSGTextEdit *edit = qobject_cast<QSGTextEdit *>(view.rootObject());
1981     QVERIFY(edit);
1982     QVERIFY(edit->hasActiveFocus() == true);
1983
1984     // test that input method event is committed
1985     QInputMethodEvent event;
1986     event.setCommitString( "Hello world!", 0, 0);
1987     QGuiApplication::sendEvent(&view, &event);
1988     QEXPECT_FAIL("", "QTBUG-21689", Abort);
1989     QCOMPARE(edit->text(), QString("Hello world!"));
1990
1991     // QTBUG-12339
1992     // test that document and internal text attribute are in sync
1993     QSGTextEditPrivate *editPrivate = static_cast<QSGTextEditPrivate*>(QSGItemPrivate::get(edit));
1994     QCOMPARE(editPrivate->text, QString("Hello world!"));
1995 }
1996
1997 void tst_qsgtextedit::openInputPanel()
1998 {
1999     QSGView view(QUrl::fromLocalFile(TESTDATA("openInputPanel.qml")));
2000     view.show();
2001     view.requestActivateWindow();
2002     QTest::qWaitForWindowShown(&view);
2003     QTRY_COMPARE(&view, qGuiApp->focusWindow());
2004
2005     QSGTextEdit *edit = qobject_cast<QSGTextEdit *>(view.rootObject());
2006     QVERIFY(edit);
2007
2008     // check default values
2009     QVERIFY(edit->focusOnPress());
2010     QVERIFY(!edit->hasActiveFocus());
2011     qDebug() << &edit << qApp->inputPanel()->inputItem();
2012     QCOMPARE(qApp->inputPanel()->inputItem(), static_cast<QObject*>(0));
2013     QEXPECT_FAIL("", "QTBUG-21946", Abort);
2014     QCOMPARE(qApp->inputPanel()->visible(), false);
2015
2016     // input panel should open on focus
2017     QPoint centerPoint(view.width()/2, view.height()/2);
2018     Qt::KeyboardModifiers noModifiers = 0;
2019     QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
2020     QGuiApplication::processEvents();
2021     QVERIFY(edit->hasActiveFocus());
2022     QCOMPARE(qApp->inputPanel()->inputItem(), edit);
2023     QCOMPARE(qApp->inputPanel()->visible(), true);
2024     QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
2025
2026     // input panel should be re-opened when pressing already focused TextEdit
2027     qApp->inputPanel()->hide();
2028     QCOMPARE(qApp->inputPanel()->visible(), false);
2029     QVERIFY(edit->hasActiveFocus());
2030     QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
2031     QGuiApplication::processEvents();
2032     QCOMPARE(qApp->inputPanel()->visible(), true);
2033     QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
2034
2035     // input panel should stay visible if focus is lost to another text editor
2036     QSignalSpy inputPanelVisibilitySpy(qApp->inputPanel(), SIGNAL(visibleChanged()));
2037     QSGTextEdit anotherEdit;
2038     anotherEdit.setParentItem(view.rootObject());
2039     anotherEdit.setFocus(true);
2040     QCOMPARE(qApp->inputPanel()->visible(), true);
2041     QCOMPARE(qApp->inputPanel()->inputItem(), qobject_cast<QObject*>(&anotherEdit));
2042     QCOMPARE(inputPanelVisibilitySpy.count(), 0);
2043
2044     anotherEdit.setFocus(false);
2045     QCOMPARE(qApp->inputPanel()->inputItem(), static_cast<QObject*>(0));
2046     QCOMPARE(view.activeFocusItem(), view.rootItem());
2047     anotherEdit.setFocus(true);
2048
2049     // input item should be null if focus is lost to an item that doesn't accept inputs
2050     QSGItem item;
2051     item.setParentItem(view.rootObject());
2052     item.setFocus(true);
2053     QCOMPARE(qApp->inputPanel()->inputItem(), static_cast<QObject*>(0));
2054     QCOMPARE(view.activeFocusItem(), &item);
2055
2056     qApp->inputPanel()->hide();
2057
2058     // input panel should not be opened if TextEdit is read only
2059     edit->setReadOnly(true);
2060     edit->setFocus(true);
2061     QCOMPARE(qApp->inputPanel()->visible(), false);
2062     QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
2063     QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
2064     QGuiApplication::processEvents();
2065     QCOMPARE(qApp->inputPanel()->visible(), false);
2066
2067     // input panel should not be opened if focusOnPress is set to false
2068     edit->setFocusOnPress(false);
2069     edit->setFocus(false);
2070     edit->setFocus(true);
2071     QCOMPARE(qApp->inputPanel()->visible(), false);
2072     QTest::mousePress(&view, Qt::LeftButton, noModifiers, centerPoint);
2073     QTest::mouseRelease(&view, Qt::LeftButton, noModifiers, centerPoint);
2074     QCOMPARE(qApp->inputPanel()->visible(), false);
2075
2076     // input panel should open when openSoftwareInputPanel is called
2077     edit->openSoftwareInputPanel();
2078     QCOMPARE(qApp->inputPanel()->visible(), true);
2079
2080     // input panel should close when closeSoftwareInputPanel is called
2081     edit->closeSoftwareInputPanel();
2082     QCOMPARE(qApp->inputPanel()->visible(), false);
2083 }
2084
2085 void tst_qsgtextedit::geometrySignals()
2086 {
2087     QDeclarativeComponent component(&engine, TESTDATA("geometrySignals.qml"));
2088     QObject *o = component.create();
2089     QVERIFY(o);
2090     QCOMPARE(o->property("bindingWidth").toInt(), 400);
2091     QCOMPARE(o->property("bindingHeight").toInt(), 500);
2092     delete o;
2093 }
2094
2095 void tst_qsgtextedit::pastingRichText_QTBUG_14003()
2096 {
2097 #ifndef QT_NO_CLIPBOARD
2098     QString componentStr = "import QtQuick 2.0\nTextEdit { textFormat: TextEdit.PlainText }";
2099     QDeclarativeComponent component(&engine);
2100     component.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
2101     QSGTextEdit *obj = qobject_cast<QSGTextEdit*>(component.create());
2102
2103     QTRY_VERIFY(obj != 0);
2104     QTRY_VERIFY(obj->textFormat() == QSGTextEdit::PlainText);
2105
2106     QMimeData *mData = new QMimeData;
2107     mData->setHtml("<font color=\"red\">Hello</font>");
2108     QGuiApplication::clipboard()->setMimeData(mData);
2109
2110     obj->paste();
2111     QTRY_VERIFY(obj->text() == "");
2112     QTRY_VERIFY(obj->textFormat() == QSGTextEdit::PlainText);
2113 #endif
2114 }
2115
2116 void tst_qsgtextedit::implicitSize_data()
2117 {
2118     QTest::addColumn<QString>("text");
2119     QTest::addColumn<QString>("wrap");
2120     QTest::newRow("plain") << "The quick red fox jumped over the lazy brown dog" << "TextEdit.NoWrap";
2121     QTest::newRow("richtext") << "<b>The quick red fox jumped over the lazy brown dog</b>" << "TextEdit.NoWrap";
2122     QTest::newRow("plain_wrap") << "The quick red fox jumped over the lazy brown dog" << "TextEdit.Wrap";
2123     QTest::newRow("richtext_wrap") << "<b>The quick red fox jumped over the lazy brown dog</b>" << "TextEdit.Wrap";
2124 }
2125
2126 void tst_qsgtextedit::implicitSize()
2127 {
2128     QFETCH(QString, text);
2129     QFETCH(QString, wrap);
2130     QString componentStr = "import QtQuick 2.0\nTextEdit { text: \"" + text + "\"; width: 50; wrapMode: " + wrap + " }";
2131     QDeclarativeComponent textComponent(&engine);
2132     textComponent.setData(componentStr.toLatin1(), QUrl::fromLocalFile(""));
2133     QSGTextEdit *textObject = qobject_cast<QSGTextEdit*>(textComponent.create());
2134
2135     QVERIFY(textObject->width() < textObject->implicitWidth());
2136     QVERIFY(textObject->height() == textObject->implicitHeight());
2137
2138     textObject->resetWidth();
2139     QVERIFY(textObject->width() == textObject->implicitWidth());
2140     QVERIFY(textObject->height() == textObject->implicitHeight());
2141 }
2142
2143 void tst_qsgtextedit::testQtQuick11Attributes()
2144 {
2145     QFETCH(QString, code);
2146     QFETCH(QString, warning);
2147     QFETCH(QString, error);
2148
2149     QDeclarativeEngine engine;
2150     QObject *obj;
2151
2152     QDeclarativeComponent valid(&engine);
2153     valid.setData("import QtQuick 2.0; TextEdit { " + code.toUtf8() + " }", QUrl(""));
2154     obj = valid.create();
2155     QVERIFY(obj);
2156     QVERIFY(valid.errorString().isEmpty());
2157     delete obj;
2158
2159     QDeclarativeComponent invalid(&engine);
2160     invalid.setData("import QtQuick 1.0; TextEdit { " + code.toUtf8() + " }", QUrl(""));
2161     QTest::ignoreMessage(QtWarningMsg, warning.toUtf8());
2162     obj = invalid.create();
2163     QCOMPARE(invalid.errorString(), error);
2164     delete obj;
2165 }
2166
2167 void tst_qsgtextedit::testQtQuick11Attributes_data()
2168 {
2169     QTest::addColumn<QString>("code");
2170     QTest::addColumn<QString>("warning");
2171     QTest::addColumn<QString>("error");
2172
2173     QTest::newRow("canPaste") << "property bool foo: canPaste"
2174         << "<Unknown File>:1: ReferenceError: Can't find variable: canPaste"
2175         << "";
2176
2177     QTest::newRow("lineCount") << "property int foo: lineCount"
2178         << "<Unknown File>:1: ReferenceError: Can't find variable: lineCount"
2179         << "";
2180
2181     QTest::newRow("moveCursorSelection") << "Component.onCompleted: moveCursorSelection(0, TextEdit.SelectCharacters)"
2182         << "<Unknown File>:1: ReferenceError: Can't find variable: moveCursorSelection"
2183         << "";
2184
2185     QTest::newRow("deselect") << "Component.onCompleted: deselect()"
2186         << "<Unknown File>:1: ReferenceError: Can't find variable: deselect"
2187         << "";
2188
2189     QTest::newRow("onLinkActivated") << "onLinkActivated: {}"
2190         << "QDeclarativeComponent: Component is not ready"
2191         << ":1 \"TextEdit.onLinkActivated\" is not available in QtQuick 1.0.\n";
2192 }
2193
2194 void tst_qsgtextedit::preeditMicroFocus()
2195 {
2196 #ifdef QTBUG_21691
2197     QEXPECT_FAIL("", QTBUG_21691_MESSAGE, Abort);
2198     QVERIFY(false);
2199 #else
2200     QString preeditText = "super";
2201
2202     QSGView view(QUrl::fromLocalFile(TESTDATA("inputMethodEvent.qml")));
2203     view.show();
2204     view.requestActivateWindow();
2205     QTest::qWaitForWindowShown(&view);
2206
2207     QTRY_COMPARE(&view, qGuiApp->focusWindow());
2208     QSGTextEdit *edit = qobject_cast<QSGTextEdit *>(view.rootObject());
2209     QVERIFY(edit);
2210
2211     QSignalSpy cursorRectangleSpy(edit, SIGNAL(cursorRectangleChanged()));
2212
2213     QRect currentRect;
2214     QRect previousRect = edit->inputMethodQuery(Qt::ImCursorRectangle).toRect();
2215
2216     // Verify that the micro focus rect is positioned the same for position 0 as
2217     // it would be if there was no preedit text.
2218     ic.updateReceived = false;
2219     ic.sendPreeditText(preeditText, 0);
2220     currentRect = edit->inputMethodQuery(Qt::ImCursorRectangle).toRect();
2221     QCOMPARE(currentRect, previousRect);
2222 #if defined(Q_WS_X11) || defined(Q_WS_QWS)
2223     QCOMPARE(ic.updateReceived, false); // The cursor position hasn't changed.
2224 #endif
2225     QCOMPARE(cursorRectangleSpy.count(), 0);
2226
2227     // Verify that the micro focus rect moves to the left as the cursor position
2228     // is incremented.
2229     for (int i = 1; i <= 5; ++i) {
2230         ic.updateReceived = false;
2231         ic.sendPreeditText(preeditText, i);
2232         currentRect = edit->inputMethodQuery(Qt::ImCursorRectangle).toRect();
2233         QVERIFY(previousRect.left() < currentRect.left());
2234 #if defined(Q_WS_X11) || defined(Q_WS_QWS)
2235         QCOMPARE(ic.updateReceived, true);
2236 #endif
2237         QVERIFY(cursorRectangleSpy.count() > 0);
2238         cursorRectangleSpy.clear();
2239         previousRect = currentRect;
2240     }
2241
2242     // Verify that if there is no preedit cursor then the micro focus rect is the
2243     // same as it would be if it were positioned at the end of the preedit text.
2244     ic.sendPreeditText(preeditText, 0);
2245     ic.updateReceived = false;
2246     ic.sendEvent(QInputMethodEvent(preeditText, QList<QInputMethodEvent::Attribute>()));
2247     currentRect = edit->inputMethodQuery(Qt::ImCursorRectangle).toRect();
2248     QCOMPARE(currentRect, previousRect);
2249 #if defined(Q_WS_X11) || defined(Q_WS_QWS)
2250     QCOMPARE(ic.updateReceived, true);
2251 #endif
2252     QVERIFY(cursorRectangleSpy.count() > 0);
2253 #endif
2254 }
2255
2256 void tst_qsgtextedit::inputContextMouseHandler()
2257 {
2258
2259 #ifdef QTBUG_21691
2260     QEXPECT_FAIL("", QTBUG_21691_MESSAGE, Abort);
2261     QVERIFY(false);
2262 #else
2263     QString text = "supercalifragisiticexpialidocious!";
2264
2265     QSGView view(QUrl::fromLocalFile(TESTDATA("inputContext.qml")));
2266     MyInputContext ic;
2267     // QSGCanvas won't set the Qt::WA_InputMethodEnabled flag unless a suitable item has focus
2268     // and QWidget won't allow an input context to be set when the flag is not set.
2269     view.setAttribute(Qt::WA_InputMethodEnabled, true);
2270     view.setInputContext(&ic);
2271     view.setAttribute(Qt::WA_InputMethodEnabled, false);
2272     view.show();
2273     view.requestActivateWindow();
2274     QTest::qWaitForWindowShown(&view);
2275
2276     QTRY_COMPARE(&view, qGuiApp->focusWindow());
2277     QSGTextEdit *edit = qobject_cast<QSGTextEdit *>(view.rootObject());
2278     QVERIFY(edit);
2279     edit->setCursorPosition(12);
2280
2281     QFontMetricsF fm(edit->font());
2282     const qreal y = fm.height() / 2;
2283
2284     QPoint position2 = edit->mapToScene(QPointF(fm.width(text.mid(0, 2)), y)).toPoint();
2285     QPoint position8 = edit->mapToScene(QPointF(fm.width(text.mid(0, 8)), y)).toPoint();
2286     QPoint position20 = edit->mapToScene(QPointF(fm.width(text.mid(0, 20)), y)).toPoint();
2287     QPoint position27 = edit->mapToScene(QPointF(fm.width(text.mid(0, 27)), y)).toPoint();
2288     QPoint globalPosition2 = view.mapToGlobal(position2);
2289     QPoint globalposition8 = view.mapToGlobal(position8);
2290     QPoint globalposition20 = view.mapToGlobal(position20);
2291     QPoint globalposition27 = view.mapToGlobal(position27);
2292
2293     ic.sendEvent(QInputMethodEvent(text.mid(12), QList<QInputMethodEvent::Attribute>()));
2294
2295     QTest::mouseDClick(&view, Qt::LeftButton, Qt::NoModifier, position2);
2296     QCOMPARE(ic.eventType, QEvent::MouseButtonDblClick);
2297     QCOMPARE(ic.eventPosition, position2);
2298     QCOMPARE(ic.eventGlobalPosition, globalPosition2);
2299     QCOMPARE(ic.eventButton, Qt::LeftButton);
2300     QCOMPARE(ic.eventModifiers, Qt::NoModifier);
2301     QVERIFY(ic.cursor < 0);
2302     ic.eventType = QEvent::None;
2303
2304     QTest::mousePress(&view, Qt::LeftButton, Qt::NoModifier, position2);
2305     QCOMPARE(ic.eventType, QEvent::MouseButtonPress);
2306     QCOMPARE(ic.eventPosition, position2);
2307     QCOMPARE(ic.eventGlobalPosition, globalPosition2);
2308     QCOMPARE(ic.eventButton, Qt::LeftButton);
2309     QCOMPARE(ic.eventModifiers, Qt::NoModifier);
2310     QVERIFY(ic.cursor < 0);
2311     ic.eventType = QEvent::None;
2312
2313     {   QMouseEvent mv(QEvent::MouseMove, position8, globalposition8, Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
2314         QGuiApplication::sendEvent(&view, &mv); }
2315     QCOMPARE(ic.eventType, QEvent::None);
2316
2317     {   QMouseEvent mv(QEvent::MouseMove, position27, globalposition27, Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
2318         QGuiApplication::sendEvent(&view, &mv); }
2319     QCOMPARE(ic.eventType, QEvent::MouseMove);
2320     QCOMPARE(ic.eventPosition, position27);
2321         QCOMPARE(ic.eventGlobalPosition, globalposition27);
2322     QCOMPARE(ic.eventButton, Qt::LeftButton);
2323     QCOMPARE(ic.eventModifiers, Qt::NoModifier);
2324     QVERIFY(ic.cursor >= 14 && ic.cursor <= 16);    // 15 is expected but some platforms may be off by one.
2325     ic.eventType = QEvent::None;
2326
2327     QTest::mouseRelease(&view, Qt::LeftButton, Qt::NoModifier, position27);
2328     QCOMPARE(ic.eventType, QEvent::MouseButtonRelease);
2329     QCOMPARE(ic.eventPosition, position27);
2330     QCOMPARE(ic.eventGlobalPosition, globalposition27);
2331     QCOMPARE(ic.eventButton, Qt::LeftButton);
2332     QCOMPARE(ic.eventModifiers, Qt::NoModifier);
2333     QVERIFY(ic.cursor >= 14 && ic.cursor <= 16);
2334     ic.eventType = QEvent::None;
2335
2336     // And in the other direction.
2337     QTest::mouseDClick(&view, Qt::LeftButton, Qt::ControlModifier, position27);
2338     QCOMPARE(ic.eventType, QEvent::MouseButtonDblClick);
2339     QCOMPARE(ic.eventPosition, position27);
2340     QCOMPARE(ic.eventGlobalPosition, globalposition27);
2341     QCOMPARE(ic.eventButton, Qt::LeftButton);
2342     QCOMPARE(ic.eventModifiers, Qt::ControlModifier);
2343     QVERIFY(ic.cursor >= 14 && ic.cursor <= 16);
2344     ic.eventType = QEvent::None;
2345
2346     QTest::mousePress(&view, Qt::RightButton, Qt::ControlModifier, position27);
2347     QCOMPARE(ic.eventType, QEvent::MouseButtonPress);
2348     QCOMPARE(ic.eventPosition, position27);
2349     QCOMPARE(ic.eventGlobalPosition, globalposition27);
2350     QCOMPARE(ic.eventButton, Qt::RightButton);
2351     QCOMPARE(ic.eventModifiers, Qt::ControlModifier);
2352     QVERIFY(ic.cursor >= 14 && ic.cursor <= 16);
2353     ic.eventType = QEvent::None;
2354
2355     {   QMouseEvent mv(QEvent::MouseMove, position20, globalposition20, Qt::RightButton, Qt::RightButton,Qt::ControlModifier);
2356         QGuiApplication::sendEvent(&view, &mv); }
2357     QCOMPARE(ic.eventType, QEvent::MouseMove);
2358     QCOMPARE(ic.eventPosition, position20);
2359     QCOMPARE(ic.eventGlobalPosition, globalposition20);
2360     QCOMPARE(ic.eventButton, Qt::RightButton);
2361     QCOMPARE(ic.eventModifiers, Qt::ControlModifier);
2362     QVERIFY(ic.cursor >= 7 && ic.cursor <= 9);
2363     ic.eventType = QEvent::None;
2364
2365     {   QMouseEvent mv(QEvent::MouseMove, position2, globalPosition2, Qt::RightButton, Qt::RightButton,Qt::ControlModifier);
2366         QGuiApplication::sendEvent(&view, &mv); }
2367     QCOMPARE(ic.eventType, QEvent::None);
2368
2369     QTest::mouseRelease(&view, Qt::RightButton, Qt::ControlModifier, position2);
2370     QCOMPARE(ic.eventType, QEvent::MouseButtonRelease);
2371     QCOMPARE(ic.eventPosition, position2);
2372     QCOMPARE(ic.eventGlobalPosition, globalPosition2);
2373     QCOMPARE(ic.eventButton, Qt::RightButton);
2374     QCOMPARE(ic.eventModifiers, Qt::ControlModifier);
2375     QVERIFY(ic.cursor < 0);
2376     ic.eventType = QEvent::None;
2377 #endif
2378 }
2379
2380 void tst_qsgtextedit::inputMethodComposing()
2381 {
2382     QString text = "supercalifragisiticexpialidocious!";
2383
2384     QSGView view(QUrl::fromLocalFile(TESTDATA("inputContext.qml")));
2385     view.show();
2386     view.requestActivateWindow();
2387     QTest::qWaitForWindowShown(&view);
2388     QTRY_COMPARE(&view, qGuiApp->focusWindow());
2389     QSGTextEdit *edit = qobject_cast<QSGTextEdit *>(view.rootObject());
2390     QVERIFY(edit);
2391     QSignalSpy spy(edit, SIGNAL(inputMethodComposingChanged()));
2392     edit->setCursorPosition(12);
2393
2394     QCOMPARE(edit->isInputMethodComposing(), false);
2395
2396     {
2397         QInputMethodEvent event(text.mid(3), QList<QInputMethodEvent::Attribute>());
2398         QGuiApplication::sendEvent(edit, &event);
2399     }
2400
2401     QCOMPARE(edit->isInputMethodComposing(), true);
2402     QCOMPARE(spy.count(), 1);
2403
2404     {
2405         QInputMethodEvent event(text.mid(12), QList<QInputMethodEvent::Attribute>());
2406         QGuiApplication::sendEvent(edit, &event);
2407     }
2408     QCOMPARE(spy.count(), 1);
2409
2410     {
2411         QInputMethodEvent event;
2412         QGuiApplication::sendEvent(edit, &event);
2413     }
2414     QCOMPARE(edit->isInputMethodComposing(), false);
2415     QCOMPARE(spy.count(), 2);
2416 }
2417
2418 void tst_qsgtextedit::cursorRectangleSize()
2419 {
2420     QSGView *canvas = new QSGView(QUrl::fromLocalFile(TESTDATA("CursorRect.qml")));
2421     QVERIFY(canvas->rootObject() != 0);
2422     canvas->show();
2423     canvas->requestActivateWindow();
2424     QTest::qWaitForWindowShown(canvas);
2425
2426     QSGTextEdit *textEdit = qobject_cast<QSGTextEdit *>(canvas->rootObject());
2427     QVERIFY(textEdit != 0);
2428     textEdit->setFocus(Qt::OtherFocusReason);
2429     QRectF cursorRect = textEdit->positionToRectangle(textEdit->cursorPosition());
2430     QRectF microFocusFromScene = canvas->inputMethodQuery(Qt::ImCursorRectangle).toRectF();
2431     QInputMethodQueryEvent event(Qt::ImCursorRectangle);
2432     qApp->sendEvent(qApp->inputPanel()->inputItem(), &event);
2433
2434     QRectF microFocusFromApp = event.value(Qt::ImCursorRectangle).toRectF();
2435
2436     QCOMPARE(microFocusFromScene.size(), cursorRect.size());
2437     QCOMPARE(microFocusFromApp.size(), cursorRect.size());
2438
2439     delete canvas;
2440 }
2441
2442 QTEST_MAIN(tst_qsgtextedit)
2443
2444 #include "tst_qsgtextedit.moc"