Bug 609843 - Improve usage of Meego Input Method. - Minimal Meego/Qt Fixes. r=romaxa a=npodb
authorJeremias Bosch <jeremias.bosch@gmail.com>
Fri, 28 Jan 2011 13:00:31 +0200
changeset 61585 f2148c181f5597d8ae8fd4fe01b958518c678cdc
parent 61584 11db9166ba1c991ae12446351d40bf2e7bce11bd
child 61587 94a51a3b64d40175c2d32419cbe467604f183f4d
push idunknown
push userunknown
push dateunknown
reviewersromaxa, npodb
bugs609843
milestone2.0b11pre
Bug 609843 - Improve usage of Meego Input Method. - Minimal Meego/Qt Fixes. r=romaxa a=npodb
widget/src/qt/mozqwidget.cpp
widget/src/qt/mozqwidget.h
--- a/widget/src/qt/mozqwidget.cpp
+++ b/widget/src/qt/mozqwidget.cpp
@@ -69,16 +69,21 @@ static bool gFailedOpenKeyboard = false;
   For websites that focus editable elements during other operations for a very
   short time, we add some decoupling to prevent the VKB from appearing and 
   reappearing for a very short time. This global is set when the keyboard should
   be opened and if it is still set when a timer runs out, the VKB is really
   shown.
 */
 static bool gPendingVKBOpen = false;
 
