src/gui/inputmethod/qcoefepinputcontext_s60.cpp
changeset 37 758a864f9613
parent 33 3e2da88830cd
equal deleted inserted replaced
36:ef0373b55136 37:758a864f9613
    45 #include <qapplication.h>
    45 #include <qapplication.h>
    46 #include <qtextformat.h>
    46 #include <qtextformat.h>
    47 #include <qgraphicsview.h>
    47 #include <qgraphicsview.h>
    48 #include <qgraphicsscene.h>
    48 #include <qgraphicsscene.h>
    49 #include <qgraphicswidget.h>
    49 #include <qgraphicswidget.h>
       
    50 #include <qsymbianevent.h>
    50 #include <private/qcore_symbian_p.h>
    51 #include <private/qcore_symbian_p.h>
    51 
    52 
    52 #include <fepitfr.h>
    53 #include <fepitfr.h>
    53 #include <hal.h>
    54 #include <hal.h>
    54 
    55 
    77       m_pendingInputCapabilitiesChanged(false),
    78       m_pendingInputCapabilitiesChanged(false),
    78       m_cursorVisibility(1),
    79       m_cursorVisibility(1),
    79       m_inlinePosition(0),
    80       m_inlinePosition(0),
    80       m_formatRetriever(0),
    81       m_formatRetriever(0),
    81       m_pointerHandler(0),
    82       m_pointerHandler(0),
    82       m_cursorPos(0),
       
    83       m_hasTempPreeditString(false)
    83       m_hasTempPreeditString(false)
    84 {
    84 {
    85     m_fepState->SetObjectProvider(this);
    85     m_fepState->SetObjectProvider(this);
    86     if (QSysInfo::s60Version() > QSysInfo::SV_S60_5_0)
    86     if (QSysInfo::s60Version() > QSysInfo::SV_S60_5_0)
    87         m_fepState->SetFlags(EAknEditorFlagDefault | QT_EAknEditorFlagSelectionVisible);
    87         m_fepState->SetFlags(EAknEditorFlagDefault | QT_EAknEditorFlagSelectionVisible);
   235             break;
   235             break;
   236         default:
   236         default:
   237             break;
   237             break;
   238         }
   238         }
   239 
   239 
       
   240         QString widgetText = focusWidget()->inputMethodQuery(Qt::ImSurroundingText).toString();
       
   241         int maxLength = focusWidget()->inputMethodQuery(Qt::ImMaximumTextLength).toInt();
       
   242         if (!keyEvent->text().isEmpty() && widgetText.size() + m_preeditString.size() >= maxLength) {
       
   243             // Don't send key events with string content if the widget is "full".
       
   244             return true;
       
   245         }
       
   246 
   240         if (keyEvent->type() == QEvent::KeyPress
   247         if (keyEvent->type() == QEvent::KeyPress
   241             && focusWidget()->inputMethodHints() & Qt::ImhHiddenText
   248             && focusWidget()->inputMethodHints() & Qt::ImhHiddenText
   242             && !keyEvent->text().isEmpty()) {
   249             && !keyEvent->text().isEmpty()) {
   243             // Send some temporary preedit text in order to make text visible for a moment.
   250             // Send some temporary preedit text in order to make text visible for a moment.
   244             m_cursorPos = focusWidget()->inputMethodQuery(Qt::ImCursorPosition).toInt();
       
   245             m_preeditString = keyEvent->text();
   251             m_preeditString = keyEvent->text();
   246             QList<QInputMethodEvent::Attribute> attributes;
   252             QList<QInputMethodEvent::Attribute> attributes;
   247             QInputMethodEvent imEvent(m_preeditString, attributes);
   253             QInputMethodEvent imEvent(m_preeditString, attributes);
   248             sendEvent(imEvent);
   254             sendEvent(imEvent);
   249             m_tempPreeditStringTimeout.start(1000, this);
   255             m_tempPreeditStringTimeout.start(1000, this);
   280     }
   286     }
   281 
   287 
   282     return false;
   288     return false;
   283 }
   289 }
   284 
   290 
       
   291 bool QCoeFepInputContext::symbianFilterEvent(QWidget *keyWidget, const QSymbianEvent *event)
       
   292 {
       
   293     Q_UNUSED(keyWidget);
       
   294     if (event->type() == QSymbianEvent::CommandEvent)
       
   295         // A command basically means the same as a button being pushed. With Qt buttons
       
   296         // that would normally result in a reset of the input method due to the focus change.
       
   297         // This should also happen for commands.
       
   298         reset();
       
   299 
       
   300     return false;
       
   301 }
       
   302 
   285 void QCoeFepInputContext::timerEvent(QTimerEvent *timerEvent)
   303 void QCoeFepInputContext::timerEvent(QTimerEvent *timerEvent)
   286 {
   304 {
   287     if (timerEvent->timerId() == m_tempPreeditStringTimeout.timerId())
   305     if (timerEvent->timerId() == m_tempPreeditStringTimeout.timerId())
   288         commitTemporaryPreeditString();
   306         commitTemporaryPreeditString();
   289 }
   307 }
   295 
   313 
   296     if (!m_hasTempPreeditString)
   314     if (!m_hasTempPreeditString)
   297         return;
   315         return;
   298 
   316 
   299     commitCurrentString(false);
   317     commitCurrentString(false);
   300 
       
   301     //update cursor position, now this pre-edit text has been committed.
       
   302     //this prevents next keypress overwriting it (QTBUG-11673)
       
   303     m_cursorPos = focusWidget()->inputMethodQuery(Qt::ImCursorPosition).toInt();
       
   304 }
   318 }
   305 
   319 
   306 void QCoeFepInputContext::mouseHandler( int x, QMouseEvent *event)
   320 void QCoeFepInputContext::mouseHandler( int x, QMouseEvent *event)
   307 {
   321 {
   308     Q_ASSERT(focusWidget());
   322     Q_ASSERT(focusWidget());
   362 {
   376 {
   363     using namespace Qt;
   377     using namespace Qt;
   364 
   378 
   365     commitTemporaryPreeditString();
   379     commitTemporaryPreeditString();
   366 
   380 
   367     bool numbersOnly = hints & ImhDigitsOnly || hints & ImhFormattedNumbersOnly
   381     const bool anynumbermodes = hints & (ImhDigitsOnly | ImhFormattedNumbersOnly | ImhDialableCharactersOnly);
   368             || hints & ImhDialableCharactersOnly;
   382     const bool anytextmodes = hints & (ImhUppercaseOnly | ImhLowercaseOnly | ImhEmailCharactersOnly | ImhUrlCharactersOnly);
   369     bool noOnlys = !(numbersOnly || hints & ImhUppercaseOnly
   383     const bool numbersOnly = anynumbermodes && !anytextmodes;
   370             || hints & ImhLowercaseOnly);
   384     const bool noOnlys = !(hints & ImhExclusiveInputMask);
   371     TInt flags;
   385     TInt flags;
   372     Qt::InputMethodHints oldHints = hints;
   386     Qt::InputMethodHints oldHints = hints;
   373 
   387 
   374     // Some sanity checking. Make sure that only one preference is set.
   388     // Some sanity checking. Make sure that only one preference is set.
   375     InputMethodHints prefs = ImhPreferNumbers | ImhPreferUppercase | ImhPreferLowercase;
   389     InputMethodHints prefs = ImhPreferNumbers | ImhPreferUppercase | ImhPreferLowercase;
   377     if (prefs != ImhPreferNumbers && prefs != ImhPreferUppercase && prefs != ImhPreferLowercase) {
   391     if (prefs != ImhPreferNumbers && prefs != ImhPreferUppercase && prefs != ImhPreferLowercase) {
   378         hints &= ~prefs;
   392         hints &= ~prefs;
   379     }
   393     }
   380     if (!noOnlys) {
   394     if (!noOnlys) {
   381         // Make sure that the preference is within the permitted set.
   395         // Make sure that the preference is within the permitted set.
   382         if (hints & ImhPreferNumbers && !(hints & ImhDigitsOnly || hints & ImhFormattedNumbersOnly
   396         if (hints & ImhPreferNumbers && !anynumbermodes) {
   383                 || hints & ImhDialableCharactersOnly)) {
       
   384             hints &= ~ImhPreferNumbers;
   397             hints &= ~ImhPreferNumbers;
   385         } else if (hints & ImhPreferUppercase && !(hints & ImhUppercaseOnly)) {
   398         } else if (hints & ImhPreferUppercase && !(hints & ImhUppercaseOnly)) {
   386             hints &= ~ImhPreferUppercase;
   399             hints &= ~ImhPreferUppercase;
   387         } else if (hints & ImhPreferLowercase && !(hints & ImhLowercaseOnly)) {
   400         } else if (hints & ImhPreferLowercase && !(hints & ImhLowercaseOnly)) {
   388             hints &= ~ImhPreferLowercase;
   401             hints &= ~ImhPreferLowercase;
   391         if (!(hints & ImhPreferNumbers || hints & ImhPreferUppercase || hints & ImhPreferLowercase)) {
   404         if (!(hints & ImhPreferNumbers || hints & ImhPreferUppercase || hints & ImhPreferLowercase)) {
   392             if (hints & ImhLowercaseOnly) {
   405             if (hints & ImhLowercaseOnly) {
   393                 hints |= ImhPreferLowercase;
   406                 hints |= ImhPreferLowercase;
   394             } else if (hints & ImhUppercaseOnly) {
   407             } else if (hints & ImhUppercaseOnly) {
   395                 hints |= ImhPreferUppercase;
   408                 hints |= ImhPreferUppercase;
   396             } else if (hints & ImhDigitsOnly || hints & ImhFormattedNumbersOnly
   409             } else if (numbersOnly) {
   397                     || hints & ImhDialableCharactersOnly) {
       
   398                 hints |= ImhPreferNumbers;
   410                 hints |= ImhPreferNumbers;
   399             }
   411             }
   400         }
   412         }
   401     }
   413     }
   402 
   414 
   406     } else {
   418     } else {
   407         m_fepState->SetDefaultInputMode(EAknEditorTextInputMode);
   419         m_fepState->SetDefaultInputMode(EAknEditorTextInputMode);
   408         m_fepState->SetCurrentInputMode(EAknEditorTextInputMode);
   420         m_fepState->SetCurrentInputMode(EAknEditorTextInputMode);
   409     }
   421     }
   410     flags = 0;
   422     flags = 0;
   411     if (numbersOnly) {
   423     if (noOnlys || (anynumbermodes && anytextmodes)) {
       
   424         flags = EAknEditorAllInputModes;
       
   425     }
       
   426     else if (anynumbermodes) {
   412         flags |= EAknEditorNumericInputMode;
   427         flags |= EAknEditorNumericInputMode;
   413     }
   428         if (QSysInfo::s60Version() > QSysInfo::SV_S60_5_0
   414     if (hints & ImhUppercaseOnly || hints & ImhLowercaseOnly) {
   429             && ((hints & ImhFormattedNumbersOnly) || (hints & ImhDialableCharactersOnly))) {
       
   430             //workaround - the * key does not launch the symbols menu, making it impossible to use these modes unless text mode is enabled.
       
   431             flags |= EAknEditorTextInputMode;
       
   432         }
       
   433     }
       
   434     else if (anytextmodes) {
   415         flags |= EAknEditorTextInputMode;
   435         flags |= EAknEditorTextInputMode;
   416     }
   436     }
   417     if (flags == 0) {
   437     else {
   418         flags = EAknEditorAllInputModes;
   438         flags = EAknEditorAllInputModes;
   419     }
   439     }
   420     m_fepState->SetPermittedInputModes(flags);
   440     m_fepState->SetPermittedInputModes(flags);
   421     ReportAknEdStateEvent(MAknEdStateObserver::EAknEdwinStateInputModeUpdate);
   441     ReportAknEdStateEvent(MAknEdStateObserver::EAknEdwinStateInputModeUpdate);
   422 
   442 
   459     }
   479     }
   460     // Using T9 and hidden text together may actually crash the FEP, so check for hidden text too.
   480     // Using T9 and hidden text together may actually crash the FEP, so check for hidden text too.
   461     if (hints & ImhNoPredictiveText || hints & ImhHiddenText) {
   481     if (hints & ImhNoPredictiveText || hints & ImhHiddenText) {
   462         flags |= EAknEditorFlagNoT9;
   482         flags |= EAknEditorFlagNoT9;
   463     }
   483     }
       
   484     // if alphanumeric input, or if multiple incompatible number modes are selected;
       
   485     // then make all symbols available in numeric mode too.
       
   486     if (!numbersOnly || ((hints & ImhFormattedNumbersOnly) && (hints & ImhDialableCharactersOnly)))
       
   487         flags |= EAknEditorFlagUseSCTNumericCharmap;
   464     m_fepState->SetFlags(flags);
   488     m_fepState->SetFlags(flags);
   465     ReportAknEdStateEvent(MAknEdStateObserver::EAknEdwinStateFlagsUpdate);
   489     ReportAknEdStateEvent(MAknEdStateObserver::EAknEdwinStateFlagsUpdate);
   466 
   490 
   467     if (hints & ImhFormattedNumbersOnly) {
   491     if (hints & ImhDialableCharactersOnly) {
       
   492         // This is first, because if (ImhDialableCharactersOnly | ImhFormattedNumbersOnly)
       
   493         // is specified, this one is more natural (# key enters a #)
       
   494         flags = EAknEditorStandardNumberModeKeymap;
       
   495     } else if (hints & ImhFormattedNumbersOnly) {
       
   496         // # key enters decimal point
   468         flags = EAknEditorCalculatorNumberModeKeymap;
   497         flags = EAknEditorCalculatorNumberModeKeymap;
   469     } else if (hints & ImhDigitsOnly) {
   498     } else if (hints & ImhDigitsOnly) {
       
   499         // This is last, because it is most restrictive (# key is inactive)
   470         flags = EAknEditorPlainNumberModeKeymap;
   500         flags = EAknEditorPlainNumberModeKeymap;
   471     } else {
   501     } else {
   472         // ImhDialableCharactersOnly is the fallback as well, so we don't need to check for
       
   473         // that flag.
       
   474         flags = EAknEditorStandardNumberModeKeymap;
   502         flags = EAknEditorStandardNumberModeKeymap;
   475     }
   503     }
   476     m_fepState->SetNumericKeymap(static_cast<TAknEditorNumericKeymap>(flags));
   504     m_fepState->SetNumericKeymap(static_cast<TAknEditorNumericKeymap>(flags));
   477 
   505 
   478     if (hints & ImhEmailCharactersOnly) {
   506     if (hints & ImhUrlCharactersOnly) {
       
   507         // URL characters is everything except space, so a superset of the other restrictions
       
   508         m_fepState->SetSpecialCharacterTableResourceId(R_AVKON_URL_SPECIAL_CHARACTER_TABLE_DIALOG);
       
   509     } else if (hints & ImhEmailCharactersOnly) {
   479         m_fepState->SetSpecialCharacterTableResourceId(R_AVKON_EMAIL_ADDR_SPECIAL_CHARACTER_TABLE_DIALOG);
   510         m_fepState->SetSpecialCharacterTableResourceId(R_AVKON_EMAIL_ADDR_SPECIAL_CHARACTER_TABLE_DIALOG);
   480     } else if (hints & ImhUrlCharactersOnly) {
       
   481         m_fepState->SetSpecialCharacterTableResourceId(R_AVKON_URL_SPECIAL_CHARACTER_TABLE_DIALOG);
       
   482     } else {
   511     } else {
   483         m_fepState->SetSpecialCharacterTableResourceId(R_AVKON_SPECIAL_CHARACTER_TABLE_DIALOG);
   512         m_fepState->SetSpecialCharacterTableResourceId(R_AVKON_SPECIAL_CHARACTER_TABLE_DIALOG);
   484     }
   513     }
   485 
   514 
   486     if (hints & ImhHiddenText) {
   515     if (hints & ImhHiddenText) {
   579     if (!w)
   608     if (!w)
   580         return;
   609         return;
   581 
   610 
   582     commitTemporaryPreeditString();
   611     commitTemporaryPreeditString();
   583 
   612 
   584     m_cursorPos = w->inputMethodQuery(Qt::ImCursorPosition).toInt();
       
   585     
       
   586     QList<QInputMethodEvent::Attribute> attributes;
   613     QList<QInputMethodEvent::Attribute> attributes;
   587 
   614 
   588     m_cursorVisibility = aCursorVisibility ? 1 : 0;
   615     m_cursorVisibility = aCursorVisibility ? 1 : 0;
   589     m_inlinePosition = aPositionOfInsertionPointInInlineText;
   616     m_inlinePosition = aPositionOfInsertionPointInInlineText;
   590     m_preeditString = qt_TDesC2QString(aInitialInlineText);
   617     m_preeditString = qt_TDesC2QString(aInitialInlineText);
   595     // With T9 aInitialInlineText is typically empty when StartFepInlineEditL is called,
   622     // With T9 aInitialInlineText is typically empty when StartFepInlineEditL is called,
   596     // but FEP requires that selected text is always removed at StartFepInlineEditL.
   623     // but FEP requires that selected text is always removed at StartFepInlineEditL.
   597     // Let's remove the selected text if aInitialInlineText is empty and there is selected text
   624     // Let's remove the selected text if aInitialInlineText is empty and there is selected text
   598     if (m_preeditString.isEmpty()) {
   625     if (m_preeditString.isEmpty()) {
   599         int anchor = w->inputMethodQuery(Qt::ImAnchorPosition).toInt();
   626         int anchor = w->inputMethodQuery(Qt::ImAnchorPosition).toInt();
   600         int replacementLength = qAbs(m_cursorPos-anchor);
   627         int cursorPos = w->inputMethodQuery(Qt::ImCursorPosition).toInt();
       
   628         int replacementLength = qAbs(cursorPos-anchor);
   601         if (replacementLength > 0) {
   629         if (replacementLength > 0) {
   602             int replacementStart = m_cursorPos < anchor ? 0 : -replacementLength;
   630             int replacementStart = cursorPos < anchor ? 0 : -replacementLength;
   603             QList<QInputMethodEvent::Attribute> clearSelectionAttributes;
   631             QList<QInputMethodEvent::Attribute> clearSelectionAttributes;
   604             QInputMethodEvent clearSelectionEvent(QLatin1String(""), clearSelectionAttributes);
   632             QInputMethodEvent clearSelectionEvent(QLatin1String(""), clearSelectionAttributes);
   605             clearSelectionEvent.setCommitString(QLatin1String(""), replacementStart, replacementLength);
   633             clearSelectionEvent.setCommitString(QLatin1String(""), replacementStart, replacementLength);
   606             sendEvent(clearSelectionEvent);
   634             sendEvent(clearSelectionEvent);
   607         }
   635         }
   630     applyFormat(&attributes);
   658     applyFormat(&attributes);
   631     attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Cursor,
   659     attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Cursor,
   632                                                    m_inlinePosition,
   660                                                    m_inlinePosition,
   633                                                    m_cursorVisibility,
   661                                                    m_cursorVisibility,
   634                                                    QVariant()));
   662                                                    QVariant()));
   635     m_preeditString = qt_TDesC2QString(aNewInlineText);
   663     QString newPreeditString = qt_TDesC2QString(aNewInlineText);
   636     QInputMethodEvent event(m_preeditString, attributes);
   664     QInputMethodEvent event(newPreeditString, attributes);
       
   665     if (newPreeditString.isEmpty() && m_preeditString.isEmpty()) {
       
   666         // In Symbian world this means "erase last character".
       
   667         event.setCommitString(QLatin1String(""), -1, 1);
       
   668     }
       
   669     m_preeditString = newPreeditString;
   637     sendEvent(event);
   670     sendEvent(event);
   638 }
   671 }
   639 
   672 
   640 void QCoeFepInputContext::SetInlineEditingCursorVisibilityL(TBool aCursorVisibility)
   673 void QCoeFepInputContext::SetInlineEditingCursorVisibilityL(TBool aCursorVisibility)
   641 {
   674 {
   801 
   834 
   802 }
   835 }
   803 
   836 
   804 void QCoeFepInputContext::commitCurrentString(bool cancelFepTransaction)
   837 void QCoeFepInputContext::commitCurrentString(bool cancelFepTransaction)
   805 {
   838 {
   806     int longPress = 0;
       
   807 
       
   808     if (m_preeditString.size() == 0) {
       
   809         QWidget *w = focusWidget();
       
   810         if (!cancelFepTransaction && w) {
       
   811             // We must replace the last character only if the input box has already accepted one 
       
   812             if (w->inputMethodQuery(Qt::ImCursorPosition).toInt() != m_cursorPos)
       
   813                 longPress = 1;
       
   814         }
       
   815     }
       
   816 
       
   817     QList<QInputMethodEvent::Attribute> attributes;
   839     QList<QInputMethodEvent::Attribute> attributes;
   818     QInputMethodEvent event(QLatin1String(""), attributes);
   840     QInputMethodEvent event(QLatin1String(""), attributes);
   819     event.setCommitString(m_preeditString, 0-longPress, longPress);
   841     event.setCommitString(m_preeditString, 0, 0);
   820     m_preeditString.clear();
   842     m_preeditString.clear();
   821     sendEvent(event);
   843     sendEvent(event);
   822 
   844 
   823     m_hasTempPreeditString = false;
   845     m_hasTempPreeditString = false;
   824     longPress = 0;
       
   825 
   846 
   826     if (cancelFepTransaction) {
   847     if (cancelFepTransaction) {
   827         CCoeFep* fep = CCoeEnv::Static()->Fep();
   848         CCoeFep* fep = CCoeEnv::Static()->Fep();
   828         if (fep)
   849         if (fep)
   829             fep->CancelTransaction();
   850             fep->CancelTransaction();