Merge branch 4.7 into qt-4.8-from-4.7
[qt:qt.git] / src / gui / inputmethod / qcoefepinputcontext_s60.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 QtGui 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 #ifndef QT_NO_IM
43
44 #include "qcoefepinputcontext_p.h"
45 #include <qapplication.h>
46 #include <qtextformat.h>
47 #include <qgraphicsview.h>
48 #include <qgraphicsscene.h>
49 #include <qgraphicswidget.h>
50 #include <qsymbianevent.h>
51 #include <qlayout.h>
52 #include <qdesktopwidget.h>
53 #include <private/qcore_symbian_p.h>
54
55 #include <fepitfr.h>
56 #include <hal.h>
57 #include <e32property.h>
58
59 #include <limits.h>
60 // You only find these enumerations on SDK 5 onwards, so we need to provide our own
61 // to remain compatible with older releases. They won't be called by pre-5.0 SDKs.
62
63 // MAknEdStateObserver::EAknCursorPositionChanged
64 #define QT_EAknCursorPositionChanged MAknEdStateObserver::EAknEdwinStateEvent(6)
65 // MAknEdStateObserver::EAknActivatePenInputRequest
66 #define QT_EAknActivatePenInputRequest MAknEdStateObserver::EAknEdwinStateEvent(7)
67 // MAknEdStateObserver::EAknClosePenInputRequest
68 #define QT_EAknClosePenInputRequest MAknEdStateObserver::EAknEdwinStateEvent(10)
69
70 // EAknEditorFlagSelectionVisible is only valid from 3.2 onwards.
71 // Sym^3 AVKON FEP manager expects that this flag is used for FEP-aware editors
72 // that support text selection.
73 #define QT_EAknEditorFlagSelectionVisible 0x100000
74
75 // EAknEditorFlagEnablePartialScreen is only valid from Sym^3 onwards.
76 #define QT_EAknEditorFlagEnablePartialScreen 0x200000
77
78 // Properties to detect VKB status from AknFepInternalPSKeys.h
79 #define QT_EPSUidAknFep 0x100056de
80 #define QT_EAknFepTouchInputActive 0x00000004
81
82 QT_BEGIN_NAMESPACE
83
84 Q_GUI_EXPORT void qt_s60_setPartialScreenInputMode(bool enable)
85 {
86     S60->partial_keyboard = enable;
87
88     QApplication::setAttribute(Qt::AA_S60DisablePartialScreenInputMode, !S60->partial_keyboard);
89
90     QInputContext *ic = 0;
91     if (QApplication::focusWidget()) {
92         ic = QApplication::focusWidget()->inputContext();
93     } else if (qApp && qApp->inputContext()) {
94         ic = qApp->inputContext();
95     }
96     if (ic)
97         ic->update();
98 }
99
100 Q_GUI_EXPORT void qt_s60_setPartialScreenAutomaticTranslation(bool enable)
101 {
102     S60->partial_keyboardAutoTranslation = enable;
103 }
104
105 QCoeFepInputContext::QCoeFepInputContext(QObject *parent)
106     : QInputContext(parent),
107       m_fepState(q_check_ptr(new CAknEdwinState)),              // CBase derived object needs check on new
108       m_lastImHints(Qt::ImhNone),
109       m_textCapabilities(TCoeInputCapabilities::EAllText),
110       m_inDestruction(false),
111       m_pendingInputCapabilitiesChanged(false),
112       m_pendingTransactionCancel(false),
113       m_cursorVisibility(1),
114       m_inlinePosition(0),
115       m_formatRetriever(0),
116       m_pointerHandler(0),
117       m_hasTempPreeditString(false),
118       m_splitViewResizeBy(0),
119       m_splitViewPreviousWindowStates(Qt::WindowNoState)
120 {
121     m_fepState->SetObjectProvider(this);
122     int defaultFlags = EAknEditorFlagDefault;
123     if (QSysInfo::s60Version() > QSysInfo::SV_S60_5_0) {
124         if (isPartialKeyboardSupported()) {
125             defaultFlags |= QT_EAknEditorFlagEnablePartialScreen;
126         }
127         defaultFlags |= QT_EAknEditorFlagSelectionVisible;
128     }
129     m_fepState->SetFlags(defaultFlags);
130     m_fepState->SetDefaultInputMode( EAknEditorTextInputMode );
131     m_fepState->SetPermittedInputModes( EAknEditorAllInputModes );
132     m_fepState->SetDefaultCase( EAknEditorTextCase );
133     m_fepState->SetPermittedCases( EAknEditorAllCaseModes );
134     m_fepState->SetSpecialCharacterTableResourceId(R_AVKON_SPECIAL_CHARACTER_TABLE_DIALOG);
135     m_fepState->SetNumericKeymap(EAknEditorAlphanumericNumberModeKeymap);
136 }
137
138 QCoeFepInputContext::~QCoeFepInputContext()
139 {
140     m_inDestruction = true;
141
142     // This is to make sure that the FEP manager "forgets" about us,
143     // otherwise we may get callbacks even after we're destroyed.
144     // The call below is essentially equivalent to InputCapabilitiesChanged(),
145     // but is synchronous, rather than asynchronous.
146     CCoeEnv::Static()->SyncNotifyFocusObserversOfChangeInFocus();
147
148     if (m_fepState)
149         delete m_fepState;
150 }
151
152 void QCoeFepInputContext::reset()
153 {
154     Qt::InputMethodHints currentHints = Qt::ImhNone;
155     if (focusWidget()) {
156         QWidget *proxy = focusWidget()->focusProxy();
157         currentHints = proxy ? proxy->inputMethodHints() : focusWidget()->inputMethodHints();
158     }
159     // Store a copy of preedit text, if prediction is active and input context is reseted.
160     // This is to ensure that we can replace preedit string after losing focus to FEP manager's
161     // internal sub-windows.
162     if (m_cachedPreeditString.isEmpty() && !(currentHints & Qt::ImhNoPredictiveText))
163         m_cachedPreeditString = m_preeditString;
164     commitCurrentString(true);
165 }
166
167 void QCoeFepInputContext::ReportAknEdStateEvent(MAknEdStateObserver::EAknEdwinStateEvent aEventType)
168 {
169     QT_TRAP_THROWING(m_fepState->ReportAknEdStateEventL(aEventType));
170 }
171
172 void QCoeFepInputContext::update()
173 {
174     updateHints(false);
175
176     // For pre-5.0 SDKs, we don't do text updates on S60 side.
177     if (QSysInfo::s60Version() < QSysInfo::SV_S60_5_0) {
178         return;
179     }
180
181     // Don't be fooled (as I was) by the name of this enumeration.
182     // What it really does is tell the virtual keyboard UI that the text has been
183     // updated and it should be reflected in the internal display of the VK.
184     ReportAknEdStateEvent(QT_EAknCursorPositionChanged);
185 }
186
187 void QCoeFepInputContext::setFocusWidget(QWidget *w)
188 {
189     commitCurrentString(true);
190
191     QInputContext::setFocusWidget(w);
192
193     updateHints(true);
194 }
195
196 void QCoeFepInputContext::widgetDestroyed(QWidget *w)
197 {
198     m_cachedPreeditString.clear();
199
200     // Make sure that the input capabilities of whatever new widget got focused are queried.
201     CCoeControl *ctrl = w->effectiveWinId();
202     if (ctrl->IsFocused()) {
203         queueInputCapabilitiesChanged();
204     }
205 }
206
207 QString QCoeFepInputContext::language()
208 {
209     TLanguage lang = m_fepState->LocalLanguage();
210     const QByteArray localeName = qt_symbianLocaleName(lang);
211     if (!localeName.isEmpty()) {
212         return QString::fromLatin1(localeName);
213     } else {
214         return QString::fromLatin1("C");
215     }
216 }
217
218 bool QCoeFepInputContext::needsInputPanel()
219 {
220     switch (QSysInfo::s60Version()) {
221     case QSysInfo::SV_S60_3_1:
222     case QSysInfo::SV_S60_3_2:
223         // There are no touch phones for pre-5.0 SDKs.
224         return false;
225 #ifdef Q_CC_NOKIAX86
226     default:
227         // For emulator we assume that we need an input panel, since we can't
228         // separate between phone types.
229         return true;
230 #else
231     case QSysInfo::SV_S60_5_0: {
232         // For SDK == 5.0, we need phone specific detection, since the HAL API
233         // is no good on most phones. However, all phones at the time of writing use the
234         // input panel, except N97 in landscape mode, but in this mode it refuses to bring
235         // up the panel anyway, so we don't have to care.
236         return true;
237     }
238     default:
239         // For unknown/newer types, we try to use the HAL API.
240         int keyboardEnabled;
241         int keyboardType;
242         int err[2];
243         err[0] = HAL::Get(HAL::EKeyboard, keyboardType);
244         err[1] = HAL::Get(HAL::EKeyboardState, keyboardEnabled);
245         if (err[0] == KErrNone && err[1] == KErrNone
246                 && keyboardType != 0 && keyboardEnabled)
247             // Means that we have some sort of keyboard.
248             return false;
249
250         // Fall back to using the input panel.
251         return true;
252 #endif // !Q_CC_NOKIAX86
253     }
254 }
255
256 bool QCoeFepInputContext::filterEvent(const QEvent *event)
257 {
258     if (!focusWidget())
259         return false;
260
261     switch (event->type()) {
262     case QEvent::KeyPress:
263         commitTemporaryPreeditString();
264         // fall through intended
265     case QEvent::KeyRelease:
266         const QKeyEvent *keyEvent = static_cast<const QKeyEvent *>(event);
267         //If proxy exists, always use hints from proxy.
268         QWidget *proxy = focusWidget()->focusProxy();
269         Qt::InputMethodHints currentHints = proxy ? proxy->inputMethodHints() : focusWidget()->inputMethodHints();
270
271         switch (keyEvent->key()) {
272         case Qt::Key_F20:
273             Q_ASSERT(m_lastImHints == currentHints);
274             if (m_lastImHints & Qt::ImhHiddenText) {
275                 // Special case in Symbian. On editors with secret text, F20 is for some reason
276                 // considered to be a backspace.
277                 QKeyEvent modifiedEvent(keyEvent->type(), Qt::Key_Backspace, keyEvent->modifiers(),
278                         keyEvent->text(), keyEvent->isAutoRepeat(), keyEvent->count());
279                 QApplication::sendEvent(focusWidget(), &modifiedEvent);
280                 return true;
281             }
282             break;
283         case Qt::Key_Select:
284             if (!m_preeditString.isEmpty()) {
285                 commitCurrentString(true);
286                 return true;
287             }
288             break;
289         default:
290             break;
291         }
292
293         QString widgetText = focusWidget()->inputMethodQuery(Qt::ImSurroundingText).toString();
294         bool validLength;
295         int maxLength = focusWidget()->inputMethodQuery(Qt::ImMaximumTextLength).toInt(&validLength);
296         if (!keyEvent->text().isEmpty() && validLength
297                 && widgetText.size() + m_preeditString.size() >= maxLength) {
298             // Don't send key events with string content if the widget is "full".
299             return true;
300         }
301
302         if (keyEvent->type() == QEvent::KeyPress
303             && currentHints & Qt::ImhHiddenText
304             && !keyEvent->text().isEmpty()) {
305             // Send some temporary preedit text in order to make text visible for a moment.
306             m_preeditString = keyEvent->text();
307             QList<QInputMethodEvent::Attribute> attributes;
308             QInputMethodEvent imEvent(m_preeditString, attributes);
309             sendEvent(imEvent);
310             m_tempPreeditStringTimeout.start(1000, this);
311             m_hasTempPreeditString = true;
312             update();
313             return true;
314         }
315         break;
316     }
317
318     if (!needsInputPanel())
319         return false;
320
321     if ((event->type() == QEvent::CloseSoftwareInputPanel)
322         && (QSysInfo::s60Version() > QSysInfo::SV_S60_5_0)) {
323         m_fepState->ReportAknEdStateEventL(QT_EAknClosePenInputRequest);
324         return false;
325     }
326
327     if (event->type() == QEvent::RequestSoftwareInputPanel) {
328         // Only request virtual keyboard if it is not yet active or if this is the first time
329         // panel is requested for this application.
330         static bool firstTime = true;
331         int vkbActive = 0;
332
333         if (firstTime) {
334             // Sometimes the global QT_EAknFepTouchInputActive value can be left incorrect at
335             // application exit if the application is exited when input panel is active.
336             // Therefore we always want to open the panel the first time application requests it.
337             firstTime = false;
338         } else {
339             const TUid KPSUidAknFep = {QT_EPSUidAknFep};
340             // No need to check for return value, as vkbActive stays zero in that case
341             RProperty::Get(KPSUidAknFep, QT_EAknFepTouchInputActive, vkbActive);
342         }
343
344         if (!vkbActive) {
345             // Notify S60 that we want the virtual keyboard to show up.
346             QSymbianControl *sControl;
347             sControl = focusWidget()->effectiveWinId()->MopGetObject(sControl);
348             Q_ASSERT(sControl);
349
350             // The FEP UI temporarily steals focus when it shows up the first time, causing
351             // all sorts of weird effects on the focused widgets. Since it will immediately give
352             // back focus to us, we temporarily disable focus handling until the job's done.
353             if (sControl) {
354                 sControl->setIgnoreFocusChanged(true);
355             }
356
357             ensureInputCapabilitiesChanged();
358             m_fepState->ReportAknEdStateEventL(MAknEdStateObserver::QT_EAknActivatePenInputRequest);
359
360             if (sControl) {
361                 sControl->setIgnoreFocusChanged(false);
362             }
363         }
364     }
365
366     return false;
367 }
368
369 bool QCoeFepInputContext::symbianFilterEvent(QWidget *keyWidget, const QSymbianEvent *event)
370 {
371     Q_UNUSED(keyWidget);
372     if (event->type() == QSymbianEvent::CommandEvent)
373         // A command basically means the same as a button being pushed. With Qt buttons
374         // that would normally result in a reset of the input method due to the focus change.
375         // This should also happen for commands.
376         reset();
377
378     if (event->type() == QSymbianEvent::WindowServerEvent
379         && event->windowServerEvent()
380         && event->windowServerEvent()->Type() == EEventWindowVisibilityChanged
381         && S60->splitViewLastWidget) {
382
383         QGraphicsView *gv = qobject_cast<QGraphicsView*>(S60->splitViewLastWidget);
384         const bool alwaysResize = (gv && gv->verticalScrollBarPolicy() != Qt::ScrollBarAlwaysOff);
385
386         if (alwaysResize) {
387             TUint visibleFlags = event->windowServerEvent()->VisibilityChanged()->iFlags;
388             if (visibleFlags & TWsVisibilityChangedEvent::EPartiallyVisible)
389                 ensureFocusWidgetVisible(S60->splitViewLastWidget);
390             if (visibleFlags & TWsVisibilityChangedEvent::ENotVisible)
391                 resetSplitViewWidget(true);
392         }
393     }
394
395     if (event->type() == QSymbianEvent::ResourceChangeEvent
396          && (event->resourceChangeType() == KEikMessageFadeAllWindows
397          || event->resourceChangeType() == KEikDynamicLayoutVariantSwitch)) {
398         reset();
399     }
400
401     return false;
402 }
403
404 void QCoeFepInputContext::timerEvent(QTimerEvent *timerEvent)
405 {
406     if (timerEvent->timerId() == m_tempPreeditStringTimeout.timerId())
407         commitTemporaryPreeditString();
408 }
409
410 void QCoeFepInputContext::commitTemporaryPreeditString()
411 {
412     if (m_tempPreeditStringTimeout.isActive())
413         m_tempPreeditStringTimeout.stop();
414
415     if (!m_hasTempPreeditString)
416         return;
417
418     commitCurrentString(false);
419 }
420
421 void QCoeFepInputContext::mouseHandler(int x, QMouseEvent *event)
422 {
423     Q_ASSERT(focusWidget());
424
425     if (event->type() == QEvent::MouseButtonPress && event->button() == Qt::LeftButton) {
426         QWidget *proxy = focusWidget()->focusProxy();
427         Qt::InputMethodHints currentHints = proxy ? proxy->inputMethodHints() : focusWidget()->inputMethodHints();
428
429         //If splitview is open and T9 word is tapped, pass the pointer event to pointer handler.
430         //This will open the "suggested words" list. Pass pointer position always as zero, to make
431         //full word replacement in case user makes a selection.
432         if (isPartialKeyboardSupported()
433             && S60->partialKeyboardOpen
434             && m_pointerHandler
435             && !(currentHints & Qt::ImhNoPredictiveText)
436             && (x > 0 && x < m_preeditString.length())) {
437             m_pointerHandler->HandlePointerEventInInlineTextL(TPointerEvent::EButton1Up, 0, 0);
438         } else {
439             commitCurrentString(true);
440             int pos = focusWidget()->inputMethodQuery(Qt::ImCursorPosition).toInt();
441
442             QList<QInputMethodEvent::Attribute> attributes;
443             attributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, pos + x, 0, QVariant());
444             QInputMethodEvent event(QLatin1String(""), attributes);
445             sendEvent(event);
446         }
447     }
448 }
449
450 TCoeInputCapabilities QCoeFepInputContext::inputCapabilities()
451 {
452     if (m_inDestruction || !focusWidget()) {
453         return TCoeInputCapabilities(TCoeInputCapabilities::ENone, 0, 0);
454     }
455
456     return TCoeInputCapabilities(m_textCapabilities, this, 0);
457 }
458
459 void QCoeFepInputContext::resetSplitViewWidget(bool keepInputWidget)
460 {
461     QGraphicsView *gv = qobject_cast<QGraphicsView*>(S60->splitViewLastWidget);
462
463     if (!gv) {
464         return;
465     }
466
467     QSymbianControl *symControl = static_cast<QSymbianControl*>(gv->effectiveWinId());
468     symControl->CancelLongTapTimer();
469
470     const bool alwaysResize = (gv->verticalScrollBarPolicy() != Qt::ScrollBarAlwaysOff);
471     QWidget *windowToMove = gv->window();
472
473     bool userResize = gv->testAttribute(Qt::WA_Resized);
474
475     windowToMove->setUpdatesEnabled(false);
476
477     if (!alwaysResize) {
478         if (gv->scene()) {
479             if (gv->scene()->focusItem() && S60->partial_keyboardAutoTranslation) {
480                 // Check if the widget contains cursorPositionChanged signal and disconnect from it.
481                 QByteArray signal = QMetaObject::normalizedSignature(SIGNAL(cursorPositionChanged()));
482                 int index = gv->scene()->focusItem()->toGraphicsObject()->metaObject()->indexOfSignal(signal.right(signal.length() - 1));
483                 if (index != -1)
484                     disconnect(gv->scene()->focusItem()->toGraphicsObject(), SIGNAL(cursorPositionChanged()), this, SLOT(translateInputWidget()));
485             }
486
487             QGraphicsItem *rootItem = 0;
488             foreach (QGraphicsItem *item, gv->scene()->items()) {
489                 if (!item->parentItem()) {
490                     rootItem = item;
491                     break;
492                 }
493             }
494             if (rootItem)
495                 rootItem->resetTransform();
496         }
497     } else {
498         if (m_splitViewResizeBy)
499             if (m_splitViewPreviousWindowStates & Qt::WindowFullScreen)
500                 gv->resize(gv->rect().width(), qApp->desktop()->height());
501             else
502                 gv->resize(gv->rect().width(), m_splitViewResizeBy);
503     }
504     // Resizing might have led to widget losing its original windowstate.
505     // Restore previous window state.
506
507     if (m_splitViewPreviousWindowStates != windowToMove->windowState())
508         windowToMove->setWindowState(m_splitViewPreviousWindowStates);
509
510     windowToMove->setUpdatesEnabled(true);
511
512     gv->setAttribute(Qt::WA_Resized, userResize); //not a user resize
513
514     m_splitViewResizeBy = 0;
515     if (!keepInputWidget) {
516         m_splitViewPreviousWindowStates = Qt::WindowNoState;
517         S60->splitViewLastWidget = 0;
518     }
519 }
520
521 // Checks if a given widget is visible in the splitview rect. The offset
522 // parameter can be used to validate if moving widget upwards or downwards
523 // by the offset would make a difference for the visibility.
524
525 bool QCoeFepInputContext::isWidgetVisible(QWidget *widget, int offset)
526 {
527     bool visible = false;
528     if (widget) {
529         QRect splitViewRect = qt_TRect2QRect(static_cast<CEikAppUi*>(S60->appUi())->ClientRect());
530         QWidget *window = QApplication::activeWindow();
531         QGraphicsView *gv = qobject_cast<QGraphicsView*>(widget);
532         if (gv && window) {
533             if (QGraphicsScene *scene = gv->scene()) {
534                 if (QGraphicsItem *focusItem = scene->focusItem()) {
535                     QPoint cursorPos = window->mapToGlobal(focusItem->cursor().pos());
536                     cursorPos.setY(cursorPos.y() + offset);
537                     if (splitViewRect.contains(cursorPos)) {
538                         visible = true;
539                     }
540                 }
541             }
542         }
543     }
544     return visible;
545 }
546
547 bool QCoeFepInputContext::isPartialKeyboardSupported()
548 {
549     return (S60->partial_keyboard || !QApplication::testAttribute(Qt::AA_S60DisablePartialScreenInputMode));
550 }
551
552 // Ensure that the input widget is visible in the splitview rect.
553
554 void QCoeFepInputContext::ensureFocusWidgetVisible(QWidget *widget)
555 {
556     // Native side opening and closing its virtual keyboard when it changes the keyboard layout,
557     // has an adverse impact on long tap timer. Cancel the timer when splitview opens to avoid this.
558     QSymbianControl *symControl = static_cast<QSymbianControl*>(widget->effectiveWinId());
559     symControl->CancelLongTapTimer();
560
561     // Graphicsviews that have vertical scrollbars should always be resized to the splitview area.
562     // Graphicsviews without scrollbars should be translated.
563
564     QGraphicsView *gv = qobject_cast<QGraphicsView*>(widget);
565     if (!gv)
566         return;
567
568     const bool alwaysResize = (gv && gv->verticalScrollBarPolicy() != Qt::ScrollBarAlwaysOff);
569     const bool moveWithinVisibleArea = (S60->splitViewLastWidget != 0);
570
571     QWidget *windowToMove = gv ? gv : symControl->widget();
572     if (!windowToMove->isWindow())
573         windowToMove = windowToMove->window();
574     if (!windowToMove) {
575         return;
576     }
577
578     // When opening the keyboard (not moving within the splitview area), save the original
579     // window state. In some cases, ensuring input widget visibility might lead to window
580     // states getting changed.
581
582     if (!moveWithinVisibleArea) {
583         // Check if the widget contains cursorPositionChanged signal and connect to it.
584         QByteArray signal = QMetaObject::normalizedSignature(SIGNAL(cursorPositionChanged()));
585         if (gv->scene() && gv->scene()->focusItem() && S60->partial_keyboardAutoTranslation) {
586             int index = gv->scene()->focusItem()->toGraphicsObject()->metaObject()->indexOfSignal(signal.right(signal.length() - 1));
587             if (index != -1)
588                 connect(gv->scene()->focusItem()->toGraphicsObject(), SIGNAL(cursorPositionChanged()), this, SLOT(translateInputWidget()));
589         }
590         S60->splitViewLastWidget = widget;
591         m_splitViewPreviousWindowStates = windowToMove->windowState();
592     }
593
594     int windowTop = widget->window()->pos().y();
595
596     const bool userResize = widget->testAttribute(Qt::WA_Resized);
597
598     QRect splitViewRect = qt_TRect2QRect(static_cast<CEikAppUi*>(S60->appUi())->ClientRect());
599
600
601     // When resizing a window widget, it will lose its maximized window state.
602     // Native applications hide statuspane in splitview state, so lets move to
603     // fullscreen mode. This makes available area slightly bigger, which helps usability
604     // and greatly reduces event passing in orientation switch cases,
605     // as the statuspane size is not changing.
606
607     if (alwaysResize)
608         windowToMove->setUpdatesEnabled(false);
609
610     if (!(windowToMove->windowState() & Qt::WindowFullScreen)) {
611         windowToMove->setWindowState(
612             (windowToMove->windowState() & ~(Qt::WindowMinimized | Qt::WindowFullScreen)) | Qt::WindowFullScreen);
613     }
614
615     if (alwaysResize) {
616         if (!moveWithinVisibleArea) {
617             m_splitViewResizeBy = widget->height();
618             windowTop = widget->geometry().top();
619             widget->resize(widget->width(), splitViewRect.height() - windowTop);
620         }
621
622         if (gv->scene() && S60->partial_keyboardAutoTranslation) {
623             const QRectF microFocusRect = gv->scene()->inputMethodQuery(Qt::ImMicroFocus).toRectF();
624             gv->ensureVisible(microFocusRect);
625         }
626     } else {
627         if (S60->partial_keyboardAutoTranslation)
628             translateInputWidget();
629     }
630
631     if (alwaysResize)
632         windowToMove->setUpdatesEnabled(true);
633
634     widget->setAttribute(Qt::WA_Resized, userResize); //not a user resize
635 }
636
637 static QTextCharFormat qt_TCharFormat2QTextCharFormat(const TCharFormat &cFormat, bool validStyleColor)
638 {
639     QTextCharFormat qFormat;
640
641     if (validStyleColor) {
642         QBrush foreground(QColor(cFormat.iFontPresentation.iTextColor.Internal()));
643         qFormat.setForeground(foreground);
644     }
645
646     qFormat.setFontStrikeOut(cFormat.iFontPresentation.iStrikethrough == EStrikethroughOn);
647     qFormat.setFontUnderline(cFormat.iFontPresentation.iUnderline == EUnderlineOn);
648
649     return qFormat;
650 }
651
652 void QCoeFepInputContext::updateHints(bool mustUpdateInputCapabilities)
653 {
654     QWidget *w = focusWidget();
655     if (w) {
656         QWidget *proxy = w->focusProxy();
657         Qt::InputMethodHints hints = proxy ? proxy->inputMethodHints() : w->inputMethodHints();
658
659         // Since splitview support works like an input method hint, yet it is private flag,
660         // we need to update its state separately.
661         if (QSysInfo::s60Version() > QSysInfo::SV_S60_5_0) {
662             TInt currentFlags = m_fepState->Flags();
663             if (isPartialKeyboardSupported())
664                 currentFlags |= QT_EAknEditorFlagEnablePartialScreen;
665             else
666                 currentFlags &= ~QT_EAknEditorFlagEnablePartialScreen;
667             if (currentFlags != m_fepState->Flags())
668                 m_fepState->SetFlags(currentFlags);
669         }
670
671         if (hints != m_lastImHints) {
672             m_lastImHints = hints;
673             applyHints(hints);
674         } else if (!mustUpdateInputCapabilities) {
675             // Optimization. Return immediately if there was no change.
676             return;
677         }
678     }
679     queueInputCapabilitiesChanged();
680 }
681
682 void QCoeFepInputContext::applyHints(Qt::InputMethodHints hints)
683 {
684     using namespace Qt;
685
686     reset();
687     commitTemporaryPreeditString();
688
689     const bool anynumbermodes = hints & (ImhDigitsOnly | ImhFormattedNumbersOnly | ImhDialableCharactersOnly);
690     const bool anytextmodes = hints & (ImhUppercaseOnly | ImhLowercaseOnly | ImhEmailCharactersOnly | ImhUrlCharactersOnly);
691     const bool numbersOnly = anynumbermodes && !anytextmodes;
692     const bool noOnlys = !(hints & ImhExclusiveInputMask);
693     // if alphanumeric input, or if multiple incompatible number modes are selected;
694     // then make all symbols available in numeric mode too.
695     const bool needsCharMap= !numbersOnly || ((hints & ImhFormattedNumbersOnly) && (hints & ImhDialableCharactersOnly));
696     TInt flags;
697     Qt::InputMethodHints oldHints = hints;
698
699     // Some sanity checking. Make sure that only one preference is set.
700     InputMethodHints prefs = ImhPreferNumbers | ImhPreferUppercase | ImhPreferLowercase;
701     prefs &= hints;
702     if (prefs != ImhPreferNumbers && prefs != ImhPreferUppercase && prefs != ImhPreferLowercase) {
703         hints &= ~prefs;
704     }
705     if (!noOnlys) {
706         // Make sure that the preference is within the permitted set.
707         if (hints & ImhPreferNumbers && !anynumbermodes) {
708             hints &= ~ImhPreferNumbers;
709         } else if (hints & ImhPreferUppercase && !(hints & ImhUppercaseOnly)) {
710             hints &= ~ImhPreferUppercase;
711         } else if (hints & ImhPreferLowercase && !(hints & ImhLowercaseOnly)) {
712             hints &= ~ImhPreferLowercase;
713         }
714         // If there is no preference, set it to something within the permitted set.
715         if (!(hints & ImhPreferNumbers || hints & ImhPreferUppercase || hints & ImhPreferLowercase)) {
716             if (hints & ImhLowercaseOnly) {
717                 hints |= ImhPreferLowercase;
718             } else if (hints & ImhUppercaseOnly) {
719                 hints |= ImhPreferUppercase;
720             } else if (numbersOnly) {
721                 hints |= ImhPreferNumbers;
722             }
723         }
724     }
725
726     if (hints & ImhPreferNumbers) {
727         m_fepState->SetDefaultInputMode(EAknEditorNumericInputMode);
728         m_fepState->SetCurrentInputMode(EAknEditorNumericInputMode);
729     } else {
730         m_fepState->SetDefaultInputMode(EAknEditorTextInputMode);
731         m_fepState->SetCurrentInputMode(EAknEditorTextInputMode);
732     }
733     flags = 0;
734     if (noOnlys || (anynumbermodes && anytextmodes)) {
735         flags = EAknEditorAllInputModes;
736     }
737     else if (anynumbermodes) {
738         flags |= EAknEditorNumericInputMode;
739     }
740     else if (anytextmodes) {
741         flags |= EAknEditorTextInputMode;
742     }
743     else {
744         flags = EAknEditorAllInputModes;
745     }
746     m_fepState->SetPermittedInputModes(flags);
747     ReportAknEdStateEvent(MAknEdStateObserver::EAknEdwinStateInputModeUpdate);
748
749     if (hints & ImhPreferLowercase) {
750         m_fepState->SetDefaultCase(EAknEditorLowerCase);
751         m_fepState->SetCurrentCase(EAknEditorLowerCase);
752     } else if (hints & ImhPreferUppercase) {
753         m_fepState->SetDefaultCase(EAknEditorUpperCase);
754         m_fepState->SetCurrentCase(EAknEditorUpperCase);
755     } else if (hints & ImhNoAutoUppercase) {
756         m_fepState->SetDefaultCase(EAknEditorLowerCase);
757         m_fepState->SetCurrentCase(EAknEditorLowerCase);
758     } else {
759         m_fepState->SetDefaultCase(EAknEditorTextCase);
760         m_fepState->SetCurrentCase(EAknEditorTextCase);
761     }
762     flags = 0;
763     if (hints & ImhUppercaseOnly) {
764         flags |= EAknEditorUpperCase;
765     }
766     if (hints & ImhLowercaseOnly) {
767         flags |= EAknEditorLowerCase;
768     }
769     if (flags == 0) {
770         flags = EAknEditorAllCaseModes;
771         if (hints & ImhNoAutoUppercase) {
772             flags &= ~EAknEditorTextCase;
773         }
774     }
775     m_fepState->SetPermittedCases(flags);
776     ReportAknEdStateEvent(MAknEdStateObserver::EAknEdwinStateCaseModeUpdate);
777
778     flags = 0;
779     if (QSysInfo::s60Version() > QSysInfo::SV_S60_5_0) {
780         if (isPartialKeyboardSupported())
781             flags |= QT_EAknEditorFlagEnablePartialScreen;
782         flags |= QT_EAknEditorFlagSelectionVisible;
783     }
784     if (hints & ImhUppercaseOnly && !(hints & ImhLowercaseOnly)
785             || hints & ImhLowercaseOnly && !(hints & ImhUppercaseOnly)) {
786         flags |= EAknEditorFlagFixedCase;
787     }
788     // Using T9 and hidden text together may actually crash the FEP, so check for hidden text too.
789     if (hints & ImhNoPredictiveText || hints & ImhHiddenText) {
790         flags |= EAknEditorFlagNoT9;
791     }
792     if (needsCharMap)
793         flags |= EAknEditorFlagUseSCTNumericCharmap;
794     m_fepState->SetFlags(flags);
795     ReportAknEdStateEvent(MAknEdStateObserver::EAknEdwinStateFlagsUpdate);
796
797     if (hints & ImhDialableCharactersOnly) {
798         // This is first, because if (ImhDialableCharactersOnly | ImhFormattedNumbersOnly)
799         // is specified, this one is more natural (# key enters a #)
800         flags = EAknEditorStandardNumberModeKeymap;
801     } else if (hints & ImhFormattedNumbersOnly) {
802         // # key enters decimal point
803         flags = EAknEditorCalculatorNumberModeKeymap;
804     } else if (hints & ImhDigitsOnly) {
805         // This is last, because it is most restrictive (# key is inactive)
806         flags = EAknEditorPlainNumberModeKeymap;
807     } else {
808         flags = EAknEditorStandardNumberModeKeymap;
809     }
810     m_fepState->SetNumericKeymap(static_cast<TAknEditorNumericKeymap>(flags));
811
812     if (hints & ImhUrlCharactersOnly) {
813         // URL characters is everything except space, so a superset of the other restrictions
814         m_fepState->SetSpecialCharacterTableResourceId(R_AVKON_URL_SPECIAL_CHARACTER_TABLE_DIALOG);
815     } else if (hints & ImhEmailCharactersOnly) {
816         m_fepState->SetSpecialCharacterTableResourceId(R_AVKON_EMAIL_ADDR_SPECIAL_CHARACTER_TABLE_DIALOG);
817     } else if (needsCharMap) {
818         m_fepState->SetSpecialCharacterTableResourceId(R_AVKON_SPECIAL_CHARACTER_TABLE_DIALOG);
819     } else {
820         m_fepState->SetSpecialCharacterTableResourceId(0);
821     }
822
823     if (hints & ImhHiddenText) {
824         m_textCapabilities = TCoeInputCapabilities::EAllText | TCoeInputCapabilities::ESecretText;
825     } else {
826         m_textCapabilities = TCoeInputCapabilities::EAllText;
827     }
828 }
829
830 void QCoeFepInputContext::applyFormat(QList<QInputMethodEvent::Attribute> *attributes)
831 {
832     TCharFormat cFormat;
833     QColor styleTextColor;
834     if (QWidget *focused = focusWidget()) {
835         QGraphicsView *gv = qobject_cast<QGraphicsView*>(focused);
836         if (!gv) // could be either the QGV or its viewport that has focus
837             gv = qobject_cast<QGraphicsView*>(focused->parentWidget());
838         if (gv) {
839             if (QGraphicsScene *scene = gv->scene()) {
840                 if (QGraphicsItem *focusItem = scene->focusItem()) {
841                     if (focusItem->isWidget()) {
842                         styleTextColor = static_cast<QGraphicsWidget*>(focusItem)->palette().text().color();
843                     }
844                 }
845             }
846         } else {
847             styleTextColor = focused->palette().text().color();
848         }
849     } else {
850         styleTextColor = QApplication::palette("QLineEdit").text().color();
851     }
852
853     if (styleTextColor.isValid()) {
854         const TLogicalRgb fontColor(TRgb(styleTextColor.red(), styleTextColor.green(), styleTextColor.blue(), styleTextColor.alpha()));
855         cFormat.iFontPresentation.iTextColor = fontColor;
856     }
857
858     TInt numChars = 0;
859     TInt charPos = 0;
860     int oldSize = attributes->size();
861     while (m_formatRetriever) {
862         m_formatRetriever->GetFormatOfFepInlineText(cFormat, numChars, charPos);
863         if (numChars <= 0) {
864             // This shouldn't happen according to S60 docs, but apparently does sometimes.
865             break;
866         }
867         attributes->append(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat,
868                                                         charPos,
869                                                         numChars,
870                                                         QVariant(qt_TCharFormat2QTextCharFormat(cFormat, styleTextColor.isValid()))));
871         charPos += numChars;
872         if (charPos >= m_preeditString.size()) {
873             break;
874         }
875     }
876
877 }
878
879 void QCoeFepInputContext::queueInputCapabilitiesChanged()
880 {
881     if (m_pendingInputCapabilitiesChanged)
882         return;
883
884     // Call ensureInputCapabilitiesChanged asynchronously. This is done to improve performance
885     // by not updating input capabilities too often. The reason we don't call the Symbian
886     // asynchronous version of InputCapabilitiesChanged is because we need to ensure that it
887     // is synchronous in some specific cases. Those will call ensureInputCapabilitesChanged.
888     QMetaObject::invokeMethod(this, "ensureInputCapabilitiesChanged", Qt::QueuedConnection);
889     m_pendingInputCapabilitiesChanged = true;
890 }
891
892 void QCoeFepInputContext::ensureInputCapabilitiesChanged()
893 {
894     if (!m_pendingInputCapabilitiesChanged)
895         return;
896
897     // The call below is essentially equivalent to InputCapabilitiesChanged(),
898     // but is synchronous, rather than asynchronous.
899     CCoeEnv::Static()->SyncNotifyFocusObserversOfChangeInFocus();
900     m_pendingInputCapabilitiesChanged = false;
901 }
902
903 void QCoeFepInputContext::translateInputWidget()
904 {
905     QGraphicsView *gv = qobject_cast<QGraphicsView *>(S60->splitViewLastWidget);
906     if (!gv)
907         return;
908     QRect splitViewRect = qt_TRect2QRect(static_cast<CEikAppUi*>(S60->appUi())->ClientRect());
909
910     QRectF cursor = gv->scene()->inputMethodQuery(Qt::ImMicroFocus).toRectF();
911     QPolygon cursorP = gv->mapFromScene(cursor);
912     QRectF vkbRect = QRectF(splitViewRect.bottomLeft(), qApp->desktop()->rect().bottomRight());
913     if (cursor.isEmpty() || vkbRect.isEmpty())
914         return;
915
916     // Fetch root item (i.e. graphicsitem with no parent)
917     QGraphicsItem *rootItem = 0;
918     foreach (QGraphicsItem *item, gv->scene()->items()) {
919         if (!item->parentItem()) {
920             rootItem = item;
921             break;
922         }
923     }
924     if (!rootItem)
925         return;
926
927     m_transformation = (rootItem->transform().isTranslating()) ? QRectF(0,0, gv->width(), rootItem->transform().dy()) : QRectF();
928
929     // Adjust cursor bounding rect to be lower, so that view translates if the cursor gets near
930     // the splitview border.
931     QRect cursorRect = cursorP.boundingRect().adjusted(0, cursor.height(), 0, cursor.height());
932     if (splitViewRect.contains(cursorRect))
933         return;
934
935     // New Y position should be ideally just above the keyboard.
936     // If that would expose unpainted canvas, limit the tranformation to the visible scene rect or
937     // to the focus item's shape/clip path.
938
939     const QPainterPath path = gv->scene()->focusItem()->isClipped() ?
940         gv->scene()->focusItem()->clipPath() : gv->scene()->focusItem()->shape();
941     const qreal itemHeight = path.boundingRect().height();
942
943     // Limit the maximum translation so that underlaying window content is not exposed.
944     qreal maxY = gv->sceneRect().bottom() - splitViewRect.bottom();
945     maxY = m_transformation.height() ? (qMin(itemHeight, maxY) + m_transformation.height()) : maxY;
946     if (maxY < 0)
947         maxY = 0;
948
949     // Translation should happen row-by-row, but initially it needs to ensure that cursor is visible.
950     const qreal translation = m_transformation.height() ?
951         cursor.height() : (cursorRect.bottom() - vkbRect.top());
952     const qreal dy = -(qMin(maxY, translation));
953
954     // Do not allow transform above screen top, nor beyond scenerect
955     if (m_transformation.height() + dy > 0 || gv->sceneRect().bottom() + m_transformation.height() < 0) {
956         // If we already have some transformation, remove it.
957         if (m_transformation.height() < 0 || gv->sceneRect().bottom() + m_transformation.height() < 0) {
958             rootItem->resetTransform();
959             translateInputWidget();
960         }
961         return;
962     }
963
964     rootItem->setTransform(QTransform::fromTranslate(0, dy), true);
965 }
966
967 void QCoeFepInputContext::StartFepInlineEditL(const TDesC& aInitialInlineText,
968         TInt aPositionOfInsertionPointInInlineText, TBool aCursorVisibility, const MFormCustomDraw* /*aCustomDraw*/,
969         MFepInlineTextFormatRetriever& aInlineTextFormatRetriever,
970         MFepPointerEventHandlerDuringInlineEdit& aPointerEventHandlerDuringInlineEdit)
971 {
972     QWidget *w = focusWidget();
973     if (!w)
974         return;
975
976     m_cachedPreeditString.clear();
977
978     commitTemporaryPreeditString();
979
980     QList<QInputMethodEvent::Attribute> attributes;
981
982     m_cursorVisibility = aCursorVisibility ? 1 : 0;
983     m_inlinePosition = aPositionOfInsertionPointInInlineText;
984     m_preeditString = qt_TDesC2QString(aInitialInlineText);
985
986     m_formatRetriever = &aInlineTextFormatRetriever;
987     m_pointerHandler = &aPointerEventHandlerDuringInlineEdit;
988
989     // With T9 aInitialInlineText is typically empty when StartFepInlineEditL is called,
990     // but FEP requires that selected text is always removed at StartFepInlineEditL.
991     // Let's remove the selected text if aInitialInlineText is empty and there is selected text
992     if (m_preeditString.isEmpty()) {
993         int anchor = w->inputMethodQuery(Qt::ImAnchorPosition).toInt();
994         int cursorPos = w->inputMethodQuery(Qt::ImCursorPosition).toInt();
995         int replacementLength = qAbs(cursorPos-anchor);
996         if (replacementLength > 0) {
997             int replacementStart = cursorPos < anchor ? 0 : -replacementLength;
998             QList<QInputMethodEvent::Attribute> clearSelectionAttributes;
999             QInputMethodEvent clearSelectionEvent(QLatin1String(""), clearSelectionAttributes);
1000             clearSelectionEvent.setCommitString(QLatin1String(""), replacementStart, replacementLength);
1001             sendEvent(clearSelectionEvent);
1002         }
1003     }
1004
1005     applyFormat(&attributes);
1006
1007     attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Cursor,
1008                                                    m_inlinePosition,
1009                                                    m_cursorVisibility,
1010                                                    QVariant()));
1011     QInputMethodEvent event(m_preeditString, attributes);
1012     sendEvent(event);
1013 }
1014
1015 void QCoeFepInputContext::UpdateFepInlineTextL(const TDesC& aNewInlineText,
1016         TInt aPositionOfInsertionPointInInlineText)
1017 {
1018     QWidget *w = focusWidget();
1019     if (!w)
1020         return;
1021
1022     commitTemporaryPreeditString();
1023
1024     m_inlinePosition = aPositionOfInsertionPointInInlineText;
1025
1026     QList<QInputMethodEvent::Attribute> attributes;
1027     applyFormat(&attributes);
1028     attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Cursor,
1029                                                    m_inlinePosition,
1030                                                    m_cursorVisibility,
1031                                                    QVariant()));
1032     QString newPreeditString = qt_TDesC2QString(aNewInlineText);
1033     QInputMethodEvent event(newPreeditString, attributes);
1034     if (!m_cachedPreeditString.isEmpty()) {
1035         event.setCommitString(QLatin1String(""), -m_cachedPreeditString.length(), m_cachedPreeditString.length());
1036         m_cachedPreeditString.clear();
1037     } else if (newPreeditString.isEmpty() && m_preeditString.isEmpty()) {
1038         // In Symbian world this means "erase last character".
1039         event.setCommitString(QLatin1String(""), -1, 1);
1040     }
1041     m_preeditString = newPreeditString;
1042     sendEvent(event);
1043 }
1044
1045 void QCoeFepInputContext::SetInlineEditingCursorVisibilityL(TBool aCursorVisibility)
1046 {
1047     QWidget *w = focusWidget();
1048     if (!w)
1049         return;
1050
1051     m_cursorVisibility = aCursorVisibility ? 1 : 0;
1052
1053     QList<QInputMethodEvent::Attribute> attributes;
1054     attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Cursor,
1055                                                    m_inlinePosition,
1056                                                    m_cursorVisibility,
1057                                                    QVariant()));
1058     QInputMethodEvent event(m_preeditString, attributes);
1059     sendEvent(event);
1060 }
1061
1062 void QCoeFepInputContext::CancelFepInlineEdit()
1063 {
1064     // We are not supposed to ever have a tempPreeditString and a real preedit string
1065     // from S60 at the same time, so it should be safe to rely on this test to determine
1066     // whether we should honor S60's request to clear the text or not.
1067     if (m_hasTempPreeditString || m_pendingTransactionCancel)
1068         return;
1069
1070     m_pendingTransactionCancel = true;
1071
1072     QList<QInputMethodEvent::Attribute> attributes;
1073     QInputMethodEvent event(QLatin1String(""), attributes);
1074     event.setCommitString(QLatin1String(""), 0, 0);
1075     m_preeditString.clear();
1076     m_inlinePosition = 0;
1077     sendEvent(event);
1078
1079     // Sync with native side editor state. Native side can then do various operations
1080     // based on editor state, such as removing 'exact word bubble'.
1081     if (!m_pendingInputCapabilitiesChanged)
1082         ReportAknEdStateEvent(MAknEdStateObserver::EAknSyncEdwinState);
1083
1084     m_pendingTransactionCancel = false;
1085 }
1086
1087 TInt QCoeFepInputContext::DocumentLengthForFep() const
1088 {
1089     QWidget *w = focusWidget();
1090     if (!w)
1091         return 0;
1092
1093     QVariant variant = w->inputMethodQuery(Qt::ImSurroundingText);
1094
1095     int size = variant.value<QString>().size() + m_preeditString.size();
1096
1097     // To fix an issue with backspaces not being generated if document size is zero,
1098     // fake document length to be at least one always, except when dealing with
1099     // hidden text widgets, where this faking would generate extra asterisk. Since the
1100     // primary use of hidden text widgets is password fields, they are unlikely to
1101     // support multiple lines anyway.
1102     if (size == 0 && !(m_textCapabilities & TCoeInputCapabilities::ESecretText))
1103         size = 1;
1104
1105     return size;
1106 }
1107
1108 TInt QCoeFepInputContext::DocumentMaximumLengthForFep() const
1109 {
1110     QWidget *w = focusWidget();
1111     if (!w)
1112         return 0;
1113
1114     QVariant variant = w->inputMethodQuery(Qt::ImMaximumTextLength);
1115     int size;
1116     if (variant.isValid()) {
1117         size = variant.toInt();
1118     } else {
1119         size = INT_MAX; // Sensible default for S60.
1120     }
1121     return size;
1122 }
1123
1124 void QCoeFepInputContext::SetCursorSelectionForFepL(const TCursorSelection& aCursorSelection)
1125 {
1126     QWidget *w = focusWidget();
1127     if (!w)
1128         return;
1129
1130     commitTemporaryPreeditString();
1131
1132     int pos = aCursorSelection.iAnchorPos;
1133     int length = aCursorSelection.iCursorPos - pos;
1134
1135     QList<QInputMethodEvent::Attribute> attributes;
1136     attributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, pos, length, QVariant());
1137     QInputMethodEvent event(m_preeditString, attributes);
1138     sendEvent(event);
1139 }
1140
1141 void QCoeFepInputContext::GetCursorSelectionForFep(TCursorSelection& aCursorSelection) const
1142 {
1143     QWidget *w = focusWidget();
1144     if (!w) {
1145         aCursorSelection.SetSelection(0,0);
1146         return;
1147     }
1148
1149     int cursor = w->inputMethodQuery(Qt::ImCursorPosition).toInt() + m_preeditString.size();
1150     int anchor = w->inputMethodQuery(Qt::ImAnchorPosition).toInt() + m_preeditString.size();
1151     QString text = w->inputMethodQuery(Qt::ImSurroundingText).value<QString>();
1152     int combinedSize = text.size() + m_preeditString.size();
1153     if (combinedSize < anchor || combinedSize < cursor) {
1154         // ### TODO! FIXME! QTBUG-5050
1155         // This is a hack to prevent crashing in 4.6 with QLineEdits that use input masks.
1156         // The root problem is that cursor position is relative to displayed text instead of the
1157         // actual text we get.
1158         //
1159         // To properly fix this we would need to know the displayText of QLineEdits instead
1160         // of just the text, which on itself should be a trivial change. The difficulties start
1161         // when we need to commit the changes back to the QLineEdit, which would have to be somehow
1162         // able to handle displayText, too.
1163         //
1164         // Until properly fixed, the cursor and anchor positions will not reflect correct positions
1165         // for masked QLineEdits, unless all the masked positions are filled in order so that
1166         // cursor position relative to the displayed text matches position relative to actual text.
1167         aCursorSelection.iAnchorPos = combinedSize;
1168         aCursorSelection.iCursorPos = combinedSize;
1169     } else {
1170         aCursorSelection.iAnchorPos = anchor;
1171         aCursorSelection.iCursorPos = cursor;
1172     }
1173 }
1174
1175 void QCoeFepInputContext::GetEditorContentForFep(TDes& aEditorContent, TInt aDocumentPosition,
1176         TInt aLengthToRetrieve) const
1177 {
1178     QWidget *w = focusWidget();
1179     if (!w) {
1180         aEditorContent.FillZ(aLengthToRetrieve);
1181         return;
1182     }
1183
1184     QString text = w->inputMethodQuery(Qt::ImSurroundingText).value<QString>();
1185     // FEP expects the preedit string to be part of the editor content, so let's mix it in.
1186     int cursor = w->inputMethodQuery(Qt::ImCursorPosition).toInt();
1187     text.insert(cursor, m_preeditString);
1188
1189     // Add additional space to empty non-password text to compensate
1190     // for the fake length we specified in DocumentLengthForFep().
1191     if (text.size() == 0 && !(m_textCapabilities & TCoeInputCapabilities::ESecretText))
1192         text += QChar(0x20);
1193
1194     aEditorContent.Copy(qt_QString2TPtrC(text.mid(aDocumentPosition, aLengthToRetrieve)));
1195 }
1196
1197 void QCoeFepInputContext::GetFormatForFep(TCharFormat& aFormat, TInt /* aDocumentPosition */) const
1198 {
1199     QWidget *w = focusWidget();
1200     if (!w) {
1201         aFormat = TCharFormat();
1202         return;
1203     }
1204
1205     QFont font = w->inputMethodQuery(Qt::ImFont).value<QFont>();
1206     QFontMetrics metrics(font);
1207     //QString name = font.rawName();
1208     QString name = font.defaultFamily(); // TODO! FIXME! Should be the above.
1209     QHBufC hBufC(name);
1210     aFormat = TCharFormat(hBufC->Des(), metrics.height());
1211 }
1212
1213 void QCoeFepInputContext::GetScreenCoordinatesForFepL(TPoint& aLeftSideOfBaseLine, TInt& aHeight,
1214         TInt& aAscent, TInt /* aDocumentPosition */) const
1215 {
1216     QWidget *w = focusWidget();
1217     if (!w) {
1218         aLeftSideOfBaseLine = TPoint(0,0);
1219         aHeight = 0;
1220         aAscent = 0;
1221         return;
1222     }
1223
1224     QRect rect = w->inputMethodQuery(Qt::ImMicroFocus).value<QRect>();
1225     aLeftSideOfBaseLine.iX = rect.left();
1226     aLeftSideOfBaseLine.iY = rect.bottom();
1227
1228     QFont font = w->inputMethodQuery(Qt::ImFont).value<QFont>();
1229     QFontMetrics metrics(font);
1230     aHeight = metrics.height();
1231     aAscent = metrics.ascent();
1232 }
1233
1234 void QCoeFepInputContext::DoCommitFepInlineEditL()
1235 {
1236     commitCurrentString(false);
1237     if (QSysInfo::s60Version() > QSysInfo::SV_S60_5_0)
1238         ReportAknEdStateEvent(QT_EAknCursorPositionChanged);
1239
1240 }
1241
1242 void QCoeFepInputContext::commitCurrentString(bool cancelFepTransaction)
1243 {
1244     QList<QInputMethodEvent::Attribute> attributes;
1245     QInputMethodEvent event(QLatin1String(""), attributes);
1246     event.setCommitString(m_preeditString, 0, 0);
1247     m_preeditString.clear();
1248     m_inlinePosition = 0;
1249     sendEvent(event);
1250
1251     m_hasTempPreeditString = false;
1252
1253     //Only cancel FEP transactions with prediction, when there is still active window.
1254     Qt::InputMethodHints currentHints = Qt::ImhNone;
1255     if (focusWidget()) {
1256         if (focusWidget()->focusProxy())
1257             currentHints = focusWidget()->focusProxy()->inputMethodHints();
1258         else
1259             currentHints = focusWidget()->inputMethodHints();
1260     }
1261     bool predictive = !(currentHints & Qt::ImhNoPredictiveText);
1262     bool widgetAndWindowAvailable = QApplication::activeWindow() && focusWidget();
1263
1264     if (cancelFepTransaction && ((predictive && widgetAndWindowAvailable) || !predictive)) {
1265         CCoeFep* fep = CCoeEnv::Static()->Fep();
1266         if (fep)
1267             fep->CancelTransaction();
1268     }
1269 }
1270
1271 MCoeFepAwareTextEditor_Extension1* QCoeFepInputContext::Extension1(TBool& aSetToTrue)
1272 {
1273     aSetToTrue = ETrue;
1274     return this;
1275 }
1276
1277 void QCoeFepInputContext::SetStateTransferingOwnershipL(MCoeFepAwareTextEditor_Extension1::CState* aState,
1278         TUid /*aTypeSafetyUid*/)
1279 {
1280     // Note: The S60 docs are wrong! See the State() function.
1281     if (m_fepState)
1282         delete m_fepState;
1283     m_fepState = static_cast<CAknEdwinState *>(aState);
1284 }
1285
1286 MCoeFepAwareTextEditor_Extension1::CState* QCoeFepInputContext::State(TUid /*aTypeSafetyUid*/)
1287 {
1288     // Note: The S60 docs are horribly wrong when describing the
1289     // SetStateTransferingOwnershipL function and this function. They say that the former
1290     // sets a CState object identified by the TUid, and the latter retrieves it.
1291     // In reality, the CState is expected to always be a CAknEdwinState (even if it was not
1292     // previously set), and the TUid is ignored. All in all, there is a single CAknEdwinState
1293     // per QCoeFepInputContext, which should be deleted if the SetStateTransferingOwnershipL
1294     // function is used to set a new one.
1295     return m_fepState;
1296 }
1297
1298 TTypeUid::Ptr QCoeFepInputContext::MopSupplyObject(TTypeUid /*id*/)
1299 {
1300     return TTypeUid::Null();
1301 }
1302
1303 MObjectProvider *QCoeFepInputContext::MopNext()
1304 {
1305     QWidget *w = focusWidget();
1306     if (w)
1307         return w->effectiveWinId();
1308     return 0;
1309 }
1310
1311 QT_END_NAMESPACE
1312
1313 #endif // QT_NO_IM