src/gui/inputmethod/qcoefepinputcontext_s60.cpp
changeset 3 41300fa6a67c
parent 0 1918ee327afb
child 4 3b1da2848fc7
child 7 f7bc934e204c
child 18 2f34d5167611
--- a/src/gui/inputmethod/qcoefepinputcontext_s60.cpp	Tue Jan 26 12:42:25 2010 +0200
+++ b/src/gui/inputmethod/qcoefepinputcontext_s60.cpp	Tue Feb 02 00:43:10 2010 +0200
@@ -4,7 +4,7 @@
 ** All rights reserved.
 ** Contact: Nokia Corporation (qt-info@nokia.com)
 **
-** This file is part of the QtGui of the Qt Toolkit.
+** This file is part of the QtGui module of the Qt Toolkit.
 **
 ** $QT_BEGIN_LICENSE:LGPL$
 ** No Commercial Usage
@@ -47,6 +47,7 @@
 #include <private/qcore_symbian_p.h>
 
 #include <fepitfr.h>
+#include <hal.h>
 
 #include <limits.h>
 // You only find these enumerations on SDK 5 onwards, so we need to provide our own
@@ -64,13 +65,14 @@
       m_fepState(q_check_ptr(new CAknEdwinState)),		// CBase derived object needs check on new
       m_lastImHints(Qt::ImhNone),
       m_textCapabilities(TCoeInputCapabilities::EAllText),
-      m_isEditing(false),
       m_inDestruction(false),
       m_pendingInputCapabilitiesChanged(false),
       m_cursorVisibility(1),
       m_inlinePosition(0),
       m_formatRetriever(0),
-      m_pointerHandler(0)
+      m_pointerHandler(0),
+      m_longPress(0),
+      m_cursorPos(0)
 {
     m_fepState->SetObjectProvider(this);
     m_fepState->SetFlags(EAknEditorFlagDefault);
@@ -78,7 +80,7 @@
     m_fepState->SetPermittedInputModes( EAknEditorAllInputModes );
     m_fepState->SetDefaultCase( EAknEditorLowerCase );
     m_fepState->SetPermittedCases( EAknEditorLowerCase|EAknEditorUpperCase );
-    m_fepState->SetSpecialCharacterTableResourceId( 0 );
+    m_fepState->SetSpecialCharacterTableResourceId(R_AVKON_SPECIAL_CHARACTER_TABLE_DIALOG);
     m_fepState->SetNumericKeymap( EAknEditorStandardNumberModeKeymap );
 }
 
@@ -138,8 +140,7 @@
     // 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);
+        queueInputCapabilitiesChanged();
     }
 }
 
@@ -154,6 +155,44 @@
     }
 }
 
