1c0ed62f0da4265ab767822912ac768ca10d7329
[profile/ivi/qtdeclarative.git] / src / declarative / items / 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 QtDeclarative module 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
42 #include "qsgtextedit_p.h"
43 #include "qsgtextedit_p_p.h"
44 #include "qsgevents_p_p.h"
45 #include "qsgcanvas.h"
46 #include "qsgtextnode_p.h"
47 #include "qsgsimplerectnode.h"
48
49 #include <QtDeclarative/qdeclarativeinfo.h>
50 #include <QtWidgets/qapplication.h>
51 #include <QtWidgets/qgraphicssceneevent.h>
52 #include <QtGui/qpainter.h>
53 #include <QtGui/qtextobject.h>
54 #include <QtCore/qmath.h>
55
56 #include <private/qdeclarativeglobal_p.h>
57 #include <private/qtextcontrol_p.h>
58 #include <private/qtextengine_p.h>
59 #include <private/qwidget_p.h>
60 #include <private/qsgdistancefieldglyphcache_p.h>
61 #include <private/qsgtexture_p.h>
62 #include <private/qsgadaptationlayer_p.h>
63
64 QT_BEGIN_NAMESPACE
65
66 DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD)
67
68 QWidgetPrivate *qt_widget_private(QWidget *widget);
69 /*!
70     \qmlclass TextEdit QSGTextEdit
71     \inqmlmodule QtQuick 2
72     \ingroup qml-basic-visual-elements
73     \brief The TextEdit item displays multiple lines of editable formatted text.
74     \inherits Item
75
76     The TextEdit item displays a block of editable, formatted text.
77
78     It can display both plain and rich text. For example:
79
80     \qml
81 TextEdit {
82     width: 240
83     text: "<b>Hello</b> <i>World!</i>"
84     font.family: "Helvetica"
85     font.pointSize: 20
86     color: "blue"
87     focus: true
88 }
89     \endqml
90
91     \image declarative-textedit.gif
92
93     Setting \l {Item::focus}{focus} to \c true enables the TextEdit item to receive keyboard focus.
94
95     Note that the TextEdit does not implement scrolling, following the cursor, or other behaviors specific
96     to a look-and-feel. For example, to add flickable scrolling that follows the cursor:
97
98     \snippet snippets/declarative/texteditor.qml 0
99
100     A particular look-and-feel might use smooth scrolling (eg. using SmoothedFollow), might have a visible
101     scrollbar, or a scrollbar that fades in to show location, etc.
102
103     Clipboard support is provided by the cut(), copy(), and paste() functions, and the selection can
104     be handled in a traditional "mouse" mechanism by setting selectByMouse, or handled completely
105     from QML by manipulating selectionStart and selectionEnd, or using selectAll() or selectWord().
106
107     You can translate between cursor positions (characters from the start of the document) and pixel
108     points using positionAt() and positionToRectangle().
109
110     \sa Text, TextInput, {declarative/text/textselection}{Text Selection example}
111 */
112
113 /*!
114     \qmlsignal QtQuick2::TextEdit::onLinkActivated(string link)
115
116     This handler is called when the user clicks on a link embedded in the text.
117     The link must be in rich text or HTML format and the
118     \a link string provides access to the particular link.
119 */
120 QSGTextEdit::QSGTextEdit(QSGItem *parent)
121 : QSGImplicitSizeItem(*(new QSGTextEditPrivate), parent)
122 {
123     Q_D(QSGTextEdit);
124     d->init();
125 }
126
127 QString QSGTextEdit::text() const
128 {
129     Q_D(const QSGTextEdit);
130
131 #ifndef QT_NO_TEXTHTMLPARSER
132     if (d->richText)
133         return d->document->toHtml();
134     else
135 #endif
136         return d->document->toPlainText();
137 }
138
139 /*!
140     \qmlproperty string QtQuick2::TextEdit::font.family
141
142     Sets the family name of the font.
143
144     The family name is case insensitive and may optionally include a foundry name, e.g. "Helvetica [Cronyx]".
145     If the family is available from more than one foundry and the foundry isn't specified, an arbitrary foundry is chosen.
146     If the family isn't available a family will be set using the font matching algorithm.
147 */
148
149 /*!
150     \qmlproperty bool QtQuick2::TextEdit::font.bold
151
152     Sets whether the font weight is bold.
153 */
154
155 /*!
156     \qmlproperty enumeration QtQuick2::TextEdit::font.weight
157
158     Sets the font's weight.
159
160     The weight can be one of:
161     \list
162     \o Font.Light
163     \o Font.Normal - the default
164     \o Font.DemiBold
165     \o Font.Bold
166     \o Font.Black
167     \endlist
168
169     \qml
170     TextEdit { text: "Hello"; font.weight: Font.DemiBold }
171     \endqml
172 */
173
174 /*!
175     \qmlproperty bool QtQuick2::TextEdit::font.italic
176
177     Sets whether the font has an italic style.
178 */
179
180 /*!
181     \qmlproperty bool QtQuick2::TextEdit::font.underline
182
183     Sets whether the text is underlined.
184 */
185
186 /*!
187     \qmlproperty bool QtQuick2::TextEdit::font.strikeout
188
189     Sets whether the font has a strikeout style.
190 */
191
192 /*!
193     \qmlproperty real QtQuick2::TextEdit::font.pointSize
194
195     Sets the font size in points. The point size must be greater than zero.
196 */
197
198 /*!
199     \qmlproperty int QtQuick2::TextEdit::font.pixelSize
200
201     Sets the font size in pixels.
202
203     Using this function makes the font device dependent.  Use
204     \l{TextEdit::font.pointSize} to set the size of the font in a
205     device independent manner.
206 */
207
208 /*!
209     \qmlproperty real QtQuick2::TextEdit::font.letterSpacing
210
211     Sets the letter spacing for the font.
212
213     Letter spacing changes the default spacing between individual letters in the font.
214     A positive value increases the letter spacing by the corresponding pixels; a negative value decreases the spacing.
215 */
216
217 /*!
218     \qmlproperty real QtQuick2::TextEdit::font.wordSpacing
219
220     Sets the word spacing for the font.
221
222     Word spacing changes the default spacing between individual words.
223     A positive value increases the word spacing by a corresponding amount of pixels,
224     while a negative value decreases the inter-word spacing accordingly.
225 */
226
227 /*!
228     \qmlproperty enumeration QtQuick2::TextEdit::font.capitalization
229
230     Sets the capitalization for the text.
231
232     \list
233     \o Font.MixedCase - This is the normal text rendering option where no capitalization change is applied.
234     \o Font.AllUppercase - This alters the text to be rendered in all uppercase type.
235     \o Font.AllLowercase         - This alters the text to be rendered in all lowercase type.
236     \o Font.SmallCaps - This alters the text to be rendered in small-caps type.
237     \o Font.Capitalize - This alters the text to be rendered with the first character of each word as an uppercase character.
238     \endlist
239
240     \qml
241     TextEdit { text: "Hello"; font.capitalization: Font.AllLowercase }
242     \endqml
243 */
244
245 /*!
246     \qmlproperty string QtQuick2::TextEdit::text
247
248     The text to display.  If the text format is AutoText the text edit will
249     automatically determine whether the text should be treated as
250     rich text.  This determination is made using Qt::mightBeRichText().
251 */
252 void QSGTextEdit::setText(const QString &text)
253 {
254     Q_D(QSGTextEdit);
255     if (QSGTextEdit::text() == text)
256         return;
257
258     d->richText = d->format == RichText || (d->format == AutoText && Qt::mightBeRichText(text));
259     if (d->richText) {
260 #ifndef QT_NO_TEXTHTMLPARSER
261         d->control->setHtml(text);
262 #else
263         d->control->setPlainText(text);
264 #endif
265         d->isComplexRichText = QSGTextNode::isComplexRichText(d->document);
266     } else {
267         d->control->setPlainText(text);
268     }
269     q_textChanged();
270 }
271
272 /*!
273     \qmlproperty enumeration QtQuick2::TextEdit::textFormat
274
275     The way the text property should be displayed.
276
277     \list
278     \o TextEdit.AutoText
279     \o TextEdit.PlainText
280     \o TextEdit.RichText
281     \endlist
282
283     The default is TextEdit.AutoText.  If the text format is TextEdit.AutoText the text edit
284     will automatically determine whether the text should be treated as
285     rich text.  This determination is made using Qt::mightBeRichText().
286
287     \table
288     \row
289     \o
290     \qml
291 Column {
292     TextEdit {
293         font.pointSize: 24
294         text: "<b>Hello</b> <i>World!</i>"
295     }
296     TextEdit {
297         font.pointSize: 24
298         textFormat: TextEdit.RichText
299         text: "<b>Hello</b> <i>World!</i>"
300     }
301     TextEdit {
302         font.pointSize: 24
303         textFormat: TextEdit.PlainText
304         text: "<b>Hello</b> <i>World!</i>"
305     }
306 }
307     \endqml
308     \o \image declarative-textformat.png
309     \endtable
310 */
311 QSGTextEdit::TextFormat QSGTextEdit::textFormat() const
312 {
313     Q_D(const QSGTextEdit);
314     return d->format;
315 }
316
317 void QSGTextEdit::setTextFormat(TextFormat format)
318 {
319     Q_D(QSGTextEdit);
320     if (format == d->format)
321         return;
322     bool wasRich = d->richText;
323     d->richText = format == RichText || (format == AutoText && Qt::mightBeRichText(d->text));
324
325     if (wasRich && !d->richText) {
326         d->control->setPlainText(d->text);
327         updateSize();
328     } else if (!wasRich && d->richText) {
329 #ifndef QT_NO_TEXTHTMLPARSER
330         d->control->setHtml(d->text);
331 #else
332         d->control->setPlainText(d->text);
333 #endif
334         updateSize();
335         d->isComplexRichText = QSGTextNode::isComplexRichText(d->document);
336     }
337     d->format = format;
338     d->control->setAcceptRichText(d->format != PlainText);
339     emit textFormatChanged(d->format);
340 }
341
342 QFont QSGTextEdit::font() const
343 {
344     Q_D(const QSGTextEdit);
345     return d->sourceFont;
346 }
347
348 void QSGTextEdit::setFont(const QFont &font)
349 {
350     Q_D(QSGTextEdit);
351     if (d->sourceFont == font)
352         return;
353
354     d->sourceFont = font;
355     QFont oldFont = d->font;
356     d->font = font;
357     if (d->font.pointSizeF() != -1) {
358         // 0.5pt resolution
359         qreal size = qRound(d->font.pointSizeF()*2.0);
360         d->font.setPointSizeF(size/2.0);
361     }
362
363     if (oldFont != d->font) {
364         d->document->setDefaultFont(d->font);
365         if(d->cursor){
366             d->cursor->setHeight(QFontMetrics(d->font).height());
367             moveCursorDelegate();
368         }
369         updateSize();
370         updateDocument();
371     }
372     emit fontChanged(d->sourceFont);
373 }
374
375 /*!
376     \qmlproperty color QtQuick2::TextEdit::color
377
378     The text color.
379
380     \qml
381     // green text using hexadecimal notation
382     TextEdit { color: "#00FF00" }
383     \endqml
384
385     \qml
386     // steelblue text using SVG color name
387     TextEdit { color: "steelblue" }
388     \endqml
389 */
390 QColor QSGTextEdit::color() const
391 {
392     Q_D(const QSGTextEdit);
393     return d->color;
394 }
395
396 void QSGTextEdit::setColor(const QColor &color)
397 {
398     Q_D(QSGTextEdit);
399     if (d->color == color)
400         return;
401
402     d->color = color;
403     QPalette pal = d->control->palette();
404     pal.setColor(QPalette::Text, color);
405     d->control->setPalette(pal);
406     updateDocument();
407     emit colorChanged(d->color);
408 }
409
410 /*!
411     \qmlproperty color QtQuick2::TextEdit::selectionColor
412
413     The text highlight color, used behind selections.
414 */
415 QColor QSGTextEdit::selectionColor() const
416 {
417     Q_D(const QSGTextEdit);
418     return d->selectionColor;
419 }
420
421 void QSGTextEdit::setSelectionColor(const QColor &color)
422 {
423     Q_D(QSGTextEdit);
424     if (d->selectionColor == color)
425         return;
426
427     d->selectionColor = color;
428     QPalette pal = d->control->palette();
429     pal.setColor(QPalette::Highlight, color);
430     d->control->setPalette(pal);
431     updateDocument();
432     emit selectionColorChanged(d->selectionColor);
433 }
434
435 /*!
436     \qmlproperty color QtQuick2::TextEdit::selectedTextColor
437
438     The selected text color, used in selections.
439 */
440 QColor QSGTextEdit::selectedTextColor() const
441 {
442     Q_D(const QSGTextEdit);
443     return d->selectedTextColor;
444 }
445
446 void QSGTextEdit::setSelectedTextColor(const QColor &color)
447 {
448     Q_D(QSGTextEdit);
449     if (d->selectedTextColor == color)
450         return;
451
452     d->selectedTextColor = color;
453     QPalette pal = d->control->palette();
454     pal.setColor(QPalette::HighlightedText, color);
455     d->control->setPalette(pal);
456     updateDocument();
457     emit selectedTextColorChanged(d->selectedTextColor);
458 }
459
460 /*!
461     \qmlproperty enumeration QtQuick2::TextEdit::horizontalAlignment
462     \qmlproperty enumeration QtQuick2::TextEdit::verticalAlignment
463     \qmlproperty enumeration QtQuick2::TextEdit::effectiveHorizontalAlignment
464
465     Sets the horizontal and vertical alignment of the text within the TextEdit item's
466     width and height. By default, the text alignment follows the natural alignment
467     of the text, for example text that is read from left to right will be aligned to
468     the left.
469
470     Valid values for \c horizontalAlignment are:
471     \list
472     \o TextEdit.AlignLeft (default)
473     \o TextEdit.AlignRight
474     \o TextEdit.AlignHCenter
475     \o TextEdit.AlignJustify
476     \endlist
477
478     Valid values for \c verticalAlignment are:
479     \list
480     \o TextEdit.AlignTop (default)
481     \o TextEdit.AlignBottom
482     \o TextEdit.AlignVCenter
483     \endlist
484
485     When using the attached property LayoutMirroring::enabled to mirror application
486     layouts, the horizontal alignment of text will also be mirrored. However, the property
487     \c horizontalAlignment will remain unchanged. To query the effective horizontal alignment
488     of TextEdit, use the read-only property \c effectiveHorizontalAlignment.
489 */
490 QSGTextEdit::HAlignment QSGTextEdit::hAlign() const
491 {
492     Q_D(const QSGTextEdit);
493     return d->hAlign;
494 }
495
496 void QSGTextEdit::setHAlign(HAlignment align)
497 {
498     Q_D(QSGTextEdit);
499     bool forceAlign = d->hAlignImplicit && d->effectiveLayoutMirror;
500     d->hAlignImplicit = false;
501     if (d->setHAlign(align, forceAlign) && isComponentComplete()) {
502         d->updateDefaultTextOption();
503         updateSize();
504     }
505 }
506
507 void QSGTextEdit::resetHAlign()
508 {
509     Q_D(QSGTextEdit);
510     d->hAlignImplicit = true;
511     if (d->determineHorizontalAlignment() && isComponentComplete()) {
512         d->updateDefaultTextOption();
513         updateSize();
514     }
515 }
516
517 QSGTextEdit::HAlignment QSGTextEdit::effectiveHAlign() const
518 {
519     Q_D(const QSGTextEdit);
520     QSGTextEdit::HAlignment effectiveAlignment = d->hAlign;
521     if (!d->hAlignImplicit && d->effectiveLayoutMirror) {
522         switch (d->hAlign) {
523         case QSGTextEdit::AlignLeft:
524             effectiveAlignment = QSGTextEdit::AlignRight;
525             break;
526         case QSGTextEdit::AlignRight:
527             effectiveAlignment = QSGTextEdit::AlignLeft;
528             break;
529         default:
530             break;
531         }
532     }
533     return effectiveAlignment;
534 }
535
536 bool QSGTextEditPrivate::setHAlign(QSGTextEdit::HAlignment alignment, bool forceAlign)
537 {
538     Q_Q(QSGTextEdit);
539     if (hAlign != alignment || forceAlign) {
540         QSGTextEdit::HAlignment oldEffectiveHAlign = q->effectiveHAlign();
541         hAlign = alignment;
542         emit q->horizontalAlignmentChanged(alignment);
543         if (oldEffectiveHAlign != q->effectiveHAlign())
544             emit q->effectiveHorizontalAlignmentChanged();
545         return true;
546     }
547     return false;
548 }
549
550 bool QSGTextEditPrivate::determineHorizontalAlignment()
551 {
552     Q_Q(QSGTextEdit);
553     if (hAlignImplicit && q->isComponentComplete()) {
554         bool alignToRight = text.isEmpty() ? QApplication::keyboardInputDirection() == Qt::RightToLeft : rightToLeftText;
555         return setHAlign(alignToRight ? QSGTextEdit::AlignRight : QSGTextEdit::AlignLeft);
556     }
557     return false;
558 }
559
560 void QSGTextEditPrivate::mirrorChange()
561 {
562     Q_Q(QSGTextEdit);
563     if (q->isComponentComplete()) {
564         if (!hAlignImplicit && (hAlign == QSGTextEdit::AlignRight || hAlign == QSGTextEdit::AlignLeft)) {
565             updateDefaultTextOption();
566             q->updateSize();
567             emit q->effectiveHorizontalAlignmentChanged();
568         }
569     }
570 }
571
572 QSGTextEdit::VAlignment QSGTextEdit::vAlign() const
573 {
574     Q_D(const QSGTextEdit);
575     return d->vAlign;
576 }
577
578 void QSGTextEdit::setVAlign(QSGTextEdit::VAlignment alignment)
579 {
580     Q_D(QSGTextEdit);
581     if (alignment == d->vAlign)
582         return;
583     d->vAlign = alignment;
584     d->updateDefaultTextOption();
585     updateSize();
586     moveCursorDelegate();
587     emit verticalAlignmentChanged(d->vAlign);
588 }
589 /*!
590     \qmlproperty enumeration QtQuick2::TextEdit::wrapMode
591
592     Set this property to wrap the text to the TextEdit item's width.
593     The text will only wrap if an explicit width has been set.
594
595     \list
596     \o TextEdit.NoWrap - no wrapping will be performed. If the text contains insufficient newlines, then implicitWidth will exceed a set width.
597     \o TextEdit.WordWrap - wrapping is done on word boundaries only. If a word is too long, implicitWidth will exceed a set width.
598     \o TextEdit.WrapAnywhere - wrapping is done at any point on a line, even if it occurs in the middle of a word.
599     \o TextEdit.Wrap - if possible, wrapping occurs at a word boundary; otherwise it will occur at the appropriate point on the line, even in the middle of a word.
600     \endlist
601
602     The default is TextEdit.NoWrap. If you set a width, consider using TextEdit.Wrap.
603 */
604 QSGTextEdit::WrapMode QSGTextEdit::wrapMode() const
605 {
606     Q_D(const QSGTextEdit);
607     return d->wrapMode;
608 }
609
610 void QSGTextEdit::setWrapMode(WrapMode mode)
611 {
612     Q_D(QSGTextEdit);
613     if (mode == d->wrapMode)
614         return;
615     d->wrapMode = mode;
616     d->updateDefaultTextOption();
617     updateSize();
618     emit wrapModeChanged();
619 }
620
621 /*!
622     \qmlproperty int QtQuick2::TextEdit::lineCount
623
624     Returns the total number of lines in the textEdit item.
625 */
626 int QSGTextEdit::lineCount() const
627 {
628     Q_D(const QSGTextEdit);
629     return d->lineCount;
630 }
631
632 /*!
633     \qmlproperty real QtQuick2::TextEdit::paintedWidth
634
635     Returns the width of the text, including the width past the width
636     which is covered due to insufficient wrapping if \l wrapMode is set.
637 */
638 qreal QSGTextEdit::paintedWidth() const
639 {
640     Q_D(const QSGTextEdit);
641     return d->paintedSize.width();
642 }
643
644 /*!
645     \qmlproperty real QtQuick2::TextEdit::paintedHeight
646
647     Returns the height of the text, including the height past the height
648     that is covered if the text does not fit within the set height.
649 */
650 qreal QSGTextEdit::paintedHeight() const
651 {
652     Q_D(const QSGTextEdit);
653     return d->paintedSize.height();
654 }
655
656 /*!
657     \qmlmethod rectangle QtQuick2::TextEdit::positionToRectangle(position)
658
659     Returns the rectangle at the given \a position in the text. The x, y,
660     and height properties correspond to the cursor that would describe
661     that position.
662 */
663 QRectF QSGTextEdit::positionToRectangle(int pos) const
664 {
665     Q_D(const QSGTextEdit);
666     QTextCursor c(d->document);
667     c.setPosition(pos);
668     return d->control->cursorRect(c);
669
670 }
671
672 /*!
673     \qmlmethod int QtQuick2::TextEdit::positionAt(int x, int y)
674
675     Returns the text position closest to pixel position (\a x, \a y).
676
677     Position 0 is before the first character, position 1 is after the first character
678     but before the second, and so on until position \l {text}.length, which is after all characters.
679 */
680 int QSGTextEdit::positionAt(int x, int y) const
681 {
682     Q_D(const QSGTextEdit);
683     int r = d->document->documentLayout()->hitTest(QPoint(x,y-d->yoff), Qt::FuzzyHit);
684     QTextCursor cursor = d->control->textCursor();
685     if (r > cursor.position()) {
686         // The cursor position includes positions within the preedit text, but only positions in the
687         // same text block are offset so it is possible to get a position that is either part of the
688         // preedit or the next text block.
689         QTextLayout *layout = cursor.block().layout();
690         const int preeditLength = layout
691                 ? layout->preeditAreaText().length()
692                 : 0;
693         if (preeditLength > 0
694                 && d->document->documentLayout()->blockBoundingRect(cursor.block()).contains(x,y-d->yoff)) {
695             r = r > cursor.position() + preeditLength
696                     ? r - preeditLength
697                     : cursor.position();
698         }
699     }
700     return r;
701 }
702
703 /*!
704     \qmlmethod void QtQuick2::TextEdit::moveCursorSelection(int position, SelectionMode mode = TextEdit.SelectCharacters)
705
706     Moves the cursor to \a position and updates the selection according to the optional \a mode
707     parameter. (To only move the cursor, set the \l cursorPosition property.)
708
709     When this method is called it additionally sets either the
710     selectionStart or the selectionEnd (whichever was at the previous cursor position)
711     to the specified position. This allows you to easily extend and contract the selected
712     text range.
713
714     The selection mode specifies whether the selection is updated on a per character or a per word
715     basis.  If not specified the selection mode will default to TextEdit.SelectCharacters.
716
717     \list
718     \o TextEdit.SelectCharacters - Sets either the selectionStart or selectionEnd (whichever was at
719     the previous cursor position) to the specified position.
720     \o TextEdit.SelectWords - Sets the selectionStart and selectionEnd to include all
721     words between the specified postion and the previous cursor position.  Words partially in the
722     range are included.
723     \endlist
724
725     For example, take this sequence of calls:
726
727     \code
728         cursorPosition = 5
729         moveCursorSelection(9, TextEdit.SelectCharacters)
730         moveCursorSelection(7, TextEdit.SelectCharacters)
731     \endcode
732
733     This moves the cursor to position 5, extend the selection end from 5 to 9
734     and then retract the selection end from 9 to 7, leaving the text from position 5 to 7
735     selected (the 6th and 7th characters).
736
737     The same sequence with TextEdit.SelectWords will extend the selection start to a word boundary
738     before or on position 5 and extend the selection end to a word boundary on or past position 9.
739 */
740 void QSGTextEdit::moveCursorSelection(int pos)
741 {
742     //Note that this is the same as setCursorPosition but with the KeepAnchor flag set
743     Q_D(QSGTextEdit);
744     QTextCursor cursor = d->control->textCursor();
745     if (cursor.position() == pos)
746         return;
747     cursor.setPosition(pos, QTextCursor::KeepAnchor);
748     d->control->setTextCursor(cursor);
749 }
750
751 void QSGTextEdit::moveCursorSelection(int pos, SelectionMode mode)
752 {
753     Q_D(QSGTextEdit);
754     QTextCursor cursor = d->control->textCursor();
755     if (cursor.position() == pos)
756         return;
757     if (mode == SelectCharacters) {
758         cursor.setPosition(pos, QTextCursor::KeepAnchor);
759     } else if (cursor.anchor() < pos || (cursor.anchor() == pos && cursor.position() < pos)) {
760         if (cursor.anchor() > cursor.position()) {
761             cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor);
762             cursor.movePosition(QTextCursor::StartOfWord, QTextCursor::KeepAnchor);
763             if (cursor.position() == cursor.anchor())
764                 cursor.movePosition(QTextCursor::PreviousWord, QTextCursor::MoveAnchor);
765             else
766                 cursor.setPosition(cursor.position(), QTextCursor::MoveAnchor);
767         } else {
768             cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor);
769             cursor.movePosition(QTextCursor::StartOfWord, QTextCursor::MoveAnchor);
770         }
771
772         cursor.setPosition(pos, QTextCursor::KeepAnchor);
773         cursor.movePosition(QTextCursor::StartOfWord, QTextCursor::KeepAnchor);
774         if (cursor.position() != pos)
775             cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
776     } else if (cursor.anchor() > pos || (cursor.anchor() == pos && cursor.position() > pos)) {
777         if (cursor.anchor() < cursor.position()) {
778             cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor);
779             cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::MoveAnchor);
780         } else {
781             cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor);
782             cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor);
783             cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
784             if (cursor.position() != cursor.anchor()) {
785                 cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor);
786                 cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::MoveAnchor);
787             }
788         }
789
790         cursor.setPosition(pos, QTextCursor::KeepAnchor);
791         cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
792         if (cursor.position() != pos) {
793             cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor);
794             cursor.movePosition(QTextCursor::StartOfWord, QTextCursor::KeepAnchor);
795         }
796     }
797     d->control->setTextCursor(cursor);
798 }
799
800 /*!
801     \qmlproperty bool QtQuick2::TextEdit::cursorVisible
802     If true the text edit shows a cursor.
803
804     This property is set and unset when the text edit gets active focus, but it can also
805     be set directly (useful, for example, if a KeyProxy might forward keys to it).
806 */
807 bool QSGTextEdit::isCursorVisible() const
808 {
809     Q_D(const QSGTextEdit);
810     return d->cursorVisible;
811 }
812
813 void QSGTextEdit::setCursorVisible(bool on)
814 {
815     Q_D(QSGTextEdit);
816     if (d->cursorVisible == on)
817         return;
818     d->cursorVisible = on;
819     QFocusEvent focusEvent(on ? QEvent::FocusIn : QEvent::FocusOut);
820     if (!on && !d->persistentSelection)
821         d->control->setCursorIsFocusIndicator(true);
822     d->control->processEvent(&focusEvent, QPointF(0, -d->yoff));
823     emit cursorVisibleChanged(d->cursorVisible);
824 }
825
826 /*!
827     \qmlproperty int QtQuick2::TextEdit::cursorPosition
828     The position of the cursor in the TextEdit.
829 */
830 int QSGTextEdit::cursorPosition() const
831 {
832     Q_D(const QSGTextEdit);
833     return d->control->textCursor().position();
834 }
835
836 void QSGTextEdit::setCursorPosition(int pos)
837 {
838     Q_D(QSGTextEdit);
839     if (pos < 0 || pos > d->text.length())
840         return;
841     QTextCursor cursor = d->control->textCursor();
842     if (cursor.position() == pos && cursor.anchor() == pos)
843         return;
844     cursor.setPosition(pos);
845     d->control->setTextCursor(cursor);
846 }
847
848 /*!
849     \qmlproperty Component QtQuick2::TextEdit::cursorDelegate
850     The delegate for the cursor in the TextEdit.
851
852     If you set a cursorDelegate for a TextEdit, this delegate will be used for
853     drawing the cursor instead of the standard cursor. An instance of the
854     delegate will be created and managed by the text edit when a cursor is
855     needed, and the x and y properties of delegate instance will be set so as
856     to be one pixel before the top left of the current character.
857
858     Note that the root item of the delegate component must be a QDeclarativeItem or
859     QDeclarativeItem derived item.
860 */
861 QDeclarativeComponent* QSGTextEdit::cursorDelegate() const
862 {
863     Q_D(const QSGTextEdit);
864     return d->cursorComponent;
865 }
866
867 void QSGTextEdit::setCursorDelegate(QDeclarativeComponent* c)
868 {
869     Q_D(QSGTextEdit);
870     if(d->cursorComponent){
871         if(d->cursor){
872             d->control->setCursorWidth(-1);
873             updateCursor();
874             delete d->cursor;
875             d->cursor = 0;
876         }
877     }
878     d->cursorComponent = c;
879     if(c && c->isReady()){
880         loadCursorDelegate();
881     }else{
882         if(c)
883             connect(c, SIGNAL(statusChanged()),
884                     this, SLOT(loadCursorDelegate()));
885     }
886
887     emit cursorDelegateChanged();
888 }
889
890 void QSGTextEdit::loadCursorDelegate()
891 {
892     Q_D(QSGTextEdit);
893     if(d->cursorComponent->isLoading())
894         return;
895     d->cursor = qobject_cast<QSGItem*>(d->cursorComponent->create(qmlContext(this)));
896     if(d->cursor){
897         d->control->setCursorWidth(0);
898         updateCursor();
899         QDeclarative_setParent_noEvent(d->cursor, this);
900         d->cursor->setParentItem(this);
901         d->cursor->setHeight(QFontMetrics(d->font).height());
902         moveCursorDelegate();
903     }else{
904         qmlInfo(this) << "Error loading cursor delegate.";
905     }
906 }
907
908 /*!
909     \qmlproperty int QtQuick2::TextEdit::selectionStart
910
911     The cursor position before the first character in the current selection.
912
913     This property is read-only. To change the selection, use select(start,end),
914     selectAll(), or selectWord().
915
916     \sa selectionEnd, cursorPosition, selectedText
917 */
918 int QSGTextEdit::selectionStart() const
919 {
920     Q_D(const QSGTextEdit);
921     return d->control->textCursor().selectionStart();
922 }
923
924 /*!
925     \qmlproperty int QtQuick2::TextEdit::selectionEnd
926
927     The cursor position after the last character in the current selection.
928
929     This property is read-only. To change the selection, use select(start,end),
930     selectAll(), or selectWord().
931
932     \sa selectionStart, cursorPosition, selectedText
933 */
934 int QSGTextEdit::selectionEnd() const
935 {
936     Q_D(const QSGTextEdit);
937     return d->control->textCursor().selectionEnd();
938 }
939
940 /*!
941     \qmlproperty string QtQuick2::TextEdit::selectedText
942
943     This read-only property provides the text currently selected in the
944     text edit.
945
946     It is equivalent to the following snippet, but is faster and easier
947     to use.
948     \code
949     //myTextEdit is the id of the TextEdit
950     myTextEdit.text.toString().substring(myTextEdit.selectionStart,
951             myTextEdit.selectionEnd);
952     \endcode
953 */
954 QString QSGTextEdit::selectedText() const
955 {
956     Q_D(const QSGTextEdit);
957     return d->control->textCursor().selectedText();
958 }
959
960 /*!
961     \qmlproperty bool QtQuick2::TextEdit::activeFocusOnPress
962
963     Whether the TextEdit should gain active focus on a mouse press. By default this is
964     set to true.
965 */
966 bool QSGTextEdit::focusOnPress() const
967 {
968     Q_D(const QSGTextEdit);
969     return d->focusOnPress;
970 }
971
972 void QSGTextEdit::setFocusOnPress(bool on)
973 {
974     Q_D(QSGTextEdit);
975     if (d->focusOnPress == on)
976         return;
977     d->focusOnPress = on;
978     emit activeFocusOnPressChanged(d->focusOnPress);
979 }
980
981 /*!
982     \qmlproperty bool QtQuick2::TextEdit::persistentSelection
983
984     Whether the TextEdit should keep the selection visible when it loses active focus to another
985     item in the scene. By default this is set to true;
986 */
987 bool QSGTextEdit::persistentSelection() const
988 {
989     Q_D(const QSGTextEdit);
990     return d->persistentSelection;
991 }
992
993 void QSGTextEdit::setPersistentSelection(bool on)
994 {
995     Q_D(QSGTextEdit);
996     if (d->persistentSelection == on)
997         return;
998     d->persistentSelection = on;
999     emit persistentSelectionChanged(d->persistentSelection);
1000 }
1001
1002 /*
1003    \qmlproperty real QtQuick2::TextEdit::textMargin
1004
1005    The margin, in pixels, around the text in the TextEdit.
1006 */
1007 qreal QSGTextEdit::textMargin() const
1008 {
1009     Q_D(const QSGTextEdit);
1010     return d->textMargin;
1011 }
1012
1013 void QSGTextEdit::setTextMargin(qreal margin)
1014 {
1015     Q_D(QSGTextEdit);
1016     if (d->textMargin == margin)
1017         return;
1018     d->textMargin = margin;
1019     d->document->setDocumentMargin(d->textMargin);
1020     emit textMarginChanged(d->textMargin);
1021 }
1022
1023 void QSGTextEdit::geometryChanged(const QRectF &newGeometry,
1024                                   const QRectF &oldGeometry)
1025 {
1026     if (newGeometry.width() != oldGeometry.width())
1027         updateSize();
1028     QSGImplicitSizeItem::geometryChanged(newGeometry, oldGeometry);
1029 }
1030
1031 /*!
1032     Ensures any delayed caching or data loading the class
1033     needs to performed is complete.
1034 */
1035 void QSGTextEdit::componentComplete()
1036 {
1037     Q_D(QSGTextEdit);
1038     QSGImplicitSizeItem::componentComplete();
1039
1040     if (d->richText) {
1041         d->isComplexRichText = QSGTextNode::isComplexRichText(d->document);
1042     }
1043
1044     if (d->dirty) {
1045         d->determineHorizontalAlignment();
1046         d->updateDefaultTextOption();
1047         updateSize();
1048         d->dirty = false;
1049     }
1050
1051 }
1052 /*!
1053     \qmlproperty bool QtQuick2::TextEdit::selectByMouse
1054
1055     Defaults to false.
1056
1057     If true, the user can use the mouse to select text in some
1058     platform-specific way. Note that for some platforms this may
1059     not be an appropriate interaction (eg. may conflict with how
1060     the text needs to behave inside a Flickable.
1061 */
1062 bool QSGTextEdit::selectByMouse() const
1063 {
1064     Q_D(const QSGTextEdit);
1065     return d->selectByMouse;
1066 }
1067
1068 void QSGTextEdit::setSelectByMouse(bool on)
1069 {
1070     Q_D(QSGTextEdit);
1071     if (d->selectByMouse != on) {
1072         d->selectByMouse = on;
1073         setKeepMouseGrab(on);
1074         if (on)
1075             setTextInteractionFlags(d->control->textInteractionFlags() | Qt::TextSelectableByMouse);
1076         else
1077             setTextInteractionFlags(d->control->textInteractionFlags() & ~Qt::TextSelectableByMouse);
1078         emit selectByMouseChanged(on);
1079     }
1080 }
1081
1082 /*!
1083     \qmlproperty enum QtQuick2::TextEdit::mouseSelectionMode
1084
1085     Specifies how text should be selected using a mouse.
1086
1087     \list
1088     \o TextEdit.SelectCharacters - The selection is updated with individual characters. (Default)
1089     \o TextEdit.SelectWords - The selection is updated with whole words.
1090     \endlist
1091
1092     This property only applies when \l selectByMouse is true.
1093 */
1094 QSGTextEdit::SelectionMode QSGTextEdit::mouseSelectionMode() const
1095 {
1096     Q_D(const QSGTextEdit);
1097     return d->mouseSelectionMode;
1098 }
1099
1100 void QSGTextEdit::setMouseSelectionMode(SelectionMode mode)
1101 {
1102     Q_D(QSGTextEdit);
1103     if (d->mouseSelectionMode != mode) {
1104         d->mouseSelectionMode = mode;
1105         d->control->setWordSelectionEnabled(mode == SelectWords);
1106         emit mouseSelectionModeChanged(mode);
1107     }
1108 }
1109
1110 /*!
1111     \qmlproperty bool QtQuick2::TextEdit::readOnly
1112
1113     Whether the user can interact with the TextEdit item. If this
1114     property is set to true the text cannot be edited by user interaction.
1115
1116     By default this property is false.
1117 */
1118 void QSGTextEdit::setReadOnly(bool r)
1119 {
1120     Q_D(QSGTextEdit);
1121     if (r == isReadOnly())
1122         return;
1123
1124     setFlag(QSGItem::ItemAcceptsInputMethod, !r);
1125     Qt::TextInteractionFlags flags = Qt::LinksAccessibleByMouse;
1126     if (d->selectByMouse)
1127         flags = flags | Qt::TextSelectableByMouse;
1128     if (!r)
1129         flags = flags | Qt::TextSelectableByKeyboard | Qt::TextEditable;
1130     d->control->setTextInteractionFlags(flags);
1131     if (!r)
1132         d->control->moveCursor(QTextCursor::End);
1133
1134     emit readOnlyChanged(r);
1135 }
1136
1137 bool QSGTextEdit::isReadOnly() const
1138 {
1139     Q_D(const QSGTextEdit);
1140     return !(d->control->textInteractionFlags() & Qt::TextEditable);
1141 }
1142
1143 /*!
1144     Sets how the text edit should interact with user input to the given
1145     \a flags.
1146 */
1147 void QSGTextEdit::setTextInteractionFlags(Qt::TextInteractionFlags flags)
1148 {
1149     Q_D(QSGTextEdit);
1150     d->control->setTextInteractionFlags(flags);
1151 }
1152
1153 /*!
1154     Returns the flags specifying how the text edit should interact
1155     with user input.
1156 */
1157 Qt::TextInteractionFlags QSGTextEdit::textInteractionFlags() const
1158 {
1159     Q_D(const QSGTextEdit);
1160     return d->control->textInteractionFlags();
1161 }
1162
1163 /*!
1164     \qmlproperty rectangle QtQuick2::TextEdit::cursorRectangle
1165
1166     The rectangle where the text cursor is rendered
1167     within the text edit. Read-only.
1168 */
1169 QRect QSGTextEdit::cursorRectangle() const
1170 {
1171     Q_D(const QSGTextEdit);
1172     return d->control->cursorRect().toRect().translated(0,d->yoff);
1173 }
1174
1175 bool QSGTextEdit::event(QEvent *event)
1176 {
1177     Q_D(QSGTextEdit);
1178     if (event->type() == QEvent::ShortcutOverride) {
1179         d->control->processEvent(event, QPointF(0, -d->yoff));
1180         return event->isAccepted();
1181     }
1182     return QSGImplicitSizeItem::event(event);
1183 }
1184
1185 /*!
1186 \overload
1187 Handles the given key \a event.
1188 */
1189 void QSGTextEdit::keyPressEvent(QKeyEvent *event)
1190 {
1191     Q_D(QSGTextEdit);
1192     d->control->processEvent(event, QPointF(0, -d->yoff));
1193     if (!event->isAccepted())
1194         QSGImplicitSizeItem::keyPressEvent(event);
1195 }
1196
1197 /*!
1198 \overload
1199 Handles the given key \a event.
1200 */
1201 void QSGTextEdit::keyReleaseEvent(QKeyEvent *event)
1202 {
1203     Q_D(QSGTextEdit);
1204     d->control->processEvent(event, QPointF(0, -d->yoff));
1205     if (!event->isAccepted())
1206         QSGImplicitSizeItem::keyReleaseEvent(event);
1207 }
1208
1209 /*!
1210     \qmlmethod void QtQuick2::TextEdit::deselect()
1211
1212     Removes active text selection.
1213 */
1214 void QSGTextEdit::deselect()
1215 {
1216     Q_D(QSGTextEdit);
1217     QTextCursor c = d->control->textCursor();
1218     c.clearSelection();
1219     d->control->setTextCursor(c);
1220 }
1221
1222 /*!
1223     \qmlmethod void QtQuick2::TextEdit::selectAll()
1224
1225     Causes all text to be selected.
1226 */
1227 void QSGTextEdit::selectAll()
1228 {
1229     Q_D(QSGTextEdit);
1230     d->control->selectAll();
1231 }
1232
1233 /*!
1234     \qmlmethod void QtQuick2::TextEdit::selectWord()
1235
1236     Causes the word closest to the current cursor position to be selected.
1237 */
1238 void QSGTextEdit::selectWord()
1239 {
1240     Q_D(QSGTextEdit);
1241     QTextCursor c = d->control->textCursor();
1242     c.select(QTextCursor::WordUnderCursor);
1243     d->control->setTextCursor(c);
1244 }
1245
1246 /*!
1247     \qmlmethod void QtQuick2::TextEdit::select(int start, int end)
1248
1249     Causes the text from \a start to \a end to be selected.
1250
1251     If either start or end is out of range, the selection is not changed.
1252
1253     After calling this, selectionStart will become the lesser
1254     and selectionEnd will become the greater (regardless of the order passed
1255     to this method).
1256
1257     \sa selectionStart, selectionEnd
1258 */
1259 void QSGTextEdit::select(int start, int end)
1260 {
1261     Q_D(QSGTextEdit);
1262     if (start < 0 || end < 0 || start > d->text.length() || end > d->text.length())
1263         return;
1264     QTextCursor cursor = d->control->textCursor();
1265     cursor.beginEditBlock();
1266     cursor.setPosition(start, QTextCursor::MoveAnchor);
1267     cursor.setPosition(end, QTextCursor::KeepAnchor);
1268     cursor.endEditBlock();
1269     d->control->setTextCursor(cursor);
1270
1271     // QTBUG-11100
1272     updateSelectionMarkers();
1273 }
1274
1275 /*!
1276     \qmlmethod void QtQuick2::TextEdit::isRightToLeft(int start, int end)
1277
1278     Returns true if the natural reading direction of the editor text
1279     found between positions \a start and \a end is right to left.
1280 */
1281 bool QSGTextEdit::isRightToLeft(int start, int end)
1282 {
1283     Q_D(QSGTextEdit);
1284     if (start > end) {
1285         qmlInfo(this) << "isRightToLeft(start, end) called with the end property being smaller than the start.";
1286         return false;
1287     } else {
1288         return d->text.mid(start, end - start).isRightToLeft();
1289     }
1290 }
1291
1292 #ifndef QT_NO_CLIPBOARD
1293 /*!
1294     \qmlmethod QtQuick2::TextEdit::cut()
1295
1296     Moves the currently selected text to the system clipboard.
1297 */
1298 void QSGTextEdit::cut()
1299 {
1300     Q_D(QSGTextEdit);
1301     d->control->cut();
1302 }
1303
1304 /*!
1305     \qmlmethod QtQuick2::TextEdit::copy()
1306
1307     Copies the currently selected text to the system clipboard.
1308 */
1309 void QSGTextEdit::copy()
1310 {
1311     Q_D(QSGTextEdit);
1312     d->control->copy();
1313 }
1314
1315 /*!
1316     \qmlmethod QtQuick2::TextEdit::paste()
1317
1318     Replaces the currently selected text by the contents of the system clipboard.
1319 */
1320 void QSGTextEdit::paste()
1321 {
1322     Q_D(QSGTextEdit);
1323     d->control->paste();
1324 }
1325 #endif // QT_NO_CLIPBOARD
1326
1327 /*!
1328 \overload
1329 Handles the given mouse \a event.
1330 */
1331 void QSGTextEdit::mousePressEvent(QGraphicsSceneMouseEvent *event)
1332 {
1333     Q_D(QSGTextEdit);
1334     if (d->focusOnPress){
1335         bool hadActiveFocus = hasActiveFocus();
1336         forceActiveFocus();
1337         if (d->showInputPanelOnFocus) {
1338             if (hasActiveFocus() && hadActiveFocus && !isReadOnly()) {
1339                 // re-open input panel on press if already focused
1340                 openSoftwareInputPanel();
1341             }
1342         } else { // show input panel on click
1343             if (hasActiveFocus() && !hadActiveFocus) {
1344                 d->clickCausedFocus = true;
1345             }
1346         }
1347     }
1348     d->control->processEvent(event, QPointF(0, -d->yoff));
1349     if (!event->isAccepted())
1350         QSGImplicitSizeItem::mousePressEvent(event);
1351 }
1352
1353 /*!
1354 \overload
1355 Handles the given mouse \a event.
1356 */
1357 void QSGTextEdit::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
1358 {
1359     Q_D(QSGTextEdit);
1360     d->control->processEvent(event, QPointF(0, -d->yoff));
1361     if (!d->showInputPanelOnFocus) { // input panel on click
1362         if (d->focusOnPress && !isReadOnly() && boundingRect().contains(event->pos())) {
1363             // ### refactor: port properly
1364             qDebug("QSGTextEdit: virtual keyboard handling not implemented");
1365 //            if (canvas() && canvas() == qApp->focusWidget()) {
1366 //                qt_widget_private(canvas())->handleSoftwareInputPanel(event->button(), d->clickCausedFocus);
1367 //            }
1368         }
1369     }
1370     d->clickCausedFocus = false;
1371
1372     if (!event->isAccepted())
1373         QSGImplicitSizeItem::mouseReleaseEvent(event);
1374 }
1375
1376 /*!
1377 \overload
1378 Handles the given mouse \a event.
1379 */
1380 void QSGTextEdit::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
1381 {
1382     Q_D(QSGTextEdit);
1383     d->control->processEvent(event, QPointF(0, -d->yoff));
1384     if (!event->isAccepted())
1385         QSGImplicitSizeItem::mouseDoubleClickEvent(event);
1386 }
1387
1388 /*!
1389 \overload
1390 Handles the given mouse \a event.
1391 */
1392 void QSGTextEdit::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
1393 {
1394     Q_D(QSGTextEdit);
1395     d->control->processEvent(event, QPointF(0, -d->yoff));
1396     if (!event->isAccepted())
1397         QSGImplicitSizeItem::mouseMoveEvent(event);
1398 }
1399
1400 /*!
1401 \overload
1402 Handles the given input method \a event.
1403 */
1404 void QSGTextEdit::inputMethodEvent(QInputMethodEvent *event)
1405 {
1406     Q_D(QSGTextEdit);
1407     const bool wasComposing = isInputMethodComposing();
1408     d->control->processEvent(event, QPointF(0, -d->yoff));
1409     if (wasComposing != isInputMethodComposing())
1410         emit inputMethodComposingChanged();
1411 }
1412
1413 void QSGTextEdit::itemChange(ItemChange change, const ItemChangeData &value)
1414 {
1415     Q_D(QSGTextEdit);
1416     if (change == ItemActiveFocusHasChanged) {
1417         setCursorVisible(value.boolValue); // ### refactor: focus handling && d->canvas && d->canvas->hasFocus());
1418     }
1419     QSGItem::itemChange(change, value);
1420 }
1421
1422 /*!
1423 \overload
1424 Returns the value of the given \a property.
1425 */
1426 QVariant QSGTextEdit::inputMethodQuery(Qt::InputMethodQuery property) const
1427 {
1428     Q_D(const QSGTextEdit);
1429     return d->control->inputMethodQuery(property);
1430 }
1431
1432 void QSGTextEdit::updateImageCache(const QRectF &)
1433 {
1434     Q_D(QSGTextEdit);
1435
1436     // Do we really need the image cache?
1437     if (!d->richText || !d->isComplexRichText) {
1438         if (!d->pixmapCache.isNull())
1439             d->pixmapCache = QPixmap();
1440         return;
1441     }
1442
1443     if (width() != d->pixmapCache.width() || height() != d->pixmapCache.height())
1444         d->pixmapCache = QPixmap(width(), height());
1445
1446     if (d->pixmapCache.isNull())
1447         return;
1448
1449     // ### Use supplied rect, clear area and update only this part (for cursor updates)
1450     QRectF bounds = QRectF(0, 0, width(), height());
1451     d->pixmapCache.fill(Qt::transparent);
1452     {
1453         QPainter painter(&d->pixmapCache);
1454
1455         painter.setRenderHint(QPainter::TextAntialiasing);
1456         painter.translate(0, d->yoff);
1457
1458         d->control->drawContents(&painter, bounds);
1459     }
1460
1461 }
1462
1463 QSGNode *QSGTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *updatePaintNodeData)
1464 {
1465     Q_UNUSED(updatePaintNodeData);
1466     Q_D(QSGTextEdit);
1467
1468     QSGNode *currentNode = oldNode;
1469     if (d->richText && d->isComplexRichText) {
1470         QSGImageNode *node = 0;
1471         if (oldNode == 0 || d->nodeType != QSGTextEditPrivate::NodeIsTexture) {
1472             delete oldNode;
1473             node = QSGItemPrivate::get(this)->sceneGraphContext()->createImageNode();
1474             d->texture = new QSGPlainTexture();
1475             d->nodeType = QSGTextEditPrivate::NodeIsTexture;
1476             currentNode = node;
1477         } else {
1478             node = static_cast<QSGImageNode *>(oldNode);
1479         }
1480
1481         qobject_cast<QSGPlainTexture *>(d->texture)->setImage(d->pixmapCache.toImage());
1482         node->setTexture(0);
1483         node->setTexture(d->texture);
1484
1485         node->setTargetRect(QRectF(0, 0, d->pixmapCache.width(), d->pixmapCache.height()));
1486         node->setSourceRect(QRectF(0, 0, 1, 1));
1487         node->setHorizontalWrapMode(QSGTexture::ClampToEdge);
1488         node->setVerticalWrapMode(QSGTexture::ClampToEdge);
1489         node->setFiltering(QSGTexture::Linear); // Nonsmooth text just ugly, so don't do that..
1490         node->update();
1491
1492     } else if (oldNode == 0 || d->documentDirty) {
1493         d->documentDirty = false;
1494
1495 #if defined(Q_WS_MAC)
1496         // Make sure document is relayouted in the paint node on Mac
1497         // to avoid crashes due to the font engines created in the
1498         // shaping process
1499         d->document->markContentsDirty(0, d->document->characterCount());
1500 #endif
1501
1502         QSGTextNode *node = 0;
1503         if (oldNode == 0 || d->nodeType != QSGTextEditPrivate::NodeIsText) {
1504             delete oldNode;
1505             node = new QSGTextNode(QSGItemPrivate::get(this)->sceneGraphContext());
1506             d->nodeType = QSGTextEditPrivate::NodeIsText;
1507             currentNode = node;
1508         } else {
1509             node = static_cast<QSGTextNode *>(oldNode);
1510         }
1511
1512         node->deleteContent();
1513         node->setMatrix(QMatrix4x4());
1514
1515         QRectF bounds = boundingRect();
1516
1517         QColor selectionColor = d->control->palette().color(QPalette::Highlight);
1518         QColor selectedTextColor = d->control->palette().color(QPalette::HighlightedText);
1519         node->addTextDocument(bounds.topLeft(), d->document, d->color, QSGText::Normal, QColor(),
1520                               selectionColor, selectedTextColor, selectionStart(),
1521                               selectionEnd());
1522
1523 #if defined(Q_WS_MAC)
1524         // We also need to make sure the document layout is redone when
1525         // control is returned to the main thread, as all the font engines
1526         // are now owned by the rendering thread
1527         d->document->markContentsDirty(0, d->document->characterCount());
1528 #endif
1529     }
1530
1531     if (d->nodeType == QSGTextEditPrivate::NodeIsText && d->cursorComponent == 0 && !isReadOnly()) {
1532         QSGTextNode *node = static_cast<QSGTextNode *>(currentNode);
1533
1534         QColor color = (!d->cursorVisible || !d->control->cursorOn())
1535                 ? QColor(0, 0, 0, 0)
1536                 : d->color;
1537
1538         if (node->cursorNode() == 0) {
1539             node->setCursor(cursorRectangle(), color);
1540         } else {
1541             node->cursorNode()->setRect(cursorRectangle());
1542             node->cursorNode()->setColor(color);
1543         }
1544
1545     }
1546
1547     return currentNode;
1548 }
1549
1550 /*!
1551     \qmlproperty bool QtQuick2::TextEdit::smooth
1552
1553     This property holds whether the text is smoothly scaled or transformed.
1554
1555     Smooth filtering gives better visual quality, but is slower.  If
1556     the item is displayed at its natural size, this property has no visual or
1557     performance effect.
1558
1559     \note Generally scaling artifacts are only visible if the item is stationary on
1560     the screen.  A common pattern when animating an item is to disable smooth
1561     filtering at the beginning of the animation and reenable it at the conclusion.
1562 */
1563
1564 /*!
1565     \qmlproperty bool QtQuick2::TextEdit::canPaste
1566
1567     Returns true if the TextEdit is writable and the content of the clipboard is
1568     suitable for pasting into the TextEdit.
1569 */
1570 bool QSGTextEdit::canPaste() const
1571 {
1572     Q_D(const QSGTextEdit);
1573     return d->canPaste;
1574 }
1575
1576 /*!
1577     \qmlproperty bool QtQuick2::TextEdit::inputMethodComposing
1578
1579
1580     This property holds whether the TextEdit has partial text input from an
1581     input method.
1582
1583     While it is composing an input method may rely on mouse or key events from
1584     the TextEdit to edit or commit the partial text.  This property can be used
1585     to determine when to disable events handlers that may interfere with the
1586     correct operation of an input method.
1587 */
1588 bool QSGTextEdit::isInputMethodComposing() const
1589 {
1590     Q_D(const QSGTextEdit);
1591     if (QTextLayout *layout = d->control->textCursor().block().layout())
1592         return layout->preeditAreaText().length() > 0;
1593     return false;
1594 }
1595
1596 void QSGTextEditPrivate::init()
1597 {
1598     Q_Q(QSGTextEdit);
1599
1600     q->setSmooth(smooth);
1601     q->setAcceptedMouseButtons(Qt::LeftButton);
1602     q->setFlag(QSGItem::ItemAcceptsInputMethod);
1603     q->setFlag(QSGItem::ItemHasContents);
1604
1605     control = new QTextControl(q);
1606     control->setIgnoreUnusedNavigationEvents(true);
1607     control->setTextInteractionFlags(Qt::LinksAccessibleByMouse | Qt::TextSelectableByKeyboard | Qt::TextEditable);
1608     control->setDragEnabled(false);
1609
1610     // By default, QTextControl will issue both a updateCursorRequest() and an updateRequest()
1611     // when the cursor needs to be repainted. We need the signals to be separate to be able to
1612     // distinguish the cursor updates so that we can avoid updating the whole subtree when the
1613     // cursor blinks.
1614     if (!QObject::disconnect(control, SIGNAL(updateCursorRequest(QRectF)),
1615                              control, SIGNAL(updateRequest(QRectF)))) {
1616         qWarning("QSGTextEditPrivate::init: Failed to disconnect updateCursorRequest and updateRequest");
1617     }
1618
1619     // QTextControl follows the default text color
1620     // defined by the platform, declarative text
1621     // should be black by default
1622     QPalette pal = control->palette();
1623     if (pal.color(QPalette::Text) != color) {
1624         pal.setColor(QPalette::Text, color);
1625         control->setPalette(pal);
1626     }
1627
1628     QObject::connect(control, SIGNAL(updateRequest(QRectF)), q, SLOT(updateDocument()));
1629     QObject::connect(control, SIGNAL(updateCursorRequest()), q, SLOT(updateCursor()));
1630     QObject::connect(control, SIGNAL(textChanged()), q, SLOT(q_textChanged()));
1631     QObject::connect(control, SIGNAL(selectionChanged()), q, SIGNAL(selectionChanged()));
1632     QObject::connect(control, SIGNAL(selectionChanged()), q, SLOT(updateSelectionMarkers()));
1633     QObject::connect(control, SIGNAL(cursorPositionChanged()), q, SLOT(updateSelectionMarkers()));
1634     QObject::connect(control, SIGNAL(cursorPositionChanged()), q, SIGNAL(cursorPositionChanged()));
1635     QObject::connect(control, SIGNAL(microFocusChanged()), q, SLOT(moveCursorDelegate()));
1636     QObject::connect(control, SIGNAL(linkActivated(QString)), q, SIGNAL(linkActivated(QString)));
1637 #ifndef QT_NO_CLIPBOARD
1638     QObject::connect(q, SIGNAL(readOnlyChanged(bool)), q, SLOT(q_canPasteChanged()));
1639     QObject::connect(QApplication::clipboard(), SIGNAL(dataChanged()), q, SLOT(q_canPasteChanged()));
1640     canPaste = control->canPaste();
1641 #endif
1642
1643     document = control->document();
1644     document->setDefaultFont(font);
1645     document->setDocumentMargin(textMargin);
1646     document->setUndoRedoEnabled(false); // flush undo buffer.
1647     document->setUndoRedoEnabled(true);
1648     updateDefaultTextOption();
1649 }
1650
1651 void QSGTextEdit::q_textChanged()
1652 {
1653     Q_D(QSGTextEdit);
1654     d->text = text();
1655     d->rightToLeftText = d->document->begin().layout()->engine()->isRightToLeft();
1656     d->determineHorizontalAlignment();
1657     d->updateDefaultTextOption();
1658     updateSize();
1659     updateTotalLines();
1660     emit textChanged(d->text);
1661 }
1662
1663 void QSGTextEdit::moveCursorDelegate()
1664 {
1665     Q_D(QSGTextEdit);
1666     updateMicroFocus();
1667     emit cursorRectangleChanged();
1668     if(!d->cursor)
1669         return;
1670     QRectF cursorRect = cursorRectangle();
1671     d->cursor->setX(cursorRect.x());
1672     d->cursor->setY(cursorRect.y());
1673 }
1674
1675 void QSGTextEditPrivate::updateSelection()
1676 {
1677     Q_Q(QSGTextEdit);
1678     QTextCursor cursor = control->textCursor();
1679     bool startChange = (lastSelectionStart != cursor.selectionStart());
1680     bool endChange = (lastSelectionEnd != cursor.selectionEnd());
1681     cursor.beginEditBlock();
1682     cursor.setPosition(lastSelectionStart, QTextCursor::MoveAnchor);
1683     cursor.setPosition(lastSelectionEnd, QTextCursor::KeepAnchor);
1684     cursor.endEditBlock();
1685     control->setTextCursor(cursor);
1686     if(startChange)
1687         q->selectionStartChanged();
1688     if(endChange)
1689         q->selectionEndChanged();
1690 }
1691
1692 void QSGTextEdit::updateSelectionMarkers()
1693 {
1694     Q_D(QSGTextEdit);
1695     if(d->lastSelectionStart != d->control->textCursor().selectionStart()){
1696         d->lastSelectionStart = d->control->textCursor().selectionStart();
1697         emit selectionStartChanged();
1698     }
1699     if(d->lastSelectionEnd != d->control->textCursor().selectionEnd()){
1700         d->lastSelectionEnd = d->control->textCursor().selectionEnd();
1701         emit selectionEndChanged();
1702     }
1703 }
1704
1705 QRectF QSGTextEdit::boundingRect() const
1706 {
1707     Q_D(const QSGTextEdit);
1708     QRectF r = QSGImplicitSizeItem::boundingRect();
1709     int cursorWidth = 1;
1710     if(d->cursor)
1711         cursorWidth = d->cursor->width();
1712     if(!d->document->isEmpty())
1713         cursorWidth += 3;// ### Need a better way of accounting for space between char and cursor
1714
1715     // Could include font max left/right bearings to either side of rectangle.
1716
1717     r.setRight(r.right() + cursorWidth);
1718     return r.translated(0,d->yoff);
1719 }
1720
1721 qreal QSGTextEditPrivate::getImplicitWidth() const
1722 {
1723     Q_Q(const QSGTextEdit);
1724     if (!requireImplicitWidth) {
1725         // We don't calculate implicitWidth unless it is required.
1726         // We need to force a size update now to ensure implicitWidth is calculated
1727         const_cast<QSGTextEditPrivate*>(this)->requireImplicitWidth = true;
1728         const_cast<QSGTextEdit*>(q)->updateSize();
1729     }
1730     return implicitWidth;
1731 }
1732
1733 //### we should perhaps be a bit smarter here -- depending on what has changed, we shouldn't
1734 //    need to do all the calculations each time
1735 void QSGTextEdit::updateSize()
1736 {
1737     Q_D(QSGTextEdit);
1738     if (isComponentComplete()) {
1739         qreal naturalWidth = d->implicitWidth;
1740         // ### assumes that if the width is set, the text will fill to edges
1741         // ### (unless wrap is false, then clipping will occur)
1742         if (widthValid()) {
1743             if (!d->requireImplicitWidth) {
1744                 emit implicitWidthChanged();
1745                 // if the implicitWidth is used, then updateSize() has already been called (recursively)
1746                 if (d->requireImplicitWidth)
1747                     return;
1748             }
1749             if (d->requireImplicitWidth) {
1750                 d->document->setTextWidth(-1);
1751                 naturalWidth = d->document->idealWidth();
1752             }
1753             if (d->document->textWidth() != width())
1754                 d->document->setTextWidth(width());
1755         } else {
1756             d->document->setTextWidth(-1);
1757         }
1758         QFontMetrics fm = QFontMetrics(d->font);
1759         int dy = height();
1760         dy -= (int)d->document->size().height();
1761
1762         int nyoff;
1763         if (heightValid()) {
1764             if (d->vAlign == AlignBottom)
1765                 nyoff = dy;
1766             else if (d->vAlign == AlignVCenter)
1767                 nyoff = dy/2;
1768             else
1769                 nyoff = 0;
1770         } else {
1771             nyoff = 0;
1772         }
1773         if (nyoff != d->yoff)
1774             d->yoff = nyoff;
1775         setBaselineOffset(fm.ascent() + d->yoff + d->textMargin);
1776
1777         //### need to comfirm cost of always setting these
1778         int newWidth = qCeil(d->document->idealWidth());
1779         if (!widthValid() && d->document->textWidth() != newWidth)
1780             d->document->setTextWidth(newWidth); // ### Text does not align if width is not set (QTextDoc bug)
1781         // ### Setting the implicitWidth triggers another updateSize(), and unless there are bindings nothing has changed.
1782         if (!widthValid())
1783             setImplicitWidth(newWidth);
1784         else if (d->requireImplicitWidth)
1785             setImplicitWidth(naturalWidth);
1786         qreal newHeight = d->document->isEmpty() ? fm.height() : (int)d->document->size().height();
1787         setImplicitHeight(newHeight);
1788
1789         d->paintedSize = QSize(newWidth, newHeight);
1790         emit paintedSizeChanged();
1791     } else {
1792         d->dirty = true;
1793     }
1794     updateDocument();
1795 }
1796
1797 void QSGTextEdit::updateDocument()
1798 {
1799     Q_D(QSGTextEdit);
1800     d->documentDirty = true;
1801
1802     if (isComponentComplete()) {
1803         updateImageCache();
1804         update();
1805     }
1806 }
1807
1808 void QSGTextEdit::updateCursor()
1809 {
1810     Q_D(QSGTextEdit);
1811     if (isComponentComplete()) {
1812         updateImageCache(d->control->cursorRect());
1813         update();
1814     }
1815 }
1816
1817 void QSGTextEdit::updateTotalLines()
1818 {
1819     Q_D(QSGTextEdit);
1820
1821     int subLines = 0;
1822
1823     for (QTextBlock it = d->document->begin(); it != d->document->end(); it = it.next()) {
1824         QTextLayout *layout = it.layout();
1825         if (!layout)
1826             continue;
1827         subLines += layout->lineCount()-1;
1828     }
1829
1830     int newTotalLines = d->document->lineCount() + subLines;
1831     if (d->lineCount != newTotalLines) {
1832         d->lineCount = newTotalLines;
1833         emit lineCountChanged();
1834     }
1835 }
1836
1837 void QSGTextEditPrivate::updateDefaultTextOption()
1838 {
1839     Q_Q(QSGTextEdit);
1840     QTextOption opt = document->defaultTextOption();
1841     int oldAlignment = opt.alignment();
1842
1843     QSGTextEdit::HAlignment horizontalAlignment = q->effectiveHAlign();
1844     if (rightToLeftText) {
1845         if (horizontalAlignment == QSGTextEdit::AlignLeft)
1846             horizontalAlignment = QSGTextEdit::AlignRight;
1847         else if (horizontalAlignment == QSGTextEdit::AlignRight)
1848             horizontalAlignment = QSGTextEdit::AlignLeft;
1849     }
1850     opt.setAlignment((Qt::Alignment)(int)(horizontalAlignment | vAlign));
1851
1852     QTextOption::WrapMode oldWrapMode = opt.wrapMode();
1853     opt.setWrapMode(QTextOption::WrapMode(wrapMode));
1854
1855     bool oldUseDesignMetrics = opt.useDesignMetrics();
1856     bool useDesignMetrics = !qmlDisableDistanceField();
1857     opt.setUseDesignMetrics(useDesignMetrics);
1858
1859     if (oldWrapMode == opt.wrapMode()
1860             && oldAlignment == opt.alignment()
1861             && oldUseDesignMetrics == useDesignMetrics) {
1862         return;
1863     }
1864     document->setDefaultTextOption(opt);
1865 }
1866
1867
1868
1869 /*!
1870     \qmlmethod void QtQuick2::TextEdit::openSoftwareInputPanel()
1871
1872     Opens software input panels like virtual keyboards for typing, useful for
1873     customizing when you want the input keyboard to be shown and hidden in
1874     your application.
1875
1876     By default the opening of input panels follows the platform style. On Symbian^1 and
1877     Symbian^3 -based devices the panels are opened by clicking TextEdit. On other platforms
1878     the panels are automatically opened when TextEdit element gains active focus. Input panels are
1879     always closed if no editor has active focus.
1880
1881     You can disable the automatic behavior by setting the property \c activeFocusOnPress to false
1882     and use functions openSoftwareInputPanel() and closeSoftwareInputPanel() to implement
1883     the behavior you want.
1884
1885     Only relevant on platforms, which provide virtual keyboards.
1886
1887     \code
1888         import QtQuick 1.0
1889         TextEdit {
1890             id: textEdit
1891             text: "Hello world!"
1892             activeFocusOnPress: false
1893             MouseArea {
1894                 anchors.fill: parent
1895                 onClicked: {
1896                     if (!textEdit.activeFocus) {
1897                         textEdit.forceActiveFocus();
1898                         textEdit.openSoftwareInputPanel();
1899                     } else {
1900                         textEdit.focus = false;
1901                     }
1902                 }
1903                 onPressAndHold: textEdit.closeSoftwareInputPanel();
1904             }
1905         }
1906     \endcode
1907 */
1908 void QSGTextEdit::openSoftwareInputPanel()
1909 {
1910     if (qApp) {
1911         if (canvas()) {
1912             QEvent event(QEvent::RequestSoftwareInputPanel);
1913             QApplication::sendEvent(canvas(), &event);
1914         }
1915     }
1916 }
1917
1918 /*!
1919     \qmlmethod void QtQuick2::TextEdit::closeSoftwareInputPanel()
1920
1921     Closes a software input panel like a virtual keyboard shown on the screen, useful
1922     for customizing when you want the input keyboard to be shown and hidden in
1923     your application.
1924
1925     By default the opening of input panels follows the platform style. On Symbian^1 and
1926     Symbian^3 -based devices the panels are opened by clicking TextEdit. On other platforms
1927     the panels are automatically opened when TextEdit element gains active focus. Input panels are
1928     always closed if no editor has active focus.
1929
1930     You can disable the automatic behavior by setting the property \c activeFocusOnPress to false
1931     and use functions openSoftwareInputPanel() and closeSoftwareInputPanel() to implement
1932     the behavior you want.
1933
1934     Only relevant on platforms, which provide virtual keyboards.
1935
1936     \code
1937         import QtQuick 1.0
1938         TextEdit {
1939             id: textEdit
1940             text: "Hello world!"
1941             activeFocusOnPress: false
1942             MouseArea {
1943                 anchors.fill: parent
1944                 onClicked: {
1945                     if (!textEdit.activeFocus) {
1946                         textEdit.forceActiveFocus();
1947                         textEdit.openSoftwareInputPanel();
1948                     } else {
1949                         textEdit.focus = false;
1950                     }
1951                 }
1952                 onPressAndHold: textEdit.closeSoftwareInputPanel();
1953             }
1954         }
1955     \endcode
1956 */
1957 void QSGTextEdit::closeSoftwareInputPanel()
1958 {  
1959     if (qApp) {
1960         if (canvas()) {
1961             QEvent event(QEvent::CloseSoftwareInputPanel);
1962             QApplication::sendEvent(canvas(), &event);
1963         }
1964     }
1965 }
1966
1967 void QSGTextEdit::focusInEvent(QFocusEvent *event)
1968 {
1969     Q_D(const QSGTextEdit);
1970     if (d->showInputPanelOnFocus) {
1971         if (d->focusOnPress && !isReadOnly()) {
1972             openSoftwareInputPanel();
1973         }
1974     }
1975     QSGImplicitSizeItem::focusInEvent(event);
1976 }
1977
1978 void QSGTextEdit::q_canPasteChanged()
1979 {
1980     Q_D(QSGTextEdit);
1981     bool old = d->canPaste;
1982     d->canPaste = d->control->canPaste();
1983     if(old!=d->canPaste)
1984         emit canPasteChanged();
1985 }
1986
1987 QT_END_NAMESPACE