+/*
+  Contains the last preedit String, this is needed in order to generate KeyEvents
+*/
+static QString gLastPreeditString;
+
 MozQWidget::MozQWidget(nsWindow* aReceiver, QGraphicsItem* aParent)
     : QGraphicsWidget(aParent),
       mReceiver(aReceiver)
 {
 #if (QT_VERSION >= QT_VERSION_CHECK(4, 6, 0))
      setFlag(QGraphicsItem::ItemAcceptsInputMethod);
      setAcceptTouchEvents(true);
 #endif
@@ -148,16 +153,20 @@ void MozQWidget::focusInEvent(QFocusEven
     // requesting the VKB without any timeout.
     if (gFailedOpenKeyboard)
         requestVKB(0);
 }
 
 void MozQWidget::focusOutEvent(QFocusEvent* aEvent)
 {
     mReceiver->OnFocusOutEvent(aEvent);
+    //OtherFocusReason most like means VKB was closed manual (done button)
+    if (aEvent->reason() == Qt::OtherFocusReason && gKeyboardOpen) {
+        hideVKB();
+    }
 }
 
 void MozQWidget::hoverEnterEvent(QGraphicsSceneHoverEvent* aEvent)
 {
     mReceiver->OnEnterNotifyEvent(aEvent);
 }
 
 void MozQWidget::hoverLeaveEvent(QGraphicsSceneHoverEvent* aEvent)
@@ -167,34 +176,144 @@ void MozQWidget::hoverLeaveEvent(QGraphi
 
 void MozQWidget::hoverMoveEvent(QGraphicsSceneHoverEvent* aEvent)
 {
     mReceiver->OnMoveEvent(aEvent);
 }
 
 void MozQWidget::keyPressEvent(QKeyEvent* aEvent)
 {
-#if (MOZ_PLATFORM_MAEMO==5)
+#if (MOZ_PLATFORM_MAEMO == 6)
+    if (!gKeyboardOpen ||
+       //those might get sended as KeyEvents, even in 'NormalMode'
+       aEvent->key() == Qt::Key_Space ||
+       aEvent->key() == Qt::Key_Return ||
+       aEvent->key() == Qt::Key_Backspace) {
+        mReceiver->OnKeyPressEvent(aEvent);
+    }
+#elif (MOZ_PLATFORM_MAEMO == 5)
     // Below removed to prevent invertion of upper and lower case
     // See bug 561234
     // mReceiver->OnKeyPressEvent(aEvent);
 #else
     mReceiver->OnKeyPressEvent(aEvent);
 #endif
 }
 
 void MozQWidget::keyReleaseEvent(QKeyEvent* aEvent)
 {
-#if (MOZ_PLATFORM_MAEMO==5)
+#if (MOZ_PLATFORM_MAEMO == 6)
+    if (!gKeyboardOpen ||
+       //those might get sended as KeyEvents, even in 'NormalMode'
+       aEvent->key() == Qt::Key_Space ||
+       aEvent->key() == Qt::Key_Return ||
+       aEvent->key() == Qt::Key_Backspace) {
+        mReceiver->OnKeyReleaseEvent(aEvent);
+    }
+    return;
+#elif (MOZ_PLATFORM_MAEMO == 5)
     // Below line should be removed when bug 561234 is fixed
     mReceiver->OnKeyPressEvent(aEvent);
 #endif
     mReceiver->OnKeyReleaseEvent(aEvent);
 }
 
+void MozQWidget::inputMethodEvent(QInputMethodEvent* aEvent)
+{
+    QString currentPreeditString = aEvent->preeditString();
+    QString currentCommitString = aEvent->commitString();
+
+    //first check for some controllkeys send as text...
+    if (currentCommitString == " ") {
+        sendPressReleaseKeyEvent(Qt::Key_Space, currentCommitString.unicode());
+    } else if (currentCommitString == "\n") {
+        sendPressReleaseKeyEvent(Qt::Key_Return, currentCommitString.unicode());
+    } else if (currentCommitString.isEmpty()) {
+        //if its no controllkey than check if current Commit is empty
+        //if yes than we have some preedit text here
+        if (currentPreeditString.length() == 1 && gLastPreeditString.isEmpty()) {
+            //Preedit text can change its entire look'a'like
+            //check if length of new compared to the old is 1,
+            //means that its a new startup
+            sendPressReleaseKeyEvent(0, currentPreeditString.unicode());
+        } else if (currentPreeditString.startsWith(gLastPreeditString)) {
+            //Length was not 1 or not a new startup
+            //check if the current preedit starts with the last one,
+            //if so: Add new letters (note: this can be more then one new letter)
+            const QChar * text = currentPreeditString.unicode();
+            for (int i = gLastPreeditString.length(); i < currentPreeditString.length(); i++) {
+                sendPressReleaseKeyEvent(0, &text[i]);
+            }
+        } else {
+            //last possible case, we had a PreeditString which was now completely changed.
+            //first, check if just one letter was removed (normal Backspace case!)
+            //if so: just send the backspace
+            QString tempLastPre = gLastPreeditString;
+            tempLastPre.truncate(gLastPreeditString.length()-1);
+            if (currentPreeditString == tempLastPre) {
+                sendPressReleaseKeyEvent(Qt::Key_Backspace);
+            } else if (currentPreeditString != tempLastPre) {
+                //more than one character changed, so just renew everything
+                //delete all preedit
+                for (int i = 0; i < gLastPreeditString.length(); i++) {
+                    sendPressReleaseKeyEvent(Qt::Key_Backspace);
+                }
+                //send new Preedit
+                const QChar * text = currentPreeditString.unicode();
+                for (int i = 0; i < currentPreeditString.length(); i++) {
+                    sendPressReleaseKeyEvent(0, &text[i]);
+                }
+            }
+        }
+    } else if (gLastPreeditString != currentCommitString) {
+        //User commited something
+        if (currentCommitString.length() == 1 && gLastPreeditString.isEmpty()) {
+            //if commit string ist one and there is no Preedit String
+            //case i.e. when no error correction is enabled in the system (default meego.com)
+            sendPressReleaseKeyEvent(0, currentCommitString.unicode());
+        } else {
+            //There is a Preedit, first remove it
+            for (int i = 0; i < gLastPreeditString.length(); i++) {
+                sendPressReleaseKeyEvent(Qt::Key_Backspace);
+            }
+            //Now push commited String into
+            const QChar * text = currentCommitString.unicode();
+            for (int i = 0; i < currentCommitString.length(); i++) {
+                sendPressReleaseKeyEvent(0, &text[i]);
+            }
+        }
+    }
+
+    //save preedit for next round.
+    gLastPreeditString = currentPreeditString;
+
+    //pre edit is continues string of new chars pressed by the user.
+    //if pre edit is changing rapidly without commit string first then user choose some overed text
+    //if commitstring comes directly after, forget about it
+    QGraphicsWidget::inputMethodEvent(aEvent);
+}
+
+void MozQWidget::sendPressReleaseKeyEvent(int key,
+                                          const QChar* letter,
+                                          bool autorep,
+                                          ushort count)
+{
+     Qt::KeyboardModifiers modifiers  = Qt::NoModifier;
+     if (letter && letter->isUpper()) {
+         modifiers = Qt::ShiftModifier;
+     }
+
+     QString text = letter ? QString(*letter) : QString();
+
+     QKeyEvent press(QEvent::KeyPress, key, modifiers, text, autorep, count);
+     mReceiver->OnKeyPressEvent(&press);
+     QKeyEvent release(QEvent::KeyRelease, key, modifiers, text, autorep, count);
+     mReceiver->OnKeyReleaseEvent(&release);
+}
+
 void MozQWidget::mouseDoubleClickEvent(QGraphicsSceneMouseEvent* aEvent)
 {
     // Qt sends double click event, but not second press event.
     mReceiver->OnButtonPressEvent(aEvent);
     mReceiver->OnMouseDoubleClickEvent(aEvent);
 }
 
 void MozQWidget::mouseMoveEvent(QGraphicsSceneMouseEvent* aEvent)
@@ -233,22 +352,25 @@ bool MozQWidget::event ( QEvent * event 
         return handled;
     }
     case (QEvent::Gesture):
     {
         PRBool handled = PR_FALSE;
         mReceiver->OnGestureEvent(static_cast<QGestureEvent*>(event),handled);
         return handled;
     }
+#if (MOZ_PLATFORM_MAEMO != 6)
+    // This does not work for maemo6, due to partially implemented IM framework
     case QEvent::InputMethod:
     {
         PRBool handled = PR_FALSE;
         mReceiver->imComposeEvent(static_cast<QInputMethodEvent*>(event),handled);
         return handled;
     }
+#endif
 
     default:
         break;
     }
 #endif
     return QGraphicsWidget::event(event);
 }
 
