Cleaned up position tracking in the Symbian input methods.
[qt:kde-qt.git] / tests / auto / qinputcontext / tst_qinputcontext.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the test suite of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
14 ** this package.
15 **
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file.  Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23 **
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27 **
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
30 **
31 **
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include <QtTest/QtTest>
43 #include "../../shared/util.h"
44
45 #include <qinputcontext.h>
46 #include <qlineedit.h>
47 #include <qplaintextedit.h>
48 #include <qlayout.h>
49 #include <qradiobutton.h>
50 #include <qwindowsstyle.h>
51 #include <qdesktopwidget.h>
52
53 #ifdef Q_OS_SYMBIAN
54 #include <private/qt_s60_p.h>
55 #include <private/qcoefepinputcontext_p.h>
56
57 #include <w32std.h>
58 #include <coecntrl.h>
59 #endif
60
61 class tst_QInputContext : public QObject
62 {
63 Q_OBJECT
64
65 public:
66     tst_QInputContext() : m_phoneIsQwerty(false) {}
67     virtual ~tst_QInputContext() {}
68
69 public slots:
70     void initTestCase();
71     void cleanupTestCase() {}
72     void init() {}
73     void cleanup() {}
74 private slots:
75     void maximumTextLength();
76     void filterMouseEvents();
77     void requestSoftwareInputPanel();
78     void closeSoftwareInputPanel();
79     void selections();
80     void focusProxy();
81     void symbianTestCoeFepInputContext_data();
82     void symbianTestCoeFepInputContext();
83
84 private:
85     bool m_phoneIsQwerty;
86 };
87
88 #ifdef Q_OS_SYMBIAN
89 class KeyEvent : public TWsEvent
90 {
91 public:
92     KeyEvent(QWidget *w, TInt type, TInt scanCode, TUint code, TUint modifiers, TInt repeats) {
93         iHandle = w->effectiveWinId()->DrawableWindow()->WindowGroupId();
94         iType = type;
95         SetTimeNow();
96         TKeyEvent *keyEvent = reinterpret_cast<TKeyEvent *>(iEventData);
97         keyEvent->iScanCode = scanCode;
98         keyEvent->iCode = code;
99         keyEvent->iModifiers = modifiers;
100         keyEvent->iRepeats = repeats;
101     }
102 };
103
104 class FepReplayEvent
105 {
106 public:
107     enum Type {
108         Pause,
109         Key,
110         CompleteKey
111     };
112
113     FepReplayEvent(int msecsToPause)
114         : m_type(Pause)
115         , m_msecsToPause(msecsToPause)
116     {
117     }
118
119     FepReplayEvent(TInt keyType, TInt scanCode, TUint code, TUint modifiers, TInt repeats)
120         : m_type(Key)
121         , m_keyType(keyType)
122         , m_scanCode(scanCode)
123         , m_code(code)
124         , m_modifiers(modifiers)
125         , m_repeats(repeats)
126     {
127     }
128
129     FepReplayEvent(TInt scanCode, TUint code, TUint modifiers, TInt repeats)
130         : m_type(CompleteKey)
131         , m_scanCode(scanCode)
132         , m_code(code)
133         , m_modifiers(modifiers)
134         , m_repeats(repeats)
135     {
136     }
137
138     void sendEvent(QWidget *w, TInt type, TInt scanCode, TUint code, TUint modifiers, TInt repeats)
139     {
140         KeyEvent event(w, type, scanCode, code, modifiers, repeats);
141         S60->wsSession().SendEventToWindowGroup(w->effectiveWinId()->DrawableWindow()->WindowGroupId(), event);
142     }
143
144     void pause(int msecs)
145     {
146         // Don't use qWait here. The polling nature of that function screws up the test.
147         QTimer timer;
148         QEventLoop loop;
149         QObject::connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit()));
150         timer.setSingleShot(true);
151         timer.start(msecs);
152         loop.exec();
153     }
154
155     // For some reason, the test fails if using processEvents instead of an event loop
156     // with a zero timer to quit it, so use the timer.
157 #define KEY_WAIT 0
158
159     void replay(QWidget *w)
160     {
161         if (m_type == Pause) {
162             pause(m_msecsToPause);
163         } else if (m_type == Key) {
164             sendEvent(w, m_keyType, m_scanCode, m_code, m_modifiers, m_repeats);
165             if (m_keyType != EEventKeyDown)
166                 // EEventKeyDown events should have no pause before the EEventKey event.
167                 pause(KEY_WAIT);
168         } else if (m_type == CompleteKey) {
169             sendEvent(w, EEventKeyDown, m_scanCode, 0, m_modifiers, m_repeats);
170             // EEventKeyDown events should have no pause before the EEventKey event.
171             sendEvent(w, EEventKey, m_scanCode, m_code, m_modifiers, m_repeats);
172             pause(KEY_WAIT);
173             sendEvent(w, EEventKeyUp, m_scanCode, 0, m_modifiers, m_repeats);
174             pause(KEY_WAIT);
175         }
176     }
177
178 private:
179     Type m_type;
180     int m_msecsToPause;
181     TInt m_keyType;
182     TInt m_scanCode;
183     TUint m_code;
184     TUint m_modifiers;
185     TInt m_repeats;
186 };
187
188 Q_DECLARE_METATYPE(QList<FepReplayEvent>)
189 Q_DECLARE_METATYPE(Qt::InputMethodHints)
190 Q_DECLARE_METATYPE(QLineEdit::EchoMode);
191
192 #endif // Q_OS_SYMBIAN
193
194 void tst_QInputContext::initTestCase()
195 {
196 #ifdef Q_OS_SYMBIAN
197     // Sanity test. Checks FEP for:
198     // - T9 mode is default (it will attempt to fix this)
199     // - Language is English (it cannot fix this; bail out if not correct)
200     QWidget w;
201     QLayout *layout = new QVBoxLayout;
202     w.setLayout(layout);
203     QLineEdit *lineedit = new QLineEdit;
204     layout->addWidget(lineedit);
205     lineedit->setFocus();
206 #ifdef QT_KEYPAD_NAVIGATION
207     lineedit->setEditFocus(true);
208 #endif
209     w.show();
210
211     QDesktopWidget desktop;
212     QRect screenSize = desktop.screenGeometry(&w);
213     if (screenSize.width() > screenSize.height()) {
214         // Crude way of finding out we are running on a qwerty phone.
215         m_phoneIsQwerty = true;
216         return;
217     }
218
219     for (int iterations = 0; iterations < 16; iterations++) {
220         QTest::qWait(500);
221
222         QList<FepReplayEvent> keyEvents;
223
224         keyEvents << FepReplayEvent('9', '9', 0, 0);
225         keyEvents << FepReplayEvent('6', '6', 0, 0);
226         keyEvents << FepReplayEvent('8', '8', 0, 0);
227         keyEvents << FepReplayEvent(EStdKeyRightArrow, EKeyRightArrow, 0, 0);
228
229         foreach(FepReplayEvent event, keyEvents) {
230             event.replay(lineedit);
231         }
232
233         QApplication::processEvents();
234
235         if (lineedit->text().endsWith("you", Qt::CaseInsensitive)) {
236             // Success!
237             return;
238         }
239
240         // Try changing modes.
241         // After 8 iterations, try to press the mode switch twice before typing.
242         for (int c = 0; c <= iterations / 8; c++) {
243             FepReplayEvent(EStdKeyHash, '#', 0, 0).replay(lineedit);
244         }
245     }
246
247     QFAIL("FEP sanity test failed. Either the phone is not set to English, or the test was unable to enable T9");
248 #endif
249 }
250
251 void tst_QInputContext::maximumTextLength()
252 {
253     QLineEdit le;
254
255     le.setMaxLength(15);
256     QVariant variant = le.inputMethodQuery(Qt::ImMaximumTextLength);
257     QVERIFY(variant.isValid());
258     QCOMPARE(variant.toInt(), 15);
259
260     QPlainTextEdit pte;
261     // For BC/historical reasons, QPlainTextEdit::inputMethodQuery is protected.
262     variant = static_cast<QWidget *>(&pte)->inputMethodQuery(Qt::ImMaximumTextLength);
263     QVERIFY(!variant.isValid());
264 }
265
266 class QFilterInputContext : public QInputContext
267 {
268 public:
269     QFilterInputContext() {}
270     ~QFilterInputContext() {}
271
272     QString identifierName() { return QString(); }
273     QString language() { return QString(); }
274
275     void reset() {}
276
277     bool isComposing() const { return false; }
278
279     bool filterEvent( const QEvent *event )
280     {
281         lastTypes.append(event->type());
282         return false;
283     }
284
285 public:
286     QList<QEvent::Type> lastTypes;
287 };
288
289 void tst_QInputContext::filterMouseEvents()
290 {
291     QLineEdit le;
292     le.show();
293     QApplication::setActiveWindow(&le);
294
295     QFilterInputContext *ic = new QFilterInputContext;
296     le.setInputContext(ic);
297     QTest::mouseClick(&le, Qt::LeftButton);
298
299     QVERIFY(ic->lastTypes.indexOf(QEvent::MouseButtonRelease) >= 0);
300
301     le.setInputContext(0);
302 }
303
304 class RequestSoftwareInputPanelStyle : public QWindowsStyle
305 {
306 public:
307     RequestSoftwareInputPanelStyle()
308         : m_rsipBehavior(RSIP_OnMouseClickAndAlreadyFocused)
309     {
310 #ifdef Q_OS_WINCE
311         qApp->setAutoSipEnabled(true);
312 #endif
313     }
314     ~RequestSoftwareInputPanelStyle()
315     {
316     }
317
318     int styleHint(StyleHint hint, const QStyleOption *opt = 0,
319                   const QWidget *widget = 0, QStyleHintReturn* returnData = 0) const
320     {
321         if (hint == SH_RequestSoftwareInputPanel) {
322             return m_rsipBehavior;
323         } else {
324             return QWindowsStyle::styleHint(hint, opt, widget, returnData);
325         }
326     }
327
328     RequestSoftwareInputPanel m_rsipBehavior;
329 };
330
331 void tst_QInputContext::requestSoftwareInputPanel()
332 {
333     QStyle *oldStyle = qApp->style();
334     oldStyle->setParent(this); // Prevent it being deleted.
335     RequestSoftwareInputPanelStyle *newStyle = new RequestSoftwareInputPanelStyle;
336     qApp->setStyle(newStyle);
337
338     QWidget w;
339     QLayout *layout = new QVBoxLayout;
340     QLineEdit *le1, *le2;
341     le1 = new QLineEdit;
342     le2 = new QLineEdit;
343     layout->addWidget(le1);
344     layout->addWidget(le2);
345     w.setLayout(layout);
346
347     QFilterInputContext *ic1, *ic2;
348     ic1 = new QFilterInputContext;
349     ic2 = new QFilterInputContext;
350     le1->setInputContext(ic1);
351     le2->setInputContext(ic2);
352
353     w.show();
354     QApplication::setActiveWindow(&w);
355
356     // Testing single click panel activation.
357     newStyle->m_rsipBehavior = QStyle::RSIP_OnMouseClick;
358     QTest::mouseClick(le2, Qt::LeftButton, Qt::NoModifier, QPoint(5, 5));
359     QVERIFY(ic2->lastTypes.indexOf(QEvent::RequestSoftwareInputPanel) >= 0);
360     ic2->lastTypes.clear();
361
362     // Testing double click panel activation.
363     newStyle->m_rsipBehavior = QStyle::RSIP_OnMouseClickAndAlreadyFocused;
364     QTest::mouseClick(le1, Qt::LeftButton, Qt::NoModifier, QPoint(5, 5));
365     QVERIFY(ic1->lastTypes.indexOf(QEvent::RequestSoftwareInputPanel) < 0);
366     QTest::mouseClick(le1, Qt::LeftButton, Qt::NoModifier, QPoint(5, 5));
367     QVERIFY(ic1->lastTypes.indexOf(QEvent::RequestSoftwareInputPanel) >= 0);
368     ic1->lastTypes.clear();
369
370     // Testing right mouse button
371     QTest::mouseClick(le1, Qt::RightButton, Qt::NoModifier, QPoint(5, 5));
372     QVERIFY(ic1->lastTypes.indexOf(QEvent::RequestSoftwareInputPanel) < 0);
373
374     qApp->setStyle(oldStyle);
375     oldStyle->setParent(qApp);
376 }
377
378 void tst_QInputContext::closeSoftwareInputPanel()
379 {
380     QWidget w;
381     QLayout *layout = new QVBoxLayout;
382     QLineEdit *le1, *le2;
383     QRadioButton *rb;
384     le1 = new QLineEdit;
385     le2 = new QLineEdit;
386     rb = new QRadioButton;
387     layout->addWidget(le1);
388     layout->addWidget(le2);
389     layout->addWidget(rb);
390     w.setLayout(layout);
391
392     QFilterInputContext *ic1, *ic2;
393     ic1 = new QFilterInputContext;
394     ic2 = new QFilterInputContext;
395     le1->setInputContext(ic1);
396     le2->setInputContext(ic2);
397
398     w.show();
399     QApplication::setActiveWindow(&w);
400
401     // Testing that panel doesn't close between two input methods aware widgets.
402     QTest::mouseClick(le1, Qt::LeftButton, Qt::NoModifier, QPoint(5, 5));
403     QTest::mouseClick(le2, Qt::LeftButton, Qt::NoModifier, QPoint(5, 5));
404     QVERIFY(ic2->lastTypes.indexOf(QEvent::CloseSoftwareInputPanel) < 0);
405
406     // Testing that panel closes when focusing non-aware widget.
407     QTest::mouseClick(rb, Qt::LeftButton, Qt::NoModifier, QPoint(5, 5));
408     QVERIFY(ic2->lastTypes.indexOf(QEvent::CloseSoftwareInputPanel) >= 0);
409 }
410
411 void tst_QInputContext::selections()
412 {
413     QLineEdit le;
414     le.setText("Test text");
415     le.setSelection(2, 2);
416     QCOMPARE(le.inputMethodQuery(Qt::ImCursorPosition).toInt(), 4);
417     QCOMPARE(le.inputMethodQuery(Qt::ImAnchorPosition).toInt(), 2);
418
419     QList<QInputMethodEvent::Attribute> attributes;
420     attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 5, 3, QVariant()));
421     QInputMethodEvent event("", attributes);
422     QApplication::sendEvent(&le, &event);
423     QCOMPARE(le.cursorPosition(), 8);
424     QCOMPARE(le.selectionStart(), 5);
425     QCOMPARE(le.inputMethodQuery(Qt::ImCursorPosition).toInt(), 8);
426     QCOMPARE(le.inputMethodQuery(Qt::ImAnchorPosition).toInt(), 5);
427 }
428
429 void tst_QInputContext::focusProxy()
430 {
431     QWidget toplevel(0, Qt::X11BypassWindowManagerHint); toplevel.setObjectName("toplevel");
432     QWidget w(&toplevel); w.setObjectName("w");
433     QWidget proxy(&w); proxy.setObjectName("proxy");
434     QWidget proxy2(&w); proxy2.setObjectName("proxy2");
435     w.setFocusProxy(&proxy);
436     w.setAttribute(Qt::WA_InputMethodEnabled);
437     toplevel.show();
438     QApplication::setActiveWindow(&toplevel);
439     QTest::qWaitForWindowShown(&toplevel);
440     w.setFocus();
441     w.setAttribute(Qt::WA_NativeWindow); // we shouldn't crash!
442
443     proxy.setAttribute(Qt::WA_InputMethodEnabled);
444     proxy2.setAttribute(Qt::WA_InputMethodEnabled);
445
446     proxy2.setFocus();
447     w.setFocus();
448
449     QInputContext *gic = qApp->inputContext();
450     QVERIFY(gic);
451     qDebug() << gic->focusWidget() << &proxy;
452     QCOMPARE(gic->focusWidget(), &proxy);
453
454     // then change the focus proxy and check that input context is valid
455     QVERIFY(w.hasFocus());
456     QVERIFY(proxy.hasFocus());
457     QVERIFY(!proxy2.hasFocus());
458     w.setFocusProxy(&proxy2);
459     QVERIFY(!w.hasFocus());
460     QVERIFY(proxy.hasFocus());
461     QVERIFY(!proxy2.hasFocus());
462     QCOMPARE(gic->focusWidget(), &proxy);
463 }
464
465 void tst_QInputContext::symbianTestCoeFepInputContext_data()
466 {
467 #ifdef Q_OS_SYMBIAN
468     QTest::addColumn<bool>                   ("inputMethodEnabled");
469     QTest::addColumn<Qt::InputMethodHints>   ("inputMethodHints");
470     QTest::addColumn<int>                    ("maxLength"); // Zero for no limit
471     QTest::addColumn<QLineEdit::EchoMode>    ("echoMode");
472     QTest::addColumn<QList<FepReplayEvent> > ("keyEvents");
473     QTest::addColumn<QString>                ("finalString");
474     QTest::addColumn<QString>                ("preeditString");
475     QList<FepReplayEvent> events;
476
477     events << FepReplayEvent(EStdKeyBackspace, EKeyBackspace, 0, 0);
478     events << FepReplayEvent(EStdKeyBackspace, EKeyBackspace, 0, 0);
479     events << FepReplayEvent('5', '5', 0, 0);
480     events << FepReplayEvent('4', '4', 0, 0);
481     events << FepReplayEvent('6', '6', 0, 0);
482     events << FepReplayEvent(EStdKeyBackspace, EKeyBackspace, 0, 0);
483     events << FepReplayEvent(EStdKeyBackspace, EKeyBackspace, 0, 0);
484     events << FepReplayEvent('1', '1', 0, 0);
485     events << FepReplayEvent(EStdKeyBackspace, EKeyBackspace, 0, 0);
486     events << FepReplayEvent('2', '2', 0, 0);
487     events << FepReplayEvent('1', '1', 0, 0);
488     QTest::newRow("Numbers (no FEP)")
489             << false
490             << Qt::InputMethodHints(Qt::ImhNone)
491             << 0
492             << QLineEdit::Normal
493             << events
494             << QString("521")
495             << QString("");
496     QTest::newRow("Numbers and password mode (no FEP)")
497             << false
498             << Qt::InputMethodHints(Qt::ImhNone)
499             << 0
500             << QLineEdit::Password
501             << events
502             << QString("521")
503             << QString("");
504     QTest::newRow("Numbers")
505             << true
506             << Qt::InputMethodHints(Qt::ImhDigitsOnly)
507             << 0
508             << QLineEdit::Normal
509             << events
510             << QString("521")
511             << QString("");
512     QTest::newRow("Numbers max length (no FEP)")
513             << false
514             << Qt::InputMethodHints(Qt::ImhNone)
515             << 2
516             << QLineEdit::Normal
517             << events
518             << QString("21")
519             << QString("");
520     QTest::newRow("Numbers max length")
521             << true
522             << Qt::InputMethodHints(Qt::ImhDigitsOnly)
523             << 2
524             << QLineEdit::Normal
525             << events
526             << QString("21")
527             << QString("");
528     events.clear();
529
530     events << FepReplayEvent(EEventKeyDown, '5', 0, 0, 0);
531     events << FepReplayEvent(EEventKey, '5', '5', 0, 0);
532     events << FepReplayEvent(EEventKey, '5', '5', 0, 1);
533     events << FepReplayEvent(EEventKey, '5', '5', 0, 1);
534     events << FepReplayEvent(EEventKeyUp, '5', 0, 0, 0);
535     QTest::newRow("Numbers and autorepeat (no FEP)")
536             << false
537             << Qt::InputMethodHints(Qt::ImhNone)
538             << 0
539             << QLineEdit::Normal
540             << events
541             << QString("555")
542             << QString("");
543     events.clear();
544
545     events << FepReplayEvent(EStdKeyBackspace, EKeyBackspace, 0, 0);
546     events << FepReplayEvent('2', '2', 0, 0);
547     events << FepReplayEvent('3', '3', 0, 0);
548     events << FepReplayEvent('4', '4', 0, 0);
549     events << FepReplayEvent('4', '4', 0, 0);
550     events << FepReplayEvent('5', '5', 0, 0);
551     events << FepReplayEvent('5', '5', 0, 0);
552     events << FepReplayEvent(EStdKeyBackspace, EKeyBackspace, 0, 0);
553     QTest::newRow("Multitap")
554             << true
555             << Qt::InputMethodHints(Qt::ImhNoPredictiveText)
556             << 0
557             << QLineEdit::Normal
558             << events
559             << QString("Adh")
560             << QString("");
561     QTest::newRow("Multitap with no auto uppercase")
562             << true
563             << Qt::InputMethodHints(Qt::ImhNoPredictiveText | Qt::ImhNoAutoUppercase)
564             << 0
565             << QLineEdit::Normal
566             << events
567             << QString("adh")
568             << QString("");
569     QTest::newRow("Multitap with uppercase")
570             << true
571             << Qt::InputMethodHints(Qt::ImhNoPredictiveText | Qt::ImhPreferUppercase)
572             << 0
573             << QLineEdit::Normal
574             << events
575             << QString("ADH")
576             << QString("");
577     QTest::newRow("Multitap with lowercase")
578             << true
579             << Qt::InputMethodHints(Qt::ImhNoPredictiveText | Qt::ImhPreferLowercase)
580             << 0
581             << QLineEdit::Normal
582             << events
583             << QString("adh")
584             << QString("");
585     QTest::newRow("Multitap with forced uppercase")
586             << true
587             << Qt::InputMethodHints(Qt::ImhNoPredictiveText | Qt::ImhUppercaseOnly)
588             << 0
589             << QLineEdit::Normal
590             << events
591             << QString("ADH")
592             << QString("");
593     QTest::newRow("Multitap with forced lowercase")
594             << true
595             << Qt::InputMethodHints(Qt::ImhNoPredictiveText | Qt::ImhLowercaseOnly)
596             << 0
597             << QLineEdit::Normal
598             << events
599             << QString("adh")
600             << QString("");
601     events.clear();
602
603     events << FepReplayEvent(EStdKeyHash, '#', 0, 0);
604     events << FepReplayEvent('2', '2', 0, 0);
605     events << FepReplayEvent('2', '2', 0, 0);
606     events << FepReplayEvent('3', '3', 0, 0);
607     events << FepReplayEvent('4', '4', 0, 0);
608     events << FepReplayEvent('4', '4', 0, 0);
609     events << FepReplayEvent('5', '5', 0, 0);
610     events << FepReplayEvent('5', '5', 0, 0);
611     events << FepReplayEvent(EStdKeyBackspace, EKeyBackspace, 0, 0);
612     QTest::newRow("Multitap with mode switch")
613             << true
614             << Qt::InputMethodHints(Qt::ImhNoPredictiveText)
615             << 0
616             << QLineEdit::Normal
617             << events
618             << QString("bdh")
619             << QString("");
620     events.clear();
621
622     events << FepReplayEvent('7', '7', 0, 0);
623     events << FepReplayEvent('7', '7', 0, 0);
624     events << FepReplayEvent('8', '8', 0, 0);
625     events << FepReplayEvent('9', '9', 0, 0);
626     events << FepReplayEvent('9', '9', 0, 0);
627     QTest::newRow("Multitap with unfinished text")
628             << true
629             << Qt::InputMethodHints(Qt::ImhNoPredictiveText)
630             << 0
631             << QLineEdit::Normal
632             << events
633             << QString("Qt")
634             << QString("x");
635     events << FepReplayEvent(2000);
636     QTest::newRow("Multitap with committed text")
637             << true
638             << Qt::InputMethodHints(Qt::ImhNoPredictiveText)
639             << 0
640             << QLineEdit::Normal
641             << events
642             << QString("Qtx")
643             << QString("");
644     events.clear();
645
646     events << FepReplayEvent('4', '4', 0, 0);
647     events << FepReplayEvent('4', '4', 0, 0);
648     // Simulate holding down hash key.
649     events << FepReplayEvent(EEventKeyDown, EStdKeyHash, 0, 0, 0);
650     events << FepReplayEvent(EEventKey, EStdKeyHash, '#', 0, 0);
651     events << FepReplayEvent(500);
652     events << FepReplayEvent(EEventKey, EStdKeyHash, '#', 0, 1);
653     events << FepReplayEvent(EEventKey, EStdKeyHash, '#', 0, 1);
654     events << FepReplayEvent(EEventKey, EStdKeyHash, '#', 0, 1);
655     events << FepReplayEvent(EEventKeyUp, EStdKeyHash, 0, 0, 0);
656     events << FepReplayEvent('7', '7', 0, 0);
657     events << FepReplayEvent('7', '7', 0, 0);
658     events << FepReplayEvent('8', '8', 0, 0);
659     // QTBUG-9867: Switch back as well to make sure we don't get extra symbols
660     events << FepReplayEvent(EEventKeyDown, EStdKeyHash, 0, 0, 0);
661     events << FepReplayEvent(EEventKey, EStdKeyHash, '#', 0, 0);
662     events << FepReplayEvent(500);
663     events << FepReplayEvent(EEventKey, EStdKeyHash, '#', 0, 1);
664     events << FepReplayEvent(EEventKey, EStdKeyHash, '#', 0, 1);
665     events << FepReplayEvent(EEventKey, EStdKeyHash, '#', 0, 1);
666     events << FepReplayEvent(EEventKeyUp, EStdKeyHash, 0, 0, 0);
667     events << FepReplayEvent('9', '9', 0, 0);
668     events << FepReplayEvent('6', '6', 0, 0);
669     events << FepReplayEvent('8', '8', 0, 0);
670     events << FepReplayEvent(2000);
671     events << FepReplayEvent(EStdKeyDevice3, EKeyDevice3, 0, 0); // Select key
672     QTest::newRow("Multitap and numbers")
673             << true
674             << Qt::InputMethodHints(Qt::ImhNoPredictiveText)
675             << 0
676             << QLineEdit::Normal
677             << events
678             << QString("H778wmt")
679             << QString("");
680     QTest::newRow("T9 and numbers")
681             << true
682             << Qt::InputMethodHints(Qt::ImhPreferLowercase)
683             << 0
684             << QLineEdit::Normal
685             << events
686             << QString("hi778you")
687             << QString("");
688     events.clear();
689
690     events << FepReplayEvent('4', '4', 0, 0);
691     events << FepReplayEvent('4', '4', 0, 0);
692     events << FepReplayEvent(EStdKeyDevice3, EKeyDevice3, 0, 0); // Select key
693     QTest::newRow("T9")
694             << true
695             << Qt::InputMethodHints(Qt::ImhPreferLowercase)
696             << 0
697             << QLineEdit::Normal
698             << events
699             << QString("hi")
700             << QString("");
701     QTest::newRow("T9 with uppercase")
702             << true
703             << Qt::InputMethodHints(Qt::ImhPreferUppercase)
704             << 0
705             << QLineEdit::Normal
706             << events
707             << QString("HI")
708             << QString("");
709     QTest::newRow("T9 with forced lowercase")
710             << true
711             << Qt::InputMethodHints(Qt::ImhLowercaseOnly)
712             << 0
713             << QLineEdit::Normal
714             << events
715             << QString("hi")
716             << QString("");
717     QTest::newRow("T9 with forced uppercase")
718             << true
719             << Qt::InputMethodHints(Qt::ImhUppercaseOnly)
720             << 0
721             << QLineEdit::Normal
722             << events
723             << QString("HI")
724             << QString("");
725     QTest::newRow("T9 with maxlength")
726             << true
727             << Qt::InputMethodHints(Qt::ImhLowercaseOnly)
728             << 1
729             << QLineEdit::Normal
730             << events
731             << QString("i")
732             << QString("");
733     events.clear();
734
735     events << FepReplayEvent('4', '4', 0, 0);
736     events << FepReplayEvent('4', '4', 0, 0);
737     events << FepReplayEvent(EStdKeyLeftArrow, EKeyLeftArrow, 0, 0);
738     events << FepReplayEvent(EStdKeyLeftArrow, EKeyLeftArrow, 0, 0);
739     events << FepReplayEvent('9', '9', 0, 0);
740     events << FepReplayEvent('6', '6', 0, 0);
741     events << FepReplayEvent('8', '8', 0, 0);
742     events << FepReplayEvent('0', '0', 0, 0);
743     events << FepReplayEvent(EStdKeyRightArrow, EKeyRightArrow, 0, 0);
744     events << FepReplayEvent(EStdKeyRightArrow, EKeyRightArrow, 0, 0);
745     events << FepReplayEvent('8', '8', 0, 0);
746     events << FepReplayEvent('8', '8', 0, 0);
747     QTest::newRow("T9 with movement and unfinished text")
748             << true
749             << Qt::InputMethodHints(Qt::ImhPreferLowercase)
750             << 0
751             << QLineEdit::Normal
752             << events
753             << QString("you hi")
754             << QString("tv");
755     QTest::newRow("T9 with movement, password and unfinished text")
756             << true
757             << Qt::InputMethodHints(Qt::ImhPreferLowercase)
758             << 0
759             << QLineEdit::Password
760             << events
761             << QString("wmt h")
762             << QString("u");
763     QTest::newRow("T9 with movement, maxlength, password and unfinished text")
764             << true
765             << Qt::InputMethodHints(Qt::ImhPreferLowercase)
766             << 2
767             << QLineEdit::Password
768             << events
769             << QString("wh")
770             << QString("");
771     QTest::newRow("T9 with movement, maxlength and unfinished text")
772             << true
773             << Qt::InputMethodHints(Qt::ImhPreferLowercase)
774             << 2
775             << QLineEdit::Normal
776             << events
777             << QString("hi")
778             << QString("");
779     QTest::newRow("Multitap with movement and unfinished text")
780             << true
781             << Qt::InputMethodHints(Qt::ImhNoPredictiveText | Qt::ImhPreferLowercase)
782             << 0
783             << QLineEdit::Normal
784             << events
785             << QString("wmt h")
786             << QString("u");
787     QTest::newRow("Multitap with movement, maxlength and unfinished text")
788             << true
789             << Qt::InputMethodHints(Qt::ImhNoPredictiveText | Qt::ImhPreferLowercase)
790             << 2
791             << QLineEdit::Normal
792             << events
793             << QString("wh")
794             << QString("");
795     QTest::newRow("Numbers with movement")
796             << true
797             << Qt::InputMethodHints(Qt::ImhDigitsOnly)
798             << 0
799             << QLineEdit::Normal
800             << events
801             << QString("96804488")
802             << QString("");
803     QTest::newRow("Numbers with movement and maxlength")
804             << true
805             << Qt::InputMethodHints(Qt::ImhDigitsOnly)
806             << 2
807             << QLineEdit::Normal
808             << events
809             << QString("44")
810             << QString("");
811     QTest::newRow("Numbers with movement, password and unfinished text")
812             << true
813             << Qt::InputMethodHints(Qt::ImhDigitsOnly)
814             << 0
815             << QLineEdit::Password
816             << events
817             << QString("9680448")
818             << QString("8");
819     QTest::newRow("Numbers with movement, maxlength, password and unfinished text")
820             << true
821             << Qt::InputMethodHints(Qt::ImhDigitsOnly)
822             << 2
823             << QLineEdit::Password
824             << events
825             << QString("44")
826             << QString("");
827     events << FepReplayEvent(EStdKeyRightArrow, EKeyRightArrow, 0, 0);
828     QTest::newRow("T9 with movement")
829             << true
830             << Qt::InputMethodHints(Qt::ImhPreferLowercase)
831             << 0
832             << QLineEdit::Normal
833             << events
834             << QString("you htvi")
835             << QString("");
836     QTest::newRow("T9 with movement and password")
837             << true
838             << Qt::InputMethodHints(Qt::ImhPreferLowercase)
839             << 0
840             << QLineEdit::Password
841             << events
842             << QString("wmt hu")
843             << QString("");
844     QTest::newRow("T9 with movement, maxlength and password")
845             << true
846             << Qt::InputMethodHints(Qt::ImhPreferLowercase)
847             << 2
848             << QLineEdit::Password
849             << events
850             << QString("wh")
851             << QString("");
852     QTest::newRow("Multitap with movement")
853             << true
854             << Qt::InputMethodHints(Qt::ImhNoPredictiveText | Qt::ImhPreferLowercase)
855             << 0
856             << QLineEdit::Normal
857             << events
858             << QString("wmt hu")
859             << QString("");
860     QTest::newRow("Multitap with movement and maxlength")
861             << true
862             << Qt::InputMethodHints(Qt::ImhNoPredictiveText | Qt::ImhPreferLowercase)
863             << 2
864             << QLineEdit::Normal
865             << events
866             << QString("wh")
867             << QString("");
868     QTest::newRow("Numbers with movement and password")
869             << true
870             << Qt::InputMethodHints(Qt::ImhDigitsOnly)
871             << 0
872             << QLineEdit::Password
873             << events
874             << QString("96804488")
875             << QString("");
876     QTest::newRow("Numbers with movement, maxlength and password")
877             << true
878             << Qt::InputMethodHints(Qt::ImhDigitsOnly)
879             << 2
880             << QLineEdit::Password
881             << events
882             << QString("44")
883             << QString("");
884     events.clear();
885 #endif
886 }
887
888 void tst_QInputContext::symbianTestCoeFepInputContext()
889 {
890 #ifndef Q_OS_SYMBIAN
891     QSKIP("This is a Symbian-only test", SkipAll);
892 #else
893     QCoeFepInputContext *ic = qobject_cast<QCoeFepInputContext *>(qApp->inputContext());
894     if (!ic) {
895         QSKIP("coefep is not the active input context; skipping test", SkipAll);
896     }
897
898     QFETCH(bool,                  inputMethodEnabled);
899     QFETCH(Qt::InputMethodHints,  inputMethodHints);
900     QFETCH(int,                   maxLength);
901     QFETCH(QLineEdit::EchoMode,   echoMode);
902     QFETCH(QList<FepReplayEvent>, keyEvents);
903     QFETCH(QString,               finalString);
904     QFETCH(QString,               preeditString);
905
906     if (inputMethodEnabled && m_phoneIsQwerty) {
907         QSKIP("Skipping advanced input method tests on QWERTY phones", SkipSingle);
908     }
909
910     QWidget w;
911     QLayout *layout = new QVBoxLayout;
912     w.setLayout(layout);
913     QLineEdit *lineedit = new QLineEdit;
914     layout->addWidget(lineedit);
915     lineedit->setFocus();
916 #ifdef QT_KEYPAD_NAVIGATION
917     lineedit->setEditFocus(true);
918 #endif
919     w.show();
920
921     lineedit->setAttribute(Qt::WA_InputMethodEnabled, inputMethodEnabled);
922     lineedit->setInputMethodHints(inputMethodHints);
923     if (maxLength > 0)
924         lineedit->setMaxLength(maxLength);
925     lineedit->setEchoMode(echoMode);
926
927     QTest::qWait(200);
928
929     foreach(FepReplayEvent event, keyEvents) {
930         event.replay(lineedit);
931     }
932
933     QApplication::processEvents();
934
935     QCOMPARE(lineedit->text(), finalString);
936     QEXPECT_FAIL("Numbers with movement, maxlength, password and unfinished text"
937             , "Fails due to QTBUG-12949"
938             , Continue);
939     QCOMPARE(ic->m_preeditString, preeditString);
940 #endif
941 }
942
943 QTEST_MAIN(tst_QInputContext)
944 #include "tst_qinputcontext.moc"