# HG changeset patch # User Brendan Donegan # Date 1276252857 -3600 # Node ID 72f7e4177ac7f38f71cb06fdc0e7d038c8e19e45 # Parent 14c7476c20d20db7ebdeb68a56cc33312a8381ba Smoketest for Qt: Gestures and QApplication diff -r 14c7476c20d2 -r 72f7e4177ac7 qt/gestures/smoketest_qgestures.pro --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qt/gestures/smoketest_qgestures.pro Fri Jun 11 11:40:57 2010 +0100 @@ -0,0 +1,5 @@ +load(qttest_p4) +SOURCES += tst_gestures.cpp + + + diff -r 14c7476c20d2 -r 72f7e4177ac7 qt/gestures/tst_gestures.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qt/gestures/tst_gestures.cpp Fri Jun 11 11:40:57 2010 +0100 @@ -0,0 +1,1660 @@ +/**************************************************************************** +** +** Copyright (C) 2010 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 "../shared/util.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +//TESTED_CLASS= +//TESTED_FILES= + +static QPointF mapToGlobal(const QPointF &pt, QGraphicsItem *item, QGraphicsView *view) +{ + return view->mapToGlobal(view->mapFromScene(item->mapToScene(pt))); +} + +class CustomGesture : public QGesture +{ + Q_OBJECT +public: + static Qt::GestureType GestureType; + + CustomGesture(QObject *parent = 0) + : QGesture(parent), serial(0) + { + } + + int serial; + + static const int SerialMaybeThreshold; + static const int SerialStartedThreshold; + static const int SerialFinishedThreshold; +}; +Qt::GestureType CustomGesture::GestureType = Qt::CustomGesture; +const int CustomGesture::SerialMaybeThreshold = 1; +const int CustomGesture::SerialStartedThreshold = 3; +const int CustomGesture::SerialFinishedThreshold = 6; + +class CustomEvent : public QEvent +{ +public: + static int EventType; + + CustomEvent(int serial_ = 0) + : QEvent(QEvent::Type(CustomEvent::EventType)), + serial(serial_), hasHotSpot(false) + { + } + + int serial; + QPointF hotSpot; + bool hasHotSpot; +}; +int CustomEvent::EventType = 0; + +class CustomGestureRecognizer : public QGestureRecognizer +{ +public: + static bool ConsumeEvents; + + CustomGestureRecognizer() + { + if (!CustomEvent::EventType) + CustomEvent::EventType = QEvent::registerEventType(); + } + + QGesture* create(QObject *) + { + return new CustomGesture; + } + + QGestureRecognizer::Result recognize(QGesture *state, QObject*, QEvent *event) + { + if (event->type() == CustomEvent::EventType) { + QGestureRecognizer::Result result = 0; + if (CustomGestureRecognizer::ConsumeEvents) + result |= QGestureRecognizer::ConsumeEventHint; + CustomGesture *g = static_cast(state); + CustomEvent *e = static_cast(event); + g->serial = e->serial; + if (e->hasHotSpot) + g->setHotSpot(e->hotSpot); + if (g->serial >= CustomGesture::SerialFinishedThreshold) + result |= QGestureRecognizer::FinishGesture; + else if (g->serial >= CustomGesture::SerialStartedThreshold) + result |= QGestureRecognizer::TriggerGesture; + else if (g->serial >= CustomGesture::SerialMaybeThreshold) + result |= QGestureRecognizer::MayBeGesture; + else + result = QGestureRecognizer::CancelGesture; + return result; + } + return QGestureRecognizer::Ignore; + } + + void reset(QGesture *state) + { + CustomGesture *g = static_cast(state); + g->serial = 0; + QGestureRecognizer::reset(state); + } +}; +bool CustomGestureRecognizer::ConsumeEvents = false; + +// same as CustomGestureRecognizer but triggers early without the maybe state +class CustomContinuousGestureRecognizer : public QGestureRecognizer +{ +public: + CustomContinuousGestureRecognizer() + { + if (!CustomEvent::EventType) + CustomEvent::EventType = QEvent::registerEventType(); + } + + QGesture* create(QObject *) + { + return new CustomGesture; + } + + QGestureRecognizer::Result recognize(QGesture *state, QObject*, QEvent *event) + { + if (event->type() == CustomEvent::EventType) { + QGestureRecognizer::Result result = QGestureRecognizer::ConsumeEventHint; + CustomGesture *g = static_cast(state); + CustomEvent *e = static_cast(event); + g->serial = e->serial; + if (e->hasHotSpot) + g->setHotSpot(e->hotSpot); + if (g->serial >= CustomGesture::SerialFinishedThreshold) + result |= QGestureRecognizer::FinishGesture; + else if (g->serial >= CustomGesture::SerialMaybeThreshold) + result |= QGestureRecognizer::TriggerGesture; + else + result = QGestureRecognizer::CancelGesture; + return result; + } + return QGestureRecognizer::Ignore; + } + + void reset(QGesture *state) + { + CustomGesture *g = static_cast(state); + g->serial = 0; + QGestureRecognizer::reset(state); + } +}; + +class GestureWidget : public QWidget +{ + Q_OBJECT +public: + GestureWidget(const char *name = 0, QWidget *parent = 0) + : QWidget(parent) + { + if (name) + setObjectName(QLatin1String(name)); + reset(); + acceptGestureOverride = false; + } + void reset() + { + customEventsReceived = 0; + gestureEventsReceived = 0; + gestureOverrideEventsReceived = 0; + events.clear(); + overrideEvents.clear(); + ignoredGestures.clear(); + } + + int customEventsReceived; + int gestureEventsReceived; + int gestureOverrideEventsReceived; + struct Events + { + QList all; + QList started; + QList updated; + QList finished; + QList canceled; + + void clear() + { + all.clear(); + started.clear(); + updated.clear(); + finished.clear(); + canceled.clear(); + } + } events, overrideEvents; + + bool acceptGestureOverride; + QSet ignoredGestures; + +protected: + bool event(QEvent *event) + { + Events *eventsPtr = 0; + if (event->type() == QEvent::Gesture) { + QGestureEvent *e = static_cast(event); + ++gestureEventsReceived; + eventsPtr = &events; + foreach(Qt::GestureType type, ignoredGestures) + e->ignore(e->gesture(type)); + } else if (event->type() == QEvent::GestureOverride) { + ++gestureOverrideEventsReceived; + eventsPtr = &overrideEvents; + if (acceptGestureOverride) + event->accept(); + } + if (eventsPtr) { + QGestureEvent *e = static_cast(event); + QList gestures = e->gestures(); + foreach(QGesture *g, gestures) { + eventsPtr->all << g->gestureType(); + switch(g->state()) { + case Qt::GestureStarted: + eventsPtr->started << g->gestureType(); + break; + case Qt::GestureUpdated: + eventsPtr->updated << g->gestureType(); + break; + case Qt::GestureFinished: + eventsPtr->finished << g->gestureType(); + break; + case Qt::GestureCanceled: + eventsPtr->canceled << g->gestureType(); + break; + default: + Q_ASSERT(false); + } + } + } else if (event->type() == CustomEvent::EventType) { + ++customEventsReceived; + } else { + return QWidget::event(event); + } + return true; + } +}; + +// TODO rename to sendGestureSequence +static void sendCustomGesture(CustomEvent *event, QObject *object, QGraphicsScene *scene = 0) +{ + for (int i = CustomGesture::SerialMaybeThreshold; + i <= CustomGesture::SerialFinishedThreshold; ++i) { + event->serial = i; + if (scene) + scene->sendEvent(qobject_cast(object), event); + else + QApplication::sendEvent(object, event); + } +} + +class tst_Gestures : public QObject +{ +Q_OBJECT + +public: + tst_Gestures(); + virtual ~tst_Gestures(); + +public slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + +private slots: + void customGesture(); + /* + void autoCancelingGestures(); + void gestureOverChild(); + void multipleWidgetOnlyGestureInTree(); + void conflictingGestures(); + void finishedWithoutStarted(); + void unknownGesture(); + void graphicsItemGesture(); + void graphicsItemTreeGesture(); + void explicitGraphicsObjectTarget(); + void gestureOverChildGraphicsItem(); + void twoGesturesOnDifferentLevel(); + void multipleGesturesInTree(); + void multipleGesturesInComplexTree(); + void testMapToScene(); + void ungrabGesture(); + void consumeEventHint(); + void unregisterRecognizer(); + void autoCancelGestures(); + void autoCancelGestures2(); + void panelPropagation(); + void panelStacksBehindParent(); + */ +}; + +tst_Gestures::tst_Gestures() +{ +} + +tst_Gestures::~tst_Gestures() +{ +} + +void tst_Gestures::initTestCase() +{ + CustomGesture::GestureType = QGestureRecognizer::registerRecognizer(new CustomGestureRecognizer); + QVERIFY(CustomGesture::GestureType != Qt::GestureType(0)); + QVERIFY(CustomGesture::GestureType != Qt::CustomGesture); +} + +void tst_Gestures::cleanupTestCase() +{ + QGestureRecognizer::unregisterRecognizer(CustomGesture::GestureType); +} + +void tst_Gestures::init() +{ +} + +void tst_Gestures::cleanup() +{ +} + +void tst_Gestures::customGesture() +{ + GestureWidget widget; + widget.grabGesture(CustomGesture::GestureType, Qt::DontStartGestureOnChildren); + CustomEvent event; + sendCustomGesture(&event, &widget); + + static const int TotalGestureEventsCount = CustomGesture::SerialFinishedThreshold - CustomGesture::SerialStartedThreshold + 1; + static const int TotalCustomEventsCount = CustomGesture::SerialFinishedThreshold - CustomGesture::SerialMaybeThreshold + 1; + QCOMPARE(widget.customEventsReceived, TotalCustomEventsCount); + QCOMPARE(widget.gestureEventsReceived, TotalGestureEventsCount); + QCOMPARE(widget.gestureOverrideEventsReceived, 0); + QCOMPARE(widget.events.all.size(), TotalGestureEventsCount); + for(int i = 0; i < widget.events.all.size(); ++i) + QCOMPARE(widget.events.all.at(i), CustomGesture::GestureType); + QCOMPARE(widget.events.started.size(), 1); + QCOMPARE(widget.events.updated.size(), TotalGestureEventsCount - 2); + QCOMPARE(widget.events.finished.size(), 1); + QCOMPARE(widget.events.canceled.size(), 0); +} + +/* +void tst_Gestures::consumeEventHint() +{ + GestureWidget widget; + widget.grabGesture(CustomGesture::GestureType, Qt::DontStartGestureOnChildren); + + CustomGestureRecognizer::ConsumeEvents = true; + CustomEvent event; + sendCustomGesture(&event, &widget); + CustomGestureRecognizer::ConsumeEvents = false; + + QCOMPARE(widget.customEventsReceived, 0); +} +*/ + +/* + +void tst_Gestures::autoCancelingGestures() +{ + GestureWidget widget; + widget.grabGesture(CustomGesture::GestureType, Qt::DontStartGestureOnChildren); + // send partial gesture. The gesture will be in the "maybe" state, but will + // never get enough events to fire, so Qt will have to kill it. + CustomEvent ev; + for (int i = CustomGesture::SerialMaybeThreshold; + i < CustomGesture::SerialStartedThreshold; ++i) { + ev.serial = i; + QApplication::sendEvent(&widget, &ev); + } + // wait long enough so the gesture manager will cancel the gesture + QTest::qWait(5000); + QCOMPARE(widget.customEventsReceived, CustomGesture::SerialStartedThreshold - CustomGesture::SerialMaybeThreshold); + QCOMPARE(widget.gestureEventsReceived, 0); + QCOMPARE(widget.gestureOverrideEventsReceived, 0); + QCOMPARE(widget.events.all.size(), 0); +} + +void tst_Gestures::gestureOverChild() +{ + GestureWidget widget("widget"); + QVBoxLayout *l = new QVBoxLayout(&widget); + GestureWidget *child = new GestureWidget("child"); + l->addWidget(child); + + widget.grabGesture(CustomGesture::GestureType, Qt::DontStartGestureOnChildren); + + CustomEvent event; + sendCustomGesture(&event, child); + + static const int TotalGestureEventsCount = CustomGesture::SerialFinishedThreshold - CustomGesture::SerialStartedThreshold + 1; + static const int TotalCustomEventsCount = CustomGesture::SerialFinishedThreshold - CustomGesture::SerialMaybeThreshold + 1; + + QCOMPARE(child->customEventsReceived, TotalCustomEventsCount); + QCOMPARE(widget.customEventsReceived, 0); + QCOMPARE(child->gestureEventsReceived, 0); + QCOMPARE(child->gestureOverrideEventsReceived, 0); + QCOMPARE(widget.gestureEventsReceived, 0); + QCOMPARE(widget.gestureOverrideEventsReceived, 0); + + // enable gestures over the children + widget.grabGesture(CustomGesture::GestureType); + + widget.reset(); + child->reset(); + + sendCustomGesture(&event, child); + + QCOMPARE(child->customEventsReceived, TotalCustomEventsCount); + QCOMPARE(widget.customEventsReceived, 0); + + QCOMPARE(child->gestureEventsReceived, 0); + QCOMPARE(child->gestureOverrideEventsReceived, 0); + QCOMPARE(widget.gestureEventsReceived, TotalGestureEventsCount); + QCOMPARE(widget.gestureOverrideEventsReceived, 0); + for(int i = 0; i < widget.events.all.size(); ++i) + QCOMPARE(widget.events.all.at(i), CustomGesture::GestureType); + QCOMPARE(widget.events.started.size(), 1); + QCOMPARE(widget.events.updated.size(), TotalGestureEventsCount - 2); + QCOMPARE(widget.events.finished.size(), 1); + QCOMPARE(widget.events.canceled.size(), 0); +} + +void tst_Gestures::multipleWidgetOnlyGestureInTree() +{ + GestureWidget parent("parent"); + QVBoxLayout *l = new QVBoxLayout(&parent); + GestureWidget *child = new GestureWidget("child"); + l->addWidget(child); + + parent.grabGesture(CustomGesture::GestureType, Qt::DontStartGestureOnChildren); + child->grabGesture(CustomGesture::GestureType, Qt::DontStartGestureOnChildren); + + static const int TotalGestureEventsCount = CustomGesture::SerialFinishedThreshold - CustomGesture::SerialStartedThreshold + 1; + static const int TotalCustomEventsCount = CustomGesture::SerialFinishedThreshold - CustomGesture::SerialMaybeThreshold + 1; + + // sending events to the child and making sure there is no conflict + CustomEvent event; + sendCustomGesture(&event, child); + + QCOMPARE(child->customEventsReceived, TotalCustomEventsCount); + QCOMPARE(parent.customEventsReceived, 0); + QCOMPARE(child->gestureEventsReceived, TotalGestureEventsCount); + QCOMPARE(child->gestureOverrideEventsReceived, 0); + QCOMPARE(parent.gestureEventsReceived, 0); + QCOMPARE(parent.gestureOverrideEventsReceived, 0); + + parent.reset(); + child->reset(); + + // same for the parent widget + sendCustomGesture(&event, &parent); + + QCOMPARE(child->customEventsReceived, 0); + QCOMPARE(parent.customEventsReceived, TotalCustomEventsCount); + QCOMPARE(child->gestureEventsReceived, 0); + QCOMPARE(child->gestureOverrideEventsReceived, 0); + QCOMPARE(parent.gestureEventsReceived, TotalGestureEventsCount); + QCOMPARE(parent.gestureOverrideEventsReceived, 0); +} + +void tst_Gestures::conflictingGestures() +{ + GestureWidget parent("parent"); + QVBoxLayout *l = new QVBoxLayout(&parent); + GestureWidget *child = new GestureWidget("child"); + l->addWidget(child); + + parent.grabGesture(CustomGesture::GestureType); + child->grabGesture(CustomGesture::GestureType); + + static const int TotalGestureEventsCount = CustomGesture::SerialFinishedThreshold - CustomGesture::SerialStartedThreshold + 1; + + // child accepts the override, parent will not receive anything + parent.acceptGestureOverride = false; + child->acceptGestureOverride = true; + + // sending events to the child and making sure there is no conflict + CustomEvent event; + sendCustomGesture(&event, child); + + QCOMPARE(child->gestureOverrideEventsReceived, 1); + QCOMPARE(child->gestureEventsReceived, TotalGestureEventsCount); + QCOMPARE(parent.gestureOverrideEventsReceived, 0); + QCOMPARE(parent.gestureEventsReceived, 0); + + parent.reset(); + child->reset(); + + // parent accepts the override + parent.acceptGestureOverride = true; + child->acceptGestureOverride = false; + + // sending events to the child and making sure there is no conflict + sendCustomGesture(&event, child); + + QCOMPARE(child->gestureOverrideEventsReceived, 1); + QCOMPARE(child->gestureEventsReceived, 0); + QCOMPARE(parent.gestureOverrideEventsReceived, 1); + QCOMPARE(parent.gestureEventsReceived, TotalGestureEventsCount); + + parent.reset(); + child->reset(); + + // nobody accepts the override, we will send normal events to the closest + // context (i.e. to the child widget) and it will be propagated and + // accepted by the parent widget + parent.acceptGestureOverride = false; + child->acceptGestureOverride = false; + child->ignoredGestures << CustomGesture::GestureType; + + // sending events to the child and making sure there is no conflict + sendCustomGesture(&event, child); + + QCOMPARE(child->gestureOverrideEventsReceived, 1); + QCOMPARE(child->gestureEventsReceived, 1); + QCOMPARE(parent.gestureOverrideEventsReceived, 1); + QCOMPARE(parent.gestureEventsReceived, TotalGestureEventsCount); + + parent.reset(); + child->reset(); + + // nobody accepts the override, and nobody accepts the gesture event + parent.acceptGestureOverride = false; + child->acceptGestureOverride = false; + parent.ignoredGestures << CustomGesture::GestureType; + child->ignoredGestures << CustomGesture::GestureType; + + // sending events to the child and making sure there is no conflict + sendCustomGesture(&event, child); + + QCOMPARE(child->gestureOverrideEventsReceived, 1); + QCOMPARE(child->gestureEventsReceived, TotalGestureEventsCount); + QCOMPARE(parent.gestureOverrideEventsReceived, 1); + QCOMPARE(parent.gestureEventsReceived, 1); + + parent.reset(); + child->reset(); + + // we set an attribute to make sure all gesture events are propagated + parent.grabGesture(CustomGesture::GestureType, Qt::ReceivePartialGestures); + parent.acceptGestureOverride = false; + child->acceptGestureOverride = false; + parent.ignoredGestures << CustomGesture::GestureType; + child->ignoredGestures << CustomGesture::GestureType; + + // sending events to the child and making sure there is no conflict + sendCustomGesture(&event, child); + + QCOMPARE(child->gestureOverrideEventsReceived, 1); + QCOMPARE(child->gestureEventsReceived, TotalGestureEventsCount); + QCOMPARE(parent.gestureOverrideEventsReceived, 1); + QCOMPARE(parent.gestureEventsReceived, TotalGestureEventsCount); + + parent.reset(); + child->reset(); + + Qt::GestureType ContinuousGesture = QGestureRecognizer::registerRecognizer(new CustomContinuousGestureRecognizer); + static const int ContinuousGestureEventsCount = CustomGesture::SerialFinishedThreshold - CustomGesture::SerialMaybeThreshold + 1; + child->grabGesture(ContinuousGesture); + // child accepts override. And it also receives another custom gesture. + parent.acceptGestureOverride = false; + child->acceptGestureOverride = true; + sendCustomGesture(&event, child); + + QCOMPARE(child->gestureOverrideEventsReceived, 1); + QVERIFY(child->gestureEventsReceived > TotalGestureEventsCount); + QCOMPARE(child->events.all.count(), TotalGestureEventsCount + ContinuousGestureEventsCount); + QCOMPARE(parent.gestureOverrideEventsReceived, 0); + QCOMPARE(parent.gestureEventsReceived, 0); + + QGestureRecognizer::unregisterRecognizer(ContinuousGesture); +} + +void tst_Gestures::finishedWithoutStarted() +{ + GestureWidget widget; + widget.grabGesture(CustomGesture::GestureType, Qt::DontStartGestureOnChildren); + + // the gesture will claim it finished, but it was never started. + CustomEvent ev; + ev.serial = CustomGesture::SerialFinishedThreshold; + QApplication::sendEvent(&widget, &ev); + + QCOMPARE(widget.customEventsReceived, 1); + QCOMPARE(widget.gestureEventsReceived, 2); + QCOMPARE(widget.gestureOverrideEventsReceived, 0); + QCOMPARE(widget.events.all.size(), 2); + QCOMPARE(widget.events.started.size(), 1); + QCOMPARE(widget.events.updated.size(), 0); + QCOMPARE(widget.events.finished.size(), 1); + QCOMPARE(widget.events.canceled.size(), 0); +} + +*/ + +/* +void tst_Gestures::unknownGesture() +{ + GestureWidget widget; + widget.grabGesture(CustomGesture::GestureType, Qt::DontStartGestureOnChildren); + widget.grabGesture(Qt::CustomGesture, Qt::DontStartGestureOnChildren); + widget.grabGesture(Qt::GestureType(Qt::PanGesture+512), Qt::DontStartGestureOnChildren); + + CustomEvent event; + sendCustomGesture(&event, &widget); + + static const int TotalGestureEventsCount = CustomGesture::SerialFinishedThreshold - CustomGesture::SerialStartedThreshold + 1; + + QCOMPARE(widget.gestureEventsReceived, TotalGestureEventsCount); +} +*/ + +static const QColor InstanceColors[] = { + Qt::blue, Qt::red, Qt::green, Qt::gray, Qt::yellow +}; + +class GestureItem : public QGraphicsObject +{ + static int InstanceCount; + +public: + GestureItem(const char *name = 0) + { + instanceNumber = InstanceCount++; + if (name) + setObjectName(QLatin1String(name)); + size = QRectF(0, 0, 100, 100); + customEventsReceived = 0; + gestureEventsReceived = 0; + gestureOverrideEventsReceived = 0; + events.clear(); + overrideEvents.clear(); + acceptGestureOverride = false; + } + ~GestureItem() + { + --InstanceCount; + } + + int customEventsReceived; + int gestureEventsReceived; + int gestureOverrideEventsReceived; + struct Events + { + QList all; + QList started; + QList updated; + QList finished; + QList canceled; + + void clear() + { + all.clear(); + started.clear(); + updated.clear(); + finished.clear(); + canceled.clear(); + } + } events, overrideEvents; + + bool acceptGestureOverride; + QSet ignoredGestures; + + QRectF size; + int instanceNumber; + + void reset() + { + customEventsReceived = 0; + gestureEventsReceived = 0; + gestureOverrideEventsReceived = 0; + events.clear(); + overrideEvents.clear(); + ignoredGestures.clear(); + } + +protected: + QRectF boundingRect() const + { + return size; + } + void paint(QPainter *p, const QStyleOptionGraphicsItem *, QWidget *) + { + QColor color = InstanceColors[instanceNumber % (sizeof(InstanceColors)/sizeof(InstanceColors[0]))]; + p->fillRect(boundingRect(), color); + } + + bool event(QEvent *event) + { + Events *eventsPtr = 0; + if (event->type() == QEvent::Gesture) { + ++gestureEventsReceived; + eventsPtr = &events; + QGestureEvent *e = static_cast(event); + foreach(Qt::GestureType type, ignoredGestures) + e->ignore(e->gesture(type)); + } else if (event->type() == QEvent::GestureOverride) { + ++gestureOverrideEventsReceived; + eventsPtr = &overrideEvents; + if (acceptGestureOverride) + event->accept(); + } + if (eventsPtr) { + QGestureEvent *e = static_cast(event); + QList gestures = e->gestures(); + foreach(QGesture *g, gestures) { + eventsPtr->all << g->gestureType(); + switch(g->state()) { + case Qt::GestureStarted: + eventsPtr->started << g->gestureType(); + break; + case Qt::GestureUpdated: + eventsPtr->updated << g->gestureType(); + break; + case Qt::GestureFinished: + eventsPtr->finished << g->gestureType(); + break; + case Qt::GestureCanceled: + eventsPtr->canceled << g->gestureType(); + break; + default: + Q_ASSERT(false); + } + } + } else if (event->type() == CustomEvent::EventType) { + ++customEventsReceived; + } else { + return QGraphicsObject::event(event); + } + return true; + } +}; +int GestureItem::InstanceCount = 0; + +/* +void tst_Gestures::graphicsItemGesture() +{ + QGraphicsScene scene; + QGraphicsView view(&scene); + view.setWindowFlags(Qt::X11BypassWindowManagerHint); + + GestureItem *item = new GestureItem("item"); + scene.addItem(item); + item->setPos(100, 100); + + view.show(); + QTest::qWaitForWindowShown(&view); + view.ensureVisible(scene.sceneRect()); + + view.viewport()->grabGesture(CustomGesture::GestureType, Qt::DontStartGestureOnChildren); + item->grabGesture(CustomGesture::GestureType); + + static const int TotalGestureEventsCount = CustomGesture::SerialFinishedThreshold - CustomGesture::SerialStartedThreshold + 1; + static const int TotalCustomEventsCount = CustomGesture::SerialFinishedThreshold - CustomGesture::SerialMaybeThreshold + 1; + + CustomEvent event; + // gesture without hotspot should not be delivered to items in the view + QTest::ignoreMessage(QtWarningMsg, "QGestureManager::deliverEvent: could not find the target for gesture"); + QTest::ignoreMessage(QtWarningMsg, "QGestureManager::deliverEvent: could not find the target for gesture"); + QTest::ignoreMessage(QtWarningMsg, "QGestureManager::deliverEvent: could not find the target for gesture"); + QTest::ignoreMessage(QtWarningMsg, "QGestureManager::deliverEvent: could not find the target for gesture"); + sendCustomGesture(&event, item, &scene); + + QCOMPARE(item->customEventsReceived, TotalCustomEventsCount); + QCOMPARE(item->gestureEventsReceived, 0); + QCOMPARE(item->gestureOverrideEventsReceived, 0); + + item->reset(); + + // make sure the event is properly delivered if only the hotspot is set. + event.hotSpot = mapToGlobal(QPointF(10, 10), item, &view); + event.hasHotSpot = true; + sendCustomGesture(&event, item, &scene); + + QCOMPARE(item->customEventsReceived, TotalCustomEventsCount); + QCOMPARE(item->gestureEventsReceived, TotalGestureEventsCount); + QCOMPARE(item->gestureOverrideEventsReceived, 0); + QCOMPARE(item->events.all.size(), TotalGestureEventsCount); + for(int i = 0; i < item->events.all.size(); ++i) + QCOMPARE(item->events.all.at(i), CustomGesture::GestureType); + QCOMPARE(item->events.started.size(), 1); + QCOMPARE(item->events.updated.size(), TotalGestureEventsCount - 2); + QCOMPARE(item->events.finished.size(), 1); + QCOMPARE(item->events.canceled.size(), 0); + + item->reset(); + + // send gesture to the item which ignores it. + item->ignoredGestures << CustomGesture::GestureType; + + event.hotSpot = mapToGlobal(QPointF(10, 10), item, &view); + event.hasHotSpot = true; + sendCustomGesture(&event, item, &scene); + QCOMPARE(item->customEventsReceived, TotalCustomEventsCount); + QCOMPARE(item->gestureEventsReceived, TotalGestureEventsCount); + QCOMPARE(item->gestureOverrideEventsReceived, 0); +} + +void tst_Gestures::graphicsItemTreeGesture() +{ + QGraphicsScene scene; + QGraphicsView view(&scene); + view.setWindowFlags(Qt::X11BypassWindowManagerHint); + + GestureItem *item1 = new GestureItem("item1"); + item1->setPos(100, 100); + item1->size = QRectF(0, 0, 350, 200); + scene.addItem(item1); + + GestureItem *item1_child1 = new GestureItem("item1_child1"); + item1_child1->setPos(50, 50); + item1_child1->size = QRectF(0, 0, 100, 100); + item1_child1->setParentItem(item1); + + GestureItem *item1_child2 = new GestureItem("item1_child2"); + item1_child2->size = QRectF(0, 0, 100, 100); + item1_child2->setPos(200, 50); + item1_child2->setParentItem(item1); + + view.show(); + QTest::qWaitForWindowShown(&view); + view.ensureVisible(scene.sceneRect()); + + view.viewport()->grabGesture(CustomGesture::GestureType, Qt::DontStartGestureOnChildren); + item1->grabGesture(CustomGesture::GestureType); + + static const int TotalGestureEventsCount = CustomGesture::SerialFinishedThreshold - CustomGesture::SerialStartedThreshold + 1; + + CustomEvent event; + event.hotSpot = mapToGlobal(QPointF(10, 10), item1_child1, &view); + event.hasHotSpot = true; + + item1->ignoredGestures << CustomGesture::GestureType; + sendCustomGesture(&event, item1_child1, &scene); + QCOMPARE(item1_child1->gestureOverrideEventsReceived, 0); + QCOMPARE(item1_child1->gestureEventsReceived, 0); + QCOMPARE(item1_child2->gestureEventsReceived, 0); + QCOMPARE(item1_child2->gestureOverrideEventsReceived, 0); + QCOMPARE(item1->gestureOverrideEventsReceived, 0); + QCOMPARE(item1->gestureEventsReceived, TotalGestureEventsCount); + + item1->reset(); item1_child1->reset(); item1_child2->reset(); + + item1_child1->grabGesture(CustomGesture::GestureType); + + item1->ignoredGestures << CustomGesture::GestureType; + item1_child1->ignoredGestures << CustomGesture::GestureType; + sendCustomGesture(&event, item1_child1, &scene); + QCOMPARE(item1_child1->gestureOverrideEventsReceived, 1); + QCOMPARE(item1_child1->gestureEventsReceived, TotalGestureEventsCount); + QCOMPARE(item1_child2->gestureEventsReceived, 0); + QCOMPARE(item1_child2->gestureOverrideEventsReceived, 0); + QCOMPARE(item1->gestureOverrideEventsReceived, 1); + QCOMPARE(item1->gestureEventsReceived, 1); +} + +void tst_Gestures::explicitGraphicsObjectTarget() +{ + QGraphicsScene scene; + QGraphicsView view(&scene); + view.setWindowFlags(Qt::X11BypassWindowManagerHint); + + GestureItem *item1 = new GestureItem("item1"); + scene.addItem(item1); + item1->setPos(100, 100); + item1->setZValue(1); + + GestureItem *item2 = new GestureItem("item2"); + scene.addItem(item2); + item2->setPos(100, 100); + item2->setZValue(5); + + GestureItem *item2_child1 = new GestureItem("item2_child1"); + scene.addItem(item2_child1); + item2_child1->setParentItem(item2); + item2_child1->setPos(10, 10); + + view.show(); + QTest::qWaitForWindowShown(&view); + view.ensureVisible(scene.sceneRect()); + + view.viewport()->grabGesture(CustomGesture::GestureType, Qt::DontStartGestureOnChildren); + item1->grabGesture(CustomGesture::GestureType, Qt::DontStartGestureOnChildren); + item2->grabGesture(CustomGesture::GestureType, Qt::DontStartGestureOnChildren); + item2_child1->grabGesture(CustomGesture::GestureType, Qt::DontStartGestureOnChildren); + + static const int TotalGestureEventsCount = CustomGesture::SerialFinishedThreshold - CustomGesture::SerialStartedThreshold + 1; + + // sending events to item1, but the hotSpot is set to item2 + CustomEvent event; + event.hotSpot = mapToGlobal(QPointF(15, 15), item2, &view); + event.hasHotSpot = true; + + sendCustomGesture(&event, item1, &scene); + + QCOMPARE(item1->gestureEventsReceived, 0); + QCOMPARE(item1->gestureOverrideEventsReceived, 1); + QCOMPARE(item2_child1->gestureEventsReceived, TotalGestureEventsCount); + QCOMPARE(item2_child1->gestureOverrideEventsReceived, 1); + QCOMPARE(item2_child1->events.all.size(), TotalGestureEventsCount); + for(int i = 0; i < item2_child1->events.all.size(); ++i) + QCOMPARE(item2_child1->events.all.at(i), CustomGesture::GestureType); + QCOMPARE(item2_child1->events.started.size(), 1); + QCOMPARE(item2_child1->events.updated.size(), TotalGestureEventsCount - 2); + QCOMPARE(item2_child1->events.finished.size(), 1); + QCOMPARE(item2_child1->events.canceled.size(), 0); + QCOMPARE(item2->gestureEventsReceived, 0); + QCOMPARE(item2->gestureOverrideEventsReceived, 1); +} + +void tst_Gestures::gestureOverChildGraphicsItem() +{ + QGraphicsScene scene; + QGraphicsView view(&scene); + view.setWindowFlags(Qt::X11BypassWindowManagerHint); + + GestureItem *item0 = new GestureItem("item0"); + scene.addItem(item0); + item0->setPos(0, 0); + item0->grabGesture(CustomGesture::GestureType); + item0->setZValue(1); + + GestureItem *item1 = new GestureItem("item1"); + scene.addItem(item1); + item1->setPos(100, 100); + item1->setZValue(5); + + GestureItem *item2 = new GestureItem("item2"); + scene.addItem(item2); + item2->setPos(100, 100); + item2->setZValue(10); + + GestureItem *item2_child1 = new GestureItem("item2_child1"); + scene.addItem(item2_child1); + item2_child1->setParentItem(item2); + item2_child1->setPos(0, 0); + + view.show(); + QTest::qWaitForWindowShown(&view); + view.ensureVisible(scene.sceneRect()); + + view.viewport()->grabGesture(CustomGesture::GestureType, Qt::DontStartGestureOnChildren); + item1->grabGesture(CustomGesture::GestureType); + + static const int TotalGestureEventsCount = CustomGesture::SerialFinishedThreshold - CustomGesture::SerialStartedThreshold + 1; + static const int TotalCustomEventsCount = CustomGesture::SerialFinishedThreshold - CustomGesture::SerialMaybeThreshold + 1; + + CustomEvent event; + event.hotSpot = mapToGlobal(QPointF(10, 10), item2_child1, &view); + event.hasHotSpot = true; + sendCustomGesture(&event, item0, &scene); + + QCOMPARE(item0->customEventsReceived, TotalCustomEventsCount); + QCOMPARE(item2_child1->gestureEventsReceived, 0); + QCOMPARE(item2_child1->gestureOverrideEventsReceived, 0); + QCOMPARE(item2->gestureEventsReceived, 0); + QCOMPARE(item2->gestureOverrideEventsReceived, 0); + QCOMPARE(item1->gestureEventsReceived, TotalGestureEventsCount); + QCOMPARE(item1->gestureOverrideEventsReceived, 0); + + item0->reset(); item1->reset(); item2->reset(); item2_child1->reset(); + item2->grabGesture(CustomGesture::GestureType); + item2->ignoredGestures << CustomGesture::GestureType; + + event.hotSpot = mapToGlobal(QPointF(10, 10), item2_child1, &view); + event.hasHotSpot = true; + sendCustomGesture(&event, item0, &scene); + + QCOMPARE(item2_child1->gestureEventsReceived, 0); + QCOMPARE(item2_child1->gestureOverrideEventsReceived, 0); + QCOMPARE(item2->gestureEventsReceived, 1); + QCOMPARE(item2->gestureOverrideEventsReceived, 1); + QCOMPARE(item1->gestureEventsReceived, TotalGestureEventsCount); + QCOMPARE(item1->gestureOverrideEventsReceived, 1); + + item0->reset(); item1->reset(); item2->reset(); item2_child1->reset(); + item2->grabGesture(CustomGesture::GestureType); + item2->ignoredGestures << CustomGesture::GestureType; + item1->ignoredGestures << CustomGesture::GestureType; + + event.hotSpot = mapToGlobal(QPointF(10, 10), item2_child1, &view); + event.hasHotSpot = true; + sendCustomGesture(&event, item0, &scene); + + QCOMPARE(item2_child1->gestureEventsReceived, 0); + QCOMPARE(item2_child1->gestureOverrideEventsReceived, 0); + QCOMPARE(item2->gestureEventsReceived, TotalGestureEventsCount); + QCOMPARE(item2->gestureOverrideEventsReceived, 1); + QCOMPARE(item1->gestureEventsReceived, 1); + QCOMPARE(item1->gestureOverrideEventsReceived, 1); + + item0->reset(); item1->reset(); item2->reset(); item2_child1->reset(); + item2->grabGesture(CustomGesture::GestureType); + item2->ignoredGestures << CustomGesture::GestureType; + item1->ignoredGestures << CustomGesture::GestureType; + item1->grabGesture(CustomGesture::GestureType, Qt::ReceivePartialGestures); + + event.hotSpot = mapToGlobal(QPointF(10, 10), item2_child1, &view); + event.hasHotSpot = true; + sendCustomGesture(&event, item0, &scene); + + QCOMPARE(item2_child1->gestureEventsReceived, 0); + QCOMPARE(item2_child1->gestureOverrideEventsReceived, 0); + QCOMPARE(item2->gestureEventsReceived, TotalGestureEventsCount); + QCOMPARE(item2->gestureOverrideEventsReceived, 1); + QCOMPARE(item1->gestureEventsReceived, TotalGestureEventsCount); + QCOMPARE(item1->gestureOverrideEventsReceived, 1); +} + +void tst_Gestures::twoGesturesOnDifferentLevel() +{ + GestureWidget parent("parent"); + QVBoxLayout *l = new QVBoxLayout(&parent); + GestureWidget *child = new GestureWidget("child"); + l->addWidget(child); + + Qt::GestureType SecondGesture = QGestureRecognizer::registerRecognizer(new CustomGestureRecognizer); + + parent.grabGesture(CustomGesture::GestureType); + child->grabGesture(SecondGesture); + + CustomEvent event; + // sending events that form a gesture to one widget, but they will be + // filtered by two different gesture recognizers and will generate two + // QGesture objects. Check that those gesture objects are delivered to + // different widgets properly. + sendCustomGesture(&event, child); + + static const int TotalGestureEventsCount = CustomGesture::SerialFinishedThreshold - CustomGesture::SerialStartedThreshold + 1; + static const int TotalCustomEventsCount = CustomGesture::SerialFinishedThreshold - CustomGesture::SerialMaybeThreshold + 1; + + QCOMPARE(child->customEventsReceived, TotalCustomEventsCount); + QCOMPARE(child->gestureEventsReceived, TotalGestureEventsCount); + QCOMPARE(child->gestureOverrideEventsReceived, 0); + QCOMPARE(child->events.all.size(), TotalGestureEventsCount); + for(int i = 0; i < child->events.all.size(); ++i) + QCOMPARE(child->events.all.at(i), SecondGesture); + + QCOMPARE(parent.gestureEventsReceived, TotalGestureEventsCount); + QCOMPARE(parent.gestureOverrideEventsReceived, 0); + QCOMPARE(parent.events.all.size(), TotalGestureEventsCount); + for(int i = 0; i < child->events.all.size(); ++i) + QCOMPARE(parent.events.all.at(i), CustomGesture::GestureType); + + QGestureRecognizer::unregisterRecognizer(SecondGesture); +} + +void tst_Gestures::multipleGesturesInTree() +{ + GestureWidget a("A"); + GestureWidget *A = &a; + GestureWidget *B = new GestureWidget("B", A); + GestureWidget *C = new GestureWidget("C", B); + GestureWidget *D = new GestureWidget("D", C); + + Qt::GestureType FirstGesture = CustomGesture::GestureType; + Qt::GestureType SecondGesture = QGestureRecognizer::registerRecognizer(new CustomGestureRecognizer); + Qt::GestureType ThirdGesture = QGestureRecognizer::registerRecognizer(new CustomGestureRecognizer); + + Qt::GestureFlags flags = Qt::ReceivePartialGestures; + A->grabGesture(FirstGesture, flags); // A [1 3] + A->grabGesture(ThirdGesture, flags); // | + B->grabGesture(SecondGesture, flags); // B [ 2 3] + B->grabGesture(ThirdGesture, flags); // | + C->grabGesture(FirstGesture, flags); // C [1 2 3] + C->grabGesture(SecondGesture, flags); // | + C->grabGesture(ThirdGesture, flags); // D [1 3] + D->grabGesture(FirstGesture, flags); + D->grabGesture(ThirdGesture, flags); + + // make sure all widgets ignore events, so they get propagated. + A->ignoredGestures << FirstGesture << ThirdGesture; + B->ignoredGestures << SecondGesture << ThirdGesture; + C->ignoredGestures << FirstGesture << SecondGesture << ThirdGesture; + D->ignoredGestures << FirstGesture << ThirdGesture; + + CustomEvent event; + sendCustomGesture(&event, D); + + static const int TotalGestureEventsCount = CustomGesture::SerialFinishedThreshold - CustomGesture::SerialStartedThreshold + 1; + + // gesture override events + QCOMPARE(D->overrideEvents.all.count(FirstGesture), 1); + QCOMPARE(D->overrideEvents.all.count(SecondGesture), 0); + QCOMPARE(D->overrideEvents.all.count(ThirdGesture), 1); + + QCOMPARE(C->overrideEvents.all.count(FirstGesture), 1); + QCOMPARE(C->overrideEvents.all.count(SecondGesture), 1); + QCOMPARE(C->overrideEvents.all.count(ThirdGesture), 1); + + QCOMPARE(B->overrideEvents.all.count(FirstGesture), 0); + QCOMPARE(B->overrideEvents.all.count(SecondGesture), 1); + QCOMPARE(B->overrideEvents.all.count(ThirdGesture), 1); + + QCOMPARE(A->overrideEvents.all.count(FirstGesture), 1); + QCOMPARE(A->overrideEvents.all.count(SecondGesture), 0); + QCOMPARE(A->overrideEvents.all.count(ThirdGesture), 1); + + // normal gesture events + QCOMPARE(D->events.all.count(FirstGesture), TotalGestureEventsCount); + QCOMPARE(D->events.all.count(SecondGesture), 0); + QCOMPARE(D->events.all.count(ThirdGesture), TotalGestureEventsCount); + + QCOMPARE(C->events.all.count(FirstGesture), TotalGestureEventsCount); + QCOMPARE(C->events.all.count(SecondGesture), TotalGestureEventsCount); + QCOMPARE(C->events.all.count(ThirdGesture), TotalGestureEventsCount); + + QCOMPARE(B->events.all.count(FirstGesture), 0); + QCOMPARE(B->events.all.count(SecondGesture), TotalGestureEventsCount); + QCOMPARE(B->events.all.count(ThirdGesture), TotalGestureEventsCount); + + QCOMPARE(A->events.all.count(FirstGesture), TotalGestureEventsCount); + QCOMPARE(A->events.all.count(SecondGesture), 0); + QCOMPARE(A->events.all.count(ThirdGesture), TotalGestureEventsCount); + + QGestureRecognizer::unregisterRecognizer(SecondGesture); + QGestureRecognizer::unregisterRecognizer(ThirdGesture); +} + +void tst_Gestures::multipleGesturesInComplexTree() +{ + GestureWidget a("A"); + GestureWidget *A = &a; + GestureWidget *B = new GestureWidget("B", A); + GestureWidget *C = new GestureWidget("C", B); + GestureWidget *D = new GestureWidget("D", C); + + Qt::GestureType FirstGesture = CustomGesture::GestureType; + Qt::GestureType SecondGesture = QGestureRecognizer::registerRecognizer(new CustomGestureRecognizer); + Qt::GestureType ThirdGesture = QGestureRecognizer::registerRecognizer(new CustomGestureRecognizer); + Qt::GestureType FourthGesture = QGestureRecognizer::registerRecognizer(new CustomGestureRecognizer); + Qt::GestureType FifthGesture = QGestureRecognizer::registerRecognizer(new CustomGestureRecognizer); + Qt::GestureType SixthGesture = QGestureRecognizer::registerRecognizer(new CustomGestureRecognizer); + Qt::GestureType SeventhGesture = QGestureRecognizer::registerRecognizer(new CustomGestureRecognizer); + + Qt::GestureFlags flags = Qt::ReceivePartialGestures; + A->grabGesture(FirstGesture, flags); // A [1,3,4] + A->grabGesture(ThirdGesture, flags); // | + A->grabGesture(FourthGesture, flags); // B [2,3,5] + B->grabGesture(SecondGesture, flags); // | + B->grabGesture(ThirdGesture, flags); // C [1,2,3,6] + B->grabGesture(FifthGesture, flags); // | + C->grabGesture(FirstGesture, flags); // D [1,3,7] + C->grabGesture(SecondGesture, flags); + C->grabGesture(ThirdGesture, flags); + C->grabGesture(SixthGesture, flags); + D->grabGesture(FirstGesture, flags); + D->grabGesture(ThirdGesture, flags); + D->grabGesture(SeventhGesture, flags); + + // make sure all widgets ignore events, so they get propagated. + QSet allGestureTypes; + allGestureTypes << FirstGesture << SecondGesture << ThirdGesture + << FourthGesture << FifthGesture << SixthGesture << SeventhGesture; + A->ignoredGestures = B->ignoredGestures = allGestureTypes; + C->ignoredGestures = D->ignoredGestures = allGestureTypes; + + CustomEvent event; + sendCustomGesture(&event, D); + + static const int TotalGestureEventsCount = CustomGesture::SerialFinishedThreshold - CustomGesture::SerialStartedThreshold + 1; + + // gesture override events + QCOMPARE(D->overrideEvents.all.count(FirstGesture), 1); + QCOMPARE(D->overrideEvents.all.count(SecondGesture), 0); + QCOMPARE(D->overrideEvents.all.count(ThirdGesture), 1); + + QCOMPARE(C->overrideEvents.all.count(FirstGesture), 1); + QCOMPARE(C->overrideEvents.all.count(SecondGesture), 1); + QCOMPARE(C->overrideEvents.all.count(ThirdGesture), 1); + + QCOMPARE(B->overrideEvents.all.count(FirstGesture), 0); + QCOMPARE(B->overrideEvents.all.count(SecondGesture), 1); + QCOMPARE(B->overrideEvents.all.count(ThirdGesture), 1); + + QCOMPARE(A->overrideEvents.all.count(FirstGesture), 1); + QCOMPARE(A->overrideEvents.all.count(SecondGesture), 0); + QCOMPARE(A->overrideEvents.all.count(ThirdGesture), 1); + + // normal gesture events + QCOMPARE(D->events.all.count(FirstGesture), TotalGestureEventsCount); + QCOMPARE(D->events.all.count(SecondGesture), 0); + QCOMPARE(D->events.all.count(ThirdGesture), TotalGestureEventsCount); + QCOMPARE(D->events.all.count(FourthGesture), 0); + QCOMPARE(D->events.all.count(FifthGesture), 0); + QCOMPARE(D->events.all.count(SixthGesture), 0); + QCOMPARE(D->events.all.count(SeventhGesture), TotalGestureEventsCount); + + QCOMPARE(C->events.all.count(FirstGesture), TotalGestureEventsCount); + QCOMPARE(C->events.all.count(SecondGesture), TotalGestureEventsCount); + QCOMPARE(C->events.all.count(ThirdGesture), TotalGestureEventsCount); + QCOMPARE(C->events.all.count(FourthGesture), 0); + QCOMPARE(C->events.all.count(FifthGesture), 0); + QCOMPARE(C->events.all.count(SixthGesture), TotalGestureEventsCount); + QCOMPARE(C->events.all.count(SeventhGesture), 0); + + QCOMPARE(B->events.all.count(FirstGesture), 0); + QCOMPARE(B->events.all.count(SecondGesture), TotalGestureEventsCount); + QCOMPARE(B->events.all.count(ThirdGesture), TotalGestureEventsCount); + QCOMPARE(B->events.all.count(FourthGesture), 0); + QCOMPARE(B->events.all.count(FifthGesture), TotalGestureEventsCount); + QCOMPARE(B->events.all.count(SixthGesture), 0); + QCOMPARE(B->events.all.count(SeventhGesture), 0); + + QCOMPARE(A->events.all.count(FirstGesture), TotalGestureEventsCount); + QCOMPARE(A->events.all.count(SecondGesture), 0); + QCOMPARE(A->events.all.count(ThirdGesture), TotalGestureEventsCount); + QCOMPARE(A->events.all.count(FourthGesture), TotalGestureEventsCount); + QCOMPARE(A->events.all.count(FifthGesture), 0); + QCOMPARE(A->events.all.count(SixthGesture), 0); + QCOMPARE(A->events.all.count(SeventhGesture), 0); + + QGestureRecognizer::unregisterRecognizer(SecondGesture); + QGestureRecognizer::unregisterRecognizer(ThirdGesture); + QGestureRecognizer::unregisterRecognizer(FourthGesture); + QGestureRecognizer::unregisterRecognizer(FifthGesture); + QGestureRecognizer::unregisterRecognizer(SixthGesture); + QGestureRecognizer::unregisterRecognizer(SeventhGesture); +} + +void tst_Gestures::testMapToScene() +{ + QGesture gesture; + QList list; + list << &gesture; + QGestureEvent event(list); + QCOMPARE(event.mapToGraphicsScene(gesture.hotSpot()), QPointF()); // not set, can't do much + + QGraphicsScene scene; + QGraphicsView view(&scene); + view.setWindowFlags(Qt::X11BypassWindowManagerHint); + + GestureItem *item0 = new GestureItem; + scene.addItem(item0); + item0->setPos(14, 16); + + view.show(); // need to show to give it a global coordinate + QTest::qWaitForWindowShown(&view); + view.ensureVisible(scene.sceneRect()); + + QPoint origin = view.mapToGlobal(QPoint()); + event.setWidget(view.viewport()); + + QCOMPARE(event.mapToGraphicsScene(origin + QPoint(100, 200)), view.mapToScene(QPoint(100, 200))); +} +*/ + +/* +void tst_Gestures::ungrabGesture() // a method on QWidget +{ + class MockGestureWidget : public GestureWidget { + public: + MockGestureWidget(const char *name = 0, QWidget *parent = 0) + : GestureWidget(name, parent) { } + + + QSet gestures; + protected: + bool event(QEvent *event) + { + if (event->type() == QEvent::Gesture) { + QGestureEvent *gestureEvent = static_cast(event); + if (gestureEvent) + foreach (QGesture *g, gestureEvent->gestures()) + gestures.insert(g); + } + return GestureWidget::event(event); + } + }; + + MockGestureWidget parent("A"); + MockGestureWidget *a = &parent; + MockGestureWidget *b = new MockGestureWidget("B", a); + + a->grabGesture(CustomGesture::GestureType, Qt::DontStartGestureOnChildren); + b->grabGesture(CustomGesture::GestureType); + b->ignoredGestures << CustomGesture::GestureType; + + CustomEvent event; + // sending an event will cause the QGesture objects to be instantiated for the widgets + sendCustomGesture(&event, b); + + QCOMPARE(a->gestures.count(), 1); + QPointer customGestureA; + customGestureA = *(a->gestures.begin()); + QVERIFY(!customGestureA.isNull()); + QCOMPARE(customGestureA->gestureType(), CustomGesture::GestureType); + + QCOMPARE(b->gestures.count(), 1); + QPointer customGestureB; + customGestureB = *(b->gestures.begin()); + QVERIFY(!customGestureB.isNull()); + QVERIFY(customGestureA.data() == customGestureB.data()); + QCOMPARE(customGestureB->gestureType(), CustomGesture::GestureType); + + a->gestures.clear(); + // sending an event will cause the QGesture objects to be instantiated for the widget + sendCustomGesture(&event, a); + + QCOMPARE(a->gestures.count(), 1); + customGestureA = *(a->gestures.begin()); + QVERIFY(!customGestureA.isNull()); + QCOMPARE(customGestureA->gestureType(), CustomGesture::GestureType); + QVERIFY(customGestureA.data() != customGestureB.data()); + + a->ungrabGesture(CustomGesture::GestureType); + QVERIFY(customGestureA.isNull()); + QVERIFY(!customGestureB.isNull()); + + a->gestures.clear(); + a->reset(); + // send again to 'b' and make sure a never gets it. + sendCustomGesture(&event, b); + QCOMPARE(a->gestureEventsReceived, 0); + QCOMPARE(a->gestureOverrideEventsReceived, 0); +} +*/ + +/* +void tst_Gestures::unregisterRecognizer() // a method on QApplication +{ + The hardest usecase to get right is when we remove a recognizer while several + of the gestures it created are in active state and we immediately add a new recognizer + for the same type (thus replacing the old one). + The expected result is that all old gestures continue till they are finished/cancelled + and the new recognizer starts creating gestures immediately at registration. + + This implies that deleting of the recognizer happens only when there are no more gestures + that it created. (since gestures might have a pointer to the recognizer) +} +*/ + +/* +void tst_Gestures::autoCancelGestures() +{ + class MockWidget : public GestureWidget { + public: + MockWidget(const char *name) : GestureWidget(name) { } + + bool event(QEvent *event) + { + if (event->type() == QEvent::Gesture) { + QGestureEvent *ge = static_cast(event); + Q_ASSERT(ge->gestures().count() == 1); // can't use QCOMPARE here... + ge->gestures().first()->setGestureCancelPolicy(QGesture::CancelAllInContext); + } + return GestureWidget::event(event); + } + }; + + const Qt::GestureType secondGesture = QGestureRecognizer::registerRecognizer(new CustomGestureRecognizer); + + MockWidget parent("parent"); // this one sets the cancel policy to CancelAllInContext + parent.resize(300, 100); + parent.setWindowFlags(Qt::X11BypassWindowManagerHint); + GestureWidget *child = new GestureWidget("child", &parent); + child->setGeometry(10, 10, 100, 80); + + parent.grabGesture(CustomGesture::GestureType); + child->grabGesture(secondGesture); + parent.show(); + QTest::qWaitForWindowShown(&parent); + + An event is send to both the child and the parent, when the child gets it a gesture is triggered + and send to the child. + When the parent gets the event a new gesture is triggered and delivered to the parent. When the + parent gets it he accepts it and that causes the cancel policy to activate. + The cause of that is the gesture for the child is cancelled and send to the child as such. + + CustomEvent event; + event.serial = CustomGesture::SerialStartedThreshold; + QApplication::sendEvent(child, &event); + QCOMPARE(child->events.all.count(), 2); + QCOMPARE(child->events.started.count(), 1); + QCOMPARE(child->events.canceled.count(), 1); + QCOMPARE(parent.events.all.count(), 1); + + // clean up, make the parent gesture finish + event.serial = CustomGesture::SerialFinishedThreshold; + QApplication::sendEvent(child, &event); + QCOMPARE(parent.events.all.count(), 2); +} + +void tst_Gestures::autoCancelGestures2() +{ + class MockItem : public GestureItem { + public: + MockItem(const char *name) : GestureItem(name) { } + + bool event(QEvent *event) { + if (event->type() == QEvent::Gesture) { + QGestureEvent *ge = static_cast(event); + Q_ASSERT(ge->gestures().count() == 1); // can't use QCOMPARE here... + ge->gestures().first()->setGestureCancelPolicy(QGesture::CancelAllInContext); + } + return GestureItem::event(event); + } + }; + + const Qt::GestureType secondGesture = QGestureRecognizer ::registerRecognizer(new CustomGestureRecognizer); + + QGraphicsScene scene; + QGraphicsView view(&scene); + view.setWindowFlags(Qt::X11BypassWindowManagerHint); + + MockItem *parent = new MockItem("parent"); + GestureItem *child = new GestureItem("child"); + child->setParentItem(parent); + parent->setPos(0, 0); + child->setPos(10, 10); + scene.addItem(parent); + view.viewport()->grabGesture(CustomGesture::GestureType, Qt::DontStartGestureOnChildren); + view.viewport()->grabGesture(secondGesture, Qt::DontStartGestureOnChildren); + parent->grabGesture(CustomGesture::GestureType); + child->grabGesture(secondGesture); + + view.show(); + QTest::qWaitForWindowShown(&view); + view.ensureVisible(scene.sceneRect()); + + CustomEvent event; + event.serial = CustomGesture::SerialStartedThreshold; + event.hasHotSpot = true; + event.hotSpot = mapToGlobal(QPointF(5, 5), child, &view); + scene.sendEvent(child, &event); + QCOMPARE(parent->events.all.count(), 1); + QCOMPARE(child->events.started.count(), 1); + QCOMPARE(child->events.canceled.count(), 1); + QCOMPARE(child->events.all.count(), 2); + + // clean up, make the parent gesture finish + event.serial = CustomGesture::SerialFinishedThreshold; + scene.sendEvent(child, &event); + QCOMPARE(parent->events.all.count(), 2); +} +*/ + +/* +void tst_Gestures::panelPropagation() +{ + QGraphicsScene scene; + QGraphicsView view(&scene); + view.setWindowFlags(Qt::X11BypassWindowManagerHint); + + GestureItem *item0 = new GestureItem("item0"); + scene.addItem(item0); + item0->setPos(0, 0); + item0->size = QRectF(0, 0, 200, 200); + item0->grabGesture(CustomGesture::GestureType); + item0->setZValue(1); + + GestureItem *item1 = new GestureItem("item1"); + item1->grabGesture(CustomGesture::GestureType); + scene.addItem(item1); + item1->setPos(10, 10); + item1->size = QRectF(0, 0, 180, 180); + item1->setZValue(2); + + GestureItem *item1_child1 = new GestureItem("item1_child1[panel]"); + item1_child1->setFlags(QGraphicsItem::ItemIsPanel); + item1_child1->setParentItem(item1); + item1_child1->grabGesture(CustomGesture::GestureType); + item1_child1->setPos(10, 10); + item1_child1->size = QRectF(0, 0, 160, 160); + item1_child1->setZValue(5); + + GestureItem *item1_child1_child1 = new GestureItem("item1_child1_child1"); + item1_child1_child1->setParentItem(item1_child1); + item1_child1_child1->grabGesture(CustomGesture::GestureType); + item1_child1_child1->setPos(10, 10); + item1_child1_child1->size = QRectF(0, 0, 140, 140); + item1_child1_child1->setZValue(10); + + view.show(); + QTest::qWaitForWindowShown(&view); + view.ensureVisible(scene.sceneRect()); + + view.viewport()->grabGesture(CustomGesture::GestureType, Qt::DontStartGestureOnChildren); + + static const int TotalGestureEventsCount = CustomGesture::SerialFinishedThreshold - CustomGesture::SerialStartedThreshold + 1; + static const int TotalCustomEventsCount = CustomGesture::SerialFinishedThreshold - CustomGesture::SerialMaybeThreshold + 1; + + CustomEvent event; + event.hotSpot = mapToGlobal(QPointF(5, 5), item1_child1_child1, &view); + event.hasHotSpot = true; + sendCustomGesture(&event, item0, &scene); + + QCOMPARE(item0->customEventsReceived, TotalCustomEventsCount); + QCOMPARE(item1_child1_child1->gestureEventsReceived, TotalGestureEventsCount); + QCOMPARE(item1_child1_child1->gestureOverrideEventsReceived, 1); + QCOMPARE(item1_child1->gestureOverrideEventsReceived, 1); + QCOMPARE(item1->gestureEventsReceived, 0); + QCOMPARE(item1->gestureOverrideEventsReceived, 0); + QCOMPARE(item0->gestureEventsReceived, 0); + QCOMPARE(item0->gestureOverrideEventsReceived, 0); + + item0->reset(); item1->reset(); item1_child1->reset(); item1_child1_child1->reset(); + + event.hotSpot = mapToGlobal(QPointF(5, 5), item1, &view); + event.hasHotSpot = true; + sendCustomGesture(&event, item1, &scene); + + QCOMPARE(item1_child1_child1->gestureEventsReceived, 0); + QCOMPARE(item1_child1_child1->gestureOverrideEventsReceived, 0); + QCOMPARE(item1_child1->gestureEventsReceived, 0); + QCOMPARE(item1_child1->gestureOverrideEventsReceived, 0); + QCOMPARE(item1->gestureEventsReceived, TotalGestureEventsCount); + QCOMPARE(item1->gestureOverrideEventsReceived, 1); + QCOMPARE(item0->gestureEventsReceived, 0); + QCOMPARE(item0->gestureOverrideEventsReceived, 1); + + item0->reset(); item1->reset(); item1_child1->reset(); item1_child1_child1->reset(); + // try with a modal panel + item1_child1->setPanelModality(QGraphicsItem::PanelModal); + + event.hotSpot = mapToGlobal(QPointF(5, 5), item1, &view); + event.hasHotSpot = true; + sendCustomGesture(&event, item1, &scene); + + QCOMPARE(item1_child1_child1->gestureEventsReceived, 0); + QCOMPARE(item1_child1_child1->gestureOverrideEventsReceived, 0); + QCOMPARE(item1_child1->gestureEventsReceived, TotalGestureEventsCount); + QCOMPARE(item1_child1->gestureOverrideEventsReceived, 0); + QCOMPARE(item1->gestureEventsReceived, 0); + QCOMPARE(item1->gestureOverrideEventsReceived, 0); + QCOMPARE(item0->gestureEventsReceived, 0); + QCOMPARE(item0->gestureOverrideEventsReceived, 0); + + item0->reset(); item1->reset(); item1_child1->reset(); item1_child1_child1->reset(); + // try with a modal panel, however set the hotspot to be outside of the + // panel and its parent + item1_child1->setPanelModality(QGraphicsItem::PanelModal); + + event.hotSpot = mapToGlobal(QPointF(5, 5), item0, &view); + event.hasHotSpot = true; + sendCustomGesture(&event, item1, &scene); + + QCOMPARE(item1_child1_child1->gestureEventsReceived, 0); + QCOMPARE(item1_child1_child1->gestureOverrideEventsReceived, 0); + QCOMPARE(item1_child1->gestureEventsReceived, 0); + QCOMPARE(item1_child1->gestureOverrideEventsReceived, 0); + QCOMPARE(item1->gestureEventsReceived, 0); + QCOMPARE(item1->gestureOverrideEventsReceived, 0); + QCOMPARE(item0->gestureEventsReceived, TotalGestureEventsCount); + QCOMPARE(item0->gestureOverrideEventsReceived, 0); + + item0->reset(); item1->reset(); item1_child1->reset(); item1_child1_child1->reset(); + // try with a scene modal panel + item1_child1->setPanelModality(QGraphicsItem::SceneModal); + + event.hotSpot = mapToGlobal(QPointF(5, 5), item0, &view); + event.hasHotSpot = true; + sendCustomGesture(&event, item0, &scene); + + QCOMPARE(item1_child1_child1->gestureEventsReceived, 0); + QCOMPARE(item1_child1_child1->gestureOverrideEventsReceived, 0); + QCOMPARE(item1_child1->gestureEventsReceived, TotalGestureEventsCount); + QCOMPARE(item1_child1->gestureOverrideEventsReceived, 0); + QCOMPARE(item1->gestureEventsReceived, 0); + QCOMPARE(item1->gestureOverrideEventsReceived, 0); + QCOMPARE(item0->gestureEventsReceived, 0); + QCOMPARE(item0->gestureOverrideEventsReceived, 0); +} +*/ + +/* +void tst_Gestures::panelStacksBehindParent() +{ + QGraphicsScene scene; + QGraphicsView view(&scene); + view.setWindowFlags(Qt::X11BypassWindowManagerHint); + + GestureItem *item1 = new GestureItem("item1"); + item1->grabGesture(CustomGesture::GestureType); + scene.addItem(item1); + item1->setPos(10, 10); + item1->size = QRectF(0, 0, 180, 180); + item1->setZValue(2); + + GestureItem *panel = new GestureItem("panel"); + panel->setFlags(QGraphicsItem::ItemIsPanel | QGraphicsItem::ItemStacksBehindParent); + panel->setPanelModality(QGraphicsItem::PanelModal); + panel->setParentItem(item1); + panel->grabGesture(CustomGesture::GestureType); + panel->setPos(-10, -10); + panel->size = QRectF(0, 0, 200, 200); + panel->setZValue(5); + + view.show(); + QTest::qWaitForWindowShown(&view); + view.ensureVisible(scene.sceneRect()); + + view.viewport()->grabGesture(CustomGesture::GestureType, Qt::DontStartGestureOnChildren); + + static const int TotalGestureEventsCount = CustomGesture::SerialFinishedThreshold - CustomGesture::SerialStartedThreshold + 1; + + CustomEvent event; + event.hotSpot = mapToGlobal(QPointF(5, 5), item1, &view); + event.hasHotSpot = true; + sendCustomGesture(&event, item1, &scene); + + QCOMPARE(item1->gestureEventsReceived, 0); + QCOMPARE(item1->gestureOverrideEventsReceived, 0); + QCOMPARE(panel->gestureEventsReceived, TotalGestureEventsCount); + QCOMPARE(panel->gestureOverrideEventsReceived, 0); +} +*/ + +QTEST_MAIN(tst_Gestures) +#include "tst_gestures.moc" diff -r 14c7476c20d2 -r 72f7e4177ac7 qt/qapplication/smoketest_qapplication.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qt/qapplication/smoketest_qapplication.cpp Fri Jun 11 11:40:57 2010 +0100 @@ -0,0 +1,2202 @@ +/**************************************************************************** +** +** Copyright (C) 2010 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$ +** +****************************************************************************/ + + +//#define QT_TST_QAPP_DEBUG +#include + +#include + +#include "qabstracteventdispatcher.h" +#include + +#include "private/qapplication_p.h" +#include "private/qstylesheetstyle_p.h" +#ifdef Q_OS_WINCE +#include +#endif +#ifdef Q_OS_SYMBIAN +#include +#endif + +//TESTED_CLASS= +//TESTED_FILES= + +#if defined(Q_OS_SYMBIAN) +// In Symbian, the PluginsPath doesn't specify the only absolute path; just the dir that can be found on any drive +/*static void addExpectedSymbianPluginsPath(QStringList& expected) +{ + QString installPathPlugins = QDir::fromNativeSeparators(QLibraryInfo::location(QLibraryInfo::PluginsPath)); + QFileInfoList driveList = QDir::drives(); + QListIterator iter(driveList); + while (iter.hasNext()) { + QFileInfo testFi(iter.next().canonicalPath().append(installPathPlugins)); + if (testFi.exists()) + expected << testFi.canonicalFilePath(); + } +} +*/ +#endif + +class tst_QApplication : public QObject +{ +Q_OBJECT + +public: + tst_QApplication(); + virtual ~tst_QApplication(); + +public slots: + void init(); + void cleanup(); +private slots: + /*void sendEventsOnProcessEvents(); // this must be the first test + void getSetCheck(); + void staticSetup(); + + void alert(); + + void multiple_data(); + void multiple(); + + void nonGui(); + + void setFont_data(); + void setFont(); + + void args_data(); + void args(); + + void lastWindowClosed(); + void quitOnLastWindowClosed(); + void testDeleteLater(); + void testDeleteLaterProcessEvents(); + + void libraryPaths(); + void libraryPaths_qt_plugin_path(); + void libraryPaths_qt_plugin_path_2(); + + void sendPostedEvents(); + + void thread(); + void desktopSettingsAware();*/ + + void setActiveWindow(); + + /*void focusChanged(); + void focusOut(); + + void execAfterExit(); + + void wheelScrollLines(); + + void task109149(); + + void style(); + + void allWidgets(); + void topLevelWidgets(); + + void setAttribute(); + + void windowsCommandLine_data(); + void windowsCommandLine(); + + void touchEventPropagation(); + + void symbianNoApplicationPanes(); + + void symbianNeedForTraps(); + void symbianLeaveThroughMain();*/ +}; + +/*class EventSpy : public QObject +{ + Q_OBJECT + +public: + QList recordedEvents; + bool eventFilter(QObject *, QEvent *event) + { + recordedEvents.append(event->type()); + return false; + } +}; +*/ + +/* +void tst_QApplication::sendEventsOnProcessEvents() +{ + int argc = 0; + QApplication app(argc, 0, QApplication::GuiServer); + + EventSpy spy; + app.installEventFilter(&spy); + + QCoreApplication::postEvent(&app, new QEvent(QEvent::Type(QEvent::User + 1))); + QCoreApplication::processEvents(); + QVERIFY(spy.recordedEvents.contains(QEvent::User + 1)); +} + +class MyInputContext : public QInputContext +{ +public: + MyInputContext() : QInputContext() {} + QString identifierName() { return QString("NoName"); } + QString language() { return QString("NoLanguage"); } + void reset() {} + bool isComposing() const { return false; } +}; + +// Testing get/set functions +void tst_QApplication::getSetCheck() +{ + int argc = 0; + QApplication obj1(argc, 0, QApplication::GuiServer); + // QInputContext * QApplication::inputContext() + // void QApplication::setInputContext(QInputContext *) + MyInputContext *var1 = new MyInputContext; + obj1.setInputContext(var1); + QCOMPARE((QInputContext *)var1, obj1.inputContext()); + QTest::ignoreMessage(QtWarningMsg, "QApplication::setInputContext: called with 0 input context"); + obj1.setInputContext((QInputContext *)0); + QCOMPARE((QInputContext *)var1, obj1.inputContext()); + // delete var1; // No delete, since QApplication takes ownership +} + +class CloseEventTestWindow : public QWidget +{ +public: + CloseEventTestWindow(QWidget *parent = 0) + : QWidget(parent) + { + } + + void closeEvent(QCloseEvent *event) + { + QWidget dialog; + dialog.show(); + dialog.close(); + + hide(); + event->ignore(); + } +}; +*/ + +static char *argv0; + +tst_QApplication::tst_QApplication() +{ +#ifdef Q_OS_WINCE + // Clean up environment previously to launching test + qputenv("QT_PLUGIN_PATH", QByteArray()); +#endif +} + +tst_QApplication::~tst_QApplication() +{ + +} + +void tst_QApplication::init() +{ +// TODO: Add initialization code here. +// This will be executed immediately before each test is run. +} + +void tst_QApplication::cleanup() +{ +// TODO: Add cleanup code here. +// This will be executed immediately after each test is run. +} + +/* +void tst_QApplication::staticSetup() +{ + QVERIFY(!qApp); + + QStyle *style = QStyleFactory::create(QLatin1String("Windows")); + QVERIFY(style); + QApplication::setStyle(style); + + QPalette pal; + QApplication::setPalette(pal); + + //QFont font; + //QApplication::setFont(font); + + int argc = 0; + QApplication app(argc, 0, QApplication::GuiServer); +} + + +// QApp subclass that exits the event loop after 150ms +class TestApplication : public QApplication +{ +public: + TestApplication( int &argc, char **argv ) + : QApplication( argc, argv, QApplication::GuiServer ) + { + startTimer( 150 ); + } + + void timerEvent( QTimerEvent * ) + { + quit(); + } +}; + +void tst_QApplication::alert() +{ + int argc = 0; + QApplication app(argc, 0, QApplication::GuiServer); + app.alert(0, 0); + + QWidget widget; + QWidget widget2; + app.alert(&widget, 100); + widget.show(); + widget2.show(); +#ifdef Q_WS_X11 + qt_x11_wait_for_window_manager(&widget); + qt_x11_wait_for_window_manager(&widget2); +#endif + QTest::qWait(100); + app.alert(&widget, -1); + app.alert(&widget, 250); + widget2.activateWindow(); + QApplication::setActiveWindow(&widget2); + app.alert(&widget, 0); + widget.activateWindow(); + QApplication::setActiveWindow(&widget); + app.alert(&widget, 200); + app.syncX(); +} + +void tst_QApplication::multiple_data() +{ + QTest::addColumn("features"); + + // return a list of things to try + QTest::newRow( "data0" ) << QStringList( "" ); + QTest::newRow( "data1" ) << QStringList( "QFont" ); + QTest::newRow( "data2" ) << QStringList( "QPixmap" ); + QTest::newRow( "data3" ) << QStringList( "QWidget" ); +} + +void tst_QApplication::multiple() +{ + QFETCH(QStringList,features); + + int i = 0; + int argc = 0; + while ( i++ < 5 ) { + TestApplication app( argc, 0 ); + + if ( features.contains( "QFont" ) ) { + // create font and force loading + QFont font( "Arial", 12 ); + QFontInfo finfo( font ); + finfo.exactMatch(); + } + if ( features.contains( "QPixmap" ) ) { + QPixmap pix( 100, 100 ); + pix.fill( Qt::black ); + } + if ( features.contains( "QWidget" ) ) { + QWidget widget; + } + + QVERIFY(!app.exec()); + } +} + +void tst_QApplication::nonGui() +{ +#ifdef Q_OS_HPUX + // ### This is only to allow us to generate a test report for now. + QSKIP("This test shuts down the window manager on HP-UX.", SkipAll); +#endif + + int argc = 0; + QApplication app(argc, 0, false); + QCOMPARE(qApp, &app); +} + +void tst_QApplication::setFont_data() +{ + QTest::addColumn("family"); + QTest::addColumn("pointsize"); + QTest::addColumn("beforeAppConstructor"); + + int argc = 0; + QApplication app(argc, 0, QApplication::GuiServer); // Needed for QFontDatabase + + int cnt = 0; + QFontDatabase fdb; + QStringList families = fdb.families(); + for (QStringList::const_iterator itr = families.begin(); + itr != families.end(); + ++itr) { + if (cnt < 3) { + QString family = *itr; + QStringList styles = fdb.styles(family); + if (styles.size() > 0) { + QString style = styles.first(); + QList sizes = fdb.pointSizes(family, style); + if (!sizes.size()) + sizes = fdb.standardSizes(); + if (sizes.size() > 0) { + QTest::newRow(QString("data%1a").arg(cnt).toLatin1().constData()) + << family + << sizes.first() + << false; + QTest::newRow(QString("data%1b").arg(cnt).toLatin1().constData()) + << family + << sizes.first() + << true; + } + } + } + ++cnt; + } + + QTest::newRow("nonexistingfont") << "nosuchfont_probably_quiteunlikely" + << 0 << false; + QTest::newRow("nonexistingfont") << "nosuchfont_probably_quiteunlikely" + << 0 << true; + + QTest::newRow("largescaleable") << "smoothtimes" << 100 << false; + QTest::newRow("largescaleable") << "smoothtimes" << 100 << true; + + QTest::newRow("largeunscaleale") << "helvetica" << 100 << false; + QTest::newRow("largeunscaleale") << "helvetica" << 100 << true; +} + +void tst_QApplication::setFont() +{ + QFETCH( QString, family ); + QFETCH( int, pointsize ); + QFETCH( bool, beforeAppConstructor ); + + QFont font( family, pointsize ); + if (beforeAppConstructor) { + QApplication::setFont( font ); + QCOMPARE(QApplication::font(), font); + } + + int argc = 0; + QApplication app( argc, 0, QApplication::GuiServer ); + if (!beforeAppConstructor) + QApplication::setFont( font ); + + QCOMPARE( app.font(), font ); +} + +void tst_QApplication::args_data() +{ + QTest::addColumn("argc_in"); + QTest::addColumn("args_in"); + QTest::addColumn("argc_out"); + QTest::addColumn("args_out"); + + QTest::newRow( "App name" ) << 1 << "/usr/bin/appname" << 1 << "/usr/bin/appname"; + QTest::newRow( "No arguments" ) << 0 << QString() << 0 << QString(); + QTest::newRow( "App name, style" ) << 3 << "/usr/bin/appname -style motif" << 1 << "/usr/bin/appname"; + QTest::newRow( "App name, style, arbitrary, reverse" ) << 5 << "/usr/bin/appname -style motif -arbitrary -reverse" + << 2 << "/usr/bin/appname -arbitrary"; +} + +void tst_QApplication::task109149() +{ + int argc = 0; + QApplication app(argc, 0, QApplication::GuiServer); + QApplication::setFont(QFont("helvetica", 100)); + + QWidget w; + w.setWindowTitle("hello"); + w.show(); + + app.processEvents(); +} + +static char ** QString2cstrings( const QString &args ) +{ + static QList cache; + + int i; + char **argarray = 0; + QStringList list = args.split(' ');; + argarray = new char*[list.count()+1]; + + for (i = 0; i < (int)list.count(); ++i ) { + QByteArray l1 = list[i].toLatin1(); + argarray[i] = l1.data(); + cache.append(l1); + } + argarray[i] = 0; + + return argarray; +} + +static QString cstrings2QString( char **args ) +{ + QString string; + if ( !args ) + return string; + + int i = 0; + while ( args[i] ) { + string += args[i]; + if ( args[i+1] ) + string += " "; + ++i; + } + return string; +} + +void tst_QApplication::args() +{ + QFETCH( int, argc_in ); + QFETCH( QString, args_in ); + QFETCH( int, argc_out ); + QFETCH( QString, args_out ); + + char **argv = QString2cstrings( args_in ); + + QApplication app( argc_in, argv, QApplication::GuiServer ); + QString argv_out = cstrings2QString(argv); + + QCOMPARE( argc_in, argc_out ); + QCOMPARE( argv_out, args_out ); + + delete [] argv; +} + +class CloseWidget : public QWidget +{ + Q_OBJECT +public: + CloseWidget() + { + startTimer(500); + } + +protected: + void timerEvent(QTimerEvent *) + { + close(); + } + +}; + +void tst_QApplication::lastWindowClosed() +{ + int argc = 0; + QApplication app(argc, 0, QApplication::GuiServer); + + QSignalSpy spy(&app, SIGNAL(lastWindowClosed())); + + QPointer dialog = new QDialog; + QVERIFY(dialog->testAttribute(Qt::WA_QuitOnClose)); + QTimer::singleShot(1000, dialog, SLOT(accept())); + dialog->exec(); + QVERIFY(dialog); + QCOMPARE(spy.count(), 0); + + QPointerwidget = new CloseWidget; + QVERIFY(widget->testAttribute(Qt::WA_QuitOnClose)); + QObject::connect(&app, SIGNAL(lastWindowClosed()), widget, SLOT(deleteLater())); + app.exec(); + QVERIFY(!widget); + QCOMPARE(spy.count(), 1); + spy.clear(); + +#if 0 + // everything is closed, so doing this should not emit lastWindowClosed() again + QMetaObject::invokeMethod(dialog, "close", Qt::QueuedConnection); + QTimer::singleShot(1000, &app, SLOT(quit())); + app.exec(); + QCOMPARE(spy.count(), 0); +#endif + + delete dialog; + + // show 3 windows, close them, should only get lastWindowClosed once + QWidget w1; + QWidget w2; + QWidget w3; + w1.show(); + w2.show(); + w3.show(); + + QTimer::singleShot(1000, &app, SLOT(closeAllWindows())); + app.exec(); + QCOMPARE(spy.count(), 1); +} + +class QuitOnLastWindowClosedDialog : public QDialog +{ + Q_OBJECT +public: + QPushButton *okButton; + + QuitOnLastWindowClosedDialog() + { + QHBoxLayout *hbox = new QHBoxLayout(this); + okButton = new QPushButton("&ok", this); + + hbox->addWidget(okButton); + connect(okButton, SIGNAL(clicked()), this, SLOT(accept())); + connect(okButton, SIGNAL(clicked()), this, SLOT(ok_clicked())); + } + +public slots: + void ok_clicked() + { + QDialog other; + + QTimer timer; + connect(&timer, SIGNAL(timeout()), &other, SLOT(accept())); + QSignalSpy spy(&timer, SIGNAL(timeout())); + QSignalSpy appSpy(qApp, SIGNAL(lastWindowClosed())); + + timer.start(1000); + other.exec(); + + // verify that the eventloop ran and let the timer fire + QCOMPARE(spy.count(), 1); + QCOMPARE(appSpy.count(), 1); + } +}; + +class QuitOnLastWindowClosedWindow : public QWidget +{ + Q_OBJECT + +public: + QuitOnLastWindowClosedWindow() + { } + +public slots: + void execDialogThenShow() + { + QDialog dialog; + QTimer timer1; + connect(&timer1, SIGNAL(timeout()), &dialog, SLOT(accept())); + QSignalSpy spy1(&timer1, SIGNAL(timeout())); + timer1.setSingleShot(true); + timer1.start(1000); + dialog.exec(); + QCOMPARE(spy1.count(), 1); + + show(); + } +}; + +void tst_QApplication::quitOnLastWindowClosed() +{ + { + int argc = 0; + QApplication app(argc, 0, QApplication::GuiServer); + + QuitOnLastWindowClosedDialog d; + d.show(); + QTimer::singleShot(1000, d.okButton, SLOT(animateClick())); + + QSignalSpy appSpy(&app, SIGNAL(lastWindowClosed())); + app.exec(); + + // lastWindowClosed() signal should only be sent after the last dialog is closed + QCOMPARE(appSpy.count(), 2); + } + { + int argc = 0; + QApplication app(argc, 0, QApplication::GuiServer); + QSignalSpy appSpy(&app, SIGNAL(lastWindowClosed())); + + QDialog dialog; + QTimer timer1; + connect(&timer1, SIGNAL(timeout()), &dialog, SLOT(accept())); + QSignalSpy spy1(&timer1, SIGNAL(timeout())); + timer1.setSingleShot(true); + timer1.start(1000); + dialog.exec(); + QCOMPARE(spy1.count(), 1); + QCOMPARE(appSpy.count(), 0); + + QTimer timer2; + connect(&timer2, SIGNAL(timeout()), &app, SLOT(quit())); + QSignalSpy spy2(&timer2, SIGNAL(timeout())); + timer2.setSingleShot(true); + timer2.start(1000); + int returnValue = app.exec(); + QCOMPARE(returnValue, 0); + QCOMPARE(spy2.count(), 1); + QCOMPARE(appSpy.count(), 0); + } + { + int argc = 0; + QApplication app(argc, 0, QApplication::GuiServer); + QTimer timer; + timer.setInterval(100); + + QSignalSpy spy(&app, SIGNAL(aboutToQuit())); + QSignalSpy spy2(&timer, SIGNAL(timeout())); + + QPointer mainWindow = new QMainWindow; + QPointer dialog = new QDialog(mainWindow); + + QVERIFY(app.quitOnLastWindowClosed()); + QVERIFY(mainWindow->testAttribute(Qt::WA_QuitOnClose)); + QVERIFY(dialog->testAttribute(Qt::WA_QuitOnClose)); + + mainWindow->show(); + dialog->show(); + + timer.start(); + QTimer::singleShot(1000, mainWindow, SLOT(close())); // This should quit the application + QTimer::singleShot(2000, &app, SLOT(quit())); // This makes sure we quit even if it didn't + + app.exec(); + + QCOMPARE(spy.count(), 1); + QVERIFY(spy2.count() < 15); // Should be around 10 if closing caused the quit + } + { + int argc = 0; + QApplication app(argc, 0, QApplication::GuiServer); + QTimer timer; + timer.setInterval(100); + + QSignalSpy spy(&app, SIGNAL(aboutToQuit())); + QSignalSpy spy2(&timer, SIGNAL(timeout())); + + QPointer mainWindow = new QMainWindow; + QPointer invisibleTopLevelWidget = new QWidget; + invisibleTopLevelWidget->setAttribute(Qt::WA_DontShowOnScreen); + + QVERIFY(app.quitOnLastWindowClosed()); + QVERIFY(mainWindow->testAttribute(Qt::WA_QuitOnClose)); + QVERIFY(invisibleTopLevelWidget->testAttribute(Qt::WA_QuitOnClose)); + QVERIFY(invisibleTopLevelWidget->testAttribute(Qt::WA_DontShowOnScreen)); + + mainWindow->show(); + invisibleTopLevelWidget->show(); + + timer.start(); + QTimer::singleShot(1000, mainWindow, SLOT(close())); // This should quit the application + QTimer::singleShot(2000, &app, SLOT(quit())); // This makes sure we quit even if it didn't + + app.exec(); + + QCOMPARE(spy.count(), 1); + QVERIFY(spy2.count() < 15); // Should be around 10 if closing caused the quit + } + { + int argc = 0; + QApplication app(argc, 0, QApplication::GuiServer); + QTimer timer; + timer.setInterval(100); + + QSignalSpy spy(&app, SIGNAL(aboutToQuit())); + QSignalSpy spy2(&timer, SIGNAL(timeout())); + + QPointer mainWindow = new CloseEventTestWindow; + + QVERIFY(app.quitOnLastWindowClosed()); + QVERIFY(mainWindow->testAttribute(Qt::WA_QuitOnClose)); + + mainWindow->show(); + + timer.start(); + QTimer::singleShot(1000, mainWindow, SLOT(close())); // This should quit the application + QTimer::singleShot(2000, &app, SLOT(quit())); // This makes sure we quit even if it didn't + + app.exec(); + + QCOMPARE(spy.count(), 1); + QVERIFY(spy2.count() > 15); // Should be around 20 if closing did not caused the quit + } + { + int argc = 0; + QApplication app(argc, 0, QApplication::GuiServer); + QSignalSpy appSpy(&app, SIGNAL(lastWindowClosed())); + + // exec a dialog for 1 second, then show the window + QuitOnLastWindowClosedWindow window; + QTimer::singleShot(0, &window, SLOT(execDialogThenShow())); + + QTimer timer; + QSignalSpy timerSpy(&timer, SIGNAL(timeout())); + connect(&timer, SIGNAL(timeout()), &window, SLOT(close())); + timer.setSingleShot(true); + timer.start(2000); + int returnValue = app.exec(); + QCOMPARE(returnValue, 0); + // failure here means the timer above didn't fire, and the + // quit was caused the the dialog being closed (not the window) + QCOMPARE(timerSpy.count(), 1); + QCOMPARE(appSpy.count(), 2); + } +} + +bool isPathListIncluded(const QStringList &l, const QStringList &r) +{ + int size = r.count(); + if (size > l.count()) + return false; +#if defined (Q_OS_WIN) + Qt::CaseSensitivity cs = Qt::CaseInsensitive; +#else + Qt::CaseSensitivity cs = Qt::CaseSensitive; +#endif + int i = 0, j = 0; + for ( ; i < l.count() && j < r.count(); ++i) { + if (QDir::toNativeSeparators(l[i]).compare(QDir::toNativeSeparators(r[j]), cs) == 0) { + ++j; + i = -1; + } + } + return j == r.count(); +} + +#define QT_TST_QAPP_DEBUG +void tst_QApplication::libraryPaths() +{ + { +#ifndef Q_OS_WINCE + QString testDir = QDir::current().canonicalPath() + "/test"; +#else + // On Windows CE we need QApplication object to have valid + // current Path. Therefore we need to identify it ourselves + // here for the test. + QFileInfo filePath; + wchar_t module_name[MAX_PATH]; + GetModuleFileName(0, module_name, MAX_PATH); + filePath = QString::fromWCharArray(module_name); + QString testDir = filePath.path() + "/test"; +#endif + QApplication::setLibraryPaths(QStringList() << testDir); + QCOMPARE(QApplication::libraryPaths(), (QStringList() << testDir)); + + // creating QApplication adds the applicationDirPath to the libraryPath + int argc = 1; + QApplication app(argc, &argv0, QApplication::GuiServer); + QString appDirPath = QDir(app.applicationDirPath()).canonicalPath(); + + QStringList actual = QApplication::libraryPaths(); + actual.sort(); + QStringList expected = QSet::fromList((QStringList() << testDir << appDirPath)).toList(); + expected.sort(); + + QVERIFY2(isPathListIncluded(actual, expected), + qPrintable("actual:\n - " + actual.join("\n - ") + + "\nexpected:\n - " + expected.join("\n - "))); + } + { + // creating QApplication adds the applicationDirPath and plugin install path to the libraryPath + int argc = 1; + QApplication app(argc, &argv0, QApplication::GuiServer); + QString appDirPath = app.applicationDirPath(); + QString installPathPlugins = QLibraryInfo::location(QLibraryInfo::PluginsPath); + + QStringList actual = QApplication::libraryPaths(); + actual.sort(); + +#if defined(Q_OS_SYMBIAN) + QStringList expected; + addExpectedSymbianPluginsPath(expected); + expected << appDirPath; +#else + QStringList expected = QSet::fromList((QStringList() << installPathPlugins << appDirPath)).toList(); +#endif + expected.sort(); + + QVERIFY2(isPathListIncluded(actual, expected), + qPrintable("actual:\n - " + actual.join("\n - ") + + "\nexpected:\n - " + expected.join("\n - "))); + + // setting the library paths overrides everything + QString testDir = QDir::currentPath() + "/test"; + QApplication::setLibraryPaths(QStringList() << testDir); + QVERIFY2(isPathListIncluded(QApplication::libraryPaths(), (QStringList() << testDir)), + qPrintable("actual:\n - " + QApplication::libraryPaths().join("\n - ") + + "\nexpected:\n - " + testDir)); + } + { +#ifdef QT_TST_QAPP_DEBUG + qDebug() << "Initial library path:" << QApplication::libraryPaths(); +#endif + + int count = QApplication::libraryPaths().count(); +#if 0 + // this test doesn't work if KDE 4 is installed + QCOMPARE(count, 1); // before creating QApplication, only the PluginsPath is in the libraryPaths() +#endif + QString installPathPlugins = QLibraryInfo::location(QLibraryInfo::PluginsPath); + QApplication::addLibraryPath(installPathPlugins); +#ifdef QT_TST_QAPP_DEBUG + qDebug() << "installPathPlugins" << installPathPlugins; + qDebug() << "After adding plugins path:" << QApplication::libraryPaths(); +#endif + QCOMPARE(QApplication::libraryPaths().count(), count); + + QApplication::addLibraryPath(QDir::currentPath() + "/test"); + QCOMPARE(QApplication::libraryPaths().count(), count + 1); + + // creating QApplication adds the applicationDirPath to the libraryPath + int argc = 1; + QApplication app(argc, &argv0, QApplication::GuiServer); + QString appDirPath = app.applicationDirPath(); + qDebug() << QApplication::libraryPaths(); + // On Windows CE these are identical and might also be the case for other + // systems too + if (appDirPath != installPathPlugins) + QCOMPARE(QApplication::libraryPaths().count(), count + 2); + } + { + int argc = 1; + QApplication app(argc, &argv0, QApplication::GuiServer); + +#ifdef QT_TST_QAPP_DEBUG + qDebug() << "Initial library path:" << app.libraryPaths(); +#endif + int count = app.libraryPaths().count(); + QString installPathPlugins = QLibraryInfo::location(QLibraryInfo::PluginsPath); + app.addLibraryPath(installPathPlugins); +#ifdef QT_TST_QAPP_DEBUG + qDebug() << "installPathPlugins" << installPathPlugins; + qDebug() << "After adding plugins path:" << app.libraryPaths(); +#endif + QCOMPARE(app.libraryPaths().count(), count); + + QString appDirPath = app.applicationDirPath(); + + app.addLibraryPath(appDirPath); +#ifdef Q_OS_WINCE + app.addLibraryPath(appDirPath + "/../.."); +#else + app.addLibraryPath(appDirPath + "/.."); +#endif +#ifdef QT_TST_QAPP_DEBUG + qDebug() << "appDirPath" << appDirPath; + qDebug() << "After adding appDirPath && appDirPath + /..:" << app.libraryPaths(); +#endif + QCOMPARE(app.libraryPaths().count(), count + 1); +#ifdef Q_OS_MAC + app.addLibraryPath(appDirPath + "/../MacOS"); +#else + app.addLibraryPath(appDirPath + "/tmp/.."); +#endif +#ifdef QT_TST_QAPP_DEBUG + qDebug() << "After adding appDirPath + /tmp/..:" << app.libraryPaths(); +#endif + QCOMPARE(app.libraryPaths().count(), count + 1); + } +} + +void tst_QApplication::libraryPaths_qt_plugin_path() +{ + int argc = 1; + + QApplication app(argc, &argv0, QApplication::GuiServer); + QString appDirPath = app.applicationDirPath(); + + // Our hook into libraryPaths() initialization: Set the QT_PLUGIN_PATH environment variable + QString installPathPluginsDeCanon = appDirPath + QString::fromLatin1("/tmp/.."); + QByteArray ascii = installPathPluginsDeCanon.toAscii(); + qputenv("QT_PLUGIN_PATH", ascii); + + QVERIFY(!app.libraryPaths().contains(appDirPath + QString::fromLatin1("/tmp/.."))); +} + +void tst_QApplication::libraryPaths_qt_plugin_path_2() +{ +#ifdef Q_OS_SYMBIAN + QByteArray validPath = "C:\\data"; + QByteArray nonExistentPath = "Z:\\nonexistent"; + QByteArray pluginPath = validPath + ";" + nonExistentPath; +#elif defined(Q_OS_UNIX) + QByteArray validPath = QDir("/tmp").canonicalPath().toLatin1(); + QByteArray nonExistentPath = "/nonexistent"; + QByteArray pluginPath = validPath + ":" + nonExistentPath; +#elif defined(Q_OS_WIN) +# ifdef Q_OS_WINCE + QByteArray validPath = "/Temp"; + QByteArray nonExistentPath = "/nonexistent"; + QByteArray pluginPath = validPath + ";" + nonExistentPath; +# else + QByteArray validPath = "C:\\windows"; + QByteArray nonExistentPath = "Z:\\nonexistent"; + QByteArray pluginPath = validPath + ";" + nonExistentPath; +# endif +#endif + + { + // Our hook into libraryPaths() initialization: Set the QT_PLUGIN_PATH environment variable + qputenv("QT_PLUGIN_PATH", pluginPath); + + int argc = 1; + + QApplication app(argc, &argv0, QApplication::GuiServer); + + // library path list should contain the default plus the one valid path +#if defined(Q_OS_SYMBIAN) + // In Symbian, the PluginsPath doesn't specify the only absolute path; just the dir that can be found on any drive + QStringList expected; + addExpectedSymbianPluginsPath(expected); + expected << QDir(app.applicationDirPath()).canonicalPath() + << QDir(QDir::fromNativeSeparators(QString::fromLatin1(validPath))).canonicalPath(); +#else + QStringList expected = + QStringList() + << QLibraryInfo::location(QLibraryInfo::PluginsPath) + << QDir(app.applicationDirPath()).canonicalPath() + << QDir(QDir::fromNativeSeparators(QString::fromLatin1(validPath))).canonicalPath(); +# ifdef Q_OS_WINCE + expected = QSet::fromList(expected).toList(); +# endif +#endif + QVERIFY2(isPathListIncluded(app.libraryPaths(), expected), + qPrintable("actual:\n - " + app.libraryPaths().join("\n - ") + + "\nexpected:\n - " + expected.join("\n - "))); + } + + { + int argc = 1; + + QApplication app(argc, &argv0, QApplication::GuiServer); + + // library paths are initialized by the QApplication, setting + // the environment variable here doesn't work + qputenv("QT_PLUGIN_PATH", pluginPath); + + // library path list should contain the default +#if defined(Q_OS_SYMBIAN) + QStringList expected; + addExpectedSymbianPluginsPath(expected); + expected << app.applicationDirPath(); +#else + QStringList expected = + QStringList() + << QLibraryInfo::location(QLibraryInfo::PluginsPath) + << app.applicationDirPath(); +# ifdef Q_OS_WINCE + expected = QSet::fromList(expected).toList(); +# endif +#endif + QVERIFY(isPathListIncluded(app.libraryPaths(), expected)); + + qputenv("QT_PLUGIN_PATH", QByteArray()); + } +} + +class SendPostedEventsTester : public QObject +{ + Q_OBJECT +public: + QList eventSpy; + bool event(QEvent *e); +private slots: + void doTest(); +}; + +bool SendPostedEventsTester::event(QEvent *e) +{ + eventSpy.append(e->type()); + return QObject::event(e); +} + +void SendPostedEventsTester::doTest() +{ + QPointer p = this; + QApplication::postEvent(this, new QEvent(QEvent::User)); + // DeferredDelete should not be delivered until returning from this function + QApplication::postEvent(this, new QEvent(QEvent::DeferredDelete)); + + QEventLoop eventLoop; + QMetaObject::invokeMethod(&eventLoop, "quit", Qt::QueuedConnection); + eventLoop.exec(); + QVERIFY(p != 0); + + QCOMPARE(eventSpy.count(), 2); + QCOMPARE(eventSpy.at(0), int(QEvent::MetaCall)); + QCOMPARE(eventSpy.at(1), int(QEvent::User)); + eventSpy.clear(); +} + +void tst_QApplication::sendPostedEvents() +{ + int argc = 0; + QApplication app(argc, 0, QApplication::GuiServer); + SendPostedEventsTester *tester = new SendPostedEventsTester; + QMetaObject::invokeMethod(tester, "doTest", Qt::QueuedConnection); + QMetaObject::invokeMethod(&app, "quit", Qt::QueuedConnection); + QPointer p = tester; + (void) app.exec(); + QVERIFY(p == 0); +} + +void tst_QApplication::thread() +{ + QThread *currentThread = QThread::currentThread(); + // no app, but still have a valid thread + QVERIFY(currentThread != 0); + + // the thread should be running and not finished + QVERIFY(currentThread->isRunning()); + QVERIFY(!currentThread->isFinished()); + + // this should probably be in the tst_QObject::thread() test, but + // we put it here since we want to make sure that objects created + // *before* the QApplication has a thread + QObject object; + QObject child(&object); + QVERIFY(object.thread() == currentThread); + QVERIFY(child.thread() == currentThread); + + { + int argc = 0; + QApplication app(argc, 0, QApplication::GuiServer); + + // current thread still valid + QVERIFY(QThread::currentThread() != 0); + // thread should be the same as before + QCOMPARE(QThread::currentThread(), currentThread); + + // app's thread should be the current thread + QCOMPARE(app.thread(), currentThread); + + // the thread should still be running and not finished + QVERIFY(currentThread->isRunning()); + QVERIFY(!currentThread->isFinished()); + + QTestEventLoop::instance().enterLoop(1); + } + + // app dead, current thread still valid + QVERIFY(QThread::currentThread() != 0); + QCOMPARE(QThread::currentThread(), currentThread); + + // the thread should still be running and not finished + QVERIFY(currentThread->isRunning()); + QVERIFY(!currentThread->isFinished()); + + // should still have a thread + QVERIFY(object.thread() == currentThread); + QVERIFY(child.thread() == currentThread); + + // do the test again, making sure that the thread is the same as + // before + { + int argc = 0; + QApplication app(argc, 0, QApplication::GuiServer); + + // current thread still valid + QVERIFY(QThread::currentThread() != 0); + // thread should be the same as before + QCOMPARE(QThread::currentThread(), currentThread); + + // app's thread should be the current thread + QCOMPARE(app.thread(), currentThread); + + // the thread should be running and not finished + QVERIFY(currentThread->isRunning()); + QVERIFY(!currentThread->isFinished()); + + // should still have a thread + QVERIFY(object.thread() == currentThread); + QVERIFY(child.thread() == currentThread); + + QTestEventLoop::instance().enterLoop(1); + } + + // app dead, current thread still valid + QVERIFY(QThread::currentThread() != 0); + QCOMPARE(QThread::currentThread(), currentThread); + + // the thread should still be running and not finished + QVERIFY(currentThread->isRunning()); + QVERIFY(!currentThread->isFinished()); + + // should still have a thread + QVERIFY(object.thread() == currentThread); + QVERIFY(child.thread() == currentThread); +} + +class DeleteLaterWidget : public QWidget +{ + Q_OBJECT +public: + DeleteLaterWidget(QApplication *_app, QWidget *parent = 0) + : QWidget(parent) { app = _app; child_deleted = false; } + + bool child_deleted; + QApplication *app; + +public slots: + void runTest(); + void checkDeleteLater(); + void childDeleted() { child_deleted = true; } +}; + + +void DeleteLaterWidget::runTest() +{ + QObject *stillAlive = qFindChild(this, "deleteLater"); + + QWidget *w = new QWidget(this); + connect(w, SIGNAL(destroyed()), this, SLOT(childDeleted())); + + w->deleteLater(); + QVERIFY(!child_deleted); + + QDialog dlg; + QTimer::singleShot(500, &dlg, SLOT(reject())); + dlg.exec(); + + QVERIFY(!child_deleted); + app->processEvents(); + QVERIFY(!child_deleted); + + QTimer::singleShot(500, this, SLOT(checkDeleteLater())); + + app->processEvents(); + + QVERIFY(!stillAlive); // verify at the end to make test terminate +} + +void DeleteLaterWidget::checkDeleteLater() +{ + QVERIFY(child_deleted); + + close(); +} + +void tst_QApplication::testDeleteLater() +{ + int argc = 0; + QApplication app(argc, 0, QApplication::GuiServer); + connect(&app, SIGNAL(lastWindowClosed()), &app, SLOT(quit())); + + DeleteLaterWidget *wgt = new DeleteLaterWidget(&app); + QTimer::singleShot(500, wgt, SLOT(runTest())); + + QObject *object = new QObject(wgt); + object->setObjectName("deleteLater"); + object->deleteLater(); + + QObject *stillAlive = qFindChild(wgt, "deleteLater"); + QVERIFY(stillAlive); + + app.exec(); + + delete wgt; + +} + +class EventLoopNester : public QObject +{ + Q_OBJECT +public slots: + void deleteLaterAndEnterLoop() + { + QEventLoop eventLoop; + QPointer p(this); + deleteLater(); + + // DeferredDelete events are compressed, meaning this second + // deleteLater() will *not* delete the object in the nested + // event loop + QMetaObject::invokeMethod(this, "deleteLater", Qt::QueuedConnection); + QTimer::singleShot(1000, &eventLoop, SLOT(quit())); + eventLoop.exec(); + QVERIFY(p); + } + void deleteLaterAndExitLoop() + { + // Check that 'p' is not deleted before exec returns, since the call + // to QEventLoop::quit() should stop 'eventLoop' from processing + // any more events (that is, delete later) until we return to the + // _current_ event loop: + QEventLoop eventLoop; + QPointer p(this); + QMetaObject::invokeMethod(this, "deleteLater", Qt::QueuedConnection); + QMetaObject::invokeMethod(&eventLoop, "quit", Qt::QueuedConnection); + eventLoop.exec(); + QVERIFY(p); // not dead yet + } + + void processEventsOnly() + { + QApplication::processEvents(); + } + void processEventsWithDeferredDeletion() + { + QApplication::processEvents(QEventLoop::DeferredDeletion); + } + void sendPostedEventsWithDeferredDelete() + { + QApplication::sendPostedEvents(0, QEvent::DeferredDelete); + } + void deleteLaterAndProcessEvents1() + { + QEventLoop eventLoop; + + QPointer p = this; + deleteLater(); + + // trying to delete this object in a deeper eventloop just won't work + QMetaObject::invokeMethod(this, + "processEventsOnly", + Qt::QueuedConnection); + QMetaObject::invokeMethod(&eventLoop, "quit", Qt::QueuedConnection); + eventLoop.exec(); + QVERIFY(p); + QMetaObject::invokeMethod(this, + "processEventsWithDeferredDeletion", + Qt::QueuedConnection); + QMetaObject::invokeMethod(&eventLoop, "quit", Qt::QueuedConnection); + eventLoop.exec(); + QVERIFY(p); + QMetaObject::invokeMethod(this, + "sendPostedEventsWithDeferredDelete", + Qt::QueuedConnection); + QMetaObject::invokeMethod(&eventLoop, "quit", Qt::QueuedConnection); + eventLoop.exec(); + QVERIFY(p); + + // trying to delete it from this eventloop still doesn't work + QApplication::processEvents(); + QVERIFY(p); + + // however, it *will* work with this magic incantation + QApplication::processEvents(QEventLoop::DeferredDeletion); + QVERIFY(!p); + } + + void deleteLaterAndProcessEvents2() + { + QEventLoop eventLoop; + + QPointer p = this; + deleteLater(); + + // trying to delete this object in a deeper eventloop just won't work + QMetaObject::invokeMethod(this, + "processEventsOnly", + Qt::QueuedConnection); + QMetaObject::invokeMethod(&eventLoop, "quit", Qt::QueuedConnection); + eventLoop.exec(); + QVERIFY(p); + QMetaObject::invokeMethod(this, + "processEventsWithDeferredDeletion", + Qt::QueuedConnection); + QMetaObject::invokeMethod(&eventLoop, "quit", Qt::QueuedConnection); + eventLoop.exec(); + QVERIFY(p); + QMetaObject::invokeMethod(this, + "sendPostedEventsWithDeferredDelete", + Qt::QueuedConnection); + QMetaObject::invokeMethod(&eventLoop, "quit", Qt::QueuedConnection); + eventLoop.exec(); + QVERIFY(p); + + // trying to delete it from this eventloop still doesn't work + QApplication::processEvents(); + QVERIFY(p); + + // however, it *will* work with this magic incantation + QApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QVERIFY(!p); + } +}; + +void tst_QApplication::testDeleteLaterProcessEvents() +{ + int argc = 0; + + // Calling processEvents() with no event dispatcher does nothing. + QObject *object = new QObject; + QPointer p(object); + object->deleteLater(); + QApplication::processEvents(); + QVERIFY(p); + delete object; + + { + QApplication app(argc, 0, QApplication::GuiServer); + // If you call processEvents() with an event dispatcher present, but + // outside any event loops, deferred deletes are not processed unless + // QEventLoop::DeferredDeletion is passed. + object = new QObject; + p = object; + object->deleteLater(); + app.processEvents(); + QVERIFY(p); + app.processEvents(QEventLoop::ProcessEventsFlag(0x10)); // 0x10 == QEventLoop::DeferredDeletion + QVERIFY(!p); + + // sendPostedEvents(0, DeferredDelete); also works + object = new QObject; + p = object; + object->deleteLater(); + app.processEvents(); + QVERIFY(p); + QApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QVERIFY(!p); + + // If you call deleteLater() on an object when there is no parent + // event loop, and then enter an event loop, the object will get + // deleted. + object = new QObject; + p = object; + object->deleteLater(); + QEventLoop loop; + QTimer::singleShot(1000, &loop, SLOT(quit())); + loop.exec(); + QVERIFY(!p); + } + { + // When an object is in an event loop, then calls deleteLater() and enters + // an event loop recursively, it should not die until the parent event + // loop continues. + QApplication app(argc, 0, QApplication::GuiServer); + QEventLoop loop; + EventLoopNester *nester = new EventLoopNester; + p = nester; + QTimer::singleShot(3000, &loop, SLOT(quit())); + QTimer::singleShot(0, nester, SLOT(deleteLaterAndEnterLoop())); + + loop.exec(); + QVERIFY(!p); + } + + { + // When the event loop that calls deleteLater() is exited + // immediately, the object should die when returning to the + // parent event loop + QApplication app(argc, 0, QApplication::GuiServer); + QEventLoop loop; + EventLoopNester *nester = new EventLoopNester; + p = nester; + QTimer::singleShot(3000, &loop, SLOT(quit())); + QTimer::singleShot(0, nester, SLOT(deleteLaterAndExitLoop())); + + loop.exec(); + QVERIFY(!p); + } + + { + // when the event loop that calls deleteLater() also calls + // processEvents() immediately afterwards, the object should + // not die until the parent loop continues + QApplication app(argc, 0, QApplication::GuiServer); + QEventLoop loop; + EventLoopNester *nester = new EventLoopNester(); + p = nester; + QTimer::singleShot(3000, &loop, SLOT(quit())); + QTimer::singleShot(0, nester, SLOT(deleteLaterAndProcessEvents1())); + + loop.exec(); + QVERIFY(!p); + } + + { + // when the event loop that calls deleteLater() also calls + // processEvents() immediately afterwards, the object should + // not die until the parent loop continues + QApplication app(argc, 0, QApplication::GuiServer); + QEventLoop loop; + EventLoopNester *nester = new EventLoopNester(); + p = nester; + QTimer::singleShot(3000, &loop, SLOT(quit())); + QTimer::singleShot(0, nester, SLOT(deleteLaterAndProcessEvents2())); + + loop.exec(); + QVERIFY(!p); + } +} + + +// Test for crash whith QApplication::setDesktopSettingsAware(false). + +void tst_QApplication::desktopSettingsAware() +{ +#ifndef QT_NO_PROCESS + QProcess testProcess; +#ifdef Q_OS_WINCE + int argc = 0; + QApplication tmpApp(argc, 0, QApplication::GuiServer); + testProcess.start("desktopsettingsaware/desktopsettingsaware"); +#else +#if defined(Q_OS_WIN) && defined(QT_DEBUG) + testProcess.start("desktopsettingsaware/debug/desktopsettingsaware"); +#elif defined(Q_OS_WIN) + testProcess.start("desktopsettingsaware/release/desktopsettingsaware"); +#elif defined(Q_OS_SYMBIAN) + testProcess.start("desktopsettingsaware"); +#if defined(Q_CC_NOKIAX86) + QEXPECT_FAIL("", "QProcess on Q_CC_NOKIAX86 cannot launch another Qt application, due to DLL conflicts.", Abort); + // TODO: Remove XFAIL, as soon as we can launch Qt applications from within Qt applications on Symbian + QVERIFY(testProcess.error() != QProcess::FailedToStart); +#endif // defined(Q_CC_NOKIAX86) +#else + testProcess.start("desktopsettingsaware/desktopsettingsaware"); +#endif +#endif + QVERIFY(testProcess.waitForFinished(10000)); + QCOMPARE(int(testProcess.state()), int(QProcess::NotRunning)); + QVERIFY(int(testProcess.error()) != int(QProcess::Crashed)); +#endif +} +*/ + +void tst_QApplication::setActiveWindow() +{ + int argc = 0; + QApplication MyApp(argc, 0, QApplication::GuiServer); + + QWidget* w = new QWidget; + QVBoxLayout* layout = new QVBoxLayout(w); + + QLineEdit* pb1 = new QLineEdit("Testbutton1", w); + QLineEdit* pb2 = new QLineEdit("Test Line Edit", w); + + layout->addWidget(pb1); + layout->addWidget(pb2); + + pb2->setFocus(); + pb2->setParent(0); + delete pb2; + + w->show(); + QApplication::setActiveWindow(w); // needs this on twm (focus follows mouse) + QVERIFY(pb1->hasFocus()); + delete w; +} + + +/* This might fail on some X11 window managers? */ +/* +void tst_QApplication::focusChanged() +{ + int argc = 0; + QApplication app(argc, 0, QApplication::GuiServer); + + QSignalSpy spy(&app, SIGNAL(focusChanged(QWidget *, QWidget *))); + QWidget *now = 0; + QWidget *old = 0; + + QWidget parent1; + QHBoxLayout hbox1(&parent1); + QLabel lb1(&parent1); + QLineEdit le1(&parent1); + QPushButton pb1(&parent1); + hbox1.addWidget(&lb1); + hbox1.addWidget(&le1); + hbox1.addWidget(&pb1); + + QCOMPARE(spy.count(), 0); + + parent1.show(); + QApplication::setActiveWindow(&parent1); // needs this on twm (focus follows mouse) + QCOMPARE(spy.count(), 1); + QCOMPARE(spy.at(0).count(), 2); + old = qVariantValue(spy.at(0).at(0)); + now = qVariantValue(spy.at(0).at(1)); + QVERIFY(now == &le1); + QVERIFY(now == QApplication::focusWidget()); + QVERIFY(old == 0); + spy.clear(); + QCOMPARE(spy.count(), 0); + + pb1.setFocus(); + QCOMPARE(spy.count(), 1); + old = qVariantValue(spy.at(0).at(0)); + now = qVariantValue(spy.at(0).at(1)); + QVERIFY(now == &pb1); + QVERIFY(now == QApplication::focusWidget()); + QVERIFY(old == &le1); + spy.clear(); + + lb1.setFocus(); + QCOMPARE(spy.count(), 1); + old = qVariantValue(spy.at(0).at(0)); + now = qVariantValue(spy.at(0).at(1)); + QVERIFY(now == &lb1); + QVERIFY(now == QApplication::focusWidget()); + QVERIFY(old == &pb1); + spy.clear(); + + lb1.clearFocus(); + QCOMPARE(spy.count(), 1); + old = qVariantValue(spy.at(0).at(0)); + now = qVariantValue(spy.at(0).at(1)); + QVERIFY(now == 0); + QVERIFY(now == QApplication::focusWidget()); + QVERIFY(old == &lb1); + spy.clear(); + + QWidget parent2; + QHBoxLayout hbox2(&parent2); + QLabel lb2(&parent2); + QLineEdit le2(&parent2); + QPushButton pb2(&parent2); + hbox2.addWidget(&lb2); + hbox2.addWidget(&le2); + hbox2.addWidget(&pb2); + + parent2.show(); + QApplication::setActiveWindow(&parent2); // needs this on twm (focus follows mouse) + QVERIFY(spy.count() > 0); // one for deactivation, one for activation on Windows + old = qVariantValue(spy.at(spy.count()-1).at(0)); + now = qVariantValue(spy.at(spy.count()-1).at(1)); + QVERIFY(now == &le2); + QVERIFY(now == QApplication::focusWidget()); + QVERIFY(old == 0); + spy.clear(); + + QTestKeyEvent tab(QTest::Press, Qt::Key_Tab, 0, 0); + QTestKeyEvent backtab(QTest::Press, Qt::Key_Backtab, 0, 0); + QTestMouseEvent click(QTest::MouseClick, Qt::LeftButton, 0, QPoint(5, 5), 0); + + bool tabAllControls = true; +#ifdef Q_WS_MAC + // Mac has two modes, one where you tab to everything, one where you can + // only tab to input controls, here's what we get. Determine which ones we + // should get. + QSettings appleSettings(QLatin1String("apple.com")); + QVariant appleValue = appleSettings.value(QLatin1String("AppleKeyboardUIMode"), 0); + tabAllControls = (appleValue.toInt() & 0x2); +#endif + + tab.simulate(now); + if (!tabAllControls) { + QVERIFY(spy.count() == 0); + QVERIFY(now == QApplication::focusWidget()); + } else { + QVERIFY(spy.count() > 0); + old = qVariantValue(spy.at(0).at(0)); + now = qVariantValue(spy.at(0).at(1)); + QVERIFY(now == &pb2); + QVERIFY(now == QApplication::focusWidget()); + QVERIFY(old == &le2); + spy.clear(); + } + + if (!tabAllControls) { + QVERIFY(spy.count() == 0); + QVERIFY(now == QApplication::focusWidget()); + } else { + tab.simulate(now); + QVERIFY(spy.count() > 0); + old = qVariantValue(spy.at(0).at(0)); + now = qVariantValue(spy.at(0).at(1)); + QVERIFY(now == &le2); + QVERIFY(now == QApplication::focusWidget()); + QVERIFY(old == &pb2); + spy.clear(); + } + + if (!tabAllControls) { + QVERIFY(spy.count() == 0); + QVERIFY(now == QApplication::focusWidget()); + } else { + backtab.simulate(now); + QVERIFY(spy.count() > 0); + old = qVariantValue(spy.at(0).at(0)); + now = qVariantValue(spy.at(0).at(1)); + QVERIFY(now == &pb2); + QVERIFY(now == QApplication::focusWidget()); + QVERIFY(old == &le2); + spy.clear(); + } + + + if (!tabAllControls) { + QVERIFY(spy.count() == 0); + QVERIFY(now == QApplication::focusWidget()); + old = &pb2; + } else { + backtab.simulate(now); + QVERIFY(spy.count() > 0); + old = qVariantValue(spy.at(0).at(0)); + now = qVariantValue(spy.at(0).at(1)); + QVERIFY(now == &le2); + QVERIFY(now == QApplication::focusWidget()); + QVERIFY(old == &pb2); + spy.clear(); + } + + click.simulate(old); + if (!(pb2.focusPolicy() & Qt::ClickFocus)) { + QVERIFY(spy.count() == 0); + QVERIFY(now == QApplication::focusWidget()); + } else { + QVERIFY(spy.count() > 0); + old = qVariantValue(spy.at(0).at(0)); + now = qVariantValue(spy.at(0).at(1)); + QVERIFY(now == &pb2); + QVERIFY(now == QApplication::focusWidget()); + QVERIFY(old == &le2); + spy.clear(); + + click.simulate(old); + QVERIFY(spy.count() > 0); + old = qVariantValue(spy.at(0).at(0)); + now = qVariantValue(spy.at(0).at(1)); + QVERIFY(now == &le2); + QVERIFY(now == QApplication::focusWidget()); + QVERIFY(old == &pb2); + spy.clear(); + } + + parent1.activateWindow(); + QApplication::setActiveWindow(&parent1); // needs this on twm (focus follows mouse) + QVERIFY(spy.count() == 1 || spy.count() == 2); // one for deactivation, one for activation on Windows + + //on windows, the change of focus is made in 2 steps + //(the focusChanged SIGNAL is emitted twice) + if (spy.count()==1) + old = qVariantValue(spy.at(spy.count()-1).at(0)); + else + old = qVariantValue(spy.at(spy.count()-2).at(0)); + now = qVariantValue(spy.at(spy.count()-1).at(1)); + QVERIFY(now == &le1); + QVERIFY(now == QApplication::focusWidget()); + QVERIFY(old == &le2); + spy.clear(); +} + +class LineEdit : public QLineEdit +{ +public: + LineEdit(QWidget *parent = 0) : QLineEdit(parent) { } + +protected: + void focusOutEvent(QFocusEvent *e) { + QLineEdit::focusOutEvent(e); + if (objectName() == "le1") + setStyleSheet(""); + } + + void focusInEvent(QFocusEvent *e) { + QLineEdit::focusInEvent(e); + if (objectName() == "le2") + setStyleSheet(""); + } +}; + +void tst_QApplication::focusOut() +{ + int argc = 1; + QApplication app(argc, &argv0, QApplication::GuiServer); + + // Tests the case where the style pointer changes when on focus in/out + // (the above is the case when the stylesheet changes) + QWidget w; + QLineEdit *le1 = new LineEdit(&w); + le1->setObjectName("le1"); + le1->setStyleSheet("background: #fee"); + le1->setFocus(); + + QLineEdit *le2 = new LineEdit(&w); + le2->setObjectName("le2"); + le2->setStyleSheet("background: #fee"); + le2->move(100, 100); + w.show(); + + QTest::qWait(2000); + le2->setFocus(); + QTest::qWait(2000); +} + +void tst_QApplication::execAfterExit() +{ + int argc = 1; + QApplication app(argc, &argv0, QApplication::GuiServer); + QMetaObject::invokeMethod(&app, "quit", Qt::QueuedConnection); + // this should be ignored, as exec() will reset the exitCode + QApplication::exit(1); + int exitCode = app.exec(); + QCOMPARE(exitCode, 0); + + // the quitNow flag should have been reset, so we can spin an + // eventloop after QApplication::exec() returns + QEventLoop eventLoop; + QMetaObject::invokeMethod(&eventLoop, "quit", Qt::QueuedConnection); + exitCode = eventLoop.exec(); + QCOMPARE(exitCode, 0); +} + +void tst_QApplication::wheelScrollLines() +{ + int argc = 1; + QApplication app(argc, &argv0, QApplication::GuiServer); + // If wheelScrollLines returns 0, the mose wheel will be disabled. + QVERIFY(app.wheelScrollLines() > 0); +} + +void tst_QApplication::style() +{ + int argc = 1; + + { + QApplication app(argc, &argv0, QApplication::GuiServer); + QPointer style = app.style(); + app.setStyle(new QWindowsStyle); + QVERIFY(style.isNull()); + } + + QApplication app(argc, &argv0, QApplication::GuiServer); + + // qApp style can never be 0 + QVERIFY(QApplication::style() != 0); +} + +void tst_QApplication::allWidgets() +{ + int argc = 1; + QApplication app(argc, &argv0, QApplication::GuiServer); + QWidget *w = new QWidget; + QVERIFY(app.allWidgets().contains(w)); // uncreate widget test + QVERIFY(app.allWidgets().contains(w)); // created widget test + delete w; + w = 0; + QVERIFY(!app.allWidgets().contains(w)); // removal test +} + +void tst_QApplication::topLevelWidgets() +{ + int argc = 1; + QApplication app(argc, &argv0, QApplication::GuiServer); + QWidget *w = new QWidget; + w->show(); +#ifndef QT_NO_CLIPBOARD + QClipboard *clipboard = QApplication::clipboard(); + QString originalText = clipboard->text(); + clipboard->setText(QString("newText")); +#endif + app.processEvents(); + QVERIFY(QApplication::topLevelWidgets().contains(w)); + QCOMPARE(QApplication::topLevelWidgets().count(), 1); + delete w; + w = 0; + app.processEvents(); + QCOMPARE(QApplication::topLevelWidgets().count(), 0); +} + + + +void tst_QApplication::setAttribute() +{ + int argc = 1; + QApplication app(argc, &argv0, QApplication::GuiServer); + QVERIFY(!QApplication::testAttribute(Qt::AA_ImmediateWidgetCreation)); + QWidget *w = new QWidget; + QVERIFY(!w->testAttribute(Qt::WA_WState_Created)); + delete w; + + QApplication::setAttribute(Qt::AA_ImmediateWidgetCreation); + QVERIFY(QApplication::testAttribute(Qt::AA_ImmediateWidgetCreation)); + w = new QWidget; + QVERIFY(w->testAttribute(Qt::WA_WState_Created)); + QWidget *w2 = new QWidget(w); + w2->setParent(0); + QVERIFY(w2->testAttribute(Qt::WA_WState_Created)); + delete w; + delete w2; + + QApplication::setAttribute(Qt::AA_ImmediateWidgetCreation, false); + QVERIFY(!QApplication::testAttribute(Qt::AA_ImmediateWidgetCreation)); + w = new QWidget; + QVERIFY(!w->testAttribute(Qt::WA_WState_Created)); + delete w; +} + +void tst_QApplication::windowsCommandLine_data() +{ +#if defined(Q_OS_WIN) + QTest::addColumn("args"); + QTest::addColumn("expected"); + + QTest::newRow("hello world") + << QString("Hello \"World\"") + << QString("Hello \"World\""); + QTest::newRow("sql") + << QString("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'PNR' AND TABLE_TYPE = 'VIEW' ORDER BY TABLE_NAME") + << QString("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'PNR' AND TABLE_TYPE = 'VIEW' ORDER BY TABLE_NAME"); +#endif +} + +void tst_QApplication::windowsCommandLine() +{ +#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) + QFETCH(QString, args); + QFETCH(QString, expected); + + QProcess testProcess; +#if defined(QT_DEBUG) + testProcess.start("wincmdline/debug/wincmdline", QStringList(args)); +#else + testProcess.start("wincmdline/release/wincmdline", QStringList(args)); +#endif + QVERIFY(testProcess.waitForFinished(10000)); + QByteArray error = testProcess.readAllStandardError(); + QString procError(error); + QCOMPARE(procError, expected); +#endif +} + +class TouchEventPropagationTestWidget : public QWidget +{ + Q_OBJECT + +public: + bool seenTouchEvent, acceptTouchEvent, seenMouseEvent, acceptMouseEvent; + + TouchEventPropagationTestWidget(QWidget *parent = 0) + : QWidget(parent), seenTouchEvent(false), acceptTouchEvent(false), seenMouseEvent(false), acceptMouseEvent(false) + { } + + void reset() + { + seenTouchEvent = acceptTouchEvent = seenMouseEvent = acceptMouseEvent = false; + } + + bool event(QEvent *event) + { + switch (event->type()) { + case QEvent::MouseButtonPress: + case QEvent::MouseMove: + case QEvent::MouseButtonRelease: + // qDebug() << objectName() << "seenMouseEvent = true"; + seenMouseEvent = true; + event->setAccepted(acceptMouseEvent); + break; + case QEvent::TouchBegin: + case QEvent::TouchUpdate: + case QEvent::TouchEnd: + // qDebug() << objectName() << "seenTouchEvent = true"; + seenTouchEvent = true; + event->setAccepted(acceptTouchEvent); + break; + default: + return QWidget::event(event); + } + return true; + } +}; + +void tst_QApplication::touchEventPropagation() +{ + int argc = 1; + QApplication app(argc, &argv0, QApplication::GuiServer); + + QList pressedTouchPoints; + QTouchEvent::TouchPoint press(0); + press.setState(Qt::TouchPointPressed); + pressedTouchPoints << press; + + QList releasedTouchPoints; + QTouchEvent::TouchPoint release(0); + release.setState(Qt::TouchPointReleased); + releasedTouchPoints << release; + + { + // touch event behavior on a window + TouchEventPropagationTestWidget window; + window.setObjectName("1. window"); + + qt_translateRawTouchEvent(&window, QTouchEvent::TouchScreen, pressedTouchPoints); + qt_translateRawTouchEvent(&window, QTouchEvent::TouchScreen, releasedTouchPoints); + QVERIFY(!window.seenTouchEvent); + QVERIFY(!window.seenMouseEvent); + + window.reset(); + window.setAttribute(Qt::WA_AcceptTouchEvents); + qt_translateRawTouchEvent(&window, QTouchEvent::TouchScreen, pressedTouchPoints); + qt_translateRawTouchEvent(&window, QTouchEvent::TouchScreen, releasedTouchPoints); + QVERIFY(window.seenTouchEvent); + QVERIFY(!window.seenMouseEvent); + + window.reset(); + window.acceptTouchEvent = true; + qt_translateRawTouchEvent(&window, QTouchEvent::TouchScreen, pressedTouchPoints); + qt_translateRawTouchEvent(&window, QTouchEvent::TouchScreen, releasedTouchPoints); + QVERIFY(window.seenTouchEvent); + QVERIFY(!window.seenMouseEvent); + } + + { + // touch event behavior on a window with a child widget + TouchEventPropagationTestWidget window; + window.setObjectName("2. window"); + TouchEventPropagationTestWidget widget(&window); + widget.setObjectName("2. widget"); + + qt_translateRawTouchEvent(&window, QTouchEvent::TouchScreen, pressedTouchPoints); + qt_translateRawTouchEvent(&window, QTouchEvent::TouchScreen, releasedTouchPoints); + QVERIFY(!widget.seenTouchEvent); + QVERIFY(!widget.seenMouseEvent); + QVERIFY(!window.seenTouchEvent); + QVERIFY(!window.seenMouseEvent); + + window.reset(); + widget.reset(); + widget.setAttribute(Qt::WA_AcceptTouchEvents); + qt_translateRawTouchEvent(&window, QTouchEvent::TouchScreen, pressedTouchPoints); + qt_translateRawTouchEvent(&window, QTouchEvent::TouchScreen, releasedTouchPoints); + QVERIFY(widget.seenTouchEvent); + QVERIFY(!widget.seenMouseEvent); + QVERIFY(!window.seenTouchEvent); + QVERIFY(!window.seenMouseEvent); + + window.reset(); + widget.reset(); + widget.acceptMouseEvent = true; + qt_translateRawTouchEvent(&window, QTouchEvent::TouchScreen, pressedTouchPoints); + qt_translateRawTouchEvent(&window, QTouchEvent::TouchScreen, releasedTouchPoints); + QVERIFY(widget.seenTouchEvent); + QVERIFY(!widget.seenMouseEvent); + QVERIFY(!window.seenTouchEvent); + QVERIFY(!window.seenMouseEvent); + + window.reset(); + widget.reset(); + widget.acceptTouchEvent = true; + qt_translateRawTouchEvent(&window, QTouchEvent::TouchScreen, pressedTouchPoints); + qt_translateRawTouchEvent(&window, QTouchEvent::TouchScreen, releasedTouchPoints); + QVERIFY(widget.seenTouchEvent); + QVERIFY(!widget.seenMouseEvent); + QVERIFY(!window.seenTouchEvent); + QVERIFY(!window.seenMouseEvent); + + window.reset(); + widget.reset(); + widget.setAttribute(Qt::WA_AcceptTouchEvents, false); + window.setAttribute(Qt::WA_AcceptTouchEvents); + qt_translateRawTouchEvent(&window, QTouchEvent::TouchScreen, pressedTouchPoints); + qt_translateRawTouchEvent(&window, QTouchEvent::TouchScreen, releasedTouchPoints); + QVERIFY(!widget.seenTouchEvent); + QVERIFY(!widget.seenMouseEvent); + QVERIFY(window.seenTouchEvent); + QVERIFY(!window.seenMouseEvent); + + window.reset(); + widget.reset(); + window.acceptTouchEvent = true; + qt_translateRawTouchEvent(&window, QTouchEvent::TouchScreen, pressedTouchPoints); + qt_translateRawTouchEvent(&window, QTouchEvent::TouchScreen, releasedTouchPoints); + QVERIFY(!widget.seenTouchEvent); + QVERIFY(!widget.seenMouseEvent); + QVERIFY(window.seenTouchEvent); + QVERIFY(!window.seenMouseEvent); + + window.reset(); + widget.reset(); + widget.acceptMouseEvent = true; // doesn't matter, touch events are propagated first + window.acceptTouchEvent = true; + qt_translateRawTouchEvent(&window, QTouchEvent::TouchScreen, pressedTouchPoints); + qt_translateRawTouchEvent(&window, QTouchEvent::TouchScreen, releasedTouchPoints); + QVERIFY(!widget.seenTouchEvent); + QVERIFY(!widget.seenMouseEvent); + QVERIFY(window.seenTouchEvent); + QVERIFY(!window.seenMouseEvent); + } +} + +void tst_QApplication::symbianNoApplicationPanes() +{ +#ifndef Q_OS_SYMBIAN + QSKIP("This is a Symbian only test", SkipAll); +#else + QApplication::setAttribute(Qt::AA_S60DontConstructApplicationPanes); + + // Run in a block so that QApplication is destroyed before resetting the attribute. + { + // Actually I wasn't able to get the forced orientation change to work properly, + // but I'll leave the code here for the future in case we manage to test that + // later. If someone knows how to force an orientation switch in an autotest, do + // feel free to fix this testcase. + int argc = 0; + QApplication app(argc, 0); + QWidget *w; + + w = new QWidget; + w->show(); + QT_TRAP_THROWING(static_cast(CCoeEnv::Static()->AppUi()) + ->SetOrientationL(CAknAppUi::EAppUiOrientationLandscape)); + app.processEvents(); + delete w; + + w = new QWidget; + w->show(); + QT_TRAP_THROWING(static_cast(CCoeEnv::Static()->AppUi()) + ->SetOrientationL(CAknAppUi::EAppUiOrientationPortrait)); + app.processEvents(); + delete w; + + w = new QWidget; + w->showMaximized(); + QT_TRAP_THROWING(static_cast(CCoeEnv::Static()->AppUi()) + ->SetOrientationL(CAknAppUi::EAppUiOrientationLandscape)); + app.processEvents(); + delete w; + + w = new QWidget; + w->showMaximized(); + QT_TRAP_THROWING(static_cast(CCoeEnv::Static()->AppUi()) + ->SetOrientationL(CAknAppUi::EAppUiOrientationPortrait)); + app.processEvents(); + delete w; + + w = new QWidget; + w->showFullScreen(); + QT_TRAP_THROWING(static_cast(CCoeEnv::Static()->AppUi()) + ->SetOrientationL(CAknAppUi::EAppUiOrientationLandscape)); + app.processEvents(); + delete w; + + w = new QWidget; + w->showFullScreen(); + QT_TRAP_THROWING(static_cast(CCoeEnv::Static()->AppUi()) + ->SetOrientationL(CAknAppUi::EAppUiOrientationPortrait)); + app.processEvents(); + delete w; + + // These will have no effect, since there is no status pane, but they shouldn't + // crash either. + w = new QWidget; + w->show(); + w->setWindowTitle("Testing title"); + app.processEvents(); + delete w; + + w = new QWidget; + w->show(); + w->setWindowIcon(QIcon(QPixmap("heart.svg"))); + app.processEvents(); + delete w; + + QDesktopWidget desktop; + QCOMPARE(desktop.availableGeometry(), desktop.screenGeometry()); + } + + QApplication::setAttribute(Qt::AA_S60DontConstructApplicationPanes, false); + + // No other error condition. Program will crash if unsuccessful. +#endif +} + +#ifdef Q_OS_SYMBIAN +class CBaseDummy : public CBase +{ +public: + CBaseDummy(int *numDestroyed) : numDestroyed(numDestroyed) + { + } + ~CBaseDummy() + { + (*numDestroyed)++; + } + +private: + int *numDestroyed; +}; + +static void fakeMain(int *numDestroyed) +{ + // Push a few objects, just so that the cleanup stack has something to clean up. + CleanupStack::PushL(new (ELeave) CBaseDummy(numDestroyed)); + int argc = 0; + QApplication app(argc, 0); + CleanupStack::PushL(new (ELeave) CBaseDummy(numDestroyed)); + + User::Leave(KErrGeneral); // Fake error +} +#endif + +void tst_QApplication::symbianNeedForTraps() +{ +#ifndef Q_OS_SYMBIAN + QSKIP("This is a Symbian-only test", SkipAll); +#else + int argc = 0; + QApplication app(argc, 0); + int numDestroyed = 0; + + // This next part should not require a trap. If it does, the test will crash. + CleanupStack::PushL(new (ELeave) CBaseDummy(&numDestroyed)); + CleanupStack::PopAndDestroy(); + + QCOMPARE(numDestroyed, 1); + + // No other failure condition. The program will crash if it does not pass. +#endif +} + +void tst_QApplication::symbianLeaveThroughMain() +{ +#ifndef Q_OS_SYMBIAN + QSKIP("This is a Symbian-only test", SkipAll); +#else + int numDestroyed = 0; + TInt err; + TRAP(err, fakeMain(&numDestroyed)); + + QCOMPARE(numDestroyed, 2); +#endif +} +*/ + +//QTEST_APPLESS_MAIN(tst_QApplication) +int main(int argc, char *argv[]) +{ + tst_QApplication tc; + argv0 = argv[0]; + return QTest::qExec(&tc, argc, argv); +} + +#include "smoketest_qapplication.moc" diff -r 14c7476c20d2 -r 72f7e4177ac7 qt/qapplication/smoketest_qapplication.pro --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qt/qapplication/smoketest_qapplication.pro Fri Jun 11 11:40:57 2010 +0100 @@ -0,0 +1,4 @@ +TEMPLATE = subdirs +SUBDIRS = test \ + + diff -r 14c7476c20d2 -r 72f7e4177ac7 qt/qapplication/test/test.pro --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qt/qapplication/test/test.pro Fri Jun 11 11:40:57 2010 +0100 @@ -0,0 +1,15 @@ +load(qttest_p4) + +SOURCES += ../smoketest_qapplication.cpp +TARGET = ../smoketest_qapplication + +someTest.sources = test.pro +someTest.path = test +DEPLOYMENT = additional deploy someTest windowIcon +LIBS += -lcone -lavkon + +symbian { + TARGET = ../../release/smoketest_qapplication +} + + diff -r 14c7476c20d2 -r 72f7e4177ac7 qt/shared/filesystem.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qt/shared/filesystem.h Fri Jun 11 11:40:57 2010 +0100 @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2010 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$ +** +****************************************************************************/ +// Helper functions for creating file-system hierarchies and cleaning up. + +#ifndef QT_TESTS_SHARED_FILESYSTEM_H_INCLUDED +#define QT_TESTS_SHARED_FILESYSTEM_H_INCLUDED + +#include +#include +#include +#include + +struct FileSystem +{ + ~FileSystem() + { + Q_FOREACH(QString fileName, createdFiles) + QFile::remove(fileName); + + Q_FOREACH(QString dirName, createdDirectories) + currentDir.rmdir(dirName); + } + + bool createDirectory(const QString &dirName) + { + if (currentDir.mkdir(dirName)) { + createdDirectories.prepend(dirName); + return true; + } + return false; + } + + bool createFile(const QString &fileName) + { + QFile file(fileName); + if (file.open(QIODevice::WriteOnly)) { + createdFiles << fileName; + return true; + } + return false; + } + + bool createLink(const QString &destination, const QString &linkName) + { + if (QFile::link(destination, linkName)) { + createdFiles << linkName; + return true; + } + return false; + } + +private: + QDir currentDir; + + QStringList createdDirectories; + QStringList createdFiles; +}; + +#endif // include guard diff -r 14c7476c20d2 -r 72f7e4177ac7 qt/shared/util.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qt/shared/util.h Fri Jun 11 11:40:57 2010 +0100 @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2010 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$ +** +****************************************************************************/ +// Functions and macros that really need to be in QTestLib + +// Will try to wait for the condition while allowing event processing +#define QTRY_VERIFY(__expr) \ + do { \ + const int __step = 50; \ + const int __timeout = 5000; \ + if (!(__expr)) { \ + QTest::qWait(0); \ + } \ + for (int __i = 0; __i < __timeout && !(__expr); __i+=__step) { \ + QTest::qWait(__step); \ + } \ + QVERIFY(__expr); \ + } while(0) + +// Will try to wait for the condition while allowing event processing +#define QTRY_COMPARE(__expr, __expected) \ + do { \ + const int __step = 50; \ + const int __timeout = 5000; \ + if ((__expr) != (__expected)) { \ + QTest::qWait(0); \ + } \ + for (int __i = 0; __i < __timeout && ((__expr) != (__expected)); __i+=__step) { \ + QTest::qWait(__step); \ + } \ + QCOMPARE(__expr, __expected); \ + } while(0) +