Initial import from qtquick2.
[profile/ivi/qtdeclarative.git] / src / declarative / items / qsgtextedit.cpp
1 // Commit: 6980bca15b411f86b9fadb7484a6dd782b9d1403
2 /****************************************************************************
3 **
4 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
5 ** All rights reserved.
6 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 **
8 ** This file is part of the QtDeclarative module of the Qt Toolkit.
9 **
10 ** $QT_BEGIN_LICENSE:LGPL$
11 ** No Commercial Usage
12 ** This file contains pre-release code and may not be distributed.
13 ** You may use this file in accordance with the terms and conditions
14 ** contained in the Technology Preview License Agreement accompanying
15 ** this package.
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, Nokia gives you certain additional
26 ** rights.  These rights are described in the Nokia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
31 **
32 **
33 **
34 **
35 **
36 **
37 **
38 **
39 ** $QT_END_LICENSE$
40 **
41 ****************************************************************************/
42
43 #include "qsgtextedit_p.h"
44 #include "qsgtextedit_p_p.h"
45 #include "qsgevents_p_p.h"
46 #include "qsgcanvas.h"
47
48 #include <QtDeclarative/qdeclarativeinfo.h>
49 #include <QtGui/qapplication.h>
50 #include <QtGui/qgraphicssceneevent.h>
51 #include <QtGui/qpainter.h>
52 #include <QtGui/qtextobject.h>
53 #include <QtCore/qmath.h>
54
55 #include <private/qdeclarativeglobal_p.h>
56 #include <private/qtextcontrol_p.h>
57 #include <private/qtextengine_p.h>
58 #include <private/qwidget_p.h>
59
60 QT_BEGIN_NAMESPACE
61
62 QWidgetPrivate *qt_widget_private(QWidget *widget);
63
64 QSGTextEdit::QSGTextEdit(QSGItem *parent)
65 : QSGImplicitSizePaintedItem(*(new QSGTextEditPrivate), parent)
66 {
67     Q_D(QSGTextEdit);
68     d->init();
69 }
70
71 QString QSGTextEdit::text() const
72 {
73     Q_D(const QSGTextEdit);
74
75 #ifndef QT_NO_TEXTHTMLPARSER
76     if (d->richText)
77         return d->document->toHtml();
78     else
79 #endif
80         return d->document->toPlainText();
81 }
82
83 void QSGTextEdit::setText(const QString &text)
84 {
85     Q_D(QSGTextEdit);
86     if (QSGTextEdit::text() == text)
87         return;
88
89     d->richText = d->format == RichText || (d->format == AutoText && Qt::mightBeRichText(text));
90     if (d->richText) {
91 #ifndef QT_NO_TEXTHTMLPARSER
92         d->control->setHtml(text);
93 #else
94         d->control->setPlainText(text);
95 #endif
96     } else {
97         d->control->setPlainText(text);
98     }
99     q_textChanged();
100 }
101
102 QSGTextEdit::TextFormat QSGTextEdit::textFormat() const
103 {
104     Q_D(const QSGTextEdit);
105     return d->format;
106 }
107
108 void QSGTextEdit::setTextFormat(TextFormat format)
109 {
110     Q_D(QSGTextEdit);
111     if (format == d->format)
112         return;
113     bool wasRich = d->richText;
114     d->richText = format == RichText || (format == AutoText && Qt::mightBeRichText(d->text));
115
116     if (wasRich && !d->richText) {
117         d->control->setPlainText(d->text);
118         updateSize();
119     } else if (!wasRich && d->richText) {
120 #ifndef QT_NO_TEXTHTMLPARSER
121         d->control->setHtml(d->text);
122 #else
123         d->control->setPlainText(d->text);
124 #endif
125         updateSize();
126     }
127     d->format = format;
128     d->control->setAcceptRichText(d->format != PlainText);
129     emit textFormatChanged(d->format);
130 }
131
132 QFont QSGTextEdit::font() const
133 {
134     Q_D(const QSGTextEdit);
135     return d->sourceFont;
136 }
137
138 void QSGTextEdit::setFont(const QFont &font)
139 {
140     Q_D(QSGTextEdit);
141     if (d->sourceFont == font)
142         return;
143
144     d->sourceFont = font;
145     QFont oldFont = d->font;
146     d->font = font;
147     if (d->font.pointSizeF() != -1) {
148         // 0.5pt resolution
149         qreal size = qRound(d->font.pointSizeF()*2.0);
150         d->font.setPointSizeF(size/2.0);
151     }
152
153     if (oldFont != d->font) {
154         d->document->setDefaultFont(d->font);
155         if(d->cursor){
156             d->cursor->setHeight(QFontMetrics(d->font).height());
157             moveCursorDelegate();
158         }
159         updateSize();
160         update();
161     }
162     emit fontChanged(d->sourceFont);
163 }
164
165 QColor QSGTextEdit::color() const
166 {
167     Q_D(const QSGTextEdit);
168     return d->color;
169 }
170
171 void QSGTextEdit::setColor(const QColor &color)
172 {
173     Q_D(QSGTextEdit);
174     if (d->color == color)
175         return;
176
177     d->color = color;
178     QPalette pal = d->control->palette();
179     pal.setColor(QPalette::Text, color);
180     d->control->setPalette(pal);
181     update();
182     emit colorChanged(d->color);
183 }
184
185 QColor QSGTextEdit::selectionColor() const
186 {
187     Q_D(const QSGTextEdit);
188     return d->selectionColor;
189 }
190
191 void QSGTextEdit::setSelectionColor(const QColor &color)
192 {
193     Q_D(QSGTextEdit);
194     if (d->selectionColor == color)
195         return;
196
197     d->selectionColor = color;
198     QPalette pal = d->control->palette();
199     pal.setColor(QPalette::Highlight, color);
200     d->control->setPalette(pal);
201     update();
202     emit selectionColorChanged(d->selectionColor);
203 }
204
205 QColor QSGTextEdit::selectedTextColor() const
206 {
207     Q_D(const QSGTextEdit);
208     return d->selectedTextColor;
209 }
210
211 void QSGTextEdit::setSelectedTextColor(const QColor &color)
212 {
213     Q_D(QSGTextEdit);
214     if (d->selectedTextColor == color)
215         return;
216
217     d->selectedTextColor = color;
218     QPalette pal = d->control->palette();
219     pal.setColor(QPalette::HighlightedText, color);
220     d->control->setPalette(pal);
221     update();
222     emit selectedTextColorChanged(d->selectedTextColor);
223 }
224
225 QSGTextEdit::HAlignment QSGTextEdit::hAlign() const
226 {
227     Q_D(const QSGTextEdit);
228     return d->hAlign;
229 }
230
231 void QSGTextEdit::setHAlign(HAlignment align)
232 {
233     Q_D(QSGTextEdit);
234     bool forceAlign = d->hAlignImplicit && d->effectiveLayoutMirror;
235     d->hAlignImplicit = false;
236     if (d->setHAlign(align, forceAlign) && isComponentComplete()) {
237         d->updateDefaultTextOption();
238         updateSize();
239     }
240 }
241
242 void QSGTextEdit::resetHAlign()
243 {
244     Q_D(QSGTextEdit);
245     d->hAlignImplicit = true;
246     if (d->determineHorizontalAlignment() && isComponentComplete()) {
247         d->updateDefaultTextOption();
248         updateSize();
249     }
250 }
251
252 QSGTextEdit::HAlignment QSGTextEdit::effectiveHAlign() const
253 {
254     Q_D(const QSGTextEdit);
255     QSGTextEdit::HAlignment effectiveAlignment = d->hAlign;
256     if (!d->hAlignImplicit && d->effectiveLayoutMirror) {
257         switch (d->hAlign) {
258         case QSGTextEdit::AlignLeft:
259             effectiveAlignment = QSGTextEdit::AlignRight;
260             break;
261         case QSGTextEdit::AlignRight:
262             effectiveAlignment = QSGTextEdit::AlignLeft;
263             break;
264         default:
265             break;
266         }
267     }
268     return effectiveAlignment;
269 }
270
271 bool QSGTextEditPrivate::setHAlign(QSGTextEdit::HAlignment alignment, bool forceAlign)
272 {
273     Q_Q(QSGTextEdit);
274     if (hAlign != alignment || forceAlign) {
275         QSGTextEdit::HAlignment oldEffectiveHAlign = q->effectiveHAlign();
276         hAlign = alignment;
277         emit q->horizontalAlignmentChanged(alignment);
278         if (oldEffectiveHAlign != q->effectiveHAlign())
279             emit q->effectiveHorizontalAlignmentChanged();
280         return true;
281     }
282     return false;
283 }
284
285 bool QSGTextEditPrivate::determineHorizontalAlignment()
286 {
287     Q_Q(QSGTextEdit);
288     if (hAlignImplicit && q->isComponentComplete()) {
289         bool alignToRight = text.isEmpty() ? QApplication::keyboardInputDirection() == Qt::RightToLeft : rightToLeftText;
290         return setHAlign(alignToRight ? QSGTextEdit::AlignRight : QSGTextEdit::AlignLeft);
291     }
292     return false;
293 }
294
295 void QSGTextEditPrivate::mirrorChange()
296 {
297     Q_Q(QSGTextEdit);
298     if (q->isComponentComplete()) {
299         if (!hAlignImplicit && (hAlign == QSGTextEdit::AlignRight || hAlign == QSGTextEdit::AlignLeft)) {
300             updateDefaultTextOption();
301             q->updateSize();
302             emit q->effectiveHorizontalAlignmentChanged();
303         }
304     }
305 }
306
307 QSGTextEdit::VAlignment QSGTextEdit::vAlign() const
308 {
309     Q_D(const QSGTextEdit);
310     return d->vAlign;
311 }
312
313 void QSGTextEdit::setVAlign(QSGTextEdit::VAlignment alignment)
314 {
315     Q_D(QSGTextEdit);
316     if (alignment == d->vAlign)
317         return;
318     d->vAlign = alignment;
319     d->updateDefaultTextOption();
320     updateSize();
321     emit verticalAlignmentChanged(d->vAlign);
322 }
323
324 QSGTextEdit::WrapMode QSGTextEdit::wrapMode() const
325 {
326     Q_D(const QSGTextEdit);
327     return d->wrapMode;
328 }
329
330 void QSGTextEdit::setWrapMode(WrapMode mode)
331 {
332     Q_D(QSGTextEdit);
333     if (mode == d->wrapMode)
334         return;
335     d->wrapMode = mode;
336     d->updateDefaultTextOption();
337     updateSize();
338     emit wrapModeChanged();
339 }
340
341 int QSGTextEdit::lineCount() const
342 {
343     Q_D(const QSGTextEdit);
344     return d->lineCount;
345 }
346
347 qreal QSGTextEdit::paintedWidth() const
348 {
349     Q_D(const QSGTextEdit);
350     return d->paintedSize.width();
351 }
352
353 qreal QSGTextEdit::paintedHeight() const
354 {
355     Q_D(const QSGTextEdit);
356     return d->paintedSize.height();
357 }
358
359 QRectF QSGTextEdit::positionToRectangle(int pos) const
360 {
361     Q_D(const QSGTextEdit);
362     QTextCursor c(d->document);
363     c.setPosition(pos);
364     return d->control->cursorRect(c);
365
366 }
367
368 int QSGTextEdit::positionAt(int x, int y) const
369 {
370     Q_D(const QSGTextEdit);
371     int r = d->document->documentLayout()->hitTest(QPoint(x,y-d->yoff), Qt::FuzzyHit);
372     QTextCursor cursor = d->control->textCursor();
373     if (r > cursor.position()) {
374         // The cursor position includes positions within the preedit text, but only positions in the
375         // same text block are offset so it is possible to get a position that is either part of the
376         // preedit or the next text block.
377         QTextLayout *layout = cursor.block().layout();
378         const int preeditLength = layout
379                 ? layout->preeditAreaText().length()
380                 : 0;
381         if (preeditLength > 0
382                 && d->document->documentLayout()->blockBoundingRect(cursor.block()).contains(x,y-d->yoff)) {
383             r = r > cursor.position() + preeditLength
384                     ? r - preeditLength
385                     : cursor.position();
386         }
387     }
388     return r;
389 }
390
391 void QSGTextEdit::moveCursorSelection(int pos)
392 {
393     //Note that this is the same as setCursorPosition but with the KeepAnchor flag set
394     Q_D(QSGTextEdit);
395     QTextCursor cursor = d->control->textCursor();
396     if (cursor.position() == pos)
397         return;
398     cursor.setPosition(pos, QTextCursor::KeepAnchor);
399     d->control->setTextCursor(cursor);
400 }
401
402 void QSGTextEdit::moveCursorSelection(int pos, SelectionMode mode)
403 {
404     Q_D(QSGTextEdit);
405     QTextCursor cursor = d->control->textCursor();
406     if (cursor.position() == pos)
407         return;
408     if (mode == SelectCharacters) {
409         cursor.setPosition(pos, QTextCursor::KeepAnchor);
410     } else if (cursor.anchor() < pos || (cursor.anchor() == pos && cursor.position() < pos)) {
411         if (cursor.anchor() > cursor.position()) {
412             cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor);
413             cursor.movePosition(QTextCursor::StartOfWord, QTextCursor::KeepAnchor);
414             if (cursor.position() == cursor.anchor())
415                 cursor.movePosition(QTextCursor::PreviousWord, QTextCursor::MoveAnchor);
416             else
417                 cursor.setPosition(cursor.position(), QTextCursor::MoveAnchor);
418         } else {
419             cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor);
420             cursor.movePosition(QTextCursor::StartOfWord, QTextCursor::MoveAnchor);
421         }
422
423         cursor.setPosition(pos, QTextCursor::KeepAnchor);
424         cursor.movePosition(QTextCursor::StartOfWord, QTextCursor::KeepAnchor);
425         if (cursor.position() != pos)
426             cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
427     } else if (cursor.anchor() > pos || (cursor.anchor() == pos && cursor.position() > pos)) {
428         if (cursor.anchor() < cursor.position()) {
429             cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor);
430             cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::MoveAnchor);
431         } else {
432             cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor);
433             cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor);
434             cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
435             if (cursor.position() != cursor.anchor()) {
436                 cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor);
437                 cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::MoveAnchor);
438             }
439         }
440
441         cursor.setPosition(pos, QTextCursor::KeepAnchor);
442         cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
443         if (cursor.position() != pos) {
444             cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor);
445             cursor.movePosition(QTextCursor::StartOfWord, QTextCursor::KeepAnchor);
446         }
447     }
448     d->control->setTextCursor(cursor);
449 }
450
451 bool QSGTextEdit::isCursorVisible() const
452 {
453     Q_D(const QSGTextEdit);
454     return d->cursorVisible;
455 }
456
457 void QSGTextEdit::setCursorVisible(bool on)
458 {
459     Q_D(QSGTextEdit);
460     if (d->cursorVisible == on)
461         return;
462     d->cursorVisible = on;
463     QFocusEvent focusEvent(on ? QEvent::FocusIn : QEvent::FocusOut);
464     if (!on && !d->persistentSelection)
465         d->control->setCursorIsFocusIndicator(true);
466     d->control->processEvent(&focusEvent, QPointF(0, -d->yoff));
467     emit cursorVisibleChanged(d->cursorVisible);
468 }
469
470 int QSGTextEdit::cursorPosition() const
471 {
472     Q_D(const QSGTextEdit);
473     return d->control->textCursor().position();
474 }
475
476 void QSGTextEdit::setCursorPosition(int pos)
477 {
478     Q_D(QSGTextEdit);
479     if (pos < 0 || pos > d->text.length())
480         return;
481     QTextCursor cursor = d->control->textCursor();
482     if (cursor.position() == pos && cursor.anchor() == pos)
483         return;
484     cursor.setPosition(pos);
485     d->control->setTextCursor(cursor);
486 }
487
488 QDeclarativeComponent* QSGTextEdit::cursorDelegate() const
489 {
490     Q_D(const QSGTextEdit);
491     return d->cursorComponent;
492 }
493
494 void QSGTextEdit::setCursorDelegate(QDeclarativeComponent* c)
495 {
496     Q_D(QSGTextEdit);
497     if(d->cursorComponent){
498         if(d->cursor){
499             disconnect(d->control, SIGNAL(cursorPositionChanged()),
500                     this, SLOT(moveCursorDelegate()));
501             d->control->setCursorWidth(-1);
502             update(cursorRectangle());
503             delete d->cursor;
504             d->cursor = 0;
505         }
506     }
507     d->cursorComponent = c;
508     if(c && c->isReady()){
509         loadCursorDelegate();
510     }else{
511         if(c)
512             connect(c, SIGNAL(statusChanged()),
513                     this, SLOT(loadCursorDelegate()));
514     }
515
516     emit cursorDelegateChanged();
517 }
518
519 void QSGTextEdit::loadCursorDelegate()
520 {
521     Q_D(QSGTextEdit);
522     if(d->cursorComponent->isLoading())
523         return;
524     d->cursor = qobject_cast<QSGItem*>(d->cursorComponent->create(qmlContext(this)));
525     if(d->cursor){
526         connect(d->control, SIGNAL(cursorPositionChanged()),
527                 this, SLOT(moveCursorDelegate()));
528         d->control->setCursorWidth(0);
529         update(cursorRectangle());
530         QDeclarative_setParent_noEvent(d->cursor, this);
531         d->cursor->setParentItem(this);
532         d->cursor->setHeight(QFontMetrics(d->font).height());
533         moveCursorDelegate();
534     }else{
535         qmlInfo(this) << "Error loading cursor delegate.";
536     }
537 }
538
539 int QSGTextEdit::selectionStart() const
540 {
541     Q_D(const QSGTextEdit);
542     return d->control->textCursor().selectionStart();
543 }
544
545 int QSGTextEdit::selectionEnd() const
546 {
547     Q_D(const QSGTextEdit);
548     return d->control->textCursor().selectionEnd();
549 }
550
551 QString QSGTextEdit::selectedText() const
552 {
553     Q_D(const QSGTextEdit);
554     return d->control->textCursor().selectedText();
555 }
556
557 bool QSGTextEdit::focusOnPress() const
558 {
559     Q_D(const QSGTextEdit);
560     return d->focusOnPress;
561 }
562
563 void QSGTextEdit::setFocusOnPress(bool on)
564 {
565     Q_D(QSGTextEdit);
566     if (d->focusOnPress == on)
567         return;
568     d->focusOnPress = on;
569     emit activeFocusOnPressChanged(d->focusOnPress);
570 }
571
572 bool QSGTextEdit::persistentSelection() const
573 {
574     Q_D(const QSGTextEdit);
575     return d->persistentSelection;
576 }
577
578 void QSGTextEdit::setPersistentSelection(bool on)
579 {
580     Q_D(QSGTextEdit);
581     if (d->persistentSelection == on)
582         return;
583     d->persistentSelection = on;
584     emit persistentSelectionChanged(d->persistentSelection);
585 }
586
587 qreal QSGTextEdit::textMargin() const
588 {
589     Q_D(const QSGTextEdit);
590     return d->textMargin;
591 }
592
593 void QSGTextEdit::setTextMargin(qreal margin)
594 {
595     Q_D(QSGTextEdit);
596     if (d->textMargin == margin)
597         return;
598     d->textMargin = margin;
599     d->document->setDocumentMargin(d->textMargin);
600     emit textMarginChanged(d->textMargin);
601 }
602
603 void QSGTextEdit::geometryChanged(const QRectF &newGeometry,
604                                   const QRectF &oldGeometry)
605 {
606     if (newGeometry.width() != oldGeometry.width())
607         updateSize();
608     QSGPaintedItem::geometryChanged(newGeometry, oldGeometry);
609 }
610
611 void QSGTextEdit::componentComplete()
612 {
613     Q_D(QSGTextEdit);
614     QSGPaintedItem::componentComplete();
615     if (d->dirty) {
616         d->determineHorizontalAlignment();
617         d->updateDefaultTextOption();
618         updateSize();
619         d->dirty = false;
620     }
621 }
622
623 bool QSGTextEdit::selectByMouse() const
624 {
625     Q_D(const QSGTextEdit);
626     return d->selectByMouse;
627 }
628
629 void QSGTextEdit::setSelectByMouse(bool on)
630 {
631     Q_D(QSGTextEdit);
632     if (d->selectByMouse != on) {
633         d->selectByMouse = on;
634         setKeepMouseGrab(on);
635         if (on)
636             setTextInteractionFlags(d->control->textInteractionFlags() | Qt::TextSelectableByMouse);
637         else
638             setTextInteractionFlags(d->control->textInteractionFlags() & ~Qt::TextSelectableByMouse);
639         emit selectByMouseChanged(on);
640     }
641 }
642
643 QSGTextEdit::SelectionMode QSGTextEdit::mouseSelectionMode() const
644 {
645     Q_D(const QSGTextEdit);
646     return d->mouseSelectionMode;
647 }
648
649 void QSGTextEdit::setMouseSelectionMode(SelectionMode mode)
650 {
651     Q_D(QSGTextEdit);
652     if (d->mouseSelectionMode != mode) {
653         d->mouseSelectionMode = mode;
654         d->control->setWordSelectionEnabled(mode == SelectWords);
655         emit mouseSelectionModeChanged(mode);
656     }
657 }
658
659 void QSGTextEdit::setReadOnly(bool r)
660 {
661     Q_D(QSGTextEdit);
662     if (r == isReadOnly())
663         return;
664
665     setFlag(QSGItem::ItemAcceptsInputMethod, !r);
666     Qt::TextInteractionFlags flags = Qt::LinksAccessibleByMouse;
667     if (d->selectByMouse)
668         flags = flags | Qt::TextSelectableByMouse;
669     if (!r)
670         flags = flags | Qt::TextSelectableByKeyboard | Qt::TextEditable;
671     d->control->setTextInteractionFlags(flags);
672     if (!r)
673         d->control->moveCursor(QTextCursor::End);
674
675     emit readOnlyChanged(r);
676 }
677
678 bool QSGTextEdit::isReadOnly() const
679 {
680     Q_D(const QSGTextEdit);
681     return !(d->control->textInteractionFlags() & Qt::TextEditable);
682 }
683
684 void QSGTextEdit::setTextInteractionFlags(Qt::TextInteractionFlags flags)
685 {
686     Q_D(QSGTextEdit);
687     d->control->setTextInteractionFlags(flags);
688 }
689
690 Qt::TextInteractionFlags QSGTextEdit::textInteractionFlags() const
691 {
692     Q_D(const QSGTextEdit);
693     return d->control->textInteractionFlags();
694 }
695
696 QRect QSGTextEdit::cursorRectangle() const
697 {
698     Q_D(const QSGTextEdit);
699     return d->control->cursorRect().toRect().translated(0,-d->yoff);
700 }
701
702 bool QSGTextEdit::event(QEvent *event)
703 {
704     Q_D(QSGTextEdit);
705     if (event->type() == QEvent::ShortcutOverride) {
706         d->control->processEvent(event, QPointF(0, -d->yoff));
707         return event->isAccepted();
708     }
709     return QSGPaintedItem::event(event);
710 }
711
712 /*!
713 \overload
714 Handles the given key \a event.
715 */
716 void QSGTextEdit::keyPressEvent(QKeyEvent *event)
717 {
718     Q_D(QSGTextEdit);
719     d->control->processEvent(event, QPointF(0, -d->yoff));
720     if (!event->isAccepted())
721         QSGPaintedItem::keyPressEvent(event);
722 }
723
724 /*!
725 \overload
726 Handles the given key \a event.
727 */
728 void QSGTextEdit::keyReleaseEvent(QKeyEvent *event)
729 {
730     Q_D(QSGTextEdit);
731     d->control->processEvent(event, QPointF(0, -d->yoff));
732     if (!event->isAccepted())
733         QSGPaintedItem::keyReleaseEvent(event);
734 }
735
736 void QSGTextEdit::deselect()
737 {
738     Q_D(QSGTextEdit);
739     QTextCursor c = d->control->textCursor();
740     c.clearSelection();
741     d->control->setTextCursor(c);
742 }
743
744 void QSGTextEdit::selectAll()
745 {
746     Q_D(QSGTextEdit);
747     d->control->selectAll();
748 }
749
750 void QSGTextEdit::selectWord()
751 {
752     Q_D(QSGTextEdit);
753     QTextCursor c = d->control->textCursor();
754     c.select(QTextCursor::WordUnderCursor);
755     d->control->setTextCursor(c);
756 }
757
758 void QSGTextEdit::select(int start, int end)
759 {
760     Q_D(QSGTextEdit);
761     if (start < 0 || end < 0 || start > d->text.length() || end > d->text.length())
762         return;
763     QTextCursor cursor = d->control->textCursor();
764     cursor.beginEditBlock();
765     cursor.setPosition(start, QTextCursor::MoveAnchor);
766     cursor.setPosition(end, QTextCursor::KeepAnchor);
767     cursor.endEditBlock();
768     d->control->setTextCursor(cursor);
769
770     // QTBUG-11100
771     updateSelectionMarkers();
772 }
773
774 bool QSGTextEdit::isRightToLeft(int start, int end)
775 {
776     Q_D(QSGTextEdit);
777     if (start > end) {
778         qmlInfo(this) << "isRightToLeft(start, end) called with the end property being smaller than the start.";
779         return false;
780     } else {
781         return d->text.mid(start, end - start).isRightToLeft();
782     }
783 }
784
785 #ifndef QT_NO_CLIPBOARD
786 void QSGTextEdit::cut()
787 {
788     Q_D(QSGTextEdit);
789     d->control->cut();
790 }
791
792 void QSGTextEdit::copy()
793 {
794     Q_D(QSGTextEdit);
795     d->control->copy();
796 }
797
798 void QSGTextEdit::paste()
799 {
800     Q_D(QSGTextEdit);
801     d->control->paste();
802 }
803 #endif // QT_NO_CLIPBOARD
804
805 /*!
806 \overload
807 Handles the given mouse \a event.
808 */
809 void QSGTextEdit::mousePressEvent(QGraphicsSceneMouseEvent *event)
810 {
811     Q_D(QSGTextEdit);
812     if (d->focusOnPress){
813         bool hadActiveFocus = hasActiveFocus();
814         forceActiveFocus();
815         if (d->showInputPanelOnFocus) {
816             if (hasActiveFocus() && hadActiveFocus && !isReadOnly()) {
817                 // re-open input panel on press if already focused
818                 openSoftwareInputPanel();
819             }
820         } else { // show input panel on click
821             if (hasActiveFocus() && !hadActiveFocus) {
822                 d->clickCausedFocus = true;
823             }
824         }
825     }
826     d->control->processEvent(event, QPointF(0, -d->yoff));
827     if (!event->isAccepted())
828         QSGPaintedItem::mousePressEvent(event);
829 }
830
831 /*!
832 \overload
833 Handles the given mouse \a event.
834 */
835 void QSGTextEdit::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
836 {
837     Q_D(QSGTextEdit);
838     d->control->processEvent(event, QPointF(0, -d->yoff));
839     if (!d->showInputPanelOnFocus) { // input panel on click
840         if (d->focusOnPress && !isReadOnly() && boundingRect().contains(event->pos())) {
841             if (canvas() && canvas() == qApp->focusWidget()) {
842                 qt_widget_private(canvas())->handleSoftwareInputPanel(event->button(), d->clickCausedFocus);
843             }
844         }
845     }
846     d->clickCausedFocus = false;
847
848     if (!event->isAccepted())
849         QSGPaintedItem::mouseReleaseEvent(event);
850 }
851
852 /*!
853 \overload
854 Handles the given mouse \a event.
855 */
856 void QSGTextEdit::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
857 {
858     Q_D(QSGTextEdit);
859     d->control->processEvent(event, QPointF(0, -d->yoff));
860     if (!event->isAccepted())
861         QSGPaintedItem::mouseDoubleClickEvent(event);
862 }
863
864 /*!
865 \overload
866 Handles the given mouse \a event.
867 */
868 void QSGTextEdit::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
869 {
870     Q_D(QSGTextEdit);
871     d->control->processEvent(event, QPointF(0, -d->yoff));
872     if (!event->isAccepted())
873         QSGPaintedItem::mouseMoveEvent(event);
874 }
875
876 /*!
877 \overload
878 Handles the given input method \a event.
879 */
880 void QSGTextEdit::inputMethodEvent(QInputMethodEvent *event)
881 {
882     Q_D(QSGTextEdit);
883     const bool wasComposing = isInputMethodComposing();
884     d->control->processEvent(event, QPointF(0, -d->yoff));
885     if (wasComposing != isInputMethodComposing())
886         emit inputMethodComposingChanged();
887 }
888
889 void QSGTextEdit::itemChange(ItemChange change, const ItemChangeData &value)
890 {
891     Q_D(QSGTextEdit);
892     if (change == ItemActiveFocusHasChanged) {
893         setCursorVisible(value.boolValue && d->canvas && d->canvas->hasFocus());
894     }
895     QSGItem::itemChange(change, value);
896 }
897
898 /*!
899 \overload
900 Returns the value of the given \a property.
901 */
902 QVariant QSGTextEdit::inputMethodQuery(Qt::InputMethodQuery property) const
903 {
904     Q_D(const QSGTextEdit);
905     return d->control->inputMethodQuery(property);
906 }
907
908 /*!
909 Draws the contents of the text edit using the given \a painter within
910 the given \a bounds.
911 */
912 void QSGTextEdit::paint(QPainter *painter)
913 {
914     // XXX todo
915     QRect bounds(0, 0, width(), height());
916     Q_D(QSGTextEdit);
917
918     painter->setRenderHint(QPainter::TextAntialiasing, true);
919     painter->translate(0,d->yoff);
920
921     d->control->drawContents(painter, bounds.translated(0,-d->yoff));
922
923     painter->translate(0,-d->yoff);
924 }
925
926 void QSGTextEdit::updateImgCache(const QRectF &rf)
927 {
928     Q_D(const QSGTextEdit);
929     QRect r;
930     if (!rf.isValid()) {
931         r = QRect(0,0,INT_MAX,INT_MAX);
932     } else {
933         r = rf.toRect();
934         if (r.height() > INT_MAX/2) {
935             // Take care of overflow when translating "everything"
936             r.setTop(r.y() + d->yoff);
937             r.setBottom(INT_MAX/2);
938         } else {
939             r = r.translated(0,d->yoff);
940         }
941     }
942     update(r);
943 }
944
945 bool QSGTextEdit::canPaste() const
946 {
947     Q_D(const QSGTextEdit);
948     return d->canPaste;
949 }
950
951 bool QSGTextEdit::isInputMethodComposing() const
952 {
953     Q_D(const QSGTextEdit);
954     if (QTextLayout *layout = d->control->textCursor().block().layout())
955         return layout->preeditAreaText().length() > 0;
956     return false;
957 }
958
959 void QSGTextEditPrivate::init()
960 {
961     Q_Q(QSGTextEdit);
962
963     q->setSmooth(smooth);
964     q->setAcceptedMouseButtons(Qt::LeftButton);
965     q->setFlag(QSGItem::ItemAcceptsInputMethod);
966
967     control = new QTextControl(q);
968     control->setIgnoreUnusedNavigationEvents(true);
969     control->setTextInteractionFlags(Qt::LinksAccessibleByMouse | Qt::TextSelectableByKeyboard | Qt::TextEditable);
970     control->setDragEnabled(false);
971
972     // QTextControl follows the default text color
973     // defined by the platform, declarative text
974     // should be black by default
975     QPalette pal = control->palette();
976     if (pal.color(QPalette::Text) != color) {
977         pal.setColor(QPalette::Text, color);
978         control->setPalette(pal);
979     }
980
981     QObject::connect(control, SIGNAL(updateRequest(QRectF)), q, SLOT(updateImgCache(QRectF)));
982
983     QObject::connect(control, SIGNAL(textChanged()), q, SLOT(q_textChanged()));
984     QObject::connect(control, SIGNAL(selectionChanged()), q, SIGNAL(selectionChanged()));
985     QObject::connect(control, SIGNAL(selectionChanged()), q, SLOT(updateSelectionMarkers()));
986     QObject::connect(control, SIGNAL(cursorPositionChanged()), q, SLOT(updateSelectionMarkers()));
987     QObject::connect(control, SIGNAL(cursorPositionChanged()), q, SIGNAL(cursorPositionChanged()));
988     QObject::connect(control, SIGNAL(cursorPositionChanged()), q, SIGNAL(cursorRectangleChanged()));
989     QObject::connect(control, SIGNAL(linkActivated(QString)), q, SIGNAL(linkActivated(QString)));
990 #ifndef QT_NO_CLIPBOARD
991     QObject::connect(q, SIGNAL(readOnlyChanged(bool)), q, SLOT(q_canPasteChanged()));
992     QObject::connect(QApplication::clipboard(), SIGNAL(dataChanged()), q, SLOT(q_canPasteChanged()));
993     canPaste = control->canPaste();
994 #endif
995
996     document = control->document();
997     document->setDefaultFont(font);
998     document->setDocumentMargin(textMargin);
999     document->setUndoRedoEnabled(false); // flush undo buffer.
1000     document->setUndoRedoEnabled(true);
1001     updateDefaultTextOption();
1002 }
1003
1004 void QSGTextEdit::q_textChanged()
1005 {
1006     Q_D(QSGTextEdit);
1007     d->text = text();
1008     d->rightToLeftText = d->document->begin().layout()->engine()->isRightToLeft();
1009     d->determineHorizontalAlignment();
1010     d->updateDefaultTextOption();
1011     updateSize();
1012     updateTotalLines();
1013     updateMicroFocus();
1014     emit textChanged(d->text);
1015 }
1016
1017 void QSGTextEdit::moveCursorDelegate()
1018 {
1019     Q_D(QSGTextEdit);
1020     if(!d->cursor)
1021         return;
1022     QRectF cursorRect = d->control->cursorRect();
1023     d->cursor->setX(cursorRect.x());
1024     d->cursor->setY(cursorRect.y());
1025 }
1026
1027 void QSGTextEditPrivate::updateSelection()
1028 {
1029     Q_Q(QSGTextEdit);
1030     QTextCursor cursor = control->textCursor();
1031     bool startChange = (lastSelectionStart != cursor.selectionStart());
1032     bool endChange = (lastSelectionEnd != cursor.selectionEnd());
1033     cursor.beginEditBlock();
1034     cursor.setPosition(lastSelectionStart, QTextCursor::MoveAnchor);
1035     cursor.setPosition(lastSelectionEnd, QTextCursor::KeepAnchor);
1036     cursor.endEditBlock();
1037     control->setTextCursor(cursor);
1038     if(startChange)
1039         q->selectionStartChanged();
1040     if(endChange)
1041         q->selectionEndChanged();
1042 }
1043
1044 void QSGTextEdit::updateSelectionMarkers()
1045 {
1046     Q_D(QSGTextEdit);
1047     if(d->lastSelectionStart != d->control->textCursor().selectionStart()){
1048         d->lastSelectionStart = d->control->textCursor().selectionStart();
1049         emit selectionStartChanged();
1050     }
1051     if(d->lastSelectionEnd != d->control->textCursor().selectionEnd()){
1052         d->lastSelectionEnd = d->control->textCursor().selectionEnd();
1053         emit selectionEndChanged();
1054     }
1055     updateMicroFocus();
1056 }
1057
1058 QRectF QSGTextEdit::boundingRect() const
1059 {
1060     Q_D(const QSGTextEdit);
1061     QRectF r = QSGPaintedItem::boundingRect();
1062     int cursorWidth = 1;
1063     if(d->cursor)
1064         cursorWidth = d->cursor->width();
1065     if(!d->document->isEmpty())
1066         cursorWidth += 3;// ### Need a better way of accounting for space between char and cursor
1067
1068     // Could include font max left/right bearings to either side of rectangle.
1069
1070     r.setRight(r.right() + cursorWidth);
1071     return r.translated(0,d->yoff);
1072 }
1073
1074 qreal QSGTextEditPrivate::getImplicitWidth() const
1075 {
1076     Q_Q(const QSGTextEdit);
1077     if (!requireImplicitWidth) {
1078         // We don't calculate implicitWidth unless it is required.
1079         // We need to force a size update now to ensure implicitWidth is calculated
1080         const_cast<QSGTextEditPrivate*>(this)->requireImplicitWidth = true;
1081         const_cast<QSGTextEdit*>(q)->updateSize();
1082     }
1083     return implicitWidth;
1084 }
1085
1086 //### we should perhaps be a bit smarter here -- depending on what has changed, we shouldn't
1087 //    need to do all the calculations each time
1088 void QSGTextEdit::updateSize()
1089 {
1090     Q_D(QSGTextEdit);
1091     if (isComponentComplete()) {
1092         qreal naturalWidth = d->implicitWidth;
1093         // ### assumes that if the width is set, the text will fill to edges
1094         // ### (unless wrap is false, then clipping will occur)
1095         if (widthValid()) {
1096             if (!d->requireImplicitWidth) {
1097                 emit implicitWidthChanged();
1098                 // if the implicitWidth is used, then updateSize() has already been called (recursively)
1099                 if (d->requireImplicitWidth)
1100                     return;
1101             }
1102             if (d->requireImplicitWidth) {
1103                 d->document->setTextWidth(-1);
1104                 naturalWidth = d->document->idealWidth();
1105             }
1106             if (d->document->textWidth() != width())
1107                 d->document->setTextWidth(width());
1108         } else {
1109             d->document->setTextWidth(-1);
1110         }
1111         QFontMetrics fm = QFontMetrics(d->font);
1112         int dy = height();
1113         dy -= (int)d->document->size().height();
1114
1115         int nyoff;
1116         if (heightValid()) {
1117             if (d->vAlign == AlignBottom)
1118                 nyoff = dy;
1119             else if (d->vAlign == AlignVCenter)
1120                 nyoff = dy/2;
1121             else
1122                 nyoff = 0;
1123         } else {
1124             nyoff = 0;
1125         }
1126         if (nyoff != d->yoff) 
1127             d->yoff = nyoff;
1128         setBaselineOffset(fm.ascent() + d->yoff + d->textMargin);
1129
1130         //### need to comfirm cost of always setting these
1131         int newWidth = qCeil(d->document->idealWidth());
1132         if (!widthValid() && d->document->textWidth() != newWidth)
1133             d->document->setTextWidth(newWidth); // ### Text does not align if width is not set (QTextDoc bug)
1134         // ### Setting the implicitWidth triggers another updateSize(), and unless there are bindings nothing has changed.
1135         if (!widthValid())
1136             setImplicitWidth(newWidth);
1137         else if (d->requireImplicitWidth)
1138             setImplicitWidth(naturalWidth);
1139         qreal newHeight = d->document->isEmpty() ? fm.height() : (int)d->document->size().height();
1140         setImplicitHeight(newHeight);
1141
1142         d->paintedSize = QSize(newWidth, newHeight);
1143         setContentsSize(d->paintedSize);
1144         emit paintedSizeChanged();
1145     } else {
1146         d->dirty = true;
1147     }
1148     update();
1149 }
1150
1151 void QSGTextEdit::updateTotalLines()
1152 {
1153     Q_D(QSGTextEdit);
1154
1155     int subLines = 0;
1156
1157     for (QTextBlock it = d->document->begin(); it != d->document->end(); it = it.next()) {
1158         QTextLayout *layout = it.layout();
1159         if (!layout)
1160             continue;
1161         subLines += layout->lineCount()-1;
1162     }
1163
1164     int newTotalLines = d->document->lineCount() + subLines;
1165     if (d->lineCount != newTotalLines) {
1166         d->lineCount = newTotalLines;
1167         emit lineCountChanged();
1168     }
1169 }
1170
1171 void QSGTextEditPrivate::updateDefaultTextOption()
1172 {
1173     Q_Q(QSGTextEdit);
1174     QTextOption opt = document->defaultTextOption();
1175     int oldAlignment = opt.alignment();
1176
1177     QSGTextEdit::HAlignment horizontalAlignment = q->effectiveHAlign();
1178     if (rightToLeftText) {
1179         if (horizontalAlignment == QSGTextEdit::AlignLeft)
1180             horizontalAlignment = QSGTextEdit::AlignRight;
1181         else if (horizontalAlignment == QSGTextEdit::AlignRight)
1182             horizontalAlignment = QSGTextEdit::AlignLeft;
1183     }
1184     opt.setAlignment((Qt::Alignment)(int)(horizontalAlignment | vAlign));
1185
1186     QTextOption::WrapMode oldWrapMode = opt.wrapMode();
1187     opt.setWrapMode(QTextOption::WrapMode(wrapMode));
1188
1189     if (oldWrapMode == opt.wrapMode() && oldAlignment == opt.alignment())
1190         return;
1191     document->setDefaultTextOption(opt);
1192 }
1193
1194
1195 void QSGTextEdit::openSoftwareInputPanel()
1196 {
1197     if (qApp) {
1198         if (canvas() && canvas() == qApp->focusWidget()) {
1199             QEvent event(QEvent::RequestSoftwareInputPanel);
1200             QApplication::sendEvent(canvas(), &event);
1201         }
1202     }
1203 }
1204
1205 void QSGTextEdit::closeSoftwareInputPanel()
1206 {
1207     if (qApp) {
1208         if (canvas() && canvas() == qApp->focusWidget()) {
1209             QEvent event(QEvent::CloseSoftwareInputPanel);
1210             QApplication::sendEvent(canvas(), &event);
1211         }
1212     }
1213 }
1214
1215 void QSGTextEdit::focusInEvent(QFocusEvent *event)
1216 {
1217     Q_D(const QSGTextEdit);
1218     if (d->showInputPanelOnFocus) {
1219         if (d->focusOnPress && !isReadOnly()) {
1220             openSoftwareInputPanel();
1221         }
1222     }
1223     QSGPaintedItem::focusInEvent(event);
1224 }
1225
1226 void QSGTextEdit::q_canPasteChanged()
1227 {
1228     Q_D(QSGTextEdit);
1229     bool old = d->canPaste;
1230     d->canPaste = d->control->canPaste();
1231     if(old!=d->canPaste)
1232         emit canPasteChanged();
1233 }
1234
1235 QT_END_NAMESPACE