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