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