b32e02dcaedc3996ba84d97c28b6eca6f19917be
[profile/ivi/qtdeclarative.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 Quick 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     \qmlproperty enumeration TextEdit::effectiveHorizontalAlignment
462
463     Sets the horizontal and vertical alignment of the text within the TextEdit item's
464     width and height. By default, the text alignment follows the natural alignment
465     of the text, for example text that is read from left to right will be aligned to
466     the left.
467
468     Valid values for \c horizontalAlignment are:
469     \list
470     \o TextEdit.AlignLeft (default)
471     \o TextEdit.AlignRight 
472     \o TextEdit.AlignHCenter
473     \o TextEdit.AlignJustify
474     \endlist
475     
476     Valid values for \c verticalAlignment are:
477     \list
478     \o TextEdit.AlignTop (default)
479     \o TextEdit.AlignBottom
480     \o TextEdit.AlignVCenter
481     \endlist
482
483     When using the attached property LayoutMirroring::enabled to mirror application
484     layouts, the horizontal alignment of text will also be mirrored. However, the property
485     \c horizontalAlignment will remain unchanged. To query the effective horizontal alignment
486     of TextEdit, use the read-only property \c effectiveHorizontalAlignment.
487 */
488 QDeclarativeTextEdit::HAlignment QDeclarativeTextEdit::hAlign() const
489 {
490     Q_D(const QDeclarativeTextEdit);
491     return d->hAlign;
492 }
493
494 void QDeclarativeTextEdit::setHAlign(HAlignment align)
495 {
496     Q_D(QDeclarativeTextEdit);
497     bool forceAlign = d->hAlignImplicit && d->effectiveLayoutMirror;
498     d->hAlignImplicit = false;
499     if (d->setHAlign(align, forceAlign) && isComponentComplete()) {
500         d->updateDefaultTextOption();
501         updateSize();
502     }
503 }
504
505 void QDeclarativeTextEdit::resetHAlign()
506 {
507     Q_D(QDeclarativeTextEdit);
508     d->hAlignImplicit = true;
509     if (d->determineHorizontalAlignment() && isComponentComplete()) {
510         d->updateDefaultTextOption();
511         updateSize();
512     }
513 }
514
515 QDeclarativeTextEdit::HAlignment QDeclarativeTextEdit::effectiveHAlign() const
516 {
517     Q_D(const QDeclarativeTextEdit);
518     QDeclarativeTextEdit::HAlignment effectiveAlignment = d->hAlign;
519     if (!d->hAlignImplicit && d->effectiveLayoutMirror) {
520         switch (d->hAlign) {
521         case QDeclarativeTextEdit::AlignLeft:
522             effectiveAlignment = QDeclarativeTextEdit::AlignRight;
523             break;
524         case QDeclarativeTextEdit::AlignRight:
525             effectiveAlignment = QDeclarativeTextEdit::AlignLeft;
526             break;
527         default:
528             break;
529         }
530     }
531     return effectiveAlignment;
532 }
533
534 bool QDeclarativeTextEditPrivate::setHAlign(QDeclarativeTextEdit::HAlignment alignment, bool forceAlign)
535 {
536     Q_Q(QDeclarativeTextEdit);
537     if (hAlign != alignment || forceAlign) {
538         QDeclarativeTextEdit::HAlignment oldEffectiveHAlign = q->effectiveHAlign();
539         hAlign = alignment;
540         emit q->horizontalAlignmentChanged(alignment);
541         if (oldEffectiveHAlign != q->effectiveHAlign())
542             emit q->effectiveHorizontalAlignmentChanged();
543         return true;
544     }
545     return false;
546 }
547
548 bool QDeclarativeTextEditPrivate::determineHorizontalAlignment()
549 {
550     Q_Q(QDeclarativeTextEdit);
551     if (hAlignImplicit && q->isComponentComplete()) {
552         bool alignToRight = text.isEmpty() ? QApplication::keyboardInputDirection() == Qt::RightToLeft : rightToLeftText;
553         return setHAlign(alignToRight ? QDeclarativeTextEdit::AlignRight : QDeclarativeTextEdit::AlignLeft);
554     }
555     return false;
556 }
557
558 void QDeclarativeTextEditPrivate::mirrorChange()
559 {
560     Q_Q(QDeclarativeTextEdit);
561     if (q->isComponentComplete()) {
562         if (!hAlignImplicit && (hAlign == QDeclarativeTextEdit::AlignRight || hAlign == QDeclarativeTextEdit::AlignLeft)) {
563             updateDefaultTextOption();
564             q->updateSize();
565             emit q->effectiveHorizontalAlignmentChanged();
566         }
567     }
568 }
569
570 QDeclarativeTextEdit::VAlignment QDeclarativeTextEdit::vAlign() const
571 {
572     Q_D(const QDeclarativeTextEdit);
573     return d->vAlign;
574 }
575
576 void QDeclarativeTextEdit::setVAlign(QDeclarativeTextEdit::VAlignment alignment)
577 {
578     Q_D(QDeclarativeTextEdit);
579     if (alignment == d->vAlign)
580         return;
581     d->vAlign = alignment;
582     d->updateDefaultTextOption();
583     updateSize();
584     moveCursorDelegate();
585     emit verticalAlignmentChanged(d->vAlign);
586 }
587
588 /*!
589     \qmlproperty enumeration 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 QDeclarativeTextEdit::WrapMode QDeclarativeTextEdit::wrapMode() const
604 {
605     Q_D(const QDeclarativeTextEdit);
606     return d->wrapMode;
607 }
608
609 void QDeclarativeTextEdit::setWrapMode(WrapMode mode)
610 {
611     Q_D(QDeclarativeTextEdit);
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 TextEdit::lineCount
622     \since Quick 1.1
623
624     Returns the total number of lines in the textEdit item.
625 */
626 int QDeclarativeTextEdit::lineCount() const
627 {
628     Q_D(const QDeclarativeTextEdit);
629     return d->lineCount;
630 }
631
632 /*!
633     \qmlproperty real TextEdit::paintedWidth
634
635     Returns the width of the text, including the width past the width
636     which is covered due to insufficient wrapping if \l wrapMode is set.
637 */
638 qreal QDeclarativeTextEdit::paintedWidth() const
639 {
640     Q_D(const QDeclarativeTextEdit);
641     return d->paintedSize.width();
642 }
643
644 /*!
645     \qmlproperty real TextEdit::paintedHeight
646
647     Returns the height of the text, including the height past the height
648     that is covered if the text does not fit within the set height.
649 */
650 qreal QDeclarativeTextEdit::paintedHeight() const
651 {
652     Q_D(const QDeclarativeTextEdit);
653     return d->paintedSize.height();
654 }
655
656 /*!
657     \qmlmethod rectangle TextEdit::positionToRectangle(position)
658
659     Returns the rectangle at the given \a position in the text. The x, y,
660     and height properties correspond to the cursor that would describe
661     that position.
662 */
663 QRectF QDeclarativeTextEdit::positionToRectangle(int pos) const
664 {
665     Q_D(const QDeclarativeTextEdit);
666     QTextCursor c(d->document);
667     c.setPosition(pos);
668     return d->control->cursorRect(c);
669
670 }
671
672 /*!
673     \qmlmethod int TextEdit::positionAt(int x, int y)
674
675     Returns the text position closest to pixel position (\a x, \a y).
676
677     Position 0 is before the first character, position 1 is after the first character
678     but before the second, and so on until position \l {text}.length, which is after all characters.
679 */
680 int QDeclarativeTextEdit::positionAt(int x, int y) const
681 {
682     Q_D(const QDeclarativeTextEdit);
683     int r = d->document->documentLayout()->hitTest(QPoint(x,y-d->yoff), Qt::FuzzyHit);
684     QTextCursor cursor = d->control->textCursor();
685     if (r > cursor.position()) {
686         // The cursor position includes positions within the preedit text, but only positions in the
687         // same text block are offset so it is possible to get a position that is either part of the
688         // preedit or the next text block.
689         QTextLayout *layout = cursor.block().layout();
690         const int preeditLength = layout
691                 ? layout->preeditAreaText().length()
692                 : 0;
693         if (preeditLength > 0
694                 && d->document->documentLayout()->blockBoundingRect(cursor.block()).contains(x,y-d->yoff)) {
695             r = r > cursor.position() + preeditLength
696                     ? r - preeditLength
697                     : cursor.position();
698         }
699     }
700     return r;
701 }
702
703 void QDeclarativeTextEdit::moveCursorSelection(int pos)
704 {
705     //Note that this is the same as setCursorPosition but with the KeepAnchor flag set
706     Q_D(QDeclarativeTextEdit);
707     QTextCursor cursor = d->control->textCursor();
708     if (cursor.position() == pos)
709         return;
710     cursor.setPosition(pos, QTextCursor::KeepAnchor);
711     d->control->setTextCursor(cursor);
712 }
713
714 /*!
715     \qmlmethod void TextEdit::moveCursorSelection(int position, SelectionMode mode = TextEdit.SelectCharacters)
716     \since Quick 1.1
717
718     Moves the cursor to \a position and updates the selection according to the optional \a mode
719     parameter. (To only move the cursor, set the \l cursorPosition property.)
720
721     When this method is called it additionally sets either the
722     selectionStart or the selectionEnd (whichever was at the previous cursor position)
723     to the specified position. This allows you to easily extend and contract the selected
724     text range.
725
726     The selection mode specifies whether the selection is updated on a per character or a per word
727     basis.  If not specified the selection mode will default to TextEdit.SelectCharacters.
728
729     \list
730     \o TextEdit.SelectCharacters - Sets either the selectionStart or selectionEnd (whichever was at
731     the previous cursor position) to the specified position.
732     \o TextEdit.SelectWords - Sets the selectionStart and selectionEnd to include all
733     words between the specified postion and the previous cursor position.  Words partially in the
734     range are included.
735     \endlist
736
737     For example, take this sequence of calls:
738
739     \code
740         cursorPosition = 5
741         moveCursorSelection(9, TextEdit.SelectCharacters)
742         moveCursorSelection(7, TextEdit.SelectCharacters)
743     \endcode
744
745     This moves the cursor to position 5, extend the selection end from 5 to 9
746     and then retract the selection end from 9 to 7, leaving the text from position 5 to 7
747     selected (the 6th and 7th characters).
748
749     The same sequence with TextEdit.SelectWords will extend the selection start to a word boundary
750     before or on position 5 and extend the selection end to a word boundary on or past position 9.
751 */
752 void QDeclarativeTextEdit::moveCursorSelection(int pos, SelectionMode mode)
753 {
754     Q_D(QDeclarativeTextEdit);
755     QTextCursor cursor = d->control->textCursor();
756     if (cursor.position() == pos)
757         return;
758     if (mode == SelectCharacters) {
759         cursor.setPosition(pos, QTextCursor::KeepAnchor);
760     } else if (cursor.anchor() < pos || (cursor.anchor() == pos && cursor.position() < pos)) {
761         if (cursor.anchor() > cursor.position()) {
762             cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor);
763             cursor.movePosition(QTextCursor::StartOfWord, QTextCursor::KeepAnchor);
764             if (cursor.position() == cursor.anchor())
765                 cursor.movePosition(QTextCursor::PreviousWord, QTextCursor::MoveAnchor);
766             else
767                 cursor.setPosition(cursor.position(), QTextCursor::MoveAnchor);
768         } else {
769             cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor);
770             cursor.movePosition(QTextCursor::StartOfWord, QTextCursor::MoveAnchor);
771         }
772
773         cursor.setPosition(pos, QTextCursor::KeepAnchor);
774         cursor.movePosition(QTextCursor::StartOfWord, QTextCursor::KeepAnchor);
775         if (cursor.position() != pos)
776             cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
777     } else if (cursor.anchor() > pos || (cursor.anchor() == pos && cursor.position() > pos)) {
778         if (cursor.anchor() < cursor.position()) {
779             cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor);
780             cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::MoveAnchor);
781         } else {
782             cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor);
783             cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor);
784             cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
785             if (cursor.position() != cursor.anchor()) {
786                 cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor);
787                 cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::MoveAnchor);
788             }
789         }
790
791         cursor.setPosition(pos, QTextCursor::KeepAnchor);
792         cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
793         if (cursor.position() != pos) {
794             cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor);
795             cursor.movePosition(QTextCursor::StartOfWord, QTextCursor::KeepAnchor);
796         }
797     }
798     d->control->setTextCursor(cursor);
799 }
800
801 /*!
802     \qmlproperty bool TextEdit::cursorVisible
803     If true the text edit shows a cursor.
804
805     This property is set and unset when the text edit gets active focus, but it can also
806     be set directly (useful, for example, if a KeyProxy might forward keys to it).
807 */
808 bool QDeclarativeTextEdit::isCursorVisible() const
809 {
810     Q_D(const QDeclarativeTextEdit);
811     return d->cursorVisible;
812 }
813
814 void QDeclarativeTextEdit::setCursorVisible(bool on)
815 {
816     Q_D(QDeclarativeTextEdit);
817     if (d->cursorVisible == on)
818         return;
819     d->cursorVisible = on;
820     QFocusEvent focusEvent(on ? QEvent::FocusIn : QEvent::FocusOut);
821     if (!on && !d->persistentSelection)
822         d->control->setCursorIsFocusIndicator(true);
823     d->control->processEvent(&focusEvent, QPointF(0, -d->yoff));
824     emit cursorVisibleChanged(d->cursorVisible);
825 }
826
827 /*!
828     \qmlproperty int TextEdit::cursorPosition
829     The position of the cursor in the TextEdit.
830 */
831 int QDeclarativeTextEdit::cursorPosition() const
832 {
833     Q_D(const QDeclarativeTextEdit);
834     return d->control->textCursor().position();
835 }
836
837 void QDeclarativeTextEdit::setCursorPosition(int pos)
838 {
839     Q_D(QDeclarativeTextEdit);
840     if (pos < 0 || pos > d->text.length())
841         return;
842     QTextCursor cursor = d->control->textCursor();
843     if (cursor.position() == pos && cursor.anchor() == pos)
844         return;
845     cursor.setPosition(pos);
846     d->control->setTextCursor(cursor);
847 }
848
849 /*!
850     \qmlproperty Component TextEdit::cursorDelegate
851     The delegate for the cursor in the TextEdit.
852
853     If you set a cursorDelegate for a TextEdit, this delegate will be used for
854     drawing the cursor instead of the standard cursor. An instance of the
855     delegate will be created and managed by the text edit when a cursor is
856     needed, and the x and y properties of delegate instance will be set so as
857     to be one pixel before the top left of the current character.
858
859     Note that the root item of the delegate component must be a QDeclarativeItem or
860     QDeclarativeItem derived item.
861 */
862 QDeclarativeComponent* QDeclarativeTextEdit::cursorDelegate() const
863 {
864     Q_D(const QDeclarativeTextEdit);
865     return d->cursorComponent;
866 }
867
868 void QDeclarativeTextEdit::setCursorDelegate(QDeclarativeComponent* c)
869 {
870     Q_D(QDeclarativeTextEdit);
871     if(d->cursorComponent){
872         if(d->cursor){
873             d->control->setCursorWidth(-1);
874             dirtyCache(cursorRectangle());
875             delete d->cursor;
876             d->cursor = 0;
877         }
878     }
879     d->cursorComponent = c;
880     if(c && c->isReady()){
881         loadCursorDelegate();
882     }else{
883         if(c)
884             connect(c, SIGNAL(statusChanged()),
885                     this, SLOT(loadCursorDelegate()));
886     }
887
888     emit cursorDelegateChanged();
889 }
890
891 void QDeclarativeTextEdit::loadCursorDelegate()
892 {
893     Q_D(QDeclarativeTextEdit);
894     if(d->cursorComponent->isLoading())
895         return;
896     d->cursor = qobject_cast<QDeclarativeItem*>(d->cursorComponent->create(qmlContext(this)));
897     if(d->cursor){
898         d->control->setCursorWidth(0);
899         dirtyCache(cursorRectangle());
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         qmlInfo(this) << "Error loading cursor delegate.";
906     }
907 }
908
909 /*!
910     \qmlproperty int TextEdit::selectionStart
911
912     The cursor position before the first character in the current selection.
913
914     This property is read-only. To change the selection, use select(start,end),
915     selectAll(), or selectWord().
916
917     \sa selectionEnd, cursorPosition, selectedText
918 */
919 int QDeclarativeTextEdit::selectionStart() const
920 {
921     Q_D(const QDeclarativeTextEdit);
922     return d->control->textCursor().selectionStart();
923 }
924
925 /*!
926     \qmlproperty int TextEdit::selectionEnd
927
928     The cursor position after the last character in the current selection.
929
930     This property is read-only. To change the selection, use select(start,end),
931     selectAll(), or selectWord().
932
933     \sa selectionStart, cursorPosition, selectedText
934 */
935 int QDeclarativeTextEdit::selectionEnd() const
936 {
937     Q_D(const QDeclarativeTextEdit);
938     return d->control->textCursor().selectionEnd();
939 }
940
941 /*!
942     \qmlproperty string TextEdit::selectedText
943
944     This read-only property provides the text currently selected in the
945     text edit.
946
947     It is equivalent to the following snippet, but is faster and easier
948     to use.
949     \code
950     //myTextEdit is the id of the TextEdit
951     myTextEdit.text.toString().substring(myTextEdit.selectionStart,
952             myTextEdit.selectionEnd);
953     \endcode
954 */
955 QString QDeclarativeTextEdit::selectedText() const
956 {
957     Q_D(const QDeclarativeTextEdit);
958     return d->control->textCursor().selectedText();
959 }
960
961 /*!
962     \qmlproperty bool TextEdit::activeFocusOnPress
963
964     Whether the TextEdit should gain active focus on a mouse press. By default this is
965     set to true.
966 */
967 bool QDeclarativeTextEdit::focusOnPress() const
968 {
969     Q_D(const QDeclarativeTextEdit);
970     return d->focusOnPress;
971 }
972
973 void QDeclarativeTextEdit::setFocusOnPress(bool on)
974 {
975     Q_D(QDeclarativeTextEdit);
976     if (d->focusOnPress == on)
977         return;
978     d->focusOnPress = on;
979     emit activeFocusOnPressChanged(d->focusOnPress);
980 }
981
982 /*!
983     \qmlproperty bool TextEdit::persistentSelection
984
985     Whether the TextEdit should keep the selection visible when it loses active focus to another
986     item in the scene. By default this is set to true;
987 */
988 bool QDeclarativeTextEdit::persistentSelection() const
989 {
990     Q_D(const QDeclarativeTextEdit);
991     return d->persistentSelection;
992 }
993
994 void QDeclarativeTextEdit::setPersistentSelection(bool on)
995 {
996     Q_D(QDeclarativeTextEdit);
997     if (d->persistentSelection == on)
998         return;
999     d->persistentSelection = on;
1000     emit persistentSelectionChanged(d->persistentSelection);
1001 }
1002
1003 /*
1004    \qmlproperty real TextEdit::textMargin
1005
1006    The margin, in pixels, around the text in the TextEdit.
1007 */
1008 qreal QDeclarativeTextEdit::textMargin() const
1009 {
1010     Q_D(const QDeclarativeTextEdit);
1011     return d->textMargin;
1012 }
1013
1014 void QDeclarativeTextEdit::setTextMargin(qreal margin)
1015 {
1016     Q_D(QDeclarativeTextEdit);
1017     if (d->textMargin == margin)
1018         return;
1019     d->textMargin = margin;
1020     d->document->setDocumentMargin(d->textMargin);
1021     emit textMarginChanged(d->textMargin);
1022 }
1023
1024 void QDeclarativeTextEdit::geometryChanged(const QRectF &newGeometry,
1025                                   const QRectF &oldGeometry)
1026 {
1027     if (newGeometry.width() != oldGeometry.width())
1028         updateSize();
1029     QDeclarativePaintedItem::geometryChanged(newGeometry, oldGeometry);
1030 }
1031
1032 /*!
1033     Ensures any delayed caching or data loading the class
1034     needs to performed is complete.
1035 */
1036 void QDeclarativeTextEdit::componentComplete()
1037 {
1038     Q_D(QDeclarativeTextEdit);
1039     QDeclarativePaintedItem::componentComplete();
1040     if (d->dirty) {
1041         d->determineHorizontalAlignment();
1042         d->updateDefaultTextOption();
1043         updateSize();
1044         d->dirty = false;
1045     }
1046 }
1047
1048 /*!
1049     \qmlproperty bool TextEdit::selectByMouse
1050
1051     Defaults to false.
1052
1053     If true, the user can use the mouse to select text in some
1054     platform-specific way. Note that for some platforms this may
1055     not be an appropriate interaction (eg. may conflict with how
1056     the text needs to behave inside a Flickable.
1057 */
1058 bool QDeclarativeTextEdit::selectByMouse() const
1059 {
1060     Q_D(const QDeclarativeTextEdit);
1061     return d->selectByMouse;
1062 }
1063
1064 void QDeclarativeTextEdit::setSelectByMouse(bool on)
1065 {
1066     Q_D(QDeclarativeTextEdit);
1067     if (d->selectByMouse != on) {
1068         d->selectByMouse = on;
1069         setKeepMouseGrab(on);
1070         if (on)
1071             setTextInteractionFlags(d->control->textInteractionFlags() | Qt::TextSelectableByMouse);
1072         else
1073             setTextInteractionFlags(d->control->textInteractionFlags() & ~Qt::TextSelectableByMouse);
1074         emit selectByMouseChanged(on);
1075     }
1076 }
1077
1078
1079 /*!
1080     \qmlproperty enum TextEdit::mouseSelectionMode
1081     \since Quick 1.1
1082
1083     Specifies how text should be selected using a mouse.
1084
1085     \list
1086     \o TextEdit.SelectCharacters - The selection is updated with individual characters. (Default)
1087     \o TextEdit.SelectWords - The selection is updated with whole words.
1088     \endlist
1089
1090     This property only applies when \l selectByMouse is true.
1091 */
1092
1093 QDeclarativeTextEdit::SelectionMode QDeclarativeTextEdit::mouseSelectionMode() const
1094 {
1095     Q_D(const QDeclarativeTextEdit);
1096     return d->mouseSelectionMode;
1097 }
1098
1099 void QDeclarativeTextEdit::setMouseSelectionMode(SelectionMode mode)
1100 {
1101     Q_D(QDeclarativeTextEdit);
1102     if (d->mouseSelectionMode != mode) {
1103         d->mouseSelectionMode = mode;
1104         d->control->setWordSelectionEnabled(mode == SelectWords);
1105         emit mouseSelectionModeChanged(mode);
1106     }
1107 }
1108
1109 /*!
1110     \qmlproperty bool TextEdit::readOnly
1111
1112     Whether the user can interact with the TextEdit item. If this
1113     property is set to true the text cannot be edited by user interaction.
1114
1115     By default this property is false.
1116 */
1117 void QDeclarativeTextEdit::setReadOnly(bool r)
1118 {
1119     Q_D(QDeclarativeTextEdit);
1120     if (r == isReadOnly())
1121         return;
1122
1123     setFlag(QGraphicsItem::ItemAcceptsInputMethod, !r);
1124
1125     Qt::TextInteractionFlags flags = Qt::LinksAccessibleByMouse;
1126     if (d->selectByMouse)
1127         flags = flags | Qt::TextSelectableByMouse;
1128     if (!r)
1129         flags = flags | Qt::TextSelectableByKeyboard | Qt::TextEditable;
1130     d->control->setTextInteractionFlags(flags);
1131     if (!r)
1132         d->control->moveCursor(QTextCursor::End);
1133
1134     emit readOnlyChanged(r);
1135 }
1136
1137 bool QDeclarativeTextEdit::isReadOnly() const
1138 {
1139     Q_D(const QDeclarativeTextEdit);
1140     return !(d->control->textInteractionFlags() & Qt::TextEditable);
1141 }
1142
1143 /*!
1144     Sets how the text edit should interact with user input to the given
1145     \a flags.
1146 */
1147 void QDeclarativeTextEdit::setTextInteractionFlags(Qt::TextInteractionFlags flags)
1148 {
1149     Q_D(QDeclarativeTextEdit);
1150     d->control->setTextInteractionFlags(flags);
1151 }
1152
1153 /*!
1154     Returns the flags specifying how the text edit should interact
1155     with user input.
1156 */
1157 Qt::TextInteractionFlags QDeclarativeTextEdit::textInteractionFlags() const
1158 {
1159     Q_D(const QDeclarativeTextEdit);
1160     return d->control->textInteractionFlags();
1161 }
1162
1163 /*!
1164     \qmlproperty rectangle TextEdit::cursorRectangle
1165
1166     The rectangle where the text cursor is rendered
1167     within the text edit. Read-only.
1168 */
1169 QRect QDeclarativeTextEdit::cursorRectangle() const
1170 {
1171     Q_D(const QDeclarativeTextEdit);
1172     return d->control->cursorRect().toRect().translated(0,d->yoff);
1173 }
1174
1175
1176 /*!
1177 \overload
1178 Handles the given \a event.
1179 */
1180 bool QDeclarativeTextEdit::event(QEvent *event)
1181 {
1182     Q_D(QDeclarativeTextEdit);
1183     if (event->type() == QEvent::ShortcutOverride) {
1184         d->control->processEvent(event, QPointF(0, -d->yoff));
1185         return event->isAccepted();
1186     }
1187     return QDeclarativePaintedItem::event(event);
1188 }
1189
1190 /*!
1191 \overload
1192 Handles the given key \a event.
1193 */
1194 void QDeclarativeTextEdit::keyPressEvent(QKeyEvent *event)
1195 {
1196     Q_D(QDeclarativeTextEdit);
1197     keyPressPreHandler(event);
1198     if (!event->isAccepted())
1199         d->control->processEvent(event, QPointF(0, -d->yoff));
1200     if (!event->isAccepted())
1201         QDeclarativePaintedItem::keyPressEvent(event);
1202 }
1203
1204 /*!
1205 \overload
1206 Handles the given key \a event.
1207 */
1208 void QDeclarativeTextEdit::keyReleaseEvent(QKeyEvent *event)
1209 {
1210     Q_D(QDeclarativeTextEdit);
1211     keyReleasePreHandler(event);
1212     if (!event->isAccepted())
1213         d->control->processEvent(event, QPointF(0, -d->yoff));
1214     if (!event->isAccepted())
1215         QDeclarativePaintedItem::keyReleaseEvent(event);
1216 }
1217
1218 void QDeclarativeTextEditPrivate::focusChanged(bool hasFocus)
1219 {
1220     Q_Q(QDeclarativeTextEdit);
1221     q->setCursorVisible(hasFocus && scene && scene->hasFocus());
1222     QDeclarativeItemPrivate::focusChanged(hasFocus);
1223 }
1224
1225 /*!
1226     \qmlmethod void TextEdit::deselect()
1227     \since Quick 1.1
1228
1229     Removes active text selection.
1230 */
1231 void QDeclarativeTextEdit::deselect()
1232 {
1233     Q_D(QDeclarativeTextEdit);
1234     QTextCursor c = d->control->textCursor();
1235     c.clearSelection();
1236     d->control->setTextCursor(c);
1237 }
1238
1239 /*!
1240     \qmlmethod void TextEdit::selectAll()
1241
1242     Causes all text to be selected.
1243 */
1244 void QDeclarativeTextEdit::selectAll()
1245 {
1246     Q_D(QDeclarativeTextEdit);
1247     d->control->selectAll();
1248 }
1249
1250 /*!
1251     \qmlmethod void TextEdit::selectWord()
1252
1253     Causes the word closest to the current cursor position to be selected.
1254 */
1255 void QDeclarativeTextEdit::selectWord()
1256 {
1257     Q_D(QDeclarativeTextEdit);
1258     QTextCursor c = d->control->textCursor();
1259     c.select(QTextCursor::WordUnderCursor);
1260     d->control->setTextCursor(c);
1261 }
1262
1263 /*!
1264     \qmlmethod void TextEdit::select(int start, int end)
1265
1266     Causes the text from \a start to \a end to be selected.
1267
1268     If either start or end is out of range, the selection is not changed.
1269
1270     After calling this, selectionStart will become the lesser
1271     and selectionEnd will become the greater (regardless of the order passed
1272     to this method).
1273
1274     \sa selectionStart, selectionEnd
1275 */
1276 void QDeclarativeTextEdit::select(int start, int end)
1277 {
1278     Q_D(QDeclarativeTextEdit);
1279     if (start < 0 || end < 0 || start > d->text.length() || end > d->text.length())
1280         return;
1281     QTextCursor cursor = d->control->textCursor();
1282     cursor.beginEditBlock();
1283     cursor.setPosition(start, QTextCursor::MoveAnchor);
1284     cursor.setPosition(end, QTextCursor::KeepAnchor);
1285     cursor.endEditBlock();
1286     d->control->setTextCursor(cursor);
1287
1288     // QTBUG-11100
1289     updateSelectionMarkers();
1290 }
1291
1292 /*!
1293     \qmlmethod void TextEdit::isRightToLeft(int start, int end)
1294
1295     Returns true if the natural reading direction of the editor text
1296     found between positions \a start and \a end is right to left.
1297 */
1298 bool QDeclarativeTextEdit::isRightToLeft(int start, int end)
1299 {
1300     Q_D(QDeclarativeTextEdit);
1301     if (start > end) {
1302         qmlInfo(this) << "isRightToLeft(start, end) called with the end property being smaller than the start.";
1303         return false;
1304     } else {
1305         return d->text.mid(start, end - start).isRightToLeft();
1306     }
1307 }
1308
1309 #ifndef QT_NO_CLIPBOARD
1310 /*!
1311     \qmlmethod TextEdit::cut()
1312
1313     Moves the currently selected text to the system clipboard.
1314 */
1315 void QDeclarativeTextEdit::cut()
1316 {
1317     Q_D(QDeclarativeTextEdit);
1318     d->control->cut();
1319 }
1320
1321 /*!
1322     \qmlmethod TextEdit::copy()
1323
1324     Copies the currently selected text to the system clipboard.
1325 */
1326 void QDeclarativeTextEdit::copy()
1327 {
1328     Q_D(QDeclarativeTextEdit);
1329     d->control->copy();
1330 }
1331
1332 /*!
1333     \qmlmethod TextEdit::paste()
1334
1335     Replaces the currently selected text by the contents of the system clipboard.
1336 */
1337 void QDeclarativeTextEdit::paste()
1338 {
1339     Q_D(QDeclarativeTextEdit);
1340     d->control->paste();
1341 }
1342 #endif // QT_NO_CLIPBOARD
1343
1344 /*!
1345 \overload
1346 Handles the given mouse \a event.
1347 */
1348 void QDeclarativeTextEdit::mousePressEvent(QGraphicsSceneMouseEvent *event)
1349 {
1350     Q_D(QDeclarativeTextEdit);
1351     if (d->focusOnPress){
1352         bool hadActiveFocus = hasActiveFocus();
1353         forceActiveFocus();
1354         if (d->showInputPanelOnFocus) {
1355             if (hasActiveFocus() && hadActiveFocus && !isReadOnly()) {
1356                 // re-open input panel on press if already focused
1357                 openSoftwareInputPanel();
1358             }
1359         } else { // show input panel on click
1360             if (hasActiveFocus() && !hadActiveFocus) {
1361                 d->clickCausedFocus = true;
1362             }
1363         }
1364     }
1365
1366     d->control->processEvent(event, QPointF(0, -d->yoff));
1367     if (!event->isAccepted())
1368         QDeclarativePaintedItem::mousePressEvent(event);
1369 }
1370
1371 /*!
1372 \overload
1373 Handles the given mouse \a event.
1374 */
1375 void QDeclarativeTextEdit::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
1376 {
1377     Q_D(QDeclarativeTextEdit);
1378     d->control->processEvent(event, QPointF(0, -d->yoff));
1379     if (!d->showInputPanelOnFocus) { // input panel on click
1380         if (d->focusOnPress && !isReadOnly() && boundingRect().contains(event->pos())) {
1381             if (QGraphicsView * view = qobject_cast<QGraphicsView*>(qApp->focusWidget())) {
1382                 if (view->scene() && view->scene() == scene()) {
1383                     qt_widget_private(view)->handleSoftwareInputPanel(event->button(), d->clickCausedFocus);
1384                 }
1385             }
1386         }
1387     }
1388     d->clickCausedFocus = false;
1389
1390     if (!event->isAccepted())
1391         QDeclarativePaintedItem::mouseReleaseEvent(event);
1392 }
1393
1394 /*!
1395 \overload
1396 Handles the given mouse \a event.
1397 */
1398 void QDeclarativeTextEdit::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
1399 {
1400     Q_D(QDeclarativeTextEdit);
1401
1402     d->control->processEvent(event, QPointF(0, -d->yoff));
1403     if (!event->isAccepted())
1404         QDeclarativePaintedItem::mouseDoubleClickEvent(event);
1405
1406 }
1407
1408 /*!
1409 \overload
1410 Handles the given mouse \a event.
1411 */
1412 void QDeclarativeTextEdit::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
1413 {
1414     Q_D(QDeclarativeTextEdit);
1415     d->control->processEvent(event, QPointF(0, -d->yoff));
1416     if (!event->isAccepted())
1417         QDeclarativePaintedItem::mouseMoveEvent(event);
1418 }
1419
1420 /*!
1421 \overload
1422 Handles the given input method \a event.
1423 */
1424 void QDeclarativeTextEdit::inputMethodEvent(QInputMethodEvent *event)
1425 {
1426     Q_D(QDeclarativeTextEdit);
1427     const bool wasComposing = isInputMethodComposing();
1428     d->control->processEvent(event, QPointF(0, -d->yoff));
1429     if (wasComposing != isInputMethodComposing())
1430         emit inputMethodComposingChanged();
1431 }
1432
1433 /*!
1434 \overload
1435 Returns the value of the given \a property.
1436 */
1437 QVariant QDeclarativeTextEdit::inputMethodQuery(Qt::InputMethodQuery property) const
1438 {
1439     Q_D(const QDeclarativeTextEdit);
1440     return d->control->inputMethodQuery(property);
1441 }
1442
1443 /*!
1444 Draws the contents of the text edit using the given \a painter within
1445 the given \a bounds.
1446 */
1447 void QDeclarativeTextEdit::drawContents(QPainter *painter, const QRect &bounds)
1448 {
1449     Q_D(QDeclarativeTextEdit);
1450
1451     painter->setRenderHint(QPainter::TextAntialiasing, true);
1452     painter->translate(0,d->yoff);
1453
1454     d->control->drawContents(painter, bounds.translated(0,-d->yoff));
1455
1456     painter->translate(0,-d->yoff);
1457 }
1458
1459 void QDeclarativeTextEdit::updateImgCache(const QRectF &rf)
1460 {
1461     Q_D(const QDeclarativeTextEdit);
1462     QRect r;
1463     if (!rf.isValid()) {
1464         r = QRect(0,0,INT_MAX,INT_MAX);
1465     } else {
1466         r = rf.toRect();
1467         if (r.height() > INT_MAX/2) {
1468             // Take care of overflow when translating "everything"
1469             r.setTop(r.y() + d->yoff);
1470             r.setBottom(INT_MAX/2);
1471         } else {
1472             r = r.translated(0,d->yoff);
1473         }
1474     }
1475     dirtyCache(r);
1476     emit update();
1477 }
1478
1479 /*!
1480     \qmlproperty bool TextEdit::smooth
1481
1482     This property holds whether the text is smoothly scaled or transformed.
1483
1484     Smooth filtering gives better visual quality, but is slower.  If
1485     the item is displayed at its natural size, this property has no visual or
1486     performance effect.
1487
1488     \note Generally scaling artifacts are only visible if the item is stationary on
1489     the screen.  A common pattern when animating an item is to disable smooth
1490     filtering at the beginning of the animation and reenable it at the conclusion.
1491 */
1492
1493 /*!
1494     \qmlproperty bool TextEdit::canPaste
1495     \since QtQuick 1.1
1496
1497     Returns true if the TextEdit is writable and the content of the clipboard is
1498     suitable for pasting into the TextEdit.
1499 */
1500 bool QDeclarativeTextEdit::canPaste() const
1501 {
1502     Q_D(const QDeclarativeTextEdit);
1503     return d->canPaste;
1504 }
1505
1506 /*!
1507     \qmlproperty bool TextEdit::inputMethodComposing
1508
1509     \since QtQuick 1.1
1510
1511     This property holds whether the TextEdit has partial text input from an
1512     input method.
1513
1514     While it is composing an input method may rely on mouse or key events from
1515     the TextEdit to edit or commit the partial text.  This property can be used
1516     to determine when to disable events handlers that may interfere with the
1517     correct operation of an input method.
1518 */
1519 bool QDeclarativeTextEdit::isInputMethodComposing() const
1520 {
1521     Q_D(const QDeclarativeTextEdit);
1522     if (QTextLayout *layout = d->control->textCursor().block().layout())
1523         return layout->preeditAreaText().length() > 0;
1524     return false;
1525 }
1526
1527 void QDeclarativeTextEditPrivate::init()
1528 {
1529     Q_Q(QDeclarativeTextEdit);
1530
1531     q->setSmooth(smooth);
1532     q->setAcceptedMouseButtons(Qt::LeftButton);
1533     q->setFlag(QGraphicsItem::ItemHasNoContents, false);
1534     q->setFlag(QGraphicsItem::ItemAcceptsInputMethod);
1535
1536     control = new QTextControl(q);
1537     control->setIgnoreUnusedNavigationEvents(true);
1538     control->setTextInteractionFlags(Qt::LinksAccessibleByMouse | Qt::TextSelectableByKeyboard | Qt::TextEditable);
1539     control->setDragEnabled(false);
1540
1541     // QTextControl follows the default text color
1542     // defined by the platform, declarative text
1543     // should be black by default
1544     QPalette pal = control->palette();
1545     if (pal.color(QPalette::Text) != color) {
1546         pal.setColor(QPalette::Text, color);
1547         control->setPalette(pal);
1548     }
1549
1550     QObject::connect(control, SIGNAL(updateRequest(QRectF)), q, SLOT(updateImgCache(QRectF)));
1551
1552     QObject::connect(control, SIGNAL(textChanged()), q, SLOT(q_textChanged()));
1553     QObject::connect(control, SIGNAL(selectionChanged()), q, SIGNAL(selectionChanged()));
1554     QObject::connect(control, SIGNAL(selectionChanged()), q, SLOT(updateSelectionMarkers()));
1555     QObject::connect(control, SIGNAL(cursorPositionChanged()), q, SLOT(updateSelectionMarkers()));
1556     QObject::connect(control, SIGNAL(cursorPositionChanged()), q, SIGNAL(cursorPositionChanged()));
1557     QObject::connect(control, SIGNAL(microFocusChanged()), q, SLOT(moveCursorDelegate()));
1558     QObject::connect(control, SIGNAL(linkActivated(QString)), q, SIGNAL(linkActivated(QString)));
1559 #ifndef QT_NO_CLIPBOARD
1560     QObject::connect(q, SIGNAL(readOnlyChanged(bool)), q, SLOT(q_canPasteChanged()));
1561     QObject::connect(QApplication::clipboard(), SIGNAL(dataChanged()), q, SLOT(q_canPasteChanged()));
1562     canPaste = control->canPaste();
1563 #endif
1564
1565     document = control->document();
1566     document->setDefaultFont(font);
1567     document->setDocumentMargin(textMargin);
1568     document->setUndoRedoEnabled(false); // flush undo buffer.
1569     document->setUndoRedoEnabled(true);
1570     updateDefaultTextOption();
1571 }
1572
1573 void QDeclarativeTextEdit::q_textChanged()
1574 {
1575     Q_D(QDeclarativeTextEdit);
1576     d->text = text();
1577     d->rightToLeftText = d->document->begin().layout()->engine()->isRightToLeft();
1578     d->determineHorizontalAlignment();
1579     d->updateDefaultTextOption();
1580     updateSize();
1581     updateTotalLines();
1582     emit textChanged(d->text);
1583 }
1584
1585 void QDeclarativeTextEdit::moveCursorDelegate()
1586 {
1587     Q_D(QDeclarativeTextEdit);
1588     updateMicroFocus();
1589     emit cursorRectangleChanged();
1590     if(!d->cursor)
1591         return;
1592     QRectF cursorRect = cursorRectangle();
1593     d->cursor->setX(cursorRect.x());
1594     d->cursor->setY(cursorRect.y());
1595 }
1596
1597 void QDeclarativeTextEditPrivate::updateSelection()
1598 {
1599     Q_Q(QDeclarativeTextEdit);
1600     QTextCursor cursor = control->textCursor();
1601     bool startChange = (lastSelectionStart != cursor.selectionStart());
1602     bool endChange = (lastSelectionEnd != cursor.selectionEnd());
1603     cursor.beginEditBlock();
1604     cursor.setPosition(lastSelectionStart, QTextCursor::MoveAnchor);
1605     cursor.setPosition(lastSelectionEnd, QTextCursor::KeepAnchor);
1606     cursor.endEditBlock();
1607     control->setTextCursor(cursor);
1608     if(startChange)
1609         q->selectionStartChanged();
1610     if(endChange)
1611         q->selectionEndChanged();
1612 }
1613
1614 void QDeclarativeTextEdit::updateSelectionMarkers()
1615 {
1616     Q_D(QDeclarativeTextEdit);
1617     if(d->lastSelectionStart != d->control->textCursor().selectionStart()){
1618         d->lastSelectionStart = d->control->textCursor().selectionStart();
1619         emit selectionStartChanged();
1620     }
1621     if(d->lastSelectionEnd != d->control->textCursor().selectionEnd()){
1622         d->lastSelectionEnd = d->control->textCursor().selectionEnd();
1623         emit selectionEndChanged();
1624     }
1625 }
1626
1627 QRectF QDeclarativeTextEdit::boundingRect() const
1628 {
1629     Q_D(const QDeclarativeTextEdit);
1630     QRectF r = QDeclarativePaintedItem::boundingRect();
1631     int cursorWidth = 1;
1632     if(d->cursor)
1633         cursorWidth = d->cursor->width();
1634     if(!d->document->isEmpty())
1635         cursorWidth += 3;// ### Need a better way of accounting for space between char and cursor
1636
1637     // Could include font max left/right bearings to either side of rectangle.
1638
1639     r.setRight(r.right() + cursorWidth);
1640     return r.translated(0,d->yoff);
1641 }
1642
1643 qreal QDeclarativeTextEditPrivate::implicitWidth() const
1644 {
1645     Q_Q(const QDeclarativeTextEdit);
1646     if (!requireImplicitWidth) {
1647         // We don't calculate implicitWidth unless it is required.
1648         // We need to force a size update now to ensure implicitWidth is calculated
1649         const_cast<QDeclarativeTextEditPrivate*>(this)->requireImplicitWidth = true;
1650         const_cast<QDeclarativeTextEdit*>(q)->updateSize();
1651     }
1652     return mImplicitWidth;
1653 }
1654
1655 //### we should perhaps be a bit smarter here -- depending on what has changed, we shouldn't
1656 //    need to do all the calculations each time
1657 void QDeclarativeTextEdit::updateSize()
1658 {
1659     Q_D(QDeclarativeTextEdit);
1660     if (isComponentComplete()) {
1661         qreal naturalWidth = d->mImplicitWidth;
1662         // ### assumes that if the width is set, the text will fill to edges
1663         // ### (unless wrap is false, then clipping will occur)
1664         if (widthValid()) {
1665             if (!d->requireImplicitWidth) {
1666                 emit implicitWidthChanged();
1667                 // if the implicitWidth is used, then updateSize() has already been called (recursively)
1668                 if (d->requireImplicitWidth)
1669                     return;
1670             }
1671             if (d->requireImplicitWidth) {
1672                 d->document->setTextWidth(-1);
1673                 naturalWidth = d->document->idealWidth();
1674             }
1675             if (d->document->textWidth() != width())
1676                 d->document->setTextWidth(width());
1677         } else {
1678             d->document->setTextWidth(-1);
1679         }
1680         QFontMetrics fm = QFontMetrics(d->font);
1681         int dy = height();
1682         dy -= (int)d->document->size().height();
1683
1684         int nyoff;
1685         if (heightValid()) {
1686             if (d->vAlign == AlignBottom)
1687                 nyoff = dy;
1688             else if (d->vAlign == AlignVCenter)
1689                 nyoff = dy/2;
1690             else
1691                 nyoff = 0;
1692         } else {
1693             nyoff = 0;
1694         }
1695         if (nyoff != d->yoff) {
1696             prepareGeometryChange();
1697             d->yoff = nyoff;
1698         }
1699         setBaselineOffset(fm.ascent() + d->yoff + d->textMargin);
1700
1701         //### need to comfirm cost of always setting these
1702         int newWidth = qCeil(d->document->idealWidth());
1703         if (!widthValid() && d->document->textWidth() != newWidth)
1704             d->document->setTextWidth(newWidth); // ### Text does not align if width is not set (QTextDoc bug)
1705         // ### Setting the implicitWidth triggers another updateSize(), and unless there are bindings nothing has changed.
1706         if (!widthValid())
1707             setImplicitWidth(newWidth);
1708         else if (d->requireImplicitWidth)
1709             setImplicitWidth(naturalWidth);
1710         qreal newHeight = d->document->isEmpty() ? fm.height() : (int)d->document->size().height();
1711         setImplicitHeight(newHeight);
1712
1713         d->paintedSize = QSize(newWidth, newHeight);
1714         setContentsSize(d->paintedSize);
1715         emit paintedSizeChanged();
1716     } else {
1717         d->dirty = true;
1718     }
1719     emit update();
1720 }
1721
1722 void QDeclarativeTextEdit::updateTotalLines()
1723 {
1724     Q_D(QDeclarativeTextEdit);
1725
1726     int subLines = 0;
1727
1728     for (QTextBlock it = d->document->begin(); it != d->document->end(); it = it.next()) {
1729         QTextLayout *layout = it.layout();
1730         if (!layout)
1731             continue;
1732         subLines += layout->lineCount()-1;
1733     }
1734
1735     int newTotalLines = d->document->lineCount() + subLines;
1736     if (d->lineCount != newTotalLines) {
1737         d->lineCount = newTotalLines;
1738         emit lineCountChanged();
1739     }
1740 }
1741
1742 void QDeclarativeTextEditPrivate::updateDefaultTextOption()
1743 {
1744     Q_Q(QDeclarativeTextEdit);
1745     QTextOption opt = document->defaultTextOption();
1746     int oldAlignment = opt.alignment();
1747
1748     QDeclarativeTextEdit::HAlignment horizontalAlignment = q->effectiveHAlign();
1749     if (rightToLeftText) {
1750         if (horizontalAlignment == QDeclarativeTextEdit::AlignLeft)
1751             horizontalAlignment = QDeclarativeTextEdit::AlignRight;
1752         else if (horizontalAlignment == QDeclarativeTextEdit::AlignRight)
1753             horizontalAlignment = QDeclarativeTextEdit::AlignLeft;
1754     }
1755     opt.setAlignment((Qt::Alignment)(int)(horizontalAlignment | vAlign));
1756
1757     QTextOption::WrapMode oldWrapMode = opt.wrapMode();
1758     opt.setWrapMode(QTextOption::WrapMode(wrapMode));
1759
1760     if (oldWrapMode == opt.wrapMode() && oldAlignment == opt.alignment())
1761         return;
1762     document->setDefaultTextOption(opt);
1763 }
1764
1765
1766 /*!
1767     \qmlmethod void TextEdit::openSoftwareInputPanel()
1768
1769     Opens software input panels like virtual keyboards for typing, useful for
1770     customizing when you want the input keyboard to be shown and hidden in
1771     your application.
1772
1773     By default the opening of input panels follows the platform style. On Symbian^1 and
1774     Symbian^3 -based devices the panels are opened by clicking TextEdit. On other platforms
1775     the panels are automatically opened when TextEdit element gains active focus. Input panels are
1776     always closed if no editor has active focus.
1777
1778     You can disable the automatic behavior by setting the property \c activeFocusOnPress to false
1779     and use functions openSoftwareInputPanel() and closeSoftwareInputPanel() to implement
1780     the behavior you want.
1781
1782     Only relevant on platforms, which provide virtual keyboards.
1783
1784     \code
1785         import QtQuick 1.0
1786         TextEdit {
1787             id: textEdit
1788             text: "Hello world!"
1789             activeFocusOnPress: false
1790             MouseArea {
1791                 anchors.fill: parent
1792                 onClicked: {
1793                     if (!textEdit.activeFocus) {
1794                         textEdit.forceActiveFocus();
1795                         textEdit.openSoftwareInputPanel();
1796                     } else {
1797                         textEdit.focus = false;
1798                     }
1799                 }
1800                 onPressAndHold: textEdit.closeSoftwareInputPanel();
1801             }
1802         }
1803     \endcode
1804 */
1805 void QDeclarativeTextEdit::openSoftwareInputPanel()
1806 {
1807     QEvent event(QEvent::RequestSoftwareInputPanel);
1808     if (qApp) {
1809         if (QGraphicsView * view = qobject_cast<QGraphicsView*>(qApp->focusWidget())) {
1810             if (view->scene() && view->scene() == scene()) {
1811                 QApplication::sendEvent(view, &event);
1812             }
1813         }
1814     }
1815 }
1816
1817 /*!
1818     \qmlmethod void TextEdit::closeSoftwareInputPanel()
1819
1820     Closes a software input panel like a virtual keyboard shown on the screen, useful
1821     for customizing when you want the input keyboard to be shown and hidden in
1822     your application.
1823
1824     By default the opening of input panels follows the platform style. On Symbian^1 and
1825     Symbian^3 -based devices the panels are opened by clicking TextEdit. On other platforms
1826     the panels are automatically opened when TextEdit element gains active focus. Input panels are
1827     always closed if no editor has active focus.
1828
1829     You can disable the automatic behavior by setting the property \c activeFocusOnPress to false
1830     and use functions openSoftwareInputPanel() and closeSoftwareInputPanel() to implement
1831     the behavior you want.
1832
1833     Only relevant on platforms, which provide virtual keyboards.
1834
1835     \code
1836         import QtQuick 1.0
1837         TextEdit {
1838             id: textEdit
1839             text: "Hello world!"
1840             activeFocusOnPress: false
1841             MouseArea {
1842                 anchors.fill: parent
1843                 onClicked: {
1844                     if (!textEdit.activeFocus) {
1845                         textEdit.forceActiveFocus();
1846                         textEdit.openSoftwareInputPanel();
1847                     } else {
1848                         textEdit.focus = false;
1849                     }
1850                 }
1851                 onPressAndHold: textEdit.closeSoftwareInputPanel();
1852             }
1853         }
1854     \endcode
1855 */
1856 void QDeclarativeTextEdit::closeSoftwareInputPanel()
1857 {
1858     QEvent event(QEvent::CloseSoftwareInputPanel);
1859     if (qApp) {
1860         if (QGraphicsView * view = qobject_cast<QGraphicsView*>(qApp->focusWidget())) {
1861             if (view->scene() && view->scene() == scene()) {
1862                 QApplication::sendEvent(view, &event);
1863             }
1864         }
1865     }
1866 }
1867
1868 void QDeclarativeTextEdit::focusInEvent(QFocusEvent *event)
1869 {
1870     Q_D(const QDeclarativeTextEdit);
1871     if (d->showInputPanelOnFocus) {
1872         if (d->focusOnPress && !isReadOnly()) {
1873             openSoftwareInputPanel();
1874         }
1875     }
1876     QDeclarativePaintedItem::focusInEvent(event);
1877 }
1878
1879 void QDeclarativeTextEdit::q_canPasteChanged()
1880 {
1881     Q_D(QDeclarativeTextEdit);
1882     bool old = d->canPaste;
1883     d->canPaste = d->control->canPaste();
1884     if(old!=d->canPaste)
1885         emit canPasteChanged();
1886 }
1887
1888 QT_END_NAMESPACE