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