[meego-commits] 9764: Changes to devel:qt-mtf/meegotouch-inputmethodkeyboard

araujo no_reply at build.meego.com
Thu Nov 25 17:54:13 UTC 2010


Hi,
I have made the following changes to meegotouch-inputmethodkeyboard in project devel:qt-mtf. Please review and accept ASAP.

Thank You,
araujo

[This message was auto-generated]

---

Request #9764:

  submit:   home:araujo:branches:devel:qt-mtf/meegotouch-inputmethodkeyboard(r6)(cleanup) -> devel:qt-mtf/meegotouch-inputmethodkeyboard


Message:
    Update to version 0.5.12 (BMC#10501)

State:   new          2010-11-25T09:54:11 araujo
Comment: None



changes files:
--------------
--- meegotouch-inputmethodkeyboard.changes
+++ meegotouch-inputmethodkeyboard.changes
@@ -0,0 +1,21 @@
+* Thu Nov 25 2010 Luis Araujo <luis.araujo at collabora.co.uk> - 0.5.12
+- Update to release tag 0.5.12-1
+- Changes: Update the VKB settings according to the new layout
+- Fixes: wrong round corner value for word tracker background.
+- Fixes: Key spacing in landscape mode
+- Changes: use queued connection for key signals
+- Fixes: crash in ut_mimtoolbar.
+- Fixes: Caps lock notification is not shown
+- Changes: support disable toolbar buttons according toolbar xml definition.
+- Fixes: HW key navigation fails in phone dialer while entering numbers
+- Fixes: Ctrl related keys are not working in the number field and the phone application
+- New: Maintain a list of active keys within MImAbstractKeys
+- Changes: Remove active*Key members from MImAbstractKeyArea
+- New: Cache background of keys in common background pixmap
+- Changes: Use a visitor instead of MImAbstractKey::filterActiveKeys
+- Changes: Paint icons and backgrounds of keys separately
+- Fixes: Background caching during language change animation
+- Fixes: Don't touch style in WidgetBar constructor
+- Changes: use \r as vkb return key event text
+- Changes: vkb: send return as a key event, not as input method event
+

old:
----
  meegotouch-inputmethodkeyboard-0.5.9.tar.bz2

new:
----
  meegotouch-inputmethodkeyboard-0.5.12.tar.bz2

spec files:
-----------
--- meegotouch-inputmethodkeyboard.spec
+++ meegotouch-inputmethodkeyboard.spec
@@ -7,7 +7,7 @@
 
 Name:       meegotouch-inputmethodkeyboard
 Summary:    MeeGo Virtual Keyboard
-Version:    0.5.9
+Version:    0.5.12
 Release:    1
 Group:      System/GUI/Other
 License:    LGPL v2.1
@@ -23,12 +23,12 @@
 BuildRequires:  pkgconfig(QtNetwork)
 BuildRequires:  pkgconfig(QtGui)
 BuildRequires:  pkgconfig(meegotouch)
-BuildRequires:  pkgconfig(MeegoImFramework)
-BuildRequires:  pkgconfig(MeegoImEngine)
 BuildRequires:  pkgconfig(meegotouch-feedbackreactionmaps)
 BuildRequires:  pkgconfig(x11)
 BuildRequires:  pkgconfig(xproto)
 BuildRequires:  pkgconfig(xkbfile)
+BuildRequires:  meegotouch-inputmethodframework-devel
+BuildRequires:  libmeegoimenginewords-devel
 Provides:   duikeyboard >= 0.4.0
 Provides:   meegokeyboard > 0.4.2
 Obsoletes:   duikeyboard < 0.4.0

other changes:
--------------

++++++ meegotouch-inputmethodkeyboard-0.5.9.tar.bz2 -> meegotouch-inputmethodkeyboard-0.5.12.tar.bz2
--- NEWS
+++ NEWS
@@ -1,3 +1,29 @@
+0.5.11
+========
+
+* Changes in meego-keyboard:
+    - changed signature of following signals in MImAbstractKeyArea:
+        - keyPressed
+        - keyReleased
+        - keyClicked
+        - longKeyPressed
+
+0.5.10-1
+========
+
+* Internal changes in meego-keyboard:
+  - MImAbstractKey:
+    - renamed key() => model()
+    - added resetActiveKeys(): will release all stuck keys
+    - added visitActiveKeys(.): takes a functor, and applies it to all active
+      keys. Aborts iteration if functor returns true.
+    - added MImAbstractKeyVisitor interface
+    - added lastActiveKey(): returns most recently pressed key that wasn't
+      released yet.
+
+  - MImAbstractKeyArea:
+    - renamed updateButtonGeometriesForWith() => updateKeyGeometries()
+
 0.5.9-1
 =======
 
--- debian/changelog
+++ debian/changelog
@@ -1,8 +1,34 @@
-meego-keyboard (0.5.10~1) unstable; urgency=low
+meego-keyboard (0.5.13~1) unstable; urgency=low
 
-  *[UNRELEASED] 
+  * [UNRELEASED]
 
- -- Gibran Rodriguez <ext-gibran.rodriguez at nokia.com>  Wed, 03 Nov 2010 14:38:51 +0200
+ -- huaming wang <huaming.wang at nokia.com>  Tue, 23 Nov 2010 12:03:05 +0200
+
+meego-keyboard (0.5.12-1) unstable; urgency=low
+
+  * Fixes: NB#204452
+  * Fixes: NB#202563 - HW key navigation fails in phone dialer while entering numbers
+  * Fixes: NB#185293 - Ctrl related keys are not working in the number field and the phone application
+  * Implemented: SWP#TINME-1169 Hardware keyboard "text copied to clipboard" notification
+
+ -- huaming wang <huaming.wang at nokia.com>  Tue, 23 Nov 2010 11:59:17 +0200
+
+meego-keyboard (0.5.11-1) unstable; urgency=low
+
+  * [UNRELEASED]  
+  * Fixes: NB#199711 - COREWEB: /usr/bin/meego-im-uiserver 'MWidgetView::style MWidgetController::style style WidgetBar::styleChanged WidgetBar::WidgetBar'
+  * Fixes: NB#195713 - Pre-edit text disappears when emoticon is inserted in custom toolbar in widgetsgallery
+  * Implemented: SWP#TINME-1135
+
+ -- huaming wang <huaming.wang at nokia.com>  Wed, 17 Nov 2010 13:50:01 +0200
+
+meego-keyboard (0.5.10-1) unstable; urgency=low
+
+  * Fixes: NB#194728 - Settings: Latin America Spanish endonym wrong in active input method
+  * Implemented: SWP#TINME-1117
+  * Fixes: NB#194521, Dead key is desactivated when pressing shift
+  
+ -- Gibran Rodriguez <ext-gibran.rodriguez at nokia.com>  Wed, 10 Nov 2010 13:45:30 +0200
 
 meego-keyboard (0.5.9-1) unstable; urgency=low
 
--- debian/control
+++ debian/control
@@ -2,7 +2,7 @@
 Section: libs
 Priority: extra
 Maintainer: Mohammad Anwari <Mohammad.Anwari at nokia.com> 
-Build-Depends: debhelper (>= 5), libqt4-dev (>= 4.6), doxygen, libmeegoimenginewords-dev (>= 0.3), libmeegoimframework-dev (>= 0.19.27~1), libmeegotouch-dev (>= 0.20), libmeegoreactionmap-dev (>= 0.14.0-1), libxkbfile-dev (>= 1.0.6)
+Build-Depends: debhelper (>= 5), libqt4-dev (>= 4.6), doxygen, libmeegoimenginewords-dev (>= 0.3), libmeegoimframework-dev (>= 0.19.30~1), libmeegotouch-dev (>= 0.20), libmeegoreactionmap-dev (>= 0.14.0-1), libxkbfile-dev (>= 1.0.6)
 Standards-Version: 3.7.2
 
 Package: meego-keyboard
--- debian/rules
+++ debian/rules
@@ -15,6 +15,7 @@
 	CFLAGS += -O0
 else
 	CFLAGS += -O2
+        QMAKE_OPTIONS += DEFINES+=RELEASE_BUILD
 endif
 
 ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS)))
@@ -32,15 +33,10 @@
 	QMAKE_OPTIONS += COV_OPTION=on
 endif
 
-ifneq (,$(filter n900,$(TMP_BUILD_OPTS)))
-    QMAKE_OPTIONS += DEFINES+=NOCONTROLPANEL
-endif
-
 ifneq (,$(filter timestamps,$(TMP_BUILD_OPTS)))
     QMAKE_OPTIONS += DEFINES+=M_TIMESTAMP
 endif
 
-QMAKE_OPTIONS += DEFINES+=RELEASE_BUILD
 
 # shared library versions, option 1
 version=0.1.0
--- fixture_virtualkeyboard/fixture_virtualkeyboard.cpp
+++ fixture_virtualkeyboard/fixture_virtualkeyboard.cpp
@@ -103,14 +103,14 @@
 }
 
 const MImAbstractKey * FixtureVirtualKeyboard::getKey(const MImKeyArea * const widget,
-                                                  const QString &label) const
+                                                      const QString &label) const
 {
     Q_ASSERT(widget);
 
-    foreach (const MImKeyArea::ButtonRow &row, widget->rowList) {
-        foreach (const MImKey *button, row.buttons) {
-            if (button->label() == label) {
-                return button;
+    foreach (const MImKeyArea::KeyRow &row, widget->rowList) {
+        foreach (const MImKey *key, row.keys) {
+            if (key->label() == label) {
+                return key;
             }
         }
     }
@@ -119,14 +119,14 @@
 }
 
 const MImAbstractKey * FixtureVirtualKeyboard::getKey(const MImKeyArea * const widget,
-                                                  MImKeyBinding::KeyAction action) const
+                                                      MImKeyBinding::KeyAction action) const
 {
     Q_ASSERT(widget);
 
-    foreach (const MImKeyArea::ButtonRow &row, widget->rowList) {
-        foreach (const MImKey *button, row.buttons) {
-            if (button->binding().action() == action) {
-                return button;
+    foreach (const MImKeyArea::KeyRow &row, widget->rowList) {
+        foreach (const MImKey *key, row.keys) {
+            if (key->binding().action() == action) {
+                return key;
             }
         }
     }
--- fixture_virtualkeyboard/fixture_virtualkeyboard.h
+++ fixture_virtualkeyboard/fixture_virtualkeyboard.h
@@ -47,14 +47,14 @@
      *  \param label  button label of the virtualkeyboard
      */
     virtual const MImAbstractKey *getKey(const MImKeyArea * const widget,
-                                     const QString &label) const;
+                                         const QString &label) const;
     /*!
      *  \brief Get the IkeyButton (button details) by providing the action associated  by  a keyboard button
      *  \param widget the button area widget that is queried for the button of a given action.
      *  \param action  Action associated with a keyboard button
      */
     virtual const MImAbstractKey *getKey(const MImKeyArea * widget,
-                                     const MImKeyBinding::KeyAction action) const;
+                                         const MImKeyBinding::KeyAction action) const;
 };
 
 #endif
--- m-keyboard/common/keyeventhandler.cpp
+++ m-keyboard/common/keyeventhandler.cpp
@@ -20,6 +20,7 @@
 #include "mimabstractkey.h"
 #include "mimabstractkeyarea.h"
 
+#include <MTimestamp>
 #include <QDebug>
 
 KeyEventHandler::KeyEventHandler(QObject *parent)
@@ -33,21 +34,29 @@
 {
     bool ok = false;
 
-    ok = connect(eventSource, SIGNAL(keyPressed(const MImAbstractKey *, const QString &, bool)),
-                 this, SLOT(handleKeyPress(const MImAbstractKey *, const QString &, bool)));
+    ok = connect(eventSource, SIGNAL(keyPressed(const MImAbstractKey *, QString, bool)),
+                 this, SLOT(handleKeyPress(const MImAbstractKey *, const QString &, bool)),
+                 Qt::QueuedConnection);
     Q_ASSERT(ok);
 
-    ok = connect(eventSource, SIGNAL(keyReleased(const MImAbstractKey *, const QString &, bool)),
-                 this, SLOT(handleKeyRelease(const MImAbstractKey *, const QString &, bool)));
+    ok = connect(eventSource, SIGNAL(keyReleased(const MImAbstractKey *, QString, bool)),
+                 this, SLOT(handleKeyRelease(const MImAbstractKey *, const QString &, bool)),
+                 Qt::QueuedConnection);
     Q_ASSERT(ok);
 
-    ok = connect(eventSource, SIGNAL(keyClicked(const MImAbstractKey *, const QString &, bool, const QPoint &)),
-                 this, SLOT(handleKeyClick(const MImAbstractKey *, const QString &, bool, const QPoint &)));
+    ok = connect(eventSource, SIGNAL(keyClicked(const MImAbstractKey *, QString, bool, QPoint)),
+                 this, SLOT(handleKeyClick(const MImAbstractKey *, const QString &, bool, const QPoint &)),
+                 Qt::QueuedConnection);
+    Q_ASSERT(ok);
+
+    ok = connect(eventSource, SIGNAL(longKeyPressed(const MImAbstractKey *, QString, bool)),
+                 this, SLOT(handleLongKeyPress(const MImAbstractKey *, const QString &, bool)));
     Q_ASSERT(ok);
 }
 
 void KeyEventHandler::handleKeyPress(const MImAbstractKey *key, const QString &accent, bool upperCase)
 {
+    mTimestamp("KeyEventHandler", "start");
     const KeyEvent event = keyToKeyEvent(*key, QEvent::KeyPress, accent, upperCase);
     emit keyPressed(event);
 
@@ -57,10 +66,12 @@
     } else if (shiftHeldDown) {
         ignoreShiftClick = true;
     }
+    mTimestamp("KeyEventHandler", "end");
 }
 
 void KeyEventHandler::handleKeyRelease(const MImAbstractKey *key, const QString &accent, bool upperCase)
 {
+    mTimestamp("KeyEventHandler", "start");
     const KeyEvent event = keyToKeyEvent(*key, QEvent::KeyRelease, accent, upperCase);
 
     emit keyReleased(event);
@@ -69,11 +80,13 @@
         shiftHeldDown = false;
         emit shiftPressed(false);
     }
+    mTimestamp("KeyEventHandler", "end");
 }
 
 void KeyEventHandler::handleKeyClick(const MImAbstractKey *key, const QString &accent, bool upperCase,
                                      const QPoint &point)
 {
+    mTimestamp("KeyEventHandler", "start");
     const KeyEvent event = keyToKeyEvent(*key, QEvent::KeyRelease, accent, upperCase, point);
 
     if (event.qtKey() == Qt::Key_Shift && ignoreShiftClick) {
@@ -81,6 +94,14 @@
     } else {
         emit keyClicked(event);
     }
+    mTimestamp("KeyEventHandler", "end");
+}
+
+void KeyEventHandler::handleLongKeyPress(const MImAbstractKey *key, const QString &accent, bool upperCase)
+{
+    const KeyEvent event = keyToKeyEvent(*key, QEvent::KeyPress, accent, upperCase);
+
+    emit longKeyPressed(event);
 }
 
 KeyEvent KeyEventHandler::keyToKeyEvent(const MImAbstractKey &key, QKeyEvent::Type eventType,
@@ -92,9 +113,9 @@
     upperCase |= shiftHeldDown;
 
     if (accent.isEmpty()) {
-        event = key.key().toKeyEvent(eventType, upperCase);
+        event = key.model().toKeyEvent(eventType, upperCase);
     } else {
-        event = key.key().toKeyEvent(eventType, accent.at(0), upperCase);
+        event = key.model().toKeyEvent(eventType, accent.at(0), upperCase);
     }
     event.setPos(point);
 
--- m-keyboard/common/keyeventhandler.h
+++ m-keyboard/common/keyeventhandler.h
@@ -71,6 +71,13 @@
     void keyClicked(const KeyEvent &event);
 
     /*!
+     * \brief Emitted when key is long pressed.
+     *
+     * \param event key event
+     */
+    void longKeyPressed(const KeyEvent &event);
+
+    /*!
      * \brief Emitted when shift key is pressed or released
      * \param state Contains true is key is pressed
      */
@@ -92,6 +99,11 @@
      */
     void handleKeyClick(const MImAbstractKey *key, const QString &accent, bool upperCase, const QPoint &point);
 
+    /*!
+     * \brief Generates KeyEvent for given \a key and emits longKeyPress
+     */
+    void handleLongKeyPress(const MImAbstractKey *key, const QString &accent, bool upperCase);
+
 private:
     //! Turn key button into a KeyEvent, considering current accent and modifier state
     KeyEvent keyToKeyEvent(const MImAbstractKey &key, QKeyEvent::Type eventType,
--- m-keyboard/common/mhardwarekeyboard.cpp
+++ m-keyboard/common/mhardwarekeyboard.cpp
@@ -17,8 +17,13 @@
 #include <QDebug>
 #include <QX11Info>
 #include <QTextCodec>
+#include <QApplication>
+#include <QClipboard>
 #include <algorithm>
 
+#include <mplainwindow.h>
+#include <MSceneWindow>
+#include <MBanner>
 #include <mabstractinputmethodhost.h>
 
 #include "mhardwarekeyboard.h"
@@ -36,6 +41,7 @@
 
 namespace
 {
+    const int CopyValidForNotificationInterval(500); // in ms
     const Qt::KeyboardModifier FnLevelModifier = Qt::GroupSwitchModifier;
     const Qt::Key FnLevelKey = Qt::Key_AltGr;
     const Qt::Key SymKey = Qt::Key_Multi_key;
@@ -209,10 +215,8 @@
 {
     qDebug() << __PRETTY_FUNCTION__;
 
-    // We don't know how many requirements for direct mode will be yet besides
-    // symbol view is required for symbol key. So redirect all the keys to plugin,
-    // but now only handle symbol key.
     if (imMode != M::InputMethodModeDirect) {
+        connect(QApplication::clipboard(), SIGNAL(dataChanged()), this, SLOT(handleClipboardDataChange()));
         toggleCustomAutoRepeat(true);
 
         shiftShiftCapsLock = false;
@@ -254,6 +258,8 @@
 {
     qDebug() << __PRETTY_FUNCTION__;
 
+    disconnect(QApplication::clipboard(), SIGNAL(dataChanged()), this, 0);
+
     if (!preedit.isEmpty()) {
         inputMethodHost.sendPreeditString("", MInputMethod::PreeditKeyPress);
         preedit.clear();
@@ -286,11 +292,28 @@
 }
 
 
+void MHardwareKeyboard::handleClipboardDataChange()
+{
+    if (!lastCtrlCTime.isValid()
+        || (lastCtrlCTime.addMSecs(CopyValidForNotificationInterval) < QTime::currentTime())) {
+        lastCtrlCTime = QTime();
+        return;
+    }
+    lastCtrlCTime = QTime();
+
+    MBanner &textCopiedBanner(*new MBanner);
+    textCopiedBanner.setStyleName("InformationBanner");
+    textCopiedBanner.setTitle(qtTrId("qtn_comm_text_copied"));
+    textCopiedBanner.appear(MPlainWindow::instance(), MSceneWindow::DestroyWhenDone);
+}
+
+
 bool MHardwareKeyboard::actionOnPress(Qt::Key keyCode) const
 {
     static const Qt::Key pressPassKeys[] = {
         Qt::Key_Return, Qt::Key_Backspace, Qt::Key_Delete,
-        Qt::Key_Left, Qt::Key_Right, Qt::Key_Up, Qt::Key_Down };
+        Qt::Key_Left, Qt::Key_Right, Qt::Key_Up, Qt::Key_Down,
+        Qt::Key_Home, Qt::Key_End, Qt::Key_PageUp, Qt::Key_PageDown };
     static const Qt::Key * const keysEnd = pressPassKeys + ELEMENTS(pressPassKeys);
 
     return keysEnd != std::find(pressPassKeys, keysEnd, keyCode);
@@ -439,6 +462,56 @@
     }
 }
 
+bool MHardwareKeyboard::filterArrowKeys(QEvent::Type eventType, Qt::Key keyCode,
+                                        Qt::KeyboardModifiers modifiers,
+                                        QString text, bool autoRepeat, int count,
+                                        quint32 nativeModifiers) const
+{
+    // Home/End/PageUp/PageDown only when user is holding Fn down
+    if (!fnPressed && (nativeModifiers & FnModifierMask)
+        && (keyCode <= Qt::Key_PageDown) && (keyCode >= Qt::Key_Home)) {
+        Q_ASSERT(text.isEmpty());
+        switch (keyCode) {
+        case Qt::Key_Home:
+            keyCode = Qt::Key_Left;
+            break;
+        case Qt::Key_End:
+            keyCode = Qt::Key_Right;
+            break;
+        case Qt::Key_PageUp:
+            keyCode = Qt::Key_Up;
+            break;
+        case Qt::Key_PageDown:
+            keyCode = Qt::Key_Down;
+            break;
+        default:
+            Q_ASSERT_X(false, "MHardwareKeyboard::filterArrowKeys",
+                       "Left, Right, Up or Down with Fn modifier");
+            break;
+        }
+        inputMethodHost.sendKeyEvent(
+            QKeyEvent(eventType, keyCode, modifiers, text, autoRepeat, count),
+            MInputMethod::EventRequestEventOnly);
+        return true;
+    }
+
+    return false;
+}
+
+void MHardwareKeyboard::filterMaybeIgnoreFn(Qt::Key &keyCode, QString &text,
+                                            quint32 nativeScanCode, quint32 nativeModifiers) const
+{
+    // When Fn is pressed while also being locked, fn modifier is ignored for obtaining the text.
+    // We also ignore Fn completely with Control.
+    if ((keyCode != FnLevelKey) && ((fnPressed && (currentLockedMods & FnModifierMask))
+                                    || ((nativeModifiers & FnModifierMask)
+                                        && (nativeModifiers & ControlMask)))) {
+        text = keycodeToString(nativeScanCode, (nativeModifiers & ShiftMask) ? 1 : 0);
+        keyCode = text.isEmpty() ? Qt::Key_unknown : static_cast<Qt::Key>(QKeySequence(text)[0]);
+    }
+}
+
+
 bool MHardwareKeyboard::filterKeyPress(Qt::Key keyCode, Qt::KeyboardModifiers modifiers,
                                        QString text, bool autoRepeat, int count,
                                        quint32 nativeScanCode, quint32 nativeModifiers)
@@ -457,14 +530,11 @@
         return eaten;
     }
 
-    // When Fn is pressed while also being locked, fn modifier is ignored for obtaining the text
-    if ((keyCode != FnLevelKey) && fnPressed && (currentLockedMods & FnModifierMask)) {
-        text = keycodeToString(nativeScanCode, (nativeModifiers & ShiftMask) ? 1 : 0);
-        keyCode = text.isEmpty() ? Qt::Key_unknown : static_cast<Qt::Key>(QKeySequence(text)[0]);
-    }
+    filterMaybeIgnoreFn(keyCode, text, nativeScanCode, nativeModifiers);
+
     // When shift is latched (using lock modifier), shift modifier must not reverse the
     // effect of Lock
-    else if ((currentLatchedMods & LockMask) && (nativeModifiers & ShiftMask)
+    if ((currentLatchedMods & LockMask) && (nativeModifiers & ShiftMask)
              && (keyCode != Qt::Key_Shift) && (text.length() == 1) && text[0].isLetter()) {
         text = keycodeToString(nativeScanCode, (nativeModifiers & FnModifierMask) ? 3 : 1);
         keyCode = text.isEmpty() ? Qt::Key_unknown : static_cast<Qt::Key>(QKeySequence(text)[0]);
@@ -493,6 +563,8 @@
         if (nativeModifiers & SymModifierMask) {
             eatenBySymHandler = handlePressWithSymModifier(text, nativeScanCode, nativeModifiers);
             eaten = eaten || eatenBySymHandler;
+        } else if ((nativeModifiers & ControlMask) && (keyCode == Qt::Key_C)) {
+            lastCtrlCTime.start();
         }
 
         if (eaten && !eatenBySymHandler) {
@@ -534,6 +606,16 @@
             MInputMethod::EventRequestEventOnly);
         eaten = true;
     }
+    // We ignore Fn completely with Control.  keyCode and text have been adjusted already.
+    else if ((nativeModifiers & ControlMask) && (nativeModifiers & FnModifierMask)) {
+        inputMethodHost.sendKeyEvent(
+            QKeyEvent(QEvent::KeyPress, keyCode, modifiers, text, autoRepeat, count),
+            MInputMethod::EventRequestEventOnly);
+        eaten = true;
+    } else if (!eaten){
+        eaten = filterArrowKeys(QEvent::KeyPress, keyCode, modifiers, text, autoRepeat, count,
+                                nativeModifiers);
+    }
 
     // Relatch modifiers, X unlatches them on press but we want to unlatch on release
     // (and not even always on release, e.g. with Sym+aaab we unlatch only after the
@@ -562,11 +644,8 @@
         return eaten;
     }
 
-    // When Fn is pressed while also being locked, fn modifier is ignored for obtaining the text
-    if ((keyCode != FnLevelKey) && fnPressed && (currentLockedMods & FnModifierMask)) {
-        text = keycodeToString(nativeScanCode, (nativeModifiers & ShiftMask) ? 1 : 0);
-        keyCode = text.isEmpty() ? Qt::Key_unknown : static_cast<Qt::Key>(QKeySequence(text)[0]);
-    }
+    filterMaybeIgnoreFn(keyCode, text, nativeScanCode, nativeModifiers);
+
     const bool keyWasPressed(pressedKeys.contains(nativeScanCode));
     const quint32 pressNativeModifiers(pressedKeys.value(nativeScanCode));
 
@@ -615,6 +694,15 @@
                       modifiers & ~Qt::KeyboardModifiers(Qt::ShiftModifier), text, false, 1),
             MInputMethod::EventRequestEventOnly);
         eaten = true;
+    // We ignore Fn completely with Control.  keyCode and text have been adjusted already.
+    } else if ((nativeModifiers & ControlMask) && (nativeModifiers & FnModifierMask)) {
+        inputMethodHost.sendKeyEvent(
+            QKeyEvent(QEvent::KeyRelease, keyCode, modifiers, text, false, 1),
+            MInputMethod::EventRequestEventOnly);
+        eaten = true;
+    } else if (!eaten) {
+        eaten = filterArrowKeys(QEvent::KeyRelease, keyCode, modifiers, text, false, 1,
+                                nativeModifiers);
     }
 
     pressedKeys.remove(nativeScanCode);
--- m-keyboard/common/mhardwarekeyboard.h
+++ m-keyboard/common/mhardwarekeyboard.h
@@ -24,6 +24,7 @@
 #include <QString>
 #include <QEvent>
 #include <QTimer>
+#include <QTime>
 #include <QRegExp>
 #include "mxkb.h"
 #include "hwkbcharloopsmanager.h"
@@ -152,6 +153,9 @@
     //! Called when long press timer started on key press timeouts.
     void handleLongPressTimeout();
 
+    //! Display notification for Ctrl+C
+    void handleClipboardDataChange();
+
 private:
     //! \brief If character is not accepted by the client, try to find an acceptable character
     //! on another shift level
@@ -267,6 +271,16 @@
                           QString text,
                           quint32 nativeScanCode, quint32 nativeModifiers);
 
+    //! Helper for filterKeyRelease and filterKeyPress, handle arrow keys with Fn
+    bool filterArrowKeys(QEvent::Type eventType, Qt::Key keyCode,
+                         Qt::KeyboardModifiers modifiers,
+                         QString text, bool autoRepeat, int count,
+                         quint32 nativeModifiers) const;
+
+    //! Recognize cases where we need to pretend Fn modifier was not on and change
+    //! \a keyCode and \a text accordingly.
+    void filterMaybeIgnoreFn(Qt::Key &keyCode, QString &text,
+                             quint32 nativeScanCode, quint32 nativeModifiers) const;
 
     //! \brief Toggle custom autorepeat if \a enable is true, disable it otherwise
     //!
@@ -344,6 +358,8 @@
 
     HwKbDeadKeyMapper deadKeyMapper;
 
+    QTime lastCtrlCTime;
+
     friend class Ut_MHardwareKeyboard;
     friend class Ft_MHardwareKeyboard;
 };
--- m-keyboard/common/mimkeymodel.cpp
+++ m-keyboard/common/mimkeymodel.cpp
@@ -77,7 +77,7 @@
         break;
     case ActionReturn:
         key = Qt::Key_Return;
-        text = "\n";
+        text = "\r";
         break;
     case ActionTab:
         key = Qt::Key_Tab;
--- m-keyboard/common/mimkeymodel.h
+++ m-keyboard/common/mimkeymodel.h
@@ -117,7 +117,7 @@
 
     friend class KeyboardData;
     friend class Ut_MImKeyModel;
-    friend class Ut_KeyButton;
+    friend class Ut_MImKey;
     friend class Ut_MVirtualKeyboard;
 };
 
@@ -243,7 +243,7 @@
 
     friend class KeyboardData;
     friend class Ut_MImKeyModel;
-    friend class Ut_KeyButton;
+    friend class Ut_MImKey;
 };
 
 
--- m-keyboard/layouts/es_mx.xml
+++ m-keyboard/layouts/es_mx.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!DOCTYPE keyboard SYSTEM 'VirtualKeyboardLayout.dtd'>
-<keyboard title="Español-Latin America" version="1.0" catalog="es_mx" language="es_mx">
+<keyboard title="Español (América Latina)" version="1.0" catalog="" language="es_mx">
     <layout type="general" orientation="landscape">
         <section id="main">
           <row>
--- m-keyboard/layouts/symbols_ar.xml
+++ m-keyboard/layouts/symbols_ar.xml
@@ -360,9 +360,6 @@
             <key>
               <binding label="&#x0021;"/>
             </key>
-            <key>
-              <binding label="&#x066B;"/>
-            </key>
             <key style="special" rtl="true">
               <binding action="backspace"/>
             </key>
--- m-keyboard/m-keyboard.pro
+++ m-keyboard/m-keyboard.pro
@@ -3,6 +3,7 @@
 
 OBJECTS_DIR = .obj
 MOC_DIR = .moc
+M_MGEN_OUTDIR = .gen
 
 # we have this line temporarily until new libmeegotouch without rpath is integrated
 QT += xml
@@ -10,14 +11,14 @@
 contains( DEFINES, RELEASE_BUILD ) {
     QMAKE_CFLAGS -= -Werror
     QMAKE_CXXFLAGS -= -Werror
+    CONFIG += release
 } else {
     QMAKE_CFLAGS += -Werror
     QMAKE_CXXFLAGS += -Werror
+    CONFIG += debug
 } 
 
 CONFIG += plugin meegotouch meegoimengine meegoimenginewords meegoimframework meegoreactionmap
-#CONFIG += mcontrolpanel
-DEFINES += NOCONTROLPANEL
 
 CONFIG += link_pkgconfig
 PKGCONFIG += gconf-2.0 xkbfile
--- m-keyboard/mkeyboardhost.cpp
+++ m-keyboard/mkeyboardhost.cpp
@@ -20,7 +20,7 @@
 #include "mvirtualkeyboardstyle.h"
 #include "mvirtualkeyboard.h"
 #include "mhardwarekeyboard.h"
-#include "mimcorrectioncandidatewidget.h"
+#include "mimcorrectionhost.h"
 #include "keyboarddata.h"
 #include "layoutsmanager.h"
 #include "symbolview.h"
@@ -63,13 +63,17 @@
     bool DefaultInputMethodCorrectionSettingOption = true;
     const QString InputMethodCorrectionEngine("/meegotouch/inputmethods/correctionengine");
     const QString AutoCapsSentenceDelimiters(".?!¡¿"); // used as regexp character set content!
+    const QString AutoPunctuationTriggers(".,?!");
+    const int MaximumErrorCorrectionCandidate = 5;
     const int RotationDuration = 750; //! After vkb hidden, how long to wait until shown again
     const int AutoBackspaceDelay = 500;      // in ms
+    const int LongPressTime = 600;           // in ms
     const int BackspaceRepeatInterval = 100; // in ms
     const int MultitapTime = 1500;           // in ms
     const Qt::KeyboardModifier FnLevelModifier = Qt::GroupSwitchModifier;
     // This GConf item defines whether multitouch is enabled or disabled
     const char * const MultitouchSettings = "/meegotouch/inputmethods/multitouch/enabled";
+    const char * const NotificationObjectName = "ModifierLockNotification";
 }
 
 MKeyboardHost::CycleKeyHandler::CycleKeyHandler(MKeyboardHost &parent)
@@ -144,13 +148,12 @@
 MKeyboardHost::MKeyboardHost(MAbstractInputMethodHost *imHost, QObject *parent)
     : MAbstractInputMethod(imHost, parent),
       vkbStyleContainer(0),
-      correctionCandidateWidget(0),
+      correctionHost(0),
       vkbWidget(0),
       symbolView(0),
       imCorrectionEngine(0),
       inputMethodCorrectionSettings(new MGConfItem(InputMethodCorrectionSetting)),
       inputMethodCorrectionEngine(new MGConfItem(InputMethodCorrectionEngine)),
-      engineReady(false),
       angle(M::Angle0),
       rotationInProgress(false),
       correctionEnabled(false),
@@ -159,7 +162,7 @@
       autoCapsTriggered(false),
       cursorPos(-1),
       inputMethodMode(M::InputMethodModeNormal),
-      backSpaceTimer(),
+      backspaceTimer(),
       shiftHeldDown(false),
       activeState(MInputMethod::OnScreen),
       modifierLockOnBanner(0),
@@ -167,7 +170,9 @@
       enableMultiTouch(false),
       cycleKeyHandler(new CycleKeyHandler(*this)),
       currentIndicatorDeadKey(false),
-      engineLayoutDirty(false)
+      engineLayoutDirty(false),
+      backspaceMode(NormalBackspace),
+      wordTrackerSuggestionAcceptedWithSpace(false)
 {
     displayHeight = MPlainWindow::instance()->visibleSceneSize(M::Landscape).height();
     displayWidth  = MPlainWindow::instance()->visibleSceneSize(M::Landscape).width();
@@ -182,7 +187,9 @@
     // It uses animation to carry out the orientation change transform
     // (e.g. rotation and position animation). We do this because transform
     // happens in the scene, not in the view (MWindow) anymore.
+    // Enforcing full viewport updates helps to paint correctly in software mode.
     MPlainWindow::instance()->sceneManager()->appearSceneWindowNow(sceneWindow);
+    MPlainWindow::instance()->setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
 
     // Because we set vkbWidget as a child of sceneWindow the vkb
     // will always be in correct orientation. However the animation will be
@@ -210,6 +217,8 @@
             this, SLOT(handleKeyPress(const KeyEvent &)));
     connect(vkbWidget, SIGNAL(keyReleased(const KeyEvent &)),
             this, SLOT(handleKeyRelease(const KeyEvent &)));
+    connect(vkbWidget, SIGNAL(longKeyPressed(const KeyEvent &)),
+            this, SLOT(handleLongKeyPress(const KeyEvent &)));
     connect(vkbWidget, SIGNAL(showSymbolViewRequested()),
             this, SLOT(showSymbolView()));
 
@@ -243,7 +252,7 @@
                  this, SLOT(sendKeyEvent(const QKeyEvent &)));
     Q_ASSERT(ok);
     ok = connect(imToolbar, SIGNAL(sendStringRequest(const QString &)),
-                 this, SLOT(sendString(const QString &)));
+                 this, SLOT(sendStringFromToolbar(const QString &)));
     Q_ASSERT(ok);
     ok = connect(imToolbar, SIGNAL(copyPasteClicked(CopyPasteState)),
                  this, SLOT(sendCopyPaste(CopyPasteState)));
@@ -313,6 +322,8 @@
             this, SLOT(handleKeyPress(const KeyEvent &)));
     connect(symbolView, SIGNAL(keyReleased(const KeyEvent &)),
             this, SLOT(handleKeyRelease(const KeyEvent &)));
+    connect(symbolView, SIGNAL(longKeyPressed(const KeyEvent &)),
+            this, SLOT(handleLongKeyPress(const KeyEvent &)));
 
     symbolView->setSharedHandleArea(sharedHandleArea);
     sharedHandleArea->watchOnWidget(symbolView);
@@ -321,24 +332,19 @@
             this, SLOT(handleVirtualKeyboardLayoutChanged(const QString &)));
 
     connect(vkbWidget, SIGNAL(shiftLevelChanged()),
+            this, SLOT(handleVirtualKeyboardCapsLock()));
+
+    connect(vkbWidget, SIGNAL(shiftLevelChanged()),
             this, SLOT(updateSymbolViewLevel()));
 
     connect(hardwareKeyboard, SIGNAL(shiftStateChanged()),
             this, SLOT(updateSymbolViewLevel()));
 
-    connect(vkbWidget, SIGNAL(copyPasteRequest(CopyPasteState)),
-            this, SLOT(sendCopyPaste(CopyPasteState)));
-    connect(vkbWidget, SIGNAL(sendKeyEventRequest(const QKeyEvent &)),
-            this, SLOT(sendKeyEvent(const QKeyEvent &)));
-    connect(vkbWidget, SIGNAL(sendStringRequest(const QString &)),
-            this, SLOT(sendString(const QString &)));
-
     if (!inputMethodCorrectionEngine->value().isNull()) {
         imCorrectionEngine = MImEngineWordsInterfaceFactory::instance()->createEngine(
                                 inputMethodCorrectionEngine->value().toString());
 
         if (imCorrectionEngine) {
-            engineReady = true;
             initializeInputEngine();
             connect(inputMethodCorrectionSettings, SIGNAL(valueChanged()),
                     this, SLOT(synchronizeCorrectionSetting()));
@@ -350,8 +356,8 @@
 
     feedbackPlayer = MComponentData::feedbackPlayer();
 
-    backSpaceTimer.setSingleShot(true);
-    connect(&backSpaceTimer, SIGNAL(timeout()), this, SLOT(autoBackspace()));
+    backspaceTimer.setSingleShot(true);
+    connect(&backspaceTimer, SIGNAL(timeout()), this, SLOT(autoBackspace()));
 
     // hide main layout when symbol view is shown to improve performance
     connect(symbolView, SIGNAL(opened()), vkbWidget, SLOT(hideMainArea()));
@@ -367,8 +373,8 @@
     vkbWidget = 0;
     delete symbolView;
     symbolView = 0;
-    delete correctionCandidateWidget;
-    correctionCandidateWidget = 0;
+    delete correctionHost;
+    correctionHost = 0;
     delete sceneWindow;
     sceneWindow = 0;
     delete vkbStyleContainer;
@@ -379,20 +385,21 @@
         MImEngineWordsInterfaceFactory::instance()->deleteEngine(imCorrectionEngine);
         imCorrectionEngine = 0;
     }
-    backSpaceTimer.stop();
+    backspaceMode = NormalBackspace;
+    backspaceTimer.stop();
     LayoutsManager::destroyInstance();
 }
 
 void MKeyboardHost::createCorrectionCandidateWidget()
 {
     // construct correction candidate widget
-    correctionCandidateWidget = new MImCorrectionCandidateWidget(sceneWindow);
-    correctionCandidateWidget->hide();
+    correctionHost = new MImCorrectionHost(sceneWindow);
+    correctionHost->hideCorrectionWidget();
 
-    connect(correctionCandidateWidget, SIGNAL(regionUpdated(const QRegion &)),
+    connect(correctionHost, SIGNAL(regionUpdated(const QRegion &)),
             this, SLOT(handleRegionUpdate(const QRegion &)));
-    connect(correctionCandidateWidget, SIGNAL(candidateClicked(const QString &)),
-            this, SLOT(updatePreedit(const QString &)));
+    connect(correctionHost, SIGNAL(candidateClicked(const QString &)),
+            this, SLOT(commitString(const QString &)));
 }
 
 
@@ -426,14 +433,6 @@
 
 void MKeyboardHost::show()
 {
-    QWidget *p = MPlainWindow::instance();
-
-    if (p->nativeParentWidget()) {
-        p = p->nativeParentWidget();
-    }
-
-    p->raise(); // make sure the window gets displayed
-
     if (activeState == MInputMethod::Hardware) {
         if (!hardwareKeyboard->symViewAvailable())
             symbolView->hideSymbolView();
@@ -458,7 +457,7 @@
 
 void MKeyboardHost::hide()
 {
-    correctionCandidateWidget->disappear();
+    correctionHost->hideCorrectionWidget();
     symbolView->hideSymbolView();
     vkbWidget->hideKeyboard();
 }
@@ -468,12 +467,13 @@
 {
     preedit = preeditString;
     candidates.clear();
-    correctionCandidateWidget->setPreeditString(preeditString);
     if (imCorrectionEngine) {
         imCorrectionEngine->clearEngineBuffer();
         imCorrectionEngine->reselectString(preeditString);
         candidates = imCorrectionEngine->candidates();
+        correctionHost->setCandidates(candidates);
     }
+    updatePreedit(preedit, candidates.count());
 }
 
 
@@ -600,12 +600,13 @@
 
 void MKeyboardHost::resetInternalState()
 {
-    backSpaceTimer.stop();
+    wordTrackerSuggestionAcceptedWithSpace = false;
+    backspaceMode = NormalBackspace;
+    backspaceTimer.stop();
     preedit.clear();
     candidates.clear();
-    correctionCandidateWidget->setPreeditString("");
-    correctionCandidateWidget->disappear();
-    if (engineReady)
+    correctionHost->reset();
+    if (imCorrectionEngine)
         imCorrectionEngine->clearEngineBuffer();
 }
 
@@ -618,7 +619,7 @@
 
     symbolView->prepareToOrientationChange();
     vkbWidget->prepareToOrientationChange();
-    correctionCandidateWidget->prepareToOrientationChange();
+    correctionHost->prepareToOrientationChange();
 }
 
 void MKeyboardHost::finalizeOrientationChange()
@@ -637,10 +638,10 @@
     }
 
     // Finalize candidate list after so its region will apply.
-    correctionCandidateWidget->finalizeOrientationChange();
+    correctionHost->finalizeOrientationChange();
 
     // If correction candidate widget was open we need to reposition it.
-    if (correctionCandidateWidget->isVisible()) {
+    if (correctionHost->isActive()) {
         bool success = false;
         const QRect rect = inputMethodHost()->preeditRectangle(success);
         QRect localRect;
@@ -648,11 +649,10 @@
         // the correct coordinates for pre-edit rectangle, so rect here
         // is null.
         if (success && !rect.isNull() && rotateRect(rect, localRect)) {
-            const int bottomLimit = sceneWindow->mapRectFromScene(vkbWidget->mainAreaSceneRect()).top();
-
-            correctionCandidateWidget->setPosition(localRect, bottomLimit);
+            correctionHost->setPosition(localRect);
+            correctionHost->showCorrectionWidget(correctionHost->candidateMode());
         } else {
-            correctionCandidateWidget->disappear();
+            correctionHost->hideCorrectionWidget();
         }
     }
 
@@ -735,29 +735,14 @@
 
 void MKeyboardHost::handleMouseClickOnPreedit(const QPoint &mousePos, const QRect &preeditRect)
 {
-    if (candidates.size() <= 1)
+    Q_UNUSED(mousePos);
+    Q_UNUSED(preeditRect);
+    // Shows suggestion list when there are some candidates.
+    // Even show suggestion list when there is only original input word in candidates.
+    if (candidates.size() <= 0)
         return;
 
-    QPoint localMousePos;
-    QRect localRect;
-
-    // Bottom limit for positioning candidate list widget. Keep above keyboard.
-    const int bottomLimit = sceneWindow->mapRectFromScene(vkbWidget->mainAreaSceneRect()).top();
-
-    // Use preeditRect if one was passed (not null).
-    if (!preeditRect.isNull() && rotateRect(preeditRect, localRect)) {
-        correctionCandidateWidget->setPreeditString(preedit);
-        correctionCandidateWidget->setCandidates(candidates);
-        correctionCandidateWidget->setPosition(localRect, bottomLimit);
-    } else if (rotatePoint(mousePos, localMousePos)) {
-        correctionCandidateWidget->setPreeditString(preedit);
-        correctionCandidateWidget->setCandidates(candidates);
-        correctionCandidateWidget->setPosition(localMousePos, bottomLimit);
-    } else {
-        return;
-    }
-
-    correctionCandidateWidget->showWidget();
+    correctionHost->showCorrectionWidget(MImCorrectionHost::WordListMode);
 }
 
 
@@ -771,20 +756,23 @@
 void MKeyboardHost::handleAppOrientationChange(int angle)
 {
     // The application receiving input has changed its orientation. Let's change ours.
-    MPlainWindow::instance()->setOrientationAngle((M::OrientationAngle)angle);
+    MPlainWindow::instance()->setOrientationAngle(static_cast<M::OrientationAngle>(angle));
+    this->angle = static_cast<M::OrientationAngle>(angle);
 }
 
 
-void MKeyboardHost::updatePreedit(const QString &updatedString)
+void MKeyboardHost::commitString(const QString &updatedString)
 {
-    MInputMethod::PreeditFace face = MInputMethod::PreeditDefault;
-
-    if (candidates.count() < 2)
-        face = MInputMethod::PreeditNoCandidates;
-
-    preedit = updatedString;
-    inputMethodHost()->sendPreeditString(updatedString, face);
-    correctionCandidateWidget->setPreeditString(updatedString);
+    if (candidates.count() > 1) {
+        int suggestionIndex = candidates.indexOf(updatedString);
+        if (suggestionIndex >= 0) {
+            qDebug() << "save index:" << suggestionIndex;
+            imCorrectionEngine->setSuggestedCandidateIndex(suggestionIndex);
+        }
+    }
+    imCorrectionEngine->saveAndClearEngineBuffer();
+    inputMethodHost()->sendCommitString(updatedString);
+    preedit.clear();
 }
 
 
@@ -792,12 +780,8 @@
 {
     // note: backspace shouldn't start accurate mode
     if (preedit.length() > 0) {
-        if (!backSpaceTimer.isActive()) {
+        if (!backspaceTimer.isActive()) {
             setPreedit(preedit.left(preedit.length() - 1));
-            const MInputMethod::PreeditFace face
-                = candidates.count() < 2
-                ? MInputMethod::PreeditNoCandidates : MInputMethod::PreeditDefault;
-            inputMethodHost()->sendPreeditString(preedit, face);
         } else {
             resetInternalState();
             inputMethodHost()->sendCommitString("");
@@ -820,7 +804,7 @@
 
 void MKeyboardHost::autoBackspace()
 {
-    backSpaceTimer.start(BackspaceRepeatInterval); // Must restart before doBackspace
+    backspaceTimer.start(BackspaceRepeatInterval); // Must restart before doBackspace
     doBackspace();
 }
 
@@ -844,7 +828,17 @@
         requestType = MInputMethod::EventRequestBoth;
 
     } else if (event.qtKey() == Qt::Key_Backspace) {
-        backSpaceTimer.start(AutoBackspaceDelay);
+        if (correctionHost->isActive()
+            && correctionHost->candidateMode() == MImCorrectionHost::WordTrackerMode) {
+            // hide word tracker when backspace key press
+            correctionHost->hideCorrectionWidget();
+            // WordTrackerBackspace mode: hide word tracker when backspace key press.
+            // And remove preedit if holding backspace long enough. But does nothing
+            // for backspace key release.
+            startBackspace(WordTrackerBackspace);
+        } else {
+            startBackspace(NormalBackspace);
+        }
     }
 
     inputMethodHost()->sendKeyEvent(event.toQKeyEvent(), requestType);
@@ -869,9 +863,16 @@
 
         requestType = MInputMethod::EventRequestBoth;
 
-    } else if ((event.qtKey() == Qt::Key_Backspace) && backSpaceTimer.isActive()) {
-        backSpaceTimer.stop();
-        doBackspace();
+    } else if ((event.qtKey() == Qt::Key_Backspace)) {
+        if ( backspaceTimer.isActive()) {
+            backspaceTimer.stop();
+            // If the backspace Mode is WordTrackerBackspace, don't need to
+            // do backspace.
+            if (backspaceMode != WordTrackerBackspace) {
+                doBackspace();
+            }
+            backspaceMode = NormalBackspace;
+        }
     }
 
     inputMethodHost()->sendKeyEvent(event.toQKeyEvent(), requestType);
@@ -895,11 +896,12 @@
         }
 
         // Candidates widget
-        if (correctionCandidateWidget && correctionCandidateWidget->isVisible()) {
-            correctionCandidateWidget->paintReactionMap(reactionMap, view);
+        if (correctionHost && correctionHost->isActive()) {
+            correctionHost->paintReactionMap(reactionMap, view);
 
-            // Correction candidate widget always occupies whole screen.
-            continue;
+            // Correction candidate widget occupies whole screen when it is WordListMode.
+            if (correctionHost->candidateMode() == MImCorrectionHost::WordListMode)
+                continue;
         }
 
         // Paint either symview or vkb widget reactive areas.
@@ -1004,8 +1006,41 @@
     }
 }
 
+void MKeyboardHost::handleLongKeyPress(const KeyEvent &event)
+{
+    if (event.qtKey() == Qt::Key_Space
+        && correctionEnabled
+        && correctionHost->isActive()
+        && correctionHost->candidateMode() == MImCorrectionHost::WordTrackerMode
+        && candidates.size() > 0) {
+        // long tap space key when word tracker is visible will switch to word list.
+        correctionHost->showCorrectionWidget(MImCorrectionHost::WordListMode);
+    }
+}
+
+void MKeyboardHost::sendCommitStringOrReturnEvent(const KeyEvent &event) const
+{
+    // We send return as a normal key event instead of an input method event so
+    // that it properly triggers returnPressed signal emission in MTextEdit and
+    // QLineEdit (and possibly in other similar targets).
+    if (event.qtKey() == Qt::Key_Return) {
+        inputMethodHost()->sendKeyEvent(KeyEvent(event, QEvent::KeyPress).toQKeyEvent(), MInputMethod::EventRequestEventOnly);
+        inputMethodHost()->sendKeyEvent(event.toQKeyEvent(), MInputMethod::EventRequestEventOnly);
+    } else {
+        inputMethodHost()->sendCommitString(event.text());
+    }
+}
+
 void MKeyboardHost::handleTextInputKeyClick(const KeyEvent &event)
 {
+    const bool wordTrackerSuggestionAcceptedWithSpacePrev(wordTrackerSuggestionAcceptedWithSpace);
+    if (!((event.specialKey() == KeyEvent::Sym)
+          || (event.specialKey() == KeyEvent::Switch)
+          || (event.specialKey() == KeyEvent::Sym)
+          || (event.qtKey() == Qt::Key_Shift))) {
+        wordTrackerSuggestionAcceptedWithSpace = false;
+    }
+
     // Discard KeyPress & Drop type of events.
     if (event.type() != QEvent::KeyRelease
 
@@ -1036,32 +1071,49 @@
         if (preedit.length() > 0) {
             // we just entered accurate mode. send the previous preedit stuff.
             inputMethodHost()->sendCommitString(preedit);
+            if (imCorrectionEngine)
+                imCorrectionEngine->clearEngineBuffer();
             preedit.clear();
         }
 
-        inputMethodHost()->sendCommitString(text);
+        sendCommitStringOrReturnEvent(event);
 
     } else if ((event.qtKey() == Qt::Key_Space) || (event.qtKey() == Qt::Key_Return) || (event.qtKey() == Qt::Key_Tab)) {
-        // commit string
-        inputMethodHost()->sendCommitString(preedit);
-        if (lastClickEvent.specialKey() != KeyEvent::CycleSet) {
-            imCorrectionEngine->setSuggestedCandidateIndex(correctionCandidateWidget->activeIndex());
-            imCorrectionEngine->saveAndClearEngineBuffer();
-            correctionCandidateWidget->setPreeditString("");
+        // commit suggestion if correction candidate widget is visible and with popupMode
+        // or ignore it if correction widget is visible and with suggestionlist mode
+        // otherwise commit preedit
+        if (event.qtKey() == Qt::Key_Space
+            && correctionHost->isActive()) {
+            if (correctionHost->candidateMode() == MImCorrectionHost::WordTrackerMode) {
+                wordTrackerSuggestionAcceptedWithSpace = true;
+                inputMethodHost()->sendCommitString(correctionHost->suggestion());
+            } else {
+                // ignore space click when word list is visible.
+                return;
+            }
         } else {
-            imCorrectionEngine->clearEngineBuffer();
+            inputMethodHost()->sendCommitString(preedit);
         }
-        // send trailing space
-        inputMethodHost()->sendCommitString(text);
 
+        if (lastClickEvent.specialKey() != KeyEvent::CycleSet) {
+            candidates.clear();
+            correctionHost->reset();
+        }
+        // Send trailing space.
+        sendCommitStringOrReturnEvent(event);
+
+        imCorrectionEngine->clearEngineBuffer();
         preedit.clear();
+    } else if (wordTrackerSuggestionAcceptedWithSpacePrev && (text.length() == 1)
+               && AutoPunctuationTriggers.contains(text[0])) {
+        doBackspace();
+        inputMethodHost()->sendCommitString(text + " ");
     } else {
         // common case: just append stuff to current preedit
         preedit += text;
 
         candidates.clear();
 
-        qDebug() << "event touch point:" << event.pos();
         // send touch point to engine if not in symbol view
         // otherwise send character to engine.
         if ( !symbolView->isActive())
@@ -1070,11 +1122,27 @@
             imCorrectionEngine->appendCharacter(text.at(0));
 
         candidates = imCorrectionEngine->candidates();
+        MImEngine::DictionaryType sourceDictionaryType = imCorrectionEngine->candidateSource(0);
+
+        correctionHost->setCandidates(candidates);
 
-        const MInputMethod::PreeditFace face
-            = candidates.count() < 2
-            ? MInputMethod::PreeditNoCandidates : MInputMethod::PreeditDefault;
-        inputMethodHost()->sendPreeditString(preedit, face);
+        updatePreedit(preedit, candidates.count());
+
+        // because the written word (preedit) should be on the top of candidate list.
+        // So when there are some other candidates and the preedit is not a valid
+        // dictionary word, show word tracker.
+        if (candidates.count() > 1
+            && sourceDictionaryType == MImEngine::DictionaryTypeInvalid) {
+            bool success = false;
+            const QRect rect = inputMethodHost()->preeditRectangle(success);
+            QRect localRect;
+            if (success && !rect.isNull() && rotateRect(rect, localRect)) {
+                correctionHost->setPosition(localRect);
+                correctionHost->showCorrectionWidget(MImCorrectionHost::WordTrackerMode);
+            }
+        } else {
+            correctionHost->hideCorrectionWidget();
+        }
     }
 }
 
@@ -1089,7 +1157,7 @@
 
     qDebug() << __PRETTY_FUNCTION__ << "- used language:" << language;
 
-    if (engineReady) {
+    if (imCorrectionEngine) {
         // TODO: maybe we should check return values here and in case of failure
         // be always in accurate mode, for example
         imCorrectionEngine->setLanguage(language, MImEngine::LanguagePriorityPrimary);
@@ -1097,8 +1165,8 @@
         updateEngineKeyboardLayout();
         synchronizeCorrectionSetting();
         imCorrectionEngine->disablePrediction();
-        imCorrectionEngine->disableCompletion();
-        imCorrectionEngine->setMaximumErrors(6);
+        imCorrectionEngine->enableCompletion();
+        imCorrectionEngine->setMaximumCandidates(MaximumErrorCorrectionCandidate);
         imCorrectionEngine->setExactWordPositionInList(MImEngine::ExactInListFirst);
     }
 }
@@ -1123,7 +1191,7 @@
         inputMethodHost()->setGlobalCorrectionEnabled(false);
         correctionEnabled = false;
     } else {
-        if (!engineReady) {
+        if (!imCorrectionEngine) {
             inputMethodHost()->setGlobalCorrectionEnabled(false);
             correctionEnabled = false;
             return;
@@ -1265,6 +1333,15 @@
     inputMethodHost()->sendCommitString(text);
 }
 
+void MKeyboardHost::sendStringFromToolbar(const QString &text)
+{
+    if (!preedit.isEmpty()) {
+        sendString(preedit);
+    }
+    reset();
+    sendString(text);
+}
+
 void MKeyboardHost::setToolbar(QSharedPointer<const MToolbarData> toolbar)
 {
     if (toolbar && toolbar->isVisible()) {
@@ -1484,6 +1561,20 @@
     }
 }
 
+void MKeyboardHost::handleVirtualKeyboardCapsLock()
+{
+    if (activeState != MInputMethod::OnScreen)
+        return;
+
+    if (vkbWidget->shiftStatus() == ModifierLockedState) {
+        //% "Caps lock on"
+        QString lockOnNotificationLabel = qtTrId("qtn_hwkb_caps_lock");
+        showLockOnInfoBanner(lockOnNotificationLabel);
+    } else if (modifierLockOnBanner) {
+        hideLockOnInfoBanner();
+    }
+}
+
 void MKeyboardHost::showLockOnInfoBanner(const QString &notification)
 {
     if (modifierLockOnBanner) {
@@ -1492,6 +1583,7 @@
         //TODO: discuss with UI designer whether we need to specify
         // the disappear time out.
         modifierLockOnBanner = new MBanner;
+        modifierLockOnBanner->setObjectName(NotificationObjectName);
         modifierLockOnBanner->setTitle(notification);
         modifierLockOnBanner->appear(MSceneWindow::DestroyWhenDone);
     }
@@ -1564,7 +1656,7 @@
 
 void MKeyboardHost::updateEngineKeyboardLayout()
 {
-    if (!engineReady || !engineLayoutDirty)
+    if (!imCorrectionEngine || !engineLayoutDirty)
         return;
 
     if (activeState == MInputMethod::OnScreen) {
@@ -1572,3 +1664,23 @@
     }
     engineLayoutDirty = false;
 }
+
+void MKeyboardHost::updatePreedit(const QString &string, int candidateCount)
+{
+    // preedit style type depends on candidateCount.
+    // candidateCount     styleType
+    //  0 or 1            PreeditNoCandidates
+    //  1 or >1           PreeditDefault
+    MInputMethod::PreeditFace face = MInputMethod::PreeditNoCandidates;
+    if (candidateCount > 1) {
+        face = MInputMethod::PreeditDefault;
+    }
+
+    inputMethodHost()->sendPreeditString(string, face);
+}
+
+void MKeyboardHost::startBackspace(MKeyboardHost::BackspaceMode mode)
+{
+    backspaceMode = mode;
+    backspaceTimer.start(AutoBackspaceDelay);
+}
--- m-keyboard/mkeyboardhost.h
+++ m-keyboard/mkeyboardhost.h
@@ -29,7 +29,7 @@
 
 class MFeedbackPlayer;
 class MGConfItem;
-class MImCorrectionCandidateWidget;
+class MImCorrectionHost;
 class MSceneWindow;
 class MVirtualKeyboard;
 class MVirtualKeyboardStyleContainer;
@@ -97,13 +97,21 @@
      */
     void handleKeyRelease(const KeyEvent &event);
 
+    /*!
+     * Handles user long press a key.
+     * \param event internal key event
+     */
+    void handleLongKeyPress(const KeyEvent &event);
+
     //! \brief Draws reaction maps for the topmost widget.
     void updateReactionMaps();
 
     /*!
-     * Update the pre-edit word
+     * \brief Commits \a string.
+     *
+     * If there exists preedit, the preedit will be replaced.
      */
-    void updatePreedit(const QString &string);
+    void commitString(const QString &string);
 
     /*! \brief Prepares vkb for orientation change when application is about to rotate.
      *
@@ -161,6 +169,9 @@
     //! Sends string
     void sendString(const QString &);
 
+    //! Sends string from toolbar
+    void sendStringFromToolbar(const QString &);
+
     //! Handle symbol key click.
     void handleSymbolKeyClick();
 
@@ -177,6 +188,9 @@
      */
     void handleHwKeyboardStateChanged();
 
+    //!Receives modifier state changed and shows Caps Lock infobanner
+    void handleVirtualKeyboardCapsLock();
+
     //! show FN/Caps Lock infobanner
     void showLockOnInfoBanner(const QString &notification);
 
@@ -281,6 +295,24 @@
     //! update input engine keyboard layout according keyboard layout.
     void updateEngineKeyboardLayout();
 
+    //! update preedit according error correction candidates.
+    void updatePreedit(const QString &string, int candidateCount);
+
+    /*!
+     * \brief This enum defines different mode for backspace clicking.
+     */
+    enum BackspaceMode {
+        NormalBackspace, //!< backspace for normal state.
+        WordTrackerBackspace //!< backspace when word tracker is visible.
+    };
+
+    //! start backspaceTimer according \a mode
+    void startBackspace(BackspaceMode mode);
+
+    //! Send return as press&release event pair, send as input method event with
+    //! commit string otherwise.
+    void sendCommitStringOrReturnEvent(const KeyEvent &event) const;
+
 private:
     class CycleKeyHandler; //! Reacts to cycle key press events.
     friend class CycleKeyHandler;
@@ -288,7 +320,7 @@
 
     MVirtualKeyboardStyleContainer *vkbStyleContainer;
 
-    MImCorrectionCandidateWidget *correctionCandidateWidget;
+    MImCorrectionHost *correctionHost;
     MVirtualKeyboard *vkbWidget;
     MHardwareKeyboard *hardwareKeyboard;
     SymbolView *symbolView;
@@ -299,7 +331,6 @@
     MGConfItem *inputMethodCorrectionEngine;
 
     QStringList candidates;
-    bool engineReady;
 
     M::OrientationAngle angle;
     int displayWidth;
@@ -322,7 +353,7 @@
 
     int inputMethodMode;
 
-    QTimer backSpaceTimer;
+    QTimer backspaceTimer;
 
     KeyEvent lastClickEvent;
 
@@ -362,6 +393,10 @@
     //! Indicates whether engine layout need to be updated
     bool engineLayoutDirty;
 
+    BackspaceMode backspaceMode;
+
+    bool wordTrackerSuggestionAcceptedWithSpace;
+
 #ifdef UNIT_TEST
     friend class Ut_MKeyboardHost;
 #endif
--- m-keyboard/mkeyboardsettings.cpp
+++ m-keyboard/mkeyboardsettings.cpp
@@ -25,7 +25,8 @@
 #include <QDebug>
 
 namespace {
-    const QString SettingsIMCorrectionSetting("/meegotouch/inputmethods/virtualkeyboard/correctionenabled");
+    const QString SettingsImErrorCorrection("/meegotouch/inputmethods/virtualkeyboard/correctionenabled");
+    const QString SettingsImWordCompletion("/meegotouch/inputmethods/virtualkeyboard/completionenabled");
     const QString InputMethodLayouts("/meegotouch/inputmethods/virtualkeyboard/layouts");
     const QString VKBConfigurationPath("/usr/share/meegotouch/virtual-keyboard/layouts/");
     const QString VKBLayoutsFilterRule("*.xml");
@@ -33,12 +34,15 @@
 };
 
 MKeyboardSettings::MKeyboardSettings()
-    : keyboardErrorCorrectionConf(SettingsIMCorrectionSetting),
+    : keyboardErrorCorrectionConf(SettingsImErrorCorrection),
+      keyboardWordCompletionConf(SettingsImWordCompletion),
       selectedKeyboardsConf(InputMethodLayouts)
 {
     readAvailableKeyboards();
     connect(&keyboardErrorCorrectionConf, SIGNAL(valueChanged()),
             this, SIGNAL(errorCorrectionChanged()));
+    connect(&keyboardWordCompletionConf, SIGNAL(valueChanged()),
+            this, SIGNAL(wordCompletionChanged()));
     connect(&selectedKeyboardsConf, SIGNAL(valueChanged()),
             this, SIGNAL(selectedKeyboardsChanged()));
 }
@@ -154,6 +158,15 @@
 
 void  MKeyboardSettings::setErrorCorrection(bool enabled)
 {
-    if (keyboardErrorCorrectionConf.value().toBool() != enabled)
-        keyboardErrorCorrectionConf.set(enabled);
+    keyboardErrorCorrectionConf.set(enabled);
+}
+
+bool  MKeyboardSettings::wordCompletion() const
+{
+    return keyboardWordCompletionConf.value().toBool();
+}
+
+void  MKeyboardSettings::setWordCompletion(bool enabled)
+{
+    keyboardWordCompletionConf.set(enabled);
 }
--- m-keyboard/mkeyboardsettings.h
+++ m-keyboard/mkeyboardsettings.h
@@ -27,8 +27,8 @@
 /*!
  * \brief MKeyboardSettings is the implemetation of meego-keyboard setting.
  * MKeyboardSettings implement MAbstractInputMethodSettings and create the meego-keyboard
- * setting. It provides below functionalities: get/set error corretion, get/set
- * installed (selected) keyboards.
+ * setting. It provides below functionalities: get/set error corretion, get/set word
+ * completion, get/set installed (selected) keyboards.
  */
 class MKeyboardSettings: public QObject, public MAbstractInputMethodSettings
 {
@@ -65,6 +65,12 @@
     //! Sets error correction option.
     void setErrorCorrection(bool);
 
+    //! Returns the boolean value of word completion option.
+    bool wordCompletion() const;
+
+    //! Sets word completion option.
+    void setWordCompletion(bool);
+
 Q_SIGNALS:
     //! Emitted when selected keyboards are changed.
     void selectedKeyboardsChanged();
@@ -72,6 +78,9 @@
     //! Emitted when error correction option is changed.
     void errorCorrectionChanged();
 
+    //! Emitted when word completion option is changed.
+    void wordCompletionChanged();
+
 private:
     QString keyboardTitle(const QString &layoutFile) const;
     QString keyboardLayoutFile(const QString &title) const;
@@ -84,6 +93,7 @@
     //! all available keyboards
     QList<KeyboardInfo> availableKeyboardInfos;
     MGConfItem keyboardErrorCorrectionConf;
+    MGConfItem keyboardWordCompletionConf;
     MGConfItem selectedKeyboardsConf;
 };
 
--- m-keyboard/theme/libmeego-keyboard.css
+++ m-keyboard/theme/libmeego-keyboard.css
@@ -1,14 +1,13 @@
- at const HEIGHT_CANDIDATEITEM:  6.4mm;
+ at const HEIGHT_CANDIDATEITEM:  3.2mm;
 @const HEIGHT_TOOLBARITEM: 4mm;
 
 MVirtualKeyboardStyle {
     font-opacity: 0.9;
-    font-color: #00FF00;
-    text-color: #0000FF;
 
     deadkey-locked-color: #FFA500;
 
-    notification-font: "Nokia Sans Light" 56;
+    notification-font: $FONT_KEYBOARD;
+    notification-font-size: 56;
     notification-font-opacity: 0.9;
     notification-border-color: #808080;
     notification-background-color: #323232;
@@ -33,13 +32,12 @@
 MImAbstractKeyAreaStyle {
     background-image: "meegotouch-keyboard-background" 15px 15px 15px 15px;
 
-    font: "Nokia Sans Light" 28;
-    font-color: #000000;
+    font: $FONT_KEYBOARD;
+    font-color: $COLOR_FOREGROUND;
     secondary-font: $FONT_SMALL;
 
     spacing-vertical: 2mm;
-    /* should be 0.4mm or 4px, need to check*/
-    spacing-horizontal: 0.2mm;
+    spacing-horizontal: 0.4mm;
 
     padding-top: 2.2mm;
     padding-bottom: $MARGIN_DOUBLE;
@@ -101,6 +99,9 @@
     key-shift-icon-size: 38 38;
     key-shift-icon-id: "icon-m-input-methods-shift";
     key-shift-uppercase-icon-id: "icon-m-input-methods-capslock";
+
+    key-tab-icon-size: 0 0;
+    key-tab-icon-id: ; /* non-existant currently */
     /* End of single widget keyboard button styling */
 
     /* touch behaviour */
@@ -211,8 +212,8 @@
     text-left-offset: 12;
     text-right-offset: 12;
 
-    font: "Nokia Sans" 20;
-    text-color: #000000;
+    font: $FONT_SMALL_REGULAR;
+    text-color: $COLOR_FOREGROUND;
 
     preferred-size: 414 56;
     minimum-size: 414 56;
@@ -281,30 +282,96 @@
     padding-right: 11;
 }
 
-MImCorrectionCandidateContainerStyle {
-    background-image: "meegotouch-candidatelist-background" 15 15 15 15;
+MImWordTrackerStyle {
+    background-image: "meegotouch-wordtracker-background" 18px 18px 18px 18px;
+
+    wordtracker-pointer-image: "meegotouch-wordtracker-pointer" 0 0 0 0;
+    wordtracker-pointer-size: 13 12;
+    wordtracker-pointer-overlap: 1;
+
+    show-hide-frames: 100;
+    show-hide-time: 400;
+    show-hide-interval: 30;
+
     margin-top: 0;
     margin-bottom: 0;
     margin-left: 0;
     margin-right: 0;
 
-    minimum-size: 15mm $HEIGHT_CANDIDATEITEM;
+    padding-top:  0;
+    padding-bottom: 0;
+    padding-right: 0.8mm;
+    padding-left: 0.8mm;
+   
+    minimum-size: 6.4mm $HEIGHT_CANDIDATEITEM;
     maximum-size: 100% 100%;
 }
 
+MImWordList MContentItemStyle {
+    title-object-name: "MImWordListLabel";
+    background-image : meegotouch-list-fullwidth-background $CORNER_MARGINS;
+}
+
+MImWordList MContentItemStyle:pressed {
+    background-image: meegotouch-wordtracker-item-pressed 15px 15px 15px 15px;
+}
+
+MImWordList MContentItemStyle:selected {
+    title-object-name: "MImWordListLabelSelected";
+    background-image : meegotouch-list-fullwidth-background $CORNER_MARGINS;
+}
+
+MImWordListItem MLabelStyle#MImWordListLabel {
+    font: $FONT_LARGE_BOLD;
+    color: $COLOR_INVERTED_FOREGROUND;
+    margin-top:    $INDENT_DEFAULT;
+    margin-bottom: 0;
+    margin-left:   $INDENT_DEFAULT;
+    margin-right:  $INDENT_DEFAULT;
+}
+
+MImWordListItem MLabelStyle#MImWordListLabelSelected {
+    font: $FONT_LARGE_BOLD;
+    color: #0000ff;
+    margin-top:    $INDENT_DEFAULT;
+    margin-bottom: 0;
+    margin-left:   $INDENT_DEFAULT;
+    margin-right:  $INDENT_DEFAULT;
+}
+
 MImCorrectionCandidateItemStyle {
     preferred-size: -1 $HEIGHT_CANDIDATEITEM;
-    minimum-size: 10mm $HEIGHT_CANDIDATEITEM;
+    minimum-size: 4.8mm $HEIGHT_CANDIDATEITEM;
     maximum-size: 100% $HEIGHT_CANDIDATEITEM;
 
-    title-object-name: "CorrectionCandidateItemTitle";
+    background-opacity: 1.0;
+
+    font: $FONT_DEFAULT; 
+    font-color: $COLOR_INVERTED_FOREGROUND;
+    press-timeout: 250;
+    release-miss-delta: 30;
+    long-tap-timeout: 600;
+
+    margin-top: 1.1mm;
+    margin-bottom: 1.1mm;
+    margin-left: 0;
+    margin-right: 0;
+
+    padding-top:  0;
+    padding-bottom: 0;
+    padding-right: 0;
+    padding-left: 0;
 }
 
-#CorrectionCandidateItemTitle {
-    margin-left: $MARGIN_XLARGE;
-    margin-right: $MARGIN_XLARGE;
+MImCorrectionCandidateItemStyle:selected {
+    background-image: meegotouch-wordtracker-selection 15px 15px 15px 15px;
 }
 
+MImCorrectionCandidateItemStyle:pressed {
+    background-image: meegotouch-wordtracker-item-pressed 15px 15px 15px 15px;
+}
+
+
 MImToolbarStyle {
     background-image: "meegotouch-keyboard-toolbar-background" 15px 15px 15px 15px;
     margin-top: 0;
--- m-keyboard/widgets/horizontalswitcher.cpp
+++ m-keyboard/widgets/horizontalswitcher.cpp
@@ -16,6 +16,9 @@
 
 
 #include "horizontalswitcher.h"
+#include "mimabstractkey.h"
+#include "mimabstractkeyarea.h"
+
 #include <QGraphicsSceneResizeEvent>
 #include <QGraphicsScene>
 #include <QDebug>
@@ -82,6 +85,8 @@
     // New item is about to enter
     enterAnim.setItem(nextWidget);
     nextWidget->setEnabled(false);
+    MImAbstractKey::resetActiveKeys();
+
 
     // Try to fit current size.
     nextWidget->resize(size());
@@ -148,9 +153,15 @@
         emit switchDone(old, widget);
 
         updateGeometry();
+        MImAbstractKey::resetActiveKeys();
 
         if (old) {
             old->hide();
+
+            MImAbstractKeyArea *const keyArea = dynamic_cast<MImAbstractKeyArea *>(old);
+            if (keyArea) {
+                keyArea->modifiersChanged(false);
+            }
         }
     }
 }
--- m-keyboard/widgets/mimabstractkey.cpp
+++ m-keyboard/widgets/mimabstractkey.cpp
+/* * This file is part of meego-keyboard *
+ *
+ * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ * All rights reserved.
+ * Contact: Nokia Corporation (directui at nokia.com)
+ *
+ * If you have questions regarding the use of this file, please contact
+ * Nokia at directui at nokia.com.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it 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.
+ */
+
+#include "mimabstractkey.h"
+
+QList<MImAbstractKey *> MImAbstractKey::activeKeys;
+
+MImAbstractKey::~MImAbstractKey()
+{
+    activeKeys.removeAll(this);
+}
+
+MImAbstractKey* MImAbstractKey::lastActiveKey()
+{
+    return (activeKeys.isEmpty() ? 0 : activeKeys.last());
+}
+
+void MImAbstractKey::resetActiveKeys()
+{
+    while (!activeKeys.isEmpty()) {
+        MImAbstractKey *key = activeKeys.takeFirst();
+        key->setSelected(false);
+        key->resetTouchPointCount();
+    }
+}
+
+void MImAbstractKey::visitActiveKeys(MImAbstractKeyVisitor *visitor)
+{
+    foreach(MImAbstractKey *key, activeKeys) {
+        if ((*visitor)(key)) {
+            break;
+        }
+    }
+}
+
--- m-keyboard/widgets/mimabstractkey.h
+++ m-keyboard/widgets/mimabstractkey.h
@@ -20,10 +20,25 @@
 #define MIMABSTRACTKEY_H
 
 #include "mimkeymodel.h"
+#include <QList>
+
+class QRect;
+class QPainter;
+class MImAbstractKey;
+
+//! Visitor interface that can be used for MImAbstractKey::visitActiveKeys
+class MImAbstractKeyVisitor
+{
+public:
+    virtual bool operator()(MImAbstractKey *key) = 0;
+};
 
 //! Represents a key model with the key's current binding state, and also contains its visible area.
 class MImAbstractKey
 {
+protected:
+    static QList<MImAbstractKey *> activeKeys;
+
 public:
     enum ButtonState {
         Normal,  //! Normal is the "up" state for not selected buttons.
@@ -31,6 +46,8 @@
         Selected //! Selected is the "up" state for selected buttons.
     };
 
+    virtual ~MImAbstractKey();
+
     //! \brief Returns current label. It is affected by active modifiers.
     virtual const QString label() const = 0;
 
@@ -44,7 +61,8 @@
     virtual const QRectF &buttonBoundingRect() const = 0;
 
     //! \brief Sets shift and accent. Affects label and/or icon.
-    virtual void setModifiers(bool shift, QChar accent = QChar()) = 0;
+    virtual void setModifiers(bool shift,
+                              QChar accent = QChar()) = 0;
 
     //! \brief Sets the button's state to pressed. Selectable has this too.
     virtual void setDownState(bool down) = 0;
@@ -56,7 +74,7 @@
     virtual ButtonState state() const = 0;
 
     //! \return the key this button represents
-    virtual const MImKeyModel &key() const = 0;
+    virtual const MImKeyModel &model() const = 0;
 
     //! \brief Get current active key binding.
     virtual const MImKeyBinding &binding() const = 0;
@@ -71,7 +89,6 @@
     //!        (e.g., q, w, e, r, t, y).
     virtual bool isNormalKey() const = 0;
 
-
     //! \brief Called when a new touchpoint was registered on this button.
     //! \returns true if the counter could be increased.
     //!          Cannot exceed total active touchpoint limit
@@ -87,6 +104,25 @@
 
     //! \brief Get current touchpoint count.
     virtual int touchPointCount() const = 0;
+
+    //! \brief Returns most recent key that became active, and wasn't released yet.
+    //!        If no key is active, returns 0.
+    static MImAbstractKey* lastActiveKey();
+
+    //! \brief Resets active keys to normal state.
+    //! \warning Be careful when using this. Some key areas may have changed
+    //!          their visual appearance according to key modifiers. However,
+    //!          if all active keys are reset, those key areas might not be
+    //!          aware of this change.
+    //!          It is usually better to write a specialized visitor, unless
+    //!          you know what you're doing.
+    //! \sa visitActiveKeys, MImAbstractKeyVisitor
+    static void resetActiveKeys();
+
+    //! \brief Visit active keys.
+    //! \param visitor must overload 'bool operator()(MImAbstractKey *key)',
+    //!        aborts to visit next key when true is returned.
+    static void visitActiveKeys(MImAbstractKeyVisitor *visitor);
 };
 
 #endif
--- m-keyboard/widgets/mimabstractkeyarea.cpp
+++ m-keyboard/widgets/mimabstractkeyarea.cpp
@@ -14,8 +14,6 @@
  * of this file.
  */
 
-
-
 #include "flickgesture.h"
 #include "flickgesturerecognizer.h"
 #include "mvirtualkeyboardstyle.h"
@@ -25,7 +23,7 @@
 
 #include <MApplication>
 #include <MComponentData>
-#include <MFeedbackPlayer>
+#include <MFeedback>
 #include <MSceneManager>
 #include <MGConfItem>
 #include <QDebug>
@@ -36,6 +34,7 @@
 #include <QKeyEvent>
 #include <mtimestamp.h>
 
+
 namespace
 {
     const qreal ZValueButtons = 0.0;
@@ -70,6 +69,79 @@
                 && (target.y() > origin.y() - yDistance)
                 && (target.y() < origin.y() + yDistance));
     }
+
+    //! \brief Helper class responsible for finding active special keys.
+    //!
+    //! Can be used as visitor.
+    class SpecialKeyFinder
+            : public MImAbstractKeyVisitor
+    {
+    public:
+        enum FindMode {
+            FindShiftKey,
+            FindDeadKey,
+            FindBoth
+        };
+
+    private:
+        MImAbstractKey *mShiftKey;
+        MImAbstractKey *mDeadKey;
+        FindMode mode;
+
+    public:
+        explicit SpecialKeyFinder(FindMode newMode = FindBoth)
+            : MImAbstractKeyVisitor()
+            , mShiftKey(0)
+            , mDeadKey(0)
+            , mode(newMode)
+        {}
+
+        MImAbstractKey *shiftKey() const
+        {
+            return mShiftKey;
+        }
+
+        MImAbstractKey *deadKey() const
+        {
+            return mDeadKey;
+        }
+
+        bool operator()(MImAbstractKey *key)
+        {
+            if (!key) {
+                return false;
+            }
+
+            if (key->isShiftKey()) {
+                mShiftKey = key;
+            } else if (key->isDeadKey()) {
+                mDeadKey = key;
+            }
+
+            switch (mode) {
+            case FindShiftKey:
+                if (mShiftKey) {
+                    return true;
+                }
+                break;
+
+            case FindDeadKey:
+                if (mDeadKey) {
+                    return true;
+                }
+                break;
+
+            case FindBoth:
+                if (mShiftKey && mDeadKey) {
+                    return true;
+                }
+                break;
+            }
+
+
+            return false;
+        }
+    };
 }
 
 M::InputMethodMode MImAbstractKeyArea::InputMethodMode;
@@ -78,16 +150,14 @@
                                        bool usePopup,
                                        QGraphicsWidget *parent)
     : MStylableWidget(parent),
-      mRelativeButtonBaseWidth(0),
+      mRelativeKeyBaseWidth(0),
       debugTouchPoints(style()->debugTouchPoints()),
       currentLevel(0),
       mPopup(usePopup ? PopupFactory::instance()->createPopup(this) : 0),
       wasGestureTriggered(false),
       enableMultiTouch(MGConfItem(MultitouchSettings).value().toBool()),
-      activeKey(0),
-      activeDeadkey(0),
-      activeShiftKey(0),
-      feedbackPlayer(0),
+      feedbackPress(MFeedback::Press),
+      feedbackCancel(MFeedback::Cancel),
       section(newSection)
 {
     // By default multi-touch is disabled
@@ -97,7 +167,6 @@
 
     lastTouchPointPressEvent.restart();
     grabGesture(FlickGestureRecognizer::sharedGestureType());
-    feedbackPlayer = MComponentData::feedbackPlayer();
 
     longPressTimer.setSingleShot(true);
     idleVkbTimer.setSingleShot(true);
@@ -123,9 +192,9 @@
     InputMethodMode = inputMethodMode;
 }
 
-qreal MImAbstractKeyArea::relativeButtonBaseWidth() const
+qreal MImAbstractKeyArea::relativeKeyBaseWidth() const
 {
-    return mRelativeButtonBaseWidth;
+    return mRelativeKeyBaseWidth;
 }
 
 const LayoutData::SharedLayoutSection &MImAbstractKeyArea::sectionModel() const
@@ -149,9 +218,12 @@
     // screen position, so we can use mapToScene to calculate screen position:
     const QPoint pos = mapToScene(buttonRect.topLeft()).toPoint();
 
+    SpecialKeyFinder finder(SpecialKeyFinder::FindDeadKey);
+    MImAbstractKey::visitActiveKeys(&finder);
+
     mPopup->updatePos(buttonRect.topLeft(), pos, buttonRect.toRect().size());
     mPopup->handleKeyPressedOnMainArea(key,
-                                       activeDeadkey ? activeDeadkey->label() : QString(),
+                                       (finder.deadKey() ? finder.deadKey()->label() : QString()),
                                        level() % 2);
 }
 
@@ -169,11 +241,14 @@
 MImAbstractKeyArea::handleVisibilityChanged(bool visible)
 {
     if (mPopup) {
-        mPopup->setEnabled(visible);
+        mPopup->setVisible(visible);
     }
 
     if (!visible) {
-        unlockDeadkeys();
+        SpecialKeyFinder finder(SpecialKeyFinder::FindDeadKey);
+        MImAbstractKey::visitActiveKeys(&finder);
+
+        unlockDeadKeys(finder.deadKey());
     }
 }
 
@@ -184,7 +259,10 @@
         currentLevel = level;
 
         // Update uppercase / lowercase
-        updateButtonModifiers();
+        SpecialKeyFinder finder(SpecialKeyFinder::FindDeadKey);
+        MImAbstractKey::visitActiveKeys(&finder);
+
+        updateKeyModifiers(finder.deadKey() ? finder.deadKey()->label().at(0) : '\0');
 
         update();
     }
@@ -205,7 +283,7 @@
     const int newWidth = static_cast<int>(event->newSize().width());
 
     if (newWidth != static_cast<int>(event->oldSize().width())) {
-        updateButtonGeometriesForWidth(newWidth);
+        updateKeyGeometries(newWidth);
     }
 }
 
@@ -253,32 +331,35 @@
     gLastMousePos = ev->pos();
 }
 
-void MImAbstractKeyArea::click(MImAbstractKey *key, const QPoint &touchPoint)
+void MImAbstractKeyArea::click(MImAbstractKey *key,
+                               const QPoint &pos)
 {
     if (!key) {
         return;
     }
 
+    SpecialKeyFinder finder;
+    MImAbstractKey::visitActiveKeys(&finder);
+    const bool hasActiveShiftKeys = (finder.shiftKey() != 0);
+
     if (!key->isDeadKey()) {
-        const QString accent = (activeDeadkey ? activeDeadkey->label() : QString());
-        emit keyClicked(key, accent, activeShiftKey || level() % 2, touchPoint);
-        unlockDeadkeys();
-    } else if (key == activeDeadkey) {
-        unlockDeadkeys();
+        const QString accent = (finder.deadKey() ? finder.deadKey()->label() : QString());
+        emit keyClicked(key, accent, hasActiveShiftKeys || level() % 2, pos);
+
+        if (!key->isShiftKey()) {
+            unlockDeadKeys(finder.deadKey());
+        }
+    } else if (key == finder.deadKey()) {
+        unlockDeadKeys(finder.deadKey());
     } else {
-        // Deselect previous dead key, if any
-        if (activeDeadkey) {
-            activeDeadkey->setSelected(false);
+        // Deselect previous dead key, if any:
+        if (finder.deadKey()) {
+            finder.deadKey()->setSelected(false);
         }
 
-        activeDeadkey = key;
-        activeDeadkey->setSelected(true);
-
-        updateButtonModifiers();
-    }
-
-    if (key == activeShiftKey) {
-        activeShiftKey = 0;
+        // key is the new deadkey:
+        key->setSelected(true);
+        updateKeyModifiers(key->label().at(0));
     }
 }
 
@@ -322,12 +403,15 @@
     }
 
     longPressTimer.stop();
-    activeKey = 0;
 }
 
 bool MImAbstractKeyArea::event(QEvent *ev)
 {
     bool eaten = false;
+    QString start, end;
+    start = QString("%1|start").arg(ev->type());
+    end = QString("%1|end").arg(ev->type());
+    mTimestamp("MImAbstractKeyArea", start);
 
     if (ev->type() == QEvent::Gesture) {
         const Qt::GestureType flickGestureType = FlickGestureRecognizer::sharedGestureType();
@@ -364,7 +448,10 @@
         eaten = true;
     }
 
-    return eaten || MWidget::event(ev);
+    const bool result = eaten || MWidget::event(ev);
+
+    mTimestamp("MImAbstractKeyArea", end);
+    return result;
 }
 
 void MImAbstractKeyArea::handleFlickGesture(FlickGesture *gesture)
@@ -380,11 +467,10 @@
             mPopup->cancel();
         }
 
-        if (activeKey) {
-            activeKey->resetTouchPointCount();
+        if (MImAbstractKey *key = MImAbstractKey::lastActiveKey()) {
+            key->resetTouchPointCount();
         }
 
-        activeKey = 0;
         longPressTimer.stop();
         wasGestureTriggered = true;
     }
@@ -420,6 +506,7 @@
 
 void MImAbstractKeyArea::touchPointPressed(const QTouchEvent::TouchPoint &tp)
 {
+    mTimestamp("MImAbstractKeyArea", "start");
     wasGestureTriggered = false;
 
     // Gestures only slow down in speed typing mode:
@@ -438,36 +525,37 @@
 
     if (!key) {
         longPressTimer.stop();
+        mTimestamp("MImAbstractKeyArea", "end");
         return;
     }
 
     // Try to commit currently active key before activating new key:
-    if (activeKey
-        && activeKey->isNormalKey()
-        && activeKey->touchPointCount() > 0) {
+    MImAbstractKey *const lastActiveKey = MImAbstractKey::lastActiveKey();
+    SpecialKeyFinder finder;
+    MImAbstractKey::visitActiveKeys(&finder);
+    const bool hasActiveShiftKeys = (finder.shiftKey() != 0);
+
+    if (lastActiveKey
+        && lastActiveKey->isNormalKey()
+        && lastActiveKey->touchPointCount() > 0) {
         // TODO: play release sound? Potentially confusing to user, who
         // might still press this key.
-        emit keyClicked(activeKey, QString(), activeShiftKey || level() % 2,
+        emit keyClicked(lastActiveKey, QString(),
+                        hasActiveShiftKeys || level() % 2,
                         gAdjustedPositionForCorrection);
 
-        activeKey->resetTouchPointCount();
-        activeKey = 0;
+        lastActiveKey->resetTouchPointCount();
     }
 
     if (key->increaseTouchPointCount()
         && key->touchPointCount() == 1) {
-
-        if (key->isShiftKey()) {
-            activeShiftKey = key;
-        } else {
-            activeKey = key;
-        }
-
         updatePopup(key);
         longPressTimer.start(style()->longPressTimeout());
-        emit keyPressed(key, (activeDeadkey ? activeDeadkey->label() : QString()),
-                        activeShiftKey || level() % 2);
+
+        emit keyPressed(key, (finder.deadKey() ? finder.deadKey()->label() : QString()),
+                        hasActiveShiftKeys || level() % 2);
     }
+    mTimestamp("MImAbstractKeyArea", "end");
 }
 
 void MImAbstractKeyArea::touchPointMoved(const QTouchEvent::TouchPoint &tp)
@@ -477,25 +565,38 @@
         return;
     }
 
+    if (tp.scenePos() == tp.lastScenePos()) {
+        return;
+    }
+
+    mTimestamp("MImAbstractKeyArea", "start");
+
     const QPoint pos = mapFromScene(tp.scenePos()).toPoint();
     const QPoint lastPos = mapFromScene(tp.lastScenePos()).toPoint();
     const QPoint startPos = mapFromScene(tp.startScenePos()).toPoint();
 
     const GravitationalLookupResult lookup = gravitationalKeyAt(pos, lastPos, startPos);
+    SpecialKeyFinder finder;
+    MImAbstractKey::visitActiveKeys(&finder);
+    const bool hasActiveShiftKeys = (finder.shiftKey() != 0);
 
     // For a moving touchpoint, we only need to consider enter-key or leave-key events:
     if (lookup.key != lookup.lastKey) {
 
-        if (lookup.key
-            && lookup.key->increaseTouchPointCount()
-            && lookup.key->touchPointCount() == 1) {
-            activeKey = lookup.key;
-            // Reaction map cannot discover when we move from one key
-            // (= reactive area) to another
-            feedbackPlayer->play(MFeedbackPlayer::Press);
-            longPressTimer.start(style()->longPressTimeout());
-            emit keyPressed(lookup.key, (activeDeadkey ? activeDeadkey->label() : QString()),
-                            activeShiftKey || level() % 2);
+        if (lookup.key) {
+            updatePopup(lookup.key);
+
+            if (lookup.key->increaseTouchPointCount()
+                && lookup.key->touchPointCount() == 1) {
+                // Reaction map cannot discover when we move from one key
+                // (= reactive area) to another
+                // slot is called asynchronously to get screen update as fast as possible
+                QMetaObject::invokeMethod(&feedbackPress, "play", Qt::QueuedConnection);
+                longPressTimer.start(style()->longPressTimeout());
+                emit keyPressed(lookup.key,
+                                (finder.deadKey() ? finder.deadKey()->label() : QString()),
+                                hasActiveShiftKeys || level() % 2);
+            }
         }
 
         if (lookup.lastKey
@@ -503,21 +604,22 @@
             && lookup.lastKey->touchPointCount() == 0) {
             // Reaction map cannot discover when we move from one key
             // (= reactive area) to another
-            feedbackPlayer->play(MFeedbackPlayer::Cancel);
-            emit keyReleased(lookup.lastKey, (activeDeadkey ? activeDeadkey->label() : QString()),
-                             activeShiftKey || level() % 2);
+            // slot is called asynchronously to get screen update as fast as possible
+            QMetaObject::invokeMethod(&feedbackCancel, "play", Qt::QueuedConnection);
+            emit keyReleased(lookup.lastKey,
+                             (finder.deadKey() ? finder.deadKey()->label() : QString()),
+                             hasActiveShiftKeys || level() % 2);
         }
     }
 
     if (!lookup.key) {
         longPressTimer.stop();
-    } else {
-        updatePopup(lookup.key);
     }
 
     if (debugTouchPoints) {
         printTouchPoint(tp, lookup.key, lookup.lastKey);
     }
+    mTimestamp("MImAbstractKeyArea", "end");
 }
 
 void MImAbstractKeyArea::touchPointReleased(const QTouchEvent::TouchPoint &tp)
@@ -525,6 +627,7 @@
     if (wasGestureTriggered) {
         return;
     }
+    mTimestamp("MImAbstractKeyArea", "start");
 
     idleVkbTimer.start(style()->idleVkbTimeout());
 
@@ -533,22 +636,17 @@
     const QPoint startPos = mapFromScene(tp.startScenePos()).toPoint();
 
     const GravitationalLookupResult lookup = gravitationalKeyAt(pos, lastPos, startPos);
+    SpecialKeyFinder finder;
+    MImAbstractKey::visitActiveKeys(&finder);
+    const bool hasActiveShiftKeys = (finder.shiftKey() != 0);
 
     if (lookup.key
         && lookup.key->decreaseTouchPointCount()
         && lookup.key->touchPointCount() == 0) {
-
-        if (lookup.key == activeKey) {
-            activeKey = 0;
-        }
-
         longPressTimer.stop();
-        emit keyReleased(lookup.key, (activeDeadkey ? activeDeadkey->label() : QString()),
-                         activeShiftKey || level() % 2);
-
-        if (lookup.key == activeShiftKey) {
-            activeShiftKey = 0;
-        }
+        emit keyReleased(lookup.key,
+                         (finder.deadKey() ? finder.deadKey()->label() : QString()),
+                         hasActiveShiftKeys || level() % 2);
 
         click(lookup.key, gAdjustedPositionForCorrection);
     }
@@ -557,12 +655,9 @@
         && lookup.lastKey != lookup.key
         && lookup.lastKey->decreaseTouchPointCount()
         && lookup.lastKey->touchPointCount() == 0) {
-        emit keyReleased(lookup.lastKey, (activeDeadkey ? activeDeadkey->label() : QString()),
-                         activeShiftKey || level() % 2);
-
-        if (lookup.lastKey == activeShiftKey) {
-            activeShiftKey = 0;
-        }
+        emit keyReleased(lookup.lastKey,
+                         (finder.deadKey() ? finder.deadKey()->label() : QString()),
+                         hasActiveShiftKeys || level() % 2);
     }
 
     // We're finished with this touch point, inform popup:
@@ -575,12 +670,13 @@
     if (debugTouchPoints) {
         printTouchPoint(tp, lookup.key, lookup.lastKey);
     }
+    mTimestamp("MImAbstractKeyArea", "end");
 }
 
 QTouchEvent::TouchPoint MImAbstractKeyArea::createTouchPoint(int id,
-                                                        Qt::TouchPointState state,
-                                                        const QPointF &pos,
-                                                        const QPointF &lastPos)
+                                                             Qt::TouchPointState state,
+                                                             const QPointF &pos,
+                                                             const QPointF &lastPos)
 {
     QTouchEvent::TouchPoint tp(id);
     tp.setState(state);
@@ -625,17 +721,28 @@
     return GravitationalLookupResult(key, lastKey);
 }
 
-void
-MImAbstractKeyArea::unlockDeadkeys()
+void MImAbstractKeyArea::unlockDeadKeys(MImAbstractKey *deadKey)
+{
+    if (!deadKey || !deadKey->isDeadKey()) {
+        return;
+    }
+
+    deadKey->setSelected(false);
+    deadKey->resetTouchPointCount();
+    updateKeyModifiers('\0');
+}
+
+void MImAbstractKeyArea::hidePopup()
 {
-    if (activeDeadkey) {
-        activeDeadkey->setSelected(false);
-        activeDeadkey = 0;
-        updateButtonModifiers();
+    if (!mPopup) {
+        return;
     }
+
+    mPopup->setVisible(false);
 }
 
-void MImAbstractKeyArea::drawReactiveAreas(MReactionMap */*reactionMap*/, QGraphicsView */*view*/)
+void MImAbstractKeyArea::drawReactiveAreas(MReactionMap *,
+                                           QGraphicsView *)
 {
     // Empty default implementation. Geometries of buttons are known by derived classes.
 }
@@ -663,26 +770,25 @@
                                                      : QString()) << ")";
 }
 
-void MImAbstractKeyArea::updateButtonModifiers()
+void MImAbstractKeyArea::updateKeyModifiers(const QChar &accent)
 {
-    bool shift = (currentLevel == 1);
-
     // We currently don't allow active dead key level changing. If we did,
-    // we should update activeDeadkey level before delivering its accent to
+    // we should update activeDeadKey level before delivering its accent to
     // other keys.
-    const QChar accent(activeDeadkey ? activeDeadkey->label().at(0) : '\0');
-
+    bool shift = (currentLevel == 1);
     modifiersChanged(shift, accent);
 }
 
-void MImAbstractKeyArea::modifiersChanged(bool /*shift*/, const QChar /*accent*/)
+void MImAbstractKeyArea::modifiersChanged(bool,
+                                          const QChar &)
 {
     // Empty default implementation
 }
 
 void MImAbstractKeyArea::onThemeChangeCompleted()
 {
-    updateButtonGeometriesForWidth(size().width());
+    // TODO: update all other CSS attributes that are mapped to members.
+    updateKeyGeometries(size().width());
 }
 
 const MImAbstractKeyAreaStyleContainer &MImAbstractKeyArea::baseStyle() const
@@ -692,18 +798,23 @@
 
 void MImAbstractKeyArea::handleLongKeyPressed()
 {
-    if (!activeKey) {
+    MImAbstractKey *const lastActiveKey = MImAbstractKey::lastActiveKey();
+
+    if (!lastActiveKey) {
         return;
     }
 
-    const QString accent = (activeDeadkey ? activeDeadkey->label()
-                                          : QString());
+    SpecialKeyFinder finder(SpecialKeyFinder::FindDeadKey);
+    MImAbstractKey::visitActiveKeys(&finder);
+
+    const QString accent = (finder.deadKey() ? finder.deadKey()->label()
+                                           : QString());
 
     if (mPopup) {
-        mPopup->handleLongKeyPressedOnMainArea(activeKey, accent, level() % 2);
+        mPopup->handleLongKeyPressedOnMainArea(lastActiveKey, accent, level() % 2);
     }
 
-    emit longKeyPressed(activeKey, accent, level() % 2);
+    emit longKeyPressed(lastActiveKey, accent, level() % 2);
 }
 
 void MImAbstractKeyArea::handleIdleVkb()
--- m-keyboard/widgets/mimabstractkeyarea.h
+++ m-keyboard/widgets/mimabstractkeyarea.h
@@ -14,8 +14,6 @@
  * of this file.
  */
 
-
-
 #ifndef MIMABSTRACTKEYAREA_H
 #define MIMABSTRACTKEYAREA_H
 
@@ -27,6 +25,7 @@
 #include "mkeyboardcommon.h"
 
 #include <MStylableWidget>
+#include <MFeedback>
 #include <QColor>
 #include <QHash>
 #include <QList>
@@ -36,7 +35,6 @@
 #include <QTime>
 
 class FlickGesture;
-class MFeedbackPlayer;
 class MReactionMap;
 class MScalableImage;
 class MVirtualKeyboardStyleContainer;
@@ -46,10 +44,7 @@
 class PopupHost;
 class PopupBase;
 
-/*!
- * \class MImAbstractKeyArea
- * \brief MImAbstractKeyArea is a view for virtual keyboard layout represented by LayoutModel
- */
+//! \brief MImAbstractKeyArea is a view for virtual keyboard layout represented by LayoutModel
 class MImAbstractKeyArea
     : public MStylableWidget
 {
@@ -57,12 +52,10 @@
     Q_DISABLE_COPY(MImAbstractKeyArea)
 
 public:
-    /*!
-    * \brief Constructor
-    * \param section A section that this MImAbstractKeyArea visualizes.
-    * \param usePopup Sets whether popup should be used when long press occurs.
-    * \param parent The widget's parent.
-    */
+    //! \brief Constructor
+    //! \param section section that is shown by this key area
+    //! \param usePopup whether popup should be used
+    //! \param parent key area's parent
     explicit MImAbstractKeyArea(const LayoutData::SharedLayoutSection &section,
                                 bool usePopup = false,
                                 QGraphicsWidget *parent = 0);
@@ -70,106 +63,127 @@
     //! \brief Destructor
     virtual ~MImAbstractKeyArea();
 
-    //! \return layout model
+    //! \brief Returns section shown by this key area.
     const LayoutData::SharedLayoutSection &sectionModel() const;
 
-    //! Returns current level of this layout.
+    //! \brief Returns current level of this layout.
     int level() const;
 
-    //! Expose style used by MImAbstractKeyArea.
+    //! \brief Exposes style used by this key area.
     const MImAbstractKeyAreaStyleContainer &baseStyle() const;
 
-    //! Set input method mode for all MImAbstractKeyArea instances
+    //! \brief Sets input method mode for all MImAbstractKeyArea instances.
+    //! \param inputMethodMode the new input method mode
     static void setInputMethodMode(M::InputMethodMode inputMethodMode);
 
-    //! Returns relative button base width
-    qreal relativeButtonBaseWidth() const;
+    //! \brief Returns relative button base width
+    qreal relativeKeyBaseWidth() const;
+
+    //! \brief Returns all keys from this key area.
+    virtual QList<const MImAbstractKey *> keys() const = 0;
 
-    //! Returns all keys.
-    virtual QList<const MImAbstractKey *> keys() = 0;
+    //! \brief Notification for derived classes about button modifier change.
+    //!
+    //! Derived classes should not change the level of selected dead keys. This is to
+    //! ensure all dead keys can be used with all characters in every level.
+    //! \param shift whether shift modifier is enabled
+    //! \param accent which accented version should be used, for a key
+    virtual void modifiersChanged(bool shift,
+                                  const QChar &accent = QChar());
 
 public slots:
-    /*!
-     * This slot is used to switch levels
-     */
+    //! \brief Tell key area to switch levels for all keys.
+    //! \param level the new level
     void switchLevel(int level);
 
-    virtual void setShiftState(ModifierState newShiftState);
-
-    virtual void drawReactiveAreas(MReactionMap *reactionMap, QGraphicsView *view);
+    //! \brief Set shift state.
+    //! \param shiftState the new shift state
+    virtual void setShiftState(ModifierState shiftState);
+
+    //! \brief Draw reactive areas for all keys.
+    //! \param reactionMap the reaction map to draw onto
+    //! \param view the view to be used
+    virtual void drawReactiveAreas(MReactionMap *reactionMap,
+                                   QGraphicsView *view);
+
+    //! \brief Unlock all locked dead keys.
+    //! \param deadKey the corresponding dead key
+    void unlockDeadKeys(MImAbstractKey *deadKey);
 
-    /*!
-     * \brief unlock all locked deadkeys
-     */
-    void unlockDeadkeys();
+    //! \brief Hide popup
+    void hidePopup();
 
 signals:
     //! \brief Emitted when the covered region changed
     //! \param region The changed region
     void regionUpdated(const QRegion &region);
 
-    /*!
-     * \brief Emitted when key is pressed
-     * Note that this happens also when user keeps finger down/mouse
-     * button pressed and moves over another key (event is about the new key)
-     * \param key describes pressed button
-     * \param accent label of pressed dead key if any
-     * \param upperCase contains true if key is in uppercase state
-     */
-    void keyPressed(const MImAbstractKey *key, const QString &accent, bool upperCase);
-
-    /*!
-     * \brief Emitted when key is released
-     * Note that this happens also when user keeps finger down/mouse
-     * button pressed and moves over another key (event is about the old key)
-     * \param key describes released button
-     * \param accent label of pressed dead key if any
-     * \param upperCase contains true if key is in uppercase state
-     */
-    void keyReleased(const MImAbstractKey *key, const QString &accent, bool upperCase);
-
-    /*!
-     * \brief Emitted when user releases mouse button/lifts finger
-     * Except when done on a dead key
-     * \param key describes clicked button
-     * \param accent label of pressed dead key if any
-     * \param upperCase contains true if key is in uppercase state
-     * \param touchPoint the touch point for the key
-     */
-    void keyClicked(const MImAbstractKey *key, const QString &accent, bool upperCase, const QPoint &touchPoint);
-
-    /*!
-     * \brief Emitted when long press is detected.
-     * Long press detection is:
-     * - cancelled when latest pressed key is released;
-     * - restarted when finger is moved to other key;
-     * - restarted when new touch point is recognized by MImAbstractKeyArea.
-     *
-     * \param key describes pressed button.
-     * \param accent Active accent in MImAbstractKeyArea at the time of long press occured.
-     * \param upperCase Upper case state in MImAbstractKeyArea at the time of long press occured.
-     */
-    void longKeyPressed(const MImAbstractKey *key, const QString &accent, bool upperCase);
+    //! \brief Emitted when key is pressed
+    //!
+    //! Note that this happens also when user keeps finger down/mouse
+    //! button pressed and moves over another key (event is about the new key)
+    //! \param key describes pressed button
+    //! \param accent label of pressed dead key if any
+    //! \param upperCase contains true if key is in uppercase state
+    void keyPressed(const MImAbstractKey *key,
+                    QString accent,
+                    bool upperCase);
+
+    //! \brief Emitted when key is released.
+    //!
+    //! Note that this happens also when user keeps finger down/mouse
+    //! button pressed and moves over another key (event is about the old key)
+    //! \param key describes released button
+    //! \param accent label of pressed dead key if any
+    //! \param upperCase contains true if key is in uppercase state
+    void keyReleased(const MImAbstractKey *key,
+                     QString accent,
+                     bool upperCase);
+
+    //! \brief Emitted when user releases mouse button/lifts finger.
+    //!
+    //! Except when done on a dead key
+    //! \param key describes clicked button
+    //! \param accent label of pressed dead key if any
+    //! \param upperCase contains true if key is in uppercase state
+    //! \param touchPoint the touch point for the key
+    void keyClicked(const MImAbstractKey *key,
+                    QString accent,
+                    bool upperCase,
+                    QPoint touchPoint);
+
+    //! \brief Emitted when long press is detected.
+    //!
+    //! Long press detection is:
+    //! - cancelled when latest pressed key is released;
+    //! - restarted when finger is moved to other key;
+    //! - restarted when new touch point is recognized by MImAbstractKeyArea.
+    //! \param key describes pressed button
+    //! \param accent active accent in MImAbstractKeyArea at the time of long press occured
+    //! \param upperCase upper case state in MImAbstractKeyArea at the time of long press occured
+    void longKeyPressed(const MImAbstractKey *key,
+                        QString accent,
+                        bool upperCase);
 
-    //! Emitted when flicked right
+    //! \brief Emitted when key area is flicked right.
     void flickRight();
 
-    //! Emitted when flicked left
+    //! \brief Emitted when key area is flicked left.
     void flickLeft();
 
-    //! Emitted when flicked down
+    //! \brief Emitted when key area is flicked down.
     void flickDown();
 
-    //! \brief Emitted when flicked up
+    //! \brief Emitted when key area is flicked up.
     //! \param binding Information about the key where mouse button was pressed
     void flickUp(const MImKeyBinding &binding);
 
     //! \brief Emitted if button width has changed
     //! \param baseWidth base width used for relative button widths
-    void relativeButtonBaseWidthChanged(qreal baseWidth);
+    void relativeKeyBaseWidthChanged(qreal baseWidth);
 
 protected:
-    /*! \reimp */
+    //! \reimp
     virtual void resizeEvent(QGraphicsSceneResizeEvent *event);
     virtual void mousePressEvent(QGraphicsSceneMouseEvent *event);
     virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
@@ -180,61 +194,54 @@
     virtual void grabMouseEvent(QEvent *event);
     virtual void ungrabMouseEvent(QEvent *event);
     virtual bool event(QEvent *event);
-    /*! \reimp_end */
+    //! \reimp_end
 
-    //! Called when widget is about to lose visibility.
+    //! \brief Called when key area's visibility changed.
+    //! \param visible the new visbility status
     virtual void handleVisibilityChanged(bool visible);
 
+    //! \brief Invalidates the current background cache.
+    virtual void invalidateBackgroundCache() = 0;
+
     //! Shows popup and updates its content and position.
     //! \param key current key
     void updatePopup(MImAbstractKey *key = 0);
 
-    /*!
-    * \brief Get level count of the virtual keyboard.
-    * \return int. The level count.
-    */
+    //! \brief Get maximum number of columns in this key area.
     int maxColumns() const;
 
-    /*!
-    * \brief Get row count of the virtual keyboard, in current level.
-    * \return int. The row count.
-    */
+    //! \brief Get number of rows in this key area.
     int rowCount() const;
 
-    //! \brief Updates button labels and/or icons according to current level and deadkey.
-    void updateButtonModifiers();
-
-    /*! \brief Notification for derived classes about button modifier change.
-     *
-     *  Derived classes should not change the level of selected dead keys. This is to
-     *  ensure all dead keys can be used with all characters in every level.
-     */
-    virtual void modifiersChanged(bool shift, QChar accent = QChar());
+    //! \brief Updates button labels and/or icons according to current level
+    //!        and deadkey.
+    //! \param accent the accent version of a dead key.
+    void updateKeyModifiers(const QChar &accent = QChar());
 
     //! \brief Returns key at given \a pos.
     //!
     //! Accepts positions outside widget geometry because
     //! of reactive margins.
+    //! \param pos the position (in key area space) to look up key
     virtual MImAbstractKey *keyAt(const QPoint &pos) const = 0;
 
-    /*! \brief Calculates button and row geometry based on given \a availableWidth.
-     *  \post Button rectangle cache and row width cache are up to date.
-     */
-    virtual void updateButtonGeometriesForWidth(int availableWidth) = 0;
+    //! \brief Updates key (and row) geometry based on given \a availableWidth.
+    //! \param availableWidth with of the key area
+    virtual void updateKeyGeometries(int availableWidth) = 0;
 
+    //! \brief Returns popup
     const PopupBase &popup() const;
 
+    //! \brief Print touch point information, for debugging purposes.
+    //! \param tp the touch point
+    //! \param key key underneath touch point
+    //! \param lastKey last key that was associated to the touch point
     void printTouchPoint(const QTouchEvent::TouchPoint &tp,
                          const MImAbstractKey *key,
                          const MImAbstractKey *lastKey = 0) const;
 
-    //! Sets button state and sends release & press events.
-    //void setActiveKey(MImAbstractKey *key, TouchPointInfo &tpi);
-
-    //! Relative button base width in currently active layout
-    qreal mRelativeButtonBaseWidth;
-
-    bool debugTouchPoints;
+    qreal mRelativeKeyBaseWidth; //!< Relative key base width in currently active layout
+    bool debugTouchPoints; //!< Whether touch point debugging is enabled
 
 protected slots:
     //! Update background images, text layouts, etc. when the theme changed.
@@ -248,6 +255,7 @@
 
 private:
     //! \brief Handler for flick gestures from Qt gesture framework.
+    //! \param gesture the flick gesture
     void handleFlickGesture(FlickGesture *gesture);
 
     //! \brief Touch point press handler.
@@ -262,10 +270,17 @@
     //! \param tp The unprocessed Qt touchpoint.
     void touchPointReleased(const QTouchEvent::TouchPoint &tp);
 
+    //! \brief Helper method to create touch points
+    //! \param id touch point id
+    //! \param state touch point state
+    //! \param pos touch point scene position
+    //! \param lastPos last touch point scene position
     static QTouchEvent::TouchPoint createTouchPoint(int id,
                                                     Qt::TouchPointState state,
                                                     const QPointF &pos,
                                                     const QPointF &lastPos);
+
+    //! \brief Helper struct to store results of \a gravitationalKeyAt
     struct GravitationalLookupResult
     {
         GravitationalLookupResult(MImAbstractKey *newKey,
@@ -290,56 +305,29 @@
                                                  const QPoint &mappedLastPos,
                                                  const QPoint &mappedStartPos) const;
 
-    void click(MImAbstractKey *key, const QPoint &touchPoint = QPoint());
+    //! \brief Trigger a keyClicked signal, and update key area state.
+    //! \param key the clicked key
+    //! \param pos where the key was clicked
+    void click(MImAbstractKey *key,
+               const QPoint &pos = QPoint());
 
-    //! Checks for speed typing mode
+    //! \brief Checks for speed typing mode
     //! \warning Not side-effect free when \a restartTimers is actively used.
     bool isInSpeedTypingMode(bool restartTimers = false);
 
-    //! Current level
-    int currentLevel;
-
-    //! Popup to show additional information for a button
-    PopupBase *mPopup;
-
-    //! List of punctuation labels
-    QList<QStringList> punctuationsLabels;
-
-    //! List of accent labels
-    QList<QStringList> accentLabels;
-
-    //! Whether a gesture was already triggered for any active touch point.
-    bool wasGestureTriggered;
-    bool enableMultiTouch;
-
-    //! Active key, there can only be one at a time.
-    MImAbstractKey *activeKey;
-
-    //! Activated dead key
-    MImAbstractKey *activeDeadkey;
-
-    //! Activated shift key
-    MImAbstractKey *activeShiftKey;
-
-    /*!
-     * Feedback player instance
-     */
-    MFeedbackPlayer *feedbackPlayer;
-
-    //! layout section viewed by this class
-    const LayoutData::SharedLayoutSection section;
-
-    static M::InputMethodMode InputMethodMode;
-
-    //! This timer is used to recognize long press.
-    QTimer longPressTimer;
-
-
-    //! Whenever the VKB idles, gestures are activated.
-    QTimer idleVkbTimer;
-
-    //! Used to measure elapsed time between two touchpoint press events:
-    QTime lastTouchPointPressEvent;
+    int currentLevel; //!< current level
+    PopupBase *mPopup; //!< popup to show additional information for a button
+    QList<QStringList> punctuationsLabels; //!< list of punctuation labels
+    QList<QStringList> accentLabels; //!< list of accent labels
+    bool wasGestureTriggered; //!< whether a gesture was already triggered for any active touch point
+    bool enableMultiTouch; //!< whether this key area operates in multitouch mode
+    MFeedback feedbackPress; //!< Press feedback
+    MFeedback feedbackCancel; //!< Cancel feedback
+    const LayoutData::SharedLayoutSection section; //!< layout section shown by this key area
+    static M::InputMethodMode InputMethodMode; //!< used input method mode (same for all key areas)
+    QTimer longPressTimer; //!< used to recognize long press
+    QTimer idleVkbTimer;  //!< Whenever this key area of the VKB idles, gestures are activated.
+    QTime lastTouchPointPressEvent; //!< measures elapsed time between two touchpoint press events
 
     M_STYLABLE_WIDGET(MImAbstractKeyAreaStyle)
 
--- m-keyboard/widgets/mimcorrectioncandidatecontainerstyle.h
+++ m-keyboard/widgets/mimcorrectioncandidatecontainerstyle.h
-/* * This file is part of meego-keyboard *
- *
- * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
- * All rights reserved.
- * Contact: Nokia Corporation (directui at nokia.com)
- *
- * If you have questions regarding the use of this file, please contact
- * Nokia at directui at nokia.com.
- *
- * This library is free software; you can redistribute it and/or
- * modify it 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.
- */
-
-#ifndef MIMCORRECTIONCANDIDATECONTAINERSTYLE_H
-#define MIMCORRECTIONCANDIDATECONTAINERSTYLE_H
-
-#include <MWidgetStyle>
-
-class MImCorrectionCandidateContainerStyle : public MWidgetStyle
-{
-    Q_OBJECT
-    M_STYLE(MImCorrectionCandidateContainerStyle)
-};
-
-class M_EXPORT MImCorrectionCandidateContainerStyleContainer : public MWidgetStyleContainer
-{
-    M_STYLE_CONTAINER(MImCorrectionCandidateContainerStyle)
-};
-
-#endif
-
--- m-keyboard/widgets/mimcorrectioncandidateitem.cpp
+++ m-keyboard/widgets/mimcorrectioncandidateitem.cpp
@@ -15,15 +15,237 @@
  */
 
 #include "mimcorrectioncandidateitem.h"
-#include "mimcorrectioncandidateitemview.h"
+#include <QTimer>
+#include <QFontMetricsF>
+#include <QGraphicsSceneMouseEvent>
+#include <QTapAndHoldGesture>
+#include <QDebug>
 
-MImCorrectionCandidateItem::MImCorrectionCandidateItem(MContentItem::ContentItemStyle itemStyle, QGraphicsItem *parent)
-    : MContentItem(itemStyle, parent)
+#include <MTheme>
+
+namespace {
+    const int DefaultPressTimeout = 250;
+    const int DefaultReleaseMissDelta = 30;
+    const int DefaultLongTapTimeout = 600;
+}
+
+#include <mwidgetcreator.h>
+M_REGISTER_WIDGET_NO_CREATE(MImCorrectionCandidateItem)
+
+MImCorrectionCandidateItem::MImCorrectionCandidateItem(const QString &title, QGraphicsItem *parent)
+    : MStylableWidget(parent),
+      mSelected(false),
+      mDown(false),
+      mTitle(title),
+      styleModeChangeTimer(),
+      longTapTimer(),
+      queuedStyleModeChange(false)
+      
 {
-    setView(new MImCorrectionCandidateItemView(this));
+    styleModeChangeTimer.setSingleShot(true);
+    connect(&styleModeChangeTimer, SIGNAL(timeout()), SLOT(applyQueuedStyleModeChange()));
+
+    connect(this, SIGNAL(visibleChanged()),
+            this, SLOT(handleVisibilityChanged()));
+
+    setupLongTapTimer();
+    connect(MTheme::instance(), SIGNAL(themeChangeCompleted()),
+            this, SLOT(onThemeChangeCompleted()),
+            Qt::UniqueConnection);
 }
 
 MImCorrectionCandidateItem::~MImCorrectionCandidateItem()
 {
 }
 
+void MImCorrectionCandidateItem::setTitle(const QString &string)
+{
+    if (mTitle != string) {
+        mTitle = string;
+        update();
+    }
+}
+
+QString MImCorrectionCandidateItem::title() const
+{
+    return mTitle;
+}
+
+void MImCorrectionCandidateItem::setSelected(bool select)
+{
+    mSelected = select;
+    if (mSelected)
+        style().setModeSelected();
+    else 
+        style().setModeDefault();
+}
+
+bool MImCorrectionCandidateItem::isSelected() const
+{
+    return mSelected;
+}
+
+void MImCorrectionCandidateItem::click()
+{
+    qDebug() << __PRETTY_FUNCTION__;
+    emit clicked();
+}
+
+void MImCorrectionCandidateItem::longTap()
+{
+    qDebug() << __PRETTY_FUNCTION__;
+    // Clear down state and update style.
+    if (mDown) {
+        mDown = false;
+        updateStyleMode();
+    }
+
+    emit longTapped();
+}
+
+void MImCorrectionCandidateItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
+{
+    event->accept();
+    if (mDown)
+        return;
+    
+    style()->pressFeedback().play();
+    mDown = true;
+    updateStyleMode();
+    longTapTimer.start();
+}
+
+void MImCorrectionCandidateItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
+{
+    event->accept();
+    if (!mDown) {
+        return;
+    }
+    longTapTimer.stop();
+    
+    mDown = false;
+    updateStyleMode();
+
+    QPointF touch = event->scenePos();
+    QRectF rect = sceneBoundingRect();
+    int releaseMissDelta = style()->releaseMissDelta();
+    releaseMissDelta = (releaseMissDelta > 0) ? releaseMissDelta : DefaultReleaseMissDelta;
+    rect.adjust(-releaseMissDelta, -releaseMissDelta,
+                releaseMissDelta, releaseMissDelta);
+    bool pressed = rect.contains(touch);
+
+    if (pressed) {
+        style()->releaseFeedback().play();
+        click();
+    }
+}
+
+void MImCorrectionCandidateItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
+{
+    event->accept();
+
+    QPointF touch = event->scenePos();
+    QRectF rect = sceneBoundingRect();
+    int releaseMissDelta = style()->releaseMissDelta();
+    releaseMissDelta = (releaseMissDelta > 0) ? releaseMissDelta : DefaultReleaseMissDelta;
+    rect.adjust(-releaseMissDelta, -releaseMissDelta,
+                releaseMissDelta, releaseMissDelta);
+    bool pressed = rect.contains(touch);
+
+    if (pressed != mDown) {
+        longTapTimer.stop();
+        if (pressed) {
+            style()->pressFeedback().play();
+        } else {
+            style()->cancelFeedback().play();
+        }
+        mDown = pressed;
+        updateStyleMode();
+    }
+}
+
+
+void MImCorrectionCandidateItem::updateStyleMode()
+{
+    if (mDown) {
+        int pressTimeout = style()->pressTimeout();
+        pressTimeout = (pressTimeout > 0) ? pressTimeout : DefaultPressTimeout;
+        if (styleModeChangeTimer.isActive()) {
+            styleModeChangeTimer.start(pressTimeout);
+            return;
+        }
+        styleModeChangeTimer.start(pressTimeout);
+        style().setModePressed();
+    } else {
+        if (isSelected()) {
+            style().setModeSelected();
+        } else {
+            if (styleModeChangeTimer.isActive()) {
+                queuedStyleModeChange = true;
+                return;
+            }
+            style().setModeDefault();
+        }
+    }
+
+    applyStyle();
+    update();
+}
+
+void MImCorrectionCandidateItem::applyQueuedStyleModeChange()
+{
+    if (queuedStyleModeChange) {
+        queuedStyleModeChange = false;
+        updateStyleMode();
+    }
+}
+
+void MImCorrectionCandidateItem::drawContents(QPainter *painter, const QStyleOptionGraphicsItem *option) const
+{
+    Q_UNUSED(option);
+    if (!mTitle.isEmpty()) {
+        painter->setFont(style()->font());
+        painter->setPen(style()->fontColor());
+        QSizeF s = size() - QSizeF(style()->marginLeft() + style()->marginRight(),
+                                   style()->marginTop() + style()->marginBottom());
+        painter->drawText(QRectF(0, 0, s.width(), s.height()), Qt::AlignCenter, mTitle); 
+    }
+}
+
+qreal MImCorrectionCandidateItem::idealWidth() const
+{
+    qreal width = 0.0;
+    if (!mTitle.isEmpty()) {
+        QFontMetricsF fm(style()->font());
+        width = fm.width(mTitle);
+    }
+    width += style()->marginLeft() + style()->marginRight()
+             + style()->paddingRight() + style()->paddingLeft();
+    return width;
+}
+
+void MImCorrectionCandidateItem::handleVisibilityChanged()
+{
+    //clear select and down state when hidden
+    if (!isVisible()) {
+        if (mDown || mSelected) {
+            mSelected = false;
+            mDown =false;
+            updateStyleMode();
+        }
+    }
+}
+
+void MImCorrectionCandidateItem::setupLongTapTimer()
+{
+    longTapTimer.setSingleShot(true);
+    int longTapTimeout = style()->longTapTimeout();
+    longTapTimer.setInterval(longTapTimeout);
+    connect(&longTapTimer, SIGNAL(timeout()), SLOT(longTap()), Qt::UniqueConnection);
+}
+
+void MImCorrectionCandidateItem::onThemeChangeCompleted()
+{
+    // reset long tap timer
+    setupLongTapTimer();
+}
--- m-keyboard/widgets/mimcorrectioncandidateitem.h
+++ m-keyboard/widgets/mimcorrectioncandidateitem.h
@@ -16,16 +16,104 @@
 
 #ifndef MIMCORRECTIONCANDIDATEITEM_H
 #define MIMCORRECTIONCANDIDATEITEM_H
-#include <MContentItem>
 
-class MImCorrectionCandidateItem: public MContentItem
+#include <QTimer>
+#include <MStylableWidget>
+#include "mimcorrectioncandidateitemstyle.h"
+
+class MImCorrectionCandidateItem: public MStylableWidget
 {
     Q_OBJECT
     Q_DISABLE_COPY(MImCorrectionCandidateItem)
+
+    Q_PROPERTY(QString title READ title WRITE setTitle)
+
 public:
-    explicit MImCorrectionCandidateItem(MContentItem::ContentItemStyle itemStyle = MContentItem::SingleTextLabel,
-                                        QGraphicsItem *parent = 0);
+    explicit MImCorrectionCandidateItem(const QString &title = QString(), QGraphicsItem *parent = 0);
+
     virtual ~MImCorrectionCandidateItem();
+
+    /*
+     * \brief Sets title label.
+     */
+    void setTitle(const QString &);
+
+    /*
+     * \brief Returns current title label.
+     */
+    QString title() const;
+
+    /*!
+     * \brief Returns the ideal width of the container widget.
+     *
+     * The ideal width is the actually used width of the title together with margins and paddings.
+     */
+    qreal idealWidth() const;
+    
+    /*!
+     * \brief Select item.
+     */
+    void setSelected(bool);
+
+    /*
+     * \brief Returns selected state.
+     */
+    bool isSelected() const;
+
+public Q_SLOTS:
+    /*!
+     \brief Makes the list cell to send clicked() signal.
+     */
+     void click();
+
+    /*!
+     *\brief Makes the list cell to send longTapped signal.
+     *\param pos The position of the tap.
+     */
+    void longTap();
+
+protected:
+    /*! \reimp */
+    virtual void mousePressEvent(QGraphicsSceneMouseEvent *event);
+    virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
+    virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event);
+    virtual void drawContents(QPainter *painter, const QStyleOptionGraphicsItem *option) const;
+    /*! \reimp_end */
+
+Q_SIGNALS:
+    /*!
+     * \brief The signal is emitted when the item is clicked.
+     */
+    void clicked();
+
+    /*!
+     * \brief The signal is emitted when the item has been tapped and holded.
+     */
+    void longTapped();
+
+private Q_SLOTS:
+    void applyQueuedStyleModeChange();
+
+    void handleVisibilityChanged();
+
+    /*!
+     * Update stored style stuffs when the theme changed.
+     */
+    void onThemeChangeCompleted();
+
+private:
+    void updateStyleMode();
+
+    void setupLongTapTimer();
+
+    bool mSelected;
+    bool mDown;
+    QString mTitle;
+    QTimer styleModeChangeTimer;
+    QTimer longTapTimer;
+    bool queuedStyleModeChange;
+
+    M_STYLABLE_WIDGET(MImCorrectionCandidateItemStyle)
 };
 
 #endif
--- m-keyboard/widgets/mimcorrectioncandidateitemstyle.h
+++ m-keyboard/widgets/mimcorrectioncandidateitemstyle.h
@@ -17,17 +17,27 @@
 #ifndef MIMCORRECTIONCANDIDATEITEMSTYLE_H
 #define MIMCORRECTIONCANDIDATEITEMSTYLE_H
 
-#include <mcontentitemstyle.h>
+#include <MWidgetStyle>
 
-class MImCorrectionCandidateItemStyle : public MContentItemStyle
+class MImCorrectionCandidateItemStyle : public MWidgetStyle
 {
     Q_OBJECT
     M_STYLE(MImCorrectionCandidateItemStyle)
+
+    M_STYLE_ATTRIBUTE(QFont, font, Font)
+    M_STYLE_ATTRIBUTE(QColor, fontColor, FontColor)
+
+    M_STYLE_ATTRIBUTE(int, pressTimeout, PressTimeout)
+    M_STYLE_ATTRIBUTE(int, releaseMissDelta, ReleaseMissDelta)
+    M_STYLE_ATTRIBUTE(int, longTapTimeout, LongTapTimeout)
 };
 
-class M_EXPORT MImCorrectionCandidateItemStyleContainer : public MContentItemStyleContainer
+class M_EXPORT MImCorrectionCandidateItemStyleContainer : public MWidgetStyleContainer
 {
     M_STYLE_CONTAINER(MImCorrectionCandidateItemStyle)
+
+    M_STYLE_MODE(Landscape)
+    M_STYLE_MODE(Portrait)
 };
 
 #endif
--- m-keyboard/widgets/mimcorrectioncandidateitemview.cpp
+++ m-keyboard/widgets/mimcorrectioncandidateitemview.cpp
-/* * This file is part of meego-keyboard *
- *
- * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
- * All rights reserved.
- * Contact: Nokia Corporation (directui at nokia.com)
- *
- * If you have questions regarding the use of this file, please contact
- * Nokia at directui at nokia.com.
- *
- * This library is free software; you can redistribute it and/or
- * modify it 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.
- */
-
-#include "mimcorrectioncandidateitemview.h"
-#include "mimcorrectioncandidateitem.h"
-
-MImCorrectionCandidateItemView::MImCorrectionCandidateItemView(MImCorrectionCandidateItem *controller)
-    : MContentItemView(controller)
-{
-}
-
--- m-keyboard/widgets/mimcorrectioncandidateitemview.h
+++ m-keyboard/widgets/mimcorrectioncandidateitemview.h
-/* * This file is part of meego-keyboard *
- *
- * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
- * All rights reserved.
- * Contact: Nokia Corporation (directui at nokia.com)
- *
- * If you have questions regarding the use of this file, please contact
- * Nokia at directui at nokia.com.
- *
- * This library is free software; you can redistribute it and/or
- * modify it 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.
- */
-
-#ifndef MIMCORRECTIONCANDIDATEITEMVIEW_H
-#define MIMCORRECTIONCANDIDATEITEMVIEW_H
-
-#include <mcontentitemview.h>
-#include "mimcorrectioncandidateitemstyle.h"
-
-class MImCorrectionCandidateItem;
-
-class MImCorrectionCandidateItemView : public MContentItemView
-{
-    Q_OBJECT
-    M_VIEW(MContentItemModel, MImCorrectionCandidateItemStyle)
-
-public:
-    explicit MImCorrectionCandidateItemView(MImCorrectionCandidateItem *controller);
-};
-
-#endif
-
--- m-keyboard/widgets/mimcorrectioncandidatewidget.cpp
+++ m-keyboard/widgets/mimcorrectioncandidatewidget.cpp
-/* * This file is part of meego-keyboard *
- *
- * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
- * All rights reserved.
- * Contact: Nokia Corporation (directui at nokia.com)
- *
- * If you have questions regarding the use of this file, please contact
- * Nokia at directui at nokia.com.
- *
- * This library is free software; you can redistribute it and/or
- * modify it 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.
- */
-
-
-
-#include "mimcorrectioncandidatewidget.h"
-#include "mimcorrectioncandidateitem.h"
-
-#include <QGraphicsSceneMouseEvent>
-#include <QDebug>
-#include <QString>
-
-#include <MSceneManager>
-#include <mreactionmap.h>
-#include <mplainwindow.h>
-#include <MWidgetRecycler>
-#include <MList>
-#include <MLabel>
-#include <MGConfItem>
-#include <QGraphicsLinearLayout>
-#include <QStringListModel>
-
-namespace
-{
-    const int ZValue = 10;
-    const int CandidatesPreeditMargin = 10; // Margin between pre-edit rectangle and candidates
-    const int MaximumCandidateLength = 100; // Maximum length of a candidate.
-    const QString CandidatesListObjectName("CorrectionCandidateList");
-    const QString CandidatesItemObjectName("CorrectionCandidateItem");
-    const QString CandidatesItemLabelObjectName("CorrectionCandidateItemTitle");
-
-    // This GConf item defines whether multitouch is enabled or disabled
-    const QString MultitouchSettings = "/meegotouch/inputmethods/multitouch/enabled";
-};
-
-class MImCorrectionContentItemCreator : public MAbstractCellCreator<MImCorrectionCandidateItem>
-{
-public:
-    MImCorrectionContentItemCreator();
-    /*! \reimp */
-    virtual MWidget *createCell(const QModelIndex &index, MWidgetRecycler &recycler) const;
-    virtual void updateCell(const QModelIndex &index, MWidget *cell) const;
-    /*! \reimp_end */
-private:
-    void updateContentItemMode(const QModelIndex &index, MImCorrectionCandidateItem *contentItem) const;
-};
-
-MImCorrectionContentItemCreator::MImCorrectionContentItemCreator()
-{
-}
-
-MWidget *MImCorrectionContentItemCreator::createCell(const QModelIndex &index, MWidgetRecycler &recycler) const
-{
-    MWidget *cell = recycler.take(MContentItem::staticMetaObject.className());
-    if (cell == NULL) {
-        cell = new MImCorrectionCandidateItem(MContentItem::SingleTextLabel);
-        cell->setObjectName(CandidatesItemObjectName);
-    }
-    updateCell(index, cell);
-    return cell;
-}
-
-void MImCorrectionContentItemCreator::updateCell(const QModelIndex &index, MWidget *cell) const
-{
-    if (cell == NULL)
-        return;
-    MImCorrectionCandidateItem *contentItem = qobject_cast<MImCorrectionCandidateItem *>(cell);
-    const QVariant data = index.data(Qt::DisplayRole);
-    const QStringList rowData = data.value<QStringList>();
-    if (rowData.size() > 0) {
-        // Restrict the candidate length to MaximumCandidateLength characters.
-        contentItem->setTitle(rowData[0].left(MaximumCandidateLength));
-    }
-    updateContentItemMode(index, contentItem);
-}
-
-void MImCorrectionContentItemCreator::updateContentItemMode(const QModelIndex &index,
-        MImCorrectionCandidateItem *contentItem) const
-{
-    const int row = index.row();
-    bool thereIsNextRow = index.sibling(row + 1, 0).isValid();
-    if (row == 0) {
-        contentItem->setItemMode(MContentItem::SingleColumnTop);
-    } else if (thereIsNextRow) {
-        contentItem->setItemMode(MContentItem::SingleColumnCenter);
-    } else {
-        contentItem->setItemMode(MContentItem::SingleColumnBottom);
-    }
-}
-
-MImCorrectionCandidateContainer::MImCorrectionCandidateContainer(QGraphicsItem *parent)
-    : MStylableWidget(parent)
-{
-}
-
-MImCorrectionCandidateWidget::MImCorrectionCandidateWidget(QGraphicsWidget *parent)
-    : MSceneWindow(parent),
-      rotationInProgress(false),
-      candidatePosition(0, 0),
-      sceneManager(MPlainWindow::instance()->sceneManager()),
-      containerWidget(new MImCorrectionCandidateContainer(this)),
-      candidatesWidget(new MList(containerWidget)),
-      cellCreator(new MImCorrectionContentItemCreator),
-      candidatesModel(new QStringListModel(candidatesWidget)),
-      candidateWidth(0)
-{
-    // By default multi-touch is disabled
-    setAcceptTouchEvents(MGConfItem(MultitouchSettings).value().toBool());
-
-    setGeometry(QRectF(0, 0, sceneManager->visibleSceneSize().width(),
-                sceneManager->visibleSceneSize().height()));
-
-    // The z-value should always be more than vkb and text widget's z-value
-    setZValue(ZValue);
-
-    QGraphicsLinearLayout *layout = new QGraphicsLinearLayout;
-    containerWidget->setLayout(layout);
-
-    layout->addItem(candidatesWidget);
-    candidatesWidget->setObjectName(CandidatesListObjectName);
-    candidatesWidget->setCellCreator(cellCreator);
-    candidatesWidget->setItemModel(candidatesModel);
-    connect(candidatesWidget, SIGNAL(itemClicked(const QModelIndex &)), this, SLOT(select(const QModelIndex &)));
-}
-
-
-MImCorrectionCandidateWidget::~MImCorrectionCandidateWidget()
-{
-}
-
-void MImCorrectionCandidateWidget::setCandidates(const QStringList candidateList)
-{
-    // Filter the preedit from the candidate list.
-    QStringList filteredCandidateList = candidateList;
-    for (int i = 0; i < filteredCandidateList.size(); i++) {
-        if (m_preeditString.compare(filteredCandidateList.at(i), Qt::CaseInsensitive) == 0) {
-            filteredCandidateList.removeAt(i);
-            break;
-        }
-    }
-
-    // Below is the QT way to update model size
-    if (candidatesModel->rowCount() > 0)
-        candidatesModel->removeRows(0, candidatesModel->rowCount());
-    candidatesModel->insertRows(0, filteredCandidateList.size());
-    candidatesModel->setStringList(filteredCandidateList);
-
-    // Calculate the width for MContentItem dynamically.
-    // To ensure the whole words in candidate list could be shown.
-    candidateWidth = 0;
-    MLabel label;
-    label.setObjectName(CandidatesItemLabelObjectName);
-    label.setWordWrap(false);
-    // Below "label.preferredSize().width()" could be bigger than actual left + right
-    // margin of the label. But unfortunately there is no way to get styling parameters
-    // outside of styled object. So we assume the preferredSize of an empty label is
-    // just its left + right margin.
-    const int leftRightMargins = label.preferredSize().width();
-    foreach (const QString &candidate, filteredCandidateList) {
-        label.setText(candidate);
-        candidateWidth = qMax(candidateWidth, label.preferredSize().width());
-    }
-
-    candidateWidth += leftRightMargins;
-    // not less than minimum width
-    if (candidateWidth < containerWidget->minimumSize().width())
-        candidateWidth = containerWidget->minimumSize().width();
-}
-
-void MImCorrectionCandidateWidget::setPreeditString(const QString &string)
-{
-    m_preeditString = string;
-}
-
-QPoint MImCorrectionCandidateWidget::position() const
-{
-    return candidatePosition;
-}
-
-QStringList MImCorrectionCandidateWidget::candidates() const
-{
-    return candidatesModel->stringList();
-}
-
-QString MImCorrectionCandidateWidget::preeditString() const
-{
-    return m_preeditString;
-}
-
-void MImCorrectionCandidateWidget::setPosition(const QPoint &position, int bottomLimit)
-{
-    qDebug() << __PRETTY_FUNCTION__;
-    QSize sceneSize = sceneManager->visibleSceneSize();
-    int popupWidth = candidateWidth;
-    int popupHeight = candidatesWidget->preferredSize().height();
-    if (bottomLimit < 0) {
-        bottomLimit = sceneManager->visibleSceneSize().height();
-    }
-
-    candidatePosition = position;
-
-    // Adjust candidates list so that it doesn't
-    // overlap with scene boundary, if possible.
-
-    if (candidatePosition.x() + popupWidth > sceneSize.width())
-        candidatePosition.setX(sceneSize.width() - popupWidth);
-
-    if (candidatePosition.y() + popupHeight > bottomLimit)
-        candidatePosition.setY(bottomLimit - popupHeight);
-
-    if (candidatePosition.x() < 0)
-        candidatePosition.setX(0);
-    if (candidatePosition.y() < 0)
-        candidatePosition.setY(0);
-
-    containerWidget->setPos(candidatePosition.x(), candidatePosition.y());
-}
-
-void MImCorrectionCandidateWidget::setPosition(const QRect &preeditRect, const int bottomLimit)
-{
-    qDebug() << "in " << __PRETTY_FUNCTION__;
-
-    if (preeditRect.isNull() || !preeditRect.isValid()) {
-        candidatePosition = QPoint(0, 0);
-        return;
-    }
-
-    QPoint position;
-    QSize sceneSize = sceneManager->visibleSceneSize();
-    int popupWidth = candidateWidth;
-    int popupHeight = candidatesWidget->preferredSize().height();
-
-    // Set horizontal position
-
-    if (preeditRect.right() + CandidatesPreeditMargin + popupWidth < sceneSize.width()) {
-        // List is positioned to the right of pre-edit rectangle.
-        position.setX(preeditRect.x() + preeditRect.width() + CandidatesPreeditMargin);
-    } else if (preeditRect.x() - CandidatesPreeditMargin - popupWidth >= 0) {
-        // List is positioned to the left of pre-edit rectangle.
-        position.setX(preeditRect.x() - CandidatesPreeditMargin - popupWidth);
-    } else {
-        // No room in neither side. Pick one that has more.
-        int roomRight = sceneSize.width() - preeditRect.right();
-        int roomLeft = preeditRect.x();
-        if (roomRight >= roomLeft) {
-            // Align to right side of scene rect
-            position.setX(sceneSize.width() - popupWidth);
-        } else {
-            // Align to left side of scene rect
-            position.setX(0);
-        }
-    }
-
-    // Set vertical position
-
-    // Vertically the candidatesWidget is centered at the pre-edit rectangle.
-    position.setY(preeditRect.y() + preeditRect.height() / 2 - popupHeight / 2);
-
-    // Finally handle scene boundaries.
-    setPosition(position, bottomLimit);
-}
-
-void MImCorrectionCandidateWidget::showWidget()
-{
-    // The height of MList is automatically expanded.
-    // But the width of MList is not automatically expanded.
-    // So set the container widget's width to candidateWidth,
-    // to make MList have the enough width to show whole words.
-    containerWidget->setPreferredWidth(candidateWidth);
-    appear();
-
-    // Extend overlay window to whole screen area.
-    emit regionUpdated(mapRectToScene(QRect(QPoint(0, 0), sceneManager->visibleSceneSize())).toRect());
-}
-
-void MImCorrectionCandidateWidget::mousePressEvent(QGraphicsSceneMouseEvent *e)
-{
-    Q_UNUSED(e);
-}
-
-void MImCorrectionCandidateWidget::mouseReleaseEvent(QGraphicsSceneMouseEvent *e)
-{
-    Q_UNUSED(e);
-    disappear();
-}
-
-void MImCorrectionCandidateWidget::select(const QModelIndex &index)
-{
-    if (!index.isValid())
-        return;
-    const QVariant selectedVariant = candidatesModel->data(index, Qt::DisplayRole);
-    Q_ASSERT(selectedVariant.isValid());
-    const QString candidate = selectedVariant.toString();
-    if (candidate != m_preeditString) {
-        emit candidateClicked(candidate);
-    }
-    disappear();
-}
-
-void MImCorrectionCandidateWidget::hideEvent(QHideEvent *event)
-{
-    MWidget::hideEvent(event);
-    emit regionUpdated(QRegion());
-}
-
-int MImCorrectionCandidateWidget::activeIndex() const
-{
-    int activeWordIndex = -1;
-    QStringList candidateList = candidatesModel->stringList();
-    for (int i = 0; i < candidateList.size(); i++) {
-        if (m_preeditString.compare(candidateList.at(i), Qt::CaseInsensitive) == 0) {
-            activeWordIndex = i;
-            break;
-        }
-    }
-    return activeWordIndex;
-}
-
-void MImCorrectionCandidateWidget::prepareToOrientationChange()
-{
-    if (sceneWindowState() != MSceneWindow::Disappeared) {
-        rotationInProgress = true;
-        disappear();
-    }
-}
-
-void MImCorrectionCandidateWidget::finalizeOrientationChange()
-{
-    setGeometry(QRect(QPoint(0, 0), sceneManager->visibleSceneSize()));
-    if (rotationInProgress) {
-        showWidget();
-        rotationInProgress = false;
-    }
-}
-
-void MImCorrectionCandidateWidget::paintReactionMap(MReactionMap *reactionMap, QGraphicsView *view)
-{
-    // Clear all with inactive color.
-    reactionMap->setInactiveDrawingValue();
-    reactionMap->setTransform(QTransform());
-    reactionMap->fillRectangle(0, 0, reactionMap->width(), reactionMap->height());
-
-    // Draw the actual candidate candidatesWidget area.
-    reactionMap->setTransform(this, view);
-    reactionMap->setReactiveDrawingValue();
-    reactionMap->fillRectangle(candidatesWidget->geometry());
-}
-
-bool MImCorrectionCandidateWidget::sceneEvent(QEvent *e)
-{
-    MSceneWindow::sceneEvent(e);
-
-    // eat all the touch events to avoid the touch events
-    // go to the background virtual keyboard.
-    e->setAccepted(e->isAccepted()
-                   || e->type() == QEvent::TouchBegin
-                   || e->type() == QEvent::TouchUpdate
-                   || e->type() == QEvent::TouchEnd);
-    return e->isAccepted();
-}
-
--- m-keyboard/widgets/mimcorrectioncandidatewidget.h
+++ m-keyboard/widgets/mimcorrectioncandidatewidget.h
-/* * This file is part of meego-keyboard *
- *
- * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
- * All rights reserved.
- * Contact: Nokia Corporation (directui at nokia.com)
- *
- * If you have questions regarding the use of this file, please contact
- * Nokia at directui at nokia.com.
- *
- * This library is free software; you can redistribute it and/or
- * modify it 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.
- */
-
-
-
-#ifndef MIMCORRECTIONCANDIDATEWIDGET_H
-#define MIMCORRECTIONCANDIDATEWIDGET_H
-
-#include <QModelIndex>
-#include <MSceneWindow>
-#include <MStylableWidget>
-#include "mimcorrectioncandidatecontainerstyle.h"
-
-class MSceneManager;
-class MList;
-class MImCorrectionContentItemCreator;
-class MReactionMap;
-class QStringListModel;
-
-class MImCorrectionCandidateContainer: public MStylableWidget
-{
-    Q_OBJECT
-public:
-    explicit MImCorrectionCandidateContainer(QGraphicsItem *parent = 0);
-
-private:
-    M_STYLABLE_WIDGET(MImCorrectionCandidateContainerStyle)
-};
-
-/*!
-  \class MImCorrectionCandidateWidget
-  \brief The MImCorrectionCandidateWidget class is used to show error correction candidate list
-*/
-class MImCorrectionCandidateWidget: public MSceneWindow
-{
-    Q_OBJECT
-
-    friend class Ut_MImCorrectionCandidateWidget;
-
-public:
-    /*! Constructor
-     *
-     */
-    explicit MImCorrectionCandidateWidget(QGraphicsWidget *parent = 0);
-
-    /*! Destructor
-     *
-     */
-    ~MImCorrectionCandidateWidget();
-
-    /*! Set the candidate list
-     *
-     */
-    void setCandidates(QStringList candidate);
-
-    virtual void showWidget();
-
-    /*! \brief Sets the position of candidate list. The list cannot be outside screen.
-     *
-     * If \a bottomLimit is provided, it is respected but not at the expense of
-     * being out of screen.
-     */
-    void setPosition(const QPoint &pos, int bottomLimit = -1);
-
-    /*!
-     * \brief Sets the position of candidate list based on pre-edit rectangle.
-     *
-     * Candidate list is put horizontally next to the pre-edit rectangle. Right side
-     * of the rectangle is preferred but left side will be used if there is not enough
-     * space. Vertically list will be in the middle of the rectangle. If \a bottomLimit
-     * is provided, it is respected but not at the expense of being out of screen.
-     */
-    void setPosition(const QRect &preeditRect, int bottomLimit = -1);
-
-    /*! Returns the index of preedit string in the candidate list.
-     */
-    int activeIndex() const;
-
-    /*! Sets the preedit string
-     */
-    void setPreeditString(const QString &);
-
-    /*! Returns the actual position
-     * \return QPoint Returns the actual set position
-     */
-    QPoint position() const;
-
-    /*! Returns the actual set candidates
-     */
-    QStringList candidates() const;
-
-    /*! Returns the Preedit String
-     */
-    QString preeditString() const;
-
-    /*!
-     * Draw its reactive areas onto the reaction map
-     */
-    void paintReactionMap(MReactionMap *reactionMap, QGraphicsView *view);
-
-    //! Prepare virtual keyboard for orientation change
-    void prepareToOrientationChange();
-
-    //! Finalize orientation change
-    void finalizeOrientationChange();
-
-signals:
-    /*! Updates the preedit word
-     */
-    void candidateClicked(const QString &);
-
-    /*! Updates the screen region used by the widget
-     */
-    void regionUpdated(const QRegion &);
-
-protected:
-    /*! \reimp */
-    virtual void mousePressEvent(QGraphicsSceneMouseEvent *e);
-    virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *e);
-    virtual void hideEvent(QHideEvent *event);
-    virtual bool sceneEvent(QEvent *event);
-    /*! \reimp_end */
-
-protected slots:
-    void select(const QModelIndex &);
-
-private:
-    bool rotationInProgress;
-    QString m_preeditString;
-    QPoint candidatePosition;
-    MSceneManager *sceneManager;
-    MImCorrectionCandidateContainer *containerWidget;
-    MList *candidatesWidget;
-    MImCorrectionContentItemCreator *cellCreator;
-    QStringListModel *candidatesModel;
-    qreal candidateWidth;
-
-    Q_DISABLE_COPY(MImCorrectionCandidateWidget)
-};
-
-#endif
--- m-keyboard/widgets/mimcorrectionhost.cpp
+++ m-keyboard/widgets/mimcorrectionhost.cpp
+/* * This file is part of meego-keyboard *
+ *
+ * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ * All rights reserved.
+ * Contact: Nokia Corporation (directui at nokia.com)
+ *
+ * If you have questions regarding the use of this file, please contact
+ * Nokia at directui at nokia.com.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it 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.
+ */
+
+
+#include "mimcorrectionhost.h"
+#include "mimwordtracker.h"
+#include "mimwordlist.h"
+
+#include <QGraphicsLinearLayout>
+#include <QDebug>
+#include <QString>
+
+#include <MSceneWindow>
+#include <MSceneManager>
+#include <mreactionmap.h>
+#include <mplainwindow.h>
+#include <MGConfItem>
+
+namespace
+{
+    const int CandidatesPreeditGap = 10; // Gap between pre-edit and candidates
+    const int MinimumCandidateWidget = 64; // Minimum length of a candidate.
+};
+
+MImCorrectionHost::MImCorrectionHost(MSceneWindow *parentWindow)
+    : QObject(parentWindow),
+      rotationInProgress(false),
+      wordTrackerPosition(0, 0),
+      currentMode(MImCorrectionHost::WordTrackerMode),
+      wordTracker(new MImWordTracker(parentWindow)),
+      wordList(new MImWordList())
+{
+    connect(wordTracker, SIGNAL(candidateClicked(QString)), this, SLOT(handleCandidateClicked(QString)));
+    connect(wordTracker, SIGNAL(longTapped()), this, SLOT(longTap()));
+    connect(wordTracker, SIGNAL(regionChanged()), this, SLOT(sendRegion()));
+
+    connect(wordList, SIGNAL(candidateClicked(QString)), this, SLOT(handleCandidateClicked(QString)));
+    connect(wordList, SIGNAL(regionChanged()), this, SLOT(sendRegion()));
+}
+
+
+MImCorrectionHost::~MImCorrectionHost()
+{
+    delete wordTracker;
+    delete wordList;
+}
+
+bool MImCorrectionHost::isActive() const
+{
+    return (wordTracker->isVisible() || wordList->isVisible());
+}
+
+void MImCorrectionHost::setCandidates(const QStringList list)
+{
+    candidates = list;
+    suggestionString.clear();
+    if (candidates.isEmpty()) {
+        return;
+    }
+    // The first candidate is always the original input word.
+    // So if there are more than one suggestions, the second one is
+    // the suggestion word.
+    suggestionString = candidates.at(0);
+    if (candidates.count() > 1) {
+        suggestionString = candidates.at(1);
+    }
+
+    wordTracker->setCandidate(suggestionString);
+    wordList->setCandidates(candidates);
+}
+
+QPoint MImCorrectionHost::position() const
+{
+    return wordTrackerPosition;
+}
+
+void MImCorrectionHost::setPosition(const QPoint &position)
+{
+    wordTrackerPosition = position;
+
+    int sceneWidth = MPlainWindow::instance()->sceneManager()->visibleSceneSize().width();
+    wordTrackerPosition.setX(qBound(0, wordTrackerPosition.x(), (int)(sceneWidth - wordTracker->idealWidth())));
+
+    wordTracker->setPosition(wordTrackerPosition);
+}
+
+void MImCorrectionHost::setPosition(const QRect &preeditRect)
+{
+    if (preeditRect.isNull() || !preeditRect.isValid()) {
+        setPosition(QPoint(0, 0));
+        return;
+    }
+
+    QPoint position;
+
+    // Set horizontal position
+    // the right side correction widget is aligned with the right side of
+    // pre-edit rectangle + MinimumCandidateWidget
+    position.setX(preeditRect.right() + MinimumCandidateWidget - wordTracker->idealWidth());
+
+    // Set vertical position
+    // Vertically the candidatesWidget is below the pre-edit + CandidatesPreeditGap.
+    position.setY(preeditRect.bottom() + CandidatesPreeditGap);
+    setPosition(position);
+}
+
+void MImCorrectionHost::showCorrectionWidget(MImCorrectionHost::CandidateMode mode)
+{
+    currentMode = mode;
+
+    if (candidates.isEmpty()) {
+        hideCorrectionWidget();
+        return;
+    }
+
+    if (currentMode == WordTrackerMode) {
+        suggestionString = wordTracker->candidate();
+
+        if (!wordTracker->isVisible()) {
+            wordList->disappear();
+            wordTracker->appear(false);
+        }
+    } else {
+        // Always highlight the first item in the candidate list
+        // which is the origin input word
+        wordList->setHighlightCandidate(candidates.at(0));
+        wordTracker->disappear(false);
+        wordList->appear();
+    }
+}
+
+void MImCorrectionHost::hideCorrectionWidget()
+{
+    wordTracker->disappear(false);
+    wordList->disappear();
+}
+
+MImCorrectionHost::CandidateMode MImCorrectionHost::candidateMode() const
+{
+    return currentMode;
+}
+
+QString MImCorrectionHost::suggestion() const
+{
+    return suggestionString;
+}
+
+void MImCorrectionHost::prepareToOrientationChange()
+{
+    if (isActive()) {
+        rotationInProgress = true;
+        hideCorrectionWidget();
+    }
+}
+
+void MImCorrectionHost::finalizeOrientationChange()
+{
+    if (rotationInProgress) {
+        showCorrectionWidget(currentMode);
+        rotationInProgress = false;
+    }
+}
+
+void MImCorrectionHost::paintReactionMap(MReactionMap *reactionMap, QGraphicsView *view)
+{
+    if (!isActive())
+        return;
+
+    if (wordTracker->isVisible()) {
+        wordTracker->paintReactionMap(reactionMap, view);
+    }
+    if (wordList->isVisible()) {
+        wordList->paintReactionMap(reactionMap, view);
+    }
+}
+
+void MImCorrectionHost::sendRegion()
+{
+    QRegion region;
+    if (isActive()) {
+        if (currentMode == WordListMode) {
+            region = wordList->region();
+        } else {
+            region = wordTracker->region();
+        }
+    }
+    emit regionUpdated(region);
+}
+
+void MImCorrectionHost::handleCandidateClicked(const QString &candidate)
+{
+    if (!candidate.isEmpty() && isActive()) {
+        suggestionString = candidate;
+        emit candidateClicked(candidate);
+    }
+
+    hideCorrectionWidget();
+}
+
+void MImCorrectionHost::reset()
+{
+    setCandidates(QStringList());
+    hideCorrectionWidget();
+}
+
+void MImCorrectionHost::longTap()
+{
+    qDebug() << __PRETTY_FUNCTION__;
+    if (!isActive())
+        return;
+
+    showCorrectionWidget(WordListMode);
+}
--- m-keyboard/widgets/mimcorrectionhost.h
+++ m-keyboard/widgets/mimcorrectionhost.h
+/* * This file is part of meego-keyboard *
+ *
+ * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ * All rights reserved.
+ * Contact: Nokia Corporation (directui at nokia.com)
+ *
+ * If you have questions regarding the use of this file, please contact
+ * Nokia at directui at nokia.com.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it 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.
+ */
+
+
+
+#ifndef MIMCORRECTIONHOST_H
+#define MIMCORRECTIONHOST_H
+
+#include <QObject>
+#include <QPoint>
+#include <QString>
+#include <QStringList>
+#include <QRect>
+#include <QRegion>
+
+class MSceneWindow;
+class MReactionMap;
+class MImWordTracker;
+class MImWordList;
+class MReactionMap;
+class QGraphicsView;
+
+/*!
+  \class MImCorrectionHost
+  \brief The MImCorrectionHost class is used to show error correction
+  candidate word tracker or word list.
+*/
+class MImCorrectionHost : public QObject
+{
+    Q_OBJECT
+
+    friend class Ut_MImCorrectionHost;
+
+public:
+    //! CandidateMode is used by showCorrectionWidget and setMode.
+    enum CandidateMode {
+        WordTrackerMode,  //!< word tracker
+        WordListMode      //!< word suggestion list
+    };
+
+    /*! Constructor
+     *
+     */
+    explicit MImCorrectionHost(MSceneWindow *parentWindow);
+
+    /*! Destructor
+     *
+     */
+    ~MImCorrectionHost();
+
+    /*!
+     * \brief Returns true if tracker or word list is active.
+     */
+    bool isActive() const;
+
+    /*! Set the candidate list
+     *
+     */
+    void setCandidates(QStringList candidate);
+
+    /*!
+     * \brief Shows candidate widget with different \a mode.
+     *
+     * \sa CandidateMode.
+     */
+    virtual void showCorrectionWidget(CandidateMode mode = WordTrackerMode);
+
+    /*!
+     * \brief Hides candidate widget.
+     */
+    virtual void hideCorrectionWidget();
+
+    /*!
+     * \brief Returns current used mode for the candidate widget.
+     *
+     * \sa CandidateMode.
+     */
+    CandidateMode candidateMode() const;
+
+    /*! \brief Sets the position of word tracker.
+     *
+     * The word tracker cannot be outside screen.
+     */
+    void setPosition(const QPoint &pos);
+
+    /*!
+     * \brief Sets the position of word tracker based on pre-edit rectangle.
+     *
+     * TODO: this method will be removed. Will only support to set word tracker position
+     * according the cursor position, not base on pre-edit rectangle anymore.
+     */
+    void setPosition(const QRect &preeditRect);
+
+    /*!
+     * \brief Returns the suggested word.
+     *
+     * The suggestion is the word present on word tracker or the one clicked by user in
+     * the word list.
+     */
+    QString suggestion() const;
+
+    /*! Returns the actual position
+     * \return QPoint Returns the actual set position.
+     */
+    QPoint position() const;
+
+    /*!
+     * \brief Draw its reactive areas onto the reaction map
+     */
+    void paintReactionMap(MReactionMap *reactionMap, QGraphicsView *view);
+
+    //! Prepare virtual keyboard for orientation change
+    void prepareToOrientationChange();
+
+    //! Finalize orientation change
+    void finalizeOrientationChange();
+
+    //! Clear stored suggestion and hide candidate widget.
+    void reset();
+
+signals:
+    //! Updates the preedit word
+    void candidateClicked(const QString &);
+
+    //! Updates the screen region used by the widget
+    void regionUpdated(const QRegion &);
+
+protected slots:
+
+    void handleCandidateClicked(const QString &candidate);
+
+    void sendRegion();
+
+    void longTap();
+
+private:
+    bool rotationInProgress;
+    QPoint wordTrackerPosition;
+    QStringList candidates;
+    CandidateMode currentMode;
+    QString suggestionString;
+
+    MImWordTracker *wordTracker;
+    MImWordList *wordList;
+
+    Q_DISABLE_COPY(MImCorrectionHost)
+};
+
+#endif
--- m-keyboard/widgets/mimkey.cpp
+++ m-keyboard/widgets/mimkey.cpp
@@ -49,19 +49,19 @@
                const MImAbstractKeyAreaStyleContainer &style,
                QGraphicsItem &parent)
     : width(0),
-      model(newModel),
+      mModel(newModel),
       shift(false),
-      currentLabel(model.binding(false)->label()),
+      currentLabel(mModel.binding(false)->label()),
       currentState(Normal),
       selected(false),
       styleContainer(style),
       parentItem(parent),
       currentTouchPointCount(0)
 {
-    if (model.binding(false)) {
+    if (mModel.binding(false)) {
         loadIcon(false);
     }
-    if (model.binding(true)) {
+    if (mModel.binding(true)) {
         loadIcon(true);
     }
 }
@@ -114,6 +114,14 @@
 
     if (newState != currentState) {
         currentState = newState;
+
+        if ((currentState == Pressed || currentState == Selected)
+            && (not activeKeys.contains(this))) {
+            activeKeys.append(this);
+        } else { // currentState == Normal
+            activeKeys.removeAll(this);
+        }
+
         update();
     }
 }
@@ -133,14 +141,14 @@
     return currentState;
 }
 
-const MImKeyModel &MImKey::key() const
+const MImKeyModel &MImKey::model() const
 {
-    return model;
+    return mModel;
 }
 
 const MImKeyBinding &MImKey::binding() const
 {
-    return *model.binding(shift);
+    return *mModel.binding(shift);
 }
 
 bool MImKey::isDeadKey() const
@@ -212,9 +220,11 @@
     return iconInfo().id;
 }
 
-void MImKey::drawIcon(const QRect &rectangle, QPainter *painter) const
+void MImKey::drawIcon(QPainter *painter) const
 {
-    const QPixmap *iconPixmap = icon();
+    const QPixmap *iconPixmap(icon());
+    const QRect rectangle(buttonRect().toRect());
+
     if (iconPixmap) {
         QPointF iconPos(rectangle.x() + (rectangle.width() - iconPixmap->width()) / 2,
                         rectangle.y() + (rectangle.height() - iconPixmap->height()) / 2);
@@ -230,7 +240,7 @@
 
 int MImKey::preferredFixedWidth() const
 {
-    switch(model.width()) {
+    switch(mModel.width()) {
     case MImKeyModel::Small:
         return styleContainer->keyWidthSmallFixed();
 
@@ -258,7 +268,7 @@
 
 qreal MImKey::preferredWidth(qreal pixelPerSizeUnit, qreal spacing) const
 {
-    switch(model.width()) {
+    switch(mModel.width()) {
     case MImKeyModel::Small:
         return computeWidth(pixelPerSizeUnit,
                             spacing,
@@ -295,10 +305,14 @@
     return -1;
 }
 
+bool MImKey::belongsTo(const QGraphicsItem *item) const
+{
+    return (item && (&parentItem == item));
+}
 void MImKey::loadIcon(bool shift)
 {
     IconInfo &iconInfo(shift ? upperCaseIcon : lowerCaseIcon);
-    const MImKeyBinding::KeyAction action(model.binding(shift)->action());
+    const MImKeyBinding::KeyAction action(mModel.binding(shift)->action());
     QSize size;
     QString iconProperty;
 
@@ -316,7 +330,7 @@
             size = styleContainer->keyShiftIconSize();
             break;
         case MImKeyBinding::ActionReturn:
-            if (model.binding(shift)->label().isEmpty()) {
+            if (mModel.binding(shift)->label().isEmpty()) {
                 iconProperty = "keyEnterIconId";
                 size = styleContainer->keyEnterIconSize();
             }
@@ -326,7 +340,7 @@
             size = styleContainer->keyMenuIconSize();
             break;
         case MImKeyBinding::ActionTab:
-            if (model.binding(shift)->label().isEmpty()) {
+            if (mModel.binding(shift)->label().isEmpty()) {
                 iconProperty = "keyTabIconId";
                 size = styleContainer->keyTabIconSize();
             }
@@ -335,7 +349,7 @@
             break;
     }
 
-    iconInfo.id = getCSSProperty<QString>(styleContainer, iconProperty, model.rtl());
+    iconInfo.id = getCSSProperty<QString>(styleContainer, iconProperty, mModel.rtl());
 
     if (!iconInfo.id.isEmpty()) {
         iconInfo.pixmap = MTheme::pixmap(iconInfo.id, size);
--- m-keyboard/widgets/mimkey.h
+++ m-keyboard/widgets/mimkey.h
@@ -23,13 +23,14 @@
 
 class MImAbstractKeyAreaStyleContainer;
 class QGraphicsItem;
+class MImKeyArea;
 
 //! Represents a key model with the key's current binding state, and also contains its visible area.
 class MImKey
     : public MImAbstractKey
 {
 public:
-    explicit MImKey(const MImKeyModel &model,
+    explicit MImKey(const MImKeyModel &mModel,
                     const MImAbstractKeyAreaStyleContainer &style,
                     QGraphicsItem &parent);
 
@@ -40,11 +41,12 @@
     virtual const QString secondaryLabel() const;
     virtual const QRectF &buttonRect() const;
     virtual const QRectF &buttonBoundingRect() const;
-    virtual void setModifiers(bool shift, QChar accent = QChar());
+    virtual void setModifiers(bool shift,
+                              QChar accent = QChar());
     virtual void setDownState(bool down);
     virtual void setSelected(bool select);
     virtual ButtonState state() const;
-    virtual const MImKeyModel &key() const;
+    virtual const MImKeyModel &model() const;
     virtual const MImKeyBinding &binding() const;
     virtual bool isDeadKey() const;
     virtual bool isShiftKey() const;
@@ -64,9 +66,8 @@
     //! \brief Returns icon identifier, if it was loaded.
     QString iconId() const;
 
-    //! \brief Draws the icon of this button, if it has one, to the given rectangle.
-    void drawIcon(const QRect &rectangle, QPainter *painter) const;
-
+    //! \brief Draws the icon of this key, if available.
+    virtual void drawIcon(QPainter *painter) const;
     //! \brief Calls parent item's QGraphicsItem::update() who actually draws the button.
     void update();
 
@@ -76,6 +77,10 @@
     //! Returns preferred dynamic width
     qreal preferredWidth(qreal pixelPerSizeUnit, qreal spacing) const;
 
+    //! \brief Whether a key belongs to a given graphics item.
+    //! \param item the graphics item that logically contains this key
+    virtual bool belongsTo(const QGraphicsItem *item) const;
+
     //! Cache for the buttons position and size. They can always
     //! be calculated but are faster to access this way.
     QRectF cachedBoundingRect;
@@ -101,7 +106,7 @@
     void loadIcon(bool shift);
     const IconInfo &iconInfo() const;
 
-    const MImKeyModel &model;
+    const MImKeyModel &mModel;
 
     bool shift;
     QChar accent;
--- m-keyboard/widgets/mimkeyarea.cpp
+++ m-keyboard/widgets/mimkeyarea.cpp
@@ -14,8 +14,6 @@
  * of this file.
  */
 
-
-
 #include "mvirtualkeyboardstyle.h"
 #include "mimkeyarea.h"
 
@@ -26,6 +24,7 @@
 #include <QPainter>
 #include <QTextCharFormat>
 #include <QTextLine>
+#include <QList>
 
 #include <MApplication>
 #include <MComponentData>
@@ -33,6 +32,7 @@
 #include <mplainwindow.h>
 #include <mreactionmap.h>
 #include <MTheme>
+#include <MTimestamp>
 
 namespace {
     template<class T>
@@ -58,6 +58,150 @@
         // not found:
         return -1;
     }
+
+    //! \brief Helper class responsible for key-painting aspect.
+    //!
+    //! Can be used as visitor.
+    class KeyPainter
+        : public MImAbstractKeyVisitor
+    {
+    public:
+        //! PaintMode can be used to optimize drawing for HW acceleration
+        enum PaintMode {
+            PaintBackground, //!< Only draw background of given keys, fills up iconKeys
+            PaintBackgroundAndIcon  //!< Draw both, background and icon, in one go
+        };
+
+    private:
+        const MImKeyArea *const keyArea; //!< owner of the keys
+        QPainter *const painter; //!< used for painting
+        PaintMode mode; //!< current paint mode
+        mutable QList<const MImKey *> iconKeys; //!< collects keys with icons, \sa drawIcons
+
+    public:
+        explicit KeyPainter(const MImKeyArea *newKeyArea,
+                            QPainter *newPainter,
+                            PaintMode newMode = PaintBackgroundAndIcon)
+            : keyArea(newKeyArea)
+            , painter(newPainter)
+            , mode(newMode)
+        {
+            Q_ASSERT(keyArea != 0);
+            Q_ASSERT(painter != 0);
+        }
+
+        //! \brief Paints background or icon of given key, depending on PaintMode
+        //! \param abstractKey the given key
+        bool operator()(MImAbstractKey *abstractKey)
+        {
+            return operator()(dynamic_cast<const MImKey *>(abstractKey));
+        }
+
+        //! \brief Paints background or icon of given key.
+        //! \param key the given key
+        bool operator()(const MImKey *key) const
+        {
+            if (key && key->belongsTo(keyArea)) {
+                switch (mode) {
+
+                case PaintBackground:
+                    // Collect keys first, need to call drawIcons separately:
+                    drawBackground(painter, key);
+
+                    if (key->icon()) {
+                        iconKeys.append(key);
+                    }
+
+                    break;
+
+                case PaintBackgroundAndIcon:
+                default:
+                    drawBackground(painter, key);
+                    key->drawIcon(painter);
+                    break;
+                }
+            }
+
+            // If used as visitor, then we need to visit all active keys:
+            return false;
+        }
+
+        //! \brief Draw all previously visited keys with icons
+        void drawIcons() const
+        {
+            foreach (const MImKey *key, iconKeys) {
+                key->drawIcon(painter);
+            }
+        }
+
+        //! \brief Draws background for a given key.
+        //! \param painter the painter to be used
+        //! \param key key for which background shall be drawn
+        void drawBackground(QPainter *painter,
+                            const MImAbstractKey *key) const
+        {
+            if (!key) {
+                return;
+            }
+
+            const MScalableImage *background = 0;
+
+            switch (key->state()) {
+
+            case MImAbstractKey::Normal:
+                switch (key->model().style()) {
+                case MImKeyModel::SpecialStyle:
+                    background = keyArea->baseStyle()->keyBackgroundSpecial();
+                    break;
+                case MImKeyModel::DeadkeyStyle:
+                    background = keyArea->baseStyle()->keyBackgroundDeadkey();
+                    break;
+                case MImKeyModel::NormalStyle:
+                default:
+                    background = keyArea->baseStyle()->keyBackground();
+                    break;
+                }
+                break;
+
+            case MImAbstractKey::Pressed:
+                switch (key->model().style()) {
+                case MImKeyModel::SpecialStyle:
+                    background = keyArea->baseStyle()->keyBackgroundSpecialPressed();
+                    break;
+                case MImKeyModel::DeadkeyStyle:
+                    background = keyArea->baseStyle()->keyBackgroundDeadkeyPressed();
+                    break;
+                case MImKeyModel::NormalStyle:
+                default:
+                    background = keyArea->baseStyle()->keyBackgroundPressed();
+                    break;
+                }
+                break;
+
+            case MImAbstractKey::Selected:
+                switch (key->model().style()) {
+                case MImKeyModel::SpecialStyle:
+                    background = keyArea->baseStyle()->keyBackgroundSpecialSelected();
+                    break;
+                case MImKeyModel::DeadkeyStyle:
+                    background = keyArea->baseStyle()->keyBackgroundDeadkeySelected();
+                    break;
+                case MImKeyModel::NormalStyle:
+                default:
+                    background = keyArea->baseStyle()->keyBackgroundSelected();
+                    break;
+                }
+                break;
+
+            default:
+                break;
+            }
+
+            if (background) {
+                background->draw(key->buttonRect().toRect(), painter);
+            }
+        }
+    };
 }
 
 MImKeyArea::MImKeyArea(const LayoutData::SharedLayoutSection &newSection,
@@ -65,11 +209,13 @@
                        QGraphicsWidget *parent)
     : MImAbstractKeyArea(newSection, usePopup, parent),
       rowList(newSection->rowCount()),
-      widgetHeight(computeWidgetHeight()),
-      mMaxNormalizedWidth(maxNormalizedWidth()),
-      shiftButton(0),
+      cachedWidgetHeight(computeWidgetHeight()),
+      mMaxNormalizedWidth(computeMaxNormalizedWidth()),
+      shiftKey(0),
       textDirty(false),
-      equalWidthButtons(true)
+      cachedBackgroundDirty(true),
+      hasCachedBackground(false),
+      equalWidthKeys(true)
 {
     textLayout.setCacheEnabled(true);
     setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
@@ -80,12 +226,13 @@
 MImKeyArea::~MImKeyArea()
 {
     for (RowIterator rowIter(rowList.begin()); rowIter != rowList.end(); ++rowIter) {
-        qDeleteAll(rowIter->buttons);
-        rowIter->buttons.clear();
+        qDeleteAll(rowIter->keys);
+        rowIter->keys.clear();
     }
 }
 
-QSizeF MImKeyArea::sizeHint(Qt::SizeHint which, const QSizeF &/*constraint*/) const
+QSizeF MImKeyArea::sizeHint(Qt::SizeHint which,
+                            const QSizeF &) const
 {
     int width = 0;
     if (which == Qt::MaximumSize) {
@@ -93,17 +240,18 @@
         // will apply a constraint for this.
         width = QWIDGETSIZE_MAX;
     }
-    return QSizeF(width, widgetHeight);
+    return QSizeF(width, cachedWidgetHeight);
 }
 
-void MImKeyArea::drawReactiveAreas(MReactionMap *reactionMap, QGraphicsView *view)
+void MImKeyArea::drawReactiveAreas(MReactionMap *reactionMap,
+                                   QGraphicsView *view)
 {
     reactionMap->setTransform(this, view);
     reactionMap->setReactiveDrawingValue();
 
-    foreach (const ButtonRow &row, rowList) {
-        foreach (const MImKey *const button, row.buttons) {
-            reactionMap->fillRectangle(button->buttonBoundingRect());
+    foreach (const KeyRow &row, rowList) {
+        foreach (const MImKey *const key, row.keys) {
+            reactionMap->fillRectangle(key->buttonBoundingRect());
         }
     }
 }
@@ -117,25 +265,25 @@
     for (int row = 0; row != numRows; ++row, ++rowIter) {
         const int numColumns = sectionModel()->columnsAt(row);
 
-        rowIter->stretchButton = 0;
+        rowIter->stretchKey = 0;
 
-        // Add buttons
+        // Add keys
         for (int col = 0; col < numColumns; ++col) {
             // Parameters to fetch from base class.
             MImKeyModel *dataKey = sectionModel()->keyModel(row, col);
-            MImKey *button = new MImKey(*dataKey, baseStyle(), *this);
+            MImKey *key = new MImKey(*dataKey, baseStyle(), *this);
 
-            // TODO: Remove restriction to have only one shift button per layout?
+            // TODO: Remove restriction to have only one shift key per layout?
             if (dataKey->binding()->action() == MImKeyBinding::ActionShift) {
-                shiftButton = button;
+                shiftKey = key;
             }
 
             // Only one stretching item per row.
-            if (!rowIter->stretchButton) {
-                rowIter->stretchButton = (dataKey->width() == MImKeyModel::Stretched ? button : 0);
+            if (!rowIter->stretchKey) {
+                rowIter->stretchKey = (dataKey->width() == MImKeyModel::Stretched ? key : 0);
             }
 
-            rowIter->buttons.append(button);
+            rowIter->keys.append(key);
         }
     }
 
@@ -157,22 +305,23 @@
     // labels. This must be done before QTextLayout::beginLayout().
     QString labelContent;
 
-    foreach (const ButtonRow &row, rowList) {
-
-        foreach (const MImKey *button, row.buttons) {
+    foreach (const KeyRow &row, rowList) {
+        foreach (const MImKey *key, row.keys) {
             // primary label
-            QString label = button->label();
+            QString label = key->label();
 
             if (!label.isEmpty()) {
                 // Add whitespace for QTextLine to be able to cut.
                 labelContent += label + " ";
 
                 // try secondary label
-                label = button->secondaryLabel();
+                label = key->secondaryLabel();
 
                 if (!label.isEmpty()) {
                     // Add formatting for this secondary label.
-                    QTextLayout::FormatRange formatRange = {labelContent.length(), label.length(), secondaryFormat};
+                    QTextLayout::FormatRange formatRange = {labelContent.length(),
+                                                            label.length(),
+                                                            secondaryFormat};
                     formatList.append(formatRange);
                     labelContent += label + " ";
                 }
@@ -203,16 +352,16 @@
         // rowHasSecondaryLabel is needed for the vertical alignment of
         // secondary label purposes.
         bool rowHasSecondaryLabel = false;
-        foreach (MImKey *button, row->buttons) {
-            if (!button->secondaryLabel().isEmpty()) {
+
+        foreach (const MImKey *key, row->keys) {
+            if (!key->secondaryLabel().isEmpty()) {
                 rowHasSecondaryLabel = true;
             }
         }
 
-        foreach (MImKey *button, row->buttons) {
-
-            const QString &label(button->label());
-            const QString &secondary(button->secondaryLabel());
+        foreach (const MImKey *key, row->keys) {
+            const QString &label(key->label());
+            const QString &secondary(key->secondaryLabel());
             QPoint labelPos;
             QPoint secondaryLabelPos;
 
@@ -221,14 +370,14 @@
                 continue;
             }
 
-            const QRectF &buttonRect = button->cachedButtonRect;
+            const QRectF &keyRect = key->cachedButtonRect;
 
             if (!rowHasSecondaryLabel) {
                 // All horizontally centered.
-                labelPos = fm.boundingRect(buttonRect.x(),
-                                           buttonRect.y(),
-                                           buttonRect.width(),
-                                           buttonRect.height(),
+                labelPos = fm.boundingRect(keyRect.x(),
+                                           keyRect.y(),
+                                           keyRect.width(),
+                                           keyRect.height(),
                                            Qt::AlignCenter,
                                            label).topLeft();
             } else {
@@ -241,22 +390,22 @@
                 if (landscape) {
                     // primary label: horizontally centered, top margin defines y
                     // secondary: horizontally centered, primary bottom + separation margin defines y
-                    const int primaryY = buttonRect.top() + topMargin;
-                    labelPos.setX(buttonRect.center().x() - fm.width(label) / 2);
+                    const int primaryY = keyRect.top() + topMargin;
+                    labelPos.setX(keyRect.center().x() - fm.width(label) / 2);
                     labelPos.setY(primaryY);
                     if (!secondary.isEmpty()) {
-                        secondaryLabelPos.setX(buttonRect.center().x() - secondaryFm.width(secondary) / 2);
+                        secondaryLabelPos.setX(keyRect.center().x() - secondaryFm.width(secondary) / 2);
                         secondaryLabelPos.setY(primaryY + labelHeight + secondarySeparation);
                     }
                 } else {
                     // primary label: horizontally according to left margin, vertically centered
                     // secondary: horizontally on right of primary + separation margin, vertically centered
-                    const int primaryX = buttonRect.left() + labelLeftWithSecondary;
+                    const int primaryX = keyRect.left() + labelLeftWithSecondary;
                     labelPos.setX(primaryX);
-                    labelPos.setY(buttonRect.center().y() - labelHeight / 2);
+                    labelPos.setY(keyRect.center().y() - labelHeight / 2);
                     if (!secondary.isEmpty()) {
                         secondaryLabelPos.setX(primaryX + fm.width(label) + secondarySeparation);
-                        secondaryLabelPos.setY(buttonRect.center().y() - secondaryLabelHeight / 2);
+                        secondaryLabelPos.setY(keyRect.center().y() - secondaryLabelHeight / 2);
                     }
                 }
             }
@@ -302,117 +451,95 @@
     return qMax<qreal>(0.0, height);
 }
 
-void MImKeyArea::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *)
+void MImKeyArea::paint(QPainter *onScreenPainter,
+                       const QStyleOptionGraphicsItem *,
+                       QWidget *)
 {
+    mTimestamp("MImKeyArea", "start");
     const MImAbstractKeyAreaStyleContainer &style(baseStyle());
-    const MScalableImage *background = style->backgroundImage();
 
-    if (background) {
-        background->draw(boundingRect().toRect(), painter);
-    }
+    // Key areas are disabled during animations. Once we are animated, it
+    // makes no sense to maintain the offscreen cache (especially when
+    // HW-accelerated). Therefore, we draw directly to the screen.
+    // However, we need to remember whether we drew a current version of the
+    // key area to the offscreen cache at all (that's what hasCachedBackground
+    // is for).
+    QPainter *currentPainter = onScreenPainter;
+    if (cachedBackgroundDirty
+        || !isEnabled()
+        || !hasCachedBackground) {
 
-    // Draw images first.
-    foreach (const ButtonRow &row, rowList) {
-        foreach (const MImKey *button, row.buttons) {
-            drawKeyBackground(painter, button);
-            button->drawIcon(button->cachedButtonRect.toRect(), painter);
-            drawDebugRects(painter, button,
-                           style->drawButtonBoundingRects(),
-                           style->drawButtonRects());
-        }
-    }
-
-    if (style->drawReactiveAreas()) {
-        drawDebugReactiveAreas(painter);
-    }
+        // TODO: find out why size()'s height is incorrect for popup.
+        initCachedBackground(QSize(size().width(), cachedWidgetHeight));
+        QPainter offScreenPainter(cachedBackground.get());
 
-    if (textDirty) {
-        buildTextLayout();
-    }
+        if (isEnabled()) {
+            currentPainter = &offScreenPainter;
+            hasCachedBackground = true;
+        }
 
-    // Draw text next.
-    painter->setPen(style->fontColor());
-    textLayout.draw(painter, QPoint());
-}
+        const MScalableImage *background = style->backgroundImage();
 
-void MImKeyArea::drawKeyBackground(QPainter *painter,
-                                               const MImKey *button)
-{
-    if (!button) {
-        return;
-    }
+        if (background) {
+            background->draw(boundingRect().toRect(), currentPainter);
+        }
 
-    const MScalableImage *background = 0;
+        const bool drawButtonBoundingRects(style->drawButtonBoundingRects());
+        const bool drawButtonRects(style->drawButtonRects());
 
-    switch (button->state()) {
+        // In case of HW acceleration, we want to avoid switching between textures, if possible
+        // Draw backgrounds first, icons later:
+        const KeyPainter kp(this, currentPainter, KeyPainter::PaintBackground);
 
-    case MImAbstractKey::Normal:
-        switch (button->key().style()) {
-        case MImKeyModel::SpecialStyle:
-            background = baseStyle()->keyBackgroundSpecial();
-            break;
-        case MImKeyModel::DeadkeyStyle:
-            background = baseStyle()->keyBackgroundDeadkey();
-            break;
-        case MImKeyModel::NormalStyle:
-        default:
-            background = baseStyle()->keyBackground();
-            break;
+        foreach (const KeyRow &row, rowList) {
+            foreach (const MImKey *key, row.keys) {
+                kp(key);
+                drawDebugRects(currentPainter, key,
+                               drawButtonBoundingRects,
+                               drawButtonRects);
+            }
         }
-        break;
 
-    case MImAbstractKey::Pressed:
-        switch (button->key().style()) {
-        case MImKeyModel::SpecialStyle:
-            background = baseStyle()->keyBackgroundSpecialPressed();
-            break;
-        case MImKeyModel::DeadkeyStyle:
-            background = baseStyle()->keyBackgroundDeadkeyPressed();
-            break;
-        case MImKeyModel::NormalStyle:
-        default:
-            background = baseStyle()->keyBackgroundPressed();
-            break;
-        }
-        break;
+        kp.drawIcons();
 
-    case MImAbstractKey::Selected:
-        switch (button->key().style()) {
-        case MImKeyModel::SpecialStyle:
-            background = baseStyle()->keyBackgroundSpecialSelected();
-            break;
-        case MImKeyModel::DeadkeyStyle:
-            background = baseStyle()->keyBackgroundDeadkeySelected();
-            break;
-        case MImKeyModel::NormalStyle:
-        default:
-            background = baseStyle()->keyBackgroundSelected();
-            break;
+        if (style->drawReactiveAreas()) {
+            drawDebugReactiveAreas(currentPainter);
         }
-        break;
+    }
 
-    default:
-        break;
+    if (hasCachedBackground) {
+        onScreenPainter->drawPixmap(boundingRect().toRect(), *cachedBackground.get());
     }
 
-    if (background) {
-        background->draw(button->cachedButtonRect.toRect(), painter);
+    KeyPainter kp(this, onScreenPainter, KeyPainter::PaintBackground);
+    MImAbstractKey::visitActiveKeys(&kp);
+    kp.drawIcons();
+
+    if (textDirty) {
+        buildTextLayout();
     }
+
+    // Draw text next.
+    onScreenPainter->setPen(style->fontColor());
+    textLayout.draw(onScreenPainter, QPoint());
+
+    cachedBackgroundDirty = false;
+    mTimestamp("MImKeyArea", "end");
 }
 
 void MImKeyArea::drawDebugRects(QPainter *painter,
-                                            const MImKey *button,
-                                            bool drawBoundingRects,
-                                            bool drawRects)
+                                const MImAbstractKey *key,
+                                bool drawBoundingRects,
+                                bool drawRects) const
 {
     if (drawBoundingRects) {
         painter->save();
         painter->setPen(Qt::red);
         painter->setBrush(QBrush(QColor(64, 0, 0, 64)));
-        painter->drawRect(button->buttonBoundingRect());
-        painter->drawText(button->buttonRect().adjusted(4, 4, -4, -4),
-                          QString("%1x%2").arg(button->buttonBoundingRect().width())
-                          .arg(button->buttonBoundingRect().height()));
+        painter->drawRect(key->buttonBoundingRect());
+        painter->drawText(key->buttonRect().adjusted(4, 4, -4, -4),
+                          QString("%1x%2").arg(key->buttonBoundingRect().width())
+                          .arg(key->buttonBoundingRect().height()));
         painter->restore();
     }
 
@@ -420,10 +547,10 @@
         painter->save();
         painter->setPen(Qt::green);
         painter->setBrush(QBrush(QColor(0, 64, 0, 64)));
-        painter->drawRect(button->buttonRect());
-        painter->drawText(button->buttonRect().adjusted(4, 16, -4, -16),
-                          QString("%1x%2").arg(button->buttonRect().width())
-                          .arg(button->buttonRect().height()));
+        painter->drawRect(key->buttonRect());
+        painter->drawText(key->buttonRect().adjusted(4, 16, -4, -16),
+                          QString("%1x%2").arg(key->buttonRect().width())
+                          .arg(key->buttonRect().height()));
         painter->restore();
     }
 }
@@ -442,10 +569,10 @@
         painter->drawLine(QPointF(0, rowPair.second),
                           QPointF(size().width(), rowPair.second));
 
-        QVector<QPair<qreal, qreal> > buttonOffsets = rowList[rowIdx].buttonOffsets;
+        const QVector<QPair<qreal, qreal> > &keyOffsets = rowList[rowIdx].keyOffsets;
 
-        for(int colIdx = 0; colIdx < buttonOffsets.size(); ++colIdx) {
-            QPair<qreal, qreal> colPair = buttonOffsets[colIdx];
+        for(int colIdx = 0; colIdx < keyOffsets.size(); ++colIdx) {
+            QPair<qreal, qreal> colPair = keyOffsets[colIdx];
             painter->setPen(Qt::cyan);
             painter->drawLine(QPointF(colPair.first, rowPair.first),
                               QPointF(colPair.first, rowPair.second));
@@ -459,6 +586,17 @@
     painter->restore();
 }
 
+void MImKeyArea::initCachedBackground(const QSize &newSize)
+{
+    if (cachedBackground.get() && newSize == cachedBackground->size()) {
+        return; // already initialized
+    }
+
+    cachedBackground.reset(new QPixmap(newSize));
+    cachedBackground->fill(Qt::transparent);
+    hasCachedBackground = false;
+}
+
 MImAbstractKey *MImKeyArea::keyAt(const QPoint &pos) const
 {
     const int numRows = rowList.count();
@@ -473,47 +611,49 @@
         return 0;
     }
 
-    const ButtonRow &currentRow = rowList.at(rowIndex);
-    const int buttonIndex = binaryRangeFind<qreal>(pos.x(), currentRow.buttonOffsets);
+    const KeyRow &currentRow = rowList.at(rowIndex);
+    const int keyIndex = binaryRangeFind<qreal>(pos.x(), currentRow.keyOffsets);
 
-    if (buttonIndex == -1) {
+    if (keyIndex == -1) {
         return 0;
     }
 
-    return currentRow.buttons.at(buttonIndex);
+    return currentRow.keys.at(keyIndex);
 }
 
 void MImKeyArea::setShiftState(ModifierState newShiftState)
 {
-    if (shiftButton) {
-        shiftButton->setModifiers(newShiftState != ModifierClearState);
-        shiftButton->setSelected(newShiftState == ModifierLockedState);
+    if (shiftKey) {
+        shiftKey->setModifiers(newShiftState != ModifierClearState);
+        shiftKey->setSelected(newShiftState == ModifierLockedState);
     }
 }
 
-void MImKeyArea::modifiersChanged(const bool shift, const QChar accent)
+void MImKeyArea::modifiersChanged(const bool shift,
+                                  const QChar &accent)
 {
     for (RowIterator row(rowList.begin()); row != rowList.end(); ++row) {
-        foreach (MImKey *button, row->buttons) {
-            // Shift button and selected keys are detached from the normal level changing.
-            if (button != this->shiftButton
-                && button->state() != MImAbstractKey::Selected) {
-                button->setModifiers(shift, accent);
+        foreach (MImKey *key, row->keys) {
+            // Shift key and selected keys are detached from the normal level changing.
+            if (key != this->shiftKey
+                && key->state() != MImAbstractKey::Selected) {
+                key->setModifiers(shift, accent);
             }
         }
     }
 
     textDirty = true;
-
 }
 
-void MImKeyArea::updateButtonGeometriesForWidth(const int newAvailableWidth)
+void MImKeyArea::updateKeyGeometries(const int newAvailableWidth)
 {
     if (sectionModel()->maxColumns() == 0) {
         return;
     }
 
-    widgetHeight = computeWidgetHeight();
+    cachedWidgetHeight = computeWidgetHeight();
+    initCachedBackground(QSize(newAvailableWidth, cachedWidgetHeight));
+
     rowOffsets.clear();
 
     const MImAbstractKeyAreaStyleContainer &style(baseStyle());
@@ -525,27 +665,27 @@
                                         - (style->paddingLeft() + style->paddingRight()));
 
     const qreal normalizedWidth = qMax<qreal>(1.0, mMaxNormalizedWidth);
-    const qreal availableWidthForButtons = availableWidth - ((normalizedWidth - 1) * HorizontalSpacing);
-    mRelativeButtonBaseWidth = availableWidthForButtons / normalizedWidth;
-    emit relativeButtonBaseWidthChanged(mRelativeButtonBaseWidth);
+    const qreal availableWidthForKeys = availableWidth - ((normalizedWidth - 1) * HorizontalSpacing);
+    mRelativeKeyBaseWidth = availableWidthForKeys / normalizedWidth;
+    emit relativeKeyBaseWidthChanged(mRelativeKeyBaseWidth);
 
-    // This is used to update the button rectangles
+    // This is used to update the key rectangles
     qreal y = style->paddingTop();
 
-    // Button margins
+    // key margins
     const qreal leftMargin = HorizontalSpacing / 2;
     const qreal rightMargin = HorizontalSpacing - leftMargin;
     const qreal topMargin = VerticalSpacing / 2;
     const qreal bottomMargin = VerticalSpacing - topMargin;
 
-    QRectF br; // button bounding rectangle
+    QRectF br; // key bounding rectangle
     const qreal rowListFactor = (rowList.count() > 1 ? 1 : 0);
 
     for (RowIterator row(rowList.begin()); row != rowList.end(); ++row) {
         const qreal rowHeight = preferredRowHeight(row - rowList.begin());
         br.setHeight(rowHeight + style->spacingVertical() * rowListFactor);
 
-        row->buttonOffsets.clear();
+        row->keyOffsets.clear();
 
         // Store the row offsets for fast key lookup (the first row's height
         // can be adjusted through buttonBoundingRectTopAdjustment,
@@ -567,23 +707,23 @@
 
         // Update row width
         qreal rowWidth = 0;
-        foreach (MImKey *button, row->buttons) {
-            button->width = button->preferredWidth(mRelativeButtonBaseWidth, HorizontalSpacing);
-            rowWidth += button->width + HorizontalSpacing;
+        foreach (MImKey *key, row->keys) {
+            key->width = key->preferredWidth(mRelativeKeyBaseWidth, HorizontalSpacing);
+            rowWidth += key->width + HorizontalSpacing;
         }
         rowWidth -= HorizontalSpacing;
 
         qreal availableWidthForSpacers = 0;
         const QList<int> spacerIndices = sectionModel()->spacerIndices(row - rowList.begin());
-        int spacerCount = row->stretchButton ? spacerIndices.count() + 1
-                                             : spacerIndices.count();
+        int spacerCount = row->stretchKey ? spacerIndices.count() + 1
+                                          : spacerIndices.count();
 
-        if (row->stretchButton) {
-            rowWidth -=  row->stretchButton->width;
+        if (row->stretchKey) {
+            rowWidth -=  row->stretchKey->width;
 
-            // Handle the case of one stretch button/no other spacer elments directly:
+            // Handle the case of one stretch key/no other spacer elments directly:
             if (spacerCount == 1) {
-                row->stretchButton->width = availableWidth - rowWidth;
+                row->stretchKey->width = availableWidth - rowWidth;
                 rowWidth = availableWidth;
                 spacerCount = 0;
             }
@@ -592,40 +732,40 @@
         if ((spacerCount > 0) && (availableWidth > rowWidth)) {
             availableWidthForSpacers = (availableWidth - rowWidth) / spacerCount;
 
-            if (row->stretchButton) {
-                row->stretchButton->width = availableWidthForSpacers;
+            if (row->stretchKey) {
+                row->stretchKey->width = availableWidthForSpacers;
             }
         }
 
-        // We can precalculate button rectangles.
+        // We can precalculate key rectangles.
         br.moveTop(y - (rowList.count() > 1 ? topMargin : 0));
 
-        // A spacer with an index of -1 means it was put before any button in that row.
+        // A spacer with an index of -1 means it was put before any key in that row.
         // Also add layout padding:
         qreal x = style->paddingLeft() + spacerIndices.count(-1) * availableWidthForSpacers;
 
-        for (int buttonIndex = 0; buttonIndex < row->buttons.count(); ++buttonIndex) {
-            MImKey *button = row->buttons.at(buttonIndex);
+        for (int keyIndex = 0; keyIndex < row->keys.count(); ++keyIndex) {
+            MImKey *const key = row->keys.at(keyIndex);
 
             br.moveLeft(x - leftMargin);
-            br.setWidth(button->width + leftMargin + rightMargin);
+            br.setWidth(key->width + leftMargin + rightMargin);
 
             // save it (but cover up for rounding errors, ie, extra spacing pixels):
-            button->cachedBoundingRect = br.adjusted(-1, style->buttonBoundingRectTopAdjustment(),
-                                                      1, style->buttonBoundingRectBottomAdjustment());
+            key->cachedBoundingRect = br.adjusted(-1, style->buttonBoundingRectTopAdjustment(),
+                                                   1, style->buttonBoundingRectBottomAdjustment());
 
-            button->cachedButtonRect = br.adjusted(leftMargin, topMargin, -rightMargin, -bottomMargin);
+            key->cachedButtonRect = br.adjusted(leftMargin, topMargin, -rightMargin, -bottomMargin);
 
-            // Store the button offsets for fast key lookup:
-            row->buttonOffsets.append(QPair<qreal, qreal>(button->cachedBoundingRect.left(),
-                                                          button->cachedBoundingRect.right()));
+            // Store the key offsets for fast key lookup:
+            row->keyOffsets.append(QPair<qreal, qreal>(key->cachedBoundingRect.left(),
+                                                       key->cachedBoundingRect.right()));
 
-            // Increase x to the next button bounding rect border.
-            x += button->width + HorizontalSpacing;
+            // Increase x to the next key bounding rect border.
+            x += key->width + HorizontalSpacing;
 
             // Our spacerIndex is a multi-set, hence we need to add
             // availableWidthForSpacers for every ocurrence of spacerIndex:
-            x += spacerIndices.count(buttonIndex) * availableWidthForSpacers;
+            x += spacerIndices.count(keyIndex) * availableWidthForSpacers;
         }
 
         y += br.height();
@@ -637,7 +777,7 @@
 
 QRectF MImKeyArea::boundingRect() const
 {
-    return QRectF(0, 0, size().width(), widgetHeight);
+    return QRectF(0, 0, size().width(), cachedWidgetHeight);
 }
 
 qreal MImKeyArea::preferredRowHeight(int row) const
@@ -670,24 +810,28 @@
     return 0.0;
 }
 
-qreal MImKeyArea::maxNormalizedWidth() const
+qreal MImKeyArea::computeMaxNormalizedWidth() const
 {
     qreal maxRowWidth = 0.0;
 
     for (int j = 0; j < sectionModel()->rowCount(); ++j) {
         qreal rowWidth = 0.0;
+
         for (int i = 0; i < sectionModel()->columnsAt(j); ++i) {
             const MImKeyModel *key = sectionModel()->keyModel(j, i);
             rowWidth += normalizedKeyWidth(key);
         }
+
         maxRowWidth = qMax(maxRowWidth, rowWidth);
     }
+
     return maxRowWidth;
 }
 
-qreal MImKeyArea::normalizedKeyWidth(const MImKeyModel *key) const
+qreal MImKeyArea::normalizedKeyWidth(const MImKeyModel *model) const
 {
-    switch(key->width()) {
+    switch(model->width()) {
+
     case MImKeyModel::Small:
         return baseStyle()->keyWidthSmall();
 
@@ -712,18 +856,31 @@
 
 void MImKeyArea::onThemeChangeCompleted()
 {
-    mMaxNormalizedWidth = maxNormalizedWidth();
+    mMaxNormalizedWidth = computeMaxNormalizedWidth();
     MImAbstractKeyArea::onThemeChangeCompleted();
     buildTextLayout();
 }
 
-QList<const MImAbstractKey *> MImKeyArea::keys()
+void MImKeyArea::handleVisibilityChanged(bool)
+{
+    invalidateBackgroundCache();
+}
+
+void MImKeyArea::invalidateBackgroundCache()
+{
+    cachedBackgroundDirty = true;
+    hasCachedBackground = false;
+}
+
+QList<const MImAbstractKey *> MImKeyArea::keys() const
 {
     QList<const MImAbstractKey *> keyList;
-    foreach (const ButtonRow &row, rowList) {
-        foreach (const MImKey *button, row.buttons) {
-            keyList << button;
+
+    foreach (const KeyRow &row, rowList) {
+        foreach (const MImKey *key, row.keys) {
+            keyList.append(key);
         }
     }
+
     return keyList;
 }
--- m-keyboard/widgets/mimkeyarea.h
+++ m-keyboard/widgets/mimkeyarea.h
@@ -14,8 +14,6 @@
  * of this file.
  */
 
-
-
 #ifndef MIMKEYAREA_H
 #define MIMKEYAREA_H
 
@@ -24,95 +22,111 @@
 
 #include <MScalableImage>
 #include <QTextLayout>
+#include <QPixmap>
+#include <QSize>
+#include <memory>
 
-/*!
- * \brief MImKeyArea is an implementation of MImAbstractKeyArea which
- * does not use separate widgets for buttons, but instead draws them explicitly.
- */
+//! \brief MImKeyArea reimplements MImAbstractKeyArea and is optimized for drawing.
 class MImKeyArea
     : public MImAbstractKeyArea
 {
+    Q_DISABLE_COPY(MImKeyArea)
+
 public:
+    //! \brief Contructor, see \a MImAbstractKeyArea
     explicit MImKeyArea(const LayoutData::SharedLayoutSection &section,
                         bool usePopup = false,
                         QGraphicsWidget *parent = 0);
 
+    //! \brief Destructor
     virtual ~MImKeyArea();
 
     //! \reimp
-    virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *view);
+    virtual void modifiersChanged(bool shift,
+                                  const QChar &accent = QChar());
+    virtual void paint(QPainter *painter,
+                       const QStyleOptionGraphicsItem *option,
+                       QWidget *view);
     virtual QRectF boundingRect() const;
     virtual void setShiftState(ModifierState newShiftState);
-    virtual QList<const MImAbstractKey *> keys();
+    virtual QList<const MImAbstractKey *> keys() const;
     //! \reimp_end
 
 protected:
-    /*! \reimp */
-    virtual QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint) const;
-    virtual void drawReactiveAreas(MReactionMap *reactionMap, QGraphicsView *view);
-    virtual void updateButtonGeometriesForWidth(int availableWidth);
+    //! \reimp
+    virtual QSizeF sizeHint(Qt::SizeHint which,
+                            const QSizeF &constraint) const;
+    virtual void drawReactiveAreas(MReactionMap *reactionMap,
+                                   QGraphicsView *view);
+    virtual void updateKeyGeometries(int availableWidth);
     virtual MImAbstractKey *keyAt(const QPoint &pos) const;
-    virtual void modifiersChanged(bool shift, QChar accent = QChar());
     virtual void onThemeChangeCompleted();
-    /*! \reimp_end */
+    virtual void handleVisibilityChanged(bool visible);
+    virtual void invalidateBackgroundCache();
+    //! \reimp_end
 
 private:
-    //! \brief Creates buttons for key data models
+    //! \brief Creates buttons for key data models.
     void loadKeys();
 
     //! \brief Builds QTextLayout representation of current button labels for faster drawing.
     void buildTextLayout();
 
+    //! \brief Returns the new height of the key area.
     qreal computeWidgetHeight() const;
 
-    //! \brief Return preferred height for a row
+    //! \brief Return preferred height for a row.
+    //! \param row the index of the queried row
     qreal preferredRowHeight(int row) const;
 
-    //! \brief Get the maximum width in this widget, in normalized units
-    qreal maxNormalizedWidth() const;
-
-    //! \brief Normalized width for a particular MImKeyModel.
-    qreal normalizedKeyWidth(const MImKeyModel *key) const;
+    //! \brief Compute the maximum width in this widget, in normalized units.
+    qreal computeMaxNormalizedWidth() const;
 
-
-    //! \brief Draws background.
-    void drawKeyBackground(QPainter *painter,
-                           const MImKey *button);
-
-    //! \brief Draws button rects/bounding rects, for debugging purposes
+    //! \brief Returns normalized width for a particular MImKeyModel.
+    //! \param model the key model to be queried
+    qreal normalizedKeyWidth(const MImKeyModel *model) const;
+
+    //! \brief Draws button rects/bounding rects, for debugging purposes.
+    //! \param painter the painter to be used
+    //! \param key key for which rects/bounding rects shall be drawn
+    //! \param drawBoundingRects whether to draw bounding rects
+    //! \param drawRects whether to draw rects
     void drawDebugRects(QPainter *painter,
-                        const MImKey *button,
+                        const MImAbstractKey *key,
                         bool drawBoundingRects,
-                        bool drawRects);
+                        bool drawRects) const;
 
-    //! \brief Draws reactive areas of buttons, for debugging purposes
+    //! \brief Draws reactive areas of buttons, for debugging purposes.
+    //! \param painter the painter to be used
     void drawDebugReactiveAreas(QPainter *painter);
 
-    struct ButtonRow {
-        QList<MImKey*> buttons;
-        QVector<QPair<qreal, qreal> > buttonOffsets;
+    //! \brief Initializes the pixmap used for background caching
+    //! \param size the new size
+    void initCachedBackground(const QSize &size);
+
+    //! \brief Helper struct to store a row of keys.
+    struct KeyRow {
+        QList<MImKey*> keys; //!< keys in a row
+        QVector<QPair<qreal, qreal> > keyOffsets; //!< cached offsets for faster key lookups
+        MImKey *stretchKey; //!< each row can have one stretched key
 
-        //! each row can have one stretch button
-        MImKey *stretchButton;
     };
 
-    typedef QVector<ButtonRow> ButtonRowList;
-    typedef ButtonRowList::iterator RowIterator;
-    typedef ButtonRowList::const_iterator ConstRowIterator;
-
-    ButtonRowList rowList;
-    qreal widgetHeight;
-    qreal mMaxNormalizedWidth;
-
-    QVector<QPair<int, int> > rowOffsets;
-
-    //! Shift button is stored here if current layout has a shift button.
-    MImKey *shiftButton;
-
-    QTextLayout textLayout;
-    bool textDirty;
-
-    bool equalWidthButtons;
+    typedef QVector<KeyRow> KeyRowList;
+    typedef KeyRowList::iterator RowIterator;
+    typedef KeyRowList::const_iterator ConstRowIterator;
+
+    KeyRowList rowList; //!< stores all rows of this key area
+    qreal cachedWidgetHeight; //!< cached widget height
+    qreal mMaxNormalizedWidth; //!< maximal normalized width, for all rows
+    QVector<QPair<int, int> > rowOffsets; //!< cached offsets for faster key lookups
+    MImKey *shiftKey; //!< stores shift key, if available in this key area
+    QTextLayout textLayout; //!< used to draw key labels onto key area
+    bool textDirty; //!< dirty text cache flag
+    std::auto_ptr<QPixmap> cachedBackground; //!< cached background, containing all keys in inactive state
+    bool cachedBackgroundDirty; //!< dirty background cache flag
+    bool hasCachedBackground; //!< stores whether we already cached the background
+    bool equalWidthKeys; //!< whether to assume equal width for all keys
 
 #ifdef UNIT_TEST
     friend class Ut_MImAbstractKeyArea;
--- m-keyboard/widgets/mimwordlist.cpp
+++ m-keyboard/widgets/mimwordlist.cpp
+/* * This file is part of meego-keyboard *
+ *
+ * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ * All rights reserved.
+ * Contact: Nokia Corporation (directui at nokia.com)
+ *
+ * If you have questions regarding the use of this file, please contact
+ * Nokia at directui at nokia.com.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it 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.
+ */
+
+
+
+#include "mimwordlist.h"
+#include "mimwordlistitem.h"
+#include <mplainwindow.h>
+
+#include <QGraphicsLinearLayout>
+#include <QDebug>
+#include <QString>
+
+#include <MSceneManager>
+#include <MScene>
+#include <MGConfItem>
+#include <MContentItem>
+#include <MTheme>
+#include <mreactionmap.h>
+
+#include <mwidgetcreator.h>
+M_REGISTER_WIDGET_NO_CREATE(MImWordList)
+
+namespace
+{
+    const char * const WordListObjectName = "CorrectionWordList";
+};
+
+MIMWordListWindow::MIMWordListWindow(MImWordList *widget)
+    : MImOverlay(),
+      listWidget(widget)
+{
+    setVisible(false);
+}
+
+bool MIMWordListWindow::sceneEvent(QEvent *e)
+{
+    // TODO: below hiding list widget could be removed when meegotouch
+    // decide not hiding dialog when tap outside.
+    if (e->type() == QEvent::GraphicsSceneMouseRelease) {
+        listWidget->disappear();
+    }
+    return MImOverlay::sceneEvent(e);
+}
+
+void MIMWordListWindow::handleListAppeared()
+{
+    setVisible(true);
+    listWidget->setParentItem(this);
+}
+
+void MIMWordListWindow::handleListDisappeared()
+{
+    setVisible(false);
+}
+
+MImWordList::MImWordList()
+    : MDialog(),
+      parentWindow(new MIMWordListWindow(this))
+{
+    // for MATTI
+    setObjectName(WordListObjectName);
+
+    MWidget *contentWidget = new MWidget(this);
+    mainLayout = new QGraphicsLinearLayout(Qt::Vertical);
+    mainLayout->setSpacing(0);
+    mainLayout->setContentsMargins(0, 0, 0, 0);
+    contentWidget->setLayout(mainLayout);
+
+    for (int i = 0; i < MaxCandidateCount; i++) {
+        candidateItems[i] = new MImWordListItem(contentWidget);
+        candidateItems[i]->setVisible(false);
+        connect(candidateItems[i], SIGNAL(clicked()), this, SLOT(select()));
+        mainLayout->addItem(candidateItems[i]);
+    }
+    setCentralWidget(contentWidget);
+    hide();
+
+    connect(this, SIGNAL(visibleChanged()),
+            this, SLOT(handleVisibilityChanged()));
+    connect(this, SIGNAL(appeared()),
+            parentWindow, SLOT(handleListAppeared()));
+    connect(this, SIGNAL(disappeared()),
+            parentWindow, SLOT(handleListDisappeared()));
+}
+
+MImWordList::~MImWordList()
+{
+    setParentItem(0);
+    delete parentWindow;
+    parentWindow = 0;
+}
+
+void MImWordList::setCandidates(const QStringList &candidates)
+{
+    mCandidates = candidates.mid(0, MaxCandidateCount);
+
+    for (int i = 0; i < MaxCandidateCount; i++) {
+        if (i < candidates.count()) {
+            candidateItems[i]->setSelected(false);
+            mainLayout->addItem(candidateItems[i]);
+            candidateItems[i]->setTitle(candidates.at(i));
+            candidateItems[i]->setVisible(true);
+            candidateItems[i]->setEnabled(true);
+            candidateItems[i]->setLayoutPosition(M::VerticalCenterPosition);
+        } else {
+            mainLayout->removeItem(candidateItems[i]);
+            candidateItems[i]->setVisible(false);
+        }
+    }
+    mainLayout->invalidate();
+}
+
+QStringList MImWordList::candidates() const
+{
+    return mCandidates;
+}
+
+void MImWordList::setHighlightCandidate(const QString &candidate)
+{
+    int index = mCandidates.indexOf(candidate);
+    if (index >= 0) {
+        candidateItems[index]->setSelected(true);
+    }
+}
+
+void MImWordList::select()
+{
+    // ignore the select actions during animation
+    if (!isVisible() || sceneWindowState() == MSceneWindow::Appearing
+        || sceneWindowState() == MSceneWindow::Disappearing) {
+        return;
+    }
+    MImWordListItem *item = qobject_cast<MImWordListItem *> (sender());
+    if (item) {
+        const QString candidate = item->title();
+        emit candidateClicked(candidate);
+    }
+}
+
+void MImWordList::handleVisibilityChanged()
+{
+    emit regionChanged();
+}
+
+QRegion MImWordList::region() const
+{
+    QRegion ret;
+    if (isVisible()) {
+        const QSize visibleSceneSize = MPlainWindow::instance()->visibleSceneSize(M::Landscape);
+        ret = QRegion(0, 0, visibleSceneSize.width(), visibleSceneSize.height());
+    }
+    return ret;
+}
+
+void MImWordList::paintReactionMap(MReactionMap *reactionMap, QGraphicsView *)
+{
+    if (!isVisible())
+        return;
+    // word list take whole screen. And inner contentitem will play their
+    // default feedback. Don't need reaction map.
+    reactionMap->setInactiveDrawingValue();
+    reactionMap->setTransform(QTransform());
+    reactionMap->fillRectangle(0, 0, reactionMap->width(), reactionMap->height());
+}
--- m-keyboard/widgets/mimwordlist.h
+++ m-keyboard/widgets/mimwordlist.h
+/* * This file is part of meego-keyboard *
+ *
+ * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ * All rights reserved.
+ * Contact: Nokia Corporation (directui at nokia.com)
+ *
+ * If you have questions regarding the use of this file, please contact
+ * Nokia at directui at nokia.com.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it 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.
+ */
+
+
+#ifndef MIMWORDLIST_H
+#define MIMWORDLIST_H
+
+#include <MDialog>
+#include "mimoverlay.h" 
+
+class QGraphicsLinearLayout;
+class MContentItem;
+class MImWordListItem;
+class MImWordList;
+class MReactionMap;
+
+/*!
+ * \brief MIMWordListWindow is used as the plain translucent parent window for word list dialog.
+ *
+  * MIMWordListWindow prevents mouse and touch events from reaching the virtual keyboard or the application.
+  * \sa MImOverlay.
+ */
+class MIMWordListWindow : public MImOverlay
+{
+    Q_OBJECT
+public:
+    //! Constructor
+    explicit MIMWordListWindow(MImWordList *widget);
+
+public slots:
+    /*
+     * \brief This slot is connected with word list widget's appeared() signal.
+     */
+    void handleListAppeared();
+
+    /*
+     * \brief This slot is connected with word list widget's disappeared() signal.
+     */
+    void handleListDisappeared();
+
+protected:
+    /*! \reimp */
+    virtual bool sceneEvent(QEvent *event);
+    /*! \reimp_end */
+
+private:
+    MImWordList *listWidget;
+};
+
+/*!
+ * \brief MIMWordList is used for word list dialog.
+ *
+ * MImWordList shows a dialog which list the suggested candidates.
+ */
+class MImWordList : public MDialog
+{
+    Q_OBJECT
+    friend class Ut_MImWordList;
+    friend class Ut_MImCorrectionCandidateWidget;
+
+public:
+    enum {
+        MaxCandidateCount = 5
+    };
+
+    //! Constructor
+    explicit MImWordList();
+
+    //! Destructor
+    virtual ~MImWordList();
+
+    /*!
+     * \brief Sets suggestion candidates.
+     */
+    void setCandidates(const QStringList &candidates);
+
+    /*!
+     * \brief Returns the suggestion candidates.
+     */
+    QStringList candidates() const;
+
+    /*!
+     * \brief Sets the highlight suggestion candidate.
+     */
+    void setHighlightCandidate(const QString &);
+
+    /*!
+     * \brief Returns the occupied region of word list widget.
+     */
+    QRegion region() const;
+
+    /*!
+     * \brief Draw its reactive areas onto the reaction map
+     */
+    void paintReactionMap(MReactionMap *reactionMap, QGraphicsView *view);
+
+signals:
+    /*!
+     * \brief This signal is emitted when clicking on a candidate.
+     */
+    void candidateClicked(const QString &);
+
+    //! Emitted when the occupied region of word list is changed.
+    void regionChanged();
+
+private slots:
+
+    void select();
+
+    void handleVisibilityChanged();
+
+private:
+    QStringList mCandidates;
+    QGraphicsLinearLayout *mainLayout;
+    MImWordListItem *candidateItems[MaxCandidateCount];
+    MIMWordListWindow *parentWindow;
+};
+
+#endif
--- m-keyboard/widgets/mimwordlistitem.cpp
+++ m-keyboard/widgets/mimwordlistitem.cpp
+/* * This file is part of meego-keyboard *
+ *
+ * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ * All rights reserved.
+ * Contact: Nokia Corporation (directui at nokia.com)
+ *
+ * If you have questions regarding the use of this file, please contact
+ * Nokia at directui at nokia.com.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it 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.
+ */
+
+
+
+#include "mimwordlistitem.h"
+
+#include <mwidgetcreator.h>
+M_REGISTER_WIDGET_NO_CREATE(MImWordListItem)
+
+MImWordListItem::MImWordListItem(QGraphicsItem *parent)
+    :  MContentItem(MContentItem::SingleTextLabel, parent)
+{
+}
--- m-keyboard/widgets/mimwordlistitem.h
+++ m-keyboard/widgets/mimwordlistitem.h
+/* * This file is part of meego-keyboard *
+ *
+ * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ * All rights reserved.
+ * Contact: Nokia Corporation (directui at nokia.com)
+ *
+ * If you have questions regarding the use of this file, please contact
+ * Nokia at directui at nokia.com.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it 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.
+ */
+
+
+
+#ifndef MIMWORDLISTITEM_H
+#define MIMWORDLISTITEM_H
+
+#include <MContentItem>
+
+class MImWordListItem : public MContentItem
+{
+    Q_OBJECT
+public:
+    explicit MImWordListItem(QGraphicsItem *parent = 0);
+};
+
+#endif
--- m-keyboard/widgets/mimwordtracker.cpp
+++ m-keyboard/widgets/mimwordtracker.cpp
+/* * This file is part of meego-keyboard *
+ *
+ * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ * All rights reserved.
+ * Contact: Nokia Corporation (directui at nokia.com)
+ *
+ * If you have questions regarding the use of this file, please contact
+ * Nokia at directui at nokia.com.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it 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.
+ */
+
+
+
+#include "mimwordtracker.h"
+#include "mimcorrectioncandidateitem.h"
+
+#include <QGraphicsLinearLayout>
+#include <QGraphicsSceneMouseEvent>
+#include <QDebug>
+
+#include <mplainwindow.h>
+#include <mreactionmap.h>
+#include <MScalableImage>
+#include <MSceneManager>
+#include <MScene>
+#include <MSceneWindow>
+#include <MGConfItem>
+
+#include <mwidgetcreator.h>
+M_REGISTER_WIDGET_NO_CREATE(MImWordTracker)
+
+namespace
+{
+    // This GConf item defines whether multitouch is enabled or disabled
+    const char * const MultitouchSetting = "/meegotouch/inputmethods/multitouch/enabled";
+
+    const char * const WordTrackerObjectName = "CorrectionWordTracker";
+    const int DefaultShowHideFrames = 100;
+    const int DefaultShowHideTime = 400;
+    const int DefaultShowHideInterval = 30;
+
+    QRect mapToScreenRect(const QRect &widgetRect)
+    {
+        if (!widgetRect.isValid()) {
+            return QRect();
+        }
+
+        M::OrientationAngle angle  = MPlainWindow::instance()->orientationAngle();
+        int displayHeight = MPlainWindow::instance()->visibleSceneSize(M::Landscape).height();
+        int displayWidth  = MPlainWindow::instance()->visibleSceneSize(M::Landscape).width();
+
+        QRect rect;
+        switch (angle) {
+            case M::Angle90:
+                rect.setRect(displayWidth - widgetRect.y()- widgetRect.height(),
+                             widgetRect.x(),
+                             widgetRect.height(), widgetRect.width());
+                break;
+            case M::Angle270:
+                rect.setRect(widgetRect.y(),
+                             displayHeight - widgetRect.x() - widgetRect.width(),
+                             widgetRect.height(), widgetRect.width());
+                break;
+            case M::Angle180:
+                rect.setRect(displayWidth - widgetRect.x() - widgetRect.width(),
+                             displayHeight - widgetRect.y() - widgetRect.height(),
+                             widgetRect.width(), widgetRect.height());
+                break;
+            case M::Angle0:
+                rect = widgetRect;
+                break;
+            default:
+                qCritical() << __FILE__ << __LINE__ << " Incorrect orientation " << angle;
+                rect = QRect();
+                break;
+        }
+        return rect;
+    }
+};
+
+
+MImWordTracker::MImWordTracker(MSceneWindow *parentWindow)
+    : MStylableWidget(),
+      containerWidget(new QGraphicsWidget()),
+      mIdealWidth(0),
+      candidateItem(new MImCorrectionCandidateItem("", this))
+{
+    containerWidget->setParentItem(parentWindow);
+    this->setParentItem(containerWidget);
+
+    // By default multi-touch is disabled
+    setAcceptTouchEvents(MGConfItem(MultitouchSetting).value().toBool());
+
+    setObjectName(WordTrackerObjectName);
+
+    mainLayout = new QGraphicsLinearLayout(Qt::Vertical);
+    mainLayout->setSpacing(0);
+    mainLayout->setContentsMargins(0, 0, 0, 0);
+    setLayout(mainLayout);
+    mainLayout->addItem(candidateItem);
+    mainLayout->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+    connect(candidateItem, SIGNAL(clicked()), this, SLOT(select()));
+    connect(candidateItem, SIGNAL(longTapped()), this, SLOT(longTap()));
+
+    connect(MTheme::instance(), SIGNAL(themeChangeCompleted()),
+            this, SLOT(onThemeChangeCompleted()),
+            Qt::UniqueConnection);
+
+    setupTimeLine();
+    containerWidget->hide();
+}
+
+MImWordTracker::~MImWordTracker()
+{
+    this->setParentItem(0);
+    delete containerWidget;
+}
+
+void MImWordTracker::setCandidate(const QString &string)
+{
+    mCandidate = string;
+    if (isVisible()
+        && showHideTimeline.state() == QTimeLine::Running
+        && showHideTimeline.direction() == QTimeLine::Backward) {
+        // don't update during hiding animation
+        return;
+    }
+    candidateItem->setTitle(string);
+
+    mIdealWidth = candidateItem->idealWidth();;
+    // not less than minimum width
+    if (mIdealWidth < minimumSize().width())
+        mIdealWidth = minimumSize().width();
+    
+    mIdealWidth += style()->paddingLeft() + style()->paddingRight()
+                   + style()->marginLeft() + style()->marginRight();
+    setPreferredWidth(mIdealWidth);
+}
+
+QString MImWordTracker::candidate() const
+{
+    return mCandidate;
+}
+
+qreal MImWordTracker::idealWidth() const
+{
+    return mIdealWidth;
+}
+
+qreal MImWordTracker::pointerHeight() const
+{
+    return (style()->wordtrackerPointerSize().height()
+            - style()->wordtrackerPointerOverlap());
+}
+
+void MImWordTracker::drawBackground(QPainter *painter, const QStyleOptionGraphicsItem *option) const
+{
+    MStylableWidget::drawBackground(painter, option);
+    if (style()->wordtrackerPointerImage()) {
+         const QSize pointerSize = style()->wordtrackerPointerSize();
+         QRect rect = QRect((idealWidth() - pointerSize.width())/2,
+                            style()->wordtrackerPointerOverlap() - pointerSize.height(),
+                            pointerSize.width(),
+                            pointerSize.height());
+         style()->wordtrackerPointerImage()->draw(rect, painter);
+    }
+}
+
+void MImWordTracker::select()
+{
+    if (showHideTimeline.state() == QTimeLine::Running) {
+        // Ignore select actions during animation.
+        return;
+    }
+    if (!mCandidate.isEmpty()) {
+        emit candidateClicked(mCandidate);
+    }
+}
+
+void MImWordTracker::longTap()
+{
+    if (showHideTimeline.state() == QTimeLine::Running) {
+        // Ignore select actions during animation.
+        return;
+    }
+    if (!mCandidate.isEmpty()) {
+        emit longTapped();
+    }
+}
+
+void MImWordTracker::setupTimeLine()
+{
+    int showHideFrames = style()->showHideFrames();
+    showHideFrames = (showHideFrames > 0) ? showHideFrames : DefaultShowHideFrames;
+    int showHideTime = style()->showHideTime();
+    showHideTime = (showHideTime > 0) ? showHideTime : DefaultShowHideTime;
+    int showHideInterval = style()->showHideInterval();
+    showHideInterval = (showHideInterval > 0) ? showHideInterval : DefaultShowHideInterval;
+
+    showHideTimeline.setCurveShape(QTimeLine::EaseInCurve);
+    showHideTimeline.setFrameRange(0, showHideFrames);
+    showHideTimeline.setDuration(showHideTime);
+    showHideTimeline.setUpdateInterval(showHideInterval);
+    connect(&showHideTimeline, SIGNAL(frameChanged(int)), this, SLOT(fade(int)), Qt::UniqueConnection);
+    connect(&showHideTimeline, SIGNAL(finished()), this, SLOT(showHideFinished()), Qt::UniqueConnection);
+}
+
+void MImWordTracker::fade(int frame)
+{
+    int showHideFrames = showHideTimeline.endFrame();
+    showHideFrames = (showHideFrames > 0) ? showHideFrames : DefaultShowHideFrames;
+    const qreal opacity = qreal(frame) / showHideFrames;
+    parentWidget()->setOpacity(opacity);
+    parentWidget()->update();
+}
+
+
+void MImWordTracker::showHideFinished()
+{
+    const bool hiding = (showHideTimeline.direction() == QTimeLine::Backward);
+
+    if (hiding) {
+        containerWidget->hide();
+        emit regionChanged();
+    }
+}
+
+void MImWordTracker::appear(bool withAnimation)
+{
+    if (!isVisible()) {
+        if (withAnimation) {
+            showHideTimeline.setDirection(QTimeLine::Forward);
+            if (showHideTimeline.state() != QTimeLine::Running) {
+                showHideTimeline.start();
+            }
+        }
+        containerWidget->show();
+        show();
+    } else {
+        containerWidget->update();
+    }
+    emit regionChanged();
+}
+
+void MImWordTracker::disappear(bool withAnimation)
+{
+    if (!isVisible())
+        return;
+
+    if (withAnimation) {
+        showHideTimeline.setDirection(QTimeLine::Backward);
+        if (showHideTimeline.state() != QTimeLine::Running) {
+            showHideTimeline.start();
+        }
+        // will hide and emit regionChanged when timeline is finished
+    } else {
+        containerWidget->hide();
+        emit regionChanged();
+    }
+}
+
+void MImWordTracker::setPosition(const QPoint &pos)
+{
+    QRectF widgetRect, containerRect;
+    QSizeF containerSize = preferredSize();
+    containerSize.setHeight(containerSize.height() + pointerHeight());
+    containerRect = QRectF(pos, containerSize);
+    widgetRect = QRectF(QPointF(0, pointerHeight()), preferredSize());
+
+    containerWidget->setGeometry(containerRect);
+    setGeometry(widgetRect);
+
+    if (isVisible()) {
+        containerWidget->update();
+        emit regionChanged();
+    }
+}
+
+QRegion MImWordTracker::region() const
+{
+    QRegion ret;
+    if (isVisible()) {
+        ret = mapToScreenRect(containerWidget->geometry().toRect());
+    }
+    return ret;
+}
+
+void MImWordTracker::onThemeChangeCompleted()
+{
+    // reset time line
+    setupTimeLine();
+}
+
+void MImWordTracker::paintReactionMap(MReactionMap *reactionMap, QGraphicsView *view)
+{
+    if (!isVisible())
+        return;
+
+    // Clear all with inactive color.
+    reactionMap->setTransform(this, view);
+    reactionMap->setInactiveDrawingValue();
+    reactionMap->fillRectangle(geometry());
+
+    // Draw the actual word tracker area.
+    reactionMap->setReactiveDrawingValue();
+    reactionMap->fillRectangle(geometry());
+}
--- m-keyboard/widgets/mimwordtracker.h
+++ m-keyboard/widgets/mimwordtracker.h
+/* * This file is part of meego-keyboard *
+ *
+ * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ * All rights reserved.
+ * Contact: Nokia Corporation (directui at nokia.com)
+ *
+ * If you have questions regarding the use of this file, please contact
+ * Nokia at directui at nokia.com.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it 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.
+ */
+
+
+
+#ifndef MIMWORDTRACKER_H
+#define MIMWORDTRACKER_H
+
+#include <MStylableWidget>
+#include "mimwordtrackerstyle.h"
+
+#include <QTimeLine>
+
+class QGraphicsWidget;
+class QGraphicsLinearLayout;
+class MImCorrectionCandidateItem;
+class MSceneWindow;
+class MReactionMap;
+
+/*!
+ * \brief The MImWordTracker class is used to show error correction word tracker.
+ */
+class MImWordTracker : public MStylableWidget
+{
+    Q_OBJECT
+    friend class Ut_MImWordTracker;
+    friend class Ut_MImCorrectionCandidateWidget;
+
+public:
+    //! Constructor
+    explicit MImWordTracker(MSceneWindow *parentWindow);
+
+    //! Destructor
+    ~MImWordTracker();
+
+    /*!
+     * \brief Set suggested candidate.
+     */
+    void setCandidate(const QString &);
+
+    /*!
+     * \brief Returns the suggested candidate.
+     */
+    QString candidate() const;
+
+    /*!
+     * \brief Returns the ideal width of the word tracker.
+     *
+     * The ideal width is the actually used width of the word tracker together with margins and paddings.
+     */
+    qreal idealWidth() const;
+
+    /*!
+     * \brief Returns the height of pointer for word tracker.
+     */
+    qreal pointerHeight() const;
+
+    /*!
+     * \brief Appears word tracker widget with or without default animation.
+     */
+    void appear(bool withAnimation = false);
+    
+    /*!
+     * \brief Disappear word tracker widget with or without default animation.
+     */
+    void disappear(bool withAnimation = false);
+
+    /*!
+     * \brief Sets the position of word tracker.
+     */
+    void setPosition(const QPoint &pos);
+
+    /*!
+     * \brief Returns the visible region of word tracker.
+     */
+    QRegion region() const;
+
+    /*!
+     * \brief Draw its reactive areas onto the reaction map
+     */
+    void paintReactionMap(MReactionMap *reactionMap, QGraphicsView *view);
+
+signals:
+    //! Emitted when word tracker is clicked.
+    void candidateClicked(const QString &);
+
+    /*!
+     * \brief The signal is emitted when the word tracker has been tapped and holded.
+     */
+    void longTapped();
+
+    //! Emitted when the occupied region of word tracker is changed.
+    void regionChanged();
+
+protected slots:
+    void select();
+
+    void longTap();
+
+    /*!
+     * Method to fade the vkb during transition
+     */
+    void fade(int);
+
+    /*!
+     * This function gets called when fading is finished
+     */
+    void showHideFinished();
+
+    /*!
+     * Update stored style stuffs when the theme changed.
+     */
+    void onThemeChangeCompleted();
+
+protected:
+    /*! \reimp */
+    virtual void drawBackground(QPainter *painter, const QStyleOptionGraphicsItem *option) const;
+    /*! \reimp_end */
+
+private:
+    void setupTimeLine();
+
+    QGraphicsWidget *containerWidget;
+    QString mCandidate;
+    int mIdealWidth;
+    QGraphicsLinearLayout *mainLayout;
+    MImCorrectionCandidateItem *candidateItem;
+    QTimeLine showHideTimeline;
+
+    M_STYLABLE_WIDGET(MImWordTrackerStyle)
+};
+
+#endif
--- m-keyboard/widgets/mimwordtrackerstyle.h
+++ m-keyboard/widgets/mimwordtrackerstyle.h
+/* * This file is part of meego-keyboard *
+ *
+ * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ * All rights reserved.
+ * Contact: Nokia Corporation (directui at nokia.com)
+ *
+ * If you have questions regarding the use of this file, please contact
+ * Nokia at directui at nokia.com.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it 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.
+ */
+
+#ifndef MIMWORDTRACKERSTYLE_H
+#define MIMWORDTRACKERSTYLE_H
+
+#include <MWidgetStyle>
+
+class MImWordTrackerStyle : public MWidgetStyle
+{
+    Q_OBJECT
+    M_STYLE(MImWordTrackerStyle)
+
+    M_STYLE_PTR_ATTRIBUTE(MScalableImage *,  wordtrackerPointerImage, WordtrackPointerImage)
+    M_STYLE_ATTRIBUTE(QSize, wordtrackerPointerSize, WordtrackerPointerSize)
+    M_STYLE_ATTRIBUTE(int, wordtrackerPointerOverlap, WordtrackerPointerOverlap)
+
+    M_STYLE_ATTRIBUTE(int, showHideFrames, ShowHideFrames)
+    M_STYLE_ATTRIBUTE(int, showHideTime, ShowHideTime)
+    M_STYLE_ATTRIBUTE(int, showHideInterval, ShowHideInterval)
+};
+
+class M_EXPORT MImWordTrackerStyleContainer : public MWidgetStyleContainer
+{
+    M_STYLE_CONTAINER(MImWordTrackerStyle)
+};
+
+#endif
+
--- m-keyboard/widgets/mkeyboardsettingswidget.cpp
+++ m-keyboard/widgets/mkeyboardsettingswidget.cpp
@@ -40,7 +40,7 @@
     //!object name for settings' widgets
     const QString ObjectNameSelectedKeyboardsItem("SelectedKeyboardsItem");
     const QString ObjectNameErrorCorrectionButton("KeyboardErrorCorrectionButton");
-
+    const QString ObjectNameWordCompletionButton("KeyboardWordCompletionButton");
     const int MKeyboardLayoutRole = Qt::UserRole + 1;
 };
 
@@ -129,6 +129,7 @@
     connect(selectedKeyboardsItem, SIGNAL(clicked()), this, SLOT(showKeyboardList()));
     addItem(selectedKeyboardsItem);
 
+    // Error correction settings
     errorCorrectionSwitch = new MButton(this);
     errorCorrectionSwitch->setObjectName(ObjectNameErrorCorrectionButton);
     errorCorrectionSwitch->setViewType(MButton::switchType);
@@ -137,11 +138,34 @@
     //% "Error correction"
     errorCorrectionLabel->setText(qtTrId("qtn_txts_error_correction"));
     errorCorrectionLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
-    QGraphicsLinearLayout *l = new QGraphicsLinearLayout(Qt::Horizontal);
-    l->addItem(errorCorrectionLabel);
-    l->addItem(errorCorrectionSwitch);
-    l->setAlignment(errorCorrectionSwitch, Qt::AlignCenter);
-    (qobject_cast<MKeyboardSettingsWidget *>(this))->addItem(l);
+    QGraphicsLinearLayout *eCLayout = new QGraphicsLinearLayout(Qt::Horizontal);
+    eCLayout->addItem(errorCorrectionLabel);
+    eCLayout->addItem(errorCorrectionSwitch);
+    eCLayout->setAlignment(errorCorrectionSwitch, Qt::AlignCenter);
+
+    // Word completion settings
+    wordCompletionSwitch = new MButton(this);
+    wordCompletionSwitch->setObjectName(ObjectNameWordCompletionButton);
+    wordCompletionSwitch->setViewType(MButton::switchType);
+    wordCompletionSwitch->setCheckable(true);
+    wordCompletionLabel = new MLabel(this);
+    //% "Word completion"
+    wordCompletionLabel->setText(qtTrId("qtn_txts_word_completion"));
+    wordCompletionLabel->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
+    QGraphicsLinearLayout *wCLayout = new QGraphicsLinearLayout(Qt::Horizontal);
+    wCLayout->addItem(wordCompletionLabel);
+    wCLayout->addItem(wordCompletionSwitch);
+    wCLayout->setAlignment(wordCompletionSwitch, Qt::AlignCenter);
+
+    // Add the error correction+word completion widgets to this layout to
+    // have proper alignment of the widgets to the right side.
+    QGraphicsLinearLayout *vertLayout = new QGraphicsLinearLayout(Qt::Vertical);
+
+    // Add the error correction widgets to a vertical layout
+    vertLayout->addItem(eCLayout);
+    // Add the word completion widgets to a vertical layout
+    vertLayout->addItem(wCLayout);
+    addItem(vertLayout);
 }
 
 void MKeyboardSettingsWidget::addItem(QGraphicsLayoutItem *item)
@@ -162,10 +186,14 @@
 
 void MKeyboardSettingsWidget::updateTitle()
 {
-    if (!errorCorrectionLabel || !settingsObject || !selectedKeyboardsItem)
+    if (!errorCorrectionLabel || !wordCompletionLabel
+        || !settingsObject || !selectedKeyboardsItem)
         return;
+
     //% "Error correction"
     errorCorrectionLabel->setText(qtTrId("qtn_txts_error_correction"));
+    //% "Word completion"
+    wordCompletionLabel->setText(qtTrId("qtn_txts_word_completion"));
     QStringList keyboards = settingsObject->selectedKeyboards().values();
     //% "Installed keyboards (%1)"
     QString title = qtTrId("qtn_txts_installed_keyboards")
@@ -194,13 +222,17 @@
     connect(this, SIGNAL(visibleChanged()),
             this, SLOT(handleVisibilityChanged()));
 
-    if (!settingsObject || !errorCorrectionSwitch)
+    if (!settingsObject || !errorCorrectionSwitch || !wordCompletionSwitch)
         return;
 
     connect(errorCorrectionSwitch, SIGNAL(toggled(bool)),
             this, SLOT(setErrorCorrectionState(bool)));
     connect(settingsObject, SIGNAL(errorCorrectionChanged()),
             this, SLOT(syncErrorCorrectionState()));
+    connect(wordCompletionSwitch, SIGNAL(toggled(bool)),
+            this, SLOT(setWordCompletionState(bool)));
+    connect(settingsObject, SIGNAL(wordCompletionChanged()),
+            this, SLOT(syncWordCompletionState()));
     connect(settingsObject, SIGNAL(selectedKeyboardsChanged()),
             this, SLOT(updateTitle()));
     connect(settingsObject, SIGNAL(selectedKeyboardsChanged()),
@@ -237,6 +269,7 @@
 {
     if (!settingsObject || !keyboardList)
         return;
+
     //always reload available layouts in case user install/remove some layouts
     settingsObject->readAvailableKeyboards();
     QStandardItemModel *model = static_cast<QStandardItemModel*> (keyboardList->itemModel());
@@ -258,6 +291,7 @@
 {
     if (!settingsObject || !keyboardList)
         return;
+
     QStandardItemModel *model = static_cast<QStandardItemModel*> (keyboardList->itemModel());
     foreach (const QString &keyboard, settingsObject->selectedKeyboards().values()) {
         QList<QStandardItem *> items = model->findItems(keyboard);
@@ -285,18 +319,20 @@
     retranslateUi();
 }
 
-void MKeyboardSettingsWidget::setErrorCorrectionState(bool toggled)
+void MKeyboardSettingsWidget::setErrorCorrectionState(bool enabled)
 {
     if (!settingsObject)
         return;
-    if (toggled != settingsObject->errorCorrection())
-        settingsObject->setErrorCorrection(toggled) ;
+
+    if (settingsObject->errorCorrection() != enabled)
+        settingsObject->setErrorCorrection(enabled) ;
 }
 
 void MKeyboardSettingsWidget::syncErrorCorrectionState()
 {
     if (!settingsObject)
         return;
+
     const bool errorCorrectionState = settingsObject->errorCorrection();
     if (errorCorrectionSwitch
         && errorCorrectionSwitch->isChecked() != errorCorrectionState) {
@@ -304,6 +340,27 @@
     }
 }
 
+void MKeyboardSettingsWidget::setWordCompletionState(bool enabled)
+{
+    if (!settingsObject)
+        return;
+
+    if (settingsObject->wordCompletion() != enabled)
+        settingsObject->setWordCompletion(enabled) ;
+}
+
+void MKeyboardSettingsWidget::syncWordCompletionState()
+{
+    if (!settingsObject)
+        return;
+
+    const bool wordCompletionState = settingsObject->wordCompletion();
+    if (wordCompletionSwitch
+        && wordCompletionSwitch->isChecked() != wordCompletionState) {
+        wordCompletionSwitch->setChecked(wordCompletionState);
+    }
+}
+
 void MKeyboardSettingsWidget::notifyNoKeyboards()
 {
     MBanner *noKeyboardsNotification = new MBanner;
--- m-keyboard/widgets/mkeyboardsettingswidget.h
+++ m-keyboard/widgets/mkeyboardsettingswidget.h
@@ -51,6 +51,8 @@
     void updateSelectedKeyboards(const QModelIndex &);
     void setErrorCorrectionState(bool toggled);
     void syncErrorCorrectionState();
+    void setWordCompletionState(bool toggled);
+    void syncWordCompletionState();
     void handleVisibilityChanged();
 
 private:
@@ -65,6 +67,8 @@
     MLinearLayoutPolicy *portraitPolicy;
     MButton *errorCorrectionSwitch;
     MLabel *errorCorrectionLabel;
+    MButton *wordCompletionSwitch;
+    MLabel *wordCompletionLabel;
     MDialog *keyboardDialog;
     MList *keyboardList;
     MContentItem *selectedKeyboardsItem;
--- m-keyboard/widgets/mtoolbarbutton.cpp
+++ m-keyboard/widgets/mtoolbarbutton.cpp
@@ -61,6 +61,7 @@
         connect(this, SIGNAL(clicked(bool)),
                 itemPtr.data(), SLOT(setPressed(bool)));
     }
+    setEnabled(itemPtr->enabled());
     setVisible(item->isVisible());
 
     connect(this, SIGNAL(clicked(bool)),
@@ -154,6 +155,8 @@
         setText(qtTrId(itemPtr->textId().toUtf8().data()));
     } else if (attribute == "pressed" && itemPtr->toggle()) {
         setChecked(itemPtr->pressed());
+    } else if (attribute == "enabled") {
+        setEnabled(itemPtr->enabled());
     } else if (attribute == "visible") {
         setVisible(itemPtr->isVisible());
         emit availabilityChanged();
--- m-keyboard/widgets/mvirtualkeyboard.cpp
+++ m-keyboard/widgets/mvirtualkeyboard.cpp
@@ -93,6 +93,8 @@
             this, SIGNAL(keyReleased(const KeyEvent &)));
     connect(&eventHandler, SIGNAL(keyClicked(const KeyEvent &)),
             this, SIGNAL(keyClicked(const KeyEvent &)));
+    connect(&eventHandler, SIGNAL(longKeyPressed(const KeyEvent &)),
+            this, SIGNAL(longKeyPressed(const KeyEvent &)));
     connect(&eventHandler, SIGNAL(shiftPressed(bool)),
             this, SLOT(handleShiftPressed(bool)));
 
@@ -406,7 +408,7 @@
 void
 MVirtualKeyboard::fade(int frame)
 {
-    const float opacity = float(frame) / ShowHideFrames;
+    const qreal opacity = static_cast<qreal>(frame) / ShowHideFrames;
     const QSize sceneSize = sceneManager->visibleSceneSize();
 
     if (!hideShowByFadingOnly) {
@@ -537,7 +539,16 @@
     activeState = newState;
     resetState();
 
+
     static_cast<QGraphicsWidget *>(mainLayout->itemAt(KeyboardIndex))->setVisible(newState == MInputMethod::OnScreen);
+    if (newState != MInputMethod::OnScreen) {
+        MImAbstractKeyArea *keyArea = dynamic_cast<MImAbstractKeyArea *>(mainKeyboardSwitcher->currentWidget());
+
+        if (keyArea) {
+            keyArea->hidePopup();
+        }
+    }
+
     showHideTimeline.stop(); // position must be updated by organizeContentAndSendRegion()
     organizeContentAndSendRegion();
     sendRegionUpdates = savedSendRegionUpdates;
@@ -785,7 +796,7 @@
     // Load certain type and orientation from all layouts.
     foreach (const QString &layoutFile, layoutsMgr.layoutFileList()) {
         MImAbstractKeyArea *mainSection = createMainSectionView(layoutFile, LayoutData::General,
-                                                           currentOrientation);
+                                                                currentOrientation);
         mainSection->setObjectName("VirtualKeyboardMainRow");
         mainSection->setPreferredWidth(MPlainWindow::instance()->visibleSceneSize().width());
         mainKeyboardSwitcher->addWidget(mainSection);
@@ -794,31 +805,32 @@
 
 
 MImAbstractKeyArea *MVirtualKeyboard::createMainSectionView(const QString &layout,
-                                                       LayoutData::LayoutType layoutType,
-                                                       M::Orientation orientation,
-                                                       QGraphicsWidget *parent)
-{
-    MImAbstractKeyArea *buttonArea = createSectionView(layout, layoutType, orientation,
-                                                  LayoutData::mainSection,
-                                                  true, parent);
+                                                            LayoutData::LayoutType layoutType,
+                                                            M::Orientation orientation,
+                                                            QGraphicsWidget *parent)
+{
+    MImAbstractKeyArea *keyArea = createSectionView(layout, layoutType, orientation,
+                                                    LayoutData::mainSection,
+                                                    true, parent);
 
     // horizontal flick handling only on main section of qwerty
-    connect(buttonArea, SIGNAL(flickLeft()), this, SLOT(flickLeftHandler()));
-    connect(buttonArea, SIGNAL(flickRight()), this, SLOT(flickRightHandler()));
+    connect(keyArea, SIGNAL(flickLeft()), this, SLOT(flickLeftHandler()));
+    connect(keyArea, SIGNAL(flickRight()), this, SLOT(flickRightHandler()));
+    connect(this, SIGNAL(hidden()), keyArea, SLOT(hidePopup()));
 
-    return buttonArea;
+    return keyArea;
 }
 
 MImAbstractKeyArea * MVirtualKeyboard::createSectionView(const QString &layout,
-                                                    LayoutData::LayoutType layoutType,
-                                                    M::Orientation orientation,
-                                                    const QString &section,
-                                                    bool usePopup,
-                                                    QGraphicsWidget *parent)
+                                                         LayoutData::LayoutType layoutType,
+                                                         M::Orientation orientation,
+                                                         const QString &section,
+                                                         bool usePopup,
+                                                         QGraphicsWidget *parent)
 {
     const LayoutData *model = layoutsMgr.layout(layout, layoutType, orientation);
     MImAbstractKeyArea *view = new MImKeyArea(model->section(section),
-                                                     usePopup, parent);
+                                              usePopup, parent);
 
     eventHandler.addEventSource(view);
 
@@ -1039,11 +1051,11 @@
     MImAbstractKeyArea *mainKb = keyboardWidget();
     foreach (const MImAbstractKey *ikey, mainKb->keys()) {
         // only care about the keys which insert characters.
-        if (ikey->key().binding()->action() == MImKeyBinding::ActionInsert) {
+        if (ikey->model().binding()->action() == MImKeyBinding::ActionInsert) {
             bool isPunctuation = false;
             bool isSymbol = false;
             QList<QChar> symbols;
-            foreach (const QChar &c, ikey->key().binding()->label()) {
+            foreach (const QChar &c, ikey->model().binding()->label()) {
                 symbols << c;
                 if (c.isPunct())
                     isPunctuation = true;
@@ -1054,7 +1066,7 @@
             if (isSymbol)
                 continue;
 
-            foreach (const QChar &c, ikey->key().binding()->accentedLabels()) {
+            foreach (const QChar &c, ikey->model().binding()->accentedLabels()) {
                 symbols << c;
             }
 
--- m-keyboard/widgets/mvirtualkeyboard.h
+++ m-keyboard/widgets/mvirtualkeyboard.h
@@ -294,6 +294,11 @@
      */
     void keyClicked(const KeyEvent &event);
 
+    /*!
+     * \brief Emitted when key is long pressed
+     */
+    void longKeyPressed(const KeyEvent &event);
+
     //! \see MAbstractInputMethod::regionUpdated()
     void regionUpdated(const QRegion &);
 
@@ -313,15 +318,6 @@
      */
     void copyPasteClicked(CopyPasteState action);
 
-    //! Emitted when require a copy/paste action
-    void copyPasteRequest(CopyPasteState);
-
-    //! Emitted when require sending a keyevent
-    void sendKeyEventRequest(const QKeyEvent &);
-
-    //! Emitted when require sending a string
-    void sendStringRequest(const QString &);
-
     //! Emitted when shift state is changed
     void shiftLevelChanged();
 
--- m-keyboard/widgets/mvirtualkeyboardstyle.h
+++ m-keyboard/widgets/mvirtualkeyboardstyle.h
@@ -44,6 +44,7 @@
     M_STYLE_ATTRIBUTE(QSize, menuSize, MenuSize)
 
     M_STYLE_ATTRIBUTE(QFont, notificationFont, NotificationFont)
+    M_STYLE_ATTRIBUTE(int, notificationFontSize, NotificationFontSize)
     M_STYLE_ATTRIBUTE(QColor, notificationBorderColor, NotificationBorderColor)
     M_STYLE_ATTRIBUTE(QColor, notificationBackgroundColor, NotificationBackgroundColor)
     M_STYLE_ATTRIBUTE(QColor, notificationTextColor, NotificationTextColor)
--- m-keyboard/widgets/notification.cpp
+++ m-keyboard/widgets/notification.cpp
@@ -37,7 +37,8 @@
 
 Notification::Notification(const MVirtualKeyboardStyleContainer *style, QGraphicsWidget *parent)
     : MWidget(parent),
-      styleContainer(style)
+      styleContainer(style),
+      opacity(0)
 {
     // Notification sets its own absolute opacity
     setFlag(ItemIgnoresParentOpacity, true);
@@ -124,6 +125,7 @@
 void Notification::getStyleValues()
 {
     font = style()->notificationFont();
+    font.setPixelSize(style()->notificationFontSize());
     border = style()->notificationBorderColor();
     background = style()->notificationBackgroundColor();
     textColor = style()->notificationTextColor();
--- m-keyboard/widgets/popupbase.h
+++ m-keyboard/widgets/popupbase.h
@@ -68,8 +68,8 @@
     //! Returns whether PopupBase has any visible components
     virtual bool isVisible() const = 0;
 
-    //! Enables/disables PopupBase completely (affects visibility)
-    virtual void setEnabled(bool ok) = 0;
+    //! Toggles visibility of PopupBase
+    virtual void setVisible(bool visible) = 0;
 };
 
 #endif
--- m-keyboard/widgets/popupfactory.cpp
+++ m-keyboard/widgets/popupfactory.cpp
@@ -62,7 +62,7 @@
         return false;
     }
 
-    virtual void setEnabled(bool)
+    virtual void setVisible(bool)
     {}
     //! \reimp_end
 };
--- m-keyboard/widgets/symbolview.cpp
+++ m-keyboard/widgets/symbolview.cpp
@@ -148,6 +148,8 @@
             this,          SIGNAL(keyReleased(KeyEvent)));
     connect(&eventHandler, SIGNAL(keyClicked(KeyEvent)),
             this,          SIGNAL(keyClicked(KeyEvent)));
+    connect(&eventHandler, SIGNAL(longKeyPressed(const KeyEvent &)),
+            this,          SIGNAL(longKeyPressed(const KeyEvent &)));
     connect(&eventHandler, SIGNAL(shiftPressed(bool)),
             this,          SLOT(handleShiftPressed(bool)));
 
--- m-keyboard/widgets/symbolview.h
+++ m-keyboard/widgets/symbolview.h
@@ -168,6 +168,11 @@
      */
     void keyReleased(const KeyEvent &event);
 
+    /*!
+     * \brief Emitted when key is long pressed
+     */
+    void longKeyPressed(const KeyEvent &event);
+
     //! Emitted when SymbolView has changed its interactive region.
     void regionUpdated(const QRegion &);
 
--- m-keyboard/widgets/widgetbar.cpp
+++ m-keyboard/widgets/widgetbar.cpp
@@ -34,10 +34,6 @@
 {
     mainLayout.setSpacing(0); // Spacing is handled by dividers.
     mainLayout.setContentsMargins(0, 0, 0, 0);
-
-    // Update style by calling styleChanged(). This if for widgets that don't have
-    // object name set.
-    styleChanged();
 }
 
 WidgetBar::~WidgetBar()
@@ -105,12 +101,6 @@
     // Stop propagating
 }
 
-void WidgetBar::styleChanged()
-{
-    setContentsMargins(style()->paddingLeft(), style()->paddingTop(),
-                       style()->paddingRight(), 0);
-}
-
 QSizeF WidgetBar::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
 {
     // if there is no visible items in the widgetbar, just return empty size.
--- m-keyboard/widgets/widgetbar.h
+++ m-keyboard/widgets/widgetbar.h
@@ -89,7 +89,6 @@
 protected:
     //! \reimp
     virtual void mousePressEvent(QGraphicsSceneMouseEvent *event);
-    virtual void styleChanged();
     //! \reimp_end
 
 private:
--- m-keyboard/widgets/widgets.pri
+++ m-keyboard/widgets/widgets.pri
@@ -19,7 +19,7 @@
     $$WIDGETS_DIR/handlestyle.h \
     $$WIDGETS_DIR/mtoolbarbuttonstyle.h \
     $$WIDGETS_DIR/mimtoolbarstyle.h \
-    $$WIDGETS_DIR/mimcorrectioncandidatecontainerstyle.h \
+    $$WIDGETS_DIR/mimwordtrackerstyle.h \
     $$WIDGETS_DIR/mimcorrectioncandidateitemstyle.h \
     $$WIDGETS_DIR/mtoolbarlabelstyle.h \
 
@@ -27,9 +27,11 @@
     $$PUBLIC_HEADERS \
     $$STYLE_HEADERS \
     $$WIDGETS_DIR/widgetbar.h \
-    $$WIDGETS_DIR/mimcorrectioncandidatewidget.h \
+    $$WIDGETS_DIR/mimcorrectionhost.h \
     $$WIDGETS_DIR/mimcorrectioncandidateitem.h \
-    $$WIDGETS_DIR/mimcorrectioncandidateitemview.h \
+    $$WIDGETS_DIR/mimwordtracker.h \
+    $$WIDGETS_DIR/mimwordlist.h \
+    $$WIDGETS_DIR/mimwordlistitem.h \
     $$WIDGETS_DIR/mimtoolbar.h \
     $$WIDGETS_DIR/mvirtualkeyboard.h \
     $$WIDGETS_DIR/horizontalswitcher.h \
@@ -52,14 +54,17 @@
 
 SOURCES += \
     $$WIDGETS_DIR/widgetbar.cpp \
-    $$WIDGETS_DIR/mimcorrectioncandidatewidget.cpp \
+    $$WIDGETS_DIR/mimcorrectionhost.cpp \
     $$WIDGETS_DIR/mimcorrectioncandidateitem.cpp \
-    $$WIDGETS_DIR/mimcorrectioncandidateitemview.cpp \
+    $$WIDGETS_DIR/mimwordtracker.cpp \
+    $$WIDGETS_DIR/mimwordlist.cpp \
+    $$WIDGETS_DIR/mimwordlistitem.cpp \
     $$WIDGETS_DIR/mimtoolbar.cpp \
     $$WIDGETS_DIR/mvirtualkeyboard.cpp \
     $$WIDGETS_DIR/horizontalswitcher.cpp \
     $$WIDGETS_DIR/notification.cpp \
     $$WIDGETS_DIR/symbolview.cpp \
+    $$WIDGETS_DIR/mimabstractkey.cpp \
     $$WIDGETS_DIR/mimabstractkeyarea.cpp \
     $$WIDGETS_DIR/mimkey.cpp \
     $$WIDGETS_DIR/mimkeyarea.cpp \
--- tests/bm_mimabstractkeyarea/bm_mimabstractkeyarea.cpp
+++ tests/bm_mimabstractkeyarea/bm_mimabstractkeyarea.cpp
@@ -86,8 +86,8 @@
     subject = new MImKeyArea(keyboard->layout(LayoutData::General, M::Landscape)->section(LayoutData::mainSection));
 
     QBENCHMARK {
-        subject->updateButtonGeometriesForWidth(864);
-        subject->updateButtonModifiers();
+        subject->updateKeyGeometries(864);
+        subject->updateKeyModifiers();
     }
 }
 
--- tests/tests.pro
+++ tests/tests.pro
@@ -6,7 +6,9 @@
           ut_mvirtualkeyboard \
           ut_mkeyboardhost \
           ut_mkeyboardplugin \
-          ut_mimcorrectioncandidatewidget \
+          ut_mimcorrectionhost \
+          ut_mimwordlist \
+          ut_mimwordtracker \
           ut_mimkey \
           ut_mimabstractkeyarea \
           ut_flickrecognizer \
--- tests/ut_horizontalswitcher/ut_horizontalswitcher.cpp
+++ tests/ut_horizontalswitcher/ut_horizontalswitcher.cpp
@@ -14,8 +14,11 @@
  * of this file.
  */
 
+//#define MIMABSTRACTKEYAREA_H
+
 #include "ut_horizontalswitcher.h"
 #include "horizontalswitcher.h"
+#include "mimabstractkeyarea.h"
 #include "utils.h"
 
 #include <QGraphicsView>
--- tests/ut_horizontalswitcher/ut_horizontalswitcher.pro
+++ tests/ut_horizontalswitcher/ut_horizontalswitcher.pro
@@ -1,10 +1,12 @@
 TEMPLATE = app
 
+CONFIG += meegotouch
+
 include(../common_check.pri)
 
+LIBS += -L/usr/lib -Wl,-rpath=/usr/lib/meego-im-plugins/ -lmeego-keyboard
+
 SOURCES += ut_horizontalswitcher.cpp \
-           $$WIDGETS_DIR/horizontalswitcher.cpp
 
 HEADERS += ut_horizontalswitcher.h \
-           $$WIDGETS_DIR/horizontalswitcher.h
 
--- tests/ut_mhardwarekeyboard/ut_mhardwarekeyboard.cpp
+++ tests/ut_mhardwarekeyboard/ut_mhardwarekeyboard.cpp
@@ -1134,4 +1134,46 @@
 }
 
 
+void Ut_MHardwareKeyboard::testArrowKeyFiltering()
+{
+    // Filter when Fn is not being held down
+    QVERIFY(filterKeyPress(Qt::Key_Home, Qt::NoModifier, "", KeycodeCharacter, FnModifierMask));
+
+    QCOMPARE(inputMethodHost->keyEventsSent(), static_cast<unsigned int>(1));
+    QCOMPARE(inputMethodHost->lastKeyEvent().key(), static_cast<int>(Qt::Key_Left));
+    QCOMPARE(inputMethodHost->lastKeyEvent().type(), QEvent::KeyPress);
+
+    QVERIFY(filterKeyRelease(Qt::Key_Home, Qt::NoModifier, "", KeycodeCharacter, FnModifierMask));
+
+    QCOMPARE(inputMethodHost->keyEventsSent(), static_cast<unsigned int>(2));
+    QCOMPARE(inputMethodHost->lastKeyEvent().key(), static_cast<int>(Qt::Key_Left));
+    QCOMPARE(inputMethodHost->lastKeyEvent().type(), QEvent::KeyRelease);
+
+    // Don't filter when Fn is pressed
+    filterKeyPress(FnLevelKey, Qt::NoModifier, "", KeycodeNonCharacter, 0);
+    QVERIFY(!filterKeyPress(Qt::Key_Home, Qt::NoModifier, "", KeycodeCharacter, FnModifierMask));
+    QVERIFY(!filterKeyRelease(Qt::Key_Home, Qt::NoModifier, "", KeycodeCharacter, FnModifierMask));
+    filterKeyRelease(FnLevelKey, Qt::NoModifier, "", KeycodeNonCharacter, FnModifierMask);
+}
+
+
+void Ut_MHardwareKeyboard::testCtrlShortcutsWithFn()
+{
+    // Must work as if Fn was not on.
+    QVERIFY(filterKeyPress(Qt::Key_Percent, Qt::ControlModifier, "", KeycodeCharacter, FnModifierMask | ControlMask));
+
+    QCOMPARE(inputMethodHost->keyEventsSent(), static_cast<unsigned int>(1));
+    QCOMPARE(inputMethodHost->lastKeyEvent().key(), static_cast<int>(Qt::Key_A));
+    QCOMPARE(inputMethodHost->lastKeyEvent().modifiers(), Qt::ControlModifier);
+    QCOMPARE(inputMethodHost->lastKeyEvent().type(), QEvent::KeyPress);
+
+    QVERIFY(filterKeyRelease(Qt::Key_Percent, Qt::ControlModifier, "", KeycodeCharacter, FnModifierMask | ControlMask));
+
+    QCOMPARE(inputMethodHost->keyEventsSent(), static_cast<unsigned int>(2));
+    QCOMPARE(inputMethodHost->lastKeyEvent().key(), static_cast<int>(Qt::Key_A));
+    QCOMPARE(inputMethodHost->lastKeyEvent().modifiers(), Qt::ControlModifier);
+    QCOMPARE(inputMethodHost->lastKeyEvent().type(), QEvent::KeyRelease);
+}
+
+
 QTEST_APPLESS_MAIN(Ut_MHardwareKeyboard);
--- tests/ut_mhardwarekeyboard/ut_mhardwarekeyboard.h
+++ tests/ut_mhardwarekeyboard/ut_mhardwarekeyboard.h
@@ -73,6 +73,10 @@
 
     void testDeadKeys();
 
+    void testArrowKeyFiltering();
+
+    void testCtrlShortcutsWithFn();
+
 private:
     bool checkLatchedState(unsigned int mask, unsigned int value) const;
     bool checkLockedState(unsigned int mask, unsigned int value) const;
--- tests/ut_mimabstractkeyarea/ut_mimabstractkeyarea.cpp
+++ tests/ut_mimabstractkeyarea/ut_mimabstractkeyarea.cpp
@@ -27,6 +27,7 @@
 
 #include <MApplication>
 #include <MScene>
+#include <MSceneWindow>
 #include <MSceneManager>
 #include <MTheme>
 
@@ -68,11 +69,14 @@
 Q_DECLARE_METATYPE(QList<MImKeyBinding::KeyAction>);
 Q_DECLARE_METATYPE(Ut_MImAbstractKeyArea::TestOpList);
 
-MImAbstractKeyArea *createSingleWidgetMImAbstractKeyArea(const LayoutData::SharedLayoutSection &section,
-                                               bool usePopup = false,
-                                               QGraphicsWidget *parent = 0)
-{
-    return new MImKeyArea(section, usePopup, parent);
+namespace {
+
+    MImAbstractKeyArea *createKeyArea(const LayoutData::SharedLayoutSection &section,
+                                      bool usePopup = false,
+                                      QGraphicsWidget *parent = 0)
+    {
+        return new MImKeyArea(section, usePopup, parent);
+    }
 }
 
 void Ut_MImAbstractKeyArea::initTestCase()
@@ -91,7 +95,7 @@
     qRegisterMetaType<MImAbstractKey::ButtonState>();
     qRegisterMetaType<TestOpList>("TestOpList");
 
-    createMScene(new MPlainWindow); // also create singleton
+    sceneWindow = createMSceneWindow(new MPlainWindow); // also create singleton
 
     FlickGestureRecognizer::registerSharedRecognizer();
 
@@ -103,6 +107,7 @@
 void Ut_MImAbstractKeyArea::cleanupTestCase()
 {
     FlickGestureRecognizer::unregisterSharedRecognizer();
+    delete sceneWindow;
     delete MPlainWindow::instance();
     delete app;
     app = 0;
@@ -124,7 +129,7 @@
 void Ut_MImAbstractKeyArea::testLandscapeBoxSize_data()
 {
     QTest::addColumn<KBACreator>("createKba");
-    QTest::newRow("SingleWidgetArea") << &createSingleWidgetMImAbstractKeyArea;
+    QTest::newRow("SingleWidgetArea") << &createKeyArea;
 }
 
 void Ut_MImAbstractKeyArea::testLandscapeBoxSize()
@@ -170,7 +175,7 @@
 void Ut_MImAbstractKeyArea::testPortraitBoxSize_data()
 {
     QTest::addColumn<KBACreator>("createKba");
-    QTest::newRow("SingleWidgetArea") << &createSingleWidgetMImAbstractKeyArea;
+    QTest::newRow("SingleWidgetArea") << &createKeyArea;
 }
 
 void Ut_MImAbstractKeyArea::testPortraitBoxSize()
@@ -213,7 +218,7 @@
 void Ut_MImAbstractKeyArea::testLabelPosition_data()
 {
     QTest::addColumn<KBACreator>("createKba");
-    QTest::newRow("SingleWidgetArea") << &createSingleWidgetMImAbstractKeyArea;
+    QTest::newRow("SingleWidgetArea") << &createKeyArea;
 }
 
 void Ut_MImAbstractKeyArea::testLabelPosition()
@@ -259,7 +264,7 @@
     for (int n = 0; n < positions.count(); ++n) {
         qDebug() << "test position" << positions.at(n);
         button = subject->keyAt(positions.at(n));
-        const MImKeyModel *result = (button ? &button->key() : 0);
+        const MImKeyModel *result = (button ? &button->model() : 0);
         QCOMPARE(result, outcome.at(n));
     }
 }
@@ -267,7 +272,7 @@
 void Ut_MImAbstractKeyArea::testSceneEvent_data()
 {
     QTest::addColumn<KBACreator>("createKba");
-    QTest::newRow("SingleWidgetArea") << &createSingleWidgetMImAbstractKeyArea;
+    QTest::newRow("SingleWidgetArea") << &createKeyArea;
 }
 
 void Ut_MImAbstractKeyArea::testSceneEvent()
@@ -328,7 +333,7 @@
 void Ut_MImAbstractKeyArea::testPaint_data()
 {
     QTest::addColumn<KBACreator>("createKba");
-    QTest::newRow("SingleWidgetArea") << &createSingleWidgetMImAbstractKeyArea;
+    QTest::newRow("SingleWidgetArea") << &createKeyArea;
 }
 
 void Ut_MImAbstractKeyArea::testPaint()
@@ -355,7 +360,7 @@
 void Ut_MImAbstractKeyArea::testDeadkeys_data()
 {
     QTest::addColumn<KBACreator>("createKba");
-    QTest::newRow("SingleWidgetArea") << &createSingleWidgetMImAbstractKeyArea;
+    QTest::newRow("SingleWidgetArea") << &createKeyArea;
 }
 
 void Ut_MImAbstractKeyArea::testDeadkeys()
@@ -416,7 +421,10 @@
     }
 
     //after unlock the dead key, test the shift status
-    subject->unlockDeadkeys();
+    SpecialKeyFinder finder(SpecialKeyFinder::FindDeadKey);
+    MImAbstractKey::visitActiveKeys(&finder);
+
+    subject->unlockDeadKeys(finder.deadKey());
     for (i = 0; i < positions.count(); i++) {
         QCOMPARE(keyAt(0, positions[i])->label(), upperUnicodes.at(i));
     }
@@ -446,7 +454,7 @@
 {
     keyboard = new KeyboardData;
     QVERIFY(keyboard->loadNokiaKeyboard("fr.xml"));
-    subject = createSingleWidgetMImAbstractKeyArea(keyboard->layout(LayoutData::General, M::Landscape)->section(LayoutData::mainSection),
+    subject = createKeyArea(keyboard->layout(LayoutData::General, M::Landscape)->section(LayoutData::mainSection),
                                               false, 0);
     MPlainWindow::instance()->scene()->addItem(subject);
 
@@ -513,15 +521,15 @@
 
     keyboard = new KeyboardData;
     QVERIFY(keyboard->loadNokiaKeyboard("test-layout.xml"));
-    subject = createSingleWidgetMImAbstractKeyArea(keyboard->layout(LayoutData::General, M::Landscape)->section(LayoutData::mainSection),
+    subject = createKeyArea(keyboard->layout(LayoutData::General, M::Landscape)->section(LayoutData::mainSection),
                                               false, 0);
 
     MImAbstractKey *deadkey = keyAt(2, 8); // accents ´ and ¨
     MImAbstractKey *characterKey = keyAt(0, 2); // e, éë, ÉË
 
     QVERIFY(deadkey);
-    QVERIFY(deadkey->key().binding(false)->isDead());
-    QVERIFY(deadkey->key().binding(true)->isDead());
+    QVERIFY(deadkey->model().binding(false)->isDead());
+    QVERIFY(deadkey->model().binding(true)->isDead());
 
     foreach (TestOperation op, operations) {
         switch (op) {
@@ -547,18 +555,18 @@
 {
     keyboard = new KeyboardData;
     QVERIFY(keyboard->loadNokiaKeyboard("test-layout.xml"));
-    subject = createSingleWidgetMImAbstractKeyArea(keyboard->layout(LayoutData::General, M::Landscape)->section(LayoutData::mainSection),
+    subject = createKeyArea(keyboard->layout(LayoutData::General, M::Landscape)->section(LayoutData::mainSection),
                                               false, 0);
 
     const MImAbstractKey *eKey(keyAt(0, 2)); // e, ...
-    QCOMPARE(eKey->key().binding(false)->extendedLabels(), QString("%1%2").arg(QChar(0xea)).arg(QChar(0xe8)));
-    QCOMPARE(eKey->key().binding(true)->extendedLabels(), QString("%1%2").arg(QChar(0xca)).arg(QChar(0xc8)));
+    QCOMPARE(eKey->model().binding(false)->extendedLabels(), QString("%1%2").arg(QChar(0xea)).arg(QChar(0xe8)));
+    QCOMPARE(eKey->model().binding(true)->extendedLabels(), QString("%1%2").arg(QChar(0xca)).arg(QChar(0xc8)));
 }
 
 void Ut_MImAbstractKeyArea::testImportedLayouts_data()
 {
     QTest::addColumn<KBACreator>("createKba");
-    QTest::newRow("SingleWidgetArea") << &createSingleWidgetMImAbstractKeyArea;
+    QTest::newRow("SingleWidgetArea") << &createKeyArea;
 }
 
 void Ut_MImAbstractKeyArea::testImportedLayouts()
@@ -601,7 +609,7 @@
 void Ut_MImAbstractKeyArea::testPopup_data()
 {
     QTest::addColumn<KBACreator>("createKba");
-    QTest::newRow("SingleWidgetArea") << &createSingleWidgetMImAbstractKeyArea;
+    QTest::newRow("SingleWidgetArea") << &createKeyArea;
 }
 
 void Ut_MImAbstractKeyArea::testPopup()
@@ -634,7 +642,7 @@
 void Ut_MImAbstractKeyArea::testInitialization_data()
 {
     QTest::addColumn<KBACreator>("createKba");
-    QTest::newRow("SingleWidgetArea") << &createSingleWidgetMImAbstractKeyArea;
+    QTest::newRow("SingleWidgetArea") << &createKeyArea;
 }
 
 void Ut_MImAbstractKeyArea::testInitialization()
@@ -657,10 +665,10 @@
     QVERIFY(layout);
     const LayoutData::SharedLayoutSection section = layout->section(LayoutData::mainSection);
 
-    subject = createSingleWidgetMImAbstractKeyArea(section,
+    subject = createKeyArea(section,
                                               false, 0);
 
-    MImKey *shiftButton = static_cast<MImKeyArea *>(subject)->shiftButton;
+    MImKey *shiftButton = static_cast<MImKeyArea *>(subject)->shiftKey;
     QVERIFY(shiftButton);
     QVERIFY(shiftButton->state() == MImAbstractKey::Normal);
 
@@ -682,7 +690,7 @@
     QVERIFY(layout);
     const LayoutData::SharedLayoutSection section = layout->section(LayoutData::mainSection);
 
-    subject = createSingleWidgetMImAbstractKeyArea(section,
+    subject = createKeyArea(section,
                                               false, 0);
     MPlainWindow::instance()->scene()->addItem(subject);
     subject->resize(defaultLayoutSize());
@@ -786,25 +794,25 @@
     rtlKeys << MImKeyBinding::ActionBackspace;
 
     QTest::newRow("SingleWidgetArea Landscape Arabic")
-        << &createSingleWidgetMImAbstractKeyArea
+        << &createKeyArea
         << M::Landscape
         << ar
         << rtlKeys;
 
     QTest::newRow("SingleWidgetArea Portrait Arabic" )
-        << &createSingleWidgetMImAbstractKeyArea
+        << &createKeyArea
         << M::Portrait
         << ar
         << rtlKeys;
 
     QTest::newRow("SingleWidgetArea Landscape English")
-        << &createSingleWidgetMImAbstractKeyArea
+        << &createKeyArea
         << M::Landscape
         << en_gb
         << nothing;
 
     QTest::newRow("SingleWidgetArea Portrait English" )
-        << &createSingleWidgetMImAbstractKeyArea
+        << &createKeyArea
         << M::Portrait
         << en_gb
         << nothing;
@@ -822,19 +830,19 @@
     subject = createKba(keyboard->layout(LayoutData::General, orientation)->section(LayoutData::mainSection),
                         false, 0);
 
-    MImKeyArea *buttonArea = dynamic_cast<MImKeyArea *>(subject);
+    MImKeyArea *keyArea = dynamic_cast<MImKeyArea *>(subject);
 
-    QVERIFY2(buttonArea != 0, "Unknown type of button area");
-    for (int row = 0; row < buttonArea->rowCount(); ++row) {
-        for (int column = 0; column < buttonArea->sectionModel()->columnsAt(row); ++column) {
-            MImKey *button = buttonArea->rowList[row].buttons[column];
-            QVERIFY(button != 0);
-            if (expectedRtlKeys.contains(button->key().binding()->action())) {
-                QVERIFY(button->key().rtl());
-                QVERIFY2(button->iconId().contains("-rtl-"), "This is not RTL icon");
-            } else if (!button->iconId().isEmpty()) {
-                QVERIFY(!button->key().rtl());
-                QVERIFY2(!button->iconId().contains("-rtl-"), "This is not LTR icon");
+    QVERIFY2(keyArea != 0, "Unknown type of button area");
+    for (int row = 0; row < keyArea->rowCount(); ++row) {
+        for (int column = 0; column < keyArea->sectionModel()->columnsAt(row); ++column) {
+            MImKey *key = keyArea->rowList[row].keys[column];
+            QVERIFY(key != 0);
+            if (expectedRtlKeys.contains(key->model().binding()->action())) {
+                QVERIFY(key->model().rtl());
+                QVERIFY2(key->iconId().contains("-rtl-"), "This is not RTL icon");
+            } else if (!key->iconId().isEmpty()) {
+                QVERIFY(!key->model().rtl());
+                QVERIFY2(!key->iconId().contains("-rtl-"), "This is not LTR icon");
             }
         }
     }
@@ -848,7 +856,7 @@
     keyboard = new KeyboardData;
     QVERIFY(keyboard->loadNokiaKeyboard("en_us.xml"));
 
-    subject = createSingleWidgetMImAbstractKeyArea(keyboard->layout(LayoutData::General, M::Landscape)->section(LayoutData::mainSection),
+    subject = createKeyArea(keyboard->layout(LayoutData::General, M::Landscape)->section(LayoutData::mainSection),
                                               true, 0);
 
     MPlainWindow::instance()->scene()->addItem(subject);
@@ -1151,7 +1159,7 @@
 
     MImKeyArea *buttonArea = dynamic_cast<MImKeyArea *>(subject);
     if (buttonArea) {
-        key = buttonArea->rowList[row].buttons[column];
+        key = buttonArea->rowList[row].keys[column];
     }
 
     return key;
--- tests/ut_mimabstractkeyarea/ut_mimabstractkeyarea.h
+++ tests/ut_mimabstractkeyarea/ut_mimabstractkeyarea.h
@@ -28,6 +28,7 @@
 class MImAbstractKeyArea;
 class KeyboardData;
 class MImAbstractKey;
+class MSceneWindow;
 
 class Ut_MImAbstractKeyArea : public QObject
 {
@@ -36,6 +37,7 @@
     MApplication *app;
     MImAbstractKeyArea *subject;
     KeyboardData *keyboard;
+    MSceneWindow *sceneWindow;
 
 private slots:
     void init();
--- tests/ut_mimcorrectioncandidatewidget
+++ tests/ut_mimcorrectioncandidatewidget
-(directory)
--- tests/ut_mimcorrectioncandidatewidget/ut_mimcorrectioncandidatewidget.cpp
+++ tests/ut_mimcorrectioncandidatewidget/ut_mimcorrectioncandidatewidget.cpp
-/* * This file is part of meego-keyboard *
- *
- * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
- * All rights reserved.
- * Contact: Nokia Corporation (directui at nokia.com)
- *
- * If you have questions regarding the use of this file, please contact
- * Nokia at directui at nokia.com.
- *
- * This library is free software; you can redistribute it and/or
- * modify it 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.
- */
-
-
-
-#include "ut_mimcorrectioncandidatewidget.h"
-#include "mplainwindow.h"
-#include "utils.h"
-#include <MTheme>
-#include <MList>
-#include <QtTest/QTest>
-#include <QObject>
-#include <QDebug>
-#include <QStringList>
-#include <QStringListModel>
-#include <QSignalSpy>
-#include <QGraphicsSceneMouseEvent>
-#include <MSceneManager>
-
-void Ut_MImCorrectionCandidateWidget::initTestCase()
-{
-    static int dummyArgc = 2;
-    static char *dummyArgv[2] = { (char *) "./ut_mimcorrectioncandidatewidget",
-                                  (char *) "-local-theme" };
-    disableQtPlugins();
-    app = new MApplication(dummyArgc, dummyArgv);
-
-    // MImCorrectionCandidateWidget uses this internally
-    new MPlainWindow;
-    if (MPlainWindow::instance()->orientationAngle() != M::Angle0) {
-        MPlainWindow::instance()->setOrientationAngle(M::Angle0);
-        QTest::qWait(1000);
-    }
-
-    // initialize testCandidateWidgetSize
-    m_subject = new MImCorrectionCandidateWidget;
-    QStringList candidates;
-    candidates << "1" << "2" << "3";
-    m_subject->setCandidates(candidates);
-    testCandidateWidgetSize = m_subject->candidatesWidget->preferredSize().toSize();
-    testCandidateWidgetSize.setWidth(m_subject->candidateWidth);
-    delete m_subject;
-}
-
-
-void Ut_MImCorrectionCandidateWidget::init()
-{
-    m_subject = new MImCorrectionCandidateWidget;
-
-    if (MPlainWindow::instance()->orientationAngle() != M::Angle0) {
-        MPlainWindow::instance()->setOrientationAngle(M::Angle0);
-        QTest::qWait(1000);
-    }
-}
-
-
-void Ut_MImCorrectionCandidateWidget::cleanup()
-{
-    delete m_subject;
-}
-
-void Ut_MImCorrectionCandidateWidget::cleanupTestCase()
-{
-    delete MPlainWindow::instance();
-    delete app;
-    app = 0;
-}
-
-void Ut_MImCorrectionCandidateWidget::checkPositionByPoint_data()
-{
-    QTest::addColumn<QPoint>("pos");
-    QTest::addColumn<int>("bottomLimit");
-    QTest::addColumn<QPoint>("expected");
-
-    const int sceneHeight = MPlainWindow::instance()->visibleSceneSize().height();
-
-    QTest::newRow("null")      << QPoint() << -1 << QPoint();
-    QTest::newRow("origo")     << QPoint(0, 0) << -1 << QPoint(0, 0);
-    QTest::newRow("negative")  << QPoint(-10, -10) << -1 << QPoint(0, 0);
-    QTest::newRow("positive1") << QPoint(10, 10) << -1 << QPoint(10, 10);
-    QTest::newRow("positive2") << QPoint(80, 80) << -1 << QPoint(80, 80);
-
-    // Goes over sceneHeight limit
-    QTest::newRow("positive3") <<QPoint(700, sceneHeight - 50) << -1
-                               << QPoint(700, ((sceneHeight - testCandidateWidgetSize.height()) > 0)?
-                                               (sceneHeight - testCandidateWidgetSize.height()):0);
-
-    // No room above so aligns to y=0.
-    QTest::newRow("bottom limit 1") << QPoint(10, 10) << 4 << QPoint(10, 0);
-
-    // There is room above so bottom limit holds (y+height=bottomlimit).
-    QTest::newRow("bottom limit 2") << QPoint(10, 250) << 220
-                                    << QPoint(10, ((220 - testCandidateWidgetSize.height()) > 0)?
-                                              (220 - testCandidateWidgetSize.height()):0);
-}
-
-void Ut_MImCorrectionCandidateWidget::checkPositionByPoint()
-{
-    QStringList candidates;
-    candidates << "1" << "2" << "3";
-    m_subject->setCandidates(candidates);
-    QFETCH(QPoint, pos);
-    QFETCH(int, bottomLimit);
-    QFETCH(QPoint, expected);
-
-    m_subject->setPosition(pos, bottomLimit);
-    QCOMPARE(m_subject->position(), expected);
-}
-
-
-void Ut_MImCorrectionCandidateWidget::checkPositionByPreeditRect()
-{
-    const int CandidatesPreeditMargin = 10;
-    const QSize sceneSize = MPlainWindow::instance()->visibleSceneSize();
-
-    QList<QRect> rects;
-    QList<QPoint> positionsCheck;
-    QPoint pos;
-    QSize size;
-
-    QStringList candidates;
-    candidates << "1" << "2" << "3";
-    m_subject->setCandidates(candidates);
-
-    // check with null rectangle
-    rects.append(QRect());
-    positionsCheck.append(QPoint());
-    rects.append(QRect(0, 0, 0, 0));
-    positionsCheck.append(QPoint(0, 0));
-    rects.append(QRect(QPoint(10, 10), QSize(0, 0)));
-    positionsCheck.append(QPoint(0, 0));
-
-    // invalid rectangle
-    rects.append(QRect(QPoint(10, 10), QPoint(5, 5)));
-    positionsCheck.append(QPoint());
-    rects.append(QRect(QPoint(), QPoint(-100, -100)));
-    positionsCheck.append(QPoint());
-    rects.append(QRect(QPoint(0, 0), QPoint(5, -5)));
-    positionsCheck.append(QPoint());
-
-    // valid rectangle inside area
-    pos = QPoint(10, 10 + testCandidateWidgetSize.height());
-    size = QSize(1, 1);
-    rects.append(QRect(pos, size));
-    positionsCheck.append(QPoint(pos.x() + size.width() + CandidatesPreeditMargin,
-                                 pos.y() + size.height() / 2 - testCandidateWidgetSize.height() / 2));
-
-    pos = QPoint(4, 5 + testCandidateWidgetSize.height());
-    size = QSize(100, 5);
-    rects.append(QRect(pos, size));
-    positionsCheck.append(QPoint(pos.x() + size.width() + CandidatesPreeditMargin,
-                                 pos.y() + size.height() / 2 - testCandidateWidgetSize.height() / 2));
-
-    // valid rectangle outside area
-    size = QSize(100, 5);
-    pos = QPoint(0, testCandidateWidgetSize.height() / 2 - size.height() / 2 - 1);
-    rects.append(QRect(pos, size));
-    positionsCheck.append(QPoint(pos.x() + size.width() + CandidatesPreeditMargin, 0));
-
-    size = QSize(10, 50);
-    pos = QPoint(0, testCandidateWidgetSize.height() / 2 - size.height() / 2 - 1);
-    rects.append(QRect(pos, size));
-    positionsCheck.append(QPoint(pos.x() + size.width() + CandidatesPreeditMargin, 0));
-
-    // switch to left side, vertical overlap
-    size = QSize(10, 10);
-    pos = QPoint(sceneSize.width() - testCandidateWidgetSize.width() + 1,
-                 sceneSize.height() - testCandidateWidgetSize.height() / 2);
-    rects.append(QRect(pos, size));
-    positionsCheck.append(QPoint(pos.x() - CandidatesPreeditMargin - testCandidateWidgetSize.width(),
-                                 sceneSize.height() - testCandidateWidgetSize.height()));
-
-    for (int i = 0; i < rects.size(); ++i) {
-        m_subject->setPosition(rects.at(i));
-        QCOMPARE(m_subject->position(), positionsCheck.at(i));
-    }
-}
-
-void Ut_MImCorrectionCandidateWidget::checkActiveIndex()
-{
-    QStringList candidates;
-    candidates << "1" << "2" << "3" << "4" << "5"
-        << "ab" << "cd" << "ef" << "gf";
-    m_subject->setPreeditString(candidates.last());
-    m_subject->setCandidates(candidates);
-    //default preedit is not in the list
-    QCOMPARE(m_subject->candidatesModel->rowCount(), (candidates.count() - 1));
-    QCOMPARE(m_subject->activeIndex(), -1);
-
-    QList<int> index;
-    index << 0 << 1 << 2 << 3 << 4 << 5 << 6 << 7;
-
-    for (int i = 0; i < index.size(); ++i) {
-        m_subject->setPreeditString(candidates.at(i));
-        QCOMPARE(m_subject->activeIndex(), index.at(i));
-    }
-}
-
-
-void Ut_MImCorrectionCandidateWidget::checkPreeditString()
-{
-    QStringList str;
-    str << "autobahnraststaettenbetreiber" << "Nobody_is_there" <<
-        "99ab88bc!§%/()=?" << "lalallala";
-
-    foreach(const QString & tmp, str) {
-        m_subject->setPreeditString(tmp);
-        QVERIFY(m_subject->preeditString() == tmp);
-    }
-}
-
-void Ut_MImCorrectionCandidateWidget::setCandidatesAndSelect()
-{
-    QStringList candidates;
-    QStringList expectedWord;
-    QList<QPoint> positions;
-    QList<int> expectedSignal;
-    QGraphicsSceneMouseEvent *press = 0;
-    QGraphicsSceneMouseEvent *release = 0;
-    int rowHeight = testCandidateWidgetSize.height() / 3;
-
-    candidates << "1" << "2" << "3";
-    expectedWord << "" << "1" << "2" << "3" << "";
-    positions << QPoint(2, -50)
-              << QPoint(2, rowHeight / 2)
-              << QPoint(2, rowHeight * 3 / 2)
-              << QPoint(2, rowHeight * 5 / 2)
-              << QPoint(800, rowHeight / 2)
-              << QPoint(-20, rowHeight / 2);
-    expectedSignal << 0 << 1 << 1 << 0 << 0 << 0;
-
-    for (int n = 0; n < positions.count(); ++n) {
-        qDebug() << "Test position" << positions.at(n);
-        press = new QGraphicsSceneMouseEvent(QEvent::GraphicsSceneMousePress);
-        release = new QGraphicsSceneMouseEvent(QEvent::GraphicsSceneMouseRelease);
-        press->setPos(positions.at(n));
-        release->setPos(positions.at(n));
-
-        delete m_subject;
-        m_subject = new MImCorrectionCandidateWidget;
-        // set initial state to hidden
-        m_subject->hide();
-        QSignalSpy spyRegion(m_subject, SIGNAL(regionUpdated(const QRegion &)));
-        QSignalSpy spyCandidate(m_subject, SIGNAL(candidateClicked(const QString &)));
-        QSignalSpy spyDisappeared(m_subject, SIGNAL(disappeared()));
-
-        //initialization
-        m_subject->setPreeditString(candidates.last());
-        m_subject->setCandidates(candidates);
-        m_subject->setPosition(QPoint(0, 0));
-        m_subject->showWidget();
-        // wait until animation is finished
-        QTest::qWait(500);
-        QStringList filteredCandidates = candidates;
-        filteredCandidates.removeOne(candidates.last());
-        QVERIFY(m_subject->candidates() == filteredCandidates);
-        //actual testing
-        QCOMPARE(spyRegion.count(), 1);
-
-        QRectF candidatesWidgetRect(m_subject->position(), m_subject->candidatesWidget->preferredSize());
-        if (candidatesWidgetRect.contains(positions.at(n))) {
-            int index = positions.at(n).y() / rowHeight;
-            if (index >= 0 && index < m_subject->candidatesModel->rowCount()) {
-                m_subject->candidatesWidget->selectItem(m_subject->candidatesWidget->itemModel()->index(index, 0));
-            }
-        } else {
-            m_subject->mousePressEvent(press);
-            m_subject->mouseMoveEvent(press);
-        }
-        //if we are not crashed then it is ok
-
-        m_subject->mouseReleaseEvent(release);
-        QCOMPARE(spyCandidate.count(), expectedSignal.at(n));
-        if (spyCandidate.count() > 0) {
-            QCOMPARE(spyCandidate.first().count(), 1);
-            QCOMPARE(spyCandidate.first().first().toString(), expectedWord.at(n));
-        }
-        // wait until animation is finished
-        QTest::qWait(500);
-        QCOMPARE(spyDisappeared.count(), 1);
-
-        delete press;
-        delete release;
-    }
-}
-
-void Ut_MImCorrectionCandidateWidget::checkShowWidget()
-{
-    QStringList candidates;
-    candidates << "1" << "2" << "3" << "4" << "5";
-    m_subject->setCandidates(candidates);
-    // At least, no crash for show and hide
-    m_subject->showWidget();
-    m_subject->hide();
-}
-
-QTEST_APPLESS_MAIN(Ut_MImCorrectionCandidateWidget);
-
--- tests/ut_mimcorrectioncandidatewidget/ut_mimcorrectioncandidatewidget.h
+++ tests/ut_mimcorrectioncandidatewidget/ut_mimcorrectioncandidatewidget.h
-/* * This file is part of meego-keyboard *
- *
- * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
- * All rights reserved.
- * Contact: Nokia Corporation (directui at nokia.com)
- *
- * If you have questions regarding the use of this file, please contact
- * Nokia at directui at nokia.com.
- *
- * This library is free software; you can redistribute it and/or
- * modify it 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.
- */
-
-
-
-#ifndef UT_MIMCORRECTIONCANDIDATEWIDGET_H
-#define UT_MIMCORRECTIONCANDIDATEWIDGET_H
-
-#include "mimcorrectioncandidatewidget.h"
-#include "mapplication.h"
-#include <QtTest/QTest>
-#include <QObject>
-
-class Ut_MImCorrectionCandidateWidget : public QObject
-{
-    Q_OBJECT
-
-private:
-    MApplication *app;
-    MImCorrectionCandidateWidget *m_subject;
-
-private slots:
-    //! initialize application and class
-    void initTestCase();
-    void init();
-    void cleanup();
-    void cleanupTestCase();
-    void checkPositionByPoint_data();
-    void checkPositionByPoint();
-    void checkPositionByPreeditRect();
-    void checkActiveIndex();
-    void checkPreeditString();
-    void setCandidatesAndSelect();
-    void checkShowWidget();
-
-private:
-    QSize testCandidateWidgetSize;
-};
-
-#endif
--- tests/ut_mimcorrectioncandidatewidget/ut_mimcorrectioncandidatewidget.pro
+++ tests/ut_mimcorrectioncandidatewidget/ut_mimcorrectioncandidatewidget.pro
-TEMPLATE = app
-CONFIG += meegotouch
-
-DEPENDPATH += .
-INCLUDEPATH += 	. \
-
-include(../common_check.pri)
-
-LIBS += -Wl,-rpath=/usr/lib/meego-im-plugins/ -lmeego-keyboard
-
-HEADERS += ut_mimcorrectioncandidatewidget.h \
-
-SOURCES += ut_mimcorrectioncandidatewidget.cpp \
-
--- tests/ut_mimcorrectionhost
+++ tests/ut_mimcorrectionhost
+(directory)
--- tests/ut_mimcorrectionhost/ut_mimcorrectionhost.cpp
+++ tests/ut_mimcorrectionhost/ut_mimcorrectionhost.cpp
+/* * This file is part of meego-keyboard *
+ *
+ * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ * All rights reserved.
+ * Contact: Nokia Corporation (directui at nokia.com)
+ *
+ * If you have questions regarding the use of this file, please contact
+ * Nokia at directui at nokia.com.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it 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.
+ */
+
+
+
+#include "ut_mimcorrectionhost.h"
+#include "mimwordtracker.h"
+#include "mimwordlist.h"
+#include <mplainwindow.h>
+#include "utils.h"
+#include <QtTest/QTest>
+#include <QObject>
+#include <QDebug>
+#include <QStringList>
+#include <QSignalSpy>
+#include <MSceneManager>
+#include <MSceneWindow>
+
+Q_DECLARE_METATYPE(MImCorrectionHost::CandidateMode)
+
+void Ut_MImCorrectionHost::initTestCase()
+{
+    static int dummyArgc = 2;
+    static char *dummyArgv[2] = { (char *) "./ut_mimcorrectioncandidatewidget",
+                                  (char *) "-local-theme" };
+    disableQtPlugins();
+    app = new MApplication(dummyArgc, dummyArgv);
+
+    // MImCorrectionHost uses this internally
+    new MPlainWindow;
+    if (MPlainWindow::instance()->orientationAngle() != M::Angle0) {
+        MPlainWindow::instance()->setOrientationAngle(M::Angle0);
+        QTest::qWait(1000);
+    }
+    parentWindow = new MSceneWindow;
+    parentWindow->setManagedManually(true); // we want the scene window to remain in origin
+    // Adds scene window to scene.
+    MPlainWindow::instance()->sceneManager()->appearSceneWindowNow(parentWindow);
+}
+
+
+void Ut_MImCorrectionHost::init()
+{
+    m_subject = new MImCorrectionHost(parentWindow);
+
+    if (MPlainWindow::instance()->orientationAngle() != M::Angle0) {
+        MPlainWindow::instance()->setOrientationAngle(M::Angle0);
+        QTest::qWait(1000);
+    }
+}
+
+
+void Ut_MImCorrectionHost::cleanup()
+{
+    delete m_subject;
+}
+
+void Ut_MImCorrectionHost::cleanupTestCase()
+{
+    delete parentWindow;
+    delete MPlainWindow::instance();
+    delete app;
+    app = 0;
+}
+
+void Ut_MImCorrectionHost::checkShowWidget()
+{
+    QStringList candidates;
+    candidates << "1" << "2" << "3" << "4" << "5";
+    m_subject->setCandidates(candidates);
+    // At least, no crash for show and hide
+    m_subject->showCorrectionWidget(MImCorrectionHost::WordTrackerMode);
+    m_subject->hideCorrectionWidget();
+    m_subject->showCorrectionWidget(MImCorrectionHost::WordListMode);
+    m_subject->hideCorrectionWidget();
+}
+
+void Ut_MImCorrectionHost::checkModes()
+{
+    QStringList candidates;
+    candidates << "1" << "2" << "3" << "4" << "5";
+    m_subject->setCandidates(candidates);
+    m_subject->showCorrectionWidget(MImCorrectionHost::WordTrackerMode);
+    QCOMPARE(m_subject->candidateMode(),  MImCorrectionHost::WordTrackerMode);
+    m_subject->showCorrectionWidget(MImCorrectionHost::WordListMode);
+    QCOMPARE(m_subject->candidateMode(), MImCorrectionHost::WordListMode);
+}
+
+void Ut_MImCorrectionHost::checkCandidatesAndPreedit_data()
+{
+    QTest::addColumn<QStringList>("candidates");
+
+    QTest::newRow("test1")  << (QStringList() << "1" << "2" << "3" << "4" << "5");
+    QTest::newRow("test2")  << (QStringList() << "ab" << "cd" << "ef" << "fg" << "gh");
+}
+
+void Ut_MImCorrectionHost::checkCandidatesAndPreedit()
+{
+    QFETCH(QStringList, candidates);
+
+    m_subject->setCandidates(candidates);
+    QCOMPARE(candidates, m_subject->candidates);
+}
+
+void Ut_MImCorrectionHost::checkSuggestion_data()
+{
+    QTest::addColumn<QStringList>("candidates");
+    QTest::addColumn<MImCorrectionHost::CandidateMode>("candidateMode");
+    QTest::addColumn<QString>("clickedCandidate");
+
+    QTest::newRow("testWordTrackerMode") << (QStringList() << "1" << "2" << "3" << "4" << "5")
+                            << MImCorrectionHost::WordTrackerMode
+                            << "3";
+    QTest::newRow("test2WordListMode") << (QStringList() << "ab" << "cd" << "ef" << "fg" << "gh")
+                            << MImCorrectionHost::WordListMode
+                            << "gh";
+}
+
+void Ut_MImCorrectionHost::checkSuggestion()
+{
+    QFETCH(QStringList, candidates);
+    QFETCH(MImCorrectionHost::CandidateMode, candidateMode);
+    QFETCH(QString, clickedCandidate);
+
+    QSignalSpy spy(m_subject, SIGNAL(candidateClicked(const QString &)));
+
+    m_subject->setCandidates(candidates);
+    m_subject->showCorrectionWidget(candidateMode);
+    if (candidateMode == MImCorrectionHost::WordListMode) {
+        QTest::qWait(600);
+    }
+    // default suggestion is the first one in candidate list which is
+    // different with preedit
+    QCOMPARE(m_subject->suggestion(), candidates.at(1));
+
+    m_subject->handleCandidateClicked(clickedCandidate);
+
+    // suggestion is the clciked word
+    QCOMPARE(m_subject->suggestion(), clickedCandidate);
+    QCOMPARE(spy.count(), 1);
+    QCOMPARE(spy.first().first().toString(), clickedCandidate);
+}
+
+void Ut_MImCorrectionHost::checkPosition()
+{
+    QStringList candidates;
+    candidates << "fug" << "rug" << "dug" << "tug";
+    m_subject->setCandidates(candidates);
+
+    const QSize sceneSize = MPlainWindow::instance()->visibleSceneSize();
+    int width = m_subject->wordTracker->idealWidth();
+
+    //set a position to check whether the word tracker is still inside screen.
+    QPoint insidePos(sceneSize.width() - width, 100);
+    m_subject->setPosition(insidePos);
+    QCOMPARE(m_subject->position(), insidePos);
+
+    QPoint outsidepPos(sceneSize.width(), 100);
+    m_subject->setPosition(outsidepPos);
+    QVERIFY(m_subject->position() != outsidepPos);
+    QCOMPARE(m_subject->position(), insidePos);
+}
+
+QTEST_APPLESS_MAIN(Ut_MImCorrectionHost);
+
--- tests/ut_mimcorrectionhost/ut_mimcorrectionhost.h
+++ tests/ut_mimcorrectionhost/ut_mimcorrectionhost.h
+/* * This file is part of meego-keyboard *
+ *
+ * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ * All rights reserved.
+ * Contact: Nokia Corporation (directui at nokia.com)
+ *
+ * If you have questions regarding the use of this file, please contact
+ * Nokia at directui at nokia.com.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it 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.
+ */
+
+
+
+#ifndef UT_MIMCORRECTIONHOST_H
+#define UT_MIMCORRECTIONHOST_H
+
+#include "mimcorrectionhost.h"
+#include "mapplication.h"
+#include <QtTest/QTest>
+#include <QObject>
+
+class MSceneWindow;
+
+class Ut_MImCorrectionHost : public QObject
+{
+    Q_OBJECT
+
+private:
+    MApplication *app;
+    MImCorrectionHost *m_subject;
+    MSceneWindow *parentWindow;
+
+private slots:
+    //! initialize application and class
+    void initTestCase();
+    void init();
+    void cleanup();
+    void cleanupTestCase();
+    void checkShowWidget();
+    void checkModes();
+    void checkCandidatesAndPreedit_data();
+    void checkCandidatesAndPreedit();
+    void checkSuggestion_data();
+    void checkSuggestion();
+    void checkPosition();
+};
+
+#endif
--- tests/ut_mimcorrectionhost/ut_mimcorrectionhost.pro
+++ tests/ut_mimcorrectionhost/ut_mimcorrectionhost.pro
+TEMPLATE = app
+CONFIG += meegotouch
+
+DEPENDPATH += .
+INCLUDEPATH += 	. \
+
+include(../common_check.pri)
+
+LIBS += -Wl,-rpath=/usr/lib/meego-im-plugins/ -lmeego-keyboard
+
+HEADERS += ut_mimcorrectionhost.h \
+
+SOURCES += ut_mimcorrectionhost.cpp \
+
--- tests/ut_mimkey/ut_mimkey.cpp
+++ tests/ut_mimkey/ut_mimkey.cpp
@@ -18,7 +18,6 @@
 
 #include "ut_mimkey.h"
 
-#include "mimabstractkey.h"
 #include "mimabstractkeyareastyle.h"
 #include "mimkey.h"
 #include "mimkeyarea.h"
@@ -31,14 +30,51 @@
 #include <QSignalSpy>
 #include <QDebug>
 
-Q_DECLARE_METATYPE(QList<Ut_KeyButton::DirectionPair>)
+Q_DECLARE_METATYPE(QList<Ut_MImKey::DirectionPair>)
+Q_DECLARE_METATYPE(Ut_MImKey::KeyList)
+Q_DECLARE_METATYPE(QList<Ut_MImKey::KeyTriple>)
+Q_DECLARE_METATYPE(QList<int>)
+
+namespace {
+
+    class ActiveKeyFinder
+            : public MImAbstractKeyVisitor
+    {
+    public:
+        bool found;
+        MImAbstractKey *findMe;
+        int visits;
+
+        explicit ActiveKeyFinder(MImAbstractKey *newFindMe = 0)
+            : found(false)
+            , findMe(newFindMe)
+            , visits(0)
+        {}
+
+        bool operator()(MImAbstractKey *key)
+        {
+            ++visits;
 
-void Ut_KeyButton::initTestCase()
+            found = (found || (key == findMe));
+            return found;
+        }
+    };
+
+    bool isActiveKeyState(MImAbstractKey::ButtonState state)
+    {
+        return ((state == MImAbstractKey::Pressed)
+                || (state == MImAbstractKey::Selected));
+    }
+}
+
+void Ut_MImKey::initTestCase()
 {
     qRegisterMetaType< QList<DirectionPair> >("QList<DirectionPair>");
+    qRegisterMetaType<KeyList>("KeyList");
+    qRegisterMetaType< QList<KeyTriple> >("QList<KeyTriple>");
 
     static int argc = 2;
-    static char *app_name[] = { (char*) "ut_keybutton",
+    static char *app_name[] = { (char*) "ut_mimkey",
                                 (char *) "-local-theme" };
 
     disableQtPlugins();
@@ -48,10 +84,10 @@
     style->initialize("", "", 0);
 
     parent = new QGraphicsWidget;
-    dataKey = createDataKey();
+    dataKey = createKeyModel();
 }
 
-void Ut_KeyButton::cleanupTestCase()
+void Ut_MImKey::cleanupTestCase()
 {
     delete style;
     delete dataKey;
@@ -60,18 +96,19 @@
     delete parent;
 }
 
-void Ut_KeyButton::init()
+void Ut_MImKey::init()
 {
     subject = new MImKey(*dataKey, *style, *parent);
 }
 
-void Ut_KeyButton::cleanup()
+void Ut_MImKey::cleanup()
 {
+    MImAbstractKey::resetActiveKeys();
     delete subject;
     subject = 0;
 }
 
-void Ut_KeyButton::testSetModifier_data()
+void Ut_MImKey::testSetModifier_data()
 {
     QTest::addColumn<bool>("shift");
     QTest::addColumn<QChar>("accent");
@@ -89,7 +126,7 @@
     QTest::newRow("shift, l'accent grave")          << true  << grave << QString(L'À');
 }
 
-void Ut_KeyButton::testSetModifier()
+void Ut_MImKey::testSetModifier()
 {
     QFETCH(bool, shift);
     QFETCH(QChar, accent);
@@ -99,12 +136,12 @@
     QCOMPARE(subject->label(), expectedLabel);
 }
 
-void Ut_KeyButton::testKey()
+void Ut_MImKey::testKey()
 {
-    QCOMPARE(&subject->key(), dataKey);
+    QCOMPARE(&subject->model(), dataKey);
 }
 
-void Ut_KeyButton::testBinding()
+void Ut_MImKey::testBinding()
 {
     bool shift = false;
     subject->setModifiers(shift);
@@ -115,7 +152,7 @@
     QCOMPARE(&subject->binding(), dataKey->binding(shift));
 }
 
-void Ut_KeyButton::testIsDead()
+void Ut_MImKey::testIsDead()
 {
     MImKeyModel *key = new MImKeyModel;
     MImKeyBinding *binding = new MImKeyBinding;
@@ -133,7 +170,7 @@
     delete key;
 }
 
-void Ut_KeyButton::testTouchPointCount_data()
+void Ut_MImKey::testTouchPointCount_data()
 {
     QTest::addColumn<int>("initialCount");
     QTest::addColumn< QList<DirectionPair> >("countDirectionList");
@@ -172,7 +209,7 @@
         << MImKey::touchPointLimit() - 2 << MImAbstractKey::Pressed;
 }
 
-void Ut_KeyButton::testTouchPointCount()
+void Ut_MImKey::testTouchPointCount()
 {
     QFETCH(int, initialCount);
     QFETCH(QList<DirectionPair>, countDirectionList);
@@ -201,7 +238,127 @@
     QCOMPARE(subject->state(), expectedButtonState);
 }
 
-MImKeyModel *Ut_KeyButton::createDataKey()
+void Ut_MImKey::testResetTouchPointCount()
+{
+    QCOMPARE(subject->touchPointCount(), 0);
+
+    for (int idx = 0; idx < 3; ++idx) {
+        subject->increaseTouchPointCount();
+    }
+
+    QCOMPARE(subject->touchPointCount(), 3);
+
+    subject->resetTouchPointCount();
+    QCOMPARE(subject->touchPointCount(), 0);
+}
+
+void Ut_MImKey::testActiveKeys_data()
+{
+    QTest::addColumn<KeyList>("availableKeys");
+    QTest::addColumn< QList<KeyTriple> >("keyControlSequence");
+    QTest::addColumn< QList<int> >("expectedActiveKeys");
+
+    QTest::newRow("single key")
+        << (KeyList() << createKey())
+        << (QList<KeyTriple>() << KeyTriple(0, MImAbstractKey::Pressed, 0))
+        << (QList<int>() << 0);
+
+    QTest::newRow("two keys")
+        << (KeyList() << createKey() << createKey())
+        << (QList<KeyTriple>() << KeyTriple(0, MImAbstractKey::Pressed, 0)
+                               << KeyTriple(1, MImAbstractKey::Selected, 1)
+                               << KeyTriple(0, MImAbstractKey::Normal, 1))
+        << (QList<int>() << 1);
+
+    QTest::newRow("last active key")
+        << (KeyList() << createKey() << createKey() << createKey())
+        << (QList<KeyTriple>() << KeyTriple(0, MImAbstractKey::Pressed, 0)
+                               << KeyTriple(1, MImAbstractKey::Selected, 1)
+                               << KeyTriple(2, MImAbstractKey::Normal, 1)
+                               << KeyTriple(1, MImAbstractKey::Normal, 0)
+                               << KeyTriple(2, MImAbstractKey::Pressed, 2)
+                               << KeyTriple(0, MImAbstractKey::Normal, 2)
+                               << KeyTriple(2, MImAbstractKey::Normal, -1))
+        << (QList<int>());
+}
+
+void Ut_MImKey::testActiveKeys()
+{
+    QFETCH(KeyList, availableKeys);
+    QFETCH(QList<KeyTriple>, keyControlSequence);
+    QFETCH(QList<int>, expectedActiveKeys);
+    const MImAbstractKey *const noKey = 0;
+
+    foreach (const KeyTriple &triple, keyControlSequence) {
+        availableKeys.at(triple.index)->setDownState(isActiveKeyState(triple.state));
+        if (triple.lastActiveIndex > -1) {
+            QCOMPARE(MImAbstractKey::lastActiveKey(),
+                     availableKeys.at(triple.lastActiveIndex));
+        } else {
+            QCOMPARE(MImAbstractKey::lastActiveKey(), noKey);
+        }
+    }
+
+    foreach (int idx, expectedActiveKeys) {
+        ActiveKeyFinder finder(availableKeys.at(idx));
+        MImAbstractKey::visitActiveKeys(&finder);
+
+        QVERIFY(finder.found);
+    }
+
+    // Verify that remaining keys (those not listed in expectedActiveKeys)
+    // are not in MImAbstractKey::activeKeys:
+    for (int idx = 0; idx < availableKeys.count(); ++idx) {
+        if (!expectedActiveKeys.contains(idx)) {
+            ActiveKeyFinder finder(availableKeys.at(idx));
+            MImAbstractKey::visitActiveKeys(&finder);
+
+            QVERIFY(not finder.found);
+        }
+    }
+}
+
+void Ut_MImKey::testResetActiveKeys()
+{
+    ActiveKeyFinder finder;
+    MImAbstractKey::visitActiveKeys(&finder);
+    QCOMPARE(finder.visits, 0);
+
+    KeyList keys;
+    keys << createKey(true) << createKey(false) << createKey(true);
+    MImAbstractKey::visitActiveKeys(&finder);
+    QCOMPARE(finder.visits, 2);
+
+    MImAbstractKey::resetActiveKeys();
+}
+
+void Ut_MImKey::testVisitActiveKeys()
+{
+    KeyList keys;
+    keys << createKey(true) << createKey(true);
+
+    MImKeyBinding *b = new MImKeyBinding;
+    b->keyAction = MImKeyBinding::ActionShift;
+    MImKeyModel *model = new MImKeyModel;
+    model->bindings[MImKeyModel::NoShift] = b;
+    MImKey *shift = new MImKey(*model, *style, *parent);
+    shift->setDownState(true);
+    keys << shift;
+
+    SpecialKeyFinder finder;
+    MImAbstractKey::visitActiveKeys(&finder);
+    QCOMPARE(finder.shiftKey(), shift);
+    QCOMPARE(finder.visits(), keys.count());
+}
+
+MImKey *Ut_MImKey::createKey(bool state)
+{
+    MImKey *key = new MImKey(*dataKey, *style, *parent);
+    key->setDownState(state);
+    return key;
+}
+
+MImKeyModel *Ut_MImKey::createKeyModel()
 {
     MImKeyModel *key = new MImKeyModel;
 
@@ -210,12 +367,14 @@
     binding1->dead = false;
     binding1->accents = "`´^¨";
     binding1->accented_labels = QString(L'à') + L'á' + L'á' + L'â' + L'ä';
+    binding1->keyAction = MImKeyBinding::ActionInsert;
 
     MImKeyBinding *binding2 = new MImKeyBinding;
     binding2->keyLabel = "A";
     binding2->dead = false;
     binding2->accents = "`´^¨";
     binding2->accented_labels = QString(L'À') + L'Á' + L'Â' + L'Ä';
+    binding2->keyAction = MImKeyBinding::ActionInsert;
 
     key->bindings[MImKeyModel::NoShift] = binding1;
     key->bindings[MImKeyModel::Shift] = binding2;
@@ -223,4 +382,4 @@
     return key;
 }
 
-QTEST_APPLESS_MAIN(Ut_KeyButton);
+QTEST_APPLESS_MAIN(Ut_MImKey);
--- tests/ut_mimkey/ut_mimkey.h
+++ tests/ut_mimkey/ut_mimkey.h
@@ -16,8 +16,8 @@
 
 
 
-#ifndef UT_KEYBUTTON_H
-#define UT_KEYBUTTON_H
+#ifndef UT_MIMKEY_H
+#define UT_MIMKEY_H
 
 #include <mimabstractkey.h>
 
@@ -27,11 +27,12 @@
 
 class MApplication;
 class MImKeyModel;
+class MImKey;
 class MImAbstractKeyAreaStyleContainer;
 class QGraphicsItem;
 class KeyboardData;
 
-class Ut_KeyButton: public QObject
+class Ut_MImKey: public QObject
 {
     Q_OBJECT
 
@@ -41,7 +42,30 @@
         Up
     };
 
+    struct KeyTriple {
+        int index;
+        MImAbstractKey::ButtonState state;
+        int lastActiveIndex;
+
+        // Needed for metatype registration:
+        KeyTriple()
+            : index(-1)
+            , state(MImAbstractKey::Normal)
+            , lastActiveIndex(-1)
+        {}
+
+        KeyTriple(int newIndex,
+                  MImAbstractKey::ButtonState newState,
+                  int newLastActiveIndex)
+            : index(newIndex)
+            , state(newState)
+            , lastActiveIndex(newLastActiveIndex)
+        {}
+
+    };
+
     typedef QPair<Direction, bool> DirectionPair;
+    typedef QList<MImAbstractKey *> KeyList;
 
 private:
 
@@ -65,12 +89,20 @@
 
     void testTouchPointCount_data();
     void testTouchPointCount();
+    void testResetTouchPointCount();
+
+    void testActiveKeys_data();
+    void testActiveKeys();
+    void testResetActiveKeys();
+    void testVisitActiveKeys();
 
 private:
-    MImKeyModel *createDataKey();
+    MImKey *createKey(bool state = false);
+    MImKeyModel *createKeyModel();
 };
 
-Q_DECLARE_METATYPE(Ut_KeyButton::DirectionPair)
+Q_DECLARE_METATYPE(Ut_MImKey::DirectionPair)
+Q_DECLARE_METATYPE(Ut_MImKey::KeyTriple)
 Q_DECLARE_METATYPE(MImAbstractKey::ButtonState)
 
 #endif
--- tests/ut_mimkey/ut_mimkey.pro
+++ tests/ut_mimkey/ut_mimkey.pro
@@ -16,6 +16,7 @@
            $$STYLE_HEADERS
 
 SOURCES += ut_mimkey.cpp \
+           $$WIDGETS_DIR/mimabstractkey.cpp \
            $$WIDGETS_DIR/mimkey.cpp \
            $$COMMON_DIR/mimkeymodel.cpp \
            $$COMMON_DIR/keyevent.cpp
--- tests/ut_mimtoolbar/ut_mimtoolbar.cpp
+++ tests/ut_mimtoolbar/ut_mimtoolbar.cpp
@@ -82,7 +82,7 @@
     qRegisterMetaType<CopyPasteState>("CopyPasteState");
     LayoutsManager::createInstance();
 
-    createMScene(new MPlainWindow); // also create singleton
+    sceneWindow = createMSceneWindow(new MPlainWindow); // also create singleton MPlainWindow
 
     ToolbarFileName = QCoreApplication::applicationDirPath() + ToolbarFileName;
     QVERIFY(QFile::exists(ToolbarFileName));
@@ -113,6 +113,7 @@
 {
     toolbarData.clear();
     LayoutsManager::destroyInstance();
+    delete sceneWindow;
     delete MPlainWindow::instance();
     delete app;
     app = 0;
--- tests/ut_mimtoolbar/ut_mimtoolbar.h
+++ tests/ut_mimtoolbar/ut_mimtoolbar.h
@@ -28,6 +28,7 @@
 class MImToolbar;
 class QKeyEvent;
 class MWidget;
+class MSceneWindow;
 
 class Ut_MImToolbar : public QObject
 {
@@ -36,6 +37,7 @@
 private:
     MApplication *app;
     MImToolbar *m_subject;
+    MSceneWindow *sceneWindow;
     int keyEvents;
     QSharedPointer<MToolbarData> toolbarData;
 
--- tests/ut_mimwordlist
+++ tests/ut_mimwordlist
+(directory)
--- tests/ut_mimwordlist/ut_mimwordlist.cpp
+++ tests/ut_mimwordlist/ut_mimwordlist.cpp
+/* * This file is part of meego-keyboard *
+ *
+ * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ * All rights reserved.
+ * Contact: Nokia Corporation (directui at nokia.com)
+ *
+ * If you have questions regarding the use of this file, please contact
+ * Nokia at directui at nokia.com.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it 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.
+ */
+
+
+
+#include "ut_mimwordlist.h"
+#include "mimwordlist.h"
+#include "mimwordlistitem.h"
+#include "mplainwindow.h"
+#include "utils.h"
+#include <QtTest/QTest>
+#include <QObject>
+#include <QDebug>
+#include <QStringList>
+#include <QSignalSpy>
+#include <QGraphicsSceneMouseEvent>
+#include <MSceneManager>
+
+void Ut_MImWordList::initTestCase()
+{
+    static int dummyArgc = 2;
+    static char *dummyArgv[2] = { (char *) "./ut_mimcorrectioncandidatewidget",
+                                  (char *) "-local-theme" };
+    disableQtPlugins();
+    app = new MApplication(dummyArgc, dummyArgv);
+
+    // MImWordList uses this internally
+    new MPlainWindow;
+    if (MPlainWindow::instance()->orientationAngle() != M::Angle0) {
+        MPlainWindow::instance()->setOrientationAngle(M::Angle0);
+        QTest::qWait(1000);
+    }
+}
+
+
+void Ut_MImWordList::init()
+{
+    m_subject = new MImWordList;
+
+    if (MPlainWindow::instance()->orientationAngle() != M::Angle0) {
+        MPlainWindow::instance()->setOrientationAngle(M::Angle0);
+        QTest::qWait(1000);
+    }
+}
+
+
+void Ut_MImWordList::cleanup()
+{
+    delete m_subject;
+}
+
+void Ut_MImWordList::cleanupTestCase()
+{
+    delete MPlainWindow::instance();
+    delete app;
+    app = 0;
+}
+
+void Ut_MImWordList::testCandidates_data()
+{
+    QTest::addColumn<QStringList>("candidates");
+
+    QTest::newRow("test1")  << (QStringList() << "1" << "2" << "3" << "4" << "5");
+    QTest::newRow("test2")  << (QStringList() << "abc" << "def" << "ghi" << "jfk" << "lmn");
+}
+
+void Ut_MImWordList::testCandidates()
+{
+    QFETCH(QStringList, candidates);
+    m_subject->setCandidates(candidates);
+    QCOMPARE(m_subject->candidates(), candidates);
+}
+
+void Ut_MImWordList::testSelect()
+{
+    QStringList candidates = (QStringList() << "abc" << "def" << "ghi" << "jfk" << "lmn");
+    m_subject->setCandidates(candidates);
+    m_subject->appear();
+    QTest::qWait(600);
+    QVERIFY(m_subject->isVisible());
+    if (m_subject->sceneWindowState() == MSceneWindow::Appearing) {
+        QSKIP("word list is during appearing animation", SkipSingle);
+    }
+    QSignalSpy clickSpy(m_subject, SIGNAL(candidateClicked(const QString &)));
+
+    m_subject->candidateItems[2]->click();
+
+    QCOMPARE(clickSpy.count(), 1);
+    QCOMPARE(clickSpy.first().first().toString(), QString("ghi"));
+    m_subject->disappear();
+}
+
+QTEST_APPLESS_MAIN(Ut_MImWordList);
+
--- tests/ut_mimwordlist/ut_mimwordlist.h
+++ tests/ut_mimwordlist/ut_mimwordlist.h
+/* * This file is part of meego-keyboard *
+ *
+ * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ * All rights reserved.
+ * Contact: Nokia Corporation (directui at nokia.com)
+ *
+ * If you have questions regarding the use of this file, please contact
+ * Nokia at directui at nokia.com.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it 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.
+ */
+
+
+
+#ifndef UT_MIMWORDLIST_H
+#define UT_MIMWORDLIST_H
+
+#include "mimwordlist.h"
+#include "mapplication.h"
+#include <QtTest/QTest>
+#include <QObject>
+
+class Ut_MImWordList : public QObject
+{
+    Q_OBJECT
+
+private:
+    MApplication *app;
+    MImWordList *m_subject;
+
+private slots:
+    //! initialize application and class
+    void initTestCase();
+    void init();
+    void cleanup();
+    void cleanupTestCase();
+    void testCandidates_data();
+    void testCandidates();
+    void testSelect();
+};
+
+#endif
--- tests/ut_mimwordlist/ut_mimwordlist.pro
+++ tests/ut_mimwordlist/ut_mimwordlist.pro
+TEMPLATE = app
+CONFIG += meegotouch
+
+DEPENDPATH += .
+INCLUDEPATH += 	. \
+
+include(../common_check.pri)
+
+LIBS += -Wl,-rpath=/usr/lib/meego-im-plugins/ -lmeego-keyboard
+
+HEADERS += ut_mimwordlist.h \
+
+SOURCES += ut_mimwordlist.cpp \
+
--- tests/ut_mimwordtracker
+++ tests/ut_mimwordtracker
+(directory)
--- tests/ut_mimwordtracker/ut_mimwordtracker.cpp
+++ tests/ut_mimwordtracker/ut_mimwordtracker.cpp
+/* * This file is part of meego-keyboard *
+ *
+ * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ * All rights reserved.
+ * Contact: Nokia Corporation (directui at nokia.com)
+ *
+ * If you have questions regarding the use of this file, please contact
+ * Nokia at directui at nokia.com.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it 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.
+ */
+
+
+
+#include "ut_mimwordtracker.h"
+#include "mimwordtracker.h"
+#include "mplainwindow.h"
+#include "utils.h"
+#include <QtTest/QTest>
+#include <QObject>
+#include <QDebug>
+#include <QStringList>
+#include <QSignalSpy>
+#include <QGraphicsSceneMouseEvent>
+#include <MSceneManager>
+
+void Ut_MImWordTracker::initTestCase()
+{
+    static int dummyArgc = 2;
+    static char *dummyArgv[2] = { (char *) "./ut_mimcorrectioncandidatewidget",
+                                  (char *) "-local-theme" };
+    disableQtPlugins();
+    app = new MApplication(dummyArgc, dummyArgv);
+
+    // MImWordTracker uses this internally
+    new MPlainWindow;
+    if (MPlainWindow::instance()->orientationAngle() != M::Angle0) {
+        MPlainWindow::instance()->setOrientationAngle(M::Angle0);
+        QTest::qWait(1000);
+    }
+    parentWindow = new MSceneWindow;
+    parentWindow->setManagedManually(true); // we want the scene window to remain in origin
+    // Adds scene window to scene.
+    MPlainWindow::instance()->sceneManager()->appearSceneWindowNow(parentWindow);
+}
+
+
+void Ut_MImWordTracker::init()
+{
+    m_subject = new MImWordTracker(parentWindow);
+
+    if (MPlainWindow::instance()->orientationAngle() != M::Angle0) {
+        MPlainWindow::instance()->setOrientationAngle(M::Angle0);
+        QTest::qWait(1000);
+    }
+}
+
+
+void Ut_MImWordTracker::cleanup()
+{
+    delete m_subject;
+}
+
+void Ut_MImWordTracker::cleanupTestCase()
+{
+    delete parentWindow;
+    delete MPlainWindow::instance();
+    delete app;
+    app = 0;
+}
+
+void Ut_MImWordTracker::testCandidate_data()
+{
+    QTest::addColumn<QString>("candidate");
+
+    QTest::newRow("test1")  << "1";
+    QTest::newRow("test2")  << "abc";
+}
+
+void Ut_MImWordTracker::testCandidate()
+{
+    QFETCH(QString, candidate);
+    m_subject->setCandidate(candidate);
+    QCOMPARE(m_subject->candidate(), candidate);
+}
+
+void Ut_MImWordTracker::testAppearAndDisappear()
+{
+    m_subject->appear(false);
+    QVERIFY(m_subject->isVisible());
+    m_subject->disappear(false);
+    QVERIFY(!m_subject->isVisible());
+}
+
+void Ut_MImWordTracker::testSelect()
+{
+    QString candidate("test");
+    m_subject->appear(false);
+    QVERIFY(m_subject->isVisible());
+    m_subject->setCandidate(candidate);
+
+    QSignalSpy clickSpy(m_subject, SIGNAL(candidateClicked(const QString &)));
+    m_subject->select();
+    QCOMPARE(clickSpy.count(), 1);
+    QCOMPARE(clickSpy.first().first().toString(), candidate);
+}
+
+void Ut_MImWordTracker::testLongTap()
+{
+    QString candidate("test");
+    m_subject->appear(false);
+    QVERIFY(m_subject->isVisible());
+    m_subject->setCandidate(candidate);
+
+    QSignalSpy clickSpy(m_subject, SIGNAL(longTapped()));
+    m_subject->longTap();
+    QCOMPARE(clickSpy.count(), 1);
+}
+
+
+QTEST_APPLESS_MAIN(Ut_MImWordTracker);
+
--- tests/ut_mimwordtracker/ut_mimwordtracker.h
+++ tests/ut_mimwordtracker/ut_mimwordtracker.h
+/* * This file is part of meego-keyboard *
+ *
+ * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ * All rights reserved.
+ * Contact: Nokia Corporation (directui at nokia.com)
+ *
+ * If you have questions regarding the use of this file, please contact
+ * Nokia at directui at nokia.com.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it 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.
+ */
+
+
+
+#ifndef UT_MIMWORDTRACKER_H
+#define UT_MIMWORDTRACKER_H
+
+#include "mimwordtracker.h"
+#include "mapplication.h"
+#include <QtTest/QTest>
+#include <QObject>
+
+class MSceneWindow;
+
+class Ut_MImWordTracker : public QObject
+{
+    Q_OBJECT
+
+private:
+    MApplication *app;
+    MImWordTracker *m_subject;
+    MSceneWindow *parentWindow;
+
+private slots:
+    //! initialize application and class
+    void initTestCase();
+    void init();
+    void cleanup();
+    void cleanupTestCase();
+    void testCandidate_data();
+    void testCandidate();
+    void testAppearAndDisappear();
+    void testSelect();
+    void testLongTap();
+};
+
+#endif
--- tests/ut_mimwordtracker/ut_mimwordtracker.pro
+++ tests/ut_mimwordtracker/ut_mimwordtracker.pro
+TEMPLATE = app
+CONFIG += meegotouch
+
+DEPENDPATH += .
+INCLUDEPATH += 	. \
+
+include(../common_check.pri)
+
+LIBS += -Wl,-rpath=/usr/lib/meego-im-plugins/ -lmeego-keyboard
+
+HEADERS += ut_mimwordtracker.h \
+
+SOURCES += ut_mimwordtracker.cpp \
+
--- tests/ut_mkeyboardhost/minputmethodhoststub.cpp
+++ tests/ut_mkeyboardhost/minputmethodhoststub.cpp
@@ -39,6 +39,7 @@
 
 void MInputMethodHostStub::clear()
 {
+    preeditRectangleReturnValue = QRect();
     preedit.clear();
     commit.clear();
     qDeleteAll(keyEvents);
@@ -135,7 +136,7 @@
 QRect MInputMethodHostStub::preeditRectangle(bool &valid)
 {
     valid = true;
-    return QRect();
+    return preeditRectangleReturnValue;
 }
 
 bool MInputMethodHostStub::autoCapitalizationEnabled(bool &val)
--- tests/ut_mkeyboardhost/minputmethodhoststub.h
+++ tests/ut_mkeyboardhost/minputmethodhoststub.h
@@ -96,6 +96,7 @@
     bool textSelected;
     bool keyRedirectionEnabled;
     MInputMethod::InputModeIndicator indicator;
+    QRect preeditRectangleReturnValue;
 };
 
 #endif
--- tests/ut_mkeyboardhost/ut_mkeyboardhost.cpp
+++ tests/ut_mkeyboardhost/ut_mkeyboardhost.cpp
@@ -16,7 +16,7 @@
 
 
 
-#include <mimcorrectioncandidatewidget.h>
+#include <mimcorrectionhost.h>
 #include <mvirtualkeyboard.h>
 #include <mhardwarekeyboard.h>
 #include <mkeyboardhost.h>
@@ -340,9 +340,16 @@
     inputMethodHost->clear();
 
     subject->handleKeyClick(KeyEvent("a"));
-    subject->handleKeyClick(KeyEvent("\n", QEvent::KeyRelease, Qt::Key_Return));
+    subject->handleKeyClick(KeyEvent("\r", QEvent::KeyRelease, Qt::Key_Return));
     QVERIFY(subject->preedit.isEmpty());
-    QCOMPARE(inputMethodHost->commit, QString("a\n"));
+    QCOMPARE(inputMethodHost->commit, QString("a"));
+    QCOMPARE(inputMethodHost->keyEvents.count(), 2);
+    QCOMPARE(inputMethodHost->keyEvents[0]->text(), QString("\r"));
+    QCOMPARE(inputMethodHost->keyEvents[0]->key(), static_cast<int>(Qt::Key_Return));
+    QCOMPARE(inputMethodHost->keyEvents[0]->type(), QEvent::KeyPress);
+    QCOMPARE(inputMethodHost->keyEvents[1]->text(), QString("\r"));
+    QCOMPARE(inputMethodHost->keyEvents[1]->key(), static_cast<int>(Qt::Key_Return));
+    QCOMPARE(inputMethodHost->keyEvents[1]->type(), QEvent::KeyRelease);
     inputMethodHost->clear();
 
     subject->handleKeyClick(KeyEvent("a"));
@@ -357,6 +364,19 @@
     QCOMPARE(subject->imCorrectionEngine->correctionEnabled(), false);
     QCOMPARE(subject->correctionEnabled, false);
 
+    subject->handleKeyClick(KeyEvent("a"));
+    subject->handleKeyClick(KeyEvent("\r", QEvent::KeyRelease, Qt::Key_Return));
+    QVERIFY(subject->preedit.isEmpty());
+    QCOMPARE(inputMethodHost->commit, QString("a"));
+    QCOMPARE(inputMethodHost->keyEvents.count(), 2);
+    QCOMPARE(inputMethodHost->keyEvents[0]->text(), QString("\r"));
+    QCOMPARE(inputMethodHost->keyEvents[0]->key(), static_cast<int>(Qt::Key_Return));
+    QCOMPARE(inputMethodHost->keyEvents[0]->type(), QEvent::KeyPress);
+    QCOMPARE(inputMethodHost->keyEvents[1]->text(), QString("\r"));
+    QCOMPARE(inputMethodHost->keyEvents[1]->key(), static_cast<int>(Qt::Key_Return));
+    QCOMPARE(inputMethodHost->keyEvents[1]->type(), QEvent::KeyRelease);
+    inputMethodHost->clear();
+
     subject->handleKeyClick(KeyEvent("m"));
     subject->handleKeyClick(KeyEvent("a"));
     QCOMPARE(inputMethodHost->commit, QString("ma"));
@@ -382,7 +402,7 @@
     QList<Qt::Key> expectedKeys;
 
     testData << KeyEvent("\b", QEvent::KeyRelease, Qt::Key_Backspace)
-             << KeyEvent("\n", QEvent::KeyRelease, Qt::Key_Return)
+             << KeyEvent("\r", QEvent::KeyRelease, Qt::Key_Return)
              << KeyEvent(" ", QEvent::KeyRelease, Qt::Key_Space);
     expectedKeys << Qt::Key_Backspace << Qt::Key_Return << Qt::Key_Space;
     QVERIFY(testData.count() == expectedKeys.count());
@@ -541,7 +561,7 @@
 
     // press and release backspace before timeout will only delete one character,
     subject->handleKeyPress(press);
-    QVERIFY(subject->backSpaceTimer.isActive());
+    QVERIFY(subject->backspaceTimer.isActive());
     subject->handleKeyRelease(release);
     subject->handleKeyClick(release);
     subject->update();
@@ -550,15 +570,15 @@
 
     // but hold backspace longer than timeout, will delete the whole preedit.
     subject->handleKeyPress(press);
-    int interval = subject->backSpaceTimer.interval();
+    int interval = subject->backspaceTimer.interval();
     QTest::qWait(interval / 2);
-    QVERIFY(subject->backSpaceTimer.isActive());
+    QVERIFY(subject->backspaceTimer.isActive());
     subject->update();
     QVERIFY(subject->vkbWidget->shiftStatus() == ModifierClearState);
     QTest::qWait((interval / 2) + 50);
     // final state: preedit(""), shift state:on, after holding backspace enough time.
     QVERIFY(subject->preedit.isEmpty());
-    QVERIFY(!subject->backSpaceTimer.isActive());
+    QVERIFY(!subject->backspaceTimer.isActive());
     inputMethodHost->cursorPos = 13;
     subject->update();
     QVERIFY(subject->vkbWidget->shiftStatus() == ModifierLatchedState);
@@ -656,6 +676,34 @@
     QCOMPARE(inputMethodHost->keyEvents.at(1)->type(), QEvent::KeyRelease);
 }
 
+void Ut_MKeyboardHost::testSendString()
+{
+    QString testString("bacon");
+
+    inputMethodHost->clear();
+
+    subject->sendString(testString);
+    QCOMPARE(inputMethodHost->sendCommitStringCalls, 1);
+    QCOMPARE(inputMethodHost->commit, testString);
+}
+
+void Ut_MKeyboardHost::testSendStringFromToolbar()
+{
+    QString preeditString("delicious");
+    QString toolbarString("bacon");
+
+    inputMethodHost->clear();
+    subject->setPreedit(preeditString);
+    subject->sendStringFromToolbar(toolbarString);
+    QCOMPARE(inputMethodHost->sendCommitStringCalls, 2);
+    QCOMPARE(inputMethodHost->commit, preeditString+toolbarString);
+
+    inputMethodHost->clear();
+    subject->sendStringFromToolbar(toolbarString);
+    QCOMPARE(inputMethodHost->sendCommitStringCalls, 1);
+    QCOMPARE(inputMethodHost->commit, toolbarString);
+}
+
 QRegion Ut_MKeyboardHost::region(RegionType type, int index)
 {
     switch(type) {
@@ -738,12 +786,13 @@
     // In opaque mode, candidate widget has its own window, so no regions are sent to kbhost.
 #ifndef DUI_IM_DISABLE_TRANSLUCENCY
     // Ditto for correction candidate widget
-    subject->correctionCandidateWidget->showWidget();
+    subject->correctionHost->setCandidates((QStringList() << "abc" << "def"));
+    subject->correctionHost->showCorrectionWidget();
     ++c1;
     QCOMPARE(inputMethodHost->setScreenRegionCalls, c1);
     QCOMPARE(inputMethodHost->setInputMethodAreaCalls, c2);
 
-    subject->correctionCandidateWidget->hide();
+    subject->correctionHost->hideCorrectionWidget();
     ++c1;
     QCOMPARE(inputMethodHost->setScreenRegionCalls, c1);
     QCOMPARE(inputMethodHost->setInputMethodAreaCalls, c2);
@@ -1592,5 +1641,55 @@
     QCOMPARE(gRequestLanguageNotificationCallCount, expectedCallCount);
 }
 
+
+void Ut_MKeyboardHost::testAutoPunctuation_data()
+{
+    QTest::addColumn<QChar>("character");
+    QTest::addColumn<bool>("autopunctuated");
+
+    QTest::newRow(".") << QChar('.') << true;
+    QTest::newRow(",") << QChar(',') << true;
+    QTest::newRow("!") << QChar('!') << true;
+    QTest::newRow("?") << QChar('?') << true;
+    QTest::newRow("a") << QChar('a') << false;
+}
+
+void Ut_MKeyboardHost::testAutoPunctuation()
+{
+    QFETCH(QChar, character);
+    QFETCH(bool, autopunctuated);
+
+    MGConfItem config(InputMethodCorrectionSetting);
+    config.set(QVariant(true));
+
+    if (subject->imCorrectionEngine) {
+        MImEngineWordsInterfaceFactory::instance()->deleteEngine(subject->imCorrectionEngine);
+    }
+
+    DummyDriverMkh *engine(new DummyDriverMkh);
+    subject->imCorrectionEngine = engine;
+    QStringList candidates;
+    candidates << "foo" << "foobar";
+    subject->setPreedit("fo");
+    engine->setCandidates(candidates);
+    engine->setSuggestedCandidateIndexReturnValue(0);
+    inputMethodHost->preeditRectangleReturnValue = QRect(0, 0, 100, 100);
+    subject->handleKeyClick(KeyEvent("o"));
+    subject->handleKeyClick(KeyEvent(" ", QEvent::KeyRelease, Qt::Key_Space));
+    inputMethodHost->commit.clear();
+    inputMethodHost->preedit.clear();
+    subject->handleKeyClick(KeyEvent(character, QEvent::KeyRelease));
+
+    if (autopunctuated) {
+        QCOMPARE(inputMethodHost->commit, QString(character) + " ");
+        QCOMPARE(inputMethodHost->sendKeyEventCalls, 1);
+        QCOMPARE(inputMethodHost->keyEvents[0]->key(), static_cast<int>(Qt::Key_Backspace));
+    } else {
+        QCOMPARE(inputMethodHost->commit, QString());
+        QCOMPARE(inputMethodHost->preedit, QString(character));
+        QCOMPARE(inputMethodHost->sendKeyEventCalls, 0);
+    }
+}
+
 QTEST_APPLESS_MAIN(Ut_MKeyboardHost);
 
--- tests/ut_mkeyboardhost/ut_mkeyboardhost.h
+++ tests/ut_mkeyboardhost/ut_mkeyboardhost.h
@@ -60,6 +60,9 @@
     void testCopyPaste();
     void testPlusMinus();
 
+    void testSendString();
+    void testSendStringFromToolbar();
+
     void testRegionSignals();
 
     void testSetState_data();
@@ -101,6 +104,9 @@
     void testShowLanguageNotification_data();
     void testShowLanguageNotification();
 
+    void testAutoPunctuation_data();
+    void testAutoPunctuation();
+
 private:
     void rotateToAngle(M::OrientationAngle);
     void triggerAutoCaps();
--- tests/ut_mvirtualkeyboard/ut_mvirtualkeyboard.cpp
+++ tests/ut_mvirtualkeyboard/ut_mvirtualkeyboard.cpp
@@ -1014,7 +1014,7 @@
     // Those adjustments dont work for reaction maps:
     s->setButtonBoundingRectTopAdjustment(0);
     s->setButtonBoundingRectBottomAdjustment(0);
-    area->updateButtonGeometriesForWidth(area->geometry().width());
+    area->updateKeyGeometries(area->geometry().width());
 }
 
 QTEST_APPLESS_MAIN(Ut_MVirtualKeyboard);
--- tests/ut_notification/test.css
+++ tests/ut_notification/test.css
@@ -18,7 +18,8 @@
 
     close-button-margin : 10;
 
-    notification-font: "Nokia Sans Light" 42;
+    notification-font: $FONT_KEYBOARD;
+    notification-font-size: 42;
     notification-border-color: #00FF00;
     notification-background-color: #FF0000;
     notification-text-color: #0000FF;
--- tests/ut_notification/ut_notification.cpp
+++ tests/ut_notification/ut_notification.cpp
@@ -125,15 +125,11 @@
 //This test depends on values in test.css
 void Ut_Notification::testCSS()
 {
-    //construct font in the same way as mstylesheet.cpp
-    QFont expected("Nokia Sans Light");
-    expected.setPixelSize(42);
-
-    QVERIFY(subject->background == QColor(Qt::red));
-    QVERIFY(subject->border == QColor(Qt::green));
-    QVERIFY(subject->textColor == QColor(Qt::blue));
-    QVERIFY(subject->opacity == 1.0);
-    QCOMPARE(subject->font, expected);
+    QCOMPARE(subject->background, QColor(Qt::red));
+    QCOMPARE(subject->border, QColor(Qt::green));
+    QCOMPARE(subject->textColor, QColor(Qt::blue));
+    QCOMPARE(subject->opacity, 1.0);
+    QCOMPARE(subject->font.pixelSize(), 42);
 }
 
 QTEST_APPLESS_MAIN(Ut_Notification);
--- tests/ut_sharedhandlearea/ut_sharedhandlearea.cpp
+++ tests/ut_sharedhandlearea/ut_sharedhandlearea.cpp
@@ -42,7 +42,7 @@
     disableQtPlugins();
     app = new MApplication(dummyArgc, dummyArgv);
 
-    createMScene(new MPlainWindow); // also create singleton
+    sceneWindow = createMSceneWindow(new MPlainWindow); // also create singleton
 
     parent = new QGraphicsWidget;
     MPlainWindow::instance()->scene()->addItem(parent);
@@ -51,6 +51,7 @@
 void Ut_SharedHandleArea::cleanupTestCase()
 {
     delete parent;
+    delete sceneWindow;
     delete MPlainWindow::instance();
     delete app;
 }
--- tests/ut_sharedhandlearea/ut_sharedhandlearea.h
+++ tests/ut_sharedhandlearea/ut_sharedhandlearea.h
@@ -26,6 +26,7 @@
 #include <mimtoolbar.h>
 
 class MApplication;
+class MSceneWindow;
 class SharedHandleArea;
 
 class Ut_SharedHandleArea : public QObject
@@ -33,6 +34,7 @@
     Q_OBJECT
 
     MApplication *app;
+    MSceneWindow *sceneWindow;
     QPointer<SharedHandleArea> subject;
     QPointer<QGraphicsWidget> parent;
     QPointer<MImToolbar> imToolbar;
--- tests/utils/utils.cpp
+++ tests/utils/utils.cpp
@@ -21,6 +21,7 @@
 #include <MApplication>
 #include <mplainwindow.h>
 #include <MSceneWindow>
+#include <MSceneManager>
 #endif
 #include <QObject>
 #include <QTimer>
@@ -55,13 +56,15 @@
 }
 
 
-// Create graphics scene
+// Create a scene window, set it to manual managed, and appear it.
 #ifdef MEEGOTOUCH
-void createMScene(MPlainWindow *w)
+MSceneWindow * createMSceneWindow(MPlainWindow *w)
 {
-    w->show();
     MSceneWindow *sceneWindow = new MSceneWindow;
-    sceneWindow->appear(w);
+    sceneWindow->setManagedManually(true); // we want the scene window to remain in origin
+    w->sceneManager()->appearSceneWindowNow(sceneWindow);
+    w->setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
+    return sceneWindow;
 }
 #endif
 
--- tests/utils/utils.h
+++ tests/utils/utils.h
@@ -17,7 +17,10 @@
 
 #ifndef UTILS_H
 
+#include "mimabstractkey.h"
+
 class MPlainWindow;
+class MSceneWindow;
 class QObject;
 
 // Disable loading of MInputContext and QtMaemo6Style
@@ -27,9 +30,88 @@
 void waitForSignal(const QObject* object, const char* signal, int timeout = 500);
 
 #ifdef MEEGOTOUCH
-// Create graphics scene
-void createMScene(MPlainWindow *w);
+// Create a scene window, set it to manual managed, and appear it.
+MSceneWindow *createMSceneWindow(MPlainWindow *w);
 #endif
 
+// copy'n'paste from MImAbstractArea impl file, but counts visits, too:
+class SpecialKeyFinder
+    : public MImAbstractKeyVisitor
+{
+public:
+    enum FindMode {
+        FindShiftKey,
+        FindDeadKey,
+        FindBoth
+    };
+
+private:
+    MImAbstractKey *mShiftKey;
+    MImAbstractKey *mDeadKey;
+    FindMode mode;
+    int mVisits;
+
+public:
+    explicit SpecialKeyFinder(FindMode newMode = FindBoth)
+        : mShiftKey(0)
+        , mDeadKey(0)
+        , mode(newMode)
+        , mVisits(0)
+    {}
+
+    MImAbstractKey *shiftKey() const
+    {
+        return mShiftKey;
+    }
+
+    MImAbstractKey *deadKey() const
+    {
+        return mDeadKey;
+    }
+
+    int visits() const
+    {
+        return mVisits;
+    }
+
+    bool operator()(MImAbstractKey *key)
+    {
+        ++mVisits;
+
+        if (!key) {
+            return false;
+        }
+
+        if (key->isShiftKey()) {
+            mShiftKey = key;
+        } else if (key->isDeadKey()) {
+            mDeadKey = key;
+        }
+
+        switch (mode) {
+        case FindShiftKey:
+            if (mShiftKey) {
+                return true;
+            }
+            break;
+
+        case FindDeadKey:
+            if (mDeadKey) {
+                return true;
+            }
+            break;
+
+        case FindBoth:
+            if (mShiftKey && mDeadKey) {
+                return true;
+            }
+            break;
+        }
+
+
+        return false;
+    }
+};
+
 #endif
 

++++++ meegotouch-inputmethodkeyboard.yaml
--- meegotouch-inputmethodkeyboard.yaml
+++ meegotouch-inputmethodkeyboard.yaml
@@ -1,6 +1,6 @@
 Name: meegotouch-inputmethodkeyboard
 Summary: MeeGo Virtual Keyboard
-Version: 0.5.9
+Version: 0.5.12
 Release: 1
 Group: System/GUI/Other 
 License: LGPL v2.1
@@ -9,6 +9,10 @@
     - "%{name}-%{version}.tar.bz2"
 Description: MeeGo Virtual Keyboard
 
+PkgBR:
+    - meegotouch-inputmethodframework-devel
+    - libmeegoimenginewords-devel
+
 PkgConfigBR:
     - QtCore >= 4.6.0
     - QtDBus
@@ -16,8 +20,6 @@
     - QtNetwork
     - QtGui
     - meegotouch 
-    - MeegoImFramework
-    - MeegoImEngine
     - meegotouch-feedbackreactionmaps
     - x11
     - xproto




More information about the MeeGo-commits mailing list