summaryrefslogtreecommitdiffstats
path: root/src/gui/inputmethod/qcoefepinputcontext_s60.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/inputmethod/qcoefepinputcontext_s60.cpp')
-rw-r--r--src/gui/inputmethod/qcoefepinputcontext_s60.cpp573
1 files changed, 553 insertions, 20 deletions
diff --git a/src/gui/inputmethod/qcoefepinputcontext_s60.cpp b/src/gui/inputmethod/qcoefepinputcontext_s60.cpp
index 87c4045..e7084ac 100644
--- a/src/gui/inputmethod/qcoefepinputcontext_s60.cpp
+++ b/src/gui/inputmethod/qcoefepinputcontext_s60.cpp
@@ -57,6 +57,21 @@
#include <e32property.h>
#include <limits.h>
+
+#include <eikccpu.h>
+#include <aknedsts.h>
+#include <coeinput.h>
+#include <w32std.h>
+#include <akndiscreetpopup.h>
+
+#include <qtextedit.h>
+#include <qplaintextedit.h>
+#include <qlineedit.h>
+#include <qclipboard.h>
+#include <qvalidator.h>
+#include <qgraphicsproxywidget.h>
+#include <qgraphicsitem.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.
@@ -79,8 +94,253 @@
#define QT_EPSUidAknFep 0x100056de
#define QT_EAknFepTouchInputActive 0x00000004
+_LIT(KAvkonResourceFile, "z:\\resource\\avkon.rsc" );
+
QT_BEGIN_NAMESPACE
+static QWidget* getFocusedChild(const QList<QObject*>& objectList)
+{
+ for (int j = 0; j < objectList.count(); j++) {
+ if (QWidget* ow = qobject_cast<QWidget *>(objectList[j])) {
+ if (ow->hasFocus()) {
+ return ow;
+ } else {
+ if (QWidget* rw = getFocusedChild(ow->children()))
+ return rw;
+ }
+ }
+ }
+ return 0;
+}
+
+// A generic method for invoking "cut", "copy", and "paste" slots on editor
+// All supported editors are expected to have these.
+static bool ccpuInvokeSlot(QObject *obj, QObject *focusObject, const char *member)
+{
+ QObject *invokeTarget = obj;
+ if (focusObject)
+ invokeTarget = focusObject;
+
+ return QMetaObject::invokeMethod(invokeTarget, member, Qt::DirectConnection);
+}
+
+// focusObject is used to return a pointer to focused graphics object, if any
+static QWidget *getQWidgetFromQGraphicsView(QWidget *widget, QObject **focusObject = 0)
+{
+ if (focusObject)
+ *focusObject = 0;
+
+ if (!widget)
+ return 0;
+
+ if (QGraphicsView* qgv = qobject_cast<QGraphicsView *>(widget)) {
+ QGraphicsItem *focusItem = 0;
+ if (qgv->scene())
+ focusItem = qgv->scene()->focusItem();
+ if (focusItem) {
+ if (focusObject)
+ *focusObject = focusItem->toGraphicsObject();
+ if (QGraphicsProxyWidget* const qgpw = qgraphicsitem_cast<QGraphicsProxyWidget* const>(focusItem)) {
+ if (QWidget* w = qgpw->widget()) {
+ if (w->layout()) {
+ if (QWidget* rw = getFocusedChild(w->children()))
+ return rw;
+ } else {
+ return w;
+ }
+ }
+ }
+ }
+ }
+ return widget;
+}
+
+QCoeFepInputMaskHandler::QCoeFepInputMaskHandler(const QString &mask)
+{
+ QString inputMask;
+ int delimiter = mask.indexOf(QLatin1Char(';'));
+ if (mask.isEmpty() || delimiter == 0)
+ return;
+
+ if (delimiter == -1) {
+ m_blank = QLatin1Char(' ');
+ inputMask = mask;
+ } else {
+ inputMask = mask.left(delimiter);
+ m_blank = (delimiter + 1 < mask.length()) ? mask[delimiter + 1] : QLatin1Char(' ');
+ }
+
+ // Calculate m_maxLength / m_maskData length
+ m_maxLength = 0;
+ QChar c = 0;
+ for (int i = 0; i < inputMask.length(); i++) {
+ c = inputMask.at(i);
+ if (i > 0 && inputMask.at(i - 1) == QLatin1Char('\\')) {
+ m_maxLength++;
+ continue;
+ }
+ if (c != QLatin1Char('\\') && c != QLatin1Char('!')
+ && c != QLatin1Char('<') && c != QLatin1Char('>')
+ && c != QLatin1Char('{') && c != QLatin1Char('}')
+ && c != QLatin1Char('[') && c != QLatin1Char(']')) {
+ m_maxLength++;
+ }
+ }
+
+ m_maskData = new MaskInputData[m_maxLength];
+
+ MaskInputData::Casemode m = MaskInputData::NoCaseMode;
+ c = 0;
+ bool s = false;
+ bool escape = false;
+ int index = 0;
+ for (int i = 0; i < inputMask.length(); i++) {
+ c = inputMask.at(i);
+ if (escape) {
+ s = true;
+ m_maskData[index].maskChar = c;
+ m_maskData[index].separator = s;
+ m_maskData[index].caseMode = m;
+ index++;
+ escape = false;
+ } else if (c == QLatin1Char('<')) {
+ m = MaskInputData::Lower;
+ } else if (c == QLatin1Char('>')) {
+ m = MaskInputData::Upper;
+ } else if (c == QLatin1Char('!')) {
+ m = MaskInputData::NoCaseMode;
+ } else if (c != QLatin1Char('{') && c != QLatin1Char('}') && c != QLatin1Char('[') && c != QLatin1Char(']')) {
+ switch (c.unicode()) {
+ case 'A':
+ case 'a':
+ case 'N':
+ case 'n':
+ case 'X':
+ case 'x':
+ case '9':
+ case '0':
+ case 'D':
+ case 'd':
+ case '#':
+ case 'H':
+ case 'h':
+ case 'B':
+ case 'b':
+ s = false;
+ break;
+ case '\\':
+ escape = true;
+ break;
+ default:
+ s = true;
+ break;
+ }
+
+ if (!escape) {
+ m_maskData[index].maskChar = c;
+ m_maskData[index].separator = s;
+ m_maskData[index].caseMode = m;
+ index++;
+ }
+ }
+ }
+}
+
+QCoeFepInputMaskHandler::~QCoeFepInputMaskHandler()
+{
+ if (m_maskData)
+ delete[] m_maskData;
+}
+
+bool QCoeFepInputMaskHandler::canPasteClipboard(const QString &text)
+{
+ if (!m_maskData)
+ return true;
+
+ if (text.length() > m_maxLength)
+ return false;
+ int limit = qMin(m_maxLength, text.length());
+ for (int i = 0; i < limit; ++i) {
+ if (m_maskData[i].separator) {
+ if (text.at(i) != m_maskData[i].maskChar)
+ return false;
+ } else {
+ if (!isValidInput(text.at(i), m_maskData[i].maskChar))
+ return false;
+ }
+ }
+ return true;
+}
+
+bool QCoeFepInputMaskHandler::isValidInput(QChar key, QChar mask) const
+{
+ switch (mask.unicode()) {
+ case 'A':
+ if (key.isLetter())
+ return true;
+ break;
+ case 'a':
+ if (key.isLetter() || key == m_blank)
+ return true;
+ break;
+ case 'N':
+ if (key.isLetterOrNumber())
+ return true;
+ break;
+ case 'n':
+ if (key.isLetterOrNumber() || key == m_blank)
+ return true;
+ break;
+ case 'X':
+ if (key.isPrint())
+ return true;
+ break;
+ case 'x':
+ if (key.isPrint() || key == m_blank)
+ return true;
+ break;
+ case '9':
+ if (key.isNumber())
+ return true;
+ break;
+ case '0':
+ if (key.isNumber() || key == m_blank)
+ return true;
+ break;
+ case 'D':
+ if (key.isNumber() && key.digitValue() > 0)
+ return true;
+ break;
+ case 'd':
+ if ((key.isNumber() && key.digitValue() > 0) || key == m_blank)
+ return true;
+ break;
+ case '#':
+ if (key.isNumber() || key == QLatin1Char('+') || key == QLatin1Char('-') || key == m_blank)
+ return true;
+ break;
+ case 'B':
+ if (key == QLatin1Char('0') || key == QLatin1Char('1'))
+ return true;
+ break;
+ case 'b':
+ if (key == QLatin1Char('0') || key == QLatin1Char('1') || key == m_blank)
+ return true;
+ break;
+ case 'H':
+ if (key.isNumber() || (key >= QLatin1Char('a') && key <= QLatin1Char('f')) || (key >= QLatin1Char('A') && key <= QLatin1Char('F')))
+ return true;
+ break;
+ case 'h':
+ if (key.isNumber() || (key >= QLatin1Char('a') && key <= QLatin1Char('f')) || (key >= QLatin1Char('A') && key <= QLatin1Char('F')) || key == m_blank)
+ return true;
+ break;
+ default:
+ break;
+ }
+ return false;
+}
+
Q_GUI_EXPORT void qt_s60_setPartialScreenInputMode(bool enable)
{
S60->partial_keyboard = enable;
@@ -116,7 +376,8 @@ QCoeFepInputContext::QCoeFepInputContext(QObject *parent)
m_pointerHandler(0),
m_hasTempPreeditString(false),
m_splitViewResizeBy(0),
- m_splitViewPreviousWindowStates(Qt::WindowNoState)
+ m_splitViewPreviousWindowStates(Qt::WindowNoState),
+ m_ccpu(0)
{
m_fepState->SetObjectProvider(this);
int defaultFlags = EAknEditorFlagDefault;
@@ -133,6 +394,29 @@ QCoeFepInputContext::QCoeFepInputContext(QObject *parent)
m_fepState->SetPermittedCases( EAknEditorAllCaseModes );
m_fepState->SetSpecialCharacterTableResourceId(R_AVKON_SPECIAL_CHARACTER_TABLE_DIALOG);
m_fepState->SetNumericKeymap(EAknEditorAlphanumericNumberModeKeymap);
+ enableSymbianCcpuSupport();
+
+ //adding softkeys
+ QString copyLabel = QLatin1String("Copy");
+ QString pasteLabel = QLatin1String("Paste");
+ TRAP_IGNORE(
+ CEikonEnv* coe = CEikonEnv::Static();
+ if (coe) {
+ HBufC* copyBuf = coe->AllocReadResourceLC(R_TEXT_SOFTKEY_COPY);
+ copyLabel = qt_TDesC2QString(*copyBuf);
+ CleanupStack::PopAndDestroy(copyBuf);
+ HBufC* pasteBuf = coe->AllocReadResourceLC(R_TEXT_SOFTKEY_PASTE);
+ pasteLabel = qt_TDesC2QString(*pasteBuf);
+ CleanupStack::PopAndDestroy(pasteBuf);
+ }
+ )
+
+ m_copyAction = new QAction(copyLabel, QApplication::desktop());
+ m_pasteAction = new QAction(pasteLabel, QApplication::desktop());
+ m_copyAction->setSoftKeyRole(QAction::PositiveSoftKey);
+ m_pasteAction->setSoftKeyRole(QAction::NegativeSoftKey);
+ connect(m_copyAction, SIGNAL(triggered()), this, SLOT(copy()));
+ connect(m_pasteAction, SIGNAL(triggered()), this, SLOT(paste()));
}
QCoeFepInputContext::~QCoeFepInputContext()
@@ -145,8 +429,8 @@ QCoeFepInputContext::~QCoeFepInputContext()
// but is synchronous, rather than asynchronous.
CCoeEnv::Static()->SyncNotifyFocusObserversOfChangeInFocus();
- if (m_fepState)
- delete m_fepState;
+ delete m_fepState;
+ delete m_ccpu;
}
void QCoeFepInputContext::reset()
@@ -347,6 +631,12 @@ bool QCoeFepInputContext::filterEvent(const QEvent *event)
sControl = focusWidget()->effectiveWinId()->MopGetObject(sControl);
Q_ASSERT(sControl);
+ // Store last focused widget and object in case of fullscreen VKB
+ QObject *focusObject = 0;
+ m_lastFocusedEditor = getQWidgetFromQGraphicsView(focusWidget(), &focusObject);
+ m_lastFocusedObject = focusObject; // Can be null
+ Q_ASSERT(m_lastFocusedEditor);
+
// 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.
@@ -369,28 +659,66 @@ bool QCoeFepInputContext::filterEvent(const QEvent *event)
bool QCoeFepInputContext::symbianFilterEvent(QWidget *keyWidget, const QSymbianEvent *event)
{
Q_UNUSED(keyWidget);
+ if (event->type() == QSymbianEvent::WindowServerEvent) {
+ const TWsEvent* wsEvent = event->windowServerEvent();
+ TInt eventType = 0;
+ if (wsEvent)
+ eventType = wsEvent->Type();
+
+ if (eventType == EEventKey) {
+ TKeyEvent* keyEvent = wsEvent->Key();
+ if (keyEvent) {
+ switch (keyEvent->iScanCode) {
+ case EEikCmdEditCopy:
+ CcpuCopyL();
+ break;
+ case EEikCmdEditCut:
+ CcpuCutL();
+ break;
+ case EEikCmdEditPaste:
+ CcpuPasteL();
+ break;
+ case EStdKeyF21:
+ changeCBA(true);
+ break;
+ default:
+ break;
+ }
+ switch (keyEvent->iCode) {
+ case EKeyLeftArrow:
+ case EKeyRightArrow:
+ case EKeyUpArrow:
+ case EKeyDownArrow:
+ if (CcpuCanCopy() && ((keyEvent->iModifiers & EModifierShift) == EModifierShift))
+ changeCBA(true);
+ break;
+ default:
+ break;
+ }
+ }
+ } else if (eventType == EEventKeyUp) {
+ if (wsEvent->Key() && wsEvent->Key()->iScanCode == EStdKeyLeftShift)
+ changeCBA(false);
+ } else if (eventType == 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);
+ }
+ }
+ }
+
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);
- }
- }
if (event->type() == QSymbianEvent::ResourceChangeEvent
&& (event->resourceChangeType() == KEikMessageFadeAllWindows
@@ -956,7 +1284,11 @@ void QCoeFepInputContext::translateInputWidget()
// Translation should happen row-by-row, but initially it needs to ensure that cursor is visible.
const qreal translation = m_transformation.height() ?
cursor.height() : (cursorRect.bottom() - vkbRect.top());
- const qreal dy = -(qMin(maxY, translation));
+ qreal dy = -(qMin(maxY, translation));
+
+ // Correct the translation direction, if the cursor rect would be moved outside of application area.
+ if ((cursorRect.bottom() + dy) < 0)
+ dy *= -1;
// Do not allow transform above screen top, nor beyond scenerect
if (m_transformation.height() + dy > 0 || gv->sceneRect().bottom() + m_transformation.height() < 0) {
@@ -1238,6 +1570,71 @@ void QCoeFepInputContext::GetScreenCoordinatesForFepL(TPoint& aLeftSideOfBaseLin
aAscent = metrics.ascent();
}
+void QCoeFepInputContext::enableSymbianCcpuSupport()
+{
+ if (!m_ccpu) {
+ QT_TRAP_THROWING(
+ m_ccpu = new (ELeave) CAknCcpuSupport(this);
+ m_ccpu->SetMopParent(this);
+ CleanupStack::PushL(m_ccpu);
+ m_ccpu->ConstructL();
+ CleanupStack::Pop(m_ccpu);
+ );
+ Q_ASSERT(m_fepState);
+ if (m_fepState)
+ m_fepState->SetCcpuState(this);
+ }
+}
+
+void QCoeFepInputContext::changeCBA(bool showCopyAndOrPaste)
+{
+ QWidget *w = focusWidget();
+ if (!w)
+ w = m_lastFocusedEditor;
+
+ if (w) {
+ if (showCopyAndOrPaste) {
+ if (CcpuCanCopy())
+ w->addAction(m_copyAction);
+ if (CcpuCanPaste())
+ w->addAction(m_pasteAction);
+ } else {
+ w->removeAction(m_copyAction);
+ w->removeAction(m_pasteAction);
+ }
+ }
+}
+
+void QCoeFepInputContext::copyOrCutTextToClipboard(const char *operation)
+{
+ bool hasText = false;
+
+ QWidget *w = focusWidget();
+ QObject *focusObject = 0;
+ if (!w) {
+ w = m_lastFocusedEditor;
+ focusObject = m_lastFocusedObject;
+ } else {
+ w = getQWidgetFromQGraphicsView(w, &focusObject);
+ }
+
+ if (w) {
+ int cursor = w->inputMethodQuery(Qt::ImCursorPosition).toInt();
+ int anchor = w->inputMethodQuery(Qt::ImAnchorPosition).toInt();
+
+ if (cursor != anchor) {
+ if (ccpuInvokeSlot(w, focusObject, operation)) {
+ TRAP_IGNORE(
+ CAknDiscreetPopup::ShowGlobalPopupL(
+ R_AVKON_DISCREET_POPUP_TEXT_COPIED,
+ KAvkonResourceFile);
+ )
+ }
+ }
+ }
+}
+
+
void QCoeFepInputContext::DoCommitFepInlineEditL()
{
commitCurrentString(false);
@@ -1302,6 +1699,142 @@ MCoeFepAwareTextEditor_Extension1::CState* QCoeFepInputContext::State(TUid /*aTy
return m_fepState;
}
+TBool QCoeFepInputContext::CcpuIsFocused() const
+{
+ return focusWidget() != 0;
+}
+
+TBool QCoeFepInputContext::CcpuCanCut() const
+{
+ bool retval = false;
+ QWidget *w = focusWidget();
+ if (!w)
+ w = m_lastFocusedEditor;
+ else
+ w = getQWidgetFromQGraphicsView(w);
+ if (w) {
+ int cursor = w->inputMethodQuery(Qt::ImCursorPosition).toInt();
+ int anchor = w->inputMethodQuery(Qt::ImAnchorPosition).toInt();
+ retval = cursor != anchor;
+ }
+ return retval;
+}
+
+void QCoeFepInputContext::CcpuCutL()
+{
+ copyOrCutTextToClipboard("cut");
+}
+
+TBool QCoeFepInputContext::CcpuCanCopy() const
+{
+ return CcpuCanCut();
+}
+
+void QCoeFepInputContext::CcpuCopyL()
+{
+ copyOrCutTextToClipboard("copy");
+}
+
+TBool QCoeFepInputContext::CcpuCanPaste() const
+{
+ bool canPaste = false;
+ QString textToPaste = QApplication::clipboard()->text();
+ if (!textToPaste.isEmpty()) {
+ QWidget *w = focusWidget();
+ QObject *focusObject = 0;
+ if (!w) {
+ w = m_lastFocusedEditor;
+ focusObject = m_lastFocusedObject;
+ } else {
+ w = getQWidgetFromQGraphicsView(w, &focusObject);
+ }
+ if (w) {
+ // First, check if we are dealing with standard Qt editors (QLineEdit, QTextEdit, or QPlainTextEdit),
+ // as they do not have queryable property.
+ if (QTextEdit* tedit = qobject_cast<QTextEdit *>(w)) {
+ canPaste = tedit->canPaste();
+ } else if (QPlainTextEdit* ptedit = qobject_cast<QPlainTextEdit *>(w)) {
+ canPaste = ptedit->canPaste();
+ } else if (QLineEdit* ledit = qobject_cast<QLineEdit *>(w)) {
+ QString fullText = ledit->text();
+ if (ledit->hasSelectedText()) {
+ fullText.remove(ledit->selectionStart(), ledit->selectedText().length());
+ fullText.insert(ledit->selectionStart(), textToPaste);
+ } else {
+ fullText.insert(ledit->cursorPosition(), textToPaste);
+ }
+
+ if (fullText.length() > ledit->maxLength()) {
+ canPaste = false;
+ } else {
+ const QValidator* validator = ledit->validator();
+ if (validator) {
+ int pos = 0;
+ if (validator->validate(fullText, pos) == QValidator::Invalid)
+ canPaste = false;
+ else
+ canPaste = true;
+ } else {
+ QString mask(ledit->inputMask());
+ if (!mask.isEmpty()) {
+ QCoeFepInputMaskHandler maskhandler(mask);
+ if (maskhandler.canPasteClipboard(fullText))
+ canPaste = true;
+ else
+ canPaste = false;
+ } else {
+ canPaste = true;
+ }
+ }
+ }
+ } else {
+ // Unknown editor (probably a QML one); Request the "canPaste" property.
+ QObject *invokeTarget = w;
+ if (focusObject)
+ invokeTarget = focusObject;
+
+ canPaste = invokeTarget->property("canPaste").toBool();
+ }
+ }
+ }
+ return canPaste;
+}
+
+void QCoeFepInputContext::CcpuPasteL()
+{
+ QWidget *w = focusWidget();
+ QObject *focusObject = 0;
+ if (!w) {
+ w = m_lastFocusedEditor;
+ focusObject = m_lastFocusedObject;
+ } else {
+ w = getQWidgetFromQGraphicsView(w, &focusObject);
+ }
+ if (w)
+ ccpuInvokeSlot(w, focusObject, "paste");
+}
+
+TBool QCoeFepInputContext::CcpuCanUndo() const
+{
+ //not supported
+ return EFalse;
+}
+
+void QCoeFepInputContext::CcpuUndoL()
+{
+ //not supported
+}
+
+void QCoeFepInputContext::copy()
+{
+ QT_TRAP_THROWING(CcpuCopyL());
+}
+
+void QCoeFepInputContext::paste()
+{
+ QT_TRAP_THROWING(CcpuPasteL());
+}
+
TTypeUid::Ptr QCoeFepInputContext::MopSupplyObject(TTypeUid /*id*/)
{
return TTypeUid::Null();