@@ -350,17 +472,17 @@ void MozQWidget::setModal(bool modal)
 
 QVariant MozQWidget::inputMethodQuery(Qt::InputMethodQuery aQuery) const
 {
     // The following query uses enums for the values, which are defined in
     // MeegoTouch headers, because this should also work in the pure Qt case
     // we use the values directly here. The original values are in the comments.
     if (static_cast<Qt::InputMethodQuery>(/*M::ImModeQuery*/ 10004 ) == aQuery)
     {
-        return QVariant(/*M::InputMethodModeDirect*/ 1 );
+        return QVariant(/*M::InputMethodModeNormal*/ 0 );
     }
 
     return QGraphicsWidget::inputMethodQuery(aQuery);
 }
 
 /**
   Request the VKB and starts a timer with the given timeout in milliseconds.
   If the request is not canceled when the timer runs out, the VKB is actually
@@ -417,16 +539,20 @@ void MozQWidget::showVKB()
 
 void MozQWidget::hideVKB()
 {
     if (gPendingVKBOpen) {
         // do not really open
         gPendingVKBOpen = false;
     }
 
+    if (!gKeyboardOpen) {
+        return;
+    }
+
 #if (QT_VERSION >= QT_VERSION_CHECK(4, 6, 0))
     QInputContext *inputContext = qApp->inputContext();
     if (!inputContext) {
         NS_WARNING("Closing SIP: but no input context");
         return;
     }
 
     QEvent request(QEvent::CloseSoftwareInputPanel);
--- a/widget/src/qt/mozqwidget.h
+++ b/widget/src/qt/mozqwidget.h
@@ -105,28 +105,30 @@ protected:
     virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent* aEvent);
     virtual void hoverMoveEvent(QGraphicsSceneHoverEvent* aEvent);
     virtual void keyPressEvent(QKeyEvent* aEvent);
     virtual void keyReleaseEvent(QKeyEvent* aEvent);
     virtual void mouseDoubleClickEvent(QGraphicsSceneMouseEvent* aEvent);
     virtual void mouseMoveEvent(QGraphicsSceneMouseEvent* aEvent);
     virtual void mousePressEvent(QGraphicsSceneMouseEvent* aEvent);
     virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent* aEvent);
+    virtual void inputMethodEvent(QInputMethodEvent* aEvent);
 
     virtual void wheelEvent(QGraphicsSceneWheelEvent* aEvent);
     virtual void paint(QPainter* aPainter, const QStyleOptionGraphicsItem* aOption, QWidget* aWidget = 0);
     virtual void resizeEvent(QGraphicsSceneResizeEvent* aEvent);
     virtual void closeEvent(QCloseEvent* aEvent);
     virtual void hideEvent(QHideEvent* aEvent);
     virtual void showEvent(QShowEvent* aEvent);
     virtual bool event(QEvent* aEvent);
 
     bool SetCursor(const QPixmap& aPixmap, int, int);
 
 private:
+    void sendPressReleaseKeyEvent(int key, const QChar* letter = 0, bool autorep = false, ushort count = 1);
     nsWindow *mReceiver;
 };
 
 class MozQGraphicsViewEvents
 {
 public:
 
     MozQGraphicsViewEvents(QGraphicsView* aView)