diff options
16 files changed, 243 insertions, 21 deletions
diff --git a/src/declarative/qml/qdeclarativecompiler.cpp b/src/declarative/qml/qdeclarativecompiler.cpp index 31a9f668ef..3a686be9ec 100644 --- a/src/declarative/qml/qdeclarativecompiler.cpp +++ b/src/declarative/qml/qdeclarativecompiler.cpp @@ -122,13 +122,26 @@ bool QDeclarativeCompiler::isAttachedPropertyName(const QByteArray &name) /*! Returns true if \a name refers to a signal property, false otherwise. - Signal property names are those that start with "on", followed by a capital - letter. + Signal property names are those that start with "on", followed by a first + character which is either a capital letter or one or more underscores followed + by a capital letter, which is then followed by other allowed characters. + + Note that although ECMA-262r3 supports dollarsigns and escaped unicode + character codes in property names, for simplicity and performance reasons + QML only supports letters, numbers and underscores. */ bool QDeclarativeCompiler::isSignalPropertyName(const QByteArray &name) { - return name.length() >= 3 && name.startsWith("on") && - 'A' <= name.at(2) && 'Z' >= name.at(2); + if (name.length() < 3) return false; + if (!name.startsWith("on")) return false; + int ns = name.size(); + for (int i = 2; i < ns; ++i) { + char curr = name.at(i); + if (curr == '_') continue; + if (curr >= 'A' && curr <= 'Z') return true; + return false; + } + return false; // consists solely of underscores - invalid. } /*! @@ -1340,8 +1353,15 @@ bool QDeclarativeCompiler::buildSignal(QDeclarativeParser::Property *prop, QDecl QByteArray name = prop->name; Q_ASSERT(name.startsWith("on")); name = name.mid(2); - if(name[0] >= 'A' && name[0] <= 'Z') - name[0] = name[0] - 'A' + 'a'; + + // Note that the property name could start with any alpha or '_' or '$' character, + // so we need to do the lower-casing of the first alpha character. + for (int firstAlphaIndex = 0; firstAlphaIndex < name.size(); ++firstAlphaIndex) { + if (name[firstAlphaIndex] >= 'A' && name[firstAlphaIndex] <= 'Z') { + name[firstAlphaIndex] = name[firstAlphaIndex] - 'A' + 'a'; + break; + } + } bool notInRevision = false; int sigIdx = indexOfSignal(obj, name, ¬InRevision); diff --git a/src/declarative/scenegraph/qsgcontext.cpp b/src/declarative/scenegraph/qsgcontext.cpp index dd35c06ecc..e36d432e40 100644 --- a/src/declarative/scenegraph/qsgcontext.cpp +++ b/src/declarative/scenegraph/qsgcontext.cpp @@ -164,7 +164,7 @@ QSGEngine *QSGContext::engine() const The texture can be considered as deleted after this function has been called. */ -void QSGContext::schdelueTextureForCleanup(QSGTexture *texture) +void QSGContext::scheduleTextureForCleanup(QSGTexture *texture) { Q_D(QSGContext); d->textureMutex.lock(); diff --git a/src/declarative/scenegraph/qsgcontext_p.h b/src/declarative/scenegraph/qsgcontext_p.h index 59522b6f73..1344ac705d 100644 --- a/src/declarative/scenegraph/qsgcontext_p.h +++ b/src/declarative/scenegraph/qsgcontext_p.h @@ -104,7 +104,7 @@ public: static QSGContext *createDefaultContext(); - void schdelueTextureForCleanup(QSGTexture *texture); + void scheduleTextureForCleanup(QSGTexture *texture); void cleanupTextures(); void setFlashModeEnabled(bool enabled); diff --git a/src/declarative/util/qdeclarativepixmapcache.cpp b/src/declarative/util/qdeclarativepixmapcache.cpp index ef0c422411..d2e8af5867 100644 --- a/src/declarative/util/qdeclarativepixmapcache.cpp +++ b/src/declarative/util/qdeclarativepixmapcache.cpp @@ -228,7 +228,7 @@ public: ~QDeclarativePixmapData() { if (texture && context) { - context->schdelueTextureForCleanup(texture); + context->scheduleTextureForCleanup(texture); } } diff --git a/src/imports/folderlistmodel/qdeclarativefolderlistmodel.cpp b/src/imports/folderlistmodel/qdeclarativefolderlistmodel.cpp index 9fe01bf22c..8a9166751d 100644 --- a/src/imports/folderlistmodel/qdeclarativefolderlistmodel.cpp +++ b/src/imports/folderlistmodel/qdeclarativefolderlistmodel.cpp @@ -53,7 +53,7 @@ class QDeclarativeFolderListModelPrivate { public: QDeclarativeFolderListModelPrivate() - : sortField(QDeclarativeFolderListModel::Name), sortReversed(false), count(0) { + : sortField(QDeclarativeFolderListModel::Name), sortReversed(false), count(0), showDirs(true), showDots(false), showOnlyReadable(false), insideRefresh(false) { nameFilters << QLatin1String("*"); } @@ -90,6 +90,10 @@ public: QDeclarativeFolderListModel::SortField sortField; bool sortReversed; int count; + bool showDirs; + bool showDots; + bool showOnlyReadable; + bool insideRefresh; }; /*! @@ -222,15 +226,39 @@ void QDeclarativeFolderListModel::setFolder(const QUrl &folder) { if (folder == d->folder) return; - QModelIndex index = d->model.index(folder.toLocalFile()); - if ((index.isValid() && d->model.isDir(index)) || folder.toLocalFile().isEmpty()) { + QModelIndex index = d->model.index(folder.toLocalFile()); // This can modify the filtering rules. + if ((index.isValid() && d->model.isDir(index)) || folder.toLocalFile().isEmpty()) { d->folder = folder; - QMetaObject::invokeMethod(this, "refresh", Qt::QueuedConnection); + QMetaObject::invokeMethod(this, "resetFiltering", Qt::QueuedConnection); // resetFiltering will invoke refresh(). emit folderChanged(); } } +void QDeclarativeFolderListModel::resetFiltering() +{ + // ensure that we reset the filtering rules, because the QDirModel::index() + // function isn't quite as const as it claims to be. + QDir::Filters filt = d->model.filter(); + + if (d->showDirs) + filt |= (QDir::AllDirs | QDir::Drives); + else + filt &= ~(QDir::AllDirs | QDir::Drives); + + if (d->showDots) + filt &= ~QDir::NoDotAndDotDot; + else + filt |= QDir::NoDotAndDotDot; + + if (d->showOnlyReadable) + filt |= QDir::Readable; + else + filt &= ~QDir::Readable; + + d->model.setFilter(filt); // this causes a refresh(). +} + /*! \qmlproperty url FolderListModel::parentFolder @@ -363,12 +391,17 @@ bool QDeclarativeFolderListModel::isFolder(int index) const void QDeclarativeFolderListModel::refresh() { + if (d->insideRefresh) + return; + d->insideRefresh = true; + d->folderIndex = QModelIndex(); if (d->count) { emit beginRemoveRows(QModelIndex(), 0, d->count-1); d->count = 0; emit endRemoveRows(); } + d->folderIndex = d->model.index(d->folder.toLocalFile()); int newcount = d->model.rowCount(d->folderIndex); if (newcount) { @@ -376,6 +409,8 @@ void QDeclarativeFolderListModel::refresh() d->count = newcount; emit endInsertRows(); } + + d->insideRefresh = false; // finished refreshing. } void QDeclarativeFolderListModel::inserted(const QModelIndex &index, int start, int end) @@ -423,10 +458,13 @@ void QDeclarativeFolderListModel::setShowDirs(bool on) { if (!(d->model.filter() & QDir::AllDirs) == !on) return; - if (on) + if (on) { + d->showDirs = true; d->model.setFilter(d->model.filter() | QDir::AllDirs | QDir::Drives); - else + } else { + d->showDirs = false; d->model.setFilter(d->model.filter() & ~(QDir::AllDirs | QDir::Drives)); + } } /*! @@ -448,10 +486,13 @@ void QDeclarativeFolderListModel::setShowDotAndDotDot(bool on) { if (!(d->model.filter() & QDir::NoDotAndDotDot) == on) return; - if (on) + if (on) { + d->showDots = true; d->model.setFilter(d->model.filter() & ~QDir::NoDotAndDotDot); - else + } else { + d->showDots = false; d->model.setFilter(d->model.filter() | QDir::NoDotAndDotDot); + } } /*! @@ -473,10 +514,13 @@ void QDeclarativeFolderListModel::setShowOnlyReadable(bool on) { if (!(d->model.filter() & QDir::Readable) == !on) return; - if (on) + if (on) { + d->showOnlyReadable = true; d->model.setFilter(d->model.filter() | QDir::Readable); - else + } else { + d->showOnlyReadable = false; d->model.setFilter(d->model.filter() & ~QDir::Readable); + } } //![code] diff --git a/src/imports/folderlistmodel/qdeclarativefolderlistmodel.h b/src/imports/folderlistmodel/qdeclarativefolderlistmodel.h index 17dc84c026..b0d91e4797 100644 --- a/src/imports/folderlistmodel/qdeclarativefolderlistmodel.h +++ b/src/imports/folderlistmodel/qdeclarativefolderlistmodel.h @@ -136,6 +136,7 @@ Q_SIGNALS: //![class end] private Q_SLOTS: void refresh(); + void resetFiltering(); void inserted(const QModelIndex &index, int start, int end); void removed(const QModelIndex &index, int start, int end); void handleDataChanged(const QModelIndex &start, const QModelIndex &end); diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/changeslots/propertyChangeSlotErrors.1.qml b/tests/auto/declarative/qdeclarativeecmascript/data/changeslots/propertyChangeSlotErrors.1.qml new file mode 100644 index 0000000000..3182d6b4ab --- /dev/null +++ b/tests/auto/declarative/qdeclarativeecmascript/data/changeslots/propertyChangeSlotErrors.1.qml @@ -0,0 +1,12 @@ +import QtQuick 1.0 + +Item { + property int changeCount: 0 + + property bool _nameWithUnderscore: false + + // this should error, since the first alpha isn't capitalised. + on_nameWithUnderscoreChanged: { + changeCount = changeCount + 2; + } +} diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/changeslots/propertyChangeSlotErrors.2.qml b/tests/auto/declarative/qdeclarativeecmascript/data/changeslots/propertyChangeSlotErrors.2.qml new file mode 100644 index 0000000000..50ef0b34dd --- /dev/null +++ b/tests/auto/declarative/qdeclarativeecmascript/data/changeslots/propertyChangeSlotErrors.2.qml @@ -0,0 +1,12 @@ +import QtQuick 1.0 + +Item { + property int changeCount: 0 + + property bool ____nameWithUnderscores: false + + // this should error, since the first alpha isn't capitalised + on____nameWithUnderscoresChanged: { + changeCount = changeCount + 3; + } +} diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/changeslots/propertyChangeSlotErrors.3.qml b/tests/auto/declarative/qdeclarativeecmascript/data/changeslots/propertyChangeSlotErrors.3.qml new file mode 100644 index 0000000000..343cf91720 --- /dev/null +++ b/tests/auto/declarative/qdeclarativeecmascript/data/changeslots/propertyChangeSlotErrors.3.qml @@ -0,0 +1,12 @@ +import QtQuick 1.0 + +Item { + property int changeCount: 0 + + // invalid property name - we don't allow $ + property bool $nameWithDollarsign: false + + on$NameWithDollarsignChanged: { + changeCount = changeCount + 4; + } +} diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/changeslots/propertyChangeSlotErrors.4.qml b/tests/auto/declarative/qdeclarativeecmascript/data/changeslots/propertyChangeSlotErrors.4.qml new file mode 100644 index 0000000000..b8459875df --- /dev/null +++ b/tests/auto/declarative/qdeclarativeecmascript/data/changeslots/propertyChangeSlotErrors.4.qml @@ -0,0 +1,12 @@ +import QtQuick 1.0 + +Item { + property int changeCount: 0 + + property bool _6nameWithUnderscoreNumber: false + + // invalid property name - the first character after an underscore must be a letter + on_6NameWithUnderscoreNumberChanged: { + changeCount = changeCount + 3; + } +} diff --git a/tests/auto/declarative/qdeclarativeecmascript/data/changeslots/propertyChangeSlots.qml b/tests/auto/declarative/qdeclarativeecmascript/data/changeslots/propertyChangeSlots.qml new file mode 100644 index 0000000000..d31c89382b --- /dev/null +++ b/tests/auto/declarative/qdeclarativeecmascript/data/changeslots/propertyChangeSlots.qml @@ -0,0 +1,27 @@ +import QtQuick 1.0 + +Item { + property int changeCount: 0 + + property bool normalName: false + property bool _nameWithUnderscore: false + property bool ____nameWithUnderscores: false + + onNormalNameChanged: { + changeCount = changeCount + 1; + } + + on_NameWithUnderscoreChanged: { + changeCount = changeCount + 2; + } + + on____NameWithUnderscoresChanged: { + changeCount = changeCount + 3; + } + + Component.onCompleted: { + normalName = true; + _nameWithUnderscore = true; + ____nameWithUnderscores = true; + } +} diff --git a/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp b/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp index a408000a37..b2859b5b5b 100644 --- a/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp +++ b/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp @@ -147,6 +147,7 @@ private slots: void moduleApi(); void importScripts(); void scarceResources(); + void propertyChangeSlots(); void bug1(); void bug2(); @@ -2777,6 +2778,48 @@ void tst_qdeclarativeecmascript::scarceResources() delete object; } +void tst_qdeclarativeecmascript::propertyChangeSlots() +{ + // ensure that allowable property names are allowed and onPropertyNameChanged slots are generated correctly. + QDeclarativeComponent component(&engine, TEST_FILE("changeslots/propertyChangeSlots.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + delete object; + + // ensure that invalid property names fail properly. + QTest::ignoreMessage(QtWarningMsg, "QDeclarativeComponent: Component is not ready"); + QDeclarativeComponent e1(&engine, TEST_FILE("changeslots/propertyChangeSlotErrors.1.qml")); + QString expectedErrorString = e1.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on_nameWithUnderscoreChanged\""); + QCOMPARE(e1.errors().at(0).toString(), expectedErrorString); + object = e1.create(); + QVERIFY(object == 0); + delete object; + + QTest::ignoreMessage(QtWarningMsg, "QDeclarativeComponent: Component is not ready"); + QDeclarativeComponent e2(&engine, TEST_FILE("changeslots/propertyChangeSlotErrors.2.qml")); + expectedErrorString = e2.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on____nameWithUnderscoresChanged\""); + QCOMPARE(e2.errors().at(0).toString(), expectedErrorString); + object = e2.create(); + QVERIFY(object == 0); + delete object; + + QTest::ignoreMessage(QtWarningMsg, "QDeclarativeComponent: Component is not ready"); + QDeclarativeComponent e3(&engine, TEST_FILE("changeslots/propertyChangeSlotErrors.3.qml")); + expectedErrorString = e3.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on$NameWithDollarsignChanged\""); + QCOMPARE(e3.errors().at(0).toString(), expectedErrorString); + object = e3.create(); + QVERIFY(object == 0); + delete object; + + QTest::ignoreMessage(QtWarningMsg, "QDeclarativeComponent: Component is not ready"); + QDeclarativeComponent e4(&engine, TEST_FILE("changeslots/propertyChangeSlotErrors.4.qml")); + expectedErrorString = e4.url().toString() + QLatin1String(":9:5: Cannot assign to non-existent property \"on_6NameWithUnderscoreNumberChanged\""); + QCOMPARE(e4.errors().at(0).toString(), expectedErrorString); + object = e4.create(); + QVERIFY(object == 0); + delete object; +} + // Test that assigning a null object works // Regressed with: df1788b4dbbb2826ae63f26bdf166342595343f4 void tst_qdeclarativeecmascript::nullObjectBinding() diff --git a/tests/auto/declarative/qdeclarativefolderlistmodel/data/resetFiltering.qml b/tests/auto/declarative/qdeclarativefolderlistmodel/data/resetFiltering.qml new file mode 100644 index 0000000000..d9a8ec4535 --- /dev/null +++ b/tests/auto/declarative/qdeclarativefolderlistmodel/data/resetFiltering.qml @@ -0,0 +1,5 @@ +import Qt.labs.folderlistmodel 1.0 + +FolderListModel { + showDirs: false +} diff --git a/tests/auto/declarative/qdeclarativefolderlistmodel/data/resetfiltering/innerdir/test2.txt b/tests/auto/declarative/qdeclarativefolderlistmodel/data/resetfiltering/innerdir/test2.txt new file mode 100644 index 0000000000..97e64bb130 --- /dev/null +++ b/tests/auto/declarative/qdeclarativefolderlistmodel/data/resetfiltering/innerdir/test2.txt @@ -0,0 +1 @@ +This file contains some text. diff --git a/tests/auto/declarative/qdeclarativefolderlistmodel/data/resetfiltering/test.txt b/tests/auto/declarative/qdeclarativefolderlistmodel/data/resetfiltering/test.txt new file mode 100644 index 0000000000..97e64bb130 --- /dev/null +++ b/tests/auto/declarative/qdeclarativefolderlistmodel/data/resetfiltering/test.txt @@ -0,0 +1 @@ +This file contains some text. diff --git a/tests/auto/declarative/qdeclarativefolderlistmodel/tst_qdeclarativefolderlistmodel.cpp b/tests/auto/declarative/qdeclarativefolderlistmodel/tst_qdeclarativefolderlistmodel.cpp index ffd5d381ce..0594cbe304 100644 --- a/tests/auto/declarative/qdeclarativefolderlistmodel/tst_qdeclarativefolderlistmodel.cpp +++ b/tests/auto/declarative/qdeclarativefolderlistmodel/tst_qdeclarativefolderlistmodel.cpp @@ -72,6 +72,7 @@ public slots: private slots: void basicProperties(); + void resetFiltering(); void refresh(); private: @@ -109,7 +110,7 @@ void tst_qdeclarativefolderlistmodel::basicProperties() QVERIFY(flm != 0); flm->setProperty("folder",QUrl::fromLocalFile(SRCDIR "/data")); - QTRY_COMPARE(flm->property("count").toInt(),2); // wait for refresh + QTRY_COMPARE(flm->property("count").toInt(),4); // wait for refresh QCOMPARE(flm->property("folder").toUrl(), QUrl::fromLocalFile(SRCDIR "/data")); QCOMPARE(flm->property("parentFolder").toUrl(), QUrl::fromLocalFile(SRCDIR)); QCOMPARE(flm->property("sortField").toInt(), int(Name)); @@ -125,6 +126,37 @@ void tst_qdeclarativefolderlistmodel::basicProperties() QCOMPARE(flm->property("folder").toUrl(), QUrl::fromLocalFile("")); } +void tst_qdeclarativefolderlistmodel::resetFiltering() +{ + // see QTBUG-17837 + QDeclarativeComponent component(&engine, QUrl::fromLocalFile(SRCDIR "/data/resetFiltering.qml")); + checkNoErrors(component); + + QAbstractListModel *flm = qobject_cast<QAbstractListModel*>(component.create()); + QVERIFY(flm != 0); + + connect(flm, SIGNAL(rowsRemoved(const QModelIndex&,int,int)), + this, SLOT(removed(const QModelIndex&,int,int))); + + flm->setProperty("folder",QUrl::fromLocalFile(SRCDIR "/data/resetfiltering")); + QTRY_COMPARE(flm->property("count").toInt(),1); // should just be "test.txt" visible + int count = flm->rowCount(); + QCOMPARE(removeStart, 0); + QCOMPARE(removeEnd, count-1); + + flm->setProperty("folder",QUrl::fromLocalFile(SRCDIR "/data/resetfiltering/innerdir")); + QTRY_COMPARE(flm->property("count").toInt(),1); // should just be "test2.txt" visible + count = flm->rowCount(); + QCOMPARE(removeStart, 0); + QCOMPARE(removeEnd, count-1); + + flm->setProperty("folder",QUrl::fromLocalFile(SRCDIR "/data/resetfiltering")); + QTRY_COMPARE(flm->property("count").toInt(),1); // should just be "test.txt" visible + count = flm->rowCount(); + QCOMPARE(removeStart, 0); + QCOMPARE(removeEnd, count-1); +} + void tst_qdeclarativefolderlistmodel::refresh() { QDeclarativeComponent component(&engine, QUrl::fromLocalFile(SRCDIR "/data/basic.qml")); @@ -134,7 +166,7 @@ void tst_qdeclarativefolderlistmodel::refresh() QVERIFY(flm != 0); flm->setProperty("folder",QUrl::fromLocalFile(SRCDIR "/data")); - QTRY_COMPARE(flm->property("count").toInt(),2); // wait for refresh + QTRY_COMPARE(flm->property("count").toInt(),4); // wait for refresh int count = flm->rowCount(); |