diff -r 000000000000 -r 1918ee327afb tests/auto/qshortcut/tst_qshortcut.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/auto/qshortcut/tst_qshortcut.cpp Mon Jan 11 14:00:40 2010 +0000 @@ -0,0 +1,1272 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class AccelForm; +QT_BEGIN_NAMESPACE +class QMainWindow; +class QTextEdit; +QT_END_NAMESPACE + +//TESTED_CLASS= +//TESTED_FILES= + +class tst_QShortcut : public QObject +{ + Q_OBJECT +public: + tst_QShortcut(); + virtual ~tst_QShortcut(); + + + enum Action { + SetupAccel, + TestAccel, + ClearAll + } currentAction; + + enum Widget { + NoWidget, + TriggerSlot1, + TriggerSlot2, + TriggerSlot3, + TriggerSlot4, + TriggerSlot5, + TriggerSlot6, + TriggerSlot7 + }; + + enum Result { + NoResult, + Slot1Triggered, + Slot2Triggered, + Slot3Triggered, + Slot4Triggered, + Slot5Triggered, + Slot6Triggered, + Slot7Triggered, + Ambiguous + } currentResult; + +public slots: + void slotTrig1() { currentResult = Slot1Triggered; } + void slotTrig2() { currentResult = Slot2Triggered; } + void slotTrig3() { currentResult = Slot3Triggered; } + void slotTrig4() { currentResult = Slot4Triggered; } + void slotTrig5() { currentResult = Slot5Triggered; } + void slotTrig6() { currentResult = Slot6Triggered; } + void slotTrig7() { currentResult = Slot7Triggered; } + void ambigSlot1() { currentResult = Ambiguous; ambigResult = Slot1Triggered; } + void ambigSlot2() { currentResult = Ambiguous; ambigResult = Slot2Triggered; } + void ambigSlot3() { currentResult = Ambiguous; ambigResult = Slot3Triggered; } + void ambigSlot4() { currentResult = Ambiguous; ambigResult = Slot4Triggered; } + void ambigSlot5() { currentResult = Ambiguous; ambigResult = Slot5Triggered; } + void ambigSlot6() { currentResult = Ambiguous; ambigResult = Slot6Triggered; } + void ambigSlot7() { currentResult = Ambiguous; ambigResult = Slot7Triggered; } + void statusMessage( const QString& message ) { sbText = message; } + void shortcutDestroyed(QObject* obj); + +public slots: + void initTestCase(); + void cleanupTestCase(); + +private slots: + void number_data(); + void number(); + void text_data(); + void text(); + void disabledItems(); + void ambiguousItems(); + void ambiguousRotation(); + void keypressConsumption(); + void unicodeCompare(); + void context(); + +protected: + static Qt::KeyboardModifiers toButtons( int key ); + void defElements(); + + void clearAllShortcuts(); + QShortcut *setupShortcut(int testWidget, const QKeySequence &ks); + QShortcut *setupShortcut(int testWidget, const QString &txt, int k1 = 0, int k2 = 0, int k3 = 0, int k4 = 0); + + QShortcut *setupShortcut(QWidget *parent, const char *name, int testWidget, const QString &txt, int k1 = 0, int k2 = 0, int k3 = 0, int k4 = 0); + QShortcut *setupShortcut(QWidget *parent, const char *name, int testWidget, const QKeySequence &ks, Qt::ShortcutContext context = Qt::WindowShortcut); + + void sendKeyEvents(QWidget *w, int k1, QChar c1 = 0, int k2 = 0, QChar c2 = 0, int k3 = 0, QChar c3 = 0, int k4 = 0, QChar c4 = 0); + void sendKeyEvents(int k1, QChar c1 = 0, int k2 = 0, QChar c2 = 0, int k3 = 0, QChar c3 = 0, int k4 = 0, QChar c4 = 0); + + void testElement(); + + QMainWindow *mainW; + QList shortcuts; + QTextEdit *edit; + QString sbText; + Result ambigResult; +}; + +QT_BEGIN_NAMESPACE +template<> struct QMetaTypeId +{ enum { Defined = 1 }; static inline int qt_metatype_id() { return QMetaType::Int; } }; +template<> struct QMetaTypeId +{ enum { Defined = 1 }; static inline int qt_metatype_id() { return QMetaType::Int; } }; +template<> struct QMetaTypeId +{ enum { Defined = 1 }; static inline int qt_metatype_id() { return QMetaType::Int; } }; +QT_END_NAMESPACE + +class TestEdit : public QTextEdit +{ + Q_OBJECT +public: + TestEdit(QWidget *parent, const char *name) + : QTextEdit(parent) + { + setObjectName(name); + } + +protected: + bool event(QEvent *e) { + // Make testedit allow any Ctrl+Key as shortcut + if (e->type() == QEvent::ShortcutOverride) { + QKeyEvent *ke = static_cast(e); + if (ke->modifiers() == Qt::ControlModifier + && ke->key() > Qt::Key_Any + && ke->key() < Qt::Key_ydiaeresis) { + ke->ignore(); + return true; + } + } + + // If keypress not processed as normal, check for + // Ctrl+Key event, and input custom string for + // result comparison. + if (e->type() == QEvent::KeyPress) { + QKeyEvent *ke = static_cast(e); + if (ke->modifiers() && ke->key() > Qt::Key_Any + && ke->key() < Qt::Key_ydiaeresis) { + if (ke->modifiers() == Qt::ControlModifier) + insertPlainText(QString("").arg(char(ke->key()))); + else if (ke->modifiers() == Qt::AltModifier) + insertPlainText(QString("").arg(char(ke->key()))); + else if (ke->modifiers() == Qt::ShiftModifier) + insertPlainText(QString("").arg(char(ke->key()))); + return true; + } + } + return QTextEdit::event(e); + } +}; + +tst_QShortcut::tst_QShortcut(): mainW( 0 ) +{ +} + +tst_QShortcut::~tst_QShortcut() +{ + clearAllShortcuts(); +} + +void tst_QShortcut::initTestCase() +{ + currentResult = NoResult; + mainW = new QMainWindow(0); + mainW->setWindowFlags(Qt::X11BypassWindowManagerHint); + edit = new TestEdit(mainW, "test_edit"); + mainW->setFixedSize( 100, 100 ); + mainW->setCentralWidget( edit ); + mainW->show(); + mainW->activateWindow(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(mainW); +#endif + QTest::qWait(100); + connect( mainW->statusBar(), SIGNAL(messageChanged(const QString&)), + this, SLOT(statusMessage(const QString&)) ); +} + +void tst_QShortcut::cleanupTestCase() +{ + delete mainW; +} + +Qt::KeyboardModifiers tst_QShortcut::toButtons( int key ) +{ + Qt::KeyboardModifiers result = Qt::NoModifier; + if ( key & Qt::SHIFT ) + result |= Qt::ShiftModifier; + if ( key & Qt::CTRL ) + result |= Qt::ControlModifier; + if ( key & Qt::META ) + result |= Qt::MetaModifier; + if ( key & Qt::ALT ) + result |= Qt::AltModifier; + return result; +} + +void tst_QShortcut::defElements() +{ + QTest::addColumn("action"); + QTest::addColumn("testWidget"); + QTest::addColumn("txt"); + QTest::addColumn("k1"); + QTest::addColumn("c1"); + QTest::addColumn("k2"); + QTest::addColumn("c2"); + QTest::addColumn("k3"); + QTest::addColumn("c3"); + QTest::addColumn("k4"); + QTest::addColumn("c4"); + QTest::addColumn("result"); +} + +void tst_QShortcut::number() +{ + // We expect a failure on these tests, until QtTestKeyboard is + // fixed to do real platform dependent keyboard simulations + if (QTest::currentDataTag() == QString("N006a:Shift+Tab - [BackTab]") + || QTest::currentDataTag() == QString("N006b:Shift+Tab - [Shift+BackTab]")) + QEXPECT_FAIL("", "FLAW IN QTESTKEYBOARD: Keyboard events not passed through " + "platform dependent key handling code", Continue); + testElement(); +} +void tst_QShortcut::text() +{ + testElement(); +} +// ------------------------------------------------------------------ +// Number Elements -------------------------------------------------- +// ------------------------------------------------------------------ +void tst_QShortcut::number_data() +{ + defElements(); + + // Clear all + QTest::newRow("N00 - clear") << ClearAll <<0<activateWindow(); + qApp->syncX(); + QTest::qWait(100); + + /* Testing Disabled Shortcuts + Qt::Key_M on slot1 + Shift + Qt::Key_M on slot1 + Qt::Key_M on slot2 (disabled) + Shift + Qt::Key_M on slot2 (disabled) + */ + + // Setup two identical shortcuts on different pushbuttons + QPushButton pb1(mainW); + QPushButton pb2(mainW); + pb1.setObjectName("pushbutton-1"); + pb2.setObjectName("pushbutton-2"); + pb1.show(); // Must be show for QShortcutMap::correctSubWindow to trigger + pb2.show(); + + QShortcut *cut1 = setupShortcut(&pb1, "shortcut1-pb1", TriggerSlot1, "M"); + QShortcut *cut2 = setupShortcut(&pb1, "shortcut2-pb1", TriggerSlot1, "Shift+M"); + QShortcut *cut3 = setupShortcut(&pb2, "shortcut3-pb2", TriggerSlot2, "M"); + QShortcut *cut4 = setupShortcut(&pb2, "shortcut4-pb2", TriggerSlot2, "Shift+M"); + + cut3->setEnabled(false); + cut4->setEnabled(false); + + currentResult = NoResult; + sendKeyEvents(Qt::Key_M, 'm'); + QCOMPARE(currentResult, Slot1Triggered); + + currentResult = NoResult; + sendKeyEvents(Qt::SHIFT+Qt::Key_M, 'M'); + QCOMPARE(currentResult, Slot1Triggered); + + cut2->setEnabled(false); + cut4->setEnabled(true); + + /* Testing Disabled Shortcuts + Qt::Key_M on slot1 + Shift + Qt::Key_M on slot1 (disabled) + Qt::Key_M on slot2 (disabled) + Shift + Qt::Key_M on slot2 + */ + currentResult = NoResult; + sendKeyEvents( Qt::Key_M, 'm' ); + QCOMPARE( currentResult, Slot1Triggered ); + + currentResult = NoResult; + sendKeyEvents( Qt::SHIFT+Qt::Key_M, 'M' ); + QCOMPARE( currentResult, Slot2Triggered ); + + + /* Testing Disabled Accel + Qt::Key_F5 on slot1 + Shift + Qt::Key_F5 on slot2 (disabled) + */ + clearAllShortcuts(); + cut1 = setupShortcut(&pb1, "shortcut1-pb1", TriggerSlot1, "F5"); + cut4 = setupShortcut(&pb2, "shortcut4-pb2", TriggerSlot2, "Shift+F5"); + + cut1->setKey(QKeySequence("F5")); + cut4->setKey(QKeySequence("Shift+F5")); + + cut1->setEnabled(true); + cut4->setEnabled(false); + + currentResult = NoResult; + sendKeyEvents( Qt::Key_F5, 0 ); + QCOMPARE( currentResult, Slot1Triggered ); + + currentResult = NoResult; + sendKeyEvents( Qt::SHIFT+Qt::Key_F5, 0 ); + QCOMPARE( currentResult, NoResult ); + +#if 0 + qFatal("Not testing statusbar text feedback yet, since not implemented"); + /* Testing Disabled Accel, and the corresponding statusbar feedback + Ctrl + Qt::Key_K, Ctrl + Qt::Key_L on slot1 + Ctrl + Qt::Key_K, Ctrl + Qt::Key_M on slot2 (disabled) + */ + cut1->setKey(QKeySequence("Ctrl+K, Ctrl+L")); + cut4->setKey(QKeySequence("Ctrl+K, Ctrl+M")); + + cut1->setEnabled(true); + cut4->setEnabled(false); + + currentResult = NoResult; + sendKeyEvents( Qt::CTRL+Qt::Key_K, 0 ); + sendKeyEvents( Qt::CTRL+Qt::Key_Q, 0 ); + QCOMPARE( currentResult, NoResult ); + if (over_330) + QCOMPARE( sbText, QString("Ctrl+K, Ctrl+Q not defined") ); + + currentResult = NoResult; + sendKeyEvents( Qt::CTRL+Qt::Key_K, 0 ); + sendKeyEvents( Qt::CTRL+Qt::Key_M, 0 ); + QCOMPARE( currentResult, NoResult ); + if (over_330) + QCOMPARE( sbText, QString::null ); + + currentResult = NoResult; + sendKeyEvents( Qt::CTRL+Qt::Key_K, 0 ); + sendKeyEvents( Qt::CTRL+Qt::Key_L, 0 ); + QCOMPARE( currentResult, Slot1Triggered ); + if (over_330) + QCOMPARE( sbText, QString::null ); +#endif + clearAllShortcuts(); + cut1 = 0; + cut4 = 0; +} +// ------------------------------------------------------------------ +// Ambiguous Elements ----------------------------------------------- +// ------------------------------------------------------------------ +void tst_QShortcut::ambiguousRotation() +{ + clearAllShortcuts(); + /* Testing Shortcut rotation scheme + Ctrl + Qt::Key_A on slot1 (disabled) + Ctrl + Qt::Key_A on slot2 (disabled) + Ctrl + Qt::Key_A on slot3 + Ctrl + Qt::Key_A on slot4 + Ctrl + Qt::Key_A on slot5 (disabled) + Ctrl + Qt::Key_A on slot6 + Ctrl + Qt::Key_A on slot7 (disabled) + */ + QShortcut *cut1 = setupShortcut(TriggerSlot1, "Ctrl+A"); + QShortcut *cut2 = setupShortcut(TriggerSlot2, "Ctrl+A"); + QShortcut *cut3 = setupShortcut(TriggerSlot3, "Ctrl+A"); + QShortcut *cut4 = setupShortcut(TriggerSlot4, "Ctrl+A"); + QShortcut *cut5 = setupShortcut(TriggerSlot5, "Ctrl+A"); + QShortcut *cut6 = setupShortcut(TriggerSlot6, "Ctrl+A"); + QShortcut *cut7 = setupShortcut(TriggerSlot7, "Ctrl+A"); + + cut1->setEnabled(false); + cut2->setEnabled(false); + cut5->setEnabled(false); + cut7->setEnabled(false); + + // Test proper rotation + // Start on first + // Go to last + // Go back to first + // Continue... + currentResult = NoResult; + ambigResult = NoResult; + sendKeyEvents(Qt::CTRL+Qt::Key_A); + QCOMPARE(currentResult, Ambiguous); + QCOMPARE(ambigResult, Slot3Triggered); + + currentResult = NoResult; + ambigResult = NoResult; + sendKeyEvents(Qt::CTRL+Qt::Key_A); + QCOMPARE(currentResult, Ambiguous); + QCOMPARE(ambigResult, Slot4Triggered); + + currentResult = NoResult; + ambigResult = NoResult; + sendKeyEvents(Qt::CTRL+Qt::Key_A); + QCOMPARE(currentResult, Ambiguous); + QCOMPARE(ambigResult, Slot6Triggered); + + currentResult = NoResult; + ambigResult = NoResult; + sendKeyEvents(Qt::CTRL+Qt::Key_A); + QCOMPARE(currentResult, Ambiguous); + QCOMPARE(ambigResult, Slot3Triggered); + + currentResult = NoResult; + ambigResult = NoResult; + sendKeyEvents(Qt::CTRL+Qt::Key_A); + QCOMPARE(currentResult, Ambiguous); + QCOMPARE(ambigResult, Slot4Triggered); + + currentResult = NoResult; + ambigResult = NoResult; + sendKeyEvents(Qt::CTRL+Qt::Key_A); + QCOMPARE(currentResult, Ambiguous); + QCOMPARE(ambigResult, Slot6Triggered); + + /* Testing Shortcut rotation scheme + Ctrl + Qt::Key_A on slot1 + Ctrl + Qt::Key_A on slot2 + Ctrl + Qt::Key_A on slot3 (disabled) + Ctrl + Qt::Key_A on slot4 (disabled) + Ctrl + Qt::Key_A on slot5 + Ctrl + Qt::Key_A on slot6 (disabled) + Ctrl + Qt::Key_A on slot7 + */ + + cut1->setEnabled(true); + cut2->setEnabled(true); + cut5->setEnabled(true); + cut7->setEnabled(true); + + cut3->setEnabled(false); + cut4->setEnabled(false); + cut6->setEnabled(false); + + currentResult = NoResult; + ambigResult = NoResult; + sendKeyEvents(Qt::CTRL+Qt::Key_A); + QCOMPARE(currentResult, Ambiguous); + QCOMPARE(ambigResult, Slot1Triggered); + + currentResult = NoResult; + ambigResult = NoResult; + sendKeyEvents(Qt::CTRL+Qt::Key_A); + QCOMPARE(currentResult, Ambiguous); + QCOMPARE(ambigResult, Slot2Triggered); + + currentResult = NoResult; + ambigResult = NoResult; + sendKeyEvents(Qt::CTRL+Qt::Key_A); + QCOMPARE(currentResult, Ambiguous); + QCOMPARE(ambigResult, Slot5Triggered); + + currentResult = NoResult; + ambigResult = NoResult; + sendKeyEvents(Qt::CTRL+Qt::Key_A); + QCOMPARE(currentResult, Ambiguous); + QCOMPARE(ambigResult, Slot7Triggered); + + currentResult = NoResult; + ambigResult = NoResult; + sendKeyEvents(Qt::CTRL+Qt::Key_A); + QCOMPARE(currentResult, Ambiguous); + QCOMPARE(ambigResult, Slot1Triggered); + + currentResult = NoResult; + ambigResult = NoResult; + sendKeyEvents(Qt::CTRL+Qt::Key_A); + QCOMPARE(currentResult, Ambiguous); + QCOMPARE(ambigResult, Slot2Triggered); + + currentResult = NoResult; + ambigResult = NoResult; + sendKeyEvents(Qt::CTRL+Qt::Key_A); + QCOMPARE(currentResult, Ambiguous); + QCOMPARE(ambigResult, Slot5Triggered); + + currentResult = NoResult; + ambigResult = NoResult; + sendKeyEvents(Qt::CTRL+Qt::Key_A); + QCOMPARE(currentResult, Ambiguous); + QCOMPARE(ambigResult, Slot7Triggered); + + clearAllShortcuts(); + cut1 = 0; cut2 = 0; + cut3 = 0; cut4 = 0; + cut5 = 0; cut6 = 0; + cut7 = 0; +} + +void tst_QShortcut::ambiguousItems() +{ + clearAllShortcuts(); + /* Testing Ambiguous Shortcuts + Qt::Key_M on Pushbutton 1 + Qt::Key_M on Pushbutton 2 + */ + + // Setup two identical shortcuts on different pushbuttons + QPushButton pb1(mainW); + QPushButton pb2(mainW); + pb1.setObjectName("pushbutton-1"); + pb2.setObjectName("pushbutton-2"); + pb1.show(); // Must be show for QShortcutMap::correctSubWindow to trigger + pb2.show(); + + QShortcut *cut1 = setupShortcut(&pb1, "shortcut1-pb1", TriggerSlot1, "M"); + QShortcut *cut2 = setupShortcut(&pb1, "shortcut2-pb2", TriggerSlot2, "M"); + + currentResult = NoResult; + sendKeyEvents( Qt::Key_M, 'm' ); + QCOMPARE( currentResult, Ambiguous ); + QCOMPARE( ambigResult, Slot1Triggered ); + + currentResult = NoResult; + sendKeyEvents( Qt::Key_M, 'm' ); + QCOMPARE( currentResult, Ambiguous ); + QCOMPARE( ambigResult, Slot2Triggered ); + + currentResult = NoResult; + sendKeyEvents( Qt::Key_M, 'm' ); + QCOMPARE( currentResult, Ambiguous ); + QCOMPARE( ambigResult, Slot1Triggered ); + + clearAllShortcuts(); + cut1 = 0; cut2 = 0; +} + + +// ------------------------------------------------------------------ +// Unicode and non-unicode Elements --------------------------------- +// ------------------------------------------------------------------ +void tst_QShortcut::unicodeCompare() +{ + clearAllShortcuts(); + /* Testing Unicode/non-Unicode Shortcuts + Qt::Key_M on Pushbutton 1 + Qt::Key_M on Pushbutton 2 + */ + QPushButton pb1(mainW); + QPushButton pb2(mainW); + pb1.setObjectName("pushbutton-1"); + pb2.setObjectName("pushbutton-2"); + pb1.show(); // Must be show for QShortcutMap::correctSubWindow to trigger + pb2.show(); + + QKeySequence ks1("Ctrl+M"); // Unicode + QKeySequence ks2(Qt::CTRL+Qt::Key_M); // non-Unicode + QShortcut *cut1 = setupShortcut(&pb1, "shortcut1-pb1", TriggerSlot1, ks1); + QShortcut *cut2 = setupShortcut(&pb1, "shortcut2-pb2", TriggerSlot2, ks2); + + currentResult = NoResult; + sendKeyEvents( Qt::CTRL+Qt::Key_M, 0 ); + QCOMPARE( currentResult, Ambiguous ); + // They _are_ ambiguous, so the QKeySequence operator== + // should indicate the same + QVERIFY( ks1 == ks2 ); + QVERIFY( !(ks1 != ks2) ); + + clearAllShortcuts(); + cut1 = 0; cut2 = 0; +} + +// ------------------------------------------------------------------ +// Keypress consumption verification -------------------------------- +// ------------------------------------------------------------------ +void tst_QShortcut::keypressConsumption() +{ + clearAllShortcuts(); + edit->clear(); + QCOMPARE(edit->toPlainText().size(), 0); + + QShortcut *cut1 = setupShortcut(edit, "shortcut1-line", TriggerSlot1, "Ctrl+I, A"); + QShortcut *cut2 = setupShortcut(edit, "shortcut1-line", TriggerSlot2, "Ctrl+I, B"); + + currentResult = NoResult; + ambigResult = NoResult; + sendKeyEvents(edit, Qt::CTRL + Qt::Key_I, 0); // Send key to edit + QCOMPARE( currentResult, NoResult ); + QCOMPARE( ambigResult, NoResult ); + QCOMPARE(edit->toPlainText(), QString("")); + + // Make sure next keypress is eaten (failing multiple keysequence) + sendKeyEvents(edit, Qt::Key_C, 'c'); // Send key to edit + QCOMPARE( currentResult, NoResult ); + QCOMPARE( ambigResult, NoResult ); + QCOMPARE(edit->toPlainText(), QString("")); + + // Next keypress should be normal + sendKeyEvents(edit, Qt::Key_C, 'c'); // Send key to edit + QCOMPARE( currentResult, NoResult ); + QCOMPARE( ambigResult, NoResult ); + QCOMPARE(edit->toPlainText(), QString("c")); + + currentResult = NoResult; + ambigResult = NoResult; + edit->clear(); + QCOMPARE(edit->toPlainText().size(), 0); + + cut1->setEnabled(false); + cut2->setEnabled(false); + + // Make sure keypresses is passed on, since all multiple keysequences + // with Ctrl+I are disabled + sendKeyEvents(edit, Qt::CTRL + Qt::Key_I, 0); // Send key to edit + QCOMPARE( currentResult, NoResult ); + QCOMPARE( ambigResult, NoResult ); + QVERIFY(edit->toPlainText().endsWith("")); + + sendKeyEvents(edit, Qt::Key_A, 'a'); // Send key to edit + QCOMPARE( currentResult, NoResult ); + QCOMPARE( ambigResult, NoResult ); + QVERIFY(edit->toPlainText().endsWith("a")); + + clearAllShortcuts(); +} + +// ------------------------------------------------------------------ +// Context Validation ----------------------------------------------- +// ------------------------------------------------------------------ +void tst_QShortcut::context() +{ + clearAllShortcuts(); + + QWidget myBox; + TestEdit *other1 = new TestEdit(&myBox, "test_edit_other1"); + TestEdit *other2 = new TestEdit(&myBox, "test_edit_other2"); + QHBoxLayout *layout = new QHBoxLayout(&myBox); + layout->addWidget(other1); + layout->addWidget(other2); + myBox.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&myBox); +#endif + + setupShortcut(other1, "ActiveWindow", TriggerSlot1, QKeySequence("Alt+1"), Qt::WindowShortcut); + setupShortcut(other2, "Focus", TriggerSlot2, QKeySequence("Alt+2"), Qt::WidgetShortcut); + setupShortcut(edit, "Application", TriggerSlot3, QKeySequence("Alt+3"), Qt::ApplicationShortcut); + + currentResult = NoResult; + ambigResult = NoResult; + edit->clear(); + other1->clear(); + other2->clear(); + + // edit doens't have focus, so ActiveWindow context should work + // ..but Focus context shouldn't.. + // Changing focus to edit should make focus context work + // Application context should always work + + + // Focus on 'other1' edit, so Active Window context should trigger + other1->activateWindow(); // <--- + QApplication::setActiveWindow(other1); + QCOMPARE(qApp->activeWindow(), other1->window()); + QCOMPARE(qApp->focusWidget(), (QWidget *)other1); + + currentResult = NoResult; + ambigResult = NoResult; + edit->clear(); + other1->clear(); + other2->clear(); + + QCOMPARE(qApp->focusWidget(), (QWidget *)other1); + sendKeyEvents(other1, Qt::ALT+Qt::Key_1); + QCOMPARE(currentResult, Slot1Triggered); + QCOMPARE(ambigResult, NoResult); + QCOMPARE(edit->toPlainText(), QString("")); + QCOMPARE(other1->toPlainText(), QString("")); + QCOMPARE(other2->toPlainText(), QString("")); + + // ..but not Focus context on 'other2'.. + currentResult = NoResult; + ambigResult = NoResult; + edit->clear(); + other1->clear(); + other2->clear(); + + sendKeyEvents(other1, Qt::ALT+Qt::Key_2); + QCOMPARE(currentResult, NoResult); + QCOMPARE(ambigResult, NoResult); + QCOMPARE(edit->toPlainText(), QString("")); + QCOMPARE(other1->toPlainText(), QString("")); + QCOMPARE(other2->toPlainText(), QString("")); + + // ..however, application global context on 'edit' should.. + currentResult = NoResult; + ambigResult = NoResult; + edit->clear(); + other1->clear(); + other2->clear(); + + sendKeyEvents(other1, Qt::ALT+Qt::Key_3); + QCOMPARE(currentResult, Slot3Triggered); + QCOMPARE(ambigResult, NoResult); + QCOMPARE(edit->toPlainText(), QString("")); + QCOMPARE(other1->toPlainText(), QString("")); + QCOMPARE(other2->toPlainText(), QString("")); + + // Changing focus to 'other2' should make the Focus context there work + other2->activateWindow(); + other2->setFocus(); // ### + qApp->syncX(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(other2); +#endif + QTest::qWait(100); + QCOMPARE(qApp->activeWindow(), other2->window()); + QCOMPARE(qApp->focusWidget(), (QWidget *)other2); + + currentResult = NoResult; + ambigResult = NoResult; + edit->clear(); + other1->clear(); + other2->clear(); + + sendKeyEvents(other2, Qt::ALT+Qt::Key_2); + QCOMPARE(currentResult, Slot2Triggered); + QCOMPARE(ambigResult, NoResult); + QCOMPARE(edit->toPlainText(), QString("")); + QCOMPARE(other1->toPlainText(), QString("")); + QCOMPARE(other2->toPlainText(), QString("")); + + clearAllShortcuts(); + delete other1; + delete other2; + edit->activateWindow(); + qApp->syncX(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(edit); +#endif + QTest::qWait(100); +} + +// ------------------------------------------------------------------ +// Element Testing helper functions --------------------------------- +// ------------------------------------------------------------------ +void tst_QShortcut::clearAllShortcuts() +{ + qDeleteAll(shortcuts); + shortcuts.clear(); +} + +QShortcut *tst_QShortcut::setupShortcut(int testWidget, const QKeySequence &ks) +{ + return setupShortcut(mainW, QTest::currentDataTag() ? QTest::currentDataTag() : "", testWidget, ks); +} + +QShortcut *tst_QShortcut::setupShortcut(int testWidget, const QString &txt, int k1, int k2, int k3, int k4) +{ + return setupShortcut(mainW, QTest::currentDataTag() ? QTest::currentDataTag() : "", testWidget, + (txt.isEmpty() ? QKeySequence(k1, k2, k3, k4) : QKeySequence(txt))); +} + +QShortcut *tst_QShortcut::setupShortcut(QWidget *parent, const char *name, int testWidget, const QString &txt, int k1, int k2, int k3, int k4) +{ + return setupShortcut(parent, name, testWidget, + (txt.isEmpty() ? QKeySequence(k1, k2, k3, k4) : QKeySequence(txt))); +} + +QShortcut *tst_QShortcut::setupShortcut(QWidget *parent, const char *name, int testWidget, + const QKeySequence &ks, Qt::ShortcutContext context) +{ + // Set up shortcut for next test + QShortcut *cut = new QShortcut(QKeySequence(), parent, 0, 0, context); + cut->setObjectName(name); + cut->setKey(ks); + + const char *normal = 0; + const char *ambig = 0; + switch(testWidget) + { + case TriggerSlot1: + normal = SLOT(slotTrig1()); + ambig = SLOT(ambigSlot1()); + break; + case TriggerSlot2: + normal = SLOT(slotTrig2()); + ambig = SLOT(ambigSlot2()); + break; + case TriggerSlot3: + normal = SLOT(slotTrig3()); + ambig = SLOT(ambigSlot3()); + break; + case TriggerSlot4: + normal = SLOT(slotTrig4()); + ambig = SLOT(ambigSlot4()); + break; + case TriggerSlot5: + normal = SLOT(slotTrig5()); + ambig = SLOT(ambigSlot5()); + break; + case TriggerSlot6: + normal = SLOT(slotTrig6()); + ambig = SLOT(ambigSlot6()); + break; + case TriggerSlot7: + normal = SLOT(slotTrig7()); + ambig = SLOT(ambigSlot7()); + break; + } + connect(cut, SIGNAL(activated()), this, normal); + connect(cut, SIGNAL(activatedAmbiguously()), this, ambig); + connect(cut, SIGNAL(destroyed(QObject*)), this, SLOT(shortcutDestroyed(QObject*))); + shortcuts.append(cut); + return cut; +} + +void tst_QShortcut::shortcutDestroyed(QObject* obj) +{ + shortcuts.removeAll(static_cast(obj)); +} + +void tst_QShortcut::sendKeyEvents(int k1, QChar c1, int k2, QChar c2, int k3, QChar c3, int k4, QChar c4) +{ + sendKeyEvents(mainW, k1, c1, k2, c2, k3, c3, k4, c4); +} + +void tst_QShortcut::sendKeyEvents(QWidget *w, int k1, QChar c1, int k2, QChar c2, int k3, QChar c3, int k4, QChar c4) +{ + Qt::KeyboardModifiers b1 = toButtons( k1 ); + Qt::KeyboardModifiers b2 = toButtons( k2 ); + Qt::KeyboardModifiers b3 = toButtons( k3 ); + Qt::KeyboardModifiers b4 = toButtons( k4 ); + k1 &= ~Qt::MODIFIER_MASK; + k2 &= ~Qt::MODIFIER_MASK; + k3 &= ~Qt::MODIFIER_MASK; + k4 &= ~Qt::MODIFIER_MASK; + + + if (k1 || c1.toAscii()) { + QString c(c1.unicode() == QChar::Null ? QString() : QString(c1)); + QTest::sendKeyEvent(QTest::Press, w, static_cast(k1), c, b1); + QTest::sendKeyEvent(QTest::Release, w, static_cast(k1), c, b1); + } + + if (k2 || c2.toAscii()) { + QString c(c2.unicode() == QChar::Null ? QString() : QString(c2)); + QTest::sendKeyEvent(QTest::Press, w, static_cast(k2), c, b2); + QTest::sendKeyEvent(QTest::Release, w, static_cast(k2), c, b2); + } + + if (k3 || c3.toAscii()) { + QString c(c3.unicode() == QChar::Null ? QString() : QString(c3)); + QTest::sendKeyEvent(QTest::Press, w, static_cast(k3), c, b3); + QTest::sendKeyEvent(QTest::Release, w, static_cast(k3), c, b3); + } + + if (k4 || c4.toAscii()) { + QString c(c4.unicode() == QChar::Null ? QString() : QString(c4)); + QTest::sendKeyEvent(QTest::Press, w, static_cast(k4), c, b4); + QTest::sendKeyEvent(QTest::Release, w, static_cast(k4), c, b4); + } +} + +void tst_QShortcut::testElement() +{ + currentResult = NoResult; + QFETCH(int, action); + QFETCH(int, testWidget); + QFETCH(QString, txt); + QFETCH(int, k1); + QFETCH(int, c1); + QFETCH(int, k2); + QFETCH(int, c2); + QFETCH(int, k3); + QFETCH(int, c3); + QFETCH(int, k4); + QFETCH(int, c4); + QFETCH(int, result); + + if (action == ClearAll) { + clearAllShortcuts(); + QCOMPARE(TRUE, TRUE); + } else if (action == SetupAccel) { + setupShortcut(testWidget, txt, k1, k2, k3, k4); + QCOMPARE(TRUE, TRUE); + } else { + sendKeyEvents(k1, c1, k2, c2, k3, c3, k4, c4); + QCOMPARE(int(currentResult), result); + } +} + +QTEST_MAIN(tst_QShortcut) +#include "tst_qshortcut.moc"