util/src/gui/inputmethod/qcoefepinputcontext_s60.cpp
changeset 7 f7bc934e204c
equal deleted inserted replaced
3:41300fa6a67c 7:f7bc934e204c
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2010 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 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    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 <private/qcore_symbian_p.h>
       
    48 
       
    49 #include <fepitfr.h>
       
    50 #include <hal.h>
       
    51 
       
    52 #include <limits.h>
       
    53 // You only find these enumerations on SDK 5 onwards, so we need to provide our own
       
    54 // to remain compatible with older releases. They won't be called by pre-5.0 SDKs.
       
    55 
       
    56 // MAknEdStateObserver::EAknCursorPositionChanged
       
    57 #define QT_EAknCursorPositionChanged MAknEdStateObserver::EAknEdwinStateEvent(6)
       
    58 // MAknEdStateObserver::EAknActivatePenInputRequest
       
    59 #define QT_EAknActivatePenInputRequest MAknEdStateObserver::EAknEdwinStateEvent(7)
       
    60 
       
    61 QT_BEGIN_NAMESPACE
       
    62 
       
    63 QCoeFepInputContext::QCoeFepInputContext(QObject *parent)
       
    64     : QInputContext(parent),
       
    65       m_fepState(q_check_ptr(new CAknEdwinState)),		// CBase derived object needs check on new
       
    66       m_lastImHints(Qt::ImhNone),
       
    67       m_textCapabilities(TCoeInputCapabilities::EAllText),
       
    68       m_inDestruction(false),
       
    69       m_pendingInputCapabilitiesChanged(false),
       
    70       m_cursorVisibility(1),
       
    71       m_inlinePosition(0),
       
    72       m_formatRetriever(0),
       
    73       m_pointerHandler(0),
       
    74       m_cursorPos(0),
       
    75       m_hasTempPreeditString(false)
       
    76 {
       
    77     m_fepState->SetObjectProvider(this);
       
    78     m_fepState->SetFlags(EAknEditorFlagDefault);
       
    79     m_fepState->SetDefaultInputMode( EAknEditorTextInputMode );
       
    80     m_fepState->SetPermittedInputModes( EAknEditorAllInputModes );
       
    81     m_fepState->SetDefaultCase( EAknEditorLowerCase );
       
    82     m_fepState->SetPermittedCases( EAknEditorLowerCase|EAknEditorUpperCase );
       
    83     m_fepState->SetSpecialCharacterTableResourceId(R_AVKON_SPECIAL_CHARACTER_TABLE_DIALOG);
       
    84     m_fepState->SetNumericKeymap( EAknEditorStandardNumberModeKeymap );
       
    85 }
       
    86 
       
    87 QCoeFepInputContext::~QCoeFepInputContext()
       
    88 {
       
    89     m_inDestruction = true;
       
    90 
       
    91     // This is to make sure that the FEP manager "forgets" about us,
       
    92     // otherwise we may get callbacks even after we're destroyed.
       
    93     // The call below is essentially equivalent to InputCapabilitiesChanged(),
       
    94     // but is synchronous, rather than asynchronous.
       
    95     CCoeEnv::Static()->SyncNotifyFocusObserversOfChangeInFocus();
       
    96 
       
    97     if (m_fepState)
       
    98         delete m_fepState;
       
    99 }
       
   100 
       
   101 void QCoeFepInputContext::reset()
       
   102 {
       
   103     commitCurrentString(true);
       
   104 }
       
   105 
       
   106 void QCoeFepInputContext::ReportAknEdStateEvent(MAknEdStateObserver::EAknEdwinStateEvent aEventType)
       
   107 {
       
   108     QT_TRAP_THROWING(m_fepState->ReportAknEdStateEventL(aEventType));
       
   109 }
       
   110 
       
   111 void QCoeFepInputContext::update()
       
   112 {
       
   113     updateHints(false);
       
   114 
       
   115     // For pre-5.0 SDKs, we don't do text updates on S60 side.
       
   116     if (QSysInfo::s60Version() < QSysInfo::SV_S60_5_0) {
       
   117         return;
       
   118     }
       
   119 
       
   120     // Don't be fooled (as I was) by the name of this enumeration.
       
   121     // What it really does is tell the virtual keyboard UI that the text has been
       
   122     // updated and it should be reflected in the internal display of the VK.
       
   123     ReportAknEdStateEvent(QT_EAknCursorPositionChanged);
       
   124 }
       
   125 
       
   126 void QCoeFepInputContext::setFocusWidget(QWidget *w)
       
   127 {
       
   128     commitCurrentString(true);
       
   129 
       
   130     QInputContext::setFocusWidget(w);
       
   131 
       
   132     updateHints(true);
       
   133 }
       
   134 
       
   135 void QCoeFepInputContext::widgetDestroyed(QWidget *w)
       
   136 {
       
   137     // Make sure that the input capabilities of whatever new widget got focused are queried.
       
   138     CCoeControl *ctrl = w->effectiveWinId();
       
   139     if (ctrl->IsFocused()) {
       
   140         queueInputCapabilitiesChanged();
       
   141     }
       
   142 }
       
   143 
       
   144 QString QCoeFepInputContext::language()
       
   145 {
       
   146     TLanguage lang = m_fepState->LocalLanguage();
       
   147     const QByteArray localeName = qt_symbianLocaleName(lang);
       
   148     if (!localeName.isEmpty()) {
       
   149         return QString::fromLatin1(localeName);
       
   150     } else {
       
   151         return QString::fromLatin1("C");
       
   152     }
       
   153 }
       
   154 
       
   155 bool QCoeFepInputContext::needsInputPanel()
       
   156 {
       
   157     switch (QSysInfo::s60Version()) {
       
   158     case QSysInfo::SV_S60_3_1:
       
   159     case QSysInfo::SV_S60_3_2:
       
   160         // There are no touch phones for pre-5.0 SDKs.
       
   161         return false;
       
   162 #ifdef Q_CC_NOKIAX86
       
   163     default:
       
   164         // For emulator we assume that we need an input panel, since we can't
       
   165         // separate between phone types.
       
   166         return true;
       
   167 #else
       
   168     case QSysInfo::SV_S60_5_0: {
       
   169         // For SDK == 5.0, we need phone specific detection, since the HAL API
       
   170         // is no good on most phones. However, all phones at the time of writing use the
       
   171         // input panel, except N97 in landscape mode, but in this mode it refuses to bring
       
   172         // up the panel anyway, so we don't have to care.
       
   173         return true;
       
   174     }
       
   175     default:
       
   176         // For unknown/newer types, we try to use the HAL API.
       
   177         int keyboardEnabled;
       
   178         int keyboardType;
       
   179         int err[2];
       
   180         err[0] = HAL::Get(HAL::EKeyboard, keyboardType);
       
   181         err[1] = HAL::Get(HAL::EKeyboardState, keyboardEnabled);
       
   182         if (err[0] == KErrNone && err[1] == KErrNone
       
   183                 && keyboardType != 0 && keyboardEnabled)
       
   184             // Means that we have some sort of keyboard.
       
   185             return false;
       
   186 
       
   187         // Fall back to using the input panel.
       
   188         return true;
       
   189 #endif // !Q_CC_NOKIAX86
       
   190     }
       
   191 }
       
   192 
       
   193 bool QCoeFepInputContext::filterEvent(const QEvent *event)
       
   194 {
       
   195     // The CloseSoftwareInputPanel event is not handled here, because the VK will automatically
       
   196     // close when it discovers that the underlying widget does not have input capabilities.
       
   197 
       
   198     if (!focusWidget())
       
   199         return false;
       
   200 
       
   201     switch (event->type()) {
       
   202     case QEvent::KeyPress:
       
   203         commitTemporaryPreeditString();
       
   204         // fall through intended
       
   205     case QEvent::KeyRelease:
       
   206         const QKeyEvent *keyEvent = static_cast<const QKeyEvent *>(event);
       
   207         switch (keyEvent->key()) {
       
   208         case Qt::Key_F20:
       
   209             Q_ASSERT(m_lastImHints == focusWidget()->inputMethodHints());
       
   210             if (m_lastImHints & Qt::ImhHiddenText) {
       
   211                 // Special case in Symbian. On editors with secret text, F20 is for some reason
       
   212                 // considered to be a backspace.
       
   213                 QKeyEvent modifiedEvent(keyEvent->type(), Qt::Key_Backspace, keyEvent->modifiers(),
       
   214                         keyEvent->text(), keyEvent->isAutoRepeat(), keyEvent->count());
       
   215                 QApplication::sendEvent(focusWidget(), &modifiedEvent);
       
   216                 return true;
       
   217             }
       
   218             break;
       
   219         case Qt::Key_Select:
       
   220             if (!m_preeditString.isEmpty()) {
       
   221                 commitCurrentString(true);
       
   222                 return true;
       
   223             }
       
   224             break;
       
   225         default:
       
   226             break;
       
   227         }
       
   228 
       
   229         if (keyEvent->type() == QEvent::KeyPress
       
   230             && focusWidget()->inputMethodHints() & Qt::ImhHiddenText
       
   231             && !keyEvent->text().isEmpty()) {
       
   232             // Send some temporary preedit text in order to make text visible for a moment.
       
   233             m_cursorPos = focusWidget()->inputMethodQuery(Qt::ImCursorPosition).toInt();
       
   234             m_preeditString = keyEvent->text();
       
   235             QList<QInputMethodEvent::Attribute> attributes;
       
   236             QInputMethodEvent imEvent(m_preeditString, attributes);
       
   237             sendEvent(imEvent);
       
   238             m_tempPreeditStringTimeout.start(1000, this);
       
   239             m_hasTempPreeditString = true;
       
   240             update();
       
   241             return true;
       
   242         }
       
   243         break;
       
   244     }
       
   245 
       
   246     if (!needsInputPanel())
       
   247         return false;
       
   248 
       
   249     if (event->type() == QEvent::RequestSoftwareInputPanel) {
       
   250         // Notify S60 that we want the virtual keyboard to show up.
       
   251         QSymbianControl *sControl;
       
   252         sControl = focusWidget()->effectiveWinId()->MopGetObject(sControl);
       
   253         Q_ASSERT(sControl);
       
   254 
       
   255         // The FEP UI temporarily steals focus when it shows up the first time, causing
       
   256         // all sorts of weird effects on the focused widgets. Since it will immediately give
       
   257         // back focus to us, we temporarily disable focus handling until the job's done.
       
   258         if (sControl) {
       
   259             sControl->setIgnoreFocusChanged(true);
       
   260         }
       
   261 
       
   262         ensureInputCapabilitiesChanged();
       
   263         m_fepState->ReportAknEdStateEventL(MAknEdStateObserver::QT_EAknActivatePenInputRequest);
       
   264 
       
   265         if (sControl) {
       
   266             sControl->setIgnoreFocusChanged(false);
       
   267         }
       
   268         return true;
       
   269     }
       
   270 
       
   271     return false;
       
   272 }
       
   273 
       
   274 void QCoeFepInputContext::timerEvent(QTimerEvent *timerEvent)
       
   275 {
       
   276     if (timerEvent->timerId() == m_tempPreeditStringTimeout.timerId())
       
   277         commitTemporaryPreeditString();
       
   278 }
       
   279 
       
   280 void QCoeFepInputContext::commitTemporaryPreeditString()
       
   281 {
       
   282     if (m_tempPreeditStringTimeout.isActive())
       
   283         m_tempPreeditStringTimeout.stop();
       
   284 
       
   285     if (!m_hasTempPreeditString)
       
   286         return;
       
   287 
       
   288     commitCurrentString(false);
       
   289 }
       
   290 
       
   291 void QCoeFepInputContext::mouseHandler( int x, QMouseEvent *event)
       
   292 {
       
   293     Q_ASSERT(focusWidget());
       
   294 
       
   295     if (event->type() == QEvent::MouseButtonPress && event->button() == Qt::LeftButton) {
       
   296         commitCurrentString(true);
       
   297         int pos = focusWidget()->inputMethodQuery(Qt::ImCursorPosition).toInt();
       
   298 
       
   299         QList<QInputMethodEvent::Attribute> attributes;
       
   300         attributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, pos + x, 0, QVariant());
       
   301         QInputMethodEvent event(QLatin1String(""), attributes);
       
   302         sendEvent(event);
       
   303     }
       
   304 }
       
   305 
       
   306 TCoeInputCapabilities QCoeFepInputContext::inputCapabilities()
       
   307 {
       
   308     if (m_inDestruction || !focusWidget()) {
       
   309         return TCoeInputCapabilities(TCoeInputCapabilities::ENone, 0, 0);
       
   310     }
       
   311 
       
   312     return TCoeInputCapabilities(m_textCapabilities, this, 0);
       
   313 }
       
   314 
       
   315 static QTextCharFormat qt_TCharFormat2QTextCharFormat(const TCharFormat &cFormat)
       
   316 {
       
   317     QTextCharFormat qFormat;
       
   318 
       
   319     QBrush foreground(QColor(cFormat.iFontPresentation.iTextColor.Internal()));
       
   320     qFormat.setForeground(foreground);
       
   321 
       
   322     qFormat.setFontStrikeOut(cFormat.iFontPresentation.iStrikethrough == EStrikethroughOn);
       
   323     qFormat.setFontUnderline(cFormat.iFontPresentation.iUnderline == EUnderlineOn);
       
   324 
       
   325     return qFormat;
       
   326 }
       
   327 
       
   328 void QCoeFepInputContext::updateHints(bool mustUpdateInputCapabilities)
       
   329 {
       
   330     QWidget *w = focusWidget();
       
   331     if (w) {
       
   332         Qt::InputMethodHints hints = w->inputMethodHints();
       
   333         if (hints != m_lastImHints) {
       
   334             m_lastImHints = hints;
       
   335             applyHints(hints);
       
   336         } else if (!mustUpdateInputCapabilities) {
       
   337             // Optimization. Return immediately if there was no change.
       
   338             return;
       
   339         }
       
   340     }
       
   341     queueInputCapabilitiesChanged();
       
   342 }
       
   343 
       
   344 void QCoeFepInputContext::applyHints(Qt::InputMethodHints hints)
       
   345 {
       
   346     using namespace Qt;
       
   347 
       
   348     commitTemporaryPreeditString();
       
   349 
       
   350     bool numbersOnly = hints & ImhDigitsOnly || hints & ImhFormattedNumbersOnly
       
   351             || hints & ImhDialableCharactersOnly;
       
   352     bool noOnlys = !(numbersOnly || hints & ImhUppercaseOnly
       
   353             || hints & ImhLowercaseOnly);
       
   354     TInt flags;
       
   355     Qt::InputMethodHints oldHints = hints;
       
   356 
       
   357     // Some sanity checking. Make sure that only one preference is set.
       
   358     InputMethodHints prefs = ImhPreferNumbers | ImhPreferUppercase | ImhPreferLowercase;
       
   359     prefs &= hints;
       
   360     if (prefs != ImhPreferNumbers && prefs != ImhPreferUppercase && prefs != ImhPreferLowercase) {
       
   361         hints &= ~prefs;
       
   362     }
       
   363     if (!noOnlys) {
       
   364         // Make sure that the preference is within the permitted set.
       
   365         if (hints & ImhPreferNumbers && !(hints & ImhDigitsOnly || hints & ImhFormattedNumbersOnly
       
   366                 || hints & ImhDialableCharactersOnly)) {
       
   367             hints &= ~ImhPreferNumbers;
       
   368         } else if (hints & ImhPreferUppercase && !(hints & ImhUppercaseOnly)) {
       
   369             hints &= ~ImhPreferUppercase;
       
   370         } else if (hints & ImhPreferLowercase && !(hints & ImhLowercaseOnly)) {
       
   371             hints &= ~ImhPreferLowercase;
       
   372         }
       
   373         // If there is no preference, set it to something within the permitted set.
       
   374         if (!(hints & ImhPreferNumbers || hints & ImhPreferUppercase || hints & ImhPreferLowercase)) {
       
   375             if (hints & ImhLowercaseOnly) {
       
   376                 hints |= ImhPreferLowercase;
       
   377             } else if (hints & ImhUppercaseOnly) {
       
   378                 hints |= ImhPreferUppercase;
       
   379             } else if (hints & ImhDigitsOnly || hints & ImhFormattedNumbersOnly
       
   380                     || hints & ImhDialableCharactersOnly) {
       
   381                 hints |= ImhPreferNumbers;
       
   382             }
       
   383         }
       
   384     }
       
   385 
       
   386     if (hints & ImhPreferNumbers) {
       
   387         m_fepState->SetDefaultInputMode(EAknEditorNumericInputMode);
       
   388         m_fepState->SetCurrentInputMode(EAknEditorNumericInputMode);
       
   389     } else {
       
   390         m_fepState->SetDefaultInputMode(EAknEditorTextInputMode);
       
   391         m_fepState->SetCurrentInputMode(EAknEditorTextInputMode);
       
   392     }
       
   393     flags = 0;
       
   394     if (numbersOnly) {
       
   395         flags |= EAknEditorNumericInputMode;
       
   396     }
       
   397     if (hints & ImhUppercaseOnly || hints & ImhLowercaseOnly) {
       
   398         flags |= EAknEditorTextInputMode;
       
   399     }
       
   400     if (flags == 0) {
       
   401         flags = EAknEditorAllInputModes;
       
   402     }
       
   403     m_fepState->SetPermittedInputModes(flags);
       
   404     ReportAknEdStateEvent(MAknEdStateObserver::EAknEdwinStateInputModeUpdate);
       
   405 
       
   406     if (hints & ImhPreferLowercase) {
       
   407         m_fepState->SetDefaultCase(EAknEditorLowerCase);
       
   408         m_fepState->SetCurrentCase(EAknEditorLowerCase);
       
   409     } else if (hints & ImhPreferUppercase) {
       
   410         m_fepState->SetDefaultCase(EAknEditorUpperCase);
       
   411         m_fepState->SetCurrentCase(EAknEditorUpperCase);
       
   412     } else if (hints & ImhNoAutoUppercase) {
       
   413         m_fepState->SetDefaultCase(EAknEditorLowerCase);
       
   414         m_fepState->SetCurrentCase(EAknEditorLowerCase);
       
   415     } else {
       
   416         m_fepState->SetDefaultCase(EAknEditorTextCase);
       
   417         m_fepState->SetCurrentCase(EAknEditorTextCase);
       
   418     }
       
   419     flags = 0;
       
   420     if (hints & ImhUppercaseOnly) {
       
   421         flags |= EAknEditorUpperCase;
       
   422     }
       
   423     if (hints & ImhLowercaseOnly) {
       
   424         flags |= EAknEditorLowerCase;
       
   425     }
       
   426     if (flags == 0) {
       
   427         flags = EAknEditorAllCaseModes;
       
   428         if (hints & ImhNoAutoUppercase) {
       
   429             flags &= ~EAknEditorTextCase;
       
   430         }
       
   431     }
       
   432     m_fepState->SetPermittedCases(flags);
       
   433     ReportAknEdStateEvent(MAknEdStateObserver::EAknEdwinStateCaseModeUpdate);
       
   434 
       
   435     flags = 0;
       
   436     if (hints & ImhUppercaseOnly && !(hints & ImhLowercaseOnly)
       
   437             || hints & ImhLowercaseOnly && !(hints & ImhUppercaseOnly)) {
       
   438         flags |= EAknEditorFlagFixedCase;
       
   439     }
       
   440     // Using T9 and hidden text together may actually crash the FEP, so check for hidden text too.
       
   441     if (hints & ImhNoPredictiveText || hints & ImhHiddenText) {
       
   442         flags |= EAknEditorFlagNoT9;
       
   443     }
       
   444     m_fepState->SetFlags(flags);
       
   445     ReportAknEdStateEvent(MAknEdStateObserver::EAknEdwinStateFlagsUpdate);
       
   446 
       
   447     if (hints & ImhFormattedNumbersOnly) {
       
   448         flags = EAknEditorCalculatorNumberModeKeymap;
       
   449     } else if (hints & ImhDigitsOnly) {
       
   450         flags = EAknEditorPlainNumberModeKeymap;
       
   451     } else {
       
   452         // ImhDialableCharactersOnly is the fallback as well, so we don't need to check for
       
   453         // that flag.
       
   454         flags = EAknEditorStandardNumberModeKeymap;
       
   455     }
       
   456     m_fepState->SetNumericKeymap(static_cast<TAknEditorNumericKeymap>(flags));
       
   457 
       
   458     if (hints & ImhEmailCharactersOnly) {
       
   459         m_fepState->SetSpecialCharacterTableResourceId(R_AVKON_EMAIL_ADDR_SPECIAL_CHARACTER_TABLE_DIALOG);
       
   460     } else if (hints & ImhUrlCharactersOnly) {
       
   461         m_fepState->SetSpecialCharacterTableResourceId(R_AVKON_URL_SPECIAL_CHARACTER_TABLE_DIALOG);
       
   462     } else {
       
   463         m_fepState->SetSpecialCharacterTableResourceId(R_AVKON_SPECIAL_CHARACTER_TABLE_DIALOG);
       
   464     }
       
   465 
       
   466     if (hints & ImhHiddenText) {
       
   467         m_textCapabilities = TCoeInputCapabilities::EAllText | TCoeInputCapabilities::ESecretText;
       
   468     } else {
       
   469         m_textCapabilities = TCoeInputCapabilities::EAllText;
       
   470     }
       
   471 }
       
   472 
       
   473 void QCoeFepInputContext::applyFormat(QList<QInputMethodEvent::Attribute> *attributes)
       
   474 {
       
   475     TCharFormat cFormat;
       
   476     QColor styleTextColor = QApplication::palette("QLineEdit").text().color();
       
   477     TLogicalRgb tontColor(TRgb(styleTextColor.red(), styleTextColor.green(), styleTextColor.blue(), styleTextColor.alpha()));
       
   478     cFormat.iFontPresentation.iTextColor = tontColor;
       
   479 
       
   480     TInt numChars = 0;
       
   481     TInt charPos = 0;
       
   482     int oldSize = attributes->size();
       
   483     while (m_formatRetriever) {
       
   484         m_formatRetriever->GetFormatOfFepInlineText(cFormat, numChars, charPos);
       
   485         if (numChars <= 0) {
       
   486             // This shouldn't happen according to S60 docs, but apparently does sometimes.
       
   487             break;
       
   488         }
       
   489         attributes->append(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat,
       
   490                                                         charPos,
       
   491                                                         numChars,
       
   492                                                         QVariant(qt_TCharFormat2QTextCharFormat(cFormat))));
       
   493         charPos += numChars;
       
   494         if (charPos >= m_preeditString.size()) {
       
   495             break;
       
   496         }
       
   497     }
       
   498 
       
   499     if (attributes->size() == oldSize) {
       
   500         // S60 didn't provide any format, so let's give our own instead.
       
   501         attributes->append(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat,
       
   502                                                         0,
       
   503                                                         m_preeditString.size(),
       
   504                                                         standardFormat(PreeditFormat)));
       
   505     }
       
   506 }
       
   507 
       
   508 void QCoeFepInputContext::queueInputCapabilitiesChanged()
       
   509 {
       
   510     if (m_pendingInputCapabilitiesChanged)
       
   511         return;
       
   512 
       
   513     // Call ensureInputCapabilitiesChanged asynchronously. This is done to improve performance
       
   514     // by not updating input capabilities too often. The reason we don't call the Symbian
       
   515     // asynchronous version of InputCapabilitiesChanged is because we need to ensure that it
       
   516     // is synchronous in some specific cases. Those will call ensureInputCapabilitesChanged.
       
   517     QMetaObject::invokeMethod(this, "ensureInputCapabilitiesChanged", Qt::QueuedConnection);
       
   518     m_pendingInputCapabilitiesChanged = true;
       
   519 }
       
   520 
       
   521 void QCoeFepInputContext::ensureInputCapabilitiesChanged()
       
   522 {
       
   523     if (!m_pendingInputCapabilitiesChanged)
       
   524         return;
       
   525 
       
   526     // The call below is essentially equivalent to InputCapabilitiesChanged(),
       
   527     // but is synchronous, rather than asynchronous.
       
   528     CCoeEnv::Static()->SyncNotifyFocusObserversOfChangeInFocus();
       
   529     m_pendingInputCapabilitiesChanged = false;
       
   530 }
       
   531 
       
   532 void QCoeFepInputContext::StartFepInlineEditL(const TDesC& aInitialInlineText,
       
   533         TInt aPositionOfInsertionPointInInlineText, TBool aCursorVisibility, const MFormCustomDraw* /*aCustomDraw*/,
       
   534         MFepInlineTextFormatRetriever& aInlineTextFormatRetriever,
       
   535         MFepPointerEventHandlerDuringInlineEdit& aPointerEventHandlerDuringInlineEdit)
       
   536 {
       
   537     QWidget *w = focusWidget();
       
   538     if (!w)
       
   539         return;
       
   540 
       
   541     commitTemporaryPreeditString();
       
   542 
       
   543     m_cursorPos = w->inputMethodQuery(Qt::ImCursorPosition).toInt();
       
   544     
       
   545     QList<QInputMethodEvent::Attribute> attributes;
       
   546 
       
   547     m_cursorVisibility = aCursorVisibility ? 1 : 0;
       
   548     m_inlinePosition = aPositionOfInsertionPointInInlineText;
       
   549     m_preeditString = qt_TDesC2QString(aInitialInlineText);
       
   550 
       
   551     m_formatRetriever = &aInlineTextFormatRetriever;
       
   552     m_pointerHandler = &aPointerEventHandlerDuringInlineEdit;
       
   553 
       
   554     // With T9 aInitialInlineText is typically empty when StartFepInlineEditL is called,
       
   555     // but FEP requires that selected text is always removed at StartFepInlineEditL.
       
   556     // Let's remove the selected text if aInitialInlineText is empty and there is selected text
       
   557     if (m_preeditString.isEmpty()) {
       
   558         int anchor = w->inputMethodQuery(Qt::ImAnchorPosition).toInt();
       
   559         int replacementLength = qAbs(m_cursorPos-anchor);
       
   560         if (replacementLength > 0) {
       
   561             int replacementStart = m_cursorPos < anchor ? 0 : -replacementLength;
       
   562             QList<QInputMethodEvent::Attribute> clearSelectionAttributes;
       
   563             QInputMethodEvent clearSelectionEvent(QLatin1String(""), clearSelectionAttributes);
       
   564             clearSelectionEvent.setCommitString(QLatin1String(""), replacementStart, replacementLength);
       
   565             sendEvent(clearSelectionEvent);
       
   566         }
       
   567     }
       
   568 
       
   569     applyFormat(&attributes);
       
   570 
       
   571     attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Cursor,
       
   572                                                    m_inlinePosition,
       
   573                                                    m_cursorVisibility,
       
   574                                                    QVariant()));
       
   575     QInputMethodEvent event(m_preeditString, attributes);
       
   576     sendEvent(event);
       
   577 }
       
   578 
       
   579 void QCoeFepInputContext::UpdateFepInlineTextL(const TDesC& aNewInlineText,
       
   580         TInt aPositionOfInsertionPointInInlineText)
       
   581 {
       
   582     QWidget *w = focusWidget();
       
   583     if (!w)
       
   584         return;
       
   585 
       
   586     m_inlinePosition = aPositionOfInsertionPointInInlineText;
       
   587 
       
   588     QList<QInputMethodEvent::Attribute> attributes;
       
   589     applyFormat(&attributes);
       
   590     attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Cursor,
       
   591                                                    m_inlinePosition,
       
   592                                                    m_cursorVisibility,
       
   593                                                    QVariant()));
       
   594     m_preeditString = qt_TDesC2QString(aNewInlineText);
       
   595     QInputMethodEvent event(m_preeditString, attributes);
       
   596     sendEvent(event);
       
   597 }
       
   598 
       
   599 void QCoeFepInputContext::SetInlineEditingCursorVisibilityL(TBool aCursorVisibility)
       
   600 {
       
   601     QWidget *w = focusWidget();
       
   602     if (!w)
       
   603         return;
       
   604 
       
   605     m_cursorVisibility = aCursorVisibility ? 1 : 0;
       
   606 
       
   607     QList<QInputMethodEvent::Attribute> attributes;
       
   608     attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Cursor,
       
   609                                                    m_inlinePosition,
       
   610                                                    m_cursorVisibility,
       
   611                                                    QVariant()));
       
   612     QInputMethodEvent event(m_preeditString, attributes);
       
   613     sendEvent(event);
       
   614 }
       
   615 
       
   616 void QCoeFepInputContext::CancelFepInlineEdit()
       
   617 {
       
   618     QList<QInputMethodEvent::Attribute> attributes;
       
   619     QInputMethodEvent event(QLatin1String(""), attributes);
       
   620     event.setCommitString(QLatin1String(""), 0, 0);
       
   621     m_preeditString.clear();
       
   622     sendEvent(event);
       
   623 }
       
   624 
       
   625 TInt QCoeFepInputContext::DocumentLengthForFep() const
       
   626 {
       
   627     QWidget *w = focusWidget();
       
   628     if (!w)
       
   629         return 0;
       
   630 
       
   631     QVariant variant = w->inputMethodQuery(Qt::ImSurroundingText);
       
   632     return variant.value<QString>().size() + m_preeditString.size();
       
   633 }
       
   634 
       
   635 TInt QCoeFepInputContext::DocumentMaximumLengthForFep() const
       
   636 {
       
   637     QWidget *w = focusWidget();
       
   638     if (!w)
       
   639         return 0;
       
   640 
       
   641     QVariant variant = w->inputMethodQuery(Qt::ImMaximumTextLength);
       
   642     int size;
       
   643     if (variant.isValid()) {
       
   644         size = variant.toInt();
       
   645     } else {
       
   646         size = INT_MAX; // Sensible default for S60.
       
   647     }
       
   648     return size;
       
   649 }
       
   650 
       
   651 void QCoeFepInputContext::SetCursorSelectionForFepL(const TCursorSelection& aCursorSelection)
       
   652 {
       
   653     QWidget *w = focusWidget();
       
   654     if (!w)
       
   655         return;
       
   656 
       
   657     commitTemporaryPreeditString();
       
   658 
       
   659     int pos = aCursorSelection.iAnchorPos;
       
   660     int length = aCursorSelection.iCursorPos - pos;
       
   661 
       
   662     QList<QInputMethodEvent::Attribute> attributes;
       
   663     attributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, pos, length, QVariant());
       
   664     QInputMethodEvent event(m_preeditString, attributes);
       
   665     sendEvent(event);
       
   666 }
       
   667 
       
   668 void QCoeFepInputContext::GetCursorSelectionForFep(TCursorSelection& aCursorSelection) const
       
   669 {
       
   670     QWidget *w = focusWidget();
       
   671     if (!w) {
       
   672         aCursorSelection.SetSelection(0,0);
       
   673         return;
       
   674     }
       
   675 
       
   676     int cursor = w->inputMethodQuery(Qt::ImCursorPosition).toInt() + m_preeditString.size();
       
   677     int anchor = w->inputMethodQuery(Qt::ImAnchorPosition).toInt() + m_preeditString.size();
       
   678     QString text = w->inputMethodQuery(Qt::ImSurroundingText).value<QString>();
       
   679     int combinedSize = text.size() + m_preeditString.size();
       
   680     if (combinedSize < anchor || combinedSize < cursor) {
       
   681         // ### TODO! FIXME! QTBUG-5050
       
   682         // This is a hack to prevent crashing in 4.6 with QLineEdits that use input masks.
       
   683         // The root problem is that cursor position is relative to displayed text instead of the
       
   684         // actual text we get.
       
   685         //
       
   686         // To properly fix this we would need to know the displayText of QLineEdits instead
       
   687         // of just the text, which on itself should be a trivial change. The difficulties start
       
   688         // when we need to commit the changes back to the QLineEdit, which would have to be somehow
       
   689         // able to handle displayText, too.
       
   690         //
       
   691         // Until properly fixed, the cursor and anchor positions will not reflect correct positions
       
   692         // for masked QLineEdits, unless all the masked positions are filled in order so that
       
   693         // cursor position relative to the displayed text matches position relative to actual text.
       
   694         aCursorSelection.iAnchorPos = combinedSize;
       
   695         aCursorSelection.iCursorPos = combinedSize;
       
   696     } else {
       
   697         aCursorSelection.iAnchorPos = anchor;
       
   698         aCursorSelection.iCursorPos = cursor;
       
   699     }
       
   700 }
       
   701 
       
   702 void QCoeFepInputContext::GetEditorContentForFep(TDes& aEditorContent, TInt aDocumentPosition,
       
   703         TInt aLengthToRetrieve) const
       
   704 {
       
   705     QWidget *w = focusWidget();
       
   706     if (!w) {
       
   707         aEditorContent.FillZ(aLengthToRetrieve);
       
   708         return;
       
   709     }
       
   710 
       
   711     QString text = w->inputMethodQuery(Qt::ImSurroundingText).value<QString>();
       
   712     // FEP expects the preedit string to be part of the editor content, so let's mix it in.
       
   713     int cursor = w->inputMethodQuery(Qt::ImCursorPosition).toInt();
       
   714     text.insert(cursor, m_preeditString);
       
   715     aEditorContent.Copy(qt_QString2TPtrC(text.mid(aDocumentPosition, aLengthToRetrieve)));
       
   716 }
       
   717 
       
   718 void QCoeFepInputContext::GetFormatForFep(TCharFormat& aFormat, TInt /* aDocumentPosition */) const
       
   719 {
       
   720     QWidget *w = focusWidget();
       
   721     if (!w) {
       
   722         aFormat = TCharFormat();
       
   723         return;
       
   724     }
       
   725 
       
   726     QFont font = w->inputMethodQuery(Qt::ImFont).value<QFont>();
       
   727     QFontMetrics metrics(font);
       
   728     //QString name = font.rawName();
       
   729     QString name = font.defaultFamily(); // TODO! FIXME! Should be the above.
       
   730     QHBufC hBufC(name);
       
   731     aFormat = TCharFormat(hBufC->Des(), metrics.height());
       
   732 }
       
   733 
       
   734 void QCoeFepInputContext::GetScreenCoordinatesForFepL(TPoint& aLeftSideOfBaseLine, TInt& aHeight,
       
   735         TInt& aAscent, TInt /* aDocumentPosition */) const
       
   736 {
       
   737     QWidget *w = focusWidget();
       
   738     if (!w) {
       
   739         aLeftSideOfBaseLine = TPoint(0,0);
       
   740         aHeight = 0;
       
   741         aAscent = 0;
       
   742         return;
       
   743     }
       
   744 
       
   745     QRect rect = w->inputMethodQuery(Qt::ImMicroFocus).value<QRect>();
       
   746     aLeftSideOfBaseLine.iX = rect.left();
       
   747     aLeftSideOfBaseLine.iY = rect.bottom();
       
   748 
       
   749     QFont font = w->inputMethodQuery(Qt::ImFont).value<QFont>();
       
   750     QFontMetrics metrics(font);
       
   751     aHeight = metrics.height();
       
   752     aAscent = metrics.ascent();
       
   753 }
       
   754 
       
   755 void QCoeFepInputContext::DoCommitFepInlineEditL()
       
   756 {
       
   757     commitCurrentString(false);
       
   758     if (QSysInfo::s60Version() > QSysInfo::SV_S60_5_0)
       
   759         ReportAknEdStateEvent(QT_EAknCursorPositionChanged);
       
   760 
       
   761 }
       
   762 
       
   763 void QCoeFepInputContext::commitCurrentString(bool cancelFepTransaction)
       
   764 {
       
   765     int longPress = 0;
       
   766 
       
   767     if (m_preeditString.size() == 0) {
       
   768         QWidget *w = focusWidget();
       
   769         if (!cancelFepTransaction && w) {
       
   770             // We must replace the last character only if the input box has already accepted one 
       
   771             if (w->inputMethodQuery(Qt::ImCursorPosition).toInt() != m_cursorPos)
       
   772                 longPress = 1;
       
   773         }
       
   774         return;
       
   775     }
       
   776 
       
   777     QList<QInputMethodEvent::Attribute> attributes;
       
   778     QInputMethodEvent event(QLatin1String(""), attributes);
       
   779     event.setCommitString(m_preeditString, 0-longPress, longPress);
       
   780     m_preeditString.clear();
       
   781     sendEvent(event);
       
   782 
       
   783     m_hasTempPreeditString = false;
       
   784     longPress = 0;
       
   785 
       
   786     if (cancelFepTransaction) {
       
   787         CCoeFep* fep = CCoeEnv::Static()->Fep();
       
   788         if (fep)
       
   789             fep->CancelTransaction();
       
   790     }
       
   791 }
       
   792 
       
   793 MCoeFepAwareTextEditor_Extension1* QCoeFepInputContext::Extension1(TBool& aSetToTrue)
       
   794 {
       
   795     aSetToTrue = ETrue;
       
   796     return this;
       
   797 }
       
   798 
       
   799 void QCoeFepInputContext::SetStateTransferingOwnershipL(MCoeFepAwareTextEditor_Extension1::CState* aState,
       
   800         TUid /*aTypeSafetyUid*/)
       
   801 {
       
   802     // Note: The S60 docs are wrong! See the State() function.
       
   803     if (m_fepState)
       
   804         delete m_fepState;
       
   805     m_fepState = static_cast<CAknEdwinState *>(aState);
       
   806 }
       
   807 
       
   808 MCoeFepAwareTextEditor_Extension1::CState* QCoeFepInputContext::State(TUid /*aTypeSafetyUid*/)
       
   809 {
       
   810     // Note: The S60 docs are horribly wrong when describing the
       
   811     // SetStateTransferingOwnershipL function and this function. They say that the former
       
   812     // sets a CState object identified by the TUid, and the latter retrieves it.
       
   813     // In reality, the CState is expected to always be a CAknEdwinState (even if it was not
       
   814     // previously set), and the TUid is ignored. All in all, there is a single CAknEdwinState
       
   815     // per QCoeFepInputContext, which should be deleted if the SetStateTransferingOwnershipL
       
   816     // function is used to set a new one.
       
   817     return m_fepState;
       
   818 }
       
   819 
       
   820 TTypeUid::Ptr QCoeFepInputContext::MopSupplyObject(TTypeUid /*id*/)
       
   821 {
       
   822     return TTypeUid::Null();
       
   823 }
       
   824 
       
   825 MObjectProvider *QCoeFepInputContext::MopNext()
       
   826 {
       
   827     QWidget *w = focusWidget();
       
   828     if (w)
       
   829         return w->effectiveWinId();
       
   830     return 0;
       
   831 }
       
   832 
       
   833 QT_END_NAMESPACE
       
   834 
       
   835 #endif // QT_NO_IM