1 // Commit: 6980bca15b411f86b9fadb7484a6dd782b9d1403
2 /****************************************************************************
4 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
5 ** All rights reserved.
6 ** Contact: Nokia Corporation (qt-info@nokia.com)
8 ** This file is part of the QtDeclarative module of the Qt Toolkit.
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
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.
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.
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
41 ****************************************************************************/
43 #include "qsgtextedit_p.h"
44 #include "qsgtextedit_p_p.h"
45 #include "qsgevents_p_p.h"
46 #include "qsgcanvas.h"
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>
55 #include <private/qdeclarativeglobal_p.h>
56 #include <private/qtextcontrol_p.h>
57 #include <private/qtextengine_p.h>
58 #include <private/qwidget_p.h>
62 QWidgetPrivate *qt_widget_private(QWidget *widget);
64 QSGTextEdit::QSGTextEdit(QSGItem *parent)
65 : QSGImplicitSizePaintedItem(*(new QSGTextEditPrivate), parent)
71 QString QSGTextEdit::text() const
73 Q_D(const QSGTextEdit);
75 #ifndef QT_NO_TEXTHTMLPARSER
77 return d->document->toHtml();
80 return d->document->toPlainText();
83 void QSGTextEdit::setText(const QString &text)
86 if (QSGTextEdit::text() == text)
89 d->richText = d->format == RichText || (d->format == AutoText && Qt::mightBeRichText(text));
91 #ifndef QT_NO_TEXTHTMLPARSER
92 d->control->setHtml(text);
94 d->control->setPlainText(text);
97 d->control->setPlainText(text);
102 QSGTextEdit::TextFormat QSGTextEdit::textFormat() const
104 Q_D(const QSGTextEdit);
108 void QSGTextEdit::setTextFormat(TextFormat format)
111 if (format == d->format)
113 bool wasRich = d->richText;
114 d->richText = format == RichText || (format == AutoText && Qt::mightBeRichText(d->text));
116 if (wasRich && !d->richText) {
117 d->control->setPlainText(d->text);
119 } else if (!wasRich && d->richText) {
120 #ifndef QT_NO_TEXTHTMLPARSER
121 d->control->setHtml(d->text);
123 d->control->setPlainText(d->text);
128 d->control->setAcceptRichText(d->format != PlainText);
129 emit textFormatChanged(d->format);
132 QFont QSGTextEdit::font() const
134 Q_D(const QSGTextEdit);
135 return d->sourceFont;
138 void QSGTextEdit::setFont(const QFont &font)
141 if (d->sourceFont == font)
144 d->sourceFont = font;
145 QFont oldFont = d->font;
147 if (d->font.pointSizeF() != -1) {
149 qreal size = qRound(d->font.pointSizeF()*2.0);
150 d->font.setPointSizeF(size/2.0);
153 if (oldFont != d->font) {
154 d->document->setDefaultFont(d->font);
156 d->cursor->setHeight(QFontMetrics(d->font).height());
157 moveCursorDelegate();
162 emit fontChanged(d->sourceFont);
165 QColor QSGTextEdit::color() const
167 Q_D(const QSGTextEdit);
171 void QSGTextEdit::setColor(const QColor &color)
174 if (d->color == color)
178 QPalette pal = d->control->palette();
179 pal.setColor(QPalette::Text, color);
180 d->control->setPalette(pal);
182 emit colorChanged(d->color);
185 QColor QSGTextEdit::selectionColor() const
187 Q_D(const QSGTextEdit);
188 return d->selectionColor;
191 void QSGTextEdit::setSelectionColor(const QColor &color)
194 if (d->selectionColor == color)
197 d->selectionColor = color;
198 QPalette pal = d->control->palette();
199 pal.setColor(QPalette::Highlight, color);
200 d->control->setPalette(pal);
202 emit selectionColorChanged(d->selectionColor);
205 QColor QSGTextEdit::selectedTextColor() const
207 Q_D(const QSGTextEdit);
208 return d->selectedTextColor;
211 void QSGTextEdit::setSelectedTextColor(const QColor &color)
214 if (d->selectedTextColor == color)
217 d->selectedTextColor = color;
218 QPalette pal = d->control->palette();
219 pal.setColor(QPalette::HighlightedText, color);
220 d->control->setPalette(pal);
222 emit selectedTextColorChanged(d->selectedTextColor);
225 QSGTextEdit::HAlignment QSGTextEdit::hAlign() const
227 Q_D(const QSGTextEdit);
231 void QSGTextEdit::setHAlign(HAlignment align)
234 bool forceAlign = d->hAlignImplicit && d->effectiveLayoutMirror;
235 d->hAlignImplicit = false;
236 if (d->setHAlign(align, forceAlign) && isComponentComplete()) {
237 d->updateDefaultTextOption();
242 void QSGTextEdit::resetHAlign()
245 d->hAlignImplicit = true;
246 if (d->determineHorizontalAlignment() && isComponentComplete()) {
247 d->updateDefaultTextOption();
252 QSGTextEdit::HAlignment QSGTextEdit::effectiveHAlign() const
254 Q_D(const QSGTextEdit);
255 QSGTextEdit::HAlignment effectiveAlignment = d->hAlign;
256 if (!d->hAlignImplicit && d->effectiveLayoutMirror) {
258 case QSGTextEdit::AlignLeft:
259 effectiveAlignment = QSGTextEdit::AlignRight;
261 case QSGTextEdit::AlignRight:
262 effectiveAlignment = QSGTextEdit::AlignLeft;
268 return effectiveAlignment;
271 bool QSGTextEditPrivate::setHAlign(QSGTextEdit::HAlignment alignment, bool forceAlign)
274 if (hAlign != alignment || forceAlign) {
275 QSGTextEdit::HAlignment oldEffectiveHAlign = q->effectiveHAlign();
277 emit q->horizontalAlignmentChanged(alignment);
278 if (oldEffectiveHAlign != q->effectiveHAlign())
279 emit q->effectiveHorizontalAlignmentChanged();
285 bool QSGTextEditPrivate::determineHorizontalAlignment()
288 if (hAlignImplicit && q->isComponentComplete()) {
289 bool alignToRight = text.isEmpty() ? QApplication::keyboardInputDirection() == Qt::RightToLeft : rightToLeftText;
290 return setHAlign(alignToRight ? QSGTextEdit::AlignRight : QSGTextEdit::AlignLeft);
295 void QSGTextEditPrivate::mirrorChange()
298 if (q->isComponentComplete()) {
299 if (!hAlignImplicit && (hAlign == QSGTextEdit::AlignRight || hAlign == QSGTextEdit::AlignLeft)) {
300 updateDefaultTextOption();
302 emit q->effectiveHorizontalAlignmentChanged();
307 QSGTextEdit::VAlignment QSGTextEdit::vAlign() const
309 Q_D(const QSGTextEdit);
313 void QSGTextEdit::setVAlign(QSGTextEdit::VAlignment alignment)
316 if (alignment == d->vAlign)
318 d->vAlign = alignment;
319 d->updateDefaultTextOption();
321 emit verticalAlignmentChanged(d->vAlign);
324 QSGTextEdit::WrapMode QSGTextEdit::wrapMode() const
326 Q_D(const QSGTextEdit);
330 void QSGTextEdit::setWrapMode(WrapMode mode)
333 if (mode == d->wrapMode)
336 d->updateDefaultTextOption();
338 emit wrapModeChanged();
341 int QSGTextEdit::lineCount() const
343 Q_D(const QSGTextEdit);
347 qreal QSGTextEdit::paintedWidth() const
349 Q_D(const QSGTextEdit);
350 return d->paintedSize.width();
353 qreal QSGTextEdit::paintedHeight() const
355 Q_D(const QSGTextEdit);
356 return d->paintedSize.height();
359 QRectF QSGTextEdit::positionToRectangle(int pos) const
361 Q_D(const QSGTextEdit);
362 QTextCursor c(d->document);
364 return d->control->cursorRect(c);
368 int QSGTextEdit::positionAt(int x, int y) const
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()
381 if (preeditLength > 0
382 && d->document->documentLayout()->blockBoundingRect(cursor.block()).contains(x,y-d->yoff)) {
383 r = r > cursor.position() + preeditLength
391 void QSGTextEdit::moveCursorSelection(int pos)
393 //Note that this is the same as setCursorPosition but with the KeepAnchor flag set
395 QTextCursor cursor = d->control->textCursor();
396 if (cursor.position() == pos)
398 cursor.setPosition(pos, QTextCursor::KeepAnchor);
399 d->control->setTextCursor(cursor);
402 void QSGTextEdit::moveCursorSelection(int pos, SelectionMode mode)
405 QTextCursor cursor = d->control->textCursor();
406 if (cursor.position() == pos)
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);
417 cursor.setPosition(cursor.position(), QTextCursor::MoveAnchor);
419 cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor);
420 cursor.movePosition(QTextCursor::StartOfWord, QTextCursor::MoveAnchor);
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);
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);
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);
448 d->control->setTextCursor(cursor);
451 bool QSGTextEdit::isCursorVisible() const
453 Q_D(const QSGTextEdit);
454 return d->cursorVisible;
457 void QSGTextEdit::setCursorVisible(bool on)
460 if (d->cursorVisible == on)
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);
470 int QSGTextEdit::cursorPosition() const
472 Q_D(const QSGTextEdit);
473 return d->control->textCursor().position();
476 void QSGTextEdit::setCursorPosition(int pos)
479 if (pos < 0 || pos > d->text.length())
481 QTextCursor cursor = d->control->textCursor();
482 if (cursor.position() == pos && cursor.anchor() == pos)
484 cursor.setPosition(pos);
485 d->control->setTextCursor(cursor);
488 QDeclarativeComponent* QSGTextEdit::cursorDelegate() const
490 Q_D(const QSGTextEdit);
491 return d->cursorComponent;
494 void QSGTextEdit::setCursorDelegate(QDeclarativeComponent* c)
497 if(d->cursorComponent){
499 disconnect(d->control, SIGNAL(cursorPositionChanged()),
500 this, SLOT(moveCursorDelegate()));
501 d->control->setCursorWidth(-1);
502 update(cursorRectangle());
507 d->cursorComponent = c;
508 if(c && c->isReady()){
509 loadCursorDelegate();
512 connect(c, SIGNAL(statusChanged()),
513 this, SLOT(loadCursorDelegate()));
516 emit cursorDelegateChanged();
519 void QSGTextEdit::loadCursorDelegate()
522 if(d->cursorComponent->isLoading())
524 d->cursor = qobject_cast<QSGItem*>(d->cursorComponent->create(qmlContext(this)));
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();
535 qmlInfo(this) << "Error loading cursor delegate.";
539 int QSGTextEdit::selectionStart() const
541 Q_D(const QSGTextEdit);
542 return d->control->textCursor().selectionStart();
545 int QSGTextEdit::selectionEnd() const
547 Q_D(const QSGTextEdit);
548 return d->control->textCursor().selectionEnd();
551 QString QSGTextEdit::selectedText() const
553 Q_D(const QSGTextEdit);
554 return d->control->textCursor().selectedText();
557 bool QSGTextEdit::focusOnPress() const
559 Q_D(const QSGTextEdit);
560 return d->focusOnPress;
563 void QSGTextEdit::setFocusOnPress(bool on)
566 if (d->focusOnPress == on)
568 d->focusOnPress = on;
569 emit activeFocusOnPressChanged(d->focusOnPress);
572 bool QSGTextEdit::persistentSelection() const
574 Q_D(const QSGTextEdit);
575 return d->persistentSelection;
578 void QSGTextEdit::setPersistentSelection(bool on)
581 if (d->persistentSelection == on)
583 d->persistentSelection = on;
584 emit persistentSelectionChanged(d->persistentSelection);
587 qreal QSGTextEdit::textMargin() const
589 Q_D(const QSGTextEdit);
590 return d->textMargin;
593 void QSGTextEdit::setTextMargin(qreal margin)
596 if (d->textMargin == margin)
598 d->textMargin = margin;
599 d->document->setDocumentMargin(d->textMargin);
600 emit textMarginChanged(d->textMargin);
603 void QSGTextEdit::geometryChanged(const QRectF &newGeometry,
604 const QRectF &oldGeometry)
606 if (newGeometry.width() != oldGeometry.width())
608 QSGPaintedItem::geometryChanged(newGeometry, oldGeometry);
611 void QSGTextEdit::componentComplete()
614 QSGPaintedItem::componentComplete();
616 d->determineHorizontalAlignment();
617 d->updateDefaultTextOption();
623 bool QSGTextEdit::selectByMouse() const
625 Q_D(const QSGTextEdit);
626 return d->selectByMouse;
629 void QSGTextEdit::setSelectByMouse(bool on)
632 if (d->selectByMouse != on) {
633 d->selectByMouse = on;
634 setKeepMouseGrab(on);
636 setTextInteractionFlags(d->control->textInteractionFlags() | Qt::TextSelectableByMouse);
638 setTextInteractionFlags(d->control->textInteractionFlags() & ~Qt::TextSelectableByMouse);
639 emit selectByMouseChanged(on);
643 QSGTextEdit::SelectionMode QSGTextEdit::mouseSelectionMode() const
645 Q_D(const QSGTextEdit);
646 return d->mouseSelectionMode;
649 void QSGTextEdit::setMouseSelectionMode(SelectionMode mode)
652 if (d->mouseSelectionMode != mode) {
653 d->mouseSelectionMode = mode;
654 d->control->setWordSelectionEnabled(mode == SelectWords);
655 emit mouseSelectionModeChanged(mode);
659 void QSGTextEdit::setReadOnly(bool r)
662 if (r == isReadOnly())
665 setFlag(QSGItem::ItemAcceptsInputMethod, !r);
666 Qt::TextInteractionFlags flags = Qt::LinksAccessibleByMouse;
667 if (d->selectByMouse)
668 flags = flags | Qt::TextSelectableByMouse;
670 flags = flags | Qt::TextSelectableByKeyboard | Qt::TextEditable;
671 d->control->setTextInteractionFlags(flags);
673 d->control->moveCursor(QTextCursor::End);
675 emit readOnlyChanged(r);
678 bool QSGTextEdit::isReadOnly() const
680 Q_D(const QSGTextEdit);
681 return !(d->control->textInteractionFlags() & Qt::TextEditable);
684 void QSGTextEdit::setTextInteractionFlags(Qt::TextInteractionFlags flags)
687 d->control->setTextInteractionFlags(flags);
690 Qt::TextInteractionFlags QSGTextEdit::textInteractionFlags() const
692 Q_D(const QSGTextEdit);
693 return d->control->textInteractionFlags();
696 QRect QSGTextEdit::cursorRectangle() const
698 Q_D(const QSGTextEdit);
699 return d->control->cursorRect().toRect().translated(0,-d->yoff);
702 bool QSGTextEdit::event(QEvent *event)
705 if (event->type() == QEvent::ShortcutOverride) {
706 d->control->processEvent(event, QPointF(0, -d->yoff));
707 return event->isAccepted();
709 return QSGPaintedItem::event(event);
714 Handles the given key \a event.
716 void QSGTextEdit::keyPressEvent(QKeyEvent *event)
719 d->control->processEvent(event, QPointF(0, -d->yoff));
720 if (!event->isAccepted())
721 QSGPaintedItem::keyPressEvent(event);
726 Handles the given key \a event.
728 void QSGTextEdit::keyReleaseEvent(QKeyEvent *event)
731 d->control->processEvent(event, QPointF(0, -d->yoff));
732 if (!event->isAccepted())
733 QSGPaintedItem::keyReleaseEvent(event);
736 void QSGTextEdit::deselect()
739 QTextCursor c = d->control->textCursor();
741 d->control->setTextCursor(c);
744 void QSGTextEdit::selectAll()
747 d->control->selectAll();
750 void QSGTextEdit::selectWord()
753 QTextCursor c = d->control->textCursor();
754 c.select(QTextCursor::WordUnderCursor);
755 d->control->setTextCursor(c);
758 void QSGTextEdit::select(int start, int end)
761 if (start < 0 || end < 0 || start > d->text.length() || end > d->text.length())
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);
771 updateSelectionMarkers();
774 bool QSGTextEdit::isRightToLeft(int start, int end)
778 qmlInfo(this) << "isRightToLeft(start, end) called with the end property being smaller than the start.";
781 return d->text.mid(start, end - start).isRightToLeft();
785 #ifndef QT_NO_CLIPBOARD
786 void QSGTextEdit::cut()
792 void QSGTextEdit::copy()
798 void QSGTextEdit::paste()
803 #endif // QT_NO_CLIPBOARD
807 Handles the given mouse \a event.
809 void QSGTextEdit::mousePressEvent(QGraphicsSceneMouseEvent *event)
812 if (d->focusOnPress){
813 bool hadActiveFocus = hasActiveFocus();
815 if (d->showInputPanelOnFocus) {
816 if (hasActiveFocus() && hadActiveFocus && !isReadOnly()) {
817 // re-open input panel on press if already focused
818 openSoftwareInputPanel();
820 } else { // show input panel on click
821 if (hasActiveFocus() && !hadActiveFocus) {
822 d->clickCausedFocus = true;
826 d->control->processEvent(event, QPointF(0, -d->yoff));
827 if (!event->isAccepted())
828 QSGPaintedItem::mousePressEvent(event);
833 Handles the given mouse \a event.
835 void QSGTextEdit::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
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);
846 d->clickCausedFocus = false;
848 if (!event->isAccepted())
849 QSGPaintedItem::mouseReleaseEvent(event);
854 Handles the given mouse \a event.
856 void QSGTextEdit::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
859 d->control->processEvent(event, QPointF(0, -d->yoff));
860 if (!event->isAccepted())
861 QSGPaintedItem::mouseDoubleClickEvent(event);
866 Handles the given mouse \a event.
868 void QSGTextEdit::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
871 d->control->processEvent(event, QPointF(0, -d->yoff));
872 if (!event->isAccepted())
873 QSGPaintedItem::mouseMoveEvent(event);
878 Handles the given input method \a event.
880 void QSGTextEdit::inputMethodEvent(QInputMethodEvent *event)
883 const bool wasComposing = isInputMethodComposing();
884 d->control->processEvent(event, QPointF(0, -d->yoff));
885 if (wasComposing != isInputMethodComposing())
886 emit inputMethodComposingChanged();
889 void QSGTextEdit::itemChange(ItemChange change, const ItemChangeData &value)
892 if (change == ItemActiveFocusHasChanged) {
893 setCursorVisible(value.boolValue && d->canvas && d->canvas->hasFocus());
895 QSGItem::itemChange(change, value);
900 Returns the value of the given \a property.
902 QVariant QSGTextEdit::inputMethodQuery(Qt::InputMethodQuery property) const
904 Q_D(const QSGTextEdit);
905 return d->control->inputMethodQuery(property);
909 Draws the contents of the text edit using the given \a painter within
912 void QSGTextEdit::paint(QPainter *painter)
915 QRect bounds(0, 0, width(), height());
918 painter->setRenderHint(QPainter::TextAntialiasing, true);
919 painter->translate(0,d->yoff);
921 d->control->drawContents(painter, bounds.translated(0,-d->yoff));
923 painter->translate(0,-d->yoff);
926 void QSGTextEdit::updateImgCache(const QRectF &rf)
928 Q_D(const QSGTextEdit);
931 r = QRect(0,0,INT_MAX,INT_MAX);
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);
939 r = r.translated(0,d->yoff);
945 bool QSGTextEdit::canPaste() const
947 Q_D(const QSGTextEdit);
951 bool QSGTextEdit::isInputMethodComposing() const
953 Q_D(const QSGTextEdit);
954 if (QTextLayout *layout = d->control->textCursor().block().layout())
955 return layout->preeditAreaText().length() > 0;
959 void QSGTextEditPrivate::init()
963 q->setSmooth(smooth);
964 q->setAcceptedMouseButtons(Qt::LeftButton);
965 q->setFlag(QSGItem::ItemAcceptsInputMethod);
967 control = new QTextControl(q);
968 control->setIgnoreUnusedNavigationEvents(true);
969 control->setTextInteractionFlags(Qt::LinksAccessibleByMouse | Qt::TextSelectableByKeyboard | Qt::TextEditable);
970 control->setDragEnabled(false);
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);
981 QObject::connect(control, SIGNAL(updateRequest(QRectF)), q, SLOT(updateImgCache(QRectF)));
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();
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();
1004 void QSGTextEdit::q_textChanged()
1008 d->rightToLeftText = d->document->begin().layout()->engine()->isRightToLeft();
1009 d->determineHorizontalAlignment();
1010 d->updateDefaultTextOption();
1014 emit textChanged(d->text);
1017 void QSGTextEdit::moveCursorDelegate()
1022 QRectF cursorRect = d->control->cursorRect();
1023 d->cursor->setX(cursorRect.x());
1024 d->cursor->setY(cursorRect.y());
1027 void QSGTextEditPrivate::updateSelection()
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);
1039 q->selectionStartChanged();
1041 q->selectionEndChanged();
1044 void QSGTextEdit::updateSelectionMarkers()
1047 if(d->lastSelectionStart != d->control->textCursor().selectionStart()){
1048 d->lastSelectionStart = d->control->textCursor().selectionStart();
1049 emit selectionStartChanged();
1051 if(d->lastSelectionEnd != d->control->textCursor().selectionEnd()){
1052 d->lastSelectionEnd = d->control->textCursor().selectionEnd();
1053 emit selectionEndChanged();
1058 QRectF QSGTextEdit::boundingRect() const
1060 Q_D(const QSGTextEdit);
1061 QRectF r = QSGPaintedItem::boundingRect();
1062 int cursorWidth = 1;
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
1068 // Could include font max left/right bearings to either side of rectangle.
1070 r.setRight(r.right() + cursorWidth);
1071 return r.translated(0,d->yoff);
1074 qreal QSGTextEditPrivate::getImplicitWidth() const
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();
1083 return implicitWidth;
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()
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)
1096 if (!d->requireImplicitWidth) {
1097 emit implicitWidthChanged();
1098 // if the implicitWidth is used, then updateSize() has already been called (recursively)
1099 if (d->requireImplicitWidth)
1102 if (d->requireImplicitWidth) {
1103 d->document->setTextWidth(-1);
1104 naturalWidth = d->document->idealWidth();
1106 if (d->document->textWidth() != width())
1107 d->document->setTextWidth(width());
1109 d->document->setTextWidth(-1);
1111 QFontMetrics fm = QFontMetrics(d->font);
1113 dy -= (int)d->document->size().height();
1116 if (heightValid()) {
1117 if (d->vAlign == AlignBottom)
1119 else if (d->vAlign == AlignVCenter)
1126 if (nyoff != d->yoff)
1128 setBaselineOffset(fm.ascent() + d->yoff + d->textMargin);
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.
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);
1142 d->paintedSize = QSize(newWidth, newHeight);
1143 setContentsSize(d->paintedSize);
1144 emit paintedSizeChanged();
1151 void QSGTextEdit::updateTotalLines()
1157 for (QTextBlock it = d->document->begin(); it != d->document->end(); it = it.next()) {
1158 QTextLayout *layout = it.layout();
1161 subLines += layout->lineCount()-1;
1164 int newTotalLines = d->document->lineCount() + subLines;
1165 if (d->lineCount != newTotalLines) {
1166 d->lineCount = newTotalLines;
1167 emit lineCountChanged();
1171 void QSGTextEditPrivate::updateDefaultTextOption()
1174 QTextOption opt = document->defaultTextOption();
1175 int oldAlignment = opt.alignment();
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;
1184 opt.setAlignment((Qt::Alignment)(int)(horizontalAlignment | vAlign));
1186 QTextOption::WrapMode oldWrapMode = opt.wrapMode();
1187 opt.setWrapMode(QTextOption::WrapMode(wrapMode));
1189 if (oldWrapMode == opt.wrapMode() && oldAlignment == opt.alignment())
1191 document->setDefaultTextOption(opt);
1195 void QSGTextEdit::openSoftwareInputPanel()
1198 if (canvas() && canvas() == qApp->focusWidget()) {
1199 QEvent event(QEvent::RequestSoftwareInputPanel);
1200 QApplication::sendEvent(canvas(), &event);
1205 void QSGTextEdit::closeSoftwareInputPanel()
1208 if (canvas() && canvas() == qApp->focusWidget()) {
1209 QEvent event(QEvent::CloseSoftwareInputPanel);
1210 QApplication::sendEvent(canvas(), &event);
1215 void QSGTextEdit::focusInEvent(QFocusEvent *event)
1217 Q_D(const QSGTextEdit);
1218 if (d->showInputPanelOnFocus) {
1219 if (d->focusOnPress && !isReadOnly()) {
1220 openSoftwareInputPanel();
1223 QSGPaintedItem::focusInEvent(event);
1226 void QSGTextEdit::q_canPasteChanged()
1229 bool old = d->canPaste;
1230 d->canPaste = d->control->canPaste();
1231 if(old!=d->canPaste)
1232 emit canPasteChanged();