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