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