summaryrefslogtreecommitdiffstats
path: root/src/gui/inputmethod/qcoefepinputcontext_s60.cpp
diff options
context:
space:
mode:
authoraxis <qt-info@nokia.com>2009-04-24 13:34:15 +0200
committeraxis <qt-info@nokia.com>2009-04-24 13:34:15 +0200
commit8f427b2b914d5b575a4a7c0ed65d2fb8f45acc76 (patch)
treea17e1a767a89542ab59907462206d7dcf2e504b2 /src/gui/inputmethod/qcoefepinputcontext_s60.cpp
Long live Qt for S60!
Diffstat (limited to 'src/gui/inputmethod/qcoefepinputcontext_s60.cpp')
-rw-r--r--src/gui/inputmethod/qcoefepinputcontext_s60.cpp577
1 files changed, 577 insertions, 0 deletions
diff --git a/src/gui/inputmethod/qcoefepinputcontext_s60.cpp b/src/gui/inputmethod/qcoefepinputcontext_s60.cpp
new file mode 100644
index 0000000000..b9ea4c722b
--- /dev/null
+++ b/src/gui/inputmethod/qcoefepinputcontext_s60.cpp
@@ -0,0 +1,577 @@
+/****************************************************************************
+**
+** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the $MODULE$ of the Qt Toolkit.
+**
+** $TROLLTECH_DUAL_EMBEDDED_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT_NO_IM
+
+#include "qcoefepinputcontext_p.h"
+#include <qapplication.h>
+#include <qtextformat.h>
+
+#include <fepitfr.h>
+
+#include <limits.h>
+// You only find these enumerations on SDK 5 onwards, so we need to provide our own
+// to remain compatible with older releases. They won't be called by pre-5.0 SDKs.
+
+// MAknEdStateObserver::EAknCursorPositionChanged
+#define QT_EAknCursorPositionChanged MAknEdStateObserver::EAknEdwinStateEvent(6)
+// MAknEdStateObserver::EAknActivatePenInputRequest
+#define QT_EAknActivatePenInputRequest MAknEdStateObserver::EAknEdwinStateEvent(7)
+
+QT_BEGIN_NAMESPACE
+
+QCoeFepInputContext::QCoeFepInputContext(QObject *parent)
+ : QInputContext(parent),
+ m_fepState(new (ELeave) CAknEdwinState),
+ m_isEditing(false),
+ m_inDestruction(false),
+ m_cursorVisibility(1),
+ m_inlinePosition(0),
+ m_formatRetriever(0),
+ m_pointerHandler(0)
+{
+ m_fepState->SetObjectProvider(this);
+ m_fepState->SetFlags(EAknEditorFlagDefault);
+ m_fepState->SetDefaultInputMode( EAknEditorTextInputMode );
+ m_fepState->SetCurrentInputMode( EAknEditorTextInputMode );
+ m_fepState->SetPermittedInputModes( EAknEditorAllInputModes );
+ m_fepState->SetLocalLanguage(ELangEnglish);
+ m_fepState->SetDefaultLanguage(ELangEnglish);
+ m_fepState->SetDefaultCase( EAknEditorLowerCase );
+ m_fepState->SetCurrentCase( EAknEditorLowerCase );
+ m_fepState->SetPermittedCases( EAknEditorLowerCase|EAknEditorUpperCase );
+ m_fepState->SetSpecialCharacterTableResourceId( 0 );
+ m_fepState->SetNumericKeymap( EAknEditorStandardNumberModeKeymap );
+}
+
+QCoeFepInputContext::~QCoeFepInputContext()
+{
+ m_inDestruction = true;
+
+ // This is to make sure that the FEP manager "forgets" about us,
+ // otherwise we may get callbacks even after we're destroyed.
+ // The call is asynchronous though, so we must spin the event loop
+ // to make sure it gets detected.
+ CCoeEnv::Static()->InputCapabilitiesChanged();
+ QApplication::processEvents();
+
+ if (m_fepState)
+ delete m_fepState;
+}
+
+void QCoeFepInputContext::reset()
+{
+ CCoeEnv::Static()->Fep()->CancelTransaction();
+}
+
+void QCoeFepInputContext::update()
+{
+ // For pre-5.0 SDKs, we don't do any work.
+ if (QSysInfo::s60Version() != QSysInfo::SV_S60_5_0) {
+ return;
+ }
+
+ // Don't be fooled (as I was) by the name of this enumeration.
+ // What it really does is tell the virtual keyboard UI that the text has been
+ // updated and it should be reflected in the internal display of the VK.
+ m_fepState->ReportAknEdStateEventL(QT_EAknCursorPositionChanged);
+}
+
+void QCoeFepInputContext::setFocusWidget(QWidget *w)
+{
+ commitCurrentString();
+
+ CCoeEnv::Static()->Fep()->CancelTransaction();
+
+ QInputContext::setFocusWidget(w);
+}
+
+void QCoeFepInputContext::widgetDestroyed(QWidget *w)
+{
+ // Make sure that the input capabilities of whatever new widget got focused are queried.
+ CCoeControl *ctrl = w->effectiveWinId();
+ if (ctrl->IsFocused()) {
+ ctrl->SetFocus(false);
+ ctrl->SetFocus(true);
+ }
+}
+
+/*!
+ Definition of struct for mapping Symbian to ISO locale
+ ### REMOVE
+ See below.
+*/
+struct symbianToISO {
+ int symbian_language;
+ char iso_name[8];
+};
+
+/*!
+ Mapping from Symbian to ISO locale
+ ### REMOVE
+ This was taken from the preliminary QLocale port to S60, and should be
+ removed once that is finished.
+*/
+static const symbianToISO symbian_to_iso_list[] = {
+ { ELangEnglish, "en_GB" },
+ { ELangFrench, "fr_FR" },
+ { ELangGerman, "de_DE" },
+ { ELangSpanish, "es_ES" },
+ { ELangItalian, "it_IT" },
+ { ELangSwedish, "sv_SE" },
+ { ELangDanish, "da_DK" },
+ { ELangNorwegian, "no_NO" },
+ { ELangFinnish, "fi_FI" },
+ { ELangAmerican, "en_US" },
+ { ELangPortuguese, "pt_PT" },
+ { ELangTurkish, "tr_TR" },
+ { ELangIcelandic, "is_IS" },
+ { ELangRussian, "ru_RU" },
+ { ELangHungarian, "hu_HU" },
+ { ELangDutch, "nl_NL" },
+ { ELangBelgianFlemish, "nl_BE" },
+ { ELangCzech, "cs_CZ" },
+ { ELangSlovak, "sk_SK" },
+ { ELangPolish, "pl_PL" },
+ { ELangSlovenian, "sl_SI" },
+ { ELangTaiwanChinese, "zh_TW" },
+ { ELangHongKongChinese, "zh_HK" },
+ { ELangPrcChinese, "zh_CN" },
+ { ELangJapanese, "ja_JP" },
+ { ELangThai, "th_TH" },
+ { ELangArabic, "ar_AE" },
+ { ELangTagalog, "tl_PH" },
+ { ELangBulgarian, "bg_BG" },
+ { ELangCatalan, "ca_ES" },
+ { ELangCroatian, "hr_HR" },
+ { ELangEstonian, "et_EE" },
+ { ELangFarsi, "fa_IR" },
+ { ELangCanadianFrench, "fr_CA" },
+ { ELangGreek, "el_GR" },
+ { ELangHebrew, "he_IL" },
+ { ELangHindi, "hi_IN" },
+ { ELangIndonesian, "id_ID" },
+ { ELangLatvian, "lv_LV" },
+ { ELangLithuanian, "lt_LT" },
+ { ELangMalay, "ms_MY" },
+ { ELangBrazilianPortuguese, "pt_BR" },
+ { ELangRomanian, "ro_RO" },
+ { ELangSerbian, "sr_YU" },
+ { ELangLatinAmericanSpanish, "es" },
+ { ELangUkrainian, "uk_UA" },
+ { ELangUrdu, "ur_PK" }, // India/Pakistan
+ { ELangVietnamese, "vi_VN" },
+#ifdef __E32LANG_H__
+// 5.0
+ { ELangBasque, "eu_ES" },
+ { ELangGalician, "gl_ES" },
+#endif
+ //{ ELangEnglish_Apac, "en" },
+ //{ ELangEnglish_Taiwan, "en_TW" },
+ //{ ELangEnglish_HongKong, "en_HK" },
+ //{ ELangEnglish_Prc, "en_CN" },
+ //{ ELangEnglish_Japan, "en_JP"},
+ //{ ELangEnglish_Thailand, "en_TH" },
+ //{ ELangMalay_Apac, "ms" }
+};
+
+/*!
+ Number of Symbian to ISO locale mappings
+ ### Remove.
+ See comment for array above.
+*/
+static const int symbian_to_iso_count
+ = sizeof(symbian_to_iso_list)/sizeof(symbianToISO);
+
+QString QCoeFepInputContext::language()
+{
+ TLanguage lang = m_fepState->LocalLanguage();
+ if (lang < symbian_to_iso_count) {
+ return QLatin1String(symbian_to_iso_list[lang].iso_name);
+ } else {
+ return QLatin1String("C");
+ }
+}
+
+bool QCoeFepInputContext::filterEvent(const QEvent *event)
+{
+ // For pre-5.0 SDKs, we don't do any work.
+ if (QSysInfo::s60Version() != QSysInfo::SV_S60_5_0) {
+ return false;
+ }
+
+ if (event->type() == QEvent::MouseButtonPress) {
+ const QMouseEvent *mEvent = static_cast<const QMouseEvent *>(event);
+ m_mousePressPos = mEvent->globalPos();
+ } else if (event->type() == QEvent::MouseButtonRelease) {
+ // Notify S60 that we want the virtual keyboard to show up.
+ const QMouseEvent *mEvent = static_cast<const QMouseEvent *>(event);
+
+ if (mEvent->modifiers() == Qt::NoModifier
+ && mEvent->button() == Qt::LeftButton
+ && focusWidget() // Not set if prior MouseButtonPress was not on this widget
+ && focusWidget()->rect().contains(focusWidget()->mapFromGlobal(mEvent->globalPos()))
+ && (m_mousePressPos - mEvent->globalPos()).manhattanLength() < QApplication::startDragDistance()) {
+
+ QSymbianControl *sControl;
+ sControl = focusWidget()->effectiveWinId()->MopGetObject(sControl);
+ // The FEP UI temporarily steals focus when it shows up the first time, causing
+ // all sorts of weird effects on the focused widgets. Since it will immediately give
+ // back focus to us, we temporarily disable focus handling until the job's done.
+ if (sControl) {
+ sControl->setIgnoreFocusChanged(true);
+ }
+
+ m_fepState->ReportAknEdStateEventL(MAknEdStateObserver::QT_EAknActivatePenInputRequest);
+
+ if (sControl) {
+ sControl->setIgnoreFocusChanged(false);
+ }
+
+ // Although it is tempting to let the click through by returning false, we have to return
+ // true because the event might have caused focus switches, which may in turn delete
+ // widgets.
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void QCoeFepInputContext::mouseHandler( int x, QMouseEvent *event)
+{
+ Q_ASSERT(m_isEditing);
+
+ if (!m_pointerHandler) {
+ QInputContext::mouseHandler(x, event);
+ }
+
+ TPointerEvent::TType type;
+ TUint modifiers = 0;
+
+ if (event->type() == QEvent::MouseButtonPress) {
+ if (event->button() == Qt::LeftButton) {
+ type = TPointerEvent::EButton1Down;
+ } else if (event->button() == Qt::RightButton) {
+ type = TPointerEvent::EButton3Down;
+ } else if (event->button() == Qt::MidButton) {
+ type = TPointerEvent::EButton2Down;
+ } else {
+ return;
+ }
+ } else if (event->type() == QEvent::MouseButtonRelease) {
+ if (event->button() == Qt::LeftButton) {
+ type = TPointerEvent::EButton1Up;
+ } else if (event->button() == Qt::RightButton) {
+ type = TPointerEvent::EButton3Up;
+ } else if (event->button() == Qt::MidButton) {
+ type = TPointerEvent::EButton2Up;
+ } else {
+ return;
+ }
+ } else if (event->type() == QEvent::MouseMove) {
+ type = TPointerEvent::EMove;
+ } else if (event->type() == QEvent::DragMove) {
+ type = TPointerEvent::EDrag;
+ } else {
+ return;
+ }
+
+ if (event->modifiers() & Qt::ShiftModifier) {
+ modifiers |= EModifierShift;
+ }
+ if (event->modifiers() & Qt::AltModifier) {
+ modifiers |= EModifierAlt;
+ }
+ if (event->modifiers() & Qt::ControlModifier) {
+ modifiers |= EModifierCtrl;
+ }
+ if (event->modifiers() & Qt::KeypadModifier) {
+ modifiers |= EModifierKeypad;
+ }
+
+ m_pointerHandler->HandlePointerEventInInlineTextL(type, modifiers, x);
+}
+
+TCoeInputCapabilities QCoeFepInputContext::inputCapabilities()
+{
+ if (m_inDestruction) {
+ return TCoeInputCapabilities(TCoeInputCapabilities::ENone, 0, 0);
+ }
+
+ return TCoeInputCapabilities(TCoeInputCapabilities::EAllText, this, 0);
+}
+
+static QTextCharFormat qt_TCharFormat2QTextCharFormat(const TCharFormat &cFormat)
+{
+ QTextCharFormat qFormat;
+
+ QBrush foreground(QColor(cFormat.iFontPresentation.iTextColor.Internal()));
+ qFormat.setForeground(foreground);
+
+ qFormat.setFontStrikeOut(cFormat.iFontPresentation.iStrikethrough == EStrikethroughOn);
+ qFormat.setFontUnderline(cFormat.iFontPresentation.iUnderline == EUnderlineOn);
+
+ return qFormat;
+}
+
+void QCoeFepInputContext::StartFepInlineEditL(const TDesC& aInitialInlineText,
+ TInt aPositionOfInsertionPointInInlineText, TBool aCursorVisibility, const MFormCustomDraw* /*aCustomDraw*/,
+ MFepInlineTextFormatRetriever& aInlineTextFormatRetriever,
+ MFepPointerEventHandlerDuringInlineEdit& aPointerEventHandlerDuringInlineEdit)
+{
+ QWidget *w = focusWidget();
+ if (!w)
+ return;
+
+ m_isEditing = true;
+
+ QList<QInputMethodEvent::Attribute> attributes;
+
+ m_cursorVisibility = aCursorVisibility ? 1 : 0;
+ m_inlinePosition = aPositionOfInsertionPointInInlineText;
+ m_preeditString = qt_TDesC2QStringL(aInitialInlineText);
+
+ m_formatRetriever = &aInlineTextFormatRetriever;
+ m_pointerHandler = &aPointerEventHandlerDuringInlineEdit;
+
+ TCharFormat cFormat;
+ TInt numChars = 0;
+ TInt charPos = 0;
+ while (m_formatRetriever) {
+ m_formatRetriever->GetFormatOfFepInlineText(cFormat, numChars, charPos);
+ if (numChars <= 0) {
+ // This shouldn't happen according to S60 docs, but apparently does sometimes.
+ break;
+ }
+ attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat,
+ charPos,
+ numChars,
+ QVariant(qt_TCharFormat2QTextCharFormat(cFormat))));
+ charPos += numChars;
+ if (charPos >= m_preeditString.size()) {
+ break;
+ }
+ }
+
+ attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Cursor,
+ m_inlinePosition,
+ m_cursorVisibility,
+ QVariant()));
+ QInputMethodEvent event(m_preeditString, attributes);
+ sendEvent(event);
+}
+
+void QCoeFepInputContext::UpdateFepInlineTextL(const TDesC& aNewInlineText,
+ TInt aPositionOfInsertionPointInInlineText)
+{
+ QWidget *w = focusWidget();
+ if (!w)
+ return;
+
+ m_inlinePosition = aPositionOfInsertionPointInInlineText;
+
+ QList<QInputMethodEvent::Attribute> attributes;
+ attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Cursor,
+ m_inlinePosition,
+ m_cursorVisibility,
+ QVariant()));
+ m_preeditString = qt_TDesC2QStringL(aNewInlineText);
+ QInputMethodEvent event(m_preeditString, attributes);
+ sendEvent(event);
+}
+
+void QCoeFepInputContext::SetInlineEditingCursorVisibilityL(TBool aCursorVisibility)
+{
+ QWidget *w = focusWidget();
+ if (!w)
+ return;
+
+ m_cursorVisibility = aCursorVisibility ? 1 : 0;
+
+ QList<QInputMethodEvent::Attribute> attributes;
+ attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Cursor,
+ m_inlinePosition,
+ m_cursorVisibility,
+ QVariant()));
+ QInputMethodEvent event(m_preeditString, attributes);
+ sendEvent(event);
+}
+
+void QCoeFepInputContext::CancelFepInlineEdit()
+{
+ QList<QInputMethodEvent::Attribute> attributes;
+ QInputMethodEvent event("", attributes);
+ event.setCommitString("", 0, 0);
+ m_preeditString.clear();
+ sendEvent(event);
+
+ m_isEditing = false;
+}
+
+TInt QCoeFepInputContext::DocumentLengthForFep() const
+{
+ QWidget *w = focusWidget();
+ if (!w)
+ return 0;
+
+ QVariant variant = w->inputMethodQuery(Qt::ImSurroundingText);
+ return variant.value<QString>().size() + m_preeditString.size();
+}
+
+TInt QCoeFepInputContext::DocumentMaximumLengthForFep() const
+{
+ QWidget *w = focusWidget();
+ if (!w)
+ return 0;
+
+ QVariant variant = w->inputMethodQuery(Qt::ImMaximumTextLength);
+ int size;
+ if (variant.isValid()) {
+ size = variant.toInt();
+ } else {
+ size = INT_MAX; // Sensible default for S60.
+ }
+ return size;
+}
+
+void QCoeFepInputContext::SetCursorSelectionForFepL(const TCursorSelection& aCursorSelection)
+{
+ QWidget *w = focusWidget();
+ if (!w)
+ return;
+
+ int pos = w->inputMethodQuery(Qt::ImCursorPosition).toInt() + aCursorSelection.iCursorPos + 1;
+
+ QList<QInputMethodEvent::Attribute> attributes;
+ attributes << QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, pos, 1, QVariant());
+ QInputMethodEvent event(m_preeditString, attributes);
+ // ### FIXME Sets preeditcursor and not cursor. Probably needs new API.
+ //sendEvent(event);
+}
+
+void QCoeFepInputContext::GetCursorSelectionForFep(TCursorSelection& aCursorSelection) const
+{
+ QWidget *w = focusWidget();
+ if (!w)
+ return;
+
+ QVariant cursorVar = w->inputMethodQuery(Qt::ImCursorPosition);
+ int cursor = cursorVar.toInt() + m_preeditString.size();
+ int anchor = cursor - w->inputMethodQuery(Qt::ImCurrentSelection).toString().size();
+ aCursorSelection.iAnchorPos = anchor;
+ aCursorSelection.iCursorPos = cursor;
+}
+
+void QCoeFepInputContext::GetEditorContentForFep(TDes& aEditorContent, TInt aDocumentPosition,
+ TInt aLengthToRetrieve) const
+{
+ QWidget *w = focusWidget();
+ if (!w)
+ return;
+
+ QString text = w->inputMethodQuery(Qt::ImSurroundingText).value<QString>();
+ // FEP expects the preedit string to be part of the editor content, so let's mix it in.
+ int cursor = w->inputMethodQuery(Qt::ImCursorPosition).toInt();
+ text.insert(cursor, m_preeditString);
+ aEditorContent.Copy(qt_QString2TPtrC(text.mid(aDocumentPosition, aLengthToRetrieve)));
+}
+
+void QCoeFepInputContext::GetFormatForFep(TCharFormat& aFormat, TInt aDocumentPosition) const
+{
+ QWidget *w = focusWidget();
+ if (!w)
+ return;
+
+ QFont font = w->inputMethodQuery(Qt::ImFont).value<QFont>();
+ QFontMetrics metrics(font);
+ //QString name = font.rawName();
+ QString name = font.defaultFamily(); // TODO! FIXME! Should be the above.
+ QHBufC hBufC(name);
+ aFormat = TCharFormat(hBufC->Des(), metrics.height());
+
+ aDocumentPosition = w->inputMethodQuery(Qt::ImCursorPosition).toInt();
+}
+
+void QCoeFepInputContext::GetScreenCoordinatesForFepL(TPoint& aLeftSideOfBaseLine, TInt& aHeight,
+ TInt& aAscent, TInt aDocumentPosition) const
+{
+ QWidget *w = focusWidget();
+ if (!w)
+ return;
+
+ QRect rect = w->inputMethodQuery(Qt::ImMicroFocus).value<QRect>();
+ aLeftSideOfBaseLine.iX = rect.left();
+ aLeftSideOfBaseLine.iY = rect.bottom();
+
+ QFont font = w->inputMethodQuery(Qt::ImFont).value<QFont>();
+ QFontMetrics metrics(font);
+ aHeight = metrics.height();
+ aAscent = metrics.ascent();
+
+ aDocumentPosition = w->inputMethodQuery(Qt::ImCursorPosition).toInt();
+}
+
+void QCoeFepInputContext::DoCommitFepInlineEditL()
+{
+ commitCurrentString();
+
+ m_isEditing = false;
+}
+
+void QCoeFepInputContext::commitCurrentString()
+{
+ if (m_preeditString.size() == 0) {
+ return;
+ }
+
+ QList<QInputMethodEvent::Attribute> attributes;
+ QInputMethodEvent event("", attributes);
+ event.setCommitString(m_preeditString, 0, 0);//m_preeditString.size());
+ m_preeditString.clear();
+ sendEvent(event);
+}
+
+MCoeFepAwareTextEditor_Extension1* QCoeFepInputContext::Extension1(TBool& aSetToTrue)
+{
+ aSetToTrue = ETrue;
+ return this;
+}
+
+void QCoeFepInputContext::SetStateTransferingOwnershipL(MCoeFepAwareTextEditor_Extension1::CState* aState,
+ TUid /*aTypeSafetyUid*/)
+{
+ // Note: The S60 docs are wrong! See the State() function.
+ if (m_fepState)
+ delete m_fepState;
+ m_fepState = static_cast<CAknEdwinState *>(aState);
+}
+
+MCoeFepAwareTextEditor_Extension1::CState* QCoeFepInputContext::State(TUid /*aTypeSafetyUid*/)
+{
+ // Note: The S60 docs are horribly wrong when describing the
+ // SetStateTransferingOwnershipL function and this function. They say that the former
+ // sets a CState object identified by the TUid, and the latter retrieves it.
+ // In reality, the CState is expected to always be a CAknEdwinState (even if it was not
+ // previously set), and the TUid is ignored. All in all, there is a single CAknEdwinState
+ // per QCoeFepInputContext, which should be deleted if the SetStateTransferingOwnershipL
+ // function is used to set a new one.
+ return m_fepState;
+}
+
+TTypeUid::Ptr QCoeFepInputContext::MopSupplyObject(TTypeUid /*id*/)
+{
+ return TTypeUid::Null();
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_IM