405540d419cc708a0df50fb865e2c5d3a2af5192
[profile/ivi/qtdeclarative.git] / src / declarative / items / qsgtextinput.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qsgtextinput_p.h"
43 #include "qsgtextinput_p_p.h"
44 #include "qsgcanvas.h"
45
46 #include <private/qdeclarativeglobal_p.h>
47 #include <private/qwidget_p.h>
48
49 #include <QtDeclarative/qdeclarativeinfo.h>
50 #include <QtWidgets/qgraphicssceneevent.h>
51 #include <QtWidgets/qinputcontext.h>
52 #include <QTextBoundaryFinder>
53 #include <qstyle.h>
54
55 QT_BEGIN_NAMESPACE
56
57 QWidgetPrivate *qt_widget_private(QWidget *widget);
58
59 QSGTextInput::QSGTextInput(QSGItem* parent)
60 : QSGImplicitSizePaintedItem(*(new QSGTextInputPrivate), parent)
61 {
62     Q_D(QSGTextInput);
63     d->init();
64 }
65
66 QSGTextInput::~QSGTextInput()
67 {
68 }
69
70 QString QSGTextInput::text() const
71 {
72     Q_D(const QSGTextInput);
73     return d->control->text();
74 }
75
76 void QSGTextInput::setText(const QString &s)
77 {
78     Q_D(QSGTextInput);
79     if(s == text())
80         return;
81     d->control->setText(s);
82 }
83
84 QFont QSGTextInput::font() const
85 {
86     Q_D(const QSGTextInput);
87     return d->sourceFont;
88 }
89
90 void QSGTextInput::setFont(const QFont &font)
91 {
92     Q_D(QSGTextInput);
93     if (d->sourceFont == font)
94         return;
95
96     d->sourceFont = font;
97     QFont oldFont = d->font;
98     d->font = font;
99     if (d->font.pointSizeF() != -1) {
100         // 0.5pt resolution
101         qreal size = qRound(d->font.pointSizeF()*2.0);
102         d->font.setPointSizeF(size/2.0);
103     }
104     if (oldFont != d->font) {
105         d->control->setFont(d->font);
106         updateSize();
107         updateCursorRectangle();
108         if(d->cursorItem){
109             d->cursorItem->setHeight(QFontMetrics(d->font).height());
110         }
111     }
112     emit fontChanged(d->sourceFont);
113 }
114
115 QColor QSGTextInput::color() const
116 {
117     Q_D(const QSGTextInput);
118     return d->color;
119 }
120
121 void QSGTextInput::setColor(const QColor &c)
122 {
123     Q_D(QSGTextInput);
124     if (c != d->color) {
125         d->color = c;
126         update();
127         emit colorChanged(c);
128     }
129 }
130
131 QColor QSGTextInput::selectionColor() const
132 {
133     Q_D(const QSGTextInput);
134     return d->selectionColor;
135 }
136
137 void QSGTextInput::setSelectionColor(const QColor &color)
138 {
139     Q_D(QSGTextInput);
140     if (d->selectionColor == color)
141         return;
142
143     d->selectionColor = color;
144     QPalette p = d->control->palette();
145     p.setColor(QPalette::Highlight, d->selectionColor);
146     d->control->setPalette(p);
147     if (d->control->hasSelectedText()) 
148         update();
149     emit selectionColorChanged(color);
150 }
151
152 QColor QSGTextInput::selectedTextColor() const
153 {
154     Q_D(const QSGTextInput);
155     return d->selectedTextColor;
156 }
157
158 void QSGTextInput::setSelectedTextColor(const QColor &color)
159 {
160     Q_D(QSGTextInput);
161     if (d->selectedTextColor == color)
162         return;
163
164     d->selectedTextColor = color;
165     QPalette p = d->control->palette();
166     p.setColor(QPalette::HighlightedText, d->selectedTextColor);
167     d->control->setPalette(p);
168     if (d->control->hasSelectedText()) 
169         update();
170     emit selectedTextColorChanged(color);
171 }
172
173 QSGTextInput::HAlignment QSGTextInput::hAlign() const
174 {
175     Q_D(const QSGTextInput);
176     return d->hAlign;
177 }
178
179 void QSGTextInput::setHAlign(HAlignment align)
180 {
181     Q_D(QSGTextInput);
182     bool forceAlign = d->hAlignImplicit && d->effectiveLayoutMirror;
183     d->hAlignImplicit = false;
184     if (d->setHAlign(align, forceAlign) && isComponentComplete()) {
185         updateCursorRectangle();
186     }
187 }
188
189 void QSGTextInput::resetHAlign()
190 {
191     Q_D(QSGTextInput);
192     d->hAlignImplicit = true;
193     if (d->determineHorizontalAlignment() && isComponentComplete()) {
194         updateCursorRectangle();
195     }
196 }
197
198 QSGTextInput::HAlignment QSGTextInput::effectiveHAlign() const
199 {
200     Q_D(const QSGTextInput);
201     QSGTextInput::HAlignment effectiveAlignment = d->hAlign;
202     if (!d->hAlignImplicit && d->effectiveLayoutMirror) {
203         switch (d->hAlign) {
204         case QSGTextInput::AlignLeft:
205             effectiveAlignment = QSGTextInput::AlignRight;
206             break;
207         case QSGTextInput::AlignRight:
208             effectiveAlignment = QSGTextInput::AlignLeft;
209             break;
210         default:
211             break;
212         }
213     }
214     return effectiveAlignment;
215 }
216
217 bool QSGTextInputPrivate::setHAlign(QSGTextInput::HAlignment alignment, bool forceAlign)
218 {
219     Q_Q(QSGTextInput);
220     if ((hAlign != alignment || forceAlign) && alignment <= QSGTextInput::AlignHCenter) { // justify not supported
221         QSGTextInput::HAlignment oldEffectiveHAlign = q->effectiveHAlign();
222         hAlign = alignment;
223         emit q->horizontalAlignmentChanged(alignment);
224         if (oldEffectiveHAlign != q->effectiveHAlign())
225             emit q->effectiveHorizontalAlignmentChanged();
226         return true;
227     }
228     return false;
229 }
230
231 bool QSGTextInputPrivate::determineHorizontalAlignment()
232 {
233     if (hAlignImplicit) {
234         // if no explicit alignment has been set, follow the natural layout direction of the text
235         QString text = control->text();
236         bool isRightToLeft = text.isEmpty() ? QApplication::keyboardInputDirection() == Qt::RightToLeft : text.isRightToLeft();
237         return setHAlign(isRightToLeft ? QSGTextInput::AlignRight : QSGTextInput::AlignLeft);
238     }
239     return false;
240 }
241
242 void QSGTextInputPrivate::mirrorChange()
243 {
244     Q_Q(QSGTextInput);
245     if (q->isComponentComplete()) {
246         if (!hAlignImplicit && (hAlign == QSGTextInput::AlignRight || hAlign == QSGTextInput::AlignLeft)) {
247             q->updateCursorRectangle();
248             emit q->effectiveHorizontalAlignmentChanged();
249         }
250     }
251 }
252
253 bool QSGTextInput::isReadOnly() const
254 {
255     Q_D(const QSGTextInput);
256     return d->control->isReadOnly();
257 }
258
259 void QSGTextInput::setReadOnly(bool ro)
260 {
261     Q_D(QSGTextInput);
262     if (d->control->isReadOnly() == ro)
263         return;
264
265     setFlag(QSGItem::ItemAcceptsInputMethod, !ro);
266     d->control->setReadOnly(ro);
267     if (!ro)
268         d->control->setCursorPosition(d->control->end());
269
270     emit readOnlyChanged(ro);
271 }
272
273 int QSGTextInput::maxLength() const
274 {
275     Q_D(const QSGTextInput);
276     return d->control->maxLength();
277 }
278
279 void QSGTextInput::setMaxLength(int ml)
280 {
281     Q_D(QSGTextInput);
282     if (d->control->maxLength() == ml)
283         return;
284
285     d->control->setMaxLength(ml);
286
287     emit maximumLengthChanged(ml);
288 }
289
290 bool QSGTextInput::isCursorVisible() const
291 {
292     Q_D(const QSGTextInput);
293     return d->cursorVisible;
294 }
295
296 void QSGTextInput::setCursorVisible(bool on)
297 {
298     Q_D(QSGTextInput);
299     if (d->cursorVisible == on)
300         return;
301     d->cursorVisible = on;
302     d->control->setCursorBlinkPeriod(on?QApplication::cursorFlashTime():0);
303     QRect r = d->control->cursorRect();
304     if (d->control->inputMask().isEmpty())
305         updateRect(r);
306     else
307         updateRect();
308     emit cursorVisibleChanged(d->cursorVisible);
309 }
310
311 int QSGTextInput::cursorPosition() const
312 {
313     Q_D(const QSGTextInput);
314     return d->control->cursor();
315 }
316 void QSGTextInput::setCursorPosition(int cp)
317 {
318     Q_D(QSGTextInput);
319     if (cp < 0 || cp > d->control->text().length())
320         return;
321     d->control->moveCursor(cp);
322 }
323
324 QRect QSGTextInput::cursorRectangle() const
325 {
326     Q_D(const QSGTextInput);
327     QRect r = d->control->cursorRect();
328     // Scroll and make consistent with TextEdit
329     // QLineControl inexplicably adds 1 to the height and horizontal padding
330     // for unicode direction markers.
331     r.adjust(5 - d->hscroll, 0, -4 - d->hscroll, -1);
332     return r;
333 }
334
335 int QSGTextInput::selectionStart() const
336 {
337     Q_D(const QSGTextInput);
338     return d->lastSelectionStart;
339 }
340
341 int QSGTextInput::selectionEnd() const
342 {
343     Q_D(const QSGTextInput);
344     return d->lastSelectionEnd;
345 }
346
347 void QSGTextInput::select(int start, int end)
348 {
349     Q_D(QSGTextInput);
350     if (start < 0 || end < 0 || start > d->control->text().length() || end > d->control->text().length())
351         return;
352     d->control->setSelection(start, end-start);
353 }
354
355 QString QSGTextInput::selectedText() const
356 {
357     Q_D(const QSGTextInput);
358     return d->control->selectedText();
359 }
360
361 bool QSGTextInput::focusOnPress() const
362 {
363     Q_D(const QSGTextInput);
364     return d->focusOnPress;
365 }
366
367 void QSGTextInput::setFocusOnPress(bool b)
368 {
369     Q_D(QSGTextInput);
370     if (d->focusOnPress == b)
371         return;
372
373     d->focusOnPress = b;
374
375     emit activeFocusOnPressChanged(d->focusOnPress);
376 }
377
378 bool QSGTextInput::autoScroll() const
379 {
380     Q_D(const QSGTextInput);
381     return d->autoScroll;
382 }
383
384 void QSGTextInput::setAutoScroll(bool b)
385 {
386     Q_D(QSGTextInput);
387     if (d->autoScroll == b)
388         return;
389
390     d->autoScroll = b;
391     //We need to repaint so that the scrolling is taking into account.
392     updateSize(true);
393     updateCursorRectangle();
394     emit autoScrollChanged(d->autoScroll);
395 }
396
397 #ifndef QT_NO_VALIDATOR
398 QValidator* QSGTextInput::validator() const
399 {
400     Q_D(const QSGTextInput);
401     //###const cast isn't good, but needed for property system?
402     return const_cast<QValidator*>(d->control->validator());
403 }
404
405 void QSGTextInput::setValidator(QValidator* v)
406 {
407     Q_D(QSGTextInput);
408     if (d->control->validator() == v)
409         return;
410
411     d->control->setValidator(v);
412     if(!d->control->hasAcceptableInput()){
413         d->oldValidity = false;
414         emit acceptableInputChanged();
415     }
416
417     emit validatorChanged();
418 }
419 #endif // QT_NO_VALIDATOR
420
421 QString QSGTextInput::inputMask() const
422 {
423     Q_D(const QSGTextInput);
424     return d->control->inputMask();
425 }
426
427 void QSGTextInput::setInputMask(const QString &im)
428 {
429     Q_D(QSGTextInput);
430     if (d->control->inputMask() == im)
431         return;
432
433     d->control->setInputMask(im);
434     emit inputMaskChanged(d->control->inputMask());
435 }
436
437 bool QSGTextInput::hasAcceptableInput() const
438 {
439     Q_D(const QSGTextInput);
440     return d->control->hasAcceptableInput();
441 }
442
443 void QSGTextInputPrivate::updateInputMethodHints()
444 {
445     Q_Q(QSGTextInput);
446     Qt::InputMethodHints hints = inputMethodHints;
447     uint echo = control->echoMode();
448     if (echo == QSGTextInput::Password || echo == QSGTextInput::NoEcho)
449         hints |= Qt::ImhHiddenText;
450     else if (echo == QSGTextInput::PasswordEchoOnEdit)
451         hints &= ~Qt::ImhHiddenText;
452     if (echo != QSGTextInput::Normal)
453         hints |= (Qt::ImhNoAutoUppercase | Qt::ImhNoPredictiveText);
454     q->setInputMethodHints(hints);
455 }
456
457 QSGTextInput::EchoMode QSGTextInput::echoMode() const
458 {
459     Q_D(const QSGTextInput);
460     return (QSGTextInput::EchoMode)d->control->echoMode();
461 }
462
463 void QSGTextInput::setEchoMode(QSGTextInput::EchoMode echo)
464 {
465     Q_D(QSGTextInput);
466     if (echoMode() == echo)
467         return;
468     d->control->setEchoMode((QLineControl::EchoMode)echo);
469     d->updateInputMethodHints();
470     q_textChanged();
471     emit echoModeChanged(echoMode());
472 }
473
474 Qt::InputMethodHints QSGTextInput::imHints() const
475 {
476     Q_D(const QSGTextInput);
477     return d->inputMethodHints;
478 }
479
480 void QSGTextInput::setIMHints(Qt::InputMethodHints hints)
481 {
482     Q_D(QSGTextInput);
483     if (d->inputMethodHints == hints)
484         return;
485     d->inputMethodHints = hints;
486     d->updateInputMethodHints();
487 }
488
489 QDeclarativeComponent* QSGTextInput::cursorDelegate() const
490 {
491     Q_D(const QSGTextInput);
492     return d->cursorComponent;
493 }
494
495 void QSGTextInput::setCursorDelegate(QDeclarativeComponent* c)
496 {
497     Q_D(QSGTextInput);
498     if (d->cursorComponent == c)
499         return;
500
501     d->cursorComponent = c;
502     if(!c){
503         //note that the components are owned by something else
504         delete d->cursorItem;
505     }else{
506         d->startCreatingCursor();
507     }
508
509     emit cursorDelegateChanged();
510 }
511
512 void QSGTextInputPrivate::startCreatingCursor()
513 {
514     Q_Q(QSGTextInput);
515     if(cursorComponent->isReady()){
516         q->createCursor();
517     }else if(cursorComponent->isLoading()){
518         q->connect(cursorComponent, SIGNAL(statusChanged(int)),
519                 q, SLOT(createCursor()));
520     }else {//isError
521         qmlInfo(q, cursorComponent->errors()) << QSGTextInput::tr("Could not load cursor delegate");
522     }
523 }
524
525 void QSGTextInput::createCursor()
526 {
527     Q_D(QSGTextInput);
528     if(d->cursorComponent->isError()){
529         qmlInfo(this, d->cursorComponent->errors()) << tr("Could not load cursor delegate");
530         return;
531     }
532
533     if(!d->cursorComponent->isReady())
534         return;
535
536     if(d->cursorItem)
537         delete d->cursorItem;
538     d->cursorItem = qobject_cast<QSGItem*>(d->cursorComponent->create());
539     if(!d->cursorItem){
540         qmlInfo(this, d->cursorComponent->errors()) << tr("Could not instantiate cursor delegate");
541         return;
542     }
543
544     QDeclarative_setParent_noEvent(d->cursorItem, this);
545     d->cursorItem->setParentItem(this);
546     d->cursorItem->setX(d->control->cursorToX());
547     d->cursorItem->setHeight(d->control->height()-1); // -1 to counter QLineControl's +1 which is not consistent with Text.
548 }
549
550 QRectF QSGTextInput::positionToRectangle(int pos) const
551 {
552     Q_D(const QSGTextInput);
553     if (pos > d->control->cursorPosition())
554         pos += d->control->preeditAreaText().length();
555     return QRectF(d->control->cursorToX(pos)-d->hscroll,
556         0.0,
557         d->control->cursorWidth(),
558         cursorRectangle().height());
559 }
560
561 int QSGTextInput::positionAt(int x) const
562 {
563     return positionAt(x, CursorBetweenCharacters);
564 }
565
566 int QSGTextInput::positionAt(int x, CursorPosition position) const
567 {
568     Q_D(const QSGTextInput);
569     int pos = d->control->xToPos(x + d->hscroll, QTextLine::CursorPosition(position));
570     const int cursor = d->control->cursor();
571     if (pos > cursor) {
572         const int preeditLength = d->control->preeditAreaText().length();
573         pos = pos > cursor + preeditLength
574                 ? pos - preeditLength
575                 : cursor;
576     }
577     return pos;
578 }
579
580 void QSGTextInput::keyPressEvent(QKeyEvent* ev)
581 {
582     Q_D(QSGTextInput);
583     // Don't allow MacOSX up/down support, and we don't allow a completer.
584     bool ignore = (ev->key() == Qt::Key_Up || ev->key() == Qt::Key_Down) && ev->modifiers() == Qt::NoModifier;
585     if (!ignore && (d->lastSelectionStart == d->lastSelectionEnd) && (ev->key() == Qt::Key_Right || ev->key() == Qt::Key_Left)) {
586         // Ignore when moving off the end unless there is a selection,
587         // because then moving will do something (deselect).
588         int cursorPosition = d->control->cursor();
589         if (cursorPosition == 0)
590             ignore = ev->key() == (d->control->layoutDirection() == Qt::LeftToRight ? Qt::Key_Left : Qt::Key_Right);
591         if (cursorPosition == d->control->text().length())
592             ignore = ev->key() == (d->control->layoutDirection() == Qt::LeftToRight ? Qt::Key_Right : Qt::Key_Left);
593     }
594     if (ignore) {
595         ev->ignore();
596     } else {
597         d->control->processKeyEvent(ev);
598     }
599     if (!ev->isAccepted())
600         QSGPaintedItem::keyPressEvent(ev);
601 }
602
603 void QSGTextInput::inputMethodEvent(QInputMethodEvent *ev)
604 {
605     Q_D(QSGTextInput);
606     const bool wasComposing = d->control->preeditAreaText().length() > 0;
607     if (d->control->isReadOnly()) {
608         ev->ignore();
609     } else {
610         d->control->processInputMethodEvent(ev);
611     }
612     if (!ev->isAccepted())
613         QSGPaintedItem::inputMethodEvent(ev);
614
615     if (wasComposing != (d->control->preeditAreaText().length() > 0))
616         emit inputMethodComposingChanged();
617 }
618
619 void QSGTextInput::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
620 {
621     Q_D(QSGTextInput);
622     if (d->sendMouseEventToInputContext(event, QEvent::MouseButtonDblClick))
623         return;
624     if (d->selectByMouse) {
625         int cursor = d->xToPos(event->pos().x());
626         d->control->selectWordAtPos(cursor);
627         event->setAccepted(true);
628     } else {
629         QSGPaintedItem::mouseDoubleClickEvent(event);
630     }
631 }
632
633 void QSGTextInput::mousePressEvent(QGraphicsSceneMouseEvent *event)
634 {
635     Q_D(QSGTextInput);
636     if (d->sendMouseEventToInputContext(event, QEvent::MouseButtonPress))
637         return;
638     if(d->focusOnPress){
639         bool hadActiveFocus = hasActiveFocus();
640         forceActiveFocus();
641         if (d->showInputPanelOnFocus) {
642             if (hasActiveFocus() && hadActiveFocus && !isReadOnly()) {
643                 // re-open input panel on press if already focused
644                 openSoftwareInputPanel();
645             }
646         } else { // show input panel on click
647             if (hasActiveFocus() && !hadActiveFocus) {
648                 d->clickCausedFocus = true;
649             }
650         }
651     }
652     if (d->selectByMouse) {
653         setKeepMouseGrab(false);
654         d->selectPressed = true;
655         d->pressPos = event->pos();
656     }
657     bool mark = (event->modifiers() & Qt::ShiftModifier) && d->selectByMouse;
658     int cursor = d->xToPos(event->pos().x());
659     d->control->moveCursor(cursor, mark);
660     event->setAccepted(true);
661 }
662
663 void QSGTextInput::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
664 {
665     Q_D(QSGTextInput);
666     if (d->sendMouseEventToInputContext(event, QEvent::MouseMove))
667         return;
668     if (d->selectPressed) {
669         if (qAbs(int(event->pos().x() - d->pressPos.x())) > QApplication::startDragDistance())
670             setKeepMouseGrab(true);
671         moveCursorSelection(d->xToPos(event->pos().x()), d->mouseSelectionMode);
672         event->setAccepted(true);
673     } else {
674         QSGPaintedItem::mouseMoveEvent(event);
675     }
676 }
677
678 void QSGTextInput::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
679 {
680     Q_D(QSGTextInput);
681     if (d->sendMouseEventToInputContext(event, QEvent::MouseButtonRelease))
682         return;
683     if (d->selectPressed) {
684         d->selectPressed = false;
685         setKeepMouseGrab(false);
686     }
687     if (!d->showInputPanelOnFocus) { // input panel on click
688         if (d->focusOnPress && !isReadOnly() && boundingRect().contains(event->pos())) {
689             if (canvas() && canvas() == QGuiApplication::activeWindow()) {
690                 // ### refactor: implement virtual keyboard properly..
691                 qDebug("QSGTextInput: virtual keyboard no implemented...");
692 //                qt_widget_private(canvas())->handleSoftwareInputPanel(event->button(), d->clickCausedFocus);
693             }
694         }
695     }
696     d->clickCausedFocus = false;
697     d->control->processEvent(event);
698     if (!event->isAccepted())
699         QSGPaintedItem::mouseReleaseEvent(event);
700 }
701
702 bool QSGTextInputPrivate::sendMouseEventToInputContext(
703         QGraphicsSceneMouseEvent *event, QEvent::Type eventType)
704 {
705 #if !defined QT_NO_IM
706     if (event->widget() && control->composeMode()) {
707         int tmp_cursor = xToPos(event->pos().x());
708         int mousePos = tmp_cursor - control->cursor();
709         if (mousePos < 0 || mousePos > control->preeditAreaText().length()) {
710             mousePos = -1;
711             // don't send move events outside the preedit area
712             if (eventType == QEvent::MouseMove)
713                 return true;
714         }
715
716         QInputContext *qic = event->widget()->inputContext();
717         if (qic) {
718             QMouseEvent mouseEvent(
719                     eventType,
720                     event->widget()->mapFromGlobal(event->screenPos()),
721                     event->screenPos(),
722                     event->button(),
723                     event->buttons(),
724                     event->modifiers());
725             // may be causing reset() in some input methods
726             qic->mouseHandler(mousePos, &mouseEvent);
727             event->setAccepted(mouseEvent.isAccepted());
728         }
729         if (!control->preeditAreaText().isEmpty())
730             return true;
731     }
732 #else
733     Q_UNUSED(event);
734     Q_UNUSED(eventType)
735 #endif
736
737     return false;
738 }
739
740 void QSGTextInput::mouseUngrabEvent()
741 {
742     Q_D(QSGTextInput);
743     d->selectPressed = false;
744     setKeepMouseGrab(false);
745 }
746
747 bool QSGTextInput::event(QEvent* ev)
748 {
749     Q_D(QSGTextInput);
750     //Anything we don't deal with ourselves, pass to the control
751     bool handled = false;
752     switch(ev->type()){
753         case QEvent::KeyPress:
754         case QEvent::KeyRelease://###Should the control be doing anything with release?
755         case QEvent::InputMethod:
756         case QEvent::GraphicsSceneMousePress:
757         case QEvent::GraphicsSceneMouseMove:
758         case QEvent::GraphicsSceneMouseRelease:
759         case QEvent::GraphicsSceneMouseDoubleClick:
760             break;
761         default:
762             handled = d->control->processEvent(ev);
763     }
764     if(!handled)
765         handled = QSGPaintedItem::event(ev);
766     return handled;
767 }
768
769 void QSGTextInput::geometryChanged(const QRectF &newGeometry,
770                                   const QRectF &oldGeometry)
771 {
772     if (newGeometry.width() != oldGeometry.width()) {
773         updateSize();
774         updateCursorRectangle();
775     }
776     QSGPaintedItem::geometryChanged(newGeometry, oldGeometry);
777 }
778
779 int QSGTextInputPrivate::calculateTextWidth()
780 {
781     return qRound(control->naturalTextWidth());
782 }
783
784 void QSGTextInputPrivate::updateHorizontalScroll()
785 {
786     Q_Q(QSGTextInput);
787     const int preeditLength = control->preeditAreaText().length();
788     const int width = q->width();
789     int widthUsed = calculateTextWidth();
790
791     if (!autoScroll || widthUsed <=  width) {
792         QSGTextInput::HAlignment effectiveHAlign = q->effectiveHAlign();
793         // text fits in br; use hscroll for alignment
794         switch (effectiveHAlign & ~(Qt::AlignAbsolute|Qt::AlignVertical_Mask)) {
795         case Qt::AlignRight:
796             hscroll = widthUsed - width;
797             break;
798         case Qt::AlignHCenter:
799             hscroll = (widthUsed - width) / 2;
800             break;
801         default:
802             // Left
803             hscroll = 0;
804             break;
805         }
806     } else {
807         int cix = qRound(control->cursorToX(control->cursor() + preeditLength));
808         if (cix - hscroll >= width) {
809             // text doesn't fit, cursor is to the right of br (scroll right)
810             hscroll = cix - width;
811         } else if (cix - hscroll < 0 && hscroll < widthUsed) {
812             // text doesn't fit, cursor is to the left of br (scroll left)
813             hscroll = cix;
814         } else if (widthUsed - hscroll < width) {
815             // text doesn't fit, text document is to the left of br; align
816             // right
817             hscroll = widthUsed - width;
818         }
819         if (preeditLength > 0) {
820             // check to ensure long pre-edit text doesn't push the cursor
821             // off to the left
822              cix = qRound(control->cursorToX(
823                      control->cursor() + qMax(0, control->preeditCursor() - 1)));
824              if (cix < hscroll)
825                  hscroll = cix;
826         }
827     }
828 }
829
830 void QSGTextInput::paint(QPainter *p)
831 {
832     // XXX todo
833     QRect r = boundingRect().toRect();
834
835     Q_D(QSGTextInput);
836     p->setRenderHint(QPainter::TextAntialiasing, true);
837     p->save();
838     p->setPen(QPen(d->color));
839     int flags = QLineControl::DrawText;
840     if(!isReadOnly() && d->cursorVisible && !d->cursorItem)
841         flags |= QLineControl::DrawCursor;
842     if (d->control->hasSelectedText())
843             flags |= QLineControl::DrawSelections;
844     QFontMetrics fm = QFontMetrics(d->font);
845     // the y offset is there to keep the baseline constant in case we have script changes in the text.
846     QPoint offset(-d->hscroll, fm.ascent() - d->control->ascent());
847     d->control->draw(p, offset, r, flags);
848     p->restore();
849 }
850
851 QVariant QSGTextInput::inputMethodQuery(Qt::InputMethodQuery property) const
852 {
853     Q_D(const QSGTextInput);
854     switch(property) {
855     case Qt::ImMicroFocus:
856         return cursorRectangle();
857     case Qt::ImFont:
858         return font();
859     case Qt::ImCursorPosition:
860         return QVariant(d->control->cursor());
861     case Qt::ImSurroundingText:
862         if (d->control->echoMode() == PasswordEchoOnEdit && !d->control->passwordEchoEditing())
863             return QVariant(displayText());
864         else
865             return QVariant(text());
866     case Qt::ImCurrentSelection:
867         return QVariant(selectedText());
868     case Qt::ImMaximumTextLength:
869         return QVariant(maxLength());
870     case Qt::ImAnchorPosition:
871         if (d->control->selectionStart() == d->control->selectionEnd())
872             return QVariant(d->control->cursor());
873         else if (d->control->selectionStart() == d->control->cursor())
874             return QVariant(d->control->selectionEnd());
875         else
876             return QVariant(d->control->selectionStart());
877     default:
878         return QVariant();
879     }
880 }
881
882 void QSGTextInput::deselect()
883 {
884     Q_D(QSGTextInput);
885     d->control->deselect();
886 }
887
888 void QSGTextInput::selectAll()
889 {
890     Q_D(QSGTextInput);
891     d->control->setSelection(0, d->control->text().length());
892 }
893
894 bool QSGTextInput::isRightToLeft(int start, int end)
895 {
896     Q_D(QSGTextInput);
897     if (start > end) {
898         qmlInfo(this) << "isRightToLeft(start, end) called with the end property being smaller than the start.";
899         return false;
900     } else {
901         return d->control->text().mid(start, end - start).isRightToLeft();
902     }
903 }
904
905 #ifndef QT_NO_CLIPBOARD
906 void QSGTextInput::cut()
907 {
908     Q_D(QSGTextInput);
909     d->control->copy();
910     d->control->del();
911 }
912
913 void QSGTextInput::copy()
914 {
915     Q_D(QSGTextInput);
916     d->control->copy();
917 }
918
919 void QSGTextInput::paste()
920 {
921     Q_D(QSGTextInput);
922     if (!d->control->isReadOnly())
923         d->control->paste();
924 }
925 #endif // QT_NO_CLIPBOARD
926
927 void QSGTextInput::selectWord()
928 {
929     Q_D(QSGTextInput);
930     d->control->selectWordAtPos(d->control->cursor());
931 }
932
933 QString QSGTextInput::passwordCharacter() const
934 {
935     Q_D(const QSGTextInput);
936     return QString(d->control->passwordCharacter());
937 }
938
939 void QSGTextInput::setPasswordCharacter(const QString &str)
940 {
941     Q_D(QSGTextInput);
942     if(str.length() < 1)
943         return;
944     d->control->setPasswordCharacter(str.constData()[0]);
945     EchoMode echoMode_ = echoMode();
946     if (echoMode_ == Password || echoMode_ == PasswordEchoOnEdit) {
947         updateSize();
948     }
949     emit passwordCharacterChanged();
950 }
951
952 QString QSGTextInput::displayText() const
953 {
954     Q_D(const QSGTextInput);
955     return d->control->displayText();
956 }
957
958 bool QSGTextInput::selectByMouse() const
959 {
960     Q_D(const QSGTextInput);
961     return d->selectByMouse;
962 }
963
964 void QSGTextInput::setSelectByMouse(bool on)
965 {
966     Q_D(QSGTextInput);
967     if (d->selectByMouse != on) {
968         d->selectByMouse = on;
969         emit selectByMouseChanged(on);
970     }
971 }
972
973 QSGTextInput::SelectionMode QSGTextInput::mouseSelectionMode() const
974 {
975     Q_D(const QSGTextInput);
976     return d->mouseSelectionMode;
977 }
978
979 void QSGTextInput::setMouseSelectionMode(SelectionMode mode)
980 {
981     Q_D(QSGTextInput);
982     if (d->mouseSelectionMode != mode) {
983         d->mouseSelectionMode = mode;
984         emit mouseSelectionModeChanged(mode);
985     }
986 }
987
988 bool QSGTextInput::canPaste() const
989 {
990     Q_D(const QSGTextInput);
991     return d->canPaste;
992 }
993
994 void QSGTextInput::moveCursorSelection(int position)
995 {
996     Q_D(QSGTextInput);
997     d->control->moveCursor(position, true);
998 }
999
1000 void QSGTextInput::moveCursorSelection(int pos, SelectionMode mode)
1001 {
1002     Q_D(QSGTextInput);
1003
1004     if (mode == SelectCharacters) {
1005         d->control->moveCursor(pos, true);
1006     } else if (pos != d->control->cursor()){
1007         const int cursor = d->control->cursor();
1008         int anchor;
1009         if (!d->control->hasSelectedText())
1010             anchor = d->control->cursor();
1011         else if (d->control->selectionStart() == d->control->cursor())
1012             anchor = d->control->selectionEnd();
1013         else
1014             anchor = d->control->selectionStart();
1015
1016         if (anchor < pos || (anchor == pos && cursor < pos)) {
1017             const QString text = d->control->text();
1018             QTextBoundaryFinder finder(QTextBoundaryFinder::Word, text);
1019             finder.setPosition(anchor);
1020
1021             const QTextBoundaryFinder::BoundaryReasons reasons = finder.boundaryReasons();
1022             if (anchor < text.length() && (!(reasons & QTextBoundaryFinder::StartWord)
1023                     || ((reasons & QTextBoundaryFinder::EndWord) && anchor > cursor))) {
1024                 finder.toPreviousBoundary();
1025             }
1026             anchor = finder.position() != -1 ? finder.position() : 0;
1027
1028             finder.setPosition(pos);
1029             if (pos > 0 && !finder.boundaryReasons())
1030                 finder.toNextBoundary();
1031             const int cursor = finder.position() != -1 ? finder.position() : text.length();
1032
1033             d->control->setSelection(anchor, cursor - anchor);
1034         } else if (anchor > pos || (anchor == pos && cursor > pos)) {
1035             const QString text = d->control->text();
1036             QTextBoundaryFinder finder(QTextBoundaryFinder::Word, text);
1037             finder.setPosition(anchor);
1038
1039             const QTextBoundaryFinder::BoundaryReasons reasons = finder.boundaryReasons();
1040             if (anchor > 0 && (!(reasons & QTextBoundaryFinder::EndWord)
1041                     || ((reasons & QTextBoundaryFinder::StartWord) && anchor < cursor))) {
1042                 finder.toNextBoundary();
1043             }
1044
1045             anchor = finder.position() != -1 ? finder.position() : text.length();
1046
1047             finder.setPosition(pos);
1048             if (pos < text.length() && !finder.boundaryReasons())
1049                  finder.toPreviousBoundary();
1050             const int cursor = finder.position() != -1 ? finder.position() : 0;
1051
1052             d->control->setSelection(anchor, cursor - anchor);
1053         }
1054     }
1055 }
1056
1057 void QSGTextInput::openSoftwareInputPanel()
1058 {
1059     if (qApp) {
1060         if (canvas()) {
1061             QEvent event(QEvent::RequestSoftwareInputPanel);
1062             QApplication::sendEvent(canvas(), &event);
1063         }
1064     }
1065 }
1066
1067 void QSGTextInput::closeSoftwareInputPanel()
1068 {
1069     if (qApp) {
1070         if (canvas()) {
1071             QEvent event(QEvent::CloseSoftwareInputPanel);
1072             QApplication::sendEvent(canvas(), &event);
1073         }
1074     }
1075 }
1076
1077 void QSGTextInput::focusInEvent(QFocusEvent *event)
1078 {
1079     Q_D(const QSGTextInput);
1080     if (d->showInputPanelOnFocus) {
1081         if (d->focusOnPress && !isReadOnly()) {
1082             openSoftwareInputPanel();
1083         }
1084     }
1085     QSGPaintedItem::focusInEvent(event);
1086 }
1087
1088 void QSGTextInput::itemChange(ItemChange change, const ItemChangeData &value)
1089 {
1090     Q_D(QSGTextInput);
1091     if (change == ItemActiveFocusHasChanged) {
1092         bool hasFocus = value.boolValue;
1093         d->focused = hasFocus;
1094         setCursorVisible(hasFocus); // ### refactor:  && d->canvas && d->canvas->hasFocus()
1095         if(echoMode() == QSGTextInput::PasswordEchoOnEdit && !hasFocus)
1096             d->control->updatePasswordEchoEditing(false);//QLineControl sets it on key events, but doesn't deal with focus events
1097         if (!hasFocus)
1098             d->control->deselect();
1099     }
1100     QSGItem::itemChange(change, value);
1101 }
1102
1103 bool QSGTextInput::isInputMethodComposing() const
1104 {
1105     Q_D(const QSGTextInput);
1106     return d->control->preeditAreaText().length() > 0;
1107 }
1108
1109 void QSGTextInputPrivate::init()
1110 {
1111     Q_Q(QSGTextInput);
1112     control->setParent(q);//Now mandatory due to accessibility changes
1113     control->setCursorWidth(1);
1114     control->setPasswordCharacter(QLatin1Char('*'));
1115     q->setSmooth(smooth);
1116     q->setAcceptedMouseButtons(Qt::LeftButton);
1117     q->setFlag(QSGItem::ItemAcceptsInputMethod);
1118     q->connect(control, SIGNAL(cursorPositionChanged(int,int)),
1119                q, SLOT(cursorPosChanged()));
1120     q->connect(control, SIGNAL(selectionChanged()),
1121                q, SLOT(selectionChanged()));
1122     q->connect(control, SIGNAL(textChanged(QString)),
1123                q, SLOT(q_textChanged()));
1124     q->connect(control, SIGNAL(accepted()),
1125                q, SIGNAL(accepted()));
1126     q->connect(control, SIGNAL(updateNeeded(QRect)),
1127                q, SLOT(updateRect(QRect)));
1128 #ifndef QT_NO_CLIPBOARD
1129     q->connect(q, SIGNAL(readOnlyChanged(bool)),
1130             q, SLOT(q_canPasteChanged()));
1131     q->connect(QApplication::clipboard(), SIGNAL(dataChanged()),
1132             q, SLOT(q_canPasteChanged()));
1133     canPaste = !control->isReadOnly() && QApplication::clipboard()->text().length() != 0;
1134 #endif // QT_NO_CLIPBOARD
1135     q->connect(control, SIGNAL(updateMicroFocus()),
1136                q, SLOT(updateCursorRectangle()));
1137     q->connect(control, SIGNAL(displayTextChanged(QString)),
1138                q, SLOT(updateRect()));
1139     q->updateSize();
1140     oldValidity = control->hasAcceptableInput();
1141     lastSelectionStart = 0;
1142     lastSelectionEnd = 0;
1143     QPalette p = control->palette();
1144     selectedTextColor = p.color(QPalette::HighlightedText);
1145     selectionColor = p.color(QPalette::Highlight);
1146     determineHorizontalAlignment();
1147 }
1148
1149 void QSGTextInput::cursorPosChanged()
1150 {
1151     Q_D(QSGTextInput);
1152     updateCursorRectangle();
1153     emit cursorPositionChanged();
1154     // XXX todo - not in 4.8?
1155 #if 0
1156     d->control->resetCursorBlinkTimer();
1157 #endif
1158
1159     if(!d->control->hasSelectedText()){
1160         if(d->lastSelectionStart != d->control->cursor()){
1161             d->lastSelectionStart = d->control->cursor();
1162             emit selectionStartChanged();
1163         }
1164         if(d->lastSelectionEnd != d->control->cursor()){
1165             d->lastSelectionEnd = d->control->cursor();
1166             emit selectionEndChanged();
1167         }
1168     }
1169 }
1170
1171 void QSGTextInput::updateCursorRectangle()
1172 {
1173     Q_D(QSGTextInput);
1174     d->updateHorizontalScroll();
1175     updateRect();//TODO: Only update rect between pos's
1176     updateMicroFocus();
1177     emit cursorRectangleChanged();
1178     if (d->cursorItem)
1179         d->cursorItem->setX(d->control->cursorToX() - d->hscroll);
1180 }
1181
1182 void QSGTextInput::selectionChanged()
1183 {
1184     Q_D(QSGTextInput);
1185     updateRect();//TODO: Only update rect in selection
1186     emit selectedTextChanged();
1187
1188     if(d->lastSelectionStart != d->control->selectionStart()){
1189         d->lastSelectionStart = d->control->selectionStart();
1190         if(d->lastSelectionStart == -1)
1191             d->lastSelectionStart = d->control->cursor();
1192         emit selectionStartChanged();
1193     }
1194     if(d->lastSelectionEnd != d->control->selectionEnd()){
1195         d->lastSelectionEnd = d->control->selectionEnd();
1196         if(d->lastSelectionEnd == -1)
1197             d->lastSelectionEnd = d->control->cursor();
1198         emit selectionEndChanged();
1199     }
1200 }
1201
1202 void QSGTextInput::q_textChanged()
1203 {
1204     Q_D(QSGTextInput);
1205     emit textChanged();
1206     emit displayTextChanged();
1207     updateSize();
1208     d->determineHorizontalAlignment();
1209     d->updateHorizontalScroll();
1210     updateMicroFocus();
1211     if(hasAcceptableInput() != d->oldValidity){
1212         d->oldValidity = hasAcceptableInput();
1213         emit acceptableInputChanged();
1214     }
1215 }
1216
1217 void QSGTextInput::updateRect(const QRect &r)
1218 {
1219     Q_D(QSGTextInput);
1220     if(r == QRect())
1221         update();
1222     else
1223         update(QRect(r.x() - d->hscroll, r.y(), r.width(), r.height()));
1224 }
1225
1226 QRectF QSGTextInput::boundingRect() const
1227 {
1228     Q_D(const QSGTextInput);
1229     QRectF r = QSGPaintedItem::boundingRect();
1230
1231     int cursorWidth = d->cursorItem ? d->cursorItem->width() : d->control->cursorWidth();
1232
1233     // Could include font max left/right bearings to either side of rectangle.
1234
1235     r.setRight(r.right() + cursorWidth);
1236     return r;
1237 }
1238
1239 void QSGTextInput::updateSize(bool needsRedraw)
1240 {
1241     Q_D(QSGTextInput);
1242     int w = width();
1243     int h = height();
1244     setImplicitHeight(d->control->height()-1); // -1 to counter QLineControl's +1 which is not consistent with Text.
1245     setImplicitWidth(d->calculateTextWidth());
1246     setContentsSize(boundingRect().size().toSize());
1247     if(w==width() && h==height() && needsRedraw)
1248         update();
1249 }
1250
1251 void QSGTextInput::q_canPasteChanged()
1252 {
1253     Q_D(QSGTextInput);
1254     bool old = d->canPaste;
1255 #ifndef QT_NO_CLIPBOARD
1256     d->canPaste = !d->control->isReadOnly() && QApplication::clipboard()->text().length() != 0;
1257 #endif
1258     if(d->canPaste != old)
1259         emit canPasteChanged();
1260 }
1261
1262 QT_END_NAMESPACE
1263