src/hbplugins/inputmethods/touchinput/hbinputprediction12keyhandler.cpp
changeset 0 16d8024aca5e
child 1 f7ac710697a9
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hbplugins/inputmethods/touchinput/hbinputprediction12keyhandler.cpp	Mon Apr 19 14:02:13 2010 +0300
@@ -0,0 +1,531 @@
+/****************************************************************************
+**
+** Copyright (C) 2008-2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (developer.feedback@nokia.com)
+**
+** This file is part of the HbPlugins module of the UI Extensions for Mobile.
+**
+** 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.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at developer.feedback@nokia.com.
+**
+****************************************************************************/
+#include <QTimer>
+#include <QGraphicsScene>
+
+#include <hbinputmethod.h>
+#include <hbinputkeymapfactory.h>
+#include <hbinputpredictionengine.h>
+#include <hbinputsettingproxy.h>
+#include <hbinputvkbhost.h>
+#include <hbinputdialog.h>
+#include <hbaction.h>
+#include "virtual12key.h"
+
+#include "hbinputprediction12keyhandler.h"
+#include "hbinputpredictionhandler_p.h"
+#include "hbinputabstractbase.h"
+
+#define HbDeltaHeight 3.0
+#define MAXUDBWORDSIZE 64
+
+class HbInputPrediction12KeyHandlerPrivate: public HbInputPredictionHandlerPrivate
+{
+    Q_DECLARE_PUBLIC(HbInputPrediction12KeyHandler)
+
+public:
+    HbInputPrediction12KeyHandlerPrivate();
+    ~HbInputPrediction12KeyHandlerPrivate();
+
+    bool buttonReleased(const QKeyEvent *keyEvent);
+    bool buttonPressed(const QKeyEvent *keyEvent);
+    void _q_timeout();
+    void launchSpellDialog(QString customWord);
+    void getSpellDialogPositionAndSize(QPointF & pos,QSizeF & size,QRectF & geom);
+    void cancelButtonPress();
+public:
+    int mLastKey;
+    bool mButtonDown;
+    QChar mCurrentChar;
+    bool mLongPressHappened;
+    bool mShiftKeyDoubleTap;
+};
+
+HbInputPrediction12KeyHandlerPrivate::HbInputPrediction12KeyHandlerPrivate()
+:mLastKey(0),
+mButtonDown(false),
+mCurrentChar(0),
+mLongPressHappened(false),
+mShiftKeyDoubleTap(false)
+{
+}
+
+HbInputPrediction12KeyHandlerPrivate::~HbInputPrediction12KeyHandlerPrivate()
+{
+}
+
+void HbInputPrediction12KeyHandlerPrivate::_q_timeout()
+{
+    qDebug("HbInputPrediction12KeyHandlerPrivate::_q_timeout()");
+    Q_Q(HbInputPrediction12KeyHandler);
+
+    // let's stop the timer first.
+    mTimer->stop();
+
+    //Long key press number key is applicable to all keys
+    if (mButtonDown) {	
+        if (mLastKey == Qt::Key_Asterisk) {
+			//Remove the "?" mark if present
+			if(!mCanContinuePrediction && (*mCandidates)[mBestGuessLocation].endsWith('?')) {	
+                (*mCandidates)[mBestGuessLocation].chop(1);
+                updateEditor();
+                mCanContinuePrediction = true;
+			}
+            mInputMethod->switchMode(mLastKey);
+        } else if (mLastKey == Qt::Key_Shift) {
+            mInputMethod->switchMode(Qt::Key_Shift);
+            mLongPressHappened = true;
+        } else if (mLastKey == Qt::Key_Control) {
+            mInputMethod->selectSpecialCharacterTableMode();
+        } else {
+            //With a long key press of a key, numbers are supposed to be entered.
+            //When the existing input (along with the short key press input of the
+            //existing key) results in tail (i.e. autocompletion), we need to accept
+            //the short key press as well as the tail. In case of ? delete the questionmark and add the number value.          
+            //mTailShowing = false;            
+            // Delete "?" entered
+            if (!mCanContinuePrediction) {
+                deleteOneCharacter();
+            }
+			if (mLastKey != Qt::Key_Delete) {
+				q->commitFirstMappedNumber(mLastKey);
+			}
+            mLongPressHappened = true;
+        }
+    }
+}
+
+bool HbInputPrediction12KeyHandlerPrivate::buttonPressed(const QKeyEvent *keyEvent)
+{
+    mLongPressHappened = false;
+    HbInputFocusObject *focusObject = 0;
+    focusObject = mInputMethod->focusObject();
+    if (!focusObject) {
+        return false;
+    }
+
+    int buttonId = keyEvent->key();
+
+    
+    if (buttonId == Qt::Key_Control) {
+        mLastKey = buttonId;
+        mButtonDown = true;
+        mTimer->start(HbLongPressTimerTimeout);
+        return true;
+    } else if (buttonId == Qt::Key_Shift) {		
+    // if we get a second consequtive shift key press, 
+    // we want to handle it in buttonRelease
+        if (mTimer->isActive() && (mLastKey == buttonId)){
+            mShiftKeyDoubleTap = true;
+        }
+        if (!mTimer->isActive()) {            		
+            mTimer->start(HbLongPressTimerTimeout);
+        }
+        mLastKey = buttonId;
+        mButtonDown = true;
+        return true;
+    }
+
+    mLastKey = buttonId;
+    mButtonDown = true;
+    
+    // custom button should not start timer.
+    if ((buttonId & CUSTOM_INPUT_MASK) != CUSTOM_INPUT_MASK) {
+        mTimer->start(HbLongPressTimerTimeout);
+    }
+    return false;
+}
+
+/*!
+Handles the key release events from the VKB. Launches the SCT with key release event of
+asterisk.
+*/
+bool HbInputPrediction12KeyHandlerPrivate::buttonReleased(const QKeyEvent *keyEvent)
+{
+    Q_Q(HbInputPrediction12KeyHandler);
+    
+    if(!mButtonDown || mLongPressHappened){
+        return false;
+    }
+
+    // since button is released we can set buttonDown back to false.
+    mButtonDown = false;
+    int buttonId = keyEvent->key(); 
+    // it was a long press on sct swith button. so just return form here.
+    if (!mTimer->isActive() && buttonId == Qt::Key_Control) {
+        return true;
+    }
+
+    // Sym key is handled in this class it self, so not passing it to 
+    // the base mode handlers.	
+    if ( buttonId == Qt::Key_Control) {
+        //Same SYM key is used for launching candidate list (long key press)
+        //and also for SCT. So, do not launch SCT if candidate list is already launched.
+        mInputMethod->switchMode(buttonId);
+        return true;
+    } 
+    /* Behavior of Short Press of Asterisk Key when in inline editing state 
+		- Should launch Candidate List if we can continue with prediction i.e. "?" is not displayed
+		- Should launch Spell Query Dialog if we cannot continue with prediction 
+	- Behavior of Short Press of Asterisk Key when not in inline editing state 
+		- Should launch SCT
+	*/
+    else if (buttonId == Qt::Key_Asterisk ) {
+		if(!mCanContinuePrediction && (*mCandidates)[mBestGuessLocation].endsWith('?')) {			
+            //Remove the "?" mark
+            (*mCandidates)[mBestGuessLocation].chop(1);
+            updateEditor();
+            q->processCustomWord((*mCandidates)[mBestGuessLocation]);
+            mCanContinuePrediction = true;
+		}
+		else
+			mInputMethod->starKeySelected();
+        return true;
+    }	
+    else if (buttonId == Qt::Key_Return) {
+        mInputMethod->closeKeypad();
+        return true;
+    }
+    if (buttonId == Qt::Key_Shift) {
+        // single tap of shift key toggles prediction status in case insensitive languages
+        if (!HbInputSettingProxy::instance()->globalInputLanguage().isCaseSensitiveLanguage()) {
+            HbInputSettingProxy::instance()->togglePrediction();
+        } else {
+            if (mShiftKeyDoubleTap) {
+                mTimer->stop(); 
+                mShiftKeyDoubleTap = false;	
+                //mShowTail = false;      
+                if (HbInputSettingProxy::instance()->globalInputLanguage()== mInputMethod->inputState().language()) {
+                    // in latin variants , double tap of shift key toggles the prediction status	
+                    // revert back to the old case as this is a double tap 
+                    // (the case was changed on the single tap)
+                    updateTextCase();				 
+                    HbInputSettingProxy::instance()->togglePrediction();
+                } else {
+                    // if the global language is different from the input mode language, we should 
+                    // go back to the root state
+                    // e.g. double tap of hash/shift key is used to change 
+                    // to chinese input mode from latin input mode
+                    HbInputState rootState;
+                    mInputMethod->editorRootState(rootState);
+                    mInputMethod->activateState(rootState); 		
+                }
+            } else {
+                updateTextCase();
+                if( !mTimer->isActive()){
+                    mTimer->start();
+                }
+            }
+        }
+        return true;
+    }
+
+    // text input happens on button release		
+    if (q->HbInputPredictionHandler::filterEvent(keyEvent)) {
+        return true;
+    }	
+    return false;
+}
+
+void HbInputPrediction12KeyHandlerPrivate::launchSpellDialog(QString editorText)
+{
+    HbInputFocusObject *focusObject = mInputMethod->focusObject();
+    if (!focusObject) {
+        return;
+    }
+        QPointer<QObject> focusedQObject = focusObject->object();
+	// store the current focused editor 
+			 
+    HbTextCase currentTextCase = focusObject->editorInterface().textCase();
+    mEngine->clear();
+    mCanContinuePrediction = true;
+    // close the keypad before showing the spell dialog
+	HbVkbHost *vkbHost = focusObject->editorInterface().vkbHost();
+    if (vkbHost && vkbHost->keypadStatus() != HbVkbHost::HbVkbStatusClosed) {
+        vkbHost->closeKeypad(true);
+    }
+    // create the spell dialog
+    HbInputDialog *spellDialog = new HbInputDialog();
+    spellDialog->setInputMode(HbInputDialog::TextInput);
+    spellDialog->setPromptText("");
+    spellDialog->setValue(QVariant(editorText));
+    QSizeF  dialogSize = spellDialog->size();
+    QPointF dialogPos = spellDialog->pos();
+    QRectF geom = spellDialog->geometry();
+    
+    //set the spell dialog position
+    getSpellDialogPositionAndSize(dialogPos,dialogSize, geom);
+    geom.setHeight(dialogSize.height());
+    geom.setWidth(dialogSize.width());
+    spellDialog->setGeometry(geom);
+    spellDialog->setPos(dialogPos);
+
+    // change the focus to spell dialog editor
+    HbLineEdit *spellEdit = spellDialog->lineEdit();
+
+    if (spellEdit) {
+        spellEdit->setFocus();
+        spellEdit->clearFocus();
+        spellEdit->setFocus();
+        spellEdit->setMaxLength(MAXUDBWORDSIZE);
+        spellEdit->setSmileysEnabled(false);
+        HbEditorInterface eInt(spellEdit);
+		spellEdit->setInputMethodHints(spellEdit->inputMethodHints() | Qt::ImhNoPredictiveText);
+        eInt.setTextCase(currentTextCase);
+    }
+    // execute the spell dialog
+    HbAction *act = spellDialog->exec();
+ 
+        //create new focus object and set the focus back to main editor
+        HbInputFocusObject *newFocusObject = new HbInputFocusObject(focusedQObject);
+	   
+		
+        newFocusObject->releaseFocus();
+        newFocusObject->setFocus();
+
+	    HbAbstractEdit *abstractEdit = qobject_cast<HbAbstractEdit*>(focusedQObject);
+        if(abstractEdit) {
+            abstractEdit->setCursorPosition(abstractEdit->cursorPosition());
+        }
+
+        mInputMethod->setFocusObject(newFocusObject);
+        mInputMethod->focusObject()->editorInterface().setTextCase(currentTextCase);
+
+    if (act->text() == spellDialog->primaryAction()->text()) {
+        commit(spellDialog->value().toString() , true, true);
+    } else if (act->text() == spellDialog->secondaryAction()->text()) {
+    //update the editor with pre-edit text
+        mEngine->setWord(editorText);
+        bool used = false;	 
+        mEngine->updateCandidates(mBestGuessLocation, used);
+		mShowTail = false;
+		updateEditor();
+	}  
+    delete spellDialog;
+}
+
+void HbInputPrediction12KeyHandlerPrivate::getSpellDialogPositionAndSize(QPointF & pos,QSizeF & size, QRectF &geom)
+{
+    QRectF cursorRect = mInputMethod->focusObject()->microFocus(); // from the top of the screen
+    pos = QPointF(cursorRect.bottomLeft().x(),cursorRect.bottomLeft().y());
+    qreal heightOfTitlebar = 80.0; // Using magic number for now...
+    qreal screenHeight = (qreal)HbDeviceProfile::current().logicalSize().height();
+    if( ((screenHeight - cursorRect.bottomLeft().y()) > (cursorRect.y() - heightOfTitlebar))
+        || ((screenHeight - cursorRect.bottomLeft().y() + HbDeltaHeight ) > geom.height()) ) {
+        // this means there is amore space below inline text than at the top or we can fit spell Dialog
+        // below inline text
+        pos.setY(cursorRect.bottomLeft().y() + HbDeltaHeight);
+        size.setHeight(screenHeight - pos.y());
+    } else {
+        // this means there is amore space above inline text than below it
+        pos.setY(cursorRect.y() - geom.height() - HbDeltaHeight);
+        if (pos.y() < heightOfTitlebar) {
+            // this means that spell dialog can not be fit in from top of inline text, we need to trim it
+            pos.setY(heightOfTitlebar);
+        }
+        size.setHeight(cursorRect.y() - heightOfTitlebar - HbDeltaHeight);
+    }
+    if ( size.height() > geom.height()) {
+        size.setHeight(geom.height());
+    }
+    if ((pos.x() + size.width()) > (qreal)HbDeviceProfile::current().logicalSize().width()) {
+        // can not fit spell dialog to the right side of inline edit text.
+        pos.setX((qreal)HbDeviceProfile::current().logicalSize().width()- size.width());
+    }
+}
+
+void HbInputPrediction12KeyHandlerPrivate::cancelButtonPress()
+{
+    mTimer->stop();
+    mButtonDown = false;
+    mLastKey = 0;
+    mShiftKeyDoubleTap = false;
+}
+
+HbInputPrediction12KeyHandler::HbInputPrediction12KeyHandler(HbInputAbstractMethod *inputMethod)
+    :HbInputPredictionHandler(* new HbInputPrediction12KeyHandlerPrivate, inputMethod)
+{
+    Q_D(HbInputPrediction12KeyHandler);
+    d->q_ptr = this;
+}
+
+HbInputPrediction12KeyHandler::~HbInputPrediction12KeyHandler()
+{
+}
+
+/*!
+    this function lists different modes.
+*/
+void HbInputPrediction12KeyHandler::listInputModes(QVector<HbInputModeProperties>& modes) const
+{
+    Q_UNUSED(modes); 
+}
+
+/*!
+    filterEvent to handler keypress/release events.
+*/
+bool HbInputPrediction12KeyHandler::filterEvent(const QKeyEvent * event)
+{
+    Q_D(HbInputPrediction12KeyHandler);
+    HbInputFocusObject *focusObject = 0;
+    focusObject = d->mInputMethod->focusObject();
+
+    //If there was a handling for empty candidate-list, i.e. the engine did not predict
+    //any meaningful word for the input sequence.
+    if(!d->mCanContinuePrediction) {
+        int eventKey = event->key();
+        switch(eventKey) {
+        case Qt::Key_0:        
+        case Qt::Key_Space: {
+            if(d->mCandidates->size() && focusObject) {
+                //Remove the "?" mark
+                (*d->mCandidates)[d->mBestGuessLocation].chop(1);
+                d->updateEditor();
+                d->mCanContinuePrediction = true;
+                }
+            }
+            break;
+        case Qt::Key_Shift: {
+            if(event->type() == QEvent::KeyRelease && d->mShiftKeyDoubleTap) {
+                //Remove the "?" mark
+                deleteOneCharacter();
+                }
+            }
+        //For the following set of keys, it does not matter.
+        case Qt::Key_Backspace:
+        case Qt::Key_Delete:
+        case Qt::Key_Return:
+        case Qt::Key_Enter:
+		case Qt::Key_Asterisk:
+            break;
+        /* Behavior for other keys i.e. from key1 to key9 - 
+        To start the long press timer as we need to handle long press functionality i.e Enter corresponding number mapped to a key */
+        case Qt::Key_1:
+        case Qt::Key_2:
+        case Qt::Key_3:
+        case Qt::Key_4:
+        case Qt::Key_5:
+        case Qt::Key_6:
+        case Qt::Key_7:
+        case Qt::Key_8:
+        case Qt::Key_9: {
+            if (event->type() == QEvent::KeyRelease) {
+                d->mButtonDown = false;
+            } else {
+                d->mButtonDown = true;			
+                d->mLastKey = event->key();		
+                // start Long Press timer as corresponding number mapped to a key should be allowed to enter
+                d->mTimer->start(HbLongPressTimerTimeout);					
+            }
+            return true;
+        }
+        //The default behavior for any other key press is just to consume the key event and
+        //not to do anything.
+        default: {
+            return true;
+            }
+        }
+    }
+
+    // prediction mode can't handle Qt::Key_0, so we will emit a passFilterEvent
+    // this signal must be connected to by the plugin to a modehandler.
+    // which can handle it.
+    if (event->key() == Qt::Key_0) {
+        if (d->mLastKey != Qt::Key_0) {
+        actionHandler(HbInputModeHandler::HbInputModeActionCommit);
+        }
+        emit passFilterEvent(event);
+        d->mLastKey = Qt::Key_0;
+        return true;
+    } else {
+        if (d->mLastKey == Qt::Key_0) {
+            emit passActionHandler(HbInputModeActionCommit);
+        }
+        if (event->type() == QEvent::KeyRelease) {
+            return d->buttonReleased(event);
+        } else {
+            return d->buttonPressed(event);
+        }
+    }
+}
+
+/*!
+Action handler 
+*/
+bool HbInputPrediction12KeyHandler::actionHandler(HbInputModeAction action)
+{
+    Q_D(HbInputPrediction12KeyHandler);
+    bool ret = true;
+    switch (action) {
+        case HbInputModeActionReset:
+            HbInputPredictionHandler::actionHandler(HbInputModeActionReset);
+            d->mTimer->stop();
+            break;
+        case HbInputModeActionCancelButtonPress:
+            d->cancelButtonPress();
+            break;
+        case HbInputModeActionFocusRecieved:
+            HbInputPredictionHandler::actionHandler(HbInputModeActionSetCandidateList);
+            HbInputPredictionHandler::actionHandler(HbInputModeActionSetKeypad);
+            d->mTimer->stop();
+            break;
+        default:
+            ret = HbInputPredictionHandler::actionHandler(action);
+            break;
+    }
+    
+    return ret;
+}
+
+void HbInputPrediction12KeyHandler::mouseHandler(int cursorPosition, QMouseEvent* mouseEvent)
+{
+    Q_D(HbInputPrediction12KeyHandler);
+    if(d->mLastKey == Qt::Key_0) {
+        // zero key handling is done in the basic handler
+        emit passActionHandler(HbInputModeActionCommit);
+    } else {
+        HbInputPredictionHandler::mouseHandler(cursorPosition,mouseEvent);
+    }
+}
+
+
+/*!
+Returns true if preidciton engine is available and initialized.
+*/
+bool HbInputPrediction12KeyHandler::isActive() const
+{ 
+    Q_D(const HbInputPrediction12KeyHandler);
+    return d->mEngine != 0;
+}
+
+void HbInputPrediction12KeyHandler::processCustomWord(QString customWord)
+{
+    Q_D(HbInputPrediction12KeyHandler);
+    if (customWord.size()) {
+        d->launchSpellDialog(customWord);
+    }
+    return;	  
+}
+//EOF