+bool QCoeFepInputContext::needsInputPanel()
+{
+    switch (QSysInfo::s60Version()) {
+    case QSysInfo::SV_S60_3_1:
+    case QSysInfo::SV_S60_3_2:
+        // There are no touch phones for pre-5.0 SDKs.
+        return false;
+#ifdef Q_CC_NOKIAX86
+    default:
+        // For emulator we assume that we need an input panel, since we can't
+        // separate between phone types.
+        return true;
+#else
+    case QSysInfo::SV_S60_5_0: {
+        // For SDK == 5.0, we need phone specific detection, since the HAL API
+        // is no good on most phones. However, all phones at the time of writing use the
+        // input panel, except N97 in landscape mode, but in this mode it refuses to bring
+        // up the panel anyway, so we don't have to care.
+        return true;
+    }
+    default:
+        // For unknown/newer types, we try to use the HAL API.
+        int keyboardEnabled;
+        int keyboardType;
+        int err[2];
+        err[0] = HAL::Get(HAL::EKeyboard, keyboardType);
+        err[1] = HAL::Get(HAL::EKeyboardState, keyboardEnabled);
+        if (err[0] == KErrNone && err[1] == KErrNone
+                && keyboardType != 0 && keyboardEnabled)
+            // Means that we have some sort of keyboard.
+            return false;
+
+        // Fall back to using the input panel.
+        return true;
+#endif // !Q_CC_NOKIAX86
+    }
+}
+
 bool QCoeFepInputContext::filterEvent(const QEvent *event)
 {
     // The CloseSoftwareInputPanel event is not handled here, because the VK will automatically
@@ -164,24 +203,31 @@
 
     if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease) {
         const QKeyEvent *keyEvent = static_cast<const QKeyEvent *>(event);
-        Q_ASSERT(m_lastImHints == focusWidget()->inputMethodHints());
-        if (keyEvent->key() == Qt::Key_F20 && m_lastImHints & Qt::ImhHiddenText) {
-            // Special case in Symbian. On editors with secret text, F20 is for some reason
-            // considered to be a backspace.
-            QKeyEvent modifiedEvent(keyEvent->type(), Qt::Key_Backspace, keyEvent->modifiers(),
-                    keyEvent->text(), keyEvent->isAutoRepeat(), keyEvent->count());
-            QApplication::sendEvent(focusWidget(), &modifiedEvent);
-            return true;
+        switch (keyEvent->key()) {
+        case Qt::Key_F20:
+            Q_ASSERT(m_lastImHints == focusWidget()->inputMethodHints());
+            if (m_lastImHints & Qt::ImhHiddenText) {
+                // Special case in Symbian. On editors with secret text, F20 is for some reason
+                // considered to be a backspace.
+                QKeyEvent modifiedEvent(keyEvent->type(), Qt::Key_Backspace, keyEvent->modifiers(),
+                        keyEvent->text(), keyEvent->isAutoRepeat(), keyEvent->count());
+                QApplication::sendEvent(focusWidget(), &modifiedEvent);
+                return true;
+            }
+            break;
+        case Qt::Key_Select:
+            if (!m_preeditString.isEmpty()) {
+                commitCurrentString(false);
+                return true;
+            }
+            break;
+        default:
+            break;
         }
     }
 
-	// :QTP: Always show virtual keyboard - fix needed to use feature manager
-    // For pre-5.0 SDKs, we don't launch the keyboard.
-	/*
-    if (QSysInfo::s60Version() != QSysInfo::SV_S60_5_0) {
+    if (!needsInputPanel())
         return false;
-    }
-	*/
 
     if (event->type() == QEvent::RequestSoftwareInputPanel) {
         // Notify S60 that we want the virtual keyboard to show up.
@@ -210,7 +256,6 @@
 
 void QCoeFepInputContext::mouseHandler( int x, QMouseEvent *event)
 {
-    Q_ASSERT(m_isEditing);
     Q_ASSERT(focusWidget());
 
     if (event->type() == QEvent::MouseButtonPress && event->button() == Qt::LeftButton) {
@@ -374,6 +419,14 @@
     }
     m_fepState->SetNumericKeymap(static_cast<TAknEditorNumericKeymap>(flags));
 
+    if (hints & ImhEmailCharactersOnly) {
+        m_fepState->SetSpecialCharacterTableResourceId(R_AVKON_EMAIL_ADDR_SPECIAL_CHARACTER_TABLE_DIALOG);
+    } else if (hints & ImhUrlCharactersOnly) {
+        m_fepState->SetSpecialCharacterTableResourceId(R_AVKON_URL_SPECIAL_CHARACTER_TABLE_DIALOG);
+    } else {
+        m_fepState->SetSpecialCharacterTableResourceId(R_AVKON_SPECIAL_CHARACTER_TABLE_DIALOG);
+    }
+
     if (hints & ImhHiddenText) {
         m_textCapabilities = TCoeInputCapabilities::EAllText | TCoeInputCapabilities::ESecretText;
     } else {
@@ -384,6 +437,10 @@
 void QCoeFepInputContext::applyFormat(QList<QInputMethodEvent::Attribute> *attributes)
 {
     TCharFormat cFormat;
+    QColor styleTextColor = QApplication::palette("QLineEdit").text().color();
+    TLogicalRgb tontColor(TRgb(styleTextColor.red(), styleTextColor.green(), styleTextColor.blue(), styleTextColor.alpha()));
+    cFormat.iFontPresentation.iTextColor = tontColor;
+
     TInt numChars = 0;
     TInt charPos = 0;
     int oldSize = attributes->size();
@@ -445,8 +502,8 @@
     if (!w)
         return;
 
-    m_isEditing = true;
-
+    m_cursorPos = w->inputMethodQuery(Qt::ImCursorPosition).toInt();
+    
     QList<QInputMethodEvent::Attribute> attributes;
 
     m_cursorVisibility = aCursorVisibility ? 1 : 0;
@@ -510,8 +567,6 @@
     event.setCommitString(QLatin1String(""), 0, 0);
     m_preeditString.clear();
     sendEvent(event);
-
-    m_isEditing = false;
 }
 
 TInt QCoeFepInputContext::DocumentLengthForFep() const
@@ -565,8 +620,28 @@
 
     int cursor = w->inputMethodQuery(Qt::ImCursorPosition).toInt() + m_preeditString.size();
     int anchor = w->inputMethodQuery(Qt::ImAnchorPosition).toInt() + m_preeditString.size();
-    aCursorSelection.iAnchorPos = anchor;
-    aCursorSelection.iCursorPos = cursor;
+    QString text = w->inputMethodQuery(Qt::ImSurroundingText).value<QString>();
+    int combinedSize = text.size() + m_preeditString.size();
+    if (combinedSize < anchor || combinedSize < cursor) {
+        // ### TODO! FIXME! QTBUG-5050
+        // This is a hack to prevent crashing in 4.6 with QLineEdits that use input masks.
+        // The root problem is that cursor position is relative to displayed text instead of the
+        // actual text we get.
+        //
+        // To properly fix this we would need to know the displayText of QLineEdits instead
+        // of just the text, which on itself should be a trivial change. The difficulties start
+        // when we need to commit the changes back to the QLineEdit, which would have to be somehow
+        // able to handle displayText, too.
+        //
+        // Until properly fixed, the cursor and anchor positions will not reflect correct positions
+        // for masked QLineEdits, unless all the masked positions are filled in order so that
+        // cursor position relative to the displayed text matches position relative to actual text.
+        aCursorSelection.iAnchorPos = combinedSize;
+        aCursorSelection.iCursorPos = combinedSize;
+    } else {
+        aCursorSelection.iAnchorPos = anchor;
+        aCursorSelection.iCursorPos = cursor;
+    }
 }
 
 void QCoeFepInputContext::GetEditorContentForFep(TDes& aEditorContent, TInt aDocumentPosition,
@@ -630,16 +705,22 @@
 void QCoeFepInputContext::commitCurrentString(bool triggeredBySymbian)
 {
     if (m_preeditString.size() == 0) {
+        QWidget *w = focusWidget();
+        if (triggeredBySymbian && w) {
+            // We must replace the last character only if the input box has already accepted one 
+            if (w->inputMethodQuery(Qt::ImCursorPosition).toInt() != m_cursorPos)
+                m_longPress = 1;
+        }
         return;
     }
 
     QList<QInputMethodEvent::Attribute> attributes;
     QInputMethodEvent event(QLatin1String(""), attributes);
-    event.setCommitString(m_preeditString, 0, 0);//m_preeditString.size());
+    event.setCommitString(m_preeditString, 0-m_longPress, m_longPress);
     m_preeditString.clear();
     sendEvent(event);
 
-    m_isEditing = false;
+    m_longPress = 0;
 
     if (!triggeredBySymbian) {
         CCoeFep* fep = CCoeEnv::Static()->Fep();
@@ -680,6 +761,14 @@
     return TTypeUid::Null();
 }
 
+MObjectProvider *QCoeFepInputContext::MopNext()
+{
+    QWidget *w = focusWidget();
+    if (w)
+        return w->effectiveWinId();
+    return 0;
+}
+
 QT_END_NAMESPACE
 
 #endif // QT_NO_IM