diff options
author | Gunnar Sletta <gunnar.sletta@nokia.com> | 2011-09-13 08:54:45 +0200 |
---|---|---|
committer | Gunnar Sletta <gunnar.sletta@nokia.com> | 2011-09-13 08:54:45 +0200 |
commit | b62bd0584a7872b6917917009b707785b3abd077 (patch) | |
tree | 9981f274712c098cabbff0c4667672a3934e5393 /src/gui/inputmethod/qcoefepinputcontext_s60.cpp | |
parent | 5e10745dca1d10025404a9f268f03ae697fb10cc (diff) | |
parent | 97baad65f65783d2b5ff938f6217aec9434f2e5f (diff) |
Merge branch 'refactor'
Conflicts:
mkspecs/qws/linux-lsb-g++/qmake.conf
src/gui/image/qpixmap_mac.cpp
src/gui/painting/qpaintengine_x11.cpp
src/gui/painting/qtessellator.cpp
src/gui/text/qfontengine_qws.cpp
src/gui/text/qfontengine_x11.cpp
src/gui/widgets/qlinecontrol.cpp
src/opengl/qgl.h
src/opengl/qgl_x11egl.cpp
src/plugins/plugins.pro
Change-Id: If52dcd55cd55f2983a756c2f843967702b60a310
Diffstat (limited to 'src/gui/inputmethod/qcoefepinputcontext_s60.cpp')
-rw-r--r-- | src/gui/inputmethod/qcoefepinputcontext_s60.cpp | 1200 |
1 files changed, 0 insertions, 1200 deletions
diff --git a/src/gui/inputmethod/qcoefepinputcontext_s60.cpp b/src/gui/inputmethod/qcoefepinputcontext_s60.cpp deleted file mode 100644 index a00aa50bd3..0000000000 --- a/src/gui/inputmethod/qcoefepinputcontext_s60.cpp +++ /dev/null @@ -1,1200 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** This file may be used under the terms of the GNU Lesser General Public -** License version 2.1 as published by the Free Software Foundation and -** appearing in the file LICENSE.LGPL included in the packaging of this -** file. Please review the following information to ensure the GNU Lesser -** General Public License version 2.1 requirements will be met: -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU General -** Public License version 3.0 as published by the Free Software Foundation -** and appearing in the file LICENSE.GPL included in the packaging of this -** file. Please review the following information to ensure the GNU General -** Public License version 3.0 requirements will be met: -** http://www.gnu.org/copyleft/gpl.html. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QT_NO_IM - -#include "qcoefepinputcontext_p.h" -#include <qapplication.h> -#include <qtextformat.h> -#include <qgraphicsview.h> -#include <qgraphicsscene.h> -#include <qgraphicswidget.h> -#include <qsymbianevent.h> -#include <qlayout.h> -#include <qdesktopwidget.h> -#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 -// 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) - -// EAknEditorFlagSelectionVisible is only valid from 3.2 onwards. -// Sym^3 AVKON FEP manager expects that this flag is used for FEP-aware editors -// that support text selection. -#define QT_EAknEditorFlagSelectionVisible 0x100000 - -// EAknEditorFlagEnablePartialScreen is only valid from Sym^3 onwards. -#define QT_EAknEditorFlagEnablePartialScreen 0x200000 - -QT_BEGIN_NAMESPACE - -Q_GUI_EXPORT void qt_s60_setPartialScreenInputMode(bool enable) -{ - S60->partial_keyboard = enable; - - QInputContext *ic = 0; - if (QApplication::focusWidget()) { - ic = QApplication::focusWidget()->inputContext(); - } else if (qApp && qApp->inputContext()) { - ic = qApp->inputContext(); - } - if (ic) - ic->update(); -} - -QCoeFepInputContext::QCoeFepInputContext(QObject *parent) - : QInputContext(parent), - m_fepState(q_check_ptr(new CAknEdwinState)), // CBase derived object needs check on new - m_lastImHints(Qt::ImhNone), - m_textCapabilities(TCoeInputCapabilities::EAllText), - m_inDestruction(false), - m_pendingInputCapabilitiesChanged(false), - m_cursorVisibility(1), - m_inlinePosition(0), - m_formatRetriever(0), - m_pointerHandler(0), - m_hasTempPreeditString(false), - m_splitViewResizeBy(0), - m_splitViewPreviousWindowStates(Qt::WindowNoState) -{ - m_fepState->SetObjectProvider(this); - int defaultFlags = EAknEditorFlagDefault; - if (QSysInfo::s60Version() > QSysInfo::SV_S60_5_0) { - if (S60->partial_keyboard) { - defaultFlags |= QT_EAknEditorFlagEnablePartialScreen; - } - defaultFlags |= QT_EAknEditorFlagSelectionVisible; - } - m_fepState->SetFlags(defaultFlags); - m_fepState->SetDefaultInputMode( EAknEditorTextInputMode ); - m_fepState->SetPermittedInputModes( EAknEditorAllInputModes ); - m_fepState->SetDefaultCase( EAknEditorTextCase ); - m_fepState->SetPermittedCases( EAknEditorAllCaseModes ); - m_fepState->SetSpecialCharacterTableResourceId(R_AVKON_SPECIAL_CHARACTER_TABLE_DIALOG); - m_fepState->SetNumericKeymap(EAknEditorAlphanumericNumberModeKeymap); -} - -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 below is essentially equivalent to InputCapabilitiesChanged(), - // but is synchronous, rather than asynchronous. - CCoeEnv::Static()->SyncNotifyFocusObserversOfChangeInFocus(); - - if (m_fepState) - delete m_fepState; -} - -void QCoeFepInputContext::reset() -{ - commitCurrentString(true); -} - -void QCoeFepInputContext::ReportAknEdStateEvent(MAknEdStateObserver::EAknEdwinStateEvent aEventType) -{ - QT_TRAP_THROWING(m_fepState->ReportAknEdStateEventL(aEventType)); -} - -void QCoeFepInputContext::update() -{ - updateHints(false); - - // For pre-5.0 SDKs, we don't do text updates on S60 side. - 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. - ReportAknEdStateEvent(QT_EAknCursorPositionChanged); -} - -void QCoeFepInputContext::setFocusWidget(QWidget *w) -{ - commitCurrentString(true); - - QInputContext::setFocusWidget(w); - - updateHints(true); -} - -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()) { - queueInputCapabilitiesChanged(); - } -} - -QString QCoeFepInputContext::language() -{ - TLanguage lang = m_fepState->LocalLanguage(); - const QByteArray localeName = qt_symbianLocaleName(lang); - if (!localeName.isEmpty()) { - return QString::fromLatin1(localeName); - } else { - return QString::fromLatin1("C"); - } -} - -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 - // close when it discovers that the underlying widget does not have input capabilities. - - if (!focusWidget()) - return false; - - switch (event->type()) { - case QEvent::MouseButtonPress: - // Alphanumeric keypad doesn't like it when we click and text is still getting displayed - // It ignores the mouse event, so we need to commit and send a selection event (which will get triggered - // after the commit) - if (!m_preeditString.isEmpty()) { - commitCurrentString(true); - - int pos = focusWidget()->inputMethodQuery(Qt::ImCursorPosition).toInt(); - - QList<QInputMethodEvent::Attribute> selectAttributes; - selectAttributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, pos, 0, QVariant()); - QInputMethodEvent selectEvent(QLatin1String(""), selectAttributes); - sendEvent(selectEvent); - } - break; - case QEvent::KeyPress: - commitTemporaryPreeditString(); - // fall through intended - case QEvent::KeyRelease: - const QKeyEvent *keyEvent = static_cast<const QKeyEvent *>(event); - //If proxy exists, always use hints from proxy. - QWidget *proxy = focusWidget()->focusProxy(); - Qt::InputMethodHints currentHints = proxy ? proxy->inputMethodHints() : focusWidget()->inputMethodHints(); - - switch (keyEvent->key()) { - case Qt::Key_F20: - Q_ASSERT(m_lastImHints == currentHints); - 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(true); - return true; - } - break; - default: - break; - } - - QString widgetText = focusWidget()->inputMethodQuery(Qt::ImSurroundingText).toString(); - bool validLength; - int maxLength = focusWidget()->inputMethodQuery(Qt::ImMaximumTextLength).toInt(&validLength); - if (!keyEvent->text().isEmpty() && validLength - && widgetText.size() + m_preeditString.size() >= maxLength) { - // Don't send key events with string content if the widget is "full". - return true; - } - - if (keyEvent->type() == QEvent::KeyPress - && currentHints & Qt::ImhHiddenText - && !keyEvent->text().isEmpty()) { - // Send some temporary preedit text in order to make text visible for a moment. - m_preeditString = keyEvent->text(); - QList<QInputMethodEvent::Attribute> attributes; - QInputMethodEvent imEvent(m_preeditString, attributes); - sendEvent(imEvent); - m_tempPreeditStringTimeout.start(1000, this); - m_hasTempPreeditString = true; - update(); - return true; - } - break; - } - - if (!needsInputPanel()) - return false; - - if (event->type() == QEvent::RequestSoftwareInputPanel) { - // Notify S60 that we want the virtual keyboard to show up. - QSymbianControl *sControl; - sControl = focusWidget()->effectiveWinId()->MopGetObject(sControl); - Q_ASSERT(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); - } - - ensureInputCapabilitiesChanged(); - m_fepState->ReportAknEdStateEventL(MAknEdStateObserver::QT_EAknActivatePenInputRequest); - - if (sControl) { - sControl->setIgnoreFocusChanged(false); - } - return true; - } - - return false; -} - -bool QCoeFepInputContext::symbianFilterEvent(QWidget *keyWidget, const QSymbianEvent *event) -{ - Q_UNUSED(keyWidget); - if (event->type() == QSymbianEvent::CommandEvent) - // A command basically means the same as a button being pushed. With Qt buttons - // that would normally result in a reset of the input method due to the focus change. - // This should also happen for commands. - reset(); - - if (event->type() == QSymbianEvent::WindowServerEvent - && event->windowServerEvent() - && event->windowServerEvent()->Type() == EEventWindowVisibilityChanged - && S60->splitViewLastWidget) { - - QGraphicsView *gv = qobject_cast<QGraphicsView*>(S60->splitViewLastWidget); - const bool alwaysResize = (gv && gv->verticalScrollBarPolicy() != Qt::ScrollBarAlwaysOff); - - if (alwaysResize) { - TUint visibleFlags = event->windowServerEvent()->VisibilityChanged()->iFlags; - if (visibleFlags & TWsVisibilityChangedEvent::EPartiallyVisible) - ensureFocusWidgetVisible(S60->splitViewLastWidget); - if (visibleFlags & TWsVisibilityChangedEvent::ENotVisible) - resetSplitViewWidget(true); - } - } - - return false; -} - -void QCoeFepInputContext::timerEvent(QTimerEvent *timerEvent) -{ - if (timerEvent->timerId() == m_tempPreeditStringTimeout.timerId()) - commitTemporaryPreeditString(); -} - -void QCoeFepInputContext::commitTemporaryPreeditString() -{ - if (m_tempPreeditStringTimeout.isActive()) - m_tempPreeditStringTimeout.stop(); - - if (!m_hasTempPreeditString) - return; - - commitCurrentString(false); -} - -void QCoeFepInputContext::mouseHandler( int x, QMouseEvent *event) -{ - Q_ASSERT(focusWidget()); - - if (event->type() == QEvent::MouseButtonPress && event->button() == Qt::LeftButton) { - commitCurrentString(true); - int pos = focusWidget()->inputMethodQuery(Qt::ImCursorPosition).toInt(); - - QList<QInputMethodEvent::Attribute> attributes; - attributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, pos + x, 0, QVariant()); - QInputMethodEvent event(QLatin1String(""), attributes); - sendEvent(event); - } -} - -TCoeInputCapabilities QCoeFepInputContext::inputCapabilities() -{ - if (m_inDestruction || !focusWidget()) { - return TCoeInputCapabilities(TCoeInputCapabilities::ENone, 0, 0); - } - - return TCoeInputCapabilities(m_textCapabilities, this, 0); -} - -void QCoeFepInputContext::resetSplitViewWidget(bool keepInputWidget) -{ - QGraphicsView *gv = qobject_cast<QGraphicsView*>(S60->splitViewLastWidget); - - if (!gv) { - return; - } - - QSymbianControl *symControl = static_cast<QSymbianControl*>(gv->effectiveWinId()); - symControl->CancelLongTapTimer(); - - const bool alwaysResize = (gv->verticalScrollBarPolicy() != Qt::ScrollBarAlwaysOff); - QWidget *windowToMove = gv->window(); - - bool userResize = gv->testAttribute(Qt::WA_Resized); - - windowToMove->setUpdatesEnabled(false); - - if (!alwaysResize) { - if (gv->scene()) { - if (gv->scene()->focusItem()) { - // Check if the widget contains cursorPositionChanged signal and disconnect from it. - QByteArray signal = QMetaObject::normalizedSignature(SIGNAL(cursorPositionChanged())); - int index = gv->scene()->focusItem()->toGraphicsObject()->metaObject()->indexOfSignal(signal.right(signal.length() - 1)); - if (index != -1) - disconnect(gv->scene()->focusItem()->toGraphicsObject(), SIGNAL(cursorPositionChanged()), this, SLOT(translateInputWidget())); - } - - QGraphicsItem *rootItem = 0; - foreach (QGraphicsItem *item, gv->scene()->items()) { - if (!item->parentItem()) { - rootItem = item; - break; - } - } - if (rootItem) - rootItem->resetTransform(); - } - } else { - if (m_splitViewResizeBy) - gv->resize(gv->rect().width(), m_splitViewResizeBy); - } - // Resizing might have led to widget losing its original windowstate. - // Restore previous window state. - - if (m_splitViewPreviousWindowStates != windowToMove->windowState()) - windowToMove->setWindowState(m_splitViewPreviousWindowStates); - - windowToMove->setUpdatesEnabled(true); - - gv->setAttribute(Qt::WA_Resized, userResize); //not a user resize - - m_splitViewResizeBy = 0; - if (!keepInputWidget) { - m_splitViewPreviousWindowStates = Qt::WindowNoState; - S60->splitViewLastWidget = 0; - } -} - -// Checks if a given widget is visible in the splitview rect. The offset -// parameter can be used to validate if moving widget upwards or downwards -// by the offset would make a difference for the visibility. - -bool QCoeFepInputContext::isWidgetVisible(QWidget *widget, int offset) -{ - bool visible = false; - if (widget) { - QRect splitViewRect = qt_TRect2QRect(static_cast<CEikAppUi*>(S60->appUi())->ClientRect()); - QWidget *window = QApplication::activeWindow(); - QGraphicsView *gv = qobject_cast<QGraphicsView*>(widget); - if (gv && window) { - if (QGraphicsScene *scene = gv->scene()) { - if (QGraphicsItem *focusItem = scene->focusItem()) { - QPoint cursorPos = window->mapToGlobal(focusItem->cursor().pos()); - cursorPos.setY(cursorPos.y() + offset); - if (splitViewRect.contains(cursorPos)) { - visible = true; - } - } - } - } - } - return visible; -} - -// Ensure that the input widget is visible in the splitview rect. - -void QCoeFepInputContext::ensureFocusWidgetVisible(QWidget *widget) -{ - // Native side opening and closing its virtual keyboard when it changes the keyboard layout, - // has an adverse impact on long tap timer. Cancel the timer when splitview opens to avoid this. - QSymbianControl *symControl = static_cast<QSymbianControl*>(widget->effectiveWinId()); - symControl->CancelLongTapTimer(); - - // Graphicsviews that have vertical scrollbars should always be resized to the splitview area. - // Graphicsviews without scrollbars should be translated. - - QGraphicsView *gv = qobject_cast<QGraphicsView*>(widget); - if (!gv) - return; - - const bool alwaysResize = (gv && gv->verticalScrollBarPolicy() != Qt::ScrollBarAlwaysOff); - const bool moveWithinVisibleArea = (S60->splitViewLastWidget != 0); - - QWidget *windowToMove = gv ? gv : symControl->widget(); - if (!windowToMove->isWindow()) - windowToMove = windowToMove->window(); - if (!windowToMove) { - return; - } - - // When opening the keyboard (not moving within the splitview area), save the original - // window state. In some cases, ensuring input widget visibility might lead to window - // states getting changed. - - if (!moveWithinVisibleArea) { - // Check if the widget contains cursorPositionChanged signal and connect to it. - QByteArray signal = QMetaObject::normalizedSignature(SIGNAL(cursorPositionChanged())); - if (gv->scene() && gv->scene()->focusItem()) { - int index = gv->scene()->focusItem()->toGraphicsObject()->metaObject()->indexOfSignal(signal.right(signal.length() - 1)); - if (index != -1) - connect(gv->scene()->focusItem()->toGraphicsObject(), SIGNAL(cursorPositionChanged()), this, SLOT(translateInputWidget())); - } - S60->splitViewLastWidget = widget; - m_splitViewPreviousWindowStates = windowToMove->windowState(); - } - - int windowTop = widget->window()->pos().y(); - - const bool userResize = widget->testAttribute(Qt::WA_Resized); - - QRect splitViewRect = qt_TRect2QRect(static_cast<CEikAppUi*>(S60->appUi())->ClientRect()); - - - // When resizing a window widget, it will lose its maximized window state. - // Native applications hide statuspane in splitview state, so lets move to - // fullscreen mode. This makes available area slightly bigger, which helps usability - // and greatly reduces event passing in orientation switch cases, - // as the statuspane size is not changing. - - if (alwaysResize) - windowToMove->setUpdatesEnabled(false); - - if (!(windowToMove->windowState() & Qt::WindowFullScreen)) { - windowToMove->setWindowState( - (windowToMove->windowState() & ~(Qt::WindowMinimized | Qt::WindowFullScreen)) | Qt::WindowFullScreen); - } - - if (alwaysResize) { - if (!moveWithinVisibleArea) { - m_splitViewResizeBy = widget->height(); - windowTop = widget->geometry().top(); - widget->resize(widget->width(), splitViewRect.height() - windowTop); - } - - if (gv->scene()) { - const QRectF microFocusRect = gv->scene()->inputMethodQuery(Qt::ImMicroFocus).toRectF(); - gv->ensureVisible(microFocusRect); - } - } else { - translateInputWidget(); - } - - if (alwaysResize) - windowToMove->setUpdatesEnabled(true); - - widget->setAttribute(Qt::WA_Resized, userResize); //not a user resize -} - -static QTextCharFormat qt_TCharFormat2QTextCharFormat(const TCharFormat &cFormat, bool validStyleColor) -{ - QTextCharFormat qFormat; - - if (validStyleColor) { - 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::updateHints(bool mustUpdateInputCapabilities) -{ - QWidget *w = focusWidget(); - if (w) { - QWidget *proxy = w->focusProxy(); - Qt::InputMethodHints hints = proxy ? proxy->inputMethodHints() : w->inputMethodHints(); - - // Since splitview support works like an input method hint, yet it is private flag, - // we need to update its state separately. - if (QSysInfo::s60Version() > QSysInfo::SV_S60_5_0) { - TInt currentFlags = m_fepState->Flags(); - if (S60->partial_keyboard) - currentFlags |= QT_EAknEditorFlagEnablePartialScreen; - else - currentFlags &= ~QT_EAknEditorFlagEnablePartialScreen; - if (currentFlags != m_fepState->Flags()) - m_fepState->SetFlags(currentFlags); - } - - if (hints != m_lastImHints) { - m_lastImHints = hints; - applyHints(hints); - } else if (!mustUpdateInputCapabilities) { - // Optimization. Return immediately if there was no change. - return; - } - } - queueInputCapabilitiesChanged(); -} - -void QCoeFepInputContext::applyHints(Qt::InputMethodHints hints) -{ - using namespace Qt; - - commitTemporaryPreeditString(); - - const bool anynumbermodes = hints & (ImhDigitsOnly | ImhFormattedNumbersOnly | ImhDialableCharactersOnly); - const bool anytextmodes = hints & (ImhUppercaseOnly | ImhLowercaseOnly | ImhEmailCharactersOnly | ImhUrlCharactersOnly); - const bool numbersOnly = anynumbermodes && !anytextmodes; - const bool noOnlys = !(hints & ImhExclusiveInputMask); - // if alphanumeric input, or if multiple incompatible number modes are selected; - // then make all symbols available in numeric mode too. - const bool needsCharMap= !numbersOnly || ((hints & ImhFormattedNumbersOnly) && (hints & ImhDialableCharactersOnly)); - TInt flags; - Qt::InputMethodHints oldHints = hints; - - // Some sanity checking. Make sure that only one preference is set. - InputMethodHints prefs = ImhPreferNumbers | ImhPreferUppercase | ImhPreferLowercase; - prefs &= hints; - if (prefs != ImhPreferNumbers && prefs != ImhPreferUppercase && prefs != ImhPreferLowercase) { - hints &= ~prefs; - } - if (!noOnlys) { - // Make sure that the preference is within the permitted set. - if (hints & ImhPreferNumbers && !anynumbermodes) { - hints &= ~ImhPreferNumbers; - } else if (hints & ImhPreferUppercase && !(hints & ImhUppercaseOnly)) { - hints &= ~ImhPreferUppercase; - } else if (hints & ImhPreferLowercase && !(hints & ImhLowercaseOnly)) { - hints &= ~ImhPreferLowercase; - } - // If there is no preference, set it to something within the permitted set. - if (!(hints & ImhPreferNumbers || hints & ImhPreferUppercase || hints & ImhPreferLowercase)) { - if (hints & ImhLowercaseOnly) { - hints |= ImhPreferLowercase; - } else if (hints & ImhUppercaseOnly) { - hints |= ImhPreferUppercase; - } else if (numbersOnly) { - hints |= ImhPreferNumbers; - } - } - } - - if (hints & ImhPreferNumbers) { - m_fepState->SetDefaultInputMode(EAknEditorNumericInputMode); - m_fepState->SetCurrentInputMode(EAknEditorNumericInputMode); - } else { - m_fepState->SetDefaultInputMode(EAknEditorTextInputMode); - m_fepState->SetCurrentInputMode(EAknEditorTextInputMode); - } - flags = 0; - if (noOnlys || (anynumbermodes && anytextmodes)) { - flags = EAknEditorAllInputModes; - } - else if (anynumbermodes) { - flags |= EAknEditorNumericInputMode; - if (QSysInfo::s60Version() > QSysInfo::SV_S60_5_0 - && ((hints & ImhFormattedNumbersOnly) || (hints & ImhDialableCharactersOnly))) { - //workaround - the * key does not launch the symbols menu, making it impossible to use these modes unless text mode is enabled. - flags |= EAknEditorTextInputMode; - } - } - else if (anytextmodes) { - flags |= EAknEditorTextInputMode; - } - else { - flags = EAknEditorAllInputModes; - } - m_fepState->SetPermittedInputModes(flags); - ReportAknEdStateEvent(MAknEdStateObserver::EAknEdwinStateInputModeUpdate); - - if (hints & ImhPreferLowercase) { - m_fepState->SetDefaultCase(EAknEditorLowerCase); - m_fepState->SetCurrentCase(EAknEditorLowerCase); - } else if (hints & ImhPreferUppercase) { - m_fepState->SetDefaultCase(EAknEditorUpperCase); - m_fepState->SetCurrentCase(EAknEditorUpperCase); - } else if (hints & ImhNoAutoUppercase) { - m_fepState->SetDefaultCase(EAknEditorLowerCase); - m_fepState->SetCurrentCase(EAknEditorLowerCase); - } else { - m_fepState->SetDefaultCase(EAknEditorTextCase); - m_fepState->SetCurrentCase(EAknEditorTextCase); - } - flags = 0; - if (hints & ImhUppercaseOnly) { - flags |= EAknEditorUpperCase; - } - if (hints & ImhLowercaseOnly) { - flags |= EAknEditorLowerCase; - } - if (flags == 0) { - flags = EAknEditorAllCaseModes; - if (hints & ImhNoAutoUppercase) { - flags &= ~EAknEditorTextCase; - } - } - m_fepState->SetPermittedCases(flags); - ReportAknEdStateEvent(MAknEdStateObserver::EAknEdwinStateCaseModeUpdate); - - flags = 0; - if (QSysInfo::s60Version() > QSysInfo::SV_S60_5_0) { - if (S60->partial_keyboard) - flags |= QT_EAknEditorFlagEnablePartialScreen; - flags |= QT_EAknEditorFlagSelectionVisible; - } - if (hints & ImhUppercaseOnly && !(hints & ImhLowercaseOnly) - || hints & ImhLowercaseOnly && !(hints & ImhUppercaseOnly)) { - flags |= EAknEditorFlagFixedCase; - } - // Using T9 and hidden text together may actually crash the FEP, so check for hidden text too. - if (hints & ImhNoPredictiveText || hints & ImhHiddenText) { - flags |= EAknEditorFlagNoT9; - } - if (needsCharMap) - flags |= EAknEditorFlagUseSCTNumericCharmap; - m_fepState->SetFlags(flags); - ReportAknEdStateEvent(MAknEdStateObserver::EAknEdwinStateFlagsUpdate); - - if (hints & ImhDialableCharactersOnly) { - // This is first, because if (ImhDialableCharactersOnly | ImhFormattedNumbersOnly) - // is specified, this one is more natural (# key enters a #) - flags = EAknEditorStandardNumberModeKeymap; - } else if (hints & ImhFormattedNumbersOnly) { - // # key enters decimal point - flags = EAknEditorCalculatorNumberModeKeymap; - } else if (hints & ImhDigitsOnly) { - // This is last, because it is most restrictive (# key is inactive) - flags = EAknEditorPlainNumberModeKeymap; - } else { - flags = EAknEditorStandardNumberModeKeymap; - } - m_fepState->SetNumericKeymap(static_cast<TAknEditorNumericKeymap>(flags)); - - if (hints & ImhUrlCharactersOnly) { - // URL characters is everything except space, so a superset of the other restrictions - m_fepState->SetSpecialCharacterTableResourceId(R_AVKON_URL_SPECIAL_CHARACTER_TABLE_DIALOG); - } else if (hints & ImhEmailCharactersOnly) { - m_fepState->SetSpecialCharacterTableResourceId(R_AVKON_EMAIL_ADDR_SPECIAL_CHARACTER_TABLE_DIALOG); - } else if (needsCharMap) { - m_fepState->SetSpecialCharacterTableResourceId(R_AVKON_SPECIAL_CHARACTER_TABLE_DIALOG); - } else if ((hints & ImhFormattedNumbersOnly) || (hints & ImhDialableCharactersOnly)) { - m_fepState->SetSpecialCharacterTableResourceId(R_AVKON_SPECIAL_CHARACTER_TABLE_DIALOG); - } else { - m_fepState->SetSpecialCharacterTableResourceId(0); - } - - if (hints & ImhHiddenText) { - m_textCapabilities = TCoeInputCapabilities::EAllText | TCoeInputCapabilities::ESecretText; - } else { - m_textCapabilities = TCoeInputCapabilities::EAllText; - } -} - -void QCoeFepInputContext::applyFormat(QList<QInputMethodEvent::Attribute> *attributes) -{ - TCharFormat cFormat; - QColor styleTextColor; - if (QWidget *focused = focusWidget()) { - QGraphicsView *gv = qobject_cast<QGraphicsView*>(focused); - if (!gv) // could be either the QGV or its viewport that has focus - gv = qobject_cast<QGraphicsView*>(focused->parentWidget()); - if (gv) { - if (QGraphicsScene *scene = gv->scene()) { - if (QGraphicsItem *focusItem = scene->focusItem()) { - if (focusItem->isWidget()) { - styleTextColor = static_cast<QGraphicsWidget*>(focusItem)->palette().text().color(); - } - } - } - } else { - styleTextColor = focused->palette().text().color(); - } - } else { - styleTextColor = QApplication::palette("QLineEdit").text().color(); - } - - if (styleTextColor.isValid()) { - const TLogicalRgb fontColor(TRgb(styleTextColor.red(), styleTextColor.green(), styleTextColor.blue(), styleTextColor.alpha())); - cFormat.iFontPresentation.iTextColor = fontColor; - } - - TInt numChars = 0; - TInt charPos = 0; - int oldSize = attributes->size(); - 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, styleTextColor.isValid())))); - charPos += numChars; - if (charPos >= m_preeditString.size()) { - break; - } - } - - if (attributes->size() == oldSize) { - // S60 didn't provide any format, so let's give our own instead. - attributes->append(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, - 0, - m_preeditString.size(), - standardFormat(PreeditFormat))); - } -} - -void QCoeFepInputContext::queueInputCapabilitiesChanged() -{ - if (m_pendingInputCapabilitiesChanged) - return; - - // Call ensureInputCapabilitiesChanged asynchronously. This is done to improve performance - // by not updating input capabilities too often. The reason we don't call the Symbian - // asynchronous version of InputCapabilitiesChanged is because we need to ensure that it - // is synchronous in some specific cases. Those will call ensureInputCapabilitesChanged. - QMetaObject::invokeMethod(this, "ensureInputCapabilitiesChanged", Qt::QueuedConnection); - m_pendingInputCapabilitiesChanged = true; -} - -void QCoeFepInputContext::ensureInputCapabilitiesChanged() -{ - if (!m_pendingInputCapabilitiesChanged) - return; - - // The call below is essentially equivalent to InputCapabilitiesChanged(), - // but is synchronous, rather than asynchronous. - CCoeEnv::Static()->SyncNotifyFocusObserversOfChangeInFocus(); - m_pendingInputCapabilitiesChanged = false; -} - -void QCoeFepInputContext::translateInputWidget() -{ - QGraphicsView *gv = qobject_cast<QGraphicsView *>(S60->splitViewLastWidget); - QRect splitViewRect = qt_TRect2QRect(static_cast<CEikAppUi*>(S60->appUi())->ClientRect()); - - QRectF cursor = gv->scene()->inputMethodQuery(Qt::ImMicroFocus).toRectF(); - QPolygon cursorP = gv->mapFromScene(cursor); - QRectF vkbRect = QRectF(splitViewRect.bottomLeft(), qApp->desktop()->rect().bottomRight()); - if (cursor.isEmpty() || vkbRect.isEmpty()) - return; - - // Fetch root item (i.e. graphicsitem with no parent) - QGraphicsItem *rootItem = 0; - foreach (QGraphicsItem *item, gv->scene()->items()) { - if (!item->parentItem()) { - rootItem = item; - break; - } - } - if (!rootItem) - return; - - m_transformation = (rootItem->transform().isTranslating()) ? QRectF(0,0, gv->width(), rootItem->transform().dy()) : QRectF(); - - // Do nothing if the cursor is visible in the splitview area. - if (splitViewRect.contains(cursorP.boundingRect())) - return; - - // New Y position should be ideally at the center of the splitview area. - // If that would expose unpainted canvas, limit the tranformation to the visible scene bottom. - - const qreal maxY = gv->sceneRect().bottom() - splitViewRect.bottom() + m_transformation.height(); - qreal dy = -(qMin(maxY, (cursor.bottom() - vkbRect.top() / 2))); - - // Do not allow transform above screen top. - if (m_transformation.height() + dy > 0) - return; - - rootItem->setTransform(QTransform::fromTranslate(0, dy), true); -} - -void QCoeFepInputContext::StartFepInlineEditL(const TDesC& aInitialInlineText, - TInt aPositionOfInsertionPointInInlineText, TBool aCursorVisibility, const MFormCustomDraw* /*aCustomDraw*/, - MFepInlineTextFormatRetriever& aInlineTextFormatRetriever, - MFepPointerEventHandlerDuringInlineEdit& aPointerEventHandlerDuringInlineEdit) -{ - QWidget *w = focusWidget(); - if (!w) - return; - - commitTemporaryPreeditString(); - - QList<QInputMethodEvent::Attribute> attributes; - - m_cursorVisibility = aCursorVisibility ? 1 : 0; - m_inlinePosition = aPositionOfInsertionPointInInlineText; - m_preeditString = qt_TDesC2QString(aInitialInlineText); - - m_formatRetriever = &aInlineTextFormatRetriever; - m_pointerHandler = &aPointerEventHandlerDuringInlineEdit; - - // With T9 aInitialInlineText is typically empty when StartFepInlineEditL is called, - // but FEP requires that selected text is always removed at StartFepInlineEditL. - // Let's remove the selected text if aInitialInlineText is empty and there is selected text - if (m_preeditString.isEmpty()) { - int anchor = w->inputMethodQuery(Qt::ImAnchorPosition).toInt(); - int cursorPos = w->inputMethodQuery(Qt::ImCursorPosition).toInt(); - int replacementLength = qAbs(cursorPos-anchor); - if (replacementLength > 0) { - int replacementStart = cursorPos < anchor ? 0 : -replacementLength; - QList<QInputMethodEvent::Attribute> clearSelectionAttributes; - QInputMethodEvent clearSelectionEvent(QLatin1String(""), clearSelectionAttributes); - clearSelectionEvent.setCommitString(QLatin1String(""), replacementStart, replacementLength); - sendEvent(clearSelectionEvent); - } - } - - applyFormat(&attributes); - - 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; - - commitTemporaryPreeditString(); - - m_inlinePosition = aPositionOfInsertionPointInInlineText; - - QList<QInputMethodEvent::Attribute> attributes; - applyFormat(&attributes); - attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, - m_inlinePosition, - m_cursorVisibility, - QVariant())); - QString newPreeditString = qt_TDesC2QString(aNewInlineText); - QInputMethodEvent event(newPreeditString, attributes); - if (newPreeditString.isEmpty() && m_preeditString.isEmpty()) { - // In Symbian world this means "erase last character". - event.setCommitString(QLatin1String(""), -1, 1); - } - m_preeditString = newPreeditString; - 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() -{ - // We are not supposed to ever have a tempPreeditString and a real preedit string - // from S60 at the same time, so it should be safe to rely on this test to determine - // whether we should honor S60's request to clear the text or not. - if (m_hasTempPreeditString) - return; - - QList<QInputMethodEvent::Attribute> attributes; - QInputMethodEvent event(QLatin1String(""), attributes); - event.setCommitString(QLatin1String(""), 0, 0); - m_preeditString.clear(); - m_inlinePosition = 0; - sendEvent(event); -} - -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; - - commitTemporaryPreeditString(); - - int pos = aCursorSelection.iAnchorPos; - int length = aCursorSelection.iCursorPos - pos; - - QList<QInputMethodEvent::Attribute> attributes; - attributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, pos, length, QVariant()); - QInputMethodEvent event(m_preeditString, attributes); - sendEvent(event); -} - -void QCoeFepInputContext::GetCursorSelectionForFep(TCursorSelection& aCursorSelection) const -{ - QWidget *w = focusWidget(); - if (!w) { - aCursorSelection.SetSelection(0,0); - return; - } - - int cursor = w->inputMethodQuery(Qt::ImCursorPosition).toInt() + m_preeditString.size(); - int anchor = w->inputMethodQuery(Qt::ImAnchorPosition).toInt() + m_preeditString.size(); - 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, - TInt aLengthToRetrieve) const -{ - QWidget *w = focusWidget(); - if (!w) { - aEditorContent.FillZ(aLengthToRetrieve); - 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) { - aFormat = TCharFormat(); - 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()); -} - -void QCoeFepInputContext::GetScreenCoordinatesForFepL(TPoint& aLeftSideOfBaseLine, TInt& aHeight, - TInt& aAscent, TInt /* aDocumentPosition */) const -{ - QWidget *w = focusWidget(); - if (!w) { - aLeftSideOfBaseLine = TPoint(0,0); - aHeight = 0; - aAscent = 0; - 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(); -} - -void QCoeFepInputContext::DoCommitFepInlineEditL() -{ - commitCurrentString(false); - if (QSysInfo::s60Version() > QSysInfo::SV_S60_5_0) - ReportAknEdStateEvent(QT_EAknCursorPositionChanged); - -} - -void QCoeFepInputContext::commitCurrentString(bool cancelFepTransaction) -{ - QList<QInputMethodEvent::Attribute> attributes; - QInputMethodEvent event(QLatin1String(""), attributes); - event.setCommitString(m_preeditString, 0, 0); - m_preeditString.clear(); - m_inlinePosition = 0; - sendEvent(event); - - m_hasTempPreeditString = false; - - if (cancelFepTransaction) { - CCoeFep* fep = CCoeEnv::Static()->Fep(); - if (fep) - fep->CancelTransaction(); - } -} - -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(); -} - -MObjectProvider *QCoeFepInputContext::MopNext() -{ - QWidget *w = focusWidget(); - if (w) - return w->effectiveWinId(); - return 0; -} - -QT_END_NAMESPACE - -#endif // QT_NO_IM |