1/****************************************************************************
2**
3** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
4** Contact: http://www.qt-project.org/legal
5**
6** This file is part of the QtDeclarative module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Digia. For licensing terms and
14** conditions see http://qt.digia.com/licensing. For further information
15** use the contact form at http://qt.digia.com/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 2.1 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 2.1 requirements
23** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24**
25** In addition, as a special exception, Digia gives you certain additional
26** rights. These rights are described in the Digia Qt LGPL Exception
27** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28**
29** GNU General Public License Usage
30** Alternatively, this file may be used under the terms of the GNU
31** General Public License version 3.0 as published by the Free Software
32** Foundation and appearing in the file LICENSE.GPL included in the
33** packaging of this file. Please review the following information to
34** ensure the GNU General Public License version 3.0 requirements will be
35** met: http://www.gnu.org/copyleft/gpl.html.
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
62QT_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
76TextEdit {
77 width: 240
78 text: "<b>Hello</b> <i>World!</i>"
79 font.family: "Helvetica"
80 font.pointSize: 20
81 color: "blue"
82 focus: true
83}
84 \endqml
85
86 \image declarative-textedit.gif
87
88 Setting \l {Item::focus}{focus} to \c true enables the TextEdit item to receive keyboard focus.
89
90 Note that the TextEdit does not implement scrolling, following the cursor, or other behaviors specific
91 to a look-and-feel. For example, to add flickable scrolling that follows the cursor:
92
93 \snippet snippets/declarative/texteditor.qml 0
94
95 A particular look-and-feel might use smooth scrolling (eg. using SmoothedFollow), might have a visible
96 scrollbar, or a scrollbar that fades in to show location, etc.
97
98 Clipboard support is provided by the cut(), copy(), and paste() functions, and the selection can
99 be handled in a traditional "mouse" mechanism by setting selectByMouse, or handled completely
100 from QML by manipulating selectionStart and selectionEnd, or using selectAll() or selectWord().
101
102 You can translate between cursor positions (characters from the start of the document) and pixel
103 points using positionAt() and positionToRectangle().
104
105 \sa Text, TextInput, {declarative/text/textselection}{Text Selection example}
106*/
107
108/*!
109 \qmlsignal TextEdit::onLinkActivated(string link)
110 \since QtQuick 1.1
111
112 This handler is called when the user clicks on a link embedded in the text.
113 The link must be in rich text or HTML format and the
114 \a link string provides access to the particular link.
115*/
116QDeclarativeTextEdit::QDeclarativeTextEdit(QDeclarativeItem *parent)
117: QDeclarativeImplicitSizePaintedItem(*(new QDeclarativeTextEditPrivate), parent)
118{
119 Q_D(QDeclarativeTextEdit);
120 d->init();
121}
122
123QString 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*/
248void 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
286Column {
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*/
306QDeclarativeTextEdit::TextFormat QDeclarativeTextEdit::textFormat() const
307{
308 Q_D(const QDeclarativeTextEdit);
309 return d->format;
310}
311
312void 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
336QFont QDeclarativeTextEdit::font() const
337{
338 Q_D(const QDeclarativeTextEdit);
339 return d->sourceFont;
340}
341
342void 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*/
385QColor QDeclarativeTextEdit::color() const
386{
387 Q_D(const QDeclarativeTextEdit);
388 return d->color;
389}
390
391void 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*/
411QColor QDeclarativeTextEdit::selectionColor() const
412{
413 Q_D(const QDeclarativeTextEdit);
414 return d->selectionColor;
415}
416
417void 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*/
437QColor QDeclarativeTextEdit::selectedTextColor() const
438{
439 Q_D(const QDeclarativeTextEdit);
440 return d->selectedTextColor;
441}
442
443void QDeclarativeTextEdit::setSelectedTextColor(const QColor &color)
444{
445 Q_D(QDeclarativeTextEdit);
446 if (d->selectedTextColor == color)
447 return;
448
449 clearCache();
450 d->selectedTextColor = color;
451 QPalette pal = d->control->palette();
452 pal.setColor(QPalette::HighlightedText, color);
453 d->control->setPalette(pal);
454 update();
455 emit selectedTextColorChanged(d->selectedTextColor);
456}
457
458/*!
459 \qmlproperty enumeration TextEdit::horizontalAlignment
460 \qmlproperty enumeration TextEdit::verticalAlignment
461
462 Sets the horizontal and vertical alignment of the text within the TextEdit item's
463 width and height. By default, the text alignment follows the natural alignment
464 of the text, for example text that is read from left to right will be aligned to
465 the left.
466
467 Valid values for \c horizontalAlignment are:
468 \list
469 \o TextEdit.AlignLeft (default)
470 \o TextEdit.AlignRight
471 \o TextEdit.AlignHCenter
472 \o TextEdit.AlignJustify
473 \endlist
474
475 Valid values for \c verticalAlignment are:
476 \list
477 \o TextEdit.AlignTop (default)
478 \o TextEdit.AlignBottom
479 \o TextEdit.AlignVCenter
480 \endlist
481
482 When using the attached property \l {LayoutMirroring::enabled} to mirror application
483 layouts, the horizontal alignment of text will also be mirrored. However, the property
484 \c horizontalAlignment will remain unchanged. To query the effective horizontal alignment
485 of TextEdit, use the property \l {LayoutMirroring::enabled}.
486*/
487QDeclarativeTextEdit::HAlignment QDeclarativeTextEdit::hAlign() const
488{
489 Q_D(const QDeclarativeTextEdit);
490 return d->hAlign;
491}
492
493void QDeclarativeTextEdit::setHAlign(HAlignment align)
494{
495 Q_D(QDeclarativeTextEdit);
496 bool forceAlign = d->hAlignImplicit && d->effectiveLayoutMirror;
497 d->hAlignImplicit = false;
498 if (d->setHAlign(align, forceAlign) && isComponentComplete()) {
499 d->updateDefaultTextOption();
500 updateSize();
501 }
502}
503
504void QDeclarativeTextEdit::resetHAlign()
505{
506 Q_D(QDeclarativeTextEdit);
507 d->hAlignImplicit = true;
508 if (d->determineHorizontalAlignment() && isComponentComplete()) {
509 d->updateDefaultTextOption();
510 updateSize();
511 }
512}
513
514QDeclarativeTextEdit::HAlignment QDeclarativeTextEdit::effectiveHAlign() const
515{
516 Q_D(const QDeclarativeTextEdit);
517 QDeclarativeTextEdit::HAlignment effectiveAlignment = d->hAlign;
518 if (!d->hAlignImplicit && d->effectiveLayoutMirror) {
519 switch (d->hAlign) {
520 case QDeclarativeTextEdit::AlignLeft:
521 effectiveAlignment = QDeclarativeTextEdit::AlignRight;
522 break;
523 case QDeclarativeTextEdit::AlignRight:
524 effectiveAlignment = QDeclarativeTextEdit::AlignLeft;
525 break;
526 default:
527 break;
528 }
529 }
530 return effectiveAlignment;
531}
532
533bool QDeclarativeTextEditPrivate::setHAlign(QDeclarativeTextEdit::HAlignment alignment, bool forceAlign)
534{
535 Q_Q(QDeclarativeTextEdit);
536 if (hAlign != alignment || forceAlign) {
537 hAlign = alignment;
538 emit q->horizontalAlignmentChanged(alignment);
539 return true;
540 }
541 return false;
542}
543
544bool QDeclarativeTextEditPrivate::determineHorizontalAlignment()
545{
546 Q_Q(QDeclarativeTextEdit);
547 if (hAlignImplicit && q->isComponentComplete()) {
548 bool alignToRight;
549 if (text.isEmpty() && !control->textCursor().isNull()) {
550 const QString preeditText = control->textCursor().block().layout()->preeditAreaText();
551 alignToRight = preeditText.isEmpty()
552 ? QApplication::keyboardInputDirection() == Qt::RightToLeft
553 : preeditText.isRightToLeft();
554 } else {
555 alignToRight = rightToLeftText;
556 }
557 return setHAlign(alignToRight ? QDeclarativeTextEdit::AlignRight : QDeclarativeTextEdit::AlignLeft);
558 }
559 return false;
560}
561
562void QDeclarativeTextEditPrivate::mirrorChange()
563{
564 Q_Q(QDeclarativeTextEdit);
565 if (q->isComponentComplete()) {
566 if (!hAlignImplicit && (hAlign == QDeclarativeTextEdit::AlignRight || hAlign == QDeclarativeTextEdit::AlignLeft)) {
567 updateDefaultTextOption();
568 q->updateSize();
569 }
570 }
571}
572
573QDeclarativeTextEdit::VAlignment QDeclarativeTextEdit::vAlign() const
574{
575 Q_D(const QDeclarativeTextEdit);
576 return d->vAlign;
577}
578
579void QDeclarativeTextEdit::setVAlign(QDeclarativeTextEdit::VAlignment alignment)
580{
581 Q_D(QDeclarativeTextEdit);
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 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*/
606QDeclarativeTextEdit::WrapMode QDeclarativeTextEdit::wrapMode() const
607{
608 Q_D(const QDeclarativeTextEdit);
609 return d->wrapMode;
610}
611
612void QDeclarativeTextEdit::setWrapMode(WrapMode mode)
613{
614 Q_D(QDeclarativeTextEdit);
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 TextEdit::lineCount
625 \since QtQuick 1.1
626
627 Returns the total number of lines in the textEdit item.
628*/
629int QDeclarativeTextEdit::lineCount() const
630{
631 Q_D(const QDeclarativeTextEdit);
632 return d->lineCount;
633}
634
635/*!
636 \qmlproperty real 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*/
641qreal QDeclarativeTextEdit::paintedWidth() const
642{
643 Q_D(const QDeclarativeTextEdit);
644 return d->paintedSize.width();
645}
646
647/*!
648 \qmlproperty real 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*/
653qreal QDeclarativeTextEdit::paintedHeight() const
654{
655 Q_D(const QDeclarativeTextEdit);
656 return d->paintedSize.height();
657}
658
659/*!
660 \qmlmethod rectangle 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*/
666QRectF QDeclarativeTextEdit::positionToRectangle(int pos) const
667{
668 Q_D(const QDeclarativeTextEdit);
669 QTextCursor c(d->document);
670 c.setPosition(pos);
671 return d->control->cursorRect(c);
672
673}
674
675/*!
676 \qmlmethod int 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*/
683int QDeclarativeTextEdit::positionAt(int x, int y) const
684{
685 Q_D(const QDeclarativeTextEdit);
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
706void QDeclarativeTextEdit::moveCursorSelection(int pos)
707{
708 //Note that this is the same as setCursorPosition but with the KeepAnchor flag set
709 Q_D(QDeclarativeTextEdit);
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 TextEdit::moveCursorSelection(int position, SelectionMode mode = TextEdit.SelectCharacters)
719 \since QtQuick 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*/
755void QDeclarativeTextEdit::moveCursorSelection(int pos, SelectionMode mode)
756{
757 Q_D(QDeclarativeTextEdit);
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 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*/
811bool QDeclarativeTextEdit::isCursorVisible() const
812{
813 Q_D(const QDeclarativeTextEdit);
814 return d->cursorVisible;
815}
816
817void QDeclarativeTextEdit::setCursorVisible(bool on)
818{
819 Q_D(QDeclarativeTextEdit);
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 TextEdit::cursorPosition
832 The position of the cursor in the TextEdit.
833*/
834int QDeclarativeTextEdit::cursorPosition() const
835{
836 Q_D(const QDeclarativeTextEdit);
837 return d->control->textCursor().position();
838}
839
840void QDeclarativeTextEdit::setCursorPosition(int pos)
841{
842 Q_D(QDeclarativeTextEdit);
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 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*/
865QDeclarativeComponent* QDeclarativeTextEdit::cursorDelegate() const
866{
867 Q_D(const QDeclarativeTextEdit);
868 return d->cursorComponent;
869}
870
871void QDeclarativeTextEdit::setCursorDelegate(QDeclarativeComponent* c)
872{
873 Q_D(QDeclarativeTextEdit);
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
894void QDeclarativeTextEdit::loadCursorDelegate()
895{
896 Q_D(QDeclarativeTextEdit);
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 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*/
922int QDeclarativeTextEdit::selectionStart() const
923{
924 Q_D(const QDeclarativeTextEdit);
925 return d->control->textCursor().selectionStart();
926}
927
928/*!
929 \qmlproperty int 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*/
938int QDeclarativeTextEdit::selectionEnd() const
939{
940 Q_D(const QDeclarativeTextEdit);
941 return d->control->textCursor().selectionEnd();
942}
943
944/*!
945 \qmlproperty string 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*/
958QString QDeclarativeTextEdit::selectedText() const
959{
960 Q_D(const QDeclarativeTextEdit);
961 return d->control->textCursor().selectedText();
962}
963
964/*!
965 \qmlproperty bool TextEdit::activeFocusOnPress
966
967 Whether the TextEdit should gain active focus on a mouse press. By default this is
968 set to true.
969*/
970bool QDeclarativeTextEdit::focusOnPress() const
971{
972 Q_D(const QDeclarativeTextEdit);
973 return d->focusOnPress;
974}
975
976void QDeclarativeTextEdit::setFocusOnPress(bool on)
977{
978 Q_D(QDeclarativeTextEdit);
979 if (d->focusOnPress == on)
980 return;
981 d->focusOnPress = on;
982 emit activeFocusOnPressChanged(d->focusOnPress);
983}
984
985/*!
986 \qmlproperty bool 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*/
991bool QDeclarativeTextEdit::persistentSelection() const
992{
993 Q_D(const QDeclarativeTextEdit);
994 return d->persistentSelection;
995}
996
997void QDeclarativeTextEdit::setPersistentSelection(bool on)
998{
999 Q_D(QDeclarativeTextEdit);
1000 if (d->persistentSelection == on)
1001 return;
1002 d->persistentSelection = on;
1003 emit persistentSelectionChanged(d->persistentSelection);
1004}
1005
1006/*
1007 \qmlproperty real TextEdit::textMargin
1008
1009 The margin, in pixels, around the text in the TextEdit.
1010*/
1011qreal QDeclarativeTextEdit::textMargin() const
1012{
1013 Q_D(const QDeclarativeTextEdit);
1014 return d->textMargin;
1015}
1016
1017void QDeclarativeTextEdit::setTextMargin(qreal margin)
1018{
1019 Q_D(QDeclarativeTextEdit);
1020 if (d->textMargin == margin)
1021 return;
1022 d->textMargin = margin;
1023 d->document->setDocumentMargin(d->textMargin);
1024 emit textMarginChanged(d->textMargin);
1025}
1026
1027void QDeclarativeTextEdit::geometryChanged(const QRectF &newGeometry,
1028 const QRectF &oldGeometry)
1029{
1030 if (newGeometry.width() != oldGeometry.width())
1031 updateSize();
1032 QDeclarativePaintedItem::geometryChanged(newGeometry, oldGeometry);
1033}
1034
1035/*!
1036 Ensures any delayed caching or data loading the class
1037 needs to performed is complete.
1038*/
1039void QDeclarativeTextEdit::componentComplete()
1040{
1041 Q_D(QDeclarativeTextEdit);
1042 QDeclarativePaintedItem::componentComplete();
1043 if (d->dirty) {
1044 d->determineHorizontalAlignment();
1045 d->updateDefaultTextOption();
1046 updateSize();
1047 d->dirty = false;
1048 }
1049}
1050
1051/*!
1052 \qmlproperty bool 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*/
1061bool QDeclarativeTextEdit::selectByMouse() const
1062{
1063 Q_D(const QDeclarativeTextEdit);
1064 return d->selectByMouse;
1065}
1066
1067void QDeclarativeTextEdit::setSelectByMouse(bool on)
1068{
1069 Q_D(QDeclarativeTextEdit);
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 TextEdit::mouseSelectionMode
1084 \since QtQuick 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
1096QDeclarativeTextEdit::SelectionMode QDeclarativeTextEdit::mouseSelectionMode() const
1097{
1098 Q_D(const QDeclarativeTextEdit);
1099 return d->mouseSelectionMode;
1100}
1101
1102void QDeclarativeTextEdit::setMouseSelectionMode(SelectionMode mode)
1103{
1104 Q_D(QDeclarativeTextEdit);
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 TextEdit::readOnly
1114
1115 Whether the user an 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*/
1120void QDeclarativeTextEdit::setReadOnly(bool r)
1121{
1122 Q_D(QDeclarativeTextEdit);
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
1140bool QDeclarativeTextEdit::isReadOnly() const
1141{
1142 Q_D(const QDeclarativeTextEdit);
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*/
1150void QDeclarativeTextEdit::setTextInteractionFlags(Qt::TextInteractionFlags flags)
1151{
1152 Q_D(QDeclarativeTextEdit);
1153 d->control->setTextInteractionFlags(flags);
1154}
1155
1156/*!
1157 Returns the flags specifying how the text edit should interact
1158 with user input.
1159*/
1160Qt::TextInteractionFlags QDeclarativeTextEdit::textInteractionFlags() const
1161{
1162 Q_D(const QDeclarativeTextEdit);
1163 return d->control->textInteractionFlags();
1164}
1165
1166/*!
1167 \qmlproperty rectangle TextEdit::cursorRectangle
1168
1169 The rectangle where the text cursor is rendered
1170 within the text edit. Read-only.
1171*/
1172QRect QDeclarativeTextEdit::cursorRectangle() const
1173{
1174 Q_D(const QDeclarativeTextEdit);
1175 return d->control->cursorRect().toRect().translated(0,d->yoff);
1176}
1177
1178
1179/*!
1180\overload
1181Handles the given \a event.
1182*/
1183bool QDeclarativeTextEdit::event(QEvent *event)
1184{
1185 Q_D(QDeclarativeTextEdit);
1186 if (event->type() == QEvent::ShortcutOverride) {
1187 d->control->processEvent(event, QPointF(0, -d->yoff));
1188 return event->isAccepted();
1189 }
1190 return QDeclarativePaintedItem::event(event);
1191}
1192
1193/*!
1194\overload
1195Handles the given key \a event.
1196*/
1197void QDeclarativeTextEdit::keyPressEvent(QKeyEvent *event)
1198{
1199 Q_D(QDeclarativeTextEdit);
1200 keyPressPreHandler(event);
1201 if (!event->isAccepted())
1202 d->control->processEvent(event, QPointF(0, -d->yoff));
1203 if (!event->isAccepted())
1204 QDeclarativePaintedItem::keyPressEvent(event);
1205}
1206
1207/*!
1208\overload
1209Handles the given key \a event.
1210*/
1211void QDeclarativeTextEdit::keyReleaseEvent(QKeyEvent *event)
1212{
1213 Q_D(QDeclarativeTextEdit);
1214 keyReleasePreHandler(event);
1215 if (!event->isAccepted())
1216 d->control->processEvent(event, QPointF(0, -d->yoff));
1217 if (!event->isAccepted())
1218 QDeclarativePaintedItem::keyReleaseEvent(event);
1219}
1220
1221void QDeclarativeTextEditPrivate::focusChanged(bool hasFocus)
1222{
1223 Q_Q(QDeclarativeTextEdit);
1224 q->setCursorVisible(hasFocus && scene && scene->hasFocus());
1225 QDeclarativeItemPrivate::focusChanged(hasFocus);
1226}
1227
1228/*!
1229 \qmlmethod void TextEdit::deselect()
1230 \since QtQuick 1.1
1231
1232 Removes active text selection.
1233*/
1234void QDeclarativeTextEdit::deselect()
1235{
1236 Q_D(QDeclarativeTextEdit);
1237 QTextCursor c = d->control->textCursor();
1238 c.clearSelection();
1239 d->control->setTextCursor(c);
1240}
1241
1242/*!
1243 \qmlmethod void TextEdit::selectAll()
1244
1245 Causes all text to be selected.
1246*/
1247void QDeclarativeTextEdit::selectAll()
1248{
1249 Q_D(QDeclarativeTextEdit);
1250 d->control->selectAll();
1251}
1252
1253/*!
1254 \qmlmethod void TextEdit::selectWord()
1255
1256 Causes the word closest to the current cursor position to be selected.
1257*/
1258void QDeclarativeTextEdit::selectWord()
1259{
1260 Q_D(QDeclarativeTextEdit);
1261 QTextCursor c = d->control->textCursor();
1262 c.select(QTextCursor::WordUnderCursor);
1263 d->control->setTextCursor(c);
1264}
1265
1266/*!
1267 \qmlmethod void 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*/
1279void QDeclarativeTextEdit::select(int start, int end)
1280{
1281 Q_D(QDeclarativeTextEdit);
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 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*/
1301bool QDeclarativeTextEdit::isRightToLeft(int start, int end)
1302{
1303 Q_D(QDeclarativeTextEdit);
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 TextEdit::cut()
1315
1316 Moves the currently selected text to the system clipboard.
1317*/
1318void QDeclarativeTextEdit::cut()
1319{
1320 Q_D(QDeclarativeTextEdit);
1321 d->control->cut();
1322}
1323
1324/*!
1325 \qmlmethod TextEdit::copy()
1326
1327 Copies the currently selected text to the system clipboard.
1328*/
1329void QDeclarativeTextEdit::copy()
1330{
1331 Q_D(QDeclarativeTextEdit);
1332 d->control->copy();
1333}
1334
1335/*!
1336 \qmlmethod TextEdit::paste()
1337
1338 Replaces the currently selected text by the contents of the system clipboard.
1339*/
1340void QDeclarativeTextEdit::paste()
1341{
1342 Q_D(QDeclarativeTextEdit);
1343 d->control->paste();
1344}
1345#endif // QT_NO_CLIPBOARD
1346
1347/*!
1348\overload
1349Handles the given mouse \a event.
1350*/
1351void QDeclarativeTextEdit::mousePressEvent(QGraphicsSceneMouseEvent *event)
1352{
1353 Q_D(QDeclarativeTextEdit);
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 QDeclarativePaintedItem::mousePressEvent(event);
1372}
1373
1374/*!
1375\overload
1376Handles the given mouse \a event.
1377*/
1378void QDeclarativeTextEdit::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
1379{
1380 Q_D(QDeclarativeTextEdit);
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 QDeclarativePaintedItem::mouseReleaseEvent(event);
1395}
1396
1397/*!
1398\overload
1399Handles the given mouse \a event.
1400*/
1401void QDeclarativeTextEdit::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
1402{
1403 Q_D(QDeclarativeTextEdit);
1404
1405 d->control->processEvent(event, QPointF(0, -d->yoff));
1406 if (!event->isAccepted())
1407 QDeclarativePaintedItem::mouseDoubleClickEvent(event);
1408
1409}
1410
1411/*!
1412\overload
1413Handles the given mouse \a event.
1414*/
1415void QDeclarativeTextEdit::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
1416{
1417 Q_D(QDeclarativeTextEdit);
1418 d->control->processEvent(event, QPointF(0, -d->yoff));
1419 if (!event->isAccepted())
1420 QDeclarativePaintedItem::mouseMoveEvent(event);
1421}
1422
1423/*!
1424\overload
1425Handles the given input method \a event.
1426*/
1427void QDeclarativeTextEdit::inputMethodEvent(QInputMethodEvent *event)
1428{
1429 Q_D(QDeclarativeTextEdit);
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
1438Returns the value of the given \a property.
1439*/
1440QVariant QDeclarativeTextEdit::inputMethodQuery(Qt::InputMethodQuery property) const
1441{
1442 Q_D(const QDeclarativeTextEdit);
1443 return d->control->inputMethodQuery(property);
1444}
1445
1446/*!
1447Draws the contents of the text edit using the given \a painter within
1448the given \a bounds.
1449*/
1450void QDeclarativeTextEdit::drawContents(QPainter *painter, const QRect &bounds)
1451{
1452 Q_D(QDeclarativeTextEdit);
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
1462void QDeclarativeTextEdit::updateImgCache(const QRectF &rf)
1463{
1464 Q_D(const QDeclarativeTextEdit);
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 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 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*/
1503bool QDeclarativeTextEdit::canPaste() const
1504{
1505 Q_D(const QDeclarativeTextEdit);
1506 return d->canPaste;
1507}
1508
1509/*!
1510 \qmlproperty bool 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*/
1522bool QDeclarativeTextEdit::isInputMethodComposing() const
1523{
1524 Q_D(const QDeclarativeTextEdit);
1525 if (QTextLayout *layout = d->control->textCursor().block().layout())
1526 return layout->preeditAreaText().length() > 0;
1527 return false;
1528}
1529
1530void QDeclarativeTextEditPrivate::init()
1531{
1532 Q_Q(QDeclarativeTextEdit);
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::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
1576void QDeclarativeTextEdit::q_textChanged()
1577{
1578 Q_D(QDeclarativeTextEdit);
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
1588void QDeclarativeTextEdit::moveCursorDelegate()
1589{
1590 Q_D(QDeclarativeTextEdit);
1591 d->determineHorizontalAlignment();
1592 updateMicroFocus();
1593 emit cursorRectangleChanged();
1594 if(!d->cursor)
1595 return;
1596 QRectF cursorRect = cursorRectangle();
1597 d->cursor->setX(cursorRect.x());
1598 d->cursor->setY(cursorRect.y());
1599}
1600
1601void QDeclarativeTextEditPrivate::updateSelection()
1602{
1603 Q_Q(QDeclarativeTextEdit);
1604 QTextCursor cursor = control->textCursor();
1605 bool startChange = (lastSelectionStart != cursor.selectionStart());
1606 bool endChange = (lastSelectionEnd != cursor.selectionEnd());
1607 cursor.beginEditBlock();
1608 cursor.setPosition(lastSelectionStart, QTextCursor::MoveAnchor);
1609 cursor.setPosition(lastSelectionEnd, QTextCursor::KeepAnchor);
1610 cursor.endEditBlock();
1611 control->setTextCursor(cursor);
1612 if(startChange)
1613 q->selectionStartChanged();
1614 if(endChange)
1615 q->selectionEndChanged();
1616}
1617
1618void QDeclarativeTextEdit::updateSelectionMarkers()
1619{
1620 Q_D(QDeclarativeTextEdit);
1621 if(d->lastSelectionStart != d->control->textCursor().selectionStart()){
1622 d->lastSelectionStart = d->control->textCursor().selectionStart();
1623 emit selectionStartChanged();
1624 }
1625 if(d->lastSelectionEnd != d->control->textCursor().selectionEnd()){
1626 d->lastSelectionEnd = d->control->textCursor().selectionEnd();
1627 emit selectionEndChanged();
1628 }
1629}
1630
1631QRectF QDeclarativeTextEdit::boundingRect() const
1632{
1633 Q_D(const QDeclarativeTextEdit);
1634 QRectF r = QDeclarativePaintedItem::boundingRect();
1635 int cursorWidth = 1;
1636 if(d->cursor)
1637 cursorWidth = d->cursor->width();
1638 if(!d->document->isEmpty())
1639 cursorWidth += 3;// ### Need a better way of accounting for space between char and cursor
1640
1641 // Could include font max left/right bearings to either side of rectangle.
1642
1643 r.setRight(r.right() + cursorWidth);
1644 return r.translated(0,d->yoff);
1645}
1646
1647qreal QDeclarativeTextEditPrivate::implicitWidth() const
1648{
1649 Q_Q(const QDeclarativeTextEdit);
1650 if (!requireImplicitWidth) {
1651 // We don't calculate implicitWidth unless it is required.
1652 // We need to force a size update now to ensure implicitWidth is calculated
1653 const_cast<QDeclarativeTextEditPrivate*>(this)->requireImplicitWidth = true;
1654 const_cast<QDeclarativeTextEdit*>(q)->updateSize();
1655 }
1656 return mImplicitWidth;
1657}
1658
1659//### we should perhaps be a bit smarter here -- depending on what has changed, we shouldn't
1660// need to do all the calculations each time
1661void QDeclarativeTextEdit::updateSize()
1662{
1663 Q_D(QDeclarativeTextEdit);
1664 if (isComponentComplete()) {
1665 qreal naturalWidth = d->mImplicitWidth;
1666 // ### assumes that if the width is set, the text will fill to edges
1667 // ### (unless wrap is false, then clipping will occur)
1668 if (widthValid()) {
1669 if (!d->requireImplicitWidth) {
1670 emit implicitWidthChanged();
1671 // if the implicitWidth is used, then updateSize() has already been called (recursively)
1672 if (d->requireImplicitWidth)
1673 return;
1674 }
1675 if (d->requireImplicitWidth) {
1676 d->document->setTextWidth(-1);
1677 naturalWidth = d->document->idealWidth();
1678 }
1679 if (d->document->textWidth() != width())
1680 d->document->setTextWidth(width());
1681 } else {
1682 d->document->setTextWidth(-1);
1683 }
1684 QFontMetrics fm = QFontMetrics(d->font);
1685 int dy = height();
1686 dy -= (int)d->document->size().height();
1687
1688 int nyoff;
1689 if (heightValid()) {
1690 if (d->vAlign == AlignBottom)
1691 nyoff = dy;
1692 else if (d->vAlign == AlignVCenter)
1693 nyoff = dy/2;
1694 else
1695 nyoff = 0;
1696 } else {
1697 nyoff = 0;
1698 }
1699 if (nyoff != d->yoff) {
1700 prepareGeometryChange();
1701 d->yoff = nyoff;
1702 }
1703 setBaselineOffset(fm.ascent() + d->yoff + d->textMargin);
1704
1705 //### need to comfirm cost of always setting these
1706 int newWidth = qCeil(d->document->idealWidth());
1707 if (!widthValid() && d->document->textWidth() != newWidth)
1708 d->document->setTextWidth(newWidth); // ### Text does not align if width is not set (QTextDoc bug)
1709 // ### Setting the implicitWidth triggers another updateSize(), and unless there are bindings nothing has changed.
1710 if (!widthValid())
1711 setImplicitWidth(newWidth);
1712 else if (d->requireImplicitWidth)
1713 setImplicitWidth(naturalWidth);
1714 qreal newHeight = d->document->size().height();
1715 if (newHeight == 0)
1716 newHeight = fm.height();
1717 setImplicitHeight(newHeight);
1718
1719 d->paintedSize = QSize(newWidth, newHeight);
1720 setContentsSize(d->paintedSize);
1721 emit paintedSizeChanged();
1722 } else {
1723 d->dirty = true;
1724 }
1725 emit update();
1726}
1727
1728void QDeclarativeTextEdit::updateTotalLines()
1729{
1730 Q_D(QDeclarativeTextEdit);
1731
1732 int subLines = 0;
1733
1734 for (QTextBlock it = d->document->begin(); it != d->document->end(); it = it.next()) {
1735 QTextLayout *layout = it.layout();
1736 if (!layout)
1737 continue;
1738 subLines += layout->lineCount()-1;
1739 }
1740
1741 int newTotalLines = d->document->lineCount() + subLines;
1742 if (d->lineCount != newTotalLines) {
1743 d->lineCount = newTotalLines;
1744 emit lineCountChanged();
1745 }
1746}
1747
1748void QDeclarativeTextEditPrivate::updateDefaultTextOption()
1749{
1750 Q_Q(QDeclarativeTextEdit);
1751 QTextOption opt = document->defaultTextOption();
1752 int oldAlignment = opt.alignment();
1753
1754 QDeclarativeTextEdit::HAlignment horizontalAlignment = q->effectiveHAlign();
1755 if (rightToLeftText) {
1756 if (horizontalAlignment == QDeclarativeTextEdit::AlignLeft)
1757 horizontalAlignment = QDeclarativeTextEdit::AlignRight;
1758 else if (horizontalAlignment == QDeclarativeTextEdit::AlignRight)
1759 horizontalAlignment = QDeclarativeTextEdit::AlignLeft;
1760 }
1761 opt.setAlignment((Qt::Alignment)(int)(horizontalAlignment | vAlign));
1762
1763 QTextOption::WrapMode oldWrapMode = opt.wrapMode();
1764 opt.setWrapMode(QTextOption::WrapMode(wrapMode));
1765
1766 if (oldWrapMode == opt.wrapMode() && oldAlignment == opt.alignment())
1767 return;
1768 document->setDefaultTextOption(opt);
1769}
1770
1771
1772/*!
1773 \qmlmethod void TextEdit::openSoftwareInputPanel()
1774
1775 Opens software input panels like virtual keyboards for typing, useful for
1776 customizing when you want the input keyboard to be shown and hidden in
1777 your application.
1778
1779 By default the opening of input panels follows the platform style. On Symbian^1 and
1780 Symbian^3 -based devices the panels are opened by clicking TextEdit. On other platforms
1781 the panels are automatically opened when TextEdit element gains active focus. Input panels are
1782 always closed if no editor has active focus.
1783
1784 You can disable the automatic behavior by setting the property \c activeFocusOnPress to false
1785 and use functions openSoftwareInputPanel() and closeSoftwareInputPanel() to implement
1786 the behavior you want.
1787
1788 Only relevant on platforms, which provide virtual keyboards.
1789
1790 \code
1791 import QtQuick 1.0
1792 TextEdit {
1793 id: textEdit
1794 text: "Hello world!"
1795 activeFocusOnPress: false
1796 MouseArea {
1797 anchors.fill: parent
1798 onClicked: {
1799 if (!textEdit.activeFocus) {
1800 textEdit.forceActiveFocus();
1801 textEdit.openSoftwareInputPanel();
1802 } else {
1803 textEdit.focus = false;
1804 }
1805 }
1806 onPressAndHold: textEdit.closeSoftwareInputPanel();
1807 }
1808 }
1809 \endcode
1810*/
1811void QDeclarativeTextEdit::openSoftwareInputPanel()
1812{
1813 QEvent event(QEvent::RequestSoftwareInputPanel);
1814 if (qApp) {
1815 if (QGraphicsView * view = qobject_cast<QGraphicsView*>(qApp->focusWidget())) {
1816 if (view->scene() && view->scene() == scene()) {
1817 QApplication::sendEvent(view, &event);
1818 }
1819 }
1820 }
1821}
1822
1823/*!
1824 \qmlmethod void TextEdit::closeSoftwareInputPanel()
1825
1826 Closes a software input panel like a virtual keyboard shown on the screen, useful
1827 for customizing when you want the input keyboard to be shown and hidden in
1828 your application.
1829
1830 By default the opening of input panels follows the platform style. On Symbian^1 and
1831 Symbian^3 -based devices the panels are opened by clicking TextEdit. On other platforms
1832 the panels are automatically opened when TextEdit element gains active focus. Input panels are
1833 always closed if no editor has active focus.
1834
1835 You can disable the automatic behavior by setting the property \c activeFocusOnPress to false
1836 and use functions openSoftwareInputPanel() and closeSoftwareInputPanel() to implement
1837 the behavior you want.
1838
1839 Only relevant on platforms, which provide virtual keyboards.
1840
1841 \code
1842 import QtQuick 1.0
1843 TextEdit {
1844 id: textEdit
1845 text: "Hello world!"
1846 activeFocusOnPress: false
1847 MouseArea {
1848 anchors.fill: parent
1849 onClicked: {
1850 if (!textEdit.activeFocus) {
1851 textEdit.forceActiveFocus();
1852 textEdit.openSoftwareInputPanel();
1853 } else {
1854 textEdit.focus = false;
1855 }
1856 }
1857 onPressAndHold: textEdit.closeSoftwareInputPanel();
1858 }
1859 }
1860 \endcode
1861*/
1862void QDeclarativeTextEdit::closeSoftwareInputPanel()
1863{
1864 QEvent event(QEvent::CloseSoftwareInputPanel);
1865 if (qApp) {
1866 if (QGraphicsView * view = qobject_cast<QGraphicsView*>(qApp->focusWidget())) {
1867 if (view->scene() && view->scene() == scene()) {
1868 QApplication::sendEvent(view, &event);
1869 }
1870 }
1871 }
1872}
1873
1874void QDeclarativeTextEdit::focusInEvent(QFocusEvent *event)
1875{
1876 Q_D(const QDeclarativeTextEdit);
1877 if (d->showInputPanelOnFocus) {
1878 if (d->focusOnPress && !isReadOnly()) {
1879 openSoftwareInputPanel();
1880 }
1881 }
1882 QDeclarativePaintedItem::focusInEvent(event);
1883}
1884
1885void QDeclarativeTextEdit::q_canPasteChanged()
1886{
1887 Q_D(QDeclarativeTextEdit);
1888 bool old = d->canPaste;
1889 d->canPaste = d->control->canPaste();
1890 if(old!=d->canPaste)
1891 emit canPasteChanged();
1892}
1893
1894QT_END_NAMESPACE
1895