From f96f0083402ef0210192406aaa943b1b5297a584 Mon Sep 17 00:00:00 2001 From: Gunnar Sletta Date: Mon, 10 Dec 2012 11:46:56 +0100 Subject: Add command-line option to enable multisampling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I9ec8961342c23ea2c116c970e84aa412365412a9 Reviewed-by: Samuel Rødal --- tools/qmlscene/main.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/tools/qmlscene/main.cpp b/tools/qmlscene/main.cpp index 87a577b44e..a90870aa0b 100644 --- a/tools/qmlscene/main.cpp +++ b/tools/qmlscene/main.cpp @@ -152,6 +152,7 @@ struct Options , slowAnimations(false) , quitImmediately(false) , resizeViewToRootItem(false) + , multisample(false) { } @@ -167,6 +168,7 @@ struct Options bool slowAnimations; bool quitImmediately; bool resizeViewToRootItem; + bool multisample; QString translationFile; }; @@ -349,7 +351,7 @@ static void usage() qWarning(" --maximized ............................... run maximized"); qWarning(" --fullscreen .............................. run fullscreen"); qWarning(" --transparent ............................. Make the window transparent"); - qWarning(" --no-multisample .......................... Disable multisampling (anti-aliasing)"); + qWarning(" --multisample ............................. Enable multisampling (OpenGL anti-aliasing)"); qWarning(" --no-version-detection .................... Do not try to detect the version of the .qml file"); qWarning(" --slow-animations ......................... Run all animations in slow motion"); qWarning(" --resize-to-root .......................... Resize the window to the size of the root item"); @@ -391,6 +393,8 @@ int main(int argc, char ** argv) options.translationFile = QLatin1String(argv[++i]); else if (lowerArgument == QLatin1String("--resize-to-root")) options.resizeViewToRootItem = true; + else if (lowerArgument == QLatin1String("--multisample")) + options.multisample = true; else if (lowerArgument == QLatin1String("-i") && i + 1 < argc) imports.append(QString::fromLatin1(argv[++i])); else if (lowerArgument == QLatin1String("-b") && i + 2 < argc) { @@ -497,14 +501,16 @@ int main(int argc, char ** argv) } if (window) { + QSurfaceFormat surfaceFormat; + if (options.multisample) + surfaceFormat.setSamples(16); if (options.transparent) { - QSurfaceFormat surfaceFormat; surfaceFormat.setAlphaBufferSize(8); - window->setFormat(surfaceFormat); window->setClearBeforeRendering(true); window->setColor(QColor(Qt::transparent)); window->setFlags(Qt::FramelessWindowHint); } + window->setFormat(surfaceFormat); if (options.fullscreen) window->showFullScreen(); -- cgit v1.2.3 From bac602c454f38ddde01167a3f75cb10ce1cfb876 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 10 Dec 2012 22:06:33 +0100 Subject: Throw a parse error on octal numbers and escape sequences This is compliant with EcmaScript 5.1, where octal numbers and escape sequences are an optional and deprecated part of the syntax. Allow leading 0's in qml mode, but interpret the result as decimal. This is also to keep compatibility with existing code. Change-Id: Ic3450ec3dd17966846751ee688a90c65939ba78f Reviewed-by: Erik Verbruggen --- src/qml/qml/parser/qqmljslexer.cpp | 24 +++++++++++++++++----- .../qml/qqmlecmascript/data/numberParsing.1.qml | 9 ++++++++ .../qml/qqmlecmascript/data/numberParsing.2.qml | 9 ++++++++ .../qml/qqmlecmascript/data/numberParsing.3.qml | 9 ++++++++ .../qml/qqmlecmascript/data/numberParsing.4.qml | 9 ++++++++ .../qml/qqmlecmascript/data/numberParsing.5.qml | 9 ++++++++ .../qml/qqmlecmascript/data/numberParsing.6.qml | 9 ++++++++ .../qml/qqmlecmascript/data/numberParsing.7.qml | 9 ++++++++ .../qqmlecmascript/data/stringParsing_error.1.qml | 9 ++++++++ .../qqmlecmascript/data/stringParsing_error.2.qml | 9 ++++++++ .../qqmlecmascript/data/stringParsing_error.3.qml | 9 ++++++++ .../qqmlecmascript/data/stringParsing_error.4.qml | 9 ++++++++ .../auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp | 24 ++++++++++++++++++++++ 13 files changed, 142 insertions(+), 5 deletions(-) create mode 100644 tests/auto/qml/qqmlecmascript/data/numberParsing.1.qml create mode 100644 tests/auto/qml/qqmlecmascript/data/numberParsing.2.qml create mode 100644 tests/auto/qml/qqmlecmascript/data/numberParsing.3.qml create mode 100644 tests/auto/qml/qqmlecmascript/data/numberParsing.4.qml create mode 100644 tests/auto/qml/qqmlecmascript/data/numberParsing.5.qml create mode 100644 tests/auto/qml/qqmlecmascript/data/numberParsing.6.qml create mode 100644 tests/auto/qml/qqmlecmascript/data/numberParsing.7.qml create mode 100644 tests/auto/qml/qqmlecmascript/data/stringParsing_error.1.qml create mode 100644 tests/auto/qml/qqmlecmascript/data/stringParsing_error.2.qml create mode 100644 tests/auto/qml/qqmlecmascript/data/stringParsing_error.3.qml create mode 100644 tests/auto/qml/qqmlecmascript/data/stringParsing_error.4.qml diff --git a/src/qml/qml/parser/qqmljslexer.cpp b/src/qml/qml/parser/qqmljslexer.cpp index b53f16ca2c..0142e94db7 100644 --- a/src/qml/qml/parser/qqmljslexer.cpp +++ b/src/qml/qml/parser/qqmljslexer.cpp @@ -631,14 +631,24 @@ again: case 'v': u = QLatin1Char('\v'); scanChar(); break; case '0': - if (! _codePtr[1].isDigit()) { + if (! _codePtr->isDigit()) { scanChar(); u = QLatin1Char('\0'); - } else { - // ### parse deprecated octal escape sequence ? - u = _char; + break; } - break; + // fall through + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + _errorCode = IllegalEscapeSequence; + _errorMessage = QCoreApplication::translate("QQmlParser", "Octal escape sequences are not allowed"); + return T_ERROR; case '\r': if (isLineTerminatorSequence() == 2) { @@ -770,6 +780,10 @@ int Lexer::scanNumber(QChar ch) _tokenValue = integer; return T_NUMERIC_LITERAL; } + } else if (_char.isDigit() && !qmlMode()) { + _errorCode = IllegalCharacter; + _errorMessage = QCoreApplication::translate("QQmlParser", "Decimal numbers can't start with '0'"); + return T_ERROR; } QVarLengthArray chars; diff --git a/tests/auto/qml/qqmlecmascript/data/numberParsing.1.qml b/tests/auto/qml/qqmlecmascript/data/numberParsing.1.qml new file mode 100644 index 0000000000..1b83a1be0b --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/numberParsing.1.qml @@ -0,0 +1,9 @@ + +import QtQuick 2.0 + +QtObject { + function code() { + var x = 0; + } +} + diff --git a/tests/auto/qml/qqmlecmascript/data/numberParsing.2.qml b/tests/auto/qml/qqmlecmascript/data/numberParsing.2.qml new file mode 100644 index 0000000000..77f13178c3 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/numberParsing.2.qml @@ -0,0 +1,9 @@ + +import QtQuick 2.0 + +QtObject { + function code() { + var x = 1.01; + } +} + diff --git a/tests/auto/qml/qqmlecmascript/data/numberParsing.3.qml b/tests/auto/qml/qqmlecmascript/data/numberParsing.3.qml new file mode 100644 index 0000000000..f20baf305a --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/numberParsing.3.qml @@ -0,0 +1,9 @@ + +import QtQuick 2.0 + +QtObject { + function code() { + var x = 1e-10; + } +} + diff --git a/tests/auto/qml/qqmlecmascript/data/numberParsing.4.qml b/tests/auto/qml/qqmlecmascript/data/numberParsing.4.qml new file mode 100644 index 0000000000..e115dbbbb1 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/numberParsing.4.qml @@ -0,0 +1,9 @@ + +import QtQuick 2.0 + +QtObject { + function code() { + var x = -1.2; + } +} + diff --git a/tests/auto/qml/qqmlecmascript/data/numberParsing.5.qml b/tests/auto/qml/qqmlecmascript/data/numberParsing.5.qml new file mode 100644 index 0000000000..c3db17602a --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/numberParsing.5.qml @@ -0,0 +1,9 @@ + +import QtQuick 2.0 + +QtObject { + function code() { + var x = .4e-5; + } +} + diff --git a/tests/auto/qml/qqmlecmascript/data/numberParsing.6.qml b/tests/auto/qml/qqmlecmascript/data/numberParsing.6.qml new file mode 100644 index 0000000000..471db8708a --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/numberParsing.6.qml @@ -0,0 +1,9 @@ + +import QtQuick 2.0 + +QtObject { + function code() { + var x = 0x1; + } +} + diff --git a/tests/auto/qml/qqmlecmascript/data/numberParsing.7.qml b/tests/auto/qml/qqmlecmascript/data/numberParsing.7.qml new file mode 100644 index 0000000000..f8f8e1aae8 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/numberParsing.7.qml @@ -0,0 +1,9 @@ + +import QtQuick 2.0 + +QtObject { + function code() { + var x = 0Xa; + } +} + diff --git a/tests/auto/qml/qqmlecmascript/data/stringParsing_error.1.qml b/tests/auto/qml/qqmlecmascript/data/stringParsing_error.1.qml new file mode 100644 index 0000000000..71b82b956d --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/stringParsing_error.1.qml @@ -0,0 +1,9 @@ + +import QtQuick 2.0 + +QtObject { + function code() { + var x = "\01"; + } +} + diff --git a/tests/auto/qml/qqmlecmascript/data/stringParsing_error.2.qml b/tests/auto/qml/qqmlecmascript/data/stringParsing_error.2.qml new file mode 100644 index 0000000000..787f1d86c7 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/stringParsing_error.2.qml @@ -0,0 +1,9 @@ + +import QtQuick 2.0 + +QtObject { + function code() { + var x = "\1a"; + } +} + diff --git a/tests/auto/qml/qqmlecmascript/data/stringParsing_error.3.qml b/tests/auto/qml/qqmlecmascript/data/stringParsing_error.3.qml new file mode 100644 index 0000000000..9954617b6b --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/stringParsing_error.3.qml @@ -0,0 +1,9 @@ + +import QtQuick 2.0 + +QtObject { + function code() { + var x = "\012"; + } +} + diff --git a/tests/auto/qml/qqmlecmascript/data/stringParsing_error.4.qml b/tests/auto/qml/qqmlecmascript/data/stringParsing_error.4.qml new file mode 100644 index 0000000000..5bf41f0c1a --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/stringParsing_error.4.qml @@ -0,0 +1,9 @@ + +import QtQuick 2.0 + +QtObject { + function code() { + var x = "\00a"; + } +} + diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp index a99c5fa8be..852c0ef4b5 100644 --- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp +++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp @@ -285,6 +285,8 @@ private slots: void propertyOverride(); void concatenatedStringPropertyAccess(); void jsOwnedObjectsDeletedOnEngineDestroy(); + void numberParsing(); + void stringParsing(); private: static void propertyVarWeakRefCallback(v8::Persistent object, void* parameter); @@ -7335,6 +7337,28 @@ void tst_qqmlecmascript::jsOwnedObjectsDeletedOnEngineDestroy() delete object; } +void tst_qqmlecmascript::numberParsing() +{ + for (int i = 1; i < 8; ++i) { + QString file("numberParsing.%1.qml"); + file = file.arg(i); + QQmlComponent component(&engine, testFileUrl(file)); + QObject *object = component.create(); + QVERIFY(object != 0); + } +} + +void tst_qqmlecmascript::stringParsing() +{ + for (int i = 1; i < 5; ++i) { + QString file("stringParsing_error.%1.qml"); + file = file.arg(i); + QQmlComponent component(&engine, testFileUrl(file)); + QObject *object = component.create(); + QVERIFY(object == 0); + } +} + QTEST_MAIN(tst_qqmlecmascript) #include "tst_qqmlecmascript.moc" -- cgit v1.2.3 From 731139b512db04dcb52db4ef3c4c1ad51007e2c7 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 11 Dec 2012 00:25:55 +0100 Subject: Parse identifiers according to spec If a unicode escape sequence decodes to a char that is not allowed in the identifier we need to throw a parse error. So decode before checking whether the char is valid. Also fix the set of accepted unicode characters for the start and in the middle of an identifier to what the spec demands. Change-Id: I35075b0587ef4e4edb745750cec6d5c764631c0d Reviewed-by: Erik Verbruggen --- src/qml/qml/parser/qqmljslexer.cpp | 125 ++++++++++++++++++++++++++++--------- 1 file changed, 94 insertions(+), 31 deletions(-) diff --git a/src/qml/qml/parser/qqmljslexer.cpp b/src/qml/qml/parser/qqmljslexer.cpp index 0142e94db7..1270fdbc91 100644 --- a/src/qml/qml/parser/qqmljslexer.cpp +++ b/src/qml/qml/parser/qqmljslexer.cpp @@ -275,6 +275,59 @@ QChar Lexer::decodeUnicodeEscapeCharacter(bool *ok) return QChar(); } +static inline bool isIdentifierStart(QChar ch) +{ + // fast path for ascii + if ((ch.unicode() >= 'a' && ch.unicode() <= 'z') || + (ch.unicode() >= 'A' && ch.unicode() <= 'Z') || + ch == '$' || ch == '_') + return true; + + switch (ch.category()) { + case QChar::Number_Letter: + case QChar::Letter_Uppercase: + case QChar::Letter_Lowercase: + case QChar::Letter_Titlecase: + case QChar::Letter_Modifier: + case QChar::Letter_Other: + return true; + default: + break; + } + return false; +} + +static bool isIdentifierPart(QChar ch) +{ + // fast path for ascii + if ((ch.unicode() >= 'a' && ch.unicode() <= 'z') || + (ch.unicode() >= 'A' && ch.unicode() <= 'Z') || + (ch.unicode() >= '0' && ch.unicode() <= '9') || + ch == '$' || ch == '_' || + ch.unicode() == 0x200c /* ZWNJ */ || ch.unicode() == 0x200d /* ZWJ */) + return true; + + switch (ch.category()) { + case QChar::Mark_NonSpacing: + case QChar::Mark_SpacingCombining: + + case QChar::Number_DecimalDigit: + case QChar::Number_Letter: + + case QChar::Letter_Uppercase: + case QChar::Letter_Lowercase: + case QChar::Letter_Titlecase: + case QChar::Letter_Modifier: + case QChar::Letter_Other: + + case QChar::Punctuation_Connector: + return true; + default: + break; + } + return false; +} + int Lexer::scanToken() { if (_stackToken != -1) { @@ -697,28 +750,28 @@ again: case '9': return scanNumber(ch); - default: - if (ch.isLetter() || ch == QLatin1Char('$') || ch == QLatin1Char('_') || (ch == QLatin1Char('\\') && _char == QLatin1Char('u'))) { - bool identifierWithEscapeChars = false; - if (ch == QLatin1Char('\\')) { - identifierWithEscapeChars = true; + default: { + QChar c = ch; + bool identifierWithEscapeChars = false; + if (c == QLatin1Char('\\') && _char == QLatin1Char('u')) { + identifierWithEscapeChars = true; + bool ok = false; + c = decodeUnicodeEscapeCharacter(&ok); + if (! ok) { + _errorCode = IllegalUnicodeEscapeSequence; + _errorMessage = QCoreApplication::translate("QQmlParser", "Illegal unicode escape sequence"); + return T_ERROR; + } + } + if (isIdentifierStart(c)) { + if (identifierWithEscapeChars) { _tokenText.resize(0); - bool ok = false; - _tokenText += decodeUnicodeEscapeCharacter(&ok); + _tokenText += c; _validTokenText = true; - if (! ok) { - _errorCode = IllegalUnicodeEscapeSequence; - _errorMessage = QCoreApplication::translate("QQmlParser", "Illegal unicode escape sequence"); - return T_ERROR; - } } while (true) { - if (_char.isLetterOrNumber() || _char == QLatin1Char('$') || _char == QLatin1Char('_')) { - if (identifierWithEscapeChars) - _tokenText += _char; - - scanChar(); - } else if (_char == QLatin1Char('\\') && _codePtr[0] == QLatin1Char('u')) { + c = _char; + if (_char == QLatin1Char('\\') && _codePtr[0] == QLatin1Char('u')) { if (! identifierWithEscapeChars) { identifierWithEscapeChars = true; _tokenText.resize(0); @@ -728,31 +781,41 @@ again: scanChar(); // skip '\\' bool ok = false; - _tokenText += decodeUnicodeEscapeCharacter(&ok); + c = decodeUnicodeEscapeCharacter(&ok); if (! ok) { _errorCode = IllegalUnicodeEscapeSequence; _errorMessage = QCoreApplication::translate("QQmlParser", "Illegal unicode escape sequence"); return T_ERROR; } - } else { - _tokenLength = _codePtr - _tokenStartPtr - 1; + if (isIdentifierPart(c)) + _tokenText += c; + continue; + } else if (isIdentifierPart(c)) { + if (identifierWithEscapeChars) + _tokenText += c; + + scanChar(); + continue; + } - int kind = T_IDENTIFIER; + _tokenLength = _codePtr - _tokenStartPtr - 1; - if (! identifierWithEscapeChars) - kind = classify(_tokenStartPtr, _tokenLength, _qmlMode); + int kind = T_IDENTIFIER; - if (_engine) { - if (kind == T_IDENTIFIER && identifierWithEscapeChars) - _tokenSpell = _engine->newStringRef(_tokenText); - else - _tokenSpell = _engine->midRef(_tokenStartPtr - _code.unicode(), _tokenLength); - } + if (! identifierWithEscapeChars) + kind = classify(_tokenStartPtr, _tokenLength, _qmlMode); - return kind; + if (_engine) { + if (kind == T_IDENTIFIER && identifierWithEscapeChars) + _tokenSpell = _engine->newStringRef(_tokenText); + else + _tokenSpell = _engine->midRef(_tokenStartPtr - _code.unicode(), _tokenLength); } + + return kind; } } + } break; } -- cgit v1.2.3 From 163b0056096a9d7eca2272fe7e969f99ecd1cab8 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 11 Dec 2012 06:59:37 +0100 Subject: Fix the test suite We now fail when parsing octal numbers or escape sequences. This is ok according to the EcmaScript 5.1 spec. So remove all usages of these numbers or sequences. In addition delete an invalid test case that can't possibly be parsed correctly. V8 also chokes on it. Change-Id: I889de2810310f38206343d16175f9e31ddb44d30 Reviewed-by: Erik Verbruggen --- .../tests/ecma/GlobalObject/15.1.2.2-1.js | 38 ------------ .../tests/ecma/GlobalObject/15.1.2.3-1.js | 31 ---------- .../tests/ecma/GlobalObject/15.1.2.3-2.js | 22 ------- .../tests/ecma/GlobalObject/15.1.2.6.js | 3 - .../tests/ecma/GlobalObject/15.1.2.7.js | 3 - .../tests/ecma/LexicalConventions/7.7.3-1.js | 10 --- .../tests/ecma/LexicalConventions/7.7.3-2.js | 16 ----- .../tests/ecma/LexicalConventions/7.7.3.js | 18 ------ .../tests/ecma/LexicalConventions/7.7.4.js | 48 --------------- .../qml/parserstress/tests/ecma/Math/15.8.2.1.js | 5 -- .../tests/ecma/TypeConversion/9.3.1-3.js | 10 --- .../parserstress/tests/ecma_3/RegExp/octal-002.js | 64 ------------------- .../tests/ecma_3/RegExp/regress-85721.js | 8 +-- .../tests/ecma_3/Regress/regress-441477-01.js | 2 +- .../tests/ecma_3/String/regress-392378.js | 2 +- .../tests/ecma_3/Unicode/regress-352044-02-n.js | 72 ---------------------- tests/auto/qml/qmlmin/tst_qmlmin.cpp | 4 ++ tests/auto/qml/qqmllanguage/data/literals.qml | 7 +-- tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp | 4 +- 19 files changed, 15 insertions(+), 352 deletions(-) delete mode 100755 tests/auto/qml/parserstress/tests/ecma_3/Unicode/regress-352044-02-n.js diff --git a/tests/auto/qml/parserstress/tests/ecma/GlobalObject/15.1.2.2-1.js b/tests/auto/qml/parserstress/tests/ecma/GlobalObject/15.1.2.2-1.js index a4bf1c7de6..87cba8cd2f 100644 --- a/tests/auto/qml/parserstress/tests/ecma/GlobalObject/15.1.2.2-1.js +++ b/tests/auto/qml/parserstress/tests/ecma/GlobalObject/15.1.2.2-1.js @@ -252,44 +252,6 @@ for ( HEX_STRING = "-0x0", HEX_VALUE = 0, POWER = 0; POWER < 15; POWER++, HEX_ST HEX_VALUE -= Math.pow(16,POWER)*15; } -// let us do some octal tests. numbers that start with 0 and do not provid a radix should -// default to using "0" as a radix. - -var OCT_STRING = "0"; -var OCT_VALUE = 0; - -for ( OCT_STRING = "0", OCT_VALUE = 0, POWER = 0; POWER < 15; POWER++, OCT_STRING = OCT_STRING +"7" ) { - new TestCase( SECTION, "parseInt("+OCT_STRING+")", OCT_VALUE, parseInt(OCT_STRING) ); - OCT_VALUE += Math.pow(8,POWER)*7; -} - -for ( OCT_STRING = "-0", OCT_VALUE = 0, POWER = 0; POWER < 15; POWER++, OCT_STRING = OCT_STRING +"7" ) { - new TestCase( SECTION, "parseInt("+OCT_STRING+")", OCT_VALUE, parseInt(OCT_STRING) ); - OCT_VALUE -= Math.pow(8,POWER)*7; -} - -// should get the same results as above if we provid the radix of 8 (or 010) - -for ( OCT_STRING = "0", OCT_VALUE = 0, POWER = 0; POWER < 15; POWER++, OCT_STRING = OCT_STRING +"7" ) { - new TestCase( SECTION, "parseInt("+OCT_STRING+",8)", OCT_VALUE, parseInt(OCT_STRING,8) ); - OCT_VALUE += Math.pow(8,POWER)*7; -} -for ( OCT_STRING = "-0", OCT_VALUE = 0, POWER = 0; POWER < 15; POWER++, OCT_STRING = OCT_STRING +"7" ) { - new TestCase( SECTION, "parseInt("+OCT_STRING+",010)", OCT_VALUE, parseInt(OCT_STRING,010) ); - OCT_VALUE -= Math.pow(8,POWER)*7; -} - -// we shall stop parsing digits when we get one that isn't a numeric literal of the type we think -// it should be. -for ( OCT_STRING = "0", OCT_VALUE = 0, POWER = 0; POWER < 15; POWER++, OCT_STRING = OCT_STRING +"7" ) { - new TestCase( SECTION, "parseInt("+OCT_STRING+"8,8)", OCT_VALUE, parseInt(OCT_STRING+"8",8) ); - OCT_VALUE += Math.pow(8,POWER)*7; -} -for ( OCT_STRING = "-0", OCT_VALUE = 0, POWER = 0; POWER < 15; POWER++, OCT_STRING = OCT_STRING +"7" ) { - new TestCase( SECTION, "parseInt("+OCT_STRING+"8,010)", OCT_VALUE, parseInt(OCT_STRING+"8",010) ); - OCT_VALUE -= Math.pow(8,POWER)*7; -} - new TestCase( SECTION, "parseInt( '0x' )", NaN, diff --git a/tests/auto/qml/parserstress/tests/ecma/GlobalObject/15.1.2.3-1.js b/tests/auto/qml/parserstress/tests/ecma/GlobalObject/15.1.2.3-1.js index 56bf83adcc..a89154e597 100644 --- a/tests/auto/qml/parserstress/tests/ecma/GlobalObject/15.1.2.3-1.js +++ b/tests/auto/qml/parserstress/tests/ecma/GlobalObject/15.1.2.3-1.js @@ -384,43 +384,12 @@ new TestCase( SECTION, "parseFloat(0XE)", 14, parseFloat(0XE)); new TestCase( SECTION, "parseFloat(0XF)", 15, parseFloat(0XF)); -// A StringNumericLiteral may not use octal notation - -new TestCase( SECTION, "parseFloat('00')", 0, parseFloat("00")); -new TestCase( SECTION, "parseFloat('01')", 1, parseFloat("01")); -new TestCase( SECTION, "parseFloat('02')", 2, parseFloat("02")); -new TestCase( SECTION, "parseFloat('03')", 3, parseFloat("03")); -new TestCase( SECTION, "parseFloat('04')", 4, parseFloat("04")); -new TestCase( SECTION, "parseFloat('05')", 5, parseFloat("05")); -new TestCase( SECTION, "parseFloat('06')", 6, parseFloat("06")); -new TestCase( SECTION, "parseFloat('07')", 7, parseFloat("07")); -new TestCase( SECTION, "parseFloat('010')", 10, parseFloat("010")); -new TestCase( SECTION, "parseFloat('011')", 11, parseFloat("011")); - // A StringNumericLIteral may have any number of leading 0 digits new TestCase( SECTION, "parseFloat('001')", 1, parseFloat("001")); new TestCase( SECTION, "parseFloat('0001')", 1, parseFloat("0001")); new TestCase( SECTION, "parseFloat(' 0001 ')", 1, parseFloat(" 0001 ")); -// an octal numeric literal should be treated as an octal - -new TestCase( SECTION, "parseFloat(00)", 0, parseFloat(00)); -new TestCase( SECTION, "parseFloat(01)", 1, parseFloat(01)); -new TestCase( SECTION, "parseFloat(02)", 2, parseFloat(02)); -new TestCase( SECTION, "parseFloat(03)", 3, parseFloat(03)); -new TestCase( SECTION, "parseFloat(04)", 4, parseFloat(04)); -new TestCase( SECTION, "parseFloat(05)", 5, parseFloat(05)); -new TestCase( SECTION, "parseFloat(06)", 6, parseFloat(06)); -new TestCase( SECTION, "parseFloat(07)", 7, parseFloat(07)); -new TestCase( SECTION, "parseFloat(010)", 8, parseFloat(010)); -new TestCase( SECTION, "parseFloat(011)", 9, parseFloat(011)); - -// A StringNumericLIteral may have any number of leading 0 digits - -new TestCase( SECTION, "parseFloat(001)", 1, parseFloat(001)); -new TestCase( SECTION, "parseFloat(0001)", 1, parseFloat(0001)); - // make sure it's reflexive new TestCase( SECTION, "parseFloat(Math.PI)", Math.PI, parseFloat(Math.PI)); new TestCase( SECTION, "parseFloat(Math.LN2)", Math.LN2, parseFloat(Math.LN2)); diff --git a/tests/auto/qml/parserstress/tests/ecma/GlobalObject/15.1.2.3-2.js b/tests/auto/qml/parserstress/tests/ecma/GlobalObject/15.1.2.3-2.js index 557b93f637..1167b73ee1 100644 --- a/tests/auto/qml/parserstress/tests/ecma/GlobalObject/15.1.2.3-2.js +++ b/tests/auto/qml/parserstress/tests/ecma/GlobalObject/15.1.2.3-2.js @@ -256,28 +256,6 @@ new TestCase( SECTION, "parseFloat(' 0XD')", 0, parseFl new TestCase( SECTION, "parseFloat(' 0XE')", 0, parseFloat(" 0XE")); new TestCase( SECTION, "parseFloat(' 0XF')", 0, parseFloat(" 0XF")); -// A StringNumericLiteral may not use octal notation - -new TestCase( SECTION, "parseFloat(' 00')", 0, parseFloat(" 00")); -new TestCase( SECTION, "parseFloat(' 01')", 1, parseFloat(" 01")); -new TestCase( SECTION, "parseFloat(' 02')", 2, parseFloat(" 02")); -new TestCase( SECTION, "parseFloat(' 03')", 3, parseFloat(" 03")); -new TestCase( SECTION, "parseFloat(' 04')", 4, parseFloat(" 04")); -new TestCase( SECTION, "parseFloat(' 05')", 5, parseFloat(" 05")); -new TestCase( SECTION, "parseFloat(' 06')", 6, parseFloat(" 06")); -new TestCase( SECTION, "parseFloat(' 07')", 7, parseFloat(" 07")); -new TestCase( SECTION, "parseFloat(' 010')", 10, parseFloat(" 010")); -new TestCase( SECTION, "parseFloat(' 011')", 11, parseFloat(" 011")); - -// A StringNumericLIteral may have any number of leading 0 digits - -new TestCase( SECTION, "parseFloat(' 001')", 1, parseFloat(" 001")); -new TestCase( SECTION, "parseFloat(' 0001')", 1, parseFloat(" 0001")); - -// A StringNumericLIteral may have any number of leading 0 digits - -new TestCase( SECTION, "parseFloat(001)", 1, parseFloat(001)); -new TestCase( SECTION, "parseFloat(0001)", 1, parseFloat(0001)); // make sure it' s reflexive new TestCase( SECTION, "parseFloat( ' ' +Math.PI+' ')", Math.PI, parseFloat( ' ' +Math.PI+' ')); diff --git a/tests/auto/qml/parserstress/tests/ecma/GlobalObject/15.1.2.6.js b/tests/auto/qml/parserstress/tests/ecma/GlobalObject/15.1.2.6.js index faeeb9e0b0..18f1986d7e 100644 --- a/tests/auto/qml/parserstress/tests/ecma/GlobalObject/15.1.2.6.js +++ b/tests/auto/qml/parserstress/tests/ecma/GlobalObject/15.1.2.6.js @@ -106,9 +106,6 @@ new TestCase( SECTION, "isNaN( '0xaa' )", false, isNaN( "0xaa" ) new TestCase( SECTION, "isNaN( '0x0A' )", false, isNaN( "0x0A" ) ); new TestCase( SECTION, "isNaN( '0xAA' )", false, isNaN( "0xAA" ) ); -new TestCase( SECTION, "isNaN( 077 )", false, isNaN( 077 ) ); -new TestCase( SECTION, "isNaN( '077' )", false, isNaN( "077" ) ); - new TestCase( SECTION, "isNaN( Number.NaN )", true, isNaN(Number.NaN) ); new TestCase( SECTION, "isNaN( Number.POSITIVE_INFINITY )", false, isNaN(Number.POSITIVE_INFINITY) ); diff --git a/tests/auto/qml/parserstress/tests/ecma/GlobalObject/15.1.2.7.js b/tests/auto/qml/parserstress/tests/ecma/GlobalObject/15.1.2.7.js index e3db5e7931..9eb52a8ff1 100644 --- a/tests/auto/qml/parserstress/tests/ecma/GlobalObject/15.1.2.7.js +++ b/tests/auto/qml/parserstress/tests/ecma/GlobalObject/15.1.2.7.js @@ -107,9 +107,6 @@ new TestCase( SECTION, "isFinite( '0xaa' )", true, isFinite( "0 new TestCase( SECTION, "isFinite( '0x0A' )", true, isFinite( "0x0A" ) ); new TestCase( SECTION, "isFinite( '0xAA' )", true, isFinite( "0xAA" ) ); -new TestCase( SECTION, "isFinite( 077 )", true, isFinite( 077 ) ); -new TestCase( SECTION, "isFinite( '077' )", true, isFinite( "077" ) ); - new TestCase( SECTION, "isFinite( new String('Infinity') )", false, isFinite(new String("Infinity")) ); new TestCase( SECTION, "isFinite( new String('-Infinity') )", false, isFinite(new String("-Infinity")) ); diff --git a/tests/auto/qml/parserstress/tests/ecma/LexicalConventions/7.7.3-1.js b/tests/auto/qml/parserstress/tests/ecma/LexicalConventions/7.7.3-1.js index fb13b24e07..3eacb2990f 100644 --- a/tests/auto/qml/parserstress/tests/ecma/LexicalConventions/7.7.3-1.js +++ b/tests/auto/qml/parserstress/tests/ecma/LexicalConventions/7.7.3-1.js @@ -84,16 +84,6 @@ new TestCase( SECTION, 4294967296, 0x100000000 ); -new TestCase( SECTION, - "077777777777777777", - 2251799813685247, - 077777777777777777 ); - -new TestCase( SECTION, - "077777777777777776", - 2251799813685246, - 077777777777777776 ); - new TestCase( SECTION, "0x1fffffffffffff", 9007199254740991, diff --git a/tests/auto/qml/parserstress/tests/ecma/LexicalConventions/7.7.3-2.js b/tests/auto/qml/parserstress/tests/ecma/LexicalConventions/7.7.3-2.js index 8fbe16cb8f..6121cd54fd 100644 --- a/tests/auto/qml/parserstress/tests/ecma/LexicalConventions/7.7.3-2.js +++ b/tests/auto/qml/parserstress/tests/ecma/LexicalConventions/7.7.3-2.js @@ -74,20 +74,4 @@ new TestCase( SECTION, 9, 9 ); -new TestCase( SECTION, - "09", - 9, - 09 ); - -new TestCase( SECTION, - "099", - 99, - 099 ); - - -new TestCase( SECTION, - "077", - 63, - 077 ); - test(); diff --git a/tests/auto/qml/parserstress/tests/ecma/LexicalConventions/7.7.3.js b/tests/auto/qml/parserstress/tests/ecma/LexicalConventions/7.7.3.js index 9ccb912752..29635596e1 100644 --- a/tests/auto/qml/parserstress/tests/ecma/LexicalConventions/7.7.3.js +++ b/tests/auto/qml/parserstress/tests/ecma/LexicalConventions/7.7.3.js @@ -215,24 +215,6 @@ new TestCase( SECTION, "0XE", 14, 0XE ); new TestCase( SECTION, "0XF", 15, 0XF ); -new TestCase( SECTION, "00", 0, 00 ); -new TestCase( SECTION, "01", 1, 01 ); -new TestCase( SECTION, "02", 2, 02 ); -new TestCase( SECTION, "03", 3, 03 ); -new TestCase( SECTION, "04", 4, 04 ); -new TestCase( SECTION, "05", 5, 05 ); -new TestCase( SECTION, "06", 6, 06 ); -new TestCase( SECTION, "07", 7, 07 ); - -new TestCase( SECTION, "000", 0, 000 ); -new TestCase( SECTION, "011", 9, 011 ); -new TestCase( SECTION, "022", 18, 022 ); -new TestCase( SECTION, "033", 27, 033 ); -new TestCase( SECTION, "044", 36, 044 ); -new TestCase( SECTION, "055", 45, 055 ); -new TestCase( SECTION, "066", 54, 066 ); -new TestCase( SECTION, "077", 63, 077 ); - new TestCase( SECTION, "0.00000000001", 0.00000000001, 0.00000000001 ); new TestCase( SECTION, "0.00000000001e-2", 0.0000000000001, 0.00000000001e-2 ); diff --git a/tests/auto/qml/parserstress/tests/ecma/LexicalConventions/7.7.4.js b/tests/auto/qml/parserstress/tests/ecma/LexicalConventions/7.7.4.js index 015a385220..92a04942c7 100644 --- a/tests/auto/qml/parserstress/tests/ecma/LexicalConventions/7.7.4.js +++ b/tests/auto/qml/parserstress/tests/ecma/LexicalConventions/7.7.4.js @@ -76,53 +76,6 @@ new TestCase( SECTION, "\\v", String.fromCharCode(0x000B), "\v" ); // DoubleStringCharacters:DoubleStringCharacter::EscapeSequence::OctalEscapeSequence -new TestCase( SECTION, "\\00", String.fromCharCode(0x0000), "\00" ); -new TestCase( SECTION, "\\01", String.fromCharCode(0x0001), "\01" ); -new TestCase( SECTION, "\\02", String.fromCharCode(0x0002), "\02" ); -new TestCase( SECTION, "\\03", String.fromCharCode(0x0003), "\03" ); -new TestCase( SECTION, "\\04", String.fromCharCode(0x0004), "\04" ); -new TestCase( SECTION, "\\05", String.fromCharCode(0x0005), "\05" ); -new TestCase( SECTION, "\\06", String.fromCharCode(0x0006), "\06" ); -new TestCase( SECTION, "\\07", String.fromCharCode(0x0007), "\07" ); - -new TestCase( SECTION, "\\010", String.fromCharCode(0x0008), "\010" ); -new TestCase( SECTION, "\\011", String.fromCharCode(0x0009), "\011" ); -new TestCase( SECTION, "\\012", String.fromCharCode(0x000A), "\012" ); -new TestCase( SECTION, "\\013", String.fromCharCode(0x000B), "\013" ); -new TestCase( SECTION, "\\014", String.fromCharCode(0x000C), "\014" ); -new TestCase( SECTION, "\\015", String.fromCharCode(0x000D), "\015" ); -new TestCase( SECTION, "\\016", String.fromCharCode(0x000E), "\016" ); -new TestCase( SECTION, "\\017", String.fromCharCode(0x000F), "\017" ); -new TestCase( SECTION, "\\020", String.fromCharCode(0x0010), "\020" ); -new TestCase( SECTION, "\\042", String.fromCharCode(0x0022), "\042" ); - -new TestCase( SECTION, "\\0", String.fromCharCode(0x0000), "\0" ); -new TestCase( SECTION, "\\1", String.fromCharCode(0x0001), "\1" ); -new TestCase( SECTION, "\\2", String.fromCharCode(0x0002), "\2" ); -new TestCase( SECTION, "\\3", String.fromCharCode(0x0003), "\3" ); -new TestCase( SECTION, "\\4", String.fromCharCode(0x0004), "\4" ); -new TestCase( SECTION, "\\5", String.fromCharCode(0x0005), "\5" ); -new TestCase( SECTION, "\\6", String.fromCharCode(0x0006), "\6" ); -new TestCase( SECTION, "\\7", String.fromCharCode(0x0007), "\7" ); - -new TestCase( SECTION, "\\10", String.fromCharCode(0x0008), "\10" ); -new TestCase( SECTION, "\\11", String.fromCharCode(0x0009), "\11" ); -new TestCase( SECTION, "\\12", String.fromCharCode(0x000A), "\12" ); -new TestCase( SECTION, "\\13", String.fromCharCode(0x000B), "\13" ); -new TestCase( SECTION, "\\14", String.fromCharCode(0x000C), "\14" ); -new TestCase( SECTION, "\\15", String.fromCharCode(0x000D), "\15" ); -new TestCase( SECTION, "\\16", String.fromCharCode(0x000E), "\16" ); -new TestCase( SECTION, "\\17", String.fromCharCode(0x000F), "\17" ); -new TestCase( SECTION, "\\20", String.fromCharCode(0x0010), "\20" ); -new TestCase( SECTION, "\\42", String.fromCharCode(0x0022), "\42" ); - -new TestCase( SECTION, "\\000", String.fromCharCode(0), "\000" ); -new TestCase( SECTION, "\\111", String.fromCharCode(73), "\111" ); -new TestCase( SECTION, "\\222", String.fromCharCode(146), "\222" ); -new TestCase( SECTION, "\\333", String.fromCharCode(219), "\333" ); - -// following line commented out as it causes a compile time error -// new TestCase( SECTION, "\\444", "444", "\444" ); // DoubleStringCharacters:DoubleStringCharacter::EscapeSequence::HexEscapeSequence /* @@ -208,7 +161,6 @@ new TestCase( SECTION, "\\w", "w", "\w" ); new TestCase( SECTION, "\\x", "x", "\x" ); new TestCase( SECTION, "\\y", "y", "\y" ); new TestCase( SECTION, "\\z", "z", "\z" ); -new TestCase( SECTION, "\\9", "9", "\9" ); new TestCase( SECTION, "\\A", "A", "\A" ); new TestCase( SECTION, "\\B", "B", "\B" ); diff --git a/tests/auto/qml/parserstress/tests/ecma/Math/15.8.2.1.js b/tests/auto/qml/parserstress/tests/ecma/Math/15.8.2.1.js index 0412742767..abe1095d40 100644 --- a/tests/auto/qml/parserstress/tests/ecma/Math/15.8.2.1.js +++ b/tests/auto/qml/parserstress/tests/ecma/Math/15.8.2.1.js @@ -193,11 +193,6 @@ new TestCase( SECTION, 4095, Math.abs( -0xfff ) ); -new TestCase( SECTION, - "Math.abs( -0777 )", - 511, - Math.abs(-0777 ) ); - new TestCase( SECTION, "Math.abs('-1e-1')", 0.1, diff --git a/tests/auto/qml/parserstress/tests/ecma/TypeConversion/9.3.1-3.js b/tests/auto/qml/parserstress/tests/ecma/TypeConversion/9.3.1-3.js index dc56427395..3141906d5d 100644 --- a/tests/auto/qml/parserstress/tests/ecma/TypeConversion/9.3.1-3.js +++ b/tests/auto/qml/parserstress/tests/ecma/TypeConversion/9.3.1-3.js @@ -295,16 +295,6 @@ new TestCase( SECTION, 4294967296, 0x100000000 ); -new TestCase( SECTION, - "077777777777777777", - 2251799813685247, - 077777777777777777 ); - -new TestCase( SECTION, - "077777777777777776", - 2251799813685246, - 077777777777777776 ); - new TestCase( SECTION, "0x1fffffffffffff", 9007199254740991, diff --git a/tests/auto/qml/parserstress/tests/ecma_3/RegExp/octal-002.js b/tests/auto/qml/parserstress/tests/ecma_3/RegExp/octal-002.js index 401ad43c11..053720d7e9 100644 --- a/tests/auto/qml/parserstress/tests/ecma_3/RegExp/octal-002.js +++ b/tests/auto/qml/parserstress/tests/ecma_3/RegExp/octal-002.js @@ -125,70 +125,6 @@ expectedmatch = Array(string); addThis(); -/* - * This one should produce a match. The two-character string - * 'a' + '\011' is duplicated in the pattern and test string: - */ -status = inSection(4); -pattern = /.\011/; -string = 'a\011'; -actualmatch = string.match(pattern); -expectedmatch = Array(string); -addThis(); - - -/* - * Same as above, only now, for the second character of the string, - * use the Unicode escape '\u0009' instead of the octal escape '\011' - */ -status = inSection(5); -pattern = /.\011/; -string = 'a\u0009'; -actualmatch = string.match(pattern); -expectedmatch = Array(string); -addThis(); - - -/* - * Same as above, only now for the second character of the string, - * use the hex escape '\x09' instead of the octal escape '\011' - */ -status = inSection(6); -pattern = /.\011/; -string = 'a\x09'; -actualmatch = string.match(pattern); -expectedmatch = Array(string); -addThis(); - - -/* - * Same as above, only now for the second character of the string, - * use the escape '\t' instead of the octal escape '\011' - */ -status = inSection(7); -pattern = /.\011/; -string = 'a\t'; -actualmatch = string.match(pattern); -expectedmatch = Array(string); -addThis(); - - -/* - * Return to the string from Section 1. - * - * Unlike Section 1, use the RegExp() function to create the - * regexp pattern: null character followed by the string '11'. - * - * Since this is exactly what the string is, we should get a match - - */ -status = inSection(8); -string = 'a' + String.fromCharCode(0) + '11'; -pattern = RegExp(string); -actualmatch = string.match(pattern); -expectedmatch = Array(string); -addThis(); - - //------------------------------------------------------------------------------------------------- diff --git a/tests/auto/qml/parserstress/tests/ecma_3/RegExp/regress-85721.js b/tests/auto/qml/parserstress/tests/ecma_3/RegExp/regress-85721.js index bca1a15e19..7b811abc31 100644 --- a/tests/auto/qml/parserstress/tests/ecma_3/RegExp/regress-85721.js +++ b/tests/auto/qml/parserstress/tests/ecma_3/RegExp/regress-85721.js @@ -111,11 +111,11 @@ testRegExp([status], [re], [str], [result], [expect]); //# Some things for avoiding backslashitis later on. $esc = '\\\\'; $Period = '\.'; -$space = '\040'; $tab = '\t'; +$space = '\x20'; $tab = '\t'; $OpenBR = '\\['; $CloseBR = '\\]'; $OpenParen = '\\('; $CloseParen = '\\)'; -$NonASCII = '\x80-\xff'; $ctrl = '\000-\037'; -$CRlist = '\n\015'; //# note: this should really be only \015. +$NonASCII = '\x80-\xff'; $ctrl = '\0-\x1f'; +$CRlist = '\n\x0d'; //# note: this should really be only \015. // Items 19, 20, 21 $qtext = '[^' + $esc + $NonASCII + $CRlist + '\"]'; // # for within "..." $dtext = '[^' + $esc + $NonASCII + $CRlist + $OpenBR + $CloseBR + ']'; // # for within [...] @@ -226,7 +226,7 @@ $route_addr = '>'; // # > //# Item 3: phrase........ -$phrase_ctrl = '\000-\010\012-\037'; // # like ctrl, but without tab +$phrase_ctrl = '\0-\x08\x0a-\x1f'; // # like ctrl, but without tab //# Like atom-char, but without listing space, and uses phrase_ctrl. //# Since the class is negated, this matches the same as atom-char plus space and tab diff --git a/tests/auto/qml/parserstress/tests/ecma_3/Regress/regress-441477-01.js b/tests/auto/qml/parserstress/tests/ecma_3/Regress/regress-441477-01.js index 1be199743d..27ddfab51d 100755 --- a/tests/auto/qml/parserstress/tests/ecma_3/Regress/regress-441477-01.js +++ b/tests/auto/qml/parserstress/tests/ecma_3/Regress/regress-441477-01.js @@ -37,7 +37,7 @@ var gTestfile = 'regress-441477-01.js'; //----------------------------------------------------------------------------- -var BUGNUMBER = 441477-01; +var BUGNUMBER = 441477.01; var summary = ''; var actual = 'No Exception'; var expect = 'No Exception'; diff --git a/tests/auto/qml/parserstress/tests/ecma_3/String/regress-392378.js b/tests/auto/qml/parserstress/tests/ecma_3/String/regress-392378.js index 368fde1278..59564b272e 100755 --- a/tests/auto/qml/parserstress/tests/ecma_3/String/regress-392378.js +++ b/tests/auto/qml/parserstress/tests/ecma_3/String/regress-392378.js @@ -55,7 +55,7 @@ function test() expect = ["", undefined, ""] + ''; actual = "y".split(/(x)?\1y/) + ''; - reportCompare(expect, actual, summary + ': "y".split(/(x)?\1y/)'); + reportCompare(expect, actual, summary + ': "y".split(/(x)?\\1y/)'); expect = ["", undefined, ""] + ''; actual = "y".split(/(x)?y/) + ''; diff --git a/tests/auto/qml/parserstress/tests/ecma_3/Unicode/regress-352044-02-n.js b/tests/auto/qml/parserstress/tests/ecma_3/Unicode/regress-352044-02-n.js deleted file mode 100755 index 0e3c4b0189..0000000000 --- a/tests/auto/qml/parserstress/tests/ecma_3/Unicode/regress-352044-02-n.js +++ /dev/null @@ -1,72 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is JavaScript Engine testing utilities. - * - * The Initial Developer of the Original Code is - * Mozilla Foundation. - * Portions created by the Initial Developer are Copyright (C) 2007 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): Martin Honnen - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -var gTestfile = 'regress-352044-02-n.js'; -//----------------------------------------------------------------------------- -var BUGNUMBER = 352044; -var summary = 'issues with Unicode escape sequences in JavaScript source code'; -var actual = 'No Error'; -var expect = 'SyntaxError'; - - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - enterFunc ('test'); - printBugNumber(BUGNUMBER); - printStatus (summary); - - print('This test case is expected to throw an uncaught SyntaxError'); - - try - { - var i = 1; - i \u002b= 1; - print(i); - } - catch(ex) - { - actual = ex + ''; - } - - reportCompare(expect, actual, summary); - - exitFunc ('test'); -} diff --git a/tests/auto/qml/qmlmin/tst_qmlmin.cpp b/tests/auto/qml/qmlmin/tst_qmlmin.cpp index 60b69f2239..be93a79034 100644 --- a/tests/auto/qml/qmlmin/tst_qmlmin.cpp +++ b/tests/auto/qml/qmlmin/tst_qmlmin.cpp @@ -120,6 +120,10 @@ void tst_qmlmin::initTestCase() invalidFiles << "tests/auto/qml/qqmlecmascript/data/jsimportfail/missingFileQualifier.js"; invalidFiles << "tests/auto/qml/qqmlecmascript/data/jsimportfail/missingModuleQualifier.js"; invalidFiles << "tests/auto/qml/qqmlecmascript/data/jsimportfail/missingModuleVersion.js"; + invalidFiles << "tests/auto/qml/qqmlecmascript/data/stringParsing_error.1.qml"; + invalidFiles << "tests/auto/qml/qqmlecmascript/data/stringParsing_error.2.qml"; + invalidFiles << "tests/auto/qml/qqmlecmascript/data/stringParsing_error.3.qml"; + invalidFiles << "tests/auto/qml/qqmlecmascript/data/stringParsing_error.4.qml"; } QStringList tst_qmlmin::findFiles(const QDir &d) diff --git a/tests/auto/qml/qqmllanguage/data/literals.qml b/tests/auto/qml/qqmllanguage/data/literals.qml index 3a3e8461e4..9c6003dc40 100644 --- a/tests/auto/qml/qqmllanguage/data/literals.qml +++ b/tests/auto/qml/qqmllanguage/data/literals.qml @@ -2,7 +2,7 @@ import QtQuick 2.0 QtObject { property variant n1: 0xFe32 // hex - property variant n2: 015 // octal + property variant n2: 015 property variant n3: -4.2E11 // floating-point literals property variant n4: .1e9 property variant n5: 3e-12 @@ -18,7 +18,6 @@ QtObject { property variant c7: "\'" property variant c8: "\"" property variant c9: "\\" - property variant c10: "\251" - property variant c11: "\xA9" - property variant c12: "\u00A9" // unicode + property variant c10: "\xA9" + property variant c11: "\u00A9" // unicode } diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index cf6c83444e..70f8f484e0 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -3066,8 +3066,8 @@ void tst_qqmllanguage::literals_data() QTest::newRow("special9") << "c9" << QVariant(QString("\\")); // We don't handle octal escape sequences // QTest::newRow("special10") << "c10" << QVariant(QString("\251")); - QTest::newRow("special11") << "c11" << QVariant(QString::fromLatin1("\xa9")); - QTest::newRow("special12") << "c12" << QVariant(QString::fromUtf8("\u00A9")); + QTest::newRow("special11") << "c10" << QVariant(QString::fromLatin1("\xa9")); + QTest::newRow("special12") << "c11" << QVariant(QString::fromUtf8("\u00A9")); } void tst_qqmllanguage::literals() -- cgit v1.2.3 From 8147bc2f70306f4dff95c933e97566e75afb18f8 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 11 Dec 2012 07:50:10 +0100 Subject: Don't bail out on 0 chars in comments Gets a few more parser tests in the test suite to pass. Change-Id: Id9ac5211f64aafdd621f6187ad7061ff967bc947 Reviewed-by: Erik Verbruggen --- src/qml/qml/parser/qqmljslexer.cpp | 5 +++-- src/qml/qml/parser/qqmljslexer_p.h | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/qml/qml/parser/qqmljslexer.cpp b/src/qml/qml/parser/qqmljslexer.cpp index 1270fdbc91..4e15065e30 100644 --- a/src/qml/qml/parser/qqmljslexer.cpp +++ b/src/qml/qml/parser/qqmljslexer.cpp @@ -136,6 +136,7 @@ void Lexer::setCode(const QString &code, int lineno, bool qmlMode) _tokenSpell = QStringRef(); _codePtr = code.unicode(); + _endPtr = _codePtr + code.length(); _lastLinePtr = _codePtr; _tokenLinePtr = _codePtr; _tokenStartPtr = _codePtr; @@ -448,7 +449,7 @@ again: case '/': if (_char == QLatin1Char('*')) { scanChar(); - while (!_char.isNull()) { + while (_codePtr <= _endPtr) { if (_char == QLatin1Char('*')) { scanChar(); if (_char == QLatin1Char('/')) { @@ -466,7 +467,7 @@ again: } } } else if (_char == QLatin1Char('/')) { - while (!_char.isNull() && !isLineTerminator()) { + while (_codePtr <= _endPtr && !isLineTerminator()) { scanChar(); } if (_engine) { diff --git a/src/qml/qml/parser/qqmljslexer_p.h b/src/qml/qml/parser/qqmljslexer_p.h index 66dbb39e1a..e8ffbd3e70 100644 --- a/src/qml/qml/parser/qqmljslexer_p.h +++ b/src/qml/qml/parser/qqmljslexer_p.h @@ -211,6 +211,7 @@ private: QStringRef _tokenSpell; const QChar *_codePtr; + const QChar *_endPtr; const QChar *_lastLinePtr; const QChar *_tokenLinePtr; const QChar *_tokenStartPtr; -- cgit v1.2.3 From 8077a5ea7cb8536307f24fb9b4dd605dd64ed711 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 11 Dec 2012 07:51:22 +0100 Subject: Bail out on stray newlines in strings in JS mode Still allow newlines in QML mode to keep compatibility for existing code. Change-Id: I11dbd5a73ea8958f5ddc199b77a919969f8a5214 Reviewed-by: Erik Verbruggen --- src/qml/qml/parser/qqmljslexer.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/qml/qml/parser/qqmljslexer.cpp b/src/qml/qml/parser/qqmljslexer.cpp index 4e15065e30..158640c47e 100644 --- a/src/qml/qml/parser/qqmljslexer.cpp +++ b/src/qml/qml/parser/qqmljslexer.cpp @@ -610,7 +610,13 @@ again: if (_engine) { while (!_char.isNull()) { - if (isLineTerminator() || _char == QLatin1Char('\\')) { + if (isLineTerminator()) { + if (qmlMode()) + break; + _errorCode = IllegalCharacter; + _errorMessage = QCoreApplication::translate("QQmlParser", "Stray newline in string literal"); + return T_ERROR; + } else if (_char == QLatin1Char('\\')) { break; } else if (_char == quote) { _tokenSpell = _engine->midRef(startCode - _code.unicode() - 1, _codePtr - startCode); -- cgit v1.2.3 From 8bd89ac26344c1a617dcfd4730f93559ad48e9d4 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 11 Dec 2012 08:02:09 +0100 Subject: super is always a reserved keyword According to 7.6.1.2 of the EcmaScript 5.1 spec, super is always a reserved keyword. Change-Id: Idc300326c036eb9f0a12aa8eec8427023b7652b2 Reviewed-by: Erik Verbruggen --- src/qml/qml/parser/qqmljskeywords_p.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qml/qml/parser/qqmljskeywords_p.h b/src/qml/qml/parser/qqmljskeywords_p.h index 49ce0e2a8f..5d28f88829 100644 --- a/src/qml/qml/parser/qqmljskeywords_p.h +++ b/src/qml/qml/parser/qqmljskeywords_p.h @@ -309,7 +309,7 @@ static inline int classify5(const QChar *s, bool qmlMode) { if (s[2].unicode() == 'p') { if (s[3].unicode() == 'e') { if (s[4].unicode() == 'r') { - return qmlMode ? Lexer::T_SUPER : Lexer::T_IDENTIFIER; + return qmlMode ? Lexer::T_SUPER : Lexer::T_RESERVED_WORD; } } } -- cgit v1.2.3 From e0f21c696a1f960d0e55f33eed9d1dc37d8edae0 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 11 Dec 2012 21:09:47 +0100 Subject: Don't assume QChar::null() is the end of input The lexer currently assumes a null QChar implies the end of our input stream. Change it to take the full string into account. null QChar's inside comments or strings are now accepted, everywhere else they will cause a parse error. Change-Id: I60c40a32d5afe94c6c56d8a8092fc32726b98420 Reviewed-by: Erik Verbruggen --- src/qml/qml/parser/qqmljslexer.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/qml/qml/parser/qqmljslexer.cpp b/src/qml/qml/parser/qqmljslexer.cpp index 158640c47e..c3ddd0a9e8 100644 --- a/src/qml/qml/parser/qqmljslexer.cpp +++ b/src/qml/qml/parser/qqmljslexer.cpp @@ -364,7 +364,7 @@ again: _tokenStartPtr = _codePtr - 1; _tokenLine = _currentLineNumber; - if (_char.isNull()) + if (_codePtr > _endPtr) return EOF_SYMBOL; const QChar ch = _char; @@ -609,7 +609,7 @@ again: const QChar *startCode = _codePtr; if (_engine) { - while (!_char.isNull()) { + while (_codePtr <= _endPtr) { if (isLineTerminator()) { if (qmlMode()) break; @@ -634,7 +634,7 @@ again: while (startCode != _codePtr - 1) _tokenText += *startCode++; - while (! _char.isNull()) { + while (_codePtr <= _endPtr) { if (unsigned sequenceLength = isLineTerminatorSequence()) { multilineStringLiteral = true; _tokenText += _char; @@ -984,7 +984,7 @@ bool Lexer::scanRegExp(RegExpBodyPrefix prefix) _tokenText += _char; scanChar(); - if (_char.isNull() || isLineTerminator()) { + if (_codePtr > _endPtr || isLineTerminator()) { _errorMessage = QCoreApplication::translate("QQmlParser", "Unterminated regular expression backslash sequence"); return false; } @@ -998,7 +998,7 @@ bool Lexer::scanRegExp(RegExpBodyPrefix prefix) _tokenText += _char; scanChar(); - while (! _char.isNull() && ! isLineTerminator()) { + while (_codePtr <= _endPtr && ! isLineTerminator()) { if (_char == QLatin1Char(']')) break; else if (_char == QLatin1Char('\\')) { @@ -1006,7 +1006,7 @@ bool Lexer::scanRegExp(RegExpBodyPrefix prefix) _tokenText += _char; scanChar(); - if (_char.isNull() || isLineTerminator()) { + if (_codePtr > _endPtr || isLineTerminator()) { _errorMessage = QCoreApplication::translate("QQmlParser", "Unterminated regular expression backslash sequence"); return false; } @@ -1029,7 +1029,7 @@ bool Lexer::scanRegExp(RegExpBodyPrefix prefix) break; default: - if (_char.isNull() || isLineTerminator()) { + if (_codePtr > _endPtr || isLineTerminator()) { _errorMessage = QCoreApplication::translate("QQmlParser", "Unterminated regular expression literal"); return false; } else { -- cgit v1.2.3 From 0f5eec1e36ec1b61e0ff93b62245fb9377a26d6d Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 11 Dec 2012 22:24:29 +0100 Subject: Fix invalid compression in qmlmin qmlmin could compress identifiers in an invalid way if it contained special chars that would get expanded to unicode escape sequences Change-Id: I35b3ba01f68b69b34c4cd19616afb8b4b4cd6fa3 Reviewed-by: Erik Verbruggen --- tools/qmlmin/main.cpp | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/tools/qmlmin/main.cpp b/tools/qmlmin/main.cpp index 0d6950360f..9500c45915 100644 --- a/tools/qmlmin/main.cpp +++ b/tools/qmlmin/main.cpp @@ -352,13 +352,7 @@ bool Minify::parse(int startToken) if (isIdentChar(lastChar)) assembled += QLatin1Char(' '); - foreach (const QChar &ch, identifier) { - if (isIdentChar(ch)) - assembled += ch; - else { - escape(ch, &assembled); - } - } + assembled += identifier; } else if (yytoken == T_STRING_LITERAL || yytoken == T_MULTILINE_STRING_LITERAL) { assembled += QLatin1Char('"'); -- cgit v1.2.3 From f685e3463b32bfd05a7b2574b4699e5f42f02f96 Mon Sep 17 00:00:00 2001 From: Sergio Ahumada Date: Wed, 12 Dec 2012 21:55:33 +0100 Subject: sync.profile: Point dependencies to 'refs/heads/dev' We should test dev branches against dev branches only by default. At some point we should automate the merges from release->stable->dev andi/or decide how to handle possible conflicts. This is good enough for the time being. Change-Id: Ifeae1b5de37e7bcaefbfa9038e50f3667645eca2 Reviewed-by: Alan Alpert Reviewed-by: Thiago Macieira --- sync.profile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sync.profile b/sync.profile index d65c740a66..61d25bafa5 100644 --- a/sync.profile +++ b/sync.profile @@ -17,7 +17,7 @@ # - any git symbolic ref resolvable from the module's repository (e.g. "refs/heads/master" to track master branch) # %dependencies = ( - "qtbase" => "refs/heads/master", - "qtxmlpatterns" => "refs/heads/master", - "qtjsbackend" => "refs/heads/master", + "qtbase" => "refs/heads/dev", + "qtxmlpatterns" => "refs/heads/dev", + "qtjsbackend" => "refs/heads/dev", ); -- cgit v1.2.3 From ac9aa6bf4b46d39273df12e8b8a1f6c5df3a899d Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 12 Dec 2012 12:24:15 +0100 Subject: Added parsing for getter/setter definitions in property assignments. Specified in ecma 5.1, 11.1.5. Change-Id: I93d12593534ed8a987922c8aa329124940e77c6f Reviewed-by: Lars Knoll --- src/qml/qml/parser/qqmljs.g | 117 +- src/qml/qml/parser/qqmljsast.cpp | 28 +- src/qml/qml/parser/qqmljsast_p.h | 107 +- src/qml/qml/parser/qqmljsastfwd_p.h | 4 +- src/qml/qml/parser/qqmljsastvisitor_p.h | 10 +- src/qml/qml/parser/qqmljsgrammar.cpp | 1785 +++++++++++++------------ src/qml/qml/parser/qqmljsgrammar_p.h | 36 +- src/qml/qml/parser/qqmljskeywords_p.h | 24 + src/qml/qml/parser/qqmljslexer.cpp | 3 +- src/qml/qml/parser/qqmljsparser.cpp | 414 +++--- src/qml/qml/parser/qqmljsparser_p.h | 7 +- src/qml/qml/v4/qv4irbuilder.cpp | 14 +- src/qml/qml/v4/qv4irbuilder_p.h | 4 +- tests/auto/qml/qqmlecmascript/data/getSet.qml | 14 + 14 files changed, 1380 insertions(+), 1187 deletions(-) create mode 100644 tests/auto/qml/qqmlecmascript/data/getSet.qml diff --git a/src/qml/qml/parser/qqmljs.g b/src/qml/qml/parser/qqmljs.g index a32123fc12..71cf67f408 100644 --- a/src/qml/qml/parser/qqmljs.g +++ b/src/qml/qml/parser/qqmljs.g @@ -24,7 +24,7 @@ %parser QQmlJSGrammar %decl qqmljsparser_p.h %impl qqmljsparser.cpp -%expect 2 +%expect 5 %expect-rr 2 %token T_AND "&" T_AND_AND "&&" T_AND_EQ "&=" @@ -67,6 +67,8 @@ %token T_IMPORT "import" %token T_AS "as" %token T_ON "on" +%token T_GET "get" +%token T_SET "set" %token T_ERROR @@ -79,7 +81,7 @@ %token T_FEED_JS_PROGRAM %nonassoc SHIFT_THERE -%nonassoc T_IDENTIFIER T_COLON T_SIGNAL T_PROPERTY T_READONLY +%nonassoc T_IDENTIFIER T_COLON T_SIGNAL T_PROPERTY T_READONLY T_ON T_SET T_GET %nonassoc REDUCE_HERE %start TopLevel @@ -240,7 +242,8 @@ public: AST::FunctionDeclaration *FunctionDeclaration; AST::Node *Node; AST::PropertyName *PropertyName; - AST::PropertyNameAndValueList *PropertyNameAndValueList; + AST::PropertyAssignment *PropertyAssignment; + AST::PropertyAssignmentList *PropertyAssignmentList; AST::SourceElement *SourceElement; AST::SourceElements *SourceElements; AST::Statement *Statement; @@ -1043,6 +1046,8 @@ JsIdentifier: T_PROPERTY ; JsIdentifier: T_SIGNAL ; JsIdentifier: T_READONLY ; JsIdentifier: T_ON ; +JsIdentifier: T_GET ; +JsIdentifier: T_SET ; -------------------------------------------------------------------------------------------------------- -- Expressions @@ -1219,13 +1224,13 @@ case $rule_number: { -- } break; -- ./ -PrimaryExpression: T_LBRACE PropertyNameAndValueListOpt T_RBRACE ; +PrimaryExpression: T_LBRACE PropertyAssignmentListOpt T_RBRACE ; /. case $rule_number: { AST::ObjectLiteral *node = 0; if (sym(2).Node) node = new (pool) AST::ObjectLiteral( - sym(2).PropertyNameAndValueList->finish ()); + sym(2).PropertyAssignmentList->finish ()); else node = new (pool) AST::ObjectLiteral(); node->lbraceToken = loc(1); @@ -1234,11 +1239,11 @@ case $rule_number: { } break; ./ -PrimaryExpression: T_LBRACE PropertyNameAndValueList T_COMMA T_RBRACE ; +PrimaryExpression: T_LBRACE PropertyAssignmentList T_COMMA T_RBRACE ; /. case $rule_number: { AST::ObjectLiteral *node = new (pool) AST::ObjectLiteral( - sym(2).PropertyNameAndValueList->finish ()); + sym(2).PropertyAssignmentList->finish ()); node->lbraceToken = loc(1); node->rbraceToken = loc(4); sym(1).Node = node; @@ -1330,40 +1335,62 @@ case $rule_number: { } break; ./ -PropertyNameAndValueList: PropertyName T_COLON AssignmentExpression ; +PropertyAssignment: PropertyName T_COLON AssignmentExpression ; /. case $rule_number: { - AST::PropertyNameAndValueList *node = new (pool) AST::PropertyNameAndValueList( + AST::PropertyNameAndValue *node = new (pool) AST::PropertyNameAndValue( sym(1).PropertyName, sym(3).Expression); node->colonToken = loc(2); sym(1).Node = node; } break; ./ -PropertyNameAndValueList: PropertyNameAndValueList T_COMMA PropertyName T_COLON AssignmentExpression ; +PropertyAssignment: T_GET PropertyName T_LPAREN T_RPAREN T_LBRACE FunctionBodyOpt T_RBRACE ; /. case $rule_number: { - AST::PropertyNameAndValueList *node = new (pool) AST::PropertyNameAndValueList( - sym(1).PropertyNameAndValueList, sym(3).PropertyName, sym(5).Expression); - node->commaToken = loc(2); - node->colonToken = loc(4); + AST::PropertyGetterSetter *node = new (pool) AST::PropertyGetterSetter( + sym(2).PropertyName, sym(6).FunctionBody); + node->getSetToken = loc(1); + node->lparenToken = loc(3); + node->rparenToken = loc(4); + node->lbraceToken = loc(5); + node->rbraceToken = loc(7); sym(1).Node = node; } break; ./ -PropertyName: T_IDENTIFIER %prec SHIFT_THERE ; +PropertyAssignment: T_SET PropertyName T_LPAREN FormalParameterListOpt T_RPAREN T_LBRACE FunctionBodyOpt T_RBRACE ; /. case $rule_number: { - AST::IdentifierPropertyName *node = new (pool) AST::IdentifierPropertyName(stringRef(1)); - node->propertyNameToken = loc(1); + AST::PropertyGetterSetter *node = new (pool) AST::PropertyGetterSetter( + sym(2).PropertyName, sym(4).FormalParameterList, sym(7).FunctionBody); + node->getSetToken = loc(1); + node->lparenToken = loc(3); + node->rparenToken = loc(5); + node->lbraceToken = loc(6); + node->rbraceToken = loc(8); sym(1).Node = node; } break; ./ -PropertyName: T_SIGNAL ; -/.case $rule_number:./ +PropertyAssignmentList: PropertyAssignment ; +/. +case $rule_number: { + sym(1).Node = new (pool) AST::PropertyAssignmentList(sym(1).PropertyAssignment); +} break; +./ + +PropertyAssignmentList: PropertyAssignmentList T_COMMA PropertyAssignment ; +/. +case $rule_number: { + AST::PropertyAssignmentList *node = new (pool) AST::PropertyAssignmentList( + sym(1).PropertyAssignmentList, sym(3).PropertyAssignment); + node->commaToken = loc(2); + sym(1).Node = node; +} break; +./ -PropertyName: T_PROPERTY ; +PropertyName: JsIdentifier %prec SHIFT_THERE ; /. case $rule_number: { AST::IdentifierPropertyName *node = new (pool) AST::IdentifierPropertyName(stringRef(1)); @@ -2669,20 +2696,7 @@ case $rule_number: { } break; ./ -LabelledStatement: T_SIGNAL T_COLON Statement ; -/.case $rule_number:./ - -LabelledStatement: T_PROPERTY T_COLON Statement ; -/. -case $rule_number: { - AST::LabelledStatement *node = new (pool) AST::LabelledStatement(stringRef(1), sym(3).Statement); - node->identifierToken = loc(1); - node->colonToken = loc(2); - sym(1).Node = node; -} break; -./ - -LabelledStatement: T_IDENTIFIER T_COLON Statement ; +LabelledStatement: JsIdentifier T_COLON Statement ; /. case $rule_number: { AST::LabelledStatement *node = new (pool) AST::LabelledStatement(stringRef(1), sym(3).Statement); @@ -2762,7 +2776,12 @@ case $rule_number: { } break; ./ -FunctionDeclaration: T_FUNCTION JsIdentifier T_LPAREN FormalParameterListOpt T_RPAREN T_LBRACE FunctionBodyOpt T_RBRACE ; +-- tell the parser to prefer function declarations to function expressions. +-- That is, the `Function' symbol is used to mark the start of a function +-- declaration. +Function: T_FUNCTION %prec REDUCE_HERE ; + +FunctionDeclaration: Function JsIdentifier T_LPAREN FormalParameterListOpt T_RPAREN T_LBRACE FunctionBodyOpt T_RBRACE ; /. case $rule_number: { AST::FunctionDeclaration *node = new (pool) AST::FunctionDeclaration(stringRef(2), sym(4).FormalParameterList, sym(7).FunctionBody); @@ -2776,7 +2795,7 @@ case $rule_number: { } break; ./ -FunctionExpression: T_FUNCTION IdentifierOpt T_LPAREN FormalParameterListOpt T_RPAREN T_LBRACE FunctionBodyOpt T_RBRACE ; +FunctionExpression: T_FUNCTION JsIdentifier T_LPAREN FormalParameterListOpt T_RPAREN T_LBRACE FunctionBodyOpt T_RBRACE ; /. case $rule_number: { AST::FunctionExpression *node = new (pool) AST::FunctionExpression(stringRef(2), sym(4).FormalParameterList, sym(7).FunctionBody); @@ -2791,6 +2810,19 @@ case $rule_number: { } break; ./ +FunctionExpression: T_FUNCTION T_LPAREN FormalParameterListOpt T_RPAREN T_LBRACE FunctionBodyOpt T_RBRACE ; +/. +case $rule_number: { + AST::FunctionExpression *node = new (pool) AST::FunctionExpression(QStringRef(), sym(3).FormalParameterList, sym(6).FunctionBody); + node->functionToken = loc(1); + node->lparenToken = loc(2); + node->rparenToken = loc(4); + node->lbraceToken = loc(5); + node->rbraceToken = loc(7); + sym(1).Node = node; +} break; +./ + FormalParameterList: JsIdentifier ; /. case $rule_number: { @@ -2877,23 +2909,14 @@ case $rule_number: { } break; ./ -IdentifierOpt: ; -/. -case $rule_number: { - stringRef(1) = QStringRef(); -} break; -./ - -IdentifierOpt: JsIdentifier ; - -PropertyNameAndValueListOpt: ; +PropertyAssignmentListOpt: ; /. case $rule_number: { sym(1).Node = 0; } break; ./ -PropertyNameAndValueListOpt: PropertyNameAndValueList ; +PropertyAssignmentListOpt: PropertyAssignmentList ; /. } // switch diff --git a/src/qml/qml/parser/qqmljsast.cpp b/src/qml/qml/parser/qqmljsast.cpp index aafb63057b..96ad80d3e2 100644 --- a/src/qml/qml/parser/qqmljsast.cpp +++ b/src/qml/qml/parser/qqmljsast.cpp @@ -213,12 +213,32 @@ void Elision::accept0(Visitor *visitor) visitor->endVisit(this); } -void PropertyNameAndValueList::accept0(Visitor *visitor) +void PropertyNameAndValue::accept0(Visitor *visitor) { if (visitor->visit(this)) { - for (PropertyNameAndValueList *it = this; it; it = it->next) { - accept(it->name, visitor); - accept(it->value, visitor); + accept(name, visitor); + accept(value, visitor); + } + + visitor->endVisit(this); +} + +void PropertyGetterSetter::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + accept(name, visitor); + accept(formals, visitor); + accept(functionBody, visitor); + } + + visitor->endVisit(this); +} + +void PropertyAssignmentList::accept0(Visitor *visitor) +{ + if (visitor->visit(this)) { + for (PropertyAssignmentList *it = this; it; it = it->next) { + accept(it->assignment, visitor); } } diff --git a/src/qml/qml/parser/qqmljsast_p.h b/src/qml/qml/parser/qqmljsast_p.h index 2f4862c894..ddab613408 100644 --- a/src/qml/qml/parser/qqmljsast_p.h +++ b/src/qml/qml/parser/qqmljsast_p.h @@ -176,8 +176,10 @@ public: Kind_PreDecrementExpression, Kind_PreIncrementExpression, Kind_Program, + Kind_PropertyAssignmentList, + Kind_PropertyGetterSetter, Kind_PropertyName, - Kind_PropertyNameAndValueList, + Kind_PropertyNameAndValue, Kind_RegExpLiteral, Kind_ReturnStatement, Kind_SourceElement, @@ -487,7 +489,7 @@ public: ObjectLiteral(): properties (0) { kind = K; } - ObjectLiteral(PropertyNameAndValueList *plist): + ObjectLiteral(PropertyAssignmentList *plist): properties (plist) { kind = K; } virtual void accept0(Visitor *visitor); @@ -499,7 +501,7 @@ public: { return rbraceToken; } // attributes - PropertyNameAndValueList *properties; + PropertyAssignmentList *properties; SourceLocation lbraceToken; SourceLocation rbraceToken; }; @@ -603,50 +605,107 @@ public: SourceLocation propertyNameToken; }; -class QML_PARSER_EXPORT PropertyNameAndValueList: public Node +class QML_PARSER_EXPORT PropertyAssignment: public Node { public: - QQMLJS_DECLARE_AST_NODE(PropertyNameAndValueList) + PropertyAssignment() {} +}; - PropertyNameAndValueList(PropertyName *n, ExpressionNode *v): - name (n), value (v), next (this) - { kind = K; } +class QML_PARSER_EXPORT PropertyAssignmentList: public Node +{ +public: + QQMLJS_DECLARE_AST_NODE(PropertyAssignmentList) - PropertyNameAndValueList(PropertyNameAndValueList *previous, PropertyName *n, ExpressionNode *v): - name (n), value (v) + PropertyAssignmentList(PropertyAssignment *assignment) + : assignment(assignment) + , next(this) + { kind = K; } + + PropertyAssignmentList(PropertyAssignmentList *previous, PropertyAssignment *assignment) + : assignment(assignment) { kind = K; next = previous->next; previous->next = this; } + inline PropertyAssignmentList *finish () + { + PropertyAssignmentList *front = next; + next = 0; + return front; + } + virtual void accept0(Visitor *visitor); virtual SourceLocation firstSourceLocation() const - { return name->firstSourceLocation(); } + { return assignment->firstSourceLocation(); } virtual SourceLocation lastSourceLocation() const - { - if (next) - return next->lastSourceLocation(); - return value->lastSourceLocation(); - } + { return next ? next->lastSourceLocation() : assignment->lastSourceLocation(); } - inline PropertyNameAndValueList *finish () - { - PropertyNameAndValueList *front = next; - next = 0; - return front; - } +// attributes + PropertyAssignment *assignment; + PropertyAssignmentList *next; + SourceLocation commaToken; +}; + +class QML_PARSER_EXPORT PropertyNameAndValue: public PropertyAssignment +{ +public: + QQMLJS_DECLARE_AST_NODE(PropertyNameAndValue) + + PropertyNameAndValue(PropertyName *n, ExpressionNode *v) + : name(n), value(v) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return name->firstSourceLocation(); } + + virtual SourceLocation lastSourceLocation() const + { return value->lastSourceLocation(); } // attributes PropertyName *name; - ExpressionNode *value; - PropertyNameAndValueList *next; SourceLocation colonToken; + ExpressionNode *value; SourceLocation commaToken; }; +class QML_PARSER_EXPORT PropertyGetterSetter: public PropertyAssignment +{ +public: + QQMLJS_DECLARE_AST_NODE(PropertyGetterSetter) + + PropertyGetterSetter(PropertyName *n, FunctionBody *b) + : name(n), formals(0), functionBody (b) + { kind = K; } + + PropertyGetterSetter(PropertyName *n, FormalParameterList *f, FunctionBody *b) + : name(n), formals(f), functionBody (b) + { kind = K; } + + virtual void accept0(Visitor *visitor); + + virtual SourceLocation firstSourceLocation() const + { return getSetToken; } + + virtual SourceLocation lastSourceLocation() const + { return rbraceToken; } + +// attributes + SourceLocation getSetToken; + PropertyName *name; + SourceLocation lparenToken; + FormalParameterList *formals; + SourceLocation rparenToken; + SourceLocation lbraceToken; + FunctionBody *functionBody; + SourceLocation rbraceToken; +}; + class QML_PARSER_EXPORT IdentifierPropertyName: public PropertyName { public: diff --git a/src/qml/qml/parser/qqmljsastfwd_p.h b/src/qml/qml/parser/qqmljsastfwd_p.h index 8c289ad8b9..f770c9a87d 100644 --- a/src/qml/qml/parser/qqmljsastfwd_p.h +++ b/src/qml/qml/parser/qqmljsastfwd_p.h @@ -98,7 +98,9 @@ class ArrayLiteral; class ObjectLiteral; class ElementList; class Elision; -class PropertyNameAndValueList; +class PropertyAssignmentList; +class PropertyGetterSetter; +class PropertyNameAndValue; class PropertyName; class IdentifierPropertyName; class StringLiteralPropertyName; diff --git a/src/qml/qml/parser/qqmljsastvisitor_p.h b/src/qml/qml/parser/qqmljsastvisitor_p.h index e131ffc19b..bc65f637b4 100644 --- a/src/qml/qml/parser/qqmljsastvisitor_p.h +++ b/src/qml/qml/parser/qqmljsastvisitor_p.h @@ -137,8 +137,14 @@ public: virtual bool visit(Elision *) { return true; } virtual void endVisit(Elision *) {} - virtual bool visit(PropertyNameAndValueList *) { return true; } - virtual void endVisit(PropertyNameAndValueList *) {} + virtual bool visit(PropertyAssignmentList *) { return true; } + virtual void endVisit(PropertyAssignmentList *) {} + + virtual bool visit(PropertyNameAndValue *) { return true; } + virtual void endVisit(PropertyNameAndValue *) {} + + virtual bool visit(PropertyGetterSetter *) { return true; } + virtual void endVisit(PropertyGetterSetter *) {} virtual bool visit(NestedExpression *) { return true; } virtual void endVisit(NestedExpression *) {} diff --git a/src/qml/qml/parser/qqmljsgrammar.cpp b/src/qml/qml/parser/qqmljsgrammar.cpp index 700078650b..a950415e22 100644 --- a/src/qml/qml/parser/qqmljsgrammar.cpp +++ b/src/qml/qml/parser/qqmljsgrammar.cpp @@ -54,45 +54,46 @@ const char *const QQmlJSGrammar::spell [] = { ")", ";", 0, "*", "*=", "string literal", "property", "signal", "readonly", "switch", "this", "throw", "~", "try", "typeof", "var", "void", "while", "with", "^", "^=", "null", "true", "false", "const", "debugger", "reserved word", "multiline string literal", "comment", 0, - "public", "import", "as", "on", 0, 0, 0, 0, 0, 0, - 0, 0, 0}; + "public", "import", "as", "on", "get", "set", 0, 0, 0, 0, + 0, 0, 0, 0, 0}; const short QQmlJSGrammar::lhs [] = { - 103, 103, 103, 103, 103, 103, 104, 110, 110, 113, - 113, 115, 114, 114, 114, 114, 114, 114, 114, 114, - 117, 112, 111, 120, 120, 121, 121, 122, 122, 119, - 108, 108, 108, 108, 124, 124, 124, 124, 124, 124, - 124, 108, 132, 132, 132, 133, 133, 134, 134, 108, - 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, - 108, 108, 108, 108, 108, 108, 118, 118, 118, 118, - 118, 137, 137, 137, 137, 137, 137, 137, 137, 137, - 137, 137, 137, 137, 137, 137, 137, 137, 137, 123, - 139, 139, 139, 139, 138, 138, 141, 141, 143, 143, - 143, 143, 143, 143, 144, 144, 144, 144, 144, 144, - 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, - 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, - 144, 144, 144, 144, 144, 145, 145, 116, 116, 116, - 116, 116, 148, 148, 149, 149, 149, 149, 147, 147, - 150, 150, 151, 151, 152, 152, 152, 153, 153, 153, - 153, 153, 153, 153, 153, 153, 153, 154, 154, 154, - 154, 155, 155, 155, 156, 156, 156, 156, 157, 157, - 157, 157, 157, 157, 157, 158, 158, 158, 158, 158, - 158, 159, 159, 159, 159, 159, 160, 160, 160, 160, - 160, 161, 161, 162, 162, 163, 163, 164, 164, 165, - 165, 166, 166, 167, 167, 168, 168, 169, 169, 170, - 170, 171, 171, 172, 172, 142, 142, 173, 173, 174, - 174, 174, 174, 174, 174, 174, 174, 174, 174, 174, - 174, 106, 106, 175, 175, 176, 176, 177, 177, 105, - 105, 105, 105, 105, 105, 105, 105, 105, 105, 105, - 105, 105, 105, 105, 125, 186, 186, 185, 185, 136, - 136, 187, 187, 188, 188, 190, 190, 189, 191, 194, - 192, 192, 195, 193, 193, 126, 127, 127, 128, 128, - 178, 178, 178, 178, 178, 178, 178, 178, 179, 179, - 179, 179, 180, 180, 180, 180, 181, 181, 129, 130, - 196, 196, 199, 199, 197, 197, 200, 198, 182, 182, - 182, 183, 183, 131, 131, 131, 201, 202, 184, 184, - 135, 146, 206, 206, 203, 203, 204, 204, 207, 109, - 109, 208, 208, 107, 107, 205, 205, 140, 140, 209}; + 105, 105, 105, 105, 105, 105, 106, 112, 112, 115, + 115, 117, 116, 116, 116, 116, 116, 116, 116, 116, + 119, 114, 113, 122, 122, 123, 123, 124, 124, 121, + 110, 110, 110, 110, 126, 126, 126, 126, 126, 126, + 126, 110, 134, 134, 134, 135, 135, 136, 136, 110, + 110, 110, 110, 110, 110, 110, 110, 110, 110, 110, + 110, 110, 110, 110, 110, 110, 120, 120, 120, 120, + 120, 120, 120, 139, 139, 139, 139, 139, 139, 139, + 139, 139, 139, 139, 139, 139, 139, 139, 139, 139, + 139, 125, 141, 141, 141, 141, 140, 140, 145, 145, + 145, 143, 143, 146, 146, 146, 146, 149, 149, 149, + 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, + 149, 149, 149, 149, 149, 149, 149, 149, 149, 149, + 149, 149, 149, 149, 149, 149, 149, 149, 150, 150, + 118, 118, 118, 118, 118, 153, 153, 154, 154, 154, + 154, 152, 152, 155, 155, 156, 156, 157, 157, 157, + 158, 158, 158, 158, 158, 158, 158, 158, 158, 158, + 159, 159, 159, 159, 160, 160, 160, 161, 161, 161, + 161, 162, 162, 162, 162, 162, 162, 162, 163, 163, + 163, 163, 163, 163, 164, 164, 164, 164, 164, 165, + 165, 165, 165, 165, 166, 166, 167, 167, 168, 168, + 169, 169, 170, 170, 171, 171, 172, 172, 173, 173, + 174, 174, 175, 175, 176, 176, 177, 177, 144, 144, + 178, 178, 179, 179, 179, 179, 179, 179, 179, 179, + 179, 179, 179, 179, 108, 108, 180, 180, 181, 181, + 182, 182, 107, 107, 107, 107, 107, 107, 107, 107, + 107, 107, 107, 107, 107, 107, 107, 127, 191, 191, + 190, 190, 138, 138, 192, 192, 193, 193, 195, 195, + 194, 196, 199, 197, 197, 200, 198, 198, 128, 129, + 129, 130, 130, 183, 183, 183, 183, 183, 183, 183, + 183, 184, 184, 184, 184, 185, 185, 185, 185, 186, + 186, 131, 132, 201, 201, 204, 204, 202, 202, 205, + 203, 187, 188, 188, 133, 133, 133, 206, 207, 189, + 189, 208, 137, 151, 151, 209, 209, 148, 148, 147, + 147, 210, 111, 111, 211, 211, 109, 109, 142, 142, + 212}; const short QQmlJSGrammar::rhs [] = { 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, @@ -103,593 +104,600 @@ const short QQmlJSGrammar::rhs [] = { 6, 3, 3, 7, 7, 4, 4, 5, 5, 5, 6, 6, 10, 6, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 2, 3, 3, 4, 5, 3, 4, 3, 1, - 1, 2, 3, 4, 1, 2, 3, 5, 1, 1, + 1, 1, 1, 2, 3, 3, 4, 5, 3, 4, + 3, 1, 1, 2, 3, 4, 1, 2, 3, 7, + 8, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, - 3, 5, 1, 2, 4, 4, 4, 3, 0, 1, - 1, 3, 1, 1, 1, 2, 2, 1, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 1, 3, 3, - 3, 1, 3, 3, 1, 3, 3, 3, 1, 3, - 3, 3, 3, 3, 3, 1, 3, 3, 3, 3, - 3, 1, 3, 3, 3, 3, 1, 3, 3, 3, - 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, - 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, - 3, 1, 5, 1, 5, 1, 3, 1, 3, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 3, 0, 1, 1, 3, 0, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 3, 1, 2, 0, 1, 3, - 3, 1, 1, 1, 3, 1, 3, 2, 2, 2, - 0, 1, 2, 0, 1, 1, 2, 2, 7, 5, - 7, 7, 7, 5, 9, 10, 7, 8, 2, 2, - 3, 3, 2, 2, 3, 3, 3, 3, 5, 5, - 3, 5, 1, 2, 0, 1, 4, 3, 3, 3, - 3, 3, 3, 3, 3, 4, 5, 2, 2, 2, - 8, 8, 1, 3, 0, 1, 0, 1, 1, 1, - 1, 1, 2, 1, 1, 0, 1, 0, 1, 2}; + 1, 1, 4, 3, 5, 1, 2, 4, 4, 4, + 3, 0, 1, 1, 3, 1, 1, 1, 2, 2, + 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 1, 3, 3, 3, 1, 3, 3, 1, 3, 3, + 3, 1, 3, 3, 3, 3, 3, 3, 1, 3, + 3, 3, 3, 3, 1, 3, 3, 3, 3, 1, + 3, 3, 3, 3, 1, 3, 1, 3, 1, 3, + 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, + 1, 3, 1, 3, 1, 5, 1, 5, 1, 3, + 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 3, 0, 1, 1, 3, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 3, 1, 2, + 0, 1, 3, 3, 1, 1, 1, 3, 1, 3, + 2, 2, 2, 0, 1, 2, 0, 1, 1, 2, + 2, 7, 5, 7, 7, 7, 5, 9, 10, 7, + 8, 2, 2, 3, 3, 2, 2, 3, 3, 3, + 3, 5, 5, 3, 5, 1, 2, 0, 1, 4, + 3, 3, 3, 3, 3, 3, 4, 5, 2, 2, + 2, 1, 8, 8, 7, 1, 3, 0, 1, 0, + 1, 1, 1, 1, 1, 2, 1, 1, 0, 1, + 2}; const short QQmlJSGrammar::action_default [] = { - 0, 0, 22, 0, 0, 0, 22, 0, 175, 242, - 206, 214, 210, 154, 226, 202, 3, 139, 73, 155, - 218, 222, 143, 172, 153, 158, 138, 192, 179, 0, - 80, 81, 76, 346, 67, 348, 0, 0, 0, 0, - 78, 0, 0, 74, 77, 71, 0, 0, 68, 70, - 69, 79, 72, 0, 75, 0, 0, 168, 0, 0, - 155, 174, 157, 156, 0, 0, 0, 170, 171, 169, - 173, 0, 203, 0, 0, 0, 0, 193, 0, 0, - 0, 0, 0, 0, 183, 0, 0, 0, 177, 178, - 176, 181, 185, 184, 182, 180, 195, 194, 196, 0, - 211, 0, 207, 0, 0, 149, 136, 148, 137, 105, - 106, 107, 132, 108, 133, 109, 110, 111, 112, 113, - 114, 115, 116, 117, 118, 119, 120, 121, 134, 122, - 123, 124, 125, 126, 127, 128, 129, 130, 131, 135, - 0, 0, 147, 243, 150, 0, 151, 0, 152, 146, - 0, 239, 232, 230, 237, 238, 236, 235, 241, 234, - 233, 231, 240, 227, 0, 215, 0, 0, 219, 0, - 0, 223, 0, 0, 149, 141, 0, 140, 0, 145, - 159, 0, 347, 335, 336, 0, 333, 0, 334, 0, - 337, 250, 257, 256, 264, 252, 0, 253, 338, 0, - 345, 254, 255, 260, 258, 342, 339, 344, 261, 0, - 272, 0, 0, 0, 0, 346, 67, 0, 348, 68, - 244, 286, 69, 0, 0, 0, 273, 0, 0, 262, - 263, 0, 251, 259, 287, 288, 332, 343, 0, 303, - 304, 305, 306, 0, 299, 300, 301, 302, 329, 330, - 0, 0, 0, 0, 0, 291, 292, 293, 248, 246, - 208, 216, 212, 228, 204, 249, 0, 155, 220, 224, - 197, 186, 0, 0, 205, 0, 0, 0, 0, 198, - 0, 0, 0, 0, 0, 190, 188, 191, 189, 187, - 200, 199, 201, 0, 213, 0, 209, 0, 247, 155, - 0, 229, 244, 245, 0, 244, 0, 0, 295, 0, - 0, 0, 297, 0, 217, 0, 0, 221, 0, 0, - 225, 284, 0, 276, 285, 279, 0, 283, 0, 244, - 277, 0, 244, 0, 0, 296, 0, 0, 0, 298, - 347, 335, 0, 0, 337, 0, 331, 0, 321, 0, - 0, 0, 290, 0, 289, 0, 349, 0, 104, 266, - 269, 0, 105, 272, 108, 133, 110, 111, 76, 115, - 116, 67, 117, 120, 74, 77, 68, 244, 69, 79, - 123, 72, 125, 75, 127, 128, 273, 130, 131, 135, - 0, 97, 0, 0, 99, 103, 101, 88, 100, 102, - 0, 98, 87, 267, 265, 143, 144, 149, 0, 142, - 0, 320, 0, 307, 308, 0, 319, 0, 0, 0, - 310, 315, 313, 316, 0, 0, 314, 315, 0, 311, - 0, 312, 268, 318, 0, 268, 317, 0, 322, 323, - 0, 268, 324, 325, 0, 0, 326, 0, 0, 0, - 327, 328, 161, 160, 0, 0, 0, 294, 0, 0, - 0, 309, 281, 274, 0, 282, 278, 0, 280, 270, - 0, 271, 275, 91, 0, 0, 95, 82, 0, 84, - 93, 0, 85, 94, 96, 86, 92, 83, 0, 89, - 165, 163, 167, 164, 162, 166, 340, 6, 341, 4, - 2, 65, 90, 0, 0, 68, 70, 69, 31, 5, - 0, 66, 0, 45, 44, 43, 0, 0, 58, 0, - 59, 35, 36, 37, 38, 40, 41, 62, 39, 0, - 45, 0, 0, 0, 0, 0, 54, 0, 55, 0, - 0, 26, 0, 0, 63, 27, 0, 30, 28, 24, - 0, 29, 25, 0, 56, 0, 57, 143, 0, 60, - 64, 0, 0, 0, 0, 61, 0, 52, 46, 53, - 47, 0, 0, 0, 0, 49, 0, 50, 51, 48, - 0, 0, 143, 268, 0, 0, 42, 105, 272, 108, - 133, 110, 111, 76, 115, 116, 67, 117, 120, 74, - 77, 68, 244, 69, 79, 123, 72, 125, 75, 127, - 128, 273, 130, 131, 135, 0, 32, 33, 0, 34, + 0, 0, 22, 0, 0, 0, 22, 0, 178, 245, + 209, 217, 213, 157, 229, 205, 3, 142, 75, 158, + 221, 225, 146, 175, 156, 161, 141, 195, 182, 0, + 82, 83, 78, 0, 72, 67, 349, 0, 0, 0, + 0, 80, 0, 0, 76, 79, 71, 0, 0, 68, + 70, 73, 69, 81, 74, 0, 77, 0, 0, 171, + 0, 0, 158, 177, 160, 159, 0, 0, 0, 173, + 174, 172, 176, 0, 206, 0, 0, 0, 0, 196, + 0, 0, 0, 0, 0, 0, 186, 0, 0, 0, + 180, 181, 179, 184, 188, 187, 185, 183, 198, 197, + 199, 0, 214, 0, 210, 0, 0, 152, 139, 151, + 140, 108, 109, 110, 135, 111, 136, 112, 113, 114, + 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, + 137, 125, 126, 127, 128, 129, 130, 131, 132, 133, + 134, 138, 0, 0, 150, 246, 153, 0, 154, 0, + 155, 149, 0, 242, 235, 233, 240, 241, 239, 238, + 244, 237, 236, 234, 243, 230, 0, 218, 0, 0, + 222, 0, 0, 226, 0, 0, 152, 144, 0, 143, + 0, 148, 162, 0, 338, 338, 339, 0, 336, 0, + 337, 0, 340, 253, 260, 259, 267, 255, 0, 256, + 0, 341, 0, 348, 257, 258, 75, 263, 261, 345, + 342, 347, 264, 0, 275, 0, 0, 0, 0, 332, + 0, 349, 247, 289, 0, 0, 0, 276, 0, 0, + 265, 266, 0, 254, 262, 290, 291, 0, 338, 0, + 0, 340, 0, 333, 334, 0, 322, 346, 0, 306, + 307, 308, 309, 0, 302, 303, 304, 305, 330, 331, + 0, 0, 0, 0, 0, 294, 295, 296, 251, 249, + 211, 219, 215, 231, 207, 252, 0, 158, 223, 227, + 200, 189, 0, 0, 208, 0, 0, 0, 0, 201, + 0, 0, 0, 0, 0, 193, 191, 194, 192, 190, + 203, 202, 204, 0, 216, 0, 212, 0, 250, 158, + 0, 232, 247, 248, 0, 247, 0, 0, 298, 0, + 0, 0, 300, 0, 220, 0, 0, 224, 0, 0, + 228, 287, 0, 279, 288, 282, 0, 286, 0, 247, + 280, 0, 247, 0, 0, 299, 0, 0, 0, 301, + 0, 0, 0, 293, 0, 292, 75, 102, 350, 0, + 0, 107, 269, 272, 0, 108, 275, 111, 136, 113, + 114, 78, 118, 119, 72, 120, 123, 76, 79, 247, + 73, 81, 126, 74, 128, 77, 130, 131, 276, 133, + 134, 138, 0, 104, 103, 106, 90, 105, 89, 0, + 99, 270, 268, 0, 0, 0, 340, 0, 100, 146, + 147, 152, 0, 145, 0, 310, 311, 0, 338, 0, + 0, 340, 0, 101, 0, 0, 0, 313, 318, 316, + 319, 0, 0, 317, 318, 0, 314, 0, 315, 271, + 321, 0, 271, 320, 0, 323, 324, 0, 271, 325, + 326, 0, 0, 327, 0, 0, 0, 328, 329, 164, + 163, 0, 0, 0, 297, 0, 0, 0, 312, 284, + 277, 0, 285, 281, 0, 283, 273, 0, 274, 278, + 0, 0, 340, 0, 335, 93, 0, 0, 97, 84, + 0, 86, 95, 0, 87, 96, 98, 88, 94, 85, + 0, 91, 168, 166, 170, 167, 165, 169, 343, 6, + 344, 4, 2, 65, 92, 0, 0, 68, 70, 69, + 31, 5, 0, 66, 0, 45, 44, 43, 0, 0, + 58, 0, 59, 35, 36, 37, 38, 40, 41, 62, + 39, 0, 45, 0, 0, 0, 0, 0, 54, 0, + 55, 0, 0, 26, 0, 0, 63, 27, 0, 30, + 28, 24, 0, 29, 25, 0, 56, 0, 57, 146, + 0, 60, 64, 0, 0, 0, 0, 61, 0, 52, + 46, 53, 47, 0, 0, 0, 0, 49, 0, 50, + 51, 48, 0, 0, 146, 271, 0, 0, 42, 75, + 108, 275, 111, 136, 113, 114, 78, 118, 119, 120, + 123, 76, 79, 247, 81, 126, 74, 128, 77, 130, + 131, 276, 133, 134, 138, 0, 32, 33, 0, 34, 8, 0, 10, 0, 9, 0, 1, 21, 12, 0, 13, 0, 14, 0, 19, 20, 0, 15, 16, 0, - 17, 18, 11, 23, 7, 350}; + 17, 18, 11, 23, 7, 351}; const short QQmlJSGrammar::goto_default [] = { - 7, 626, 207, 196, 205, 509, 497, 625, 644, 496, - 624, 622, 627, 22, 623, 18, 508, 550, 540, 547, - 542, 527, 191, 195, 197, 201, 233, 208, 230, 531, - 571, 570, 200, 232, 26, 475, 474, 357, 356, 9, - 355, 358, 107, 17, 145, 24, 13, 144, 19, 25, - 57, 23, 8, 28, 27, 270, 15, 264, 10, 260, - 12, 262, 11, 261, 20, 268, 21, 269, 14, 263, - 259, 300, 412, 265, 266, 202, 193, 192, 204, 203, - 229, 194, 361, 360, 231, 464, 463, 322, 323, 466, - 325, 465, 324, 420, 424, 427, 423, 422, 442, 443, - 185, 199, 181, 184, 198, 206, 0}; + 7, 636, 211, 198, 209, 521, 509, 635, 654, 508, + 634, 632, 637, 22, 633, 18, 520, 562, 552, 559, + 554, 539, 193, 197, 199, 204, 234, 212, 231, 543, + 583, 582, 203, 233, 26, 487, 486, 359, 358, 9, + 357, 360, 202, 480, 361, 109, 17, 147, 24, 13, + 146, 19, 25, 59, 23, 8, 28, 27, 280, 15, + 274, 10, 270, 12, 272, 11, 271, 20, 278, 21, + 279, 14, 273, 269, 310, 414, 275, 276, 205, 195, + 194, 208, 207, 230, 196, 364, 363, 232, 471, 470, + 332, 333, 473, 335, 472, 334, 427, 431, 434, 430, + 429, 449, 450, 200, 186, 201, 210, 0}; const short QQmlJSGrammar::action_index [] = { - 425, 1471, 2619, 2619, 2718, 1193, 69, 100, 86, -103, - 97, 62, 60, 236, -103, 278, 93, -103, -103, 609, - 83, 117, 266, 184, -103, -103, -103, 514, 179, 1471, - -103, -103, -103, 392, -103, 2421, 1754, 1471, 1471, 1471, - -103, 1001, 1471, -103, -103, -103, 1471, 1471, -103, -103, - -103, -103, -103, 1471, -103, 1471, 1471, -103, 1471, 1471, - 115, 169, -103, -103, 1471, 1471, 1471, -103, -103, -103, - 152, 1471, 286, 1471, 1471, 1471, 1471, 562, 1471, 1471, - 1471, 1471, 1471, 1471, 198, 1471, 1471, 1471, 87, 122, - 138, 212, 197, 183, 227, 265, 562, 504, 472, 1471, - 79, 1471, 102, 2322, 1471, 1471, -103, -103, -103, -103, - -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, - -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, - -103, -103, -103, -103, -103, -103, -103, -103, -103, -103, - 132, 1471, -103, -103, 94, 11, -103, 1471, -103, -103, - 1471, -103, -103, -103, -103, -103, -103, -103, -103, -103, - -103, -103, -103, -103, 1471, 31, 1471, 1471, 67, 68, - 1471, -103, 2322, 1471, 1471, -103, 98, -103, 39, -103, - -103, 61, -103, 296, 66, 30, -103, 376, -103, 35, - 2619, -103, -103, -103, -103, -103, 237, -103, -103, 21, - -103, -103, -103, -103, -103, -103, 2619, -103, -103, 513, - -103, 541, 114, 2718, 36, 422, 59, 42, 2916, 75, - 1471, -103, 77, 53, 1471, 63, -103, 57, 58, -103, - -103, 407, -103, -103, -103, -103, -103, -103, 80, -103, - -103, -103, -103, 82, -103, -103, -103, -103, -103, -103, - 52, 51, 1471, 96, 200, -103, -103, -103, 1659, -103, - 76, 44, 46, -103, 298, 73, 48, 736, 78, 95, - 363, 233, 347, 1471, 303, 1471, 1471, 1471, 1471, 402, - 1471, 1471, 1471, 1471, 1471, 248, 189, 159, 167, 174, - 482, 482, 444, 1471, 7, 1471, 64, 1471, -103, 627, - 1471, -103, 1471, 65, 34, 1471, 54, 2718, -103, 1471, - 140, 2718, -103, 1471, 74, 1471, 1471, 81, 84, 1471, - -103, 71, 116, 33, -103, -103, 1471, -103, 313, 1471, - -103, 70, 1471, 72, 2718, -103, 1471, 234, 2718, -103, - -16, 324, -42, -12, 2619, -39, -103, 2718, -103, 1471, - 151, 2718, 12, 2718, -103, 20, 16, -32, -103, -103, - 2718, -52, 521, -2, 505, 129, 1471, 2718, -5, -35, - 497, 2, -24, 819, 6, 3, -103, 1567, -103, -1, - -36, 26, 1471, 47, 22, 1471, 45, 1471, 17, 15, - 1471, -103, 2520, 49, -103, -103, -103, -103, -103, -103, - 1471, -103, -103, -103, -103, 322, -103, 1471, -25, -103, - 2718, -103, 118, -103, -103, 2718, -103, 1471, 112, 8, - -103, 40, -103, 41, 106, 1471, -103, 38, 32, -103, - -38, -103, 2718, -103, 104, 2718, -103, 247, -103, -103, - 99, 2718, -6, -103, -10, -18, -103, 387, 10, 9, - -103, -103, -103, -103, 1471, 125, 2718, -103, 1471, 127, - 2718, -103, -13, -103, 187, -103, -103, 1471, -103, -103, - 398, -103, -103, -103, 110, 2039, -103, -103, 1849, -103, - -103, 1944, -103, -103, -103, -103, -103, -103, 105, -103, - -103, -103, -103, -103, -103, -103, -103, -103, 2619, -103, - -103, -103, 113, -7, 1009, 145, -8, 19, -103, -103, - 186, -103, 178, -103, -103, -103, 356, 226, -103, 2131, - -103, -103, -103, -103, -103, -103, -103, -103, -103, 322, - -26, 364, 205, 43, 316, 206, -103, -3, -103, 1009, - 107, -103, -11, 827, -103, -103, 1379, -103, -103, -103, - 1286, -103, -103, 195, -103, 2131, -103, 305, -4, -103, - -103, 209, 379, 18, 2131, -103, 182, -103, 199, -103, - 0, -53, 306, 154, 284, -103, 108, -103, -103, -103, - 2223, 918, 300, 2817, 1754, 5, -103, 549, 139, 636, - 114, 1471, 2718, 50, 24, 463, 55, -17, 910, 23, - 56, -103, 1567, -103, 27, 1, 29, 1471, 37, 14, - 1471, 25, 1471, 4, 13, 126, -103, -103, 28, -103, - -103, 1100, -103, 267, -41, 714, -103, -103, 121, 301, - -103, 215, -103, 91, -103, -103, 336, -103, -103, 89, - -103, -103, -103, -103, -103, -103, + 235, 1289, 2663, 2663, 2562, 1005, 64, 90, 103, -105, + 88, 94, 79, 173, -105, 302, 69, -105, -105, 724, + 65, 135, 195, 239, -105, -105, -105, 367, 278, 1289, + -105, -105, -105, 485, -105, -105, 2360, 1772, 1289, 1289, + 1289, -105, 817, 1289, -105, -105, -105, 1289, 1289, -105, + -105, -105, -105, -105, -105, 1289, -105, 1289, 1289, -105, + 1289, 1289, 95, 207, -105, -105, 1289, 1289, 1289, -105, + -105, -105, 202, 1289, 300, 1289, 1289, 1289, 1289, 377, + 1289, 1289, 1289, 1289, 1289, 1289, 253, 1289, 1289, 1289, + 151, 147, 129, 196, 170, 199, 279, 270, 470, 470, + 387, 1289, 53, 1289, 80, 2158, 1289, 1289, -105, -105, + -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, + -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, + -105, -105, -105, -105, -105, -105, -105, -105, -105, -105, + -105, -105, 128, 1289, -105, -105, 74, 52, -105, 1289, + -105, -105, 1289, -105, -105, -105, -105, -105, -105, -105, + -105, -105, -105, -105, -105, -105, 1289, 51, 1289, 1289, + 77, 66, 1289, -105, 2158, 1289, 1289, -105, 125, -105, + 48, -105, -105, 47, 451, 374, 83, 87, -105, 397, + -105, 62, 2663, -105, -105, -105, -105, -105, 205, -105, + 415, -105, 68, -105, -105, -105, 86, -105, -105, -105, + 2663, -105, -105, 622, -105, 576, 102, 2562, 75, 89, + 81, 2865, 1289, -105, 70, 1289, 63, -105, 92, 93, + -105, -105, 546, -105, -105, -105, -105, 91, 546, 40, + 45, 2663, 49, -105, -105, 2562, -105, -105, 106, -105, + -105, -105, -105, 121, -105, -105, -105, -105, -105, -105, + 42, 44, 1289, 114, 222, -105, -105, -105, 1481, -105, + 84, 57, 56, -105, 388, 78, 54, 682, 82, 99, + 357, 247, 546, 1289, 295, 1289, 1289, 1289, 1289, 334, + 1289, 1289, 1289, 1289, 1289, 203, 217, 244, 263, 211, + 341, 319, 351, 1289, 56, 1289, 73, 1289, -105, 724, + 1289, -105, 1289, 67, 46, 1289, 61, 2562, -105, 1289, + 136, 2562, -105, 1289, 76, 1289, 1289, 85, 59, 1289, + -105, 71, 133, 72, -105, -105, 1289, -105, 546, 1289, + -105, -53, 1289, -60, 2562, -105, 1289, 143, 2562, -105, + 1289, 132, 2562, 8, 2562, -105, 7, -105, 12, -37, + 107, -105, -105, 2562, -33, 622, 22, 555, 115, 1289, + 2562, 23, -13, 502, 2259, -10, 817, 18, 6, 1387, + 2259, 0, 9, -6, 1289, -4, -23, 1289, 5, 1289, + -25, -27, 2461, -105, -105, -105, -105, -105, -105, 1289, + -105, -105, -105, -3, -1, 21, 2663, 1, -105, 218, + -105, 1289, 4, -105, 111, -105, -105, 26, 466, 16, + 38, 2663, 39, -105, 1289, 110, 37, -105, 55, -105, + 60, 116, 1289, -105, 58, 43, -105, -15, -105, 2562, + -105, 123, 2562, -105, 154, -105, -105, 96, 2562, 32, + -105, 3, 14, -105, 546, -11, 13, -105, -105, -105, + -105, 1289, 126, 2562, -105, 1289, 130, 2562, -105, 15, + -105, 301, -105, -105, 1289, -105, -105, 546, -105, -105, + -45, -12, 2663, -24, -105, -105, 204, 1578, -105, -105, + 1869, -105, -105, 1675, -105, -105, -105, -105, -105, -105, + 101, -105, -105, -105, -105, -105, -105, -105, -105, -105, + 2663, -105, -105, -105, 105, 2, 910, 206, -47, -2, + -105, -105, 246, -105, 214, -105, -105, -105, 364, 232, + -105, 1963, -105, -105, -105, -105, -105, -105, -105, -105, + -105, 191, 24, 394, 172, -18, 384, 215, -105, -30, + -105, 910, 149, -105, -16, 910, -105, -105, 1100, -105, + -105, -105, 1195, -105, -105, 225, -105, 1963, -105, 316, + -17, -105, -105, 269, 418, -5, 1963, -105, 184, -105, + 175, -105, 20, -9, 546, 182, 469, -105, 104, -105, + -105, -105, 2057, 910, 292, 2764, 1772, 10, -105, 35, + 622, 34, 525, 98, 1289, 2562, 50, 17, 536, 19, + 817, 31, 27, 1387, 28, 9, 29, 1289, 30, 11, + 1289, 41, 1289, 33, 36, 137, -105, -105, 25, -105, + -105, 910, -105, 268, -86, 910, -105, -105, 141, 546, + -105, 156, -105, 117, -105, -105, 546, -105, -105, 138, + -105, -105, -105, -105, -105, -105, - -107, 12, -95, 3, 6, 275, 2, -107, -107, -107, - -107, -107, -107, -107, -107, -107, -107, -107, -107, -37, - -107, -107, -107, -107, -107, -107, -107, -107, -107, 96, - -107, -107, -107, 8, -107, -107, -17, 24, 95, 74, - -107, 85, 175, -107, -107, -107, 172, 168, -107, -107, - -107, -107, -107, 169, -107, 165, 164, -107, 156, 176, - -107, -107, -107, -107, 182, 178, 112, -107, -107, -107, - -107, 121, -107, 144, 118, 116, 117, -107, 108, 133, - 134, 137, 143, 152, -107, 147, 141, 136, -107, -107, - -107, -107, -107, -107, -107, -107, -107, -107, -107, 107, - -107, 173, -107, 153, 84, 56, -107, -107, -107, -107, - -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, - -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, - -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, - -107, 36, -107, -107, -107, -107, -107, 30, -107, -107, - 0, -107, -107, -107, -107, -107, -107, -107, -107, -107, - -107, -107, -107, -107, 79, -107, 99, 44, -107, -107, - 46, -107, 194, 70, 64, -107, -107, -107, -107, -107, - -107, -107, -107, 29, -107, -107, -107, 65, -107, -107, - -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, - -107, -107, -107, -107, -107, -107, 80, -107, -107, 51, - -107, 48, -107, 40, -107, 34, -107, -107, 86, -107, - 88, -107, -107, -107, 78, 60, -107, -107, -107, -107, - -107, -6, -107, -107, -107, -107, -107, -107, -107, -107, - -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, - -107, -107, 21, -107, -107, -107, -107, -107, 104, -107, - -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, - -107, -107, 16, 201, -107, 202, 224, 223, 215, -107, - 67, 73, 75, 59, 106, -107, -107, -107, -107, -107, - -107, -107, -107, 185, -107, 208, -107, 233, -107, -107, - 234, -107, 203, -107, -107, 299, -107, 90, -107, -2, - -107, 11, -107, 181, -107, 200, 192, -107, -107, 189, - -107, -107, -107, -107, -107, -107, 199, -107, 123, 131, - -107, -107, 111, -107, 77, -107, 87, -107, 195, -107, - -107, 110, -107, -107, -50, -107, -107, 39, -107, 42, - -107, 63, -107, 66, -107, -107, -107, -107, -107, -107, - 68, -107, 43, -107, 47, -107, 92, 35, -107, -107, - 31, -107, -107, 105, -107, -107, -107, 94, -107, -107, - -107, -107, 71, -107, 54, 101, -107, 89, -107, -107, - 55, -107, 49, -107, -107, -107, -107, -107, -107, -107, - 38, -107, -107, -107, -107, -107, -107, 114, -107, -107, - 76, -107, -107, -107, -107, 91, -107, 93, -107, -107, - -107, -107, -107, -57, -107, 50, -107, -44, -107, -107, - -107, -107, 98, -107, -107, 97, -107, -107, -107, -107, - -107, 52, -42, -107, -107, 45, -107, 41, -107, 37, - -107, -107, -107, -107, 57, -107, 53, -107, 58, -107, - 62, -107, -107, -107, -107, -107, -107, 9, -107, -107, - 187, -107, -107, -107, -107, 33, -107, -107, 139, -107, - -107, 32, -107, -107, -107, -107, -107, -107, -107, -107, - -107, -107, -107, -107, -107, -107, -107, -107, 82, -107, - -107, -107, -107, -107, -11, -107, -107, -107, -107, -107, - -107, -107, -25, -107, -107, -107, -9, -107, -107, 297, - -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, - -107, -3, -26, -107, -10, -107, -107, -107, -107, 160, - -107, -107, -107, 120, -107, -107, 279, -107, -107, -107, - 285, -107, -107, -107, -107, 329, -107, -107, 17, -107, - -107, -13, 14, -107, 385, -107, -107, -107, 23, -107, - -107, -107, 28, 18, 20, -107, -107, -107, -107, -107, - 313, 188, -107, 26, 267, 7, -107, 5, -107, -1, - -107, 69, 19, -107, -107, 10, -107, -107, 103, -107, - -107, -107, 27, -107, -107, -107, -107, 15, -107, -5, - 61, -107, 81, -107, -107, -107, -107, -107, 13, -107, - -107, 25, -107, -107, 22, 119, -107, -107, -107, 4, - -107, -107, -107, -107, -107, -107, -15, -107, -107, -107, - -107, -107, -107, -107, -107, -107}; + -108, 0, 79, 128, 132, 301, 2, -108, -108, -108, + -108, -108, -108, -108, -108, -108, -108, -108, -108, -47, + -108, -108, -108, -108, -108, -108, -108, -108, -108, 51, + -108, -108, -108, -3, -108, -108, 8, -23, 12, 78, + 106, -108, 69, 74, -108, -108, -108, 195, 204, -108, + -108, -108, -108, -108, -108, 188, -108, 201, 200, -108, + 127, 129, -108, -108, -108, -108, 140, 137, 133, -108, + -108, -108, -108, 146, -108, 177, 168, 170, 167, -108, + 144, 152, 166, 158, 160, 131, -108, 194, 187, 207, + -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, + -108, 88, -108, 112, -108, 121, 90, -38, -108, -108, + -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, + -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, + -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, + -108, -108, -108, 32, -108, -108, -108, -108, -108, 26, + -108, -108, 27, -108, -108, -108, -108, -108, -108, -108, + -108, -108, -108, -108, -108, -108, 102, -108, 103, 41, + -108, -108, 37, -108, 250, 38, 83, -108, -108, -108, + -108, -108, -108, -108, 42, 126, -108, -108, -108, 40, + -108, -108, 43, -108, -108, -108, -108, -108, -108, -108, + 39, -108, -108, -108, -108, -108, -108, -108, -108, -108, + 225, -108, -108, 30, -108, 24, -108, 211, -108, 55, + -108, 77, 60, -108, -108, 66, 34, -108, -108, -108, + -108, -108, -8, -108, -108, -108, -108, -108, 153, -108, + -108, 164, -108, -108, -108, 241, -108, -108, -108, -108, + -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, + -108, -108, 11, -108, -108, -108, -108, -108, 179, -108, + -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, + -108, -108, 19, 259, -108, 255, 228, 240, 246, -108, + 52, 63, 67, 65, 50, -108, -108, -108, -108, -108, + -108, -108, -108, 210, -108, 256, -108, 226, -108, -108, + 252, -108, 161, -108, -108, 268, -108, 197, -108, 5, + -108, 218, -108, 222, -108, 213, 249, -108, -108, 236, + -108, -108, -108, -108, -108, -108, 212, -108, 80, 87, + -108, -108, 86, -108, 98, -108, 61, -108, 245, -108, + 59, -108, 208, -108, 192, -108, -108, -108, -108, -108, + -108, -108, -108, 257, -108, 33, -108, 28, -108, 73, + 71, -108, -108, 36, 57, -108, 62, -108, -108, 46, + 70, -108, -108, -108, 49, -108, 45, 99, -108, 84, + -108, -108, 100, -108, -108, -108, -108, -108, -108, 21, + -108, -108, -108, -108, -108, -108, 118, -108, -108, -108, + -108, 81, -108, -108, -108, -108, -108, -108, 123, -108, + -108, 134, -108, -108, 56, -108, -108, -108, -108, -108, + -58, -108, 47, -108, -57, -108, -108, -108, -108, 265, + -108, -108, 374, -108, -108, -108, -108, -108, 94, -66, + -108, -108, 25, -108, 22, -108, 31, -108, -108, -108, + -108, 58, -108, 229, -108, 35, -108, 235, -108, -108, + -108, -108, -108, -108, 29, -108, -108, 186, -108, -108, + -108, -108, 162, -108, -108, -108, -108, 48, -108, -108, + 163, -108, -108, 44, -108, -108, -108, -108, -108, -108, + -108, -108, -108, -108, -108, -108, -108, -108, -108, -108, + 141, -108, -108, -108, -108, -108, -7, -108, -108, -108, + -108, -108, -108, -108, -19, -108, -108, -108, -6, -108, + -108, 334, -108, -108, -108, -108, -108, -108, -108, -108, + -108, -108, -108, -15, -27, -108, -10, -108, -108, -108, + -108, 159, -108, -108, -108, 176, -108, -108, 319, -108, + -108, -108, 322, -108, -108, -108, -108, 469, -108, -108, + 10, -108, -108, 6, 16, -108, 342, -108, -108, -108, + 17, -108, -108, -108, 15, 3, 9, -108, -108, -108, + -108, -108, 358, 68, -108, 82, 310, 1, -108, -108, + -2, -108, 7, -108, 54, 76, -108, -108, 4, -108, + 64, -108, -108, 23, -108, -108, -108, 18, -108, -5, + 95, -108, 91, -108, -108, -108, -108, -108, -1, -108, + -108, 20, -108, -108, 14, 142, -108, -108, -108, 13, + -108, -108, -108, -108, -108, -108, -11, -108, -108, -108, + -108, -108, -108, -108, -108, -108}; const short QQmlJSGrammar::action_info [] = { - 417, 258, -113, 404, 467, -132, -102, 576, 573, 347, - -103, 532, 349, -121, 445, 441, 346, 431, 343, 349, - 341, 344, 546, 402, 392, 564, 447, 390, 353, 546, - -121, 539, -129, -124, -102, 409, -124, 417, 546, 432, - 454, 421, 441, 425, -126, 425, 425, 441, 566, 458, - 621, 458, -129, 454, -126, 441, 400, -113, 561, 512, - 258, 546, 347, -103, 336, 273, 347, 534, 190, 164, - 449, 149, 258, 141, 187, 170, 236, 273, 349, 99, - 313, 297, 410, 313, 415, 164, 295, 252, 326, 417, - 189, 319, 293, 454, 458, 305, 441, 183, 71, 179, - 645, 141, 147, 71, 141, 444, 141, 0, 0, 302, - 99, 435, 141, 141, 307, 543, 428, 0, 478, 445, - 141, 0, 293, 0, 328, 295, 58, 58, 172, 251, - 0, 332, 334, 141, 543, 141, 172, 59, 59, 101, - 141, 242, 241, 247, 246, 315, -132, 173, 141, 316, - 641, 640, 635, 634, 177, 173, 254, 62, 101, 141, - 621, 429, 58, 544, 64, 489, 479, 166, 63, 578, - 577, 167, 419, 59, 530, 249, 248, 329, 58, 414, - 413, 64, 616, 513, 85, 456, 86, 460, 142, 59, - 249, 248, 85, 580, 86, 470, 64, 87, 0, 85, - 311, 86, 555, 0, 85, 87, 86, 513, 85, 65, - 86, 351, 87, 537, 85, 66, 86, 87, 568, 546, - 515, 87, 85, 85, 86, 86, 65, 87, 513, 515, - 0, 514, 66, 519, 513, 87, 87, 85, 513, 86, - 514, 65, 141, 569, 567, 141, 0, 66, 471, 469, - 87, 103, 85, 515, 86, 141, 556, 554, 85, 0, - 86, 257, 255, 0, 514, 87, 0, 538, 536, 0, - 104, 87, 105, 85, 515, 86, 638, 637, 0, 581, - 515, 172, 0, 0, 515, 514, 87, 520, 518, 256, - 85, 514, 86, 0, 338, 514, 73, 74, 235, 234, - 173, 0, 174, 87, 73, 74, 0, 636, 439, 438, - 0, 0, 0, 34, 631, 172, 275, 276, 0, 0, - 172, 275, 276, 75, 76, 34, 0, 0, 632, 630, - 34, 75, 76, -90, 173, 34, 174, 172, -90, 173, - 0, 174, 34, 277, 278, 34, 0, 0, 277, 278, - 48, 50, 49, 34, 0, 0, 173, 0, 407, 629, - 0, 0, 48, 50, 49, 34, 0, 48, 50, 49, - 0, 0, 48, 50, 49, 0, 34, 45, 0, 48, - 50, 49, 48, 50, 49, 34, 280, 281, 0, 45, - 48, 50, 49, 34, 45, 282, 0, 0, 283, 45, - 284, 0, 48, 50, 49, 34, 45, 0, 34, 45, - 0, 0, 0, 48, 50, 49, 34, 45, 0, 0, - 0, 34, 48, 50, 49, 280, 281, 34, 0, 45, - 48, 50, 49, 0, 282, 0, 34, 283, 0, 284, - 45, 0, 48, 50, 49, 48, 50, 49, 0, 45, - 0, 34, 0, 48, 50, 49, 0, 45, 48, 50, - 49, 0, 0, 0, 48, 50, 49, 280, 281, 45, - 0, 0, 45, 48, 50, 49, 282, 0, 0, 283, - 45, 284, 0, 0, 0, 45, 0, 0, 48, 50, - 49, 45, 34, 0, 0, 78, 79, 0, 0, -346, - 45, 0, 0, 80, 81, 280, 281, 82, 0, 83, - 0, 0, 0, 0, 282, 45, 0, 283, 0, 284, - 6, 5, 4, 1, 3, 2, 34, 78, 79, 48, - 50, 49, 0, -346, 34, 80, 81, 78, 79, 82, - 0, 83, 34, 0, 0, 80, 81, 0, 0, 82, - 34, 83, 0, 0, 0, 0, 45, 0, 0, 0, - 0, 0, 0, 48, 50, 49, 245, 244, 0, 0, - 34, 48, 50, 49, 240, 239, 0, 0, 34, 48, - 50, 49, 240, 239, 0, 78, 79, 48, 50, 49, - 45, 0, 0, 80, 81, 0, 0, 82, 45, 83, - 0, 0, 245, 244, 0, 0, 45, 48, 50, 49, - 240, 239, 151, 0, 45, 48, 50, 49, 0, 0, - 0, 0, 152, 0, 0, 0, 153, 0, 0, 0, - 151, 0, 0, 0, 45, 154, 0, 155, 0, 0, - 152, 0, 45, 0, 153, 0, 0, 0, 156, 0, - 157, 62, 0, 154, 0, 155, 0, 0, 158, 0, - 0, 159, 63, 0, 0, 34, 156, 160, 157, 62, - 0, 0, 0, 161, 0, 0, 158, 0, 0, 159, - 63, 0, 0, 0, 0, 160, 0, 0, 0, 162, - 0, 161, 0, 0, 0, 0, 0, 245, 244, 0, - 0, 0, 48, 50, 49, 0, 0, 162, 0, 0, + 344, -127, 576, -129, 551, 631, 546, -105, 342, 465, + 448, 461, -132, -106, 245, 481, 558, 558, 398, 573, + 392, 482, 402, 268, 354, -124, 350, 578, 585, -135, + -116, 484, 474, 404, -106, -105, -127, -129, -124, 454, + 438, -135, 245, 558, 448, 424, 448, 448, -132, 456, + 439, 588, 452, 268, 406, 350, 408, -116, 558, 405, + 432, 544, 418, 432, 413, 432, 329, 166, 524, 461, + 428, 421, 465, 172, 283, 143, 420, 143, 241, 166, + 262, 73, 149, 185, 323, 283, 307, 323, 336, 73, + 655, 189, 0, 245, 423, 192, 448, 0, 0, 101, + 240, 0, 451, 346, 243, 303, 424, 315, 181, 143, + 0, 268, 151, 0, 399, 312, 452, 350, 143, 261, + 174, 317, 143, 244, 303, 184, 435, 238, 461, 465, + 442, 143, 103, 143, 143, 305, 143, 64, 143, 175, + 143, 338, 101, 60, 143, 555, 0, 191, 65, 325, + 0, 143, 0, 326, 61, 631, 174, 555, 103, 259, + 258, 501, 143, 259, 258, 590, 589, 252, 251, 60, + 426, 436, 416, 415, 264, 175, 259, 258, 645, 644, + 61, 179, 257, 256, 144, 168, 463, 60, 105, 169, + 467, 60, 352, 626, 339, 87, 321, 88, 61, 651, + 650, 525, 61, 348, 525, 556, 174, 106, 89, 107, + 174, 525, 490, 143, 66, 446, 445, 648, 647, 66, + 580, 87, 549, 88, 87, 175, 88, 411, 87, 175, + 88, 176, 567, 174, 89, 542, 87, 89, 88, 531, + 0, 89, 87, 525, 88, 581, 579, 527, 646, 89, + 527, 66, 175, 592, 411, 89, 0, 527, 526, 67, + 491, 526, 0, 0, 67, 68, 236, 235, 526, 87, + 68, 88, 87, 0, 88, 0, 550, 548, 87, 558, + 88, 527, 89, 267, 265, 89, 568, 566, 87, 527, + 88, 89, 526, 532, 530, 87, 67, 88, 525, 0, + 526, 89, 68, 87, 87, 88, 88, 174, 89, 477, + 0, 266, 0, 285, 286, 641, 89, 89, 75, 76, + 75, 76, 0, 0, 0, -92, 175, 0, 176, 642, + 640, 174, 6, 5, 4, 1, 3, 2, 0, 593, + 287, 288, 290, 291, 527, 77, 78, 77, 78, -92, + 175, 292, 176, 0, 293, 526, 294, 290, 291, 0, + 639, 0, 478, 476, 290, 291, 292, 0, 0, 293, + 0, 294, 0, 292, 290, 291, 293, 0, 294, 0, + 290, 291, 0, 292, 0, 0, 293, 0, 294, 292, + 80, 81, 293, 35, 294, 0, 0, 0, 82, 83, + 80, 81, 84, 35, 85, 0, 285, 286, 82, 83, + 80, 81, 84, 35, 85, 0, 0, 0, 82, 83, + 0, 0, 84, 35, 85, 0, 35, 0, 0, 0, + 49, 52, 50, 287, 288, 0, 0, 0, 0, 0, + 49, 52, 50, 0, 35, 0, 0, 35, 0, 0, + 49, 52, 50, 0, 0, 0, 0, 46, 34, 51, + 49, 52, 50, 49, 52, 50, 0, 46, 34, 51, + 0, 0, 0, 0, 0, 0, 0, 46, 34, 51, + 35, 49, 52, 50, 49, 52, 50, 46, 34, 51, + 46, 34, 51, 80, 81, 35, 0, 0, 35, 0, + 0, 82, 83, 0, 0, 84, 0, 85, 46, 34, + 51, 46, 34, 51, 35, 0, 0, 49, 52, 50, + 0, 184, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 35, 49, 52, 50, 49, 52, 50, 184, 0, + 0, 0, 0, 0, 46, 34, 51, 0, 0, 0, + 0, 49, 52, 50, 35, 0, 0, 0, 0, 46, + 34, 51, 46, 34, 51, 35, 0, 0, 49, 52, + 50, 0, 184, 0, 0, 35, 0, 0, 46, 34, + 51, 0, 0, 0, 35, 0, 255, 254, 0, 0, + 0, 49, 52, 50, 0, 46, 34, 51, 0, 0, + 0, 0, 49, 52, 50, 35, 0, 0, 0, 0, + 0, 0, 49, 52, 50, 0, 255, 254, 46, 34, + 51, 49, 52, 50, 0, 0, 0, 0, 0, 46, + 34, 51, 0, 0, 0, 0, 0, 255, 254, 46, + 34, 51, 49, 52, 50, 0, 0, 0, 46, 34, + 51, 35, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 46, + 34, 51, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 250, 249, 153, 0, 0, 49, 52, + 50, 0, 0, 0, 0, 154, 0, 0, 0, 155, + 0, 0, 0, 0, 0, 0, 0, 0, 156, 0, + 157, 0, 0, 319, 0, 46, 34, 51, 0, 0, + 0, 158, 0, 159, 64, 0, 0, 153, 0, 0, + 0, 160, 0, 0, 161, 65, 0, 154, 0, 0, + 162, 155, 0, 0, 0, 0, 163, 0, 0, 0, + 156, 0, 157, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 164, 158, 0, 159, 64, 0, 0, 0, + 0, 0, 0, 160, 0, 0, 161, 65, 0, 0, + 0, 0, 162, 0, 0, 0, 0, 0, 163, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 30, 31, 0, 45, - 0, 0, 0, 0, 0, 0, 33, 0, 0, 151, - 0, 0, 0, 34, 0, 0, 0, 35, 36, 152, - 37, 0, 0, 153, 0, 0, 0, 504, 0, 0, - 0, 44, 154, 0, 155, 0, 0, 309, 0, 0, - 0, 0, 0, 0, 0, 156, 0, 157, 62, 51, - 48, 50, 49, 0, 52, 158, 0, 0, 159, 63, - 0, 0, 0, 0, 160, 43, 54, 32, 0, 0, - 161, 40, 0, 0, 0, 0, 0, 45, 0, 0, - 0, 0, 0, 0, 0, 0, 162, 0, 0, 0, + 0, 0, 0, 0, 164, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 30, 31, 0, 0, 0, 0, 0, 0, 30, - 31, 33, 0, 0, 0, 0, 0, 0, 34, 33, - 0, 0, 35, 36, 0, 37, 34, 0, 0, 0, - 35, 36, 41, 37, 0, 0, 44, 0, 0, 0, - 504, 0, 0, 0, 44, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 51, 48, 50, 49, 0, 52, - 0, 0, 51, 48, 50, 49, 0, 52, 0, 0, - 43, 54, 32, 0, 0, 0, 40, 0, 43, 54, - 32, 0, 45, 0, 40, 0, 0, 0, 0, 0, - 45, 0, 30, 31, 0, 0, 0, 0, 0, 0, - 30, 31, 33, 0, 0, 0, 0, 0, 0, 34, - 33, 0, 0, 35, 36, 0, 37, 34, 0, 0, - 0, 35, 36, 41, 37, 0, 0, 44, 0, 0, - 0, 504, 0, 0, 0, 44, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 51, 48, 50, 49, 0, - 52, 0, 0, 51, 48, 50, 49, 0, 52, 0, - 0, 43, 54, 32, 0, 0, 0, 40, 0, 43, - 54, 32, 0, 45, 0, 40, 0, 0, 0, 0, - 0, 45, 0, 30, 31, 0, 0, 0, 0, 0, - 0, 30, 31, 33, 0, 0, 0, 0, 0, 0, - 34, 33, 0, 0, 35, 36, 0, 37, 34, 0, - 0, 0, 35, 36, 41, 37, 0, 0, 44, 0, - 0, 0, 504, 0, 0, 0, 44, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 51, 48, 50, 49, - 0, 52, 0, 0, 51, 48, 50, 49, 0, 52, - 0, 0, 43, 54, 32, 0, 0, 0, 40, 0, - 43, 54, 32, 0, 45, 0, 40, 0, 0, 0, - 0, 0, 45, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, + 31, 0, 0, 0, 0, 0, 0, 0, 0, 33, + 0, 0, 0, 0, 0, 0, 35, 0, 0, 0, + 36, 37, 0, 38, 0, 0, 0, 0, 0, 0, + 42, 0, 0, 0, 45, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 53, 49, 52, 50, 0, 54, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 44, 56, + 32, 0, 0, 0, 41, 0, 0, 0, 0, 0, + 46, 34, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 31, 0, 0, 0, 0, 0, 0, - 0, 0, 33, 0, 0, 0, 0, 0, 0, 34, - 0, 0, 0, 35, 36, 0, 37, 0, 0, 0, - 0, 0, 0, 504, 0, 0, 0, 44, 0, 0, + 0, 0, 33, 0, 0, 0, 0, 0, 0, 35, + 0, 0, 0, 36, 37, 0, 38, 0, 0, 0, + 0, 0, 0, 516, 0, 0, 0, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 51, 48, 50, 49, 0, - 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 43, 54, 32, 0, 0, 0, 40, 0, 0, - 0, 0, 0, 45, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 503, 0, 30, 31, 0, 0, 0, - 0, 0, 0, 0, 0, 215, 0, 0, 0, 0, - 0, 0, 34, 0, 0, 0, 35, 36, 0, 37, - 0, 0, 0, 0, 0, 0, 504, 0, 0, 0, - 44, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 51, 505, - 507, 506, 0, 52, 0, 0, 0, 0, 226, 0, - 0, 0, 0, 0, 43, 54, 32, 210, 0, 0, - 40, 0, 0, 0, 0, 0, 45, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 503, 0, 30, 31, - 0, 0, 0, 0, 0, 0, 0, 0, 215, 0, - 0, 0, 0, 0, 0, 34, 0, 0, 0, 35, - 36, 0, 37, 0, 0, 0, 0, 0, 0, 504, - 0, 0, 0, 44, 0, 0, 0, 0, 0, 0, - 0, 551, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 51, 505, 507, 506, 0, 52, 0, 0, 0, - 0, 226, 0, 0, 0, 0, 0, 43, 54, 32, - 210, 0, 0, 40, 0, 0, 0, 0, 0, 45, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 503, - 0, 30, 31, 0, 0, 0, 0, 0, 0, 0, - 0, 215, 0, 0, 0, 0, 0, 0, 34, 0, - 0, 0, 35, 36, 0, 37, 0, 0, 0, 0, - 0, 0, 504, 0, 0, 0, 44, 0, 0, 0, - 0, 0, 0, 0, 548, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 51, 505, 507, 506, 0, 52, - 0, 0, 0, 0, 226, 0, 0, 0, 0, 0, - 43, 54, 32, 210, 0, 0, 40, 0, 0, 0, - 0, 0, 45, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 29, 30, 31, 0, 0, 0, 0, 0, - 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, - 34, 0, 0, 0, 35, 36, 0, 37, 0, 0, - 0, 38, 0, 39, 41, 42, 0, 0, 44, 0, - 0, 0, 46, 0, 47, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 51, 48, 50, 49, - 0, 52, 0, 53, 0, 55, 0, 56, 0, 0, - 0, 0, 43, 54, 32, 0, 0, 0, 40, 0, - 0, 0, 0, 0, 45, 0, 0, 0, 0, 0, - 0, 0, 0, 0, -122, 0, 0, 0, 29, 30, - 31, 0, 0, 0, 0, 0, 0, 0, 0, 33, - 0, 0, 0, 0, 0, 0, 34, 0, 0, 0, - 35, 36, 0, 37, 0, 0, 0, 38, 0, 39, - 41, 42, 0, 0, 44, 0, 0, 0, 46, 0, - 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 51, 48, 50, 49, 0, 52, 0, 53, - 0, 55, 0, 56, 0, 0, 0, 0, 43, 54, - 32, 0, 0, 0, 40, 0, 0, 0, 0, 0, - 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 29, 30, 31, 0, 0, 0, 0, 0, 0, 0, - 0, 33, 0, 0, 0, 0, 0, 0, 34, 0, - 0, 0, 35, 36, 0, 37, 0, 0, 0, 38, - 0, 39, 41, 42, 0, 0, 44, 0, 0, 0, - 46, 0, 47, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 51, 48, 50, 49, 0, 52, - 0, 53, 0, 55, 272, 56, 0, 0, 0, 0, - 43, 54, 32, 0, 0, 0, 40, 0, 0, 0, + 0, 0, 0, 0, 0, 53, 49, 52, 50, 0, + 54, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 44, 56, 32, 0, 0, 0, 41, 0, 0, + 0, 0, 0, 46, 34, 51, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 515, 0, 30, 31, 0, + 0, 0, 0, 0, 0, 0, 0, 219, 0, 0, + 0, 0, 0, 0, 35, 0, 0, 0, 36, 37, + 0, 38, 0, 0, 0, 0, 0, 0, 516, 0, 0, 0, 45, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 476, 0, 0, 29, 30, 31, 0, 0, - 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, - 0, 0, 0, 34, 0, 0, 0, 35, 36, 0, - 37, 0, 0, 0, 38, 0, 39, 41, 42, 0, - 0, 44, 0, 0, 0, 46, 0, 47, 0, 0, - 477, 0, 0, 0, 0, 0, 0, 0, 0, 51, - 48, 50, 49, 0, 52, 0, 53, 0, 55, 0, - 56, 0, 0, 0, 0, 43, 54, 32, 0, 0, - 0, 40, 0, 0, 0, 0, 0, 45, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 476, 0, 0, - 29, 30, 31, 0, 0, 0, 0, 0, 0, 0, - 0, 33, 0, 0, 0, 0, 0, 0, 34, 0, - 0, 0, 35, 36, 0, 37, 0, 0, 0, 38, - 0, 39, 41, 42, 0, 0, 44, 0, 0, 0, - 46, 0, 47, 0, 0, 482, 0, 0, 0, 0, - 0, 0, 0, 0, 51, 48, 50, 49, 0, 52, - 0, 53, 0, 55, 0, 56, 0, 0, 0, 0, - 43, 54, 32, 0, 0, 0, 40, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 53, 517, 519, 518, 0, 54, 0, 0, 0, 0, + 227, 0, 0, 0, 0, 0, 44, 56, 32, 214, + 0, 0, 41, 0, 0, 0, 0, 0, 46, 34, + 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 515, 0, 30, 31, 0, 0, 0, 0, 0, 0, + 0, 0, 219, 0, 0, 0, 0, 0, 0, 35, + 0, 0, 0, 36, 37, 0, 38, 0, 0, 0, + 0, 0, 0, 516, 0, 0, 0, 45, 0, 0, + 0, 0, 0, 0, 0, 560, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 53, 517, 519, 518, 0, + 54, 0, 0, 0, 0, 227, 0, 0, 0, 0, + 0, 44, 56, 32, 214, 0, 0, 41, 0, 0, + 0, 0, 0, 46, 34, 51, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 515, 0, 30, 31, 0, + 0, 0, 0, 0, 0, 0, 0, 219, 0, 0, + 0, 0, 0, 0, 35, 0, 0, 0, 36, 37, + 0, 38, 0, 0, 0, 0, 0, 0, 516, 0, 0, 0, 45, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 484, 0, 0, 29, 30, 31, 0, 0, - 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, - 0, 0, 0, 34, 0, 0, 0, 35, 36, 0, - 37, 0, 0, 0, 38, 0, 39, 41, 42, 0, - 0, 44, 0, 0, 0, 46, 0, 47, 0, 0, - 485, 0, 0, 0, 0, 0, 0, 0, 0, 51, - 48, 50, 49, 0, 52, 0, 53, 0, 55, 0, - 56, 0, 0, 0, 0, 43, 54, 32, 0, 0, - 0, 40, 0, 0, 0, 0, 0, 45, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 484, 0, 0, + 563, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 53, 517, 519, 518, 0, 54, 0, 0, 0, 0, + 227, 0, 0, 0, 0, 0, 44, 56, 32, 214, + 0, 0, 41, 0, 0, 0, 0, 0, 46, 34, + 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, 30, 31, 0, 0, 0, 0, 0, 0, 0, - 0, 33, 0, 0, 0, 0, 0, 0, 34, 0, - 0, 0, 35, 36, 0, 37, 0, 0, 0, 38, - 0, 39, 41, 42, 0, 0, 44, 0, 0, 0, - 46, 0, 47, 0, 0, 487, 0, 0, 0, 0, - 0, 0, 0, 0, 51, 48, 50, 49, 0, 52, - 0, 53, 0, 55, 0, 56, 0, 0, 0, 0, - 43, 54, 32, 0, 0, 0, 40, 0, 0, 0, - 0, 0, 45, 0, 0, 0, 0, 0, 0, 0, + 0, 33, 0, 0, 0, 0, 0, 0, 35, 0, + 0, 0, 36, 37, 0, 38, 0, 0, 0, 39, + 0, 40, 42, 43, 0, 0, 45, 0, 0, 0, + 47, 0, 48, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 53, 49, 52, 50, 0, 54, + 0, 55, 0, 57, 0, 58, 0, 0, 0, 0, + 44, 56, 32, 0, 0, 0, 41, 0, 0, 0, + 0, 0, 46, 34, 51, 0, 0, 0, 0, 0, + 0, 0, 0, 0, -125, 0, 0, 0, 29, 30, + 31, 0, 0, 0, 0, 0, 0, 0, 0, 33, + 0, 0, 0, 0, 0, 0, 35, 0, 0, 0, + 36, 37, 0, 38, 0, 0, 0, 39, 0, 40, + 42, 43, 0, 0, 45, 0, 0, 0, 47, 0, + 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 53, 49, 52, 50, 0, 54, 0, 55, + 0, 57, 0, 58, 0, 0, 0, 0, 44, 56, + 32, 0, 0, 0, 41, 0, 0, 0, 0, 0, + 46, 34, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, 30, 31, 0, 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, - 34, 217, 0, 0, 218, 36, 0, 37, 0, 0, - 0, 38, 0, 39, 41, 42, 0, 0, 44, 0, - 0, 0, 46, 0, 47, 0, 0, 0, 0, 0, - 0, 0, 221, 0, 0, 0, 51, 48, 50, 49, - 223, 52, 0, 53, 225, 55, 0, 56, 0, 228, - 0, 0, 43, 54, 32, 0, 0, 0, 40, 0, - 0, 0, 0, 0, 45, 0, 0, 0, 0, 0, + 35, 0, 0, 0, 36, 37, 0, 38, 0, 0, + 0, 39, 0, 40, 42, 43, 0, 0, 45, 0, + 0, 0, 47, 0, 48, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 53, 49, 52, 50, + 0, 54, 0, 55, 0, 57, 282, 58, 0, 0, + 0, 0, 44, 56, 32, 0, 0, 0, 41, 0, + 0, 0, 0, 0, 46, 34, 51, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 496, 0, 0, 29, + 30, 31, 0, 0, 0, 0, 0, 0, 0, 0, + 33, 0, 0, 0, 0, 0, 0, 35, 0, 0, + 0, 36, 37, 0, 38, 0, 0, 0, 39, 0, + 40, 42, 43, 0, 0, 45, 0, 0, 0, 47, + 0, 48, 0, 0, 499, 0, 0, 0, 0, 0, + 0, 0, 0, 53, 49, 52, 50, 0, 54, 0, + 55, 0, 57, 0, 58, 0, 0, 0, 0, 44, + 56, 32, 0, 0, 0, 41, 0, 0, 0, 0, + 0, 46, 34, 51, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 496, 0, 0, 29, 30, 31, 0, + 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, + 0, 0, 0, 0, 35, 0, 0, 0, 36, 37, + 0, 38, 0, 0, 0, 39, 0, 40, 42, 43, + 0, 0, 45, 0, 0, 0, 47, 0, 48, 0, + 0, 497, 0, 0, 0, 0, 0, 0, 0, 0, + 53, 49, 52, 50, 0, 54, 0, 55, 0, 57, + 0, 58, 0, 0, 0, 0, 44, 56, 32, 0, + 0, 0, 41, 0, 0, 0, 0, 0, 46, 34, + 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 488, 0, 0, 29, 30, 31, 0, 0, 0, 0, + 0, 0, 0, 0, 33, 0, 0, 0, 0, 0, + 0, 35, 0, 0, 0, 36, 37, 0, 38, 0, + 0, 0, 39, 0, 40, 42, 43, 0, 0, 45, + 0, 0, 0, 47, 0, 48, 0, 0, 489, 0, + 0, 0, 0, 0, 0, 0, 0, 53, 49, 52, + 50, 0, 54, 0, 55, 0, 57, 0, 58, 0, + 0, 0, 0, 44, 56, 32, 0, 0, 0, 41, + 0, 0, 0, 0, 0, 46, 34, 51, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 488, 0, 0, + 29, 30, 31, 0, 0, 0, 0, 0, 0, 0, + 0, 33, 0, 0, 0, 0, 0, 0, 35, 0, + 0, 0, 36, 37, 0, 38, 0, 0, 0, 39, + 0, 40, 42, 43, 0, 0, 45, 0, 0, 0, + 47, 0, 48, 0, 0, 494, 0, 0, 0, 0, + 0, 0, 0, 0, 53, 49, 52, 50, 0, 54, + 0, 55, 0, 57, 0, 58, 0, 0, 0, 0, + 44, 56, 32, 0, 0, 0, 41, 0, 0, 0, + 0, 0, 46, 34, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, 30, 31, 0, 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, 0, - 0, 0, 34, 217, 0, 0, 583, 584, 0, 37, - 0, 0, 0, 38, 0, 39, 41, 42, 0, 0, - 44, 0, 0, 0, 46, 0, 47, 0, 0, 0, - 0, 0, 0, 0, 221, 0, 0, 0, 51, 48, - 50, 49, 223, 52, 0, 53, 225, 55, 0, 56, - 0, 228, 0, 0, 43, 54, 32, 0, 0, 0, - 40, 0, 0, 0, 0, 0, 45, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 109, 110, 111, 0, - 0, 113, 115, 116, 0, 0, 117, 0, 118, 0, - 0, 0, 120, 121, 122, 0, 0, 0, 0, 0, - 0, 34, 123, 124, 125, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 126, 0, 0, 0, 0, + 0, 0, 35, 220, 0, 0, 221, 37, 0, 38, + 0, 0, 0, 39, 0, 40, 42, 43, 0, 0, + 45, 0, 0, 0, 47, 0, 48, 0, 0, 0, + 0, 0, 0, 0, 223, 0, 0, 0, 53, 49, + 52, 50, 224, 54, 0, 55, 226, 57, 0, 58, + 0, 229, 0, 0, 44, 56, 32, 0, 0, 0, + 41, 0, 0, 0, 0, 0, 46, 34, 51, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 29, 30, + 31, 0, 0, 0, 0, 0, 0, 0, 0, 33, + 0, 0, 0, 0, 0, 0, 35, 220, 0, 0, + 595, 596, 0, 38, 0, 0, 0, 39, 0, 40, + 42, 43, 0, 0, 45, 0, 0, 0, 47, 0, + 48, 0, 0, 0, 0, 0, 0, 0, 223, 0, + 0, 0, 53, 49, 52, 50, 224, 54, 0, 55, + 226, 57, 0, 58, 0, 229, 0, 0, 44, 56, + 32, 0, 0, 0, 41, 0, 0, 0, 0, 0, + 46, 34, 51, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 111, 112, 113, 0, 0, 115, 117, 118, + 0, 0, 119, 0, 120, 0, 0, 0, 122, 123, + 124, 0, 0, 0, 0, 0, 0, 35, 125, 126, + 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 131, 0, 0, + 0, 0, 0, 0, 49, 52, 50, 132, 133, 134, + 0, 136, 137, 138, 139, 140, 141, 0, 0, 129, + 135, 121, 114, 116, 130, 0, 0, 0, 0, 0, + 0, 46, 34, 51, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 111, 112, 113, 0, 0, 115, 117, + 118, 0, 0, 119, 0, 120, 0, 0, 0, 122, + 123, 124, 0, 0, 0, 0, 0, 0, 35, 125, + 126, 127, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 128, 0, 0, 0, 395, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 131, 0, + 0, 0, 0, 0, 397, 49, 52, 50, 132, 133, + 134, 0, 136, 137, 138, 139, 140, 141, 0, 0, + 129, 135, 121, 114, 116, 130, 0, 0, 0, 0, + 0, 0, 46, 34, 51, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 111, 112, 113, 0, 0, 115, + 117, 118, 0, 0, 119, 0, 120, 0, 0, 0, + 122, 123, 124, 0, 0, 0, 0, 0, 0, 35, + 125, 126, 127, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 128, 0, 0, 0, 395, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 131, + 0, 0, 0, 0, 0, 397, 49, 52, 50, 132, + 133, 134, 0, 136, 137, 138, 139, 140, 141, 0, + 0, 129, 135, 121, 114, 116, 130, 0, 0, 0, + 0, 0, 0, 46, 374, 380, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 111, 112, 113, 0, 0, + 115, 117, 118, 0, 0, 119, 0, 120, 0, 0, + 0, 122, 123, 124, 0, 0, 0, 0, 0, 0, + 35, 125, 126, 127, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 128, 0, 0, 0, 395, 0, + 0, 0, 0, 0, 0, 0, 396, 0, 0, 0, + 131, 0, 0, 0, 0, 0, 397, 49, 52, 50, + 132, 133, 134, 0, 136, 137, 138, 139, 140, 141, + 0, 0, 129, 135, 121, 114, 116, 130, 0, 0, + 0, 0, 0, 0, 46, 374, 380, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 213, 0, 0, 0, + 0, 215, 0, 29, 30, 31, 217, 0, 0, 0, + 0, 0, 0, 218, 33, 0, 0, 0, 0, 0, + 0, 35, 220, 0, 0, 221, 37, 0, 38, 0, + 0, 0, 39, 0, 40, 42, 43, 0, 0, 45, + 0, 0, 0, 47, 0, 48, 0, 0, 0, 0, + 0, 222, 0, 223, 0, 0, 0, 53, 49, 52, + 50, 224, 54, 225, 55, 226, 57, 227, 58, 228, + 229, 0, 0, 44, 56, 32, 214, 216, 0, 41, + 0, 0, 0, 0, 0, 46, 34, 51, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 213, 0, 0, + 0, 0, 215, 0, 29, 30, 31, 217, 0, 0, + 0, 0, 0, 0, 218, 219, 0, 0, 0, 0, + 0, 0, 35, 220, 0, 0, 221, 37, 0, 38, + 0, 0, 0, 39, 0, 40, 42, 43, 0, 0, + 45, 0, 0, 0, 47, 0, 48, 0, 0, 0, + 0, 0, 222, 0, 223, 0, 0, 0, 53, 49, + 52, 50, 224, 54, 225, 55, 226, 57, 227, 58, + 228, 229, 0, 0, 44, 56, 32, 214, 216, 0, + 41, 0, 0, 0, 0, 0, 46, 34, 51, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 600, 112, + 113, 0, 0, 602, 117, 604, 30, 31, 605, 0, + 120, 0, 0, 0, 122, 607, 608, 0, 0, 0, + 0, 0, 0, 35, 609, 126, 127, 221, 37, 0, + 38, 0, 0, 0, 39, 0, 40, 610, 43, 0, + 0, 612, 0, 0, 0, 47, 0, 48, 0, 0, + 0, 0, 0, 613, 0, 223, 0, 0, 0, 614, + 49, 52, 50, 615, 616, 617, 55, 619, 620, 621, + 622, 623, 624, 0, 0, 611, 618, 606, 601, 603, + 130, 41, 0, 0, 0, 0, 0, 46, 374, 380, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 365, + 112, 113, 0, 0, 367, 117, 369, 30, 31, 370, + 0, 120, 0, 0, 0, 122, 372, 373, 0, 0, + 0, 0, 0, 0, 35, 375, 126, 127, 221, 37, + 0, 38, 0, 0, 0, 39, 0, 40, 376, 43, + 0, 0, 378, 0, 0, 0, 47, 0, 48, 0, + -271, 0, 0, 0, 379, 0, 223, 0, 0, 0, + 381, 49, 52, 50, 382, 383, 384, 55, 386, 387, + 388, 389, 390, 391, 0, 0, 377, 385, 371, 366, + 368, 130, 41, 0, 0, 0, 0, 0, 46, 374, + 380, 0, 0, 0, 0, 0, 0, 0, 0, 0, + + 565, 148, 545, 16, 649, 547, 541, 469, 320, 529, + 528, 630, 183, 248, 263, 500, 485, 447, 629, 183, + 627, 444, 253, 393, 587, 652, 313, 152, 643, 572, + 591, 575, 586, 638, 331, 574, 453, 455, 466, 253, + 437, 178, 433, 253, 0, 248, 584, 458, 248, 313, + 441, 183, 444, 457, 237, 190, 447, 188, 206, 425, + 400, 462, 351, 313, 347, 150, 165, 447, 475, 444, + 183, 145, 393, 260, 0, 409, 173, 409, 260, 362, + 171, 514, 409, 495, 362, 393, 206, 498, 628, 313, + 313, 206, 356, 142, 206, 331, 362, 599, 403, 0, + 345, 62, 62, 62, 182, 62, 299, 182, 295, 206, + 410, 417, 410, 206, 62, 393, 62, 410, 62, 296, + 148, 298, 148, 297, 62, 62, 182, 504, 412, 62, + 180, 502, 511, 206, 512, 62, 108, 460, 188, 62, + 394, 188, 62, 206, 460, 247, 62, 206, 459, 206, + 62, 102, 459, 62, 62, 514, 206, 62, 653, 503, + 407, 343, 341, 62, 313, 110, 419, 167, 188, 187, + 170, 340, 514, 104, 0, 553, 422, 206, 62, 206, + 62, 63, 62, 72, 62, 510, 71, 97, 62, 514, + 70, 62, 557, 69, 355, 62, 239, 62, 493, 318, + 86, 469, 492, 62, 483, 74, 242, 206, 93, 62, + 353, 62, 206, 260, 95, 0, 96, 62, 62, 62, + 322, 62, 94, 206, 100, 98, 206, 99, 62, 247, + 277, 464, 0, 206, 79, 281, 314, 468, 62, 62, + 206, 507, 91, 246, 206, 62, 62, 349, 505, 90, + 206, 62, 62, 460, 459, 62, 206, 506, 62, 401, + 206, 62, 92, 309, 62, 108, 281, 362, 281, 281, + 0, 313, 206, 62, 304, 479, 0, 309, 281, 62, + 206, 327, 281, 0, 281, 337, 300, 309, 324, 0, + 0, 62, 281, 0, 110, 177, 281, 62, 301, 308, + 309, 0, 281, 309, 302, 281, 62, 62, 281, 330, + 62, 281, 281, 289, 514, 281, 0, 0, 306, 284, + 0, 522, 328, 569, 561, 311, 553, 564, 625, 0, + 0, 0, 514, 513, 523, 514, 0, 0, 0, 522, + 0, 0, 522, 316, 0, 0, 0, 0, 0, 485, + 440, 513, 523, 0, 513, 523, 533, 534, 535, 536, + 540, 537, 538, 577, 533, 534, 535, 536, 540, 537, + 538, 594, 0, 0, 0, 0, 362, 0, 597, 598, + 533, 534, 535, 536, 540, 537, 538, 0, 0, 206, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 129, 0, 0, 0, 0, 0, 0, 48, 50, - 49, 130, 131, 132, 0, 134, 135, 136, 137, 138, - 139, 0, 0, 127, 133, 119, 112, 114, 128, 0, - 0, 0, 0, 0, 0, 45, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 109, 110, 111, 0, 0, - 113, 115, 116, 0, 0, 117, 0, 118, 0, 0, - 0, 120, 121, 122, 0, 0, 0, 0, 0, 0, - 394, 123, 124, 125, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 126, 0, 0, 0, 395, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 129, 0, 0, 0, 0, 0, 399, 396, 398, 0, - 130, 131, 132, 0, 134, 135, 136, 137, 138, 139, - 0, 0, 127, 133, 119, 112, 114, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 109, 110, 111, 0, 0, 113, - 115, 116, 0, 0, 117, 0, 118, 0, 0, 0, - 120, 121, 122, 0, 0, 0, 0, 0, 0, 394, - 123, 124, 125, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 126, 0, 0, 0, 395, 0, 0, - 0, 0, 0, 0, 0, 397, 0, 0, 0, 129, - 0, 0, 0, 0, 0, 399, 396, 398, 0, 130, - 131, 132, 0, 134, 135, 136, 137, 138, 139, 0, - 0, 127, 133, 119, 112, 114, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 209, 0, 0, 0, 0, 211, 0, - 29, 30, 31, 213, 0, 0, 0, 0, 0, 0, - 214, 215, 0, 0, 0, 0, 0, 0, 216, 217, - 0, 0, 218, 36, 0, 37, 0, 0, 0, 38, - 0, 39, 41, 42, 0, 0, 44, 0, 0, 0, - 46, 0, 47, 0, 0, 0, 0, 0, 220, 0, - 221, 0, 0, 0, 51, 219, 222, 49, 223, 52, - 224, 53, 225, 55, 226, 56, 227, 228, 0, 0, - 43, 54, 32, 210, 212, 0, 40, 0, 0, 0, - 0, 0, 45, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 209, 0, 0, 0, 0, 211, 0, 29, - 30, 31, 213, 0, 0, 0, 0, 0, 0, 214, - 33, 0, 0, 0, 0, 0, 0, 216, 217, 0, - 0, 218, 36, 0, 37, 0, 0, 0, 38, 0, - 39, 41, 42, 0, 0, 44, 0, 0, 0, 46, - 0, 47, 0, 0, 0, 0, 0, 220, 0, 221, - 0, 0, 0, 51, 219, 222, 49, 223, 52, 224, - 53, 225, 55, 226, 56, 227, 228, 0, 0, 43, - 54, 32, 210, 212, 0, 40, 0, 0, 0, 0, - 0, 45, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 587, 110, 111, 0, 0, 589, 115, 591, 30, - 31, 592, 0, 118, 0, 0, 0, 120, 594, 595, - 0, 0, 0, 0, 0, 0, 596, 597, 124, 125, - 218, 36, 0, 37, 0, 0, 0, 38, 0, 39, - 598, 42, 0, 0, 600, 0, 0, 0, 46, 0, - 47, 0, 0, 0, 0, 0, 602, 0, 221, 0, - 0, 0, 604, 601, 603, 49, 605, 606, 607, 53, - 609, 610, 611, 612, 613, 614, 0, 0, 599, 608, - 593, 588, 590, 128, 40, 0, 0, 0, 0, 0, - 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 362, 110, 111, 0, 0, 364, 115, 366, 30, 31, - 367, 0, 118, 0, 0, 0, 120, 369, 370, 0, - 0, 0, 0, 0, 0, 371, 372, 124, 125, 218, - 36, 0, 37, 0, 0, 0, 38, 0, 39, 373, - 42, 0, 0, 375, 0, 0, 0, 46, 0, 47, - 0, -268, 0, 0, 0, 377, 0, 221, 0, 0, - 0, 379, 376, 378, 49, 380, 381, 382, 53, 384, - 385, 386, 387, 388, 389, 0, 0, 374, 383, 368, - 363, 365, 128, 40, 0, 0, 0, 0, 0, 45, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - - 639, 310, 529, 533, 516, 535, 517, 499, 500, 462, - 498, 620, 553, 312, 243, 16, 562, 440, 437, 633, - 238, 250, 473, 182, 253, 182, 617, 488, 359, 563, - 303, 321, 619, 642, 150, 575, 560, 250, 628, 163, - 426, 348, 250, 579, 186, 350, 182, 574, 468, 340, - 430, 345, 572, 434, 359, 457, 448, 446, 238, 450, - 455, 459, 243, 243, 461, 352, 238, 451, 354, 148, - 403, 483, 486, 176, 437, 143, 440, 401, 411, 335, - 188, 437, 440, 169, 237, 171, 237, 140, 359, 393, - 337, 303, 308, 416, 391, 146, 418, 303, 405, 359, - 359, 0, 0, 146, 0, 0, 0, 60, 178, 60, - 0, 452, 288, 0, 303, 60, 405, 60, 405, 180, - 285, 60, 60, 60, 491, 186, 286, 60, 287, 60, - 406, 453, 502, 502, 303, 643, 545, 60, 321, 453, - 60, 165, 180, 60, 60, 490, 180, 60, 406, 60, - 406, 452, 267, 146, 60, 60, 60, 271, 408, 289, - 60, 84, 69, 168, 60, 60, 60, 100, 106, 60, - 97, 98, 96, 502, 481, 0, 541, 72, 480, 436, - 433, 60, 60, 333, 60, 60, 91, 92, 90, 60, - 93, 60, 60, 89, 108, 60, 94, 339, 77, 88, - 60, 502, 462, 331, 60, 95, 303, 61, 618, 106, - 342, 330, 60, 60, 453, 452, 60, 60, 494, 495, - 60, 60, 493, 60, 60, 492, 60, 70, 68, 60, - 60, 102, 67, 60, 271, 108, 175, 299, 271, 0, - 299, 0, 271, 0, 314, 271, 294, 299, 60, 60, - 60, 0, 271, 271, 271, 271, 60, 279, 274, 320, - 0, 271, 318, 60, 0, 317, 0, 296, 271, 327, - 292, 60, 60, 472, 0, 304, 271, 271, 291, 290, - 557, 299, 299, 541, 549, 615, 271, 271, 502, 0, - 552, 0, 502, 0, 0, 510, 0, 0, 502, 510, - 0, 0, 303, 298, 301, 510, 473, 501, 511, 0, - 0, 501, 511, 0, 0, 0, 0, 501, 511, 521, - 522, 523, 524, 528, 525, 526, 582, 0, 0, 0, - 0, 0, 0, 585, 586, 521, 522, 523, 524, 528, - 525, 526, 557, 0, 0, 0, 0, 0, 0, 558, - 559, 521, 522, 523, 524, 528, 525, 526, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 306, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 443, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 565, 521, 522, 523, - 524, 528, 525, 526, 0, 0, 0, 0, 0, 0, + 0, 0, 569, 0, 0, 0, 0, 0, 0, 570, + 571, 533, 534, 535, 536, 540, 537, 538, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -697,120 +705,101 @@ const short QQmlJSGrammar::action_info [] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0}; + 0, 0, 0, 0, 0, 0, 0}; const short QQmlJSGrammar::action_check [] = { - 36, 36, 7, 55, 17, 7, 7, 60, 8, 7, - 7, 37, 36, 7, 20, 33, 55, 55, 60, 36, - 36, 33, 33, 55, 8, 7, 36, 7, 16, 33, - 7, 34, 7, 7, 7, 60, 7, 36, 33, 7, - 36, 33, 33, 5, 7, 5, 5, 33, 29, 36, - 91, 36, 7, 36, 7, 33, 7, 7, 66, 66, - 36, 33, 7, 7, 31, 1, 7, 24, 33, 2, - 60, 60, 36, 8, 8, 7, 55, 1, 36, 48, - 2, 8, 7, 2, 7, 2, 79, 36, 17, 36, - 60, 7, 48, 36, 36, 61, 33, 36, 1, 60, - 0, 8, 8, 1, 8, 6, 8, -1, -1, 61, - 48, 7, 8, 8, 60, 8, 10, -1, 8, 20, - 8, -1, 48, -1, 8, 79, 40, 40, 15, 77, - -1, 61, 60, 8, 8, 8, 15, 51, 51, 79, - 8, 61, 62, 61, 62, 50, 7, 34, 8, 54, - 61, 62, 61, 62, 56, 34, 60, 42, 79, 8, - 91, 55, 40, 56, 12, 60, 56, 50, 53, 61, - 62, 54, 60, 51, 29, 61, 62, 61, 40, 61, - 62, 12, 56, 29, 25, 60, 27, 60, 56, 51, - 61, 62, 25, 7, 27, 8, 12, 38, -1, 25, - 60, 27, 7, -1, 25, 38, 27, 29, 25, 57, - 27, 60, 38, 7, 25, 63, 27, 38, 36, 33, - 75, 38, 25, 25, 27, 27, 57, 38, 29, 75, - -1, 86, 63, 7, 29, 38, 38, 25, 29, 27, - 86, 57, 8, 61, 62, 8, -1, 63, 61, 62, - 38, 15, 25, 75, 27, 8, 61, 62, 25, -1, - 27, 61, 62, -1, 86, 38, -1, 61, 62, -1, - 34, 38, 36, 25, 75, 27, 61, 62, -1, 93, - 75, 15, -1, -1, 75, 86, 38, 61, 62, 89, - 25, 86, 27, -1, 60, 86, 18, 19, 61, 62, - 34, -1, 36, 38, 18, 19, -1, 92, 61, 62, - -1, -1, -1, 29, 47, 15, 18, 19, -1, -1, - 15, 18, 19, 45, 46, 29, -1, -1, 61, 62, - 29, 45, 46, 33, 34, 29, 36, 15, 33, 34, - -1, 36, 29, 45, 46, 29, -1, -1, 45, 46, - 66, 67, 68, 29, -1, -1, 34, -1, 36, 92, - -1, -1, 66, 67, 68, 29, -1, 66, 67, 68, - -1, -1, 66, 67, 68, -1, 29, 93, -1, 66, - 67, 68, 66, 67, 68, 29, 23, 24, -1, 93, - 66, 67, 68, 29, 93, 32, -1, -1, 35, 93, - 37, -1, 66, 67, 68, 29, 93, -1, 29, 93, - -1, -1, -1, 66, 67, 68, 29, 93, -1, -1, - -1, 29, 66, 67, 68, 23, 24, 29, -1, 93, - 66, 67, 68, -1, 32, -1, 29, 35, -1, 37, - 93, -1, 66, 67, 68, 66, 67, 68, -1, 93, - -1, 29, -1, 66, 67, 68, -1, 93, 66, 67, - 68, -1, -1, -1, 66, 67, 68, 23, 24, 93, - -1, -1, 93, 66, 67, 68, 32, -1, -1, 35, - 93, 37, -1, -1, -1, 93, -1, -1, 66, 67, - 68, 93, 29, -1, -1, 23, 24, -1, -1, 36, - 93, -1, -1, 31, 32, 23, 24, 35, -1, 37, - -1, -1, -1, -1, 32, 93, -1, 35, -1, 37, - 95, 96, 97, 98, 99, 100, 29, 23, 24, 66, - 67, 68, -1, 36, 29, 31, 32, 23, 24, 35, - -1, 37, 29, -1, -1, 31, 32, -1, -1, 35, - 29, 37, -1, -1, -1, -1, 93, -1, -1, -1, - -1, -1, -1, 66, 67, 68, 61, 62, -1, -1, - 29, 66, 67, 68, 61, 62, -1, -1, 29, 66, - 67, 68, 61, 62, -1, 23, 24, 66, 67, 68, - 93, -1, -1, 31, 32, -1, -1, 35, 93, 37, - -1, -1, 61, 62, -1, -1, 93, 66, 67, 68, - 61, 62, 3, -1, 93, 66, 67, 68, -1, -1, - -1, -1, 13, -1, -1, -1, 17, -1, -1, -1, - 3, -1, -1, -1, 93, 26, -1, 28, -1, -1, - 13, -1, 93, -1, 17, -1, -1, -1, 39, -1, - 41, 42, -1, 26, -1, 28, -1, -1, 49, -1, - -1, 52, 53, -1, -1, 29, 39, 58, 41, 42, - -1, -1, -1, 64, -1, -1, 49, -1, -1, 52, - 53, -1, -1, -1, -1, 58, -1, -1, -1, 80, - -1, 64, -1, -1, -1, -1, -1, 61, 62, -1, - -1, -1, 66, 67, 68, -1, -1, 80, -1, -1, + 60, 7, 7, 7, 34, 91, 24, 7, 61, 36, + 33, 36, 7, 7, 7, 60, 33, 33, 55, 66, + 8, 33, 55, 36, 16, 7, 36, 29, 8, 7, + 7, 55, 17, 36, 7, 7, 7, 7, 7, 36, + 55, 7, 7, 33, 33, 36, 33, 33, 7, 60, + 7, 60, 20, 36, 33, 36, 55, 7, 33, 60, + 5, 37, 36, 5, 60, 5, 7, 2, 66, 36, + 33, 33, 36, 7, 1, 8, 60, 8, 33, 2, + 36, 1, 8, 36, 2, 1, 8, 2, 17, 1, + 0, 8, -1, 7, 55, 33, 33, -1, -1, 48, + 60, -1, 6, 31, 55, 48, 36, 61, 60, 8, + -1, 36, 60, -1, 7, 61, 20, 36, 8, 77, + 15, 60, 8, 55, 48, 36, 10, 36, 36, 36, + 7, 8, 79, 8, 8, 79, 8, 42, 8, 34, + 8, 8, 48, 40, 8, 8, -1, 60, 53, 50, + -1, 8, -1, 54, 51, 91, 15, 8, 79, 61, + 62, 60, 8, 61, 62, 61, 62, 61, 62, 40, + 60, 55, 61, 62, 60, 34, 61, 62, 61, 62, + 51, 56, 61, 62, 56, 50, 60, 40, 15, 54, + 60, 40, 60, 56, 61, 25, 60, 27, 51, 61, + 62, 29, 51, 60, 29, 56, 15, 34, 38, 36, + 15, 29, 8, 8, 12, 61, 62, 61, 62, 12, + 36, 25, 7, 27, 25, 34, 27, 36, 25, 34, + 27, 36, 7, 15, 38, 29, 25, 38, 27, 7, + -1, 38, 25, 29, 27, 61, 62, 75, 92, 38, + 75, 12, 34, 7, 36, 38, -1, 75, 86, 57, + 56, 86, -1, -1, 57, 63, 61, 62, 86, 25, + 63, 27, 25, -1, 27, -1, 61, 62, 25, 33, + 27, 75, 38, 61, 62, 38, 61, 62, 25, 75, + 27, 38, 86, 61, 62, 25, 57, 27, 29, -1, + 86, 38, 63, 25, 25, 27, 27, 15, 38, 8, + -1, 89, -1, 18, 19, 47, 38, 38, 18, 19, + 18, 19, -1, -1, -1, 33, 34, -1, 36, 61, + 62, 15, 97, 98, 99, 100, 101, 102, -1, 93, + 45, 46, 23, 24, 75, 45, 46, 45, 46, 33, + 34, 32, 36, -1, 35, 86, 37, 23, 24, -1, + 92, -1, 61, 62, 23, 24, 32, -1, -1, 35, + -1, 37, -1, 32, 23, 24, 35, -1, 37, -1, + 23, 24, -1, 32, -1, -1, 35, -1, 37, 32, + 23, 24, 35, 29, 37, -1, -1, -1, 31, 32, + 23, 24, 35, 29, 37, -1, 18, 19, 31, 32, + 23, 24, 35, 29, 37, -1, -1, -1, 31, 32, + -1, -1, 35, 29, 37, -1, 29, -1, -1, -1, + 66, 67, 68, 45, 46, -1, -1, -1, -1, -1, + 66, 67, 68, -1, 29, -1, -1, 29, -1, -1, + 66, 67, 68, -1, -1, -1, -1, 93, 94, 95, + 66, 67, 68, 66, 67, 68, -1, 93, 94, 95, + -1, -1, -1, -1, -1, -1, -1, 93, 94, 95, + 29, 66, 67, 68, 66, 67, 68, 93, 94, 95, + 93, 94, 95, 23, 24, 29, -1, -1, 29, -1, + -1, 31, 32, -1, -1, 35, -1, 37, 93, 94, + 95, 93, 94, 95, 29, -1, -1, 66, 67, 68, + -1, 36, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 29, 66, 67, 68, 66, 67, 68, 36, -1, + -1, -1, -1, -1, 93, 94, 95, -1, -1, -1, + -1, 66, 67, 68, 29, -1, -1, -1, -1, 93, + 94, 95, 93, 94, 95, 29, -1, -1, 66, 67, + 68, -1, 36, -1, -1, 29, -1, -1, 93, 94, + 95, -1, -1, -1, 29, -1, 61, 62, -1, -1, + -1, 66, 67, 68, -1, 93, 94, 95, -1, -1, + -1, -1, 66, 67, 68, 29, -1, -1, -1, -1, + -1, -1, 66, 67, 68, -1, 61, 62, 93, 94, + 95, 66, 67, 68, -1, -1, -1, -1, -1, 93, + 94, 95, -1, -1, -1, -1, -1, 61, 62, 93, + 94, 95, 66, 67, 68, -1, -1, -1, 93, 94, + 95, 29, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 93, + 94, 95, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 61, 62, 3, -1, -1, 66, 67, + 68, -1, -1, -1, -1, 13, -1, -1, -1, 17, + -1, -1, -1, -1, -1, -1, -1, -1, 26, -1, + 28, -1, -1, 31, -1, 93, 94, 95, -1, -1, + -1, 39, -1, 41, 42, -1, -1, 3, -1, -1, + -1, 49, -1, -1, 52, 53, -1, 13, -1, -1, + 58, 17, -1, -1, -1, -1, 64, -1, -1, -1, + 26, -1, 28, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 80, 39, -1, 41, 42, -1, -1, -1, + -1, -1, -1, 49, -1, -1, 52, 53, -1, -1, + -1, -1, 58, -1, -1, -1, -1, -1, 64, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, 12, 13, -1, 93, - -1, -1, -1, -1, -1, -1, 22, -1, -1, 3, - -1, -1, -1, 29, -1, -1, -1, 33, 34, 13, - 36, -1, -1, 17, -1, -1, -1, 43, -1, -1, - -1, 47, 26, -1, 28, -1, -1, 31, -1, -1, - -1, -1, -1, -1, -1, 39, -1, 41, 42, 65, - 66, 67, 68, -1, 70, 49, -1, -1, 52, 53, - -1, -1, -1, -1, 58, 81, 82, 83, -1, -1, - 64, 87, -1, -1, -1, -1, -1, 93, -1, -1, - -1, -1, -1, -1, -1, -1, 80, -1, -1, -1, + -1, -1, -1, -1, 80, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 12, 13, -1, -1, -1, -1, -1, -1, 12, - 13, 22, -1, -1, -1, -1, -1, -1, 29, 22, - -1, -1, 33, 34, -1, 36, 29, -1, -1, -1, - 33, 34, 43, 36, -1, -1, 47, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 12, + 13, -1, -1, -1, -1, -1, -1, -1, -1, 22, + -1, -1, -1, -1, -1, -1, 29, -1, -1, -1, + 33, 34, -1, 36, -1, -1, -1, -1, -1, -1, 43, -1, -1, -1, 47, -1, -1, -1, -1, -1, - -1, -1, -1, -1, 65, 66, 67, 68, -1, 70, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 65, 66, 67, 68, -1, 70, -1, -1, - 81, 82, 83, -1, -1, -1, 87, -1, 81, 82, - 83, -1, 93, -1, 87, -1, -1, -1, -1, -1, - 93, -1, 12, 13, -1, -1, -1, -1, -1, -1, - 12, 13, 22, -1, -1, -1, -1, -1, -1, 29, - 22, -1, -1, 33, 34, -1, 36, 29, -1, -1, - -1, 33, 34, 43, 36, -1, -1, 47, -1, -1, - -1, 43, -1, -1, -1, 47, -1, -1, -1, -1, - -1, -1, -1, -1, -1, 65, 66, 67, 68, -1, - 70, -1, -1, 65, 66, 67, 68, -1, 70, -1, - -1, 81, 82, 83, -1, -1, -1, 87, -1, 81, - 82, 83, -1, 93, -1, 87, -1, -1, -1, -1, - -1, 93, -1, 12, 13, -1, -1, -1, -1, -1, - -1, 12, 13, 22, -1, -1, -1, -1, -1, -1, - 29, 22, -1, -1, 33, 34, -1, 36, 29, -1, - -1, -1, 33, 34, 43, 36, -1, -1, 47, -1, - -1, -1, 43, -1, -1, -1, 47, -1, -1, -1, - -1, -1, -1, -1, -1, -1, 65, 66, 67, 68, - -1, 70, -1, -1, 65, 66, 67, 68, -1, 70, - -1, -1, 81, 82, 83, -1, -1, -1, 87, -1, - 81, 82, 83, -1, 93, -1, 87, -1, -1, -1, - -1, -1, 93, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 81, 82, + 83, -1, -1, -1, 87, -1, -1, -1, -1, -1, + 93, 94, 95, -1, -1, -1, -1, -1, -1, -1, -1, -1, 12, 13, -1, -1, -1, -1, -1, -1, -1, -1, 22, -1, -1, -1, -1, -1, -1, 29, -1, -1, -1, 33, 34, -1, 36, -1, -1, -1, @@ -819,44 +808,45 @@ const short QQmlJSGrammar::action_check [] = { -1, -1, -1, -1, -1, 65, 66, 67, 68, -1, 70, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 81, 82, 83, -1, -1, -1, 87, -1, -1, - -1, -1, -1, 93, -1, -1, -1, -1, -1, -1, - -1, -1, -1, 10, -1, 12, 13, -1, -1, -1, - -1, -1, -1, -1, -1, 22, -1, -1, -1, -1, - -1, -1, 29, -1, -1, -1, 33, 34, -1, 36, - -1, -1, -1, -1, -1, -1, 43, -1, -1, -1, - 47, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, 65, 66, - 67, 68, -1, 70, -1, -1, -1, -1, 75, -1, - -1, -1, -1, -1, 81, 82, 83, 84, -1, -1, - 87, -1, -1, -1, -1, -1, 93, -1, -1, -1, - -1, -1, -1, -1, -1, -1, 10, -1, 12, 13, - -1, -1, -1, -1, -1, -1, -1, -1, 22, -1, - -1, -1, -1, -1, -1, 29, -1, -1, -1, 33, - 34, -1, 36, -1, -1, -1, -1, -1, -1, 43, - -1, -1, -1, 47, -1, -1, -1, -1, -1, -1, - -1, 55, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 65, 66, 67, 68, -1, 70, -1, -1, -1, - -1, 75, -1, -1, -1, -1, -1, 81, 82, 83, - 84, -1, -1, 87, -1, -1, -1, -1, -1, 93, - -1, -1, -1, -1, -1, -1, -1, -1, -1, 10, - -1, 12, 13, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 93, 94, 95, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 10, -1, 12, 13, -1, + -1, -1, -1, -1, -1, -1, -1, 22, -1, -1, + -1, -1, -1, -1, 29, -1, -1, -1, 33, 34, + -1, 36, -1, -1, -1, -1, -1, -1, 43, -1, + -1, -1, 47, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 65, 66, 67, 68, -1, 70, -1, -1, -1, -1, + 75, -1, -1, -1, -1, -1, 81, 82, 83, 84, + -1, -1, 87, -1, -1, -1, -1, -1, 93, 94, + 95, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 10, -1, 12, 13, -1, -1, -1, -1, -1, -1, + -1, -1, 22, -1, -1, -1, -1, -1, -1, 29, + -1, -1, -1, 33, 34, -1, 36, -1, -1, -1, + -1, -1, -1, 43, -1, -1, -1, 47, -1, -1, + -1, -1, -1, -1, -1, 55, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 65, 66, 67, 68, -1, + 70, -1, -1, -1, -1, 75, -1, -1, -1, -1, + -1, 81, 82, 83, 84, -1, -1, 87, -1, -1, + -1, -1, -1, 93, 94, 95, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 10, -1, 12, 13, -1, + -1, -1, -1, -1, -1, -1, -1, 22, -1, -1, + -1, -1, -1, -1, 29, -1, -1, -1, 33, 34, + -1, 36, -1, -1, -1, -1, -1, -1, 43, -1, + -1, -1, 47, -1, -1, -1, -1, -1, -1, -1, + 55, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 65, 66, 67, 68, -1, 70, -1, -1, -1, -1, + 75, -1, -1, -1, -1, -1, 81, 82, 83, 84, + -1, -1, 87, -1, -1, -1, -1, -1, 93, 94, + 95, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 11, 12, 13, -1, -1, -1, -1, -1, -1, -1, -1, 22, -1, -1, -1, -1, -1, -1, 29, -1, - -1, -1, 33, 34, -1, 36, -1, -1, -1, -1, - -1, -1, 43, -1, -1, -1, 47, -1, -1, -1, - -1, -1, -1, -1, 55, -1, -1, -1, -1, -1, + -1, -1, 33, 34, -1, 36, -1, -1, -1, 40, + -1, 42, 43, 44, -1, -1, 47, -1, -1, -1, + 51, -1, 53, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 65, 66, 67, 68, -1, 70, - -1, -1, -1, -1, 75, -1, -1, -1, -1, -1, - 81, 82, 83, 84, -1, -1, 87, -1, -1, -1, - -1, -1, 93, -1, -1, -1, -1, -1, -1, -1, - -1, -1, 11, 12, 13, -1, -1, -1, -1, -1, - -1, -1, -1, 22, -1, -1, -1, -1, -1, -1, - 29, -1, -1, -1, 33, 34, -1, 36, -1, -1, - -1, 40, -1, 42, 43, 44, -1, -1, 47, -1, - -1, -1, 51, -1, 53, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, 65, 66, 67, 68, - -1, 70, -1, 72, -1, 74, -1, 76, -1, -1, - -1, -1, 81, 82, 83, -1, -1, -1, 87, -1, - -1, -1, -1, -1, 93, -1, -1, -1, -1, -1, + -1, 72, -1, 74, -1, 76, -1, -1, -1, -1, + 81, 82, 83, -1, -1, -1, 87, -1, -1, -1, + -1, -1, 93, 94, 95, -1, -1, -1, -1, -1, -1, -1, -1, -1, 7, -1, -1, -1, 11, 12, 13, -1, -1, -1, -1, -1, -1, -1, -1, 22, -1, -1, -1, -1, -1, -1, 29, -1, -1, -1, @@ -866,44 +856,45 @@ const short QQmlJSGrammar::action_check [] = { -1, -1, 65, 66, 67, 68, -1, 70, -1, 72, -1, 74, -1, 76, -1, -1, -1, -1, 81, 82, 83, -1, -1, -1, 87, -1, -1, -1, -1, -1, - 93, -1, -1, -1, -1, -1, -1, -1, -1, -1, - 11, 12, 13, -1, -1, -1, -1, -1, -1, -1, - -1, 22, -1, -1, -1, -1, -1, -1, 29, -1, - -1, -1, 33, 34, -1, 36, -1, -1, -1, 40, - -1, 42, 43, 44, -1, -1, 47, -1, -1, -1, - 51, -1, 53, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, 65, 66, 67, 68, -1, 70, - -1, 72, -1, 74, 75, 76, -1, -1, -1, -1, - 81, 82, 83, -1, -1, -1, 87, -1, -1, -1, - -1, -1, 93, -1, -1, -1, -1, -1, -1, -1, - -1, -1, 8, -1, -1, 11, 12, 13, -1, -1, - -1, -1, -1, -1, -1, -1, 22, -1, -1, -1, - -1, -1, -1, 29, -1, -1, -1, 33, 34, -1, - 36, -1, -1, -1, 40, -1, 42, 43, 44, -1, - -1, 47, -1, -1, -1, 51, -1, 53, -1, -1, - 56, -1, -1, -1, -1, -1, -1, -1, -1, 65, - 66, 67, 68, -1, 70, -1, 72, -1, 74, -1, - 76, -1, -1, -1, -1, 81, 82, 83, -1, -1, - -1, 87, -1, -1, -1, -1, -1, 93, -1, -1, - -1, -1, -1, -1, -1, -1, -1, 8, -1, -1, - 11, 12, 13, -1, -1, -1, -1, -1, -1, -1, - -1, 22, -1, -1, -1, -1, -1, -1, 29, -1, - -1, -1, 33, 34, -1, 36, -1, -1, -1, 40, - -1, 42, 43, 44, -1, -1, 47, -1, -1, -1, - 51, -1, 53, -1, -1, 56, -1, -1, -1, -1, - -1, -1, -1, -1, 65, 66, 67, 68, -1, 70, - -1, 72, -1, 74, -1, 76, -1, -1, -1, -1, - 81, 82, 83, -1, -1, -1, 87, -1, -1, -1, - -1, -1, 93, -1, -1, -1, -1, -1, -1, -1, - -1, -1, 8, -1, -1, 11, 12, 13, -1, -1, - -1, -1, -1, -1, -1, -1, 22, -1, -1, -1, - -1, -1, -1, 29, -1, -1, -1, 33, 34, -1, - 36, -1, -1, -1, 40, -1, 42, 43, 44, -1, - -1, 47, -1, -1, -1, 51, -1, 53, -1, -1, - 56, -1, -1, -1, -1, -1, -1, -1, -1, 65, - 66, 67, 68, -1, 70, -1, 72, -1, 74, -1, - 76, -1, -1, -1, -1, 81, 82, 83, -1, -1, - -1, 87, -1, -1, -1, -1, -1, 93, -1, -1, + 93, 94, 95, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 11, 12, 13, -1, -1, -1, -1, -1, + -1, -1, -1, 22, -1, -1, -1, -1, -1, -1, + 29, -1, -1, -1, 33, 34, -1, 36, -1, -1, + -1, 40, -1, 42, 43, 44, -1, -1, 47, -1, + -1, -1, 51, -1, 53, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 65, 66, 67, 68, + -1, 70, -1, 72, -1, 74, 75, 76, -1, -1, + -1, -1, 81, 82, 83, -1, -1, -1, 87, -1, + -1, -1, -1, -1, 93, 94, 95, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 8, -1, -1, 11, + 12, 13, -1, -1, -1, -1, -1, -1, -1, -1, + 22, -1, -1, -1, -1, -1, -1, 29, -1, -1, + -1, 33, 34, -1, 36, -1, -1, -1, 40, -1, + 42, 43, 44, -1, -1, 47, -1, -1, -1, 51, + -1, 53, -1, -1, 56, -1, -1, -1, -1, -1, + -1, -1, -1, 65, 66, 67, 68, -1, 70, -1, + 72, -1, 74, -1, 76, -1, -1, -1, -1, 81, + 82, 83, -1, -1, -1, 87, -1, -1, -1, -1, + -1, 93, 94, 95, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 8, -1, -1, 11, 12, 13, -1, + -1, -1, -1, -1, -1, -1, -1, 22, -1, -1, + -1, -1, -1, -1, 29, -1, -1, -1, 33, 34, + -1, 36, -1, -1, -1, 40, -1, 42, 43, 44, + -1, -1, 47, -1, -1, -1, 51, -1, 53, -1, + -1, 56, -1, -1, -1, -1, -1, -1, -1, -1, + 65, 66, 67, 68, -1, 70, -1, 72, -1, 74, + -1, 76, -1, -1, -1, -1, 81, 82, 83, -1, + -1, -1, 87, -1, -1, -1, -1, -1, 93, 94, + 95, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 8, -1, -1, 11, 12, 13, -1, -1, -1, -1, + -1, -1, -1, -1, 22, -1, -1, -1, -1, -1, + -1, 29, -1, -1, -1, 33, 34, -1, 36, -1, + -1, -1, 40, -1, 42, 43, 44, -1, -1, 47, + -1, -1, -1, 51, -1, 53, -1, -1, 56, -1, + -1, -1, -1, -1, -1, -1, -1, 65, 66, 67, + 68, -1, 70, -1, 72, -1, 74, -1, 76, -1, + -1, -1, -1, 81, 82, 83, -1, -1, -1, 87, + -1, -1, -1, -1, -1, 93, 94, 95, -1, -1, -1, -1, -1, -1, -1, -1, -1, 8, -1, -1, 11, 12, 13, -1, -1, -1, -1, -1, -1, -1, -1, 22, -1, -1, -1, -1, -1, -1, 29, -1, @@ -913,16 +904,7 @@ const short QQmlJSGrammar::action_check [] = { -1, -1, -1, -1, 65, 66, 67, 68, -1, 70, -1, 72, -1, 74, -1, 76, -1, -1, -1, -1, 81, 82, 83, -1, -1, -1, 87, -1, -1, -1, - -1, -1, 93, -1, -1, -1, -1, -1, -1, -1, - -1, -1, 11, 12, 13, -1, -1, -1, -1, -1, - -1, -1, -1, 22, -1, -1, -1, -1, -1, -1, - 29, 30, -1, -1, 33, 34, -1, 36, -1, -1, - -1, 40, -1, 42, 43, 44, -1, -1, 47, -1, - -1, -1, 51, -1, 53, -1, -1, -1, -1, -1, - -1, -1, 61, -1, -1, -1, 65, 66, 67, 68, - 69, 70, -1, 72, 73, 74, -1, 76, -1, 78, - -1, -1, 81, 82, 83, -1, -1, -1, 87, -1, - -1, -1, -1, -1, 93, -1, -1, -1, -1, -1, + -1, -1, 93, 94, 95, -1, -1, -1, -1, -1, -1, -1, -1, -1, 11, 12, 13, -1, -1, -1, -1, -1, -1, -1, -1, 22, -1, -1, -1, -1, -1, -1, 29, 30, -1, -1, 33, 34, -1, 36, @@ -931,120 +913,149 @@ const short QQmlJSGrammar::action_check [] = { -1, -1, -1, -1, 61, -1, -1, -1, 65, 66, 67, 68, 69, 70, -1, 72, 73, 74, -1, 76, -1, 78, -1, -1, 81, 82, 83, -1, -1, -1, - 87, -1, -1, -1, -1, -1, 93, -1, -1, -1, - -1, -1, -1, -1, -1, -1, 4, 5, 6, -1, - -1, 9, 10, 11, -1, -1, 14, -1, 16, -1, - -1, -1, 20, 21, 22, -1, -1, -1, -1, -1, - -1, 29, 30, 31, 32, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, 43, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 59, -1, -1, -1, -1, -1, -1, 66, 67, - 68, 69, 70, 71, -1, 73, 74, 75, 76, 77, - 78, -1, -1, 81, 82, 83, 84, 85, 86, -1, - -1, -1, -1, -1, -1, 93, -1, -1, -1, -1, + 87, -1, -1, -1, -1, -1, 93, 94, 95, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 11, 12, + 13, -1, -1, -1, -1, -1, -1, -1, -1, 22, + -1, -1, -1, -1, -1, -1, 29, 30, -1, -1, + 33, 34, -1, 36, -1, -1, -1, 40, -1, 42, + 43, 44, -1, -1, 47, -1, -1, -1, 51, -1, + 53, -1, -1, -1, -1, -1, -1, -1, 61, -1, + -1, -1, 65, 66, 67, 68, 69, 70, -1, 72, + 73, 74, -1, 76, -1, 78, -1, -1, 81, 82, + 83, -1, -1, -1, 87, -1, -1, -1, -1, -1, + 93, 94, 95, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 4, 5, 6, -1, -1, 9, 10, 11, + -1, -1, 14, -1, 16, -1, -1, -1, 20, 21, + 22, -1, -1, -1, -1, -1, -1, 29, 30, 31, + 32, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 43, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 59, -1, -1, + -1, -1, -1, -1, 66, 67, 68, 69, 70, 71, + -1, 73, 74, 75, 76, 77, 78, -1, -1, 81, + 82, 83, 84, 85, 86, -1, -1, -1, -1, -1, + -1, 93, 94, 95, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 4, 5, 6, -1, -1, 9, 10, + 11, -1, -1, 14, -1, 16, -1, -1, -1, 20, + 21, 22, -1, -1, -1, -1, -1, -1, 29, 30, + 31, 32, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 43, -1, -1, -1, 47, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 59, -1, + -1, -1, -1, -1, 65, 66, 67, 68, 69, 70, + 71, -1, 73, 74, 75, 76, 77, 78, -1, -1, + 81, 82, 83, 84, 85, 86, -1, -1, -1, -1, + -1, -1, 93, 94, 95, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 4, 5, 6, -1, -1, 9, + 10, 11, -1, -1, 14, -1, 16, -1, -1, -1, + 20, 21, 22, -1, -1, -1, -1, -1, -1, 29, + 30, 31, 32, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 43, -1, -1, -1, 47, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 59, + -1, -1, -1, -1, -1, 65, 66, 67, 68, 69, + 70, 71, -1, 73, 74, 75, 76, 77, 78, -1, + -1, 81, 82, 83, 84, 85, 86, -1, -1, -1, + -1, -1, -1, 93, 94, 95, -1, -1, -1, -1, -1, -1, -1, -1, -1, 4, 5, 6, -1, -1, 9, 10, 11, -1, -1, 14, -1, 16, -1, -1, -1, 20, 21, 22, -1, -1, -1, -1, -1, -1, 29, 30, 31, 32, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 43, -1, -1, -1, 47, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - 59, -1, -1, -1, -1, -1, 65, 66, 67, -1, + -1, -1, -1, -1, -1, -1, 55, -1, -1, -1, + 59, -1, -1, -1, -1, -1, 65, 66, 67, 68, 69, 70, 71, -1, 73, 74, 75, 76, 77, 78, -1, -1, 81, 82, 83, 84, 85, 86, -1, -1, + -1, -1, -1, -1, 93, 94, 95, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 4, -1, -1, -1, + -1, 9, -1, 11, 12, 13, 14, -1, -1, -1, + -1, -1, -1, 21, 22, -1, -1, -1, -1, -1, + -1, 29, 30, -1, -1, 33, 34, -1, 36, -1, + -1, -1, 40, -1, 42, 43, 44, -1, -1, 47, + -1, -1, -1, 51, -1, 53, -1, -1, -1, -1, + -1, 59, -1, 61, -1, -1, -1, 65, 66, 67, + 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, + 78, -1, -1, 81, 82, 83, 84, 85, -1, 87, + -1, -1, -1, -1, -1, 93, 94, 95, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 4, -1, -1, + -1, -1, 9, -1, 11, 12, 13, 14, -1, -1, + -1, -1, -1, -1, 21, 22, -1, -1, -1, -1, + -1, -1, 29, 30, -1, -1, 33, 34, -1, 36, + -1, -1, -1, 40, -1, 42, 43, 44, -1, -1, + 47, -1, -1, -1, 51, -1, 53, -1, -1, -1, + -1, -1, 59, -1, 61, -1, -1, -1, 65, 66, + 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, + 77, 78, -1, -1, 81, 82, 83, 84, 85, -1, + 87, -1, -1, -1, -1, -1, 93, 94, 95, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 4, 5, + 6, -1, -1, 9, 10, 11, 12, 13, 14, -1, + 16, -1, -1, -1, 20, 21, 22, -1, -1, -1, + -1, -1, -1, 29, 30, 31, 32, 33, 34, -1, + 36, -1, -1, -1, 40, -1, 42, 43, 44, -1, + -1, 47, -1, -1, -1, 51, -1, 53, -1, -1, + -1, -1, -1, 59, -1, 61, -1, -1, -1, 65, + 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, + 76, 77, 78, -1, -1, 81, 82, 83, 84, 85, + 86, 87, -1, -1, -1, -1, -1, 93, 94, 95, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 4, + 5, 6, -1, -1, 9, 10, 11, 12, 13, 14, + -1, 16, -1, -1, -1, 20, 21, 22, -1, -1, + -1, -1, -1, -1, 29, 30, 31, 32, 33, 34, + -1, 36, -1, -1, -1, 40, -1, 42, 43, 44, + -1, -1, 47, -1, -1, -1, 51, -1, 53, -1, + 55, -1, -1, -1, 59, -1, 61, -1, -1, -1, + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, + 75, 76, 77, 78, -1, -1, 81, 82, 83, 84, + 85, 86, 87, -1, -1, -1, -1, -1, 93, 94, + 95, -1, -1, -1, -1, -1, -1, -1, -1, -1, + + 15, 39, 29, 3, 15, 15, 13, 15, 3, 15, + 29, 9, 15, 15, 3, 3, 39, 22, 19, 15, + 19, 3, 15, 15, 15, 11, 3, 74, 15, 19, + 15, 15, 29, 13, 15, 29, 102, 15, 3, 15, + 97, 3, 100, 15, -1, 15, 29, 22, 15, 3, + 3, 15, 3, 22, 15, 15, 22, 15, 15, 3, + 39, 3, 3, 3, 3, 39, 39, 22, 39, 3, + 15, 39, 15, 2, -1, 13, 39, 13, 2, 2, + 39, 13, 13, 39, 2, 15, 15, 39, 20, 3, + 3, 15, 15, 3, 15, 15, 2, 15, 41, -1, + 2, 51, 51, 51, 53, 51, 56, 53, 56, 15, + 48, 41, 48, 15, 51, 15, 51, 48, 51, 56, + 39, 56, 39, 56, 51, 51, 53, 53, 47, 51, + 47, 53, 4, 15, 2, 51, 15, 53, 15, 51, + 40, 15, 51, 15, 53, 4, 51, 15, 53, 15, + 51, 63, 53, 51, 51, 13, 15, 51, 16, 53, + 42, 75, 75, 51, 3, 44, 43, 65, 15, 43, + 67, 91, 13, 61, -1, 16, 42, 15, 51, 15, + 51, 54, 51, 54, 51, 106, 53, 56, 51, 13, + 53, 51, 16, 53, 2, 51, 43, 51, 35, 2, + 56, 15, 39, 51, 42, 59, 42, 15, 56, 51, + 2, 51, 15, 2, 56, -1, 56, 51, 51, 51, + 2, 51, 56, 15, 57, 57, 15, 57, 51, 4, + 51, 2, -1, 15, 57, 56, 75, 2, 51, 51, + 15, 53, 55, 2, 15, 51, 51, 2, 53, 55, + 15, 51, 51, 53, 53, 51, 15, 53, 51, 2, + 15, 51, 55, 51, 51, 15, 56, 2, 56, 56, + -1, 3, 15, 51, 64, 89, -1, 51, 56, 51, + 15, 68, 56, -1, 56, 73, 58, 51, 66, -1, + -1, 51, 56, -1, 44, 45, 56, 51, 58, 73, + 51, -1, 56, 51, 58, 56, 51, 51, 56, 73, + 51, 56, 56, 58, 13, 56, -1, -1, 62, 60, + -1, 20, 73, 13, 5, 73, 16, 5, 18, -1, + -1, -1, 13, 32, 33, 13, -1, -1, -1, 20, + -1, -1, 20, 75, -1, -1, -1, -1, -1, 39, + 85, 32, 33, -1, 32, 33, 22, 23, 24, 25, + 26, 27, 28, 21, 22, 23, 24, 25, 26, 27, + 28, 13, -1, -1, -1, -1, 2, -1, 20, 21, + 22, 23, 24, 25, 26, 27, 28, -1, -1, 15, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, 4, 5, 6, -1, -1, 9, - 10, 11, -1, -1, 14, -1, 16, -1, -1, -1, - 20, 21, 22, -1, -1, -1, -1, -1, -1, 29, - 30, 31, 32, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, 43, -1, -1, -1, 47, -1, -1, - -1, -1, -1, -1, -1, 55, -1, -1, -1, 59, - -1, -1, -1, -1, -1, 65, 66, 67, -1, 69, - 70, 71, -1, 73, 74, 75, 76, 77, 78, -1, - -1, 81, 82, 83, 84, 85, 86, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, 4, -1, -1, -1, -1, 9, -1, - 11, 12, 13, 14, -1, -1, -1, -1, -1, -1, - 21, 22, -1, -1, -1, -1, -1, -1, 29, 30, - -1, -1, 33, 34, -1, 36, -1, -1, -1, 40, - -1, 42, 43, 44, -1, -1, 47, -1, -1, -1, - 51, -1, 53, -1, -1, -1, -1, -1, 59, -1, - 61, -1, -1, -1, 65, 66, 67, 68, 69, 70, - 71, 72, 73, 74, 75, 76, 77, 78, -1, -1, - 81, 82, 83, 84, 85, -1, 87, -1, -1, -1, - -1, -1, 93, -1, -1, -1, -1, -1, -1, -1, - -1, -1, 4, -1, -1, -1, -1, 9, -1, 11, - 12, 13, 14, -1, -1, -1, -1, -1, -1, 21, - 22, -1, -1, -1, -1, -1, -1, 29, 30, -1, - -1, 33, 34, -1, 36, -1, -1, -1, 40, -1, - 42, 43, 44, -1, -1, 47, -1, -1, -1, 51, - -1, 53, -1, -1, -1, -1, -1, 59, -1, 61, - -1, -1, -1, 65, 66, 67, 68, 69, 70, 71, - 72, 73, 74, 75, 76, 77, 78, -1, -1, 81, - 82, 83, 84, 85, -1, 87, -1, -1, -1, -1, - -1, 93, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 4, 5, 6, -1, -1, 9, 10, 11, 12, - 13, 14, -1, 16, -1, -1, -1, 20, 21, 22, - -1, -1, -1, -1, -1, -1, 29, 30, 31, 32, - 33, 34, -1, 36, -1, -1, -1, 40, -1, 42, - 43, 44, -1, -1, 47, -1, -1, -1, 51, -1, - 53, -1, -1, -1, -1, -1, 59, -1, 61, -1, - -1, -1, 65, 66, 67, 68, 69, 70, 71, 72, - 73, 74, 75, 76, 77, 78, -1, -1, 81, 82, - 83, 84, 85, 86, 87, -1, -1, -1, -1, -1, - 93, -1, -1, -1, -1, -1, -1, -1, -1, -1, - 4, 5, 6, -1, -1, 9, 10, 11, 12, 13, - 14, -1, 16, -1, -1, -1, 20, 21, 22, -1, - -1, -1, -1, -1, -1, 29, 30, 31, 32, 33, - 34, -1, 36, -1, -1, -1, 40, -1, 42, 43, - 44, -1, -1, 47, -1, -1, -1, 51, -1, 53, - -1, 55, -1, -1, -1, 59, -1, 61, -1, -1, - -1, 65, 66, 67, 68, 69, 70, 71, 72, 73, - 74, 75, 76, 77, 78, -1, -1, 81, 82, 83, - 84, 85, 86, 87, -1, -1, -1, -1, -1, 93, - -1, -1, -1, -1, -1, -1, -1, -1, -1, - - 15, 3, 13, 29, 29, 15, 15, 4, 2, 15, - 105, 9, 15, 2, 15, 3, 29, 22, 3, 15, - 15, 2, 39, 15, 3, 15, 19, 3, 2, 15, - 3, 15, 19, 11, 71, 15, 19, 2, 13, 39, - 97, 2, 2, 15, 15, 3, 15, 29, 39, 15, - 94, 101, 29, 3, 2, 2, 15, 99, 15, 22, - 3, 3, 15, 15, 2, 2, 15, 22, 2, 39, - 2, 39, 39, 3, 3, 39, 22, 39, 2, 2, - 15, 3, 22, 39, 4, 39, 4, 3, 2, 40, - 3, 3, 2, 2, 39, 39, 3, 3, 13, 2, - 2, -1, -1, 39, -1, -1, -1, 48, 44, 48, - -1, 50, 53, -1, 3, 48, 13, 48, 13, 50, - 53, 48, 48, 48, 50, 15, 53, 48, 53, 48, - 45, 50, 13, 13, 3, 16, 16, 48, 15, 50, - 48, 62, 50, 48, 48, 50, 50, 48, 45, 48, - 45, 50, 48, 39, 48, 48, 48, 53, 44, 53, - 48, 53, 50, 64, 48, 48, 48, 60, 15, 48, - 54, 54, 54, 13, 35, -1, 16, 56, 39, 82, - 82, 48, 48, 72, 48, 48, 53, 53, 52, 48, - 53, 48, 48, 52, 41, 48, 53, 2, 54, 52, - 48, 13, 15, 72, 48, 53, 3, 51, 20, 15, - 100, 88, 48, 48, 50, 50, 48, 48, 50, 50, - 48, 48, 50, 48, 48, 50, 48, 51, 50, 48, - 48, 58, 50, 48, 53, 41, 42, 48, 53, -1, - 48, -1, 53, -1, 63, 53, 61, 48, 48, 48, - 48, -1, 53, 53, 53, 53, 48, 55, 57, 70, - -1, 53, 70, 48, -1, 65, -1, 59, 53, 70, - 55, 48, 48, 86, -1, 72, 53, 53, 55, 55, - 13, 48, 48, 16, 5, 18, 53, 53, 13, -1, - 5, -1, 13, -1, -1, 20, -1, -1, 13, 20, - -1, -1, 3, 70, 70, 20, 39, 32, 33, -1, - -1, 32, 33, -1, -1, -1, -1, 32, 33, 22, - 23, 24, 25, 26, 27, 28, 13, -1, -1, -1, - -1, -1, -1, 20, 21, 22, 23, 24, 25, 26, - 27, 28, 13, -1, -1, -1, -1, -1, -1, 20, - 21, 22, 23, 24, 25, 26, 27, 28, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 72, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, 21, 22, 23, 24, - 25, 26, 27, 28, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 85, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 13, -1, -1, -1, -1, -1, -1, 20, + 21, 22, 23, 24, 25, 26, 27, 28, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, @@ -1052,6 +1063,6 @@ const short QQmlJSGrammar::action_check [] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1}; + -1, -1, -1, -1, -1, -1, -1}; QT_END_NAMESPACE diff --git a/src/qml/qml/parser/qqmljsgrammar_p.h b/src/qml/qml/parser/qqmljsgrammar_p.h index 8924b2a76d..fde4e1b1eb 100644 --- a/src/qml/qml/parser/qqmljsgrammar_p.h +++ b/src/qml/qml/parser/qqmljsgrammar_p.h @@ -63,8 +63,8 @@ class QQmlJSGrammar public: enum VariousConstants { EOF_SYMBOL = 0, - REDUCE_HERE = 102, - SHIFT_THERE = 101, + REDUCE_HERE = 104, + SHIFT_THERE = 103, T_AND = 1, T_AND_AND = 2, T_AND_EQ = 3, @@ -90,18 +90,19 @@ public: T_EQ = 17, T_EQ_EQ = 18, T_EQ_EQ_EQ = 19, - T_ERROR = 94, + T_ERROR = 96, T_FALSE = 83, - T_FEED_JS_EXPRESSION = 98, - T_FEED_JS_PROGRAM = 100, - T_FEED_JS_SOURCE_ELEMENT = 99, - T_FEED_JS_STATEMENT = 97, - T_FEED_UI_OBJECT_MEMBER = 96, - T_FEED_UI_PROGRAM = 95, + T_FEED_JS_EXPRESSION = 100, + T_FEED_JS_PROGRAM = 102, + T_FEED_JS_SOURCE_ELEMENT = 101, + T_FEED_JS_STATEMENT = 99, + T_FEED_UI_OBJECT_MEMBER = 98, + T_FEED_UI_PROGRAM = 97, T_FINALLY = 20, T_FOR = 21, T_FUNCTION = 22, T_GE = 23, + T_GET = 94, T_GT = 24, T_GT_GT = 25, T_GT_GT_EQ = 26, @@ -148,6 +149,7 @@ public: T_RETURN = 59, T_RPAREN = 60, T_SEMICOLON = 61, + T_SET = 95, T_SIGNAL = 67, T_STAR = 63, T_STAR_EQ = 64, @@ -166,15 +168,15 @@ public: T_XOR = 79, T_XOR_EQ = 80, - ACCEPT_STATE = 645, - RULE_COUNT = 350, - STATE_COUNT = 646, - TERMINAL_COUNT = 103, - NON_TERMINAL_COUNT = 107, + ACCEPT_STATE = 655, + RULE_COUNT = 351, + STATE_COUNT = 656, + TERMINAL_COUNT = 105, + NON_TERMINAL_COUNT = 108, - GOTO_INDEX_OFFSET = 646, - GOTO_INFO_OFFSET = 3019, - GOTO_CHECK_OFFSET = 3019 + GOTO_INDEX_OFFSET = 656, + GOTO_INFO_OFFSET = 2970, + GOTO_CHECK_OFFSET = 2970 }; static const char *const spell []; diff --git a/src/qml/qml/parser/qqmljskeywords_p.h b/src/qml/qml/parser/qqmljskeywords_p.h index 5d28f88829..d06c996099 100644 --- a/src/qml/qml/parser/qqmljskeywords_p.h +++ b/src/qml/qml/parser/qqmljskeywords_p.h @@ -53,6 +53,12 @@ // We mean it. // +#include "qqmljslexer_p.h" + +QT_QML_BEGIN_NAMESPACE + +namespace QQmlJS { + static inline int classify2(const QChar *s, bool qmlMode) { if (s[0].unicode() == 'a') { if (s[1].unicode() == 's') { @@ -88,6 +94,13 @@ static inline int classify3(const QChar *s, bool qmlMode) { } } } + else if (s[0].unicode() == 'g') { + if (s[1].unicode() == 'e') { + if (s[2].unicode() == 't') { + return Lexer::T_GET; + } + } + } else if (s[0].unicode() == 'i') { if (s[1].unicode() == 'n') { if (s[2].unicode() == 't') { @@ -102,6 +115,13 @@ static inline int classify3(const QChar *s, bool qmlMode) { } } } + else if (s[0].unicode() == 's') { + if (s[1].unicode() == 'e') { + if (s[2].unicode() == 't') { + return Lexer::T_SET; + } + } + } else if (s[0].unicode() == 't') { if (s[1].unicode() == 'r') { if (s[2].unicode() == 'y') { @@ -857,4 +877,8 @@ int Lexer::classify(const QChar *s, int n, bool qmlMode) { } // switch } +} // namespace QQmlJS + +QT_QML_END_NAMESPACE + #endif // QQMLJSKEYWORDS_P_H diff --git a/src/qml/qml/parser/qqmljslexer.cpp b/src/qml/qml/parser/qqmljslexer.cpp index c3ddd0a9e8..79fb971dde 100644 --- a/src/qml/qml/parser/qqmljslexer.cpp +++ b/src/qml/qml/parser/qqmljslexer.cpp @@ -42,6 +42,7 @@ #include "qqmljslexer_p.h" #include "qqmljsengine_p.h" #include "qqmljsmemorypool_p.h" +#include "qqmljskeywords_p.h" #include #include @@ -1256,5 +1257,3 @@ bool Lexer::scanDirectives(Directives *directives) return true; } - -#include "qqmljskeywords_p.h" diff --git a/src/qml/qml/parser/qqmljsparser.cpp b/src/qml/qml/parser/qqmljsparser.cpp index 65aaa4331a..b2cea27c11 100644 --- a/src/qml/qml/parser/qqmljsparser.cpp +++ b/src/qml/qml/parser/qqmljsparser.cpp @@ -537,49 +537,49 @@ case 65: { sym(1).Node = new (pool) AST::UiSourceElement(sym(1).Node); } break; -case 71: { +case 73: { AST::ThisExpression *node = new (pool) AST::ThisExpression(); node->thisToken = loc(1); sym(1).Node = node; } break; -case 72: { +case 74: { AST::IdentifierExpression *node = new (pool) AST::IdentifierExpression(stringRef(1)); node->identifierToken = loc(1); sym(1).Node = node; } break; -case 73: { +case 75: { AST::NullExpression *node = new (pool) AST::NullExpression(); node->nullToken = loc(1); sym(1).Node = node; } break; -case 74: { +case 76: { AST::TrueLiteral *node = new (pool) AST::TrueLiteral(); node->trueToken = loc(1); sym(1).Node = node; } break; -case 75: { +case 77: { AST::FalseLiteral *node = new (pool) AST::FalseLiteral(); node->falseToken = loc(1); sym(1).Node = node; } break; -case 76: { +case 78: { AST::NumericLiteral *node = new (pool) AST::NumericLiteral(sym(1).dval); node->literalToken = loc(1); sym(1).Node = node; } break; -case 77: -case 78: { +case 79: +case 80: { AST::StringLiteral *node = new (pool) AST::StringLiteral(stringRef(1)); node->literalToken = loc(1); sym(1).Node = node; } break; -case 79: { +case 81: { bool rx = lexer->scanRegExp(Lexer::NoPrefix); if (!rx) { diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, location(lexer), lexer->errorMessage())); @@ -595,7 +595,7 @@ case 79: { sym(1).Node = node; } break; -case 80: { +case 82: { bool rx = lexer->scanRegExp(Lexer::EqualPrefix); if (!rx) { diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, location(lexer), lexer->errorMessage())); @@ -611,28 +611,28 @@ case 80: { sym(1).Node = node; } break; -case 81: { +case 83: { AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral((AST::Elision *) 0); node->lbracketToken = loc(1); node->rbracketToken = loc(2); sym(1).Node = node; } break; -case 82: { +case 84: { AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral(sym(2).Elision->finish()); node->lbracketToken = loc(1); node->rbracketToken = loc(3); sym(1).Node = node; } break; -case 83: { +case 85: { AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral(sym(2).ElementList->finish ()); node->lbracketToken = loc(1); node->rbracketToken = loc(3); sym(1).Node = node; } break; -case 84: { +case 86: { AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral(sym(2).ElementList->finish (), (AST::Elision *) 0); node->lbracketToken = loc(1); @@ -641,7 +641,7 @@ case 84: { sym(1).Node = node; } break; -case 85: { +case 87: { AST::ArrayLiteral *node = new (pool) AST::ArrayLiteral(sym(2).ElementList->finish (), sym(4).Elision->finish()); node->lbracketToken = loc(1); @@ -650,11 +650,11 @@ case 85: { sym(1).Node = node; } break; -case 86: { +case 88: { AST::ObjectLiteral *node = 0; if (sym(2).Node) node = new (pool) AST::ObjectLiteral( - sym(2).PropertyNameAndValueList->finish ()); + sym(2).PropertyAssignmentList->finish ()); else node = new (pool) AST::ObjectLiteral(); node->lbraceToken = loc(1); @@ -662,22 +662,22 @@ case 86: { sym(1).Node = node; } break; -case 87: { +case 89: { AST::ObjectLiteral *node = new (pool) AST::ObjectLiteral( - sym(2).PropertyNameAndValueList->finish ()); + sym(2).PropertyAssignmentList->finish ()); node->lbraceToken = loc(1); node->rbraceToken = loc(4); sym(1).Node = node; } break; -case 88: { +case 90: { AST::NestedExpression *node = new (pool) AST::NestedExpression(sym(2).Expression); node->lparenToken = loc(1); node->rparenToken = loc(3); sym(1).Node = node; } break; -case 89: { +case 91: { if (AST::ArrayMemberExpression *mem = AST::cast(sym(1).Expression)) { diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Warning, mem->lbracketToken, QLatin1String("Ignored annotation"))); @@ -697,100 +697,119 @@ case 89: { } } break; -case 90: { +case 92: { sym(1).Node = new (pool) AST::ElementList((AST::Elision *) 0, sym(1).Expression); } break; -case 91: { +case 93: { sym(1).Node = new (pool) AST::ElementList(sym(1).Elision->finish(), sym(2).Expression); } break; -case 92: { +case 94: { AST::ElementList *node = new (pool) AST::ElementList(sym(1).ElementList, (AST::Elision *) 0, sym(3).Expression); node->commaToken = loc(2); sym(1).Node = node; } break; -case 93: { +case 95: { AST::ElementList *node = new (pool) AST::ElementList(sym(1).ElementList, sym(3).Elision->finish(), sym(4).Expression); node->commaToken = loc(2); sym(1).Node = node; } break; -case 94: { +case 96: { AST::Elision *node = new (pool) AST::Elision(); node->commaToken = loc(1); sym(1).Node = node; } break; -case 95: { +case 97: { AST::Elision *node = new (pool) AST::Elision(sym(1).Elision); node->commaToken = loc(2); sym(1).Node = node; } break; -case 96: { - AST::PropertyNameAndValueList *node = new (pool) AST::PropertyNameAndValueList( +case 98: { + AST::PropertyNameAndValue *node = new (pool) AST::PropertyNameAndValue( sym(1).PropertyName, sym(3).Expression); node->colonToken = loc(2); sym(1).Node = node; } break; -case 97: { - AST::PropertyNameAndValueList *node = new (pool) AST::PropertyNameAndValueList( - sym(1).PropertyNameAndValueList, sym(3).PropertyName, sym(5).Expression); - node->commaToken = loc(2); - node->colonToken = loc(4); +case 99: { + AST::PropertyGetterSetter *node = new (pool) AST::PropertyGetterSetter( + sym(2).PropertyName, sym(6).FunctionBody); + node->getSetToken = loc(1); + node->lparenToken = loc(3); + node->rparenToken = loc(4); + node->lbraceToken = loc(5); + node->rbraceToken = loc(7); sym(1).Node = node; } break; -case 98: { - AST::IdentifierPropertyName *node = new (pool) AST::IdentifierPropertyName(stringRef(1)); - node->propertyNameToken = loc(1); +case 100: { + AST::PropertyGetterSetter *node = new (pool) AST::PropertyGetterSetter( + sym(2).PropertyName, sym(4).FormalParameterList, sym(7).FunctionBody); + node->getSetToken = loc(1); + node->lparenToken = loc(3); + node->rparenToken = loc(5); + node->lbraceToken = loc(6); + node->rbraceToken = loc(8); sym(1).Node = node; } break; -case 99: -case 100: { + +case 101: { + sym(1).Node = new (pool) AST::PropertyAssignmentList(sym(1).PropertyAssignment); +} break; + +case 102: { + AST::PropertyAssignmentList *node = new (pool) AST::PropertyAssignmentList( + sym(1).PropertyAssignmentList, sym(3).PropertyAssignment); + node->commaToken = loc(2); + sym(1).Node = node; +} break; + +case 103: { AST::IdentifierPropertyName *node = new (pool) AST::IdentifierPropertyName(stringRef(1)); node->propertyNameToken = loc(1); sym(1).Node = node; } break; -case 101: { +case 104: { AST::StringLiteralPropertyName *node = new (pool) AST::StringLiteralPropertyName(stringRef(1)); node->propertyNameToken = loc(1); sym(1).Node = node; } break; -case 102: { +case 105: { AST::NumericLiteralPropertyName *node = new (pool) AST::NumericLiteralPropertyName(sym(1).dval); node->propertyNameToken = loc(1); sym(1).Node = node; } break; -case 103: { +case 106: { AST::IdentifierPropertyName *node = new (pool) AST::IdentifierPropertyName(stringRef(1)); node->propertyNameToken = loc(1); sym(1).Node = node; } break; -case 139: { +case 142: { AST::ArrayMemberExpression *node = new (pool) AST::ArrayMemberExpression(sym(1).Expression, sym(3).Expression); node->lbracketToken = loc(2); node->rbracketToken = loc(4); sym(1).Node = node; } break; -case 140: { +case 143: { AST::FieldMemberExpression *node = new (pool) AST::FieldMemberExpression(sym(1).Expression, stringRef(3)); node->dotToken = loc(2); node->identifierToken = loc(3); sym(1).Node = node; } break; -case 141: { +case 144: { AST::NewMemberExpression *node = new (pool) AST::NewMemberExpression(sym(2).Expression, sym(4).ArgumentList); node->newToken = loc(1); node->lparenToken = loc(3); @@ -798,384 +817,384 @@ case 141: { sym(1).Node = node; } break; -case 143: { +case 146: { AST::NewExpression *node = new (pool) AST::NewExpression(sym(2).Expression); node->newToken = loc(1); sym(1).Node = node; } break; -case 144: { +case 147: { AST::CallExpression *node = new (pool) AST::CallExpression(sym(1).Expression, sym(3).ArgumentList); node->lparenToken = loc(2); node->rparenToken = loc(4); sym(1).Node = node; } break; -case 145: { +case 148: { AST::CallExpression *node = new (pool) AST::CallExpression(sym(1).Expression, sym(3).ArgumentList); node->lparenToken = loc(2); node->rparenToken = loc(4); sym(1).Node = node; } break; -case 146: { +case 149: { AST::ArrayMemberExpression *node = new (pool) AST::ArrayMemberExpression(sym(1).Expression, sym(3).Expression); node->lbracketToken = loc(2); node->rbracketToken = loc(4); sym(1).Node = node; } break; -case 147: { +case 150: { AST::FieldMemberExpression *node = new (pool) AST::FieldMemberExpression(sym(1).Expression, stringRef(3)); node->dotToken = loc(2); node->identifierToken = loc(3); sym(1).Node = node; } break; -case 148: { +case 151: { sym(1).Node = 0; } break; -case 149: { +case 152: { sym(1).Node = sym(1).ArgumentList->finish(); } break; -case 150: { +case 153: { sym(1).Node = new (pool) AST::ArgumentList(sym(1).Expression); } break; -case 151: { +case 154: { AST::ArgumentList *node = new (pool) AST::ArgumentList(sym(1).ArgumentList, sym(3).Expression); node->commaToken = loc(2); sym(1).Node = node; } break; -case 155: { +case 158: { AST::PostIncrementExpression *node = new (pool) AST::PostIncrementExpression(sym(1).Expression); node->incrementToken = loc(2); sym(1).Node = node; } break; -case 156: { +case 159: { AST::PostDecrementExpression *node = new (pool) AST::PostDecrementExpression(sym(1).Expression); node->decrementToken = loc(2); sym(1).Node = node; } break; -case 158: { +case 161: { AST::DeleteExpression *node = new (pool) AST::DeleteExpression(sym(2).Expression); node->deleteToken = loc(1); sym(1).Node = node; } break; -case 159: { +case 162: { AST::VoidExpression *node = new (pool) AST::VoidExpression(sym(2).Expression); node->voidToken = loc(1); sym(1).Node = node; } break; -case 160: { +case 163: { AST::TypeOfExpression *node = new (pool) AST::TypeOfExpression(sym(2).Expression); node->typeofToken = loc(1); sym(1).Node = node; } break; -case 161: { +case 164: { AST::PreIncrementExpression *node = new (pool) AST::PreIncrementExpression(sym(2).Expression); node->incrementToken = loc(1); sym(1).Node = node; } break; -case 162: { +case 165: { AST::PreDecrementExpression *node = new (pool) AST::PreDecrementExpression(sym(2).Expression); node->decrementToken = loc(1); sym(1).Node = node; } break; -case 163: { +case 166: { AST::UnaryPlusExpression *node = new (pool) AST::UnaryPlusExpression(sym(2).Expression); node->plusToken = loc(1); sym(1).Node = node; } break; -case 164: { +case 167: { AST::UnaryMinusExpression *node = new (pool) AST::UnaryMinusExpression(sym(2).Expression); node->minusToken = loc(1); sym(1).Node = node; } break; -case 165: { +case 168: { AST::TildeExpression *node = new (pool) AST::TildeExpression(sym(2).Expression); node->tildeToken = loc(1); sym(1).Node = node; } break; -case 166: { +case 169: { AST::NotExpression *node = new (pool) AST::NotExpression(sym(2).Expression); node->notToken = loc(1); sym(1).Node = node; } break; -case 168: { +case 171: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Mul, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 169: { +case 172: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Div, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 170: { +case 173: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Mod, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 172: { +case 175: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Add, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 173: { +case 176: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Sub, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 175: { +case 178: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::LShift, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 176: { +case 179: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::RShift, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 177: { +case 180: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::URShift, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 179: { +case 182: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Lt, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 180: { +case 183: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Gt, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 181: { +case 184: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Le, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 182: { +case 185: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Ge, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 183: { +case 186: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::InstanceOf, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 184: { +case 187: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::In, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 186: { +case 189: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Lt, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 187: { +case 190: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Gt, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 188: { +case 191: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Le, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 189: { +case 192: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Ge, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 190: { +case 193: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::InstanceOf, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 192: { +case 195: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Equal, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 193: { +case 196: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::NotEqual, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 194: { +case 197: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::StrictEqual, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 195: { +case 198: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::StrictNotEqual, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 197: { +case 200: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Equal, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 198: { +case 201: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::NotEqual, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 199: { +case 202: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::StrictEqual, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 200: { +case 203: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::StrictNotEqual, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 202: { +case 205: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::BitAnd, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 204: { +case 207: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::BitAnd, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 206: { +case 209: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::BitXor, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 208: { +case 211: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::BitXor, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 210: { +case 213: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::BitOr, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 212: { +case 215: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::BitOr, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 214: { +case 217: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::And, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 216: { +case 219: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::And, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 218: { +case 221: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Or, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 220: { +case 223: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Or, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 222: { +case 225: { AST::ConditionalExpression *node = new (pool) AST::ConditionalExpression(sym(1).Expression, sym(3).Expression, sym(5).Expression); node->questionToken = loc(2); @@ -1183,7 +1202,7 @@ case 222: { sym(1).Node = node; } break; -case 224: { +case 227: { AST::ConditionalExpression *node = new (pool) AST::ConditionalExpression(sym(1).Expression, sym(3).Expression, sym(5).Expression); node->questionToken = loc(2); @@ -1191,112 +1210,112 @@ case 224: { sym(1).Node = node; } break; -case 226: { +case 229: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, sym(2).ival, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 228: { +case 231: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, sym(2).ival, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 229: { +case 232: { sym(1).ival = QSOperator::Assign; } break; -case 230: { +case 233: { sym(1).ival = QSOperator::InplaceMul; } break; -case 231: { +case 234: { sym(1).ival = QSOperator::InplaceDiv; } break; -case 232: { +case 235: { sym(1).ival = QSOperator::InplaceMod; } break; -case 233: { +case 236: { sym(1).ival = QSOperator::InplaceAdd; } break; -case 234: { +case 237: { sym(1).ival = QSOperator::InplaceSub; } break; -case 235: { +case 238: { sym(1).ival = QSOperator::InplaceLeftShift; } break; -case 236: { +case 239: { sym(1).ival = QSOperator::InplaceRightShift; } break; -case 237: { +case 240: { sym(1).ival = QSOperator::InplaceURightShift; } break; -case 238: { +case 241: { sym(1).ival = QSOperator::InplaceAnd; } break; -case 239: { +case 242: { sym(1).ival = QSOperator::InplaceXor; } break; -case 240: { +case 243: { sym(1).ival = QSOperator::InplaceOr; } break; -case 242: { +case 245: { AST::Expression *node = new (pool) AST::Expression(sym(1).Expression, sym(3).Expression); node->commaToken = loc(2); sym(1).Node = node; } break; -case 243: { +case 246: { sym(1).Node = 0; } break; -case 246: { +case 249: { AST::Expression *node = new (pool) AST::Expression(sym(1).Expression, sym(3).Expression); node->commaToken = loc(2); sym(1).Node = node; } break; -case 247: { +case 250: { sym(1).Node = 0; } break; -case 264: { +case 267: { AST::Block *node = new (pool) AST::Block(sym(2).StatementList); node->lbraceToken = loc(1); node->rbraceToken = loc(3); sym(1).Node = node; } break; -case 265: { +case 268: { sym(1).Node = new (pool) AST::StatementList(sym(1).Statement); } break; -case 266: { +case 269: { sym(1).Node = new (pool) AST::StatementList(sym(1).StatementList, sym(2).Statement); } break; -case 267: { +case 270: { sym(1).Node = 0; } break; -case 268: { +case 271: { sym(1).Node = sym(1).StatementList->finish (); } break; -case 270: { +case 273: { AST::VariableStatement *node = new (pool) AST::VariableStatement( sym(2).VariableDeclarationList->finish (/*readOnly=*/sym(1).ival == T_CONST)); node->declarationKindToken = loc(1); @@ -1304,76 +1323,76 @@ case 270: { sym(1).Node = node; } break; -case 271: { +case 274: { sym(1).ival = T_CONST; } break; -case 272: { +case 275: { sym(1).ival = T_VAR; } break; -case 273: { +case 276: { sym(1).Node = new (pool) AST::VariableDeclarationList(sym(1).VariableDeclaration); } break; -case 274: { +case 277: { AST::VariableDeclarationList *node = new (pool) AST::VariableDeclarationList( sym(1).VariableDeclarationList, sym(3).VariableDeclaration); node->commaToken = loc(2); sym(1).Node = node; } break; -case 275: { +case 278: { sym(1).Node = new (pool) AST::VariableDeclarationList(sym(1).VariableDeclaration); } break; -case 276: { +case 279: { sym(1).Node = new (pool) AST::VariableDeclarationList(sym(1).VariableDeclarationList, sym(3).VariableDeclaration); } break; -case 277: { +case 280: { AST::VariableDeclaration *node = new (pool) AST::VariableDeclaration(stringRef(1), sym(2).Expression); node->identifierToken = loc(1); sym(1).Node = node; } break; -case 278: { +case 281: { AST::VariableDeclaration *node = new (pool) AST::VariableDeclaration(stringRef(1), sym(2).Expression); node->identifierToken = loc(1); sym(1).Node = node; } break; -case 279: { +case 282: { // ### TODO: AST for initializer sym(1) = sym(2); } break; -case 280: { +case 283: { sym(1).Node = 0; } break; -case 282: { +case 285: { // ### TODO: AST for initializer sym(1) = sym(2); } break; -case 283: { +case 286: { sym(1).Node = 0; } break; -case 285: { +case 288: { AST::EmptyStatement *node = new (pool) AST::EmptyStatement(); node->semicolonToken = loc(1); sym(1).Node = node; } break; -case 287: { +case 290: { AST::ExpressionStatement *node = new (pool) AST::ExpressionStatement(sym(1).Expression); node->semicolonToken = loc(2); sym(1).Node = node; } break; -case 288: { +case 291: { AST::IfStatement *node = new (pool) AST::IfStatement(sym(3).Expression, sym(5).Statement, sym(7).Statement); node->ifToken = loc(1); node->lparenToken = loc(2); @@ -1382,7 +1401,7 @@ case 288: { sym(1).Node = node; } break; -case 289: { +case 292: { AST::IfStatement *node = new (pool) AST::IfStatement(sym(3).Expression, sym(5).Statement); node->ifToken = loc(1); node->lparenToken = loc(2); @@ -1390,7 +1409,7 @@ case 289: { sym(1).Node = node; } break; -case 292: { +case 295: { AST::DoWhileStatement *node = new (pool) AST::DoWhileStatement(sym(2).Statement, sym(5).Expression); node->doToken = loc(1); node->whileToken = loc(3); @@ -1400,7 +1419,7 @@ case 292: { sym(1).Node = node; } break; -case 293: { +case 296: { AST::WhileStatement *node = new (pool) AST::WhileStatement(sym(3).Expression, sym(5).Statement); node->whileToken = loc(1); node->lparenToken = loc(2); @@ -1408,7 +1427,7 @@ case 293: { sym(1).Node = node; } break; -case 294: { +case 297: { AST::ForStatement *node = new (pool) AST::ForStatement(sym(3).Expression, sym(5).Expression, sym(7).Expression, sym(9).Statement); node->forToken = loc(1); @@ -1419,7 +1438,7 @@ case 294: { sym(1).Node = node; } break; -case 295: { +case 298: { AST::LocalForStatement *node = new (pool) AST::LocalForStatement( sym(4).VariableDeclarationList->finish (/*readOnly=*/false), sym(6).Expression, sym(8).Expression, sym(10).Statement); @@ -1432,7 +1451,7 @@ case 295: { sym(1).Node = node; } break; -case 296: { +case 299: { AST:: ForEachStatement *node = new (pool) AST::ForEachStatement(sym(3).Expression, sym(5).Expression, sym(7).Statement); node->forToken = loc(1); @@ -1442,7 +1461,7 @@ case 296: { sym(1).Node = node; } break; -case 297: { +case 300: { AST::LocalForEachStatement *node = new (pool) AST::LocalForEachStatement( sym(4).VariableDeclaration, sym(6).Expression, sym(8).Statement); node->forToken = loc(1); @@ -1453,14 +1472,14 @@ case 297: { sym(1).Node = node; } break; -case 299: { +case 302: { AST::ContinueStatement *node = new (pool) AST::ContinueStatement(); node->continueToken = loc(1); node->semicolonToken = loc(2); sym(1).Node = node; } break; -case 301: { +case 304: { AST::ContinueStatement *node = new (pool) AST::ContinueStatement(stringRef(2)); node->continueToken = loc(1); node->identifierToken = loc(2); @@ -1468,14 +1487,14 @@ case 301: { sym(1).Node = node; } break; -case 303: { +case 306: { AST::BreakStatement *node = new (pool) AST::BreakStatement(QStringRef()); node->breakToken = loc(1); node->semicolonToken = loc(2); sym(1).Node = node; } break; -case 305: { +case 308: { AST::BreakStatement *node = new (pool) AST::BreakStatement(stringRef(2)); node->breakToken = loc(1); node->identifierToken = loc(2); @@ -1483,14 +1502,14 @@ case 305: { sym(1).Node = node; } break; -case 307: { +case 310: { AST::ReturnStatement *node = new (pool) AST::ReturnStatement(sym(2).Expression); node->returnToken = loc(1); node->semicolonToken = loc(3); sym(1).Node = node; } break; -case 308: { +case 311: { AST::WithStatement *node = new (pool) AST::WithStatement(sym(3).Expression, sym(5).Statement); node->withToken = loc(1); node->lparenToken = loc(2); @@ -1498,7 +1517,7 @@ case 308: { sym(1).Node = node; } break; -case 309: { +case 312: { AST::SwitchStatement *node = new (pool) AST::SwitchStatement(sym(3).Expression, sym(5).CaseBlock); node->switchToken = loc(1); node->lparenToken = loc(2); @@ -1506,90 +1525,83 @@ case 309: { sym(1).Node = node; } break; -case 310: { +case 313: { AST::CaseBlock *node = new (pool) AST::CaseBlock(sym(2).CaseClauses); node->lbraceToken = loc(1); node->rbraceToken = loc(3); sym(1).Node = node; } break; -case 311: { +case 314: { AST::CaseBlock *node = new (pool) AST::CaseBlock(sym(2).CaseClauses, sym(3).DefaultClause, sym(4).CaseClauses); node->lbraceToken = loc(1); node->rbraceToken = loc(5); sym(1).Node = node; } break; -case 312: { +case 315: { sym(1).Node = new (pool) AST::CaseClauses(sym(1).CaseClause); } break; -case 313: { +case 316: { sym(1).Node = new (pool) AST::CaseClauses(sym(1).CaseClauses, sym(2).CaseClause); } break; -case 314: { +case 317: { sym(1).Node = 0; } break; -case 315: { +case 318: { sym(1).Node = sym(1).CaseClauses->finish (); } break; -case 316: { +case 319: { AST::CaseClause *node = new (pool) AST::CaseClause(sym(2).Expression, sym(4).StatementList); node->caseToken = loc(1); node->colonToken = loc(3); sym(1).Node = node; } break; -case 317: { +case 320: { AST::DefaultClause *node = new (pool) AST::DefaultClause(sym(3).StatementList); node->defaultToken = loc(1); node->colonToken = loc(2); sym(1).Node = node; } break; -case 318: -case 319: { - AST::LabelledStatement *node = new (pool) AST::LabelledStatement(stringRef(1), sym(3).Statement); - node->identifierToken = loc(1); - node->colonToken = loc(2); - sym(1).Node = node; -} break; -case 320: { +case 321: { AST::LabelledStatement *node = new (pool) AST::LabelledStatement(stringRef(1), sym(3).Statement); node->identifierToken = loc(1); node->colonToken = loc(2); sym(1).Node = node; } break; -case 322: { +case 323: { AST::ThrowStatement *node = new (pool) AST::ThrowStatement(sym(2).Expression); node->throwToken = loc(1); node->semicolonToken = loc(3); sym(1).Node = node; } break; -case 323: { +case 324: { AST::TryStatement *node = new (pool) AST::TryStatement(sym(2).Statement, sym(3).Catch); node->tryToken = loc(1); sym(1).Node = node; } break; -case 324: { +case 325: { AST::TryStatement *node = new (pool) AST::TryStatement(sym(2).Statement, sym(3).Finally); node->tryToken = loc(1); sym(1).Node = node; } break; -case 325: { +case 326: { AST::TryStatement *node = new (pool) AST::TryStatement(sym(2).Statement, sym(3).Catch, sym(4).Finally); node->tryToken = loc(1); sym(1).Node = node; } break; -case 326: { +case 327: { AST::Catch *node = new (pool) AST::Catch(stringRef(3), sym(5).Block); node->catchToken = loc(1); node->lparenToken = loc(2); @@ -1598,20 +1610,20 @@ case 326: { sym(1).Node = node; } break; -case 327: { +case 328: { AST::Finally *node = new (pool) AST::Finally(sym(2).Block); node->finallyToken = loc(1); sym(1).Node = node; } break; -case 329: { +case 330: { AST::DebuggerStatement *node = new (pool) AST::DebuggerStatement(); node->debuggerToken = loc(1); node->semicolonToken = loc(2); sym(1).Node = node; } break; -case 330: { +case 332: { AST::FunctionDeclaration *node = new (pool) AST::FunctionDeclaration(stringRef(2), sym(4).FormalParameterList, sym(7).FunctionBody); node->functionToken = loc(1); node->identifierToken = loc(2); @@ -1622,7 +1634,7 @@ case 330: { sym(1).Node = node; } break; -case 331: { +case 333: { AST::FunctionExpression *node = new (pool) AST::FunctionExpression(stringRef(2), sym(4).FormalParameterList, sym(7).FunctionBody); node->functionToken = loc(1); if (! stringRef(2).isNull()) @@ -1634,60 +1646,66 @@ case 331: { sym(1).Node = node; } break; -case 332: { +case 334: { + AST::FunctionExpression *node = new (pool) AST::FunctionExpression(QStringRef(), sym(3).FormalParameterList, sym(6).FunctionBody); + node->functionToken = loc(1); + node->lparenToken = loc(2); + node->rparenToken = loc(4); + node->lbraceToken = loc(5); + node->rbraceToken = loc(7); + sym(1).Node = node; +} break; + +case 335: { AST::FormalParameterList *node = new (pool) AST::FormalParameterList(stringRef(1)); node->identifierToken = loc(1); sym(1).Node = node; } break; -case 333: { +case 336: { AST::FormalParameterList *node = new (pool) AST::FormalParameterList(sym(1).FormalParameterList, stringRef(3)); node->commaToken = loc(2); node->identifierToken = loc(3); sym(1).Node = node; } break; -case 334: { +case 337: { sym(1).Node = 0; } break; -case 335: { +case 338: { sym(1).Node = sym(1).FormalParameterList->finish (); } break; -case 336: { +case 339: { sym(1).Node = 0; } break; -case 338: { +case 341: { sym(1).Node = new (pool) AST::FunctionBody(sym(1).SourceElements->finish ()); } break; -case 340: { +case 343: { sym(1).Node = new (pool) AST::Program(sym(1).SourceElements->finish ()); } break; -case 341: { +case 344: { sym(1).Node = new (pool) AST::SourceElements(sym(1).SourceElement); } break; -case 342: { +case 345: { sym(1).Node = new (pool) AST::SourceElements(sym(1).SourceElements, sym(2).SourceElement); } break; -case 343: { +case 346: { sym(1).Node = new (pool) AST::StatementSourceElement(sym(1).Statement); } break; -case 344: { +case 347: { sym(1).Node = new (pool) AST::FunctionSourceElement(sym(1).FunctionDeclaration); } break; -case 345: { - stringRef(1) = QStringRef(); -} break; - -case 347: { +case 348: { sym(1).Node = 0; } break; diff --git a/src/qml/qml/parser/qqmljsparser_p.h b/src/qml/qml/parser/qqmljsparser_p.h index c32adc466b..0919eb5e47 100644 --- a/src/qml/qml/parser/qqmljsparser_p.h +++ b/src/qml/qml/parser/qqmljsparser_p.h @@ -101,7 +101,8 @@ public: AST::FunctionDeclaration *FunctionDeclaration; AST::Node *Node; AST::PropertyName *PropertyName; - AST::PropertyNameAndValueList *PropertyNameAndValueList; + AST::PropertyAssignment *PropertyAssignment; + AST::PropertyAssignmentList *PropertyAssignmentList; AST::SourceElement *SourceElement; AST::SourceElements *SourceElements; AST::Statement *Statement; @@ -244,9 +245,9 @@ protected: -#define J_SCRIPT_REGEXPLITERAL_RULE1 79 +#define J_SCRIPT_REGEXPLITERAL_RULE1 81 -#define J_SCRIPT_REGEXPLITERAL_RULE2 80 +#define J_SCRIPT_REGEXPLITERAL_RULE2 82 QT_QML_END_NAMESPACE diff --git a/src/qml/qml/v4/qv4irbuilder.cpp b/src/qml/qml/v4/qv4irbuilder.cpp index 29abd51a81..bd875c185e 100644 --- a/src/qml/qml/v4/qv4irbuilder.cpp +++ b/src/qml/qml/v4/qv4irbuilder.cpp @@ -360,7 +360,19 @@ bool QV4IRBuilder::visit(AST::StatementSourceElement *) } // object literals -bool QV4IRBuilder::visit(AST::PropertyNameAndValueList *) +bool QV4IRBuilder::visit(AST::PropertyAssignmentList *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + +bool QV4IRBuilder::visit(AST::PropertyNameAndValue *) +{ + Q_ASSERT(!"unreachable"); + return false; +} + +bool QV4IRBuilder::visit(AST::PropertyGetterSetter *) { Q_ASSERT(!"unreachable"); return false; diff --git a/src/qml/qml/v4/qv4irbuilder_p.h b/src/qml/qml/v4/qv4irbuilder_p.h index 1474ca2b49..22bf71febd 100644 --- a/src/qml/qml/v4/qv4irbuilder_p.h +++ b/src/qml/qml/v4/qv4irbuilder_p.h @@ -139,7 +139,9 @@ protected: virtual bool visit(QQmlJS::AST::StatementSourceElement *ast); // object literals - virtual bool visit(QQmlJS::AST::PropertyNameAndValueList *ast); + virtual bool visit(QQmlJS::AST::PropertyAssignmentList *ast); + virtual bool visit(QQmlJS::AST::PropertyNameAndValue *ast); + virtual bool visit(QQmlJS::AST::PropertyGetterSetter *ast); virtual bool visit(QQmlJS::AST::IdentifierPropertyName *ast); virtual bool visit(QQmlJS::AST::StringLiteralPropertyName *ast); virtual bool visit(QQmlJS::AST::NumericLiteralPropertyName *ast); diff --git a/tests/auto/qml/qqmlecmascript/data/getSet.qml b/tests/auto/qml/qqmlecmascript/data/getSet.qml new file mode 100644 index 0000000000..2987986b38 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/getSet.qml @@ -0,0 +1,14 @@ +import QtQuick 2.0 + +QtObject { + function get(x) { return 1; } + function set(x) { return 1; } + function code() { + var get = 0; + var set = 1; + var o = { + get foo() { return 2; }, + set foo(x) { 1; } + } + } +} -- cgit v1.2.3 From 63064c7ed7a23c1749ed7f58d55b680190dc01c4 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 3 Jan 2013 17:13:38 +0100 Subject: Add the getter/setter type to PropertyGetterSetter Required to differentiatate between getters and setters. Change-Id: I48b2fb911192a8cc5840ff11e31b885f62ac6179 Reviewed-by: Simon Hausmann --- src/qml/qml/parser/qqmljsast_p.h | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/qml/qml/parser/qqmljsast_p.h b/src/qml/qml/parser/qqmljsast_p.h index ddab613408..261067d6b1 100644 --- a/src/qml/qml/parser/qqmljsast_p.h +++ b/src/qml/qml/parser/qqmljsast_p.h @@ -679,12 +679,17 @@ class QML_PARSER_EXPORT PropertyGetterSetter: public PropertyAssignment public: QQMLJS_DECLARE_AST_NODE(PropertyGetterSetter) + enum Type { + Getter, + Setter + }; + PropertyGetterSetter(PropertyName *n, FunctionBody *b) - : name(n), formals(0), functionBody (b) + : type(Getter), name(n), formals(0), functionBody (b) { kind = K; } PropertyGetterSetter(PropertyName *n, FormalParameterList *f, FunctionBody *b) - : name(n), formals(f), functionBody (b) + : type(Setter), name(n), formals(f), functionBody (b) { kind = K; } virtual void accept0(Visitor *visitor); @@ -696,6 +701,7 @@ public: { return rbraceToken; } // attributes + Type type; SourceLocation getSetToken; PropertyName *name; SourceLocation lparenToken; -- cgit v1.2.3 From a65824f353300dedc8440c36a29d0fb6a2cb9662 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 18 Dec 2012 14:54:16 +0100 Subject: Fix: disallow incomplete hex numbers "0x" and "0X". See also ECMA 5.1, 7.8.3, rule HexIntegerLiteral. Change-Id: I356dc7cfbc88890bb7f35c8bc4219a37633177f8 Reviewed-by: Lars Knoll --- src/qml/qml/parser/qqmljsengine_p.cpp | 2 +- src/qml/qml/parser/qqmljslexer.cpp | 9 ++++++++- src/qml/qml/parser/qqmljslexer_p.h | 1 + tests/auto/qml/qmlmin/tst_qmlmin.cpp | 2 ++ tests/auto/qml/qqmlecmascript/data/numberParsing_error.1.qml | 9 +++++++++ tests/auto/qml/qqmlecmascript/data/numberParsing_error.2.qml | 9 +++++++++ tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp | 6 ++++++ 7 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 tests/auto/qml/qqmlecmascript/data/numberParsing_error.1.qml create mode 100644 tests/auto/qml/qqmlecmascript/data/numberParsing_error.2.qml diff --git a/src/qml/qml/parser/qqmljsengine_p.cpp b/src/qml/qml/parser/qqmljsengine_p.cpp index aab5035134..5adcec2060 100644 --- a/src/qml/qml/parser/qqmljsengine_p.cpp +++ b/src/qml/qml/parser/qqmljsengine_p.cpp @@ -50,7 +50,7 @@ QT_QML_BEGIN_NAMESPACE namespace QQmlJS { -static int toDigit(char c) +static inline int toDigit(char c) { if ((c >= '0') && (c <= '9')) return c - '0'; diff --git a/src/qml/qml/parser/qqmljslexer.cpp b/src/qml/qml/parser/qqmljslexer.cpp index 79fb971dde..508d7581be 100644 --- a/src/qml/qml/parser/qqmljslexer.cpp +++ b/src/qml/qml/parser/qqmljslexer.cpp @@ -861,8 +861,9 @@ int Lexer::scanNumber(QChar ch) chars.append(ch.unicode()); if (ch == QLatin1Char('0') && (_char == QLatin1Char('x') || _char == QLatin1Char('X'))) { - // parse hex integer literal + ch = _char; // remember the x or X to use it in the error message below. + // parse hex integer literal chars.append(_char.unicode()); scanChar(); // consume `x' @@ -871,6 +872,12 @@ int Lexer::scanNumber(QChar ch) scanChar(); } + if (chars.size() < 3) { + _errorCode = IllegalHexNumber; + _errorMessage = QCoreApplication::translate("QQmlParser", "At least one hexadecimal digit is required after '0%1'").arg(ch); + return T_ERROR; + } + _tokenValue = integerFromString(chars.constData(), chars.size(), 16); return T_NUMERIC_LITERAL; } diff --git a/src/qml/qml/parser/qqmljslexer_p.h b/src/qml/qml/parser/qqmljslexer_p.h index e8ffbd3e70..708bb43e93 100644 --- a/src/qml/qml/parser/qqmljslexer_p.h +++ b/src/qml/qml/parser/qqmljslexer_p.h @@ -121,6 +121,7 @@ public: enum Error { NoError, IllegalCharacter, + IllegalHexNumber, UnclosedStringLiteral, IllegalEscapeSequence, IllegalUnicodeEscapeSequence, diff --git a/tests/auto/qml/qmlmin/tst_qmlmin.cpp b/tests/auto/qml/qmlmin/tst_qmlmin.cpp index be93a79034..3cda6c0885 100644 --- a/tests/auto/qml/qmlmin/tst_qmlmin.cpp +++ b/tests/auto/qml/qmlmin/tst_qmlmin.cpp @@ -124,6 +124,8 @@ void tst_qmlmin::initTestCase() invalidFiles << "tests/auto/qml/qqmlecmascript/data/stringParsing_error.2.qml"; invalidFiles << "tests/auto/qml/qqmlecmascript/data/stringParsing_error.3.qml"; invalidFiles << "tests/auto/qml/qqmlecmascript/data/stringParsing_error.4.qml"; + invalidFiles << "tests/auto/qml/qqmlecmascript/data/numberParsing_error.1.qml"; + invalidFiles << "tests/auto/qml/qqmlecmascript/data/numberParsing_error.2.qml"; } QStringList tst_qmlmin::findFiles(const QDir &d) diff --git a/tests/auto/qml/qqmlecmascript/data/numberParsing_error.1.qml b/tests/auto/qml/qqmlecmascript/data/numberParsing_error.1.qml new file mode 100644 index 0000000000..61233cbd5b --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/numberParsing_error.1.qml @@ -0,0 +1,9 @@ + +import QtQuick 2.0 + +QtObject { + function code() { + var x = 0x; + } +} + diff --git a/tests/auto/qml/qqmlecmascript/data/numberParsing_error.2.qml b/tests/auto/qml/qqmlecmascript/data/numberParsing_error.2.qml new file mode 100644 index 0000000000..45195452ca --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/numberParsing_error.2.qml @@ -0,0 +1,9 @@ + +import QtQuick 2.0 + +QtObject { + function code() { + var x = 0X; + } +} + diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp index 852c0ef4b5..370db68024 100644 --- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp +++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp @@ -7346,6 +7346,12 @@ void tst_qqmlecmascript::numberParsing() QObject *object = component.create(); QVERIFY(object != 0); } + for (int i = 1; i < 3; ++i) { + QString file("numberParsing_error.%1.qml"); + file = file.arg(i); + QQmlComponent component(&engine, testFileUrl(file)); + QVERIFY(!component.errors().isEmpty()); + } } void tst_qqmlecmascript::stringParsing() -- cgit v1.2.3 From fd4c5b6e1c4ef488e827a1610fd62e1105373a13 Mon Sep 17 00:00:00 2001 From: Robin Burchell Date: Fri, 14 Dec 2012 18:19:07 +0100 Subject: Deinline QQuickKeysAttached::keyToSignal. This also means we don't need to expose the SigMap struct or value - it can be made file-local. This isn't performance-critical, so it shouldn't really matter. Change-Id: I08d48df4f903df49a6ea58e6f10f3f53a97d89de Reviewed-by: Martin Jones --- src/quick/items/qquickitem.cpp | 22 +++++++++++++++++++++- src/quick/items/qquickitem_p.h | 21 +-------------------- 2 files changed, 22 insertions(+), 21 deletions(-) diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index 6668cb72c5..3af378dd84 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -739,7 +739,12 @@ void QQuickKeyNavigationAttached::setFocusNavigation(QQuickItem *currentItem, co while (currentItem != initialItem && isNextItem); } -const QQuickKeysAttached::SigMap QQuickKeysAttached::sigMap[] = { +struct SigMap { + int key; + const char *sig; +}; + +const SigMap sigMap[] = { { Qt::Key_Left, "leftPressed" }, { Qt::Key_Right, "rightPressed" }, { Qt::Key_Up, "upPressed" }, @@ -771,6 +776,21 @@ const QQuickKeysAttached::SigMap QQuickKeysAttached::sigMap[] = { { 0, 0 } }; +const QByteArray QQuickKeysAttached::keyToSignal(int key) +{ + QByteArray keySignal; + if (key >= Qt::Key_0 && key <= Qt::Key_9) { + keySignal = "digit0Pressed"; + keySignal[5] = '0' + (key - Qt::Key_0); + } else { + int i = 0; + while (sigMap[i].key && sigMap[i].key != key) + ++i; + keySignal = sigMap[i].sig; + } + return keySignal; +} + bool QQuickKeysAttached::isConnected(const char *signalName) { Q_D(QQuickKeysAttached); diff --git a/src/quick/items/qquickitem_p.h b/src/quick/items/qquickitem_p.h index 804b10deac..10c7798ff6 100644 --- a/src/quick/items/qquickitem_p.h +++ b/src/quick/items/qquickitem_p.h @@ -819,28 +819,9 @@ private: virtual void inputMethodEvent(QInputMethodEvent *, bool post); virtual QVariant inputMethodQuery(Qt::InputMethodQuery query) const; #endif - const QByteArray keyToSignal(int key) { - QByteArray keySignal; - if (key >= Qt::Key_0 && key <= Qt::Key_9) { - keySignal = "digit0Pressed"; - keySignal[5] = '0' + (key - Qt::Key_0); - } else { - int i = 0; - while (sigMap[i].key && sigMap[i].key != key) - ++i; - keySignal = sigMap[i].sig; - } - return keySignal; - } + const QByteArray keyToSignal(int key); bool isConnected(const char *signalName); - - struct SigMap { - int key; - const char *sig; - }; - - static const SigMap sigMap[]; }; Qt::MouseButtons QQuickItemPrivate::acceptedMouseButtons() const -- cgit v1.2.3 From 58985b94679f38bcea15210dbe5dc6e95168ee2b Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 18 Dec 2012 15:11:27 +0100 Subject: Fix unicode escape sequence validation in strings. Give an error message when the sequence does not conform to the grammar. Note that both \u and \x (without any numbers following it) are not valid escape sequences in ECMA5.1. Change-Id: I14348984c680b0ce86e05faad5630afc1e98cd02 Reviewed-by: Simon Hausmann --- src/qml/qml/parser/qqmljslexer.cpp | 7 +++++-- .../auto/qml/parserstress/tests/ecma/LexicalConventions/7.7.4.js | 2 -- tests/auto/qml/qmlmin/tst_qmlmin.cpp | 1 + tests/auto/qml/qqmlecmascript/data/stringParsing_error.5.qml | 9 +++++++++ tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp | 2 +- 5 files changed, 16 insertions(+), 5 deletions(-) create mode 100644 tests/auto/qml/qqmlecmascript/data/stringParsing_error.5.qml diff --git a/src/qml/qml/parser/qqmljslexer.cpp b/src/qml/qml/parser/qqmljslexer.cpp index 508d7581be..532826f15c 100644 --- a/src/qml/qml/parser/qqmljslexer.cpp +++ b/src/qml/qml/parser/qqmljslexer.cpp @@ -659,8 +659,11 @@ again: // unicode escape sequence case 'u': u = decodeUnicodeEscapeCharacter(&ok); - if (! ok) - u = _char; + if (! ok) { + _errorCode = IllegalUnicodeEscapeSequence; + _errorMessage = QCoreApplication::translate("QQmlParser", "Illegal unicode escape sequence"); + return T_ERROR; + } break; // hex escape sequence diff --git a/tests/auto/qml/parserstress/tests/ecma/LexicalConventions/7.7.4.js b/tests/auto/qml/parserstress/tests/ecma/LexicalConventions/7.7.4.js index 92a04942c7..4a3173db6c 100644 --- a/tests/auto/qml/parserstress/tests/ecma/LexicalConventions/7.7.4.js +++ b/tests/auto/qml/parserstress/tests/ecma/LexicalConventions/7.7.4.js @@ -155,10 +155,8 @@ new TestCase( SECTION, "\\o", "o", "\o" ); new TestCase( SECTION, "\\p", "p", "\p" ); new TestCase( SECTION, "\\q", "q", "\q" ); new TestCase( SECTION, "\\s", "s", "\s" ); -new TestCase( SECTION, "\\u", "u", "\u" ); new TestCase( SECTION, "\\w", "w", "\w" ); -new TestCase( SECTION, "\\x", "x", "\x" ); new TestCase( SECTION, "\\y", "y", "\y" ); new TestCase( SECTION, "\\z", "z", "\z" ); diff --git a/tests/auto/qml/qmlmin/tst_qmlmin.cpp b/tests/auto/qml/qmlmin/tst_qmlmin.cpp index 3cda6c0885..3fb51512d9 100644 --- a/tests/auto/qml/qmlmin/tst_qmlmin.cpp +++ b/tests/auto/qml/qmlmin/tst_qmlmin.cpp @@ -124,6 +124,7 @@ void tst_qmlmin::initTestCase() invalidFiles << "tests/auto/qml/qqmlecmascript/data/stringParsing_error.2.qml"; invalidFiles << "tests/auto/qml/qqmlecmascript/data/stringParsing_error.3.qml"; invalidFiles << "tests/auto/qml/qqmlecmascript/data/stringParsing_error.4.qml"; + invalidFiles << "tests/auto/qml/qqmlecmascript/data/stringParsing_error.5.qml"; invalidFiles << "tests/auto/qml/qqmlecmascript/data/numberParsing_error.1.qml"; invalidFiles << "tests/auto/qml/qqmlecmascript/data/numberParsing_error.2.qml"; } diff --git a/tests/auto/qml/qqmlecmascript/data/stringParsing_error.5.qml b/tests/auto/qml/qqmlecmascript/data/stringParsing_error.5.qml new file mode 100644 index 0000000000..563e01a995 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/stringParsing_error.5.qml @@ -0,0 +1,9 @@ + +import QtQuick 2.0 + +QtObject { + function code() { + var x = "\u000G"; + } +} + diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp index fb6efcaf5d..baee10055b 100644 --- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp +++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp @@ -7364,7 +7364,7 @@ void tst_qqmlecmascript::numberParsing() void tst_qqmlecmascript::stringParsing() { - for (int i = 1; i < 5; ++i) { + for (int i = 1; i < 6; ++i) { QString file("stringParsing_error.%1.qml"); file = file.arg(i); QQmlComponent component(&engine, testFileUrl(file)); -- cgit v1.2.3 From 5a902969566a4ca7e3937cfe61876a7fbaeb775b Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Thu, 20 Dec 2012 10:31:46 +0100 Subject: Fix semicolon insertion before pre-incr/decr operators. Do not insert a semicolon if the previous token was a binop or a question mark. Change-Id: Id2ee1d3cb57fa3fe20bfc0078d06f9e2619d88f1 Reviewed-by: Lars Knoll --- src/qml/qml/parser/qqmljslexer.cpp | 54 ++++++++++++++++++++++ .../qml/qqmlecmascript/data/incrDecrSemicolon1.qml | 47 +++++++++++++++++++ .../qml/qqmlecmascript/data/incrDecrSemicolon2.qml | 19 ++++++++ .../auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp | 16 +++++++ 4 files changed, 136 insertions(+) create mode 100644 tests/auto/qml/qqmlecmascript/data/incrDecrSemicolon1.qml create mode 100644 tests/auto/qml/qqmlecmascript/data/incrDecrSemicolon2.qml diff --git a/src/qml/qml/parser/qqmljslexer.cpp b/src/qml/qml/parser/qqmljslexer.cpp index 532826f15c..f31140a796 100644 --- a/src/qml/qml/parser/qqmljslexer.cpp +++ b/src/qml/qml/parser/qqmljslexer.cpp @@ -179,6 +179,55 @@ void Lexer::scanChar() } } +namespace { +inline bool isBinop(int tok) +{ + switch (tok) { + case Lexer::T_AND: + case Lexer::T_AND_AND: + case Lexer::T_AND_EQ: + case Lexer::T_DIVIDE_: + case Lexer::T_DIVIDE_EQ: + case Lexer::T_EQ: + case Lexer::T_EQ_EQ: + case Lexer::T_EQ_EQ_EQ: + case Lexer::T_GE: + case Lexer::T_GT: + case Lexer::T_GT_GT: + case Lexer::T_GT_GT_EQ: + case Lexer::T_GT_GT_GT: + case Lexer::T_GT_GT_GT_EQ: + case Lexer::T_LE: + case Lexer::T_LT: + case Lexer::T_LT_LT: + case Lexer::T_LT_LT_EQ: + case Lexer::T_MINUS: + case Lexer::T_MINUS_EQ: + case Lexer::T_MINUS_MINUS: + case Lexer::T_NOT_EQ: + case Lexer::T_NOT_EQ_EQ: + case Lexer::T_OR: + case Lexer::T_OR_EQ: + case Lexer::T_OR_OR: + case Lexer::T_PLUS: + case Lexer::T_PLUS_EQ: + case Lexer::T_PLUS_PLUS: + case Lexer::T_REMAINDER: + case Lexer::T_REMAINDER_EQ: + case Lexer::T_RETURN: + case Lexer::T_STAR: + case Lexer::T_STAR_EQ: + case Lexer::T_TILDE: + case Lexer::T_XOR: + case Lexer::T_XOR_EQ: + return true; + + default: + return false; + } +} +} // anonymous namespace + int Lexer::lex() { const int previousTokenKind = _tokenKind; @@ -195,9 +244,14 @@ int Lexer::lex() switch (_tokenKind) { case T_LBRACE: case T_SEMICOLON: + case T_QUESTION: case T_COLON: _delimited = true; break; + default: + if (isBinop(_tokenKind)) + _delimited = true; + break; case T_IF: case T_FOR: diff --git a/tests/auto/qml/qqmlecmascript/data/incrDecrSemicolon1.qml b/tests/auto/qml/qqmlecmascript/data/incrDecrSemicolon1.qml new file mode 100644 index 0000000000..b9a30ef8b5 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/incrDecrSemicolon1.qml @@ -0,0 +1,47 @@ +import QtQuick 2.0 + +QtObject { + + // PLEASE NOTE: the function below is whitespace and newline sensitive, + // because that is what the test is all about. + // + // So: DO NOT REFORMAT THE CODE BELOW! + + function code() { +var x=0, y=0; +var z= +x ++ +++ +y + +////////////////////////////////////////////////////////////////////////////// +if (false) { + ; +} +////////////////////////////////////////////////////////////////////////////// + +z= +x ++ ++ +y + +////////////////////////////////////////////////////////////////////////////// +if (false) { + ; +} +////////////////////////////////////////////////////////////////////////////// + +z= +x ++ ++ +y + +////////////////////////////////////////////////////////////////////////////// +if (false) { + ; +} + + } +} + diff --git a/tests/auto/qml/qqmlecmascript/data/incrDecrSemicolon2.qml b/tests/auto/qml/qqmlecmascript/data/incrDecrSemicolon2.qml new file mode 100644 index 0000000000..717cdb5715 --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/incrDecrSemicolon2.qml @@ -0,0 +1,19 @@ +import QtQuick 2.0 + +QtObject { + + // PLEASE NOTE: the function below is whitespace and newline sensitive, + // because that is what the test is all about. + // + // So: DO NOT REFORMAT THE CODE BELOW! + + function code() { +var a, b, c; +a=b +++c + +if (a === b) { +} + } +} + diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp index baee10055b..a1a51d4965 100644 --- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp +++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp @@ -266,6 +266,8 @@ private slots: void deleteLaterObjectMethodCall(); void automaticSemicolon(); void compatibilitySemicolon(); + void incrDecrSemicolon1(); + void incrDecrSemicolon2(); void unaryExpression(); void switchStatement(); void withStatement(); @@ -6627,6 +6629,20 @@ void tst_qqmlecmascript::compatibilitySemicolon() QVERIFY(object != 0); } +void tst_qqmlecmascript::incrDecrSemicolon1() +{ + QQmlComponent component(&engine, testFileUrl("incrDecrSemicolon1.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); +} + +void tst_qqmlecmascript::incrDecrSemicolon2() +{ + QQmlComponent component(&engine, testFileUrl("incrDecrSemicolon2.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); +} + void tst_qqmlecmascript::unaryExpression() { QQmlComponent component(&engine, testFileUrl("unaryExpression.qml")); -- cgit v1.2.3 From 6576728b885cb89be715db9f9e2038527a14a455 Mon Sep 17 00:00:00 2001 From: Alan Alpert <416365416c@gmail.com> Date: Sat, 15 Dec 2012 13:58:38 -0800 Subject: Remove m_ in property names of QQmlTypePrivate There's already a d-> to indicate they are internal variables. Doesn't need both. Change-Id: I9b127dff7955456aacb25138fa6ea8efb7bb921f Reviewed-by: Christopher Adams --- src/qml/qml/qqmlmetatype.cpp | 366 +++++++++++++++++++++---------------------- 1 file changed, 183 insertions(+), 183 deletions(-) diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index 595f9e8de0..89c505fb6d 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -155,41 +155,41 @@ public: void initEnums() const; void insertEnums(const QMetaObject *metaObject) const; - bool m_isInterface : 1; - const char *m_iid; - QHashedString m_module; - QString m_name; - QString m_elementName; - int m_version_maj; - int m_version_min; - int m_typeId; int m_listId; - int m_revision; - mutable bool m_containsRevisionedAttributes; - mutable QQmlType *m_superType; - - int m_allocationSize; - void (*m_newFunc)(void *); - QString m_noCreationReason; - - const QMetaObject *m_baseMetaObject; - QQmlAttachedPropertiesFunc m_attachedPropertiesFunc; - const QMetaObject *m_attachedPropertiesType; - int m_attachedPropertiesId; - int m_parserStatusCast; - int m_propertyValueSourceCast; - int m_propertyValueInterceptorCast; - QObject *(*m_extFunc)(QObject *); - const QMetaObject *m_extMetaObject; - int m_index; - QQmlCustomParser *m_customParser; - mutable volatile bool m_isSetup:1; - mutable volatile bool m_isEnumSetup:1; - mutable bool m_haveSuperType:1; - mutable QList m_metaObjects; - mutable QStringHash m_enums; - QQmlType::SingletonInstanceInfo *m_singletonInstanceInfo; - - static QHash m_attachedPropertyIds; + bool isInterface : 1; + const char *iid; + QHashedString module; + QString name; + QString elementName; + int version_maj; + int version_min; + int typeId; int listId; + int revision; + mutable bool containsRevisionedAttributes; + mutable QQmlType *superType; + + int allocationSize; + void (*newFunc)(void *); + QString noCreationReason; + + const QMetaObject *baseMetaObject; + QQmlAttachedPropertiesFunc attachedPropertiesFunc; + const QMetaObject *attachedPropertiesType; + int attachedPropertiesId; + int parserStatusCast; + int propertyValueSourceCast; + int propertyValueInterceptorCast; + QObject *(*extFunc)(QObject *); + const QMetaObject *extMetaObject; + int index; + QQmlCustomParser *customParser; + mutable volatile bool isSetup:1; + mutable volatile bool isEnumSetup:1; + mutable bool haveSuperType:1; + mutable QList metaObjects; + mutable QStringHash enums; + QQmlType::SingletonInstanceInfo *singletonInstanceInfo; + + static QHash attachedPropertyIds; }; // Avoid multiple fromUtf8(), copies and hashing of the module name. @@ -250,147 +250,147 @@ QJSValue QQmlType::SingletonInstanceInfo::scriptApi(QQmlEngine *e) const return scriptApis.value(e); } -QHash QQmlTypePrivate::m_attachedPropertyIds; +QHash QQmlTypePrivate::attachedPropertyIds; QQmlTypePrivate::QQmlTypePrivate() -: m_isInterface(false), m_iid(0), m_typeId(0), m_listId(0), m_revision(0), m_containsRevisionedAttributes(false), - m_superType(0), m_allocationSize(0), m_newFunc(0), m_baseMetaObject(0), m_attachedPropertiesFunc(0), - m_attachedPropertiesType(0), m_parserStatusCast(-1), m_propertyValueSourceCast(-1), - m_propertyValueInterceptorCast(-1), m_extFunc(0), m_extMetaObject(0), m_index(-1), m_customParser(0), - m_isSetup(false), m_isEnumSetup(false), m_haveSuperType(false), m_singletonInstanceInfo(0) +: isInterface(false), iid(0), typeId(0), listId(0), revision(0), containsRevisionedAttributes(false), + superType(0), allocationSize(0), newFunc(0), baseMetaObject(0), attachedPropertiesFunc(0), + attachedPropertiesType(0), parserStatusCast(-1), propertyValueSourceCast(-1), + propertyValueInterceptorCast(-1), extFunc(0), extMetaObject(0), index(-1), customParser(0), + isSetup(false), isEnumSetup(false), haveSuperType(false), singletonInstanceInfo(0) { } QQmlTypePrivate::~QQmlTypePrivate() { - delete m_singletonInstanceInfo; + delete singletonInstanceInfo; } QQmlType::QQmlType(int index, const QQmlPrivate::RegisterInterface &interface) : d(new QQmlTypePrivate) { - d->m_isInterface = true; - d->m_iid = interface.iid; - d->m_typeId = interface.typeId; - d->m_listId = interface.listId; - d->m_newFunc = 0; - d->m_index = index; - d->m_isSetup = true; - d->m_version_maj = 0; - d->m_version_min = 0; + d->isInterface = true; + d->iid = interface.iid; + d->typeId = interface.typeId; + d->listId = interface.listId; + d->newFunc = 0; + d->index = index; + d->isSetup = true; + d->version_maj = 0; + d->version_min = 0; } QQmlType::QQmlType(int index, const QString &elementName, const QQmlPrivate::RegisterSingletonType &type) : d(new QQmlTypePrivate) { - d->m_elementName = elementName; - d->m_module = moduleFromUtf8(type.uri); + d->elementName = elementName; + d->module = moduleFromUtf8(type.uri); - d->m_version_maj = type.versionMajor; - d->m_version_min = type.versionMinor; + d->version_maj = type.versionMajor; + d->version_min = type.versionMinor; if (type.qobjectApi) { if (type.version >= 1) // static metaobject added in version 1 - d->m_baseMetaObject = type.instanceMetaObject; + d->baseMetaObject = type.instanceMetaObject; if (type.version >= 2) // typeId added in version 2 - d->m_typeId = type.typeId; + d->typeId = type.typeId; if (type.version >= 2) // revisions added in version 2 - d->m_revision = type.revision; + d->revision = type.revision; } - d->m_newFunc = 0; - d->m_index = index; + d->newFunc = 0; + d->index = index; - d->m_singletonInstanceInfo = new SingletonInstanceInfo; - d->m_singletonInstanceInfo->scriptCallback = type.scriptApi; - d->m_singletonInstanceInfo->qobjectCallback = type.qobjectApi; - d->m_singletonInstanceInfo->typeName = QString::fromUtf8(type.typeName); - d->m_singletonInstanceInfo->instanceMetaObject = (type.qobjectApi && type.version >= 1) ? type.instanceMetaObject : 0; + d->singletonInstanceInfo = new SingletonInstanceInfo; + d->singletonInstanceInfo->scriptCallback = type.scriptApi; + d->singletonInstanceInfo->qobjectCallback = type.qobjectApi; + d->singletonInstanceInfo->typeName = QString::fromUtf8(type.typeName); + d->singletonInstanceInfo->instanceMetaObject = (type.qobjectApi && type.version >= 1) ? type.instanceMetaObject : 0; } QQmlType::QQmlType(int index, const QString &elementName, const QQmlPrivate::RegisterType &type) : d(new QQmlTypePrivate) { - d->m_elementName = elementName; - d->m_module = moduleFromUtf8(type.uri); + d->elementName = elementName; + d->module = moduleFromUtf8(type.uri); - d->m_version_maj = type.versionMajor; - d->m_version_min = type.versionMinor; + d->version_maj = type.versionMajor; + d->version_min = type.versionMinor; if (type.version >= 1) // revisions added in version 1 - d->m_revision = type.revision; - d->m_typeId = type.typeId; - d->m_listId = type.listId; - d->m_allocationSize = type.objectSize; - d->m_newFunc = type.create; - d->m_noCreationReason = type.noCreationReason; - d->m_baseMetaObject = type.metaObject; - d->m_attachedPropertiesFunc = type.attachedPropertiesFunction; - d->m_attachedPropertiesType = type.attachedPropertiesMetaObject; - if (d->m_attachedPropertiesType) { - QHash::Iterator iter = d->m_attachedPropertyIds.find(d->m_baseMetaObject); - if (iter == d->m_attachedPropertyIds.end()) - iter = d->m_attachedPropertyIds.insert(d->m_baseMetaObject, index); - d->m_attachedPropertiesId = *iter; + d->revision = type.revision; + d->typeId = type.typeId; + d->listId = type.listId; + d->allocationSize = type.objectSize; + d->newFunc = type.create; + d->noCreationReason = type.noCreationReason; + d->baseMetaObject = type.metaObject; + d->attachedPropertiesFunc = type.attachedPropertiesFunction; + d->attachedPropertiesType = type.attachedPropertiesMetaObject; + if (d->attachedPropertiesType) { + QHash::Iterator iter = d->attachedPropertyIds.find(d->baseMetaObject); + if (iter == d->attachedPropertyIds.end()) + iter = d->attachedPropertyIds.insert(d->baseMetaObject, index); + d->attachedPropertiesId = *iter; } else { - d->m_attachedPropertiesId = -1; + d->attachedPropertiesId = -1; } - d->m_parserStatusCast = type.parserStatusCast; - d->m_propertyValueSourceCast = type.valueSourceCast; - d->m_propertyValueInterceptorCast = type.valueInterceptorCast; - d->m_extFunc = type.extensionObjectCreate; - d->m_index = index; - d->m_customParser = type.customParser; + d->parserStatusCast = type.parserStatusCast; + d->propertyValueSourceCast = type.valueSourceCast; + d->propertyValueInterceptorCast = type.valueInterceptorCast; + d->extFunc = type.extensionObjectCreate; + d->index = index; + d->customParser = type.customParser; if (type.extensionMetaObject) - d->m_extMetaObject = type.extensionMetaObject; + d->extMetaObject = type.extensionMetaObject; } QQmlType::~QQmlType() { - delete d->m_customParser; + delete d->customParser; delete d; } const QHashedString &QQmlType::module() const { - return d->m_module; + return d->module; } int QQmlType::majorVersion() const { - return d->m_version_maj; + return d->version_maj; } int QQmlType::minorVersion() const { - return d->m_version_min; + return d->version_min; } bool QQmlType::availableInVersion(int vmajor, int vminor) const { Q_ASSERT(vmajor >= 0 && vminor >= 0); - return vmajor == d->m_version_maj && vminor >= d->m_version_min; + return vmajor == d->version_maj && vminor >= d->version_min; } bool QQmlType::availableInVersion(const QHashedStringRef &module, int vmajor, int vminor) const { Q_ASSERT(vmajor >= 0 && vminor >= 0); - return module == d->m_module && vmajor == d->m_version_maj && vminor >= d->m_version_min; + return module == d->module && vmajor == d->version_maj && vminor >= d->version_min; } // returns the nearest _registered_ super class QQmlType *QQmlType::superType() const { - if (!d->m_haveSuperType && d->m_baseMetaObject) { - const QMetaObject *mo = d->m_baseMetaObject->superClass(); - while (mo && !d->m_superType) { - d->m_superType = QQmlMetaType::qmlType(mo, d->m_module, d->m_version_maj, d->m_version_min); + if (!d->haveSuperType && d->baseMetaObject) { + const QMetaObject *mo = d->baseMetaObject->superClass(); + while (mo && !d->superType) { + d->superType = QQmlMetaType::qmlType(mo, d->module, d->version_maj, d->version_min); mo = mo->superClass(); } - d->m_haveSuperType = true; + d->haveSuperType = true; } - return d->m_superType; + return d->superType; } static void clone(QMetaObjectBuilder &builder, const QMetaObject *mo, @@ -481,13 +481,13 @@ static bool isPropertyRevisioned(const QMetaObject *mo, int index) void QQmlTypePrivate::init() const { - if (m_isSetup) return; + if (isSetup) return; QWriteLocker lock(metaTypeDataLock()); - if (m_isSetup) + if (isSetup) return; - const QMetaObject *mo = m_baseMetaObject; + const QMetaObject *mo = baseMetaObject; if (!mo) { // singleton type without metaobject information return; @@ -495,78 +495,78 @@ void QQmlTypePrivate::init() const // Setup extended meta object // XXX - very inefficient - if (m_extFunc) { + if (extFunc) { QMetaObjectBuilder builder; - clone(builder, m_extMetaObject, m_extMetaObject, m_extMetaObject); + clone(builder, extMetaObject, extMetaObject, extMetaObject); builder.setFlags(QMetaObjectBuilder::DynamicMetaObject); QMetaObject *mmo = builder.toMetaObject(); mmo->d.superdata = mo; - QQmlProxyMetaObject::ProxyData data = { mmo, m_extFunc, 0, 0 }; - m_metaObjects << data; + QQmlProxyMetaObject::ProxyData data = { mmo, extFunc, 0, 0 }; + metaObjects << data; } mo = mo->d.superdata; while(mo) { QQmlType *t = metaTypeData()->metaObjectToType.value(mo); if (t) { - if (t->d->m_extFunc) { + if (t->d->extFunc) { QMetaObjectBuilder builder; - clone(builder, t->d->m_extMetaObject, t->d->m_baseMetaObject, m_baseMetaObject); + clone(builder, t->d->extMetaObject, t->d->baseMetaObject, baseMetaObject); builder.setFlags(QMetaObjectBuilder::DynamicMetaObject); QMetaObject *mmo = builder.toMetaObject(); - mmo->d.superdata = m_baseMetaObject; - if (!m_metaObjects.isEmpty()) - m_metaObjects.last().metaObject->d.superdata = mmo; - QQmlProxyMetaObject::ProxyData data = { mmo, t->d->m_extFunc, 0, 0 }; - m_metaObjects << data; + mmo->d.superdata = baseMetaObject; + if (!metaObjects.isEmpty()) + metaObjects.last().metaObject->d.superdata = mmo; + QQmlProxyMetaObject::ProxyData data = { mmo, t->d->extFunc, 0, 0 }; + metaObjects << data; } } mo = mo->d.superdata; } - for (int ii = 0; ii < m_metaObjects.count(); ++ii) { - m_metaObjects[ii].propertyOffset = - m_metaObjects.at(ii).metaObject->propertyOffset(); - m_metaObjects[ii].methodOffset = - m_metaObjects.at(ii).metaObject->methodOffset(); + for (int ii = 0; ii < metaObjects.count(); ++ii) { + metaObjects[ii].propertyOffset = + metaObjects.at(ii).metaObject->propertyOffset(); + metaObjects[ii].methodOffset = + metaObjects.at(ii).metaObject->methodOffset(); } // Check for revisioned details { const QMetaObject *mo = 0; - if (m_metaObjects.isEmpty()) - mo = m_baseMetaObject; + if (metaObjects.isEmpty()) + mo = baseMetaObject; else - mo = m_metaObjects.first().metaObject; + mo = metaObjects.first().metaObject; - for (int ii = 0; !m_containsRevisionedAttributes && ii < mo->propertyCount(); ++ii) { + for (int ii = 0; !containsRevisionedAttributes && ii < mo->propertyCount(); ++ii) { if (isPropertyRevisioned(mo, ii)) - m_containsRevisionedAttributes = true; + containsRevisionedAttributes = true; } - for (int ii = 0; !m_containsRevisionedAttributes && ii < mo->methodCount(); ++ii) { + for (int ii = 0; !containsRevisionedAttributes && ii < mo->methodCount(); ++ii) { if (mo->method(ii).revision() != 0) - m_containsRevisionedAttributes = true; + containsRevisionedAttributes = true; } } - m_isSetup = true; + isSetup = true; lock.unlock(); } void QQmlTypePrivate::initEnums() const { - if (m_isEnumSetup) return; + if (isEnumSetup) return; init(); QWriteLocker lock(metaTypeDataLock()); - if (m_isEnumSetup) return; + if (isEnumSetup) return; - if (m_baseMetaObject) // could be singleton type without metaobject - insertEnums(m_baseMetaObject); + if (baseMetaObject) // could be singleton type without metaobject + insertEnums(baseMetaObject); - m_isEnumSetup = true; + isEnumSetup = true; } void QQmlTypePrivate::insertEnums(const QMetaObject *metaObject) const @@ -584,46 +584,46 @@ void QQmlTypePrivate::insertEnums(const QMetaObject *metaObject) const for (int ii = 0; ii < metaObject->enumeratorCount(); ++ii) { QMetaEnum e = metaObject->enumerator(ii); for (int jj = 0; jj < e.keyCount(); ++jj) - m_enums.insert(QString::fromUtf8(e.key(jj)), e.value(jj)); + enums.insert(QString::fromUtf8(e.key(jj)), e.value(jj)); } } QByteArray QQmlType::typeName() const { - if (d->m_singletonInstanceInfo) - return d->m_singletonInstanceInfo->typeName.toUtf8(); - if (d->m_baseMetaObject) - return d->m_baseMetaObject->className(); + if (d->singletonInstanceInfo) + return d->singletonInstanceInfo->typeName.toUtf8(); + if (d->baseMetaObject) + return d->baseMetaObject->className(); else return QByteArray(); } const QString &QQmlType::elementName() const { - return d->m_elementName; + return d->elementName; } const QString &QQmlType::qmlTypeName() const { - if (d->m_name.isEmpty()) { - if (!d->m_module.isEmpty()) - d->m_name = static_cast(d->m_module) + QLatin1Char('/') + d->m_elementName; + if (d->name.isEmpty()) { + if (!d->module.isEmpty()) + d->name = static_cast(d->module) + QLatin1Char('/') + d->elementName; else - d->m_name = d->m_elementName; + d->name = d->elementName; } - return d->m_name; + return d->name; } QObject *QQmlType::create() const { d->init(); - QObject *rv = (QObject *)operator new(d->m_allocationSize); - d->m_newFunc(rv); + QObject *rv = (QObject *)operator new(d->allocationSize); + d->newFunc(rv); - if (rv && !d->m_metaObjects.isEmpty()) - (void)new QQmlProxyMetaObject(rv, &d->m_metaObjects); + if (rv && !d->metaObjects.isEmpty()) + (void)new QQmlProxyMetaObject(rv, &d->metaObjects); return rv; } @@ -632,109 +632,109 @@ void QQmlType::create(QObject **out, void **memory, size_t additionalMemory) con { d->init(); - QObject *rv = (QObject *)operator new(d->m_allocationSize + additionalMemory); - d->m_newFunc(rv); + QObject *rv = (QObject *)operator new(d->allocationSize + additionalMemory); + d->newFunc(rv); - if (rv && !d->m_metaObjects.isEmpty()) - (void)new QQmlProxyMetaObject(rv, &d->m_metaObjects); + if (rv && !d->metaObjects.isEmpty()) + (void)new QQmlProxyMetaObject(rv, &d->metaObjects); *out = rv; - *memory = ((char *)rv) + d->m_allocationSize; + *memory = ((char *)rv) + d->allocationSize; } QQmlType::SingletonInstanceInfo *QQmlType::singletonInstanceInfo() const { - return d->m_singletonInstanceInfo; + return d->singletonInstanceInfo; } QQmlCustomParser *QQmlType::customParser() const { - return d->m_customParser; + return d->customParser; } QQmlType::CreateFunc QQmlType::createFunction() const { - return d->m_newFunc; + return d->newFunc; } QString QQmlType::noCreationReason() const { - return d->m_noCreationReason; + return d->noCreationReason; } int QQmlType::createSize() const { - return d->m_allocationSize; + return d->allocationSize; } bool QQmlType::isCreatable() const { - return d->m_newFunc != 0; + return d->newFunc != 0; } bool QQmlType::isExtendedType() const { d->init(); - return !d->m_metaObjects.isEmpty(); + return !d->metaObjects.isEmpty(); } bool QQmlType::isSingleton() const { - return d->m_singletonInstanceInfo != 0; + return d->singletonInstanceInfo != 0; } bool QQmlType::isInterface() const { - return d->m_isInterface; + return d->isInterface; } int QQmlType::typeId() const { - return d->m_typeId; + return d->typeId; } int QQmlType::qListTypeId() const { - return d->m_listId; + return d->listId; } const QMetaObject *QQmlType::metaObject() const { d->init(); - if (d->m_metaObjects.isEmpty()) - return d->m_baseMetaObject; + if (d->metaObjects.isEmpty()) + return d->baseMetaObject; else - return d->m_metaObjects.first().metaObject; + return d->metaObjects.first().metaObject; } const QMetaObject *QQmlType::baseMetaObject() const { - return d->m_baseMetaObject; + return d->baseMetaObject; } bool QQmlType::containsRevisionedAttributes() const { d->init(); - return d->m_containsRevisionedAttributes; + return d->containsRevisionedAttributes; } int QQmlType::metaObjectRevision() const { - return d->m_revision; + return d->revision; } QQmlAttachedPropertiesFunc QQmlType::attachedPropertiesFunction() const { - return d->m_attachedPropertiesFunc; + return d->attachedPropertiesFunc; } const QMetaObject *QQmlType::attachedPropertiesType() const { - return d->m_attachedPropertiesType; + return d->attachedPropertiesType; } /* @@ -744,32 +744,32 @@ Qt 4.7 and QtQuick 1.0). */ int QQmlType::attachedPropertiesId() const { - return d->m_attachedPropertiesId; + return d->attachedPropertiesId; } int QQmlType::parserStatusCast() const { - return d->m_parserStatusCast; + return d->parserStatusCast; } int QQmlType::propertyValueSourceCast() const { - return d->m_propertyValueSourceCast; + return d->propertyValueSourceCast; } int QQmlType::propertyValueInterceptorCast() const { - return d->m_propertyValueInterceptorCast; + return d->propertyValueInterceptorCast; } const char *QQmlType::interfaceIId() const { - return d->m_iid; + return d->iid; } int QQmlType::index() const { - return d->m_index; + return d->index; } int QQmlType::enumValue(const QHashedStringRef &name, bool *ok) const @@ -779,7 +779,7 @@ int QQmlType::enumValue(const QHashedStringRef &name, bool *ok) const d->initEnums(); - int *rv = d->m_enums.value(name); + int *rv = d->enums.value(name); if (rv) return *rv; @@ -794,7 +794,7 @@ int QQmlType::enumValue(const QHashedCStringRef &name, bool *ok) const d->initEnums(); - int *rv = d->m_enums.value(name); + int *rv = d->enums.value(name); if (rv) return *rv; @@ -809,7 +809,7 @@ int QQmlType::enumValue(const QHashedV8String &name, bool *ok) const d->initEnums(); - int *rv = d->m_enums.value(name); + int *rv = d->enums.value(name); if (rv) return *rv; -- cgit v1.2.3 From 6163ae74d505a3e9733795c6ce63386038f826d1 Mon Sep 17 00:00:00 2001 From: Alan Alpert <416365416c@gmail.com> Date: Sun, 16 Dec 2012 17:35:12 -0800 Subject: Move VME exception to a better place If o is not created, then ddata on the next line may also not create successfuly leading to an invalid memory access before we reach the !o exception. Change-Id: I9b127dff7955456aacb25138fabaabaabaab921f Reviewed-by: Christopher Adams --- src/qml/qml/qqmlvme.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/qml/qml/qqmlvme.cpp b/src/qml/qml/qqmlvme.cpp index 9db3ee5a4d..00d8ad829f 100644 --- a/src/qml/qml/qqmlvme.cpp +++ b/src/qml/qml/qqmlvme.cpp @@ -558,6 +558,11 @@ QObject *QQmlVME::run(QList *errors, QObject *o = 0; void *memory = 0; type.type->create(&o, &memory, sizeof(QQmlData)); + + if (!o) + VME_EXCEPTION(tr("Unable to create object of type %1").arg(type.type->elementName()), + instr.line); + QQmlData *ddata = new (memory) QQmlData; ddata->ownMemory = false; QObjectPrivate::get(o)->declarativeData = ddata; @@ -572,10 +577,6 @@ QObject *QQmlVME::run(QList *errors, ddata->propertyCache->addref(); } - if (!o) - VME_EXCEPTION(tr("Unable to create object of type %1").arg(type.type->elementName()), - instr.line); - if (states.count() == 1) { // Keep a reference to the compiled data we rely on ddata->compiledData = states[0].compiledData; -- cgit v1.2.3 From 276dcc3f1f156514cd296ae9d7480f7424bdff67 Mon Sep 17 00:00:00 2001 From: Peter Varga Date: Fri, 23 Nov 2012 11:03:47 +0100 Subject: Update some QML tests and temporarily skip them These tests should be skipped until the next V8 update is landed into QtJSBackend. The expected results of these tests currently check wrong behavior. These bugs have been already fixed in the official V8 thus we need to update and skip them until the fix is landed into QtJSBackend. Change-Id: I77d8ee50b45cd6599cbb5735ddef7d1461aeceab Reviewed-by: Simon Hausmann --- tests/auto/qml/qjsengine/tst_qjsengine.cpp | 11 ++++------- tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp | 15 +++++++-------- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp index 87d2673c2f..bbde5f4dae 100644 --- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp +++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp @@ -2226,15 +2226,12 @@ void tst_QJSEngine::jsContinueInSwitch() void tst_QJSEngine::jsShadowReadOnlyPrototypeProperty() { - // SpiderMonkey has different behavior than JSC and V8; it disallows - // creating a property on the instance if there's a property with the - // same name in the prototype, and that property is read-only. We - // adopted that behavior in the old (4.5) QtScript back-end, but it - // just seems weird -- and non-compliant. Adopt the JSC behavior instead. + QSKIP("Temporarily skip this test case until the next V8 update is landed into QtJSBackend."); + QJSEngine eng; QVERIFY(eng.evaluate("o = {}; o.__proto__ = parseInt; o.length").isNumber()); - QCOMPARE(eng.evaluate("o.length = 123; o.length").toInt(), 123); - QVERIFY(eng.evaluate("o.hasOwnProperty('length')").toBool()); + QVERIFY(eng.evaluate("o.length = 123; o.length").toInt() != 123); + QVERIFY(!eng.evaluate("o.hasOwnProperty('length')").toBool()); } void tst_QJSEngine::jsReservedWords_data() diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp index a1a51d4965..a4cd325a95 100644 --- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp +++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp @@ -1779,6 +1779,8 @@ Test file/lineNumbers for inline functions. */ void tst_qqmlecmascript::functionErrors() { + QSKIP("Temporarily skip this test case until the next V8 update is landed into QtJSBackend."); + QQmlComponent component(&engine, testFileUrl("functionErrors.qml")); QString url = component.url().toString(); @@ -1796,10 +1798,7 @@ void tst_qqmlecmascript::functionErrors() object = componentTwo.create(); QVERIFY(object != 0); - QString srpname = object->property("srp_name").toString(); - - warning = url + QLatin1String(":16: TypeError: Property 'scarceResource' of object ") + srpname - + QLatin1String(" is not a function"); + warning = url + QLatin1String(":16: TypeError: Property 'scarceResource' of object [object Object] is not a function"); QTest::ignoreMessage(QtWarningMsg, warning.toLatin1().constData()); // we expect a meaningful warning to be printed. QMetaObject::invokeMethod(object, "retrieveScarceResource"); delete object; @@ -4057,6 +4056,8 @@ void tst_qqmlecmascript::scarceResources_other() /* These tests require knowledge of state, since we test values after performing signal or function invocation. */ + QSKIP("Temporarily skip this test case until the next V8 update is landed into QtJSBackend."); + QPixmap origPixmap(100, 100); origPixmap.fill(Qt::blue); QString srp_name, expectedWarning; @@ -4115,8 +4116,7 @@ void tst_qqmlecmascript::scarceResources_other() QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid eo = qobject_cast(QQmlProperty::read(object, "a").value()); QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage. - srp_name = object->property("srp_name").toString(); - expectedWarning = varComponentTwelve.url().toString() + QLatin1String(":16: TypeError: Property 'scarceResource' of object ") + srp_name + QLatin1String(" is not a function"); + expectedWarning = varComponentTwelve.url().toString() + QLatin1String(":16: TypeError: Property 'scarceResource' of object [object Object] is not a function"); QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed. QMetaObject::invokeMethod(object, "retrieveScarceResource"); QVERIFY(!object->property("scarceResourceCopy").isValid()); // due to exception, assignment will NOT have occurred. @@ -4188,8 +4188,7 @@ void tst_qqmlecmascript::scarceResources_other() QVERIFY(!object->property("scarceResourceCopy").isValid()); // not yet assigned, so should not be valid eo = qobject_cast(QQmlProperty::read(object, "a").value()); QVERIFY(eo->scarceResourceIsDetached()); // should be no other copies of it at this stage. - srp_name = object->property("srp_name").toString(); - expectedWarning = variantComponentTwelve.url().toString() + QLatin1String(":16: TypeError: Property 'scarceResource' of object ") + srp_name + QLatin1String(" is not a function"); + expectedWarning = variantComponentTwelve.url().toString() + QLatin1String(":16: TypeError: Property 'scarceResource' of object [object Object] is not a function"); QTest::ignoreMessage(QtWarningMsg, qPrintable(expectedWarning)); // we expect a meaningful warning to be printed. QMetaObject::invokeMethod(object, "retrieveScarceResource"); QVERIFY(!object->property("scarceResourceCopy").isValid()); // due to exception, assignment will NOT have occurred. -- cgit v1.2.3 From ceac3e87b61aac2d17f3a213ef060e1109961293 Mon Sep 17 00:00:00 2001 From: Peter Varga Date: Wed, 9 Jan 2013 14:11:55 +0100 Subject: Reenable temporarily skipped QML tests Change-Id: I1e57b0e39c539648602cc480e296db6c6948ff39 Reviewed-by: Simon Hausmann --- tests/auto/qml/qjsengine/tst_qjsengine.cpp | 2 -- tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp | 4 ---- 2 files changed, 6 deletions(-) diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp index bbde5f4dae..5c27c957fb 100644 --- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp +++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp @@ -2226,8 +2226,6 @@ void tst_QJSEngine::jsContinueInSwitch() void tst_QJSEngine::jsShadowReadOnlyPrototypeProperty() { - QSKIP("Temporarily skip this test case until the next V8 update is landed into QtJSBackend."); - QJSEngine eng; QVERIFY(eng.evaluate("o = {}; o.__proto__ = parseInt; o.length").isNumber()); QVERIFY(eng.evaluate("o.length = 123; o.length").toInt() != 123); diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp index a4cd325a95..8624feff3e 100644 --- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp +++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp @@ -1779,8 +1779,6 @@ Test file/lineNumbers for inline functions. */ void tst_qqmlecmascript::functionErrors() { - QSKIP("Temporarily skip this test case until the next V8 update is landed into QtJSBackend."); - QQmlComponent component(&engine, testFileUrl("functionErrors.qml")); QString url = component.url().toString(); @@ -4056,8 +4054,6 @@ void tst_qqmlecmascript::scarceResources_other() /* These tests require knowledge of state, since we test values after performing signal or function invocation. */ - QSKIP("Temporarily skip this test case until the next V8 update is landed into QtJSBackend."); - QPixmap origPixmap(100, 100); origPixmap.fill(Qt::blue); QString srp_name, expectedWarning; -- cgit v1.2.3 From ad008479ac13b60890eccb982355b90ff738be4e Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Fri, 11 Jan 2013 10:17:24 +0100 Subject: Marking test qquickanimations insignificant on mac. Task-number: QTBUG-29062 Change-Id: Ie4c1de6ceb0e220d7c4f545ffbb14eeb44d0cc03 Reviewed-by: Lars Knoll --- tests/auto/quick/qquickanimations/qquickanimations.pro | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/auto/quick/qquickanimations/qquickanimations.pro b/tests/auto/quick/qquickanimations/qquickanimations.pro index f3cf4832e5..747a2afdff 100644 --- a/tests/auto/quick/qquickanimations/qquickanimations.pro +++ b/tests/auto/quick/qquickanimations/qquickanimations.pro @@ -1,4 +1,5 @@ CONFIG += testcase +mac:CONFIG+=insignificant_test # QTBUG-29062 TARGET = tst_qquickanimations SOURCES += tst_qquickanimations.cpp -- cgit v1.2.3 From 4dbbaf6814d327ec4b182325a8bab59314cfaf23 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 9 Jan 2013 20:34:39 +0100 Subject: Make numeric-literal parsing more robust. For cases where large non-fp numeric literals might end up triggering coversion or rounding errors when stored as doubles when lexing. This is a corner case, but it does trigger a case or two in the ECMA5 test suite (test262). Change-Id: Ie6d355e28379aba9a339c4e345b5d2a0c32d5fdd Reviewed-by: Lars Knoll --- src/qml/qml/parser/qqmljslexer.cpp | 2 +- tests/auto/qml/qqmllanguage/data/literals.qml | 1 + tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/qml/qml/parser/qqmljslexer.cpp b/src/qml/qml/parser/qqmljslexer.cpp index f31140a796..253526b714 100644 --- a/src/qml/qml/parser/qqmljslexer.cpp +++ b/src/qml/qml/parser/qqmljslexer.cpp @@ -891,7 +891,7 @@ again: int Lexer::scanNumber(QChar ch) { if (ch != QLatin1Char('0')) { - double integer = ch.unicode() - '0'; + quint64 integer = ch.unicode() - '0'; QChar n = _char; const QChar *code = _codePtr; diff --git a/tests/auto/qml/qqmllanguage/data/literals.qml b/tests/auto/qml/qqmllanguage/data/literals.qml index 9c6003dc40..462eb4c26d 100644 --- a/tests/auto/qml/qqmllanguage/data/literals.qml +++ b/tests/auto/qml/qqmllanguage/data/literals.qml @@ -8,6 +8,7 @@ QtObject { property variant n5: 3e-12 property variant n6: 3e+12 property variant n7: 0.1e9 + property variant n8: 1152921504606846976 property variant c1: "\b" // special characters property variant c2: "\f" diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index 1c7a0876f6..850e1922a4 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -3053,6 +3053,7 @@ void tst_qqmllanguage::literals_data() QTest::newRow("fp3") << "n5" << QVariant(3e-12); QTest::newRow("fp4") << "n6" << QVariant(3e+12); QTest::newRow("fp5") << "n7" << QVariant(0.1e9); + QTest::newRow("large-int") << "n8" << QVariant((double) 1152921504606846976); QTest::newRow("special1") << "c1" << QVariant(QString("\b")); QTest::newRow("special2") << "c2" << QVariant(QString("\f")); -- cgit v1.2.3 From 5558c55de88b42ecb8a1016e1544bae4104a24fd Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Thu, 10 Jan 2013 11:43:11 +0100 Subject: Fix automatic semicolon insertion before ++/-- operators. Also move the tilde token from isBinop to the lex method, because it is not a binop, but should still be delimited. Change-Id: I532260f4f3ebdde2d38128b41d11bce5a113d1f1 Reviewed-by: Lars Knoll --- src/qml/qml/parser/qqmljslexer.cpp | 4 +--- tests/auto/qml/qmlmin/tst_qmlmin.cpp | 1 + .../qqmlecmascript/data/incrDecrSemicolon_error1.qml | 19 +++++++++++++++++++ tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp | 8 ++++++++ 4 files changed, 29 insertions(+), 3 deletions(-) create mode 100644 tests/auto/qml/qqmlecmascript/data/incrDecrSemicolon_error1.qml diff --git a/src/qml/qml/parser/qqmljslexer.cpp b/src/qml/qml/parser/qqmljslexer.cpp index 253526b714..b2020d86a4 100644 --- a/src/qml/qml/parser/qqmljslexer.cpp +++ b/src/qml/qml/parser/qqmljslexer.cpp @@ -203,7 +203,6 @@ inline bool isBinop(int tok) case Lexer::T_LT_LT_EQ: case Lexer::T_MINUS: case Lexer::T_MINUS_EQ: - case Lexer::T_MINUS_MINUS: case Lexer::T_NOT_EQ: case Lexer::T_NOT_EQ_EQ: case Lexer::T_OR: @@ -211,13 +210,11 @@ inline bool isBinop(int tok) case Lexer::T_OR_OR: case Lexer::T_PLUS: case Lexer::T_PLUS_EQ: - case Lexer::T_PLUS_PLUS: case Lexer::T_REMAINDER: case Lexer::T_REMAINDER_EQ: case Lexer::T_RETURN: case Lexer::T_STAR: case Lexer::T_STAR_EQ: - case Lexer::T_TILDE: case Lexer::T_XOR: case Lexer::T_XOR_EQ: return true; @@ -246,6 +243,7 @@ int Lexer::lex() case T_SEMICOLON: case T_QUESTION: case T_COLON: + case T_TILDE: _delimited = true; break; default: diff --git a/tests/auto/qml/qmlmin/tst_qmlmin.cpp b/tests/auto/qml/qmlmin/tst_qmlmin.cpp index 3fb51512d9..028a04ae25 100644 --- a/tests/auto/qml/qmlmin/tst_qmlmin.cpp +++ b/tests/auto/qml/qmlmin/tst_qmlmin.cpp @@ -112,6 +112,7 @@ void tst_qmlmin::initTestCase() invalidFiles << "tests/auto/qml/qqmlecmascript/data/qtbug_22843.library.js"; invalidFiles << "tests/auto/qml/qquickworkerscript/data/script_error_onLoad.js"; invalidFiles << "tests/auto/qml/parserstress/tests/ecma_3/Unicode/regress-352044-02-n.js"; + invalidFiles << "tests/auto/qml/qqmlecmascript/data/incrDecrSemicolon_error1.qml"; invalidFiles << "tests/auto/qml/qqmlecmascript/data/jsimportfail/malformedFileQualifier.js"; invalidFiles << "tests/auto/qml/qqmlecmascript/data/jsimportfail/malformedImport.js"; invalidFiles << "tests/auto/qml/qqmlecmascript/data/jsimportfail/malformedModule.js"; diff --git a/tests/auto/qml/qqmlecmascript/data/incrDecrSemicolon_error1.qml b/tests/auto/qml/qqmlecmascript/data/incrDecrSemicolon_error1.qml new file mode 100644 index 0000000000..710729cbfe --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/incrDecrSemicolon_error1.qml @@ -0,0 +1,19 @@ +import QtQuick 2.0 + +QtObject { + + // PLEASE NOTE: the function below is whitespace and newline sensitive, + // because that is what the test is all about. + // + // So: DO NOT REFORMAT THE CODE BELOW! + + function code() { +var x=0, y=0; +var z= +x +++ +++ +y + } +} + diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp index 8624feff3e..7be62164b4 100644 --- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp +++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp @@ -268,6 +268,7 @@ private slots: void compatibilitySemicolon(); void incrDecrSemicolon1(); void incrDecrSemicolon2(); + void incrDecrSemicolon_error1(); void unaryExpression(); void switchStatement(); void withStatement(); @@ -6638,6 +6639,13 @@ void tst_qqmlecmascript::incrDecrSemicolon2() QVERIFY(object != 0); } +void tst_qqmlecmascript::incrDecrSemicolon_error1() +{ + QQmlComponent component(&engine, testFileUrl("incrDecrSemicolon_error1.qml")); + QObject *object = component.create(); + QVERIFY(object == 0); +} + void tst_qqmlecmascript::unaryExpression() { QQmlComponent component(&engine, testFileUrl("unaryExpression.qml")); -- cgit v1.2.3 From 0721eb1ea53e6238cf8555424c2e3464ac3db0f6 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Mon, 14 Jan 2013 10:22:12 +0100 Subject: Filter key press events through shortcut map. This allows QQuickItems to use shortcuts. Change-Id: I068109a954d92e4e7bd5e63d2b3523b66855c60a Reviewed-by: Gabriel de Dietrich --- src/quick/items/qquickwindow.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 2cde3cc434..eb3a74f62f 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -1166,6 +1166,12 @@ void QQuickWindow::keyPressEvent(QKeyEvent *e) { Q_D(QQuickWindow); +#ifndef QT_NO_SHORTCUT + // Try looking for a Shortcut before sending key events + if (QGuiApplicationPrivate::instance()->shortcutMap.tryShortcutEvent(this, e)) + return; +#endif + if (d->activeFocusItem) sendEvent(d->activeFocusItem, e); } -- cgit v1.2.3 From ffccfdcbe4fe7f6a7c30462eeb5a7028e31373a0 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Fri, 11 Jan 2013 12:46:26 +0100 Subject: Bump Qt version to 5.1.0 Change-Id: I9491f506aca249511c1eb3ad3baf44f4e4e65a9a Reviewed-by: David Faure (KDE) --- src/qml/doc/qtqml.qdocconf | 10 +++++----- src/quick/doc/qtquick.qdocconf | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/qml/doc/qtqml.qdocconf b/src/qml/doc/qtqml.qdocconf index 06c1c42a92..45fecc1896 100644 --- a/src/qml/doc/qtqml.qdocconf +++ b/src/qml/doc/qtqml.qdocconf @@ -3,19 +3,19 @@ include($QT_INSTALL_DOCS/global/qt-module-defaults.qdocconf) project = QtQml description = Qt QML Reference Documentation url = http://qt-project.org/doc/qt-5.0/qtqml -version = 5.0.1 +version = 5.1.0 qhp.projects = QtQml qhp.QtQml.file = qtqml.qhp -qhp.QtQml.namespace = org.qt-project.qtqml.500 +qhp.QtQml.namespace = org.qt-project.qtqml.510 qhp.QtQml.virtualFolder = qtqml qhp.QtQml.indexTitle = Qt QML qhp.QtQml.indexRoot = -qhp.QtQml.filterAttributes = qtqml 5.0.1 qtrefdoc -qhp.QtQml.customFilters.Qt.name = QtQml 5.0.1 -qhp.QtQml.customFilters.Qt.filterAttributes = qtqml 5.0.1 +qhp.QtQml.filterAttributes = qtqml 5.1.0 qtrefdoc +qhp.QtQml.customFilters.Qt.name = QtQml 5.1.0 +qhp.QtQml.customFilters.Qt.filterAttributes = qtqml 5.1.0 qhp.QtQml.subprojects = classes examples qhp.QtQml.subprojects.classes.title = C++ Classes qhp.QtQml.subprojects.classes.indexTitle = Qt QML Module C++ Classes diff --git a/src/quick/doc/qtquick.qdocconf b/src/quick/doc/qtquick.qdocconf index f75ae06539..de50eb2375 100644 --- a/src/quick/doc/qtquick.qdocconf +++ b/src/quick/doc/qtquick.qdocconf @@ -3,20 +3,20 @@ include($QT_INSTALL_DOCS/global/qt-module-defaults.qdocconf) project = QtQuick description = Qt Quick Reference Documentation url = http://qt-project.org/doc/qt-5.0/qtquick-index.html -version = 5.0.1 +version = 5.1.0 qhp.projects = QtQuick qhp.QtQuick.file = qtquick.qhp -qhp.QtQuick.namespace = org.qt-project.qtquick.500 +qhp.QtQuick.namespace = org.qt-project.qtquick.510 qhp.QtQuick.virtualFolder = qtquick qhp.QtQuick.indexTitle = Qt Quick qhp.QtQuick.indexRoot = -qhp.QtQuick.filterAttributes = qtquick 5.0.1 qtrefdoc -qhp.QtQuick.customFilters.Qt.name = QtQuick 5.0.1 -qhp.QtQuick.customFilters.Qt.filterAttributes = qtquick 5.0.1 +qhp.QtQuick.filterAttributes = qtquick 5.1.0 qtrefdoc +qhp.QtQuick.customFilters.Qt.name = QtQuick 5.1.0 +qhp.QtQuick.customFilters.Qt.filterAttributes = qtquick 5.1.0 qhp.QtQuick.subprojects = qmltypes classes examples qhp.QtQuick.subprojects.qmltypes.title = QML Types qhp.QtQuick.subprojects.qmltypes.indexTitle = Qt Quick QML Types -- cgit v1.2.3 From f3660c25e136966b3113f252f4900bca87f6ed3a Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 15 Jan 2013 13:30:55 +0100 Subject: Make numeric-literal parsing even more robust. The numeric value could overflow a unsigned 64-bit integer, so instead just buffer the string and have libc's strtod handle all the conversion. Change-Id: I220e490ddc22363460b0df65a91b47336e747310 Reviewed-by: Simon Hausmann --- src/qml/qml/parser/qqmljslexer.cpp | 9 ++++++--- tests/auto/qml/qqmllanguage/data/literals.qml | 1 + tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp | 3 ++- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/qml/qml/parser/qqmljslexer.cpp b/src/qml/qml/parser/qqmljslexer.cpp index b2020d86a4..d154cec0c6 100644 --- a/src/qml/qml/parser/qqmljslexer.cpp +++ b/src/qml/qml/parser/qqmljslexer.cpp @@ -889,12 +889,14 @@ again: int Lexer::scanNumber(QChar ch) { if (ch != QLatin1Char('0')) { - quint64 integer = ch.unicode() - '0'; + QByteArray buf; + buf.reserve(64); + buf += ch.toLatin1(); QChar n = _char; const QChar *code = _codePtr; while (n.isDigit()) { - integer = integer * 10 + (n.unicode() - '0'); + buf += n.toLatin1(); n = *code++; } @@ -903,7 +905,8 @@ int Lexer::scanNumber(QChar ch) _codePtr = code - 1; scanChar(); } - _tokenValue = integer; + buf.append('\0'); + _tokenValue = strtod(buf.constData(), 0); return T_NUMERIC_LITERAL; } } else if (_char.isDigit() && !qmlMode()) { diff --git a/tests/auto/qml/qqmllanguage/data/literals.qml b/tests/auto/qml/qqmllanguage/data/literals.qml index 462eb4c26d..564b389760 100644 --- a/tests/auto/qml/qqmllanguage/data/literals.qml +++ b/tests/auto/qml/qqmllanguage/data/literals.qml @@ -9,6 +9,7 @@ QtObject { property variant n6: 3e+12 property variant n7: 0.1e9 property variant n8: 1152921504606846976 + property variant n9: 100000000000000000000 property variant c1: "\b" // special characters property variant c2: "\f" diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index 850e1922a4..d7fa515f1d 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -3053,7 +3053,8 @@ void tst_qqmllanguage::literals_data() QTest::newRow("fp3") << "n5" << QVariant(3e-12); QTest::newRow("fp4") << "n6" << QVariant(3e+12); QTest::newRow("fp5") << "n7" << QVariant(0.1e9); - QTest::newRow("large-int") << "n8" << QVariant((double) 1152921504606846976); + QTest::newRow("large-int1") << "n8" << QVariant((double) 1152921504606846976); + QTest::newRow("large-int2") << "n9" << QVariant(100000000000000000000.); QTest::newRow("special1") << "c1" << QVariant(QString("\b")); QTest::newRow("special2") << "c2" << QVariant(QString("\f")); -- cgit v1.2.3 From a79839dc5287de1448d1fd858db312a7bfa7b581 Mon Sep 17 00:00:00 2001 From: Gunnar Sletta Date: Wed, 16 Jan 2013 16:31:12 +0100 Subject: Disable the QQuickFlickable test in CI. Test failures in this test has been popping up randomly in several different changes. It is poorly written with use of qWait()'s and QCOMPARE instead of using the more robust QTRY_COMPARE and QTRY_VERIFY. Change-Id: Ib56d7b2554b22fa9af767be31f1f181983ed60c7 Reviewed-by: Paul Olav Tvete --- tests/auto/quick/qquickflickable/qquickflickable.pro | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/auto/quick/qquickflickable/qquickflickable.pro b/tests/auto/quick/qquickflickable/qquickflickable.pro index 3ba752bf7d..f61a601130 100644 --- a/tests/auto/quick/qquickflickable/qquickflickable.pro +++ b/tests/auto/quick/qquickflickable/qquickflickable.pro @@ -12,3 +12,4 @@ TESTDATA = data/* CONFIG += parallel_test QT += core-private gui-private v8-private qml-private quick-private testlib DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 +CONFIG+=insignificant_test -- cgit v1.2.3 From 1030d18e0aa39676de448a8553f22df8303b6f76 Mon Sep 17 00:00:00 2001 From: Gunnar Sletta Date: Tue, 15 Jan 2013 15:39:10 +0100 Subject: Disable blending of opaque Rectangle elements Change-Id: If456248c10c24c3d2f2a383f29e72c74d2dee8bf Reviewed-by: Yoann Lopes --- src/quick/scenegraph/qsgdefaultrectanglenode.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/quick/scenegraph/qsgdefaultrectanglenode.cpp b/src/quick/scenegraph/qsgdefaultrectanglenode.cpp index 4d5094d526..6c719129d5 100644 --- a/src/quick/scenegraph/qsgdefaultrectanglenode.cpp +++ b/src/quick/scenegraph/qsgdefaultrectanglenode.cpp @@ -338,6 +338,9 @@ void QSGDefaultRectangleNode::update() updateGeometry(); m_dirty_geometry = false; } + m_material.setFlag(QSGMaterial::Blending, (m_gradient_stops.size() > 0 && !m_gradient_is_opaque) + || m_color.alpha() < 255 + || (m_pen_width > 0 && m_border_color.alpha() < 255)); } void QSGDefaultRectangleNode::updateGeometry() -- cgit v1.2.3 From 1512835ee1425a3e874d2f2dd2b01f1a1ea7b763 Mon Sep 17 00:00:00 2001 From: Gunnar Sletta Date: Fri, 11 Jan 2013 12:35:01 +0100 Subject: Do not force focus for non-focused windows. If a QQuickWindow comes to screen but is not the focus window, such as if it is a child window of another window, it should not have focus by default. Change-Id: If9015bbc179bb101178b3bc8de176a1c71c46023 Reviewed-by: Friedemann Kleint Reviewed-by: Andy Nichols --- src/quick/items/qquickwindow.cpp | 10 ++-------- tests/auto/quick/qquickfocusscope/tst_qquickfocusscope.cpp | 1 - tests/auto/quick/qquickitem/tst_qquickitem.cpp | 2 +- tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp | 2 ++ tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp | 2 ++ tests/auto/quick/qquickwindow/tst_qquickwindow.cpp | 5 +++++ 6 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index eb3a74f62f..b612576c96 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -392,12 +392,6 @@ void QQuickWindowPrivate::init(QQuickWindow *c) contentItemPrivate->windowRefCount = 1; contentItemPrivate->flags |= QQuickItem::ItemIsFocusScope; - // In the absence of a focus in event on some platforms assume the window will - // be activated immediately and set focus on the contentItem - // ### Remove when QTBUG-22415 is resolved. - //It is important that this call happens after the contentItem has a window.. - contentItem->setFocus(true); - windowManager = QQuickWindowManager::instance(); context = windowManager->sceneGraphContext(); q->setSurfaceType(QWindow::OpenGLSurface); @@ -657,10 +651,10 @@ void QQuickWindowPrivate::setFocusInScope(QQuickItem *scope, QQuickItem *item, F } if (!(options & DontChangeFocusProperty)) { -// if (item != contentItem || QGuiApplication::focusWindow() == q) { // QTBUG-22415 + if (item != contentItem || QGuiApplication::focusWindow() == q) { itemPrivate->focus = true; changed << item; -// } + } } if (newActiveFocusItem && contentItem->hasFocus()) { diff --git a/tests/auto/quick/qquickfocusscope/tst_qquickfocusscope.cpp b/tests/auto/quick/qquickfocusscope/tst_qquickfocusscope.cpp index 3f839b4fc0..3ab5a7abff 100644 --- a/tests/auto/quick/qquickfocusscope/tst_qquickfocusscope.cpp +++ b/tests/auto/quick/qquickfocusscope/tst_qquickfocusscope.cpp @@ -525,7 +525,6 @@ void tst_qquickfocusscope::canvasFocus() QSignalSpy scope2ActiveFocusSpy(scope2, SIGNAL(activeFocusChanged(bool))); QSignalSpy item2ActiveFocusSpy(item2, SIGNAL(activeFocusChanged(bool))); - QEXPECT_FAIL("", "QTBUG-22415", Abort); QCOMPARE(rootItem->hasFocus(), false); QCOMPARE(rootItem->hasActiveFocus(), false); QCOMPARE(scope1->hasFocus(), true); diff --git a/tests/auto/quick/qquickitem/tst_qquickitem.cpp b/tests/auto/quick/qquickitem/tst_qquickitem.cpp index 6209f412c3..851ff88c64 100644 --- a/tests/auto/quick/qquickitem/tst_qquickitem.cpp +++ b/tests/auto/quick/qquickitem/tst_qquickitem.cpp @@ -732,7 +732,7 @@ void tst_qquickitem::focusSubItemInNonFocusScope() QQuickView *view = new QQuickView; view->setSource(testFileUrl("focusSubItemInNonFocusScope.qml")); view->show(); - qApp->processEvents(); + QTest::qWaitForWindowActive(view); QQuickItem *dummyItem = view->rootObject()->findChild("dummyItem"); QVERIFY(dummyItem); diff --git a/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp b/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp index f8332d661d..1effc68bfe 100644 --- a/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp +++ b/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp @@ -2223,6 +2223,7 @@ void tst_qquicktextedit::cursorDelegate() QQuickView view(source); view.show(); view.requestActivate(); + QTest::qWaitForWindowActive(&view); QQuickTextEdit *textEditObject = view.rootObject()->findChild("textEditObject"); QVERIFY(textEditObject != 0); // Delegate creation is deferred until focus in or cursor visibility is forced. @@ -2338,6 +2339,7 @@ void tst_qquicktextedit::remoteCursorDelegate() view.setSource(testFileUrl("cursorTestRemote.qml")); view.show(); view.requestActivate(); + QTest::qWaitForWindowActive(&view); QQuickTextEdit *textEditObject = view.rootObject()->findChild("textEditObject"); QVERIFY(textEditObject != 0); diff --git a/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp b/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp index 7e45bd1f14..b25178cbc5 100644 --- a/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp +++ b/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp @@ -2665,6 +2665,7 @@ void tst_qquicktextinput::cursorDelegate() QQuickView view(source); view.show(); view.requestActivate(); + QTest::qWaitForWindowActive(&view); QQuickTextInput *textInputObject = view.rootObject()->findChild("textInputObject"); QVERIFY(textInputObject != 0); // Delegate is created on demand, and so won't be available immediately. Focus in or @@ -2784,6 +2785,7 @@ void tst_qquicktextinput::remoteCursorDelegate() view.setSource(testFileUrl("cursorTestRemote.qml")); view.show(); view.requestActivate(); + QTest::qWaitForWindowActive(&view); QQuickTextInput *textInputObject = view.rootObject()->findChild("textInputObject"); QVERIFY(textInputObject != 0); diff --git a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp index ec9d0e280e..6ba54558f7 100644 --- a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp +++ b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp @@ -997,6 +997,11 @@ void tst_qquickwindow::focusObject() QQuickWindow *window = qobject_cast(created); QVERIFY(window); + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window)); + window->requestActivate(); + QVERIFY(QTest::qWaitForWindowActive(window)); + QQuickItem *item1 = window->findChild("item1"); QVERIFY(item1); item1->setFocus(true); -- cgit v1.2.3 From ebe8b9408cfcd953fae80514aa67e49221541bed Mon Sep 17 00:00:00 2001 From: Gunnar Sletta Date: Wed, 5 Dec 2012 06:27:47 -0800 Subject: Complete rewrite of threaded render loop. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This change starts using the superior implementation of the scene graph render loop which has been worked on in the scenegraph-playground project for a while. It uses a far more straightforward locking/sync paradigm compared to the existing one and is less deadlock and error prone. It also enables the scene graph thread to run on its own when the GUI thread is blocked, enabling threaded animations. This changes also introduces a naming change inside Qt Quick from "Window Manager" -> "Render Loop" as that fits better to what the code does. Change-Id: I1c2170ee04fcbef79660bd7dae6cace647cdb276 Reviewed-by: Samuel Rødal --- src/quick/designer/designersupport.cpp | 4 +- src/quick/designer/designerwindowmanager_p.h | 6 +- src/quick/items/context2d/qquickcontext2d.cpp | 3 - src/quick/items/context2d/qquickcontext2d_p.h | 2 - src/quick/items/items.pri | 8 +- src/quick/items/qquickthreadedwindowmanager.cpp | 910 ----------------- src/quick/items/qquickthreadedwindowmanager_p.h | 181 ---- src/quick/items/qquickwindow.cpp | 70 +- src/quick/items/qquickwindow_p.h | 4 +- src/quick/items/qquickwindowmanager.cpp | 363 ------- src/quick/items/qquickwindowmanager_p.h | 87 -- src/quick/scenegraph/qsgcontext_p.h | 4 +- src/quick/scenegraph/qsgcontextplugin.cpp | 2 +- src/quick/scenegraph/qsgcontextplugin_p.h | 6 +- src/quick/scenegraph/qsgrenderloop.cpp | 363 +++++++ src/quick/scenegraph/qsgrenderloop_p.h | 91 ++ src/quick/scenegraph/qsgthreadedrenderloop.cpp | 1044 ++++++++++++++++++++ src/quick/scenegraph/qsgthreadedrenderloop_p.h | 125 +++ src/quick/scenegraph/scenegraph.pri | 10 +- tests/auto/quick/examples/tst_examples.cpp | 36 +- .../auto/quick/qquickflickable/qquickflickable.pro | 1 - .../quick/qquickflickable/tst_qquickflickable.cpp | 185 ++-- .../quick/qquickgridview/tst_qquickgridview.cpp | 3 +- .../quick/qquickpathview/tst_qquickpathview.cpp | 148 ++- .../quick/qquickwindow/data/showHideAnimate.qml | 5 + tests/auto/quick/qquickwindow/qquickwindow.pro | 3 +- tests/auto/quick/qquickwindow/tst_qquickwindow.cpp | 89 ++ 27 files changed, 1961 insertions(+), 1792 deletions(-) delete mode 100644 src/quick/items/qquickthreadedwindowmanager.cpp delete mode 100644 src/quick/items/qquickthreadedwindowmanager_p.h delete mode 100644 src/quick/items/qquickwindowmanager.cpp delete mode 100644 src/quick/items/qquickwindowmanager_p.h create mode 100644 src/quick/scenegraph/qsgrenderloop.cpp create mode 100644 src/quick/scenegraph/qsgrenderloop_p.h create mode 100644 src/quick/scenegraph/qsgthreadedrenderloop.cpp create mode 100644 src/quick/scenegraph/qsgthreadedrenderloop_p.h create mode 100644 tests/auto/quick/qquickwindow/data/showHideAnimate.qml diff --git a/src/quick/designer/designersupport.cpp b/src/quick/designer/designersupport.cpp index 924dfe9551..915c87e51e 100644 --- a/src/quick/designer/designersupport.cpp +++ b/src/quick/designer/designersupport.cpp @@ -47,7 +47,7 @@ #include #include #include -#include +#include #include #include #include @@ -428,7 +428,7 @@ void DesignerSupport::updateDirtyNode(QQuickItem *item) void DesignerSupport::activateDesignerWindowManager() { - QQuickWindowManager::setInstance(new DesignerWindowManager); + QSGRenderLoop::setInstance(new DesignerWindowManager); } void DesignerSupport::activateDesignerMode() diff --git a/src/quick/designer/designerwindowmanager_p.h b/src/quick/designer/designerwindowmanager_p.h index 0e95b06197..878d236314 100644 --- a/src/quick/designer/designerwindowmanager_p.h +++ b/src/quick/designer/designerwindowmanager_p.h @@ -55,7 +55,7 @@ #include -#include +#include #include #include @@ -69,7 +69,7 @@ class QSGContext; class QAnimationDriver; class QOpenGLContext; -class DesignerWindowManager : public QObject, public QQuickWindowManager +class DesignerWindowManager : public QObject, public QSGRenderLoop { Q_OBJECT public: @@ -88,7 +88,7 @@ public: void maybeUpdate(QQuickWindow *window); void update(QQuickWindow *window); // identical for this implementation. - void releaseResources() { } + void releaseResources(QQuickWindow *) { } QAnimationDriver *animationDriver() const { return 0; } diff --git a/src/quick/items/context2d/qquickcontext2d.cpp b/src/quick/items/context2d/qquickcontext2d.cpp index a46cd6ab70..c857a69f75 100644 --- a/src/quick/items/context2d/qquickcontext2d.cpp +++ b/src/quick/items/context2d/qquickcontext2d.cpp @@ -62,7 +62,6 @@ #include #include #include -#include #if defined(Q_OS_QNX) || defined(Q_OS_LINUX_ANDROID) #include @@ -3348,7 +3347,6 @@ QQuickContext2D::QQuickContext2D(QObject *parent) : QQuickCanvasContext(parent) , m_buffer(new QQuickContext2DCommandBuffer) , m_v8engine(0) - , m_windowManager(0) , m_surface(0) , m_glContext(0) , m_thread(0) @@ -3380,7 +3378,6 @@ void QQuickContext2D::init(QQuickCanvasItem *canvasItem, const QVariantMap &args m_renderTarget = canvasItem->renderTarget(); QQuickWindow *window = canvasItem->window(); - m_windowManager = QQuickWindowPrivate::get(window)->windowManager; m_renderStrategy = canvasItem->renderStrategy(); switch (m_renderTarget) { diff --git a/src/quick/items/context2d/qquickcontext2d_p.h b/src/quick/items/context2d/qquickcontext2d_p.h index 2124c731b2..8d96ab579b 100644 --- a/src/quick/items/context2d/qquickcontext2d_p.h +++ b/src/quick/items/context2d/qquickcontext2d_p.h @@ -70,7 +70,6 @@ class QQuickContext2DCommandBuffer; class QQuickContext2DTexture; class QQuickPixmap; class QSGTexture; -class QQuickWindowManager; class QSurface; class QOpenGLContext; @@ -240,7 +239,6 @@ public: v8::Local m_strokeStyle; v8::Handle m_v8path; QV8Engine *m_v8engine; - QQuickWindowManager *m_windowManager; QSurface *m_surface; QOpenGLContext *m_glContext; v8::Persistent m_v8value; diff --git a/src/quick/items/items.pri b/src/quick/items/items.pri index 13abf7b958..5272a3d5f7 100644 --- a/src/quick/items/items.pri +++ b/src/quick/items/items.pri @@ -74,9 +74,7 @@ HEADERS += \ $$PWD/qquickitemview_p_p.h \ $$PWD/qquickitemviewtransition_p.h \ $$PWD/qquickscreen_p.h \ - $$PWD/qquickwindowmodule_p.h \ - $$PWD/qquickwindowmanager_p.h \ - $$PWD/qquickthreadedwindowmanager_p.h + $$PWD/qquickwindowmodule_p.h SOURCES += \ $$PWD/qquickevents.cpp \ @@ -128,9 +126,7 @@ SOURCES += \ $$PWD/qquickitemview.cpp \ $$PWD/qquickitemviewtransition.cpp \ $$PWD/qquickwindowmodule.cpp \ - $$PWD/qquickscreen.cpp \ - $$PWD/qquickwindowmanager.cpp \ - $$PWD/qquickthreadedwindowmanager.cpp + $$PWD/qquickscreen.cpp SOURCES += \ $$PWD/qquickshadereffect.cpp \ diff --git a/src/quick/items/qquickthreadedwindowmanager.cpp b/src/quick/items/qquickthreadedwindowmanager.cpp deleted file mode 100644 index 6c7b9c0448..0000000000 --- a/src/quick/items/qquickthreadedwindowmanager.cpp +++ /dev/null @@ -1,910 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** 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, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qquickwindowmanager_p.h" -#include "qquickthreadedwindowmanager_p.h" - -#include -#include - -#include -#include -#include - -#include - -#include -#include - -QT_BEGIN_NAMESPACE - -//#define THREAD_DEBUG -extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha); - -const QEvent::Type QEvent_Sync = QEvent::Type(QEvent::User); -const QEvent::Type QEvent_DeferredUpdate = QEvent::Type(QEvent::User + 1); - -#define QQUICK_RENDER_TIMING -#ifdef QQUICK_RENDER_TIMING -DEFINE_BOOL_CONFIG_OPTION(qquick_render_timing, QML_RENDER_TIMING) -static QTime threadTimer; -static int syncTime; -static int renderTime; -static int swapTime; -#endif - - -/* - Threaded Rendering - ================== - - The threaded rendering uses a number of different variables to track potential - states used to handle resizing, initial paint, grabbing and driving animations - while ALWAYS keeping the GL context in the rendering thread and keeping the - overhead of normal one-shot paints and vblank driven animations at a minimum. - - Resize, initial show and grab suffer slightly in this model as they are locked - to the rendering in the rendering thread, but this is a necessary evil for - the system to work. - - Variables that are used: - - Private::animationRunning: This is true while the animations are running, and only - written to inside locks. - - RenderThread::isGuiLocked: This is used to indicate that the GUI thread owns the - lock. This variable is an integer to allow for recursive calls to lockInGui() - without using a recursive mutex. See isPostingSyncEvent. - - RenderThread::isPostingSyncEvent: This variable is set in the render thread just - before the sync event is sent to the GUI thread. It is used to avoid deadlocks - in the case where render thread waits while waiting for GUI to pick up the sync - event and GUI thread gets a resizeEvent, the initial paintEvent or a grab. - When this happens, we use the - exhaustSyncEvent() function to do the sync right there and mark the coming - sync event to be discarded. There can only ever be one sync incoming. - - RenderThread::isRenderBlock: This variable is true when animations are not - running and the render thread has gone to sleep, waiting for more to do. - - RenderThread::isExternalUpdatePending: This variable is set to false when - a new render pass is started and to true in maybeUpdate(). It is an - indication to the render thread that another render pass needs to take - place, rather than the render thread going to sleep after completing its swap. - - RenderThread::doGrab: This variable is set by the grab() function and - tells the renderer to do a grab after rendering is complete and before - swapping happens. - - RenderThread::shouldExit: This variable is used to determine if the render - thread should do a nother pass. It is typically set as a result of show() - and unset as a result of hide() or during shutdown() - - RenderThread::hasExited: Used by the GUI thread to synchronize the shutdown - after shouldExit has been set to true. - */ - - -void QQuickRenderThreadSingleContextWindowManager::initialize() -{ - Q_ASSERT(m_rendered_windows.size()); - - QQuickWindow *win = 0; - for (QHash::const_iterator it = m_rendered_windows.constBegin(); - it != m_rendered_windows.constEnd() && !win; ++it) { - if (QQuickWindowPrivate::get(it.key())->isRenderable()) - win = it.key(); - } - if (!win) - return; - - gl = new QOpenGLContext(); - // Pick up the surface format from one of them - gl->setFormat(win->requestedFormat()); - gl->create(); - if (!gl->makeCurrent(win)) - qWarning("QQuickWindow: makeCurrent() failed..."); - - Q_ASSERT(!sg->isReady()); - sg->initialize(gl); -} - - -/*! - This function is called when the window is created to register the window with - the window manager. - - Called on GUI Thread. - */ - -void QQuickRenderThreadSingleContextWindowManager::show(QQuickWindow *window) -{ -#ifdef THREAD_DEBUG - printf("GUI: Window added to windowing system, %p, %dx%d\n", window, window->width(), window->height()); -#endif - - WindowTracker tracker; - tracker.window = window; - tracker.isVisible = false; - tracker.toBeRemoved = false; - m_tracked_windows << tracker; - - connect(window, SIGNAL(widthChanged(int)), this, SLOT(windowVisibilityChanged()), Qt::DirectConnection); - connect(window, SIGNAL(heightChanged(int)), this, SLOT(windowVisibilityChanged()), Qt::DirectConnection); - - windowVisibilityChanged(); -} - - -void QQuickRenderThreadSingleContextWindowManager::handleAddedWindow(QQuickWindow *window) -{ -#ifdef THREAD_DEBUG - printf(" RenderThread: adding window: %p\n", window); -#endif - - WindowData *data = new WindowData; - data->sizeWasChanged = false; - data->windowSize = window->size(); - data->isVisible = window->isVisible(); - data->isRenderable = QQuickWindowPrivate::get(window)->isRenderable(); - m_rendered_windows[window] = data; - - isExternalUpdatePending = true; -} - - -/*! - Called on Render Thread - */ -void QQuickRenderThreadSingleContextWindowManager::handleAddedWindows() -{ -#ifdef THREAD_DEBUG - printf(" RenderThread: about to add %d\n", m_added_windows.size()); -#endif - - while (m_added_windows.size()) { - QQuickWindow *window = m_added_windows.takeLast(); - handleAddedWindow(window); - } -} - - -/*! - Called on the GUI Thread, from the window' destructor - */ - -void QQuickRenderThreadSingleContextWindowManager::windowDestroyed(QQuickWindow *window) -{ -#ifdef THREAD_DEBUG - printf("GUI: Window destroyed: %p\n", window); -#endif - - hide(window); -} - - -/*! - Called on GUI Thread - */ - -void QQuickRenderThreadSingleContextWindowManager::hide(QQuickWindow *window) -{ -#ifdef THREAD_DEBUG - printf("GUI: Window hidden: %p\n", window); -#endif - - int position = -1; - for (int i=0; i= 0) { - disconnect(window, SIGNAL(widthChanged(int)), this, SLOT(windowVisibilityChanged())); - disconnect(window, SIGNAL(heightChanged(int)), this, SLOT(windowVisibilityChanged())); - windowVisibilityChanged(); - m_tracked_windows.removeAt(position); - } - -#ifdef THREAD_DEBUG - printf("GUI: Window removal completed... %p\n", window); -#endif -} - -/*! - Called on Render Thread - */ -void QQuickRenderThreadSingleContextWindowManager::handleRemovedWindows(bool clearGLContext) -{ -#ifdef THREAD_DEBUG - printf(" RenderThread: about to remove %d\n", m_removed_windows.size()); -#endif - - bool removedAnything = false; - while (m_removed_windows.size()) { - QQuickWindow *window = m_removed_windows.takeLast(); -#ifdef THREAD_DEBUG - printf(" RenderThread: removing %p\n", window); -#endif - - QQuickWindowPrivate::get(window)->cleanupNodesOnShutdown(); - delete m_rendered_windows.take(window); - removedAnything = true; - } - - // If a window is removed because it has been hidden it will take with it - // the gl context (at least on Mac) if bound, so disconnect the gl context - // from anything - if (removedAnything && clearGLContext) - gl->doneCurrent(); -} - - - -/*! - Called on GUI Thread - */ - -void QQuickRenderThreadSingleContextWindowManager::windowVisibilityChanged() -{ - bool anyoneShowing = false; - QList toAdd, toRemove; - - // Not optimal, but also not frequently used... - for (int i=0; i(m_tracked_windows.at(i)); - QQuickWindow *win = t.window; - - Q_ASSERT(win->isVisible() || QQuickWindowPrivate::get(win)->renderWithoutShowing || t.toBeRemoved); - bool windowVisible = win->width() > 0 && win->height() > 0; - anyoneShowing |= (windowVisible && !t.toBeRemoved); - - if ((!windowVisible && t.isVisible) || t.toBeRemoved) { - toRemove << win; - } else if (windowVisible && !t.isVisible) { - toAdd << win; - } - t.isVisible = windowVisible; - } - - if (isRunning()) { - if (!anyoneShowing) { - stopRendering(); - } else { - lockInGui(); - exhaustSyncEvent(); - m_added_windows << toAdd; - m_removed_windows << toRemove; - while (isRunning() && (m_added_windows.size() || m_removed_windows.size())) { - if (isRenderBlocked) - wake(); - wait(); - } - unlockInGui(); - } - - } else if (anyoneShowing) { - Q_ASSERT(toRemove.isEmpty()); // since loop is not running, nothing is showing now - for (int i=0; i::const_iterator it = m_rendered_windows.constBegin(); - it != m_rendered_windows.constEnd(); ++it) { - QQuickWindow *window = it.key(); - -#ifdef THREAD_DEBUG - printf(" RenderThread: Syncing window: %p\n", window); -#endif - - WindowData *windowData = it.value(); - QQuickWindowPrivate *windowPrivate = QQuickWindowPrivate::get(window); - - windowData->isRenderable = windowPrivate->isRenderable(); - - if (windowData->isRenderable) { - gl->makeCurrent(window); - - if (windowData->viewportSize != windowData->windowSize) { -#ifdef THREAD_DEBUG - printf(" RenderThread: --- window has changed size...\n"); -#endif - windowData->viewportSize = windowData->windowSize; - windowData->sizeWasChanged = true; - glViewport(0, 0, windowData->viewportSize.width(), windowData->viewportSize.height()); - } - - windowPrivate->syncSceneGraph(); - } - } - inSync = false; - - // Wake GUI after sync to let it continue animating and event processing. - wake(); - unlock(); -#ifdef THREAD_DEBUG - printf(" RenderThread: sync done\n"); -#endif -#ifdef QQUICK_RENDER_TIMING - if (qquick_render_timing()) - syncTime = threadTimer.elapsed(); -#endif - - for (QHash::const_iterator it = m_rendered_windows.constBegin(); - it != m_rendered_windows.constEnd(); ++it) { - QQuickWindow *window = it.key(); - WindowData *windowData = it.value(); - QQuickWindowPrivate *windowPrivate = QQuickWindowPrivate::get(window); - - if (!windowData->isRenderable) - continue; - -#ifdef THREAD_DEBUG - printf(" RenderThread: Rendering window %p\n", window); -#endif - - Q_ASSERT(windowData->windowSize.width() > 0 && windowData->windowSize.height() > 0); - -#ifdef THREAD_DEBUG - printf(" RenderThread: --- rendering at size %dx%d\n", - windowData->viewportSize.width(), windowData->viewportSize.height() - ); -#endif - - // We only need to re-makeCurrent when we have multiple surfaces. - if (m_rendered_windows.size() > 1) - gl->makeCurrent(window); - - windowPrivate->renderSceneGraph(windowData->viewportSize); -#ifdef QQUICK_RENDER_TIMING - if (qquick_render_timing()) - renderTime = threadTimer.elapsed() - syncTime; -#endif - - // The content of the target buffer is undefined after swap() so grab needs - // to happen before swap(); - if (window == windowToGrab) { -#ifdef THREAD_DEBUG - printf(" RenderThread: --- grabbing...\n"); -#endif - grabContent = qt_gl_read_framebuffer(windowData->windowSize, false, false); - windowToGrab = 0; - } - -#ifdef THREAD_DEBUG - printf(" RenderThread: --- wait for swap...\n"); -#endif - - if (windowData->isVisible && window->isExposed()) - gl->swapBuffers(window); - - windowPrivate->fireFrameSwapped(); -#ifdef THREAD_DEBUG - printf(" RenderThread: --- swap complete...\n"); -#endif - - } - -#ifdef QQUICK_RENDER_TIMING - if (qquick_render_timing()) { - static QTime lastFrameTime = QTime::currentTime(); - swapTime = threadTimer.elapsed() - renderTime - syncTime; - qDebug() << "- Breakdown of frame time; sync:" << syncTime - << "ms render:" << renderTime << "ms swap:" << swapTime - << "ms total:" << swapTime + renderTime + syncTime - << "ms time since last frame:" << (lastFrameTime.msecsTo(QTime::currentTime())) - << "ms"; - lastFrameTime = QTime::currentTime(); - } -#endif - - lock(); - - handleRemovedWindows(); - - // Update sizes... - for (QHash::const_iterator it = m_rendered_windows.constBegin(); - it != m_rendered_windows.constEnd(); ++it) { - WindowData *windowData = it.value(); - if (windowData->sizeWasChanged) { - windowData->renderedSize = windowData->viewportSize; - windowData->sizeWasChanged = false; - } - } - - - // Wake the GUI thread now that rendering is complete, to signal that painting - // is done, resizing is done or grabbing is completed. For grabbing, we're - // signalling this much later than needed (we could have done it before swap) - // but we don't want to lock an extra time. - wake(); - - if (!animationRunning && !isExternalUpdatePending && !shouldExit && !windowToGrab) { -#ifdef THREAD_DEBUG - printf(" RenderThread: nothing to do, going to sleep...\n"); -#endif - isRenderBlocked = true; - wait(); - isRenderBlocked = false; - } - - unlock(); - - QCoreApplication::processEvents(); - - // Process any "deleteLater" objects... - QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); - } - -#ifdef THREAD_DEBUG - printf(" RenderThread: deleting all outstanding nodes\n"); -#endif - - m_removed_windows << m_rendered_windows.keys(); - handleRemovedWindows(false); - - sg->invalidate(); - - gl->doneCurrent(); - delete gl; - gl = 0; - -#ifdef THREAD_DEBUG - printf(" RenderThread: render loop exited... Good Night!\n"); -#endif - - lock(); - hasExited = true; - -#ifdef THREAD_DEBUG - printf(" RenderThread: waking GUI for final sleep..\n"); -#endif - wake(); - unlock(); - -#ifdef THREAD_DEBUG - printf(" RenderThread: All done...\n"); -#endif -} - -bool QQuickRenderThreadSingleContextWindowManager::event(QEvent *e) -{ - Q_ASSERT(QThread::currentThread() == qApp->thread()); - - if (e->type() == QEvent_Sync) { - - // If all windowes have been hidden, ignore the event - if (!isRunning()) - return true; - - if (!syncAlreadyHappened) - sync(false); - - syncAlreadyHappened = false; - - if (animationRunning) { -#ifdef THREAD_DEBUG - printf("GUI: Advancing animations...\n"); -#endif - - animDriver->advance(); - -#ifdef THREAD_DEBUG - printf("GUI: Animations advanced...\n"); -#endif - } - - return true; - } else if (e->type() == QEvent_DeferredUpdate) { - handleDeferredUpdate(); - - } else if (e->type() == QEvent::Timer) { -#ifdef THREAD_DEBUG - printf("GUI: Animations advanced via timer...\n"); -#endif - animDriver->advance(); - } - - return QThread::event(e); -} - - - -void QQuickRenderThreadSingleContextWindowManager::exhaustSyncEvent() -{ - if (isPostingSyncEvent) { - sync(true); - syncAlreadyHappened = true; - } -} - - - -void QQuickRenderThreadSingleContextWindowManager::sync(bool guiAlreadyLocked) -{ -#ifdef THREAD_DEBUG - printf("GUI: sync - %s\n", guiAlreadyLocked ? "outside event" : "inside event"); -#endif - if (!guiAlreadyLocked) - lockInGui(); - - for (QHash::const_iterator it = m_rendered_windows.constBegin(); - it != m_rendered_windows.constEnd(); ++it) { - QQuickWindowPrivate::get(it.key())->polishItems(); - } - - wake(); - wait(); - - if (!guiAlreadyLocked) - unlockInGui(); -} - - - - -/*! - Acquires the mutex for the GUI thread. The function uses the isGuiLocked - variable to keep track of how many recursion levels the gui is locked with. - We only actually acquire the mutex for the first level to avoid deadlocking - ourselves. - */ - -void QQuickRenderThreadSingleContextWindowManager::lockInGui() -{ - if (++isGuiLocked == 1) - lock(); - -#ifdef THREAD_DEBUG - printf("GUI: acquired lock... level=%d\n", isGuiLocked); -#endif -} - - - -void QQuickRenderThreadSingleContextWindowManager::unlockInGui() -{ -#ifdef THREAD_DEBUG - printf("GUI: releasing lock... level=%d\n", isGuiLocked); -#endif - - if (--isGuiLocked == 0) - unlock(); -} - - - - -void QQuickRenderThreadSingleContextWindowManager::animationStarted() -{ -#ifdef THREAD_DEBUG - printf("GUI: animationStarted()\n"); -#endif - - if (!isRunning()) { - animationTimer = startTimer(1000/60); - return; - } - - lockInGui(); - - animationRunning = true; - - if (isRenderBlocked) - wake(); - - unlockInGui(); -} - - - -void QQuickRenderThreadSingleContextWindowManager::animationStopped() -{ -#ifdef THREAD_DEBUG - printf("GUI: animationStopped()...\n"); -#endif - - if (!isRunning()) { - killTimer(animationTimer); - animationTimer = -1; - return; - } - - lockInGui(); - animationRunning = false; - unlockInGui(); -} - - -void QQuickRenderThreadSingleContextWindowManager::exposureChanged(QQuickWindow *window) -{ - Q_UNUSED(window); -#ifdef THREAD_DEBUG - printf("GUI: exposure changed: %p\n", window); -#endif - - if (window->isExposed()) - maybeUpdate(window); - -#ifdef THREAD_DEBUG - printf("GUI: exposure changed done: %p\n", window); -#endif -} - - - -void QQuickRenderThreadSingleContextWindowManager::resize(QQuickWindow *window, const QSize &size) -{ -#ifdef THREAD_DEBUG - printf("GUI: Resize Event: %p = %dx%d\n", window, size.width(), size.height()); -#endif - - // If the rendering thread is not running we do not need to do anything. - // Also if the window is being resized to an invalid size, it will be removed - // by the windowVisibilityChanged slot as result of width/heightcChanged() - if (!isRunning() || size.width() <= 0 || size.height() <= 0) - return; - - lockInGui(); - exhaustSyncEvent(); - - WindowData *windowData = m_rendered_windows.value(window); - if (windowData) { - windowData->windowSize = size; - while (isRunning() && windowData->renderedSize != size && size.width() > 0 && size.height() > 0) { - if (isRenderBlocked) - wake(); - wait(); - } - } - unlockInGui(); - -#ifdef THREAD_DEBUG - printf("GUI: Resize done: %p\n", window); -#endif -} - - - -void QQuickRenderThreadSingleContextWindowManager::startRendering() -{ -#ifdef THREAD_DEBUG - printf("GUI: Starting Render Thread\n"); -#endif - hasExited = false; - shouldExit = false; - isGuiLocked = 0; - isPostingSyncEvent = false; - syncAlreadyHappened = false; - inSync = false; - - lockInGui(); - animationRunning = animDriver->isRunning(); - start(); // Start the render thread... - wait(); - unlockInGui(); - - // Animations will now be driven from the rendering thread. - if (animationTimer >= 0) { - killTimer(animationTimer); - animationTimer = -1; - } - - -} - - - -void QQuickRenderThreadSingleContextWindowManager::stopRendering() -{ -#ifdef THREAD_DEBUG - printf("GUI: stopping render thread\n"); -#endif - - lockInGui(); - exhaustSyncEvent(); - shouldExit = true; - - if (isRenderBlocked) { -#ifdef THREAD_DEBUG - printf("GUI: waking up render thread\n"); -#endif - wake(); - } - - while (!hasExited) { -#ifdef THREAD_DEBUG - printf("GUI: waiting for render thread to have exited..\n"); -#endif - wait(); - } - - unlockInGui(); - -#ifdef THREAD_DEBUG - printf("GUI: waiting for render thread to terminate..\n"); -#endif - // Actually wait for the thread to terminate. Otherwise we can delete it - // too early and crash. - QThread::wait(); - -#ifdef THREAD_DEBUG - printf("GUI: thread has terminated and we're all good..\n"); -#endif - - // Activate timer to keep animations running - if (animDriver->isRunning()) - animationTimer = startTimer(1000/60); -} - - - -QImage QQuickRenderThreadSingleContextWindowManager::grab(QQuickWindow *window) -{ - if (!isRunning()) - return QImage(); - - if (QThread::currentThread() != qApp->thread()) { - qWarning("QQuickWindow::grabFrameBuffer: can only be called from the GUI thread"); - return QImage(); - } else if (window->size().width() <= 0 || window->size().height() <= 0 ) { - qWarning("QQuickWindow::grabFrameBuffer: Can't grab a Window with size %dx%d", window->size().width(), window->size().height()); - return QImage(); - } - -#ifdef THREAD_DEBUG - printf("GUI: doing a pixelwise grab..\n"); -#endif - - lockInGui(); - exhaustSyncEvent(); - - windowToGrab = window; - while (isRunning() && windowToGrab) { - if (isRenderBlocked) - wake(); - wait(); - } - - QImage grabbed = grabContent; - grabContent = QImage(); - - unlockInGui(); - - return grabbed; -} - - -void QQuickRenderThreadSingleContextWindowManager::handleDeferredUpdate() -{ -#ifdef THREAD_DEBUG - printf("GUI: handling update to ourselves...\n"); -#endif - - isDeferredUpdatePosted = false; - - lockInGui(); - isExternalUpdatePending = true; - if (isRenderBlocked) - wake(); - unlockInGui(); -} - -void QQuickRenderThreadSingleContextWindowManager::maybeUpdate(QQuickWindow *) -{ - Q_ASSERT_X(QThread::currentThread() == QCoreApplication::instance()->thread() || inSync, - "QQuickWindow::update", - "Function can only be called from GUI thread or during QQuickItem::updatePaintNode()"); - - if (inSync) { - isExternalUpdatePending = true; - - } else if (!isDeferredUpdatePosted) { -#ifdef THREAD_DEBUG - printf("GUI: posting update to ourselves...\n"); -#endif - isDeferredUpdatePosted = true; - QCoreApplication::postEvent(this, new QEvent(QEvent_DeferredUpdate)); - } - -} - -QT_END_NAMESPACE diff --git a/src/quick/items/qquickthreadedwindowmanager_p.h b/src/quick/items/qquickthreadedwindowmanager_p.h deleted file mode 100644 index 76325e2d4f..0000000000 --- a/src/quick/items/qquickthreadedwindowmanager_p.h +++ /dev/null @@ -1,181 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** 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, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQUICKTHREADEDWINDOWMANAGER_P_H -#define QQUICKTHREADEDWINDOWMANAGER_P_H - -#include "qquickwindowmanager_p.h" - -#include -#include -#include -#include - -#include -#include -#include - -QT_BEGIN_NAMESPACE - -class QQuickRenderThreadSingleContextWindowManager : public QThread, public QQuickWindowManager -{ - Q_OBJECT -public: - QQuickRenderThreadSingleContextWindowManager() - : sg(QSGContext::createDefaultContext()) - , gl(0) - , animationTimer(-1) - , isGuiLocked(0) - , animationRunning(false) - , isPostingSyncEvent(false) - , isRenderBlocked(false) - , isExternalUpdatePending(false) - , syncAlreadyHappened(false) - , inSync(false) - , shouldExit(false) - , hasExited(false) - , isDeferredUpdatePosted(false) - , windowToGrab(0) - { - sg->moveToThread(this); - - animDriver = sg->createAnimationDriver(this); - animDriver->install(); - connect(animDriver, SIGNAL(started()), this, SLOT(animationStarted())); - connect(animDriver, SIGNAL(stopped()), this, SLOT(animationStopped())); - } - - QSGContext *sceneGraphContext() const { return sg; } - - void releaseResources() { } - - void show(QQuickWindow *window); - void hide(QQuickWindow *window); - - void windowDestroyed(QQuickWindow *window); - - void exposureChanged(QQuickWindow *window); - QImage grab(QQuickWindow *window); - void resize(QQuickWindow *window, const QSize &size); - void handleDeferredUpdate(); - void maybeUpdate(QQuickWindow *window); - void update(QQuickWindow *window) { maybeUpdate(window); } // identical for this implementation - - void startRendering(); - void stopRendering(); - - void exhaustSyncEvent(); - void sync(bool guiAlreadyLocked); - - void initialize(); - - bool event(QEvent *); - - inline void lock() { mutex.lock(); } - inline void unlock() { mutex.unlock(); } - inline void wait() { condition.wait(&mutex); } - inline void wake() { condition.wakeOne(); } - void lockInGui(); - void unlockInGui(); - - void run(); - - QAnimationDriver *animationDriver() const { return animDriver; } - -public slots: - void animationStarted(); - void animationStopped(); - void windowVisibilityChanged(); - -private: - void handleAddedWindows(); - void handleAddedWindow(QQuickWindow *window); - void handleRemovedWindows(bool clearGLContext = true); - - QSGContext *sg; - QOpenGLContext *gl; - QAnimationDriver *animDriver; - int animationTimer; - - QMutex mutex; - QWaitCondition condition; - - int isGuiLocked; - uint animationRunning: 1; - uint isPostingSyncEvent : 1; - uint isRenderBlocked : 1; - uint isExternalUpdatePending : 1; - uint syncAlreadyHappened : 1; - uint inSync : 1; - uint shouldExit : 1; - uint hasExited : 1; - uint isDeferredUpdatePosted : 1; - - QQuickWindow *windowToGrab; - QImage grabContent; - - struct WindowData { - QSize renderedSize; - QSize windowSize; - QSize viewportSize; - - uint sizeWasChanged : 1; - uint isVisible : 1; - uint isRenderable : 1; - }; - - QHash m_rendered_windows; - - struct WindowTracker { - QQuickWindow *window; - uint isVisible : 1; - uint toBeRemoved : 1; - }; - - QList m_tracked_windows; - - QList m_removed_windows; - QList m_added_windows; -}; - -QT_END_NAMESPACE - -#endif // QQUICKTHREADEDWINDOWMANAGER_P_H diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 1b76dd7390..bf82423ca9 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -50,7 +50,7 @@ #include #include -#include +#include #include #include @@ -366,8 +366,8 @@ QQuickWindowPrivate::QQuickWindowPrivate() , windowManager(0) , clearColor(Qt::white) , clearBeforeRendering(true) - , persistentGLContext(false) - , persistentSceneGraph(false) + , persistentGLContext(true) + , persistentSceneGraph(true) , lastWheelEventAccepted(false) , renderTarget(0) , renderTargetId(0) @@ -392,7 +392,7 @@ void QQuickWindowPrivate::init(QQuickWindow *c) contentItemPrivate->windowRefCount = 1; contentItemPrivate->flags |= QQuickItem::ItemIsFocusScope; - windowManager = QQuickWindowManager::instance(); + windowManager = QSGRenderLoop::instance(); context = windowManager->sceneGraphContext(); q->setSurfaceType(QWindow::OpenGLSurface); q->setFormat(context->defaultSurfaceFormat()); @@ -923,6 +923,8 @@ QQuickWindow::QQuickWindow(QWindow *parent) d->init(this); } + + /*! \internal */ @@ -966,20 +968,32 @@ QQuickWindow::~QQuickWindow() void QQuickWindow::releaseResources() { Q_D(QQuickWindow); - d->windowManager->releaseResources(); + d->windowManager->releaseResources(this); QQuickPixmap::purgeCache(); } /*! - Sets whether the OpenGL context can be released as a part of a call to - releaseResources() to \a persistent. + Sets whether the OpenGL context can be released to \a + persistent. The default value is true. + + The OpenGL context can be released to free up graphics resources + when the window is obscured, hidden or not rendering. When this + happens is implementation specific. - The OpenGL context might still be released when the user makes an explicit - call to hide(). + The QOpenGLContext::aboutToBeDestroyed() signal is emitted from + the QQuickWindow::openglContext() when the OpenGL context is about + to be released. The QQuickWindow::sceneGraphInitialized() signal + is emitted when a new OpenGL context is created for this + window. Make a Qt::DirectConnection to these signals to be + notified. - \sa setPersistentSceneGraph() + The OpenGL context is still released when the last QQuickWindow is + deleted. + + \sa setPersistentSceneGraph(), + QOpenGLContext::aboutToBeDestroyed(), sceneGraphInitialized() */ void QQuickWindow::setPersistentOpenGLContext(bool persistent) @@ -989,9 +1003,13 @@ void QQuickWindow::setPersistentOpenGLContext(bool persistent) } + /*! - Returns whether the OpenGL context can be released as a part of a call to - releaseResources(). + Returns whether the OpenGL context can be released during the + lifetime of the QQuickWindow. + + \note This is a hint. When and how this happens is implementation + specific. */ bool QQuickWindow::isPersistentOpenGLContext() const @@ -1003,13 +1021,24 @@ bool QQuickWindow::isPersistentOpenGLContext() const /*! - Sets whether the scene graph nodes and resources can be released as a - part of a call to releaseResources() to \a persistent. + Sets whether the scene graph nodes and resources can be released + to \a persistent. The default value is true. + + The scene graph nodes and resources can be released to free up + graphics resources when the window is obscured, hidden or not + rendering. When this happens is implementation specific. - The scene graph nodes and resources might still be released when the user - makes an explicit call to hide(). + The QQuickWindow::sceneGraphInvalidated() signal is emitted when + cleanup occurs. The QQuickWindow::sceneGraphInitialized() signal + is emitted when a new scene graph is recreated for this + window. Make a Qt::DirectConnection to these signals to be + notified. - \sa setPersistentOpenGLContext() + The scene graph nodes and resources are still released when the + last QQuickWindow is deleted. + + \sa setPersistentOpenGLContext(), + sceneGraphInvalidated(), sceneGraphInitialized() */ void QQuickWindow::setPersistentSceneGraph(bool persistent) @@ -1021,8 +1050,11 @@ void QQuickWindow::setPersistentSceneGraph(bool persistent) /*! - Returns whether the scene graph nodes and resources can be released as a part - of a call to releaseResources(). + Returns whether the scene graph nodes and resources can be + released during the lifetime of this QQuickWindow. + + \note This is a hint. When and how this happens is implementation + specific. */ bool QQuickWindow::isPersistentSceneGraph() const diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index da2ae8284d..f272f30f8e 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -73,7 +73,7 @@ QT_BEGIN_NAMESPACE //Make it easy to identify and customize the root item if needed -class QQuickWindowManager; +class QSGRenderLoop; class QQuickRootItem : public QQuickItem { @@ -200,7 +200,7 @@ public: QSGContext *context; QSGRenderer *renderer; - QQuickWindowManager *windowManager; + QSGRenderLoop *windowManager; QColor clearColor; diff --git a/src/quick/items/qquickwindowmanager.cpp b/src/quick/items/qquickwindowmanager.cpp deleted file mode 100644 index 69aa63c278..0000000000 --- a/src/quick/items/qquickwindowmanager.cpp +++ /dev/null @@ -1,363 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** 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, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qquickwindowmanager_p.h" -#include "qquickthreadedwindowmanager_p.h" - -#include -#include -#include - -#include -#include -#include - -#include - -#include -#include -#include - -QT_BEGIN_NAMESPACE - -DEFINE_BOOL_CONFIG_OPTION(qquick_render_timing, QML_RENDER_TIMING) - -extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha); - -/*! - expectations for this manager to work: - - one opengl context to render multiple windows - - OpenGL pipeline will not block for vsync in swap - - OpenGL pipeline will block based on a full buffer queue. - - Multiple screens can share the OpenGL context - - Animations are advanced for all windows once per swap - */ - -DEFINE_BOOL_CONFIG_OPTION(qmlNoThreadedRenderer, QML_BAD_GUI_RENDER_LOOP); -DEFINE_BOOL_CONFIG_OPTION(qmlForceThreadedRenderer, QML_FORCE_THREADED_RENDERER); // Might trigger graphics driver threading bugs, use at own risk - -QQuickWindowManager *QQuickWindowManager::s_instance = 0; - -QQuickWindowManager::~QQuickWindowManager() -{ -} - -class QQuickTrivialWindowManager : public QObject, public QQuickWindowManager -{ - Q_OBJECT -public: - QQuickTrivialWindowManager(); - - void show(QQuickWindow *window); - void hide(QQuickWindow *window); - - void windowDestroyed(QQuickWindow *window); - - void initializeGL(); - void renderWindow(QQuickWindow *window); - void exposureChanged(QQuickWindow *window); - QImage grab(QQuickWindow *window); - void resize(QQuickWindow *window, const QSize &size); - - void maybeUpdate(QQuickWindow *window); - void update(QQuickWindow *window) { maybeUpdate(window); } // identical for this implementation. - - void releaseResources() { } - - QAnimationDriver *animationDriver() const { return 0; } - - QSGContext *sceneGraphContext() const; - - bool event(QEvent *); - - struct WindowData { - bool updatePending : 1; - bool grabOnly : 1; - }; - - QHash m_windows; - - QOpenGLContext *gl; - QSGContext *sg; - - QImage grabContent; - - bool eventPending; -}; - - -QQuickWindowManager *QQuickWindowManager::instance() -{ - if (!s_instance) { - - s_instance = QSGContext::createWindowManager(); - - bool bufferQueuing = QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::BufferQueueingOpenGL); -#ifdef Q_OS_WIN - bool fancy = false; // QTBUG-28037 -#else - bool fancy = QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ThreadedOpenGL); -#endif - if (qmlNoThreadedRenderer()) - fancy = false; - else if (qmlForceThreadedRenderer()) - fancy = true; - - // Enable fixed animation steps... - QByteArray fixed = qgetenv("QML_FIXED_ANIMATION_STEP"); - bool fixedAnimationSteps = bufferQueuing; - if (fixed == "no") - fixedAnimationSteps = false; - else if (fixed.length()) - fixedAnimationSteps = true; - if (fixedAnimationSteps) - QUnifiedTimer::instance(true)->setConsistentTiming(true); - - if (!s_instance) { - s_instance = fancy - ? (QQuickWindowManager*) new QQuickRenderThreadSingleContextWindowManager - : (QQuickWindowManager*) new QQuickTrivialWindowManager; - } - } - return s_instance; -} - -void QQuickWindowManager::setInstance(QQuickWindowManager *instance) -{ - Q_ASSERT(!s_instance); - s_instance = instance; -} - -QQuickTrivialWindowManager::QQuickTrivialWindowManager() - : gl(0) - , eventPending(false) -{ - sg = QSGContext::createDefaultContext(); -} - - -void QQuickTrivialWindowManager::show(QQuickWindow *window) -{ - WindowData data; - data.updatePending = false; - data.grabOnly = false; - m_windows[window] = data; - - maybeUpdate(window); -} - -void QQuickTrivialWindowManager::hide(QQuickWindow *window) -{ - if (!m_windows.contains(window)) - return; - - m_windows.remove(window); - QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window); - cd->cleanupNodesOnShutdown(); - - if (m_windows.size() == 0) { - sg->invalidate(); - delete gl; - gl = 0; - } -} - -void QQuickTrivialWindowManager::windowDestroyed(QQuickWindow *window) -{ - hide(window); -} - -void QQuickTrivialWindowManager::renderWindow(QQuickWindow *window) -{ - bool renderWithoutShowing = QQuickWindowPrivate::get(window)->renderWithoutShowing; - if ((!window->isExposed() && !renderWithoutShowing) || !m_windows.contains(window)) - return; - - WindowData &data = const_cast(m_windows[window]); - - QQuickWindow *masterWindow = 0; - if (!window->isVisible() && !renderWithoutShowing) { - // Find a "proper surface" to bind... - for (QHash::const_iterator it = m_windows.constBegin(); - it != m_windows.constEnd() && !masterWindow; ++it) { - if (it.key()->isVisible()) - masterWindow = it.key(); - } - } else { - masterWindow = window; - } - - if (!masterWindow) - return; - - if (!QQuickWindowPrivate::get(masterWindow)->isRenderable()) { - qWarning().nospace() - << "Unable to find a renderable master window " - << masterWindow << "when trying to render" - << window << " (" << window->geometry() << ")."; - return; - } - - if (!gl) { - gl = new QOpenGLContext(); - gl->setFormat(masterWindow->requestedFormat()); - gl->create(); - if (!gl->makeCurrent(masterWindow)) - qWarning("QQuickWindow: makeCurrent() failed..."); - sg->initialize(gl); - } else { - gl->makeCurrent(masterWindow); - } - - bool alsoSwap = data.updatePending; - data.updatePending = false; - - QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window); - cd->polishItems(); - - int renderTime = 0, syncTime = 0; - QTime renderTimer; - if (qquick_render_timing()) - renderTimer.start(); - - cd->syncSceneGraph(); - - if (qquick_render_timing()) - syncTime = renderTimer.elapsed(); - - cd->renderSceneGraph(window->size()); - - if (qquick_render_timing()) - renderTime = renderTimer.elapsed() - syncTime; - - if (data.grabOnly) { - grabContent = qt_gl_read_framebuffer(window->size(), false, false); - data.grabOnly = false; - } - - if (alsoSwap && window->isVisible()) { - gl->swapBuffers(window); - cd->fireFrameSwapped(); - } - - if (qquick_render_timing()) { - static QTime lastFrameTime = QTime::currentTime(); - const int swapTime = renderTimer.elapsed() - renderTime - syncTime; - qDebug() << "- Breakdown of frame time; sync:" << syncTime - << "ms render:" << renderTime << "ms swap:" << swapTime - << "ms total:" << swapTime + renderTime + syncTime - << "ms time since last frame:" << (lastFrameTime.msecsTo(QTime::currentTime())) - << "ms"; - lastFrameTime = QTime::currentTime(); - } - - // Might have been set during syncSceneGraph() - if (data.updatePending) - maybeUpdate(window); -} - -void QQuickTrivialWindowManager::exposureChanged(QQuickWindow *window) -{ - if (window->isExposed()) - maybeUpdate(window); -} - -QImage QQuickTrivialWindowManager::grab(QQuickWindow *window) -{ - if (!m_windows.contains(window)) - return QImage(); - - m_windows[window].grabOnly = true; - - renderWindow(window); - - QImage grabbed = grabContent; - grabContent = QImage(); - return grabbed; -} - - - -void QQuickTrivialWindowManager::resize(QQuickWindow *, const QSize &) -{ -} - - - -void QQuickTrivialWindowManager::maybeUpdate(QQuickWindow *window) -{ - if (!m_windows.contains(window)) - return; - - m_windows[window].updatePending = true; - - if (!eventPending) { - QCoreApplication::postEvent(this, new QEvent(QEvent::User)); - eventPending = true; - } -} - - - -QSGContext *QQuickTrivialWindowManager::sceneGraphContext() const -{ - return sg; -} - - -bool QQuickTrivialWindowManager::event(QEvent *e) -{ - if (e->type() == QEvent::User) { - eventPending = false; - for (QHash::const_iterator it = m_windows.constBegin(); - it != m_windows.constEnd(); ++it) { - const WindowData &data = it.value(); - if (data.updatePending) - renderWindow(it.key()); - } - return true; - } - return QObject::event(e); -} - -#include "qquickwindowmanager.moc" - -QT_END_NAMESPACE diff --git a/src/quick/items/qquickwindowmanager_p.h b/src/quick/items/qquickwindowmanager_p.h deleted file mode 100644 index 94142a9d90..0000000000 --- a/src/quick/items/qquickwindowmanager_p.h +++ /dev/null @@ -1,87 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** 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, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQUICKWINDOWMANAGER_P_H -#define QQUICKWINDOWMANAGER_P_H - -#include -#include - -QT_BEGIN_NAMESPACE - -class QQuickWindow; -class QSGContext; -class QAnimationDriver; - -class Q_QUICK_PRIVATE_EXPORT QQuickWindowManager -{ -public: - virtual ~QQuickWindowManager(); - - virtual void show(QQuickWindow *window) = 0; - virtual void hide(QQuickWindow *window) = 0; - - virtual void windowDestroyed(QQuickWindow *window) = 0; - - virtual void exposureChanged(QQuickWindow *window) = 0; - virtual QImage grab(QQuickWindow *window) = 0; - virtual void resize(QQuickWindow *window, const QSize &size) = 0; - - virtual void update(QQuickWindow *window) = 0; - virtual void maybeUpdate(QQuickWindow *window) = 0; - - virtual QAnimationDriver *animationDriver() const = 0; - - virtual QSGContext *sceneGraphContext() const = 0; - - virtual void releaseResources() = 0; - - // ### make this less of a singleton - static QQuickWindowManager *instance(); - static void setInstance(QQuickWindowManager *instance); - -private: - static QQuickWindowManager *s_instance; -}; - -QT_END_NAMESPACE - -#endif // QQUICKWINDOWMANAGER_P_H diff --git a/src/quick/scenegraph/qsgcontext_p.h b/src/quick/scenegraph/qsgcontext_p.h index 1ae3ada3d8..cdc83d1dee 100644 --- a/src/quick/scenegraph/qsgcontext_p.h +++ b/src/quick/scenegraph/qsgcontext_p.h @@ -69,7 +69,7 @@ class QQuickWindow; class QSGTexture; class QSGMaterial; class QSGMaterialShader; -class QQuickWindowManager; +class QSGRenderLoop; class QOpenGLContext; class QOpenGLFramebufferObject; @@ -127,7 +127,7 @@ public: virtual QAnimationDriver *createAnimationDriver(QObject *parent); static QQuickTextureFactory *createTextureFactoryFromImage(const QImage &image); - static QQuickWindowManager *createWindowManager(); + static QSGRenderLoop *createWindowManager(); public slots: diff --git a/src/quick/scenegraph/qsgcontextplugin.cpp b/src/quick/scenegraph/qsgcontextplugin.cpp index 76b4e63816..545762aa72 100644 --- a/src/quick/scenegraph/qsgcontextplugin.cpp +++ b/src/quick/scenegraph/qsgcontextplugin.cpp @@ -157,7 +157,7 @@ QQuickTextureFactory *QSGContext::createTextureFactoryFromImage(const QImage &im specific window manager. */ -QQuickWindowManager *QSGContext::createWindowManager() +QSGRenderLoop *QSGContext::createWindowManager() { QSGAdaptionPluginData *plugin = contextFactory(); if (plugin->factory) diff --git a/src/quick/scenegraph/qsgcontextplugin_p.h b/src/quick/scenegraph/qsgcontextplugin_p.h index 4e5be6edce..036bc69c66 100644 --- a/src/quick/scenegraph/qsgcontextplugin_p.h +++ b/src/quick/scenegraph/qsgcontextplugin_p.h @@ -53,14 +53,14 @@ QT_BEGIN_NAMESPACE class QSGContext; -class QQuickWindowManager; +class QSGRenderLoop; struct Q_QUICK_PRIVATE_EXPORT QSGContextFactoryInterface : public QFactoryInterface { virtual QSGContext *create(const QString &key) const = 0; virtual QQuickTextureFactory *createTextureFactoryFromImage(const QImage &image) = 0; - virtual QQuickWindowManager *createWindowManager() = 0; + virtual QSGRenderLoop *createWindowManager() = 0; }; #define QSGContextFactoryInterface_iid \ @@ -79,7 +79,7 @@ public: virtual QSGContext *create(const QString &key) const = 0; virtual QQuickTextureFactory *createTextureFactoryFromImage(const QImage &) { return 0; } - virtual QQuickWindowManager *createWindowManager() { return 0; } + virtual QSGRenderLoop *createWindowManager() { return 0; } }; QT_END_NAMESPACE diff --git a/src/quick/scenegraph/qsgrenderloop.cpp b/src/quick/scenegraph/qsgrenderloop.cpp new file mode 100644 index 0000000000..9117b2cffa --- /dev/null +++ b/src/quick/scenegraph/qsgrenderloop.cpp @@ -0,0 +1,363 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgrenderloop_p.h" +#include "qsgthreadedrenderloop_p.h" + +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +DEFINE_BOOL_CONFIG_OPTION(qquick_render_timing, QML_RENDER_TIMING) + +extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha); + +/*! + expectations for this manager to work: + - one opengl context to render multiple windows + - OpenGL pipeline will not block for vsync in swap + - OpenGL pipeline will block based on a full buffer queue. + - Multiple screens can share the OpenGL context + - Animations are advanced for all windows once per swap + */ + +DEFINE_BOOL_CONFIG_OPTION(qmlNoThreadedRenderer, QML_BAD_GUI_RENDER_LOOP); +DEFINE_BOOL_CONFIG_OPTION(qmlForceThreadedRenderer, QML_FORCE_THREADED_RENDERER); // Might trigger graphics driver threading bugs, use at own risk + +QSGRenderLoop *QSGRenderLoop::s_instance = 0; + +QSGRenderLoop::~QSGRenderLoop() +{ +} + +class QSGGuiThreadRenderLoop : public QObject, public QSGRenderLoop +{ + Q_OBJECT +public: + QSGGuiThreadRenderLoop(); + + void show(QQuickWindow *window); + void hide(QQuickWindow *window); + + void windowDestroyed(QQuickWindow *window); + + void initializeGL(); + void renderWindow(QQuickWindow *window); + void exposureChanged(QQuickWindow *window); + QImage grab(QQuickWindow *window); + void resize(QQuickWindow *window, const QSize &size); + + void maybeUpdate(QQuickWindow *window); + void update(QQuickWindow *window) { maybeUpdate(window); } // identical for this implementation. + + void releaseResources(QQuickWindow *) { } + + QAnimationDriver *animationDriver() const { return 0; } + + QSGContext *sceneGraphContext() const; + + bool event(QEvent *); + + struct WindowData { + bool updatePending : 1; + bool grabOnly : 1; + }; + + QHash m_windows; + + QOpenGLContext *gl; + QSGContext *sg; + + QImage grabContent; + + bool eventPending; +}; + + +QSGRenderLoop *QSGRenderLoop::instance() +{ + if (!s_instance) { + + s_instance = QSGContext::createWindowManager(); + + bool bufferQueuing = QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::BufferQueueingOpenGL); +#ifdef Q_OS_WIN + bool fancy = false; // QTBUG-28037 +#else + bool fancy = QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ThreadedOpenGL); +#endif + if (qmlNoThreadedRenderer()) + fancy = false; + else if (qmlForceThreadedRenderer()) + fancy = true; + + // Enable fixed animation steps... + QByteArray fixed = qgetenv("QML_FIXED_ANIMATION_STEP"); + bool fixedAnimationSteps = bufferQueuing; + if (fixed == "no") + fixedAnimationSteps = false; + else if (fixed.length()) + fixedAnimationSteps = true; + if (fixedAnimationSteps) + QUnifiedTimer::instance(true)->setConsistentTiming(true); + + if (!s_instance) { + s_instance = fancy + ? (QSGRenderLoop*) new QSGThreadedRenderLoop + : (QSGRenderLoop*) new QSGGuiThreadRenderLoop; + } + } + return s_instance; +} + +void QSGRenderLoop::setInstance(QSGRenderLoop *instance) +{ + Q_ASSERT(!s_instance); + s_instance = instance; +} + +QSGGuiThreadRenderLoop::QSGGuiThreadRenderLoop() + : gl(0) + , eventPending(false) +{ + sg = QSGContext::createDefaultContext(); +} + + +void QSGGuiThreadRenderLoop::show(QQuickWindow *window) +{ + WindowData data; + data.updatePending = false; + data.grabOnly = false; + m_windows[window] = data; + + maybeUpdate(window); +} + +void QSGGuiThreadRenderLoop::hide(QQuickWindow *window) +{ + if (!m_windows.contains(window)) + return; + + m_windows.remove(window); + QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window); + cd->cleanupNodesOnShutdown(); + + if (m_windows.size() == 0) { + sg->invalidate(); + delete gl; + gl = 0; + } +} + +void QSGGuiThreadRenderLoop::windowDestroyed(QQuickWindow *window) +{ + hide(window); +} + +void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window) +{ + bool renderWithoutShowing = QQuickWindowPrivate::get(window)->renderWithoutShowing; + if ((!window->isExposed() && !renderWithoutShowing) || !m_windows.contains(window)) + return; + + WindowData &data = const_cast(m_windows[window]); + + QQuickWindow *masterWindow = 0; + if (!window->isVisible() && !renderWithoutShowing) { + // Find a "proper surface" to bind... + for (QHash::const_iterator it = m_windows.constBegin(); + it != m_windows.constEnd() && !masterWindow; ++it) { + if (it.key()->isVisible()) + masterWindow = it.key(); + } + } else { + masterWindow = window; + } + + if (!masterWindow) + return; + + if (!QQuickWindowPrivate::get(masterWindow)->isRenderable()) { + qWarning().nospace() + << "Unable to find a renderable master window " + << masterWindow << "when trying to render" + << window << " (" << window->geometry() << ")."; + return; + } + + if (!gl) { + gl = new QOpenGLContext(); + gl->setFormat(masterWindow->requestedFormat()); + gl->create(); + if (!gl->makeCurrent(masterWindow)) + qWarning("QQuickWindow: makeCurrent() failed..."); + sg->initialize(gl); + } else { + gl->makeCurrent(masterWindow); + } + + bool alsoSwap = data.updatePending; + data.updatePending = false; + + QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window); + cd->polishItems(); + + int renderTime = 0, syncTime = 0; + QTime renderTimer; + if (qquick_render_timing()) + renderTimer.start(); + + cd->syncSceneGraph(); + + if (qquick_render_timing()) + syncTime = renderTimer.elapsed(); + + cd->renderSceneGraph(window->size()); + + if (qquick_render_timing()) + renderTime = renderTimer.elapsed() - syncTime; + + if (data.grabOnly) { + grabContent = qt_gl_read_framebuffer(window->size(), false, false); + data.grabOnly = false; + } + + if (alsoSwap && window->isVisible()) { + gl->swapBuffers(window); + cd->fireFrameSwapped(); + } + + if (qquick_render_timing()) { + static QTime lastFrameTime = QTime::currentTime(); + const int swapTime = renderTimer.elapsed() - renderTime - syncTime; + qDebug() << "- Breakdown of frame time; sync:" << syncTime + << "ms render:" << renderTime << "ms swap:" << swapTime + << "ms total:" << swapTime + renderTime + syncTime + << "ms time since last frame:" << (lastFrameTime.msecsTo(QTime::currentTime())) + << "ms"; + lastFrameTime = QTime::currentTime(); + } + + // Might have been set during syncSceneGraph() + if (data.updatePending) + maybeUpdate(window); +} + +void QSGGuiThreadRenderLoop::exposureChanged(QQuickWindow *window) +{ + if (window->isExposed()) + maybeUpdate(window); +} + +QImage QSGGuiThreadRenderLoop::grab(QQuickWindow *window) +{ + if (!m_windows.contains(window)) + return QImage(); + + m_windows[window].grabOnly = true; + + renderWindow(window); + + QImage grabbed = grabContent; + grabContent = QImage(); + return grabbed; +} + + + +void QSGGuiThreadRenderLoop::resize(QQuickWindow *, const QSize &) +{ +} + + + +void QSGGuiThreadRenderLoop::maybeUpdate(QQuickWindow *window) +{ + if (!m_windows.contains(window)) + return; + + m_windows[window].updatePending = true; + + if (!eventPending) { + QCoreApplication::postEvent(this, new QEvent(QEvent::User)); + eventPending = true; + } +} + + + +QSGContext *QSGGuiThreadRenderLoop::sceneGraphContext() const +{ + return sg; +} + + +bool QSGGuiThreadRenderLoop::event(QEvent *e) +{ + if (e->type() == QEvent::User) { + eventPending = false; + for (QHash::const_iterator it = m_windows.constBegin(); + it != m_windows.constEnd(); ++it) { + const WindowData &data = it.value(); + if (data.updatePending) + renderWindow(it.key()); + } + return true; + } + return QObject::event(e); +} + +#include "qsgrenderloop.moc" + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/qsgrenderloop_p.h b/src/quick/scenegraph/qsgrenderloop_p.h new file mode 100644 index 0000000000..fc705f48e8 --- /dev/null +++ b/src/quick/scenegraph/qsgrenderloop_p.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGRenderLoop_P_H +#define QSGRenderLoop_P_H + +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QQuickWindow; +class QSGContext; +class QAnimationDriver; + +class Q_QUICK_PRIVATE_EXPORT QSGRenderLoop +{ +public: + virtual ~QSGRenderLoop(); + + virtual void show(QQuickWindow *window) = 0; + virtual void hide(QQuickWindow *window) = 0; + + virtual void windowDestroyed(QQuickWindow *window) = 0; + + virtual void exposureChanged(QQuickWindow *window) = 0; + virtual QImage grab(QQuickWindow *window) = 0; + virtual void resize(QQuickWindow *window, const QSize &size) = 0; + + virtual void update(QQuickWindow *window) = 0; + virtual void maybeUpdate(QQuickWindow *window) = 0; + + virtual QAnimationDriver *animationDriver() const = 0; + + virtual QSGContext *sceneGraphContext() const = 0; + + virtual void releaseResources(QQuickWindow *window) = 0; + + // ### make this less of a singleton + static QSGRenderLoop *instance(); + static void setInstance(QSGRenderLoop *instance); + +private: + static QSGRenderLoop *s_instance; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QSGRenderLoop_P_H diff --git a/src/quick/scenegraph/qsgthreadedrenderloop.cpp b/src/quick/scenegraph/qsgthreadedrenderloop.cpp new file mode 100644 index 0000000000..5ec6de2f04 --- /dev/null +++ b/src/quick/scenegraph/qsgthreadedrenderloop.cpp @@ -0,0 +1,1044 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include +#include +#include + +#include +#include + +#include +#include + +#include + +#include "qsgthreadedrenderloop_p.h" + +/* + Overall design: + + There are two classes here. QSGThreadedRenderLoop and + QSGRenderThread. All communication between the two is based on + event passing and we have a number of custom events. + + In this implementation, the render thread is never blocked and the + GUI thread will initiate a polishAndSync which will block and wait + for the render thread to pick it up and release the block only + after the render thread is done syncing. The reason for this + is: + + 1. Clear blocking paradigm. We only have one real "block" point + (polishAndSync()) and all blocking is initiated by GUI and picked + up by Render at specific times based on events. This makes the + execution deterministic. + + 2. Render does not have to interact with GUI. This is done so that + the render thread can run its own animation system which stays + alive even when the GUI thread is blocked doing i/o, object + instantiation, QPainter-painting or any other non-trivial task. + + --- + + The render loop is active while any window is exposed. All visible + windows are tracked, but only exposed windows are actually added to + the render thread and rendered. That means that if all windows are + obscured, we might end up cleaning up the SG and GL context (if all + windows have disabled persistency). Especially for multiprocess, + low-end systems, this should be quite important. + + */ + +QT_BEGIN_NAMESPACE + + +// #define QSG_RENDER_LOOP_DEBUG +// #define QSG_RENDER_LOOP_DEBUG_FULL +#ifdef QSG_RENDER_LOOP_DEBUG +#define QSG_RENDER_LOOP_DEBUG_BASIC +#endif + +#ifdef QSG_RENDER_LOOP_DEBUG_FULL +#define QSG_RENDER_LOOP_DEBUG_BASIC +#endif + +#if defined (QSG_RENDER_LOOP_DEBUG_FULL) +# define RLDEBUG1(x) qDebug("%s : %4d - %s", __FILE__, __LINE__, x); +# define RLDEBUG(x) qDebug("%s : %4d - %s", __FILE__, __LINE__, x); +#elif defined (QSG_RENDER_LOOP_DEBUG_BASIC) +# define RLDEBUG1(x) qDebug("%s : %4d - %s", __FILE__, __LINE__, x); +# define RLDEBUG(x) +#else +# define RLDEBUG1(x) +# define RLDEBUG(x) +#endif + + +static int get_env_int(const char *name, int defaultValue) +{ + QByteArray content = qgetenv(name); + + bool ok = false; + int value = content.toInt(&ok); + return ok ? value : defaultValue; +} + + +static inline int qsgrl_animation_interval() { + qreal refreshRate = QGuiApplication::primaryScreen()->refreshRate(); + // To work around that some platforms wrongfully return 0 or something + // bogus for refreshrate + if (refreshRate < 1) + return 16; + return int(1000 / refreshRate); +} + + +#define QQUICK_WINDOW_TIMING +#ifdef QQUICK_WINDOW_TIMING +static bool qquick_window_timing = !qgetenv("QML_WINDOW_TIMING").isEmpty(); +static QTime threadTimer; +static int syncTime; +static int renderTime; +static int sinceLastTime; +#endif + +extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha); + +// RL: Render Loop +// RT: Render Thread + +// Passed from the RL to the RT when a window is rendeirng on screen +// and should be added to the render loop. +const QEvent::Type WM_Expose = QEvent::Type(QEvent::User + 1); + +// Passed from the RL to the RT when a window is removed obscured and +// should be removed from the render loop. +const QEvent::Type WM_Obscure = QEvent::Type(QEvent::User + 2); + +// Passed from the RL to itself to initiate a polishAndSync() call. +const QEvent::Type WM_LockAndSync = QEvent::Type(QEvent::User + 3); + +// Passed from the RL to RT when GUI has been locked, waiting for sync +// (updatePaintNode()) +const QEvent::Type WM_RequestSync = QEvent::Type(QEvent::User + 4); + +// Passed by the RT to itself to trigger another render pass. This is +// typically a result of QQuickWindow::update(). +const QEvent::Type WM_RequestRepaint = QEvent::Type(QEvent::User + 5); + +// Passed by the RL to the RT when a window has changed size. +const QEvent::Type WM_Resize = QEvent::Type(QEvent::User + 6); + +// Passed by the RL to the RT to free up maybe release SG and GL contexts +// if no windows are rendering. +const QEvent::Type WM_TryRelease = QEvent::Type(QEvent::User + 7); + +// Passed by the RL to the RL when maybeUpdate is called on the RT to +// just replay the maybeUpdate later. This typically happens when +// updatePaintNode() results in a call to QQuickItem::update(). +const QEvent::Type WM_UpdateLater = QEvent::Type(QEvent::User + 8); + +// Passed by the RL to the RT when a QQuickWindow::grabWindow() is +// called. +const QEvent::Type WM_Grab = QEvent::Type(QEvent::User + 9); + +// Passed by the RT to the RL to trigger animations to be advanced. +const QEvent::Type WM_AdvanceAnimations = QEvent::Type(QEvent::User + 10); + +template T *windowFor(const QList list, QQuickWindow *window) +{ + for (int i=0; i(&t); + } + return 0; +} + + +class WMWindowEvent : public QEvent +{ +public: + WMWindowEvent(QQuickWindow *c, QEvent::Type type) : QEvent(type), window(c) { } + QQuickWindow *window; +}; + +class WMTryReleaseEvent : public WMWindowEvent +{ +public: + WMTryReleaseEvent(QQuickWindow *win, bool destroy) + : WMWindowEvent(win, WM_TryRelease) + , inDestructor(destroy) + {} + + bool inDestructor; +}; + +class WMResizeEvent : public WMWindowEvent +{ +public: + WMResizeEvent(QQuickWindow *c, const QSize &s) : WMWindowEvent(c, WM_Resize), size(s) { } + QSize size; +}; + + +class WMExposeEvent : public WMWindowEvent +{ +public: + WMExposeEvent(QQuickWindow *c) : WMWindowEvent(c, WM_Expose), size(c->size()) { } + QSize size; +}; + + +class WMGrabEvent : public WMWindowEvent +{ +public: + WMGrabEvent(QQuickWindow *c, QImage *result) : WMWindowEvent(c, WM_Grab), image(result) {} + QImage *image; +}; + + +class QSGRenderThread : public QThread +{ + Q_OBJECT +public: + + QSGRenderThread(QSGThreadedRenderLoop *w) + : wm(w) + , gl(0) + , sg(QSGContext::createDefaultContext()) + , pendingUpdate(0) + , sleeping(false) + , animationRunning(false) + , guiIsLocked(false) + , shouldExit(false) + , allowMainThreadProcessing(true) + , animationRequestsPending(0) + { + sg->moveToThread(this); + } + + + void invalidateOpenGL(QQuickWindow *window, bool inDestructor); + void initializeOpenGL(); + + bool event(QEvent *); + void run(); + + void syncAndRender(); + void sync(); + + void requestRepaint() + { + if (sleeping) + exit(); + if (m_windows.size() > 0) + pendingUpdate |= RepaintRequest; + } + +public slots: + void animationStarted() { + RLDEBUG(" Render: animationStarted()"); + animationRunning = true; + if (sleeping) + exit(); + } + + void animationStopped() { + RLDEBUG(" Render: animationStopped()"); + animationRunning = false; + } + +public: + enum UpdateRequest { + SyncRequest = 0x01, + RepaintRequest = 0x02 + }; + + QSGThreadedRenderLoop *wm; + QOpenGLContext *gl; + QSGContext *sg; + + QEventLoop eventLoop; + + uint pendingUpdate : 2; + uint sleeping : 1; + uint animationRunning : 1; + + volatile bool guiIsLocked; + volatile bool shouldExit; + + volatile bool allowMainThreadProcessing; + volatile int animationRequestsPending; + + QMutex mutex; + QWaitCondition waitCondition; + + QElapsedTimer m_timer; + + struct Window { + QQuickWindow *window; + QSize size; + }; + QList m_windows; +}; + +bool QSGRenderThread::event(QEvent *e) +{ + switch ((int) e->type()) { + + case WM_Expose: { + RLDEBUG1(" Render: WM_Expose"); + WMExposeEvent *se = static_cast(e); + + if (windowFor(m_windows, se->window)) { + RLDEBUG1(" Render: - window already added..."); + return true; + } + + Window window; + window.window = se->window; + window.size = se->size; + m_windows << window; + return true; } + + case WM_Obscure: { + RLDEBUG1(" Render: WM_Obscure"); + WMWindowEvent *ce = static_cast(e); + for (int i=0; iwindow) { + RLDEBUG1(" Render: - removed one..."); + m_windows.removeAt(i); + break; + } + } + + if (sleeping && m_windows.size()) + exit(); + + return true; } + + case WM_RequestSync: + RLDEBUG(" Render: WM_RequestSync"); + if (sleeping) + exit(); + if (m_windows.size() > 0) + pendingUpdate |= SyncRequest; + return true; + + case WM_Resize: { + RLDEBUG(" Render: WM_Resize"); + WMResizeEvent *re = static_cast(e); + Window *w = windowFor(m_windows, re->window); + w->size = re->size; + // No need to wake up here as we will get a sync shortly.. (see QSGThreadedRenderLoop::resize()); + return true; } + + case WM_TryRelease: + RLDEBUG1(" Render: WM_TryRelease"); + mutex.lock(); + if (m_windows.size() == 0) { + WMTryReleaseEvent *wme = static_cast(e); + RLDEBUG1(" Render: - setting exit flag and invalidating GL"); + invalidateOpenGL(wme->window, wme->inDestructor); + shouldExit = !gl; + if (sleeping) + exit(); + } else { + RLDEBUG1(" Render: - not releasing anything because we have active windows..."); + } + waitCondition.wakeOne(); + mutex.unlock(); + return true; + + case WM_Grab: { + RLDEBUG1(" Render: WM_Grab"); + WMGrabEvent *ce = static_cast(e); + Window *w = windowFor(m_windows, ce->window); + mutex.lock(); + if (w) { + gl->makeCurrent(ce->window); + + RLDEBUG1(" Render: - syncing scene graph"); + QQuickWindowPrivate *d = QQuickWindowPrivate::get(w->window); + d->syncSceneGraph(); + + RLDEBUG1(" Render: - rendering scene graph"); + QQuickWindowPrivate::get(ce->window)->renderSceneGraph(w->size); + + RLDEBUG1(" Render: - grabbing result..."); + *ce->image = qt_gl_read_framebuffer(w->size, false, false); + } + RLDEBUG1(" Render: - waking gui to handle grab result"); + waitCondition.wakeOne(); + mutex.unlock(); + return true; + } + + default: + break; + } + return QThread::event(e); +} + +void QSGRenderThread::invalidateOpenGL(QQuickWindow *window, bool inDestructor) +{ + RLDEBUG1(" Render: invalidateOpenGL()"); + + if (!gl) + return; + + if (!window) { + qWarning("QSGThreadedRenderLoop:QSGRenderThread: no window to make current..."); + return; + } + + + bool persistentGL = false; + bool persistentSG = false; + + // GUI is locked so accessing the wm and window here is safe + for (int i=0; im_windows.size(); ++i) { + const QSGThreadedRenderLoop::Window &w = wm->m_windows.at(i); + if (!inDestructor || w.window != window) { + persistentSG |= w.window->isPersistentSceneGraph(); + persistentGL |= w.window->isPersistentOpenGLContext(); + } + } + + gl->makeCurrent(window); + + // The canvas nodes must be cleanded up regardless if we are in the destructor.. + if (!persistentSG || inDestructor) { + QQuickWindowPrivate *dd = QQuickWindowPrivate::get(window); + dd->cleanupNodesOnShutdown(); + } + + // We're not doing any cleanup in this case... + if (persistentSG) { + RLDEBUG1(" Render: - persistent SG, avoiding cleanup"); + return; + } + + sg->invalidate(); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + gl->doneCurrent(); + RLDEBUG1(" Render: - invalidated scenegraph.."); + + if (!persistentGL) { + delete gl; + gl = 0; + RLDEBUG1(" Render: - invalidated OpenGL"); + } else { + RLDEBUG1(" Render: - persistent GL, avoiding cleanup"); + } +} + +void QSGRenderThread::initializeOpenGL() +{ + RLDEBUG1(" Render: initializeOpenGL()"); + QWindow *win = m_windows.at(0).window; + bool temp = false; + + // Workaround for broken expose logic... We should not get an + // expose when the size of a window is invalid, but we sometimes do. + // On Mac this leads to harmless, yet annoying, console warnings + if (m_windows.at(0).size.isEmpty()) { + temp = true; + win = new QWindow(); + win->setFormat(m_windows.at(0).window->requestedFormat()); + win->setSurfaceType(QWindow::OpenGLSurface); + win->setGeometry(0, 0, 64, 64); + win->create(); + } + + gl = new QOpenGLContext(); + // Pick up the surface format from one of them + gl->setFormat(win->requestedFormat()); + gl->create(); + if (!gl->makeCurrent(win)) + qWarning("QQuickWindow: makeCurrent() failed..."); + sg->initialize(gl); + + if (temp) { + delete win; + } +} + +/*! + Enters the mutex lock to make sure GUI is blocking and performs + sync, then wakes GUI. + */ +void QSGRenderThread::sync() +{ + RLDEBUG(" Render: sync()"); + mutex.lock(); + + Q_ASSERT_X(guiIsLocked, "QSGRenderThread::sync()", "sync triggered on bad terms as gui is not already locked..."); + pendingUpdate = 0; + + for (int i=0; i(m_windows.at(i)); + if (w.size.width() == 0 || w.size.height() == 0) { + RLDEBUG(" Render: - window has bad size, waiting..."); + continue; + } + gl->makeCurrent(w.window); + QQuickWindowPrivate *d = QQuickWindowPrivate::get(w.window); + d->syncSceneGraph(); + } + + RLDEBUG(" Render: - unlocking after sync"); + + waitCondition.wakeOne(); + mutex.unlock(); +} + + +void QSGRenderThread::syncAndRender() +{ +#ifdef QQUICK_WINDOW_TIMING + if (qquick_window_timing) + sinceLastTime = threadTimer.restart(); +#endif + RLDEBUG(" Render: syncAndRender()"); + + // This animate request will get there after the sync + if (animationRunning && animationRequestsPending < 2) { + RLDEBUG(" Render: - posting animate to gui.."); + ++animationRequestsPending; + QCoreApplication::postEvent(wm, new QEvent(WM_AdvanceAnimations)); + + } + + if (pendingUpdate & SyncRequest) { + RLDEBUG(" Render: - update pending, doing sync"); + sync(); + } + +#ifdef QQUICK_WINDOW_TIMING + if (qquick_window_timing) + syncTime = threadTimer.elapsed(); +#endif + + for (int i=0; i(m_windows.at(i)); + QQuickWindowPrivate *d = QQuickWindowPrivate::get(w.window); + if (!d->renderer || w.size.width() == 0 || w.size.height() == 0) { + RLDEBUG(" Render: - Window not yet ready, skipping render..."); + continue; + } + gl->makeCurrent(w.window); + d->renderSceneGraph(w.size); +#ifdef QQUICK_WINDOW_TIMING + if (qquick_window_timing && i == 0) + renderTime = threadTimer.elapsed(); +#endif + gl->swapBuffers(w.window); + d->fireFrameSwapped(); + } + RLDEBUG(" Render: - rendering done"); + +#ifdef QQUICK_WINDOW_TIMING + if (qquick_window_timing) + qDebug("window Time: sinceLast=%d, sync=%d, first render=%d, after final swap=%d", + sinceLastTime, + syncTime, + renderTime - syncTime, + threadTimer.elapsed() - renderTime); +#endif +} + +void QSGRenderThread::run() +{ + RLDEBUG1(" Render: run()"); + while (!shouldExit) { + + if (m_windows.size() > 0) { + if (!gl) + initializeOpenGL(); + if (!sg->isReady()) + sg->initialize(gl); + syncAndRender(); + } + + QCoreApplication::processEvents(); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + + if (!shouldExit + && ((!animationRunning && pendingUpdate == 0) || m_windows.size() == 0)) { + RLDEBUG(" Render: enter event loop (going to sleep)"); + sleeping = true; + exec(); + sleeping = false; + } + + } + + Q_ASSERT_X(!gl, "QSGRenderThread::run()", "The OpenGL context should be cleaned up before exiting the render thread..."); + + RLDEBUG1(" Render: run() completed..."); +} + +QSGThreadedRenderLoop::QSGThreadedRenderLoop() + : m_animation_timer(0) + , m_update_timer(0) +{ + m_thread = new QSGRenderThread(this); + m_thread->moveToThread(m_thread); + + m_animation_driver = m_thread->sg->createAnimationDriver(this); + + m_exhaust_delay = get_env_int("QML_EXHAUST_DELAY", 5); + + connect(m_animation_driver, SIGNAL(started()), m_thread, SLOT(animationStarted())); + connect(m_animation_driver, SIGNAL(stopped()), m_thread, SLOT(animationStopped())); + connect(m_animation_driver, SIGNAL(started()), this, SLOT(animationStarted())); + connect(m_animation_driver, SIGNAL(stopped()), this, SLOT(animationStopped())); + + m_animation_driver->install(); + RLDEBUG1("GUI: QSGThreadedRenderLoop() created"); +} + +QAnimationDriver *QSGThreadedRenderLoop::animationDriver() const +{ + return m_animation_driver; +} + +QSGContext *QSGThreadedRenderLoop::sceneGraphContext() const +{ + return m_thread->sg; +} + +bool QSGThreadedRenderLoop::anyoneShowing() +{ + for (int i=0; iisVisible() && c->isExposed()) + return true; + } + return false; +} + +void QSGThreadedRenderLoop::animationStarted() +{ + RLDEBUG("GUI: animationStarted()"); + if (!anyoneShowing() && m_animation_timer == 0) + m_animation_timer = startTimer(qsgrl_animation_interval()); +} + +void QSGThreadedRenderLoop::animationStopped() +{ + RLDEBUG("GUI: animationStopped()"); + if (!anyoneShowing()) { + killTimer(m_animation_timer); + m_animation_timer = 0; + } +} + + + +/* + Adds this window to the list of tracked windowes in this window + manager. show() does not trigger rendering to start, that happens + in expose. + */ + +void QSGThreadedRenderLoop::show(QQuickWindow *window) +{ + RLDEBUG1("GUI: show()"); + + Window win; + win.window = window; + win.pendingUpdate = false; + m_windows << win; +} + + + +/* + Removes this window from the list of tracked windowes in this + window manager. hide() will trigger obscure, which in turn will + stop rendering. + */ + +void QSGThreadedRenderLoop::hide(QQuickWindow *window) +{ + RLDEBUG1("GUI: hide()"); + + if (window->isExposed()) + handleObscurity(window); + + releaseResources(window); + + for (int i=0; iisVisible()) + hide(window); + releaseResources(window, true); + + RLDEBUG1("GUI: - done with windowDestroyed()"); +} + + +void QSGThreadedRenderLoop::exposureChanged(QQuickWindow *window) +{ + RLDEBUG1("GUI: exposureChanged()"); + if (windowFor(m_windows, window) == 0) + return; + + if (window->isExposed()) { + handleExposure(window); + } else { + handleObscurity(window); + } +} + + +/*! + Will post an event to the render thread that this window should + start to render. + */ +void QSGThreadedRenderLoop::handleExposure(QQuickWindow *window) +{ + RLDEBUG1("GUI: handleExposure"); + + // Because we are going to bind a GL context to it, make sure it + // is created. + if (!window->handle()) + window->create(); + + QCoreApplication::postEvent(m_thread, new WMExposeEvent(window)); + + // Start render thread if it is not running + if (!m_thread->isRunning()) { + m_thread->shouldExit = false; + m_thread->animationRunning = m_animation_driver->isRunning(); + + RLDEBUG1("GUI: - starting render thread..."); + m_thread->start(); + + } else { + RLDEBUG1("GUI: - render thread already running"); + } + + polishAndSync(); + + // Kill non-visual animation timer if it is running + if (m_animation_timer) { + killTimer(m_animation_timer); + m_animation_timer = 0; + } + +} + +/*! + This function posts an event to the render thread to remove the window + from the list of windowses to render. + + It also starts up the non-vsync animation tick if no more windows + are showing. + */ +void QSGThreadedRenderLoop::handleObscurity(QQuickWindow *window) +{ + RLDEBUG1("GUI: handleObscurity"); + if (m_thread->isRunning()) + QCoreApplication::postEvent(m_thread, new WMWindowEvent(window, WM_Obscure)); + + if (!anyoneShowing() && m_animation_driver->isRunning() && m_animation_timer == 0) { + m_animation_timer = startTimer(qsgrl_animation_interval()); + } +} + + +/*! + Called whenever the QML scene has changed. Will post an event to + ourselves that a sync is needed. + */ +void QSGThreadedRenderLoop::maybeUpdate(QQuickWindow *window) +{ + Q_ASSERT_X(QThread::currentThread() == QCoreApplication::instance()->thread() || m_thread->guiIsLocked, + "QQuickItem::update()", + "Function can only be called from GUI thread or during QQuickItem::updatePaintNode()"); + + RLDEBUG("GUI: maybeUpdate..."); + Window *w = windowFor(m_windows, window); + if (!w || w->pendingUpdate || !m_thread->isRunning()) { + return; + } + + // Call this function from the Gui thread later as startTimer cannot be + // called from the render thread. + if (QThread::currentThread() == m_thread) { + RLDEBUG("GUI: - on render thread, posting update later"); + QCoreApplication::postEvent(this, new WMWindowEvent(window, WM_UpdateLater)); + return; + } + + + w->pendingUpdate = true; + + if (m_update_timer > 0) { + return; + } + + RLDEBUG("GUI: - posting update"); + m_update_timer = startTimer(m_animation_driver->isRunning() ? m_exhaust_delay : 0, Qt::PreciseTimer); +} + +/*! + Called when the QQuickWindow should be explicitly repainted. This function + can also be called on the render thread when the GUI thread is blocked to + keep render thread animations alive. + */ +void QSGThreadedRenderLoop::update(QQuickWindow *window) +{ + if (QThread::currentThread() == m_thread) { + RLDEBUG("Gui: update called on render thread"); + m_thread->requestRepaint(); + return; + } + + RLDEBUG("Gui: update called"); + maybeUpdate(window); +} + + + +/*! + * Release resources will post an event to the render thread to + * free up the SG and GL resources and exists the render thread. + */ +void QSGThreadedRenderLoop::releaseResources(QQuickWindow *window, bool inDestructor) +{ + RLDEBUG1("GUI: releaseResources requested..."); + + m_thread->mutex.lock(); + if (m_thread->isRunning() && !m_thread->shouldExit) { + RLDEBUG1("GUI: - posting release request to render thread"); + QCoreApplication::postEvent(m_thread, new WMTryReleaseEvent(window, inDestructor)); + m_thread->waitCondition.wait(&m_thread->mutex); + } + m_thread->mutex.unlock(); +} + + + +void QSGThreadedRenderLoop::polishAndSync() +{ + if (!anyoneShowing()) + return; + +#ifdef QQUICK_WINDOW_TIMING + QElapsedTimer timer; + int polishTime; + int waitTime; + if (qquick_window_timing) + timer.start(); +#endif + RLDEBUG("GUI: polishAndSync()"); + // Polish as the last thing we do before we allow the sync to take place + for (int i=0; ipolishItems(); + } +#ifdef QQUICK_WINDOW_TIMING + if (qquick_window_timing) + polishTime = timer.elapsed(); +#endif + + RLDEBUG("GUI: - clearing update flags..."); + for (int i=0; imutex.lock(); + m_thread->guiIsLocked = true; + QEvent *event = new QEvent(WM_RequestSync); + + QCoreApplication::postEvent(m_thread, event); + RLDEBUG("GUI: - wait for sync..."); +#ifdef QQUICK_WINDOW_TIMING + if (qquick_window_timing) + waitTime = timer.elapsed(); +#endif + m_thread->waitCondition.wait(&m_thread->mutex); + m_thread->guiIsLocked = false; + m_thread->mutex.unlock(); + RLDEBUG("GUI: - unlocked after sync..."); + +#ifdef QQUICK_WINDOW_TIMING + if (qquick_window_timing) + qDebug(" - polish=%d, wait=%d, sync=%d", polishTime, waitTime - polishTime, int(timer.elapsed() - waitTime)); +#endif +} + +bool QSGThreadedRenderLoop::event(QEvent *e) +{ + switch ((int) e->type()) { + + case QEvent::Timer: + if (static_cast(e)->timerId() == m_animation_timer) { + RLDEBUG("Gui: QEvent::Timer -> non-visual animation"); + m_animation_driver->advance(); + } else if (static_cast(e)->timerId() == m_update_timer) { + RLDEBUG("Gui: QEvent::Timer -> polishAndSync()"); + killTimer(m_update_timer); + m_update_timer = 0; + polishAndSync(); + } + return true; + + case WM_UpdateLater: { + QQuickWindow *window = static_cast(e)->window; + // The window might have gone away... + if (windowFor(m_windows, window)) + maybeUpdate(window); + return true; } + + case WM_AdvanceAnimations: + --m_thread->animationRequestsPending; + RLDEBUG("GUI: WM_AdvanceAnimations"); + if (m_animation_driver->isRunning()) { +#ifdef QQUICK_CANVAS_TIMING + QElapsedTimer timer; + timer.start(); +#endif + m_animation_driver->advance(); + RLDEBUG("GUI: - animations advanced.."); +#ifdef QQUICK_CANVAS_TIMING + if (qquick_canvas_timing) + qDebug(" - animation: %d", (int) timer.elapsed()); +#endif + } + return true; + + default: + break; + } + + return QObject::event(e); +} + + + +/* + Locks down GUI and performs a grab the scene graph, then returns the result. + + Since the QML scene could have changed since the last time it was rendered, + we need to polish and sync the scene graph. This might seem superfluous, but + - QML changes could have triggered deleteLater() which could have removed + textures or other objects from the scene graph, causing render to crash. + - Autotests rely on grab(), setProperty(), grab(), compare behavior. + */ + +QImage QSGThreadedRenderLoop::grab(QQuickWindow *window) +{ + RLDEBUG("GUI: grab"); + if (!m_thread->isRunning()) + return QImage(); + + if (!window->handle()) + window->create(); + + RLDEBUG1("GUI: - polishing items..."); + QQuickWindowPrivate *d = QQuickWindowPrivate::get(window); + d->polishItems(); + + QImage result; + m_thread->mutex.lock(); + RLDEBUG1("GUI: - locking, posting grab event"); + QCoreApplication::postEvent(m_thread, new WMGrabEvent(window, &result)); + m_thread->waitCondition.wait(&m_thread->mutex); + RLDEBUG1("GUI: - locking, grab done, unlocking"); + m_thread->mutex.unlock(); + + RLDEBUG1("Gui: - grab complete"); + + return result; +} + +/* + Notify the render thread that the window is now a new size. Then + locks GUI until render has adapted. + */ + +void QSGThreadedRenderLoop::resize(QQuickWindow *w, const QSize &size) +{ + RLDEBUG1("GUI: resize"); + + if (!m_thread->isRunning() || !m_windows.size() || !w->isExposed() || windowFor(m_windows, w) == 0) { + return; + } + + if (size.width() == 0 || size.height() == 0) + return; + + RLDEBUG("GUI: - posting resize event..."); + WMResizeEvent *e = new WMResizeEvent(w, size); + QCoreApplication::postEvent(m_thread, e); + + polishAndSync(); +} + +#include "qsgthreadedrenderloop.moc" + +QT_END_NAMESPACE diff --git a/src/quick/scenegraph/qsgthreadedrenderloop_p.h b/src/quick/scenegraph/qsgthreadedrenderloop_p.h new file mode 100644 index 0000000000..fd3ab657d3 --- /dev/null +++ b/src/quick/scenegraph/qsgthreadedrenderloop_p.h @@ -0,0 +1,125 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGTHREADEDRENDERLOOP_P_H +#define QSGTHREADEDRENDERLOOP_P_H + +#include +#include +#include + +#include "qsgrenderloop_p.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QSGRenderThread; + +class QSGThreadedRenderLoop : public QObject, public QSGRenderLoop +{ + Q_OBJECT +public: + QSGThreadedRenderLoop(); + + void show(QQuickWindow *window); + void hide(QQuickWindow *window); + + void windowDestroyed(QQuickWindow *window); + void exposureChanged(QQuickWindow *window); + + void handleExposure(QQuickWindow *window); + void handleObscurity(QQuickWindow *window); + + QImage grab(QQuickWindow *); + + void resize(QQuickWindow *, const QSize &); + + void update(QQuickWindow *window); + void maybeUpdate(QQuickWindow *window); + QSGContext *sceneGraphContext() const; + + QAnimationDriver *animationDriver() const; + + void releaseResources(QQuickWindow *window) { releaseResources(window, false); } + + bool event(QEvent *); + + void wakeup(); + +public slots: + void animationStarted(); + void animationStopped(); + +private: + friend class QSGRenderThread; + + void releaseResources(QQuickWindow *window, bool inDestructor); + bool checkAndResetForceUpdate(QQuickWindow *window); + + bool anyoneShowing(); + void initialize(); + + void waitForReleaseComplete(); + + void polishAndSync(); + + struct Window { + QQuickWindow *window; + uint pendingUpdate : 1; + }; + + QSGRenderThread *m_thread; + QAnimationDriver *m_animation_driver; + QList m_windows; + + int m_animation_timer; + int m_update_timer; + int m_exhaust_delay; +}; + + + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QSGTHREADEDRENDERLOOP_P_H diff --git a/src/quick/scenegraph/scenegraph.pri b/src/quick/scenegraph/scenegraph.pri index a58e9b46ad..8c87e23ac9 100644 --- a/src/quick/scenegraph/scenegraph.pri +++ b/src/quick/scenegraph/scenegraph.pri @@ -65,7 +65,10 @@ HEADERS += \ $$PWD/qsgdefaultimagenode_p.h \ $$PWD/qsgdefaultrectanglenode_p.h \ $$PWD/qsgflashnode_p.h \ - $$PWD/qsgshareddistancefieldglyphcache_p.h + $$PWD/qsgshareddistancefieldglyphcache_p.h \ + $$PWD/qsgrenderloop_p.h \ + $$PWD/qsgthreadedrenderloop_p.h + SOURCES += \ $$PWD/qsgadaptationlayer.cpp \ @@ -79,7 +82,10 @@ SOURCES += \ $$PWD/qsgdefaultimagenode.cpp \ $$PWD/qsgdefaultrectanglenode.cpp \ $$PWD/qsgflashnode.cpp \ - $$PWD/qsgshareddistancefieldglyphcache.cpp + $$PWD/qsgshareddistancefieldglyphcache.cpp \ + $$PWD/qsgrenderloop.cpp \ + $$PWD/qsgthreadedrenderloop.cpp + diff --git a/tests/auto/quick/examples/tst_examples.cpp b/tests/auto/quick/examples/tst_examples.cpp index 7055fb6e03..9c5fb88af2 100644 --- a/tests/auto/quick/examples/tst_examples.cpp +++ b/tests/auto/quick/examples/tst_examples.cpp @@ -82,11 +82,9 @@ private: QStringList findQmlFiles(const QDir &); QQmlEngine engine; - - QQuickWindow *window; }; -tst_examples::tst_examples() : window(0) +tst_examples::tst_examples() { // Add files to exclude here excludedFiles << "examples/quick/canvas/tiger/tiger.qml"; // QTBUG-26528 @@ -94,7 +92,7 @@ tst_examples::tst_examples() : window(0) // Add directories you want excluded here (don't add examples/, because they install to examples/qtdeclarative/) excludedDirs << "shared"; //Not an example - excludedDirs << "qtquick/text/fonts"; // QTBUG-21415 + excludedDirs << "quick/text/fonts"; // QTBUG-29004 excludedDirs << "snippets/qml/path"; //No root QQuickItem excludedDirs << "tutorials/gettingStartedQml"; //C++ example, but no cpp files in root dir @@ -120,7 +118,6 @@ tst_examples::tst_examples() : window(0) tst_examples::~tst_examples() { - delete window; } void tst_examples::init() @@ -260,6 +257,9 @@ void tst_examples::sgexamples_data() void tst_examples::sgexamples() { QFETCH(QString, file); + QQuickWindow window; + window.setPersistentOpenGLContext(true); + window.setPersistentSceneGraph(true); QQmlComponent component(&engine, QUrl::fromLocalFile(file)); if (component.status() == QQmlComponent::Error) @@ -272,13 +272,11 @@ void tst_examples::sgexamples() component.completeCreate(); QVERIFY(root); - if (!window) { - window = new QQuickWindow(); - window->resize(240, 320); - window->show(); - QVERIFY(QTest::qWaitForWindowExposed(window)); - } - root->setParentItem(window->contentItem()); + window.resize(240, 320); + window.show(); + QVERIFY(QTest::qWaitForWindowExposed(&window)); + + root->setParentItem(window.contentItem()); component.completeCreate(); qApp->processEvents(); @@ -303,6 +301,8 @@ void tst_examples::sgsnippets_data() void tst_examples::sgsnippets() { + QQuickWindow window; + QFETCH(QString, file); QQmlComponent component(&engine, QUrl::fromLocalFile(file)); @@ -316,13 +316,11 @@ void tst_examples::sgsnippets() component.completeCreate(); QVERIFY(root); - if (!window) { - window = new QQuickWindow(); - window->resize(240, 320); - window->show(); - QVERIFY(QTest::qWaitForWindowExposed(window)); - } - root->setParentItem(window->contentItem()); + window.resize(240, 320); + window.show(); + QVERIFY(QTest::qWaitForWindowExposed(&window)); + + root->setParentItem(window.contentItem()); component.completeCreate(); qApp->processEvents(); diff --git a/tests/auto/quick/qquickflickable/qquickflickable.pro b/tests/auto/quick/qquickflickable/qquickflickable.pro index f61a601130..67d5fc12f0 100644 --- a/tests/auto/quick/qquickflickable/qquickflickable.pro +++ b/tests/auto/quick/qquickflickable/qquickflickable.pro @@ -9,7 +9,6 @@ include (../shared/util.pri) TESTDATA = data/* -CONFIG += parallel_test QT += core-private gui-private v8-private qml-private quick-private testlib DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 CONFIG+=insignificant_test diff --git a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp index 662e86018c..5226434c14 100644 --- a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp +++ b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp @@ -208,10 +208,12 @@ void tst_qquickflickable::boundsBehavior() void tst_qquickflickable::rebound() { - QQuickView *window = new QQuickView; + QScopedPointer window(new QQuickView); window->setSource(testFileUrl("rebound.qml")); window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window.data())); window->requestActivate(); + QVERIFY(QTest::qWaitForWindowActive(window.data())); QVERIFY(window->rootObject() != 0); QQuickFlickable *flickable = qobject_cast(window->rootObject()); @@ -227,7 +229,7 @@ void tst_qquickflickable::rebound() QSignalSpy hMoveSpy(flickable, SIGNAL(movingHorizontallyChanged())); // flick and test the transition is run - flick(window, QPoint(20,20), QPoint(120,120), 200); + flick(window.data(), QPoint(20,20), QPoint(120,120), 200); QTRY_COMPARE(window->rootObject()->property("transitionsStarted").toInt(), 2); QCOMPARE(hMoveSpy.count(), 1); @@ -257,14 +259,13 @@ void tst_qquickflickable::rebound() #ifdef Q_OS_MAC QSKIP("QTBUG-26696 - sometimes fails on Mac"); - delete window; return; #endif // flick and trigger the transition multiple times // (moving signals are emitted as soon as the first transition starts) - flick(window, QPoint(20,20), QPoint(120,120), 200); // both x and y will bounce back - flick(window, QPoint(20,120), QPoint(120,20), 200); // only x will bounce back + flick(window.data(), QPoint(20,20), QPoint(120,120), 200); // both x and y will bounce back + flick(window.data(), QPoint(20,120), QPoint(120,20), 200); // only x will bounce back QVERIFY(flickable->isMoving()); QVERIFY(window->rootObject()->property("transitionsStarted").toInt() >= 1); @@ -293,7 +294,7 @@ void tst_qquickflickable::rebound() // (i.e. moving but transition->running = false) window->rootObject()->setProperty("transitionEnabled", false); - flick(window, QPoint(20,20), QPoint(120,120), 200); + flick(window.data(), QPoint(20,20), QPoint(120,120), 200); QCOMPARE(window->rootObject()->property("transitionsStarted").toInt(), 0); QCOMPARE(hMoveSpy.count(), 1); QCOMPARE(vMoveSpy.count(), 1); @@ -306,8 +307,6 @@ void tst_qquickflickable::rebound() QCOMPARE(movementStartedSpy.count(), 1); QCOMPARE(movementEndedSpy.count(), 1); QCOMPARE(window->rootObject()->property("transitionsStarted").toInt(), 0); - - delete window; } void tst_qquickflickable::maximumFlickVelocity() @@ -353,7 +352,9 @@ void tst_qquickflickable::pressDelay() QScopedPointer window(new QQuickView); window->setSource(testFileUrl("pressDelay.qml")); window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window.data())); window->requestActivate(); + QVERIFY(QTest::qWaitForWindowActive(window.data())); QVERIFY(window->rootObject() != 0); QQuickFlickable *flickable = qobject_cast(window->rootObject()); @@ -392,7 +393,9 @@ void tst_qquickflickable::nestedPressDelay() QScopedPointer window(new QQuickView); window->setSource(testFileUrl("nestedPressDelay.qml")); window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window.data())); window->requestActivate(); + QVERIFY(QTest::qWaitForWindowActive(window.data())); QVERIFY(window->rootObject() != 0); QQuickFlickable *outer = qobject_cast(window->rootObject()); @@ -501,7 +504,8 @@ void tst_qquickflickable::returnToBounds() { QFETCH(bool, setRebound); - QQuickView *window = new QQuickView; + QScopedPointer window(new QQuickView); + window->rootContext()->setContextProperty("setRebound", setRebound); window->setSource(testFileUrl("resize.qml")); QVERIFY(window->rootObject() != 0); @@ -532,8 +536,6 @@ void tst_qquickflickable::returnToBounds() QVERIFY(!rebound->running()); QCOMPARE(reboundSpy.count(), setRebound ? 2 : 0); - - delete window; } void tst_qquickflickable::returnToBounds_data() @@ -546,10 +548,12 @@ void tst_qquickflickable::returnToBounds_data() void tst_qquickflickable::wheel() { - QQuickView *window = new QQuickView; + QScopedPointer window(new QQuickView); window->setSource(testFileUrl("wheel.qml")); window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window.data())); window->requestActivate(); + QVERIFY(QTest::qWaitForWindowActive(window.data())); QVERIFY(window->rootObject() != 0); QQuickFlickable *flick = window->rootObject()->findChild("flick"); @@ -559,7 +563,7 @@ void tst_qquickflickable::wheel() QPoint pos(200, 200); QWheelEvent event(pos, window->mapToGlobal(pos), QPoint(), QPoint(0,-120), -120, Qt::Vertical, Qt::NoButton, Qt::NoModifier); event.setAccepted(false); - QGuiApplication::sendEvent(window, &event); + QGuiApplication::sendEvent(window.data(), &event); } QTRY_VERIFY(flick->contentY() > 0); @@ -573,13 +577,11 @@ void tst_qquickflickable::wheel() QWheelEvent event(pos, window->mapToGlobal(pos), QPoint(), QPoint(-120,0), -120, Qt::Horizontal, Qt::NoButton, Qt::NoModifier); event.setAccepted(false); - QGuiApplication::sendEvent(window, &event); + QGuiApplication::sendEvent(window.data(), &event); } QTRY_VERIFY(flick->contentX() > 0); QVERIFY(flick->contentY() == 0); - - delete window; } void tst_qquickflickable::movingAndFlicking_data() @@ -614,11 +616,12 @@ void tst_qquickflickable::movingAndFlicking() const QPoint flickFrom(50, 200); // centre - QQuickView *window = new QQuickView; + QScopedPointer window(new QQuickView); window->setSource(testFileUrl("flickable03.qml")); window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window.data())); window->requestActivate(); - QTest::qWaitForWindowActive(window); + QVERIFY(QTest::qWaitForWindowActive(window.data())); QVERIFY(window->rootObject() != 0); QQuickFlickable *flickable = qobject_cast(window->rootObject()); @@ -637,9 +640,9 @@ void tst_qquickflickable::movingAndFlicking() QSignalSpy flickEndSpy(flickable, SIGNAL(flickEnded())); // do a flick that keeps the view within the bounds - flick(window, flickFrom, flickToWithoutSnapBack, 200); + flick(window.data(), flickFrom, flickToWithoutSnapBack, 200); - QVERIFY(flickable->isMoving()); + QTRY_VERIFY(flickable->isMoving()); QCOMPARE(flickable->isMovingHorizontally(), horizontalEnabled); QCOMPARE(flickable->isMovingVertically(), verticalEnabled); QVERIFY(flickable->isFlicking()); @@ -693,9 +696,9 @@ void tst_qquickflickable::movingAndFlicking() flickable->setContentX(0); flickable->setContentY(0); QTRY_VERIFY(!flickable->isMoving()); - flick(window, flickFrom, flickToWithSnapBack, 200); + flick(window.data(), flickFrom, flickToWithSnapBack, 10); - QVERIFY(flickable->isMoving()); + QTRY_VERIFY(flickable->isMoving()); QCOMPARE(flickable->isMovingHorizontally(), horizontalEnabled); QCOMPARE(flickable->isMovingVertically(), verticalEnabled); QVERIFY(flickable->isFlicking()); @@ -737,8 +740,6 @@ void tst_qquickflickable::movingAndFlicking() QCOMPARE(flickable->contentX(), 0.0); QCOMPARE(flickable->contentY(), 0.0); - - delete window; } @@ -774,11 +775,12 @@ void tst_qquickflickable::movingAndDragging() const QPoint moveFrom(50, 200); // centre - QQuickView *window = new QQuickView; + QScopedPointer window(new QQuickView); window->setSource(testFileUrl("flickable03.qml")); window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window.data())); window->requestActivate(); - QVERIFY(QTest::qWaitForWindowExposed(window)); + QVERIFY(QTest::qWaitForWindowActive(window.data())); QVERIFY(window->rootObject() != 0); QQuickFlickable *flickable = qobject_cast(window->rootObject()); @@ -797,10 +799,10 @@ void tst_qquickflickable::movingAndDragging() QSignalSpy moveEndSpy(flickable, SIGNAL(movementEnded())); // start the drag - QTest::mousePress(window, Qt::LeftButton, 0, moveFrom); - QTest::mouseMove(window, moveFrom + moveByWithoutSnapBack); - QTest::mouseMove(window, moveFrom + moveByWithoutSnapBack*2); - QTest::mouseMove(window, moveFrom + moveByWithoutSnapBack*3); + QTest::mousePress(window.data(), Qt::LeftButton, 0, moveFrom); + QTest::mouseMove(window.data(), moveFrom + moveByWithoutSnapBack); + QTest::mouseMove(window.data(), moveFrom + moveByWithoutSnapBack*2); + QTest::mouseMove(window.data(), moveFrom + moveByWithoutSnapBack*3); QTRY_VERIFY(flickable->isMoving()); QCOMPARE(flickable->isMovingHorizontally(), horizontalEnabled); @@ -819,7 +821,7 @@ void tst_qquickflickable::movingAndDragging() QCOMPARE(moveStartSpy.count(), 1); QCOMPARE(dragStartSpy.count(), 1); - QTest::mouseRelease(window, Qt::LeftButton, 0, moveFrom + moveByWithoutSnapBack*3); + QTest::mouseRelease(window.data(), Qt::LeftButton, 0, moveFrom + moveByWithoutSnapBack*3); QVERIFY(!flickable->isDragging()); QVERIFY(!flickable->isDraggingHorizontally()); @@ -868,10 +870,10 @@ void tst_qquickflickable::movingAndDragging() flickable->setContentX(0); flickable->setContentY(0); QTRY_VERIFY(!flickable->isMoving()); - QTest::mousePress(window, Qt::LeftButton, 0, moveFrom); - QTest::mouseMove(window, moveFrom + moveByWithSnapBack); - QTest::mouseMove(window, moveFrom + moveByWithSnapBack*2); - QTest::mouseMove(window, moveFrom + moveByWithSnapBack*3); + QTest::mousePress(window.data(), Qt::LeftButton, 0, moveFrom); + QTest::mouseMove(window.data(), moveFrom + moveByWithSnapBack); + QTest::mouseMove(window.data(), moveFrom + moveByWithSnapBack*2); + QTest::mouseMove(window.data(), moveFrom + moveByWithSnapBack*3); QVERIFY(flickable->isMoving()); QCOMPARE(flickable->isMovingHorizontally(), horizontalEnabled); @@ -892,7 +894,7 @@ void tst_qquickflickable::movingAndDragging() QCOMPARE(dragStartSpy.count(), 1); QCOMPARE(dragEndSpy.count(), 0); - QTest::mouseRelease(window, Qt::LeftButton, 0, moveFrom + moveByWithSnapBack*3); + QTest::mouseRelease(window.data(), Qt::LeftButton, 0, moveFrom + moveByWithSnapBack*3); // should now start snapping back to bounds (moving but not dragging) QVERIFY(flickable->isMoving()); @@ -935,17 +937,16 @@ void tst_qquickflickable::movingAndDragging() QCOMPARE(flickable->contentX(), 0.0); QCOMPARE(flickable->contentY(), 0.0); - - delete window; } void tst_qquickflickable::flickOnRelease() { - QQuickView *window = new QQuickView; + QScopedPointer window(new QQuickView); window->setSource(testFileUrl("flickable03.qml")); window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window.data())); window->requestActivate(); - QVERIFY(QTest::qWaitForWindowExposed(window)); + QVERIFY(QTest::qWaitForWindowActive(window.data())); QVERIFY(window->rootObject() != 0); QQuickFlickable *flickable = qobject_cast(window->rootObject()); @@ -958,9 +959,9 @@ void tst_qquickflickable::flickOnRelease() // underlying drivers will hopefully provide a pre-calculated velocity // (based on more data than what the UI gets), thus making this use case // working even with small movements. - QTest::mousePress(window, Qt::LeftButton, 0, QPoint(50, 300)); - QTest::mouseMove(window, QPoint(50, 10), 10); - QTest::mouseRelease(window, Qt::LeftButton, 0, QPoint(50, 10), 10); + QTest::mousePress(window.data(), Qt::LeftButton, 0, QPoint(50, 300)); + QTest::mouseMove(window.data(), QPoint(50, 10), 10); + QTest::mouseRelease(window.data(), Qt::LeftButton, 0, QPoint(50, 10), 10); QCOMPARE(vFlickSpy.count(), 1); @@ -972,17 +973,16 @@ void tst_qquickflickable::flickOnRelease() #endif // Stop on a full pixel after user interaction QCOMPARE(flickable->contentY(), (qreal)qRound(flickable->contentY())); - - delete window; } void tst_qquickflickable::pressWhileFlicking() { - QQuickView *window = new QQuickView; + QScopedPointer window(new QQuickView); window->setSource(testFileUrl("flickable03.qml")); window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window.data())); window->requestActivate(); - QVERIFY(QTest::qWaitForWindowExposed(window)); + QVERIFY(QTest::qWaitForWindowActive(window.data())); QVERIFY(window->rootObject() != 0); QQuickFlickable *flickable = qobject_cast(window->rootObject()); @@ -997,7 +997,7 @@ void tst_qquickflickable::pressWhileFlicking() // flick then press while it is still moving // flicking == false, moving == true; - flick(window, QPoint(20,190), QPoint(20, 50), 200); + flick(window.data(), QPoint(20,190), QPoint(20, 50), 200); QVERIFY(flickable->verticalVelocity() > 0.0); QVERIFY(flickable->isFlicking()); QVERIFY(flickable->isFlickingVertically()); @@ -1012,77 +1012,76 @@ void tst_qquickflickable::pressWhileFlicking() QCOMPARE(hFlickSpy.count(), 0); QCOMPARE(flickSpy.count(), 1); - QTest::mousePress(window, Qt::LeftButton, 0, QPoint(20, 50)); + QTest::mousePress(window.data(), Qt::LeftButton, 0, QPoint(20, 50)); QTRY_VERIFY(!flickable->isFlicking()); QVERIFY(!flickable->isFlickingVertically()); QVERIFY(flickable->isMoving()); QVERIFY(flickable->isMovingVertically()); - QTest::mouseRelease(window, Qt::LeftButton, 0, QPoint(20,50)); + QTest::mouseRelease(window.data(), Qt::LeftButton, 0, QPoint(20,50)); QVERIFY(!flickable->isFlicking()); QVERIFY(!flickable->isFlickingVertically()); QTRY_VERIFY(!flickable->isMoving()); QVERIFY(!flickable->isMovingVertically()); // Stop on a full pixel after user interaction QCOMPARE(flickable->contentX(), (qreal)qRound(flickable->contentX())); - - delete window; } void tst_qquickflickable::disabled() { - QQuickView *window = new QQuickView; + QScopedPointer window(new QQuickView); window->setSource(testFileUrl("disabled.qml")); window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window.data())); window->requestActivate(); + QVERIFY(QTest::qWaitForWindowActive(window.data())); QVERIFY(window->rootObject() != 0); QQuickFlickable *flick = window->rootObject()->findChild("flickable"); QVERIFY(flick != 0); - QTest::mousePress(window, Qt::LeftButton, 0, QPoint(50, 90)); + QTest::mousePress(window.data(), Qt::LeftButton, 0, QPoint(50, 90)); - QTest::mouseMove(window, QPoint(50, 80)); - QTest::mouseMove(window, QPoint(50, 70)); - QTest::mouseMove(window, QPoint(50, 60)); + QTest::mouseMove(window.data(), QPoint(50, 80)); + QTest::mouseMove(window.data(), QPoint(50, 70)); + QTest::mouseMove(window.data(), QPoint(50, 60)); QVERIFY(flick->isMoving() == false); - QTest::mouseRelease(window, Qt::LeftButton, 0, QPoint(50, 60)); + QTest::mouseRelease(window.data(), Qt::LeftButton, 0, QPoint(50, 60)); // verify that mouse clicks on other elements still work (QTBUG-20584) - QTest::mousePress(window, Qt::LeftButton, 0, QPoint(50, 10)); - QTest::mouseRelease(window, Qt::LeftButton, 0, QPoint(50, 10)); + QTest::mousePress(window.data(), Qt::LeftButton, 0, QPoint(50, 10)); + QTest::mouseRelease(window.data(), Qt::LeftButton, 0, QPoint(50, 10)); QTRY_VERIFY(window->rootObject()->property("clicked").toBool() == true); - - delete window; } void tst_qquickflickable::flickVelocity() { - QQuickView *window = new QQuickView; + QScopedPointer window(new QQuickView); window->setSource(testFileUrl("flickable03.qml")); window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window.data())); window->requestActivate(); + QVERIFY(QTest::qWaitForWindowActive(window.data())); QVERIFY(window->rootObject() != 0); QQuickFlickable *flickable = qobject_cast(window->rootObject()); QVERIFY(flickable != 0); // flick up - flick(window, QPoint(20,190), QPoint(20, 50), 200); + flick(window.data(), QPoint(20,190), QPoint(20, 50), 200); QVERIFY(flickable->verticalVelocity() > 0.0); QTRY_VERIFY(flickable->verticalVelocity() == 0.0); // flick down - flick(window, QPoint(20,10), QPoint(20, 140), 200); - QVERIFY(flickable->verticalVelocity() < 0.0); + flick(window.data(), QPoint(20,10), QPoint(20, 140), 200); + QTRY_VERIFY(flickable->verticalVelocity() < 0.0); QTRY_VERIFY(flickable->verticalVelocity() == 0.0); #ifdef Q_OS_MAC QSKIP("boost doesn't work on OS X"); - delete window; return; #endif @@ -1090,17 +1089,15 @@ void tst_qquickflickable::flickVelocity() QQuickFlickablePrivate *fp = QQuickFlickablePrivate::get(flickable); bool boosted = false; for (int i = 0; i < 6; ++i) { - flick(window, QPoint(20,390), QPoint(20, 50), 100); + flick(window.data(), QPoint(20,390), QPoint(20, 50), 100); boosted |= fp->flickBoost > 1.0; } QVERIFY(boosted); // Flick in opposite direction -> boost cancelled. - flick(window, QPoint(20,10), QPoint(20, 340), 200); + flick(window.data(), QPoint(20,10), QPoint(20, 340), 200); QTRY_VERIFY(flickable->verticalVelocity() < 0.0); QVERIFY(fp->flickBoost == 1.0); - - delete window; } void tst_qquickflickable::margins() @@ -1164,20 +1161,22 @@ void tst_qquickflickable::margins() void tst_qquickflickable::cancelOnMouseGrab() { - QQuickView *window = new QQuickView; + QScopedPointer window(new QQuickView); window->setSource(testFileUrl("cancel.qml")); window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window.data())); window->requestActivate(); + QVERIFY(QTest::qWaitForWindowActive(window.data())); QVERIFY(window->rootObject() != 0); QQuickFlickable *flickable = qobject_cast(window->rootObject()); QVERIFY(flickable != 0); - QTest::mousePress(window, Qt::LeftButton, 0, QPoint(10, 10)); + QTest::mousePress(window.data(), Qt::LeftButton, 0, QPoint(10, 10)); // drag out of bounds - QTest::mouseMove(window, QPoint(50, 50)); - QTest::mouseMove(window, QPoint(100, 100)); - QTest::mouseMove(window, QPoint(150, 150)); + QTest::mouseMove(window.data(), QPoint(50, 50)); + QTest::mouseMove(window.data(), QPoint(100, 100)); + QTest::mouseMove(window.data(), QPoint(150, 150)); QVERIFY(flickable->contentX() != 0); QVERIFY(flickable->contentY() != 0); @@ -1193,56 +1192,54 @@ void tst_qquickflickable::cancelOnMouseGrab() QTRY_VERIFY(!flickable->isMoving()); QTRY_VERIFY(!flickable->isDragging()); - QTest::mouseRelease(window, Qt::LeftButton, 0, QPoint(50, 10)); + QTest::mouseRelease(window.data(), Qt::LeftButton, 0, QPoint(50, 10)); - delete window; } void tst_qquickflickable::clickAndDragWhenTransformed() { - QQuickView *view = new QQuickView; + QScopedPointer view(new QQuickView); view->setSource(testFileUrl("transformedFlickable.qml")); view->show(); + QVERIFY(QTest::qWaitForWindowExposed(view.data())); view->requestActivate(); - QVERIFY(QTest::qWaitForWindowExposed(view)); + QVERIFY(QTest::qWaitForWindowActive(view.data())); QVERIFY(view->rootObject() != 0); QQuickFlickable *flickable = view->rootObject()->findChild("flickable"); QVERIFY(flickable != 0); // click outside child rect - QTest::mousePress(view, Qt::LeftButton, 0, QPoint(190, 190)); + QTest::mousePress(view.data(), Qt::LeftButton, 0, QPoint(190, 190)); QTest::qWait(10); QCOMPARE(flickable->property("itemPressed").toBool(), false); - QTest::mouseRelease(view, Qt::LeftButton, 0, QPoint(190, 190)); + QTest::mouseRelease(view.data(), Qt::LeftButton, 0, QPoint(190, 190)); // click inside child rect - QTest::mousePress(view, Qt::LeftButton, 0, QPoint(200, 200)); + QTest::mousePress(view.data(), Qt::LeftButton, 0, QPoint(200, 200)); QTest::qWait(10); QCOMPARE(flickable->property("itemPressed").toBool(), true); - QTest::mouseRelease(view, Qt::LeftButton, 0, QPoint(200, 200)); + QTest::mouseRelease(view.data(), Qt::LeftButton, 0, QPoint(200, 200)); const int threshold = qApp->styleHints()->startDragDistance(); // drag outside bounds - QTest::mousePress(view, Qt::LeftButton, 0, QPoint(160, 160)); + QTest::mousePress(view.data(), Qt::LeftButton, 0, QPoint(160, 160)); QTest::qWait(10); - QTest::mouseMove(view, QPoint(160 + threshold * 2, 160)); - QTest::mouseMove(view, QPoint(160 + threshold * 3, 160)); + QTest::mouseMove(view.data(), QPoint(160 + threshold * 2, 160)); + QTest::mouseMove(view.data(), QPoint(160 + threshold * 3, 160)); QCOMPARE(flickable->isDragging(), false); QCOMPARE(flickable->property("itemPressed").toBool(), false); - QTest::mouseRelease(view, Qt::LeftButton, 0, QPoint(180, 160)); + QTest::mouseRelease(view.data(), Qt::LeftButton, 0, QPoint(180, 160)); // drag inside bounds - QTest::mousePress(view, Qt::LeftButton, 0, QPoint(200, 140)); + QTest::mousePress(view.data(), Qt::LeftButton, 0, QPoint(200, 140)); QTest::qWait(10); - QTest::mouseMove(view, QPoint(200 + threshold * 2, 140)); - QTest::mouseMove(view, QPoint(200 + threshold * 3, 140)); + QTest::mouseMove(view.data(), QPoint(200 + threshold * 2, 140)); + QTest::mouseMove(view.data(), QPoint(200 + threshold * 3, 140)); QCOMPARE(flickable->isDragging(), true); QCOMPARE(flickable->property("itemPressed").toBool(), false); - QTest::mouseRelease(view, Qt::LeftButton, 0, QPoint(220, 140)); - - delete view; + QTest::mouseRelease(view.data(), Qt::LeftButton, 0, QPoint(220, 140)); } void tst_qquickflickable::flickTwiceUsingTouches() diff --git a/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp b/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp index 802cc0a4c9..93ffbca3e5 100644 --- a/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp +++ b/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp @@ -2871,7 +2871,8 @@ void tst_QQuickGridView::enforceRange_rightToLeft() ctxt->setContextProperty("testTopToBottom", QVariant(true)); window->setSource(testFileUrl("gridview-enforcerange.qml")); - qApp->processEvents(); + window->show(); + QTRY_VERIFY(window->isExposed()); QVERIFY(window->rootObject() != 0); QQuickGridView *gridview = findItem(window->rootObject(), "grid"); diff --git a/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp b/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp index a7e0f2feb4..a78625c58e 100644 --- a/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp +++ b/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp @@ -198,7 +198,7 @@ void tst_QQuickPathView::initValues() void tst_QQuickPathView::items() { - QQuickView *window = createView(); + QScopedPointer window(createView()); QaimModel model; model.addItem("Fred", "12345"); @@ -237,8 +237,6 @@ void tst_QQuickPathView::items() offset.setX(pathview->highlightItem()->width()/2); offset.setY(pathview->highlightItem()->height()/2); QCOMPARE(pathview->highlightItem()->position() + offset, start); - - delete window; } void tst_QQuickPathView::pathview2() @@ -349,7 +347,7 @@ void tst_QQuickPathView::insertModel() QFETCH(qreal, offset); QFETCH(int, currentIndex); - QQuickView *window = createView(); + QScopedPointer window(createView()); window->show(); QaimModel model; @@ -387,8 +385,6 @@ void tst_QQuickPathView::insertModel() QTRY_COMPARE(pathview->offset(), offset); QCOMPARE(pathview->currentIndex(), currentIndex); - - delete window; } void tst_QQuickPathView::removeModel_data() @@ -443,7 +439,8 @@ void tst_QQuickPathView::removeModel() QFETCH(qreal, offset); QFETCH(int, currentIndex); - QQuickView *window = createView(); + QScopedPointer window(createView()); + window->show(); QaimModel model; @@ -477,8 +474,6 @@ void tst_QQuickPathView::removeModel() QTRY_COMPARE(pathview->offset(), offset); QCOMPARE(pathview->currentIndex(), currentIndex); - - delete window; } @@ -538,7 +533,7 @@ void tst_QQuickPathView::moveModel() QFETCH(qreal, offset); QFETCH(int, currentIndex); - QQuickView *window = createView(); + QScopedPointer window(createView()); window->show(); QaimModel model; @@ -572,8 +567,6 @@ void tst_QQuickPathView::moveModel() QTRY_COMPARE(pathview->offset(), offset); QCOMPARE(pathview->currentIndex(), currentIndex); - - delete window; } void tst_QQuickPathView::consecutiveModelChanges_data() @@ -639,7 +632,7 @@ void tst_QQuickPathView::consecutiveModelChanges() QFETCH(qreal, offset); QFETCH(int, currentIndex); - QQuickView *window = createView(); + QScopedPointer window(createView()); window->show(); QaimModel model; @@ -703,7 +696,6 @@ void tst_QQuickPathView::consecutiveModelChanges() QCOMPARE(pathview->currentIndex(), currentIndex); - delete window; } void tst_QQuickPathView::path() @@ -755,7 +747,7 @@ void tst_QQuickPathView::path() void tst_QQuickPathView::dataModel() { - QQuickView *window = createView(); + QScopedPointer window(createView()); window->show(); QQmlContext *ctxt = window->rootContext(); @@ -864,13 +856,12 @@ void tst_QQuickPathView::dataModel() model.removeItem(model.count()-1); QCOMPARE(pathview->currentIndex(), model.count()-1); - delete window; delete testObject; } void tst_QQuickPathView::pathMoved() { - QQuickView *window = createView(); + QScopedPointer window(createView()); window->show(); QaimModel model; @@ -925,7 +916,6 @@ void tst_QQuickPathView::pathMoved() window->rootObject()->setProperty("delegateScale", 1.2); QTRY_COMPARE(firstItem->position() + offset, start); - delete window; } void tst_QQuickPathView::offset_data() @@ -959,7 +949,7 @@ void tst_QQuickPathView::offset() void tst_QQuickPathView::setCurrentIndex() { - QQuickView *window = createView(); + QScopedPointer window(createView()); window->show(); QaimModel model; @@ -1102,12 +1092,11 @@ void tst_QQuickPathView::setCurrentIndex() QCOMPARE(pathview->currentItem(), firstItem); QCOMPARE(firstItem->property("onPath"), QVariant(true)); - delete window; } void tst_QQuickPathView::resetModel() { - QQuickView *window = createView(); + QScopedPointer window(createView()); QStringList strings; strings << "one" << "two" << "three"; @@ -1142,12 +1131,11 @@ void tst_QQuickPathView::resetModel() QCOMPARE(display->text(), strings.at(i)); } - delete window; } void tst_QQuickPathView::propertyChanges() { - QQuickView *window = createView(); + QScopedPointer window(createView()); QVERIFY(window); window->setSource(testFileUrl("propertychanges.qml")); @@ -1184,12 +1172,11 @@ void tst_QQuickPathView::propertyChanges() pathView->setMaximumFlickVelocity(1000); QCOMPARE(maximumFlickVelocitySpy.count(), 1); - delete window; } void tst_QQuickPathView::pathChanges() { - QQuickView *window = createView(); + QScopedPointer window(createView()); QVERIFY(window); window->setSource(testFileUrl("propertychanges.qml")); @@ -1246,12 +1233,11 @@ void tst_QQuickPathView::pathChanges() pathAttribute->setName("scale"); QCOMPARE(nameSpy.count(),1); - delete window; } void tst_QQuickPathView::componentChanges() { - QQuickView *window = createView(); + QScopedPointer window(createView()); QVERIFY(window); window->setSource(testFileUrl("propertychanges.qml")); @@ -1269,12 +1255,11 @@ void tst_QQuickPathView::componentChanges() pathView->setDelegate(&delegateComponent); QCOMPARE(delegateSpy.count(),1); - delete window; } void tst_QQuickPathView::modelChanges() { - QQuickView *window = createView(); + QScopedPointer window(createView()); QVERIFY(window); window->setSource(testFileUrl("propertychanges.qml")); @@ -1304,12 +1289,11 @@ void tst_QQuickPathView::modelChanges() QCOMPARE(pathView->currentIndex(), 0); QCOMPARE(currentIndexSpy.count(), 1); - delete window; } void tst_QQuickPathView::pathUpdateOnStartChanged() { - QQuickView *window = createView(); + QScopedPointer window(createView()); QVERIFY(window); window->setSource(testFileUrl("pathUpdateOnStartChanged.qml")); @@ -1326,17 +1310,16 @@ void tst_QQuickPathView::pathUpdateOnStartChanged() QCOMPARE(item->x(), path->startX() - item->width() / 2.0); QCOMPARE(item->y(), path->startY() - item->height() / 2.0); - delete window; } void tst_QQuickPathView::package() { - QQuickView *window = createView(); + QScopedPointer window(createView()); QVERIFY(window); window->setSource(testFileUrl("pathview_package.qml")); window->show(); window->requestActivate(); - QVERIFY(QTest::qWaitForWindowActive(window)); + QVERIFY(QTest::qWaitForWindowActive(window.data())); QQuickPathView *pathView = window->rootObject()->findChild("photoPathView"); QVERIFY(pathView); @@ -1349,13 +1332,12 @@ void tst_QQuickPathView::package() QVERIFY(item); QVERIFY(item->scale() != 1.0); - delete window; } //QTBUG-13017 void tst_QQuickPathView::emptyModel() { - QQuickView *window = createView(); + QScopedPointer window(createView()); QStringListModel model; @@ -1370,7 +1352,6 @@ void tst_QQuickPathView::emptyModel() QCOMPARE(pathview->offset(), qreal(0.0)); - delete window; } void tst_QQuickPathView::closed() @@ -1397,7 +1378,7 @@ void tst_QQuickPathView::closed() // QTBUG-14239 void tst_QQuickPathView::pathUpdate() { - QQuickView *window = createView(); + QScopedPointer window(createView()); QVERIFY(window); window->setSource(testFileUrl("pathUpdate.qml")); @@ -1408,7 +1389,6 @@ void tst_QQuickPathView::pathUpdate() QVERIFY(item); QCOMPARE(item->x(), 150.0); - delete window; } void tst_QQuickPathView::visualDataModel() @@ -1449,12 +1429,12 @@ void tst_QQuickPathView::undefinedPath() void tst_QQuickPathView::mouseDrag() { - QQuickView *window = createView(); + QScopedPointer window(createView()); window->setSource(testFileUrl("dragpath.qml")); window->show(); window->requestActivate(); - QVERIFY(QTest::qWaitForWindowActive(window)); - QCOMPARE(window, qGuiApp->focusWindow()); + QVERIFY(QTest::qWaitForWindowActive(window.data())); + QCOMPARE(window.data(), qGuiApp->focusWindow()); QQuickPathView *pathview = qobject_cast(window->rootObject()); QVERIFY(pathview != 0); @@ -1468,12 +1448,12 @@ void tst_QQuickPathView::mouseDrag() int current = pathview->currentIndex(); - QTest::mousePress(window, Qt::LeftButton, 0, QPoint(10,100)); + QTest::mousePress(window.data(), Qt::LeftButton, 0, QPoint(10,100)); QTest::qWait(100); { QMouseEvent mv(QEvent::MouseMove, QPoint(30,100), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier); - QGuiApplication::sendEvent(window, &mv); + QGuiApplication::sendEvent(window.data(), &mv); } // first move beyond threshold does not trigger drag QVERIFY(!pathview->isMoving()); @@ -1487,7 +1467,7 @@ void tst_QQuickPathView::mouseDrag() { QMouseEvent mv(QEvent::MouseMove, QPoint(90,100), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier); - QGuiApplication::sendEvent(window, &mv); + QGuiApplication::sendEvent(window.data(), &mv); } // next move beyond threshold does trigger drag QVERIFY(pathview->isMoving()); @@ -1501,7 +1481,7 @@ void tst_QQuickPathView::mouseDrag() QVERIFY(pathview->currentIndex() != current); - QTest::mouseRelease(window, Qt::LeftButton, 0, QPoint(40,100)); + QTest::mouseRelease(window.data(), Qt::LeftButton, 0, QPoint(40,100)); QVERIFY(!pathview->isDragging()); QCOMPARE(draggingSpy.count(), 2); QCOMPARE(dragStartedSpy.count(), 1); @@ -1510,12 +1490,11 @@ void tst_QQuickPathView::mouseDrag() QTRY_COMPARE(moveEndedSpy.count(), 1); QCOMPARE(moveStartedSpy.count(), 1); - delete window; } void tst_QQuickPathView::treeModel() { - QQuickView *window = createView(); + QScopedPointer window(createView()); window->show(); QStandardItemModel model; @@ -1538,18 +1517,17 @@ void tst_QQuickPathView::treeModel() QTRY_VERIFY(item = findItem(pathview, "wrapper", 0)); QTRY_COMPARE(item->text(), QLatin1String("Row 2 Child Item")); - delete window; } void tst_QQuickPathView::changePreferredHighlight() { - QQuickView *window = createView(); + QScopedPointer window(createView()); window->setGeometry(0,0,400,200); window->setSource(testFileUrl("dragpath.qml")); window->show(); window->requestActivate(); - QVERIFY(QTest::qWaitForWindowActive(window)); - QCOMPARE(window, qGuiApp->focusWindow()); + QVERIFY(QTest::qWaitForWindowActive(window.data())); + QCOMPARE(window.data(), qGuiApp->focusWindow()); QQuickPathView *pathview = qobject_cast(window->rootObject()); QVERIFY(pathview != 0); @@ -1573,7 +1551,6 @@ void tst_QQuickPathView::changePreferredHighlight() QTRY_COMPARE(firstItem->position() + offset, start); QCOMPARE(pathview->currentIndex(), 0); - delete window; } void tst_QQuickPathView::creationContext() @@ -1594,7 +1571,7 @@ void tst_QQuickPathView::creationContext() // QTBUG-21320 void tst_QQuickPathView::currentOffsetOnInsertion() { - QQuickView *window = createView(); + QScopedPointer window(createView()); window->show(); QaimModel model; @@ -1667,12 +1644,11 @@ void tst_QQuickPathView::currentOffsetOnInsertion() // verify that current item (item 1) is still at offset 0.5 QCOMPARE(item->position() + offset, start); - delete window; } void tst_QQuickPathView::asynchronous() { - QQuickView *window = createView(); + QScopedPointer window(createView()); window->show(); QQmlIncubationController controller; window->engine()->setIncubationController(&controller); @@ -1723,7 +1699,6 @@ void tst_QQuickPathView::asynchronous() QCOMPARE(curItem->position() + offset, itemPos); } - delete window; } void tst_QQuickPathView::missingPercent() @@ -1738,12 +1713,12 @@ void tst_QQuickPathView::missingPercent() void tst_QQuickPathView::cancelDrag() { - QQuickView *window = createView(); + QScopedPointer window(createView()); window->setSource(testFileUrl("dragpath.qml")); window->show(); window->requestActivate(); - QVERIFY(QTest::qWaitForWindowActive(window)); - QCOMPARE(window, qGuiApp->focusWindow()); + QVERIFY(QTest::qWaitForWindowActive(window.data())); + QCOMPARE(window.data(), qGuiApp->focusWindow()); QQuickPathView *pathview = qobject_cast(window->rootObject()); QVERIFY(pathview != 0); @@ -1753,10 +1728,10 @@ void tst_QQuickPathView::cancelDrag() QSignalSpy dragEndedSpy(pathview, SIGNAL(dragEnded())); // drag between snap points - QTest::mousePress(window, Qt::LeftButton, 0, QPoint(10,100)); + QTest::mousePress(window.data(), Qt::LeftButton, 0, QPoint(10,100)); QTest::qWait(100); - QTest::mouseMove(window, QPoint(30, 100)); - QTest::mouseMove(window, QPoint(85, 100)); + QTest::mouseMove(window.data(), QPoint(30, 100)); + QTest::mouseMove(window.data(), QPoint(85, 100)); QTRY_VERIFY(pathview->offset() != qFloor(pathview->offset())); QTRY_VERIFY(pathview->isMoving()); @@ -1777,25 +1752,24 @@ void tst_QQuickPathView::cancelDrag() QCOMPARE(dragStartedSpy.count(), 1); QCOMPARE(dragEndedSpy.count(), 1); - QTest::mouseRelease(window, Qt::LeftButton, 0, QPoint(40,100)); + QTest::mouseRelease(window.data(), Qt::LeftButton, 0, QPoint(40,100)); - delete window; } void tst_QQuickPathView::maximumFlickVelocity() { - QQuickView *window = createView(); + QScopedPointer window(createView()); window->setSource(testFileUrl("dragpath.qml")); window->show(); window->requestActivate(); - QVERIFY(QTest::qWaitForWindowActive(window)); - QCOMPARE(window, qGuiApp->focusWindow()); + QVERIFY(QTest::qWaitForWindowActive(window.data())); + QCOMPARE(window.data(), qGuiApp->focusWindow()); QQuickPathView *pathview = qobject_cast(window->rootObject()); QVERIFY(pathview != 0); pathview->setMaximumFlickVelocity(700); - flick(window, QPoint(200,10), QPoint(10,10), 180); + flick(window.data(), QPoint(200,10), QPoint(10,10), 180); QVERIFY(pathview->isMoving()); QVERIFY(pathview->isFlicking()); QTRY_VERIFY_WITH_TIMEOUT(!pathview->isMoving(), 50000); @@ -1804,7 +1778,7 @@ void tst_QQuickPathView::maximumFlickVelocity() pathview->setOffset(0.); pathview->setMaximumFlickVelocity(300); - flick(window, QPoint(200,10), QPoint(10,10), 180); + flick(window.data(), QPoint(200,10), QPoint(10,10), 180); QVERIFY(pathview->isMoving()); QVERIFY(pathview->isFlicking()); QTRY_VERIFY_WITH_TIMEOUT(!pathview->isMoving(), 50000); @@ -1813,7 +1787,7 @@ void tst_QQuickPathView::maximumFlickVelocity() pathview->setOffset(0.); pathview->setMaximumFlickVelocity(500); - flick(window, QPoint(200,10), QPoint(10,10), 180); + flick(window.data(), QPoint(200,10), QPoint(10,10), 180); QVERIFY(pathview->isMoving()); QVERIFY(pathview->isFlicking()); QTRY_VERIFY_WITH_TIMEOUT(!pathview->isMoving(), 50000); @@ -1824,14 +1798,13 @@ void tst_QQuickPathView::maximumFlickVelocity() QVERIFY(dist3 > dist2); QVERIFY(dist2 < dist1); - delete window; } void tst_QQuickPathView::snapToItem() { QFETCH(bool, enforceRange); - QQuickView *window = createView(); + QScopedPointer window(createView()); window->setSource(testFileUrl("panels.qml")); QQuickPathView *pathview = window->rootObject()->findChild("view"); QVERIFY(pathview != 0); @@ -1843,7 +1816,7 @@ void tst_QQuickPathView::snapToItem() QSignalSpy snapModeSpy(pathview, SIGNAL(snapModeChanged())); - flick(window, QPoint(200,10), QPoint(10,10), 180); + flick(window.data(), QPoint(200,10), QPoint(10,10), 180); QVERIFY(pathview->isMoving()); QTRY_VERIFY(!pathview->isMoving()); @@ -1855,7 +1828,6 @@ void tst_QQuickPathView::snapToItem() else QVERIFY(pathview->currentIndex() == currentIndex); - delete window; } void tst_QQuickPathView::snapToItem_data() @@ -1870,12 +1842,12 @@ void tst_QQuickPathView::snapOneItem() { QFETCH(bool, enforceRange); - QQuickView *window = createView(); + QScopedPointer window(createView()); window->setSource(testFileUrl("panels.qml")); window->show(); window->requestActivate(); - QVERIFY(QTest::qWaitForWindowActive(window)); - QCOMPARE(window, qGuiApp->focusWindow()); + QVERIFY(QTest::qWaitForWindowActive(window.data())); + QCOMPARE(window.data(), qGuiApp->focusWindow()); QQuickPathView *pathview = window->rootObject()->findChild("view"); QVERIFY(pathview != 0); @@ -1891,7 +1863,7 @@ void tst_QQuickPathView::snapOneItem() int currentIndex = pathview->currentIndex(); double startOffset = pathview->offset(); - flick(window, QPoint(200,10), QPoint(10,10), 180); + flick(window.data(), QPoint(200,10), QPoint(10,10), 180); QVERIFY(pathview->isMoving()); QTRY_VERIFY(!pathview->isMoving()); @@ -1904,7 +1876,6 @@ void tst_QQuickPathView::snapOneItem() else QVERIFY(pathview->currentIndex() == currentIndex); - delete window; } void tst_QQuickPathView::snapOneItem_data() @@ -1924,12 +1895,12 @@ void tst_QQuickPathView::positionViewAtIndex() QFETCH(QQuickPathView::PositionMode, mode); QFETCH(qreal, offset); - QQuickView *window = createView(); + QScopedPointer window(createView()); window->setSource(testFileUrl("pathview3.qml")); window->show(); window->requestActivate(); - QVERIFY(QTest::qWaitForWindowActive(window)); - QCOMPARE(window, qGuiApp->focusWindow()); + QVERIFY(QTest::qWaitForWindowActive(window.data())); + QCOMPARE(window.data(), qGuiApp->focusWindow()); QQuickPathView *pathview = qobject_cast(window->rootObject()); QVERIFY(pathview != 0); @@ -1945,7 +1916,6 @@ void tst_QQuickPathView::positionViewAtIndex() QCOMPARE(pathview->offset(), offset); - delete window; } void tst_QQuickPathView::positionViewAtIndex_data() @@ -1988,12 +1958,12 @@ void tst_QQuickPathView::indexAt_itemAt() QFETCH(qreal, y); QFETCH(int, index); - QQuickView *window = createView(); + QScopedPointer window(createView()); window->setSource(testFileUrl("pathview3.qml")); window->show(); window->requestActivate(); - QVERIFY(QTest::qWaitForWindowActive(window)); - QCOMPARE(window, qGuiApp->focusWindow()); + QVERIFY(QTest::qWaitForWindowActive(window.data())); + QCOMPARE(window.data(), qGuiApp->focusWindow()); QQuickPathView *pathview = qobject_cast(window->rootObject()); QVERIFY(pathview != 0); @@ -2006,7 +1976,6 @@ void tst_QQuickPathView::indexAt_itemAt() QCOMPARE(pathview->indexAt(x,y), index); QVERIFY(pathview->itemAt(x,y) == item); - delete window; } void tst_QQuickPathView::indexAt_itemAt_data() @@ -2024,7 +1993,7 @@ void tst_QQuickPathView::indexAt_itemAt_data() void tst_QQuickPathView::cacheItemCount() { - QQuickView *window = createView(); + QScopedPointer window(createView()); window->setSource(testFileUrl("pathview3.qml")); window->show(); @@ -2098,7 +2067,6 @@ void tst_QQuickPathView::cacheItemCount() controller.incubateWhile(&b); } - delete window; } QTEST_MAIN(tst_QQuickPathView) diff --git a/tests/auto/quick/qquickwindow/data/showHideAnimate.qml b/tests/auto/quick/qquickwindow/data/showHideAnimate.qml new file mode 100644 index 0000000000..e83910c5a5 --- /dev/null +++ b/tests/auto/quick/qquickwindow/data/showHideAnimate.qml @@ -0,0 +1,5 @@ +import QtQuick 2.0 + +Item { + NumberAnimation on opacity { from: 0; to: 1; duration: 100; loops: Animation.Infinite } +} diff --git a/tests/auto/quick/qquickwindow/qquickwindow.pro b/tests/auto/quick/qquickwindow/qquickwindow.pro index 9ae63a9ef1..59d96429db 100644 --- a/tests/auto/quick/qquickwindow/qquickwindow.pro +++ b/tests/auto/quick/qquickwindow/qquickwindow.pro @@ -13,6 +13,7 @@ TESTDATA = data/* OTHER_FILES += \ data/AnimationsWhileHidden.qml \ - data/Headless.qml + data/Headless.qml \ + data/showHideAnimate.qml DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 diff --git a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp index f7fc549069..3e907a5204 100644 --- a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp +++ b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp @@ -312,6 +312,11 @@ private slots: void ownershipRootItem(); + void hideThenDelete_data(); + void hideThenDelete(); + + void showHideAnimate(); + #ifndef QT_NO_CURSOR void cursor(); #endif @@ -945,6 +950,8 @@ void tst_qquickwindow::headless() QScopedPointer cleanup(created); QQuickWindow* window = qobject_cast(created); + window->setPersistentOpenGLContext(false); + window->setPersistentSceneGraph(false); QVERIFY(window); window->show(); @@ -1212,6 +1219,88 @@ void tst_qquickwindow::cursor() } #endif +void tst_qquickwindow::hideThenDelete_data() +{ + QTest::addColumn("persistentSG"); + QTest::addColumn("persistentGL"); + + QTest::newRow("persistent:SG=false,GL=false") << false << false; + QTest::newRow("persistent:SG=true,GL=false") << true << false; + QTest::newRow("persistent:SG=false,GL=true") << false << true; + QTest::newRow("persistent:SG=true,GL=true") << true << true; +} + +void tst_qquickwindow::hideThenDelete() +{ + if (QGuiApplication::platformName() == QStringLiteral("xcb")) { + QSKIP("For some obscure reason this test fails in CI only"); + return; + } + + QFETCH(bool, persistentSG); + QFETCH(bool, persistentGL); + + QSignalSpy *openglDestroyed = 0; + QSignalSpy *sgInvalidated = 0; + + { + QQuickWindow window; + window.setColor(Qt::red); + + window.setPersistentSceneGraph(persistentSG); + window.setPersistentOpenGLContext(persistentGL); + + window.resize(400, 300); + window.show(); + + QTest::qWaitForWindowExposed(&window); + + openglDestroyed = new QSignalSpy(window.openglContext(), SIGNAL(aboutToBeDestroyed())); + sgInvalidated = new QSignalSpy(&window, SIGNAL(sceneGraphInvalidated())); + + window.hide(); + + QTRY_VERIFY(!window.isExposed()); + + if (!persistentSG) { + QVERIFY(sgInvalidated->size() > 0); + if (!persistentGL) + QVERIFY(openglDestroyed->size() > 0); + else + QVERIFY(openglDestroyed->size() == 0); + } else { + QVERIFY(sgInvalidated->size() == 0); + QVERIFY(openglDestroyed->size() == 0); + } + } + + QVERIFY(sgInvalidated->size() > 0); + QVERIFY(openglDestroyed->size() > 0); +} + +void tst_qquickwindow::showHideAnimate() +{ + // This test tries to mimick a bug triggered in the qquickanimatedimage test + // A window is shown, then removed again before it is exposed. This left + // traces in the render loop which prevent other animations from running + // later on. + { + QQuickWindow window; + window.resize(400, 300); + window.show(); + } + + QQmlEngine engine; + QQmlComponent component(&engine); + component.loadUrl(testFileUrl("showHideAnimate.qml")); + QQuickItem* created = qobject_cast(component.create()); + + QVERIFY(created); + + QTRY_VERIFY(created->opacity() > 0.5); + QTRY_VERIFY(created->opacity() < 0.5); +} + QTEST_MAIN(tst_qquickwindow) #include "tst_qquickwindow.moc" -- cgit v1.2.3 From 40574b466a5773fdb7ccddded45e237df375d848 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Wed, 23 Jan 2013 13:59:23 +0100 Subject: Fix import path of TextBallon-example. Task-number: QTBUG-28144 Change-Id: Ic20fa242137a08e43b0685ebe701def929028647 Reviewed-by: Jerome Pasion --- examples/quick/customitems/painteditem/textballoons.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/quick/customitems/painteditem/textballoons.qml b/examples/quick/customitems/painteditem/textballoons.qml index a851794e2f..e498ca3a7c 100644 --- a/examples/quick/customitems/painteditem/textballoons.qml +++ b/examples/quick/customitems/painteditem/textballoons.qml @@ -40,7 +40,7 @@ ****************************************************************************/ import QtQuick 2.0 -import TextBalloonPlugin 1.0 +import "TextBalloonPlugin" 1.0 Item { height: 480 -- cgit v1.2.3 From b700796884d29f1947194553c0103b5390b6bd42 Mon Sep 17 00:00:00 2001 From: Alan Alpert Date: Mon, 24 Dec 2012 08:48:49 -0800 Subject: Move documentation to QtQml module The recent move of certain elements from the QtQuick module forgot to update the module in some of the documentation strings. Change-Id: I5da27772004c7ceeb9c5ab6184e0078ac64ba07a Reviewed-by: Jerome Pasion --- src/qml/qml/qqmlbind.cpp | 10 +++++----- src/qml/qml/qqmlconnections.cpp | 6 +++--- src/qml/qml/qqmltimer.cpp | 18 +++++++++--------- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/qml/qml/qqmlbind.cpp b/src/qml/qml/qqmlbind.cpp index 7eb182b034..fcb3079891 100644 --- a/src/qml/qml/qqmlbind.cpp +++ b/src/qml/qml/qqmlbind.cpp @@ -77,7 +77,7 @@ public: /*! \qmltype Binding \instantiates QQmlBind - \inqmlmodule QtQuick 2 + \inqmlmodule QtQml 2 \ingroup qtquick-interceptors \brief Enables the arbitrary creation of property bindings @@ -151,7 +151,7 @@ QQmlBind::~QQmlBind() } /*! - \qmlproperty bool QtQuick2::Binding::when + \qmlproperty bool QtQml2::Binding::when This property holds when the binding is active. This should be set to an expression that evaluates to true when you want the binding to be active. @@ -183,7 +183,7 @@ void QQmlBind::setWhen(bool v) } /*! - \qmlproperty Object QtQuick2::Binding::target + \qmlproperty Object QtQml2::Binding::target The object to be updated. */ @@ -210,7 +210,7 @@ void QQmlBind::setObject(QObject *obj) } /*! - \qmlproperty string QtQuick2::Binding::property + \qmlproperty string QtQml2::Binding::property The property to be updated. */ @@ -237,7 +237,7 @@ void QQmlBind::setProperty(const QString &p) } /*! - \qmlproperty any QtQuick2::Binding::value + \qmlproperty any QtQml2::Binding::value The value to be set on the target object and property. This can be a constant (which isn't very useful), or a bound expression. diff --git a/src/qml/qml/qqmlconnections.cpp b/src/qml/qml/qqmlconnections.cpp index f2d29bf393..286933e557 100644 --- a/src/qml/qml/qqmlconnections.cpp +++ b/src/qml/qml/qqmlconnections.cpp @@ -73,7 +73,7 @@ public: /*! \qmltype Connections \instantiates QQmlConnections - \inqmlmodule QtQuick 2 + \inqmlmodule QtQml 2 \ingroup qtquick-interceptors \brief Describes generalized connections to signals @@ -138,7 +138,7 @@ QQmlConnections::~QQmlConnections() } /*! - \qmlproperty Object QtQuick2::Connections::target + \qmlproperty Object QtQml2::Connections::target This property holds the object that sends the signal. If this property is not set, the \c target defaults to the parent of the Connection. @@ -183,7 +183,7 @@ void QQmlConnections::setTarget(QObject *obj) } /*! - \qmlproperty bool QtQuick2::Connections::ignoreUnknownSignals + \qmlproperty bool QtQml2::Connections::ignoreUnknownSignals Normally, a connection to a non-existent signal produces runtime errors. diff --git a/src/qml/qml/qqmltimer.cpp b/src/qml/qml/qqmltimer.cpp index c9f6bc7982..a1cb8532f7 100644 --- a/src/qml/qml/qqmltimer.cpp +++ b/src/qml/qml/qqmltimer.cpp @@ -75,7 +75,7 @@ public: /*! \qmltype Timer \instantiates QQmlTimer - \inqmlmodule QtQuick 2 + \inqmlmodule QtQml 2 \ingroup qtquick-interceptors \brief Triggers a handler at a specified interval @@ -122,7 +122,7 @@ QQmlTimer::QQmlTimer(QObject *parent) } /*! - \qmlproperty int QtQuick2::Timer::interval + \qmlproperty int QtQml2::Timer::interval Sets the \a interval between triggers, in milliseconds. @@ -145,7 +145,7 @@ int QQmlTimer::interval() const } /*! - \qmlproperty bool QtQuick2::Timer::running + \qmlproperty bool QtQml2::Timer::running If set to true, starts the timer; otherwise stops the timer. For a non-repeating timer, \a running is set to false after the @@ -173,7 +173,7 @@ void QQmlTimer::setRunning(bool running) } /*! - \qmlproperty bool QtQuick2::Timer::repeat + \qmlproperty bool QtQml2::Timer::repeat If \a repeat is true the timer is triggered repeatedly at the specified interval; otherwise, the timer will trigger once at the @@ -200,7 +200,7 @@ void QQmlTimer::setRepeating(bool repeating) } /*! - \qmlproperty bool QtQuick2::Timer::triggeredOnStart + \qmlproperty bool QtQml2::Timer::triggeredOnStart When a timer is started, the first trigger is usually after the specified interval has elapsed. It is sometimes desirable to trigger immediately @@ -233,7 +233,7 @@ void QQmlTimer::setTriggeredOnStart(bool triggeredOnStart) } /*! - \qmlmethod QtQuick2::Timer::start() + \qmlmethod QtQml2::Timer::start() \brief Starts the timer If the timer is already running, calling this method has no effect. The @@ -245,7 +245,7 @@ void QQmlTimer::start() } /*! - \qmlmethod QtQuick2::Timer::stop() + \qmlmethod QtQml2::Timer::stop() \brief Stops the timer If the timer is not running, calling this method has no effect. The @@ -257,7 +257,7 @@ void QQmlTimer::stop() } /*! - \qmlmethod QtQuick2::Timer::restart() + \qmlmethod QtQml2::Timer::restart() \brief Restarts the timer If the Timer is not running it will be started, otherwise it will be @@ -302,7 +302,7 @@ void QQmlTimer::componentComplete() } /*! - \qmlsignal QtQuick2::Timer::onTriggered() + \qmlsignal QtQml2::Timer::onTriggered() This handler is called when the Timer is triggered. */ -- cgit v1.2.3 From eab53ad9fe0aa9664702d446a704256f46106c56 Mon Sep 17 00:00:00 2001 From: Alan Alpert <416365416c@gmail.com> Date: Sun, 16 Dec 2012 14:42:40 -0800 Subject: Refactor QQmlTypePrivate Now has a registrationType, and an extraData union for data only relevant to certain registration types. Change-Id: I9b127dff7955456aacb25138fa6ea8efb7bb9220 Reviewed-by: Christopher Adams --- src/qml/qml/qqmlmetatype.cpp | 256 ++++++++++++++++++++++++++++--------------- src/qml/qml/qqmlmetatype_p.h | 5 +- 2 files changed, 169 insertions(+), 92 deletions(-) diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index 207aa1f16d..e4f51474b9 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -148,46 +148,60 @@ QQmlMetaTypeData::~QQmlMetaTypeData() class QQmlTypePrivate { public: - QQmlTypePrivate(); + QQmlTypePrivate(QQmlType::RegistrationType type); ~QQmlTypePrivate(); void init() const; void initEnums() const; void insertEnums(const QMetaObject *metaObject) const; - bool isInterface : 1; + QQmlType::RegistrationType regType; + + struct QQmlCppTypeData + { + int allocationSize; + void (*newFunc)(void *); + QString noCreationReason; + int parserStatusCast; + QObject *(*extFunc)(QObject *); + const QMetaObject *extMetaObject; + QQmlCustomParser *customParser; + QQmlAttachedPropertiesFunc attachedPropertiesFunc; + const QMetaObject *attachedPropertiesType; + int attachedPropertiesId; + int propertyValueSourceCast; + int propertyValueInterceptorCast; + }; + + struct QQmlSingletonTypeData + { + QQmlType::SingletonInstanceInfo *singletonInstanceInfo; + }; + + union extraData { + QQmlCppTypeData* cd; + QQmlSingletonTypeData* sd; + } extraData; + const char *iid; QHashedString module; QString name; QString elementName; int version_maj; int version_min; - int typeId; int listId; + int typeId; + int listId; int revision; mutable bool containsRevisionedAttributes; mutable QQmlType *superType; - - int allocationSize; - void (*newFunc)(void *); - QString noCreationReason; - const QMetaObject *baseMetaObject; - QQmlAttachedPropertiesFunc attachedPropertiesFunc; - const QMetaObject *attachedPropertiesType; - int attachedPropertiesId; - int parserStatusCast; - int propertyValueSourceCast; - int propertyValueInterceptorCast; - QObject *(*extFunc)(QObject *); - const QMetaObject *extMetaObject; + int index; - QQmlCustomParser *customParser; mutable volatile bool isSetup:1; mutable volatile bool isEnumSetup:1; mutable bool haveSuperType:1; mutable QList metaObjects; mutable QStringHash enums; - QQmlType::SingletonInstanceInfo *singletonInstanceInfo; static QHash attachedPropertyIds; }; @@ -252,28 +266,58 @@ QJSValue QQmlType::SingletonInstanceInfo::scriptApi(QQmlEngine *e) const QHash QQmlTypePrivate::attachedPropertyIds; -QQmlTypePrivate::QQmlTypePrivate() -: isInterface(false), iid(0), typeId(0), listId(0), revision(0), containsRevisionedAttributes(false), - superType(0), allocationSize(0), newFunc(0), baseMetaObject(0), attachedPropertiesFunc(0), - attachedPropertiesType(0), parserStatusCast(-1), propertyValueSourceCast(-1), - propertyValueInterceptorCast(-1), extFunc(0), extMetaObject(0), index(-1), customParser(0), - isSetup(false), isEnumSetup(false), haveSuperType(false), singletonInstanceInfo(0) -{ +QQmlTypePrivate::QQmlTypePrivate(QQmlType::RegistrationType type) +: regType(type), iid(0), typeId(0), listId(0), revision(0), + containsRevisionedAttributes(false), superType(0), baseMetaObject(0), + index(-1), isSetup(false), isEnumSetup(false), haveSuperType(false) +{ + switch (type) { + case QQmlType::CppType: + extraData.cd = new QQmlCppTypeData; + extraData.cd->allocationSize = 0; + extraData.cd->newFunc = 0; + extraData.cd->parserStatusCast = -1; + extraData.cd->extFunc = 0; + extraData.cd->extMetaObject = 0; + extraData.cd->customParser = 0; + extraData.cd->attachedPropertiesFunc = 0; + extraData.cd->attachedPropertiesType = 0; + extraData.cd->propertyValueSourceCast = -1; + extraData.cd->propertyValueInterceptorCast = -1; + break; + case QQmlType::SingletonType: + extraData.sd = new QQmlSingletonTypeData; + extraData.sd->singletonInstanceInfo = 0; + break; + case QQmlType::InterfaceType: + extraData.cd = 0; + break; + default: qFatal("QQmlTypePrivate Internal Error."); + } } QQmlTypePrivate::~QQmlTypePrivate() { - delete singletonInstanceInfo; + switch (regType) { + case QQmlType::CppType: + delete extraData.cd->customParser; + delete extraData.cd; + break; + case QQmlType::SingletonType: + delete extraData.sd->singletonInstanceInfo; + delete extraData.sd; + break; + default: //Also InterfaceType, because it has no extra data + break; + } } QQmlType::QQmlType(int index, const QQmlPrivate::RegisterInterface &interface) -: d(new QQmlTypePrivate) +: d(new QQmlTypePrivate(InterfaceType)) { - d->isInterface = true; d->iid = interface.iid; d->typeId = interface.typeId; d->listId = interface.listId; - d->newFunc = 0; d->index = index; d->isSetup = true; d->version_maj = 0; @@ -281,7 +325,7 @@ QQmlType::QQmlType(int index, const QQmlPrivate::RegisterInterface &interface) } QQmlType::QQmlType(int index, const QString &elementName, const QQmlPrivate::RegisterSingletonType &type) -: d(new QQmlTypePrivate) +: d(new QQmlTypePrivate(SingletonType)) { d->elementName = elementName; d->module = moduleFromUtf8(type.uri); @@ -298,18 +342,18 @@ QQmlType::QQmlType(int index, const QString &elementName, const QQmlPrivate::Reg d->revision = type.revision; } - d->newFunc = 0; d->index = index; - d->singletonInstanceInfo = new SingletonInstanceInfo; - d->singletonInstanceInfo->scriptCallback = type.scriptApi; - d->singletonInstanceInfo->qobjectCallback = type.qobjectApi; - d->singletonInstanceInfo->typeName = QString::fromUtf8(type.typeName); - d->singletonInstanceInfo->instanceMetaObject = (type.qobjectApi && type.version >= 1) ? type.instanceMetaObject : 0; + d->extraData.sd->singletonInstanceInfo = new SingletonInstanceInfo; + d->extraData.sd->singletonInstanceInfo->scriptCallback = type.scriptApi; + d->extraData.sd->singletonInstanceInfo->qobjectCallback = type.qobjectApi; + d->extraData.sd->singletonInstanceInfo->typeName = QString::fromUtf8(type.typeName); + d->extraData.sd->singletonInstanceInfo->instanceMetaObject + = (type.qobjectApi && type.version >= 1) ? type.instanceMetaObject : 0; } QQmlType::QQmlType(int index, const QString &elementName, const QQmlPrivate::RegisterType &type) -: d(new QQmlTypePrivate) +: d(new QQmlTypePrivate(CppType)) { d->elementName = elementName; d->module = moduleFromUtf8(type.uri); @@ -320,34 +364,33 @@ QQmlType::QQmlType(int index, const QString &elementName, const QQmlPrivate::Reg d->revision = type.revision; d->typeId = type.typeId; d->listId = type.listId; - d->allocationSize = type.objectSize; - d->newFunc = type.create; - d->noCreationReason = type.noCreationReason; + d->extraData.cd->allocationSize = type.objectSize; + d->extraData.cd->newFunc = type.create; + d->extraData.cd->noCreationReason = type.noCreationReason; d->baseMetaObject = type.metaObject; - d->attachedPropertiesFunc = type.attachedPropertiesFunction; - d->attachedPropertiesType = type.attachedPropertiesMetaObject; - if (d->attachedPropertiesType) { + d->extraData.cd->attachedPropertiesFunc = type.attachedPropertiesFunction; + d->extraData.cd->attachedPropertiesType = type.attachedPropertiesMetaObject; + if (d->extraData.cd->attachedPropertiesType) { QHash::Iterator iter = d->attachedPropertyIds.find(d->baseMetaObject); if (iter == d->attachedPropertyIds.end()) iter = d->attachedPropertyIds.insert(d->baseMetaObject, index); - d->attachedPropertiesId = *iter; + d->extraData.cd->attachedPropertiesId = *iter; } else { - d->attachedPropertiesId = -1; + d->extraData.cd->attachedPropertiesId = -1; } - d->parserStatusCast = type.parserStatusCast; - d->propertyValueSourceCast = type.valueSourceCast; - d->propertyValueInterceptorCast = type.valueInterceptorCast; - d->extFunc = type.extensionObjectCreate; + d->extraData.cd->parserStatusCast = type.parserStatusCast; + d->extraData.cd->propertyValueSourceCast = type.valueSourceCast; + d->extraData.cd->propertyValueInterceptorCast = type.valueInterceptorCast; + d->extraData.cd->extFunc = type.extensionObjectCreate; + d->extraData.cd->customParser = type.customParser; d->index = index; - d->customParser = type.customParser; if (type.extensionMetaObject) - d->extMetaObject = type.extensionMetaObject; + d->extraData.cd->extMetaObject = type.extensionMetaObject; } QQmlType::~QQmlType() { - delete d->customParser; delete d; } @@ -481,7 +524,8 @@ static bool isPropertyRevisioned(const QMetaObject *mo, int index) void QQmlTypePrivate::init() const { - if (isSetup) return; + if (isSetup) + return; QWriteLocker lock(metaTypeDataLock()); if (isSetup) @@ -489,35 +533,37 @@ void QQmlTypePrivate::init() const const QMetaObject *mo = baseMetaObject; if (!mo) { - // singleton type without metaobject information + // version 0 singleton type without metaobject information return; } - // Setup extended meta object - // XXX - very inefficient - if (extFunc) { - QMetaObjectBuilder builder; - clone(builder, extMetaObject, extMetaObject, extMetaObject); - builder.setFlags(QMetaObjectBuilder::DynamicMetaObject); - QMetaObject *mmo = builder.toMetaObject(); - mmo->d.superdata = mo; - QQmlProxyMetaObject::ProxyData data = { mmo, extFunc, 0, 0 }; - metaObjects << data; + if (regType == QQmlType::CppType) { + // Setup extended meta object + // XXX - very inefficient + if (extraData.cd->extFunc) { + QMetaObjectBuilder builder; + clone(builder, extraData.cd->extMetaObject, extraData.cd->extMetaObject, extraData.cd->extMetaObject); + builder.setFlags(QMetaObjectBuilder::DynamicMetaObject); + QMetaObject *mmo = builder.toMetaObject(); + mmo->d.superdata = mo; + QQmlProxyMetaObject::ProxyData data = { mmo, extraData.cd->extFunc, 0, 0 }; + metaObjects << data; + } } mo = mo->d.superdata; while(mo) { QQmlType *t = metaTypeData()->metaObjectToType.value(mo); if (t) { - if (t->d->extFunc) { + if (t->d->extraData.cd->extFunc) { QMetaObjectBuilder builder; - clone(builder, t->d->extMetaObject, t->d->baseMetaObject, baseMetaObject); + clone(builder, t->d->extraData.cd->extMetaObject, t->d->baseMetaObject, baseMetaObject); builder.setFlags(QMetaObjectBuilder::DynamicMetaObject); QMetaObject *mmo = builder.toMetaObject(); mmo->d.superdata = baseMetaObject; if (!metaObjects.isEmpty()) metaObjects.last().metaObject->d.superdata = mmo; - QQmlProxyMetaObject::ProxyData data = { mmo, t->d->extFunc, 0, 0 }; + QQmlProxyMetaObject::ProxyData data = { mmo, t->d->extraData.cd->extFunc, 0, 0 }; metaObjects << data; } } @@ -530,7 +576,7 @@ void QQmlTypePrivate::init() const metaObjects[ii].methodOffset = metaObjects.at(ii).metaObject->methodOffset(); } - + // Check for revisioned details { const QMetaObject *mo = 0; @@ -590,9 +636,9 @@ void QQmlTypePrivate::insertEnums(const QMetaObject *metaObject) const QByteArray QQmlType::typeName() const { - if (d->singletonInstanceInfo) - return d->singletonInstanceInfo->typeName.toUtf8(); - if (d->baseMetaObject) + if (d->regType == SingletonType) + return d->extraData.sd->singletonInstanceInfo->typeName.toUtf8(); + else if (d->baseMetaObject) return d->baseMetaObject->className(); else return QByteArray(); @@ -617,10 +663,13 @@ const QString &QQmlType::qmlTypeName() const QObject *QQmlType::create() const { + if (!isCreatable()) + return 0; + d->init(); - QObject *rv = (QObject *)operator new(d->allocationSize); - d->newFunc(rv); + QObject *rv = (QObject *)operator new(d->extraData.cd->allocationSize); + d->extraData.cd->newFunc(rv); if (rv && !d->metaObjects.isEmpty()) (void)new QQmlProxyMetaObject(rv, &d->metaObjects); @@ -630,46 +679,59 @@ QObject *QQmlType::create() const void QQmlType::create(QObject **out, void **memory, size_t additionalMemory) const { + if (!isCreatable()) + return; + d->init(); - QObject *rv = (QObject *)operator new(d->allocationSize + additionalMemory); - d->newFunc(rv); + QObject *rv = (QObject *)operator new(d->extraData.cd->allocationSize + additionalMemory); + d->extraData.cd->newFunc(rv); if (rv && !d->metaObjects.isEmpty()) (void)new QQmlProxyMetaObject(rv, &d->metaObjects); *out = rv; - *memory = ((char *)rv) + d->allocationSize; + *memory = ((char *)rv) + d->extraData.cd->allocationSize; } QQmlType::SingletonInstanceInfo *QQmlType::singletonInstanceInfo() const { - return d->singletonInstanceInfo; + if (d->regType != SingletonType) + return 0; + return d->extraData.sd->singletonInstanceInfo; } QQmlCustomParser *QQmlType::customParser() const { - return d->customParser; + if (d->regType != CppType) + return 0; + return d->extraData.cd->customParser; } QQmlType::CreateFunc QQmlType::createFunction() const { - return d->newFunc; + if (d->regType != CppType) + return 0; + return d->extraData.cd->newFunc; } QString QQmlType::noCreationReason() const { - return d->noCreationReason; + if (d->regType != CppType) + return QString(); + return d->extraData.cd->noCreationReason; } int QQmlType::createSize() const { - return d->allocationSize; + if (d->regType != CppType) + return 0; + return d->extraData.cd->allocationSize; } bool QQmlType::isCreatable() const { - return d->newFunc != 0; + return d->regType == CppType && d->extraData.cd->newFunc; } bool QQmlType::isExtendedType() const @@ -681,12 +743,12 @@ bool QQmlType::isExtendedType() const bool QQmlType::isSingleton() const { - return d->singletonInstanceInfo != 0; + return d->regType == SingletonType; } bool QQmlType::isInterface() const { - return d->isInterface; + return d->regType == InterfaceType; } int QQmlType::typeId() const @@ -729,12 +791,16 @@ int QQmlType::metaObjectRevision() const QQmlAttachedPropertiesFunc QQmlType::attachedPropertiesFunction() const { - return d->attachedPropertiesFunc; + if (d->regType != CppType) + return 0; + return d->extraData.cd->attachedPropertiesFunc; } const QMetaObject *QQmlType::attachedPropertiesType() const { - return d->attachedPropertiesType; + if (d->regType != CppType) + return 0; + return d->extraData.cd->attachedPropertiesType; } /* @@ -744,26 +810,36 @@ Qt 4.7 and QtQuick 1.0). */ int QQmlType::attachedPropertiesId() const { - return d->attachedPropertiesId; + if (d->regType != CppType) + return 0; + return d->extraData.cd->attachedPropertiesId; } int QQmlType::parserStatusCast() const { - return d->parserStatusCast; + if (d->regType != CppType) + return -1; + return d->extraData.cd->parserStatusCast; } int QQmlType::propertyValueSourceCast() const { - return d->propertyValueSourceCast; + if (d->regType != CppType) + return -1; + return d->extraData.cd->propertyValueSourceCast; } int QQmlType::propertyValueInterceptorCast() const { - return d->propertyValueInterceptorCast; + if (d->regType != CppType) + return -1; + return d->extraData.cd->propertyValueInterceptorCast; } const char *QQmlType::interfaceIId() const { + if (d->regType != InterfaceType) + return 0; return d->iid; } diff --git a/src/qml/qml/qqmlmetatype_p.h b/src/qml/qml/qqmlmetatype_p.h index 474eac184c..8d3aad519e 100644 --- a/src/qml/qml/qqmlmetatype_p.h +++ b/src/qml/qml/qqmlmetatype_p.h @@ -217,8 +217,9 @@ private: enum RegistrationType { CppType = 0, - SingletonType = 1 - // In the future, we should register all types via QQmlType, including Composite types. + SingletonType = 1, + InterfaceType = 2 + // In the near future, we should register all types via QQmlType, including Composite types. }; friend QString registrationTypeString(RegistrationType); friend bool checkRegistration(RegistrationType, QQmlMetaTypeData *, const char *, const QString &); -- cgit v1.2.3 From bbb3d5b403511f6e0bc1966835983b2574596e25 Mon Sep 17 00:00:00 2001 From: Alan Alpert Date: Thu, 10 Jan 2013 14:07:37 -0800 Subject: Move ListModel and ListElement to the QtQml import They're already in the QtQml module, but were left in the QtQuick import because they were considered to be of minimal use without QtQuick types. QtQml types are being developed would could make ListModel useful without QtQuick, indicating that they should no longer be considered QtQuick depedent. Change-Id: I31499f2cc23baf4bc70fb451ba164408bed89ff6 Reviewed-by: Andrew den Exter --- src/qml/qml/qml.pri | 10 +- src/qml/qml/qqmlengine.cpp | 6 +- src/qml/qml/qqmllistmodel.cpp | 2567 ++++++++++++++++++++ src/qml/qml/qqmllistmodel_p.h | 202 ++ src/qml/qml/qqmllistmodel_p_p.h | 382 +++ src/qml/qml/qqmllistmodelworkeragent.cpp | 259 ++ src/qml/qml/qqmllistmodelworkeragent_p.h | 160 ++ src/qml/qml/qquicklistmodel.cpp | 2567 -------------------- src/qml/qml/qquicklistmodel_p.h | 202 -- src/qml/qml/qquicklistmodel_p_p.h | 382 --- src/qml/qml/qquicklistmodelworkeragent.cpp | 259 -- src/qml/qml/qquicklistmodelworkeragent_p.h | 160 -- src/qml/qml/qquickworkerscript.cpp | 6 +- src/qml/qml/v8/qv8worker.cpp | 12 +- tests/auto/qml/qml.pro | 4 +- tests/auto/qml/qqmllistmodel/data/enumerate.qml | 24 + .../auto/qml/qqmllistmodel/data/multipleroles.qml | 25 + .../qml/qqmllistmodel/data/setmodelcachelist.qml | 20 + .../auto/qml/qqmllistmodel/data/signalhandlers.qml | 8 + tests/auto/qml/qqmllistmodel/qqmllistmodel.pro | 14 + tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp | 1258 ++++++++++ .../qml/qqmllistmodelworkerscript/data/model.qml | 26 + .../qml/qqmllistmodelworkerscript/data/script.js | 13 + .../data/workerremoveelement.js | 8 + .../data/workerremoveelement.qml | 33 + .../data/workerremovelist.js | 9 + .../data/workerremovelist.qml | 33 + .../qqmllistmodelworkerscript/data/workersync.js | 8 + .../qqmllistmodelworkerscript/data/workersync.qml | 32 + .../qqmllistmodelworkerscript.pro | 12 + .../tst_qqmllistmodelworkerscript.cpp | 859 +++++++ .../imports/com/nokia/AutoTestQmlPluginType/qmldir | 1 - tests/auto/qml/qquicklistmodel/data/enumerate.qml | 24 - .../qml/qquicklistmodel/data/multipleroles.qml | 25 - .../qml/qquicklistmodel/data/setmodelcachelist.qml | 20 - .../qml/qquicklistmodel/data/signalhandlers.qml | 8 - tests/auto/qml/qquicklistmodel/qquicklistmodel.pro | 14 - .../qml/qquicklistmodel/tst_qquicklistmodel.cpp | 1258 ---------- .../qml/qquicklistmodelworkerscript/data/model.qml | 26 - .../qml/qquicklistmodelworkerscript/data/script.js | 13 - .../data/workerremoveelement.js | 8 - .../data/workerremoveelement.qml | 33 - .../data/workerremovelist.js | 9 - .../data/workerremovelist.qml | 33 - .../qquicklistmodelworkerscript/data/workersync.js | 8 - .../data/workersync.qml | 32 - .../qquicklistmodelworkerscript.pro | 12 - .../tst_qquicklistmodelworkerscript.cpp | 859 ------- .../qquickworkerscript/tst_qquickworkerscript.cpp | 2 +- .../quick/qquickgridview/tst_qquickgridview.cpp | 4 +- .../quick/qquicklistview/tst_qquicklistview.cpp | 4 +- .../quick/qquickpathview/tst_qquickpathview.cpp | 4 +- 52 files changed, 5978 insertions(+), 5979 deletions(-) create mode 100644 src/qml/qml/qqmllistmodel.cpp create mode 100644 src/qml/qml/qqmllistmodel_p.h create mode 100644 src/qml/qml/qqmllistmodel_p_p.h create mode 100644 src/qml/qml/qqmllistmodelworkeragent.cpp create mode 100644 src/qml/qml/qqmllistmodelworkeragent_p.h delete mode 100644 src/qml/qml/qquicklistmodel.cpp delete mode 100644 src/qml/qml/qquicklistmodel_p.h delete mode 100644 src/qml/qml/qquicklistmodel_p_p.h delete mode 100644 src/qml/qml/qquicklistmodelworkeragent.cpp delete mode 100644 src/qml/qml/qquicklistmodelworkeragent_p.h create mode 100644 tests/auto/qml/qqmllistmodel/data/enumerate.qml create mode 100644 tests/auto/qml/qqmllistmodel/data/multipleroles.qml create mode 100644 tests/auto/qml/qqmllistmodel/data/setmodelcachelist.qml create mode 100644 tests/auto/qml/qqmllistmodel/data/signalhandlers.qml create mode 100644 tests/auto/qml/qqmllistmodel/qqmllistmodel.pro create mode 100644 tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp create mode 100644 tests/auto/qml/qqmllistmodelworkerscript/data/model.qml create mode 100644 tests/auto/qml/qqmllistmodelworkerscript/data/script.js create mode 100644 tests/auto/qml/qqmllistmodelworkerscript/data/workerremoveelement.js create mode 100644 tests/auto/qml/qqmllistmodelworkerscript/data/workerremoveelement.qml create mode 100644 tests/auto/qml/qqmllistmodelworkerscript/data/workerremovelist.js create mode 100644 tests/auto/qml/qqmllistmodelworkerscript/data/workerremovelist.qml create mode 100644 tests/auto/qml/qqmllistmodelworkerscript/data/workersync.js create mode 100644 tests/auto/qml/qqmllistmodelworkerscript/data/workersync.qml create mode 100644 tests/auto/qml/qqmllistmodelworkerscript/qqmllistmodelworkerscript.pro create mode 100644 tests/auto/qml/qqmllistmodelworkerscript/tst_qqmllistmodelworkerscript.cpp delete mode 100644 tests/auto/qml/qqmlmoduleplugin/imports/com/nokia/AutoTestQmlPluginType/qmldir delete mode 100644 tests/auto/qml/qquicklistmodel/data/enumerate.qml delete mode 100644 tests/auto/qml/qquicklistmodel/data/multipleroles.qml delete mode 100644 tests/auto/qml/qquicklistmodel/data/setmodelcachelist.qml delete mode 100644 tests/auto/qml/qquicklistmodel/data/signalhandlers.qml delete mode 100644 tests/auto/qml/qquicklistmodel/qquicklistmodel.pro delete mode 100644 tests/auto/qml/qquicklistmodel/tst_qquicklistmodel.cpp delete mode 100644 tests/auto/qml/qquicklistmodelworkerscript/data/model.qml delete mode 100644 tests/auto/qml/qquicklistmodelworkerscript/data/script.js delete mode 100644 tests/auto/qml/qquicklistmodelworkerscript/data/workerremoveelement.js delete mode 100644 tests/auto/qml/qquicklistmodelworkerscript/data/workerremoveelement.qml delete mode 100644 tests/auto/qml/qquicklistmodelworkerscript/data/workerremovelist.js delete mode 100644 tests/auto/qml/qquicklistmodelworkerscript/data/workerremovelist.qml delete mode 100644 tests/auto/qml/qquicklistmodelworkerscript/data/workersync.js delete mode 100644 tests/auto/qml/qquicklistmodelworkerscript/data/workersync.qml delete mode 100644 tests/auto/qml/qquicklistmodelworkerscript/qquicklistmodelworkerscript.pro delete mode 100644 tests/auto/qml/qquicklistmodelworkerscript/tst_qquicklistmodelworkerscript.cpp diff --git a/src/qml/qml/qml.pri b/src/qml/qml/qml.pri index 20b46f0939..87fe47750f 100644 --- a/src/qml/qml/qml.pri +++ b/src/qml/qml/qml.pri @@ -1,7 +1,7 @@ SOURCES += \ $$PWD/qqmlinstruction.cpp \ - $$PWD/qquicklistmodel.cpp \ - $$PWD/qquicklistmodelworkeragent.cpp \ + $$PWD/qqmllistmodel.cpp \ + $$PWD/qqmllistmodelworkeragent.cpp \ $$PWD/qqmlopenmetaobject.cpp \ $$PWD/qqmlvmemetaobject.cpp \ $$PWD/qqmlengine.cpp \ @@ -60,9 +60,9 @@ SOURCES += \ HEADERS += \ $$PWD/qqmlglobal_p.h \ $$PWD/qqmlinstruction_p.h \ - $$PWD/qquicklistmodel_p.h\ - $$PWD/qquicklistmodel_p_p.h\ - $$PWD/qquicklistmodelworkeragent_p.h \ + $$PWD/qqmllistmodel_p.h\ + $$PWD/qqmllistmodel_p_p.h\ + $$PWD/qqmllistmodelworkeragent_p.h \ $$PWD/qqmlopenmetaobject_p.h \ $$PWD/qqmlvmemetaobject_p.h \ $$PWD/qqml.h \ diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index 145818aadf..600f526c45 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -55,7 +55,7 @@ #include "qqmlxmlhttprequest_p.h" #include "qqmlscriptstring.h" #include "qqmlglobal_p.h" -#include "qquicklistmodel_p.h" +#include "qqmllistmodel_p.h" #include "qquickworkerscript_p.h" #include "qqmlcomponent_p.h" #include "qqmlnetworkaccessmanagerfactory.h" @@ -180,14 +180,14 @@ void QQmlEnginePrivate::registerBaseTypes(const char *uri, int versionMajor, int qmlRegisterType(uri, versionMajor, versionMinor,"Connections"); qmlRegisterType(uri, versionMajor, versionMinor,"Timer"); qmlRegisterCustomType(uri, versionMajor, versionMinor,"Connections", new QQmlConnectionsParser); + qmlRegisterType(uri, versionMajor, versionMinor, "ListElement"); + qmlRegisterCustomType(uri, versionMajor, versionMinor, "ListModel", new QQmlListModelParser); } // These QtQuick types' implementation resides in the QtQml module void QQmlEnginePrivate::registerQtQuick2Types(const char *uri, int versionMajor, int versionMinor) { - qmlRegisterType(uri, versionMajor, versionMinor, "ListElement"); - qmlRegisterCustomType(uri, versionMajor, versionMinor, "ListModel", new QQuickListModelParser); qmlRegisterType(uri, versionMajor, versionMinor, "WorkerScript"); } diff --git a/src/qml/qml/qqmllistmodel.cpp b/src/qml/qml/qqmllistmodel.cpp new file mode 100644 index 0000000000..5d64d37768 --- /dev/null +++ b/src/qml/qml/qqmllistmodel.cpp @@ -0,0 +1,2567 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmllistmodel_p_p.h" +#include "qqmllistmodelworkeragent_p.h" +#include "qqmlopenmetaobject_p.h" +#include +#include + + +#include +#include +#include +#include +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +// Set to 1024 as a debugging aid - easier to distinguish uids from indices of elements/models. +enum { MIN_LISTMODEL_UID = 1024 }; + +static QAtomicInt uidCounter(MIN_LISTMODEL_UID); + +template +static bool isMemoryUsed(const char *mem) +{ + for (size_t i=0 ; i < sizeof(T) ; ++i) { + if (mem[i] != 0) + return true; + } + + return false; +} + +static QString roleTypeName(ListLayout::Role::DataType t) +{ + QString result; + const char *roleTypeNames[] = { "String", "Number", "Bool", "List", "QObject", "VariantMap", "DateTime" }; + + if (t > ListLayout::Role::Invalid && t < ListLayout::Role::MaxDataType) + result = QString::fromLatin1(roleTypeNames[t]); + + return result; +} + +const ListLayout::Role &ListLayout::getRoleOrCreate(const QString &key, Role::DataType type) +{ + QStringHash::Node *node = roleHash.findNode(key); + if (node) { + const Role &r = *node->value; + if (type != r.type) + qmlInfo(0) << QString::fromLatin1("Can't assign to existing role '%1' of different type [%2 -> %3]").arg(r.name).arg(roleTypeName(type)).arg(roleTypeName(r.type)); + return r; + } + + return createRole(key, type); +} + +const ListLayout::Role &ListLayout::getRoleOrCreate(v8::Handle key, Role::DataType type) +{ + QHashedV8String hashedKey(key); + QStringHash::Node *node = roleHash.findNode(hashedKey); + if (node) { + const Role &r = *node->value; + if (type != r.type) + qmlInfo(0) << QString::fromLatin1("Can't assign to existing role '%1' of different type [%2 -> %3]").arg(r.name).arg(roleTypeName(type)).arg(roleTypeName(r.type)); + return r; + } + + QString qkey; + qkey.resize(key->Length()); + key->Write(reinterpret_cast(qkey.data())); + + return createRole(qkey, type); +} + +const ListLayout::Role &ListLayout::createRole(const QString &key, ListLayout::Role::DataType type) +{ + const int dataSizes[] = { sizeof(QString), sizeof(double), sizeof(bool), sizeof(ListModel *), sizeof(QQmlGuard), sizeof(QVariantMap), sizeof(QDateTime) }; + const int dataAlignments[] = { sizeof(QString), sizeof(double), sizeof(bool), sizeof(ListModel *), sizeof(QObject *), sizeof(QVariantMap), sizeof(QDateTime) }; + + Role *r = new Role; + r->name = key; + r->type = type; + + if (type == Role::List) { + r->subLayout = new ListLayout; + } else { + r->subLayout = 0; + } + + int dataSize = dataSizes[type]; + int dataAlignment = dataAlignments[type]; + + int dataOffset = (currentBlockOffset + dataAlignment-1) & ~(dataAlignment-1); + if (dataOffset + dataSize > ListElement::BLOCK_SIZE) { + r->blockIndex = ++currentBlock; + r->blockOffset = 0; + currentBlockOffset = dataSize; + } else { + r->blockIndex = currentBlock; + r->blockOffset = dataOffset; + currentBlockOffset = dataOffset + dataSize; + } + + int roleIndex = roles.count(); + r->index = roleIndex; + + roles.append(r); + roleHash.insert(key, r); + + return *r; +} + +ListLayout::ListLayout(const ListLayout *other) : currentBlock(0), currentBlockOffset(0) +{ + for (int i=0 ; i < other->roles.count() ; ++i) { + Role *role = new Role(other->roles[i]); + roles.append(role); + roleHash.insert(role->name, role); + } + currentBlockOffset = other->currentBlockOffset; + currentBlock = other->currentBlock; +} + +ListLayout::~ListLayout() +{ + for (int i=0 ; i < roles.count() ; ++i) { + delete roles[i]; + } +} + +void ListLayout::sync(ListLayout *src, ListLayout *target) +{ + int roleOffset = target->roles.count(); + int newRoleCount = src->roles.count() - roleOffset; + + for (int i=0 ; i < newRoleCount ; ++i) { + Role *role = new Role(src->roles[roleOffset + i]); + target->roles.append(role); + target->roleHash.insert(role->name, role); + } + + target->currentBlockOffset = src->currentBlockOffset; + target->currentBlock = src->currentBlock; +} + +ListLayout::Role::Role(const Role *other) +{ + name = other->name; + type = other->type; + blockIndex = other->blockIndex; + blockOffset = other->blockOffset; + index = other->index; + if (other->subLayout) + subLayout = new ListLayout(other->subLayout); + else + subLayout = 0; +} + +ListLayout::Role::~Role() +{ + delete subLayout; +} + +const ListLayout::Role *ListLayout::getRoleOrCreate(const QString &key, const QVariant &data) +{ + Role::DataType type; + + switch (data.type()) { + case QVariant::Double: type = Role::Number; break; + case QVariant::Int: type = Role::Number; break; + case QVariant::UserType: type = Role::List; break; + case QVariant::Bool: type = Role::Bool; break; + case QVariant::String: type = Role::String; break; + case QVariant::Map: type = Role::VariantMap; break; + default: type = Role::Invalid; break; + } + + if (type == Role::Invalid) { + qmlInfo(0) << "Can't create role for unsupported data type"; + return 0; + } + + return &getRoleOrCreate(key, type); +} + +const ListLayout::Role *ListLayout::getExistingRole(const QString &key) +{ + Role *r = 0; + QStringHash::Node *node = roleHash.findNode(key); + if (node) + r = node->value; + return r; +} + +const ListLayout::Role *ListLayout::getExistingRole(v8::Handle key) +{ + Role *r = 0; + QHashedV8String hashedKey(key); + QStringHash::Node *node = roleHash.findNode(hashedKey); + if (node) + r = node->value; + return r; +} + +ModelObject *ListModel::getOrCreateModelObject(QQmlListModel *model, int elementIndex) +{ + ListElement *e = elements[elementIndex]; + if (e->m_objectCache == 0) { + e->m_objectCache = new ModelObject(model, elementIndex); + } + return e->m_objectCache; +} + +void ListModel::sync(ListModel *src, ListModel *target, QHash *targetModelHash) +{ + // Sanity check + target->m_uid = src->m_uid; + if (targetModelHash) + targetModelHash->insert(target->m_uid, target); + + // Build hash of elements <-> uid for each of the lists + QHash elementHash; + for (int i=0 ; i < target->elements.count() ; ++i) { + ListElement *e = target->elements.at(i); + int uid = e->getUid(); + ElementSync sync; + sync.target = e; + elementHash.insert(uid, sync); + } + for (int i=0 ; i < src->elements.count() ; ++i) { + ListElement *e = src->elements.at(i); + int uid = e->getUid(); + + QHash::iterator it = elementHash.find(uid); + if (it == elementHash.end()) { + ElementSync sync; + sync.src = e; + elementHash.insert(uid, sync); + } else { + ElementSync &sync = it.value(); + sync.src = e; + } + } + + // Get list of elements that are in the target but no longer in the source. These get deleted first. + QHash::iterator it = elementHash.begin(); + QHash::iterator end = elementHash.end(); + while (it != end) { + const ElementSync &s = it.value(); + if (s.src == 0) { + s.target->destroy(target->m_layout); + target->elements.removeOne(s.target); + delete s.target; + } + ++it; + } + + // Sync the layouts + ListLayout::sync(src->m_layout, target->m_layout); + + // Clear the target list, and append in correct order from the source + target->elements.clear(); + for (int i=0 ; i < src->elements.count() ; ++i) { + ListElement *srcElement = src->elements.at(i); + it = elementHash.find(srcElement->getUid()); + const ElementSync &s = it.value(); + ListElement *targetElement = s.target; + if (targetElement == 0) { + targetElement = new ListElement(srcElement->getUid()); + } + ListElement::sync(srcElement, src->m_layout, targetElement, target->m_layout, targetModelHash); + target->elements.append(targetElement); + } + + target->updateCacheIndices(); + + // Update values stored in target meta objects + for (int i=0 ; i < target->elements.count() ; ++i) { + ListElement *e = target->elements[i]; + if (e->m_objectCache) + e->m_objectCache->updateValues(); + } +} + +ListModel::ListModel(ListLayout *layout, QQmlListModel *modelCache, int uid) : m_layout(layout), m_modelCache(modelCache) +{ + if (uid == -1) + uid = uidCounter.fetchAndAddOrdered(1); + m_uid = uid; +} + +void ListModel::destroy() +{ + clear(); + m_uid = -1; + m_layout = 0; + if (m_modelCache && m_modelCache->m_primary == false) + delete m_modelCache; + m_modelCache = 0; +} + +int ListModel::appendElement() +{ + int elementIndex = elements.count(); + newElement(elementIndex); + return elementIndex; +} + +void ListModel::insertElement(int index) +{ + newElement(index); + updateCacheIndices(); +} + +void ListModel::move(int from, int to, int n) +{ + if (from > to) { + // Only move forwards - flip if backwards moving + int tfrom = from; + int tto = to; + from = tto; + to = tto+n; + n = tfrom-tto; + } + + QPODVector store; + for (int i=0 ; i < (to-from) ; ++i) + store.append(elements[from+n+i]); + for (int i=0 ; i < n ; ++i) + store.append(elements[from+i]); + for (int i=0 ; i < store.count() ; ++i) + elements[from+i] = store[i]; + + updateCacheIndices(); +} + +void ListModel::newElement(int index) +{ + ListElement *e = new ListElement; + elements.insert(index, e); +} + +void ListModel::updateCacheIndices() +{ + for (int i=0 ; i < elements.count() ; ++i) { + ListElement *e = elements.at(i); + if (e->m_objectCache) { + e->m_objectCache->m_elementIndex = i; + } + } +} + +QVariant ListModel::getProperty(int elementIndex, int roleIndex, const QQmlListModel *owner, QV8Engine *eng) +{ + ListElement *e = elements[elementIndex]; + const ListLayout::Role &r = m_layout->getExistingRole(roleIndex); + return e->getProperty(r, owner, eng); +} + +ListModel *ListModel::getListProperty(int elementIndex, const ListLayout::Role &role) +{ + ListElement *e = elements[elementIndex]; + return e->getListProperty(role); +} + +void ListModel::set(int elementIndex, v8::Handle object, QVector *roles, QV8Engine *eng) +{ + ListElement *e = elements[elementIndex]; + + v8::Local propertyNames = object->GetPropertyNames(); + int propertyCount = propertyNames->Length(); + + for (int i=0 ; i < propertyCount ; ++i) { + v8::Local propertyName = propertyNames->Get(i)->ToString(); + v8::Local propertyValue = object->Get(propertyName); + + // Check if this key exists yet + int roleIndex = -1; + + // Add the value now + if (propertyValue->IsString()) { + const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::String); + v8::Handle jsString = propertyValue->ToString(); + QString qstr; + qstr.resize(jsString->Length()); + jsString->Write(reinterpret_cast(qstr.data())); + roleIndex = e->setStringProperty(r, qstr); + } else if (propertyValue->IsNumber()) { + const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Number); + roleIndex = e->setDoubleProperty(r, propertyValue->NumberValue()); + } else if (propertyValue->IsArray()) { + const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::List); + ListModel *subModel = new ListModel(r.subLayout, 0, -1); + + v8::Handle subArray = v8::Handle::Cast(propertyValue); + int arrayLength = subArray->Length(); + for (int j=0 ; j < arrayLength ; ++j) { + v8::Handle subObject = subArray->Get(j)->ToObject(); + subModel->append(subObject, eng); + } + + roleIndex = e->setListProperty(r, subModel); + } else if (propertyValue->IsBoolean()) { + const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Bool); + roleIndex = e->setBoolProperty(r, propertyValue->BooleanValue()); + } else if (propertyValue->IsDate()) { + const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::DateTime); + QDateTime dt = QV8Engine::qtDateTimeFromJsDate(v8::Handle::Cast(propertyValue)->NumberValue()); + roleIndex = e->setDateTimeProperty(r, dt); + } else if (propertyValue->IsObject()) { + QV8ObjectResource *r = (QV8ObjectResource *) propertyValue->ToObject()->GetExternalResource(); + if (r && r->resourceType() == QV8ObjectResource::QObjectType) { + QObject *o = QV8QObjectWrapper::toQObject(r); + const ListLayout::Role &role = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::QObject); + if (role.type == ListLayout::Role::QObject) + roleIndex = e->setQObjectProperty(role, o); + } else { + const ListLayout::Role &role = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::VariantMap); + if (role.type == ListLayout::Role::VariantMap) + roleIndex = e->setVariantMapProperty(role, propertyValue->ToObject(), eng); + } + } else if (propertyValue.IsEmpty() || propertyValue->IsUndefined() || propertyValue->IsNull()) { + const ListLayout::Role *r = m_layout->getExistingRole(propertyName); + if (r) + e->clearProperty(*r); + } + + if (roleIndex != -1) + roles->append(roleIndex); + } + + if (e->m_objectCache) { + e->m_objectCache->updateValues(*roles); + } +} + +void ListModel::set(int elementIndex, v8::Handle object, QV8Engine *eng) +{ + ListElement *e = elements[elementIndex]; + + v8::Local propertyNames = object->GetPropertyNames(); + int propertyCount = propertyNames->Length(); + + for (int i=0 ; i < propertyCount ; ++i) { + v8::Local propertyName = propertyNames->Get(i)->ToString(); + v8::Local propertyValue = object->Get(propertyName); + + // Add the value now + if (propertyValue->IsString()) { + const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::String); + if (r.type == ListLayout::Role::String) { + v8::Handle jsString = propertyValue->ToString(); + QString qstr; + qstr.resize(jsString->Length()); + jsString->Write(reinterpret_cast(qstr.data())); + e->setStringPropertyFast(r, qstr); + } + } else if (propertyValue->IsNumber()) { + const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Number); + if (r.type == ListLayout::Role::Number) { + e->setDoublePropertyFast(r, propertyValue->NumberValue()); + } + } else if (propertyValue->IsArray()) { + const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::List); + if (r.type == ListLayout::Role::List) { + ListModel *subModel = new ListModel(r.subLayout, 0, -1); + + v8::Handle subArray = v8::Handle::Cast(propertyValue); + int arrayLength = subArray->Length(); + for (int j=0 ; j < arrayLength ; ++j) { + v8::Handle subObject = subArray->Get(j)->ToObject(); + subModel->append(subObject, eng); + } + + e->setListPropertyFast(r, subModel); + } + } else if (propertyValue->IsBoolean()) { + const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Bool); + if (r.type == ListLayout::Role::Bool) { + e->setBoolPropertyFast(r, propertyValue->BooleanValue()); + } + } else if (propertyValue->IsDate()) { + const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::DateTime); + if (r.type == ListLayout::Role::DateTime) { + QDateTime dt = QV8Engine::qtDateTimeFromJsDate(v8::Handle::Cast(propertyValue)->NumberValue()); + e->setDateTimePropertyFast(r, dt); + } + } else if (propertyValue->IsObject()) { + QV8ObjectResource *r = (QV8ObjectResource *) propertyValue->ToObject()->GetExternalResource(); + if (r && r->resourceType() == QV8ObjectResource::QObjectType) { + QObject *o = QV8QObjectWrapper::toQObject(r); + const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::QObject); + if (r.type == ListLayout::Role::QObject) + e->setQObjectPropertyFast(r, o); + } else { + const ListLayout::Role &role = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::VariantMap); + if (role.type == ListLayout::Role::VariantMap) + e->setVariantMapFast(role, propertyValue->ToObject(), eng); + } + } else if (propertyValue.IsEmpty() || propertyValue->IsUndefined() || propertyValue->IsNull()) { + const ListLayout::Role *r = m_layout->getExistingRole(propertyName); + if (r) + e->clearProperty(*r); + } + } +} + +void ListModel::clear() +{ + int elementCount = elements.count(); + for (int i=0 ; i < elementCount ; ++i) { + elements[i]->destroy(m_layout); + delete elements[i]; + } + elements.clear(); +} + +void ListModel::remove(int index, int count) +{ + for (int i=0 ; i < count ; ++i) { + elements[index+i]->destroy(m_layout); + delete elements[index+i]; + } + elements.remove(index, count); + updateCacheIndices(); +} + +void ListModel::insert(int elementIndex, v8::Handle object, QV8Engine *eng) +{ + insertElement(elementIndex); + set(elementIndex, object, eng); +} + +int ListModel::append(v8::Handle object, QV8Engine *eng) +{ + int elementIndex = appendElement(); + set(elementIndex, object, eng); + return elementIndex; +} + +int ListModel::setOrCreateProperty(int elementIndex, const QString &key, const QVariant &data) +{ + int roleIndex = -1; + + if (elementIndex >= 0 && elementIndex < elements.count()) { + ListElement *e = elements[elementIndex]; + + const ListLayout::Role *r = m_layout->getRoleOrCreate(key, data); + if (r) { + roleIndex = e->setVariantProperty(*r, data); + + if (roleIndex != -1 && e->m_objectCache) { + QVector roles; + roles << roleIndex; + e->m_objectCache->updateValues(roles); + } + } + } + + return roleIndex; +} + +int ListModel::setExistingProperty(int elementIndex, const QString &key, v8::Handle data, QV8Engine *eng) +{ + int roleIndex = -1; + + if (elementIndex >= 0 && elementIndex < elements.count()) { + ListElement *e = elements[elementIndex]; + const ListLayout::Role *r = m_layout->getExistingRole(key); + if (r) + roleIndex = e->setJsProperty(*r, data, eng); + } + + return roleIndex; +} + +inline char *ListElement::getPropertyMemory(const ListLayout::Role &role) +{ + ListElement *e = this; + int blockIndex = 0; + while (blockIndex < role.blockIndex) { + if (e->next == 0) { + e->next = new ListElement; + e->next->uid = uid; + } + e = e->next; + ++blockIndex; + } + + char *mem = &e->data[role.blockOffset]; + return mem; +} + +QString *ListElement::getStringProperty(const ListLayout::Role &role) +{ + char *mem = getPropertyMemory(role); + QString *s = reinterpret_cast(mem); + return s->data_ptr() ? s : 0; +} + +QObject *ListElement::getQObjectProperty(const ListLayout::Role &role) +{ + char *mem = getPropertyMemory(role); + QQmlGuard *o = reinterpret_cast *>(mem); + return o->data(); +} + +QVariantMap *ListElement::getVariantMapProperty(const ListLayout::Role &role) +{ + QVariantMap *map = 0; + + char *mem = getPropertyMemory(role); + if (isMemoryUsed(mem)) + map = reinterpret_cast(mem); + + return map; +} + +QDateTime *ListElement::getDateTimeProperty(const ListLayout::Role &role) +{ + QDateTime *dt = 0; + + char *mem = getPropertyMemory(role); + if (isMemoryUsed(mem)) + dt = reinterpret_cast(mem); + + return dt; +} + +QQmlGuard *ListElement::getGuardProperty(const ListLayout::Role &role) +{ + char *mem = getPropertyMemory(role); + + bool existingGuard = false; + for (size_t i=0 ; i < sizeof(QQmlGuard) ; ++i) { + if (mem[i] != 0) { + existingGuard = true; + break; + } + } + + QQmlGuard *o = 0; + + if (existingGuard) + o = reinterpret_cast *>(mem); + + return o; +} + +ListModel *ListElement::getListProperty(const ListLayout::Role &role) +{ + char *mem = getPropertyMemory(role); + ListModel **value = reinterpret_cast(mem); + return *value; +} + +QVariant ListElement::getProperty(const ListLayout::Role &role, const QQmlListModel *owner, QV8Engine *eng) +{ + char *mem = getPropertyMemory(role); + + QVariant data; + + switch (role.type) { + case ListLayout::Role::Number: + { + double *value = reinterpret_cast(mem); + data = *value; + } + break; + case ListLayout::Role::String: + { + QString *value = reinterpret_cast(mem); + if (value->data_ptr() != 0) + data = *value; + } + break; + case ListLayout::Role::Bool: + { + bool *value = reinterpret_cast(mem); + data = *value; + } + break; + case ListLayout::Role::List: + { + ListModel **value = reinterpret_cast(mem); + ListModel *model = *value; + + if (model) { + if (model->m_modelCache == 0) { + model->m_modelCache = new QQmlListModel(owner, model, eng); + QQmlEngine::setContextForObject(model->m_modelCache, QQmlEngine::contextForObject(owner)); + } + + QObject *object = model->m_modelCache; + data = QVariant::fromValue(object); + } + } + break; + case ListLayout::Role::QObject: + { + QQmlGuard *guard = reinterpret_cast *>(mem); + QObject *object = guard->data(); + if (object) + data = QVariant::fromValue(object); + } + break; + case ListLayout::Role::VariantMap: + { + if (isMemoryUsed(mem)) { + QVariantMap *map = reinterpret_cast(mem); + data = *map; + } + } + break; + case ListLayout::Role::DateTime: + { + if (isMemoryUsed(mem)) { + QDateTime *dt = reinterpret_cast(mem); + data = *dt; + } + } + break; + default: + break; + } + + return data; +} + +int ListElement::setStringProperty(const ListLayout::Role &role, const QString &s) +{ + int roleIndex = -1; + + if (role.type == ListLayout::Role::String) { + char *mem = getPropertyMemory(role); + QString *c = reinterpret_cast(mem); + bool changed; + if (c->data_ptr() == 0) { + new (mem) QString(s); + changed = true; + } else { + changed = c->compare(s) != 0; + *c = s; + } + if (changed) + roleIndex = role.index; + } + + return roleIndex; +} + +int ListElement::setDoubleProperty(const ListLayout::Role &role, double d) +{ + int roleIndex = -1; + + if (role.type == ListLayout::Role::Number) { + char *mem = getPropertyMemory(role); + double *value = new (mem) double; + bool changed = *value != d; + *value = d; + if (changed) + roleIndex = role.index; + } + + return roleIndex; +} + +int ListElement::setBoolProperty(const ListLayout::Role &role, bool b) +{ + int roleIndex = -1; + + if (role.type == ListLayout::Role::Bool) { + char *mem = getPropertyMemory(role); + bool *value = new (mem) bool; + bool changed = *value != b; + *value = b; + if (changed) + roleIndex = role.index; + } + + return roleIndex; +} + +int ListElement::setListProperty(const ListLayout::Role &role, ListModel *m) +{ + int roleIndex = -1; + + if (role.type == ListLayout::Role::List) { + char *mem = getPropertyMemory(role); + ListModel **value = new (mem) ListModel *; + if (*value) { + (*value)->destroy(); + delete *value; + } + *value = m; + roleIndex = role.index; + } + + return roleIndex; +} + +int ListElement::setQObjectProperty(const ListLayout::Role &role, QObject *o) +{ + int roleIndex = -1; + + if (role.type == ListLayout::Role::QObject) { + char *mem = getPropertyMemory(role); + QQmlGuard *g = reinterpret_cast *>(mem); + bool existingGuard = false; + for (size_t i=0 ; i < sizeof(QQmlGuard) ; ++i) { + if (mem[i] != 0) { + existingGuard = true; + break; + } + } + bool changed; + if (existingGuard) { + changed = g->data() != o; + g->~QQmlGuard(); + } else { + changed = true; + } + new (mem) QQmlGuard(o); + if (changed) + roleIndex = role.index; + } + + return roleIndex; +} + +int ListElement::setVariantMapProperty(const ListLayout::Role &role, v8::Handle o, QV8Engine *eng) +{ + int roleIndex = -1; + + if (role.type == ListLayout::Role::VariantMap) { + char *mem = getPropertyMemory(role); + if (isMemoryUsed(mem)) { + QVariantMap *map = reinterpret_cast(mem); + map->~QMap(); + } + new (mem) QVariantMap(eng->variantMapFromJS(o)); + roleIndex = role.index; + } + + return roleIndex; +} + +int ListElement::setVariantMapProperty(const ListLayout::Role &role, QVariantMap *m) +{ + int roleIndex = -1; + + if (role.type == ListLayout::Role::VariantMap) { + char *mem = getPropertyMemory(role); + if (isMemoryUsed(mem)) { + QVariantMap *map = reinterpret_cast(mem); + map->~QMap(); + } + if (m) + new (mem) QVariantMap(*m); + else + new (mem) QVariantMap; + roleIndex = role.index; + } + + return roleIndex; +} + +int ListElement::setDateTimeProperty(const ListLayout::Role &role, const QDateTime &dt) +{ + int roleIndex = -1; + + if (role.type == ListLayout::Role::DateTime) { + char *mem = getPropertyMemory(role); + if (isMemoryUsed(mem)) { + QDateTime *dt = reinterpret_cast(mem); + dt->~QDateTime(); + } + new (mem) QDateTime(dt); + roleIndex = role.index; + } + + return roleIndex; +} + +void ListElement::setStringPropertyFast(const ListLayout::Role &role, const QString &s) +{ + char *mem = getPropertyMemory(role); + new (mem) QString(s); +} + +void ListElement::setDoublePropertyFast(const ListLayout::Role &role, double d) +{ + char *mem = getPropertyMemory(role); + double *value = new (mem) double; + *value = d; +} + +void ListElement::setBoolPropertyFast(const ListLayout::Role &role, bool b) +{ + char *mem = getPropertyMemory(role); + bool *value = new (mem) bool; + *value = b; +} + +void ListElement::setQObjectPropertyFast(const ListLayout::Role &role, QObject *o) +{ + char *mem = getPropertyMemory(role); + new (mem) QQmlGuard(o); +} + +void ListElement::setListPropertyFast(const ListLayout::Role &role, ListModel *m) +{ + char *mem = getPropertyMemory(role); + ListModel **value = new (mem) ListModel *; + *value = m; +} + +void ListElement::setVariantMapFast(const ListLayout::Role &role, v8::Handle o, QV8Engine *eng) +{ + char *mem = getPropertyMemory(role); + QVariantMap *map = new (mem) QVariantMap; + *map = eng->variantMapFromJS(o); +} + +void ListElement::setDateTimePropertyFast(const ListLayout::Role &role, const QDateTime &dt) +{ + char *mem = getPropertyMemory(role); + new (mem) QDateTime(dt); +} + +void ListElement::clearProperty(const ListLayout::Role &role) +{ + switch (role.type) { + case ListLayout::Role::String: + setStringProperty(role, QString()); + break; + case ListLayout::Role::Number: + setDoubleProperty(role, 0.0); + break; + case ListLayout::Role::Bool: + setBoolProperty(role, false); + break; + case ListLayout::Role::List: + setListProperty(role, 0); + break; + case ListLayout::Role::QObject: + setQObjectProperty(role, 0); + break; + case ListLayout::Role::DateTime: + setDateTimeProperty(role, QDateTime()); + break; + case ListLayout::Role::VariantMap: + setVariantMapProperty(role, 0); + break; + default: + break; + } +} + +ListElement::ListElement() +{ + m_objectCache = 0; + uid = uidCounter.fetchAndAddOrdered(1); + next = 0; + memset(data, 0, sizeof(data)); +} + +ListElement::ListElement(int existingUid) +{ + m_objectCache = 0; + uid = existingUid; + next = 0; + memset(data, 0, sizeof(data)); +} + +ListElement::~ListElement() +{ + delete next; +} + +void ListElement::sync(ListElement *src, ListLayout *srcLayout, ListElement *target, ListLayout *targetLayout, QHash *targetModelHash) +{ + for (int i=0 ; i < srcLayout->roleCount() ; ++i) { + const ListLayout::Role &srcRole = srcLayout->getExistingRole(i); + const ListLayout::Role &targetRole = targetLayout->getExistingRole(i); + + switch (srcRole.type) { + case ListLayout::Role::List: + { + ListModel *srcSubModel = src->getListProperty(srcRole); + ListModel *targetSubModel = target->getListProperty(targetRole); + + if (srcSubModel) { + if (targetSubModel == 0) { + targetSubModel = new ListModel(targetRole.subLayout, 0, srcSubModel->getUid()); + target->setListPropertyFast(targetRole, targetSubModel); + } + ListModel::sync(srcSubModel, targetSubModel, targetModelHash); + } + } + break; + case ListLayout::Role::QObject: + { + QObject *object = src->getQObjectProperty(srcRole); + target->setQObjectProperty(targetRole, object); + } + break; + case ListLayout::Role::String: + case ListLayout::Role::Number: + case ListLayout::Role::Bool: + case ListLayout::Role::DateTime: + { + QVariant v = src->getProperty(srcRole, 0, 0); + target->setVariantProperty(targetRole, v); + } + case ListLayout::Role::VariantMap: + { + QVariantMap *map = src->getVariantMapProperty(srcRole); + target->setVariantMapProperty(targetRole, map); + } + break; + default: + break; + } + } + +} + +void ListElement::destroy(ListLayout *layout) +{ + if (layout) { + for (int i=0 ; i < layout->roleCount() ; ++i) { + const ListLayout::Role &r = layout->getExistingRole(i); + + switch (r.type) { + case ListLayout::Role::String: + { + QString *string = getStringProperty(r); + if (string) + string->~QString(); + } + break; + case ListLayout::Role::List: + { + ListModel *model = getListProperty(r); + if (model) { + model->destroy(); + delete model; + } + } + break; + case ListLayout::Role::QObject: + { + QQmlGuard *guard = getGuardProperty(r); + if (guard) + guard->~QQmlGuard(); + } + break; + case ListLayout::Role::VariantMap: + { + QVariantMap *map = getVariantMapProperty(r); + if (map) + map->~QMap(); + } + break; + case ListLayout::Role::DateTime: + { + QDateTime *dt = getDateTimeProperty(r); + if (dt) + dt->~QDateTime(); + } + break; + default: + // other types don't need explicit cleanup. + break; + } + } + + delete m_objectCache; + } + + if (next) + next->destroy(0); + uid = -1; +} + +int ListElement::setVariantProperty(const ListLayout::Role &role, const QVariant &d) +{ + int roleIndex = -1; + + switch (role.type) { + case ListLayout::Role::Number: + roleIndex = setDoubleProperty(role, d.toDouble()); + break; + case ListLayout::Role::String: + roleIndex = setStringProperty(role, d.toString()); + break; + case ListLayout::Role::Bool: + roleIndex = setBoolProperty(role, d.toBool()); + break; + case ListLayout::Role::List: + roleIndex = setListProperty(role, d.value()); + break; + case ListLayout::Role::VariantMap: { + QVariantMap map = d.toMap(); + roleIndex = setVariantMapProperty(role, &map); + } + break; + case ListLayout::Role::DateTime: + roleIndex = setDateTimeProperty(role, d.toDateTime()); + break; + default: + break; + } + + return roleIndex; +} + +int ListElement::setJsProperty(const ListLayout::Role &role, v8::Handle d, QV8Engine *eng) +{ + // Check if this key exists yet + int roleIndex = -1; + + // Add the value now + if (d->IsString()) { + v8::Handle jsString = d->ToString(); + QString qstr; + qstr.resize(jsString->Length()); + jsString->Write(reinterpret_cast(qstr.data())); + roleIndex = setStringProperty(role, qstr); + } else if (d->IsNumber()) { + roleIndex = setDoubleProperty(role, d->NumberValue()); + } else if (d->IsArray()) { + if (role.type == ListLayout::Role::List) { + ListModel *subModel = new ListModel(role.subLayout, 0, -1); + v8::Handle subArray = v8::Handle::Cast(d); + int arrayLength = subArray->Length(); + for (int j=0 ; j < arrayLength ; ++j) { + v8::Handle subObject = subArray->Get(j)->ToObject(); + subModel->append(subObject, eng); + } + roleIndex = setListProperty(role, subModel); + } else { + qmlInfo(0) << QString::fromLatin1("Can't assign to existing role '%1' of different type [%2 -> %3]").arg(role.name).arg(roleTypeName(role.type)).arg(roleTypeName(ListLayout::Role::List)); + } + } else if (d->IsBoolean()) { + roleIndex = setBoolProperty(role, d->BooleanValue()); + } else if (d->IsDate()) { + QDateTime dt = QV8Engine::qtDateTimeFromJsDate(v8::Handle::Cast(d)->NumberValue()); + roleIndex = setDateTimeProperty(role, dt); + } else if (d->IsObject()) { + QV8ObjectResource *r = (QV8ObjectResource *) d->ToObject()->GetExternalResource(); + if (role.type == ListLayout::Role::QObject && r && r->resourceType() == QV8ObjectResource::QObjectType) { + QObject *o = QV8QObjectWrapper::toQObject(r); + roleIndex = setQObjectProperty(role, o); + } else if (role.type == ListLayout::Role::VariantMap) { + roleIndex = setVariantMapProperty(role, d->ToObject(), eng); + } + } else if (d.IsEmpty() || d->IsUndefined() || d->IsNull()) { + clearProperty(role); + } + + return roleIndex; +} + +ModelObject::ModelObject(QQmlListModel *model, int elementIndex) +: m_model(model), m_elementIndex(elementIndex), m_meta(new ModelNodeMetaObject(this)) +{ + updateValues(); + setNodeUpdatesEnabled(true); +} + +void ModelObject::updateValues() +{ + int roleCount = m_model->m_listModel->roleCount(); + for (int i=0 ; i < roleCount ; ++i) { + const ListLayout::Role &role = m_model->m_listModel->getExistingRole(i); + QByteArray name = role.name.toUtf8(); + const QVariant &data = m_model->data(m_elementIndex, i); + setValue(name, data, role.type == ListLayout::Role::List); + } +} + +void ModelObject::updateValues(const QVector &roles) +{ + int roleCount = roles.count(); + for (int i=0 ; i < roleCount ; ++i) { + int roleIndex = roles.at(i); + const ListLayout::Role &role = m_model->m_listModel->getExistingRole(roleIndex); + QByteArray name = role.name.toUtf8(); + const QVariant &data = m_model->data(m_elementIndex, roleIndex); + setValue(name, data, role.type == ListLayout::Role::List); + } +} + +ModelNodeMetaObject::ModelNodeMetaObject(ModelObject *object) +: QQmlOpenMetaObject(object), m_enabled(false), m_obj(object) +{ +} + +ModelNodeMetaObject::~ModelNodeMetaObject() +{ +} + +void ModelNodeMetaObject::propertyWritten(int index) +{ + if (!m_enabled) + return; + + QV8Engine *eng = m_obj->m_model->engine(); + + QString propName = QString::fromUtf8(name(index)); + QVariant value = operator[](index); + + v8::HandleScope handle_scope; + v8::Context::Scope scope(eng->context()); + + v8::Handle v = eng->fromVariant(value); + + int roleIndex = m_obj->m_model->m_listModel->setExistingProperty(m_obj->m_elementIndex, propName, v, eng); + if (roleIndex != -1) { + QVector roles; + roles << roleIndex; + m_obj->m_model->emitItemsChanged(m_obj->m_elementIndex, 1, roles); + } +} + +DynamicRoleModelNode::DynamicRoleModelNode(QQmlListModel *owner, int uid) : m_owner(owner), m_uid(uid), m_meta(new DynamicRoleModelNodeMetaObject(this)) +{ + setNodeUpdatesEnabled(true); +} + +DynamicRoleModelNode *DynamicRoleModelNode::create(const QVariantMap &obj, QQmlListModel *owner) +{ + DynamicRoleModelNode *object = new DynamicRoleModelNode(owner, uidCounter.fetchAndAddOrdered(1)); + QVector roles; + object->updateValues(obj, roles); + return object; +} + +void DynamicRoleModelNode::sync(DynamicRoleModelNode *src, DynamicRoleModelNode *target, QHash *targetModelHash) +{ + for (int i=0 ; i < src->m_meta->count() ; ++i) { + const QByteArray &name = src->m_meta->name(i); + QVariant value = src->m_meta->value(i); + + QQmlListModel *srcModel = qobject_cast(value.value()); + QQmlListModel *targetModel = qobject_cast(target->m_meta->value(i).value()); + + if (srcModel) { + if (targetModel == 0) + targetModel = QQmlListModel::createWithOwner(target->m_owner); + + QQmlListModel::sync(srcModel, targetModel, targetModelHash); + + QObject *targetModelObject = targetModel; + value = QVariant::fromValue(targetModelObject); + } else if (targetModel) { + delete targetModel; + } + + target->setValue(name, value); + } +} + +void DynamicRoleModelNode::updateValues(const QVariantMap &object, QVector &roles) +{ + const QList &keys = object.keys(); + + QList::const_iterator it = keys.begin(); + QList::const_iterator end = keys.end(); + + while (it != end) { + const QString &key = *it; + + int roleIndex = m_owner->m_roles.indexOf(key); + if (roleIndex == -1) { + roleIndex = m_owner->m_roles.count(); + m_owner->m_roles.append(key); + } + + QVariant value = object[key]; + + if (value.type() == QVariant::List) { + QQmlListModel *subModel = QQmlListModel::createWithOwner(m_owner); + + QVariantList subArray = value.toList(); + QVariantList::const_iterator subIt = subArray.begin(); + QVariantList::const_iterator subEnd = subArray.end(); + while (subIt != subEnd) { + const QVariantMap &subObject = subIt->toMap(); + subModel->m_modelObjects.append(DynamicRoleModelNode::create(subObject, subModel)); + ++subIt; + } + + QObject *subModelObject = subModel; + value = QVariant::fromValue(subModelObject); + } + + const QByteArray &keyUtf8 = key.toUtf8(); + + QQmlListModel *existingModel = qobject_cast(m_meta->value(keyUtf8).value()); + if (existingModel) + delete existingModel; + + if (m_meta->setValue(keyUtf8, value)) + roles << roleIndex; + + ++it; + } +} + +DynamicRoleModelNodeMetaObject::DynamicRoleModelNodeMetaObject(DynamicRoleModelNode *object) + : QQmlOpenMetaObject(object), m_enabled(false), m_owner(object) +{ +} + +DynamicRoleModelNodeMetaObject::~DynamicRoleModelNodeMetaObject() +{ + for (int i=0 ; i < count() ; ++i) { + QQmlListModel *subModel = qobject_cast(value(i).value()); + if (subModel) + delete subModel; + } +} + +void DynamicRoleModelNodeMetaObject::propertyWrite(int index) +{ + if (!m_enabled) + return; + + QVariant v = value(index); + QQmlListModel *model = qobject_cast(v.value()); + if (model) + delete model; +} + +void DynamicRoleModelNodeMetaObject::propertyWritten(int index) +{ + if (!m_enabled) + return; + + QQmlListModel *parentModel = m_owner->m_owner; + + QVariant v = value(index); + if (v.type() == QVariant::List) { + QQmlListModel *subModel = QQmlListModel::createWithOwner(parentModel); + + QVariantList subArray = v.toList(); + QVariantList::const_iterator subIt = subArray.begin(); + QVariantList::const_iterator subEnd = subArray.end(); + while (subIt != subEnd) { + const QVariantMap &subObject = subIt->toMap(); + subModel->m_modelObjects.append(DynamicRoleModelNode::create(subObject, subModel)); + ++subIt; + } + + QObject *subModelObject = subModel; + v = QVariant::fromValue(subModelObject); + + setValue(index, v); + } + + int elementIndex = parentModel->m_modelObjects.indexOf(m_owner); + int roleIndex = parentModel->m_roles.indexOf(QString::fromLatin1(name(index).constData())); + + if (elementIndex != -1 && roleIndex != -1) { + + QVector roles; + roles << roleIndex; + + parentModel->emitItemsChanged(elementIndex, 1, roles); + } +} + +QQmlListModelParser::ListInstruction *QQmlListModelParser::ListModelData::instructions() const +{ + return (QQmlListModelParser::ListInstruction *)((char *)this + sizeof(ListModelData)); +} + +/*! + \qmltype ListModel + \instantiates QQmlListModel + \inqmlmodule QtQuick 2 + \brief Defines a free-form list data source + \ingroup qtquick-models + + The ListModel is a simple container of ListElement definitions, each containing data roles. + The contents can be defined dynamically, or explicitly in QML. + + The number of elements in the model can be obtained from its \l count property. + A number of familiar methods are also provided to manipulate the contents of the + model, including append(), insert(), move(), remove() and set(). These methods + accept dictionaries as their arguments; these are translated to ListElement objects + by the model. + + Elements can be manipulated via the model using the setProperty() method, which + allows the roles of the specified element to be set and changed. + + \section1 Example Usage + + The following example shows a ListModel containing three elements, with the roles + "name" and "cost". + + \div {class="float-right"} + \inlineimage listmodel.png + \enddiv + + \snippet qml/listmodel/listmodel.qml 0 + + Roles (properties) in each element must begin with a lower-case letter and + should be common to all elements in a model. The ListElement documentation + provides more guidelines for how elements should be defined. + + Since the example model contains an \c id property, it can be referenced + by views, such as the ListView in this example: + + \snippet qml/listmodel/listmodel-simple.qml 0 + \dots 8 + \snippet qml/listmodel/listmodel-simple.qml 1 + + It is possible for roles to contain list data. In the following example we + create a list of fruit attributes: + + \snippet qml/listmodel/listmodel-nested.qml model + + The delegate displays all the fruit attributes: + + \div {class="float-right"} + \inlineimage listmodel-nested.png + \enddiv + + \snippet qml/listmodel/listmodel-nested.qml delegate + + \section1 Modifying List Models + + The content of a ListModel may be created and modified using the clear(), + append(), set(), insert() and setProperty() methods. For example: + + \snippet qml/listmodel/listmodel-modify.qml delegate + + Note that when creating content dynamically the set of available properties + cannot be changed once set. Whatever properties are first added to the model + are the only permitted properties in the model. + + \section1 Using Threaded List Models with WorkerScript + + ListModel can be used together with WorkerScript access a list model + from multiple threads. This is useful if list modifications are + synchronous and take some time: the list operations can be moved to a + different thread to avoid blocking of the main GUI thread. + + Here is an example that uses WorkerScript to periodically append the + current time to a list model: + + \snippet quick/threading/threadedlistmodel/timedisplay.qml 0 + + The included file, \tt dataloader.js, looks like this: + + \snippet quick/threading/threadedlistmodel/dataloader.js 0 + + The timer in the main example sends messages to the worker script by calling + \l WorkerScript::sendMessage(). When this message is received, + \l{WorkerScript::onMessage}{WorkerScript.onMessage()} is invoked in \c dataloader.js, + which appends the current time to the list model. + + Note the call to sync() from the \l{WorkerScript::onMessage}{WorkerScript.onMessage()} + handler. You must call sync() or else the changes made to the list from the external + thread will not be reflected in the list model in the main thread. + + \sa {qml-data-models}{Data Models}, {declarative/threading/threadedlistmodel}{Threaded ListModel example}, QtQml +*/ + +QQmlListModel::QQmlListModel(QObject *parent) +: QAbstractListModel(parent) +{ + m_mainThread = true; + m_primary = true; + m_agent = 0; + m_uid = uidCounter.fetchAndAddOrdered(1); + m_dynamicRoles = false; + + m_layout = new ListLayout; + m_listModel = new ListModel(m_layout, this, -1); + + m_engine = 0; +} + +QQmlListModel::QQmlListModel(const QQmlListModel *owner, ListModel *data, QV8Engine *eng, QObject *parent) +: QAbstractListModel(parent) +{ + m_mainThread = owner->m_mainThread; + m_primary = false; + m_agent = owner->m_agent; + + Q_ASSERT(owner->m_dynamicRoles == false); + m_dynamicRoles = false; + m_layout = 0; + m_listModel = data; + + m_engine = eng; +} + +QQmlListModel::QQmlListModel(QQmlListModel *orig, QQmlListModelWorkerAgent *agent) +: QAbstractListModel(agent) +{ + m_mainThread = false; + m_primary = true; + m_agent = agent; + m_dynamicRoles = orig->m_dynamicRoles; + + m_layout = new ListLayout(orig->m_layout); + m_listModel = new ListModel(m_layout, this, orig->m_listModel->getUid()); + + if (m_dynamicRoles) + sync(orig, this, 0); + else + ListModel::sync(orig->m_listModel, m_listModel, 0); + + m_engine = 0; +} + +QQmlListModel::~QQmlListModel() +{ + for (int i=0 ; i < m_modelObjects.count() ; ++i) + delete m_modelObjects[i]; + + if (m_primary) { + m_listModel->destroy(); + delete m_listModel; + + if (m_mainThread && m_agent) { + m_agent->modelDestroyed(); + m_agent->release(); + } + } + + m_listModel = 0; + + delete m_layout; + m_layout = 0; +} + +QQmlListModel *QQmlListModel::createWithOwner(QQmlListModel *newOwner) +{ + QQmlListModel *model = new QQmlListModel; + + model->m_mainThread = newOwner->m_mainThread; + model->m_engine = newOwner->m_engine; + model->m_agent = newOwner->m_agent; + model->m_dynamicRoles = newOwner->m_dynamicRoles; + + if (model->m_mainThread && model->m_agent) + model->m_agent->addref(); + + QQmlEngine::setContextForObject(model, QQmlEngine::contextForObject(newOwner)); + + return model; +} + +QV8Engine *QQmlListModel::engine() const +{ + if (m_engine == 0) { + m_engine = QQmlEnginePrivate::getV8Engine(qmlEngine(this)); + } + + return m_engine; +} + +void QQmlListModel::sync(QQmlListModel *src, QQmlListModel *target, QHash *targetModelHash) +{ + Q_ASSERT(src->m_dynamicRoles && target->m_dynamicRoles); + + target->m_uid = src->m_uid; + if (targetModelHash) + targetModelHash->insert(target->m_uid, target); + target->m_roles = src->m_roles; + + // Build hash of elements <-> uid for each of the lists + QHash elementHash; + for (int i=0 ; i < target->m_modelObjects.count() ; ++i) { + DynamicRoleModelNode *e = target->m_modelObjects.at(i); + int uid = e->getUid(); + ElementSync sync; + sync.target = e; + elementHash.insert(uid, sync); + } + for (int i=0 ; i < src->m_modelObjects.count() ; ++i) { + DynamicRoleModelNode *e = src->m_modelObjects.at(i); + int uid = e->getUid(); + + QHash::iterator it = elementHash.find(uid); + if (it == elementHash.end()) { + ElementSync sync; + sync.src = e; + elementHash.insert(uid, sync); + } else { + ElementSync &sync = it.value(); + sync.src = e; + } + } + + // Get list of elements that are in the target but no longer in the source. These get deleted first. + QHash::iterator it = elementHash.begin(); + QHash::iterator end = elementHash.end(); + while (it != end) { + const ElementSync &s = it.value(); + if (s.src == 0) { + int targetIndex = target->m_modelObjects.indexOf(s.target); + target->m_modelObjects.remove(targetIndex, 1); + delete s.target; + } + ++it; + } + + // Clear the target list, and append in correct order from the source + target->m_modelObjects.clear(); + for (int i=0 ; i < src->m_modelObjects.count() ; ++i) { + DynamicRoleModelNode *srcElement = src->m_modelObjects.at(i); + it = elementHash.find(srcElement->getUid()); + const ElementSync &s = it.value(); + DynamicRoleModelNode *targetElement = s.target; + if (targetElement == 0) { + targetElement = new DynamicRoleModelNode(target, srcElement->getUid()); + } + DynamicRoleModelNode::sync(srcElement, targetElement, targetModelHash); + target->m_modelObjects.append(targetElement); + } +} + +void QQmlListModel::emitItemsChanged(int index, int count, const QVector &roles) +{ + if (count <= 0) + return; + + if (m_mainThread) { + emit dataChanged(createIndex(index, 0), createIndex(index + count - 1, 0), roles);; + } else { + int uid = m_dynamicRoles ? getUid() : m_listModel->getUid(); + m_agent->data.changedChange(uid, index, count, roles); + } +} + +void QQmlListModel::emitItemsRemoved(int index, int count) +{ + if (count <= 0) + return; + + if (m_mainThread) { + beginRemoveRows(QModelIndex(), index, index + count - 1); + endRemoveRows(); + emit countChanged(); + } else { + int uid = m_dynamicRoles ? getUid() : m_listModel->getUid(); + if (index == 0 && count == this->count()) + m_agent->data.clearChange(uid); + m_agent->data.removeChange(uid, index, count); + } +} + +void QQmlListModel::emitItemsInserted(int index, int count) +{ + if (count <= 0) + return; + + if (m_mainThread) { + beginInsertRows(QModelIndex(), index, index + count - 1); + endInsertRows(); + emit countChanged(); + } else { + int uid = m_dynamicRoles ? getUid() : m_listModel->getUid(); + m_agent->data.insertChange(uid, index, count); + } +} + +void QQmlListModel::emitItemsMoved(int from, int to, int n) +{ + if (n <= 0) + return; + + if (m_mainThread) { + beginMoveRows(QModelIndex(), from, from + n - 1, QModelIndex(), to > from ? to + n : to); + endMoveRows(); + } else { + int uid = m_dynamicRoles ? getUid() : m_listModel->getUid(); + m_agent->data.moveChange(uid, from, n, to); + } +} + +QQmlListModelWorkerAgent *QQmlListModel::agent() +{ + if (m_agent) + return m_agent; + + m_agent = new QQmlListModelWorkerAgent(this); + return m_agent; +} + +QModelIndex QQmlListModel::index(int row, int column, const QModelIndex &parent) const +{ + return row >= 0 && row < count() && column == 0 && !parent.isValid() + ? createIndex(row, column) + : QModelIndex(); +} + +int QQmlListModel::rowCount(const QModelIndex &parent) const +{ + return !parent.isValid() ? count() : 0; +} + +QVariant QQmlListModel::data(const QModelIndex &index, int role) const +{ + return data(index.row(), role); +} + +QVariant QQmlListModel::data(int index, int role) const +{ + QVariant v; + + if (index >= count() || index < 0) + return v; + + if (m_dynamicRoles) + v = m_modelObjects[index]->getValue(m_roles[role]); + else + v = m_listModel->getProperty(index, role, this, engine()); + + return v; +} + +QHash QQmlListModel::roleNames() const +{ + QHash roleNames; + + if (m_dynamicRoles) { + for (int i = 0 ; i < m_roles.count() ; ++i) + roleNames.insert(i, m_roles.at(i).toUtf8()); + } else { + for (int i = 0 ; i < m_listModel->roleCount() ; ++i) { + const ListLayout::Role &r = m_listModel->getExistingRole(i); + roleNames.insert(i, r.name.toUtf8()); + } + } + + return roleNames; +} + +/*! + \qmlproperty bool QtQml2::ListModel::dynamicRoles + + By default, the type of a role is fixed the first time + the role is used. For example, if you create a role called + "data" and assign a number to it, you can no longer assign + a string to the "data" role. However, when the dynamicRoles + property is enabled, the type of a given role is not fixed + and can be different between elements. + + The dynamicRoles property must be set before any data is + added to the ListModel, and must be set from the main + thread. + + A ListModel that has data statically defined (via the + ListElement QML syntax) cannot have the dynamicRoles + property enabled. + + There is a significant performance cost to using a + ListModel with dynamic roles enabled. The cost varies + from platform to platform but is typically somewhere + between 4-6x slower than using static role types. + + Due to the performance cost of using dynamic roles, + they are disabled by default. +*/ +void QQmlListModel::setDynamicRoles(bool enableDynamicRoles) +{ + if (m_mainThread && m_agent == 0) { + if (enableDynamicRoles) { + if (m_layout->roleCount()) + qmlInfo(this) << tr("unable to enable dynamic roles as this model is not empty!"); + else + m_dynamicRoles = true; + } else { + if (m_roles.count()) { + qmlInfo(this) << tr("unable to enable static roles as this model is not empty!"); + } else { + m_dynamicRoles = false; + } + } + } else { + qmlInfo(this) << tr("dynamic role setting must be made from the main thread, before any worker scripts are created"); + } +} + +/*! + \qmlproperty int QtQml2::ListModel::count + The number of data entries in the model. +*/ +int QQmlListModel::count() const +{ + int count; + + if (m_dynamicRoles) + count = m_modelObjects.count(); + else { + count = m_listModel->elementCount(); + } + + return count; +} + +/*! + \qmlmethod QtQml2::ListModel::clear() + + Deletes all content from the model. + + \sa append(), remove() +*/ +void QQmlListModel::clear() +{ + int cleared = count(); + + if (m_dynamicRoles) { + for (int i=0 ; i < m_modelObjects.count() ; ++i) + delete m_modelObjects[i]; + m_modelObjects.clear(); + } else { + m_listModel->clear(); + } + + emitItemsRemoved(0, cleared); +} + +/*! + \qmlmethod QtQml2::ListModel::remove(int index, int count = 1) + + Deletes the content at \a index from the model. + + \sa clear() +*/ +void QQmlListModel::remove(QQmlV8Function *args) +{ + int argLength = args->Length(); + + if (argLength == 1 || argLength == 2) { + int index = (*args)[0]->Int32Value(); + int removeCount = (argLength == 2 ? ((*args)[1]->Int32Value()) : 1); + + if (index < 0 || index+removeCount > count() || removeCount <= 0) { + qmlInfo(this) << tr("remove: indices [%1 - %2] out of range [0 - %3]").arg(index).arg(index+removeCount).arg(count()); + return; + } + + if (m_dynamicRoles) { + for (int i=0 ; i < removeCount ; ++i) + delete m_modelObjects[index+i]; + m_modelObjects.remove(index, removeCount); + } else { + m_listModel->remove(index, removeCount); + } + + emitItemsRemoved(index, removeCount); + } else { + qmlInfo(this) << tr("remove: incorrect number of arguments"); + } +} + +/*! + \qmlmethod QtQml2::ListModel::insert(int index, jsobject dict) + + Adds a new item to the list model at position \a index, with the + values in \a dict. + + \code + fruitModel.insert(2, {"cost": 5.95, "name":"Pizza"}) + \endcode + + The \a index must be to an existing item in the list, or one past + the end of the list (equivalent to append). + + \sa set(), append() +*/ + +void QQmlListModel::insert(QQmlV8Function *args) +{ + if (args->Length() == 2) { + + v8::Handle arg0 = (*args)[0]; + int index = arg0->Int32Value(); + + if (index < 0 || index > count()) { + qmlInfo(this) << tr("insert: index %1 out of range").arg(index); + return; + } + + v8::Handle arg1 = (*args)[1]; + + if (arg1->IsArray()) { + v8::Handle objectArray = v8::Handle::Cast(arg1); + int objectArrayLength = objectArray->Length(); + for (int i=0 ; i < objectArrayLength ; ++i) { + v8::Handle argObject = objectArray->Get(i)->ToObject(); + + if (m_dynamicRoles) { + m_modelObjects.insert(index+i, DynamicRoleModelNode::create(args->engine()->variantMapFromJS(argObject), this)); + } else { + m_listModel->insert(index+i, argObject, args->engine()); + } + } + emitItemsInserted(index, objectArrayLength); + } else if (arg1->IsObject()) { + v8::Handle argObject = arg1->ToObject(); + + if (m_dynamicRoles) { + m_modelObjects.insert(index, DynamicRoleModelNode::create(args->engine()->variantMapFromJS(argObject), this)); + } else { + m_listModel->insert(index, argObject, args->engine()); + } + + emitItemsInserted(index, 1); + } else { + qmlInfo(this) << tr("insert: value is not an object"); + } + } else { + qmlInfo(this) << tr("insert: value is not an object"); + } +} + +/*! + \qmlmethod QtQml2::ListModel::move(int from, int to, int n) + + Moves \a n items \a from one position \a to another. + + The from and to ranges must exist; for example, to move the first 3 items + to the end of the list: + + \code + fruitModel.move(0, fruitModel.count - 3, 3) + \endcode + + \sa append() +*/ +void QQmlListModel::move(int from, int to, int n) +{ + if (n==0 || from==to) + return; + if (!canMove(from, to, n)) { + qmlInfo(this) << tr("move: out of range"); + return; + } + + if (m_dynamicRoles) { + + int realFrom = from; + int realTo = to; + int realN = n; + + if (from > to) { + // Only move forwards - flip if backwards moving + int tfrom = from; + int tto = to; + realFrom = tto; + realTo = tto+n; + realN = tfrom-tto; + } + + QPODVector store; + for (int i=0 ; i < (realTo-realFrom) ; ++i) + store.append(m_modelObjects[realFrom+realN+i]); + for (int i=0 ; i < realN ; ++i) + store.append(m_modelObjects[realFrom+i]); + for (int i=0 ; i < store.count() ; ++i) + m_modelObjects[realFrom+i] = store[i]; + + } else { + m_listModel->move(from, to, n); + } + + emitItemsMoved(from, to, n); +} + +/*! + \qmlmethod QtQml2::ListModel::append(jsobject dict) + + Adds a new item to the end of the list model, with the + values in \a dict. + + \code + fruitModel.append({"cost": 5.95, "name":"Pizza"}) + \endcode + + \sa set(), remove() +*/ +void QQmlListModel::append(QQmlV8Function *args) +{ + if (args->Length() == 1) { + v8::Handle arg = (*args)[0]; + + if (arg->IsArray()) { + v8::Handle objectArray = v8::Handle::Cast(arg); + int objectArrayLength = objectArray->Length(); + + int index = count(); + for (int i=0 ; i < objectArrayLength ; ++i) { + v8::Handle argObject = objectArray->Get(i)->ToObject(); + + if (m_dynamicRoles) { + m_modelObjects.append(DynamicRoleModelNode::create(args->engine()->variantMapFromJS(argObject), this)); + } else { + m_listModel->append(argObject, args->engine()); + } + } + + emitItemsInserted(index, objectArrayLength); + } else if (arg->IsObject()) { + v8::Handle argObject = arg->ToObject(); + + int index; + + if (m_dynamicRoles) { + index = m_modelObjects.count(); + m_modelObjects.append(DynamicRoleModelNode::create(args->engine()->variantMapFromJS(argObject), this)); + } else { + index = m_listModel->append(argObject, args->engine()); + } + + emitItemsInserted(index, 1); + } else { + qmlInfo(this) << tr("append: value is not an object"); + } + } else { + qmlInfo(this) << tr("append: value is not an object"); + } +} + +/*! + \qmlmethod object QtQml2::ListModel::get(int index) + + Returns the item at \a index in the list model. This allows the item + data to be accessed or modified from JavaScript: + + \code + Component.onCompleted: { + fruitModel.append({"cost": 5.95, "name":"Jackfruit"}); + console.log(fruitModel.get(0).cost); + fruitModel.get(0).cost = 10.95; + } + \endcode + + The \a index must be an element in the list. + + Note that properties of the returned object that are themselves objects + will also be models, and this get() method is used to access elements: + + \code + fruitModel.append(..., "attributes": + [{"name":"spikes","value":"7mm"}, + {"name":"color","value":"green"}]); + fruitModel.get(0).attributes.get(1).value; // == "green" + \endcode + + \warning The returned object is not guaranteed to remain valid. It + should not be used in \l{Property Binding}{property bindings}. + + \sa append() +*/ +QQmlV8Handle QQmlListModel::get(int index) const +{ + v8::Handle result = v8::Undefined(); + + if (index >= 0 && index < count()) { + QV8Engine *v8engine = engine(); + + if (m_dynamicRoles) { + DynamicRoleModelNode *object = m_modelObjects[index]; + result = v8engine->newQObject(object); + } else { + ModelObject *object = m_listModel->getOrCreateModelObject(const_cast(this), index); + result = v8engine->newQObject(object); + } + } + + return QQmlV8Handle::fromHandle(result); +} + +/*! + \qmlmethod QtQml2::ListModel::set(int index, jsobject dict) + + Changes the item at \a index in the list model with the + values in \a dict. Properties not appearing in \a dict + are left unchanged. + + \code + fruitModel.set(3, {"cost": 5.95, "name":"Pizza"}) + \endcode + + If \a index is equal to count() then a new item is appended to the + list. Otherwise, \a index must be an element in the list. + + \sa append() +*/ +void QQmlListModel::set(int index, const QQmlV8Handle &handle) +{ + v8::Handle valuemap = handle.toHandle(); + + if (!valuemap->IsObject() || valuemap->IsArray()) { + qmlInfo(this) << tr("set: value is not an object"); + return; + } + if (index > count() || index < 0) { + qmlInfo(this) << tr("set: index %1 out of range").arg(index); + return; + } + + v8::Handle object = valuemap->ToObject(); + + if (index == count()) { + + if (m_dynamicRoles) { + m_modelObjects.append(DynamicRoleModelNode::create(engine()->variantMapFromJS(object), this)); + } else { + m_listModel->insert(index, object, engine()); + } + + emitItemsInserted(index, 1); + } else { + + QVector roles; + + if (m_dynamicRoles) { + m_modelObjects[index]->updateValues(engine()->variantMapFromJS(object), roles); + } else { + m_listModel->set(index, object, &roles, engine()); + } + + if (roles.count()) + emitItemsChanged(index, 1, roles); + } +} + +/*! + \qmlmethod QtQml2::ListModel::setProperty(int index, string property, variant value) + + Changes the \a property of the item at \a index in the list model to \a value. + + \code + fruitModel.setProperty(3, "cost", 5.95) + \endcode + + The \a index must be an element in the list. + + \sa append() +*/ +void QQmlListModel::setProperty(int index, const QString& property, const QVariant& value) +{ + if (count() == 0 || index >= count() || index < 0) { + qmlInfo(this) << tr("set: index %1 out of range").arg(index); + return; + } + + if (m_dynamicRoles) { + int roleIndex = m_roles.indexOf(property); + if (roleIndex == -1) { + roleIndex = m_roles.count(); + m_roles.append(property); + } + if (m_modelObjects[index]->setValue(property.toUtf8(), value)) { + QVector roles; + roles << roleIndex; + emitItemsChanged(index, 1, roles); + } + } else { + int roleIndex = m_listModel->setOrCreateProperty(index, property, value); + if (roleIndex != -1) { + + QVector roles; + roles << roleIndex; + + emitItemsChanged(index, 1, roles); + } + } +} + +/*! + \qmlmethod QtQml2::ListModel::sync() + + Writes any unsaved changes to the list model after it has been modified + from a worker script. +*/ +void QQmlListModel::sync() +{ + // This is just a dummy method to make it look like sync() exists in + // ListModel (and not just QQmlListModelWorkerAgent) and to let + // us document sync(). + qmlInfo(this) << "List sync() can only be called from a WorkerScript"; +} + +bool QQmlListModelParser::compileProperty(const QQmlCustomParserProperty &prop, QList &instr, QByteArray &data) +{ + QList values = prop.assignedValues(); + for(int ii = 0; ii < values.count(); ++ii) { + const QVariant &value = values.at(ii); + + if(value.userType() == qMetaTypeId()) { + QQmlCustomParserNode node = + qvariant_cast(value); + + if (node.name() != listElementTypeName) { + const QMetaObject *mo = resolveType(node.name()); + if (mo != &QQmlListElement::staticMetaObject) { + error(node, QQmlListModel::tr("ListElement: cannot contain nested elements")); + return false; + } + listElementTypeName = node.name(); // cache right name for next time + } + + { + ListInstruction li; + li.type = ListInstruction::Push; + li.dataIdx = -1; + instr << li; + } + + QList props = node.properties(); + for(int jj = 0; jj < props.count(); ++jj) { + const QQmlCustomParserProperty &nodeProp = props.at(jj); + if (nodeProp.name().isEmpty()) { + error(nodeProp, QQmlListModel::tr("ListElement: cannot contain nested elements")); + return false; + } + if (nodeProp.name() == QStringLiteral("id")) { + error(nodeProp, QQmlListModel::tr("ListElement: cannot use reserved \"id\" property")); + return false; + } + + ListInstruction li; + int ref = data.count(); + data.append(nodeProp.name().toUtf8()); + data.append('\0'); + li.type = ListInstruction::Set; + li.dataIdx = ref; + instr << li; + + if(!compileProperty(nodeProp, instr, data)) + return false; + + li.type = ListInstruction::Pop; + li.dataIdx = -1; + instr << li; + } + + { + ListInstruction li; + li.type = ListInstruction::Pop; + li.dataIdx = -1; + instr << li; + } + + } else { + + QQmlScript::Variant variant = + qvariant_cast(value); + + int ref = data.count(); + + QByteArray d; + d += char(variant.type()); // type tag + if (variant.isString()) { + d += variant.asString().toUtf8(); + } else if (variant.isNumber()) { + d += QByteArray::number(variant.asNumber(),'g',20); + } else if (variant.isBoolean()) { + d += char(variant.asBoolean()); + } else if (variant.isScript()) { + if (definesEmptyList(variant.asScript())) { + d[0] = char(QQmlScript::Variant::Invalid); // marks empty list + } else { + QByteArray script = variant.asScript().toUtf8(); + bool ok; + int v = evaluateEnum(script, &ok); + if (!ok) { + using namespace QQmlJS; + AST::Node *node = variant.asAST(); + AST::StringLiteral *literal = 0; + if (AST::CallExpression *callExpr = AST::cast(node)) { + if (AST::IdentifierExpression *idExpr = AST::cast(callExpr->base)) { + if (idExpr->name == QLatin1String("QT_TR_NOOP") || idExpr->name == QLatin1String("QT_TRID_NOOP")) { + if (callExpr->arguments && !callExpr->arguments->next) + literal = AST::cast(callExpr->arguments->expression); + if (!literal) { + error(prop, QQmlListModel::tr("ListElement: improperly specified %1").arg(idExpr->name.toString())); + return false; + } + } else if (idExpr->name == QLatin1String("QT_TRANSLATE_NOOP")) { + if (callExpr->arguments && callExpr->arguments->next && !callExpr->arguments->next->next) + literal = AST::cast(callExpr->arguments->next->expression); + if (!literal) { + error(prop, QQmlListModel::tr("ListElement: improperly specified QT_TRANSLATE_NOOP")); + return false; + } + } + } + } + + if (literal) { + d[0] = char(QQmlScript::Variant::String); + d += literal->value.toUtf8(); + } else { + error(prop, QQmlListModel::tr("ListElement: cannot use script for property value")); + return false; + } + } else { + d[0] = char(QQmlScript::Variant::Number); + d += QByteArray::number(v); + } + } + } + d.append('\0'); + data.append(d); + + ListInstruction li; + li.type = ListInstruction::Value; + li.dataIdx = ref; + instr << li; + } + } + + return true; +} + +QByteArray QQmlListModelParser::compile(const QList &customProps) +{ + QList instr; + QByteArray data; + listElementTypeName = QString(); // unknown + + for(int ii = 0; ii < customProps.count(); ++ii) { + const QQmlCustomParserProperty &prop = customProps.at(ii); + if(!prop.name().isEmpty()) { // isn't default property + error(prop, QQmlListModel::tr("ListModel: undefined property '%1'").arg(prop.name())); + return QByteArray(); + } + + if(!compileProperty(prop, instr, data)) { + return QByteArray(); + } + } + + int size = sizeof(ListModelData) + + instr.count() * sizeof(ListInstruction) + + data.count(); + + QByteArray rv; + rv.resize(size); + + ListModelData *lmd = (ListModelData *)rv.data(); + lmd->dataOffset = sizeof(ListModelData) + + instr.count() * sizeof(ListInstruction); + lmd->instrCount = instr.count(); + for (int ii = 0; ii < instr.count(); ++ii) + lmd->instructions()[ii] = instr.at(ii); + ::memcpy(rv.data() + lmd->dataOffset, data.constData(), data.count()); + + return rv; +} + +void QQmlListModelParser::setCustomData(QObject *obj, const QByteArray &d) +{ + QQmlListModel *rv = static_cast(obj); + + QV8Engine *engine = QQmlEnginePrivate::getV8Engine(qmlEngine(rv)); + rv->m_engine = engine; + + const ListModelData *lmd = (const ListModelData *)d.constData(); + const char *data = ((const char *)lmd) + lmd->dataOffset; + + bool setRoles = false; + + QStack stack; + + for (int ii = 0; ii < lmd->instrCount; ++ii) { + const ListInstruction &instr = lmd->instructions()[ii]; + + switch(instr.type) { + case ListInstruction::Push: + { + Q_ASSERT(!rv->m_dynamicRoles); + + ListModel *subModel = 0; + + if (stack.count() == 0) { + subModel = rv->m_listModel; + } else { + const DataStackElement &e0 = stack.at(stack.size() - 1); + DataStackElement &e1 = stack[stack.size() - 2]; + + const ListLayout::Role &role = e1.model->getOrCreateListRole(e0.name); + if (role.type == ListLayout::Role::List) { + subModel = e1.model->getListProperty(e1.elementIndex, role); + + if (subModel == 0) { + subModel = new ListModel(role.subLayout, 0, -1); + QVariant vModel = QVariant::fromValue(subModel); + e1.model->setOrCreateProperty(e1.elementIndex, e0.name, vModel); + } + } + } + + DataStackElement e; + e.model = subModel; + e.elementIndex = subModel ? subModel->appendElement() : -1; + stack.push(e); + } + break; + + case ListInstruction::Pop: + stack.pop(); + break; + + case ListInstruction::Value: + { + const DataStackElement &e0 = stack.at(stack.size() - 1); + DataStackElement &e1 = stack[stack.size() - 2]; + + QString name = e0.name; + QVariant value; + + switch (QQmlScript::Variant::Type(data[instr.dataIdx])) { + case QQmlScript::Variant::Invalid: + { + const ListLayout::Role &role = e1.model->getOrCreateListRole(e0.name); + ListModel *emptyModel = new ListModel(role.subLayout, 0, -1); + value = QVariant::fromValue(emptyModel); + } + break; + case QQmlScript::Variant::Boolean: + value = bool(data[1 + instr.dataIdx]); + break; + case QQmlScript::Variant::Number: + value = QByteArray(data + 1 + instr.dataIdx).toDouble(); + break; + case QQmlScript::Variant::String: + value = QString::fromUtf8(data + 1 + instr.dataIdx); + break; + default: + Q_ASSERT("Format error in ListInstruction"); + } + + e1.model->setOrCreateProperty(e1.elementIndex, name, value); + setRoles = true; + } + break; + + case ListInstruction::Set: + { + DataStackElement e; + e.name = QString::fromUtf8(data + instr.dataIdx); + stack.push(e); + } + break; + } + } + + if (setRoles == false) + qmlInfo(obj) << "All ListElement declarations are empty, no roles can be created unless dynamicRoles is set."; +} + +bool QQmlListModelParser::definesEmptyList(const QString &s) +{ + if (s.startsWith(QLatin1Char('[')) && s.endsWith(QLatin1Char(']'))) { + for (int i=1; i +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + + +class QQmlListModelWorkerAgent; +class ListModel; +class ListLayout; + +class Q_QML_PRIVATE_EXPORT QQmlListModel : public QAbstractListModel +{ + Q_OBJECT + Q_PROPERTY(int count READ count NOTIFY countChanged) + Q_PROPERTY(bool dynamicRoles READ dynamicRoles WRITE setDynamicRoles) + +public: + QQmlListModel(QObject *parent=0); + ~QQmlListModel(); + + QModelIndex index(int row, int column, const QModelIndex &parent) const; + int rowCount(const QModelIndex &parent) const; + QVariant data(const QModelIndex &index, int role) const; + QHash roleNames() const; + + QVariant data(int index, int role) const; + int count() const; + + Q_INVOKABLE void clear(); + Q_INVOKABLE void remove(QQmlV8Function *args); + Q_INVOKABLE void append(QQmlV8Function *args); + Q_INVOKABLE void insert(QQmlV8Function *args); + Q_INVOKABLE QQmlV8Handle get(int index) const; + Q_INVOKABLE void set(int index, const QQmlV8Handle &); + Q_INVOKABLE void setProperty(int index, const QString& property, const QVariant& value); + Q_INVOKABLE void move(int from, int to, int count); + Q_INVOKABLE void sync(); + + QQmlListModelWorkerAgent *agent(); + + bool dynamicRoles() const { return m_dynamicRoles; } + void setDynamicRoles(bool enableDynamicRoles); + +Q_SIGNALS: + void countChanged(); + +private: + friend class QQmlListModelParser; + friend class QQmlListModelWorkerAgent; + friend class ModelObject; + friend class ModelNodeMetaObject; + friend class ListModel; + friend class ListElement; + friend class DynamicRoleModelNode; + friend class DynamicRoleModelNodeMetaObject; + + // Constructs a flat list model for a worker agent + QQmlListModel(QQmlListModel *orig, QQmlListModelWorkerAgent *agent); + QQmlListModel(const QQmlListModel *owner, ListModel *data, QV8Engine *eng, QObject *parent=0); + + QV8Engine *engine() const; + + inline bool canMove(int from, int to, int n) const { return !(from+n > count() || to+n > count() || from < 0 || to < 0 || n < 0); } + + QQmlListModelWorkerAgent *m_agent; + mutable QV8Engine *m_engine; + bool m_mainThread; + bool m_primary; + + bool m_dynamicRoles; + + ListLayout *m_layout; + ListModel *m_listModel; + + QVector m_modelObjects; + QVector m_roles; + int m_uid; + + struct ElementSync + { + ElementSync() : src(0), target(0) {} + + DynamicRoleModelNode *src; + DynamicRoleModelNode *target; + }; + + int getUid() const { return m_uid; } + + static void sync(QQmlListModel *src, QQmlListModel *target, QHash *targetModelHash); + static QQmlListModel *createWithOwner(QQmlListModel *newOwner); + + void emitItemsChanged(int index, int count, const QVector &roles); + void emitItemsRemoved(int index, int count); + void emitItemsInserted(int index, int count); + void emitItemsMoved(int from, int to, int n); +}; + +// ### FIXME +class QQmlListElement : public QObject +{ +Q_OBJECT +}; + +class QQmlListModelParser : public QQmlCustomParser +{ +public: + QQmlListModelParser() : QQmlCustomParser(QQmlCustomParser::AcceptsSignalHandlers) {} + QByteArray compile(const QList &); + void setCustomData(QObject *, const QByteArray &); + +private: + struct ListInstruction + { + enum { Push, Pop, Value, Set } type; + int dataIdx; + }; + struct ListModelData + { + int dataOffset; + int instrCount; + ListInstruction *instructions() const; + }; + bool compileProperty(const QQmlCustomParserProperty &prop, QList &instr, QByteArray &data); + + bool definesEmptyList(const QString &); + + QString listElementTypeName; + + struct DataStackElement + { + DataStackElement() : model(0), elementIndex(0) {} + + QString name; + ListModel *model; + int elementIndex; + }; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQmlListModel) +QML_DECLARE_TYPE(QQmlListElement) + +QT_END_HEADER + +#endif // QQMLLISTMODEL_H diff --git a/src/qml/qml/qqmllistmodel_p_p.h b/src/qml/qml/qqmllistmodel_p_p.h new file mode 100644 index 0000000000..d48edec0bf --- /dev/null +++ b/src/qml/qml/qqmllistmodel_p_p.h @@ -0,0 +1,382 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLLISTMODEL_P_P_H +#define QQMLLISTMODEL_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qqmllistmodel_p.h" +#include +#include "qqmlopenmetaobject_p.h" +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + + +class DynamicRoleModelNode; + +class DynamicRoleModelNodeMetaObject : public QQmlOpenMetaObject +{ +public: + DynamicRoleModelNodeMetaObject(DynamicRoleModelNode *object); + ~DynamicRoleModelNodeMetaObject(); + + bool m_enabled; + +protected: + void propertyWrite(int index); + void propertyWritten(int index); + +private: + DynamicRoleModelNode *m_owner; +}; + +class DynamicRoleModelNode : public QObject +{ + Q_OBJECT +public: + DynamicRoleModelNode(QQmlListModel *owner, int uid); + + static DynamicRoleModelNode *create(const QVariantMap &obj, QQmlListModel *owner); + + void updateValues(const QVariantMap &object, QVector &roles); + + QVariant getValue(const QString &name) + { + return m_meta->value(name.toUtf8()); + } + + bool setValue(const QByteArray &name, const QVariant &val) + { + return m_meta->setValue(name, val); + } + + void setNodeUpdatesEnabled(bool enable) + { + m_meta->m_enabled = enable; + } + + int getUid() const + { + return m_uid; + } + + static void sync(DynamicRoleModelNode *src, DynamicRoleModelNode *target, QHash *targetModelHash); + +private: + QQmlListModel *m_owner; + int m_uid; + DynamicRoleModelNodeMetaObject *m_meta; + + friend class DynamicRoleModelNodeMetaObject; +}; + +class ModelObject; + +class ModelNodeMetaObject : public QQmlOpenMetaObject +{ +public: + ModelNodeMetaObject(ModelObject *object); + ~ModelNodeMetaObject(); + + bool m_enabled; + +protected: + void propertyWritten(int index); + +private: + + ModelObject *m_obj; +}; + +class ModelObject : public QObject +{ + Q_OBJECT +public: + ModelObject(QQmlListModel *model, int elementIndex); + + void setValue(const QByteArray &name, const QVariant &val, bool force) + { + if (force) { + QVariant existingValue = m_meta->value(name); + if (existingValue.isValid()) { + (*m_meta)[name] = QVariant(); + } + } + m_meta->setValue(name, val); + } + + void setNodeUpdatesEnabled(bool enable) + { + m_meta->m_enabled = enable; + } + + void updateValues(); + void updateValues(const QVector &roles); + + QQmlListModel *m_model; + int m_elementIndex; + +private: + ModelNodeMetaObject *m_meta; +}; + +class ListLayout +{ +public: + ListLayout() : currentBlock(0), currentBlockOffset(0) {} + ListLayout(const ListLayout *other); + ~ListLayout(); + + class Role + { + public: + + Role() : type(Invalid), blockIndex(-1), blockOffset(-1), index(-1), subLayout(0) {} + explicit Role(const Role *other); + ~Role(); + + // This enum must be kept in sync with the roleTypeNames variable in qdeclarativelistmodel.cpp + enum DataType + { + Invalid = -1, + + String, + Number, + Bool, + List, + QObject, + VariantMap, + DateTime, + + MaxDataType + }; + + QString name; + DataType type; + int blockIndex; + int blockOffset; + int index; + ListLayout *subLayout; + }; + + const Role *getRoleOrCreate(const QString &key, const QVariant &data); + const Role &getRoleOrCreate(v8::Handle key, Role::DataType type); + const Role &getRoleOrCreate(const QString &key, Role::DataType type); + + const Role &getExistingRole(int index) { return *roles.at(index); } + const Role *getExistingRole(const QString &key); + const Role *getExistingRole(v8::Handle key); + + int roleCount() const { return roles.count(); } + + static void sync(ListLayout *src, ListLayout *target); + +private: + const Role &createRole(const QString &key, Role::DataType type); + + int currentBlock; + int currentBlockOffset; + QVector roles; + QStringHash roleHash; +}; + +class ListElement +{ +public: + + ListElement(); + ListElement(int existingUid); + ~ListElement(); + + static void sync(ListElement *src, ListLayout *srcLayout, ListElement *target, ListLayout *targetLayout, QHash *targetModelHash); + + enum + { + BLOCK_SIZE = 64 - sizeof(int) - sizeof(ListElement *) - sizeof(ModelObject *) + }; + +private: + + void destroy(ListLayout *layout); + + int setVariantProperty(const ListLayout::Role &role, const QVariant &d); + + int setJsProperty(const ListLayout::Role &role, v8::Handle d, QV8Engine *eng); + + int setStringProperty(const ListLayout::Role &role, const QString &s); + int setDoubleProperty(const ListLayout::Role &role, double n); + int setBoolProperty(const ListLayout::Role &role, bool b); + int setListProperty(const ListLayout::Role &role, ListModel *m); + int setQObjectProperty(const ListLayout::Role &role, QObject *o); + int setVariantMapProperty(const ListLayout::Role &role, v8::Handle o, QV8Engine *eng); + int setVariantMapProperty(const ListLayout::Role &role, QVariantMap *m); + int setDateTimeProperty(const ListLayout::Role &role, const QDateTime &dt); + + void setStringPropertyFast(const ListLayout::Role &role, const QString &s); + void setDoublePropertyFast(const ListLayout::Role &role, double n); + void setBoolPropertyFast(const ListLayout::Role &role, bool b); + void setQObjectPropertyFast(const ListLayout::Role &role, QObject *o); + void setListPropertyFast(const ListLayout::Role &role, ListModel *m); + void setVariantMapFast(const ListLayout::Role &role, v8::Handle o, QV8Engine *eng); + void setDateTimePropertyFast(const ListLayout::Role &role, const QDateTime &dt); + + void clearProperty(const ListLayout::Role &role); + + QVariant getProperty(const ListLayout::Role &role, const QQmlListModel *owner, QV8Engine *eng); + ListModel *getListProperty(const ListLayout::Role &role); + QString *getStringProperty(const ListLayout::Role &role); + QObject *getQObjectProperty(const ListLayout::Role &role); + QQmlGuard *getGuardProperty(const ListLayout::Role &role); + QVariantMap *getVariantMapProperty(const ListLayout::Role &role); + QDateTime *getDateTimeProperty(const ListLayout::Role &role); + + inline char *getPropertyMemory(const ListLayout::Role &role); + + int getUid() const { return uid; } + + char data[BLOCK_SIZE]; + ListElement *next; + + int uid; + ModelObject *m_objectCache; + + friend class ListModel; +}; + +class ListModel +{ +public: + + ListModel(ListLayout *layout, QQmlListModel *modelCache, int uid); + ~ListModel() {} + + void destroy(); + + int setOrCreateProperty(int elementIndex, const QString &key, const QVariant &data); + int setExistingProperty(int uid, const QString &key, v8::Handle data, QV8Engine *eng); + + QVariant getProperty(int elementIndex, int roleIndex, const QQmlListModel *owner, QV8Engine *eng); + ListModel *getListProperty(int elementIndex, const ListLayout::Role &role); + + int roleCount() const + { + return m_layout->roleCount(); + } + + const ListLayout::Role &getExistingRole(int index) + { + return m_layout->getExistingRole(index); + } + + const ListLayout::Role &getOrCreateListRole(const QString &name) + { + return m_layout->getRoleOrCreate(name, ListLayout::Role::List); + } + + int elementCount() const + { + return elements.count(); + } + + void set(int elementIndex, v8::Handle object, QVector *roles, QV8Engine *eng); + void set(int elementIndex, v8::Handle object, QV8Engine *eng); + + int append(v8::Handle object, QV8Engine *eng); + void insert(int elementIndex, v8::Handle object, QV8Engine *eng); + + void clear(); + void remove(int index, int count); + + int appendElement(); + void insertElement(int index); + + void move(int from, int to, int n); + + int getUid() const { return m_uid; } + + static void sync(ListModel *src, ListModel *target, QHash *srcModelHash); + + ModelObject *getOrCreateModelObject(QQmlListModel *model, int elementIndex); + +private: + QPODVector elements; + ListLayout *m_layout; + int m_uid; + + QQmlListModel *m_modelCache; + + struct ElementSync + { + ElementSync() : src(0), target(0) {} + + ListElement *src; + ListElement *target; + }; + + void newElement(int index); + + void updateCacheIndices(); + + friend class ListElement; + friend class QQmlListModelWorkerAgent; +}; + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(ListModel *); + +QT_END_HEADER + +#endif // QQUICKLISTMODEL_P_P_H + diff --git a/src/qml/qml/qqmllistmodelworkeragent.cpp b/src/qml/qml/qqmllistmodelworkeragent.cpp new file mode 100644 index 0000000000..9554e6d1e5 --- /dev/null +++ b/src/qml/qml/qqmllistmodelworkeragent.cpp @@ -0,0 +1,259 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmllistmodelworkeragent_p.h" +#include "qqmllistmodel_p_p.h" +#include +#include +#include + +#include +#include +#include + + +QT_BEGIN_NAMESPACE + + +void QQmlListModelWorkerAgent::Data::clearChange(int uid) +{ + for (int i=0 ; i < changes.count() ; ++i) { + if (changes[i].modelUid == uid) { + changes.removeAt(i); + --i; + } + } +} + +void QQmlListModelWorkerAgent::Data::insertChange(int uid, int index, int count) +{ + Change c = { uid, Change::Inserted, index, count, 0, QVector() }; + changes << c; +} + +void QQmlListModelWorkerAgent::Data::removeChange(int uid, int index, int count) +{ + Change c = { uid, Change::Removed, index, count, 0, QVector() }; + changes << c; +} + +void QQmlListModelWorkerAgent::Data::moveChange(int uid, int index, int count, int to) +{ + Change c = { uid, Change::Moved, index, count, to, QVector() }; + changes << c; +} + +void QQmlListModelWorkerAgent::Data::changedChange(int uid, int index, int count, const QVector &roles) +{ + Change c = { uid, Change::Changed, index, count, 0, roles }; + changes << c; +} + +QQmlListModelWorkerAgent::QQmlListModelWorkerAgent(QQmlListModel *model) +: m_ref(1), m_orig(model), m_copy(new QQmlListModel(model, this)) +{ +} + +QQmlListModelWorkerAgent::~QQmlListModelWorkerAgent() +{ + mutex.lock(); + syncDone.wakeAll(); + mutex.unlock(); +} + +void QQmlListModelWorkerAgent::setV8Engine(QV8Engine *eng) +{ + m_copy->m_engine = eng; +} + +void QQmlListModelWorkerAgent::addref() +{ + m_ref.ref(); +} + +void QQmlListModelWorkerAgent::release() +{ + bool del = !m_ref.deref(); + + if (del) + deleteLater(); +} + +void QQmlListModelWorkerAgent::modelDestroyed() +{ + m_orig = 0; +} + +int QQmlListModelWorkerAgent::count() const +{ + return m_copy->count(); +} + +void QQmlListModelWorkerAgent::clear() +{ + m_copy->clear(); +} + +void QQmlListModelWorkerAgent::remove(QQmlV8Function *args) +{ + m_copy->remove(args); +} + +void QQmlListModelWorkerAgent::append(QQmlV8Function *args) +{ + m_copy->append(args); +} + +void QQmlListModelWorkerAgent::insert(QQmlV8Function *args) +{ + m_copy->insert(args); +} + +QQmlV8Handle QQmlListModelWorkerAgent::get(int index) const +{ + return m_copy->get(index); +} + +void QQmlListModelWorkerAgent::set(int index, const QQmlV8Handle &value) +{ + m_copy->set(index, value); +} + +void QQmlListModelWorkerAgent::setProperty(int index, const QString& property, const QVariant& value) +{ + m_copy->setProperty(index, property, value); +} + +void QQmlListModelWorkerAgent::move(int from, int to, int count) +{ + m_copy->move(from, to, count); +} + +void QQmlListModelWorkerAgent::sync() +{ + Sync *s = new Sync; + s->data = data; + s->list = m_copy; + data.changes.clear(); + + mutex.lock(); + QCoreApplication::postEvent(this, s); + syncDone.wait(&mutex); + mutex.unlock(); +} + +bool QQmlListModelWorkerAgent::event(QEvent *e) +{ + if (e->type() == QEvent::User) { + bool cc = false; + QMutexLocker locker(&mutex); + if (m_orig) { + Sync *s = static_cast(e); + const QList &changes = s->data.changes; + + cc = m_orig->count() != s->list->count(); + + QHash targetModelDynamicHash; + QHash targetModelStaticHash; + + Q_ASSERT(m_orig->m_dynamicRoles == s->list->m_dynamicRoles); + if (m_orig->m_dynamicRoles) + QQmlListModel::sync(s->list, m_orig, &targetModelDynamicHash); + else + ListModel::sync(s->list->m_listModel, m_orig->m_listModel, &targetModelStaticHash); + + for (int ii = 0; ii < changes.count(); ++ii) { + const Change &change = changes.at(ii); + + QQmlListModel *model = 0; + if (m_orig->m_dynamicRoles) { + model = targetModelDynamicHash.value(change.modelUid); + } else { + ListModel *lm = targetModelStaticHash.value(change.modelUid); + if (lm) + model = lm->m_modelCache; + } + + if (model) { + switch (change.type) { + case Change::Inserted: + model->beginInsertRows( + QModelIndex(), change.index, change.index + change.count - 1); + model->endInsertRows(); + break; + case Change::Removed: + model->beginRemoveRows( + QModelIndex(), change.index, change.index + change.count - 1); + model->endRemoveRows(); + break; + case Change::Moved: + model->beginMoveRows( + QModelIndex(), + change.index, + change.index + change.count - 1, + QModelIndex(), + change.to > change.index ? change.to + change.count : change.to); + model->endMoveRows(); + break; + case Change::Changed: + emit model->dataChanged( + model->createIndex(change.index, 0), + model->createIndex(change.index + change.count - 1, 0), + change.roles); + break; + } + } + } + } + + syncDone.wakeAll(); + locker.unlock(); + + if (cc) + emit m_orig->countChanged(); + return true; + } + + return QObject::event(e); +} + +QT_END_NAMESPACE + diff --git a/src/qml/qml/qqmllistmodelworkeragent_p.h b/src/qml/qml/qqmllistmodelworkeragent_p.h new file mode 100644 index 0000000000..9471d6663f --- /dev/null +++ b/src/qml/qml/qqmllistmodelworkeragent_p.h @@ -0,0 +1,160 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKLISTMODELWORKERAGENT_P_H +#define QQUICKLISTMODELWORKERAGENT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include + +#include +#include + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + + +class QQmlListModel; + +class QQmlListModelWorkerAgent : public QObject +{ + Q_OBJECT + Q_PROPERTY(int count READ count) + +public: + QQmlListModelWorkerAgent(QQmlListModel *); + ~QQmlListModelWorkerAgent(); + void setV8Engine(QV8Engine *eng); + + void addref(); + void release(); + + int count() const; + + Q_INVOKABLE void clear(); + Q_INVOKABLE void remove(QQmlV8Function *args); + Q_INVOKABLE void append(QQmlV8Function *args); + Q_INVOKABLE void insert(QQmlV8Function *args); + Q_INVOKABLE QQmlV8Handle get(int index) const; + Q_INVOKABLE void set(int index, const QQmlV8Handle &); + Q_INVOKABLE void setProperty(int index, const QString& property, const QVariant& value); + Q_INVOKABLE void move(int from, int to, int count); + Q_INVOKABLE void sync(); + + struct VariantRef + { + VariantRef() : a(0) {} + VariantRef(const VariantRef &r) : a(r.a) { if (a) a->addref(); } + VariantRef(QQmlListModelWorkerAgent *_a) : a(_a) { if (a) a->addref(); } + ~VariantRef() { if (a) a->release(); } + + VariantRef &operator=(const VariantRef &o) { + if (o.a) o.a->addref(); + if (a) a->release(); a = o.a; + return *this; + } + + QQmlListModelWorkerAgent *a; + }; + void modelDestroyed(); +protected: + virtual bool event(QEvent *); + +private: + friend class QQuickWorkerScriptEnginePrivate; + friend class QQmlListModel; + + struct Change + { + int modelUid; + enum { Inserted, Removed, Moved, Changed } type; + int index; // Inserted/Removed/Moved/Changed + int count; // Inserted/Removed/Moved/Changed + int to; // Moved + QVector roles; + }; + + struct Data + { + QList changes; + + void clearChange(int uid); + void insertChange(int uid, int index, int count); + void removeChange(int uid, int index, int count); + void moveChange(int uid, int index, int count, int to); + void changedChange(int uid, int index, int count, const QVector &roles); + }; + Data data; + + struct Sync : public QEvent { + Sync() : QEvent(QEvent::User) {} + Data data; + QQmlListModel *list; + }; + + QAtomicInt m_ref; + QQmlListModel *m_orig; + QQmlListModel *m_copy; + QMutex mutex; + QWaitCondition syncDone; +}; + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QQmlListModelWorkerAgent::VariantRef) + +QT_END_HEADER + +#endif // QQUICKLISTMODELWORKERAGENT_P_H + diff --git a/src/qml/qml/qquicklistmodel.cpp b/src/qml/qml/qquicklistmodel.cpp deleted file mode 100644 index 91c1a9c476..0000000000 --- a/src/qml/qml/qquicklistmodel.cpp +++ /dev/null @@ -1,2567 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** 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, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qquicklistmodel_p_p.h" -#include "qquicklistmodelworkeragent_p.h" -#include "qqmlopenmetaobject_p.h" -#include -#include - - -#include -#include -#include -#include -#include - -#include -#include -#include - -QT_BEGIN_NAMESPACE - -// Set to 1024 as a debugging aid - easier to distinguish uids from indices of elements/models. -enum { MIN_LISTMODEL_UID = 1024 }; - -static QAtomicInt uidCounter(MIN_LISTMODEL_UID); - -template -static bool isMemoryUsed(const char *mem) -{ - for (size_t i=0 ; i < sizeof(T) ; ++i) { - if (mem[i] != 0) - return true; - } - - return false; -} - -static QString roleTypeName(ListLayout::Role::DataType t) -{ - QString result; - const char *roleTypeNames[] = { "String", "Number", "Bool", "List", "QObject", "VariantMap", "DateTime" }; - - if (t > ListLayout::Role::Invalid && t < ListLayout::Role::MaxDataType) - result = QString::fromLatin1(roleTypeNames[t]); - - return result; -} - -const ListLayout::Role &ListLayout::getRoleOrCreate(const QString &key, Role::DataType type) -{ - QStringHash::Node *node = roleHash.findNode(key); - if (node) { - const Role &r = *node->value; - if (type != r.type) - qmlInfo(0) << QString::fromLatin1("Can't assign to existing role '%1' of different type [%2 -> %3]").arg(r.name).arg(roleTypeName(type)).arg(roleTypeName(r.type)); - return r; - } - - return createRole(key, type); -} - -const ListLayout::Role &ListLayout::getRoleOrCreate(v8::Handle key, Role::DataType type) -{ - QHashedV8String hashedKey(key); - QStringHash::Node *node = roleHash.findNode(hashedKey); - if (node) { - const Role &r = *node->value; - if (type != r.type) - qmlInfo(0) << QString::fromLatin1("Can't assign to existing role '%1' of different type [%2 -> %3]").arg(r.name).arg(roleTypeName(type)).arg(roleTypeName(r.type)); - return r; - } - - QString qkey; - qkey.resize(key->Length()); - key->Write(reinterpret_cast(qkey.data())); - - return createRole(qkey, type); -} - -const ListLayout::Role &ListLayout::createRole(const QString &key, ListLayout::Role::DataType type) -{ - const int dataSizes[] = { sizeof(QString), sizeof(double), sizeof(bool), sizeof(ListModel *), sizeof(QQmlGuard), sizeof(QVariantMap), sizeof(QDateTime) }; - const int dataAlignments[] = { sizeof(QString), sizeof(double), sizeof(bool), sizeof(ListModel *), sizeof(QObject *), sizeof(QVariantMap), sizeof(QDateTime) }; - - Role *r = new Role; - r->name = key; - r->type = type; - - if (type == Role::List) { - r->subLayout = new ListLayout; - } else { - r->subLayout = 0; - } - - int dataSize = dataSizes[type]; - int dataAlignment = dataAlignments[type]; - - int dataOffset = (currentBlockOffset + dataAlignment-1) & ~(dataAlignment-1); - if (dataOffset + dataSize > ListElement::BLOCK_SIZE) { - r->blockIndex = ++currentBlock; - r->blockOffset = 0; - currentBlockOffset = dataSize; - } else { - r->blockIndex = currentBlock; - r->blockOffset = dataOffset; - currentBlockOffset = dataOffset + dataSize; - } - - int roleIndex = roles.count(); - r->index = roleIndex; - - roles.append(r); - roleHash.insert(key, r); - - return *r; -} - -ListLayout::ListLayout(const ListLayout *other) : currentBlock(0), currentBlockOffset(0) -{ - for (int i=0 ; i < other->roles.count() ; ++i) { - Role *role = new Role(other->roles[i]); - roles.append(role); - roleHash.insert(role->name, role); - } - currentBlockOffset = other->currentBlockOffset; - currentBlock = other->currentBlock; -} - -ListLayout::~ListLayout() -{ - for (int i=0 ; i < roles.count() ; ++i) { - delete roles[i]; - } -} - -void ListLayout::sync(ListLayout *src, ListLayout *target) -{ - int roleOffset = target->roles.count(); - int newRoleCount = src->roles.count() - roleOffset; - - for (int i=0 ; i < newRoleCount ; ++i) { - Role *role = new Role(src->roles[roleOffset + i]); - target->roles.append(role); - target->roleHash.insert(role->name, role); - } - - target->currentBlockOffset = src->currentBlockOffset; - target->currentBlock = src->currentBlock; -} - -ListLayout::Role::Role(const Role *other) -{ - name = other->name; - type = other->type; - blockIndex = other->blockIndex; - blockOffset = other->blockOffset; - index = other->index; - if (other->subLayout) - subLayout = new ListLayout(other->subLayout); - else - subLayout = 0; -} - -ListLayout::Role::~Role() -{ - delete subLayout; -} - -const ListLayout::Role *ListLayout::getRoleOrCreate(const QString &key, const QVariant &data) -{ - Role::DataType type; - - switch (data.type()) { - case QVariant::Double: type = Role::Number; break; - case QVariant::Int: type = Role::Number; break; - case QVariant::UserType: type = Role::List; break; - case QVariant::Bool: type = Role::Bool; break; - case QVariant::String: type = Role::String; break; - case QVariant::Map: type = Role::VariantMap; break; - default: type = Role::Invalid; break; - } - - if (type == Role::Invalid) { - qmlInfo(0) << "Can't create role for unsupported data type"; - return 0; - } - - return &getRoleOrCreate(key, type); -} - -const ListLayout::Role *ListLayout::getExistingRole(const QString &key) -{ - Role *r = 0; - QStringHash::Node *node = roleHash.findNode(key); - if (node) - r = node->value; - return r; -} - -const ListLayout::Role *ListLayout::getExistingRole(v8::Handle key) -{ - Role *r = 0; - QHashedV8String hashedKey(key); - QStringHash::Node *node = roleHash.findNode(hashedKey); - if (node) - r = node->value; - return r; -} - -ModelObject *ListModel::getOrCreateModelObject(QQuickListModel *model, int elementIndex) -{ - ListElement *e = elements[elementIndex]; - if (e->m_objectCache == 0) { - e->m_objectCache = new ModelObject(model, elementIndex); - } - return e->m_objectCache; -} - -void ListModel::sync(ListModel *src, ListModel *target, QHash *targetModelHash) -{ - // Sanity check - target->m_uid = src->m_uid; - if (targetModelHash) - targetModelHash->insert(target->m_uid, target); - - // Build hash of elements <-> uid for each of the lists - QHash elementHash; - for (int i=0 ; i < target->elements.count() ; ++i) { - ListElement *e = target->elements.at(i); - int uid = e->getUid(); - ElementSync sync; - sync.target = e; - elementHash.insert(uid, sync); - } - for (int i=0 ; i < src->elements.count() ; ++i) { - ListElement *e = src->elements.at(i); - int uid = e->getUid(); - - QHash::iterator it = elementHash.find(uid); - if (it == elementHash.end()) { - ElementSync sync; - sync.src = e; - elementHash.insert(uid, sync); - } else { - ElementSync &sync = it.value(); - sync.src = e; - } - } - - // Get list of elements that are in the target but no longer in the source. These get deleted first. - QHash::iterator it = elementHash.begin(); - QHash::iterator end = elementHash.end(); - while (it != end) { - const ElementSync &s = it.value(); - if (s.src == 0) { - s.target->destroy(target->m_layout); - target->elements.removeOne(s.target); - delete s.target; - } - ++it; - } - - // Sync the layouts - ListLayout::sync(src->m_layout, target->m_layout); - - // Clear the target list, and append in correct order from the source - target->elements.clear(); - for (int i=0 ; i < src->elements.count() ; ++i) { - ListElement *srcElement = src->elements.at(i); - it = elementHash.find(srcElement->getUid()); - const ElementSync &s = it.value(); - ListElement *targetElement = s.target; - if (targetElement == 0) { - targetElement = new ListElement(srcElement->getUid()); - } - ListElement::sync(srcElement, src->m_layout, targetElement, target->m_layout, targetModelHash); - target->elements.append(targetElement); - } - - target->updateCacheIndices(); - - // Update values stored in target meta objects - for (int i=0 ; i < target->elements.count() ; ++i) { - ListElement *e = target->elements[i]; - if (e->m_objectCache) - e->m_objectCache->updateValues(); - } -} - -ListModel::ListModel(ListLayout *layout, QQuickListModel *modelCache, int uid) : m_layout(layout), m_modelCache(modelCache) -{ - if (uid == -1) - uid = uidCounter.fetchAndAddOrdered(1); - m_uid = uid; -} - -void ListModel::destroy() -{ - clear(); - m_uid = -1; - m_layout = 0; - if (m_modelCache && m_modelCache->m_primary == false) - delete m_modelCache; - m_modelCache = 0; -} - -int ListModel::appendElement() -{ - int elementIndex = elements.count(); - newElement(elementIndex); - return elementIndex; -} - -void ListModel::insertElement(int index) -{ - newElement(index); - updateCacheIndices(); -} - -void ListModel::move(int from, int to, int n) -{ - if (from > to) { - // Only move forwards - flip if backwards moving - int tfrom = from; - int tto = to; - from = tto; - to = tto+n; - n = tfrom-tto; - } - - QPODVector store; - for (int i=0 ; i < (to-from) ; ++i) - store.append(elements[from+n+i]); - for (int i=0 ; i < n ; ++i) - store.append(elements[from+i]); - for (int i=0 ; i < store.count() ; ++i) - elements[from+i] = store[i]; - - updateCacheIndices(); -} - -void ListModel::newElement(int index) -{ - ListElement *e = new ListElement; - elements.insert(index, e); -} - -void ListModel::updateCacheIndices() -{ - for (int i=0 ; i < elements.count() ; ++i) { - ListElement *e = elements.at(i); - if (e->m_objectCache) { - e->m_objectCache->m_elementIndex = i; - } - } -} - -QVariant ListModel::getProperty(int elementIndex, int roleIndex, const QQuickListModel *owner, QV8Engine *eng) -{ - ListElement *e = elements[elementIndex]; - const ListLayout::Role &r = m_layout->getExistingRole(roleIndex); - return e->getProperty(r, owner, eng); -} - -ListModel *ListModel::getListProperty(int elementIndex, const ListLayout::Role &role) -{ - ListElement *e = elements[elementIndex]; - return e->getListProperty(role); -} - -void ListModel::set(int elementIndex, v8::Handle object, QVector *roles, QV8Engine *eng) -{ - ListElement *e = elements[elementIndex]; - - v8::Local propertyNames = object->GetPropertyNames(); - int propertyCount = propertyNames->Length(); - - for (int i=0 ; i < propertyCount ; ++i) { - v8::Local propertyName = propertyNames->Get(i)->ToString(); - v8::Local propertyValue = object->Get(propertyName); - - // Check if this key exists yet - int roleIndex = -1; - - // Add the value now - if (propertyValue->IsString()) { - const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::String); - v8::Handle jsString = propertyValue->ToString(); - QString qstr; - qstr.resize(jsString->Length()); - jsString->Write(reinterpret_cast(qstr.data())); - roleIndex = e->setStringProperty(r, qstr); - } else if (propertyValue->IsNumber()) { - const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Number); - roleIndex = e->setDoubleProperty(r, propertyValue->NumberValue()); - } else if (propertyValue->IsArray()) { - const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::List); - ListModel *subModel = new ListModel(r.subLayout, 0, -1); - - v8::Handle subArray = v8::Handle::Cast(propertyValue); - int arrayLength = subArray->Length(); - for (int j=0 ; j < arrayLength ; ++j) { - v8::Handle subObject = subArray->Get(j)->ToObject(); - subModel->append(subObject, eng); - } - - roleIndex = e->setListProperty(r, subModel); - } else if (propertyValue->IsBoolean()) { - const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Bool); - roleIndex = e->setBoolProperty(r, propertyValue->BooleanValue()); - } else if (propertyValue->IsDate()) { - const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::DateTime); - QDateTime dt = QV8Engine::qtDateTimeFromJsDate(v8::Handle::Cast(propertyValue)->NumberValue()); - roleIndex = e->setDateTimeProperty(r, dt); - } else if (propertyValue->IsObject()) { - QV8ObjectResource *r = (QV8ObjectResource *) propertyValue->ToObject()->GetExternalResource(); - if (r && r->resourceType() == QV8ObjectResource::QObjectType) { - QObject *o = QV8QObjectWrapper::toQObject(r); - const ListLayout::Role &role = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::QObject); - if (role.type == ListLayout::Role::QObject) - roleIndex = e->setQObjectProperty(role, o); - } else { - const ListLayout::Role &role = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::VariantMap); - if (role.type == ListLayout::Role::VariantMap) - roleIndex = e->setVariantMapProperty(role, propertyValue->ToObject(), eng); - } - } else if (propertyValue.IsEmpty() || propertyValue->IsUndefined() || propertyValue->IsNull()) { - const ListLayout::Role *r = m_layout->getExistingRole(propertyName); - if (r) - e->clearProperty(*r); - } - - if (roleIndex != -1) - roles->append(roleIndex); - } - - if (e->m_objectCache) { - e->m_objectCache->updateValues(*roles); - } -} - -void ListModel::set(int elementIndex, v8::Handle object, QV8Engine *eng) -{ - ListElement *e = elements[elementIndex]; - - v8::Local propertyNames = object->GetPropertyNames(); - int propertyCount = propertyNames->Length(); - - for (int i=0 ; i < propertyCount ; ++i) { - v8::Local propertyName = propertyNames->Get(i)->ToString(); - v8::Local propertyValue = object->Get(propertyName); - - // Add the value now - if (propertyValue->IsString()) { - const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::String); - if (r.type == ListLayout::Role::String) { - v8::Handle jsString = propertyValue->ToString(); - QString qstr; - qstr.resize(jsString->Length()); - jsString->Write(reinterpret_cast(qstr.data())); - e->setStringPropertyFast(r, qstr); - } - } else if (propertyValue->IsNumber()) { - const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Number); - if (r.type == ListLayout::Role::Number) { - e->setDoublePropertyFast(r, propertyValue->NumberValue()); - } - } else if (propertyValue->IsArray()) { - const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::List); - if (r.type == ListLayout::Role::List) { - ListModel *subModel = new ListModel(r.subLayout, 0, -1); - - v8::Handle subArray = v8::Handle::Cast(propertyValue); - int arrayLength = subArray->Length(); - for (int j=0 ; j < arrayLength ; ++j) { - v8::Handle subObject = subArray->Get(j)->ToObject(); - subModel->append(subObject, eng); - } - - e->setListPropertyFast(r, subModel); - } - } else if (propertyValue->IsBoolean()) { - const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Bool); - if (r.type == ListLayout::Role::Bool) { - e->setBoolPropertyFast(r, propertyValue->BooleanValue()); - } - } else if (propertyValue->IsDate()) { - const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::DateTime); - if (r.type == ListLayout::Role::DateTime) { - QDateTime dt = QV8Engine::qtDateTimeFromJsDate(v8::Handle::Cast(propertyValue)->NumberValue()); - e->setDateTimePropertyFast(r, dt); - } - } else if (propertyValue->IsObject()) { - QV8ObjectResource *r = (QV8ObjectResource *) propertyValue->ToObject()->GetExternalResource(); - if (r && r->resourceType() == QV8ObjectResource::QObjectType) { - QObject *o = QV8QObjectWrapper::toQObject(r); - const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::QObject); - if (r.type == ListLayout::Role::QObject) - e->setQObjectPropertyFast(r, o); - } else { - const ListLayout::Role &role = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::VariantMap); - if (role.type == ListLayout::Role::VariantMap) - e->setVariantMapFast(role, propertyValue->ToObject(), eng); - } - } else if (propertyValue.IsEmpty() || propertyValue->IsUndefined() || propertyValue->IsNull()) { - const ListLayout::Role *r = m_layout->getExistingRole(propertyName); - if (r) - e->clearProperty(*r); - } - } -} - -void ListModel::clear() -{ - int elementCount = elements.count(); - for (int i=0 ; i < elementCount ; ++i) { - elements[i]->destroy(m_layout); - delete elements[i]; - } - elements.clear(); -} - -void ListModel::remove(int index, int count) -{ - for (int i=0 ; i < count ; ++i) { - elements[index+i]->destroy(m_layout); - delete elements[index+i]; - } - elements.remove(index, count); - updateCacheIndices(); -} - -void ListModel::insert(int elementIndex, v8::Handle object, QV8Engine *eng) -{ - insertElement(elementIndex); - set(elementIndex, object, eng); -} - -int ListModel::append(v8::Handle object, QV8Engine *eng) -{ - int elementIndex = appendElement(); - set(elementIndex, object, eng); - return elementIndex; -} - -int ListModel::setOrCreateProperty(int elementIndex, const QString &key, const QVariant &data) -{ - int roleIndex = -1; - - if (elementIndex >= 0 && elementIndex < elements.count()) { - ListElement *e = elements[elementIndex]; - - const ListLayout::Role *r = m_layout->getRoleOrCreate(key, data); - if (r) { - roleIndex = e->setVariantProperty(*r, data); - - if (roleIndex != -1 && e->m_objectCache) { - QVector roles; - roles << roleIndex; - e->m_objectCache->updateValues(roles); - } - } - } - - return roleIndex; -} - -int ListModel::setExistingProperty(int elementIndex, const QString &key, v8::Handle data, QV8Engine *eng) -{ - int roleIndex = -1; - - if (elementIndex >= 0 && elementIndex < elements.count()) { - ListElement *e = elements[elementIndex]; - const ListLayout::Role *r = m_layout->getExistingRole(key); - if (r) - roleIndex = e->setJsProperty(*r, data, eng); - } - - return roleIndex; -} - -inline char *ListElement::getPropertyMemory(const ListLayout::Role &role) -{ - ListElement *e = this; - int blockIndex = 0; - while (blockIndex < role.blockIndex) { - if (e->next == 0) { - e->next = new ListElement; - e->next->uid = uid; - } - e = e->next; - ++blockIndex; - } - - char *mem = &e->data[role.blockOffset]; - return mem; -} - -QString *ListElement::getStringProperty(const ListLayout::Role &role) -{ - char *mem = getPropertyMemory(role); - QString *s = reinterpret_cast(mem); - return s->data_ptr() ? s : 0; -} - -QObject *ListElement::getQObjectProperty(const ListLayout::Role &role) -{ - char *mem = getPropertyMemory(role); - QQmlGuard *o = reinterpret_cast *>(mem); - return o->data(); -} - -QVariantMap *ListElement::getVariantMapProperty(const ListLayout::Role &role) -{ - QVariantMap *map = 0; - - char *mem = getPropertyMemory(role); - if (isMemoryUsed(mem)) - map = reinterpret_cast(mem); - - return map; -} - -QDateTime *ListElement::getDateTimeProperty(const ListLayout::Role &role) -{ - QDateTime *dt = 0; - - char *mem = getPropertyMemory(role); - if (isMemoryUsed(mem)) - dt = reinterpret_cast(mem); - - return dt; -} - -QQmlGuard *ListElement::getGuardProperty(const ListLayout::Role &role) -{ - char *mem = getPropertyMemory(role); - - bool existingGuard = false; - for (size_t i=0 ; i < sizeof(QQmlGuard) ; ++i) { - if (mem[i] != 0) { - existingGuard = true; - break; - } - } - - QQmlGuard *o = 0; - - if (existingGuard) - o = reinterpret_cast *>(mem); - - return o; -} - -ListModel *ListElement::getListProperty(const ListLayout::Role &role) -{ - char *mem = getPropertyMemory(role); - ListModel **value = reinterpret_cast(mem); - return *value; -} - -QVariant ListElement::getProperty(const ListLayout::Role &role, const QQuickListModel *owner, QV8Engine *eng) -{ - char *mem = getPropertyMemory(role); - - QVariant data; - - switch (role.type) { - case ListLayout::Role::Number: - { - double *value = reinterpret_cast(mem); - data = *value; - } - break; - case ListLayout::Role::String: - { - QString *value = reinterpret_cast(mem); - if (value->data_ptr() != 0) - data = *value; - } - break; - case ListLayout::Role::Bool: - { - bool *value = reinterpret_cast(mem); - data = *value; - } - break; - case ListLayout::Role::List: - { - ListModel **value = reinterpret_cast(mem); - ListModel *model = *value; - - if (model) { - if (model->m_modelCache == 0) { - model->m_modelCache = new QQuickListModel(owner, model, eng); - QQmlEngine::setContextForObject(model->m_modelCache, QQmlEngine::contextForObject(owner)); - } - - QObject *object = model->m_modelCache; - data = QVariant::fromValue(object); - } - } - break; - case ListLayout::Role::QObject: - { - QQmlGuard *guard = reinterpret_cast *>(mem); - QObject *object = guard->data(); - if (object) - data = QVariant::fromValue(object); - } - break; - case ListLayout::Role::VariantMap: - { - if (isMemoryUsed(mem)) { - QVariantMap *map = reinterpret_cast(mem); - data = *map; - } - } - break; - case ListLayout::Role::DateTime: - { - if (isMemoryUsed(mem)) { - QDateTime *dt = reinterpret_cast(mem); - data = *dt; - } - } - break; - default: - break; - } - - return data; -} - -int ListElement::setStringProperty(const ListLayout::Role &role, const QString &s) -{ - int roleIndex = -1; - - if (role.type == ListLayout::Role::String) { - char *mem = getPropertyMemory(role); - QString *c = reinterpret_cast(mem); - bool changed; - if (c->data_ptr() == 0) { - new (mem) QString(s); - changed = true; - } else { - changed = c->compare(s) != 0; - *c = s; - } - if (changed) - roleIndex = role.index; - } - - return roleIndex; -} - -int ListElement::setDoubleProperty(const ListLayout::Role &role, double d) -{ - int roleIndex = -1; - - if (role.type == ListLayout::Role::Number) { - char *mem = getPropertyMemory(role); - double *value = new (mem) double; - bool changed = *value != d; - *value = d; - if (changed) - roleIndex = role.index; - } - - return roleIndex; -} - -int ListElement::setBoolProperty(const ListLayout::Role &role, bool b) -{ - int roleIndex = -1; - - if (role.type == ListLayout::Role::Bool) { - char *mem = getPropertyMemory(role); - bool *value = new (mem) bool; - bool changed = *value != b; - *value = b; - if (changed) - roleIndex = role.index; - } - - return roleIndex; -} - -int ListElement::setListProperty(const ListLayout::Role &role, ListModel *m) -{ - int roleIndex = -1; - - if (role.type == ListLayout::Role::List) { - char *mem = getPropertyMemory(role); - ListModel **value = new (mem) ListModel *; - if (*value) { - (*value)->destroy(); - delete *value; - } - *value = m; - roleIndex = role.index; - } - - return roleIndex; -} - -int ListElement::setQObjectProperty(const ListLayout::Role &role, QObject *o) -{ - int roleIndex = -1; - - if (role.type == ListLayout::Role::QObject) { - char *mem = getPropertyMemory(role); - QQmlGuard *g = reinterpret_cast *>(mem); - bool existingGuard = false; - for (size_t i=0 ; i < sizeof(QQmlGuard) ; ++i) { - if (mem[i] != 0) { - existingGuard = true; - break; - } - } - bool changed; - if (existingGuard) { - changed = g->data() != o; - g->~QQmlGuard(); - } else { - changed = true; - } - new (mem) QQmlGuard(o); - if (changed) - roleIndex = role.index; - } - - return roleIndex; -} - -int ListElement::setVariantMapProperty(const ListLayout::Role &role, v8::Handle o, QV8Engine *eng) -{ - int roleIndex = -1; - - if (role.type == ListLayout::Role::VariantMap) { - char *mem = getPropertyMemory(role); - if (isMemoryUsed(mem)) { - QVariantMap *map = reinterpret_cast(mem); - map->~QMap(); - } - new (mem) QVariantMap(eng->variantMapFromJS(o)); - roleIndex = role.index; - } - - return roleIndex; -} - -int ListElement::setVariantMapProperty(const ListLayout::Role &role, QVariantMap *m) -{ - int roleIndex = -1; - - if (role.type == ListLayout::Role::VariantMap) { - char *mem = getPropertyMemory(role); - if (isMemoryUsed(mem)) { - QVariantMap *map = reinterpret_cast(mem); - map->~QMap(); - } - if (m) - new (mem) QVariantMap(*m); - else - new (mem) QVariantMap; - roleIndex = role.index; - } - - return roleIndex; -} - -int ListElement::setDateTimeProperty(const ListLayout::Role &role, const QDateTime &dt) -{ - int roleIndex = -1; - - if (role.type == ListLayout::Role::DateTime) { - char *mem = getPropertyMemory(role); - if (isMemoryUsed(mem)) { - QDateTime *dt = reinterpret_cast(mem); - dt->~QDateTime(); - } - new (mem) QDateTime(dt); - roleIndex = role.index; - } - - return roleIndex; -} - -void ListElement::setStringPropertyFast(const ListLayout::Role &role, const QString &s) -{ - char *mem = getPropertyMemory(role); - new (mem) QString(s); -} - -void ListElement::setDoublePropertyFast(const ListLayout::Role &role, double d) -{ - char *mem = getPropertyMemory(role); - double *value = new (mem) double; - *value = d; -} - -void ListElement::setBoolPropertyFast(const ListLayout::Role &role, bool b) -{ - char *mem = getPropertyMemory(role); - bool *value = new (mem) bool; - *value = b; -} - -void ListElement::setQObjectPropertyFast(const ListLayout::Role &role, QObject *o) -{ - char *mem = getPropertyMemory(role); - new (mem) QQmlGuard(o); -} - -void ListElement::setListPropertyFast(const ListLayout::Role &role, ListModel *m) -{ - char *mem = getPropertyMemory(role); - ListModel **value = new (mem) ListModel *; - *value = m; -} - -void ListElement::setVariantMapFast(const ListLayout::Role &role, v8::Handle o, QV8Engine *eng) -{ - char *mem = getPropertyMemory(role); - QVariantMap *map = new (mem) QVariantMap; - *map = eng->variantMapFromJS(o); -} - -void ListElement::setDateTimePropertyFast(const ListLayout::Role &role, const QDateTime &dt) -{ - char *mem = getPropertyMemory(role); - new (mem) QDateTime(dt); -} - -void ListElement::clearProperty(const ListLayout::Role &role) -{ - switch (role.type) { - case ListLayout::Role::String: - setStringProperty(role, QString()); - break; - case ListLayout::Role::Number: - setDoubleProperty(role, 0.0); - break; - case ListLayout::Role::Bool: - setBoolProperty(role, false); - break; - case ListLayout::Role::List: - setListProperty(role, 0); - break; - case ListLayout::Role::QObject: - setQObjectProperty(role, 0); - break; - case ListLayout::Role::DateTime: - setDateTimeProperty(role, QDateTime()); - break; - case ListLayout::Role::VariantMap: - setVariantMapProperty(role, 0); - break; - default: - break; - } -} - -ListElement::ListElement() -{ - m_objectCache = 0; - uid = uidCounter.fetchAndAddOrdered(1); - next = 0; - memset(data, 0, sizeof(data)); -} - -ListElement::ListElement(int existingUid) -{ - m_objectCache = 0; - uid = existingUid; - next = 0; - memset(data, 0, sizeof(data)); -} - -ListElement::~ListElement() -{ - delete next; -} - -void ListElement::sync(ListElement *src, ListLayout *srcLayout, ListElement *target, ListLayout *targetLayout, QHash *targetModelHash) -{ - for (int i=0 ; i < srcLayout->roleCount() ; ++i) { - const ListLayout::Role &srcRole = srcLayout->getExistingRole(i); - const ListLayout::Role &targetRole = targetLayout->getExistingRole(i); - - switch (srcRole.type) { - case ListLayout::Role::List: - { - ListModel *srcSubModel = src->getListProperty(srcRole); - ListModel *targetSubModel = target->getListProperty(targetRole); - - if (srcSubModel) { - if (targetSubModel == 0) { - targetSubModel = new ListModel(targetRole.subLayout, 0, srcSubModel->getUid()); - target->setListPropertyFast(targetRole, targetSubModel); - } - ListModel::sync(srcSubModel, targetSubModel, targetModelHash); - } - } - break; - case ListLayout::Role::QObject: - { - QObject *object = src->getQObjectProperty(srcRole); - target->setQObjectProperty(targetRole, object); - } - break; - case ListLayout::Role::String: - case ListLayout::Role::Number: - case ListLayout::Role::Bool: - case ListLayout::Role::DateTime: - { - QVariant v = src->getProperty(srcRole, 0, 0); - target->setVariantProperty(targetRole, v); - } - case ListLayout::Role::VariantMap: - { - QVariantMap *map = src->getVariantMapProperty(srcRole); - target->setVariantMapProperty(targetRole, map); - } - break; - default: - break; - } - } - -} - -void ListElement::destroy(ListLayout *layout) -{ - if (layout) { - for (int i=0 ; i < layout->roleCount() ; ++i) { - const ListLayout::Role &r = layout->getExistingRole(i); - - switch (r.type) { - case ListLayout::Role::String: - { - QString *string = getStringProperty(r); - if (string) - string->~QString(); - } - break; - case ListLayout::Role::List: - { - ListModel *model = getListProperty(r); - if (model) { - model->destroy(); - delete model; - } - } - break; - case ListLayout::Role::QObject: - { - QQmlGuard *guard = getGuardProperty(r); - if (guard) - guard->~QQmlGuard(); - } - break; - case ListLayout::Role::VariantMap: - { - QVariantMap *map = getVariantMapProperty(r); - if (map) - map->~QMap(); - } - break; - case ListLayout::Role::DateTime: - { - QDateTime *dt = getDateTimeProperty(r); - if (dt) - dt->~QDateTime(); - } - break; - default: - // other types don't need explicit cleanup. - break; - } - } - - delete m_objectCache; - } - - if (next) - next->destroy(0); - uid = -1; -} - -int ListElement::setVariantProperty(const ListLayout::Role &role, const QVariant &d) -{ - int roleIndex = -1; - - switch (role.type) { - case ListLayout::Role::Number: - roleIndex = setDoubleProperty(role, d.toDouble()); - break; - case ListLayout::Role::String: - roleIndex = setStringProperty(role, d.toString()); - break; - case ListLayout::Role::Bool: - roleIndex = setBoolProperty(role, d.toBool()); - break; - case ListLayout::Role::List: - roleIndex = setListProperty(role, d.value()); - break; - case ListLayout::Role::VariantMap: { - QVariantMap map = d.toMap(); - roleIndex = setVariantMapProperty(role, &map); - } - break; - case ListLayout::Role::DateTime: - roleIndex = setDateTimeProperty(role, d.toDateTime()); - break; - default: - break; - } - - return roleIndex; -} - -int ListElement::setJsProperty(const ListLayout::Role &role, v8::Handle d, QV8Engine *eng) -{ - // Check if this key exists yet - int roleIndex = -1; - - // Add the value now - if (d->IsString()) { - v8::Handle jsString = d->ToString(); - QString qstr; - qstr.resize(jsString->Length()); - jsString->Write(reinterpret_cast(qstr.data())); - roleIndex = setStringProperty(role, qstr); - } else if (d->IsNumber()) { - roleIndex = setDoubleProperty(role, d->NumberValue()); - } else if (d->IsArray()) { - if (role.type == ListLayout::Role::List) { - ListModel *subModel = new ListModel(role.subLayout, 0, -1); - v8::Handle subArray = v8::Handle::Cast(d); - int arrayLength = subArray->Length(); - for (int j=0 ; j < arrayLength ; ++j) { - v8::Handle subObject = subArray->Get(j)->ToObject(); - subModel->append(subObject, eng); - } - roleIndex = setListProperty(role, subModel); - } else { - qmlInfo(0) << QString::fromLatin1("Can't assign to existing role '%1' of different type [%2 -> %3]").arg(role.name).arg(roleTypeName(role.type)).arg(roleTypeName(ListLayout::Role::List)); - } - } else if (d->IsBoolean()) { - roleIndex = setBoolProperty(role, d->BooleanValue()); - } else if (d->IsDate()) { - QDateTime dt = QV8Engine::qtDateTimeFromJsDate(v8::Handle::Cast(d)->NumberValue()); - roleIndex = setDateTimeProperty(role, dt); - } else if (d->IsObject()) { - QV8ObjectResource *r = (QV8ObjectResource *) d->ToObject()->GetExternalResource(); - if (role.type == ListLayout::Role::QObject && r && r->resourceType() == QV8ObjectResource::QObjectType) { - QObject *o = QV8QObjectWrapper::toQObject(r); - roleIndex = setQObjectProperty(role, o); - } else if (role.type == ListLayout::Role::VariantMap) { - roleIndex = setVariantMapProperty(role, d->ToObject(), eng); - } - } else if (d.IsEmpty() || d->IsUndefined() || d->IsNull()) { - clearProperty(role); - } - - return roleIndex; -} - -ModelObject::ModelObject(QQuickListModel *model, int elementIndex) -: m_model(model), m_elementIndex(elementIndex), m_meta(new ModelNodeMetaObject(this)) -{ - updateValues(); - setNodeUpdatesEnabled(true); -} - -void ModelObject::updateValues() -{ - int roleCount = m_model->m_listModel->roleCount(); - for (int i=0 ; i < roleCount ; ++i) { - const ListLayout::Role &role = m_model->m_listModel->getExistingRole(i); - QByteArray name = role.name.toUtf8(); - const QVariant &data = m_model->data(m_elementIndex, i); - setValue(name, data, role.type == ListLayout::Role::List); - } -} - -void ModelObject::updateValues(const QVector &roles) -{ - int roleCount = roles.count(); - for (int i=0 ; i < roleCount ; ++i) { - int roleIndex = roles.at(i); - const ListLayout::Role &role = m_model->m_listModel->getExistingRole(roleIndex); - QByteArray name = role.name.toUtf8(); - const QVariant &data = m_model->data(m_elementIndex, roleIndex); - setValue(name, data, role.type == ListLayout::Role::List); - } -} - -ModelNodeMetaObject::ModelNodeMetaObject(ModelObject *object) -: QQmlOpenMetaObject(object), m_enabled(false), m_obj(object) -{ -} - -ModelNodeMetaObject::~ModelNodeMetaObject() -{ -} - -void ModelNodeMetaObject::propertyWritten(int index) -{ - if (!m_enabled) - return; - - QV8Engine *eng = m_obj->m_model->engine(); - - QString propName = QString::fromUtf8(name(index)); - QVariant value = operator[](index); - - v8::HandleScope handle_scope; - v8::Context::Scope scope(eng->context()); - - v8::Handle v = eng->fromVariant(value); - - int roleIndex = m_obj->m_model->m_listModel->setExistingProperty(m_obj->m_elementIndex, propName, v, eng); - if (roleIndex != -1) { - QVector roles; - roles << roleIndex; - m_obj->m_model->emitItemsChanged(m_obj->m_elementIndex, 1, roles); - } -} - -DynamicRoleModelNode::DynamicRoleModelNode(QQuickListModel *owner, int uid) : m_owner(owner), m_uid(uid), m_meta(new DynamicRoleModelNodeMetaObject(this)) -{ - setNodeUpdatesEnabled(true); -} - -DynamicRoleModelNode *DynamicRoleModelNode::create(const QVariantMap &obj, QQuickListModel *owner) -{ - DynamicRoleModelNode *object = new DynamicRoleModelNode(owner, uidCounter.fetchAndAddOrdered(1)); - QVector roles; - object->updateValues(obj, roles); - return object; -} - -void DynamicRoleModelNode::sync(DynamicRoleModelNode *src, DynamicRoleModelNode *target, QHash *targetModelHash) -{ - for (int i=0 ; i < src->m_meta->count() ; ++i) { - const QByteArray &name = src->m_meta->name(i); - QVariant value = src->m_meta->value(i); - - QQuickListModel *srcModel = qobject_cast(value.value()); - QQuickListModel *targetModel = qobject_cast(target->m_meta->value(i).value()); - - if (srcModel) { - if (targetModel == 0) - targetModel = QQuickListModel::createWithOwner(target->m_owner); - - QQuickListModel::sync(srcModel, targetModel, targetModelHash); - - QObject *targetModelObject = targetModel; - value = QVariant::fromValue(targetModelObject); - } else if (targetModel) { - delete targetModel; - } - - target->setValue(name, value); - } -} - -void DynamicRoleModelNode::updateValues(const QVariantMap &object, QVector &roles) -{ - const QList &keys = object.keys(); - - QList::const_iterator it = keys.begin(); - QList::const_iterator end = keys.end(); - - while (it != end) { - const QString &key = *it; - - int roleIndex = m_owner->m_roles.indexOf(key); - if (roleIndex == -1) { - roleIndex = m_owner->m_roles.count(); - m_owner->m_roles.append(key); - } - - QVariant value = object[key]; - - if (value.type() == QVariant::List) { - QQuickListModel *subModel = QQuickListModel::createWithOwner(m_owner); - - QVariantList subArray = value.toList(); - QVariantList::const_iterator subIt = subArray.begin(); - QVariantList::const_iterator subEnd = subArray.end(); - while (subIt != subEnd) { - const QVariantMap &subObject = subIt->toMap(); - subModel->m_modelObjects.append(DynamicRoleModelNode::create(subObject, subModel)); - ++subIt; - } - - QObject *subModelObject = subModel; - value = QVariant::fromValue(subModelObject); - } - - const QByteArray &keyUtf8 = key.toUtf8(); - - QQuickListModel *existingModel = qobject_cast(m_meta->value(keyUtf8).value()); - if (existingModel) - delete existingModel; - - if (m_meta->setValue(keyUtf8, value)) - roles << roleIndex; - - ++it; - } -} - -DynamicRoleModelNodeMetaObject::DynamicRoleModelNodeMetaObject(DynamicRoleModelNode *object) - : QQmlOpenMetaObject(object), m_enabled(false), m_owner(object) -{ -} - -DynamicRoleModelNodeMetaObject::~DynamicRoleModelNodeMetaObject() -{ - for (int i=0 ; i < count() ; ++i) { - QQuickListModel *subModel = qobject_cast(value(i).value()); - if (subModel) - delete subModel; - } -} - -void DynamicRoleModelNodeMetaObject::propertyWrite(int index) -{ - if (!m_enabled) - return; - - QVariant v = value(index); - QQuickListModel *model = qobject_cast(v.value()); - if (model) - delete model; -} - -void DynamicRoleModelNodeMetaObject::propertyWritten(int index) -{ - if (!m_enabled) - return; - - QQuickListModel *parentModel = m_owner->m_owner; - - QVariant v = value(index); - if (v.type() == QVariant::List) { - QQuickListModel *subModel = QQuickListModel::createWithOwner(parentModel); - - QVariantList subArray = v.toList(); - QVariantList::const_iterator subIt = subArray.begin(); - QVariantList::const_iterator subEnd = subArray.end(); - while (subIt != subEnd) { - const QVariantMap &subObject = subIt->toMap(); - subModel->m_modelObjects.append(DynamicRoleModelNode::create(subObject, subModel)); - ++subIt; - } - - QObject *subModelObject = subModel; - v = QVariant::fromValue(subModelObject); - - setValue(index, v); - } - - int elementIndex = parentModel->m_modelObjects.indexOf(m_owner); - int roleIndex = parentModel->m_roles.indexOf(QString::fromLatin1(name(index).constData())); - - if (elementIndex != -1 && roleIndex != -1) { - - QVector roles; - roles << roleIndex; - - parentModel->emitItemsChanged(elementIndex, 1, roles); - } -} - -QQuickListModelParser::ListInstruction *QQuickListModelParser::ListModelData::instructions() const -{ - return (QQuickListModelParser::ListInstruction *)((char *)this + sizeof(ListModelData)); -} - -/*! - \qmltype ListModel - \instantiates QQuickListModel - \inqmlmodule QtQuick 2 - \brief Defines a free-form list data source - \ingroup qtquick-models - - The ListModel is a simple container of ListElement definitions, each containing data roles. - The contents can be defined dynamically, or explicitly in QML. - - The number of elements in the model can be obtained from its \l count property. - A number of familiar methods are also provided to manipulate the contents of the - model, including append(), insert(), move(), remove() and set(). These methods - accept dictionaries as their arguments; these are translated to ListElement objects - by the model. - - Elements can be manipulated via the model using the setProperty() method, which - allows the roles of the specified element to be set and changed. - - \section1 Example Usage - - The following example shows a ListModel containing three elements, with the roles - "name" and "cost". - - \div {class="float-right"} - \inlineimage listmodel.png - \enddiv - - \snippet qml/listmodel/listmodel.qml 0 - - Roles (properties) in each element must begin with a lower-case letter and - should be common to all elements in a model. The ListElement documentation - provides more guidelines for how elements should be defined. - - Since the example model contains an \c id property, it can be referenced - by views, such as the ListView in this example: - - \snippet qml/listmodel/listmodel-simple.qml 0 - \dots 8 - \snippet qml/listmodel/listmodel-simple.qml 1 - - It is possible for roles to contain list data. In the following example we - create a list of fruit attributes: - - \snippet qml/listmodel/listmodel-nested.qml model - - The delegate displays all the fruit attributes: - - \div {class="float-right"} - \inlineimage listmodel-nested.png - \enddiv - - \snippet qml/listmodel/listmodel-nested.qml delegate - - \section1 Modifying List Models - - The content of a ListModel may be created and modified using the clear(), - append(), set(), insert() and setProperty() methods. For example: - - \snippet qml/listmodel/listmodel-modify.qml delegate - - Note that when creating content dynamically the set of available properties - cannot be changed once set. Whatever properties are first added to the model - are the only permitted properties in the model. - - \section1 Using Threaded List Models with WorkerScript - - ListModel can be used together with WorkerScript access a list model - from multiple threads. This is useful if list modifications are - synchronous and take some time: the list operations can be moved to a - different thread to avoid blocking of the main GUI thread. - - Here is an example that uses WorkerScript to periodically append the - current time to a list model: - - \snippet quick/threading/threadedlistmodel/timedisplay.qml 0 - - The included file, \tt dataloader.js, looks like this: - - \snippet quick/threading/threadedlistmodel/dataloader.js 0 - - The timer in the main example sends messages to the worker script by calling - \l WorkerScript::sendMessage(). When this message is received, - \l{WorkerScript::onMessage}{WorkerScript.onMessage()} is invoked in \c dataloader.js, - which appends the current time to the list model. - - Note the call to sync() from the \l{WorkerScript::onMessage}{WorkerScript.onMessage()} - handler. You must call sync() or else the changes made to the list from the external - thread will not be reflected in the list model in the main thread. - - \sa {qml-data-models}{Data Models}, {declarative/threading/threadedlistmodel}{Threaded ListModel example}, QtQml -*/ - -QQuickListModel::QQuickListModel(QObject *parent) -: QAbstractListModel(parent) -{ - m_mainThread = true; - m_primary = true; - m_agent = 0; - m_uid = uidCounter.fetchAndAddOrdered(1); - m_dynamicRoles = false; - - m_layout = new ListLayout; - m_listModel = new ListModel(m_layout, this, -1); - - m_engine = 0; -} - -QQuickListModel::QQuickListModel(const QQuickListModel *owner, ListModel *data, QV8Engine *eng, QObject *parent) -: QAbstractListModel(parent) -{ - m_mainThread = owner->m_mainThread; - m_primary = false; - m_agent = owner->m_agent; - - Q_ASSERT(owner->m_dynamicRoles == false); - m_dynamicRoles = false; - m_layout = 0; - m_listModel = data; - - m_engine = eng; -} - -QQuickListModel::QQuickListModel(QQuickListModel *orig, QQuickListModelWorkerAgent *agent) -: QAbstractListModel(agent) -{ - m_mainThread = false; - m_primary = true; - m_agent = agent; - m_dynamicRoles = orig->m_dynamicRoles; - - m_layout = new ListLayout(orig->m_layout); - m_listModel = new ListModel(m_layout, this, orig->m_listModel->getUid()); - - if (m_dynamicRoles) - sync(orig, this, 0); - else - ListModel::sync(orig->m_listModel, m_listModel, 0); - - m_engine = 0; -} - -QQuickListModel::~QQuickListModel() -{ - for (int i=0 ; i < m_modelObjects.count() ; ++i) - delete m_modelObjects[i]; - - if (m_primary) { - m_listModel->destroy(); - delete m_listModel; - - if (m_mainThread && m_agent) { - m_agent->modelDestroyed(); - m_agent->release(); - } - } - - m_listModel = 0; - - delete m_layout; - m_layout = 0; -} - -QQuickListModel *QQuickListModel::createWithOwner(QQuickListModel *newOwner) -{ - QQuickListModel *model = new QQuickListModel; - - model->m_mainThread = newOwner->m_mainThread; - model->m_engine = newOwner->m_engine; - model->m_agent = newOwner->m_agent; - model->m_dynamicRoles = newOwner->m_dynamicRoles; - - if (model->m_mainThread && model->m_agent) - model->m_agent->addref(); - - QQmlEngine::setContextForObject(model, QQmlEngine::contextForObject(newOwner)); - - return model; -} - -QV8Engine *QQuickListModel::engine() const -{ - if (m_engine == 0) { - m_engine = QQmlEnginePrivate::getV8Engine(qmlEngine(this)); - } - - return m_engine; -} - -void QQuickListModel::sync(QQuickListModel *src, QQuickListModel *target, QHash *targetModelHash) -{ - Q_ASSERT(src->m_dynamicRoles && target->m_dynamicRoles); - - target->m_uid = src->m_uid; - if (targetModelHash) - targetModelHash->insert(target->m_uid, target); - target->m_roles = src->m_roles; - - // Build hash of elements <-> uid for each of the lists - QHash elementHash; - for (int i=0 ; i < target->m_modelObjects.count() ; ++i) { - DynamicRoleModelNode *e = target->m_modelObjects.at(i); - int uid = e->getUid(); - ElementSync sync; - sync.target = e; - elementHash.insert(uid, sync); - } - for (int i=0 ; i < src->m_modelObjects.count() ; ++i) { - DynamicRoleModelNode *e = src->m_modelObjects.at(i); - int uid = e->getUid(); - - QHash::iterator it = elementHash.find(uid); - if (it == elementHash.end()) { - ElementSync sync; - sync.src = e; - elementHash.insert(uid, sync); - } else { - ElementSync &sync = it.value(); - sync.src = e; - } - } - - // Get list of elements that are in the target but no longer in the source. These get deleted first. - QHash::iterator it = elementHash.begin(); - QHash::iterator end = elementHash.end(); - while (it != end) { - const ElementSync &s = it.value(); - if (s.src == 0) { - int targetIndex = target->m_modelObjects.indexOf(s.target); - target->m_modelObjects.remove(targetIndex, 1); - delete s.target; - } - ++it; - } - - // Clear the target list, and append in correct order from the source - target->m_modelObjects.clear(); - for (int i=0 ; i < src->m_modelObjects.count() ; ++i) { - DynamicRoleModelNode *srcElement = src->m_modelObjects.at(i); - it = elementHash.find(srcElement->getUid()); - const ElementSync &s = it.value(); - DynamicRoleModelNode *targetElement = s.target; - if (targetElement == 0) { - targetElement = new DynamicRoleModelNode(target, srcElement->getUid()); - } - DynamicRoleModelNode::sync(srcElement, targetElement, targetModelHash); - target->m_modelObjects.append(targetElement); - } -} - -void QQuickListModel::emitItemsChanged(int index, int count, const QVector &roles) -{ - if (count <= 0) - return; - - if (m_mainThread) { - emit dataChanged(createIndex(index, 0), createIndex(index + count - 1, 0), roles);; - } else { - int uid = m_dynamicRoles ? getUid() : m_listModel->getUid(); - m_agent->data.changedChange(uid, index, count, roles); - } -} - -void QQuickListModel::emitItemsRemoved(int index, int count) -{ - if (count <= 0) - return; - - if (m_mainThread) { - beginRemoveRows(QModelIndex(), index, index + count - 1); - endRemoveRows(); - emit countChanged(); - } else { - int uid = m_dynamicRoles ? getUid() : m_listModel->getUid(); - if (index == 0 && count == this->count()) - m_agent->data.clearChange(uid); - m_agent->data.removeChange(uid, index, count); - } -} - -void QQuickListModel::emitItemsInserted(int index, int count) -{ - if (count <= 0) - return; - - if (m_mainThread) { - beginInsertRows(QModelIndex(), index, index + count - 1); - endInsertRows(); - emit countChanged(); - } else { - int uid = m_dynamicRoles ? getUid() : m_listModel->getUid(); - m_agent->data.insertChange(uid, index, count); - } -} - -void QQuickListModel::emitItemsMoved(int from, int to, int n) -{ - if (n <= 0) - return; - - if (m_mainThread) { - beginMoveRows(QModelIndex(), from, from + n - 1, QModelIndex(), to > from ? to + n : to); - endMoveRows(); - } else { - int uid = m_dynamicRoles ? getUid() : m_listModel->getUid(); - m_agent->data.moveChange(uid, from, n, to); - } -} - -QQuickListModelWorkerAgent *QQuickListModel::agent() -{ - if (m_agent) - return m_agent; - - m_agent = new QQuickListModelWorkerAgent(this); - return m_agent; -} - -QModelIndex QQuickListModel::index(int row, int column, const QModelIndex &parent) const -{ - return row >= 0 && row < count() && column == 0 && !parent.isValid() - ? createIndex(row, column) - : QModelIndex(); -} - -int QQuickListModel::rowCount(const QModelIndex &parent) const -{ - return !parent.isValid() ? count() : 0; -} - -QVariant QQuickListModel::data(const QModelIndex &index, int role) const -{ - return data(index.row(), role); -} - -QVariant QQuickListModel::data(int index, int role) const -{ - QVariant v; - - if (index >= count() || index < 0) - return v; - - if (m_dynamicRoles) - v = m_modelObjects[index]->getValue(m_roles[role]); - else - v = m_listModel->getProperty(index, role, this, engine()); - - return v; -} - -QHash QQuickListModel::roleNames() const -{ - QHash roleNames; - - if (m_dynamicRoles) { - for (int i = 0 ; i < m_roles.count() ; ++i) - roleNames.insert(i, m_roles.at(i).toUtf8()); - } else { - for (int i = 0 ; i < m_listModel->roleCount() ; ++i) { - const ListLayout::Role &r = m_listModel->getExistingRole(i); - roleNames.insert(i, r.name.toUtf8()); - } - } - - return roleNames; -} - -/*! - \qmlproperty bool QtQuick2::ListModel::dynamicRoles - - By default, the type of a role is fixed the first time - the role is used. For example, if you create a role called - "data" and assign a number to it, you can no longer assign - a string to the "data" role. However, when the dynamicRoles - property is enabled, the type of a given role is not fixed - and can be different between elements. - - The dynamicRoles property must be set before any data is - added to the ListModel, and must be set from the main - thread. - - A ListModel that has data statically defined (via the - ListElement QML syntax) cannot have the dynamicRoles - property enabled. - - There is a significant performance cost to using a - ListModel with dynamic roles enabled. The cost varies - from platform to platform but is typically somewhere - between 4-6x slower than using static role types. - - Due to the performance cost of using dynamic roles, - they are disabled by default. -*/ -void QQuickListModel::setDynamicRoles(bool enableDynamicRoles) -{ - if (m_mainThread && m_agent == 0) { - if (enableDynamicRoles) { - if (m_layout->roleCount()) - qmlInfo(this) << tr("unable to enable dynamic roles as this model is not empty!"); - else - m_dynamicRoles = true; - } else { - if (m_roles.count()) { - qmlInfo(this) << tr("unable to enable static roles as this model is not empty!"); - } else { - m_dynamicRoles = false; - } - } - } else { - qmlInfo(this) << tr("dynamic role setting must be made from the main thread, before any worker scripts are created"); - } -} - -/*! - \qmlproperty int QtQuick2::ListModel::count - The number of data entries in the model. -*/ -int QQuickListModel::count() const -{ - int count; - - if (m_dynamicRoles) - count = m_modelObjects.count(); - else { - count = m_listModel->elementCount(); - } - - return count; -} - -/*! - \qmlmethod QtQuick2::ListModel::clear() - - Deletes all content from the model. - - \sa append(), remove() -*/ -void QQuickListModel::clear() -{ - int cleared = count(); - - if (m_dynamicRoles) { - for (int i=0 ; i < m_modelObjects.count() ; ++i) - delete m_modelObjects[i]; - m_modelObjects.clear(); - } else { - m_listModel->clear(); - } - - emitItemsRemoved(0, cleared); -} - -/*! - \qmlmethod QtQuick2::ListModel::remove(int index, int count = 1) - - Deletes the content at \a index from the model. - - \sa clear() -*/ -void QQuickListModel::remove(QQmlV8Function *args) -{ - int argLength = args->Length(); - - if (argLength == 1 || argLength == 2) { - int index = (*args)[0]->Int32Value(); - int removeCount = (argLength == 2 ? ((*args)[1]->Int32Value()) : 1); - - if (index < 0 || index+removeCount > count() || removeCount <= 0) { - qmlInfo(this) << tr("remove: indices [%1 - %2] out of range [0 - %3]").arg(index).arg(index+removeCount).arg(count()); - return; - } - - if (m_dynamicRoles) { - for (int i=0 ; i < removeCount ; ++i) - delete m_modelObjects[index+i]; - m_modelObjects.remove(index, removeCount); - } else { - m_listModel->remove(index, removeCount); - } - - emitItemsRemoved(index, removeCount); - } else { - qmlInfo(this) << tr("remove: incorrect number of arguments"); - } -} - -/*! - \qmlmethod QtQuick2::ListModel::insert(int index, jsobject dict) - - Adds a new item to the list model at position \a index, with the - values in \a dict. - - \code - fruitModel.insert(2, {"cost": 5.95, "name":"Pizza"}) - \endcode - - The \a index must be to an existing item in the list, or one past - the end of the list (equivalent to append). - - \sa set(), append() -*/ - -void QQuickListModel::insert(QQmlV8Function *args) -{ - if (args->Length() == 2) { - - v8::Handle arg0 = (*args)[0]; - int index = arg0->Int32Value(); - - if (index < 0 || index > count()) { - qmlInfo(this) << tr("insert: index %1 out of range").arg(index); - return; - } - - v8::Handle arg1 = (*args)[1]; - - if (arg1->IsArray()) { - v8::Handle objectArray = v8::Handle::Cast(arg1); - int objectArrayLength = objectArray->Length(); - for (int i=0 ; i < objectArrayLength ; ++i) { - v8::Handle argObject = objectArray->Get(i)->ToObject(); - - if (m_dynamicRoles) { - m_modelObjects.insert(index+i, DynamicRoleModelNode::create(args->engine()->variantMapFromJS(argObject), this)); - } else { - m_listModel->insert(index+i, argObject, args->engine()); - } - } - emitItemsInserted(index, objectArrayLength); - } else if (arg1->IsObject()) { - v8::Handle argObject = arg1->ToObject(); - - if (m_dynamicRoles) { - m_modelObjects.insert(index, DynamicRoleModelNode::create(args->engine()->variantMapFromJS(argObject), this)); - } else { - m_listModel->insert(index, argObject, args->engine()); - } - - emitItemsInserted(index, 1); - } else { - qmlInfo(this) << tr("insert: value is not an object"); - } - } else { - qmlInfo(this) << tr("insert: value is not an object"); - } -} - -/*! - \qmlmethod QtQuick2::ListModel::move(int from, int to, int n) - - Moves \a n items \a from one position \a to another. - - The from and to ranges must exist; for example, to move the first 3 items - to the end of the list: - - \code - fruitModel.move(0, fruitModel.count - 3, 3) - \endcode - - \sa append() -*/ -void QQuickListModel::move(int from, int to, int n) -{ - if (n==0 || from==to) - return; - if (!canMove(from, to, n)) { - qmlInfo(this) << tr("move: out of range"); - return; - } - - if (m_dynamicRoles) { - - int realFrom = from; - int realTo = to; - int realN = n; - - if (from > to) { - // Only move forwards - flip if backwards moving - int tfrom = from; - int tto = to; - realFrom = tto; - realTo = tto+n; - realN = tfrom-tto; - } - - QPODVector store; - for (int i=0 ; i < (realTo-realFrom) ; ++i) - store.append(m_modelObjects[realFrom+realN+i]); - for (int i=0 ; i < realN ; ++i) - store.append(m_modelObjects[realFrom+i]); - for (int i=0 ; i < store.count() ; ++i) - m_modelObjects[realFrom+i] = store[i]; - - } else { - m_listModel->move(from, to, n); - } - - emitItemsMoved(from, to, n); -} - -/*! - \qmlmethod QtQuick2::ListModel::append(jsobject dict) - - Adds a new item to the end of the list model, with the - values in \a dict. - - \code - fruitModel.append({"cost": 5.95, "name":"Pizza"}) - \endcode - - \sa set(), remove() -*/ -void QQuickListModel::append(QQmlV8Function *args) -{ - if (args->Length() == 1) { - v8::Handle arg = (*args)[0]; - - if (arg->IsArray()) { - v8::Handle objectArray = v8::Handle::Cast(arg); - int objectArrayLength = objectArray->Length(); - - int index = count(); - for (int i=0 ; i < objectArrayLength ; ++i) { - v8::Handle argObject = objectArray->Get(i)->ToObject(); - - if (m_dynamicRoles) { - m_modelObjects.append(DynamicRoleModelNode::create(args->engine()->variantMapFromJS(argObject), this)); - } else { - m_listModel->append(argObject, args->engine()); - } - } - - emitItemsInserted(index, objectArrayLength); - } else if (arg->IsObject()) { - v8::Handle argObject = arg->ToObject(); - - int index; - - if (m_dynamicRoles) { - index = m_modelObjects.count(); - m_modelObjects.append(DynamicRoleModelNode::create(args->engine()->variantMapFromJS(argObject), this)); - } else { - index = m_listModel->append(argObject, args->engine()); - } - - emitItemsInserted(index, 1); - } else { - qmlInfo(this) << tr("append: value is not an object"); - } - } else { - qmlInfo(this) << tr("append: value is not an object"); - } -} - -/*! - \qmlmethod object QtQuick2::ListModel::get(int index) - - Returns the item at \a index in the list model. This allows the item - data to be accessed or modified from JavaScript: - - \code - Component.onCompleted: { - fruitModel.append({"cost": 5.95, "name":"Jackfruit"}); - console.log(fruitModel.get(0).cost); - fruitModel.get(0).cost = 10.95; - } - \endcode - - The \a index must be an element in the list. - - Note that properties of the returned object that are themselves objects - will also be models, and this get() method is used to access elements: - - \code - fruitModel.append(..., "attributes": - [{"name":"spikes","value":"7mm"}, - {"name":"color","value":"green"}]); - fruitModel.get(0).attributes.get(1).value; // == "green" - \endcode - - \warning The returned object is not guaranteed to remain valid. It - should not be used in \l{Property Binding}{property bindings}. - - \sa append() -*/ -QQmlV8Handle QQuickListModel::get(int index) const -{ - v8::Handle result = v8::Undefined(); - - if (index >= 0 && index < count()) { - QV8Engine *v8engine = engine(); - - if (m_dynamicRoles) { - DynamicRoleModelNode *object = m_modelObjects[index]; - result = v8engine->newQObject(object); - } else { - ModelObject *object = m_listModel->getOrCreateModelObject(const_cast(this), index); - result = v8engine->newQObject(object); - } - } - - return QQmlV8Handle::fromHandle(result); -} - -/*! - \qmlmethod QtQuick2::ListModel::set(int index, jsobject dict) - - Changes the item at \a index in the list model with the - values in \a dict. Properties not appearing in \a dict - are left unchanged. - - \code - fruitModel.set(3, {"cost": 5.95, "name":"Pizza"}) - \endcode - - If \a index is equal to count() then a new item is appended to the - list. Otherwise, \a index must be an element in the list. - - \sa append() -*/ -void QQuickListModel::set(int index, const QQmlV8Handle &handle) -{ - v8::Handle valuemap = handle.toHandle(); - - if (!valuemap->IsObject() || valuemap->IsArray()) { - qmlInfo(this) << tr("set: value is not an object"); - return; - } - if (index > count() || index < 0) { - qmlInfo(this) << tr("set: index %1 out of range").arg(index); - return; - } - - v8::Handle object = valuemap->ToObject(); - - if (index == count()) { - - if (m_dynamicRoles) { - m_modelObjects.append(DynamicRoleModelNode::create(engine()->variantMapFromJS(object), this)); - } else { - m_listModel->insert(index, object, engine()); - } - - emitItemsInserted(index, 1); - } else { - - QVector roles; - - if (m_dynamicRoles) { - m_modelObjects[index]->updateValues(engine()->variantMapFromJS(object), roles); - } else { - m_listModel->set(index, object, &roles, engine()); - } - - if (roles.count()) - emitItemsChanged(index, 1, roles); - } -} - -/*! - \qmlmethod QtQuick2::ListModel::setProperty(int index, string property, variant value) - - Changes the \a property of the item at \a index in the list model to \a value. - - \code - fruitModel.setProperty(3, "cost", 5.95) - \endcode - - The \a index must be an element in the list. - - \sa append() -*/ -void QQuickListModel::setProperty(int index, const QString& property, const QVariant& value) -{ - if (count() == 0 || index >= count() || index < 0) { - qmlInfo(this) << tr("set: index %1 out of range").arg(index); - return; - } - - if (m_dynamicRoles) { - int roleIndex = m_roles.indexOf(property); - if (roleIndex == -1) { - roleIndex = m_roles.count(); - m_roles.append(property); - } - if (m_modelObjects[index]->setValue(property.toUtf8(), value)) { - QVector roles; - roles << roleIndex; - emitItemsChanged(index, 1, roles); - } - } else { - int roleIndex = m_listModel->setOrCreateProperty(index, property, value); - if (roleIndex != -1) { - - QVector roles; - roles << roleIndex; - - emitItemsChanged(index, 1, roles); - } - } -} - -/*! - \qmlmethod QtQuick2::ListModel::sync() - - Writes any unsaved changes to the list model after it has been modified - from a worker script. -*/ -void QQuickListModel::sync() -{ - // This is just a dummy method to make it look like sync() exists in - // ListModel (and not just QQuickListModelWorkerAgent) and to let - // us document sync(). - qmlInfo(this) << "List sync() can only be called from a WorkerScript"; -} - -bool QQuickListModelParser::compileProperty(const QQmlCustomParserProperty &prop, QList &instr, QByteArray &data) -{ - QList values = prop.assignedValues(); - for(int ii = 0; ii < values.count(); ++ii) { - const QVariant &value = values.at(ii); - - if(value.userType() == qMetaTypeId()) { - QQmlCustomParserNode node = - qvariant_cast(value); - - if (node.name() != listElementTypeName) { - const QMetaObject *mo = resolveType(node.name()); - if (mo != &QQuickListElement::staticMetaObject) { - error(node, QQuickListModel::tr("ListElement: cannot contain nested elements")); - return false; - } - listElementTypeName = node.name(); // cache right name for next time - } - - { - ListInstruction li; - li.type = ListInstruction::Push; - li.dataIdx = -1; - instr << li; - } - - QList props = node.properties(); - for(int jj = 0; jj < props.count(); ++jj) { - const QQmlCustomParserProperty &nodeProp = props.at(jj); - if (nodeProp.name().isEmpty()) { - error(nodeProp, QQuickListModel::tr("ListElement: cannot contain nested elements")); - return false; - } - if (nodeProp.name() == QStringLiteral("id")) { - error(nodeProp, QQuickListModel::tr("ListElement: cannot use reserved \"id\" property")); - return false; - } - - ListInstruction li; - int ref = data.count(); - data.append(nodeProp.name().toUtf8()); - data.append('\0'); - li.type = ListInstruction::Set; - li.dataIdx = ref; - instr << li; - - if(!compileProperty(nodeProp, instr, data)) - return false; - - li.type = ListInstruction::Pop; - li.dataIdx = -1; - instr << li; - } - - { - ListInstruction li; - li.type = ListInstruction::Pop; - li.dataIdx = -1; - instr << li; - } - - } else { - - QQmlScript::Variant variant = - qvariant_cast(value); - - int ref = data.count(); - - QByteArray d; - d += char(variant.type()); // type tag - if (variant.isString()) { - d += variant.asString().toUtf8(); - } else if (variant.isNumber()) { - d += QByteArray::number(variant.asNumber(),'g',20); - } else if (variant.isBoolean()) { - d += char(variant.asBoolean()); - } else if (variant.isScript()) { - if (definesEmptyList(variant.asScript())) { - d[0] = char(QQmlScript::Variant::Invalid); // marks empty list - } else { - QByteArray script = variant.asScript().toUtf8(); - bool ok; - int v = evaluateEnum(script, &ok); - if (!ok) { - using namespace QQmlJS; - AST::Node *node = variant.asAST(); - AST::StringLiteral *literal = 0; - if (AST::CallExpression *callExpr = AST::cast(node)) { - if (AST::IdentifierExpression *idExpr = AST::cast(callExpr->base)) { - if (idExpr->name == QLatin1String("QT_TR_NOOP") || idExpr->name == QLatin1String("QT_TRID_NOOP")) { - if (callExpr->arguments && !callExpr->arguments->next) - literal = AST::cast(callExpr->arguments->expression); - if (!literal) { - error(prop, QQuickListModel::tr("ListElement: improperly specified %1").arg(idExpr->name.toString())); - return false; - } - } else if (idExpr->name == QLatin1String("QT_TRANSLATE_NOOP")) { - if (callExpr->arguments && callExpr->arguments->next && !callExpr->arguments->next->next) - literal = AST::cast(callExpr->arguments->next->expression); - if (!literal) { - error(prop, QQuickListModel::tr("ListElement: improperly specified QT_TRANSLATE_NOOP")); - return false; - } - } - } - } - - if (literal) { - d[0] = char(QQmlScript::Variant::String); - d += literal->value.toUtf8(); - } else { - error(prop, QQuickListModel::tr("ListElement: cannot use script for property value")); - return false; - } - } else { - d[0] = char(QQmlScript::Variant::Number); - d += QByteArray::number(v); - } - } - } - d.append('\0'); - data.append(d); - - ListInstruction li; - li.type = ListInstruction::Value; - li.dataIdx = ref; - instr << li; - } - } - - return true; -} - -QByteArray QQuickListModelParser::compile(const QList &customProps) -{ - QList instr; - QByteArray data; - listElementTypeName = QString(); // unknown - - for(int ii = 0; ii < customProps.count(); ++ii) { - const QQmlCustomParserProperty &prop = customProps.at(ii); - if(!prop.name().isEmpty()) { // isn't default property - error(prop, QQuickListModel::tr("ListModel: undefined property '%1'").arg(prop.name())); - return QByteArray(); - } - - if(!compileProperty(prop, instr, data)) { - return QByteArray(); - } - } - - int size = sizeof(ListModelData) + - instr.count() * sizeof(ListInstruction) + - data.count(); - - QByteArray rv; - rv.resize(size); - - ListModelData *lmd = (ListModelData *)rv.data(); - lmd->dataOffset = sizeof(ListModelData) + - instr.count() * sizeof(ListInstruction); - lmd->instrCount = instr.count(); - for (int ii = 0; ii < instr.count(); ++ii) - lmd->instructions()[ii] = instr.at(ii); - ::memcpy(rv.data() + lmd->dataOffset, data.constData(), data.count()); - - return rv; -} - -void QQuickListModelParser::setCustomData(QObject *obj, const QByteArray &d) -{ - QQuickListModel *rv = static_cast(obj); - - QV8Engine *engine = QQmlEnginePrivate::getV8Engine(qmlEngine(rv)); - rv->m_engine = engine; - - const ListModelData *lmd = (const ListModelData *)d.constData(); - const char *data = ((const char *)lmd) + lmd->dataOffset; - - bool setRoles = false; - - QStack stack; - - for (int ii = 0; ii < lmd->instrCount; ++ii) { - const ListInstruction &instr = lmd->instructions()[ii]; - - switch(instr.type) { - case ListInstruction::Push: - { - Q_ASSERT(!rv->m_dynamicRoles); - - ListModel *subModel = 0; - - if (stack.count() == 0) { - subModel = rv->m_listModel; - } else { - const DataStackElement &e0 = stack.at(stack.size() - 1); - DataStackElement &e1 = stack[stack.size() - 2]; - - const ListLayout::Role &role = e1.model->getOrCreateListRole(e0.name); - if (role.type == ListLayout::Role::List) { - subModel = e1.model->getListProperty(e1.elementIndex, role); - - if (subModel == 0) { - subModel = new ListModel(role.subLayout, 0, -1); - QVariant vModel = QVariant::fromValue(subModel); - e1.model->setOrCreateProperty(e1.elementIndex, e0.name, vModel); - } - } - } - - DataStackElement e; - e.model = subModel; - e.elementIndex = subModel ? subModel->appendElement() : -1; - stack.push(e); - } - break; - - case ListInstruction::Pop: - stack.pop(); - break; - - case ListInstruction::Value: - { - const DataStackElement &e0 = stack.at(stack.size() - 1); - DataStackElement &e1 = stack[stack.size() - 2]; - - QString name = e0.name; - QVariant value; - - switch (QQmlScript::Variant::Type(data[instr.dataIdx])) { - case QQmlScript::Variant::Invalid: - { - const ListLayout::Role &role = e1.model->getOrCreateListRole(e0.name); - ListModel *emptyModel = new ListModel(role.subLayout, 0, -1); - value = QVariant::fromValue(emptyModel); - } - break; - case QQmlScript::Variant::Boolean: - value = bool(data[1 + instr.dataIdx]); - break; - case QQmlScript::Variant::Number: - value = QByteArray(data + 1 + instr.dataIdx).toDouble(); - break; - case QQmlScript::Variant::String: - value = QString::fromUtf8(data + 1 + instr.dataIdx); - break; - default: - Q_ASSERT("Format error in ListInstruction"); - } - - e1.model->setOrCreateProperty(e1.elementIndex, name, value); - setRoles = true; - } - break; - - case ListInstruction::Set: - { - DataStackElement e; - e.name = QString::fromUtf8(data + instr.dataIdx); - stack.push(e); - } - break; - } - } - - if (setRoles == false) - qmlInfo(obj) << "All ListElement declarations are empty, no roles can be created unless dynamicRoles is set."; -} - -bool QQuickListModelParser::definesEmptyList(const QString &s) -{ - if (s.startsWith(QLatin1Char('[')) && s.endsWith(QLatin1Char(']'))) { - for (int i=1; i -#include - -#include -#include -#include -#include -#include -#include - -#include -#include - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - - -class QQuickListModelWorkerAgent; -class ListModel; -class ListLayout; - -class Q_QML_PRIVATE_EXPORT QQuickListModel : public QAbstractListModel -{ - Q_OBJECT - Q_PROPERTY(int count READ count NOTIFY countChanged) - Q_PROPERTY(bool dynamicRoles READ dynamicRoles WRITE setDynamicRoles) - -public: - QQuickListModel(QObject *parent=0); - ~QQuickListModel(); - - QModelIndex index(int row, int column, const QModelIndex &parent) const; - int rowCount(const QModelIndex &parent) const; - QVariant data(const QModelIndex &index, int role) const; - QHash roleNames() const; - - QVariant data(int index, int role) const; - int count() const; - - Q_INVOKABLE void clear(); - Q_INVOKABLE void remove(QQmlV8Function *args); - Q_INVOKABLE void append(QQmlV8Function *args); - Q_INVOKABLE void insert(QQmlV8Function *args); - Q_INVOKABLE QQmlV8Handle get(int index) const; - Q_INVOKABLE void set(int index, const QQmlV8Handle &); - Q_INVOKABLE void setProperty(int index, const QString& property, const QVariant& value); - Q_INVOKABLE void move(int from, int to, int count); - Q_INVOKABLE void sync(); - - QQuickListModelWorkerAgent *agent(); - - bool dynamicRoles() const { return m_dynamicRoles; } - void setDynamicRoles(bool enableDynamicRoles); - -Q_SIGNALS: - void countChanged(); - -private: - friend class QQuickListModelParser; - friend class QQuickListModelWorkerAgent; - friend class ModelObject; - friend class ModelNodeMetaObject; - friend class ListModel; - friend class ListElement; - friend class DynamicRoleModelNode; - friend class DynamicRoleModelNodeMetaObject; - - // Constructs a flat list model for a worker agent - QQuickListModel(QQuickListModel *orig, QQuickListModelWorkerAgent *agent); - QQuickListModel(const QQuickListModel *owner, ListModel *data, QV8Engine *eng, QObject *parent=0); - - QV8Engine *engine() const; - - inline bool canMove(int from, int to, int n) const { return !(from+n > count() || to+n > count() || from < 0 || to < 0 || n < 0); } - - QQuickListModelWorkerAgent *m_agent; - mutable QV8Engine *m_engine; - bool m_mainThread; - bool m_primary; - - bool m_dynamicRoles; - - ListLayout *m_layout; - ListModel *m_listModel; - - QVector m_modelObjects; - QVector m_roles; - int m_uid; - - struct ElementSync - { - ElementSync() : src(0), target(0) {} - - DynamicRoleModelNode *src; - DynamicRoleModelNode *target; - }; - - int getUid() const { return m_uid; } - - static void sync(QQuickListModel *src, QQuickListModel *target, QHash *targetModelHash); - static QQuickListModel *createWithOwner(QQuickListModel *newOwner); - - void emitItemsChanged(int index, int count, const QVector &roles); - void emitItemsRemoved(int index, int count); - void emitItemsInserted(int index, int count); - void emitItemsMoved(int from, int to, int n); -}; - -// ### FIXME -class QQuickListElement : public QObject -{ -Q_OBJECT -}; - -class QQuickListModelParser : public QQmlCustomParser -{ -public: - QQuickListModelParser() : QQmlCustomParser(QQmlCustomParser::AcceptsSignalHandlers) {} - QByteArray compile(const QList &); - void setCustomData(QObject *, const QByteArray &); - -private: - struct ListInstruction - { - enum { Push, Pop, Value, Set } type; - int dataIdx; - }; - struct ListModelData - { - int dataOffset; - int instrCount; - ListInstruction *instructions() const; - }; - bool compileProperty(const QQmlCustomParserProperty &prop, QList &instr, QByteArray &data); - - bool definesEmptyList(const QString &); - - QString listElementTypeName; - - struct DataStackElement - { - DataStackElement() : model(0), elementIndex(0) {} - - QString name; - ListModel *model; - int elementIndex; - }; -}; - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QQuickListModel) -QML_DECLARE_TYPE(QQuickListElement) - -QT_END_HEADER - -#endif // QQUICKLISTMODEL_H diff --git a/src/qml/qml/qquicklistmodel_p_p.h b/src/qml/qml/qquicklistmodel_p_p.h deleted file mode 100644 index ff312f98e5..0000000000 --- a/src/qml/qml/qquicklistmodel_p_p.h +++ /dev/null @@ -1,382 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** 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, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQUICKLISTMODEL_P_P_H -#define QQUICKLISTMODEL_P_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "qquicklistmodel_p.h" -#include -#include "qqmlopenmetaobject_p.h" -#include - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - - -class DynamicRoleModelNode; - -class DynamicRoleModelNodeMetaObject : public QQmlOpenMetaObject -{ -public: - DynamicRoleModelNodeMetaObject(DynamicRoleModelNode *object); - ~DynamicRoleModelNodeMetaObject(); - - bool m_enabled; - -protected: - void propertyWrite(int index); - void propertyWritten(int index); - -private: - DynamicRoleModelNode *m_owner; -}; - -class DynamicRoleModelNode : public QObject -{ - Q_OBJECT -public: - DynamicRoleModelNode(QQuickListModel *owner, int uid); - - static DynamicRoleModelNode *create(const QVariantMap &obj, QQuickListModel *owner); - - void updateValues(const QVariantMap &object, QVector &roles); - - QVariant getValue(const QString &name) - { - return m_meta->value(name.toUtf8()); - } - - bool setValue(const QByteArray &name, const QVariant &val) - { - return m_meta->setValue(name, val); - } - - void setNodeUpdatesEnabled(bool enable) - { - m_meta->m_enabled = enable; - } - - int getUid() const - { - return m_uid; - } - - static void sync(DynamicRoleModelNode *src, DynamicRoleModelNode *target, QHash *targetModelHash); - -private: - QQuickListModel *m_owner; - int m_uid; - DynamicRoleModelNodeMetaObject *m_meta; - - friend class DynamicRoleModelNodeMetaObject; -}; - -class ModelObject; - -class ModelNodeMetaObject : public QQmlOpenMetaObject -{ -public: - ModelNodeMetaObject(ModelObject *object); - ~ModelNodeMetaObject(); - - bool m_enabled; - -protected: - void propertyWritten(int index); - -private: - - ModelObject *m_obj; -}; - -class ModelObject : public QObject -{ - Q_OBJECT -public: - ModelObject(QQuickListModel *model, int elementIndex); - - void setValue(const QByteArray &name, const QVariant &val, bool force) - { - if (force) { - QVariant existingValue = m_meta->value(name); - if (existingValue.isValid()) { - (*m_meta)[name] = QVariant(); - } - } - m_meta->setValue(name, val); - } - - void setNodeUpdatesEnabled(bool enable) - { - m_meta->m_enabled = enable; - } - - void updateValues(); - void updateValues(const QVector &roles); - - QQuickListModel *m_model; - int m_elementIndex; - -private: - ModelNodeMetaObject *m_meta; -}; - -class ListLayout -{ -public: - ListLayout() : currentBlock(0), currentBlockOffset(0) {} - ListLayout(const ListLayout *other); - ~ListLayout(); - - class Role - { - public: - - Role() : type(Invalid), blockIndex(-1), blockOffset(-1), index(-1), subLayout(0) {} - explicit Role(const Role *other); - ~Role(); - - // This enum must be kept in sync with the roleTypeNames variable in qdeclarativelistmodel.cpp - enum DataType - { - Invalid = -1, - - String, - Number, - Bool, - List, - QObject, - VariantMap, - DateTime, - - MaxDataType - }; - - QString name; - DataType type; - int blockIndex; - int blockOffset; - int index; - ListLayout *subLayout; - }; - - const Role *getRoleOrCreate(const QString &key, const QVariant &data); - const Role &getRoleOrCreate(v8::Handle key, Role::DataType type); - const Role &getRoleOrCreate(const QString &key, Role::DataType type); - - const Role &getExistingRole(int index) { return *roles.at(index); } - const Role *getExistingRole(const QString &key); - const Role *getExistingRole(v8::Handle key); - - int roleCount() const { return roles.count(); } - - static void sync(ListLayout *src, ListLayout *target); - -private: - const Role &createRole(const QString &key, Role::DataType type); - - int currentBlock; - int currentBlockOffset; - QVector roles; - QStringHash roleHash; -}; - -class ListElement -{ -public: - - ListElement(); - ListElement(int existingUid); - ~ListElement(); - - static void sync(ListElement *src, ListLayout *srcLayout, ListElement *target, ListLayout *targetLayout, QHash *targetModelHash); - - enum - { - BLOCK_SIZE = 64 - sizeof(int) - sizeof(ListElement *) - sizeof(ModelObject *) - }; - -private: - - void destroy(ListLayout *layout); - - int setVariantProperty(const ListLayout::Role &role, const QVariant &d); - - int setJsProperty(const ListLayout::Role &role, v8::Handle d, QV8Engine *eng); - - int setStringProperty(const ListLayout::Role &role, const QString &s); - int setDoubleProperty(const ListLayout::Role &role, double n); - int setBoolProperty(const ListLayout::Role &role, bool b); - int setListProperty(const ListLayout::Role &role, ListModel *m); - int setQObjectProperty(const ListLayout::Role &role, QObject *o); - int setVariantMapProperty(const ListLayout::Role &role, v8::Handle o, QV8Engine *eng); - int setVariantMapProperty(const ListLayout::Role &role, QVariantMap *m); - int setDateTimeProperty(const ListLayout::Role &role, const QDateTime &dt); - - void setStringPropertyFast(const ListLayout::Role &role, const QString &s); - void setDoublePropertyFast(const ListLayout::Role &role, double n); - void setBoolPropertyFast(const ListLayout::Role &role, bool b); - void setQObjectPropertyFast(const ListLayout::Role &role, QObject *o); - void setListPropertyFast(const ListLayout::Role &role, ListModel *m); - void setVariantMapFast(const ListLayout::Role &role, v8::Handle o, QV8Engine *eng); - void setDateTimePropertyFast(const ListLayout::Role &role, const QDateTime &dt); - - void clearProperty(const ListLayout::Role &role); - - QVariant getProperty(const ListLayout::Role &role, const QQuickListModel *owner, QV8Engine *eng); - ListModel *getListProperty(const ListLayout::Role &role); - QString *getStringProperty(const ListLayout::Role &role); - QObject *getQObjectProperty(const ListLayout::Role &role); - QQmlGuard *getGuardProperty(const ListLayout::Role &role); - QVariantMap *getVariantMapProperty(const ListLayout::Role &role); - QDateTime *getDateTimeProperty(const ListLayout::Role &role); - - inline char *getPropertyMemory(const ListLayout::Role &role); - - int getUid() const { return uid; } - - char data[BLOCK_SIZE]; - ListElement *next; - - int uid; - ModelObject *m_objectCache; - - friend class ListModel; -}; - -class ListModel -{ -public: - - ListModel(ListLayout *layout, QQuickListModel *modelCache, int uid); - ~ListModel() {} - - void destroy(); - - int setOrCreateProperty(int elementIndex, const QString &key, const QVariant &data); - int setExistingProperty(int uid, const QString &key, v8::Handle data, QV8Engine *eng); - - QVariant getProperty(int elementIndex, int roleIndex, const QQuickListModel *owner, QV8Engine *eng); - ListModel *getListProperty(int elementIndex, const ListLayout::Role &role); - - int roleCount() const - { - return m_layout->roleCount(); - } - - const ListLayout::Role &getExistingRole(int index) - { - return m_layout->getExistingRole(index); - } - - const ListLayout::Role &getOrCreateListRole(const QString &name) - { - return m_layout->getRoleOrCreate(name, ListLayout::Role::List); - } - - int elementCount() const - { - return elements.count(); - } - - void set(int elementIndex, v8::Handle object, QVector *roles, QV8Engine *eng); - void set(int elementIndex, v8::Handle object, QV8Engine *eng); - - int append(v8::Handle object, QV8Engine *eng); - void insert(int elementIndex, v8::Handle object, QV8Engine *eng); - - void clear(); - void remove(int index, int count); - - int appendElement(); - void insertElement(int index); - - void move(int from, int to, int n); - - int getUid() const { return m_uid; } - - static void sync(ListModel *src, ListModel *target, QHash *srcModelHash); - - ModelObject *getOrCreateModelObject(QQuickListModel *model, int elementIndex); - -private: - QPODVector elements; - ListLayout *m_layout; - int m_uid; - - QQuickListModel *m_modelCache; - - struct ElementSync - { - ElementSync() : src(0), target(0) {} - - ListElement *src; - ListElement *target; - }; - - void newElement(int index); - - void updateCacheIndices(); - - friend class ListElement; - friend class QQuickListModelWorkerAgent; -}; - -QT_END_NAMESPACE - -Q_DECLARE_METATYPE(ListModel *); - -QT_END_HEADER - -#endif // QQUICKLISTMODEL_P_P_H - diff --git a/src/qml/qml/qquicklistmodelworkeragent.cpp b/src/qml/qml/qquicklistmodelworkeragent.cpp deleted file mode 100644 index e0ab882b92..0000000000 --- a/src/qml/qml/qquicklistmodelworkeragent.cpp +++ /dev/null @@ -1,259 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** 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, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qquicklistmodelworkeragent_p.h" -#include "qquicklistmodel_p_p.h" -#include -#include -#include - -#include -#include -#include - - -QT_BEGIN_NAMESPACE - - -void QQuickListModelWorkerAgent::Data::clearChange(int uid) -{ - for (int i=0 ; i < changes.count() ; ++i) { - if (changes[i].modelUid == uid) { - changes.removeAt(i); - --i; - } - } -} - -void QQuickListModelWorkerAgent::Data::insertChange(int uid, int index, int count) -{ - Change c = { uid, Change::Inserted, index, count, 0, QVector() }; - changes << c; -} - -void QQuickListModelWorkerAgent::Data::removeChange(int uid, int index, int count) -{ - Change c = { uid, Change::Removed, index, count, 0, QVector() }; - changes << c; -} - -void QQuickListModelWorkerAgent::Data::moveChange(int uid, int index, int count, int to) -{ - Change c = { uid, Change::Moved, index, count, to, QVector() }; - changes << c; -} - -void QQuickListModelWorkerAgent::Data::changedChange(int uid, int index, int count, const QVector &roles) -{ - Change c = { uid, Change::Changed, index, count, 0, roles }; - changes << c; -} - -QQuickListModelWorkerAgent::QQuickListModelWorkerAgent(QQuickListModel *model) -: m_ref(1), m_orig(model), m_copy(new QQuickListModel(model, this)) -{ -} - -QQuickListModelWorkerAgent::~QQuickListModelWorkerAgent() -{ - mutex.lock(); - syncDone.wakeAll(); - mutex.unlock(); -} - -void QQuickListModelWorkerAgent::setV8Engine(QV8Engine *eng) -{ - m_copy->m_engine = eng; -} - -void QQuickListModelWorkerAgent::addref() -{ - m_ref.ref(); -} - -void QQuickListModelWorkerAgent::release() -{ - bool del = !m_ref.deref(); - - if (del) - deleteLater(); -} - -void QQuickListModelWorkerAgent::modelDestroyed() -{ - m_orig = 0; -} - -int QQuickListModelWorkerAgent::count() const -{ - return m_copy->count(); -} - -void QQuickListModelWorkerAgent::clear() -{ - m_copy->clear(); -} - -void QQuickListModelWorkerAgent::remove(QQmlV8Function *args) -{ - m_copy->remove(args); -} - -void QQuickListModelWorkerAgent::append(QQmlV8Function *args) -{ - m_copy->append(args); -} - -void QQuickListModelWorkerAgent::insert(QQmlV8Function *args) -{ - m_copy->insert(args); -} - -QQmlV8Handle QQuickListModelWorkerAgent::get(int index) const -{ - return m_copy->get(index); -} - -void QQuickListModelWorkerAgent::set(int index, const QQmlV8Handle &value) -{ - m_copy->set(index, value); -} - -void QQuickListModelWorkerAgent::setProperty(int index, const QString& property, const QVariant& value) -{ - m_copy->setProperty(index, property, value); -} - -void QQuickListModelWorkerAgent::move(int from, int to, int count) -{ - m_copy->move(from, to, count); -} - -void QQuickListModelWorkerAgent::sync() -{ - Sync *s = new Sync; - s->data = data; - s->list = m_copy; - data.changes.clear(); - - mutex.lock(); - QCoreApplication::postEvent(this, s); - syncDone.wait(&mutex); - mutex.unlock(); -} - -bool QQuickListModelWorkerAgent::event(QEvent *e) -{ - if (e->type() == QEvent::User) { - bool cc = false; - QMutexLocker locker(&mutex); - if (m_orig) { - Sync *s = static_cast(e); - const QList &changes = s->data.changes; - - cc = m_orig->count() != s->list->count(); - - QHash targetModelDynamicHash; - QHash targetModelStaticHash; - - Q_ASSERT(m_orig->m_dynamicRoles == s->list->m_dynamicRoles); - if (m_orig->m_dynamicRoles) - QQuickListModel::sync(s->list, m_orig, &targetModelDynamicHash); - else - ListModel::sync(s->list->m_listModel, m_orig->m_listModel, &targetModelStaticHash); - - for (int ii = 0; ii < changes.count(); ++ii) { - const Change &change = changes.at(ii); - - QQuickListModel *model = 0; - if (m_orig->m_dynamicRoles) { - model = targetModelDynamicHash.value(change.modelUid); - } else { - ListModel *lm = targetModelStaticHash.value(change.modelUid); - if (lm) - model = lm->m_modelCache; - } - - if (model) { - switch (change.type) { - case Change::Inserted: - model->beginInsertRows( - QModelIndex(), change.index, change.index + change.count - 1); - model->endInsertRows(); - break; - case Change::Removed: - model->beginRemoveRows( - QModelIndex(), change.index, change.index + change.count - 1); - model->endRemoveRows(); - break; - case Change::Moved: - model->beginMoveRows( - QModelIndex(), - change.index, - change.index + change.count - 1, - QModelIndex(), - change.to > change.index ? change.to + change.count : change.to); - model->endMoveRows(); - break; - case Change::Changed: - emit model->dataChanged( - model->createIndex(change.index, 0), - model->createIndex(change.index + change.count - 1, 0), - change.roles); - break; - } - } - } - } - - syncDone.wakeAll(); - locker.unlock(); - - if (cc) - emit m_orig->countChanged(); - return true; - } - - return QObject::event(e); -} - -QT_END_NAMESPACE - diff --git a/src/qml/qml/qquicklistmodelworkeragent_p.h b/src/qml/qml/qquicklistmodelworkeragent_p.h deleted file mode 100644 index 7cff9be8f4..0000000000 --- a/src/qml/qml/qquicklistmodelworkeragent_p.h +++ /dev/null @@ -1,160 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** 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, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQUICKLISTMODELWORKERAGENT_P_H -#define QQUICKLISTMODELWORKERAGENT_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include - -#include -#include - -#include - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - - -class QQuickListModel; - -class QQuickListModelWorkerAgent : public QObject -{ - Q_OBJECT - Q_PROPERTY(int count READ count) - -public: - QQuickListModelWorkerAgent(QQuickListModel *); - ~QQuickListModelWorkerAgent(); - void setV8Engine(QV8Engine *eng); - - void addref(); - void release(); - - int count() const; - - Q_INVOKABLE void clear(); - Q_INVOKABLE void remove(QQmlV8Function *args); - Q_INVOKABLE void append(QQmlV8Function *args); - Q_INVOKABLE void insert(QQmlV8Function *args); - Q_INVOKABLE QQmlV8Handle get(int index) const; - Q_INVOKABLE void set(int index, const QQmlV8Handle &); - Q_INVOKABLE void setProperty(int index, const QString& property, const QVariant& value); - Q_INVOKABLE void move(int from, int to, int count); - Q_INVOKABLE void sync(); - - struct VariantRef - { - VariantRef() : a(0) {} - VariantRef(const VariantRef &r) : a(r.a) { if (a) a->addref(); } - VariantRef(QQuickListModelWorkerAgent *_a) : a(_a) { if (a) a->addref(); } - ~VariantRef() { if (a) a->release(); } - - VariantRef &operator=(const VariantRef &o) { - if (o.a) o.a->addref(); - if (a) a->release(); a = o.a; - return *this; - } - - QQuickListModelWorkerAgent *a; - }; - void modelDestroyed(); -protected: - virtual bool event(QEvent *); - -private: - friend class QQuickWorkerScriptEnginePrivate; - friend class QQuickListModel; - - struct Change - { - int modelUid; - enum { Inserted, Removed, Moved, Changed } type; - int index; // Inserted/Removed/Moved/Changed - int count; // Inserted/Removed/Moved/Changed - int to; // Moved - QVector roles; - }; - - struct Data - { - QList changes; - - void clearChange(int uid); - void insertChange(int uid, int index, int count); - void removeChange(int uid, int index, int count); - void moveChange(int uid, int index, int count, int to); - void changedChange(int uid, int index, int count, const QVector &roles); - }; - Data data; - - struct Sync : public QEvent { - Sync() : QEvent(QEvent::User) {} - Data data; - QQuickListModel *list; - }; - - QAtomicInt m_ref; - QQuickListModel *m_orig; - QQuickListModel *m_copy; - QMutex mutex; - QWaitCondition syncDone; -}; - -QT_END_NAMESPACE - -Q_DECLARE_METATYPE(QQuickListModelWorkerAgent::VariantRef) - -QT_END_HEADER - -#endif // QQUICKLISTMODELWORKERAGENT_P_H - diff --git a/src/qml/qml/qquickworkerscript.cpp b/src/qml/qml/qquickworkerscript.cpp index f7559f1d36..b9b027f6ad 100644 --- a/src/qml/qml/qquickworkerscript.cpp +++ b/src/qml/qml/qquickworkerscript.cpp @@ -40,8 +40,8 @@ ****************************************************************************/ #include "qquickworkerscript_p.h" -#include "qquicklistmodel_p.h" -#include "qquicklistmodelworkeragent_p.h" +#include "qqmllistmodel_p.h" +#include "qqmllistmodelworkeragent_p.h" #include "qqmlengine_p.h" #include "qqmlexpression_p.h" @@ -484,7 +484,7 @@ QQuickWorkerScriptEngine::~QQuickWorkerScriptEngine() //We have to force to cleanup the main thread's event queue here //to make sure the main GUI release all pending locks/wait conditions which - //some worker script/agent are waiting for (QQuickListModelWorkerAgent::sync() for example). + //some worker script/agent are waiting for (QQmlListModelWorkerAgent::sync() for example). while (!isFinished()) { // We can't simply wait here, because the worker thread will not terminate // until the main thread processes the last data event it generates diff --git a/src/qml/qml/v8/qv8worker.cpp b/src/qml/qml/v8/qv8worker.cpp index bf3379b312..9556e146ef 100644 --- a/src/qml/qml/v8/qv8worker.cpp +++ b/src/qml/qml/v8/qv8worker.cpp @@ -41,8 +41,8 @@ #include "qv8worker_p.h" -#include -#include +#include +#include QT_BEGIN_NAMESPACE @@ -242,9 +242,9 @@ void QV8Worker::serialize(QByteArray &data, v8::Handle v, QV8Engine * } else if (engine->isQObject(v)) { // XXX TODO: Generalize passing objects between the main thread and worker scripts so // that others can trivially plug in their elements. - QQuickListModel *lm = qobject_cast(engine->toQObject(v)); + QQmlListModel *lm = qobject_cast(engine->toQObject(v)); if (lm && lm->agent()) { - QQuickListModelWorkerAgent *agent = lm->agent(); + QQmlListModelWorkerAgent *agent = lm->agent(); agent->addref(); push(data, valueheader(WorkerListModel)); push(data, (void *)agent); @@ -347,10 +347,10 @@ v8::Handle QV8Worker::deserialize(const char *&data, QV8Engine *engin case WorkerListModel: { void *ptr = popPtr(data); - QQuickListModelWorkerAgent *agent = (QQuickListModelWorkerAgent *)ptr; + QQmlListModelWorkerAgent *agent = (QQmlListModelWorkerAgent *)ptr; v8::Handle rv = engine->newQObject(agent); if (rv->IsObject()) { - QQuickListModelWorkerAgent::VariantRef ref(agent); + QQmlListModelWorkerAgent::VariantRef ref(agent); QVariant var = qVariantFromValue(ref); rv->ToObject()->SetHiddenValue(v8::String::New("qml::ref"), engine->fromVariant(var)); } diff --git a/tests/auto/qml/qml.pro b/tests/auto/qml/qml.pro index b77effe3e5..5d62b91094 100644 --- a/tests/auto/qml/qml.pro +++ b/tests/auto/qml/qml.pro @@ -45,8 +45,8 @@ PRIVATETESTS += \ qquickchangeset \ qqmlconnections \ qquicklistcompositor \ - qquicklistmodel \ - qquicklistmodelworkerscript \ + qqmllistmodel \ + qqmllistmodelworkerscript \ qquickworkerscript \ qqmlbundle \ qrcqml \ diff --git a/tests/auto/qml/qqmllistmodel/data/enumerate.qml b/tests/auto/qml/qqmllistmodel/data/enumerate.qml new file mode 100644 index 0000000000..f73d66b318 --- /dev/null +++ b/tests/auto/qml/qqmllistmodel/data/enumerate.qml @@ -0,0 +1,24 @@ +import QtQuick 2.0 + +Item { + property string result + + ListModel { + id: model + + ListElement { + val1: 1 + val2: 2 + val3: "str" + val4: false + val5: true + } + } + + Component.onCompleted: { + var element = model.get(0); + + for (var i in element) + result += i+"="+element[i]+(element[i] ? "Y" : "N")+":"; + } +} diff --git a/tests/auto/qml/qqmllistmodel/data/multipleroles.qml b/tests/auto/qml/qqmllistmodel/data/multipleroles.qml new file mode 100644 index 0000000000..4a331e2b3e --- /dev/null +++ b/tests/auto/qml/qqmllistmodel/data/multipleroles.qml @@ -0,0 +1,25 @@ +import QtQuick 2.0 +ListView { + width: 100 + height: 250 + delegate: Rectangle { + width: 100 + height: 50 + color: black ? "black": "white" + } + model: ListModel { + objectName: "listModel" + ListElement { + black: false + rounded: false + } + ListElement { + black: true + rounded: false + } + ListElement { + black: true + rounded: false + } + } +} diff --git a/tests/auto/qml/qqmllistmodel/data/setmodelcachelist.qml b/tests/auto/qml/qqmllistmodel/data/setmodelcachelist.qml new file mode 100644 index 0000000000..58bf1ccd04 --- /dev/null +++ b/tests/auto/qml/qqmllistmodel/data/setmodelcachelist.qml @@ -0,0 +1,20 @@ +import QtQuick 2.0 + +ListModel { + id: model + property bool ok : false + + Component.onCompleted: { + model.append({"attrs": []}) + model.get(0) + model.set(0, {"attrs": [{'abc': 123, 'def': 456}] } ) + ok = ( model.get(0).attrs.get(0).abc == 123 + && model.get(0).attrs.get(0).def == 456 ) + + model.set(0, {"attrs": [{'abc': 789, 'def': 101}] } ) + ok = ( model.get(0).attrs.get(0).abc == 789 + && model.get(0).attrs.get(0).def == 101 ) + + } +} + diff --git a/tests/auto/qml/qqmllistmodel/data/signalhandlers.qml b/tests/auto/qml/qqmllistmodel/data/signalhandlers.qml new file mode 100644 index 0000000000..750d99c5a3 --- /dev/null +++ b/tests/auto/qml/qqmllistmodel/data/signalhandlers.qml @@ -0,0 +1,8 @@ +import QtQuick 2.0 + +ListModel{ + property bool ok: false + property int foo: 5 + onFooChanged: ok = true + Component.onCompleted: foo = 6 +} diff --git a/tests/auto/qml/qqmllistmodel/qqmllistmodel.pro b/tests/auto/qml/qqmllistmodel/qqmllistmodel.pro new file mode 100644 index 0000000000..ef044f1663 --- /dev/null +++ b/tests/auto/qml/qqmllistmodel/qqmllistmodel.pro @@ -0,0 +1,14 @@ +CONFIG += testcase +TARGET = tst_qqmllistmodel +macx:CONFIG -= app_bundle + +SOURCES += tst_qqmllistmodel.cpp + +include (../../shared/util.pri) + +TESTDATA = data/* + +CONFIG += parallel_test + +QT += core-private gui-private v8-private qml-private quick-private testlib +DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 diff --git a/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp b/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp new file mode 100644 index 0000000000..22229febf3 --- /dev/null +++ b/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp @@ -0,0 +1,1258 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "../../shared/util.h" + +Q_DECLARE_METATYPE(QList) +Q_DECLARE_METATYPE(QList) + +#define RUNEVAL(object, string) \ + QVERIFY(QMetaObject::invokeMethod(object, "runEval", Q_ARG(QVariant, QString(string)))); + +inline QVariant runexpr(QQmlEngine *engine, const QString &str) +{ + QQmlExpression expr(engine->rootContext(), 0, str); + return expr.evaluate(); +} + +#define RUNEXPR(string) runexpr(&engine, QString(string)) + +static bool isValidErrorMessage(const QString &msg, bool dynamicRoleTest) +{ + bool valid = true; + + if (msg.isEmpty()) { + valid = false; + } else if (dynamicRoleTest) { + if (msg.contains("Can't assign to existing role") || msg.contains("Can't create role for unsupported data type")) + valid = false; + } + + return valid; +} + +class tst_qqmllistmodel : public QQmlDataTest +{ + Q_OBJECT +public: + tst_qqmllistmodel() + { + qRegisterMetaType >(); + } + +private: + int roleFromName(const QQmlListModel *model, const QString &roleName); + + static bool compareVariantList(const QVariantList &testList, QVariant object); + +private slots: + void static_types(); + void static_types_data(); + void static_i18n(); + void static_i18n_data(); + void static_nestedElements(); + void static_nestedElements_data(); + void dynamic_data(); + void dynamic(); + void enumerate(); + void error_data(); + void error(); + void syncError(); + void get(); + void set_data(); + void set(); + void get_data(); + void get_nested(); + void get_nested_data(); + void crash_model_with_multiple_roles(); + void set_model_cache(); + void property_changes(); + void property_changes_data(); + void clear_data(); + void clear(); + void signal_handlers_data(); + void signal_handlers(); + void role_mode_data(); + void role_mode(); + void string_to_list_crash(); + void empty_element_warning(); + void empty_element_warning_data(); + void datetime(); + void datetime_data(); +}; + +bool tst_qqmllistmodel::compareVariantList(const QVariantList &testList, QVariant object) +{ + bool allOk = true; + + QQmlListModel *model = qobject_cast(object.value()); + if (model == 0) + return false; + + if (model->count() != testList.count()) + return false; + + for (int i=0 ; i < testList.count() ; ++i) { + const QVariant &testVariant = testList.at(i); + if (testVariant.type() != QVariant::Map) + return false; + const QVariantMap &map = testVariant.toMap(); + + const QHash roleNames = model->roleNames(); + + QVariantMap::const_iterator it = map.begin(); + QVariantMap::const_iterator end = map.end(); + + while (it != end) { + const QString &testKey = it.key(); + const QVariant &testData = it.value(); + + int roleIndex = roleNames.key(testKey.toUtf8(), -1); + if (roleIndex == -1) + return false; + + const QVariant &modelData = model->data(i, roleIndex); + + if (testData.type() == QVariant::List) { + const QVariantList &subList = testData.toList(); + allOk = allOk && compareVariantList(subList, modelData); + } else { + allOk = allOk && (testData == modelData); + } + + ++it; + } + } + + return allOk; +} + +int tst_qqmllistmodel::roleFromName(const QQmlListModel *model, const QString &roleName) +{ + return model->roleNames().key(roleName.toUtf8(), -1); +} + +void tst_qqmllistmodel::static_types_data() +{ + QTest::addColumn("qml"); + QTest::addColumn("value"); + QTest::addColumn("error"); + + QTest::newRow("string") + << "ListElement { foo: \"bar\" }" + << QVariant(QString("bar")) + << QString(); + + QTest::newRow("real") + << "ListElement { foo: 10.5 }" + << QVariant(10.5) + << QString(); + + QTest::newRow("real0") + << "ListElement { foo: 0 }" + << QVariant(double(0)) + << QString(); + + QTest::newRow("bool") + << "ListElement { foo: false }" + << QVariant(false) + << QString(); + + QTest::newRow("bool") + << "ListElement { foo: true }" + << QVariant(true) + << QString(); + + QTest::newRow("enum") + << "ListElement { foo: Text.AlignHCenter }" + << QVariant(double(QQuickText::AlignHCenter)) + << QString(); + + QTest::newRow("Qt enum") + << "ListElement { foo: Qt.AlignBottom }" + << QVariant(double(Qt::AlignBottom)) + << QString(); + + QTest::newRow("negative enum") + << "ListElement { foo: Animation.Infinite }" + << QVariant(double(QQuickAbstractAnimation::Infinite)) + << QString(); + + QTest::newRow("role error") + << "ListElement { foo: 1 } ListElement { foo: 'string' }" + << QVariant() + << QString(": Can't assign to existing role 'foo' of different type [String -> Number]"); + + QTest::newRow("list type error") + << "ListElement { foo: 1 } ListElement { foo: ListElement { bar: 1 } }" + << QVariant() + << QString(": Can't assign to existing role 'foo' of different type [List -> Number]"); +} + +void tst_qqmllistmodel::static_types() +{ + QFETCH(QString, qml); + QFETCH(QVariant, value); + QFETCH(QString, error); + + qml = "import QtQuick 2.0\nItem { property variant test: model.get(0).foo; ListModel { id: model; " + qml + " } }"; + + if (!error.isEmpty()) { + QTest::ignoreMessage(QtWarningMsg, error.toLatin1()); + } + + QQmlEngine engine; + QQmlComponent component(&engine); + component.setData(qml.toUtf8(), + QUrl::fromLocalFile(QString("dummy.qml"))); + + QVERIFY(!component.isError()); + + QObject *obj = component.create(); + QVERIFY(obj != 0); + + if (error.isEmpty()) { + QVariant actual = obj->property("test"); + + QCOMPARE(actual, value); + QCOMPARE(actual.toString(), value.toString()); + } + + delete obj; +} + +void tst_qqmllistmodel::static_i18n_data() +{ + QTest::addColumn("qml"); + QTest::addColumn("value"); + QTest::addColumn("error"); + + QTest::newRow("QT_TR_NOOP") + << QString::fromUtf8("ListElement { foo: QT_TR_NOOP(\"na\303\257ve\") }") + << QVariant(QString::fromUtf8("na\303\257ve")) + << QString(); + + QTest::newRow("QT_TRANSLATE_NOOP") + << "ListElement { foo: QT_TRANSLATE_NOOP(\"MyListModel\", \"hello\") }" + << QVariant(QString("hello")) + << QString(); + + QTest::newRow("QT_TRID_NOOP") + << QString::fromUtf8("ListElement { foo: QT_TRID_NOOP(\"qtn_1st_text\") }") + << QVariant(QString("qtn_1st_text")) + << QString(); + + QTest::newRow("QT_TR_NOOP extra param") + << QString::fromUtf8("ListElement { foo: QT_TR_NOOP(\"hello\",\"world\") }") + << QVariant(QString()) + << QString("ListElement: improperly specified QT_TR_NOOP"); + + QTest::newRow("QT_TRANSLATE_NOOP missing params") + << "ListElement { foo: QT_TRANSLATE_NOOP() }" + << QVariant(QString()) + << QString("ListElement: improperly specified QT_TRANSLATE_NOOP"); + + QTest::newRow("QT_TRID_NOOP missing param") + << QString::fromUtf8("ListElement { foo: QT_TRID_NOOP() }") + << QVariant(QString()) + << QString("ListElement: improperly specified QT_TRID_NOOP"); +} + +void tst_qqmllistmodel::static_i18n() +{ + QFETCH(QString, qml); + QFETCH(QVariant, value); + QFETCH(QString, error); + + qml = "import QtQuick 2.0\nItem { property variant test: model.get(0).foo; ListModel { id: model; " + qml + " } }"; + + QQmlEngine engine; + QQmlComponent component(&engine); + component.setData(qml.toUtf8(), + QUrl::fromLocalFile(QString("dummy.qml"))); + + if (!error.isEmpty()) { + QVERIFY(component.isError()); + QCOMPARE(component.errors().at(0).description(), error); + return; + } + + QVERIFY(!component.isError()); + + QObject *obj = component.create(); + QVERIFY(obj != 0); + + QVariant actual = obj->property("test"); + + QCOMPARE(actual, value); + QCOMPARE(actual.toString(), value.toString()); + + delete obj; +} + +void tst_qqmllistmodel::static_nestedElements() +{ + QFETCH(int, elementCount); + + QStringList elements; + for (int i=0; iproperty("count"); + QCOMPARE(count.type(), QVariant::Int); + QCOMPARE(count.toInt(), elementCount); + + delete obj; +} + +void tst_qqmllistmodel::static_nestedElements_data() +{ + QTest::addColumn("elementCount"); + + QTest::newRow("0 items") << 0; + QTest::newRow("1 item") << 1; + QTest::newRow("2 items") << 2; + QTest::newRow("many items") << 5; +} + +void tst_qqmllistmodel::dynamic_data() +{ + QTest::addColumn("script"); + QTest::addColumn("result"); + QTest::addColumn("warning"); + QTest::addColumn("dynamicRoles"); + + for (int i=0 ; i < 2 ; ++i) { + bool dr = (i != 0); + + // Simple flat model + QTest::newRow("count") << "count" << 0 << "" << dr; + + QTest::newRow("get1") << "{get(0) === undefined}" << 1 << "" << dr; + QTest::newRow("get2") << "{get(-1) === undefined}" << 1 << "" << dr; + QTest::newRow("get3") << "{append({'foo':123});get(0) != undefined}" << 1 << "" << dr; + QTest::newRow("get4") << "{append({'foo':123});get(0).foo}" << 123 << "" << dr; + QTest::newRow("get-modify1") << "{append({'foo':123,'bar':456});get(0).foo = 333;get(0).foo}" << 333 << "" << dr; + QTest::newRow("get-modify2") << "{append({'z':1});append({'foo':123,'bar':456});get(1).bar = 999;get(1).bar}" << 999 << "" << dr; + + QTest::newRow("append1") << "{append({'foo':123});count}" << 1 << "" << dr; + QTest::newRow("append2") << "{append({'foo':123,'bar':456});count}" << 1 << "" << dr; + QTest::newRow("append3a") << "{append({'foo':123});append({'foo':456});get(0).foo}" << 123 << "" << dr; + QTest::newRow("append3b") << "{append({'foo':123});append({'foo':456});get(1).foo}" << 456 << "" << dr; + QTest::newRow("append4a") << "{append(123)}" << 0 << ": QML ListModel: append: value is not an object" << dr; + QTest::newRow("append4b") << "{append([{'foo':123},{'foo':456},{'foo':789}]);count}" << 3 << "" << dr; + QTest::newRow("append4c") << "{append([{'foo':123},{'foo':456},{'foo':789}]);get(1).foo}" << 456 << "" << dr; + + QTest::newRow("clear1") << "{append({'foo':456});clear();count}" << 0 << "" << dr; + QTest::newRow("clear2") << "{append({'foo':123});append({'foo':456});clear();count}" << 0 << "" << dr; + QTest::newRow("clear3") << "{append({'foo':123});clear()}" << 0 << "" << dr; + + QTest::newRow("remove1") << "{append({'foo':123});remove(0);count}" << 0 << "" << dr; + QTest::newRow("remove2a") << "{append({'foo':123});append({'foo':456});remove(0);count}" << 1 << "" << dr; + QTest::newRow("remove2b") << "{append({'foo':123});append({'foo':456});remove(0);get(0).foo}" << 456 << "" << dr; + QTest::newRow("remove2c") << "{append({'foo':123});append({'foo':456});remove(1);get(0).foo}" << 123 << "" << dr; + QTest::newRow("remove3") << "{append({'foo':123});remove(0)}" << 0 << "" << dr; + QTest::newRow("remove3a") << "{append({'foo':123});remove(-1);count}" << 1 << ": QML ListModel: remove: indices [-1 - 0] out of range [0 - 1]" << dr; + QTest::newRow("remove4a") << "{remove(0)}" << 0 << ": QML ListModel: remove: indices [0 - 1] out of range [0 - 0]" << dr; + QTest::newRow("remove4b") << "{append({'foo':123});remove(0);remove(0);count}" << 0 << ": QML ListModel: remove: indices [0 - 1] out of range [0 - 0]" << dr; + QTest::newRow("remove4c") << "{append({'foo':123});remove(1);count}" << 1 << ": QML ListModel: remove: indices [1 - 2] out of range [0 - 1]" << dr; + QTest::newRow("remove5a") << "{append({'foo':123});append({'foo':456});remove(0,2);count}" << 0 << "" << dr; + QTest::newRow("remove5b") << "{append({'foo':123});append({'foo':456});remove(0,1);count}" << 1 << "" << dr; + QTest::newRow("remove5c") << "{append({'foo':123});append({'foo':456});remove(1,1);count}" << 1 << "" << dr; + QTest::newRow("remove5d") << "{append({'foo':123});append({'foo':456});remove(0,1);get(0).foo}" << 456 << "" << dr; + QTest::newRow("remove5e") << "{append({'foo':123});append({'foo':456});remove(1,1);get(0).foo}" << 123 << "" << dr; + QTest::newRow("remove5f") << "{append({'foo':123});append({'foo':456});append({'foo':789});remove(0,1);remove(1,1);get(0).foo}" << 456 << "" << dr; + QTest::newRow("remove6a") << "{remove();count}" << 0 << ": QML ListModel: remove: incorrect number of arguments" << dr; + QTest::newRow("remove6b") << "{remove(1,2,3);count}" << 0 << ": QML ListModel: remove: incorrect number of arguments" << dr; + QTest::newRow("remove7a") << "{append({'foo':123});remove(0,0);count}" << 1 << ": QML ListModel: remove: indices [0 - 0] out of range [0 - 1]" << dr; + QTest::newRow("remove7b") << "{append({'foo':123});remove(0,-1);count}" << 1 << ": QML ListModel: remove: indices [0 - -1] out of range [0 - 1]" << dr; + + QTest::newRow("insert1") << "{insert(0,{'foo':123});count}" << 1 << "" << dr; + QTest::newRow("insert2") << "{insert(1,{'foo':123});count}" << 0 << ": QML ListModel: insert: index 1 out of range" << dr; + QTest::newRow("insert3a") << "{append({'foo':123});insert(1,{'foo':456});count}" << 2 << "" << dr; + QTest::newRow("insert3b") << "{append({'foo':123});insert(1,{'foo':456});get(0).foo}" << 123 << "" << dr; + QTest::newRow("insert3c") << "{append({'foo':123});insert(1,{'foo':456});get(1).foo}" << 456 << "" << dr; + QTest::newRow("insert3d") << "{append({'foo':123});insert(0,{'foo':456});get(0).foo}" << 456 << "" << dr; + QTest::newRow("insert3e") << "{append({'foo':123});insert(0,{'foo':456});get(1).foo}" << 123 << "" << dr; + QTest::newRow("insert4") << "{append({'foo':123});insert(-1,{'foo':456});count}" << 1 << ": QML ListModel: insert: index -1 out of range" << dr; + QTest::newRow("insert5a") << "{insert(0,123)}" << 0 << ": QML ListModel: insert: value is not an object" << dr; + QTest::newRow("insert5b") << "{insert(0,[{'foo':11},{'foo':22},{'foo':33}]);count}" << 3 << "" << dr; + QTest::newRow("insert5c") << "{insert(0,[{'foo':11},{'foo':22},{'foo':33}]);get(2).foo}" << 33 << "" << dr; + + QTest::newRow("set1") << "{append({'foo':123});set(0,{'foo':456});count}" << 1 << "" << dr; + QTest::newRow("set2") << "{append({'foo':123});set(0,{'foo':456});get(0).foo}" << 456 << "" << dr; + QTest::newRow("set3a") << "{append({'foo':123,'bar':456});set(0,{'foo':999});get(0).foo}" << 999 << "" << dr; + QTest::newRow("set3b") << "{append({'foo':123,'bar':456});set(0,{'foo':999});get(0).bar}" << 456 << "" << dr; + QTest::newRow("set4a") << "{set(0,{'foo':456});count}" << 1 << "" << dr; + QTest::newRow("set4c") << "{set(-1,{'foo':456})}" << 0 << ": QML ListModel: set: index -1 out of range" << dr; + QTest::newRow("set5a") << "{append({'foo':123,'bar':456});set(0,123);count}" << 1 << ": QML ListModel: set: value is not an object" << dr; + QTest::newRow("set5b") << "{append({'foo':123,'bar':456});set(0,[1,2,3]);count}" << 1 << ": QML ListModel: set: value is not an object" << dr; + QTest::newRow("set6") << "{append({'foo':123});set(1,{'foo':456});count}" << 2 << "" << dr; + + QTest::newRow("setprop1") << "{append({'foo':123});setProperty(0,'foo',456);count}" << 1 << "" << dr; + QTest::newRow("setprop2") << "{append({'foo':123});setProperty(0,'foo',456);get(0).foo}" << 456 << "" << dr; + QTest::newRow("setprop3a") << "{append({'foo':123,'bar':456});setProperty(0,'foo',999);get(0).foo}" << 999 << "" << dr; + QTest::newRow("setprop3b") << "{append({'foo':123,'bar':456});setProperty(0,'foo',999);get(0).bar}" << 456 << "" << dr; + QTest::newRow("setprop4a") << "{setProperty(0,'foo',456)}" << 0 << ": QML ListModel: set: index 0 out of range" << dr; + QTest::newRow("setprop4b") << "{setProperty(-1,'foo',456)}" << 0 << ": QML ListModel: set: index -1 out of range" << dr; + QTest::newRow("setprop4c") << "{append({'foo':123,'bar':456});setProperty(1,'foo',456);count}" << 1 << ": QML ListModel: set: index 1 out of range" << dr; + QTest::newRow("setprop5") << "{append({'foo':123,'bar':456});append({'foo':111});setProperty(1,'bar',222);get(1).bar}" << 222 << "" << dr; + + QTest::newRow("move1a") << "{append({'foo':123});append({'foo':456});move(0,1,1);count}" << 2 << "" << dr; + QTest::newRow("move1b") << "{append({'foo':123});append({'foo':456});move(0,1,1);get(0).foo}" << 456 << "" << dr; + QTest::newRow("move1c") << "{append({'foo':123});append({'foo':456});move(0,1,1);get(1).foo}" << 123 << "" << dr; + QTest::newRow("move1d") << "{append({'foo':123});append({'foo':456});move(1,0,1);get(0).foo}" << 456 << "" << dr; + QTest::newRow("move1e") << "{append({'foo':123});append({'foo':456});move(1,0,1);get(1).foo}" << 123 << "" << dr; + QTest::newRow("move2a") << "{append({'foo':123});append({'foo':456});append({'foo':789});move(0,1,2);count}" << 3 << "" << dr; + QTest::newRow("move2b") << "{append({'foo':123});append({'foo':456});append({'foo':789});move(0,1,2);get(0).foo}" << 789 << "" << dr; + QTest::newRow("move2c") << "{append({'foo':123});append({'foo':456});append({'foo':789});move(0,1,2);get(1).foo}" << 123 << "" << dr; + QTest::newRow("move2d") << "{append({'foo':123});append({'foo':456});append({'foo':789});move(0,1,2);get(2).foo}" << 456 << "" << dr; + QTest::newRow("move3a") << "{append({'foo':123});append({'foo':456});append({'foo':789});move(1,0,3);count}" << 3 << ": QML ListModel: move: out of range" << dr; + QTest::newRow("move3b") << "{append({'foo':123});append({'foo':456});append({'foo':789});move(1,-1,1);count}" << 3 << ": QML ListModel: move: out of range" << dr; + QTest::newRow("move3c") << "{append({'foo':123});append({'foo':456});append({'foo':789});move(1,0,-1);count}" << 3 << ": QML ListModel: move: out of range" << dr; + QTest::newRow("move3d") << "{append({'foo':123});append({'foo':456});append({'foo':789});move(0,3,1);count}" << 3 << ": QML ListModel: move: out of range" << dr; + + QTest::newRow("large1") << "{append({'a':1,'b':2,'c':3,'d':4,'e':5,'f':6,'g':7,'h':8});get(0).h}" << 8 << "" << dr; + + QTest::newRow("datatypes1") << "{append({'a':1});append({'a':'string'});}" << 0 << ": Can't assign to existing role 'a' of different type [String -> Number]" << dr; + + QTest::newRow("null") << "{append({'a':null});}" << 0 << "" << dr; + QTest::newRow("setNull") << "{append({'a':1});set(0, {'a':null});}" << 0 << "" << dr; + QTest::newRow("setString") << "{append({'a':'hello'});set(0, {'a':'world'});get(0).a == 'world'}" << 1 << "" << dr; + QTest::newRow("setInt") << "{append({'a':5});set(0, {'a':10});get(0).a}" << 10 << "" << dr; + QTest::newRow("setNumber") << "{append({'a':6});set(0, {'a':5.5});get(0).a < 5.6}" << 1 << "" << dr; + QTest::newRow("badType0") << "{append({'a':'hello'});set(0, {'a':1});}" << 0 << ": Can't assign to existing role 'a' of different type [Number -> String]" << dr; + QTest::newRow("invalidInsert0") << "{insert(0);}" << 0 << ": QML ListModel: insert: value is not an object" << dr; + QTest::newRow("invalidAppend0") << "{append();}" << 0 << ": QML ListModel: append: value is not an object" << dr; + QTest::newRow("invalidInsert1") << "{insert(0, 34);}" << 0 << ": QML ListModel: insert: value is not an object" << dr; + QTest::newRow("invalidAppend1") << "{append(37);}" << 0 << ": QML ListModel: append: value is not an object" << dr; + + // QObjects + QTest::newRow("qobject0") << "{append({'a':dummyItem0});}" << 0 << "" << dr; + QTest::newRow("qobject1") << "{append({'a':dummyItem0});set(0,{'a':dummyItem1});get(0).a == dummyItem1;}" << 1 << "" << dr; + QTest::newRow("qobject2") << "{append({'a':dummyItem0});get(0).a == dummyItem0;}" << 1 << "" << dr; + QTest::newRow("qobject3") << "{append({'a':dummyItem0});append({'b':1});}" << 0 << "" << dr; + + // JS objects + QTest::newRow("js1") << "{append({'foo':{'prop':1}});count}" << 1 << "" << dr; + QTest::newRow("js2") << "{append({'foo':{'prop':27}});get(0).foo.prop}" << 27 << "" << dr; + QTest::newRow("js3") << "{append({'foo':{'prop':27}});append({'bar':1});count}" << 2 << "" << dr; + QTest::newRow("js4") << "{append({'foo':{'prop':27}});append({'bar':1});set(0, {'foo':{'prop':28}});get(0).foo.prop}" << 28 << "" << dr; + QTest::newRow("js5") << "{append({'foo':{'prop':27}});append({'bar':1});set(1, {'foo':{'prop':33}});get(1).foo.prop}" << 33 << "" << dr; + QTest::newRow("js6") << "{append({'foo':{'prop':27}});clear();count}" << 0 << "" << dr; + QTest::newRow("js7") << "{append({'foo':{'prop':27}});set(0, {'foo':null});count}" << 1 << "" << dr; + QTest::newRow("js8") << "{append({'foo':{'prop':27}});set(0, {'foo':{'prop2':31}});get(0).foo.prop2}" << 31 << "" << dr; + + // Nested models + QTest::newRow("nested-append1") << "{append({'foo':123,'bars':[{'a':1},{'a':2},{'a':3}]});count}" << 1 << "" << dr; + QTest::newRow("nested-append2") << "{append({'foo':123,'bars':[{'a':1},{'a':2},{'a':3}]});get(0).bars.get(1).a}" << 2 << "" << dr; + QTest::newRow("nested-append3") << "{append({'foo':123,'bars':[{'a':1},{'a':2},{'a':3}]});get(0).bars.append({'a':4});get(0).bars.get(3).a}" << 4 << "" << dr; + + QTest::newRow("nested-insert") << "{append({'foo':123});insert(0,{'bars':[{'a':1},{'b':2},{'c':3}]});get(0).bars.get(0).a}" << 1 << "" << dr; + QTest::newRow("nested-set") << "{append({'foo':[{'x':1}]});set(0,{'foo':[{'x':123}]});get(0).foo.get(0).x}" << 123 << "" << dr; + + QTest::newRow("nested-count") << "{append({'foo':123,'bars':[{'a':1},{'a':2},{'a':3}]}); get(0).bars.count}" << 3 << "" << dr; + QTest::newRow("nested-clear") << "{append({'foo':123,'bars':[{'a':1},{'a':2},{'a':3}]}); get(0).bars.clear(); get(0).bars.count}" << 0 << "" << dr; + } +} + +void tst_qqmllistmodel::dynamic() +{ + QFETCH(QString, script); + QFETCH(int, result); + QFETCH(QString, warning); + QFETCH(bool, dynamicRoles); + + QQuickItem dummyItem0, dummyItem1; + QQmlEngine engine; + QQmlListModel model; + model.setDynamicRoles(dynamicRoles); + QQmlEngine::setContextForObject(&model,engine.rootContext()); + engine.rootContext()->setContextObject(&model); + engine.rootContext()->setContextProperty("dummyItem0", QVariant::fromValue(&dummyItem0)); + engine.rootContext()->setContextProperty("dummyItem1", QVariant::fromValue(&dummyItem1)); + QQmlExpression e(engine.rootContext(), &model, script); + if (isValidErrorMessage(warning, dynamicRoles)) + QTest::ignoreMessage(QtWarningMsg, warning.toLatin1()); + + QSignalSpy spyCount(&model, SIGNAL(countChanged())); + + int actual = e.evaluate().toInt(); + if (e.hasError()) + qDebug() << e.error(); // errors not expected + + QCOMPARE(actual,result); + + if (model.count() > 0) + QVERIFY(spyCount.count() > 0); +} + +void tst_qqmllistmodel::enumerate() +{ + QQmlEngine eng; + QQmlComponent component(&eng, testFileUrl("enumerate.qml")); + QVERIFY(!component.isError()); + QQuickItem *item = qobject_cast(component.create()); + QVERIFY(item != 0); + + QLatin1String expectedStrings[] = { + QLatin1String("val1=1Y"), + QLatin1String("val2=2Y"), + QLatin1String("val3=strY"), + QLatin1String("val4=falseN"), + QLatin1String("val5=trueY") + }; + + int expectedStringCount = sizeof(expectedStrings) / sizeof(expectedStrings[0]); + + QStringList r = item->property("result").toString().split(":"); + + int matchCount = 0; + for (int i=0 ; i < expectedStringCount ; ++i) { + const QLatin1String &expectedString = expectedStrings[i]; + + QStringList::const_iterator it = r.begin(); + QStringList::const_iterator end = r.end(); + + while (it != end) { + if (it->compare(expectedString) == 0) { + ++matchCount; + break; + } + ++it; + } + } + + QVERIFY(matchCount == expectedStringCount); + + delete item; +} + +void tst_qqmllistmodel::error_data() +{ + QTest::addColumn("qml"); + QTest::addColumn("error"); + + QTest::newRow("id not allowed in ListElement") + << "import QtQuick 2.0\nListModel { ListElement { id: fred } }" + << "ListElement: cannot use reserved \"id\" property"; + + QTest::newRow("id allowed in ListModel") + << "import QtQuick 2.0\nListModel { id:model }" + << ""; + + QTest::newRow("random properties not allowed in ListModel") + << "import QtQuick 2.0\nListModel { foo:123 }" + << "ListModel: undefined property 'foo'"; + + QTest::newRow("random properties allowed in ListElement") + << "import QtQuick 2.0\nListModel { ListElement { foo:123 } }" + << ""; + + QTest::newRow("bindings not allowed in ListElement") + << "import QtQuick 2.0\nRectangle { id: rect; ListModel { ListElement { foo: rect.color } } }" + << "ListElement: cannot use script for property value"; + + QTest::newRow("random object list properties allowed in ListElement") + << "import QtQuick 2.0\nListModel { ListElement { foo: [ ListElement { bar: 123 } ] } }" + << ""; + + QTest::newRow("default properties not allowed in ListElement") + << "import QtQuick 2.0\nListModel { ListElement { Item { } } }" + << "ListElement: cannot contain nested elements"; + + QTest::newRow("QML elements not allowed in ListElement") + << "import QtQuick 2.0\nListModel { ListElement { a: Item { } } }" + << "ListElement: cannot contain nested elements"; + + QTest::newRow("qualified ListElement supported") + << "import QtQuick 2.0 as Foo\nFoo.ListModel { Foo.ListElement { a: 123 } }" + << ""; + + QTest::newRow("qualified ListElement required") + << "import QtQuick 2.0 as Foo\nFoo.ListModel { ListElement { a: 123 } }" + << "ListElement is not a type"; + + QTest::newRow("unknown qualified ListElement not allowed") + << "import QtQuick 2.0\nListModel { Foo.ListElement { a: 123 } }" + << "Foo.ListElement - Foo is not a namespace"; +} + +void tst_qqmllistmodel::error() +{ + QFETCH(QString, qml); + QFETCH(QString, error); + + QQmlEngine engine; + QQmlComponent component(&engine); + component.setData(qml.toUtf8(), + QUrl::fromLocalFile(QString("dummy.qml"))); + if (error.isEmpty()) { + QVERIFY(!component.isError()); + } else { + QVERIFY(component.isError()); + QList errors = component.errors(); + QCOMPARE(errors.count(),1); + QCOMPARE(errors.at(0).description(),error); + } +} + +void tst_qqmllistmodel::syncError() +{ + QString qml = "import QtQuick 2.0\nListModel { id: lm; Component.onCompleted: lm.sync() }"; + QString error = "file:dummy.qml:2:1: QML ListModel: List sync() can only be called from a WorkerScript"; + + QQmlEngine engine; + QQmlComponent component(&engine); + component.setData(qml.toUtf8(), + QUrl::fromLocalFile(QString("dummy.qml"))); + QTest::ignoreMessage(QtWarningMsg,error.toUtf8()); + QObject *obj = component.create(); + QVERIFY(obj); + delete obj; +} + +/* + Test model changes from set() are available to the view +*/ +void tst_qqmllistmodel::set_data() +{ + QTest::addColumn("dynamicRoles"); + + QTest::newRow("staticRoles") << false; + QTest::newRow("dynamicRoles") << true; +} + +void tst_qqmllistmodel::set() +{ + QFETCH(bool, dynamicRoles); + + QQmlEngine engine; + QQmlListModel model; + model.setDynamicRoles(dynamicRoles); + QQmlEngine::setContextForObject(&model,engine.rootContext()); + engine.rootContext()->setContextProperty("model", &model); + + RUNEXPR("model.append({test:false})"); + RUNEXPR("model.set(0, {test:true})"); + + QCOMPARE(RUNEXPR("model.get(0).test").toBool(), true); // triggers creation of model cache + QCOMPARE(model.data(0, 0), qVariantFromValue(true)); + + RUNEXPR("model.set(0, {test:false})"); + QCOMPARE(RUNEXPR("model.get(0).test").toBool(), false); // tests model cache is updated + QCOMPARE(model.data(0, 0), qVariantFromValue(false)); + + QString warning = QString::fromLatin1(": Can't create role for unsupported data type"); + if (isValidErrorMessage(warning, dynamicRoles)) + QTest::ignoreMessage(QtWarningMsg, warning.toLatin1()); + QVariant invalidData = QColor(); + model.setProperty(0, "test", invalidData); +} + +/* + Test model changes on values returned by get() are available to the view +*/ +void tst_qqmllistmodel::get() +{ + QFETCH(QString, expression); + QFETCH(int, index); + QFETCH(QString, roleName); + QFETCH(QVariant, roleValue); + QFETCH(bool, dynamicRoles); + + QQmlEngine engine; + QQmlComponent component(&engine); + component.setData( + "import QtQuick 2.0\n" + "ListModel {}\n", QUrl()); + QQmlListModel *model = qobject_cast(component.create()); + model->setDynamicRoles(dynamicRoles); + engine.rootContext()->setContextProperty("model", model); + + RUNEXPR("model.append({roleA: 100})"); + RUNEXPR("model.append({roleA: 200, roleB: 400})"); + RUNEXPR("model.append({roleA: 200, roleB: 400})"); + RUNEXPR("model.append({roleC: {} })"); + RUNEXPR("model.append({roleD: [ { a:1, b:2 }, { c: 3 } ] })"); + + QSignalSpy spy(model, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector))); + QQmlExpression expr(engine.rootContext(), model, expression); + expr.evaluate(); + QVERIFY(!expr.hasError()); + + int role = roleFromName(model, roleName); + QVERIFY(role >= 0); + + if (roleValue.type() == QVariant::List) { + const QVariantList &list = roleValue.toList(); + QVERIFY(compareVariantList(list, model->data(index, role))); + } else { + QCOMPARE(model->data(index, role), roleValue); + } + + QCOMPARE(spy.count(), 1); + + QList spyResult = spy.takeFirst(); + QCOMPARE(spyResult.at(0).value(), model->index(index, 0, QModelIndex())); + QCOMPARE(spyResult.at(1).value(), model->index(index, 0, QModelIndex())); // only 1 item is modified at a time + QCOMPARE(spyResult.at(2).value >(), (QVector() << role)); + + delete model; +} + +void tst_qqmllistmodel::get_data() +{ + QTest::addColumn("expression"); + QTest::addColumn("index"); + QTest::addColumn("roleName"); + QTest::addColumn("roleValue"); + QTest::addColumn("dynamicRoles"); + + for (int i=0 ; i < 2 ; ++i) { + bool dr = (i != 0); + + QTest::newRow("simple value") << "get(0).roleA = 500" << 0 << "roleA" << QVariant(500) << dr; + QTest::newRow("simple value 2") << "get(1).roleB = 500" << 1 << "roleB" << QVariant(500) << dr; + + QVariantMap map; + QVariantList list; + map.clear(); map["a"] = 50; map["b"] = 500; + list << map; + map.clear(); map["c"] = 1000; + list << map; + QTest::newRow("list of objects") << "get(2).roleD = [{'a': 50, 'b': 500}, {'c': 1000}]" << 2 << "roleD" << QVariant::fromValue(list) << dr; + } +} + +/* + Test that the tests run in get() also work for nested list data +*/ +void tst_qqmllistmodel::get_nested() +{ + QFETCH(QString, expression); + QFETCH(int, index); + QFETCH(QString, roleName); + QFETCH(QVariant, roleValue); + QFETCH(bool, dynamicRoles); + + if (roleValue.type() == QVariant::Map) + return; + + QQmlEngine engine; + QQmlComponent component(&engine); + component.setData( + "import QtQuick 2.0\n" + "ListModel {}", QUrl()); + QQmlListModel *model = qobject_cast(component.create()); + model->setDynamicRoles(dynamicRoles); + QVERIFY(component.errorString().isEmpty()); + QQmlListModel *childModel; + engine.rootContext()->setContextProperty("model", model); + + RUNEXPR("model.append({ listRoleA: [\n" + "{ roleA: 100 },\n" + "{ roleA: 200, roleB: 400 },\n" + "{ roleA: 200, roleB: 400 }, \n" + "{ roleC: {} }, \n" + "{ roleD: [ { a: 1, b:2 }, { c: 3 } ] } \n" + "] })\n"); + + RUNEXPR("model.append({ listRoleA: [\n" + "{ roleA: 100 },\n" + "{ roleA: 200, roleB: 400 },\n" + "{ roleA: 200, roleB: 400 }, \n" + "{ roleC: {} }, \n" + "{ roleD: [ { a: 1, b:2 }, { c: 3 } ] } \n" + "],\n" + "listRoleB: [\n" + "{ roleA: 100 },\n" + "{ roleA: 200, roleB: 400 },\n" + "{ roleA: 200, roleB: 400 }, \n" + "{ roleC: {} }, \n" + "{ roleD: [ { a: 1, b:2 }, { c: 3 } ] } \n" + "],\n" + "listRoleC: [\n" + "{ roleA: 100 },\n" + "{ roleA: 200, roleB: 400 },\n" + "{ roleA: 200, roleB: 400 }, \n" + "{ roleC: {} }, \n" + "{ roleD: [ { a: 1, b:2 }, { c: 3 } ] } \n" + "] })\n"); + + // Test setting the inner list data for: + // get(0).listRoleA + // get(1).listRoleA + // get(1).listRoleB + // get(1).listRoleC + + QList > testData; + testData << qMakePair(0, QString("listRoleA")); + testData << qMakePair(1, QString("listRoleA")); + testData << qMakePair(1, QString("listRoleB")); + testData << qMakePair(1, QString("listRoleC")); + + for (int i=0; i= 0); + + childModel = qobject_cast(model->data(outerListIndex, outerListRole).value()); + QVERIFY(childModel); + + QString extendedExpression = QString("get(%1).%2.%3").arg(outerListIndex).arg(outerListRoleName).arg(expression); + QQmlExpression expr(engine.rootContext(), model, extendedExpression); + + QSignalSpy spy(childModel, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector))); + expr.evaluate(); + QVERIFY(!expr.hasError()); + + int role = roleFromName(childModel, roleName); + QVERIFY(role >= 0); + if (roleValue.type() == QVariant::List) { + QVERIFY(compareVariantList(roleValue.toList(), childModel->data(index, role))); + } else { + QCOMPARE(childModel->data(index, role), roleValue); + } + QCOMPARE(spy.count(), 1); + + QList spyResult = spy.takeFirst(); + QCOMPARE(spyResult.at(0).value(), childModel->index(index, 0, QModelIndex())); + QCOMPARE(spyResult.at(1).value(), childModel->index(index, 0, QModelIndex())); // only 1 item is modified at a time + QCOMPARE(spyResult.at(2).value >(), (QVector() << role)); + } + + delete model; +} + +void tst_qqmllistmodel::get_nested_data() +{ + get_data(); +} + +//QTBUG-13754 +void tst_qqmllistmodel::crash_model_with_multiple_roles() +{ + QQmlEngine eng; + QQmlComponent component(&eng, testFileUrl("multipleroles.qml")); + QObject *rootItem = component.create(); + QVERIFY(component.errorString().isEmpty()); + QVERIFY(rootItem != 0); + QQmlListModel *model = rootItem->findChild("listModel"); + QVERIFY(model != 0); + + // used to cause a crash + model->setProperty(0, "black", true); + + delete rootItem; +} + +//QTBUG-15190 +void tst_qqmllistmodel::set_model_cache() +{ + QQmlEngine eng; + QQmlComponent component(&eng, testFileUrl("setmodelcachelist.qml")); + QObject *model = component.create(); + QVERIFY2(component.errorString().isEmpty(), QTest::toString(component.errorString())); + QVERIFY(model != 0); + QVERIFY(model->property("ok").toBool()); + + delete model; +} + +void tst_qqmllistmodel::property_changes() +{ + QFETCH(QString, script_setup); + QFETCH(QString, script_change); + QFETCH(QString, roleName); + QFETCH(int, listIndex); + QFETCH(bool, itemsChanged); + QFETCH(QString, testExpression); + QFETCH(bool, dynamicRoles); + + QQmlEngine engine; + QQmlListModel model; + model.setDynamicRoles(dynamicRoles); + QQmlEngine::setContextForObject(&model, engine.rootContext()); + engine.rootContext()->setContextObject(&model); + + QQmlExpression expr(engine.rootContext(), &model, script_setup); + expr.evaluate(); + QVERIFY2(!expr.hasError(), QTest::toString(expr.error().toString())); + + QString signalHandler = "on" + QString(roleName[0].toUpper()) + roleName.mid(1, roleName.length()) + "Changed:"; + QString qml = "import QtQuick 2.0\n" + "Connections {\n" + "property bool gotSignal: false\n" + "target: model.get(" + QString::number(listIndex) + ")\n" + + signalHandler + " gotSignal = true\n" + "}\n"; + + QQmlComponent component(&engine); + component.setData(qml.toUtf8(), QUrl::fromLocalFile("")); + engine.rootContext()->setContextProperty("model", &model); + QObject *connectionsObject = component.create(); + QVERIFY2(component.errorString().isEmpty(), QTest::toString(component.errorString())); + + QSignalSpy spyItemsChanged(&model, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector))); + + expr.setExpression(script_change); + expr.evaluate(); + QVERIFY2(!expr.hasError(), QTest::toString(expr.error())); + + // test the object returned by get() emits the correct signals + QCOMPARE(connectionsObject->property("gotSignal").toBool(), itemsChanged); + + // test itemsChanged() is emitted correctly + if (itemsChanged) { + QCOMPARE(spyItemsChanged.count(), 1); + QCOMPARE(spyItemsChanged.at(0).at(0).value(), model.index(listIndex, 0, QModelIndex())); + QCOMPARE(spyItemsChanged.at(0).at(1).value(), model.index(listIndex, 0, QModelIndex())); + } else { + QCOMPARE(spyItemsChanged.count(), 0); + } + + expr.setExpression(testExpression); + QCOMPARE(expr.evaluate().toBool(), true); + + delete connectionsObject; +} + +void tst_qqmllistmodel::property_changes_data() +{ + QTest::addColumn("script_setup"); + QTest::addColumn("script_change"); + QTest::addColumn("roleName"); + QTest::addColumn("listIndex"); + QTest::addColumn("itemsChanged"); + QTest::addColumn("testExpression"); + QTest::addColumn("dynamicRoles"); + + for (int i=0 ; i < 2 ; ++i) { + bool dr = (i != 0); + + QTest::newRow("set: plain") << "append({'a':123, 'b':456, 'c':789});" << "set(0,{'b':123});" + << "b" << 0 << true << "get(0).b == 123" << dr; + QTest::newRow("setProperty: plain") << "append({'a':123, 'b':456, 'c':789});" << "setProperty(0, 'b', 123);" + << "b" << 0 << true << "get(0).b == 123" << dr; + + QTest::newRow("set: plain, no changes") << "append({'a':123, 'b':456, 'c':789});" << "set(0,{'b':456});" + << "b" << 0 << false << "get(0).b == 456" << dr; + QTest::newRow("setProperty: plain, no changes") << "append({'a':123, 'b':456, 'c':789});" << "setProperty(0, 'b', 456);" + << "b" << 0 << false << "get(0).b == 456" << dr; + + QTest::newRow("set: inserted item") + << "{append({'a':123, 'b':456, 'c':789}); get(0); insert(0, {'a':0, 'b':0, 'c':0});}" + << "set(1, {'a':456});" + << "a" << 1 << true << "get(1).a == 456" << dr; + QTest::newRow("setProperty: inserted item") + << "{append({'a':123, 'b':456, 'c':789}); get(0); insert(0, {'a':0, 'b':0, 'c':0});}" + << "setProperty(1, 'a', 456);" + << "a" << 1 << true << "get(1).a == 456" << dr; + QTest::newRow("get: inserted item") + << "{append({'a':123, 'b':456, 'c':789}); get(0); insert(0, {'a':0, 'b':0, 'c':0});}" + << "get(1).a = 456;" + << "a" << 1 << true << "get(1).a == 456" << dr; + QTest::newRow("set: removed item") + << "{append({'a':0, 'b':0, 'c':0}); append({'a':123, 'b':456, 'c':789}); get(1); remove(0);}" + << "set(0, {'a':456});" + << "a" << 0 << true << "get(0).a == 456" << dr; + QTest::newRow("setProperty: removed item") + << "{append({'a':0, 'b':0, 'c':0}); append({'a':123, 'b':456, 'c':789}); get(1); remove(0);}" + << "setProperty(0, 'a', 456);" + << "a" << 0 << true << "get(0).a == 456" << dr; + QTest::newRow("get: removed item") + << "{append({'a':0, 'b':0, 'c':0}); append({'a':123, 'b':456, 'c':789}); get(1); remove(0);}" + << "get(0).a = 456;" + << "a" << 0 << true << "get(0).a == 456" << dr; + + // Following tests only call set() since setProperty() only allows plain + // values, not lists, as the argument. + // Note that when a list is changed, itemsChanged() is currently always + // emitted regardless of whether it actually changed or not. + + QTest::newRow("nested-set: list, new size") << "append({'a':123, 'b':[{'a':1},{'a':2},{'a':3}], 'c':789});" << "set(0,{'b':[{'a':1},{'a':2}]});" + << "b" << 0 << true << "get(0).b.get(0).a == 1 && get(0).b.get(1).a == 2" << dr; + + QTest::newRow("nested-set: list, empty -> non-empty") << "append({'a':123, 'b':[], 'c':789});" << "set(0,{'b':[{'a':1},{'a':2},{'a':3}]});" + << "b" << 0 << true << "get(0).b.get(0).a == 1 && get(0).b.get(1).a == 2 && get(0).b.get(2).a == 3" << dr; + + QTest::newRow("nested-set: list, non-empty -> empty") << "append({'a':123, 'b':[{'a':1},{'a':2},{'a':3}], 'c':789});" << "set(0,{'b':[]});" + << "b" << 0 << true << "get(0).b.count == 0" << dr; + + QTest::newRow("nested-set: list, same size, different values") << "append({'a':123, 'b':[{'a':1},{'a':2},{'a':3}], 'c':789});" << "set(0,{'b':[{'a':1},{'a':222},{'a':3}]});" + << "b" << 0 << true << "get(0).b.get(0).a == 1 && get(0).b.get(1).a == 222 && get(0).b.get(2).a == 3" << dr; + + QTest::newRow("nested-set: list, no changes") << "append({'a':123, 'b':[{'a':1},{'a':2},{'a':3}], 'c':789});" << "set(0,{'b':[{'a':1},{'a':2},{'a':3}]});" + << "b" << 0 << true << "get(0).b.get(0).a == 1 && get(0).b.get(1).a == 2 && get(0).b.get(2).a == 3" << dr; + + QTest::newRow("nested-set: list, no changes, empty") << "append({'a':123, 'b':[], 'c':789});" << "set(0,{'b':[]});" + << "b" << 0 << true << "get(0).b.count == 0" << dr; + } +} + +void tst_qqmllistmodel::clear_data() +{ + QTest::addColumn("dynamicRoles"); + + QTest::newRow("staticRoles") << false; + QTest::newRow("dynamicRoles") << true; +} + +void tst_qqmllistmodel::clear() +{ + QFETCH(bool, dynamicRoles); + + QQmlEngine engine; + QQmlListModel model; + model.setDynamicRoles(dynamicRoles); + QQmlEngine::setContextForObject(&model, engine.rootContext()); + engine.rootContext()->setContextProperty("model", &model); + + model.clear(); + QCOMPARE(model.count(), 0); + + RUNEXPR("model.append({propertyA: \"value a\", propertyB: \"value b\"})"); + QCOMPARE(model.count(), 1); + + model.clear(); + QCOMPARE(model.count(), 0); + + RUNEXPR("model.append({propertyA: \"value a\", propertyB: \"value b\"})"); + RUNEXPR("model.append({propertyA: \"value a\", propertyB: \"value b\"})"); + QCOMPARE(model.count(), 2); + + model.clear(); + QCOMPARE(model.count(), 0); + + // clearing does not remove the roles + RUNEXPR("model.append({propertyA: \"value a\", propertyB: \"value b\", propertyC: \"value c\"})"); + QHash roleNames = model.roleNames(); + model.clear(); + QCOMPARE(model.count(), 0); + QCOMPARE(model.roleNames(), roleNames); + QCOMPARE(roleNames[0], QByteArray("propertyA")); + QCOMPARE(roleNames[1], QByteArray("propertyB")); + QCOMPARE(roleNames[2], QByteArray("propertyC")); +} + +void tst_qqmllistmodel::signal_handlers_data() +{ + QTest::addColumn("dynamicRoles"); + + QTest::newRow("staticRoles") << false; + QTest::newRow("dynamicRoles") << true; +} + +void tst_qqmllistmodel::signal_handlers() +{ + QFETCH(bool, dynamicRoles); + + QQmlEngine eng; + QQmlComponent component(&eng, testFileUrl("signalhandlers.qml")); + QObject *model = component.create(); + QQmlListModel *lm = qobject_cast(model); + QVERIFY(lm != 0); + lm->setDynamicRoles(dynamicRoles); + QVERIFY2(component.errorString().isEmpty(), QTest::toString(component.errorString())); + QVERIFY(model != 0); + QVERIFY(model->property("ok").toBool()); + + delete model; +} + +void tst_qqmllistmodel::role_mode_data() +{ + QTest::addColumn("script"); + QTest::addColumn("result"); + QTest::addColumn("warning"); + + QTest::newRow("default0") << "{dynamicRoles}" << 0 << ""; + QTest::newRow("default1") << "{append({'a':1});dynamicRoles}" << 0 << ""; + + QTest::newRow("enableDynamic0") << "{dynamicRoles=true;dynamicRoles}" << 1 << ""; + QTest::newRow("enableDynamic1") << "{append({'a':1});dynamicRoles=true;dynamicRoles}" << 0 << ": QML ListModel: unable to enable dynamic roles as this model is not empty!"; + QTest::newRow("enableDynamic2") << "{dynamicRoles=true;append({'a':1});dynamicRoles=false;dynamicRoles}" << 1 << ": QML ListModel: unable to enable static roles as this model is not empty!"; +} + +void tst_qqmllistmodel::role_mode() +{ + QFETCH(QString, script); + QFETCH(int, result); + QFETCH(QString, warning); + + QQmlEngine engine; + QQmlListModel model; + QQmlEngine::setContextForObject(&model,engine.rootContext()); + engine.rootContext()->setContextObject(&model); + QQmlExpression e(engine.rootContext(), &model, script); + if (!warning.isEmpty()) + QTest::ignoreMessage(QtWarningMsg, warning.toLatin1()); + + int actual = e.evaluate().toInt(); + if (e.hasError()) + qDebug() << e.error(); // errors not expected + + QCOMPARE(actual,result); +} + +void tst_qqmllistmodel::string_to_list_crash() +{ + QQmlEngine engine; + QQmlListModel model; + QQmlEngine::setContextForObject(&model,engine.rootContext()); + engine.rootContext()->setContextObject(&model); + QString script = QLatin1String("{append({'a':'data'});get(0).a = [{'x':123}]}"); + QTest::ignoreMessage(QtWarningMsg, ": Can't assign to existing role 'a' of different type [String -> List]"); + QQmlExpression e(engine.rootContext(), &model, script); + // Don't crash! + e.evaluate(); +} + +void tst_qqmllistmodel::empty_element_warning_data() +{ + QTest::addColumn("qml"); + QTest::addColumn("warning"); + + QTest::newRow("empty") << "import QtQuick 2.0\nListModel {}" << false; + QTest::newRow("withid") << "import QtQuick 2.0\nListModel { id: model }" << false; + QTest::newRow("emptyElement") << "import QtQuick 2.0\nListModel { ListElement {} }" << true; + QTest::newRow("emptyElements") << "import QtQuick 2.0\nListModel { ListElement {} ListElement {} }" << true; + QTest::newRow("role1") << "import QtQuick 2.0\nListModel { ListElement {a:1} }" << false; + QTest::newRow("role2") << "import QtQuick 2.0\nListModel { ListElement {} ListElement {a:1} ListElement {} }" << false; + QTest::newRow("role3") << "import QtQuick 2.0\nListModel { ListElement {} ListElement {a:1} ListElement {b:2} }" << false; +} + +void tst_qqmllistmodel::empty_element_warning() +{ + QFETCH(QString, qml); + QFETCH(bool, warning); + + if (warning) { + QString warningString = QLatin1String("file:dummy.qml:2:1: QML ListModel: All ListElement declarations are empty, no roles can be created unless dynamicRoles is set."); + QTest::ignoreMessage(QtWarningMsg, warningString.toLatin1()); + } + + QQmlEngine engine; + QQmlComponent component(&engine); + component.setData(qml.toUtf8(), QUrl::fromLocalFile(QString("dummy.qml"))); + QVERIFY(!component.isError()); + + QObject *obj = component.create(); + QVERIFY(obj != 0); + + delete obj; +} + +void tst_qqmllistmodel::datetime_data() +{ + QTest::addColumn("qml"); + QTest::addColumn("expected"); + + QDateTime dt; + QDateTime dt0(QDate(1900, 1, 2), QTime( 8, 14)); + QDateTime dt1(QDate(2000, 11, 22), QTime(10, 45)); + + QTest::newRow("dt0") << "{append({'date':dt0});get(0).date}" << dt0; + QTest::newRow("dt1") << "{append({'date':dt0});get(0).date=dt1;get(0).date}" << dt1; + QTest::newRow("dt2") << "{append({'date':dt0});set(0,{'date':dt1});get(0).date}" << dt1; + QTest::newRow("dt3") << "{append({'date':dt0});get(0).date=undefined;get(0).date}" << dt; +} + +void tst_qqmllistmodel::datetime() +{ + QFETCH(QString, qml); + QFETCH(QDateTime, expected); + + QQmlEngine engine; + QQmlListModel model; + QQmlEngine::setContextForObject(&model,engine.rootContext()); + QDateTime dt0(QDate(1900, 1, 2), QTime( 8, 14)); + QDateTime dt1(QDate(2000, 11, 22), QTime(10, 45)); + engine.rootContext()->setContextProperty("dt0", dt0); + engine.rootContext()->setContextProperty("dt1", dt1); + engine.rootContext()->setContextObject(&model); + QQmlExpression e(engine.rootContext(), &model, qml); + QVariant result = e.evaluate(); + QDateTime dtResult = result.toDateTime(); + QVERIFY(expected == dtResult); +} + +QTEST_MAIN(tst_qqmllistmodel) + +#include "tst_qqmllistmodel.moc" diff --git a/tests/auto/qml/qqmllistmodelworkerscript/data/model.qml b/tests/auto/qml/qqmllistmodelworkerscript/data/model.qml new file mode 100644 index 0000000000..5973ea8adf --- /dev/null +++ b/tests/auto/qml/qqmllistmodelworkerscript/data/model.qml @@ -0,0 +1,26 @@ +import QtQuick 2.0 + +Item { + id: item + property variant model + property bool done: false + property variant result + + function evalExpressionViaWorker(commands) { + done = false + worker.sendMessage({'commands': commands, 'model': model}) + } + + WorkerScript { + id: worker + source: "script.js" + onMessage: { + item.result = messageObject.result + item.done = true + } + } + + function runEval(js) { + eval(js); + } +} diff --git a/tests/auto/qml/qqmllistmodelworkerscript/data/script.js b/tests/auto/qml/qqmllistmodelworkerscript/data/script.js new file mode 100644 index 0000000000..66a4acb8a8 --- /dev/null +++ b/tests/auto/qml/qqmllistmodelworkerscript/data/script.js @@ -0,0 +1,13 @@ +WorkerScript.onMessage = function(msg) { + var result = null + try { + for (var i=0; i +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "../../shared/util.h" + +Q_DECLARE_METATYPE(QList) +Q_DECLARE_METATYPE(QList) + +#define RUNEVAL(object, string) \ + QVERIFY(QMetaObject::invokeMethod(object, "runEval", Q_ARG(QVariant, QString(string)))); + +inline QVariant runexpr(QQmlEngine *engine, const QString &str) +{ + QQmlExpression expr(engine->rootContext(), 0, str); + return expr.evaluate(); +} + +#define RUNEXPR(string) runexpr(&engine, QString(string)) + +static bool isValidErrorMessage(const QString &msg, bool dynamicRoleTest) +{ + bool valid = true; + + if (msg.isEmpty()) { + valid = false; + } else if (dynamicRoleTest) { + if (msg.contains("Can't assign to existing role") || msg.contains("Can't create role for unsupported data type")) + valid = false; + } + + return valid; +} + +class tst_qqmllistmodelworkerscript : public QQmlDataTest +{ + Q_OBJECT +public: + tst_qqmllistmodelworkerscript() + { + qRegisterMetaType >(); + } + +private: + int roleFromName(const QQmlListModel *model, const QString &roleName); + QQuickItem *createWorkerTest(QQmlEngine *eng, QQmlComponent *component, QQmlListModel *model); + void waitForWorker(QQuickItem *item); + + static bool compareVariantList(const QVariantList &testList, QVariant object); + +private slots: + void dynamic_data(); + void dynamic_worker_data(); + void dynamic_worker(); + void dynamic_worker_sync_data(); + void dynamic_worker_sync(); + void get_data(); + void get_worker(); + void get_worker_data(); + void property_changes_data(); + void property_changes_worker(); + void property_changes_worker_data(); + void worker_sync_data(); + void worker_sync(); + void worker_remove_element_data(); + void worker_remove_element(); + void worker_remove_list_data(); + void worker_remove_list(); + void dynamic_role_data(); + void dynamic_role(); +}; + +bool tst_qqmllistmodelworkerscript::compareVariantList(const QVariantList &testList, QVariant object) +{ + bool allOk = true; + + QQmlListModel *model = qobject_cast(object.value()); + if (model == 0) + return false; + + if (model->count() != testList.count()) + return false; + + for (int i=0 ; i < testList.count() ; ++i) { + const QVariant &testVariant = testList.at(i); + if (testVariant.type() != QVariant::Map) + return false; + const QVariantMap &map = testVariant.toMap(); + + const QHash roleNames = model->roleNames(); + + QVariantMap::const_iterator it = map.begin(); + QVariantMap::const_iterator end = map.end(); + + while (it != end) { + const QString &testKey = it.key(); + const QVariant &testData = it.value(); + + int roleIndex = roleNames.key(testKey.toUtf8(), -1); + if (roleIndex == -1) + return false; + + const QVariant &modelData = model->data(model->index(i, 0, QModelIndex()), roleIndex); + + if (testData.type() == QVariant::List) { + const QVariantList &subList = testData.toList(); + allOk = allOk && compareVariantList(subList, modelData); + } else { + allOk = allOk && (testData == modelData); + } + + ++it; + } + } + + return allOk; +} + +int tst_qqmllistmodelworkerscript::roleFromName(const QQmlListModel *model, const QString &roleName) +{ + return model->roleNames().key(roleName.toUtf8(), -1); +} + +QQuickItem *tst_qqmllistmodelworkerscript::createWorkerTest(QQmlEngine *eng, QQmlComponent *component, QQmlListModel *model) +{ + QQuickItem *item = qobject_cast(component->create()); + QQmlEngine::setContextForObject(model, eng->rootContext()); + if (item) + item->setProperty("model", qVariantFromValue(model)); + return item; +} + +void tst_qqmllistmodelworkerscript::waitForWorker(QQuickItem *item) +{ + QQmlProperty prop(item, "done"); + QVERIFY(prop.isValid()); + if (prop.read().toBool()) + return; // already finished + + QEventLoop loop; + QTimer timer; + timer.setSingleShot(true); + connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit())); + + QVERIFY(prop.connectNotifySignal(&loop, SLOT(quit()))); + timer.start(10000); + loop.exec(); + QVERIFY(timer.isActive()); + QVERIFY(prop.read().toBool()); +} + +void tst_qqmllistmodelworkerscript::dynamic_data() +{ + QTest::addColumn("script"); + QTest::addColumn("result"); + QTest::addColumn("warning"); + QTest::addColumn("dynamicRoles"); + + for (int i=0 ; i < 2 ; ++i) { + bool dr = (i != 0); + + // Simple flat model + QTest::newRow("count") << "count" << 0 << "" << dr; + + QTest::newRow("get1") << "{get(0) === undefined}" << 1 << "" << dr; + QTest::newRow("get2") << "{get(-1) === undefined}" << 1 << "" << dr; + QTest::newRow("get3") << "{append({'foo':123});get(0) != undefined}" << 1 << "" << dr; + QTest::newRow("get4") << "{append({'foo':123});get(0).foo}" << 123 << "" << dr; + QTest::newRow("get-modify1") << "{append({'foo':123,'bar':456});get(0).foo = 333;get(0).foo}" << 333 << "" << dr; + QTest::newRow("get-modify2") << "{append({'z':1});append({'foo':123,'bar':456});get(1).bar = 999;get(1).bar}" << 999 << "" << dr; + + QTest::newRow("append1") << "{append({'foo':123});count}" << 1 << "" << dr; + QTest::newRow("append2") << "{append({'foo':123,'bar':456});count}" << 1 << "" << dr; + QTest::newRow("append3a") << "{append({'foo':123});append({'foo':456});get(0).foo}" << 123 << "" << dr; + QTest::newRow("append3b") << "{append({'foo':123});append({'foo':456});get(1).foo}" << 456 << "" << dr; + QTest::newRow("append4a") << "{append(123)}" << 0 << ": QML ListModel: append: value is not an object" << dr; + QTest::newRow("append4b") << "{append([{'foo':123},{'foo':456},{'foo':789}]);count}" << 3 << "" << dr; + QTest::newRow("append4c") << "{append([{'foo':123},{'foo':456},{'foo':789}]);get(1).foo}" << 456 << "" << dr; + + QTest::newRow("clear1") << "{append({'foo':456});clear();count}" << 0 << "" << dr; + QTest::newRow("clear2") << "{append({'foo':123});append({'foo':456});clear();count}" << 0 << "" << dr; + QTest::newRow("clear3") << "{append({'foo':123});clear()}" << 0 << "" << dr; + + QTest::newRow("remove1") << "{append({'foo':123});remove(0);count}" << 0 << "" << dr; + QTest::newRow("remove2a") << "{append({'foo':123});append({'foo':456});remove(0);count}" << 1 << "" << dr; + QTest::newRow("remove2b") << "{append({'foo':123});append({'foo':456});remove(0);get(0).foo}" << 456 << "" << dr; + QTest::newRow("remove2c") << "{append({'foo':123});append({'foo':456});remove(1);get(0).foo}" << 123 << "" << dr; + QTest::newRow("remove3") << "{append({'foo':123});remove(0)}" << 0 << "" << dr; + QTest::newRow("remove3a") << "{append({'foo':123});remove(-1);count}" << 1 << ": QML ListModel: remove: indices [-1 - 0] out of range [0 - 1]" << dr; + QTest::newRow("remove4a") << "{remove(0)}" << 0 << ": QML ListModel: remove: indices [0 - 1] out of range [0 - 0]" << dr; + QTest::newRow("remove4b") << "{append({'foo':123});remove(0);remove(0);count}" << 0 << ": QML ListModel: remove: indices [0 - 1] out of range [0 - 0]" << dr; + QTest::newRow("remove4c") << "{append({'foo':123});remove(1);count}" << 1 << ": QML ListModel: remove: indices [1 - 2] out of range [0 - 1]" << dr; + QTest::newRow("remove5a") << "{append({'foo':123});append({'foo':456});remove(0,2);count}" << 0 << "" << dr; + QTest::newRow("remove5b") << "{append({'foo':123});append({'foo':456});remove(0,1);count}" << 1 << "" << dr; + QTest::newRow("remove5c") << "{append({'foo':123});append({'foo':456});remove(1,1);count}" << 1 << "" << dr; + QTest::newRow("remove5d") << "{append({'foo':123});append({'foo':456});remove(0,1);get(0).foo}" << 456 << "" << dr; + QTest::newRow("remove5e") << "{append({'foo':123});append({'foo':456});remove(1,1);get(0).foo}" << 123 << "" << dr; + QTest::newRow("remove5f") << "{append({'foo':123});append({'foo':456});append({'foo':789});remove(0,1);remove(1,1);get(0).foo}" << 456 << "" << dr; + QTest::newRow("remove6a") << "{remove();count}" << 0 << ": QML ListModel: remove: incorrect number of arguments" << dr; + QTest::newRow("remove6b") << "{remove(1,2,3);count}" << 0 << ": QML ListModel: remove: incorrect number of arguments" << dr; + QTest::newRow("remove7a") << "{append({'foo':123});remove(0,0);count}" << 1 << ": QML ListModel: remove: indices [0 - 0] out of range [0 - 1]" << dr; + QTest::newRow("remove7b") << "{append({'foo':123});remove(0,-1);count}" << 1 << ": QML ListModel: remove: indices [0 - -1] out of range [0 - 1]" << dr; + + QTest::newRow("insert1") << "{insert(0,{'foo':123});count}" << 1 << "" << dr; + QTest::newRow("insert2") << "{insert(1,{'foo':123});count}" << 0 << ": QML ListModel: insert: index 1 out of range" << dr; + QTest::newRow("insert3a") << "{append({'foo':123});insert(1,{'foo':456});count}" << 2 << "" << dr; + QTest::newRow("insert3b") << "{append({'foo':123});insert(1,{'foo':456});get(0).foo}" << 123 << "" << dr; + QTest::newRow("insert3c") << "{append({'foo':123});insert(1,{'foo':456});get(1).foo}" << 456 << "" << dr; + QTest::newRow("insert3d") << "{append({'foo':123});insert(0,{'foo':456});get(0).foo}" << 456 << "" << dr; + QTest::newRow("insert3e") << "{append({'foo':123});insert(0,{'foo':456});get(1).foo}" << 123 << "" << dr; + QTest::newRow("insert4") << "{append({'foo':123});insert(-1,{'foo':456});count}" << 1 << ": QML ListModel: insert: index -1 out of range" << dr; + QTest::newRow("insert5a") << "{insert(0,123)}" << 0 << ": QML ListModel: insert: value is not an object" << dr; + QTest::newRow("insert5b") << "{insert(0,[{'foo':11},{'foo':22},{'foo':33}]);count}" << 3 << "" << dr; + QTest::newRow("insert5c") << "{insert(0,[{'foo':11},{'foo':22},{'foo':33}]);get(2).foo}" << 33 << "" << dr; + + QTest::newRow("set1") << "{append({'foo':123});set(0,{'foo':456});count}" << 1 << "" << dr; + QTest::newRow("set2") << "{append({'foo':123});set(0,{'foo':456});get(0).foo}" << 456 << "" << dr; + QTest::newRow("set3a") << "{append({'foo':123,'bar':456});set(0,{'foo':999});get(0).foo}" << 999 << "" << dr; + QTest::newRow("set3b") << "{append({'foo':123,'bar':456});set(0,{'foo':999});get(0).bar}" << 456 << "" << dr; + QTest::newRow("set4a") << "{set(0,{'foo':456});count}" << 1 << "" << dr; + QTest::newRow("set4c") << "{set(-1,{'foo':456})}" << 0 << ": QML ListModel: set: index -1 out of range" << dr; + QTest::newRow("set5a") << "{append({'foo':123,'bar':456});set(0,123);count}" << 1 << ": QML ListModel: set: value is not an object" << dr; + QTest::newRow("set5b") << "{append({'foo':123,'bar':456});set(0,[1,2,3]);count}" << 1 << ": QML ListModel: set: value is not an object" << dr; + QTest::newRow("set6") << "{append({'foo':123});set(1,{'foo':456});count}" << 2 << "" << dr; + + QTest::newRow("setprop1") << "{append({'foo':123});setProperty(0,'foo',456);count}" << 1 << "" << dr; + QTest::newRow("setprop2") << "{append({'foo':123});setProperty(0,'foo',456);get(0).foo}" << 456 << "" << dr; + QTest::newRow("setprop3a") << "{append({'foo':123,'bar':456});setProperty(0,'foo',999);get(0).foo}" << 999 << "" << dr; + QTest::newRow("setprop3b") << "{append({'foo':123,'bar':456});setProperty(0,'foo',999);get(0).bar}" << 456 << "" << dr; + QTest::newRow("setprop4a") << "{setProperty(0,'foo',456)}" << 0 << ": QML ListModel: set: index 0 out of range" << dr; + QTest::newRow("setprop4b") << "{setProperty(-1,'foo',456)}" << 0 << ": QML ListModel: set: index -1 out of range" << dr; + QTest::newRow("setprop4c") << "{append({'foo':123,'bar':456});setProperty(1,'foo',456);count}" << 1 << ": QML ListModel: set: index 1 out of range" << dr; + QTest::newRow("setprop5") << "{append({'foo':123,'bar':456});append({'foo':111});setProperty(1,'bar',222);get(1).bar}" << 222 << "" << dr; + + QTest::newRow("move1a") << "{append({'foo':123});append({'foo':456});move(0,1,1);count}" << 2 << "" << dr; + QTest::newRow("move1b") << "{append({'foo':123});append({'foo':456});move(0,1,1);get(0).foo}" << 456 << "" << dr; + QTest::newRow("move1c") << "{append({'foo':123});append({'foo':456});move(0,1,1);get(1).foo}" << 123 << "" << dr; + QTest::newRow("move1d") << "{append({'foo':123});append({'foo':456});move(1,0,1);get(0).foo}" << 456 << "" << dr; + QTest::newRow("move1e") << "{append({'foo':123});append({'foo':456});move(1,0,1);get(1).foo}" << 123 << "" << dr; + QTest::newRow("move2a") << "{append({'foo':123});append({'foo':456});append({'foo':789});move(0,1,2);count}" << 3 << "" << dr; + QTest::newRow("move2b") << "{append({'foo':123});append({'foo':456});append({'foo':789});move(0,1,2);get(0).foo}" << 789 << "" << dr; + QTest::newRow("move2c") << "{append({'foo':123});append({'foo':456});append({'foo':789});move(0,1,2);get(1).foo}" << 123 << "" << dr; + QTest::newRow("move2d") << "{append({'foo':123});append({'foo':456});append({'foo':789});move(0,1,2);get(2).foo}" << 456 << "" << dr; + QTest::newRow("move3a") << "{append({'foo':123});append({'foo':456});append({'foo':789});move(1,0,3);count}" << 3 << ": QML ListModel: move: out of range" << dr; + QTest::newRow("move3b") << "{append({'foo':123});append({'foo':456});append({'foo':789});move(1,-1,1);count}" << 3 << ": QML ListModel: move: out of range" << dr; + QTest::newRow("move3c") << "{append({'foo':123});append({'foo':456});append({'foo':789});move(1,0,-1);count}" << 3 << ": QML ListModel: move: out of range" << dr; + QTest::newRow("move3d") << "{append({'foo':123});append({'foo':456});append({'foo':789});move(0,3,1);count}" << 3 << ": QML ListModel: move: out of range" << dr; + + QTest::newRow("large1") << "{append({'a':1,'b':2,'c':3,'d':4,'e':5,'f':6,'g':7,'h':8});get(0).h}" << 8 << "" << dr; + + QTest::newRow("datatypes1") << "{append({'a':1});append({'a':'string'});}" << 0 << ": Can't assign to existing role 'a' of different type [String -> Number]" << dr; + + QTest::newRow("null") << "{append({'a':null});}" << 0 << "" << dr; + QTest::newRow("setNull") << "{append({'a':1});set(0, {'a':null});}" << 0 << "" << dr; + QTest::newRow("setString") << "{append({'a':'hello'});set(0, {'a':'world'});get(0).a == 'world'}" << 1 << "" << dr; + QTest::newRow("setInt") << "{append({'a':5});set(0, {'a':10});get(0).a}" << 10 << "" << dr; + QTest::newRow("setNumber") << "{append({'a':6});set(0, {'a':5.5});get(0).a < 5.6}" << 1 << "" << dr; + QTest::newRow("badType0") << "{append({'a':'hello'});set(0, {'a':1});}" << 0 << ": Can't assign to existing role 'a' of different type [Number -> String]" << dr; + QTest::newRow("invalidInsert0") << "{insert(0);}" << 0 << ": QML ListModel: insert: value is not an object" << dr; + QTest::newRow("invalidAppend0") << "{append();}" << 0 << ": QML ListModel: append: value is not an object" << dr; + QTest::newRow("invalidInsert1") << "{insert(0, 34);}" << 0 << ": QML ListModel: insert: value is not an object" << dr; + QTest::newRow("invalidAppend1") << "{append(37);}" << 0 << ": QML ListModel: append: value is not an object" << dr; + + // QObjects + QTest::newRow("qobject0") << "{append({'a':dummyItem0});}" << 0 << "" << dr; + QTest::newRow("qobject1") << "{append({'a':dummyItem0});set(0,{'a':dummyItem1});get(0).a == dummyItem1;}" << 1 << "" << dr; + QTest::newRow("qobject2") << "{append({'a':dummyItem0});get(0).a == dummyItem0;}" << 1 << "" << dr; + QTest::newRow("qobject3") << "{append({'a':dummyItem0});append({'b':1});}" << 0 << "" << dr; + + // JS objects + QTest::newRow("js1") << "{append({'foo':{'prop':1}});count}" << 1 << "" << dr; + QTest::newRow("js2") << "{append({'foo':{'prop':27}});get(0).foo.prop}" << 27 << "" << dr; + QTest::newRow("js3") << "{append({'foo':{'prop':27}});append({'bar':1});count}" << 2 << "" << dr; + QTest::newRow("js4") << "{append({'foo':{'prop':27}});append({'bar':1});set(0, {'foo':{'prop':28}});get(0).foo.prop}" << 28 << "" << dr; + QTest::newRow("js5") << "{append({'foo':{'prop':27}});append({'bar':1});set(1, {'foo':{'prop':33}});get(1).foo.prop}" << 33 << "" << dr; + QTest::newRow("js6") << "{append({'foo':{'prop':27}});clear();count}" << 0 << "" << dr; + QTest::newRow("js7") << "{append({'foo':{'prop':27}});set(0, {'foo':null});count}" << 1 << "" << dr; + QTest::newRow("js8") << "{append({'foo':{'prop':27}});set(0, {'foo':{'prop2':31}});get(0).foo.prop2}" << 31 << "" << dr; + + // Nested models + QTest::newRow("nested-append1") << "{append({'foo':123,'bars':[{'a':1},{'a':2},{'a':3}]});count}" << 1 << "" << dr; + QTest::newRow("nested-append2") << "{append({'foo':123,'bars':[{'a':1},{'a':2},{'a':3}]});get(0).bars.get(1).a}" << 2 << "" << dr; + QTest::newRow("nested-append3") << "{append({'foo':123,'bars':[{'a':1},{'a':2},{'a':3}]});get(0).bars.append({'a':4});get(0).bars.get(3).a}" << 4 << "" << dr; + + QTest::newRow("nested-insert") << "{append({'foo':123});insert(0,{'bars':[{'a':1},{'b':2},{'c':3}]});get(0).bars.get(0).a}" << 1 << "" << dr; + QTest::newRow("nested-set") << "{append({'foo':[{'x':1}]});set(0,{'foo':[{'x':123}]});get(0).foo.get(0).x}" << 123 << "" << dr; + + QTest::newRow("nested-count") << "{append({'foo':123,'bars':[{'a':1},{'a':2},{'a':3}]}); get(0).bars.count}" << 3 << "" << dr; + QTest::newRow("nested-clear") << "{append({'foo':123,'bars':[{'a':1},{'a':2},{'a':3}]}); get(0).bars.clear(); get(0).bars.count}" << 0 << "" << dr; + } +} + +void tst_qqmllistmodelworkerscript::dynamic_worker_data() +{ + dynamic_data(); +} + +void tst_qqmllistmodelworkerscript::dynamic_worker() +{ + QFETCH(QString, script); + QFETCH(int, result); + QFETCH(QString, warning); + QFETCH(bool, dynamicRoles); + + if (QByteArray(QTest::currentDataTag()).startsWith("qobject")) + return; + + // This is same as dynamic() except it applies the test to a ListModel called + // from a WorkerScript. + + QQmlListModel model; + model.setDynamicRoles(dynamicRoles); + QQmlEngine eng; + QQmlComponent component(&eng, testFileUrl("model.qml")); + QQuickItem *item = createWorkerTest(&eng, &component, &model); + QVERIFY(item != 0); + + QSignalSpy spyCount(&model, SIGNAL(countChanged())); + + if (script[0] == QLatin1Char('{') && script[script.length()-1] == QLatin1Char('}')) + script = script.mid(1, script.length() - 2); + QVariantList operations; + foreach (const QString &s, script.split(';')) { + if (!s.isEmpty()) + operations << s; + } + + if (isValidErrorMessage(warning, dynamicRoles)) + QTest::ignoreMessage(QtWarningMsg, warning.toLatin1()); + + QVERIFY(QMetaObject::invokeMethod(item, "evalExpressionViaWorker", + Q_ARG(QVariant, operations))); + waitForWorker(item); + QCOMPARE(QQmlProperty(item, "result").read().toInt(), result); + + if (model.count() > 0) + QVERIFY(spyCount.count() > 0); + + delete item; + qApp->processEvents(); +} + +void tst_qqmllistmodelworkerscript::dynamic_worker_sync_data() +{ + dynamic_data(); +} + +void tst_qqmllistmodelworkerscript::dynamic_worker_sync() +{ + QFETCH(QString, script); + QFETCH(int, result); + QFETCH(QString, warning); + QFETCH(bool, dynamicRoles); + + if (QByteArray(QTest::currentDataTag()).startsWith("qobject")) + return; + + // This is the same as dynamic_worker() except that it executes a set of list operations + // from the worker script, calls sync(), and tests the changes are reflected in the + // list in the main thread + + QQmlListModel model; + model.setDynamicRoles(dynamicRoles); + QQmlEngine eng; + QQmlComponent component(&eng, testFileUrl("model.qml")); + QQuickItem *item = createWorkerTest(&eng, &component, &model); + QVERIFY(item != 0); + + if (script[0] == QLatin1Char('{') && script[script.length()-1] == QLatin1Char('}')) + script = script.mid(1, script.length() - 2); + QVariantList operations; + foreach (const QString &s, script.split(';')) { + if (!s.isEmpty()) + operations << s; + } + + if (isValidErrorMessage(warning, dynamicRoles)) + QTest::ignoreMessage(QtWarningMsg, warning.toLatin1()); + + // execute a set of commands on the worker list model, then check the + // changes are reflected in the list model in the main thread + QVERIFY(QMetaObject::invokeMethod(item, "evalExpressionViaWorker", + Q_ARG(QVariant, operations.mid(0, operations.length()-1)))); + waitForWorker(item); + + QQmlExpression e(eng.rootContext(), &model, operations.last().toString()); + QCOMPARE(e.evaluate().toInt(), result); + + delete item; + qApp->processEvents(); +} + +void tst_qqmllistmodelworkerscript::get_data() +{ + QTest::addColumn("expression"); + QTest::addColumn("index"); + QTest::addColumn("roleName"); + QTest::addColumn("roleValue"); + QTest::addColumn("dynamicRoles"); + + for (int i=0 ; i < 2 ; ++i) { + bool dr = (i != 0); + + QTest::newRow("simple value") << "get(0).roleA = 500" << 0 << "roleA" << QVariant(500) << dr; + QTest::newRow("simple value 2") << "get(1).roleB = 500" << 1 << "roleB" << QVariant(500) << dr; + + QVariantMap map; + QVariantList list; + map.clear(); map["a"] = 50; map["b"] = 500; + list << map; + map.clear(); map["c"] = 1000; + list << map; + QTest::newRow("list of objects") << "get(2).roleD = [{'a': 50, 'b': 500}, {'c': 1000}]" << 2 << "roleD" << QVariant::fromValue(list) << dr; + } +} + +void tst_qqmllistmodelworkerscript::get_worker() +{ + QFETCH(QString, expression); + QFETCH(int, index); + QFETCH(QString, roleName); + QFETCH(QVariant, roleValue); + QFETCH(bool, dynamicRoles); + + QQmlListModel model; + model.setDynamicRoles(dynamicRoles); + QQmlEngine eng; + QQmlComponent component(&eng, testFileUrl("model.qml")); + QQuickItem *item = createWorkerTest(&eng, &component, &model); + QVERIFY(item != 0); + + // Add some values like get() test + RUNEVAL(item, "model.append({roleA: 100})"); + RUNEVAL(item, "model.append({roleA: 200, roleB: 400})"); + RUNEVAL(item, "model.append({roleA: 200, roleB: 400})"); + RUNEVAL(item, "model.append({roleC: {} })"); + RUNEVAL(item, "model.append({roleD: [ { a:1, b:2 }, { c: 3 } ] })"); + + int role = roleFromName(&model, roleName); + QVERIFY(role >= 0); + + QSignalSpy spy(&model, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector))); + + // in the worker thread, change the model data and call sync() + QVERIFY(QMetaObject::invokeMethod(item, "evalExpressionViaWorker", + Q_ARG(QVariant, QStringList(expression)))); + waitForWorker(item); + + // see if we receive the model changes in the main thread's model + if (roleValue.type() == QVariant::List) { + const QVariantList &list = roleValue.toList(); + QVERIFY(compareVariantList(list, model.data(index, role))); + } else { + QCOMPARE(model.data(index, role), roleValue); + } + + QCOMPARE(spy.count(), 1); + + QList spyResult = spy.takeFirst(); + QCOMPARE(spyResult.at(0).value(), model.index(index, 0, QModelIndex())); + QCOMPARE(spyResult.at(1).value(), model.index(index, 0, QModelIndex())); // only 1 item is modified at a time + QVERIFY(spyResult.at(2).value >().contains(role)); + + delete item; +} + +void tst_qqmllistmodelworkerscript::get_worker_data() +{ + get_data(); +} + +void tst_qqmllistmodelworkerscript::property_changes_data() +{ + QTest::addColumn("script_setup"); + QTest::addColumn("script_change"); + QTest::addColumn("roleName"); + QTest::addColumn("listIndex"); + QTest::addColumn("itemsChanged"); + QTest::addColumn("testExpression"); + QTest::addColumn("dynamicRoles"); + + for (int i=0 ; i < 2 ; ++i) { + bool dr = (i != 0); + + QTest::newRow("set: plain") << "append({'a':123, 'b':456, 'c':789});" << "set(0,{'b':123});" + << "b" << 0 << true << "get(0).b == 123" << dr; + QTest::newRow("setProperty: plain") << "append({'a':123, 'b':456, 'c':789});" << "setProperty(0, 'b', 123);" + << "b" << 0 << true << "get(0).b == 123" << dr; + + QTest::newRow("set: plain, no changes") << "append({'a':123, 'b':456, 'c':789});" << "set(0,{'b':456});" + << "b" << 0 << false << "get(0).b == 456" << dr; + QTest::newRow("setProperty: plain, no changes") << "append({'a':123, 'b':456, 'c':789});" << "setProperty(0, 'b', 456);" + << "b" << 0 << false << "get(0).b == 456" << dr; + + QTest::newRow("set: inserted item") + << "{append({'a':123, 'b':456, 'c':789}); get(0); insert(0, {'a':0, 'b':0, 'c':0});}" + << "set(1, {'a':456});" + << "a" << 1 << true << "get(1).a == 456" << dr; + QTest::newRow("setProperty: inserted item") + << "{append({'a':123, 'b':456, 'c':789}); get(0); insert(0, {'a':0, 'b':0, 'c':0});}" + << "setProperty(1, 'a', 456);" + << "a" << 1 << true << "get(1).a == 456" << dr; + QTest::newRow("get: inserted item") + << "{append({'a':123, 'b':456, 'c':789}); get(0); insert(0, {'a':0, 'b':0, 'c':0});}" + << "get(1).a = 456;" + << "a" << 1 << true << "get(1).a == 456" << dr; + QTest::newRow("set: removed item") + << "{append({'a':0, 'b':0, 'c':0}); append({'a':123, 'b':456, 'c':789}); get(1); remove(0);}" + << "set(0, {'a':456});" + << "a" << 0 << true << "get(0).a == 456" << dr; + QTest::newRow("setProperty: removed item") + << "{append({'a':0, 'b':0, 'c':0}); append({'a':123, 'b':456, 'c':789}); get(1); remove(0);}" + << "setProperty(0, 'a', 456);" + << "a" << 0 << true << "get(0).a == 456" << dr; + QTest::newRow("get: removed item") + << "{append({'a':0, 'b':0, 'c':0}); append({'a':123, 'b':456, 'c':789}); get(1); remove(0);}" + << "get(0).a = 456;" + << "a" << 0 << true << "get(0).a == 456" << dr; + + // Following tests only call set() since setProperty() only allows plain + // values, not lists, as the argument. + // Note that when a list is changed, itemsChanged() is currently always + // emitted regardless of whether it actually changed or not. + + QTest::newRow("nested-set: list, new size") << "append({'a':123, 'b':[{'a':1},{'a':2},{'a':3}], 'c':789});" << "set(0,{'b':[{'a':1},{'a':2}]});" + << "b" << 0 << true << "get(0).b.get(0).a == 1 && get(0).b.get(1).a == 2" << dr; + + QTest::newRow("nested-set: list, empty -> non-empty") << "append({'a':123, 'b':[], 'c':789});" << "set(0,{'b':[{'a':1},{'a':2},{'a':3}]});" + << "b" << 0 << true << "get(0).b.get(0).a == 1 && get(0).b.get(1).a == 2 && get(0).b.get(2).a == 3" << dr; + + QTest::newRow("nested-set: list, non-empty -> empty") << "append({'a':123, 'b':[{'a':1},{'a':2},{'a':3}], 'c':789});" << "set(0,{'b':[]});" + << "b" << 0 << true << "get(0).b.count == 0" << dr; + + QTest::newRow("nested-set: list, same size, different values") << "append({'a':123, 'b':[{'a':1},{'a':2},{'a':3}], 'c':789});" << "set(0,{'b':[{'a':1},{'a':222},{'a':3}]});" + << "b" << 0 << true << "get(0).b.get(0).a == 1 && get(0).b.get(1).a == 222 && get(0).b.get(2).a == 3" << dr; + + QTest::newRow("nested-set: list, no changes") << "append({'a':123, 'b':[{'a':1},{'a':2},{'a':3}], 'c':789});" << "set(0,{'b':[{'a':1},{'a':2},{'a':3}]});" + << "b" << 0 << true << "get(0).b.get(0).a == 1 && get(0).b.get(1).a == 2 && get(0).b.get(2).a == 3" << dr; + + QTest::newRow("nested-set: list, no changes, empty") << "append({'a':123, 'b':[], 'c':789});" << "set(0,{'b':[]});" + << "b" << 0 << true << "get(0).b.count == 0" << dr; + } +} + +void tst_qqmllistmodelworkerscript::property_changes_worker() +{ + QFETCH(QString, script_setup); + QFETCH(QString, script_change); + QFETCH(QString, roleName); + QFETCH(int, listIndex); + QFETCH(bool, itemsChanged); + QFETCH(bool, dynamicRoles); + + QQmlListModel model; + model.setDynamicRoles(dynamicRoles); + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("model.qml")); + QVERIFY2(component.errorString().isEmpty(), component.errorString().toUtf8()); + QQuickItem *item = createWorkerTest(&engine, &component, &model); + QVERIFY(item != 0); + + QQmlExpression expr(engine.rootContext(), &model, script_setup); + expr.evaluate(); + QVERIFY2(!expr.hasError(), QTest::toString(expr.error().toString())); + + QSignalSpy spyItemsChanged(&model, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector))); + + QVERIFY(QMetaObject::invokeMethod(item, "evalExpressionViaWorker", + Q_ARG(QVariant, QStringList(script_change)))); + waitForWorker(item); + + // test itemsChanged() is emitted correctly + if (itemsChanged) { + QCOMPARE(spyItemsChanged.count(), 1); + QCOMPARE(spyItemsChanged.at(0).at(0).value(), model.index(listIndex, 0, QModelIndex())); + QCOMPARE(spyItemsChanged.at(0).at(1).value(), model.index(listIndex, 0, QModelIndex())); + } else { + QCOMPARE(spyItemsChanged.count(), 0); + } + + delete item; + qApp->processEvents(); +} + +void tst_qqmllistmodelworkerscript::property_changes_worker_data() +{ + property_changes_data(); +} + +void tst_qqmllistmodelworkerscript::worker_sync_data() +{ + QTest::addColumn("dynamicRoles"); + + QTest::newRow("staticRoles") << false; + QTest::newRow("dynamicRoles") << true; +} + +void tst_qqmllistmodelworkerscript::worker_sync() +{ + QFETCH(bool, dynamicRoles); + + QQmlListModel model; + model.setDynamicRoles(dynamicRoles); + QQmlEngine eng; + QQmlComponent component(&eng, testFileUrl("workersync.qml")); + QQuickItem *item = createWorkerTest(&eng, &component, &model); + QVERIFY(item != 0); + + QVERIFY(model.count() == 0); + + QVERIFY(QMetaObject::invokeMethod(item, "addItem0")); + + QVERIFY(model.count() == 2); + QVariant childData = model.data(0, 0); + QQmlListModel *childModel = qobject_cast(childData.value()); + QVERIFY(childModel); + QVERIFY(childModel->count() == 1); + + QSignalSpy spyModelInserted(&model, SIGNAL(rowsInserted(QModelIndex,int,int))); + QSignalSpy spyChildInserted(childModel, SIGNAL(rowsInserted(QModelIndex,int,int))); + + QVERIFY(QMetaObject::invokeMethod(item, "addItemViaWorker")); + waitForWorker(item); + + QVERIFY(model.count() == 2); + QVERIFY(childModel->count() == 1); + QVERIFY(spyModelInserted.count() == 0); + QVERIFY(spyChildInserted.count() == 0); + + QVERIFY(QMetaObject::invokeMethod(item, "doSync")); + waitForWorker(item); + + QVERIFY(model.count() == 2); + QVERIFY(childModel->count() == 2); + QVERIFY(spyModelInserted.count() == 0); + QVERIFY(spyChildInserted.count() == 1); + + QVERIFY(QMetaObject::invokeMethod(item, "addItemViaWorker")); + waitForWorker(item); + + QVERIFY(model.count() == 2); + QVERIFY(childModel->count() == 2); + QVERIFY(spyModelInserted.count() == 0); + QVERIFY(spyChildInserted.count() == 1); + + QVERIFY(QMetaObject::invokeMethod(item, "doSync")); + waitForWorker(item); + + QVERIFY(model.count() == 2); + QVERIFY(childModel->count() == 3); + QVERIFY(spyModelInserted.count() == 0); + QVERIFY(spyChildInserted.count() == 2); + + delete item; + qApp->processEvents(); +} + +void tst_qqmllistmodelworkerscript::worker_remove_element_data() +{ + worker_sync_data(); +} + +void tst_qqmllistmodelworkerscript::worker_remove_element() +{ + QFETCH(bool, dynamicRoles); + + QQmlListModel model; + model.setDynamicRoles(dynamicRoles); + QQmlEngine eng; + QQmlComponent component(&eng, testFileUrl("workerremoveelement.qml")); + QQuickItem *item = createWorkerTest(&eng, &component, &model); + QVERIFY(item != 0); + + QSignalSpy spyModelRemoved(&model, SIGNAL(rowsRemoved(QModelIndex,int,int))); + + QVERIFY(model.count() == 0); + QVERIFY(spyModelRemoved.count() == 0); + + QVERIFY(QMetaObject::invokeMethod(item, "addItem")); + + QVERIFY(model.count() == 1); + + QVERIFY(QMetaObject::invokeMethod(item, "removeItemViaWorker")); + waitForWorker(item); + + QVERIFY(model.count() == 1); + QVERIFY(spyModelRemoved.count() == 0); + + QVERIFY(QMetaObject::invokeMethod(item, "doSync")); + waitForWorker(item); + + QVERIFY(model.count() == 0); + QVERIFY(spyModelRemoved.count() == 1); + + delete item; + qApp->processEvents(); + + { + //don't crash if model was deleted earlier + QQmlListModel* model = new QQmlListModel; + model->setDynamicRoles(dynamicRoles); + QQmlEngine eng; + QQmlComponent component(&eng, testFileUrl("workerremoveelement.qml")); + QQuickItem *item = createWorkerTest(&eng, &component, model); + QVERIFY(item != 0); + + QVERIFY(QMetaObject::invokeMethod(item, "addItem")); + + QVERIFY(model->count() == 1); + + QVERIFY(QMetaObject::invokeMethod(item, "removeItemViaWorker")); + QVERIFY(QMetaObject::invokeMethod(item, "doSync")); + delete model; + qApp->processEvents(); //must not crash here + waitForWorker(item); + + delete item; + } +} + +void tst_qqmllistmodelworkerscript::worker_remove_list_data() +{ + worker_sync_data(); +} + +void tst_qqmllistmodelworkerscript::worker_remove_list() +{ + QFETCH(bool, dynamicRoles); + + QQmlListModel model; + model.setDynamicRoles(dynamicRoles); + QQmlEngine eng; + QQmlComponent component(&eng, testFileUrl("workerremovelist.qml")); + QQuickItem *item = createWorkerTest(&eng, &component, &model); + QVERIFY(item != 0); + + QSignalSpy spyModelRemoved(&model, SIGNAL(rowsRemoved(QModelIndex,int,int))); + + QVERIFY(model.count() == 0); + QVERIFY(spyModelRemoved.count() == 0); + + QVERIFY(QMetaObject::invokeMethod(item, "addList")); + + QVERIFY(model.count() == 1); + + QVERIFY(QMetaObject::invokeMethod(item, "removeListViaWorker")); + waitForWorker(item); + + QVERIFY(model.count() == 1); + QVERIFY(spyModelRemoved.count() == 0); + + QVERIFY(QMetaObject::invokeMethod(item, "doSync")); + waitForWorker(item); + + QVERIFY(model.count() == 0); + QVERIFY(spyModelRemoved.count() == 1); + + delete item; + qApp->processEvents(); +} + +void tst_qqmllistmodelworkerscript::dynamic_role_data() +{ + QTest::addColumn("preamble"); + QTest::addColumn("script"); + QTest::addColumn("result"); + + QTest::newRow("sync1") << "{append({'a':[{'b':1},{'b':2}]})}" << "{get(0).a = 'string';count}" << 1; +} + +void tst_qqmllistmodelworkerscript::dynamic_role() +{ + QFETCH(QString, preamble); + QFETCH(QString, script); + QFETCH(int, result); + + QQmlListModel model; + model.setDynamicRoles(true); + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("model.qml")); + QQuickItem *item = createWorkerTest(&engine, &component, &model); + QVERIFY(item != 0); + + QQmlExpression preExp(engine.rootContext(), &model, preamble); + QCOMPARE(preExp.evaluate().toInt(), 0); + + if (script[0] == QLatin1Char('{') && script[script.length()-1] == QLatin1Char('}')) + script = script.mid(1, script.length() - 2); + QVariantList operations; + foreach (const QString &s, script.split(';')) { + if (!s.isEmpty()) + operations << s; + } + + // execute a set of commands on the worker list model, then check the + // changes are reflected in the list model in the main thread + QVERIFY(QMetaObject::invokeMethod(item, "evalExpressionViaWorker", + Q_ARG(QVariant, operations.mid(0, operations.length()-1)))); + waitForWorker(item); + + QQmlExpression e(engine.rootContext(), &model, operations.last().toString()); + QCOMPARE(e.evaluate().toInt(), result); + + delete item; + qApp->processEvents(); +} + +QTEST_MAIN(tst_qqmllistmodelworkerscript) + +#include "tst_qqmllistmodelworkerscript.moc" diff --git a/tests/auto/qml/qqmlmoduleplugin/imports/com/nokia/AutoTestQmlPluginType/qmldir b/tests/auto/qml/qqmlmoduleplugin/imports/com/nokia/AutoTestQmlPluginType/qmldir deleted file mode 100644 index 0a8b5d46eb..0000000000 --- a/tests/auto/qml/qqmlmoduleplugin/imports/com/nokia/AutoTestQmlPluginType/qmldir +++ /dev/null @@ -1 +0,0 @@ -plugin plugin diff --git a/tests/auto/qml/qquicklistmodel/data/enumerate.qml b/tests/auto/qml/qquicklistmodel/data/enumerate.qml deleted file mode 100644 index f73d66b318..0000000000 --- a/tests/auto/qml/qquicklistmodel/data/enumerate.qml +++ /dev/null @@ -1,24 +0,0 @@ -import QtQuick 2.0 - -Item { - property string result - - ListModel { - id: model - - ListElement { - val1: 1 - val2: 2 - val3: "str" - val4: false - val5: true - } - } - - Component.onCompleted: { - var element = model.get(0); - - for (var i in element) - result += i+"="+element[i]+(element[i] ? "Y" : "N")+":"; - } -} diff --git a/tests/auto/qml/qquicklistmodel/data/multipleroles.qml b/tests/auto/qml/qquicklistmodel/data/multipleroles.qml deleted file mode 100644 index 4a331e2b3e..0000000000 --- a/tests/auto/qml/qquicklistmodel/data/multipleroles.qml +++ /dev/null @@ -1,25 +0,0 @@ -import QtQuick 2.0 -ListView { - width: 100 - height: 250 - delegate: Rectangle { - width: 100 - height: 50 - color: black ? "black": "white" - } - model: ListModel { - objectName: "listModel" - ListElement { - black: false - rounded: false - } - ListElement { - black: true - rounded: false - } - ListElement { - black: true - rounded: false - } - } -} diff --git a/tests/auto/qml/qquicklistmodel/data/setmodelcachelist.qml b/tests/auto/qml/qquicklistmodel/data/setmodelcachelist.qml deleted file mode 100644 index 58bf1ccd04..0000000000 --- a/tests/auto/qml/qquicklistmodel/data/setmodelcachelist.qml +++ /dev/null @@ -1,20 +0,0 @@ -import QtQuick 2.0 - -ListModel { - id: model - property bool ok : false - - Component.onCompleted: { - model.append({"attrs": []}) - model.get(0) - model.set(0, {"attrs": [{'abc': 123, 'def': 456}] } ) - ok = ( model.get(0).attrs.get(0).abc == 123 - && model.get(0).attrs.get(0).def == 456 ) - - model.set(0, {"attrs": [{'abc': 789, 'def': 101}] } ) - ok = ( model.get(0).attrs.get(0).abc == 789 - && model.get(0).attrs.get(0).def == 101 ) - - } -} - diff --git a/tests/auto/qml/qquicklistmodel/data/signalhandlers.qml b/tests/auto/qml/qquicklistmodel/data/signalhandlers.qml deleted file mode 100644 index 750d99c5a3..0000000000 --- a/tests/auto/qml/qquicklistmodel/data/signalhandlers.qml +++ /dev/null @@ -1,8 +0,0 @@ -import QtQuick 2.0 - -ListModel{ - property bool ok: false - property int foo: 5 - onFooChanged: ok = true - Component.onCompleted: foo = 6 -} diff --git a/tests/auto/qml/qquicklistmodel/qquicklistmodel.pro b/tests/auto/qml/qquicklistmodel/qquicklistmodel.pro deleted file mode 100644 index e2b88ccec6..0000000000 --- a/tests/auto/qml/qquicklistmodel/qquicklistmodel.pro +++ /dev/null @@ -1,14 +0,0 @@ -CONFIG += testcase -TARGET = tst_qquicklistmodel -macx:CONFIG -= app_bundle - -SOURCES += tst_qquicklistmodel.cpp - -include (../../shared/util.pri) - -TESTDATA = data/* - -CONFIG += parallel_test - -QT += core-private gui-private v8-private qml-private quick-private testlib -DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 diff --git a/tests/auto/qml/qquicklistmodel/tst_qquicklistmodel.cpp b/tests/auto/qml/qquicklistmodel/tst_qquicklistmodel.cpp deleted file mode 100644 index 8deaae9902..0000000000 --- a/tests/auto/qml/qquicklistmodel/tst_qquicklistmodel.cpp +++ /dev/null @@ -1,1258 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** 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, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "../../shared/util.h" - -Q_DECLARE_METATYPE(QList) -Q_DECLARE_METATYPE(QList) - -#define RUNEVAL(object, string) \ - QVERIFY(QMetaObject::invokeMethod(object, "runEval", Q_ARG(QVariant, QString(string)))); - -inline QVariant runexpr(QQmlEngine *engine, const QString &str) -{ - QQmlExpression expr(engine->rootContext(), 0, str); - return expr.evaluate(); -} - -#define RUNEXPR(string) runexpr(&engine, QString(string)) - -static bool isValidErrorMessage(const QString &msg, bool dynamicRoleTest) -{ - bool valid = true; - - if (msg.isEmpty()) { - valid = false; - } else if (dynamicRoleTest) { - if (msg.contains("Can't assign to existing role") || msg.contains("Can't create role for unsupported data type")) - valid = false; - } - - return valid; -} - -class tst_qquicklistmodel : public QQmlDataTest -{ - Q_OBJECT -public: - tst_qquicklistmodel() - { - qRegisterMetaType >(); - } - -private: - int roleFromName(const QQuickListModel *model, const QString &roleName); - - static bool compareVariantList(const QVariantList &testList, QVariant object); - -private slots: - void static_types(); - void static_types_data(); - void static_i18n(); - void static_i18n_data(); - void static_nestedElements(); - void static_nestedElements_data(); - void dynamic_data(); - void dynamic(); - void enumerate(); - void error_data(); - void error(); - void syncError(); - void get(); - void set_data(); - void set(); - void get_data(); - void get_nested(); - void get_nested_data(); - void crash_model_with_multiple_roles(); - void set_model_cache(); - void property_changes(); - void property_changes_data(); - void clear_data(); - void clear(); - void signal_handlers_data(); - void signal_handlers(); - void role_mode_data(); - void role_mode(); - void string_to_list_crash(); - void empty_element_warning(); - void empty_element_warning_data(); - void datetime(); - void datetime_data(); -}; - -bool tst_qquicklistmodel::compareVariantList(const QVariantList &testList, QVariant object) -{ - bool allOk = true; - - QQuickListModel *model = qobject_cast(object.value()); - if (model == 0) - return false; - - if (model->count() != testList.count()) - return false; - - for (int i=0 ; i < testList.count() ; ++i) { - const QVariant &testVariant = testList.at(i); - if (testVariant.type() != QVariant::Map) - return false; - const QVariantMap &map = testVariant.toMap(); - - const QHash roleNames = model->roleNames(); - - QVariantMap::const_iterator it = map.begin(); - QVariantMap::const_iterator end = map.end(); - - while (it != end) { - const QString &testKey = it.key(); - const QVariant &testData = it.value(); - - int roleIndex = roleNames.key(testKey.toUtf8(), -1); - if (roleIndex == -1) - return false; - - const QVariant &modelData = model->data(i, roleIndex); - - if (testData.type() == QVariant::List) { - const QVariantList &subList = testData.toList(); - allOk = allOk && compareVariantList(subList, modelData); - } else { - allOk = allOk && (testData == modelData); - } - - ++it; - } - } - - return allOk; -} - -int tst_qquicklistmodel::roleFromName(const QQuickListModel *model, const QString &roleName) -{ - return model->roleNames().key(roleName.toUtf8(), -1); -} - -void tst_qquicklistmodel::static_types_data() -{ - QTest::addColumn("qml"); - QTest::addColumn("value"); - QTest::addColumn("error"); - - QTest::newRow("string") - << "ListElement { foo: \"bar\" }" - << QVariant(QString("bar")) - << QString(); - - QTest::newRow("real") - << "ListElement { foo: 10.5 }" - << QVariant(10.5) - << QString(); - - QTest::newRow("real0") - << "ListElement { foo: 0 }" - << QVariant(double(0)) - << QString(); - - QTest::newRow("bool") - << "ListElement { foo: false }" - << QVariant(false) - << QString(); - - QTest::newRow("bool") - << "ListElement { foo: true }" - << QVariant(true) - << QString(); - - QTest::newRow("enum") - << "ListElement { foo: Text.AlignHCenter }" - << QVariant(double(QQuickText::AlignHCenter)) - << QString(); - - QTest::newRow("Qt enum") - << "ListElement { foo: Qt.AlignBottom }" - << QVariant(double(Qt::AlignBottom)) - << QString(); - - QTest::newRow("negative enum") - << "ListElement { foo: Animation.Infinite }" - << QVariant(double(QQuickAbstractAnimation::Infinite)) - << QString(); - - QTest::newRow("role error") - << "ListElement { foo: 1 } ListElement { foo: 'string' }" - << QVariant() - << QString(": Can't assign to existing role 'foo' of different type [String -> Number]"); - - QTest::newRow("list type error") - << "ListElement { foo: 1 } ListElement { foo: ListElement { bar: 1 } }" - << QVariant() - << QString(": Can't assign to existing role 'foo' of different type [List -> Number]"); -} - -void tst_qquicklistmodel::static_types() -{ - QFETCH(QString, qml); - QFETCH(QVariant, value); - QFETCH(QString, error); - - qml = "import QtQuick 2.0\nItem { property variant test: model.get(0).foo; ListModel { id: model; " + qml + " } }"; - - if (!error.isEmpty()) { - QTest::ignoreMessage(QtWarningMsg, error.toLatin1()); - } - - QQmlEngine engine; - QQmlComponent component(&engine); - component.setData(qml.toUtf8(), - QUrl::fromLocalFile(QString("dummy.qml"))); - - QVERIFY(!component.isError()); - - QObject *obj = component.create(); - QVERIFY(obj != 0); - - if (error.isEmpty()) { - QVariant actual = obj->property("test"); - - QCOMPARE(actual, value); - QCOMPARE(actual.toString(), value.toString()); - } - - delete obj; -} - -void tst_qquicklistmodel::static_i18n_data() -{ - QTest::addColumn("qml"); - QTest::addColumn("value"); - QTest::addColumn("error"); - - QTest::newRow("QT_TR_NOOP") - << QString::fromUtf8("ListElement { foo: QT_TR_NOOP(\"na\303\257ve\") }") - << QVariant(QString::fromUtf8("na\303\257ve")) - << QString(); - - QTest::newRow("QT_TRANSLATE_NOOP") - << "ListElement { foo: QT_TRANSLATE_NOOP(\"MyListModel\", \"hello\") }" - << QVariant(QString("hello")) - << QString(); - - QTest::newRow("QT_TRID_NOOP") - << QString::fromUtf8("ListElement { foo: QT_TRID_NOOP(\"qtn_1st_text\") }") - << QVariant(QString("qtn_1st_text")) - << QString(); - - QTest::newRow("QT_TR_NOOP extra param") - << QString::fromUtf8("ListElement { foo: QT_TR_NOOP(\"hello\",\"world\") }") - << QVariant(QString()) - << QString("ListElement: improperly specified QT_TR_NOOP"); - - QTest::newRow("QT_TRANSLATE_NOOP missing params") - << "ListElement { foo: QT_TRANSLATE_NOOP() }" - << QVariant(QString()) - << QString("ListElement: improperly specified QT_TRANSLATE_NOOP"); - - QTest::newRow("QT_TRID_NOOP missing param") - << QString::fromUtf8("ListElement { foo: QT_TRID_NOOP() }") - << QVariant(QString()) - << QString("ListElement: improperly specified QT_TRID_NOOP"); -} - -void tst_qquicklistmodel::static_i18n() -{ - QFETCH(QString, qml); - QFETCH(QVariant, value); - QFETCH(QString, error); - - qml = "import QtQuick 2.0\nItem { property variant test: model.get(0).foo; ListModel { id: model; " + qml + " } }"; - - QQmlEngine engine; - QQmlComponent component(&engine); - component.setData(qml.toUtf8(), - QUrl::fromLocalFile(QString("dummy.qml"))); - - if (!error.isEmpty()) { - QVERIFY(component.isError()); - QCOMPARE(component.errors().at(0).description(), error); - return; - } - - QVERIFY(!component.isError()); - - QObject *obj = component.create(); - QVERIFY(obj != 0); - - QVariant actual = obj->property("test"); - - QCOMPARE(actual, value); - QCOMPARE(actual.toString(), value.toString()); - - delete obj; -} - -void tst_qquicklistmodel::static_nestedElements() -{ - QFETCH(int, elementCount); - - QStringList elements; - for (int i=0; iproperty("count"); - QCOMPARE(count.type(), QVariant::Int); - QCOMPARE(count.toInt(), elementCount); - - delete obj; -} - -void tst_qquicklistmodel::static_nestedElements_data() -{ - QTest::addColumn("elementCount"); - - QTest::newRow("0 items") << 0; - QTest::newRow("1 item") << 1; - QTest::newRow("2 items") << 2; - QTest::newRow("many items") << 5; -} - -void tst_qquicklistmodel::dynamic_data() -{ - QTest::addColumn("script"); - QTest::addColumn("result"); - QTest::addColumn("warning"); - QTest::addColumn("dynamicRoles"); - - for (int i=0 ; i < 2 ; ++i) { - bool dr = (i != 0); - - // Simple flat model - QTest::newRow("count") << "count" << 0 << "" << dr; - - QTest::newRow("get1") << "{get(0) === undefined}" << 1 << "" << dr; - QTest::newRow("get2") << "{get(-1) === undefined}" << 1 << "" << dr; - QTest::newRow("get3") << "{append({'foo':123});get(0) != undefined}" << 1 << "" << dr; - QTest::newRow("get4") << "{append({'foo':123});get(0).foo}" << 123 << "" << dr; - QTest::newRow("get-modify1") << "{append({'foo':123,'bar':456});get(0).foo = 333;get(0).foo}" << 333 << "" << dr; - QTest::newRow("get-modify2") << "{append({'z':1});append({'foo':123,'bar':456});get(1).bar = 999;get(1).bar}" << 999 << "" << dr; - - QTest::newRow("append1") << "{append({'foo':123});count}" << 1 << "" << dr; - QTest::newRow("append2") << "{append({'foo':123,'bar':456});count}" << 1 << "" << dr; - QTest::newRow("append3a") << "{append({'foo':123});append({'foo':456});get(0).foo}" << 123 << "" << dr; - QTest::newRow("append3b") << "{append({'foo':123});append({'foo':456});get(1).foo}" << 456 << "" << dr; - QTest::newRow("append4a") << "{append(123)}" << 0 << ": QML ListModel: append: value is not an object" << dr; - QTest::newRow("append4b") << "{append([{'foo':123},{'foo':456},{'foo':789}]);count}" << 3 << "" << dr; - QTest::newRow("append4c") << "{append([{'foo':123},{'foo':456},{'foo':789}]);get(1).foo}" << 456 << "" << dr; - - QTest::newRow("clear1") << "{append({'foo':456});clear();count}" << 0 << "" << dr; - QTest::newRow("clear2") << "{append({'foo':123});append({'foo':456});clear();count}" << 0 << "" << dr; - QTest::newRow("clear3") << "{append({'foo':123});clear()}" << 0 << "" << dr; - - QTest::newRow("remove1") << "{append({'foo':123});remove(0);count}" << 0 << "" << dr; - QTest::newRow("remove2a") << "{append({'foo':123});append({'foo':456});remove(0);count}" << 1 << "" << dr; - QTest::newRow("remove2b") << "{append({'foo':123});append({'foo':456});remove(0);get(0).foo}" << 456 << "" << dr; - QTest::newRow("remove2c") << "{append({'foo':123});append({'foo':456});remove(1);get(0).foo}" << 123 << "" << dr; - QTest::newRow("remove3") << "{append({'foo':123});remove(0)}" << 0 << "" << dr; - QTest::newRow("remove3a") << "{append({'foo':123});remove(-1);count}" << 1 << ": QML ListModel: remove: indices [-1 - 0] out of range [0 - 1]" << dr; - QTest::newRow("remove4a") << "{remove(0)}" << 0 << ": QML ListModel: remove: indices [0 - 1] out of range [0 - 0]" << dr; - QTest::newRow("remove4b") << "{append({'foo':123});remove(0);remove(0);count}" << 0 << ": QML ListModel: remove: indices [0 - 1] out of range [0 - 0]" << dr; - QTest::newRow("remove4c") << "{append({'foo':123});remove(1);count}" << 1 << ": QML ListModel: remove: indices [1 - 2] out of range [0 - 1]" << dr; - QTest::newRow("remove5a") << "{append({'foo':123});append({'foo':456});remove(0,2);count}" << 0 << "" << dr; - QTest::newRow("remove5b") << "{append({'foo':123});append({'foo':456});remove(0,1);count}" << 1 << "" << dr; - QTest::newRow("remove5c") << "{append({'foo':123});append({'foo':456});remove(1,1);count}" << 1 << "" << dr; - QTest::newRow("remove5d") << "{append({'foo':123});append({'foo':456});remove(0,1);get(0).foo}" << 456 << "" << dr; - QTest::newRow("remove5e") << "{append({'foo':123});append({'foo':456});remove(1,1);get(0).foo}" << 123 << "" << dr; - QTest::newRow("remove5f") << "{append({'foo':123});append({'foo':456});append({'foo':789});remove(0,1);remove(1,1);get(0).foo}" << 456 << "" << dr; - QTest::newRow("remove6a") << "{remove();count}" << 0 << ": QML ListModel: remove: incorrect number of arguments" << dr; - QTest::newRow("remove6b") << "{remove(1,2,3);count}" << 0 << ": QML ListModel: remove: incorrect number of arguments" << dr; - QTest::newRow("remove7a") << "{append({'foo':123});remove(0,0);count}" << 1 << ": QML ListModel: remove: indices [0 - 0] out of range [0 - 1]" << dr; - QTest::newRow("remove7b") << "{append({'foo':123});remove(0,-1);count}" << 1 << ": QML ListModel: remove: indices [0 - -1] out of range [0 - 1]" << dr; - - QTest::newRow("insert1") << "{insert(0,{'foo':123});count}" << 1 << "" << dr; - QTest::newRow("insert2") << "{insert(1,{'foo':123});count}" << 0 << ": QML ListModel: insert: index 1 out of range" << dr; - QTest::newRow("insert3a") << "{append({'foo':123});insert(1,{'foo':456});count}" << 2 << "" << dr; - QTest::newRow("insert3b") << "{append({'foo':123});insert(1,{'foo':456});get(0).foo}" << 123 << "" << dr; - QTest::newRow("insert3c") << "{append({'foo':123});insert(1,{'foo':456});get(1).foo}" << 456 << "" << dr; - QTest::newRow("insert3d") << "{append({'foo':123});insert(0,{'foo':456});get(0).foo}" << 456 << "" << dr; - QTest::newRow("insert3e") << "{append({'foo':123});insert(0,{'foo':456});get(1).foo}" << 123 << "" << dr; - QTest::newRow("insert4") << "{append({'foo':123});insert(-1,{'foo':456});count}" << 1 << ": QML ListModel: insert: index -1 out of range" << dr; - QTest::newRow("insert5a") << "{insert(0,123)}" << 0 << ": QML ListModel: insert: value is not an object" << dr; - QTest::newRow("insert5b") << "{insert(0,[{'foo':11},{'foo':22},{'foo':33}]);count}" << 3 << "" << dr; - QTest::newRow("insert5c") << "{insert(0,[{'foo':11},{'foo':22},{'foo':33}]);get(2).foo}" << 33 << "" << dr; - - QTest::newRow("set1") << "{append({'foo':123});set(0,{'foo':456});count}" << 1 << "" << dr; - QTest::newRow("set2") << "{append({'foo':123});set(0,{'foo':456});get(0).foo}" << 456 << "" << dr; - QTest::newRow("set3a") << "{append({'foo':123,'bar':456});set(0,{'foo':999});get(0).foo}" << 999 << "" << dr; - QTest::newRow("set3b") << "{append({'foo':123,'bar':456});set(0,{'foo':999});get(0).bar}" << 456 << "" << dr; - QTest::newRow("set4a") << "{set(0,{'foo':456});count}" << 1 << "" << dr; - QTest::newRow("set4c") << "{set(-1,{'foo':456})}" << 0 << ": QML ListModel: set: index -1 out of range" << dr; - QTest::newRow("set5a") << "{append({'foo':123,'bar':456});set(0,123);count}" << 1 << ": QML ListModel: set: value is not an object" << dr; - QTest::newRow("set5b") << "{append({'foo':123,'bar':456});set(0,[1,2,3]);count}" << 1 << ": QML ListModel: set: value is not an object" << dr; - QTest::newRow("set6") << "{append({'foo':123});set(1,{'foo':456});count}" << 2 << "" << dr; - - QTest::newRow("setprop1") << "{append({'foo':123});setProperty(0,'foo',456);count}" << 1 << "" << dr; - QTest::newRow("setprop2") << "{append({'foo':123});setProperty(0,'foo',456);get(0).foo}" << 456 << "" << dr; - QTest::newRow("setprop3a") << "{append({'foo':123,'bar':456});setProperty(0,'foo',999);get(0).foo}" << 999 << "" << dr; - QTest::newRow("setprop3b") << "{append({'foo':123,'bar':456});setProperty(0,'foo',999);get(0).bar}" << 456 << "" << dr; - QTest::newRow("setprop4a") << "{setProperty(0,'foo',456)}" << 0 << ": QML ListModel: set: index 0 out of range" << dr; - QTest::newRow("setprop4b") << "{setProperty(-1,'foo',456)}" << 0 << ": QML ListModel: set: index -1 out of range" << dr; - QTest::newRow("setprop4c") << "{append({'foo':123,'bar':456});setProperty(1,'foo',456);count}" << 1 << ": QML ListModel: set: index 1 out of range" << dr; - QTest::newRow("setprop5") << "{append({'foo':123,'bar':456});append({'foo':111});setProperty(1,'bar',222);get(1).bar}" << 222 << "" << dr; - - QTest::newRow("move1a") << "{append({'foo':123});append({'foo':456});move(0,1,1);count}" << 2 << "" << dr; - QTest::newRow("move1b") << "{append({'foo':123});append({'foo':456});move(0,1,1);get(0).foo}" << 456 << "" << dr; - QTest::newRow("move1c") << "{append({'foo':123});append({'foo':456});move(0,1,1);get(1).foo}" << 123 << "" << dr; - QTest::newRow("move1d") << "{append({'foo':123});append({'foo':456});move(1,0,1);get(0).foo}" << 456 << "" << dr; - QTest::newRow("move1e") << "{append({'foo':123});append({'foo':456});move(1,0,1);get(1).foo}" << 123 << "" << dr; - QTest::newRow("move2a") << "{append({'foo':123});append({'foo':456});append({'foo':789});move(0,1,2);count}" << 3 << "" << dr; - QTest::newRow("move2b") << "{append({'foo':123});append({'foo':456});append({'foo':789});move(0,1,2);get(0).foo}" << 789 << "" << dr; - QTest::newRow("move2c") << "{append({'foo':123});append({'foo':456});append({'foo':789});move(0,1,2);get(1).foo}" << 123 << "" << dr; - QTest::newRow("move2d") << "{append({'foo':123});append({'foo':456});append({'foo':789});move(0,1,2);get(2).foo}" << 456 << "" << dr; - QTest::newRow("move3a") << "{append({'foo':123});append({'foo':456});append({'foo':789});move(1,0,3);count}" << 3 << ": QML ListModel: move: out of range" << dr; - QTest::newRow("move3b") << "{append({'foo':123});append({'foo':456});append({'foo':789});move(1,-1,1);count}" << 3 << ": QML ListModel: move: out of range" << dr; - QTest::newRow("move3c") << "{append({'foo':123});append({'foo':456});append({'foo':789});move(1,0,-1);count}" << 3 << ": QML ListModel: move: out of range" << dr; - QTest::newRow("move3d") << "{append({'foo':123});append({'foo':456});append({'foo':789});move(0,3,1);count}" << 3 << ": QML ListModel: move: out of range" << dr; - - QTest::newRow("large1") << "{append({'a':1,'b':2,'c':3,'d':4,'e':5,'f':6,'g':7,'h':8});get(0).h}" << 8 << "" << dr; - - QTest::newRow("datatypes1") << "{append({'a':1});append({'a':'string'});}" << 0 << ": Can't assign to existing role 'a' of different type [String -> Number]" << dr; - - QTest::newRow("null") << "{append({'a':null});}" << 0 << "" << dr; - QTest::newRow("setNull") << "{append({'a':1});set(0, {'a':null});}" << 0 << "" << dr; - QTest::newRow("setString") << "{append({'a':'hello'});set(0, {'a':'world'});get(0).a == 'world'}" << 1 << "" << dr; - QTest::newRow("setInt") << "{append({'a':5});set(0, {'a':10});get(0).a}" << 10 << "" << dr; - QTest::newRow("setNumber") << "{append({'a':6});set(0, {'a':5.5});get(0).a < 5.6}" << 1 << "" << dr; - QTest::newRow("badType0") << "{append({'a':'hello'});set(0, {'a':1});}" << 0 << ": Can't assign to existing role 'a' of different type [Number -> String]" << dr; - QTest::newRow("invalidInsert0") << "{insert(0);}" << 0 << ": QML ListModel: insert: value is not an object" << dr; - QTest::newRow("invalidAppend0") << "{append();}" << 0 << ": QML ListModel: append: value is not an object" << dr; - QTest::newRow("invalidInsert1") << "{insert(0, 34);}" << 0 << ": QML ListModel: insert: value is not an object" << dr; - QTest::newRow("invalidAppend1") << "{append(37);}" << 0 << ": QML ListModel: append: value is not an object" << dr; - - // QObjects - QTest::newRow("qobject0") << "{append({'a':dummyItem0});}" << 0 << "" << dr; - QTest::newRow("qobject1") << "{append({'a':dummyItem0});set(0,{'a':dummyItem1});get(0).a == dummyItem1;}" << 1 << "" << dr; - QTest::newRow("qobject2") << "{append({'a':dummyItem0});get(0).a == dummyItem0;}" << 1 << "" << dr; - QTest::newRow("qobject3") << "{append({'a':dummyItem0});append({'b':1});}" << 0 << "" << dr; - - // JS objects - QTest::newRow("js1") << "{append({'foo':{'prop':1}});count}" << 1 << "" << dr; - QTest::newRow("js2") << "{append({'foo':{'prop':27}});get(0).foo.prop}" << 27 << "" << dr; - QTest::newRow("js3") << "{append({'foo':{'prop':27}});append({'bar':1});count}" << 2 << "" << dr; - QTest::newRow("js4") << "{append({'foo':{'prop':27}});append({'bar':1});set(0, {'foo':{'prop':28}});get(0).foo.prop}" << 28 << "" << dr; - QTest::newRow("js5") << "{append({'foo':{'prop':27}});append({'bar':1});set(1, {'foo':{'prop':33}});get(1).foo.prop}" << 33 << "" << dr; - QTest::newRow("js6") << "{append({'foo':{'prop':27}});clear();count}" << 0 << "" << dr; - QTest::newRow("js7") << "{append({'foo':{'prop':27}});set(0, {'foo':null});count}" << 1 << "" << dr; - QTest::newRow("js8") << "{append({'foo':{'prop':27}});set(0, {'foo':{'prop2':31}});get(0).foo.prop2}" << 31 << "" << dr; - - // Nested models - QTest::newRow("nested-append1") << "{append({'foo':123,'bars':[{'a':1},{'a':2},{'a':3}]});count}" << 1 << "" << dr; - QTest::newRow("nested-append2") << "{append({'foo':123,'bars':[{'a':1},{'a':2},{'a':3}]});get(0).bars.get(1).a}" << 2 << "" << dr; - QTest::newRow("nested-append3") << "{append({'foo':123,'bars':[{'a':1},{'a':2},{'a':3}]});get(0).bars.append({'a':4});get(0).bars.get(3).a}" << 4 << "" << dr; - - QTest::newRow("nested-insert") << "{append({'foo':123});insert(0,{'bars':[{'a':1},{'b':2},{'c':3}]});get(0).bars.get(0).a}" << 1 << "" << dr; - QTest::newRow("nested-set") << "{append({'foo':[{'x':1}]});set(0,{'foo':[{'x':123}]});get(0).foo.get(0).x}" << 123 << "" << dr; - - QTest::newRow("nested-count") << "{append({'foo':123,'bars':[{'a':1},{'a':2},{'a':3}]}); get(0).bars.count}" << 3 << "" << dr; - QTest::newRow("nested-clear") << "{append({'foo':123,'bars':[{'a':1},{'a':2},{'a':3}]}); get(0).bars.clear(); get(0).bars.count}" << 0 << "" << dr; - } -} - -void tst_qquicklistmodel::dynamic() -{ - QFETCH(QString, script); - QFETCH(int, result); - QFETCH(QString, warning); - QFETCH(bool, dynamicRoles); - - QQuickItem dummyItem0, dummyItem1; - QQmlEngine engine; - QQuickListModel model; - model.setDynamicRoles(dynamicRoles); - QQmlEngine::setContextForObject(&model,engine.rootContext()); - engine.rootContext()->setContextObject(&model); - engine.rootContext()->setContextProperty("dummyItem0", QVariant::fromValue(&dummyItem0)); - engine.rootContext()->setContextProperty("dummyItem1", QVariant::fromValue(&dummyItem1)); - QQmlExpression e(engine.rootContext(), &model, script); - if (isValidErrorMessage(warning, dynamicRoles)) - QTest::ignoreMessage(QtWarningMsg, warning.toLatin1()); - - QSignalSpy spyCount(&model, SIGNAL(countChanged())); - - int actual = e.evaluate().toInt(); - if (e.hasError()) - qDebug() << e.error(); // errors not expected - - QCOMPARE(actual,result); - - if (model.count() > 0) - QVERIFY(spyCount.count() > 0); -} - -void tst_qquicklistmodel::enumerate() -{ - QQmlEngine eng; - QQmlComponent component(&eng, testFileUrl("enumerate.qml")); - QVERIFY(!component.isError()); - QQuickItem *item = qobject_cast(component.create()); - QVERIFY(item != 0); - - QLatin1String expectedStrings[] = { - QLatin1String("val1=1Y"), - QLatin1String("val2=2Y"), - QLatin1String("val3=strY"), - QLatin1String("val4=falseN"), - QLatin1String("val5=trueY") - }; - - int expectedStringCount = sizeof(expectedStrings) / sizeof(expectedStrings[0]); - - QStringList r = item->property("result").toString().split(":"); - - int matchCount = 0; - for (int i=0 ; i < expectedStringCount ; ++i) { - const QLatin1String &expectedString = expectedStrings[i]; - - QStringList::const_iterator it = r.begin(); - QStringList::const_iterator end = r.end(); - - while (it != end) { - if (it->compare(expectedString) == 0) { - ++matchCount; - break; - } - ++it; - } - } - - QVERIFY(matchCount == expectedStringCount); - - delete item; -} - -void tst_qquicklistmodel::error_data() -{ - QTest::addColumn("qml"); - QTest::addColumn("error"); - - QTest::newRow("id not allowed in ListElement") - << "import QtQuick 2.0\nListModel { ListElement { id: fred } }" - << "ListElement: cannot use reserved \"id\" property"; - - QTest::newRow("id allowed in ListModel") - << "import QtQuick 2.0\nListModel { id:model }" - << ""; - - QTest::newRow("random properties not allowed in ListModel") - << "import QtQuick 2.0\nListModel { foo:123 }" - << "ListModel: undefined property 'foo'"; - - QTest::newRow("random properties allowed in ListElement") - << "import QtQuick 2.0\nListModel { ListElement { foo:123 } }" - << ""; - - QTest::newRow("bindings not allowed in ListElement") - << "import QtQuick 2.0\nRectangle { id: rect; ListModel { ListElement { foo: rect.color } } }" - << "ListElement: cannot use script for property value"; - - QTest::newRow("random object list properties allowed in ListElement") - << "import QtQuick 2.0\nListModel { ListElement { foo: [ ListElement { bar: 123 } ] } }" - << ""; - - QTest::newRow("default properties not allowed in ListElement") - << "import QtQuick 2.0\nListModel { ListElement { Item { } } }" - << "ListElement: cannot contain nested elements"; - - QTest::newRow("QML elements not allowed in ListElement") - << "import QtQuick 2.0\nListModel { ListElement { a: Item { } } }" - << "ListElement: cannot contain nested elements"; - - QTest::newRow("qualified ListElement supported") - << "import QtQuick 2.0 as Foo\nFoo.ListModel { Foo.ListElement { a: 123 } }" - << ""; - - QTest::newRow("qualified ListElement required") - << "import QtQuick 2.0 as Foo\nFoo.ListModel { ListElement { a: 123 } }" - << "ListElement is not a type"; - - QTest::newRow("unknown qualified ListElement not allowed") - << "import QtQuick 2.0\nListModel { Foo.ListElement { a: 123 } }" - << "Foo.ListElement - Foo is not a namespace"; -} - -void tst_qquicklistmodel::error() -{ - QFETCH(QString, qml); - QFETCH(QString, error); - - QQmlEngine engine; - QQmlComponent component(&engine); - component.setData(qml.toUtf8(), - QUrl::fromLocalFile(QString("dummy.qml"))); - if (error.isEmpty()) { - QVERIFY(!component.isError()); - } else { - QVERIFY(component.isError()); - QList errors = component.errors(); - QCOMPARE(errors.count(),1); - QCOMPARE(errors.at(0).description(),error); - } -} - -void tst_qquicklistmodel::syncError() -{ - QString qml = "import QtQuick 2.0\nListModel { id: lm; Component.onCompleted: lm.sync() }"; - QString error = "file:dummy.qml:2:1: QML ListModel: List sync() can only be called from a WorkerScript"; - - QQmlEngine engine; - QQmlComponent component(&engine); - component.setData(qml.toUtf8(), - QUrl::fromLocalFile(QString("dummy.qml"))); - QTest::ignoreMessage(QtWarningMsg,error.toUtf8()); - QObject *obj = component.create(); - QVERIFY(obj); - delete obj; -} - -/* - Test model changes from set() are available to the view -*/ -void tst_qquicklistmodel::set_data() -{ - QTest::addColumn("dynamicRoles"); - - QTest::newRow("staticRoles") << false; - QTest::newRow("dynamicRoles") << true; -} - -void tst_qquicklistmodel::set() -{ - QFETCH(bool, dynamicRoles); - - QQmlEngine engine; - QQuickListModel model; - model.setDynamicRoles(dynamicRoles); - QQmlEngine::setContextForObject(&model,engine.rootContext()); - engine.rootContext()->setContextProperty("model", &model); - - RUNEXPR("model.append({test:false})"); - RUNEXPR("model.set(0, {test:true})"); - - QCOMPARE(RUNEXPR("model.get(0).test").toBool(), true); // triggers creation of model cache - QCOMPARE(model.data(0, 0), qVariantFromValue(true)); - - RUNEXPR("model.set(0, {test:false})"); - QCOMPARE(RUNEXPR("model.get(0).test").toBool(), false); // tests model cache is updated - QCOMPARE(model.data(0, 0), qVariantFromValue(false)); - - QString warning = QString::fromLatin1(": Can't create role for unsupported data type"); - if (isValidErrorMessage(warning, dynamicRoles)) - QTest::ignoreMessage(QtWarningMsg, warning.toLatin1()); - QVariant invalidData = QColor(); - model.setProperty(0, "test", invalidData); -} - -/* - Test model changes on values returned by get() are available to the view -*/ -void tst_qquicklistmodel::get() -{ - QFETCH(QString, expression); - QFETCH(int, index); - QFETCH(QString, roleName); - QFETCH(QVariant, roleValue); - QFETCH(bool, dynamicRoles); - - QQmlEngine engine; - QQmlComponent component(&engine); - component.setData( - "import QtQuick 2.0\n" - "ListModel {}\n", QUrl()); - QQuickListModel *model = qobject_cast(component.create()); - model->setDynamicRoles(dynamicRoles); - engine.rootContext()->setContextProperty("model", model); - - RUNEXPR("model.append({roleA: 100})"); - RUNEXPR("model.append({roleA: 200, roleB: 400})"); - RUNEXPR("model.append({roleA: 200, roleB: 400})"); - RUNEXPR("model.append({roleC: {} })"); - RUNEXPR("model.append({roleD: [ { a:1, b:2 }, { c: 3 } ] })"); - - QSignalSpy spy(model, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector))); - QQmlExpression expr(engine.rootContext(), model, expression); - expr.evaluate(); - QVERIFY(!expr.hasError()); - - int role = roleFromName(model, roleName); - QVERIFY(role >= 0); - - if (roleValue.type() == QVariant::List) { - const QVariantList &list = roleValue.toList(); - QVERIFY(compareVariantList(list, model->data(index, role))); - } else { - QCOMPARE(model->data(index, role), roleValue); - } - - QCOMPARE(spy.count(), 1); - - QList spyResult = spy.takeFirst(); - QCOMPARE(spyResult.at(0).value(), model->index(index, 0, QModelIndex())); - QCOMPARE(spyResult.at(1).value(), model->index(index, 0, QModelIndex())); // only 1 item is modified at a time - QCOMPARE(spyResult.at(2).value >(), (QVector() << role)); - - delete model; -} - -void tst_qquicklistmodel::get_data() -{ - QTest::addColumn("expression"); - QTest::addColumn("index"); - QTest::addColumn("roleName"); - QTest::addColumn("roleValue"); - QTest::addColumn("dynamicRoles"); - - for (int i=0 ; i < 2 ; ++i) { - bool dr = (i != 0); - - QTest::newRow("simple value") << "get(0).roleA = 500" << 0 << "roleA" << QVariant(500) << dr; - QTest::newRow("simple value 2") << "get(1).roleB = 500" << 1 << "roleB" << QVariant(500) << dr; - - QVariantMap map; - QVariantList list; - map.clear(); map["a"] = 50; map["b"] = 500; - list << map; - map.clear(); map["c"] = 1000; - list << map; - QTest::newRow("list of objects") << "get(2).roleD = [{'a': 50, 'b': 500}, {'c': 1000}]" << 2 << "roleD" << QVariant::fromValue(list) << dr; - } -} - -/* - Test that the tests run in get() also work for nested list data -*/ -void tst_qquicklistmodel::get_nested() -{ - QFETCH(QString, expression); - QFETCH(int, index); - QFETCH(QString, roleName); - QFETCH(QVariant, roleValue); - QFETCH(bool, dynamicRoles); - - if (roleValue.type() == QVariant::Map) - return; - - QQmlEngine engine; - QQmlComponent component(&engine); - component.setData( - "import QtQuick 2.0\n" - "ListModel {}", QUrl()); - QQuickListModel *model = qobject_cast(component.create()); - model->setDynamicRoles(dynamicRoles); - QVERIFY(component.errorString().isEmpty()); - QQuickListModel *childModel; - engine.rootContext()->setContextProperty("model", model); - - RUNEXPR("model.append({ listRoleA: [\n" - "{ roleA: 100 },\n" - "{ roleA: 200, roleB: 400 },\n" - "{ roleA: 200, roleB: 400 }, \n" - "{ roleC: {} }, \n" - "{ roleD: [ { a: 1, b:2 }, { c: 3 } ] } \n" - "] })\n"); - - RUNEXPR("model.append({ listRoleA: [\n" - "{ roleA: 100 },\n" - "{ roleA: 200, roleB: 400 },\n" - "{ roleA: 200, roleB: 400 }, \n" - "{ roleC: {} }, \n" - "{ roleD: [ { a: 1, b:2 }, { c: 3 } ] } \n" - "],\n" - "listRoleB: [\n" - "{ roleA: 100 },\n" - "{ roleA: 200, roleB: 400 },\n" - "{ roleA: 200, roleB: 400 }, \n" - "{ roleC: {} }, \n" - "{ roleD: [ { a: 1, b:2 }, { c: 3 } ] } \n" - "],\n" - "listRoleC: [\n" - "{ roleA: 100 },\n" - "{ roleA: 200, roleB: 400 },\n" - "{ roleA: 200, roleB: 400 }, \n" - "{ roleC: {} }, \n" - "{ roleD: [ { a: 1, b:2 }, { c: 3 } ] } \n" - "] })\n"); - - // Test setting the inner list data for: - // get(0).listRoleA - // get(1).listRoleA - // get(1).listRoleB - // get(1).listRoleC - - QList > testData; - testData << qMakePair(0, QString("listRoleA")); - testData << qMakePair(1, QString("listRoleA")); - testData << qMakePair(1, QString("listRoleB")); - testData << qMakePair(1, QString("listRoleC")); - - for (int i=0; i= 0); - - childModel = qobject_cast(model->data(outerListIndex, outerListRole).value()); - QVERIFY(childModel); - - QString extendedExpression = QString("get(%1).%2.%3").arg(outerListIndex).arg(outerListRoleName).arg(expression); - QQmlExpression expr(engine.rootContext(), model, extendedExpression); - - QSignalSpy spy(childModel, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector))); - expr.evaluate(); - QVERIFY(!expr.hasError()); - - int role = roleFromName(childModel, roleName); - QVERIFY(role >= 0); - if (roleValue.type() == QVariant::List) { - QVERIFY(compareVariantList(roleValue.toList(), childModel->data(index, role))); - } else { - QCOMPARE(childModel->data(index, role), roleValue); - } - QCOMPARE(spy.count(), 1); - - QList spyResult = spy.takeFirst(); - QCOMPARE(spyResult.at(0).value(), childModel->index(index, 0, QModelIndex())); - QCOMPARE(spyResult.at(1).value(), childModel->index(index, 0, QModelIndex())); // only 1 item is modified at a time - QCOMPARE(spyResult.at(2).value >(), (QVector() << role)); - } - - delete model; -} - -void tst_qquicklistmodel::get_nested_data() -{ - get_data(); -} - -//QTBUG-13754 -void tst_qquicklistmodel::crash_model_with_multiple_roles() -{ - QQmlEngine eng; - QQmlComponent component(&eng, testFileUrl("multipleroles.qml")); - QObject *rootItem = component.create(); - QVERIFY(component.errorString().isEmpty()); - QVERIFY(rootItem != 0); - QQuickListModel *model = rootItem->findChild("listModel"); - QVERIFY(model != 0); - - // used to cause a crash - model->setProperty(0, "black", true); - - delete rootItem; -} - -//QTBUG-15190 -void tst_qquicklistmodel::set_model_cache() -{ - QQmlEngine eng; - QQmlComponent component(&eng, testFileUrl("setmodelcachelist.qml")); - QObject *model = component.create(); - QVERIFY2(component.errorString().isEmpty(), QTest::toString(component.errorString())); - QVERIFY(model != 0); - QVERIFY(model->property("ok").toBool()); - - delete model; -} - -void tst_qquicklistmodel::property_changes() -{ - QFETCH(QString, script_setup); - QFETCH(QString, script_change); - QFETCH(QString, roleName); - QFETCH(int, listIndex); - QFETCH(bool, itemsChanged); - QFETCH(QString, testExpression); - QFETCH(bool, dynamicRoles); - - QQmlEngine engine; - QQuickListModel model; - model.setDynamicRoles(dynamicRoles); - QQmlEngine::setContextForObject(&model, engine.rootContext()); - engine.rootContext()->setContextObject(&model); - - QQmlExpression expr(engine.rootContext(), &model, script_setup); - expr.evaluate(); - QVERIFY2(!expr.hasError(), QTest::toString(expr.error().toString())); - - QString signalHandler = "on" + QString(roleName[0].toUpper()) + roleName.mid(1, roleName.length()) + "Changed:"; - QString qml = "import QtQuick 2.0\n" - "Connections {\n" - "property bool gotSignal: false\n" - "target: model.get(" + QString::number(listIndex) + ")\n" - + signalHandler + " gotSignal = true\n" - "}\n"; - - QQmlComponent component(&engine); - component.setData(qml.toUtf8(), QUrl::fromLocalFile("")); - engine.rootContext()->setContextProperty("model", &model); - QObject *connectionsObject = component.create(); - QVERIFY2(component.errorString().isEmpty(), QTest::toString(component.errorString())); - - QSignalSpy spyItemsChanged(&model, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector))); - - expr.setExpression(script_change); - expr.evaluate(); - QVERIFY2(!expr.hasError(), QTest::toString(expr.error())); - - // test the object returned by get() emits the correct signals - QCOMPARE(connectionsObject->property("gotSignal").toBool(), itemsChanged); - - // test itemsChanged() is emitted correctly - if (itemsChanged) { - QCOMPARE(spyItemsChanged.count(), 1); - QCOMPARE(spyItemsChanged.at(0).at(0).value(), model.index(listIndex, 0, QModelIndex())); - QCOMPARE(spyItemsChanged.at(0).at(1).value(), model.index(listIndex, 0, QModelIndex())); - } else { - QCOMPARE(spyItemsChanged.count(), 0); - } - - expr.setExpression(testExpression); - QCOMPARE(expr.evaluate().toBool(), true); - - delete connectionsObject; -} - -void tst_qquicklistmodel::property_changes_data() -{ - QTest::addColumn("script_setup"); - QTest::addColumn("script_change"); - QTest::addColumn("roleName"); - QTest::addColumn("listIndex"); - QTest::addColumn("itemsChanged"); - QTest::addColumn("testExpression"); - QTest::addColumn("dynamicRoles"); - - for (int i=0 ; i < 2 ; ++i) { - bool dr = (i != 0); - - QTest::newRow("set: plain") << "append({'a':123, 'b':456, 'c':789});" << "set(0,{'b':123});" - << "b" << 0 << true << "get(0).b == 123" << dr; - QTest::newRow("setProperty: plain") << "append({'a':123, 'b':456, 'c':789});" << "setProperty(0, 'b', 123);" - << "b" << 0 << true << "get(0).b == 123" << dr; - - QTest::newRow("set: plain, no changes") << "append({'a':123, 'b':456, 'c':789});" << "set(0,{'b':456});" - << "b" << 0 << false << "get(0).b == 456" << dr; - QTest::newRow("setProperty: plain, no changes") << "append({'a':123, 'b':456, 'c':789});" << "setProperty(0, 'b', 456);" - << "b" << 0 << false << "get(0).b == 456" << dr; - - QTest::newRow("set: inserted item") - << "{append({'a':123, 'b':456, 'c':789}); get(0); insert(0, {'a':0, 'b':0, 'c':0});}" - << "set(1, {'a':456});" - << "a" << 1 << true << "get(1).a == 456" << dr; - QTest::newRow("setProperty: inserted item") - << "{append({'a':123, 'b':456, 'c':789}); get(0); insert(0, {'a':0, 'b':0, 'c':0});}" - << "setProperty(1, 'a', 456);" - << "a" << 1 << true << "get(1).a == 456" << dr; - QTest::newRow("get: inserted item") - << "{append({'a':123, 'b':456, 'c':789}); get(0); insert(0, {'a':0, 'b':0, 'c':0});}" - << "get(1).a = 456;" - << "a" << 1 << true << "get(1).a == 456" << dr; - QTest::newRow("set: removed item") - << "{append({'a':0, 'b':0, 'c':0}); append({'a':123, 'b':456, 'c':789}); get(1); remove(0);}" - << "set(0, {'a':456});" - << "a" << 0 << true << "get(0).a == 456" << dr; - QTest::newRow("setProperty: removed item") - << "{append({'a':0, 'b':0, 'c':0}); append({'a':123, 'b':456, 'c':789}); get(1); remove(0);}" - << "setProperty(0, 'a', 456);" - << "a" << 0 << true << "get(0).a == 456" << dr; - QTest::newRow("get: removed item") - << "{append({'a':0, 'b':0, 'c':0}); append({'a':123, 'b':456, 'c':789}); get(1); remove(0);}" - << "get(0).a = 456;" - << "a" << 0 << true << "get(0).a == 456" << dr; - - // Following tests only call set() since setProperty() only allows plain - // values, not lists, as the argument. - // Note that when a list is changed, itemsChanged() is currently always - // emitted regardless of whether it actually changed or not. - - QTest::newRow("nested-set: list, new size") << "append({'a':123, 'b':[{'a':1},{'a':2},{'a':3}], 'c':789});" << "set(0,{'b':[{'a':1},{'a':2}]});" - << "b" << 0 << true << "get(0).b.get(0).a == 1 && get(0).b.get(1).a == 2" << dr; - - QTest::newRow("nested-set: list, empty -> non-empty") << "append({'a':123, 'b':[], 'c':789});" << "set(0,{'b':[{'a':1},{'a':2},{'a':3}]});" - << "b" << 0 << true << "get(0).b.get(0).a == 1 && get(0).b.get(1).a == 2 && get(0).b.get(2).a == 3" << dr; - - QTest::newRow("nested-set: list, non-empty -> empty") << "append({'a':123, 'b':[{'a':1},{'a':2},{'a':3}], 'c':789});" << "set(0,{'b':[]});" - << "b" << 0 << true << "get(0).b.count == 0" << dr; - - QTest::newRow("nested-set: list, same size, different values") << "append({'a':123, 'b':[{'a':1},{'a':2},{'a':3}], 'c':789});" << "set(0,{'b':[{'a':1},{'a':222},{'a':3}]});" - << "b" << 0 << true << "get(0).b.get(0).a == 1 && get(0).b.get(1).a == 222 && get(0).b.get(2).a == 3" << dr; - - QTest::newRow("nested-set: list, no changes") << "append({'a':123, 'b':[{'a':1},{'a':2},{'a':3}], 'c':789});" << "set(0,{'b':[{'a':1},{'a':2},{'a':3}]});" - << "b" << 0 << true << "get(0).b.get(0).a == 1 && get(0).b.get(1).a == 2 && get(0).b.get(2).a == 3" << dr; - - QTest::newRow("nested-set: list, no changes, empty") << "append({'a':123, 'b':[], 'c':789});" << "set(0,{'b':[]});" - << "b" << 0 << true << "get(0).b.count == 0" << dr; - } -} - -void tst_qquicklistmodel::clear_data() -{ - QTest::addColumn("dynamicRoles"); - - QTest::newRow("staticRoles") << false; - QTest::newRow("dynamicRoles") << true; -} - -void tst_qquicklistmodel::clear() -{ - QFETCH(bool, dynamicRoles); - - QQmlEngine engine; - QQuickListModel model; - model.setDynamicRoles(dynamicRoles); - QQmlEngine::setContextForObject(&model, engine.rootContext()); - engine.rootContext()->setContextProperty("model", &model); - - model.clear(); - QCOMPARE(model.count(), 0); - - RUNEXPR("model.append({propertyA: \"value a\", propertyB: \"value b\"})"); - QCOMPARE(model.count(), 1); - - model.clear(); - QCOMPARE(model.count(), 0); - - RUNEXPR("model.append({propertyA: \"value a\", propertyB: \"value b\"})"); - RUNEXPR("model.append({propertyA: \"value a\", propertyB: \"value b\"})"); - QCOMPARE(model.count(), 2); - - model.clear(); - QCOMPARE(model.count(), 0); - - // clearing does not remove the roles - RUNEXPR("model.append({propertyA: \"value a\", propertyB: \"value b\", propertyC: \"value c\"})"); - QHash roleNames = model.roleNames(); - model.clear(); - QCOMPARE(model.count(), 0); - QCOMPARE(model.roleNames(), roleNames); - QCOMPARE(roleNames[0], QByteArray("propertyA")); - QCOMPARE(roleNames[1], QByteArray("propertyB")); - QCOMPARE(roleNames[2], QByteArray("propertyC")); -} - -void tst_qquicklistmodel::signal_handlers_data() -{ - QTest::addColumn("dynamicRoles"); - - QTest::newRow("staticRoles") << false; - QTest::newRow("dynamicRoles") << true; -} - -void tst_qquicklistmodel::signal_handlers() -{ - QFETCH(bool, dynamicRoles); - - QQmlEngine eng; - QQmlComponent component(&eng, testFileUrl("signalhandlers.qml")); - QObject *model = component.create(); - QQuickListModel *lm = qobject_cast(model); - QVERIFY(lm != 0); - lm->setDynamicRoles(dynamicRoles); - QVERIFY2(component.errorString().isEmpty(), QTest::toString(component.errorString())); - QVERIFY(model != 0); - QVERIFY(model->property("ok").toBool()); - - delete model; -} - -void tst_qquicklistmodel::role_mode_data() -{ - QTest::addColumn("script"); - QTest::addColumn("result"); - QTest::addColumn("warning"); - - QTest::newRow("default0") << "{dynamicRoles}" << 0 << ""; - QTest::newRow("default1") << "{append({'a':1});dynamicRoles}" << 0 << ""; - - QTest::newRow("enableDynamic0") << "{dynamicRoles=true;dynamicRoles}" << 1 << ""; - QTest::newRow("enableDynamic1") << "{append({'a':1});dynamicRoles=true;dynamicRoles}" << 0 << ": QML ListModel: unable to enable dynamic roles as this model is not empty!"; - QTest::newRow("enableDynamic2") << "{dynamicRoles=true;append({'a':1});dynamicRoles=false;dynamicRoles}" << 1 << ": QML ListModel: unable to enable static roles as this model is not empty!"; -} - -void tst_qquicklistmodel::role_mode() -{ - QFETCH(QString, script); - QFETCH(int, result); - QFETCH(QString, warning); - - QQmlEngine engine; - QQuickListModel model; - QQmlEngine::setContextForObject(&model,engine.rootContext()); - engine.rootContext()->setContextObject(&model); - QQmlExpression e(engine.rootContext(), &model, script); - if (!warning.isEmpty()) - QTest::ignoreMessage(QtWarningMsg, warning.toLatin1()); - - int actual = e.evaluate().toInt(); - if (e.hasError()) - qDebug() << e.error(); // errors not expected - - QCOMPARE(actual,result); -} - -void tst_qquicklistmodel::string_to_list_crash() -{ - QQmlEngine engine; - QQuickListModel model; - QQmlEngine::setContextForObject(&model,engine.rootContext()); - engine.rootContext()->setContextObject(&model); - QString script = QLatin1String("{append({'a':'data'});get(0).a = [{'x':123}]}"); - QTest::ignoreMessage(QtWarningMsg, ": Can't assign to existing role 'a' of different type [String -> List]"); - QQmlExpression e(engine.rootContext(), &model, script); - // Don't crash! - e.evaluate(); -} - -void tst_qquicklistmodel::empty_element_warning_data() -{ - QTest::addColumn("qml"); - QTest::addColumn("warning"); - - QTest::newRow("empty") << "import QtQuick 2.0\nListModel {}" << false; - QTest::newRow("withid") << "import QtQuick 2.0\nListModel { id: model }" << false; - QTest::newRow("emptyElement") << "import QtQuick 2.0\nListModel { ListElement {} }" << true; - QTest::newRow("emptyElements") << "import QtQuick 2.0\nListModel { ListElement {} ListElement {} }" << true; - QTest::newRow("role1") << "import QtQuick 2.0\nListModel { ListElement {a:1} }" << false; - QTest::newRow("role2") << "import QtQuick 2.0\nListModel { ListElement {} ListElement {a:1} ListElement {} }" << false; - QTest::newRow("role3") << "import QtQuick 2.0\nListModel { ListElement {} ListElement {a:1} ListElement {b:2} }" << false; -} - -void tst_qquicklistmodel::empty_element_warning() -{ - QFETCH(QString, qml); - QFETCH(bool, warning); - - if (warning) { - QString warningString = QLatin1String("file:dummy.qml:2:1: QML ListModel: All ListElement declarations are empty, no roles can be created unless dynamicRoles is set."); - QTest::ignoreMessage(QtWarningMsg, warningString.toLatin1()); - } - - QQmlEngine engine; - QQmlComponent component(&engine); - component.setData(qml.toUtf8(), QUrl::fromLocalFile(QString("dummy.qml"))); - QVERIFY(!component.isError()); - - QObject *obj = component.create(); - QVERIFY(obj != 0); - - delete obj; -} - -void tst_qquicklistmodel::datetime_data() -{ - QTest::addColumn("qml"); - QTest::addColumn("expected"); - - QDateTime dt; - QDateTime dt0(QDate(1900, 1, 2), QTime( 8, 14)); - QDateTime dt1(QDate(2000, 11, 22), QTime(10, 45)); - - QTest::newRow("dt0") << "{append({'date':dt0});get(0).date}" << dt0; - QTest::newRow("dt1") << "{append({'date':dt0});get(0).date=dt1;get(0).date}" << dt1; - QTest::newRow("dt2") << "{append({'date':dt0});set(0,{'date':dt1});get(0).date}" << dt1; - QTest::newRow("dt3") << "{append({'date':dt0});get(0).date=undefined;get(0).date}" << dt; -} - -void tst_qquicklistmodel::datetime() -{ - QFETCH(QString, qml); - QFETCH(QDateTime, expected); - - QQmlEngine engine; - QQuickListModel model; - QQmlEngine::setContextForObject(&model,engine.rootContext()); - QDateTime dt0(QDate(1900, 1, 2), QTime( 8, 14)); - QDateTime dt1(QDate(2000, 11, 22), QTime(10, 45)); - engine.rootContext()->setContextProperty("dt0", dt0); - engine.rootContext()->setContextProperty("dt1", dt1); - engine.rootContext()->setContextObject(&model); - QQmlExpression e(engine.rootContext(), &model, qml); - QVariant result = e.evaluate(); - QDateTime dtResult = result.toDateTime(); - QVERIFY(expected == dtResult); -} - -QTEST_MAIN(tst_qquicklistmodel) - -#include "tst_qquicklistmodel.moc" diff --git a/tests/auto/qml/qquicklistmodelworkerscript/data/model.qml b/tests/auto/qml/qquicklistmodelworkerscript/data/model.qml deleted file mode 100644 index 5973ea8adf..0000000000 --- a/tests/auto/qml/qquicklistmodelworkerscript/data/model.qml +++ /dev/null @@ -1,26 +0,0 @@ -import QtQuick 2.0 - -Item { - id: item - property variant model - property bool done: false - property variant result - - function evalExpressionViaWorker(commands) { - done = false - worker.sendMessage({'commands': commands, 'model': model}) - } - - WorkerScript { - id: worker - source: "script.js" - onMessage: { - item.result = messageObject.result - item.done = true - } - } - - function runEval(js) { - eval(js); - } -} diff --git a/tests/auto/qml/qquicklistmodelworkerscript/data/script.js b/tests/auto/qml/qquicklistmodelworkerscript/data/script.js deleted file mode 100644 index 66a4acb8a8..0000000000 --- a/tests/auto/qml/qquicklistmodelworkerscript/data/script.js +++ /dev/null @@ -1,13 +0,0 @@ -WorkerScript.onMessage = function(msg) { - var result = null - try { - for (var i=0; i -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "../../shared/util.h" - -Q_DECLARE_METATYPE(QList) -Q_DECLARE_METATYPE(QList) - -#define RUNEVAL(object, string) \ - QVERIFY(QMetaObject::invokeMethod(object, "runEval", Q_ARG(QVariant, QString(string)))); - -inline QVariant runexpr(QQmlEngine *engine, const QString &str) -{ - QQmlExpression expr(engine->rootContext(), 0, str); - return expr.evaluate(); -} - -#define RUNEXPR(string) runexpr(&engine, QString(string)) - -static bool isValidErrorMessage(const QString &msg, bool dynamicRoleTest) -{ - bool valid = true; - - if (msg.isEmpty()) { - valid = false; - } else if (dynamicRoleTest) { - if (msg.contains("Can't assign to existing role") || msg.contains("Can't create role for unsupported data type")) - valid = false; - } - - return valid; -} - -class tst_qquicklistmodelworkerscript : public QQmlDataTest -{ - Q_OBJECT -public: - tst_qquicklistmodelworkerscript() - { - qRegisterMetaType >(); - } - -private: - int roleFromName(const QQuickListModel *model, const QString &roleName); - QQuickItem *createWorkerTest(QQmlEngine *eng, QQmlComponent *component, QQuickListModel *model); - void waitForWorker(QQuickItem *item); - - static bool compareVariantList(const QVariantList &testList, QVariant object); - -private slots: - void dynamic_data(); - void dynamic_worker_data(); - void dynamic_worker(); - void dynamic_worker_sync_data(); - void dynamic_worker_sync(); - void get_data(); - void get_worker(); - void get_worker_data(); - void property_changes_data(); - void property_changes_worker(); - void property_changes_worker_data(); - void worker_sync_data(); - void worker_sync(); - void worker_remove_element_data(); - void worker_remove_element(); - void worker_remove_list_data(); - void worker_remove_list(); - void dynamic_role_data(); - void dynamic_role(); -}; - -bool tst_qquicklistmodelworkerscript::compareVariantList(const QVariantList &testList, QVariant object) -{ - bool allOk = true; - - QQuickListModel *model = qobject_cast(object.value()); - if (model == 0) - return false; - - if (model->count() != testList.count()) - return false; - - for (int i=0 ; i < testList.count() ; ++i) { - const QVariant &testVariant = testList.at(i); - if (testVariant.type() != QVariant::Map) - return false; - const QVariantMap &map = testVariant.toMap(); - - const QHash roleNames = model->roleNames(); - - QVariantMap::const_iterator it = map.begin(); - QVariantMap::const_iterator end = map.end(); - - while (it != end) { - const QString &testKey = it.key(); - const QVariant &testData = it.value(); - - int roleIndex = roleNames.key(testKey.toUtf8(), -1); - if (roleIndex == -1) - return false; - - const QVariant &modelData = model->data(model->index(i, 0, QModelIndex()), roleIndex); - - if (testData.type() == QVariant::List) { - const QVariantList &subList = testData.toList(); - allOk = allOk && compareVariantList(subList, modelData); - } else { - allOk = allOk && (testData == modelData); - } - - ++it; - } - } - - return allOk; -} - -int tst_qquicklistmodelworkerscript::roleFromName(const QQuickListModel *model, const QString &roleName) -{ - return model->roleNames().key(roleName.toUtf8(), -1); -} - -QQuickItem *tst_qquicklistmodelworkerscript::createWorkerTest(QQmlEngine *eng, QQmlComponent *component, QQuickListModel *model) -{ - QQuickItem *item = qobject_cast(component->create()); - QQmlEngine::setContextForObject(model, eng->rootContext()); - if (item) - item->setProperty("model", qVariantFromValue(model)); - return item; -} - -void tst_qquicklistmodelworkerscript::waitForWorker(QQuickItem *item) -{ - QQmlProperty prop(item, "done"); - QVERIFY(prop.isValid()); - if (prop.read().toBool()) - return; // already finished - - QEventLoop loop; - QTimer timer; - timer.setSingleShot(true); - connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit())); - - QVERIFY(prop.connectNotifySignal(&loop, SLOT(quit()))); - timer.start(10000); - loop.exec(); - QVERIFY(timer.isActive()); - QVERIFY(prop.read().toBool()); -} - -void tst_qquicklistmodelworkerscript::dynamic_data() -{ - QTest::addColumn("script"); - QTest::addColumn("result"); - QTest::addColumn("warning"); - QTest::addColumn("dynamicRoles"); - - for (int i=0 ; i < 2 ; ++i) { - bool dr = (i != 0); - - // Simple flat model - QTest::newRow("count") << "count" << 0 << "" << dr; - - QTest::newRow("get1") << "{get(0) === undefined}" << 1 << "" << dr; - QTest::newRow("get2") << "{get(-1) === undefined}" << 1 << "" << dr; - QTest::newRow("get3") << "{append({'foo':123});get(0) != undefined}" << 1 << "" << dr; - QTest::newRow("get4") << "{append({'foo':123});get(0).foo}" << 123 << "" << dr; - QTest::newRow("get-modify1") << "{append({'foo':123,'bar':456});get(0).foo = 333;get(0).foo}" << 333 << "" << dr; - QTest::newRow("get-modify2") << "{append({'z':1});append({'foo':123,'bar':456});get(1).bar = 999;get(1).bar}" << 999 << "" << dr; - - QTest::newRow("append1") << "{append({'foo':123});count}" << 1 << "" << dr; - QTest::newRow("append2") << "{append({'foo':123,'bar':456});count}" << 1 << "" << dr; - QTest::newRow("append3a") << "{append({'foo':123});append({'foo':456});get(0).foo}" << 123 << "" << dr; - QTest::newRow("append3b") << "{append({'foo':123});append({'foo':456});get(1).foo}" << 456 << "" << dr; - QTest::newRow("append4a") << "{append(123)}" << 0 << ": QML ListModel: append: value is not an object" << dr; - QTest::newRow("append4b") << "{append([{'foo':123},{'foo':456},{'foo':789}]);count}" << 3 << "" << dr; - QTest::newRow("append4c") << "{append([{'foo':123},{'foo':456},{'foo':789}]);get(1).foo}" << 456 << "" << dr; - - QTest::newRow("clear1") << "{append({'foo':456});clear();count}" << 0 << "" << dr; - QTest::newRow("clear2") << "{append({'foo':123});append({'foo':456});clear();count}" << 0 << "" << dr; - QTest::newRow("clear3") << "{append({'foo':123});clear()}" << 0 << "" << dr; - - QTest::newRow("remove1") << "{append({'foo':123});remove(0);count}" << 0 << "" << dr; - QTest::newRow("remove2a") << "{append({'foo':123});append({'foo':456});remove(0);count}" << 1 << "" << dr; - QTest::newRow("remove2b") << "{append({'foo':123});append({'foo':456});remove(0);get(0).foo}" << 456 << "" << dr; - QTest::newRow("remove2c") << "{append({'foo':123});append({'foo':456});remove(1);get(0).foo}" << 123 << "" << dr; - QTest::newRow("remove3") << "{append({'foo':123});remove(0)}" << 0 << "" << dr; - QTest::newRow("remove3a") << "{append({'foo':123});remove(-1);count}" << 1 << ": QML ListModel: remove: indices [-1 - 0] out of range [0 - 1]" << dr; - QTest::newRow("remove4a") << "{remove(0)}" << 0 << ": QML ListModel: remove: indices [0 - 1] out of range [0 - 0]" << dr; - QTest::newRow("remove4b") << "{append({'foo':123});remove(0);remove(0);count}" << 0 << ": QML ListModel: remove: indices [0 - 1] out of range [0 - 0]" << dr; - QTest::newRow("remove4c") << "{append({'foo':123});remove(1);count}" << 1 << ": QML ListModel: remove: indices [1 - 2] out of range [0 - 1]" << dr; - QTest::newRow("remove5a") << "{append({'foo':123});append({'foo':456});remove(0,2);count}" << 0 << "" << dr; - QTest::newRow("remove5b") << "{append({'foo':123});append({'foo':456});remove(0,1);count}" << 1 << "" << dr; - QTest::newRow("remove5c") << "{append({'foo':123});append({'foo':456});remove(1,1);count}" << 1 << "" << dr; - QTest::newRow("remove5d") << "{append({'foo':123});append({'foo':456});remove(0,1);get(0).foo}" << 456 << "" << dr; - QTest::newRow("remove5e") << "{append({'foo':123});append({'foo':456});remove(1,1);get(0).foo}" << 123 << "" << dr; - QTest::newRow("remove5f") << "{append({'foo':123});append({'foo':456});append({'foo':789});remove(0,1);remove(1,1);get(0).foo}" << 456 << "" << dr; - QTest::newRow("remove6a") << "{remove();count}" << 0 << ": QML ListModel: remove: incorrect number of arguments" << dr; - QTest::newRow("remove6b") << "{remove(1,2,3);count}" << 0 << ": QML ListModel: remove: incorrect number of arguments" << dr; - QTest::newRow("remove7a") << "{append({'foo':123});remove(0,0);count}" << 1 << ": QML ListModel: remove: indices [0 - 0] out of range [0 - 1]" << dr; - QTest::newRow("remove7b") << "{append({'foo':123});remove(0,-1);count}" << 1 << ": QML ListModel: remove: indices [0 - -1] out of range [0 - 1]" << dr; - - QTest::newRow("insert1") << "{insert(0,{'foo':123});count}" << 1 << "" << dr; - QTest::newRow("insert2") << "{insert(1,{'foo':123});count}" << 0 << ": QML ListModel: insert: index 1 out of range" << dr; - QTest::newRow("insert3a") << "{append({'foo':123});insert(1,{'foo':456});count}" << 2 << "" << dr; - QTest::newRow("insert3b") << "{append({'foo':123});insert(1,{'foo':456});get(0).foo}" << 123 << "" << dr; - QTest::newRow("insert3c") << "{append({'foo':123});insert(1,{'foo':456});get(1).foo}" << 456 << "" << dr; - QTest::newRow("insert3d") << "{append({'foo':123});insert(0,{'foo':456});get(0).foo}" << 456 << "" << dr; - QTest::newRow("insert3e") << "{append({'foo':123});insert(0,{'foo':456});get(1).foo}" << 123 << "" << dr; - QTest::newRow("insert4") << "{append({'foo':123});insert(-1,{'foo':456});count}" << 1 << ": QML ListModel: insert: index -1 out of range" << dr; - QTest::newRow("insert5a") << "{insert(0,123)}" << 0 << ": QML ListModel: insert: value is not an object" << dr; - QTest::newRow("insert5b") << "{insert(0,[{'foo':11},{'foo':22},{'foo':33}]);count}" << 3 << "" << dr; - QTest::newRow("insert5c") << "{insert(0,[{'foo':11},{'foo':22},{'foo':33}]);get(2).foo}" << 33 << "" << dr; - - QTest::newRow("set1") << "{append({'foo':123});set(0,{'foo':456});count}" << 1 << "" << dr; - QTest::newRow("set2") << "{append({'foo':123});set(0,{'foo':456});get(0).foo}" << 456 << "" << dr; - QTest::newRow("set3a") << "{append({'foo':123,'bar':456});set(0,{'foo':999});get(0).foo}" << 999 << "" << dr; - QTest::newRow("set3b") << "{append({'foo':123,'bar':456});set(0,{'foo':999});get(0).bar}" << 456 << "" << dr; - QTest::newRow("set4a") << "{set(0,{'foo':456});count}" << 1 << "" << dr; - QTest::newRow("set4c") << "{set(-1,{'foo':456})}" << 0 << ": QML ListModel: set: index -1 out of range" << dr; - QTest::newRow("set5a") << "{append({'foo':123,'bar':456});set(0,123);count}" << 1 << ": QML ListModel: set: value is not an object" << dr; - QTest::newRow("set5b") << "{append({'foo':123,'bar':456});set(0,[1,2,3]);count}" << 1 << ": QML ListModel: set: value is not an object" << dr; - QTest::newRow("set6") << "{append({'foo':123});set(1,{'foo':456});count}" << 2 << "" << dr; - - QTest::newRow("setprop1") << "{append({'foo':123});setProperty(0,'foo',456);count}" << 1 << "" << dr; - QTest::newRow("setprop2") << "{append({'foo':123});setProperty(0,'foo',456);get(0).foo}" << 456 << "" << dr; - QTest::newRow("setprop3a") << "{append({'foo':123,'bar':456});setProperty(0,'foo',999);get(0).foo}" << 999 << "" << dr; - QTest::newRow("setprop3b") << "{append({'foo':123,'bar':456});setProperty(0,'foo',999);get(0).bar}" << 456 << "" << dr; - QTest::newRow("setprop4a") << "{setProperty(0,'foo',456)}" << 0 << ": QML ListModel: set: index 0 out of range" << dr; - QTest::newRow("setprop4b") << "{setProperty(-1,'foo',456)}" << 0 << ": QML ListModel: set: index -1 out of range" << dr; - QTest::newRow("setprop4c") << "{append({'foo':123,'bar':456});setProperty(1,'foo',456);count}" << 1 << ": QML ListModel: set: index 1 out of range" << dr; - QTest::newRow("setprop5") << "{append({'foo':123,'bar':456});append({'foo':111});setProperty(1,'bar',222);get(1).bar}" << 222 << "" << dr; - - QTest::newRow("move1a") << "{append({'foo':123});append({'foo':456});move(0,1,1);count}" << 2 << "" << dr; - QTest::newRow("move1b") << "{append({'foo':123});append({'foo':456});move(0,1,1);get(0).foo}" << 456 << "" << dr; - QTest::newRow("move1c") << "{append({'foo':123});append({'foo':456});move(0,1,1);get(1).foo}" << 123 << "" << dr; - QTest::newRow("move1d") << "{append({'foo':123});append({'foo':456});move(1,0,1);get(0).foo}" << 456 << "" << dr; - QTest::newRow("move1e") << "{append({'foo':123});append({'foo':456});move(1,0,1);get(1).foo}" << 123 << "" << dr; - QTest::newRow("move2a") << "{append({'foo':123});append({'foo':456});append({'foo':789});move(0,1,2);count}" << 3 << "" << dr; - QTest::newRow("move2b") << "{append({'foo':123});append({'foo':456});append({'foo':789});move(0,1,2);get(0).foo}" << 789 << "" << dr; - QTest::newRow("move2c") << "{append({'foo':123});append({'foo':456});append({'foo':789});move(0,1,2);get(1).foo}" << 123 << "" << dr; - QTest::newRow("move2d") << "{append({'foo':123});append({'foo':456});append({'foo':789});move(0,1,2);get(2).foo}" << 456 << "" << dr; - QTest::newRow("move3a") << "{append({'foo':123});append({'foo':456});append({'foo':789});move(1,0,3);count}" << 3 << ": QML ListModel: move: out of range" << dr; - QTest::newRow("move3b") << "{append({'foo':123});append({'foo':456});append({'foo':789});move(1,-1,1);count}" << 3 << ": QML ListModel: move: out of range" << dr; - QTest::newRow("move3c") << "{append({'foo':123});append({'foo':456});append({'foo':789});move(1,0,-1);count}" << 3 << ": QML ListModel: move: out of range" << dr; - QTest::newRow("move3d") << "{append({'foo':123});append({'foo':456});append({'foo':789});move(0,3,1);count}" << 3 << ": QML ListModel: move: out of range" << dr; - - QTest::newRow("large1") << "{append({'a':1,'b':2,'c':3,'d':4,'e':5,'f':6,'g':7,'h':8});get(0).h}" << 8 << "" << dr; - - QTest::newRow("datatypes1") << "{append({'a':1});append({'a':'string'});}" << 0 << ": Can't assign to existing role 'a' of different type [String -> Number]" << dr; - - QTest::newRow("null") << "{append({'a':null});}" << 0 << "" << dr; - QTest::newRow("setNull") << "{append({'a':1});set(0, {'a':null});}" << 0 << "" << dr; - QTest::newRow("setString") << "{append({'a':'hello'});set(0, {'a':'world'});get(0).a == 'world'}" << 1 << "" << dr; - QTest::newRow("setInt") << "{append({'a':5});set(0, {'a':10});get(0).a}" << 10 << "" << dr; - QTest::newRow("setNumber") << "{append({'a':6});set(0, {'a':5.5});get(0).a < 5.6}" << 1 << "" << dr; - QTest::newRow("badType0") << "{append({'a':'hello'});set(0, {'a':1});}" << 0 << ": Can't assign to existing role 'a' of different type [Number -> String]" << dr; - QTest::newRow("invalidInsert0") << "{insert(0);}" << 0 << ": QML ListModel: insert: value is not an object" << dr; - QTest::newRow("invalidAppend0") << "{append();}" << 0 << ": QML ListModel: append: value is not an object" << dr; - QTest::newRow("invalidInsert1") << "{insert(0, 34);}" << 0 << ": QML ListModel: insert: value is not an object" << dr; - QTest::newRow("invalidAppend1") << "{append(37);}" << 0 << ": QML ListModel: append: value is not an object" << dr; - - // QObjects - QTest::newRow("qobject0") << "{append({'a':dummyItem0});}" << 0 << "" << dr; - QTest::newRow("qobject1") << "{append({'a':dummyItem0});set(0,{'a':dummyItem1});get(0).a == dummyItem1;}" << 1 << "" << dr; - QTest::newRow("qobject2") << "{append({'a':dummyItem0});get(0).a == dummyItem0;}" << 1 << "" << dr; - QTest::newRow("qobject3") << "{append({'a':dummyItem0});append({'b':1});}" << 0 << "" << dr; - - // JS objects - QTest::newRow("js1") << "{append({'foo':{'prop':1}});count}" << 1 << "" << dr; - QTest::newRow("js2") << "{append({'foo':{'prop':27}});get(0).foo.prop}" << 27 << "" << dr; - QTest::newRow("js3") << "{append({'foo':{'prop':27}});append({'bar':1});count}" << 2 << "" << dr; - QTest::newRow("js4") << "{append({'foo':{'prop':27}});append({'bar':1});set(0, {'foo':{'prop':28}});get(0).foo.prop}" << 28 << "" << dr; - QTest::newRow("js5") << "{append({'foo':{'prop':27}});append({'bar':1});set(1, {'foo':{'prop':33}});get(1).foo.prop}" << 33 << "" << dr; - QTest::newRow("js6") << "{append({'foo':{'prop':27}});clear();count}" << 0 << "" << dr; - QTest::newRow("js7") << "{append({'foo':{'prop':27}});set(0, {'foo':null});count}" << 1 << "" << dr; - QTest::newRow("js8") << "{append({'foo':{'prop':27}});set(0, {'foo':{'prop2':31}});get(0).foo.prop2}" << 31 << "" << dr; - - // Nested models - QTest::newRow("nested-append1") << "{append({'foo':123,'bars':[{'a':1},{'a':2},{'a':3}]});count}" << 1 << "" << dr; - QTest::newRow("nested-append2") << "{append({'foo':123,'bars':[{'a':1},{'a':2},{'a':3}]});get(0).bars.get(1).a}" << 2 << "" << dr; - QTest::newRow("nested-append3") << "{append({'foo':123,'bars':[{'a':1},{'a':2},{'a':3}]});get(0).bars.append({'a':4});get(0).bars.get(3).a}" << 4 << "" << dr; - - QTest::newRow("nested-insert") << "{append({'foo':123});insert(0,{'bars':[{'a':1},{'b':2},{'c':3}]});get(0).bars.get(0).a}" << 1 << "" << dr; - QTest::newRow("nested-set") << "{append({'foo':[{'x':1}]});set(0,{'foo':[{'x':123}]});get(0).foo.get(0).x}" << 123 << "" << dr; - - QTest::newRow("nested-count") << "{append({'foo':123,'bars':[{'a':1},{'a':2},{'a':3}]}); get(0).bars.count}" << 3 << "" << dr; - QTest::newRow("nested-clear") << "{append({'foo':123,'bars':[{'a':1},{'a':2},{'a':3}]}); get(0).bars.clear(); get(0).bars.count}" << 0 << "" << dr; - } -} - -void tst_qquicklistmodelworkerscript::dynamic_worker_data() -{ - dynamic_data(); -} - -void tst_qquicklistmodelworkerscript::dynamic_worker() -{ - QFETCH(QString, script); - QFETCH(int, result); - QFETCH(QString, warning); - QFETCH(bool, dynamicRoles); - - if (QByteArray(QTest::currentDataTag()).startsWith("qobject")) - return; - - // This is same as dynamic() except it applies the test to a ListModel called - // from a WorkerScript. - - QQuickListModel model; - model.setDynamicRoles(dynamicRoles); - QQmlEngine eng; - QQmlComponent component(&eng, testFileUrl("model.qml")); - QQuickItem *item = createWorkerTest(&eng, &component, &model); - QVERIFY(item != 0); - - QSignalSpy spyCount(&model, SIGNAL(countChanged())); - - if (script[0] == QLatin1Char('{') && script[script.length()-1] == QLatin1Char('}')) - script = script.mid(1, script.length() - 2); - QVariantList operations; - foreach (const QString &s, script.split(';')) { - if (!s.isEmpty()) - operations << s; - } - - if (isValidErrorMessage(warning, dynamicRoles)) - QTest::ignoreMessage(QtWarningMsg, warning.toLatin1()); - - QVERIFY(QMetaObject::invokeMethod(item, "evalExpressionViaWorker", - Q_ARG(QVariant, operations))); - waitForWorker(item); - QCOMPARE(QQmlProperty(item, "result").read().toInt(), result); - - if (model.count() > 0) - QVERIFY(spyCount.count() > 0); - - delete item; - qApp->processEvents(); -} - -void tst_qquicklistmodelworkerscript::dynamic_worker_sync_data() -{ - dynamic_data(); -} - -void tst_qquicklistmodelworkerscript::dynamic_worker_sync() -{ - QFETCH(QString, script); - QFETCH(int, result); - QFETCH(QString, warning); - QFETCH(bool, dynamicRoles); - - if (QByteArray(QTest::currentDataTag()).startsWith("qobject")) - return; - - // This is the same as dynamic_worker() except that it executes a set of list operations - // from the worker script, calls sync(), and tests the changes are reflected in the - // list in the main thread - - QQuickListModel model; - model.setDynamicRoles(dynamicRoles); - QQmlEngine eng; - QQmlComponent component(&eng, testFileUrl("model.qml")); - QQuickItem *item = createWorkerTest(&eng, &component, &model); - QVERIFY(item != 0); - - if (script[0] == QLatin1Char('{') && script[script.length()-1] == QLatin1Char('}')) - script = script.mid(1, script.length() - 2); - QVariantList operations; - foreach (const QString &s, script.split(';')) { - if (!s.isEmpty()) - operations << s; - } - - if (isValidErrorMessage(warning, dynamicRoles)) - QTest::ignoreMessage(QtWarningMsg, warning.toLatin1()); - - // execute a set of commands on the worker list model, then check the - // changes are reflected in the list model in the main thread - QVERIFY(QMetaObject::invokeMethod(item, "evalExpressionViaWorker", - Q_ARG(QVariant, operations.mid(0, operations.length()-1)))); - waitForWorker(item); - - QQmlExpression e(eng.rootContext(), &model, operations.last().toString()); - QCOMPARE(e.evaluate().toInt(), result); - - delete item; - qApp->processEvents(); -} - -void tst_qquicklistmodelworkerscript::get_data() -{ - QTest::addColumn("expression"); - QTest::addColumn("index"); - QTest::addColumn("roleName"); - QTest::addColumn("roleValue"); - QTest::addColumn("dynamicRoles"); - - for (int i=0 ; i < 2 ; ++i) { - bool dr = (i != 0); - - QTest::newRow("simple value") << "get(0).roleA = 500" << 0 << "roleA" << QVariant(500) << dr; - QTest::newRow("simple value 2") << "get(1).roleB = 500" << 1 << "roleB" << QVariant(500) << dr; - - QVariantMap map; - QVariantList list; - map.clear(); map["a"] = 50; map["b"] = 500; - list << map; - map.clear(); map["c"] = 1000; - list << map; - QTest::newRow("list of objects") << "get(2).roleD = [{'a': 50, 'b': 500}, {'c': 1000}]" << 2 << "roleD" << QVariant::fromValue(list) << dr; - } -} - -void tst_qquicklistmodelworkerscript::get_worker() -{ - QFETCH(QString, expression); - QFETCH(int, index); - QFETCH(QString, roleName); - QFETCH(QVariant, roleValue); - QFETCH(bool, dynamicRoles); - - QQuickListModel model; - model.setDynamicRoles(dynamicRoles); - QQmlEngine eng; - QQmlComponent component(&eng, testFileUrl("model.qml")); - QQuickItem *item = createWorkerTest(&eng, &component, &model); - QVERIFY(item != 0); - - // Add some values like get() test - RUNEVAL(item, "model.append({roleA: 100})"); - RUNEVAL(item, "model.append({roleA: 200, roleB: 400})"); - RUNEVAL(item, "model.append({roleA: 200, roleB: 400})"); - RUNEVAL(item, "model.append({roleC: {} })"); - RUNEVAL(item, "model.append({roleD: [ { a:1, b:2 }, { c: 3 } ] })"); - - int role = roleFromName(&model, roleName); - QVERIFY(role >= 0); - - QSignalSpy spy(&model, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector))); - - // in the worker thread, change the model data and call sync() - QVERIFY(QMetaObject::invokeMethod(item, "evalExpressionViaWorker", - Q_ARG(QVariant, QStringList(expression)))); - waitForWorker(item); - - // see if we receive the model changes in the main thread's model - if (roleValue.type() == QVariant::List) { - const QVariantList &list = roleValue.toList(); - QVERIFY(compareVariantList(list, model.data(index, role))); - } else { - QCOMPARE(model.data(index, role), roleValue); - } - - QCOMPARE(spy.count(), 1); - - QList spyResult = spy.takeFirst(); - QCOMPARE(spyResult.at(0).value(), model.index(index, 0, QModelIndex())); - QCOMPARE(spyResult.at(1).value(), model.index(index, 0, QModelIndex())); // only 1 item is modified at a time - QVERIFY(spyResult.at(2).value >().contains(role)); - - delete item; -} - -void tst_qquicklistmodelworkerscript::get_worker_data() -{ - get_data(); -} - -void tst_qquicklistmodelworkerscript::property_changes_data() -{ - QTest::addColumn("script_setup"); - QTest::addColumn("script_change"); - QTest::addColumn("roleName"); - QTest::addColumn("listIndex"); - QTest::addColumn("itemsChanged"); - QTest::addColumn("testExpression"); - QTest::addColumn("dynamicRoles"); - - for (int i=0 ; i < 2 ; ++i) { - bool dr = (i != 0); - - QTest::newRow("set: plain") << "append({'a':123, 'b':456, 'c':789});" << "set(0,{'b':123});" - << "b" << 0 << true << "get(0).b == 123" << dr; - QTest::newRow("setProperty: plain") << "append({'a':123, 'b':456, 'c':789});" << "setProperty(0, 'b', 123);" - << "b" << 0 << true << "get(0).b == 123" << dr; - - QTest::newRow("set: plain, no changes") << "append({'a':123, 'b':456, 'c':789});" << "set(0,{'b':456});" - << "b" << 0 << false << "get(0).b == 456" << dr; - QTest::newRow("setProperty: plain, no changes") << "append({'a':123, 'b':456, 'c':789});" << "setProperty(0, 'b', 456);" - << "b" << 0 << false << "get(0).b == 456" << dr; - - QTest::newRow("set: inserted item") - << "{append({'a':123, 'b':456, 'c':789}); get(0); insert(0, {'a':0, 'b':0, 'c':0});}" - << "set(1, {'a':456});" - << "a" << 1 << true << "get(1).a == 456" << dr; - QTest::newRow("setProperty: inserted item") - << "{append({'a':123, 'b':456, 'c':789}); get(0); insert(0, {'a':0, 'b':0, 'c':0});}" - << "setProperty(1, 'a', 456);" - << "a" << 1 << true << "get(1).a == 456" << dr; - QTest::newRow("get: inserted item") - << "{append({'a':123, 'b':456, 'c':789}); get(0); insert(0, {'a':0, 'b':0, 'c':0});}" - << "get(1).a = 456;" - << "a" << 1 << true << "get(1).a == 456" << dr; - QTest::newRow("set: removed item") - << "{append({'a':0, 'b':0, 'c':0}); append({'a':123, 'b':456, 'c':789}); get(1); remove(0);}" - << "set(0, {'a':456});" - << "a" << 0 << true << "get(0).a == 456" << dr; - QTest::newRow("setProperty: removed item") - << "{append({'a':0, 'b':0, 'c':0}); append({'a':123, 'b':456, 'c':789}); get(1); remove(0);}" - << "setProperty(0, 'a', 456);" - << "a" << 0 << true << "get(0).a == 456" << dr; - QTest::newRow("get: removed item") - << "{append({'a':0, 'b':0, 'c':0}); append({'a':123, 'b':456, 'c':789}); get(1); remove(0);}" - << "get(0).a = 456;" - << "a" << 0 << true << "get(0).a == 456" << dr; - - // Following tests only call set() since setProperty() only allows plain - // values, not lists, as the argument. - // Note that when a list is changed, itemsChanged() is currently always - // emitted regardless of whether it actually changed or not. - - QTest::newRow("nested-set: list, new size") << "append({'a':123, 'b':[{'a':1},{'a':2},{'a':3}], 'c':789});" << "set(0,{'b':[{'a':1},{'a':2}]});" - << "b" << 0 << true << "get(0).b.get(0).a == 1 && get(0).b.get(1).a == 2" << dr; - - QTest::newRow("nested-set: list, empty -> non-empty") << "append({'a':123, 'b':[], 'c':789});" << "set(0,{'b':[{'a':1},{'a':2},{'a':3}]});" - << "b" << 0 << true << "get(0).b.get(0).a == 1 && get(0).b.get(1).a == 2 && get(0).b.get(2).a == 3" << dr; - - QTest::newRow("nested-set: list, non-empty -> empty") << "append({'a':123, 'b':[{'a':1},{'a':2},{'a':3}], 'c':789});" << "set(0,{'b':[]});" - << "b" << 0 << true << "get(0).b.count == 0" << dr; - - QTest::newRow("nested-set: list, same size, different values") << "append({'a':123, 'b':[{'a':1},{'a':2},{'a':3}], 'c':789});" << "set(0,{'b':[{'a':1},{'a':222},{'a':3}]});" - << "b" << 0 << true << "get(0).b.get(0).a == 1 && get(0).b.get(1).a == 222 && get(0).b.get(2).a == 3" << dr; - - QTest::newRow("nested-set: list, no changes") << "append({'a':123, 'b':[{'a':1},{'a':2},{'a':3}], 'c':789});" << "set(0,{'b':[{'a':1},{'a':2},{'a':3}]});" - << "b" << 0 << true << "get(0).b.get(0).a == 1 && get(0).b.get(1).a == 2 && get(0).b.get(2).a == 3" << dr; - - QTest::newRow("nested-set: list, no changes, empty") << "append({'a':123, 'b':[], 'c':789});" << "set(0,{'b':[]});" - << "b" << 0 << true << "get(0).b.count == 0" << dr; - } -} - -void tst_qquicklistmodelworkerscript::property_changes_worker() -{ - QFETCH(QString, script_setup); - QFETCH(QString, script_change); - QFETCH(QString, roleName); - QFETCH(int, listIndex); - QFETCH(bool, itemsChanged); - QFETCH(bool, dynamicRoles); - - QQuickListModel model; - model.setDynamicRoles(dynamicRoles); - QQmlEngine engine; - QQmlComponent component(&engine, testFileUrl("model.qml")); - QVERIFY2(component.errorString().isEmpty(), component.errorString().toUtf8()); - QQuickItem *item = createWorkerTest(&engine, &component, &model); - QVERIFY(item != 0); - - QQmlExpression expr(engine.rootContext(), &model, script_setup); - expr.evaluate(); - QVERIFY2(!expr.hasError(), QTest::toString(expr.error().toString())); - - QSignalSpy spyItemsChanged(&model, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector))); - - QVERIFY(QMetaObject::invokeMethod(item, "evalExpressionViaWorker", - Q_ARG(QVariant, QStringList(script_change)))); - waitForWorker(item); - - // test itemsChanged() is emitted correctly - if (itemsChanged) { - QCOMPARE(spyItemsChanged.count(), 1); - QCOMPARE(spyItemsChanged.at(0).at(0).value(), model.index(listIndex, 0, QModelIndex())); - QCOMPARE(spyItemsChanged.at(0).at(1).value(), model.index(listIndex, 0, QModelIndex())); - } else { - QCOMPARE(spyItemsChanged.count(), 0); - } - - delete item; - qApp->processEvents(); -} - -void tst_qquicklistmodelworkerscript::property_changes_worker_data() -{ - property_changes_data(); -} - -void tst_qquicklistmodelworkerscript::worker_sync_data() -{ - QTest::addColumn("dynamicRoles"); - - QTest::newRow("staticRoles") << false; - QTest::newRow("dynamicRoles") << true; -} - -void tst_qquicklistmodelworkerscript::worker_sync() -{ - QFETCH(bool, dynamicRoles); - - QQuickListModel model; - model.setDynamicRoles(dynamicRoles); - QQmlEngine eng; - QQmlComponent component(&eng, testFileUrl("workersync.qml")); - QQuickItem *item = createWorkerTest(&eng, &component, &model); - QVERIFY(item != 0); - - QVERIFY(model.count() == 0); - - QVERIFY(QMetaObject::invokeMethod(item, "addItem0")); - - QVERIFY(model.count() == 2); - QVariant childData = model.data(0, 0); - QQuickListModel *childModel = qobject_cast(childData.value()); - QVERIFY(childModel); - QVERIFY(childModel->count() == 1); - - QSignalSpy spyModelInserted(&model, SIGNAL(rowsInserted(QModelIndex,int,int))); - QSignalSpy spyChildInserted(childModel, SIGNAL(rowsInserted(QModelIndex,int,int))); - - QVERIFY(QMetaObject::invokeMethod(item, "addItemViaWorker")); - waitForWorker(item); - - QVERIFY(model.count() == 2); - QVERIFY(childModel->count() == 1); - QVERIFY(spyModelInserted.count() == 0); - QVERIFY(spyChildInserted.count() == 0); - - QVERIFY(QMetaObject::invokeMethod(item, "doSync")); - waitForWorker(item); - - QVERIFY(model.count() == 2); - QVERIFY(childModel->count() == 2); - QVERIFY(spyModelInserted.count() == 0); - QVERIFY(spyChildInserted.count() == 1); - - QVERIFY(QMetaObject::invokeMethod(item, "addItemViaWorker")); - waitForWorker(item); - - QVERIFY(model.count() == 2); - QVERIFY(childModel->count() == 2); - QVERIFY(spyModelInserted.count() == 0); - QVERIFY(spyChildInserted.count() == 1); - - QVERIFY(QMetaObject::invokeMethod(item, "doSync")); - waitForWorker(item); - - QVERIFY(model.count() == 2); - QVERIFY(childModel->count() == 3); - QVERIFY(spyModelInserted.count() == 0); - QVERIFY(spyChildInserted.count() == 2); - - delete item; - qApp->processEvents(); -} - -void tst_qquicklistmodelworkerscript::worker_remove_element_data() -{ - worker_sync_data(); -} - -void tst_qquicklistmodelworkerscript::worker_remove_element() -{ - QFETCH(bool, dynamicRoles); - - QQuickListModel model; - model.setDynamicRoles(dynamicRoles); - QQmlEngine eng; - QQmlComponent component(&eng, testFileUrl("workerremoveelement.qml")); - QQuickItem *item = createWorkerTest(&eng, &component, &model); - QVERIFY(item != 0); - - QSignalSpy spyModelRemoved(&model, SIGNAL(rowsRemoved(QModelIndex,int,int))); - - QVERIFY(model.count() == 0); - QVERIFY(spyModelRemoved.count() == 0); - - QVERIFY(QMetaObject::invokeMethod(item, "addItem")); - - QVERIFY(model.count() == 1); - - QVERIFY(QMetaObject::invokeMethod(item, "removeItemViaWorker")); - waitForWorker(item); - - QVERIFY(model.count() == 1); - QVERIFY(spyModelRemoved.count() == 0); - - QVERIFY(QMetaObject::invokeMethod(item, "doSync")); - waitForWorker(item); - - QVERIFY(model.count() == 0); - QVERIFY(spyModelRemoved.count() == 1); - - delete item; - qApp->processEvents(); - - { - //don't crash if model was deleted earlier - QQuickListModel* model = new QQuickListModel; - model->setDynamicRoles(dynamicRoles); - QQmlEngine eng; - QQmlComponent component(&eng, testFileUrl("workerremoveelement.qml")); - QQuickItem *item = createWorkerTest(&eng, &component, model); - QVERIFY(item != 0); - - QVERIFY(QMetaObject::invokeMethod(item, "addItem")); - - QVERIFY(model->count() == 1); - - QVERIFY(QMetaObject::invokeMethod(item, "removeItemViaWorker")); - QVERIFY(QMetaObject::invokeMethod(item, "doSync")); - delete model; - qApp->processEvents(); //must not crash here - waitForWorker(item); - - delete item; - } -} - -void tst_qquicklistmodelworkerscript::worker_remove_list_data() -{ - worker_sync_data(); -} - -void tst_qquicklistmodelworkerscript::worker_remove_list() -{ - QFETCH(bool, dynamicRoles); - - QQuickListModel model; - model.setDynamicRoles(dynamicRoles); - QQmlEngine eng; - QQmlComponent component(&eng, testFileUrl("workerremovelist.qml")); - QQuickItem *item = createWorkerTest(&eng, &component, &model); - QVERIFY(item != 0); - - QSignalSpy spyModelRemoved(&model, SIGNAL(rowsRemoved(QModelIndex,int,int))); - - QVERIFY(model.count() == 0); - QVERIFY(spyModelRemoved.count() == 0); - - QVERIFY(QMetaObject::invokeMethod(item, "addList")); - - QVERIFY(model.count() == 1); - - QVERIFY(QMetaObject::invokeMethod(item, "removeListViaWorker")); - waitForWorker(item); - - QVERIFY(model.count() == 1); - QVERIFY(spyModelRemoved.count() == 0); - - QVERIFY(QMetaObject::invokeMethod(item, "doSync")); - waitForWorker(item); - - QVERIFY(model.count() == 0); - QVERIFY(spyModelRemoved.count() == 1); - - delete item; - qApp->processEvents(); -} - -void tst_qquicklistmodelworkerscript::dynamic_role_data() -{ - QTest::addColumn("preamble"); - QTest::addColumn("script"); - QTest::addColumn("result"); - - QTest::newRow("sync1") << "{append({'a':[{'b':1},{'b':2}]})}" << "{get(0).a = 'string';count}" << 1; -} - -void tst_qquicklistmodelworkerscript::dynamic_role() -{ - QFETCH(QString, preamble); - QFETCH(QString, script); - QFETCH(int, result); - - QQuickListModel model; - model.setDynamicRoles(true); - QQmlEngine engine; - QQmlComponent component(&engine, testFileUrl("model.qml")); - QQuickItem *item = createWorkerTest(&engine, &component, &model); - QVERIFY(item != 0); - - QQmlExpression preExp(engine.rootContext(), &model, preamble); - QCOMPARE(preExp.evaluate().toInt(), 0); - - if (script[0] == QLatin1Char('{') && script[script.length()-1] == QLatin1Char('}')) - script = script.mid(1, script.length() - 2); - QVariantList operations; - foreach (const QString &s, script.split(';')) { - if (!s.isEmpty()) - operations << s; - } - - // execute a set of commands on the worker list model, then check the - // changes are reflected in the list model in the main thread - QVERIFY(QMetaObject::invokeMethod(item, "evalExpressionViaWorker", - Q_ARG(QVariant, operations.mid(0, operations.length()-1)))); - waitForWorker(item); - - QQmlExpression e(engine.rootContext(), &model, operations.last().toString()); - QCOMPARE(e.evaluate().toInt(), result); - - delete item; - qApp->processEvents(); -} - -QTEST_MAIN(tst_qquicklistmodelworkerscript) - -#include "tst_qquicklistmodelworkerscript.moc" diff --git a/tests/auto/qml/qquickworkerscript/tst_qquickworkerscript.cpp b/tests/auto/qml/qquickworkerscript/tst_qquickworkerscript.cpp index 46e0c9e436..a2bf06c2ba 100644 --- a/tests/auto/qml/qquickworkerscript/tst_qquickworkerscript.cpp +++ b/tests/auto/qml/qquickworkerscript/tst_qquickworkerscript.cpp @@ -145,7 +145,7 @@ void tst_QQuickWorkerScript::messaging_data() void tst_QQuickWorkerScript::messaging_sendQObjectList() { - // Not allowed to send QObjects other than QQuickListModelWorkerAgent + // Not allowed to send QObjects other than QQmlListModelWorkerAgent // instances. If objects are sent in a list, they will be sent as 'undefined' // js values. diff --git a/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp b/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp index 93ffbca3e5..aee4e0d119 100644 --- a/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp +++ b/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp @@ -52,7 +52,7 @@ #include #include #include -#include +#include #include "../../shared/util.h" #include "../shared/viewtestutil.h" #include "../shared/visualtestutil.h" @@ -2435,7 +2435,7 @@ void tst_QQuickGridView::modelChanges() QQuickGridView *gridView = window->rootObject()->findChild("gridView"); QTRY_VERIFY(gridView); - QQuickListModel *alternateModel = window->rootObject()->findChild("alternateModel"); + QQmlListModel *alternateModel = window->rootObject()->findChild("alternateModel"); QTRY_VERIFY(alternateModel); QVariant modelVariant = QVariant::fromValue(alternateModel); QSignalSpy modelSpy(gridView, SIGNAL(modelChanged())); diff --git a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp index 9fad01ef40..a1b2536b0b 100644 --- a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp +++ b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp @@ -49,7 +49,7 @@ #include #include #include -#include +#include #include "../../shared/util.h" #include "../shared/viewtestutil.h" #include "../shared/visualtestutil.h" @@ -3257,7 +3257,7 @@ void tst_QQuickListView::modelChanges() QQuickListView *listView = window->rootObject()->findChild("listView"); QTRY_VERIFY(listView); - QQuickListModel *alternateModel = window->rootObject()->findChild("alternateModel"); + QQmlListModel *alternateModel = window->rootObject()->findChild("alternateModel"); QTRY_VERIFY(alternateModel); QVariant modelVariant = QVariant::fromValue(alternateModel); QSignalSpy modelSpy(listView, SIGNAL(modelChanged())); diff --git a/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp b/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp index 65fef9abe5..f52939e628 100644 --- a/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp +++ b/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp @@ -50,7 +50,7 @@ #include #include #include -#include +#include #include #include #include @@ -1269,7 +1269,7 @@ void tst_QQuickPathView::modelChanges() pathView->setCurrentIndex(3); QTRY_COMPARE(pathView->offset(), 6.0); - QQuickListModel *alternateModel = window->rootObject()->findChild("alternateModel"); + QQmlListModel *alternateModel = window->rootObject()->findChild("alternateModel"); QVERIFY(alternateModel); QVariant modelVariant = QVariant::fromValue(alternateModel); QSignalSpy modelSpy(pathView, SIGNAL(modelChanged())); -- cgit v1.2.3 From 7cad0e52c5a020bd29635e9912fd8946a6b48124 Mon Sep 17 00:00:00 2001 From: Alan Alpert Date: Tue, 15 Jan 2013 14:26:59 -0800 Subject: Move the model classes from QtQuick to QtQml This is needed for proper support of non-GUI instantiators in QtQml. Only private C++ classes are affected. Aside from name changes, model classes now operate on QObjects instead of QQuickItems, leading to minor changes in the implementation of QtQuick classes using them. The old QML type names will still be registered in the QtQuick import for the forseeable future, but pointing to the new classes. The new QML types will be added in a second commit. Classes Affected: QQuickVisualDataGroup -> QQmlDataGroup QQuickVisualDataModel -> QQmlDelegateModel QQuickVisualItemModel -> QQmlObjectModel QQuickVisualModel -> QQmlInstanceModel QQuickChangeSet -> QQmlChangeSet QQuickListAccessor -> QQmlListAccessor QQuickListCompositor -> QQmlListCompositor QQuickPackage -> QQuickPackage (just moved for now) Change-Id: Ia19e630e53bfa9e5d459e289596cd11df1ea3930 Reviewed-by: Andrew den Exter --- src/particles/qquickitemparticle.cpp | 1 - src/qml/items/items.pri | 10 + src/qml/items/qqmldelegatemodel.cpp | 3154 +++++++++++++++++++ src/qml/items/qqmldelegatemodel_p.h | 238 ++ src/qml/items/qqmldelegatemodel_p_p.h | 411 +++ src/qml/items/qqmlobjectmodel.cpp | 251 ++ src/qml/items/qqmlobjectmodel_p.h | 174 ++ src/qml/items/qquickpackage.cpp | 198 ++ src/qml/items/qquickpackage_p.h | 96 + src/qml/qml.pro | 1 + src/qml/qml/qqmlengine.cpp | 8 + src/qml/util/qqmladaptormodel.cpp | 977 ++++++ src/qml/util/qqmladaptormodel_p.h | 156 + src/qml/util/qqmlchangeset.cpp | 621 ++++ src/qml/util/qqmlchangeset_p.h | 167 ++ src/qml/util/qqmllistaccessor.cpp | 138 + src/qml/util/qqmllistaccessor_p.h | 78 + src/qml/util/qqmllistcompositor.cpp | 1484 +++++++++ src/qml/util/qqmllistcompositor_p.h | 375 +++ src/qml/util/util.pri | 8 + src/quick/items/items.pri | 7 - src/quick/items/qquickgridview.cpp | 21 +- src/quick/items/qquickgridview_p.h | 3 +- src/quick/items/qquickitemsmodule.cpp | 6 - src/quick/items/qquickitemview.cpp | 114 +- src/quick/items/qquickitemview_p.h | 10 +- src/quick/items/qquickitemview_p_p.h | 29 +- src/quick/items/qquicklistview.cpp | 21 +- src/quick/items/qquicklistview_p.h | 4 +- src/quick/items/qquickpathview.cpp | 84 +- src/quick/items/qquickpathview_p.h | 10 +- src/quick/items/qquickpathview_p_p.h | 5 +- src/quick/items/qquickrepeater.cpp | 82 +- src/quick/items/qquickrepeater_p.h | 8 +- src/quick/items/qquickrepeater_p_p.h | 5 +- src/quick/items/qquickvisualadaptormodel.cpp | 977 ------ src/quick/items/qquickvisualadaptormodel_p.h | 156 - src/quick/items/qquickvisualdatamodel.cpp | 3173 -------------------- src/quick/items/qquickvisualdatamodel_p.h | 238 -- src/quick/items/qquickvisualdatamodel_p_p.h | 411 --- src/quick/items/qquickvisualitemmodel.cpp | 256 -- src/quick/items/qquickvisualitemmodel_p.h | 174 -- src/quick/util/qquickchangeset.cpp | 621 ---- src/quick/util/qquickchangeset_p.h | 166 - src/quick/util/qquicklistaccessor.cpp | 138 - src/quick/util/qquicklistaccessor_p.h | 78 - src/quick/util/qquicklistcompositor.cpp | 1484 --------- src/quick/util/qquicklistcompositor_p.h | 375 --- src/quick/util/qquickpackage.cpp | 198 -- src/quick/util/qquickpackage_p.h | 96 - src/quick/util/qquickutilmodule.cpp | 2 - src/quick/util/util.pri | 8 - tests/auto/qml/qml.pro | 4 +- tests/auto/qml/qqmlchangeset/qqmlchangeset.pro | 10 + tests/auto/qml/qqmlchangeset/tst_qqmlchangeset.cpp | 1574 ++++++++++ .../qml/qqmllistcompositor/qqmllistcompositor.pro | 10 + .../qqmllistcompositor/tst_qqmllistcompositor.cpp | 1740 +++++++++++ tests/auto/qml/qquickchangeset/qquickchangeset.pro | 10 - .../qml/qquickchangeset/tst_qquickchangeset.cpp | 1574 ---------- .../qquicklistcompositor/qquicklistcompositor.pro | 10 - .../tst_qquicklistcompositor.cpp | 1740 ----------- .../quick/qquickgridview/tst_qquickgridview.cpp | 1 - .../quick/qquicklistview/tst_qquicklistview.cpp | 8 +- .../tst_qquickvisualdatamodel.cpp | 264 +- 64 files changed, 12247 insertions(+), 12204 deletions(-) create mode 100644 src/qml/items/items.pri create mode 100644 src/qml/items/qqmldelegatemodel.cpp create mode 100644 src/qml/items/qqmldelegatemodel_p.h create mode 100644 src/qml/items/qqmldelegatemodel_p_p.h create mode 100644 src/qml/items/qqmlobjectmodel.cpp create mode 100644 src/qml/items/qqmlobjectmodel_p.h create mode 100644 src/qml/items/qquickpackage.cpp create mode 100644 src/qml/items/qquickpackage_p.h create mode 100644 src/qml/util/qqmladaptormodel.cpp create mode 100644 src/qml/util/qqmladaptormodel_p.h create mode 100644 src/qml/util/qqmlchangeset.cpp create mode 100644 src/qml/util/qqmlchangeset_p.h create mode 100644 src/qml/util/qqmllistaccessor.cpp create mode 100644 src/qml/util/qqmllistaccessor_p.h create mode 100644 src/qml/util/qqmllistcompositor.cpp create mode 100644 src/qml/util/qqmllistcompositor_p.h delete mode 100644 src/quick/items/qquickvisualadaptormodel.cpp delete mode 100644 src/quick/items/qquickvisualadaptormodel_p.h delete mode 100644 src/quick/items/qquickvisualdatamodel.cpp delete mode 100644 src/quick/items/qquickvisualdatamodel_p.h delete mode 100644 src/quick/items/qquickvisualdatamodel_p_p.h delete mode 100644 src/quick/items/qquickvisualitemmodel.cpp delete mode 100644 src/quick/items/qquickvisualitemmodel_p.h delete mode 100644 src/quick/util/qquickchangeset.cpp delete mode 100644 src/quick/util/qquickchangeset_p.h delete mode 100644 src/quick/util/qquicklistaccessor.cpp delete mode 100644 src/quick/util/qquicklistaccessor_p.h delete mode 100644 src/quick/util/qquicklistcompositor.cpp delete mode 100644 src/quick/util/qquicklistcompositor_p.h delete mode 100644 src/quick/util/qquickpackage.cpp delete mode 100644 src/quick/util/qquickpackage_p.h create mode 100644 tests/auto/qml/qqmlchangeset/qqmlchangeset.pro create mode 100644 tests/auto/qml/qqmlchangeset/tst_qqmlchangeset.cpp create mode 100644 tests/auto/qml/qqmllistcompositor/qqmllistcompositor.pro create mode 100644 tests/auto/qml/qqmllistcompositor/tst_qqmllistcompositor.cpp delete mode 100644 tests/auto/qml/qquickchangeset/qquickchangeset.pro delete mode 100644 tests/auto/qml/qquickchangeset/tst_qquickchangeset.cpp delete mode 100644 tests/auto/qml/qquicklistcompositor/qquicklistcompositor.pro delete mode 100644 tests/auto/qml/qquicklistcompositor/tst_qquicklistcompositor.cpp diff --git a/src/particles/qquickitemparticle.cpp b/src/particles/qquickitemparticle.cpp index 28a66e8c0b..7bc696e65f 100644 --- a/src/particles/qquickitemparticle.cpp +++ b/src/particles/qquickitemparticle.cpp @@ -40,7 +40,6 @@ ****************************************************************************/ #include "qquickitemparticle_p.h" -#include #include #include #include diff --git a/src/qml/items/items.pri b/src/qml/items/items.pri new file mode 100644 index 0000000000..0fa489f4e1 --- /dev/null +++ b/src/qml/items/items.pri @@ -0,0 +1,10 @@ +SOURCES += \ + $$PWD/qquickpackage.cpp \ + $$PWD/qqmldelegatemodel.cpp \ + $$PWD/qqmlobjectmodel.cpp + +HEADERS += \ + $$PWD/qquickpackage_p.h \ + $$PWD/qqmldelegatemodel_p.h \ + $$PWD/qqmldelegatemodel_p_p.h \ + $$PWD/qqmlobjectmodel_p.h diff --git a/src/qml/items/qqmldelegatemodel.cpp b/src/qml/items/qqmldelegatemodel.cpp new file mode 100644 index 0000000000..131b539d92 --- /dev/null +++ b/src/qml/items/qqmldelegatemodel.cpp @@ -0,0 +1,3154 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmldelegatemodel_p_p.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QQmlDelegateModelEngineData : public QV8Engine::Deletable +{ +public: + enum + { + Model, + Groups, + IsUnresolved, + ItemsIndex, + PersistedItemsIndex, + InItems, + InPersistedItems, + StringCount + }; + + QQmlDelegateModelEngineData(QV8Engine *engine); + ~QQmlDelegateModelEngineData(); + + v8::Local array( + QV8Engine *engine, const QVector &changes); + v8::Local array( + QV8Engine *engine, const QVector &changes); + v8::Local array( + QV8Engine *engine, const QVector &changes); + + + inline v8::Local model() { return strings->Get(Model)->ToString(); } + inline v8::Local groups() { return strings->Get(Groups)->ToString(); } + inline v8::Local isUnresolved() { return strings->Get(IsUnresolved)->ToString(); } + inline v8::Local itemsIndex() { return strings->Get(ItemsIndex)->ToString(); } + inline v8::Local persistedItemsIndex() { return strings->Get(PersistedItemsIndex)->ToString(); } + inline v8::Local inItems() { return strings->Get(InItems)->ToString(); } + inline v8::Local inPersistedItems() { return strings->Get(InPersistedItems)->ToString(); } + + v8::Persistent strings; + v8::Persistent constructorChange; + v8::Persistent constructorChangeArray; +}; + +V8_DEFINE_EXTENSION(QQmlDelegateModelEngineData, engineData) + + +void QQmlDelegateModelPartsMetaObject::propertyCreated(int, QMetaPropertyBuilder &prop) +{ + prop.setWritable(false); +} + +QVariant QQmlDelegateModelPartsMetaObject::initialValue(int id) +{ + QQmlDelegateModelParts *parts = static_cast(object()); + QQmlPartsModel *m = new QQmlPartsModel( + parts->model, QString::fromUtf8(name(id)), parts); + parts->models.append(m); + return QVariant::fromValue(static_cast(m)); +} + +QQmlDelegateModelParts::QQmlDelegateModelParts(QQmlDelegateModel *parent) +: QObject(parent), model(parent) +{ + new QQmlDelegateModelPartsMetaObject(this); +} + +//--------------------------------------------------------------------------- + +/*! + \qmltype VisualDataModel + \instantiates QQmlDelegateModel + \inqmlmodule QtQuick 2 + \ingroup qtquick-models + \brief Encapsulates a model and delegate + + The VisualDataModel type encapsulates a model and the delegate that will + be instantiated for items in the model. + + It is usually not necessary to create a VisualDataModel. + However, it can be useful for manipulating and accessing the \l modelIndex + when a QAbstractItemModel subclass is used as the + model. Also, VisualDataModel is used together with \l Package to + provide delegates to multiple views, and with VisualDataGroup to sort and filter + delegate items. + + The example below illustrates using a VisualDataModel with a ListView. + + \snippet qml/visualdatamodel.qml 0 +*/ + +QQmlDelegateModelPrivate::QQmlDelegateModelPrivate(QQmlContext *ctxt) + : m_delegate(0) + , m_cacheMetaType(0) + , m_context(ctxt) + , m_parts(0) + , m_filterGroup(QStringLiteral("items")) + , m_count(0) + , m_groupCount(Compositor::MinimumGroupCount) + , m_compositorGroup(Compositor::Cache) + , m_complete(false) + , m_delegateValidated(false) + , m_reset(false) + , m_transaction(false) + , m_incubatorCleanupScheduled(false) + , m_cacheItems(0) + , m_items(0) + , m_persistedItems(0) +{ +} + +QQmlDelegateModelPrivate::~QQmlDelegateModelPrivate() +{ + qDeleteAll(m_finishedIncubating); + + if (m_cacheMetaType) + m_cacheMetaType->release(); +} + +void QQmlDelegateModelPrivate::init() +{ + Q_Q(QQmlDelegateModel); + m_compositor.setRemoveGroups(Compositor::GroupMask & ~Compositor::PersistedFlag); + + m_items = new QQmlDataGroup(QStringLiteral("items"), q, Compositor::Default, q); + m_items->setDefaultInclude(true); + m_persistedItems = new QQmlDataGroup(QStringLiteral("persistedItems"), q, Compositor::Persisted, q); + QQmlDataGroupPrivate::get(m_items)->emitters.insert(this); +} + +QQmlDelegateModel::QQmlDelegateModel() +: QQmlInstanceModel(*(new QQmlDelegateModelPrivate(0))) +{ + Q_D(QQmlDelegateModel); + d->init(); +} + +QQmlDelegateModel::QQmlDelegateModel(QQmlContext *ctxt, QObject *parent) +: QQmlInstanceModel(*(new QQmlDelegateModelPrivate(ctxt)), parent) +{ + Q_D(QQmlDelegateModel); + d->init(); +} + +QQmlDelegateModel::~QQmlDelegateModel() +{ + Q_D(QQmlDelegateModel); + + foreach (QQmlDelegateModelItem *cacheItem, d->m_cache) { + if (cacheItem->object) { + delete cacheItem->object; + + cacheItem->object = 0; + cacheItem->contextData->destroy(); + cacheItem->contextData = 0; + cacheItem->scriptRef -= 1; + } + cacheItem->groups &= ~Compositor::UnresolvedFlag; + cacheItem->objectRef = 0; + if (!cacheItem->isReferenced()) + delete cacheItem; + } +} + + +void QQmlDelegateModel::classBegin() +{ + Q_D(QQmlDelegateModel); + if (!d->m_context) + d->m_context = qmlContext(this); +} + +void QQmlDelegateModel::componentComplete() +{ + Q_D(QQmlDelegateModel); + d->m_complete = true; + + int defaultGroups = 0; + QStringList groupNames; + groupNames.append(QStringLiteral("items")); + groupNames.append(QStringLiteral("persistedItems")); + if (QQmlDataGroupPrivate::get(d->m_items)->defaultInclude) + defaultGroups |= Compositor::DefaultFlag; + if (QQmlDataGroupPrivate::get(d->m_persistedItems)->defaultInclude) + defaultGroups |= Compositor::PersistedFlag; + for (int i = Compositor::MinimumGroupCount; i < d->m_groupCount; ++i) { + QString name = d->m_groups[i]->name(); + if (name.isEmpty()) { + d->m_groups[i] = d->m_groups[d->m_groupCount - 1]; + --d->m_groupCount; + --i; + } else if (name.at(0).isUpper()) { + qmlInfo(d->m_groups[i]) << QQmlDataGroup::tr("Group names must start with a lower case letter"); + d->m_groups[i] = d->m_groups[d->m_groupCount - 1]; + --d->m_groupCount; + --i; + } else { + groupNames.append(name); + + QQmlDataGroupPrivate *group = QQmlDataGroupPrivate::get(d->m_groups[i]); + group->setModel(this, Compositor::Group(i)); + if (group->defaultInclude) + defaultGroups |= (1 << i); + } + } + + d->m_cacheMetaType = new QQmlDelegateModelItemMetaType( + QQmlEnginePrivate::getV8Engine(d->m_context->engine()), this, groupNames); + + d->m_compositor.setGroupCount(d->m_groupCount); + d->m_compositor.setDefaultGroups(defaultGroups); + d->updateFilterGroup(); + + while (!d->m_pendingParts.isEmpty()) + static_cast(d->m_pendingParts.first())->updateFilterGroup(); + + QVector inserts; + d->m_count = d->m_adaptorModel.count(); + d->m_compositor.append( + &d->m_adaptorModel, + 0, + d->m_count, + defaultGroups | Compositor::AppendFlag | Compositor::PrependFlag, + &inserts); + d->itemsInserted(inserts); + d->emitChanges(); + + if (d->m_adaptorModel.canFetchMore()) + QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest)); +} + +/*! + \qmlproperty model QtQuick2::VisualDataModel::model + This property holds the model providing data for the VisualDataModel. + + The model provides a set of data that is used to create the items + for a view. For large or dynamic datasets the model is usually + provided by a C++ model object. The C++ model object must be a \l + {QAbstractItemModel} subclass or a simple list. + + Models can also be created directly in QML, using a \l{ListModel} or + \l{XmlListModel}. + + \sa {qml-data-models}{Data Models} +*/ +QVariant QQmlDelegateModel::model() const +{ + Q_D(const QQmlDelegateModel); + return d->m_adaptorModel.model(); +} + +void QQmlDelegateModel::setModel(const QVariant &model) +{ + Q_D(QQmlDelegateModel); + + if (d->m_complete) + _q_itemsRemoved(0, d->m_count); + + d->m_adaptorModel.setModel(model, this, d->m_context->engine()); + d->m_adaptorModel.replaceWatchedRoles(QList(), d->m_watchedRoles); + for (int i = 0; d->m_parts && i < d->m_parts->models.count(); ++i) { + d->m_adaptorModel.replaceWatchedRoles( + QList(), d->m_parts->models.at(i)->watchedRoles()); + } + + if (d->m_complete) { + _q_itemsInserted(0, d->m_adaptorModel.count()); + if (d->m_adaptorModel.canFetchMore()) + QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest)); + } +} + +/*! + \qmlproperty Component QtQuick2::VisualDataModel::delegate + + The delegate provides a template defining each item instantiated by a view. + The index is exposed as an accessible \c index property. Properties of the + model are also available depending upon the type of \l {qml-data-models}{Data Model}. +*/ +QQmlComponent *QQmlDelegateModel::delegate() const +{ + Q_D(const QQmlDelegateModel); + return d->m_delegate; +} + +void QQmlDelegateModel::setDelegate(QQmlComponent *delegate) +{ + Q_D(QQmlDelegateModel); + if (d->m_transaction) { + qmlInfo(this) << tr("The delegate of a VisualDataModel cannot be changed within onUpdated."); + return; + } + bool wasValid = d->m_delegate != 0; + d->m_delegate = delegate; + d->m_delegateValidated = false; + if (wasValid && d->m_complete) { + for (int i = 1; i < d->m_groupCount; ++i) { + QQmlDataGroupPrivate::get(d->m_groups[i])->changeSet.remove( + 0, d->m_compositor.count(Compositor::Group(i))); + } + } + if (d->m_complete && d->m_delegate) { + for (int i = 1; i < d->m_groupCount; ++i) { + QQmlDataGroupPrivate::get(d->m_groups[i])->changeSet.insert( + 0, d->m_compositor.count(Compositor::Group(i))); + } + } + d->emitChanges(); +} + +/*! + \qmlproperty QModelIndex QtQuick2::VisualDataModel::rootIndex + + QAbstractItemModel provides a hierarchical tree of data, whereas + QML only operates on list data. \c rootIndex allows the children of + any node in a QAbstractItemModel to be provided by this model. + + This property only affects models of type QAbstractItemModel that + are hierarchical (e.g, a tree model). + + For example, here is a simple interactive file system browser. + When a directory name is clicked, the view's \c rootIndex is set to the + QModelIndex node of the clicked directory, thus updating the view to show + the new directory's contents. + + \c main.cpp: + \snippet qml/visualdatamodel_rootindex/main.cpp 0 + + \c view.qml: + \snippet qml/visualdatamodel_rootindex/view.qml 0 + + If the \l model is a QAbstractItemModel subclass, the delegate can also + reference a \c hasModelChildren property (optionally qualified by a + \e model. prefix) that indicates whether the delegate's model item has + any child nodes. + + + \sa modelIndex(), parentModelIndex() +*/ +QVariant QQmlDelegateModel::rootIndex() const +{ + Q_D(const QQmlDelegateModel); + return QVariant::fromValue(QModelIndex(d->m_adaptorModel.rootIndex)); +} + +void QQmlDelegateModel::setRootIndex(const QVariant &root) +{ + Q_D(QQmlDelegateModel); + + QModelIndex modelIndex = qvariant_cast(root); + const bool changed = d->m_adaptorModel.rootIndex != modelIndex; + if (changed || !d->m_adaptorModel.isValid()) { + const int oldCount = d->m_count; + d->m_adaptorModel.rootIndex = modelIndex; + if (!d->m_adaptorModel.isValid() && d->m_adaptorModel.aim()) // The previous root index was invalidated, so we need to reconnect the model. + d->m_adaptorModel.setModel(d->m_adaptorModel.list.list(), this, d->m_context->engine()); + if (d->m_adaptorModel.canFetchMore()) + d->m_adaptorModel.fetchMore(); + if (d->m_complete) { + const int newCount = d->m_adaptorModel.count(); + if (oldCount) + _q_itemsRemoved(0, oldCount); + if (newCount) + _q_itemsInserted(0, newCount); + } + if (changed) + emit rootIndexChanged(); + } +} + +/*! + \qmlmethod QModelIndex QtQuick2::VisualDataModel::modelIndex(int index) + + QAbstractItemModel provides a hierarchical tree of data, whereas + QML only operates on list data. This function assists in using + tree models in QML. + + Returns a QModelIndex for the specified index. + This value can be assigned to rootIndex. + + \sa rootIndex +*/ +QVariant QQmlDelegateModel::modelIndex(int idx) const +{ + Q_D(const QQmlDelegateModel); + return d->m_adaptorModel.modelIndex(idx); +} + +/*! + \qmlmethod QModelIndex QtQuick2::VisualDataModel::parentModelIndex() + + QAbstractItemModel provides a hierarchical tree of data, whereas + QML only operates on list data. This function assists in using + tree models in QML. + + Returns a QModelIndex for the parent of the current rootIndex. + This value can be assigned to rootIndex. + + \sa rootIndex +*/ +QVariant QQmlDelegateModel::parentModelIndex() const +{ + Q_D(const QQmlDelegateModel); + return d->m_adaptorModel.parentModelIndex(); +} + +/*! + \qmlproperty int QtQuick2::VisualDataModel::count +*/ + +int QQmlDelegateModel::count() const +{ + Q_D(const QQmlDelegateModel); + if (!d->m_delegate) + return 0; + return d->m_compositor.count(d->m_compositorGroup); +} + +QQmlDelegateModel::ReleaseFlags QQmlDelegateModelPrivate::release(QObject *object) +{ + QQmlDelegateModel::ReleaseFlags stat = 0; + if (!object) + return stat; + + if (QQmlDelegateModelItem *cacheItem = QQmlDelegateModelItem::dataForObject(object)) { + if (cacheItem->releaseObject()) { + cacheItem->destroyObject(); + emitDestroyingItem(object); + if (cacheItem->incubationTask) { + releaseIncubator(cacheItem->incubationTask); + cacheItem->incubationTask = 0; + } + cacheItem->Dispose(); + stat |= QQmlInstanceModel::Destroyed; + } else { + stat |= QQmlDelegateModel::Referenced; + } + } + return stat; +} + +/* + Returns ReleaseStatus flags. +*/ + +QQmlDelegateModel::ReleaseFlags QQmlDelegateModel::release(QObject *item) +{ + Q_D(QQmlDelegateModel); + QQmlInstanceModel::ReleaseFlags stat = d->release(item); + return stat; +} + +// Cancel a requested async item +void QQmlDelegateModel::cancel(int index) +{ + Q_D(QQmlDelegateModel); + if (!d->m_delegate || index < 0 || index >= d->m_compositor.count(d->m_compositorGroup)) { + qWarning() << "VisualDataModel::cancel: index out range" << index << d->m_compositor.count(d->m_compositorGroup); + return; + } + + Compositor::iterator it = d->m_compositor.find(d->m_compositorGroup, index); + QQmlDelegateModelItem *cacheItem = it->inCache() ? d->m_cache.at(it.cacheIndex) : 0; + if (cacheItem) { + if (cacheItem->incubationTask && !cacheItem->isObjectReferenced()) { + d->releaseIncubator(cacheItem->incubationTask); + cacheItem->incubationTask = 0; + + if (cacheItem->object) { + QObject *object = cacheItem->object; + cacheItem->destroyObject(); + if (QQuickPackage *package = qmlobject_cast(object)) + d->emitDestroyingPackage(package); + else + d->emitDestroyingItem(object); + } + + cacheItem->scriptRef -= 1; + } + if (!cacheItem->isReferenced()) { + d->m_compositor.clearFlags(Compositor::Cache, it.cacheIndex, 1, Compositor::CacheFlag); + d->m_cache.removeAt(it.cacheIndex); + delete cacheItem; + Q_ASSERT(d->m_cache.count() == d->m_compositor.count(Compositor::Cache)); + } + } +} + +void QQmlDelegateModelPrivate::group_append( + QQmlListProperty *property, QQmlDataGroup *group) +{ + QQmlDelegateModelPrivate *d = static_cast(property->data); + if (d->m_complete) + return; + if (d->m_groupCount == Compositor::MaximumGroupCount) { + qmlInfo(d->q_func()) << QQmlDelegateModel::tr("The maximum number of supported VisualDataGroups is 8"); + return; + } + d->m_groups[d->m_groupCount] = group; + d->m_groupCount += 1; +} + +int QQmlDelegateModelPrivate::group_count( + QQmlListProperty *property) +{ + QQmlDelegateModelPrivate *d = static_cast(property->data); + return d->m_groupCount - 1; +} + +QQmlDataGroup *QQmlDelegateModelPrivate::group_at( + QQmlListProperty *property, int index) +{ + QQmlDelegateModelPrivate *d = static_cast(property->data); + return index >= 0 && index < d->m_groupCount - 1 + ? d->m_groups[index + 1] + : 0; +} + +/*! + \qmlproperty list QtQuick2::VisualDataModel::groups + + This property holds a visual data model's group definitions. + + Groups define a sub-set of the items in a visual data model and can be used to filter + a model. + + For every group defined in a VisualDataModel two attached properties are added to each + delegate item. The first of the form VisualDataModel.in\e{GroupName} holds whether the + item belongs to the group and the second VisualDataModel.\e{groupName}Index holds the + index of the item in that group. + + The following example illustrates using groups to select items in a model. + + \snippet qml/visualdatagroup.qml 0 +*/ + +QQmlListProperty QQmlDelegateModel::groups() +{ + Q_D(QQmlDelegateModel); + return QQmlListProperty( + this, + d, + QQmlDelegateModelPrivate::group_append, + QQmlDelegateModelPrivate::group_count, + QQmlDelegateModelPrivate::group_at, + 0); +} + +/*! + \qmlproperty VisualDataGroup QtQuick2::VisualDataModel::items + + This property holds visual data model's default group to which all new items are added. +*/ + +QQmlDataGroup *QQmlDelegateModel::items() +{ + Q_D(QQmlDelegateModel); + return d->m_items; +} + +/*! + \qmlproperty VisualDataGroup QtQuick2::VisualDataModel::persistedItems + + This property holds visual data model's persisted items group. + + Items in this group are not destroyed when released by a view, instead they are persisted + until removed from the group. + + An item can be removed from the persistedItems group by setting the + VisualDataModel.inPersistedItems property to false. If the item is not referenced by a view + at that time it will be destroyed. Adding an item to this group will not create a new + instance. + + Items returned by the \l QtQuick2::VisualDataGroup::create() function are automatically added + to this group. +*/ + +QQmlDataGroup *QQmlDelegateModel::persistedItems() +{ + Q_D(QQmlDelegateModel); + return d->m_persistedItems; +} + +/*! + \qmlproperty string QtQuick2::VisualDataModel::filterOnGroup + + This property holds the name of the group used to filter the visual data model. + + Only items which belong to this group are visible to a view. + + By default this is the \l items group. +*/ + +QString QQmlDelegateModel::filterGroup() const +{ + Q_D(const QQmlDelegateModel); + return d->m_filterGroup; +} + +void QQmlDelegateModel::setFilterGroup(const QString &group) +{ + Q_D(QQmlDelegateModel); + + if (d->m_transaction) { + qmlInfo(this) << tr("The group of a VisualDataModel cannot be changed within onChanged"); + return; + } + + if (d->m_filterGroup != group) { + d->m_filterGroup = group; + d->updateFilterGroup(); + emit filterGroupChanged(); + } +} + +void QQmlDelegateModel::resetFilterGroup() +{ + setFilterGroup(QStringLiteral("items")); +} + +void QQmlDelegateModelPrivate::updateFilterGroup() +{ + Q_Q(QQmlDelegateModel); + if (!m_cacheMetaType) + return; + + QQmlListCompositor::Group previousGroup = m_compositorGroup; + m_compositorGroup = Compositor::Default; + for (int i = 1; i < m_groupCount; ++i) { + if (m_filterGroup == m_cacheMetaType->groupNames.at(i - 1)) { + m_compositorGroup = Compositor::Group(i); + break; + } + } + + QQmlDataGroupPrivate::get(m_groups[m_compositorGroup])->emitters.insert(this); + if (m_compositorGroup != previousGroup) { + QVector removes; + QVector inserts; + m_compositor.transition(previousGroup, m_compositorGroup, &removes, &inserts); + + QQmlChangeSet changeSet; + changeSet.move(removes, inserts); + emit q->modelUpdated(changeSet, false); + + if (changeSet.difference() != 0) + emit q->countChanged(); + + if (m_parts) { + foreach (QQmlPartsModel *model, m_parts->models) + model->updateFilterGroup(m_compositorGroup, changeSet); + } + } +} + +/*! + \qmlproperty object QtQuick2::VisualDataModel::parts + + The \a parts property selects a VisualDataModel which creates + delegates from the part named. This is used in conjunction with + the \l Package type. + + For example, the code below selects a model which creates + delegates named \e list from a \l Package: + + \code + VisualDataModel { + id: visualModel + delegate: Package { + Item { Package.name: "list" } + } + model: myModel + } + + ListView { + width: 200; height:200 + model: visualModel.parts.list + } + \endcode + + \sa Package +*/ + +QObject *QQmlDelegateModel::parts() +{ + Q_D(QQmlDelegateModel); + if (!d->m_parts) + d->m_parts = new QQmlDelegateModelParts(this); + return d->m_parts; +} + +void QQmlDelegateModelPrivate::emitCreatedPackage(QQDMIncubationTask *incubationTask, QQuickPackage *package) +{ + for (int i = 1; i < m_groupCount; ++i) + QQmlDataGroupPrivate::get(m_groups[i])->createdPackage(incubationTask->index[i], package); +} + +void QQmlDelegateModelPrivate::emitInitPackage(QQDMIncubationTask *incubationTask, QQuickPackage *package) +{ + for (int i = 1; i < m_groupCount; ++i) + QQmlDataGroupPrivate::get(m_groups[i])->initPackage(incubationTask->index[i], package); +} + +void QQmlDelegateModelPrivate::emitDestroyingPackage(QQuickPackage *package) +{ + for (int i = 1; i < m_groupCount; ++i) + QQmlDataGroupPrivate::get(m_groups[i])->destroyingPackage(package); +} + +void QQDMIncubationTask::statusChanged(Status status) +{ + vdm->incubatorStatusChanged(this, status); +} + +void QQmlDelegateModelPrivate::releaseIncubator(QQDMIncubationTask *incubationTask) +{ + Q_Q(QQmlDelegateModel); + if (!incubationTask->isError()) + incubationTask->clear(); + m_finishedIncubating.append(incubationTask); + if (!m_incubatorCleanupScheduled) { + m_incubatorCleanupScheduled = true; + QCoreApplication::postEvent(q, new QEvent(QEvent::User)); + } +} + +void QQmlDelegateModelPrivate::removeCacheItem(QQmlDelegateModelItem *cacheItem) +{ + int cidx = m_cache.indexOf(cacheItem); + if (cidx >= 0) { + m_compositor.clearFlags(Compositor::Cache, cidx, 1, Compositor::CacheFlag); + m_cache.removeAt(cidx); + } + Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); +} + +void QQmlDelegateModelPrivate::incubatorStatusChanged(QQDMIncubationTask *incubationTask, QQmlIncubator::Status status) +{ + Q_Q(QQmlDelegateModel); + if (status != QQmlIncubator::Ready && status != QQmlIncubator::Error) + return; + + QQmlDelegateModelItem *cacheItem = incubationTask->incubating; + cacheItem->incubationTask = 0; + incubationTask->incubating = 0; + releaseIncubator(incubationTask); + + if (status == QQmlIncubator::Ready) { + if (QQuickPackage *package = qmlobject_cast(cacheItem->object)) + emitCreatedPackage(incubationTask, package); + else + emitCreatedItem(incubationTask, cacheItem->object); + } else if (status == QQmlIncubator::Error) { + qmlInfo(q, m_delegate->errors()) << "Error creating delegate"; + } + + if (!cacheItem->isObjectReferenced()) { + if (QQuickPackage *package = qmlobject_cast(cacheItem->object)) + emitDestroyingPackage(package); + else + emitDestroyingItem(cacheItem->object); + delete cacheItem->object; + cacheItem->object = 0; + cacheItem->scriptRef -= 1; + cacheItem->contextData->destroy(); + cacheItem->contextData = 0; + if (!cacheItem->isReferenced()) { + removeCacheItem(cacheItem); + delete cacheItem; + } + } +} + +void QQDMIncubationTask::setInitialState(QObject *o) +{ + vdm->setInitialState(this, o); +} + +void QQmlDelegateModelPrivate::setInitialState(QQDMIncubationTask *incubationTask, QObject *o) +{ + QQmlDelegateModelItem *cacheItem = incubationTask->incubating; + cacheItem->object = o; + + if (QQuickPackage *package = qmlobject_cast(cacheItem->object)) + emitInitPackage(incubationTask, package); + else + emitInitItem(incubationTask, cacheItem->object); +} + +QObject *QQmlDelegateModelPrivate::object(Compositor::Group group, int index, bool asynchronous) +{ + Q_Q(QQmlDelegateModel); + if (!m_delegate || index < 0 || index >= m_compositor.count(group)) { + qWarning() << "VisualDataModel::item: index out range" << index << m_compositor.count(group); + return 0; + } else if (!m_context->isValid()) { + return 0; + } + + Compositor::iterator it = m_compositor.find(group, index); + + QQmlDelegateModelItem *cacheItem = it->inCache() ? m_cache.at(it.cacheIndex) : 0; + + if (!cacheItem) { + cacheItem = m_adaptorModel.createItem(m_cacheMetaType, m_context->engine(), it.modelIndex()); + if (!cacheItem) + return 0; + + cacheItem->groups = it->flags; + + m_cache.insert(it.cacheIndex, cacheItem); + m_compositor.setFlags(it, 1, Compositor::CacheFlag); + Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); + } + + // Bump the reference counts temporarily so neither the content data or the delegate object + // are deleted if incubatorStatusChanged() is called synchronously. + cacheItem->scriptRef += 1; + cacheItem->referenceObject(); + + if (cacheItem->incubationTask) { + if (!asynchronous && cacheItem->incubationTask->incubationMode() == QQmlIncubator::Asynchronous) { + // previously requested async - now needed immediately + cacheItem->incubationTask->forceCompletion(); + } + } else if (!cacheItem->object) { + QQmlContext *creationContext = m_delegate->creationContext(); + + cacheItem->scriptRef += 1; + + cacheItem->incubationTask = new QQDMIncubationTask(this, asynchronous ? QQmlIncubator::Asynchronous : QQmlIncubator::AsynchronousIfNested); + cacheItem->incubationTask->incubating = cacheItem; + cacheItem->incubationTask->clear(); + + for (int i = 1; i < m_groupCount; ++i) + cacheItem->incubationTask->index[i] = it.index[i]; + + QQmlContextData *ctxt = new QQmlContextData; + ctxt->setParent(QQmlContextData::get(creationContext ? creationContext : m_context)); + ctxt->contextObject = cacheItem; + cacheItem->contextData = ctxt; + + if (m_adaptorModel.hasProxyObject()) { + if (QQmlAdaptorModelProxyInterface *proxy + = qobject_cast(cacheItem)) { + ctxt = new QQmlContextData; + ctxt->setParent(cacheItem->contextData, true); + ctxt->contextObject = proxy->proxiedObject(); + } + } + + cacheItem->incubateObject( + m_delegate, + m_context->engine(), + ctxt, + QQmlContextData::get(m_context)); + } + + if (index == m_compositor.count(group) - 1 && m_adaptorModel.canFetchMore()) + QCoreApplication::postEvent(q, new QEvent(QEvent::UpdateRequest)); + + // Remove the temporary reference count. + cacheItem->scriptRef -= 1; + if (cacheItem->object) + return cacheItem->object; + + cacheItem->releaseObject(); + if (!cacheItem->isReferenced()) { + removeCacheItem(cacheItem); + delete cacheItem; + } + + return 0; +} + +/* + If asynchronous is true or the component is being loaded asynchronously due + to an ancestor being loaded asynchronously, item() may return 0. In this + case itemCreated() will be emitted when the item is available. The item + at this stage does not have any references, so item() must be called again + to ensure a reference is held. Any call to item() which returns a valid item + must be matched by a call to release() in order to destroy the item. +*/ +QObject *QQmlDelegateModel::object(int index, bool asynchronous) +{ + Q_D(QQmlDelegateModel); + if (!d->m_delegate || index < 0 || index >= d->m_compositor.count(d->m_compositorGroup)) { + qWarning() << "VisualDataModel::item: index out range" << index << d->m_compositor.count(d->m_compositorGroup); + return 0; + } + + QObject *object = d->object(d->m_compositorGroup, index, asynchronous); + if (!object) + return 0; + + return object; +} + +QString QQmlDelegateModelPrivate::stringValue(Compositor::Group group, int index, const QString &name) +{ + Compositor::iterator it = m_compositor.find(group, index); + if (QQmlAdaptorModel *model = it.list()) { + QString role = name; + int dot = name.indexOf(QLatin1Char('.')); + if (dot > 0) + role = name.left(dot); + QVariant value = model->value(it.modelIndex(), role); + while (dot > 0) { + QObject *obj = qvariant_cast(value); + if (!obj) + return QString(); + int from = dot+1; + dot = name.indexOf(QLatin1Char('.'), from); + value = obj->property(name.mid(from, dot-from).toUtf8()); + } + return value.toString(); + } + return QString(); +} + +QString QQmlDelegateModel::stringValue(int index, const QString &name) +{ + Q_D(QQmlDelegateModel); + return d->stringValue(d->m_compositorGroup, index, name); +} + +int QQmlDelegateModel::indexOf(QObject *item, QObject *) const +{ + Q_D(const QQmlDelegateModel); + if (QQmlDelegateModelItem *cacheItem = QQmlDelegateModelItem::dataForObject(item)) + return cacheItem->groupIndex(d->m_compositorGroup); + return -1; +} + +void QQmlDelegateModel::setWatchedRoles(QList roles) +{ + Q_D(QQmlDelegateModel); + d->m_adaptorModel.replaceWatchedRoles(d->m_watchedRoles, roles); + d->m_watchedRoles = roles; +} + +void QQmlDelegateModelPrivate::addGroups( + Compositor::iterator from, int count, Compositor::Group group, int groupFlags) +{ + QVector inserts; + m_compositor.setFlags(from, count, group, groupFlags, &inserts); + itemsInserted(inserts); + emitChanges(); +} + +void QQmlDelegateModelPrivate::removeGroups( + Compositor::iterator from, int count, Compositor::Group group, int groupFlags) +{ + QVector removes; + m_compositor.clearFlags(from, count, group, groupFlags, &removes); + itemsRemoved(removes); + emitChanges(); +} + +void QQmlDelegateModelPrivate::setGroups( + Compositor::iterator from, int count, Compositor::Group group, int groupFlags) +{ + QVector removes; + QVector inserts; + + m_compositor.setFlags(from, count, group, groupFlags, &inserts); + itemsInserted(inserts); + const int removeFlags = ~groupFlags & Compositor::GroupMask; + + from = m_compositor.find(from.group, from.index[from.group]); + m_compositor.clearFlags(from, count, group, removeFlags, &removes); + itemsRemoved(removes); + emitChanges(); +} + +bool QQmlDelegateModel::event(QEvent *e) +{ + Q_D(QQmlDelegateModel); + if (e->type() == QEvent::UpdateRequest) { + d->m_adaptorModel.fetchMore(); + } else if (e->type() == QEvent::User) { + d->m_incubatorCleanupScheduled = false; + qDeleteAll(d->m_finishedIncubating); + d->m_finishedIncubating.clear(); + } + return QQmlInstanceModel::event(e); +} + +void QQmlDelegateModelPrivate::itemsChanged(const QVector &changes) +{ + if (!m_delegate) + return; + + QVarLengthArray, Compositor::MaximumGroupCount> translatedChanges(m_groupCount); + + foreach (const Compositor::Change &change, changes) { + for (int i = 1; i < m_groupCount; ++i) { + if (change.inGroup(i)) { + translatedChanges[i].append(QQmlChangeSet::Change(change.index[i], change.count)); + } + } + } + + for (int i = 1; i < m_groupCount; ++i) + QQmlDataGroupPrivate::get(m_groups[i])->changeSet.change(translatedChanges.at(i)); +} + +void QQmlDelegateModel::_q_itemsChanged(int index, int count, const QVector &roles) +{ + Q_D(QQmlDelegateModel); + if (count <= 0 || !d->m_complete) + return; + + if (d->m_adaptorModel.notify(d->m_cache, index, count, roles)) { + QVector changes; + d->m_compositor.listItemsChanged(&d->m_adaptorModel, index, count, &changes); + d->itemsChanged(changes); + d->emitChanges(); + } +} + +static void incrementIndexes(QQmlDelegateModelItem *cacheItem, int count, const int *deltas) +{ + if (QQDMIncubationTask *incubationTask = cacheItem->incubationTask) { + for (int i = 1; i < count; ++i) + incubationTask->index[i] += deltas[i]; + } + if (QQmlDelegateModelAttached *attached = cacheItem->attached) { + for (int i = 1; i < count; ++i) + attached->m_currentIndex[i] += deltas[i]; + } +} + +void QQmlDelegateModelPrivate::itemsInserted( + const QVector &inserts, + QVarLengthArray, Compositor::MaximumGroupCount> *translatedInserts, + QHash > *movedItems) +{ + int cacheIndex = 0; + + int inserted[Compositor::MaximumGroupCount]; + for (int i = 1; i < m_groupCount; ++i) + inserted[i] = 0; + + foreach (const Compositor::Insert &insert, inserts) { + for (; cacheIndex < insert.cacheIndex; ++cacheIndex) + incrementIndexes(m_cache.at(cacheIndex), m_groupCount, inserted); + + for (int i = 1; i < m_groupCount; ++i) { + if (insert.inGroup(i)) { + (*translatedInserts)[i].append( + QQmlChangeSet::Insert(insert.index[i], insert.count, insert.moveId)); + inserted[i] += insert.count; + } + } + + if (!insert.inCache()) + continue; + + if (movedItems && insert.isMove()) { + QList items = movedItems->take(insert.moveId); + Q_ASSERT(items.count() == insert.count); + m_cache = m_cache.mid(0, insert.cacheIndex) + items + m_cache.mid(insert.cacheIndex); + } + if (insert.inGroup()) { + for (int offset = 0; cacheIndex < insert.cacheIndex + insert.count; ++cacheIndex, ++offset) { + QQmlDelegateModelItem *cacheItem = m_cache.at(cacheIndex); + cacheItem->groups |= insert.flags & Compositor::GroupMask; + + if (QQDMIncubationTask *incubationTask = cacheItem->incubationTask) { + for (int i = 1; i < m_groupCount; ++i) + incubationTask->index[i] = cacheItem->groups & (1 << i) + ? insert.index[i] + offset + : insert.index[i]; + } + if (QQmlDelegateModelAttached *attached = cacheItem->attached) { + for (int i = 1; i < m_groupCount; ++i) + attached->m_currentIndex[i] = cacheItem->groups & (1 << i) + ? insert.index[i] + offset + : insert.index[i]; + } + } + } else { + cacheIndex = insert.cacheIndex + insert.count; + } + } + for (; cacheIndex < m_cache.count(); ++cacheIndex) + incrementIndexes(m_cache.at(cacheIndex), m_groupCount, inserted); +} + +void QQmlDelegateModelPrivate::itemsInserted(const QVector &inserts) +{ + QVarLengthArray, Compositor::MaximumGroupCount> translatedInserts(m_groupCount); + itemsInserted(inserts, &translatedInserts); + Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); + if (!m_delegate) + return; + + for (int i = 1; i < m_groupCount; ++i) + QQmlDataGroupPrivate::get(m_groups[i])->changeSet.insert(translatedInserts.at(i)); +} + +void QQmlDelegateModel::_q_itemsInserted(int index, int count) +{ + + Q_D(QQmlDelegateModel); + if (count <= 0 || !d->m_complete) + return; + + d->m_count += count; + + for (int i = 0, c = d->m_cache.count(); i < c; ++i) { + QQmlDelegateModelItem *item = d->m_cache.at(i); + if (item->modelIndex() >= index) + item->setModelIndex(item->modelIndex() + count); + } + + QVector inserts; + d->m_compositor.listItemsInserted(&d->m_adaptorModel, index, count, &inserts); + d->itemsInserted(inserts); + d->emitChanges(); +} + +void QQmlDelegateModelPrivate::itemsRemoved( + const QVector &removes, + QVarLengthArray, Compositor::MaximumGroupCount> *translatedRemoves, + QHash > *movedItems) +{ + int cacheIndex = 0; + int removedCache = 0; + + int removed[Compositor::MaximumGroupCount]; + for (int i = 1; i < m_groupCount; ++i) + removed[i] = 0; + + foreach (const Compositor::Remove &remove, removes) { + for (; cacheIndex < remove.cacheIndex; ++cacheIndex) + incrementIndexes(m_cache.at(cacheIndex), m_groupCount, removed); + + for (int i = 1; i < m_groupCount; ++i) { + if (remove.inGroup(i)) { + (*translatedRemoves)[i].append( + QQmlChangeSet::Remove(remove.index[i], remove.count, remove.moveId)); + removed[i] -= remove.count; + } + } + + if (!remove.inCache()) + continue; + + if (movedItems && remove.isMove()) { + movedItems->insert(remove.moveId, m_cache.mid(remove.cacheIndex, remove.count)); + QList::iterator begin = m_cache.begin() + remove.cacheIndex; + QList::iterator end = begin + remove.count; + m_cache.erase(begin, end); + } else { + for (; cacheIndex < remove.cacheIndex + remove.count - removedCache; ++cacheIndex) { + QQmlDelegateModelItem *cacheItem = m_cache.at(cacheIndex); + if (remove.inGroup(Compositor::Persisted) && cacheItem->objectRef == 0 && cacheItem->object) { + QObject *object = cacheItem->object; + cacheItem->destroyObject(); + if (QQuickPackage *package = qmlobject_cast(object)) + emitDestroyingPackage(package); + else + emitDestroyingItem(object); + cacheItem->scriptRef -= 1; + } + if (!cacheItem->isReferenced()) { + m_compositor.clearFlags(Compositor::Cache, cacheIndex, 1, Compositor::CacheFlag); + m_cache.removeAt(cacheIndex); + delete cacheItem; + --cacheIndex; + ++removedCache; + Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); + } else if (remove.groups() == cacheItem->groups) { + cacheItem->groups = 0; + if (QQDMIncubationTask *incubationTask = cacheItem->incubationTask) { + for (int i = 1; i < m_groupCount; ++i) + incubationTask->index[i] = -1; + } + if (QQmlDelegateModelAttached *attached = cacheItem->attached) { + for (int i = 1; i < m_groupCount; ++i) + attached->m_currentIndex[i] = -1; + } + } else { + if (QQDMIncubationTask *incubationTask = cacheItem->incubationTask) { + for (int i = 1; i < m_groupCount; ++i) { + if (remove.inGroup(i)) + incubationTask->index[i] = remove.index[i]; + } + } + if (QQmlDelegateModelAttached *attached = cacheItem->attached) { + for (int i = 1; i < m_groupCount; ++i) { + if (remove.inGroup(i)) + attached->m_currentIndex[i] = remove.index[i]; + } + } + cacheItem->groups &= ~remove.flags; + } + } + } + } + + for (; cacheIndex < m_cache.count(); ++cacheIndex) + incrementIndexes(m_cache.at(cacheIndex), m_groupCount, removed); +} + +void QQmlDelegateModelPrivate::itemsRemoved(const QVector &removes) +{ + QVarLengthArray, Compositor::MaximumGroupCount> translatedRemoves(m_groupCount); + itemsRemoved(removes, &translatedRemoves); + Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); + if (!m_delegate) + return; + + for (int i = 1; i < m_groupCount; ++i) + QQmlDataGroupPrivate::get(m_groups[i])->changeSet.remove(translatedRemoves.at(i)); +} + +void QQmlDelegateModel::_q_itemsRemoved(int index, int count) +{ + Q_D(QQmlDelegateModel); + if (count <= 0|| !d->m_complete) + return; + + d->m_count -= count; + + for (int i = 0, c = d->m_cache.count(); i < c; ++i) { + QQmlDelegateModelItem *item = d->m_cache.at(i); + if (item->modelIndex() >= index + count) + item->setModelIndex(item->modelIndex() - count); + else if (item->modelIndex() >= index) + item->setModelIndex(-1); + } + + QVector removes; + d->m_compositor.listItemsRemoved(&d->m_adaptorModel, index, count, &removes); + d->itemsRemoved(removes); + + d->emitChanges(); +} + +void QQmlDelegateModelPrivate::itemsMoved( + const QVector &removes, const QVector &inserts) +{ + QHash > movedItems; + + QVarLengthArray, Compositor::MaximumGroupCount> translatedRemoves(m_groupCount); + itemsRemoved(removes, &translatedRemoves, &movedItems); + + QVarLengthArray, Compositor::MaximumGroupCount> translatedInserts(m_groupCount); + itemsInserted(inserts, &translatedInserts, &movedItems); + Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); + Q_ASSERT(movedItems.isEmpty()); + if (!m_delegate) + return; + + for (int i = 1; i < m_groupCount; ++i) { + QQmlDataGroupPrivate::get(m_groups[i])->changeSet.move( + translatedRemoves.at(i), + translatedInserts.at(i)); + } +} + +void QQmlDelegateModel::_q_itemsMoved(int from, int to, int count) +{ + Q_D(QQmlDelegateModel); + if (count <= 0 || !d->m_complete) + return; + + const int minimum = qMin(from, to); + const int maximum = qMax(from, to) + count; + const int difference = from > to ? count : -count; + + for (int i = 0, c = d->m_cache.count(); i < c; ++i) { + QQmlDelegateModelItem *item = d->m_cache.at(i); + if (item->modelIndex() >= from && item->modelIndex() < from + count) + item->setModelIndex(item->modelIndex() - from + to); + else if (item->modelIndex() >= minimum && item->modelIndex() < maximum) + item->setModelIndex(item->modelIndex() + difference); + } + + QVector removes; + QVector inserts; + d->m_compositor.listItemsMoved(&d->m_adaptorModel, from, to, count, &removes, &inserts); + d->itemsMoved(removes, inserts); + d->emitChanges(); +} + +template v8::Local +QQmlDelegateModelPrivate::buildChangeList(const QVector &changes) +{ + v8::Local indexes = v8::Array::New(changes.count()); + v8::Local indexKey = v8::String::New("index"); + v8::Local countKey = v8::String::New("count"); + v8::Local moveIdKey = v8::String::New("moveId"); + + for (int i = 0; i < changes.count(); ++i) { + v8::Local object = v8::Object::New(); + object->Set(indexKey, v8::Integer::New(changes.at(i).index)); + object->Set(countKey, v8::Integer::New(changes.at(i).count)); + object->Set(moveIdKey, changes.at(i).moveId != -1 ? v8::Integer::New(changes.at(i).count) : v8::Undefined()); + indexes->Set(i, object); + } + return indexes; +} + +void QQmlDelegateModelPrivate::emitModelUpdated(const QQmlChangeSet &changeSet, bool reset) +{ + Q_Q(QQmlDelegateModel); + emit q->modelUpdated(changeSet, reset); + if (changeSet.difference() != 0) + emit q->countChanged(); +} + +void QQmlDelegateModelPrivate::emitChanges() +{ + if (m_transaction || !m_complete || !m_context->isValid()) + return; + + m_transaction = true; + QV8Engine *engine = QQmlEnginePrivate::getV8Engine(m_context->engine()); + for (int i = 1; i < m_groupCount; ++i) + QQmlDataGroupPrivate::get(m_groups[i])->emitChanges(engine); + m_transaction = false; + + const bool reset = m_reset; + m_reset = false; + for (int i = 1; i < m_groupCount; ++i) + QQmlDataGroupPrivate::get(m_groups[i])->emitModelUpdated(reset); + + foreach (QQmlDelegateModelItem *cacheItem, m_cache) { + if (cacheItem->attached) + cacheItem->attached->emitChanges(); + } +} + +void QQmlDelegateModel::_q_modelReset() +{ + Q_D(QQmlDelegateModel); + if (!d->m_delegate) + return; + + int oldCount = d->m_count; + d->m_adaptorModel.rootIndex = QModelIndex(); + + if (d->m_complete) { + d->m_count = d->m_adaptorModel.count(); + + for (int i = 0, c = d->m_cache.count(); i < c; ++i) { + QQmlDelegateModelItem *item = d->m_cache.at(i); + if (item->modelIndex() != -1) + item->setModelIndex(-1); + } + + QVector removes; + QVector inserts; + if (oldCount) + d->m_compositor.listItemsRemoved(&d->m_adaptorModel, 0, oldCount, &removes); + if (d->m_count) + d->m_compositor.listItemsInserted(&d->m_adaptorModel, 0, d->m_count, &inserts); + d->itemsMoved(removes, inserts); + d->m_reset = true; + + if (d->m_adaptorModel.canFetchMore()) + d->m_adaptorModel.fetchMore(); + + d->emitChanges(); + } + emit rootIndexChanged(); +} + +void QQmlDelegateModel::_q_rowsInserted(const QModelIndex &parent, int begin, int end) +{ + Q_D(QQmlDelegateModel); + if (parent == d->m_adaptorModel.rootIndex) + _q_itemsInserted(begin, end - begin + 1); +} + +void QQmlDelegateModel::_q_rowsAboutToBeRemoved(const QModelIndex &parent, int begin, int end) +{ + Q_D(QQmlDelegateModel); + if (!d->m_adaptorModel.rootIndex.isValid()) + return; + const QModelIndex index = d->m_adaptorModel.rootIndex; + if (index.parent() == parent && index.row() >= begin && index.row() <= end) { + const int oldCount = d->m_count; + d->m_count = 0; + d->m_adaptorModel.invalidateModel(this); + + if (d->m_complete && oldCount > 0) { + QVector removes; + d->m_compositor.listItemsRemoved(&d->m_adaptorModel, 0, oldCount, &removes); + d->itemsRemoved(removes); + d->emitChanges(); + } + } +} + +void QQmlDelegateModel::_q_rowsRemoved(const QModelIndex &parent, int begin, int end) +{ + Q_D(QQmlDelegateModel); + if (parent == d->m_adaptorModel.rootIndex) + _q_itemsRemoved(begin, end - begin + 1); +} + +void QQmlDelegateModel::_q_rowsMoved( + const QModelIndex &sourceParent, int sourceStart, int sourceEnd, + const QModelIndex &destinationParent, int destinationRow) +{ + Q_D(QQmlDelegateModel); + const int count = sourceEnd - sourceStart + 1; + if (destinationParent == d->m_adaptorModel.rootIndex && sourceParent == d->m_adaptorModel.rootIndex) { + _q_itemsMoved(sourceStart, sourceStart > destinationRow ? destinationRow : destinationRow - count, count); + } else if (sourceParent == d->m_adaptorModel.rootIndex) { + _q_itemsRemoved(sourceStart, count); + } else if (destinationParent == d->m_adaptorModel.rootIndex) { + _q_itemsInserted(destinationRow, count); + } +} + +void QQmlDelegateModel::_q_dataChanged(const QModelIndex &begin, const QModelIndex &end, const QVector &roles) +{ + Q_D(QQmlDelegateModel); + if (begin.parent() == d->m_adaptorModel.rootIndex) + _q_itemsChanged(begin.row(), end.row() - begin.row() + 1, roles); +} + +void QQmlDelegateModel::_q_layoutChanged() +{ + Q_D(QQmlDelegateModel); + _q_itemsChanged(0, d->m_count, QVector()); +} + +QQmlDelegateModelAttached *QQmlDelegateModel::qmlAttachedProperties(QObject *obj) +{ + if (QQmlDelegateModelItem *cacheItem = QQmlDelegateModelItem::dataForObject(obj)) { + if (cacheItem->object == obj) { // Don't create attached item for child objects. + cacheItem->attached = new QQmlDelegateModelAttached(cacheItem, obj); + return cacheItem->attached; + } + } + return new QQmlDelegateModelAttached(obj); +} + +bool QQmlDelegateModelPrivate::insert( + Compositor::insert_iterator &before, const v8::Local &object, int groups) +{ + if (!m_context->isValid()) + return false; + + QQmlDelegateModelItem *cacheItem = m_adaptorModel.createItem(m_cacheMetaType, m_context->engine(), -1); + if (!cacheItem) + return false; + + v8::Local propertyNames = object->GetPropertyNames(); + for (uint i = 0; i < propertyNames->Length(); ++i) { + v8::Local propertyName = propertyNames->Get(i)->ToString(); + cacheItem->setValue( + m_cacheMetaType->v8Engine->toString(propertyName), + m_cacheMetaType->v8Engine->toVariant(object->Get(propertyName), QVariant::Invalid)); + } + + cacheItem->groups = groups | Compositor::UnresolvedFlag | Compositor::CacheFlag; + + // Must be before the new object is inserted into the cache or its indexes will be adjusted too. + itemsInserted(QVector() << Compositor::Insert(before, 1, cacheItem->groups & ~Compositor::CacheFlag)); + + before = m_compositor.insert(before, 0, 0, 1, cacheItem->groups); + m_cache.insert(before.cacheIndex, cacheItem); + + return true; +} + +//============================================================================ + +QQmlDelegateModelItemMetaType::QQmlDelegateModelItemMetaType( + QV8Engine *engine, QQmlDelegateModel *model, const QStringList &groupNames) + : model(model) + , groupCount(groupNames.count() + 1) + , v8Engine(engine) + , metaObject(0) + , groupNames(groupNames) +{ +} + +QQmlDelegateModelItemMetaType::~QQmlDelegateModelItemMetaType() +{ + if (metaObject) + metaObject->release(); + qPersistentDispose(constructor); +} + +void QQmlDelegateModelItemMetaType::initializeMetaObject() +{ + QMetaObjectBuilder builder; + builder.setFlags(QMetaObjectBuilder::DynamicMetaObject); + builder.setClassName(QQmlDelegateModelAttached::staticMetaObject.className()); + builder.setSuperClass(&QQmlDelegateModelAttached::staticMetaObject); + + int notifierId = 0; + for (int i = 0; i < groupNames.count(); ++i, ++notifierId) { + QString propertyName = QStringLiteral("in") + groupNames.at(i); + propertyName.replace(2, 1, propertyName.at(2).toUpper()); + builder.addSignal("__" + propertyName.toUtf8() + "Changed()"); + QMetaPropertyBuilder propertyBuilder = builder.addProperty( + propertyName.toUtf8(), "bool", notifierId); + propertyBuilder.setWritable(true); + } + for (int i = 0; i < groupNames.count(); ++i, ++notifierId) { + const QString propertyName = groupNames.at(i) + QStringLiteral("Index"); + builder.addSignal("__" + propertyName.toUtf8() + "Changed()"); + QMetaPropertyBuilder propertyBuilder = builder.addProperty( + propertyName.toUtf8(), "int", notifierId); + propertyBuilder.setWritable(true); + } + + metaObject = new QQmlDelegateModelAttachedMetaObject(this, builder.toMetaObject()); +} + +void QQmlDelegateModelItemMetaType::initializeConstructor() +{ + v8::HandleScope handleScope; + v8::Context::Scope contextScope(v8Engine->context()); + + QQmlDelegateModelEngineData *data = engineData(v8Engine); + + constructor = qPersistentNew(v8::ObjectTemplate::New()); + + constructor->SetHasExternalResource(true); + constructor->SetAccessor(data->model(), get_model); + constructor->SetAccessor(data->groups(), get_groups, set_groups); + constructor->SetAccessor(data->isUnresolved(), get_member, 0, v8::Int32::New(30)); + constructor->SetAccessor(data->inItems(), get_member, set_member, v8::Int32::New(1)); + constructor->SetAccessor(data->inPersistedItems(), get_member, set_member, v8::Int32::New(2)); + constructor->SetAccessor(data->itemsIndex(), get_index, 0, v8::Int32::New(1)); + constructor->SetAccessor(data->persistedItemsIndex(), get_index, 0, v8::Int32::New(2)); + + for (int i = 2; i < groupNames.count(); ++i) { + QString propertyName = QStringLiteral("in") + groupNames.at(i); + propertyName.replace(2, 1, propertyName.at(2).toUpper()); + constructor->SetAccessor( + v8Engine->toString(propertyName), get_member, set_member, v8::Int32::New(i + 1)); + } + for (int i = 2; i < groupNames.count(); ++i) { + const QString propertyName = groupNames.at(i) + QStringLiteral("Index"); + constructor->SetAccessor( + v8Engine->toString(propertyName), get_index, 0, v8::Int32::New(i + 1)); + } +} + +int QQmlDelegateModelItemMetaType::parseGroups(const QStringList &groups) const +{ + int groupFlags = 0; + foreach (const QString &groupName, groups) { + int index = groupNames.indexOf(groupName); + if (index != -1) + groupFlags |= 2 << index; + } + return groupFlags; +} + +int QQmlDelegateModelItemMetaType::parseGroups(const v8::Local &groups) const +{ + int groupFlags = 0; + if (groups->IsString()) { + const QString groupName = v8Engine->toString(groups); + int index = groupNames.indexOf(groupName); + if (index != -1) + groupFlags |= 2 << index; + } else if (groups->IsArray()) { + v8::Local array = v8::Local::Cast(groups); + for (uint i = 0; i < array->Length(); ++i) { + const QString groupName = v8Engine->toString(array->Get(i)); + int index = groupNames.indexOf(groupName); + if (index != -1) + groupFlags |= 2 << index; + } + } + return groupFlags; +} + +v8::Handle QQmlDelegateModelItemMetaType::get_model( + v8::Local, const v8::AccessorInfo &info) +{ + QQmlDelegateModelItem *cacheItem = v8_resource_cast(info.This()); + V8ASSERT_TYPE(cacheItem, "Not a valid VisualData object"); + if (!cacheItem->metaType->model) + return v8::Undefined(); + + return cacheItem->get(); +} + +v8::Handle QQmlDelegateModelItemMetaType::get_groups( + v8::Local, const v8::AccessorInfo &info) +{ + QQmlDelegateModelItem *cacheItem = v8_resource_cast(info.This()); + V8ASSERT_TYPE(cacheItem, "Not a valid VisualData object"); + + QStringList groups; + for (int i = 1; i < cacheItem->metaType->groupCount; ++i) { + if (cacheItem->groups & (1 << i)) + groups.append(cacheItem->metaType->groupNames.at(i - 1)); + } + + return cacheItem->engine->fromVariant(groups); +} + +void QQmlDelegateModelItemMetaType::set_groups( + v8::Local, v8::Local value, const v8::AccessorInfo &info) +{ + QQmlDelegateModelItem *cacheItem = v8_resource_cast(info.This()); + V8ASSERT_TYPE_SETTER(cacheItem, "Not a valid VisualData object"); + + if (!cacheItem->metaType->model) + return; + QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(cacheItem->metaType->model); + + const int groupFlags = model->m_cacheMetaType->parseGroups(value); + const int cacheIndex = model->m_cache.indexOf(cacheItem); + Compositor::iterator it = model->m_compositor.find(Compositor::Cache, cacheIndex); + model->setGroups(it, 1, Compositor::Cache, groupFlags); +} + +v8::Handle QQmlDelegateModelItemMetaType::get_member( + v8::Local, const v8::AccessorInfo &info) +{ + QQmlDelegateModelItem *cacheItem = v8_resource_cast(info.This()); + V8ASSERT_TYPE(cacheItem, "Not a valid VisualData object"); + + return v8::Boolean::New(cacheItem->groups & (1 << info.Data()->Int32Value())); +} + +void QQmlDelegateModelItemMetaType::set_member( + v8::Local, v8::Local value, const v8::AccessorInfo &info) +{ + QQmlDelegateModelItem *cacheItem = v8_resource_cast(info.This()); + V8ASSERT_TYPE_SETTER(cacheItem, "Not a valid VisualData object"); + + if (!cacheItem->metaType->model) + return; + QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(cacheItem->metaType->model); + + Compositor::Group group = Compositor::Group(info.Data()->Int32Value()); + const bool member = value->BooleanValue(); + const int groupFlag = (1 << group); + if (member == ((cacheItem->groups & groupFlag) != 0)) + return; + + const int cacheIndex = model->m_cache.indexOf(cacheItem); + Compositor::iterator it = model->m_compositor.find(Compositor::Cache, cacheIndex); + if (member) + model->addGroups(it, 1, Compositor::Cache, groupFlag); + else + model->removeGroups(it, 1, Compositor::Cache, groupFlag); +} + +v8::Handle QQmlDelegateModelItemMetaType::get_index( + v8::Local, const v8::AccessorInfo &info) +{ + QQmlDelegateModelItem *cacheItem = v8_resource_cast(info.This()); + V8ASSERT_TYPE(cacheItem, "Not a valid VisualData object"); + + return v8::Integer::New(cacheItem->groupIndex(Compositor::Group(info.Data()->Int32Value()))); +} + + +//--------------------------------------------------------------------------- + +QQmlDelegateModelItem::QQmlDelegateModelItem( + QQmlDelegateModelItemMetaType *metaType, int modelIndex) + : QV8ObjectResource(metaType->v8Engine) + , metaType(metaType) + , contextData(0) + , object(0) + , attached(0) + , incubationTask(0) + , objectRef(0) + , scriptRef(0) + , groups(0) + , index(modelIndex) +{ + metaType->addref(); +} + +QQmlDelegateModelItem::~QQmlDelegateModelItem() +{ + Q_ASSERT(scriptRef == 0); + Q_ASSERT(objectRef == 0); + Q_ASSERT(!object); + + if (incubationTask && metaType->model) + QQmlDelegateModelPrivate::get(metaType->model)->releaseIncubator(incubationTask); + + metaType->release(); + +} + +void QQmlDelegateModelItem::Dispose() +{ + --scriptRef; + if (isReferenced()) + return; + + if (metaType->model) { + QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(metaType->model); + model->removeCacheItem(this); + } + delete this; +} + +/* + This is essentially a copy of QQmlComponent::create(); except it takes the QQmlContextData + arguments instead of QQmlContext which means we don't have to construct the rather weighty + wrapper class for every delegate item. +*/ +void QQmlDelegateModelItem::incubateObject( + QQmlComponent *component, + QQmlEngine *engine, + QQmlContextData *context, + QQmlContextData *forContext) +{ + QQmlIncubatorPrivate *incubatorPriv = QQmlIncubatorPrivate::get(incubationTask); + QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(engine); + QQmlComponentPrivate *componentPriv = QQmlComponentPrivate::get(component); + + incubatorPriv->compiledData = componentPriv->cc; + incubatorPriv->compiledData->addref(); + incubatorPriv->vme.init( + context, + componentPriv->cc, + componentPriv->start, + componentPriv->creationContext); + + enginePriv->incubate(*incubationTask, forContext); +} + +void QQmlDelegateModelItem::destroyObject() +{ + Q_ASSERT(object); + Q_ASSERT(contextData); + + QObjectPrivate *p = QObjectPrivate::get(object); + Q_ASSERT(p->declarativeData); + QQmlData *data = static_cast(p->declarativeData); + if (data->ownContext && data->context) + data->context->clearContext(); + object->deleteLater(); + + if (attached) { + attached->m_cacheItem = 0; + attached = 0; + } + + contextData->destroy(); + contextData = 0; + object = 0; +} + +QQmlDelegateModelItem *QQmlDelegateModelItem::dataForObject(QObject *object) +{ + QObjectPrivate *p = QObjectPrivate::get(object); + QQmlContextData *context = p->declarativeData + ? static_cast(p->declarativeData)->context + : 0; + for (context = context ? context->parent : 0; context; context = context->parent) { + if (QQmlDelegateModelItem *cacheItem = qobject_cast( + context->contextObject)) { + return cacheItem; + } + } + return 0; +} + +int QQmlDelegateModelItem::groupIndex(Compositor::Group group) +{ + if (QQmlDelegateModelPrivate * const model = metaType->model + ? QQmlDelegateModelPrivate::get(metaType->model) + : 0) { + return model->m_compositor.find(Compositor::Cache, model->m_cache.indexOf(this)).index[group]; + } + return -1; +} + +//--------------------------------------------------------------------------- + +QQmlDelegateModelAttachedMetaObject::QQmlDelegateModelAttachedMetaObject( + QQmlDelegateModelItemMetaType *metaType, QMetaObject *metaObject) + : metaType(metaType) + , metaObject(metaObject) + , memberPropertyOffset(QQmlDelegateModelAttached::staticMetaObject.propertyCount()) + , indexPropertyOffset(QQmlDelegateModelAttached::staticMetaObject.propertyCount() + metaType->groupNames.count()) +{ + // Don't reference count the meta-type here as that would create a circular reference. + // Instead we rely the fact that the meta-type's reference count can't reach 0 without first + // destroying all delegates with attached objects. + *static_cast(this) = *metaObject; +} + +QQmlDelegateModelAttachedMetaObject::~QQmlDelegateModelAttachedMetaObject() +{ + ::free(metaObject); +} + +void QQmlDelegateModelAttachedMetaObject::objectDestroyed(QObject *) +{ + release(); +} + +int QQmlDelegateModelAttachedMetaObject::metaCall(QObject *object, QMetaObject::Call call, int _id, void **arguments) +{ + QQmlDelegateModelAttached *attached = static_cast(object); + if (call == QMetaObject::ReadProperty) { + if (_id >= indexPropertyOffset) { + Compositor::Group group = Compositor::Group(_id - indexPropertyOffset + 1); + *static_cast(arguments[0]) = attached->m_currentIndex[group]; + return -1; + } else if (_id >= memberPropertyOffset) { + Compositor::Group group = Compositor::Group(_id - memberPropertyOffset + 1); + *static_cast(arguments[0]) = attached->m_cacheItem->groups & (1 << group); + return -1; + } + } else if (call == QMetaObject::WriteProperty) { + if (_id >= memberPropertyOffset) { + if (!metaType->model) + return -1; + QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(metaType->model); + Compositor::Group group = Compositor::Group(_id - memberPropertyOffset + 1); + const int groupFlag = 1 << group; + const bool member = attached->m_cacheItem->groups & groupFlag; + if (member && !*static_cast(arguments[0])) { + Compositor::iterator it = model->m_compositor.find( + group, attached->m_currentIndex[group]); + model->removeGroups(it, 1, group, groupFlag); + } else if (!member && *static_cast(arguments[0])) { + for (int i = 1; i < metaType->groupCount; ++i) { + if (attached->m_cacheItem->groups & (1 << i)) { + Compositor::iterator it = model->m_compositor.find( + Compositor::Group(i), attached->m_currentIndex[i]); + model->addGroups(it, 1, Compositor::Group(i), groupFlag); + break; + } + } + } + return -1; + } + } + return attached->qt_metacall(call, _id, arguments); +} + +QQmlDelegateModelAttached::QQmlDelegateModelAttached(QObject *parent) + : m_cacheItem(0) + , m_previousGroups(0) +{ + QQml_setParent_noEvent(this, parent); +} + +QQmlDelegateModelAttached::QQmlDelegateModelAttached( + QQmlDelegateModelItem *cacheItem, QObject *parent) + : m_cacheItem(cacheItem) + , m_previousGroups(cacheItem->groups) +{ + QQml_setParent_noEvent(this, parent); + if (QQDMIncubationTask *incubationTask = m_cacheItem->incubationTask) { + for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i) + m_currentIndex[i] = m_previousIndex[i] = incubationTask->index[i]; + } else { + QQmlDelegateModelPrivate * const model = QQmlDelegateModelPrivate::get(m_cacheItem->metaType->model); + Compositor::iterator it = model->m_compositor.find( + Compositor::Cache, model->m_cache.indexOf(m_cacheItem)); + for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i) + m_currentIndex[i] = m_previousIndex[i] = it.index[i]; + } + + if (!cacheItem->metaType->metaObject) + cacheItem->metaType->initializeMetaObject(); + + QObjectPrivate::get(this)->metaObject = cacheItem->metaType->metaObject; + cacheItem->metaType->metaObject->addref(); +} + +/*! + \qmlattachedproperty int QtQuick2::VisualDataModel::model + + This attached property holds the visual data model this delegate instance belongs to. + + It is attached to each instance of the delegate. +*/ + +QQmlDelegateModel *QQmlDelegateModelAttached::model() const +{ + return m_cacheItem ? m_cacheItem->metaType->model : 0; +} + +/*! + \qmlattachedproperty stringlist QtQuick2::VisualDataModel::groups + + This attached property holds the name of VisualDataGroups the item belongs to. + + It is attached to each instance of the delegate. +*/ + +QStringList QQmlDelegateModelAttached::groups() const +{ + QStringList groups; + + if (!m_cacheItem) + return groups; + for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i) { + if (m_cacheItem->groups & (1 << i)) + groups.append(m_cacheItem->metaType->groupNames.at(i - 1)); + } + return groups; +} + +void QQmlDelegateModelAttached::setGroups(const QStringList &groups) +{ + if (!m_cacheItem) + return; + + QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_cacheItem->metaType->model); + + const int groupFlags = model->m_cacheMetaType->parseGroups(groups); + const int cacheIndex = model->m_cache.indexOf(m_cacheItem); + Compositor::iterator it = model->m_compositor.find(Compositor::Cache, cacheIndex); + model->setGroups(it, 1, Compositor::Cache, groupFlags); +} + +/*! + \qmlattachedproperty bool QtQuick2::VisualDataModel::isUnresolved + + This attached property holds whether the visual item is bound to a data model index. + Returns true if the item is not bound to the model, and false if it is. + + An unresolved item can be bound to the data model using the VisualDataGroup::resolve() + function. + + It is attached to each instance of the delegate. +*/ + +bool QQmlDelegateModelAttached::isUnresolved() const +{ + if (!m_cacheItem) + return false; + + return m_cacheItem->groups & Compositor::UnresolvedFlag; +} + +/*! + \qmlattachedproperty int QtQuick2::VisualDataModel::inItems + + This attached property holds whether the item belongs to the default \l items VisualDataGroup. + + Changing this property will add or remove the item from the items group. + + It is attached to each instance of the delegate. +*/ + +/*! + \qmlattachedproperty int QtQuick2::VisualDataModel::itemsIndex + + This attached property holds the index of the item in the default \l items VisualDataGroup. + + It is attached to each instance of the delegate. +*/ + +/*! + \qmlattachedproperty int QtQuick2::VisualDataModel::inPersistedItems + + This attached property holds whether the item belongs to the \l persistedItems VisualDataGroup. + + Changing this property will add or remove the item from the items group. Change with caution + as removing an item from the persistedItems group will destroy the current instance if it is + not referenced by a model. + + It is attached to each instance of the delegate. +*/ + +/*! + \qmlattachedproperty int QtQuick2::VisualDataModel::persistedItemsIndex + + This attached property holds the index of the item in the \l persistedItems VisualDataGroup. + + It is attached to each instance of the delegate. +*/ + +void QQmlDelegateModelAttached::emitChanges() +{ + const int groupChanges = m_previousGroups ^ m_cacheItem->groups; + m_previousGroups = m_cacheItem->groups; + + int indexChanges = 0; + for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i) { + if (m_previousIndex[i] != m_currentIndex[i]) { + m_previousIndex[i] = m_currentIndex[i]; + indexChanges |= (1 << i); + } + } + + int notifierId = 0; + const QMetaObject *meta = metaObject(); + for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i, ++notifierId) { + if (groupChanges & (1 << i)) + QMetaObject::activate(this, meta, notifierId, 0); + } + for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i, ++notifierId) { + if (indexChanges & (1 << i)) + QMetaObject::activate(this, meta, notifierId, 0); + } + + if (groupChanges) + emit groupsChanged(); +} + +//============================================================================ + +void QQmlDataGroupPrivate::setModel(QQmlDelegateModel *m, Compositor::Group g) +{ + Q_ASSERT(!model); + model = m; + group = g; +} + +bool QQmlDataGroupPrivate::isChangedConnected() +{ + Q_Q(QQmlDataGroup); + IS_SIGNAL_CONNECTED(q, QQmlDataGroup, changed, (const QQmlV8Handle &,const QQmlV8Handle &)); +} + +void QQmlDataGroupPrivate::emitChanges(QV8Engine *engine) +{ + Q_Q(QQmlDataGroup); + if (isChangedConnected() && !changeSet.isEmpty()) { + v8::HandleScope handleScope; + v8::Context::Scope contextScope(engine->context()); + v8::Local removed = engineData(engine)->array(engine, changeSet.removes()); + v8::Local inserted = engineData(engine)->array(engine, changeSet.inserts()); + emit q->changed(QQmlV8Handle::fromHandle(removed), QQmlV8Handle::fromHandle(inserted)); + } + if (changeSet.difference() != 0) + emit q->countChanged(); +} + +void QQmlDataGroupPrivate::emitModelUpdated(bool reset) +{ + for (QQmlDataGroupEmitterList::iterator it = emitters.begin(); it != emitters.end(); ++it) + it->emitModelUpdated(changeSet, reset); + changeSet.clear(); +} + +void QQmlDataGroupPrivate::createdPackage(int index, QQuickPackage *package) +{ + for (QQmlDataGroupEmitterList::iterator it = emitters.begin(); it != emitters.end(); ++it) + it->createdPackage(index, package); +} + +void QQmlDataGroupPrivate::initPackage(int index, QQuickPackage *package) +{ + for (QQmlDataGroupEmitterList::iterator it = emitters.begin(); it != emitters.end(); ++it) + it->initPackage(index, package); +} + +void QQmlDataGroupPrivate::destroyingPackage(QQuickPackage *package) +{ + for (QQmlDataGroupEmitterList::iterator it = emitters.begin(); it != emitters.end(); ++it) + it->destroyingPackage(package); +} + +/*! + \qmltype VisualDataGroup + \instantiates QQmlDataGroup + \inqmlmodule QtQuick 2 + \ingroup qtquick-models + \brief Encapsulates a filtered set of visual data items + + The VisualDataGroup type provides a means to address the model data of a VisualDataModel's + delegate items, as well as sort and filter these delegate items. + + The initial set of instantiable delegate items in a VisualDataModel is represented + by its \l {QtQuick2::VisualDataModel::items}{items} group, which normally directly reflects + the contents of the model assigned to VisualDataModel::model. This set can be changed to + the contents of any other member of VisualDataModel::groups by assigning the \l name of that + VisualDataGroup to the VisualDataModel::filterOnGroup property. + + The data of an item in a VisualDataGroup can be accessed using the get() function, which returns + information about group membership and indexes as well as model data. In combination + with the move() function this can be used to implement view sorting, with remove() to filter + items out of a view, or with setGroups() and \l Package delegates to categorize items into + different views. + + Data from models can be supplemented by inserting data directly into a VisualDataGroup + with the insert() function. This can be used to introduce mock items into a view, or + placeholder items that are later \l {resolve()}{resolved} to real model data when it becomes + available. + + Delegate items can also be be instantiated directly from a VisualDataGroup using the + create() function, making it possible to use VisualDataModel without an accompanying view + type or to cherry-pick specific items that should be instantiated irregardless of whether + they're currently within a view's visible area. + + \sa {QML Dynamic View Ordering Tutorial} +*/ + +QQmlDataGroup::QQmlDataGroup(QObject *parent) + : QObject(*new QQmlDataGroupPrivate, parent) +{ +} + +QQmlDataGroup::QQmlDataGroup( + const QString &name, QQmlDelegateModel *model, int index, QObject *parent) + : QObject(*new QQmlDataGroupPrivate, parent) +{ + Q_D(QQmlDataGroup); + d->name = name; + d->setModel(model, Compositor::Group(index)); +} + +QQmlDataGroup::~QQmlDataGroup() +{ +} + +/*! + \qmlproperty string QtQuick2::VisualDataGroup::name + + This property holds the name of the group. + + Each group in a model must have a unique name starting with a lower case letter. +*/ + +QString QQmlDataGroup::name() const +{ + Q_D(const QQmlDataGroup); + return d->name; +} + +void QQmlDataGroup::setName(const QString &name) +{ + Q_D(QQmlDataGroup); + if (d->model) + return; + if (d->name != name) { + d->name = name; + emit nameChanged(); + } +} + +/*! + \qmlproperty int QtQuick2::VisualDataGroup::count + + This property holds the number of items in the group. +*/ + +int QQmlDataGroup::count() const +{ + Q_D(const QQmlDataGroup); + if (!d->model) + return 0; + return QQmlDelegateModelPrivate::get(d->model)->m_compositor.count(d->group); +} + +/*! + \qmlproperty bool QtQuick2::VisualDataGroup::includeByDefault + + This property holds whether new items are assigned to this group by default. +*/ + +bool QQmlDataGroup::defaultInclude() const +{ + Q_D(const QQmlDataGroup); + return d->defaultInclude; +} + +void QQmlDataGroup::setDefaultInclude(bool include) +{ + Q_D(QQmlDataGroup); + if (d->defaultInclude != include) { + d->defaultInclude = include; + + if (d->model) { + if (include) + QQmlDelegateModelPrivate::get(d->model)->m_compositor.setDefaultGroup(d->group); + else + QQmlDelegateModelPrivate::get(d->model)->m_compositor.clearDefaultGroup(d->group); + } + emit defaultIncludeChanged(); + } +} + +/*! + \qmlmethod object QtQuick2::VisualDataGroup::get(int index) + + Returns a javascript object describing the item at \a index in the group. + + The returned object contains the same information that is available to a delegate from the + VisualDataModel attached as well as the model for that item. It has the properties: + + \list + \li \b model The model data of the item. This is the same as the model context property in + a delegate + \li \b groups A list the of names of groups the item is a member of. This property can be + written to change the item's membership. + \li \b inItems Whether the item belongs to the \l {QtQuick2::VisualDataModel::items}{items} group. + Writing to this property will add or remove the item from the group. + \li \b itemsIndex The index of the item within the \l {QtQuick2::VisualDataModel::items}{items} group. + \li \b {in} Whether the item belongs to the dynamic group \e groupName. Writing to + this property will add or remove the item from the group. + \li \b {Index} The index of the item within the dynamic group \e groupName. + \li \b isUnresolved Whether the item is bound to an index in the model assigned to + VisualDataModel::model. Returns true if the item is not bound to the model, and false if it is. + \endlist +*/ + +QQmlV8Handle QQmlDataGroup::get(int index) +{ + Q_D(QQmlDataGroup); + if (!d->model) + return QQmlV8Handle::fromHandle(v8::Undefined());; + + QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); + if (!model->m_context->isValid()) { + return QQmlV8Handle::fromHandle(v8::Undefined()); + } else if (index < 0 || index >= model->m_compositor.count(d->group)) { + qmlInfo(this) << tr("get: index out of range"); + return QQmlV8Handle::fromHandle(v8::Undefined()); + } + + Compositor::iterator it = model->m_compositor.find(d->group, index); + QQmlDelegateModelItem *cacheItem = it->inCache() + ? model->m_cache.at(it.cacheIndex) + : 0; + + if (!cacheItem) { + cacheItem = model->m_adaptorModel.createItem( + model->m_cacheMetaType, model->m_context->engine(), it.modelIndex()); + if (!cacheItem) + return QQmlV8Handle::fromHandle(v8::Undefined()); + cacheItem->groups = it->flags; + + model->m_cache.insert(it.cacheIndex, cacheItem); + model->m_compositor.setFlags(it, 1, Compositor::CacheFlag); + } + + if (model->m_cacheMetaType->constructor.IsEmpty()) + model->m_cacheMetaType->initializeConstructor(); + v8::Local handle = model->m_cacheMetaType->constructor->NewInstance(); + handle->SetExternalResource(cacheItem); + ++cacheItem->scriptRef; + + return QQmlV8Handle::fromHandle(handle); +} + +bool QQmlDataGroupPrivate::parseIndex( + const v8::Local &value, int *index, Compositor::Group *group) const +{ + if (value->IsInt32()) { + *index = value->Int32Value(); + return true; + } else if (value->IsObject()) { + v8::Local object = value->ToObject(); + QQmlDelegateModelItem * const cacheItem = v8_resource_cast(object); + if (QQmlDelegateModelPrivate *model = cacheItem && cacheItem->metaType->model + ? QQmlDelegateModelPrivate::get(cacheItem->metaType->model) + : 0) { + *index = model->m_cache.indexOf(cacheItem); + *group = Compositor::Cache; + return true; + } + } + return false; +} + +/*! + \qmlmethod QtQuick2::VisualDataGroup::insert(int index, jsdict data, array groups = undefined) + \qmlmethod QtQuick2::VisualDataGroup::insert(jsdict data, var groups = undefined) + + Creates a new entry at \a index in a VisualDataModel with the values from \a data that + correspond to roles in the model assigned to VisualDataModel::model. + + If no index is supplied the data is appended to the model. + + The optional \a groups parameter identifies the groups the new entry should belong to, + if unspecified this is equal to the group insert was called on. + + Data inserted into a VisualDataModel can later be merged with an existing entry in + VisualDataModel::model using the \l resolve() function. This can be used to create placeholder + items that are later replaced by actual data. +*/ + +void QQmlDataGroup::insert(QQmlV8Function *args) +{ + Q_D(QQmlDataGroup); + QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); + + int index = model->m_compositor.count(d->group); + Compositor::Group group = d->group; + + if (args->Length() == 0) + return; + + int i = 0; + v8::Local v = (*args)[i]; + if (d->parseIndex(v, &index, &group)) { + if (index < 0 || index > model->m_compositor.count(group)) { + qmlInfo(this) << tr("insert: index out of range"); + return; + } + if (++i == args->Length()) + return; + v = (*args)[i]; + } + + Compositor::insert_iterator before = index < model->m_compositor.count(group) + ? model->m_compositor.findInsertPosition(group, index) + : model->m_compositor.end(); + + int groups = 1 << d->group; + if (++i < args->Length()) + groups |= model->m_cacheMetaType->parseGroups((*args)[i]); + + if (v->IsArray()) { + return; + } else if (v->IsObject()) { + model->insert(before, v->ToObject(), groups); + model->emitChanges(); + } +} + +/*! + \qmlmethod QtQuick2::VisualDataGroup::create(int index) + \qmlmethod QtQuick2::VisualDataGroup::create(int index, jsdict data, array groups = undefined) + \qmlmethod QtQuick2::VisualDataGroup::create(jsdict data, array groups = undefined) + + Returns a reference to the instantiated item at \a index in the group. + + If a \a data object is provided it will be \l {insert}{inserted} at \a index and an item + referencing this new entry will be returned. The optional \a groups parameter identifies + the groups the new entry should belong to, if unspecified this is equal to the group create() + was called on. + + All items returned by create are added to the + \l {QtQuick2::VisualDataModel::persistedItems}{persistedItems} group. Items in this + group remain instantiated when not referenced by any view. +*/ + +void QQmlDataGroup::create(QQmlV8Function *args) +{ + Q_D(QQmlDataGroup); + if (!d->model) + return; + + if (args->Length() == 0) + return; + + QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); + + int index = model->m_compositor.count(d->group); + Compositor::Group group = d->group; + + int i = 0; + v8::Local v = (*args)[i]; + if (d->parseIndex(v, &index, &group)) + ++i; + + if (i < args->Length() && index >= 0 && index <= model->m_compositor.count(group)) { + v = (*args)[i]; + if (v->IsObject()) { + int groups = 1 << d->group; + if (++i < args->Length()) + groups |= model->m_cacheMetaType->parseGroups((*args)[i]); + + Compositor::insert_iterator before = index < model->m_compositor.count(group) + ? model->m_compositor.findInsertPosition(group, index) + : model->m_compositor.end(); + + index = before.index[d->group]; + group = d->group; + + if (!model->insert(before, v->ToObject(), groups)) { + return; + } + } + } + if (index < 0 || index >= model->m_compositor.count(group)) { + qmlInfo(this) << tr("create: index out of range"); + return; + } + + QObject *object = model->object(group, index, false); + if (object) { + QVector inserts; + Compositor::iterator it = model->m_compositor.find(group, index); + model->m_compositor.setFlags(it, 1, d->group, Compositor::PersistedFlag, &inserts); + model->itemsInserted(inserts); + model->m_cache.at(it.cacheIndex)->releaseObject(); + } + + args->returnValue(args->engine()->newQObject(object)); + model->emitChanges(); +} + +/*! + \qmlmethod QtQuick2::VisualDataGroup::resolve(int from, int to) + + Binds an unresolved item at \a from to an item in VisualDataModel::model at index \a to. + + Unresolved items are entries whose data has been \l {insert()}{inserted} into a VisualDataGroup + instead of being derived from a VisualDataModel::model index. Resolving an item will replace + the item at the target index with the unresolved item. A resolved an item will reflect the data + of the source model at its bound index and will move when that index moves like any other item. + + If a new item is replaced in the VisualDataGroup onChanged() handler its insertion and + replacement will be communicated to views as an atomic operation, creating the appearance + that the model contents have not changed, or if the unresolved and model item are not adjacent + that the previously unresolved item has simply moved. + +*/ +void QQmlDataGroup::resolve(QQmlV8Function *args) +{ + Q_D(QQmlDataGroup); + if (!d->model) + return; + + QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); + + if (args->Length() < 2) + return; + + int from = -1; + int to = -1; + Compositor::Group fromGroup = d->group; + Compositor::Group toGroup = d->group; + + v8::Local v = (*args)[0]; + if (d->parseIndex(v, &from, &fromGroup)) { + if (from < 0 || from >= model->m_compositor.count(fromGroup)) { + qmlInfo(this) << tr("resolve: from index out of range"); + return; + } + } else { + qmlInfo(this) << tr("resolve: from index invalid"); + return; + } + + v = (*args)[1]; + if (d->parseIndex(v, &to, &toGroup)) { + if (to < 0 || to >= model->m_compositor.count(toGroup)) { + qmlInfo(this) << tr("resolve: to index out of range"); + return; + } + } else { + qmlInfo(this) << tr("resolve: to index invalid"); + return; + } + + Compositor::iterator fromIt = model->m_compositor.find(fromGroup, from); + Compositor::iterator toIt = model->m_compositor.find(toGroup, to); + + if (!fromIt->isUnresolved()) { + qmlInfo(this) << tr("resolve: from is not an unresolved item"); + return; + } + if (!toIt->list) { + qmlInfo(this) << tr("resolve: to is not a model item"); + return; + } + + const int unresolvedFlags = fromIt->flags; + const int resolvedFlags = toIt->flags; + const int resolvedIndex = toIt.modelIndex(); + void * const resolvedList = toIt->list; + + QQmlDelegateModelItem *cacheItem = model->m_cache.at(fromIt.cacheIndex); + cacheItem->groups &= ~Compositor::UnresolvedFlag; + + if (toIt.cacheIndex > fromIt.cacheIndex) + toIt.decrementIndexes(1, unresolvedFlags); + if (!toIt->inGroup(fromGroup) || toIt.index[fromGroup] > from) + from += 1; + + model->itemsMoved( + QVector() << Compositor::Remove(fromIt, 1, unresolvedFlags, 0), + QVector() << Compositor::Insert(toIt, 1, unresolvedFlags, 0)); + model->itemsInserted( + QVector() << Compositor::Insert(toIt, 1, (resolvedFlags & ~unresolvedFlags) | Compositor::CacheFlag)); + toIt.incrementIndexes(1, resolvedFlags | unresolvedFlags); + model->itemsRemoved(QVector() << Compositor::Remove(toIt, 1, resolvedFlags)); + + model->m_compositor.setFlags(toGroup, to, 1, unresolvedFlags & ~Compositor::UnresolvedFlag); + model->m_compositor.clearFlags(fromGroup, from, 1, unresolvedFlags); + + if (resolvedFlags & Compositor::CacheFlag) + model->m_compositor.insert(Compositor::Cache, toIt.cacheIndex, resolvedList, resolvedIndex, 1, Compositor::CacheFlag); + + Q_ASSERT(model->m_cache.count() == model->m_compositor.count(Compositor::Cache)); + + if (!cacheItem->isReferenced()) { + Q_ASSERT(toIt.cacheIndex == model->m_cache.indexOf(cacheItem)); + model->m_cache.removeAt(toIt.cacheIndex); + model->m_compositor.clearFlags(Compositor::Cache, toIt.cacheIndex, 1, Compositor::CacheFlag); + delete cacheItem; + Q_ASSERT(model->m_cache.count() == model->m_compositor.count(Compositor::Cache)); + } else { + cacheItem->resolveIndex(model->m_adaptorModel, resolvedIndex); + if (cacheItem->attached) + cacheItem->attached->emitUnresolvedChanged(); + } + + model->emitChanges(); +} + +/*! + \qmlmethod QtQuick2::VisualDataGroup::remove(int index, int count) + + Removes \a count items starting at \a index from the group. +*/ + +void QQmlDataGroup::remove(QQmlV8Function *args) +{ + Q_D(QQmlDataGroup); + if (!d->model) + return; + Compositor::Group group = d->group; + int index = -1; + int count = 1; + + if (args->Length() == 0) + return; + + int i = 0; + v8::Local v = (*args)[i]; + if (!d->parseIndex(v, &index, &group)) { + qmlInfo(this) << tr("remove: invalid index"); + return; + } + + if (++i < args->Length()) { + v = (*args)[i]; + if (v->IsInt32()) + count = v->Int32Value(); + } + + QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); + if (index < 0 || index >= model->m_compositor.count(group)) { + qmlInfo(this) << tr("remove: index out of range"); + } else if (count != 0) { + Compositor::iterator it = model->m_compositor.find(group, index); + if (count < 0 || count > model->m_compositor.count(d->group) - it.index[d->group]) { + qmlInfo(this) << tr("remove: invalid count"); + } else { + model->removeGroups(it, count, d->group, 1 << d->group); + } + } +} + +bool QQmlDataGroupPrivate::parseGroupArgs( + QQmlV8Function *args, Compositor::Group *group, int *index, int *count, int *groups) const +{ + if (!model || !QQmlDelegateModelPrivate::get(model)->m_cacheMetaType) + return false; + + if (args->Length() < 2) + return false; + + int i = 0; + v8::Local v = (*args)[i]; + if (!parseIndex(v, index, group)) + return false; + + v = (*args)[++i]; + if (v->IsInt32()) { + *count = v->Int32Value(); + + if (++i == args->Length()) + return false; + v = (*args)[i]; + } + + *groups = QQmlDelegateModelPrivate::get(model)->m_cacheMetaType->parseGroups(v); + + return true; +} + +/*! + \qmlmethod QtQuick2::VisualDataGroup::addGroups(int index, int count, stringlist groups) + + Adds \a count items starting at \a index to \a groups. +*/ + +void QQmlDataGroup::addGroups(QQmlV8Function *args) +{ + Q_D(QQmlDataGroup); + Compositor::Group group = d->group; + int index = -1; + int count = 1; + int groups = 0; + + if (!d->parseGroupArgs(args, &group, &index, &count, &groups)) + return; + + QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); + if (index < 0 || index >= model->m_compositor.count(group)) { + qmlInfo(this) << tr("addGroups: index out of range"); + } else if (count != 0) { + Compositor::iterator it = model->m_compositor.find(group, index); + if (count < 0 || count > model->m_compositor.count(d->group) - it.index[d->group]) { + qmlInfo(this) << tr("addGroups: invalid count"); + } else { + model->addGroups(it, count, d->group, groups); + } + } +} + +/*! + \qmlmethod QtQuick2::VisualDataGroup::removeGroups(int index, int count, stringlist groups) + + Removes \a count items starting at \a index from \a groups. +*/ + +void QQmlDataGroup::removeGroups(QQmlV8Function *args) +{ + Q_D(QQmlDataGroup); + Compositor::Group group = d->group; + int index = -1; + int count = 1; + int groups = 0; + + if (!d->parseGroupArgs(args, &group, &index, &count, &groups)) + return; + + QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); + if (index < 0 || index >= model->m_compositor.count(group)) { + qmlInfo(this) << tr("removeGroups: index out of range"); + } else if (count != 0) { + Compositor::iterator it = model->m_compositor.find(group, index); + if (count < 0 || count > model->m_compositor.count(d->group) - it.index[d->group]) { + qmlInfo(this) << tr("removeGroups: invalid count"); + } else { + model->removeGroups(it, count, d->group, groups); + } + } +} + +/*! + \qmlmethod QtQuick2::VisualDataGroup::setGroups(int index, int count, stringlist groups) + + Sets the \a groups \a count items starting at \a index belong to. +*/ + +void QQmlDataGroup::setGroups(QQmlV8Function *args) +{ + Q_D(QQmlDataGroup); + Compositor::Group group = d->group; + int index = -1; + int count = 1; + int groups = 0; + + if (!d->parseGroupArgs(args, &group, &index, &count, &groups)) + return; + + QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); + if (index < 0 || index >= model->m_compositor.count(group)) { + qmlInfo(this) << tr("setGroups: index out of range"); + } else if (count != 0) { + Compositor::iterator it = model->m_compositor.find(group, index); + if (count < 0 || count > model->m_compositor.count(d->group) - it.index[d->group]) { + qmlInfo(this) << tr("setGroups: invalid count"); + } else { + model->setGroups(it, count, d->group, groups); + } + } +} + +/*! + \qmlmethod QtQuick2::VisualDataGroup::setGroups(int index, int count, stringlist groups) + + Sets the \a groups \a count items starting at \a index belong to. +*/ + +/*! + \qmlmethod QtQuick2::VisualDataGroup::move(var from, var to, int count) + + Moves \a count at \a from in a group \a to a new position. +*/ + +void QQmlDataGroup::move(QQmlV8Function *args) +{ + Q_D(QQmlDataGroup); + + if (args->Length() < 2) + return; + + Compositor::Group fromGroup = d->group; + Compositor::Group toGroup = d->group; + int from = -1; + int to = -1; + int count = 1; + + if (!d->parseIndex((*args)[0], &from, &fromGroup)) { + qmlInfo(this) << tr("move: invalid from index"); + return; + } + + if (!d->parseIndex((*args)[1], &to, &toGroup)) { + qmlInfo(this) << tr("move: invalid to index"); + return; + } + + if (args->Length() > 2) { + v8::Local v = (*args)[2]; + if (v->IsInt32()) + count = v->Int32Value(); + } + + QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); + + if (count < 0) { + qmlInfo(this) << tr("move: invalid count"); + } else if (from < 0 || from + count > model->m_compositor.count(fromGroup)) { + qmlInfo(this) << tr("move: from index out of range"); + } else if (!model->m_compositor.verifyMoveTo(fromGroup, from, toGroup, to, count, d->group)) { + qmlInfo(this) << tr("move: to index out of range"); + } else if (count > 0) { + QVector removes; + QVector inserts; + + model->m_compositor.move(fromGroup, from, toGroup, to, count, d->group, &removes, &inserts); + model->itemsMoved(removes, inserts); + model->emitChanges(); + } + +} + +/*! + \qmlsignal QtQuick2::VisualDataGroup::onChanged(array removed, array inserted) + + This handler is called when items have been removed from or inserted into the group. + + Each object in the \a removed and \a inserted arrays has two values; the \e index of the first + item inserted or removed and a \e count of the number of consecutive items inserted or removed. + + Each index is adjusted for previous changes with all removed items preceding any inserted + items. +*/ + +//============================================================================ + +QQmlPartsModel::QQmlPartsModel(QQmlDelegateModel *model, const QString &part, QObject *parent) + : QQmlInstanceModel(*new QObjectPrivate, parent) + , m_model(model) + , m_part(part) + , m_compositorGroup(Compositor::Cache) + , m_inheritGroup(true) +{ + QQmlDelegateModelPrivate *d = QQmlDelegateModelPrivate::get(m_model); + if (d->m_cacheMetaType) { + QQmlDataGroupPrivate::get(d->m_groups[1])->emitters.insert(this); + m_compositorGroup = Compositor::Default; + } else { + d->m_pendingParts.insert(this); + } +} + +QQmlPartsModel::~QQmlPartsModel() +{ +} + +QString QQmlPartsModel::filterGroup() const +{ + if (m_inheritGroup) + return m_model->filterGroup(); + return m_filterGroup; +} + +void QQmlPartsModel::setFilterGroup(const QString &group) +{ + if (QQmlDelegateModelPrivate::get(m_model)->m_transaction) { + qmlInfo(this) << tr("The group of a VisualDataModel cannot be changed within onChanged"); + return; + } + + if (m_filterGroup != group || m_inheritGroup) { + m_filterGroup = group; + m_inheritGroup = false; + updateFilterGroup(); + + emit filterGroupChanged(); + } +} + +void QQmlPartsModel::resetFilterGroup() +{ + if (!m_inheritGroup) { + m_inheritGroup = true; + updateFilterGroup(); + emit filterGroupChanged(); + } +} + +void QQmlPartsModel::updateFilterGroup() +{ + QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_model); + if (!model->m_cacheMetaType) + return; + + if (m_inheritGroup) { + if (m_filterGroup == model->m_filterGroup) + return; + m_filterGroup = model->m_filterGroup; + } + + QQmlListCompositor::Group previousGroup = m_compositorGroup; + m_compositorGroup = Compositor::Default; + QQmlDataGroupPrivate::get(model->m_groups[Compositor::Default])->emitters.insert(this); + for (int i = 1; i < model->m_groupCount; ++i) { + if (m_filterGroup == model->m_cacheMetaType->groupNames.at(i - 1)) { + m_compositorGroup = Compositor::Group(i); + break; + } + } + + QQmlDataGroupPrivate::get(model->m_groups[m_compositorGroup])->emitters.insert(this); + if (m_compositorGroup != previousGroup) { + QVector removes; + QVector inserts; + model->m_compositor.transition(previousGroup, m_compositorGroup, &removes, &inserts); + + QQmlChangeSet changeSet; + changeSet.move(removes, inserts); + if (!changeSet.isEmpty()) + emit modelUpdated(changeSet, false); + + if (changeSet.difference() != 0) + emit countChanged(); + } +} + +void QQmlPartsModel::updateFilterGroup( + Compositor::Group group, const QQmlChangeSet &changeSet) +{ + if (!m_inheritGroup) + return; + + m_compositorGroup = group; + QQmlDataGroupPrivate::get(QQmlDelegateModelPrivate::get(m_model)->m_groups[m_compositorGroup])->emitters.insert(this); + + if (!changeSet.isEmpty()) + emit modelUpdated(changeSet, false); + + if (changeSet.difference() != 0) + emit countChanged(); + + emit filterGroupChanged(); +} + +int QQmlPartsModel::count() const +{ + QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_model); + return model->m_delegate + ? model->m_compositor.count(m_compositorGroup) + : 0; +} + +bool QQmlPartsModel::isValid() const +{ + return m_model->isValid(); +} + +QObject *QQmlPartsModel::object(int index, bool asynchronous) +{ + QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_model); + + if (!model->m_delegate || index < 0 || index >= model->m_compositor.count(m_compositorGroup)) { + qWarning() << "VisualDataModel::item: index out range" << index << model->m_compositor.count(m_compositorGroup); + return 0; + } + + QObject *object = model->object(m_compositorGroup, index, asynchronous); + + if (QQuickPackage *package = qmlobject_cast(object)) { + QObject *part = package->part(m_part); + if (!part) + return 0; + m_packaged.insertMulti(part, package); + return part; + } + + model->release(object); + if (!model->m_delegateValidated) { + if (object) + qmlInfo(model->m_delegate) << tr("Delegate component must be Package type."); + model->m_delegateValidated = true; + } + + return 0; +} + +QQmlInstanceModel::ReleaseFlags QQmlPartsModel::release(QObject *item) +{ + QQmlInstanceModel::ReleaseFlags flags = 0; + + QHash::iterator it = m_packaged.find(item); + if (it != m_packaged.end()) { + QQuickPackage *package = *it; + QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_model); + flags = model->release(package); + m_packaged.erase(it); + if (!m_packaged.contains(item)) + flags &= ~Referenced; + if (flags & Destroyed) + QQmlDelegateModelPrivate::get(m_model)->emitDestroyingPackage(package); + } + return flags; +} + +QString QQmlPartsModel::stringValue(int index, const QString &role) +{ + return QQmlDelegateModelPrivate::get(m_model)->stringValue(m_compositorGroup, index, role); +} + +void QQmlPartsModel::setWatchedRoles(QList roles) +{ + QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_model); + model->m_adaptorModel.replaceWatchedRoles(m_watchedRoles, roles); + m_watchedRoles = roles; +} + +int QQmlPartsModel::indexOf(QObject *item, QObject *) const +{ + QHash::const_iterator it = m_packaged.find(item); + if (it != m_packaged.end()) { + if (QQmlDelegateModelItem *cacheItem = QQmlDelegateModelItem::dataForObject(*it)) + return cacheItem->groupIndex(m_compositorGroup); + } + return -1; +} + +void QQmlPartsModel::createdPackage(int index, QQuickPackage *package) +{ + emit createdItem(index, package->part(m_part)); +} + +void QQmlPartsModel::initPackage(int index, QQuickPackage *package) +{ + emit initItem(index, package->part(m_part)); +} + +void QQmlPartsModel::destroyingPackage(QQuickPackage *package) +{ + QObject *item = package->part(m_part); + Q_ASSERT(!m_packaged.contains(item)); + emit destroyingItem(item); +} + +void QQmlPartsModel::emitModelUpdated(const QQmlChangeSet &changeSet, bool reset) +{ + emit modelUpdated(changeSet, reset); + if (changeSet.difference() != 0) + emit countChanged(); +} + +//============================================================================ + +v8::Handle get_change_index(v8::Local, const v8::AccessorInfo &info) +{ + return info.This()->GetInternalField(0); +} + +v8::Handle get_change_count(v8::Local, const v8::AccessorInfo &info) +{ + return info.This()->GetInternalField(1); +} + +v8::Handle get_change_moveId(v8::Local, const v8::AccessorInfo &info) +{ + return info.This()->GetInternalField(2); +} + +class QQmlDataGroupChangeArray : public QV8ObjectResource +{ + V8_RESOURCE_TYPE(ChangeSetArrayType) +public: + QQmlDataGroupChangeArray(QV8Engine *engine) + : QV8ObjectResource(engine) + { + } + + virtual quint32 count() const = 0; + virtual const QQmlChangeSet::Change &at(int index) const = 0; + + static v8::Handle get_change(quint32 index, const v8::AccessorInfo &info) + { + QQmlDataGroupChangeArray *array = v8_resource_cast(info.This()); + V8ASSERT_TYPE(array, "Not a valid change array"); + + if (index >= array->count()) + return v8::Undefined(); + + const QQmlChangeSet::Change &change = array->at(index); + + v8::Local object = engineData(array->engine)->constructorChange->NewInstance(); + object->SetInternalField(0, v8::Int32::New(change.index)); + object->SetInternalField(1, v8::Int32::New(change.count)); + if (change.isMove()) + object->SetInternalField(2, v8::Int32::New(change.moveId)); + + return object; + } + + static v8::Handle get_length(v8::Local, const v8::AccessorInfo &info) + { + QQmlDataGroupChangeArray *array = v8_resource_cast(info.This()); + V8ASSERT_TYPE(array, "Not a valid change array"); + + return v8::Integer::New(array->count()); + } + + static v8::Local constructor() + { + v8::Local changeArray = v8::FunctionTemplate::New(); + changeArray->InstanceTemplate()->SetHasExternalResource(true); + changeArray->InstanceTemplate()->SetIndexedPropertyHandler(get_change); + changeArray->InstanceTemplate()->SetAccessor(v8::String::New("length"), get_length); + return changeArray->GetFunction(); + } +}; + +class QQmlDataGroupRemoveArray : public QQmlDataGroupChangeArray +{ +public: + QQmlDataGroupRemoveArray(QV8Engine *engine, const QVector &changes) + : QQmlDataGroupChangeArray(engine) + , changes(changes) + { + } + + quint32 count() const { return changes.count(); } + const QQmlChangeSet::Change &at(int index) const { return changes.at(index); } + +private: + QVector changes; +}; + +class QQmlDataGroupInsertArray : public QQmlDataGroupChangeArray +{ +public: + QQmlDataGroupInsertArray(QV8Engine *engine, const QVector &changes) + : QQmlDataGroupChangeArray(engine) + , changes(changes) + { + } + + quint32 count() const { return changes.count(); } + const QQmlChangeSet::Change &at(int index) const { return changes.at(index); } + +private: + QVector changes; +}; + +QQmlDelegateModelEngineData::QQmlDelegateModelEngineData(QV8Engine *) +{ + strings = qPersistentNew(v8::Array::New(StringCount)); + strings->Set(Model, v8::String::New("model")); + strings->Set(Groups, v8::String::New("groups")); + strings->Set(IsUnresolved, v8::String::New("isUnresolved")); + strings->Set(ItemsIndex, v8::String::New("itemsIndex")); + strings->Set(PersistedItemsIndex, v8::String::New("persistedItemsIndex")); + strings->Set(InItems, v8::String::New("inItems")); + strings->Set(InPersistedItems, v8::String::New("inPersistedItems")); + + v8::Local change = v8::FunctionTemplate::New(); + change->InstanceTemplate()->SetAccessor(v8::String::New("index"), get_change_index); + change->InstanceTemplate()->SetAccessor(v8::String::New("count"), get_change_count); + change->InstanceTemplate()->SetAccessor(v8::String::New("moveId"), get_change_moveId); + change->InstanceTemplate()->SetInternalFieldCount(3); + constructorChange = qPersistentNew(change->GetFunction()); + constructorChangeArray = qPersistentNew(QQmlDataGroupChangeArray::constructor()); +} + +QQmlDelegateModelEngineData::~QQmlDelegateModelEngineData() +{ + qPersistentDispose(strings); + qPersistentDispose(constructorChange); + qPersistentDispose(constructorChangeArray); +} + +v8::Local QQmlDelegateModelEngineData::array( + QV8Engine *engine, const QVector &changes) +{ + v8::Local array = constructorChangeArray->NewInstance(); + array->SetExternalResource(new QQmlDataGroupRemoveArray(engine, changes)); + return array; +} + +v8::Local QQmlDelegateModelEngineData::array( + QV8Engine *engine, const QVector &changes) +{ + v8::Local array = constructorChangeArray->NewInstance(); + array->SetExternalResource(new QQmlDataGroupInsertArray(engine, changes)); + return array; +} + +QT_END_NAMESPACE + diff --git a/src/qml/items/qqmldelegatemodel_p.h b/src/qml/items/qqmldelegatemodel_p.h new file mode 100644 index 0000000000..20cc228043 --- /dev/null +++ b/src/qml/items/qqmldelegatemodel_p.h @@ -0,0 +1,238 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLDATAMODEL_P_H +#define QQMLDATAMODEL_P_H + +#include +#include +#include + +#include +#include + +#include +#include + +QT_BEGIN_HEADER + +Q_DECLARE_METATYPE(QModelIndex) + +QT_BEGIN_NAMESPACE + +class QQmlChangeSet; +class QQmlComponent; +class QQuickPackage; +class QQmlV8Function; +class QQmlDataGroup; +class QQmlDelegateModelAttached; +class QQmlDelegateModelPrivate; + + +class Q_QML_PRIVATE_EXPORT QQmlDelegateModel : public QQmlInstanceModel, public QQmlParserStatus +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QQmlDelegateModel) + + Q_PROPERTY(QVariant model READ model WRITE setModel) + Q_PROPERTY(QQmlComponent *delegate READ delegate WRITE setDelegate) + Q_PROPERTY(QString filterOnGroup READ filterGroup WRITE setFilterGroup NOTIFY filterGroupChanged RESET resetFilterGroup) + Q_PROPERTY(QQmlDataGroup *items READ items CONSTANT) //TODO : worth renaming? + Q_PROPERTY(QQmlDataGroup *persistedItems READ persistedItems CONSTANT) + Q_PROPERTY(QQmlListProperty groups READ groups CONSTANT) + Q_PROPERTY(QObject *parts READ parts CONSTANT) + Q_PROPERTY(QVariant rootIndex READ rootIndex WRITE setRootIndex NOTIFY rootIndexChanged) + Q_CLASSINFO("DefaultProperty", "delegate") + Q_INTERFACES(QQmlParserStatus) +public: + QQmlDelegateModel(); + QQmlDelegateModel(QQmlContext *, QObject *parent=0); + virtual ~QQmlDelegateModel(); + + void classBegin(); + void componentComplete(); + + QVariant model() const; + void setModel(const QVariant &); + + QQmlComponent *delegate() const; + void setDelegate(QQmlComponent *); + + QVariant rootIndex() const; + void setRootIndex(const QVariant &root); + + Q_INVOKABLE QVariant modelIndex(int idx) const; + Q_INVOKABLE QVariant parentModelIndex() const; + + int count() const; + bool isValid() const { return delegate() != 0; } + QObject *object(int index, bool asynchronous=false); + ReleaseFlags release(QObject *object); + void cancel(int index); + virtual QString stringValue(int index, const QString &role); + virtual void setWatchedRoles(QList roles); + + int indexOf(QObject *object, QObject *objectContext) const; + + QString filterGroup() const; + void setFilterGroup(const QString &group); + void resetFilterGroup(); + + QQmlDataGroup *items(); + QQmlDataGroup *persistedItems(); + QQmlListProperty groups(); + QObject *parts(); + + bool event(QEvent *); + + static QQmlDelegateModelAttached *qmlAttachedProperties(QObject *obj); + +Q_SIGNALS: + void filterGroupChanged(); + void defaultGroupsChanged(); + void rootIndexChanged(); + +private Q_SLOTS: + void _q_itemsChanged(int index, int count, const QVector &roles); + void _q_itemsInserted(int index, int count); + void _q_itemsRemoved(int index, int count); + void _q_itemsMoved(int from, int to, int count); + void _q_modelReset(); + void _q_rowsInserted(const QModelIndex &,int,int); + void _q_rowsAboutToBeRemoved(const QModelIndex &parent, int begin, int end); + void _q_rowsRemoved(const QModelIndex &,int,int); + void _q_rowsMoved(const QModelIndex &, int, int, const QModelIndex &, int); + void _q_dataChanged(const QModelIndex&,const QModelIndex&,const QVector &); + void _q_layoutChanged(); + +private: + Q_DISABLE_COPY(QQmlDelegateModel) +}; + +class QQmlDataGroupPrivate; +class Q_QML_PRIVATE_EXPORT QQmlDataGroup : public QObject +{ + Q_OBJECT + Q_PROPERTY(int count READ count NOTIFY countChanged) + Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) + Q_PROPERTY(bool includeByDefault READ defaultInclude WRITE setDefaultInclude NOTIFY defaultIncludeChanged) +public: + QQmlDataGroup(QObject *parent = 0); + QQmlDataGroup(const QString &name, QQmlDelegateModel *model, int compositorType, QObject *parent = 0); + ~QQmlDataGroup(); + + QString name() const; + void setName(const QString &name); + + int count() const; + + bool defaultInclude() const; + void setDefaultInclude(bool include); + + Q_INVOKABLE QQmlV8Handle get(int index); + +public Q_SLOTS: + void insert(QQmlV8Function *); + void create(QQmlV8Function *); + void resolve(QQmlV8Function *); + void remove(QQmlV8Function *); + void addGroups(QQmlV8Function *); + void removeGroups(QQmlV8Function *); + void setGroups(QQmlV8Function *); + void move(QQmlV8Function *); + +Q_SIGNALS: + void countChanged(); + void nameChanged(); + void defaultIncludeChanged(); + void changed(const QQmlV8Handle &removed, const QQmlV8Handle &inserted); +private: + Q_DECLARE_PRIVATE(QQmlDataGroup) +}; + +class QQmlDelegateModelItem; +class QQmlDelegateModelAttachedMetaObject; +class QQmlDelegateModelAttached : public QObject +{ + Q_OBJECT + Q_PROPERTY(QQmlDelegateModel *model READ model CONSTANT) + Q_PROPERTY(QStringList groups READ groups WRITE setGroups NOTIFY groupsChanged) + Q_PROPERTY(bool isUnresolved READ isUnresolved NOTIFY unresolvedChanged) +public: + QQmlDelegateModelAttached(QObject *parent); + QQmlDelegateModelAttached(QQmlDelegateModelItem *cacheItem, QObject *parent); + ~QQmlDelegateModelAttached() {} + + void setCacheItem(QQmlDelegateModelItem *item); + + QQmlDelegateModel *model() const; + + QStringList groups() const; + void setGroups(const QStringList &groups); + + bool isUnresolved() const; + + void emitChanges(); + + void emitUnresolvedChanged() { emit unresolvedChanged(); } + +Q_SIGNALS: + void groupsChanged(); + void unresolvedChanged(); + +public: + QQmlDelegateModelItem *m_cacheItem; + int m_previousGroups; + int m_currentIndex[QQmlListCompositor::MaximumGroupCount]; + int m_previousIndex[QQmlListCompositor::MaximumGroupCount]; + + friend class QQmlDelegateModelAttachedMetaObject; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQmlDelegateModel) +QML_DECLARE_TYPEINFO(QQmlDelegateModel, QML_HAS_ATTACHED_PROPERTIES) +QML_DECLARE_TYPE(QQmlDataGroup) + +QT_END_HEADER + +#endif // QQMLDATAMODEL_P_H diff --git a/src/qml/items/qqmldelegatemodel_p_p.h b/src/qml/items/qqmldelegatemodel_p_p.h new file mode 100644 index 0000000000..0ac7285cab --- /dev/null +++ b/src/qml/items/qqmldelegatemodel_p_p.h @@ -0,0 +1,411 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLDATAMODEL_P_P_H +#define QQMLDATAMODEL_P_P_H + +#include "qqmldelegatemodel_p.h" + + +#include +#include + +#include +#include + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +QT_BEGIN_NAMESPACE + +typedef QQmlListCompositor Compositor; + +class QQmlDelegateModelAttachedMetaObject; + +class QQmlDelegateModelItemMetaType : public QQmlRefCount +{ +public: + QQmlDelegateModelItemMetaType(QV8Engine *engine, QQmlDelegateModel *model, const QStringList &groupNames); + ~QQmlDelegateModelItemMetaType(); + + void initializeMetaObject(); + void initializeConstructor(); + + int parseGroups(const QStringList &groupNames) const; + int parseGroups(const v8::Local &groupNames) const; + + static void release_index(v8::Persistent object, void *parameter); + static void release_model(v8::Persistent object, void *parameter); + + static v8::Handle get_model(v8::Local, const v8::AccessorInfo &info); + static v8::Handle get_groups(v8::Local, const v8::AccessorInfo &info); + static void set_groups( + v8::Local, v8::Local value, const v8::AccessorInfo &info); + static v8::Handle get_member(v8::Local, const v8::AccessorInfo &info); + static void set_member( + v8::Local, v8::Local value, const v8::AccessorInfo &info); + static v8::Handle get_index(v8::Local, const v8::AccessorInfo &info); + + QQmlGuard model; + const int groupCount; + QV8Engine * const v8Engine; + QQmlDelegateModelAttachedMetaObject *metaObject; + const QStringList groupNames; + v8::Persistent constructor; +}; + +class QQmlAdaptorModel; +class QQDMIncubationTask; + +class QQmlDelegateModelItem : public QObject, public QV8ObjectResource +{ + Q_OBJECT + Q_PROPERTY(int index READ modelIndex NOTIFY modelIndexChanged) + Q_PROPERTY(QObject *model READ modelObject CONSTANT) + V8_RESOURCE_TYPE(VisualDataItemType) +public: + QQmlDelegateModelItem(QQmlDelegateModelItemMetaType *metaType, int modelIndex); + ~QQmlDelegateModelItem(); + + void referenceObject() { ++objectRef; } + bool releaseObject() { return --objectRef == 0 && !(groups & Compositor::PersistedFlag); } + bool isObjectReferenced() const { return objectRef != 0 || (groups & Compositor::PersistedFlag); } + + bool isReferenced() const { + return scriptRef + || incubationTask + || ((groups & Compositor::UnresolvedFlag) && (groups & Compositor::GroupMask)); + } + + void Dispose(); + + QObject *modelObject() { return this; } + + void incubateObject( + QQmlComponent *component, + QQmlEngine *engine, + QQmlContextData *context, + QQmlContextData *forContext); + void destroyObject(); + + static QQmlDelegateModelItem *dataForObject(QObject *object); + + int groupIndex(Compositor::Group group); + + int modelIndex() const { return index; } + void setModelIndex(int idx) { index = idx; emit modelIndexChanged(); } + + virtual v8::Handle get() { return engine->newQObject(this); } + + virtual void setValue(const QString &role, const QVariant &value) { Q_UNUSED(role); Q_UNUSED(value); } + virtual bool resolveIndex(const QQmlAdaptorModel &, int) { return false; } + + QQmlDelegateModelItemMetaType * const metaType; + QQmlContextData *contextData; + QObject *object; + QQmlDelegateModelAttached *attached; + QQDMIncubationTask *incubationTask; + int objectRef; + int scriptRef; + int groups; + int index; + + +Q_SIGNALS: + void modelIndexChanged(); + +protected: + void objectDestroyed(QObject *); +}; + + +class QQmlDelegateModelPrivate; +class QQDMIncubationTask : public QQmlIncubator +{ +public: + QQDMIncubationTask(QQmlDelegateModelPrivate *l, IncubationMode mode) + : QQmlIncubator(mode) + , incubating(0) + , vdm(l) {} + + virtual void statusChanged(Status); + virtual void setInitialState(QObject *); + + QQmlDelegateModelItem *incubating; + QQmlDelegateModelPrivate *vdm; + int index[QQmlListCompositor::MaximumGroupCount]; +}; + + +class QQmlDataGroupEmitter +{ +public: + virtual ~QQmlDataGroupEmitter() {} + virtual void emitModelUpdated(const QQmlChangeSet &changeSet, bool reset) = 0; + virtual void createdPackage(int, QQuickPackage *) {} + virtual void initPackage(int, QQuickPackage *) {} + virtual void destroyingPackage(QQuickPackage *) {} + + QIntrusiveListNode emitterNode; +}; + +typedef QIntrusiveList QQmlDataGroupEmitterList; + +class QQmlDataGroupPrivate : public QObjectPrivate +{ +public: + Q_DECLARE_PUBLIC(QQmlDataGroup) + + QQmlDataGroupPrivate() : group(Compositor::Cache), defaultInclude(false) {} + + static QQmlDataGroupPrivate *get(QQmlDataGroup *group) { + return static_cast(QObjectPrivate::get(group)); } + + void setModel(QQmlDelegateModel *model, Compositor::Group group); + bool isChangedConnected(); + void emitChanges(QV8Engine *engine); + void emitModelUpdated(bool reset); + + void createdPackage(int index, QQuickPackage *package); + void initPackage(int index, QQuickPackage *package); + void destroyingPackage(QQuickPackage *package); + + bool parseIndex(const v8::Local &value, int *index, Compositor::Group *group) const; + bool parseGroupArgs( + QQmlV8Function *args, Compositor::Group *group, int *index, int *count, int *groups) const; + + Compositor::Group group; + QQmlGuard model; + QQmlDataGroupEmitterList emitters; + QQmlChangeSet changeSet; + QString name; + bool defaultInclude; +}; + +class QQmlDelegateModelParts; + +class QQmlDelegateModelPrivate : public QObjectPrivate, public QQmlDataGroupEmitter +{ + Q_DECLARE_PUBLIC(QQmlDelegateModel) +public: + QQmlDelegateModelPrivate(QQmlContext *); + ~QQmlDelegateModelPrivate(); + + static QQmlDelegateModelPrivate *get(QQmlDelegateModel *m) { + return static_cast(QObjectPrivate::get(m)); + } + + void init(); + void connectModel(QQmlAdaptorModel *model); + + QObject *object(Compositor::Group group, int index, bool asynchronous); + QQmlDelegateModel::ReleaseFlags release(QObject *object); + QString stringValue(Compositor::Group group, int index, const QString &name); + void emitCreatedPackage(QQDMIncubationTask *incubationTask, QQuickPackage *package); + void emitInitPackage(QQDMIncubationTask *incubationTask, QQuickPackage *package); + void emitCreatedItem(QQDMIncubationTask *incubationTask, QObject *item) { + emit q_func()->createdItem(incubationTask->index[m_compositorGroup], item); } + void emitInitItem(QQDMIncubationTask *incubationTask, QObject *item) { + emit q_func()->initItem(incubationTask->index[m_compositorGroup], item); } + void emitDestroyingPackage(QQuickPackage *package); + void emitDestroyingItem(QObject *item) { emit q_func()->destroyingItem(item); } + void removeCacheItem(QQmlDelegateModelItem *cacheItem); + + void updateFilterGroup(); + + void addGroups(Compositor::iterator from, int count, Compositor::Group group, int groupFlags); + void removeGroups(Compositor::iterator from, int count, Compositor::Group group, int groupFlags); + void setGroups(Compositor::iterator from, int count, Compositor::Group group, int groupFlags); + + void itemsInserted( + const QVector &inserts, + QVarLengthArray, Compositor::MaximumGroupCount> *translatedInserts, + QHash > *movedItems = 0); + void itemsInserted(const QVector &inserts); + void itemsRemoved( + const QVector &removes, + QVarLengthArray, Compositor::MaximumGroupCount> *translatedRemoves, + QHash > *movedItems = 0); + void itemsRemoved(const QVector &removes); + void itemsMoved( + const QVector &removes, const QVector &inserts); + void itemsChanged(const QVector &changes); + template static v8::Local buildChangeList(const QVector &changes); + void emitChanges(); + void emitModelUpdated(const QQmlChangeSet &changeSet, bool reset); + + bool insert(Compositor::insert_iterator &before, const v8::Local &object, int groups); + + static void group_append(QQmlListProperty *property, QQmlDataGroup *group); + static int group_count(QQmlListProperty *property); + static QQmlDataGroup *group_at(QQmlListProperty *property, int index); + + void releaseIncubator(QQDMIncubationTask *incubationTask); + void incubatorStatusChanged(QQDMIncubationTask *incubationTask, QQmlIncubator::Status status); + void setInitialState(QQDMIncubationTask *incubationTask, QObject *o); + + QQmlAdaptorModel m_adaptorModel; + QQmlListCompositor m_compositor; + QQmlComponent *m_delegate; + QQmlDelegateModelItemMetaType *m_cacheMetaType; + QQmlContext *m_context; + QQmlDelegateModelParts *m_parts; + QQmlDataGroupEmitterList m_pendingParts; + + QList m_cache; + QList m_finishedIncubating; + QList m_watchedRoles; + + QString m_filterGroup; + + int m_count; + int m_groupCount; + + QQmlListCompositor::Group m_compositorGroup; + bool m_complete : 1; + bool m_delegateValidated : 1; + bool m_reset : 1; + bool m_transaction : 1; + bool m_incubatorCleanupScheduled : 1; + + union { + struct { + QQmlDataGroup *m_cacheItems; + QQmlDataGroup *m_items; + QQmlDataGroup *m_persistedItems; + }; + QQmlDataGroup *m_groups[Compositor::MaximumGroupCount]; + }; +}; + +class QQmlPartsModel : public QQmlInstanceModel, public QQmlDataGroupEmitter +{ + Q_OBJECT + Q_PROPERTY(QString filterOnGroup READ filterGroup WRITE setFilterGroup NOTIFY filterGroupChanged RESET resetFilterGroup) +public: + QQmlPartsModel(QQmlDelegateModel *model, const QString &part, QObject *parent = 0); + ~QQmlPartsModel(); + + QString filterGroup() const; + void setFilterGroup(const QString &group); + void resetFilterGroup(); + void updateFilterGroup(); + void updateFilterGroup(Compositor::Group group, const QQmlChangeSet &changeSet); + + int count() const; + bool isValid() const; + QObject *object(int index, bool asynchronous=false); + ReleaseFlags release(QObject *item); + QString stringValue(int index, const QString &role); + QList watchedRoles() const { return m_watchedRoles; } + void setWatchedRoles(QList roles); + + int indexOf(QObject *item, QObject *objectContext) const; + + void emitModelUpdated(const QQmlChangeSet &changeSet, bool reset); + + void createdPackage(int index, QQuickPackage *package); + void initPackage(int index, QQuickPackage *package); + void destroyingPackage(QQuickPackage *package); + +Q_SIGNALS: + void filterGroupChanged(); + +private: + QQmlDelegateModel *m_model; + QHash m_packaged; + QString m_part; + QString m_filterGroup; + QList m_watchedRoles; + Compositor::Group m_compositorGroup; + bool m_inheritGroup; +}; + +class QMetaPropertyBuilder; + +class QQmlDelegateModelPartsMetaObject : public QQmlOpenMetaObject +{ +public: + QQmlDelegateModelPartsMetaObject(QObject *parent) + : QQmlOpenMetaObject(parent) {} + + virtual void propertyCreated(int, QMetaPropertyBuilder &); + virtual QVariant initialValue(int); +}; + +class QQmlDelegateModelParts : public QObject +{ +Q_OBJECT +public: + QQmlDelegateModelParts(QQmlDelegateModel *parent); + + QQmlDelegateModel *model; + QList models; +}; + +class QQmlDelegateModelAttachedMetaObject : public QAbstractDynamicMetaObject, public QQmlRefCount +{ +public: + QQmlDelegateModelAttachedMetaObject( + QQmlDelegateModelItemMetaType *metaType, QMetaObject *metaObject); + ~QQmlDelegateModelAttachedMetaObject(); + + void objectDestroyed(QObject *); + int metaCall(QObject *, QMetaObject::Call, int _id, void **); + +private: + QQmlDelegateModelItemMetaType * const metaType; + QMetaObject * const metaObject; + const int memberPropertyOffset; + const int indexPropertyOffset; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/items/qqmlobjectmodel.cpp b/src/qml/items/qqmlobjectmodel.cpp new file mode 100644 index 0000000000..913fa79719 --- /dev/null +++ b/src/qml/items/qqmlobjectmodel.cpp @@ -0,0 +1,251 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlobjectmodel_p.h" + +#include +#include +#include + +#include +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +QHash QQmlObjectModelAttached::attachedProperties; + + +class QQmlObjectModelPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QQmlObjectModel) +public: + class Item { + public: + Item(QObject *i) : item(i), ref(0) {} + + void addRef() { ++ref; } + bool deref() { return --ref == 0; } + + QObject *item; + int ref; + }; + + QQmlObjectModelPrivate() : QObjectPrivate() {} + + static void children_append(QQmlListProperty *prop, QObject *item) { + static_cast(prop->data)->children.append(Item(item)); + static_cast(prop->data)->itemAppended(); + static_cast(prop->data)->emitChildrenChanged(); + } + + static int children_count(QQmlListProperty *prop) { + return static_cast(prop->data)->children.count(); + } + + static QObject *children_at(QQmlListProperty *prop, int index) { + return static_cast(prop->data)->children.at(index).item; + } + + static void children_clear(QQmlListProperty *prop) { + static_cast(prop->data)->itemCleared(static_cast(prop->data)->children); + static_cast(prop->data)->children.clear(); + static_cast(prop->data)->emitChildrenChanged(); + } + + void itemAppended() { + Q_Q(QQmlObjectModel); + QQmlObjectModelAttached *attached = QQmlObjectModelAttached::properties(children.last().item); + attached->setIndex(children.count()-1); + QQmlChangeSet changeSet; + changeSet.insert(children.count() - 1, 1); + emit q->modelUpdated(changeSet, false); + emit q->countChanged(); + } + + void itemCleared(const QList &children) { + Q_Q(QQmlObjectModel); + foreach (const Item &child, children) + emit q->destroyingItem(child.item); + emit q->countChanged(); + } + + void emitChildrenChanged() { + Q_Q(QQmlObjectModel); + emit q->childrenChanged(); + } + + int indexOf(QObject *item) const { + for (int i = 0; i < children.count(); ++i) + if (children.at(i).item == item) + return i; + return -1; + } + + + QList children; +}; + + +/*! + \qmltype VisualItemModel + \instantiates QQmlObjectModel + \inqmlmodule QtQuick 2 + \ingroup qtquick-models + \brief Defines items to be used added to a view + + A VisualItemModel contains the visual items to be used in a view. + When a VisualItemModel is used in a view, the view does not require + a delegate since the VisualItemModel already contains the visual + delegate (items). + + An item can determine its index within the + model via the \l{VisualItemModel::index}{index} attached property. + + The example below places three colored rectangles in a ListView. + \code + import QtQuick 2.0 + + Rectangle { + VisualItemModel { + id: itemModel + Rectangle { height: 30; width: 80; color: "red" } + Rectangle { height: 30; width: 80; color: "green" } + Rectangle { height: 30; width: 80; color: "blue" } + } + + ListView { + anchors.fill: parent + model: itemModel + } + } + \endcode + + \image visualitemmodel.png + + \sa {quick/modelviews/visualitemmodel}{VisualItemModel example} +*/ +QQmlObjectModel::QQmlObjectModel(QObject *parent) + : QQmlInstanceModel(*(new QQmlObjectModelPrivate), parent) +{ +} + +/*! + \qmlattachedproperty int QtQuick2::VisualItemModel::index + This attached property holds the index of this delegate's item within the model. + + It is attached to each instance of the delegate. +*/ + +QQmlListProperty QQmlObjectModel::children() +{ + Q_D(QQmlObjectModel); + return QQmlListProperty(this, + d, + d->children_append, + d->children_count, + d->children_at, + d->children_clear); +} + +/*! + \qmlproperty int QtQuick2::VisualItemModel::count + + The number of items in the model. This property is readonly. +*/ +int QQmlObjectModel::count() const +{ + Q_D(const QQmlObjectModel); + return d->children.count(); +} + +bool QQmlObjectModel::isValid() const +{ + return true; +} + +QObject *QQmlObjectModel::object(int index, bool) +{ + Q_D(QQmlObjectModel); + QQmlObjectModelPrivate::Item &item = d->children[index]; + item.addRef(); + if (item.ref == 1) { + emit initItem(index, item.item); + emit createdItem(index, item.item); + } + return item.item; +} + +QQmlInstanceModel::ReleaseFlags QQmlObjectModel::release(QObject *item) +{ + Q_D(QQmlObjectModel); + int idx = d->indexOf(item); + if (idx >= 0) { + if (!d->children[idx].deref()) + return QQmlInstanceModel::Referenced; + } + return 0; +} + +QString QQmlObjectModel::stringValue(int index, const QString &name) +{ + Q_D(QQmlObjectModel); + if (index < 0 || index >= d->children.count()) + return QString(); + return QQmlEngine::contextForObject(d->children.at(index).item)->contextProperty(name).toString(); +} + +int QQmlObjectModel::indexOf(QObject *item, QObject *) const +{ + Q_D(const QQmlObjectModel); + return d->indexOf(item); +} + +QQmlObjectModelAttached *QQmlObjectModel::qmlAttachedProperties(QObject *obj) +{ + return QQmlObjectModelAttached::properties(obj); +} + +QT_END_NAMESPACE + diff --git a/src/qml/items/qqmlobjectmodel_p.h b/src/qml/items/qqmlobjectmodel_p.h new file mode 100644 index 0000000000..5ec57bddc5 --- /dev/null +++ b/src/qml/items/qqmlobjectmodel_p.h @@ -0,0 +1,174 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLINSTANCEMODEL_P_H +#define QQMLINSTANCEMODEL_P_H + +#include +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QObject; +class QQmlChangeSet; + +class Q_QML_PRIVATE_EXPORT QQmlInstanceModel : public QObject +{ + Q_OBJECT + + Q_PROPERTY(int count READ count NOTIFY countChanged) + +public: + virtual ~QQmlInstanceModel() {} + + enum ReleaseFlag { Referenced = 0x01, Destroyed = 0x02 }; + Q_DECLARE_FLAGS(ReleaseFlags, ReleaseFlag) + + virtual int count() const = 0; + virtual bool isValid() const = 0; + virtual QObject *object(int index, bool asynchronous=false) = 0; + virtual ReleaseFlags release(QObject *object) = 0; + virtual void cancel(int) {} + virtual QString stringValue(int, const QString &) = 0; + virtual void setWatchedRoles(QList roles) = 0; + + virtual int indexOf(QObject *object, QObject *objectContext) const = 0; + +Q_SIGNALS: + void countChanged(); + void modelUpdated(const QQmlChangeSet &changeSet, bool reset); + void createdItem(int index, QObject *object); + void initItem(int index, QObject *object); + void destroyingItem(QObject *object); + +protected: + QQmlInstanceModel(QObjectPrivate &dd, QObject *parent = 0) + : QObject(dd, parent) {} + +private: + Q_DISABLE_COPY(QQmlInstanceModel) +}; + +class QQmlObjectModelAttached; +class QQmlObjectModelPrivate; +class Q_QML_PRIVATE_EXPORT QQmlObjectModel : public QQmlInstanceModel +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QQmlObjectModel) + + Q_PROPERTY(QQmlListProperty children READ children NOTIFY childrenChanged DESIGNABLE false) + Q_CLASSINFO("DefaultProperty", "children") + +public: + QQmlObjectModel(QObject *parent=0); + virtual ~QQmlObjectModel() {} + + virtual int count() const; + virtual bool isValid() const; + virtual QObject *object(int index, bool asynchronous=false); + virtual ReleaseFlags release(QObject *object); + virtual QString stringValue(int index, const QString &role); + virtual void setWatchedRoles(QList) {} + + virtual int indexOf(QObject *object, QObject *objectContext) const; + + QQmlListProperty children(); + + static QQmlObjectModelAttached *qmlAttachedProperties(QObject *obj); + +Q_SIGNALS: + void childrenChanged(); + +private: + Q_DISABLE_COPY(QQmlObjectModel) +}; + +class QQmlObjectModelAttached : public QObject +{ + Q_OBJECT + +public: + QQmlObjectModelAttached(QObject *parent) + : QObject(parent), m_index(0) {} + ~QQmlObjectModelAttached() { + attachedProperties.remove(parent()); + } + + Q_PROPERTY(int index READ index NOTIFY indexChanged) + int index() const { return m_index; } + void setIndex(int idx) { + if (m_index != idx) { + m_index = idx; + emit indexChanged(); + } + } + + static QQmlObjectModelAttached *properties(QObject *obj) { + QQmlObjectModelAttached *rv = attachedProperties.value(obj); + if (!rv) { + rv = new QQmlObjectModelAttached(obj); + attachedProperties.insert(obj, rv); + } + return rv; + } + +Q_SIGNALS: + void indexChanged(); + +public: + int m_index; + + static QHash attachedProperties; +}; + + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQmlInstanceModel) +QML_DECLARE_TYPE(QQmlObjectModel) +QML_DECLARE_TYPEINFO(QQmlObjectModel, QML_HAS_ATTACHED_PROPERTIES) + +QT_END_HEADER + +#endif // QQMLINSTANCEMODEL_P_H diff --git a/src/qml/items/qquickpackage.cpp b/src/qml/items/qquickpackage.cpp new file mode 100644 index 0000000000..e885524b27 --- /dev/null +++ b/src/qml/items/qquickpackage.cpp @@ -0,0 +1,198 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickpackage_p.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +/*! + \qmltype Package + \instantiates QQuickPackage + \inqmlmodule QtQuick 2 + \ingroup qtquick-views + \brief Specifies a collection of named items + + The Package class is used in conjunction with + VisualDataModel to enable delegates with a shared context + to be provided to multiple views. + + Any item within a Package may be assigned a name via the + \l{Package::name}{Package.name} attached property. + + The example below creates a Package containing two named items; + \e list and \e grid. The third item in the package (the \l Rectangle) is parented to whichever + delegate it should appear in. This allows an item to move + between views. + + \snippet quick/views/package/Delegate.qml 0 + + These named items are used as the delegates by the two views who + reference the special \l{VisualDataModel::parts} property to select + a model which provides the chosen delegate. + + \snippet quick/views/package/view.qml 0 + + \sa {quick/views/package}{Package example}, {quick/demos/photoviewer}{Photo Viewer example}, QtQml +*/ + +/*! + \qmlattachedproperty string QtQuick2::Package::name + This attached property holds the name of an item within a Package. +*/ + + +class QQuickPackagePrivate : public QObjectPrivate +{ +public: + QQuickPackagePrivate() {} + + struct DataGuard : public QQmlGuard + { + DataGuard(QObject *obj, QList *l) : list(l) { (QQmlGuard&)*this = obj; } + QList *list; + void objectDestroyed(QObject *) { + // we assume priv will always be destroyed after objectDestroyed calls + list->removeOne(*this); + } + }; + + QList dataList; + static void data_append(QQmlListProperty *prop, QObject *o) { + QList *list = static_cast *>(prop->data); + list->append(DataGuard(o, list)); + } + static void data_clear(QQmlListProperty *prop) { + QList *list = static_cast *>(prop->data); + list->clear(); + } + static QObject *data_at(QQmlListProperty *prop, int index) { + QList *list = static_cast *>(prop->data); + return list->at(index); + } + static int data_count(QQmlListProperty *prop) { + QList *list = static_cast *>(prop->data); + return list->count(); + } +}; + +QHash QQuickPackageAttached::attached; + +QQuickPackageAttached::QQuickPackageAttached(QObject *parent) +: QObject(parent) +{ + attached.insert(parent, this); +} + +QQuickPackageAttached::~QQuickPackageAttached() +{ + attached.remove(parent()); +} + +QString QQuickPackageAttached::name() const +{ + return _name; +} + +void QQuickPackageAttached::setName(const QString &n) +{ + _name = n; +} + +QQuickPackage::QQuickPackage(QObject *parent) + : QObject(*(new QQuickPackagePrivate), parent) +{ +} + +QQuickPackage::~QQuickPackage() +{ +} + +QQmlListProperty QQuickPackage::data() +{ + Q_D(QQuickPackage); + return QQmlListProperty(this, &d->dataList, QQuickPackagePrivate::data_append, + QQuickPackagePrivate::data_count, + QQuickPackagePrivate::data_at, + QQuickPackagePrivate::data_clear); +} + +bool QQuickPackage::hasPart(const QString &name) +{ + Q_D(QQuickPackage); + for (int ii = 0; ii < d->dataList.count(); ++ii) { + QObject *obj = d->dataList.at(ii); + QQuickPackageAttached *a = QQuickPackageAttached::attached.value(obj); + if (a && a->name() == name) + return true; + } + return false; +} + +QObject *QQuickPackage::part(const QString &name) +{ + Q_D(QQuickPackage); + if (name.isEmpty() && !d->dataList.isEmpty()) + return d->dataList.at(0); + + for (int ii = 0; ii < d->dataList.count(); ++ii) { + QObject *obj = d->dataList.at(ii); + QQuickPackageAttached *a = QQuickPackageAttached::attached.value(obj); + if (a && a->name() == name) + return obj; + } + + if (name == QLatin1String("default") && !d->dataList.isEmpty()) + return d->dataList.at(0); + + return 0; +} + +QQuickPackageAttached *QQuickPackage::qmlAttachedProperties(QObject *o) +{ + return new QQuickPackageAttached(o); +} + + + +QT_END_NAMESPACE diff --git a/src/qml/items/qquickpackage_p.h b/src/qml/items/qquickpackage_p.h new file mode 100644 index 0000000000..a777ff4a7e --- /dev/null +++ b/src/qml/items/qquickpackage_p.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKPACKAGE_H +#define QQUICKPACKAGE_H + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QQuickPackagePrivate; +class QQuickPackageAttached; +class Q_AUTOTEST_EXPORT QQuickPackage : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QQuickPackage) + + Q_CLASSINFO("DefaultProperty", "data") + Q_PROPERTY(QQmlListProperty data READ data) + +public: + QQuickPackage(QObject *parent=0); + virtual ~QQuickPackage(); + + QQmlListProperty data(); + + QObject *part(const QString & = QString()); + bool hasPart(const QString &); + + static QQuickPackageAttached *qmlAttachedProperties(QObject *); +}; + +class QQuickPackageAttached : public QObject +{ +Q_OBJECT +Q_PROPERTY(QString name READ name WRITE setName) +public: + QQuickPackageAttached(QObject *parent); + virtual ~QQuickPackageAttached(); + + QString name() const; + void setName(const QString &n); + + static QHash attached; +private: + QString _name; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickPackage) +QML_DECLARE_TYPEINFO(QQuickPackage, QML_HAS_ATTACHED_PROPERTIES) + +QT_END_HEADER + +#endif // QQUICKPACKAGE_H diff --git a/src/qml/qml.pro b/src/qml/qml.pro index 4f55b83e14..08aa369ac3 100644 --- a/src/qml/qml.pro +++ b/src/qml/qml.pro @@ -25,3 +25,4 @@ include(util/util.pri) include(qml/qml.pri) include(debugger/debugger.pri) include(animations/animations.pri) +include(items/items.pri) diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index 600f526c45..5657cacf7d 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -92,6 +92,9 @@ #include "qqmlbind_p.h" #include "qqmlconnections_p.h" #include "qqmltimer_p.h" +#include +#include +#include #ifdef Q_OS_WIN // for %APPDATA% #include @@ -189,6 +192,11 @@ void QQmlEnginePrivate::registerBaseTypes(const char *uri, int versionMajor, int void QQmlEnginePrivate::registerQtQuick2Types(const char *uri, int versionMajor, int versionMinor) { qmlRegisterType(uri, versionMajor, versionMinor, "WorkerScript"); + qmlRegisterType(uri, versionMajor, versionMinor, "Package"); + qmlRegisterType(uri, versionMajor, versionMinor, "VisualDataModel"); + qmlRegisterType(uri, versionMajor, versionMinor, "VisualDataGroup"); + qmlRegisterType(uri, versionMajor, versionMinor, "VisualItemModel"); + qmlRegisterType(); } void QQmlEnginePrivate::defineQtQuick2Module() diff --git a/src/qml/util/qqmladaptormodel.cpp b/src/qml/util/qqmladaptormodel.cpp new file mode 100644 index 0000000000..5b6ef79338 --- /dev/null +++ b/src/qml/util/qqmladaptormodel.cpp @@ -0,0 +1,977 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmladaptormodel_p.h" + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QQmlAdaptorModelEngineData : public QV8Engine::Deletable +{ +public: + enum + { + Index, + ModelData, + HasModelChildren, + StringCount + }; + + QQmlAdaptorModelEngineData(QV8Engine *engine); + ~QQmlAdaptorModelEngineData(); + + v8::Local index() { return strings->Get(Index)->ToString(); } + v8::Local modelData() { return strings->Get(ModelData)->ToString(); } + v8::Local hasModelChildren() { return strings->Get(HasModelChildren)->ToString(); } + + v8::Persistent constructorListItem; + v8::Persistent strings; +}; + +V8_DEFINE_EXTENSION(QQmlAdaptorModelEngineData, engineData) + +static v8::Handle get_index(v8::Local, const v8::AccessorInfo &info) +{ + QQmlDelegateModelItem *data = v8_resource_cast(info.This()); + V8ASSERT_TYPE(data, "Not a valid VisualData object"); + + return v8::Int32::New(data->index); +} + +template static void setModelDataType(QMetaObjectBuilder *builder, M *metaType) +{ + builder->setFlags(QMetaObjectBuilder::DynamicMetaObject); + builder->setClassName(T::staticMetaObject.className()); + builder->setSuperClass(&T::staticMetaObject); + metaType->propertyOffset = T::staticMetaObject.propertyCount(); + metaType->signalOffset = T::staticMetaObject.methodCount(); +} + +static void addProperty(QMetaObjectBuilder *builder, int propertyId, const QByteArray &propertyName, const QByteArray &propertyType) +{ + builder->addSignal("__" + QByteArray::number(propertyId) + "()"); + QMetaPropertyBuilder property = builder->addProperty( + propertyName, propertyType, propertyId); + property.setWritable(true); +} + +class VDMModelDelegateDataType; + +class QQmlDMCachedModelData : public QQmlDelegateModelItem +{ +public: + QQmlDMCachedModelData( + QQmlDelegateModelItemMetaType *metaType, + VDMModelDelegateDataType *dataType, + int index); + + int metaCall(QMetaObject::Call call, int id, void **arguments); + + virtual QVariant value(int role) const = 0; + virtual void setValue(int role, const QVariant &value) = 0; + + void setValue(const QString &role, const QVariant &value); + bool resolveIndex(const QQmlAdaptorModel &model, int idx); + + static v8::Handle get_property(v8::Local, const v8::AccessorInfo &info); + static void set_property( + v8::Local, v8::Local value, const v8::AccessorInfo &info); + + VDMModelDelegateDataType *type; + QVector cachedData; +}; + +class VDMModelDelegateDataType + : public QQmlRefCount + , public QQmlAdaptorModel::Accessors + , public QAbstractDynamicMetaObject +{ +public: + VDMModelDelegateDataType(QQmlAdaptorModel *model) + : model(model) + , metaObject(0) + , propertyCache(0) + , propertyOffset(0) + , signalOffset(0) + , hasModelData(false) + { + } + + ~VDMModelDelegateDataType() + { + if (propertyCache) + propertyCache->release(); + free(metaObject); + + qPersistentDispose(constructor); + } + + bool notify( + const QQmlAdaptorModel &, + const QList &items, + int index, + int count, + const QVector &roles) const + { + bool changed = roles.isEmpty() && !watchedRoles.isEmpty(); + if (!changed && !watchedRoles.isEmpty() && watchedRoleIds.isEmpty()) { + QList roleIds; + foreach (const QByteArray &r, watchedRoles) { + QHash::const_iterator it = roleNames.find(r); + if (it != roleNames.end()) + roleIds << it.value(); + } + const_cast(this)->watchedRoleIds = roleIds; + } + + QVector signalIndexes; + for (int i = 0; i < roles.count(); ++i) { + const int role = roles.at(i); + if (!changed && watchedRoleIds.contains(role)) + changed = true; + + int propertyId = propertyRoles.indexOf(role); + if (propertyId != -1) + signalIndexes.append(propertyId + signalOffset); + } + if (roles.isEmpty()) { + for (int propertyId = 0; propertyId < propertyRoles.count(); ++propertyId) + signalIndexes.append(propertyId + signalOffset); + } + + for (int i = 0, c = items.count(); i < c; ++i) { + QQmlDelegateModelItem *item = items.at(i); + const int idx = item->modelIndex(); + if (idx >= index && idx < index + count) { + for (int i = 0; i < signalIndexes.count(); ++i) + QMetaObject::activate(item, signalIndexes.at(i), 0); + } + } + return changed; + } + + void replaceWatchedRoles( + QQmlAdaptorModel &, + const QList &oldRoles, + const QList &newRoles) const + { + VDMModelDelegateDataType *dataType = const_cast(this); + + dataType->watchedRoleIds.clear(); + foreach (const QByteArray &oldRole, oldRoles) + dataType->watchedRoles.removeOne(oldRole); + dataType->watchedRoles += newRoles; + } + + void initializeConstructor(QQmlAdaptorModelEngineData *const data) + { + constructor = qPersistentNew(v8::ObjectTemplate::New()); + constructor->SetHasExternalResource(true); + constructor->SetAccessor(data->index(), get_index); + + typedef QHash::const_iterator iterator; + for (iterator it = roleNames.constBegin(), end = roleNames.constEnd(); it != end; ++it) { + const int propertyId = propertyRoles.indexOf(it.value()); + const QByteArray &propertyName = it.key(); + + constructor->SetAccessor( + v8::String::New(propertyName.constData(), propertyName.length()), + QQmlDMCachedModelData::get_property, + QQmlDMCachedModelData::set_property, + v8::Int32::New(propertyId)); + } + } + + // QAbstractDynamicMetaObject + + void objectDestroyed(QObject *) + { + release(); + } + + int metaCall(QObject *object, QMetaObject::Call call, int id, void **arguments) + { + return static_cast(object)->metaCall(call, id, arguments); + } + + v8::Persistent constructor; + QList propertyRoles; + QList watchedRoleIds; + QList watchedRoles; + QHash roleNames; + QQmlAdaptorModel *model; + QMetaObject *metaObject; + QQmlPropertyCache *propertyCache; + int propertyOffset; + int signalOffset; + bool hasModelData; +}; + +QQmlDMCachedModelData::QQmlDMCachedModelData( + QQmlDelegateModelItemMetaType *metaType, VDMModelDelegateDataType *dataType, int index) + : QQmlDelegateModelItem(metaType, index) + , type(dataType) +{ + if (index == -1) + cachedData.resize(type->hasModelData ? 1 : type->propertyRoles.count()); + + QObjectPrivate::get(this)->metaObject = type; + + type->addref(); + + QQmlData *qmldata = QQmlData::get(this, true); + qmldata->propertyCache = dataType->propertyCache; + qmldata->propertyCache->addref(); +} + +int QQmlDMCachedModelData::metaCall(QMetaObject::Call call, int id, void **arguments) +{ + if (call == QMetaObject::ReadProperty && id >= type->propertyOffset) { + const int propertyIndex = id - type->propertyOffset; + if (index == -1) { + if (!cachedData.isEmpty()) { + *static_cast(arguments[0]) = cachedData.at( + type->hasModelData ? 0 : propertyIndex); + } + } else if (*type->model) { + *static_cast(arguments[0]) = value(type->propertyRoles.at(propertyIndex)); + } + return -1; + } else if (call == QMetaObject::WriteProperty && id >= type->propertyOffset) { + const int propertyIndex = id - type->propertyOffset; + if (index == -1) { + const QMetaObject *meta = metaObject(); + if (cachedData.count() > 1) { + cachedData[propertyIndex] = *static_cast(arguments[0]); + QMetaObject::activate(this, meta, propertyIndex, 0); + } else if (cachedData.count() == 1) { + cachedData[0] = *static_cast(arguments[0]); + QMetaObject::activate(this, meta, 0, 0); + QMetaObject::activate(this, meta, 1, 0); + } + } else if (*type->model) { + setValue(type->propertyRoles.at(propertyIndex), *static_cast(arguments[0])); + } + return -1; + } else { + return qt_metacall(call, id, arguments); + } +} + +void QQmlDMCachedModelData::setValue(const QString &role, const QVariant &value) +{ + QHash::iterator it = type->roleNames.find(role.toUtf8()); + if (it != type->roleNames.end()) { + for (int i = 0; i < type->propertyRoles.count(); ++i) { + if (type->propertyRoles.at(i) == *it) { + cachedData[i] = value; + return; + } + } + } +} + +bool QQmlDMCachedModelData::resolveIndex(const QQmlAdaptorModel &, int idx) +{ + if (index == -1) { + Q_ASSERT(idx >= 0); + index = idx; + cachedData.clear(); + emit modelIndexChanged(); + const QMetaObject *meta = metaObject(); + const int propertyCount = type->propertyRoles.count(); + for (int i = 0; i < propertyCount; ++i) + QMetaObject::activate(this, meta, i, 0); + return true; + } else { + return false; + } +} + +v8::Handle QQmlDMCachedModelData::get_property( + v8::Local, const v8::AccessorInfo &info) +{ + QQmlDelegateModelItem *data = v8_resource_cast(info.This()); + V8ASSERT_TYPE(data, "Not a valid VisualData object"); + + QQmlDMCachedModelData *modelData = static_cast(data); + const int propertyId = info.Data()->Int32Value(); + if (data->index == -1) { + if (!modelData->cachedData.isEmpty()) { + return data->engine->fromVariant( + modelData->cachedData.at(modelData->type->hasModelData ? 0 : propertyId)); + } + } else if (*modelData->type->model) { + return data->engine->fromVariant( + modelData->value(modelData->type->propertyRoles.at(propertyId))); + } + return v8::Undefined(); +} + +void QQmlDMCachedModelData::set_property( + v8::Local, v8::Local value, const v8::AccessorInfo &info) +{ + QQmlDelegateModelItem *data = v8_resource_cast(info.This()); + V8ASSERT_TYPE_SETTER(data, "Not a valid VisualData object"); + + const int propertyId = info.Data()->Int32Value(); + if (data->index == -1) { + QQmlDMCachedModelData *modelData = static_cast(data); + if (!modelData->cachedData.isEmpty()) { + if (modelData->cachedData.count() > 1) { + modelData->cachedData[propertyId] = data->engine->toVariant(value, QVariant::Invalid); + QMetaObject::activate(data, data->metaObject(), propertyId, 0); + } else if (modelData->cachedData.count() == 1) { + modelData->cachedData[0] = data->engine->toVariant(value, QVariant::Invalid); + QMetaObject::activate(data, data->metaObject(), 0, 0); + QMetaObject::activate(data, data->metaObject(), 1, 0); + } + } + } +} + +//----------------------------------------------------------------- +// QAbstractItemModel +//----------------------------------------------------------------- + +class QQmlDMAbstractItemModelData : public QQmlDMCachedModelData +{ + Q_OBJECT + Q_PROPERTY(bool hasModelChildren READ hasModelChildren CONSTANT) +public: + QQmlDMAbstractItemModelData( + QQmlDelegateModelItemMetaType *metaType, + VDMModelDelegateDataType *dataType, + int index) + : QQmlDMCachedModelData(metaType, dataType, index) + { + } + + bool hasModelChildren() const + { + if (index >= 0 && *type->model) { + const QAbstractItemModel * const model = type->model->aim(); + return model->hasChildren(model->index(index, 0, type->model->rootIndex)); + } else { + return false; + } + } + + QVariant value(int role) const + { + return type->model->aim()->index(index, 0, type->model->rootIndex).data(role); + } + + void setValue(int role, const QVariant &value) + { + type->model->aim()->setData( + type->model->aim()->index(index, 0, type->model->rootIndex), value, role); + } + + v8::Handle get() + { + if (type->constructor.IsEmpty()) { + QQmlAdaptorModelEngineData * const data = engineData(engine); + v8::HandleScope handleScope; + v8::Context::Scope contextScope(engine->context()); + type->initializeConstructor(data); + type->constructor->SetAccessor(data->hasModelChildren(), get_hasModelChildren); + } + v8::Local data = type->constructor->NewInstance(); + data->SetExternalResource(this); + ++scriptRef; + return data; + } + + static v8::Handle get_hasModelChildren(v8::Local, const v8::AccessorInfo &info) + { + QQmlDelegateModelItem *data = v8_resource_cast(info.This()); + V8ASSERT_TYPE(data, "Not a valid VisualData object"); + + const QQmlAdaptorModel *const model = static_cast(data)->type->model; + if (data->index >= 0 && *model) { + const QAbstractItemModel * const aim = model->aim(); + return v8::Boolean::New(aim->hasChildren(aim->index(data->index, 0, model->rootIndex))); + } else { + return v8::Boolean::New(false); + } + } +}; + +class VDMAbstractItemModelDataType : public VDMModelDelegateDataType +{ +public: + VDMAbstractItemModelDataType(QQmlAdaptorModel *model) + : VDMModelDelegateDataType(model) + { + } + + int count(const QQmlAdaptorModel &model) const + { + return model.aim()->rowCount(model.rootIndex); + } + + void cleanup(QQmlAdaptorModel &model, QQmlDelegateModel *vdm) const + { + QAbstractItemModel * const aim = model.aim(); + if (aim && vdm) { + QObject::disconnect(aim, SIGNAL(rowsInserted(QModelIndex,int,int)), + vdm, SLOT(_q_rowsInserted(QModelIndex,int,int))); + QObject::disconnect(aim, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), + vdm, SLOT(_q_rowsAboutToBeRemoved(QModelIndex,int,int))); + QObject::disconnect(aim, SIGNAL(rowsRemoved(QModelIndex,int,int)), + vdm, SLOT(_q_rowsRemoved(QModelIndex,int,int))); + QObject::disconnect(aim, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector)), + vdm, SLOT(_q_dataChanged(QModelIndex,QModelIndex,QVector))); + QObject::disconnect(aim, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), + vdm, SLOT(_q_rowsMoved(QModelIndex,int,int,QModelIndex,int))); + QObject::disconnect(aim, SIGNAL(modelReset()), + vdm, SLOT(_q_modelReset())); + QObject::disconnect(aim, SIGNAL(layoutChanged()), + vdm, SLOT(_q_layoutChanged())); + } + + const_cast(this)->release(); + } + + QVariant value(const QQmlAdaptorModel &model, int index, const QString &role) const + { + QHash::const_iterator it = roleNames.find(role.toUtf8()); + if (it != roleNames.end()) { + return model.aim()->index(index, 0, model.rootIndex).data(*it); + } else if (role == QLatin1String("hasModelChildren")) { + return QVariant(model.aim()->hasChildren(model.aim()->index(index, 0, model.rootIndex))); + } else { + return QVariant(); + } + } + + QVariant parentModelIndex(const QQmlAdaptorModel &model) const + { + return model + ? QVariant::fromValue(model.aim()->parent(model.rootIndex)) + : QVariant(); + } + + QVariant modelIndex(const QQmlAdaptorModel &model, int index) const + { + return model + ? QVariant::fromValue(model.aim()->index(index, 0, model.rootIndex)) + : QVariant(); + } + + bool canFetchMore(const QQmlAdaptorModel &model) const + { + return model && model.aim()->canFetchMore(model.rootIndex); + } + + void fetchMore(QQmlAdaptorModel &model) const + { + if (model) + model.aim()->fetchMore(model.rootIndex); + } + + QQmlDelegateModelItem *createItem( + QQmlAdaptorModel &model, + QQmlDelegateModelItemMetaType *metaType, + QQmlEngine *engine, + int index) const + { + VDMAbstractItemModelDataType *dataType = const_cast(this); + if (!metaObject) + dataType->initializeMetaType(model, engine); + return new QQmlDMAbstractItemModelData(metaType, dataType, index); + } + + void initializeMetaType(QQmlAdaptorModel &model, QQmlEngine *engine) + { + QMetaObjectBuilder builder; + setModelDataType(&builder, this); + + const QByteArray propertyType = QByteArrayLiteral("QVariant"); + const QHash names = model.aim()->roleNames(); + for (QHash::const_iterator it = names.begin(); it != names.end(); ++it) { + const int propertyId = propertyRoles.count(); + propertyRoles.append(it.key()); + roleNames.insert(it.value(), it.key()); + addProperty(&builder, propertyId, it.value(), propertyType); + } + if (propertyRoles.count() == 1) { + hasModelData = true; + const int role = names.begin().key(); + const QByteArray propertyName = QByteArrayLiteral("modelData"); + + propertyRoles.append(role); + roleNames.insert(propertyName, role); + addProperty(&builder, 1, propertyName, propertyType); + } + + metaObject = builder.toMetaObject(); + *static_cast(this) = *metaObject; + propertyCache = new QQmlPropertyCache(engine, metaObject); + } +}; + +//----------------------------------------------------------------- +// QQmlListAccessor +//----------------------------------------------------------------- + +class QQmlDMListAccessorData : public QQmlDelegateModelItem +{ + Q_OBJECT + Q_PROPERTY(QVariant modelData READ modelData WRITE setModelData NOTIFY modelDataChanged) +public: + QQmlDMListAccessorData(QQmlDelegateModelItemMetaType *metaType, int index, const QVariant &value) + : QQmlDelegateModelItem(metaType, index) + , cachedData(value) + { + } + + QVariant modelData() const + { + return cachedData; + } + + void setModelData(const QVariant &data) + { + if (index == -1 && data != cachedData) { + cachedData = data; + emit modelDataChanged(); + } + } + + static v8::Handle get_modelData(v8::Local, const v8::AccessorInfo &info) + { + QQmlDelegateModelItem *data = v8_resource_cast(info.This()); + V8ASSERT_TYPE(data, "Not a valid VisualData object"); + + return data->engine->fromVariant(static_cast(data)->cachedData); + } + + static void set_modelData(v8::Local, v8::Local value, const v8::AccessorInfo &info) + { + QQmlDelegateModelItem *data = v8_resource_cast(info.This()); + V8ASSERT_TYPE_SETTER(data, "Not a valid VisualData object"); + + static_cast(data)->setModelData( + data->engine->toVariant(value, QVariant::Invalid)); + } + + v8::Handle get() + { + v8::Local data = engineData(engine)->constructorListItem->NewInstance(); + data->SetExternalResource(this); + ++scriptRef; + return data; + } + + void setValue(const QString &role, const QVariant &value) + { + if (role == QLatin1String("modelData")) + cachedData = value; + } + + bool resolveIndex(const QQmlAdaptorModel &model, int idx) + { + if (index == -1) { + index = idx; + cachedData = model.list.at(idx); + emit modelIndexChanged(); + emit modelDataChanged(); + return true; + } else { + return false; + } + } + + +Q_SIGNALS: + void modelDataChanged(); + +private: + QVariant cachedData; +}; + + +class VDMListDelegateDataType : public QQmlAdaptorModel::Accessors +{ +public: + inline VDMListDelegateDataType() {} + + int count(const QQmlAdaptorModel &model) const + { + return model.list.count(); + } + + QVariant value(const QQmlAdaptorModel &model, int index, const QString &role) const + { + return role == QLatin1String("modelData") + ? model.list.at(index) + : QVariant(); + } + + QQmlDelegateModelItem *createItem( + QQmlAdaptorModel &model, + QQmlDelegateModelItemMetaType *metaType, + QQmlEngine *, + int index) const + { + return new QQmlDMListAccessorData( + metaType, + index, + index >= 0 && index < model.list.count() ? model.list.at(index) : QVariant()); + } +}; + +//----------------------------------------------------------------- +// QObject +//----------------------------------------------------------------- + +class VDMObjectDelegateDataType; +class QQmlDMObjectData : public QQmlDelegateModelItem, public QQmlAdaptorModelProxyInterface +{ + Q_OBJECT + Q_PROPERTY(QObject *modelData READ modelData CONSTANT) + Q_INTERFACES(QQmlAdaptorModelProxyInterface) +public: + QQmlDMObjectData( + QQmlDelegateModelItemMetaType *metaType, + VDMObjectDelegateDataType *dataType, + int index, + QObject *object); + + QObject *modelData() const { return object; } + QObject *proxiedObject() { return object; } + + QQmlGuard object; +}; + +class VDMObjectDelegateDataType : public QQmlRefCount, public QQmlAdaptorModel::Accessors +{ +public: + QMetaObject *metaObject; + int propertyOffset; + int signalOffset; + bool shared; + QMetaObjectBuilder builder; + + VDMObjectDelegateDataType() + : metaObject(0) + , propertyOffset(0) + , signalOffset(0) + , shared(true) + { + } + + VDMObjectDelegateDataType(const VDMObjectDelegateDataType &type) + : QQmlRefCount() + , QQmlAdaptorModel::Accessors() + , metaObject(0) + , propertyOffset(type.propertyOffset) + , signalOffset(type.signalOffset) + , shared(false) + , builder(type.metaObject, QMetaObjectBuilder::Properties + | QMetaObjectBuilder::Signals + | QMetaObjectBuilder::SuperClass + | QMetaObjectBuilder::ClassName) + { + builder.setFlags(QMetaObjectBuilder::DynamicMetaObject); + } + + ~VDMObjectDelegateDataType() + { + free(metaObject); + } + + int count(const QQmlAdaptorModel &model) const + { + return model.list.count(); + } + + QVariant value(const QQmlAdaptorModel &model, int index, const QString &role) const + { + if (QObject *object = model.list.at(index).value()) + return object->property(role.toUtf8()); + return QVariant(); + } + + QQmlDelegateModelItem *createItem( + QQmlAdaptorModel &model, + QQmlDelegateModelItemMetaType *metaType, + QQmlEngine *, + int index) const + { + VDMObjectDelegateDataType *dataType = const_cast(this); + if (!metaObject) + dataType->initializeMetaType(model); + return index >= 0 && index < model.list.count() + ? new QQmlDMObjectData(metaType, dataType, index, qvariant_cast(model.list.at(index))) + : 0; + } + + void initializeMetaType(QQmlAdaptorModel &) + { + setModelDataType(&builder, this); + + metaObject = builder.toMetaObject(); + } + + void cleanup(QQmlAdaptorModel &, QQmlDelegateModel *) const + { + const_cast(this)->release(); + } +}; + +class QQmlDMObjectDataMetaObject : public QAbstractDynamicMetaObject +{ +public: + QQmlDMObjectDataMetaObject(QQmlDMObjectData *data, VDMObjectDelegateDataType *type) + : m_data(data) + , m_type(type) + { + QObjectPrivate *op = QObjectPrivate::get(m_data); + *static_cast(this) = *type->metaObject; + op->metaObject = this; + m_type->addref(); + } + + ~QQmlDMObjectDataMetaObject() + { + m_type->release(); + } + + int metaCall(QMetaObject::Call call, int id, void **arguments) + { + static const int objectPropertyOffset = QObject::staticMetaObject.propertyCount(); + if (id >= m_type->propertyOffset + && (call == QMetaObject::ReadProperty + || call == QMetaObject::WriteProperty + || call == QMetaObject::ResetProperty)) { + if (m_data->object) + QMetaObject::metacall(m_data->object, call, id - m_type->propertyOffset + objectPropertyOffset, arguments); + return -1; + } else if (id >= m_type->signalOffset && call == QMetaObject::InvokeMetaMethod) { + QMetaObject::activate(m_data, this, id - m_type->signalOffset, 0); + return -1; + } else { + return m_data->qt_metacall(call, id, arguments); + } + } + + int createProperty(const char *name, const char *) + { + if (!m_data->object) + return -1; + const QMetaObject *metaObject = m_data->object->metaObject(); + static const int objectPropertyOffset = QObject::staticMetaObject.propertyCount(); + + const int previousPropertyCount = propertyCount() - propertyOffset(); + int propertyIndex = metaObject->indexOfProperty(name); + if (propertyIndex == -1) + return -1; + if (previousPropertyCount + objectPropertyOffset == metaObject->propertyCount()) + return propertyIndex + m_type->propertyOffset - objectPropertyOffset; + + if (m_type->shared) { + VDMObjectDelegateDataType *type = m_type; + m_type = new VDMObjectDelegateDataType(*m_type); + type->release(); + } + + const int previousMethodCount = methodCount(); + int notifierId = previousMethodCount - methodOffset(); + for (int propertyId = previousPropertyCount; propertyId < metaObject->propertyCount() - objectPropertyOffset; ++propertyId) { + QMetaProperty property = metaObject->property(propertyId + objectPropertyOffset); + QMetaPropertyBuilder propertyBuilder; + if (property.hasNotifySignal()) { + m_type->builder.addSignal("__" + QByteArray::number(propertyId) + "()"); + propertyBuilder = m_type->builder.addProperty(property.name(), property.typeName(), notifierId); + ++notifierId; + } else { + propertyBuilder = m_type->builder.addProperty(property.name(), property.typeName()); + } + propertyBuilder.setWritable(property.isWritable()); + propertyBuilder.setResettable(property.isResettable()); + propertyBuilder.setConstant(property.isConstant()); + } + + if (m_type->metaObject) + free(m_type->metaObject); + m_type->metaObject = m_type->builder.toMetaObject(); + *static_cast(this) = *m_type->metaObject; + + notifierId = previousMethodCount; + for (int i = previousPropertyCount; i < metaObject->propertyCount() - objectPropertyOffset; ++i) { + QMetaProperty property = metaObject->property(i + objectPropertyOffset); + if (property.hasNotifySignal()) { + QQmlPropertyPrivate::connect( + m_data->object, property.notifySignalIndex(), m_data, notifierId); + ++notifierId; + } + } + return propertyIndex + m_type->propertyOffset - objectPropertyOffset; + } + + QQmlDMObjectData *m_data; + VDMObjectDelegateDataType *m_type; +}; + +QQmlDMObjectData::QQmlDMObjectData( + QQmlDelegateModelItemMetaType *metaType, + VDMObjectDelegateDataType *dataType, + int index, + QObject *object) + : QQmlDelegateModelItem(metaType, index) + , object(object) +{ + new QQmlDMObjectDataMetaObject(this, dataType); +} + +//----------------------------------------------------------------- +// QQmlAdaptorModel +//----------------------------------------------------------------- + +static const QQmlAdaptorModel::Accessors qt_vdm_null_accessors; +static const VDMListDelegateDataType qt_vdm_list_accessors; + +QQmlAdaptorModel::Accessors::~Accessors() +{ +} + +QQmlAdaptorModel::QQmlAdaptorModel() + : accessors(&qt_vdm_null_accessors) +{ +} + +QQmlAdaptorModel::~QQmlAdaptorModel() +{ + accessors->cleanup(*this); +} + +void QQmlAdaptorModel::setModel(const QVariant &variant, QQmlDelegateModel *vdm, QQmlEngine *engine) +{ + accessors->cleanup(*this, vdm); + + list.setList(variant, engine); + + if (QObject *object = qvariant_cast(variant)) { + setObject(object); + if (QAbstractItemModel *model = qobject_cast(object)) { + accessors = new VDMAbstractItemModelDataType(this); + + qmlobject_connect(model, QAbstractItemModel, SIGNAL(rowsInserted(QModelIndex,int,int)), + vdm, QQmlDelegateModel, SLOT(_q_rowsInserted(QModelIndex,int,int))); + qmlobject_connect(model, QAbstractItemModel, SIGNAL(rowsRemoved(QModelIndex,int,int)), + vdm, QQmlDelegateModel, SLOT(_q_rowsRemoved(QModelIndex,int,int))); + qmlobject_connect(model, QAbstractItemModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), + vdm, QQmlDelegateModel, SLOT(_q_rowsAboutToBeRemoved(QModelIndex,int,int))); + qmlobject_connect(model, QAbstractItemModel, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector)), + vdm, QQmlDelegateModel, SLOT(_q_dataChanged(QModelIndex,QModelIndex,QVector))); + qmlobject_connect(model, QAbstractItemModel, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), + vdm, QQmlDelegateModel, SLOT(_q_rowsMoved(QModelIndex,int,int,QModelIndex,int))); + qmlobject_connect(model, QAbstractItemModel, SIGNAL(modelReset()), + vdm, QQmlDelegateModel, SLOT(_q_modelReset())); + qmlobject_connect(model, QAbstractItemModel, SIGNAL(layoutChanged()), + vdm, QQmlDelegateModel, SLOT(_q_layoutChanged())); + } else { + accessors = new VDMObjectDelegateDataType; + } + } else if (list.type() == QQmlListAccessor::ListProperty) { + setObject(static_cast(variant.constData())->object()); + accessors = new VDMObjectDelegateDataType; + } else if (list.type() != QQmlListAccessor::Invalid) { + Q_ASSERT(list.type() != QQmlListAccessor::Instance); // Should have cast to QObject. + setObject(0); + accessors = &qt_vdm_list_accessors; + } else { + setObject(0); + accessors = &qt_vdm_null_accessors; + } +} + +void QQmlAdaptorModel::invalidateModel(QQmlDelegateModel *vdm) +{ + accessors->cleanup(*this, vdm); + accessors = &qt_vdm_null_accessors; + // Don't clear the model object as we still need the guard to clear the list variant if the + // object is destroyed. +} + +bool QQmlAdaptorModel::isValid() const +{ + return accessors != &qt_vdm_null_accessors; +} + +void QQmlAdaptorModel::objectDestroyed(QObject *) +{ + setModel(QVariant(), 0, 0); +} + +QQmlAdaptorModelEngineData::QQmlAdaptorModelEngineData(QV8Engine *) +{ + strings = qPersistentNew(v8::Array::New(StringCount)); + strings->Set(Index, v8::String::New("index")); + strings->Set(ModelData, v8::String::New("modelData")); + strings->Set(HasModelChildren, v8::String::New("hasModelChildren")); + + v8::Local listItem = v8::FunctionTemplate::New(); + listItem->InstanceTemplate()->SetHasExternalResource(true); + listItem->InstanceTemplate()->SetAccessor(index(), get_index); + listItem->InstanceTemplate()->SetAccessor( + modelData(), + QQmlDMListAccessorData::get_modelData, + QQmlDMListAccessorData::set_modelData); + constructorListItem = qPersistentNew(listItem->GetFunction()); +} + +QQmlAdaptorModelEngineData::~QQmlAdaptorModelEngineData() +{ + qPersistentDispose(constructorListItem); + qPersistentDispose(strings); +} + +QT_END_NAMESPACE + +#include diff --git a/src/qml/util/qqmladaptormodel_p.h b/src/qml/util/qqmladaptormodel_p.h new file mode 100644 index 0000000000..6e9af8ebda --- /dev/null +++ b/src/qml/util/qqmladaptormodel_p.h @@ -0,0 +1,156 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLADAPTORMODEL_P_H +#define QQMLADAPTORMODEL_P_H + +#include + +#include "private/qqmllistaccessor_p.h" + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QQmlEngine; + +class QQmlDelegateModel; +class QQmlDelegateModelItem; +class QQmlDelegateModelItemMetaType; + +class QQmlAdaptorModel : public QQmlGuard +{ +public: + class Accessors + { + public: + inline Accessors() {} + virtual ~Accessors(); + virtual int count(const QQmlAdaptorModel &) const { return 0; } + virtual void cleanup(QQmlAdaptorModel &, QQmlDelegateModel * = 0) const {} + + virtual QVariant value(const QQmlAdaptorModel &, int, const QString &) const { + return QVariant(); } + + virtual QQmlDelegateModelItem *createItem( + QQmlAdaptorModel &, + QQmlDelegateModelItemMetaType *, + QQmlEngine *, + int) const { return 0; } + + virtual bool notify( + const QQmlAdaptorModel &, + const QList &, + int, + int, + const QVector &) const { return false; } + virtual void replaceWatchedRoles( + QQmlAdaptorModel &, + const QList &, + const QList &) const {} + virtual QVariant parentModelIndex(const QQmlAdaptorModel &) const { + return QVariant(); } + virtual QVariant modelIndex(const QQmlAdaptorModel &, int) const { + return QVariant(); } + virtual bool canFetchMore(const QQmlAdaptorModel &) const { return false; } + virtual void fetchMore(QQmlAdaptorModel &) const {} + }; + + const Accessors *accessors; + QPersistentModelIndex rootIndex; + QQmlListAccessor list; + + QQmlAdaptorModel(); + ~QQmlAdaptorModel(); + + inline QVariant model() const { return list.list(); } + void setModel(const QVariant &variant, QQmlDelegateModel *vdm, QQmlEngine *engine); + void invalidateModel(QQmlDelegateModel *vdm); + + bool isValid() const; + + inline QAbstractItemModel *aim() { return static_cast(object()); } + inline const QAbstractItemModel *aim() const { return static_cast(object()); } + + inline int count() const { return qMax(0, accessors->count(*this)); } + inline QVariant value(int index, const QString &role) const { + return accessors->value(*this, index, role); } + inline QQmlDelegateModelItem *createItem(QQmlDelegateModelItemMetaType *metaType, QQmlEngine *engine, int index) { + return accessors->createItem(*this, metaType, engine, index); } + inline bool hasProxyObject() const { + return list.type() == QQmlListAccessor::Instance || list.type() == QQmlListAccessor::ListProperty; } + + inline bool notify( + const QList &items, + int index, + int count, + const QVector &roles) const { + return accessors->notify(*this, items, index, count, roles); } + inline void replaceWatchedRoles( + const QList &oldRoles, const QList &newRoles) { + accessors->replaceWatchedRoles(*this, oldRoles, newRoles); } + + inline QVariant modelIndex(int index) const { return accessors->modelIndex(*this, index); } + inline QVariant parentModelIndex() const { return accessors->parentModelIndex(*this); } + inline bool canFetchMore() const { return accessors->canFetchMore(*this); } + inline void fetchMore() { return accessors->fetchMore(*this); } + +protected: + void objectDestroyed(QObject *); +}; + +class QQmlAdaptorModelProxyInterface +{ +public: + virtual ~QQmlAdaptorModelProxyInterface() {} + + virtual QObject *proxiedObject() = 0; +}; + +#define QQmlAdaptorModelProxyInterface_iid "org.qt-project.Qt.QQmlAdaptorModelProxyInterface" + +Q_DECLARE_INTERFACE(QQmlAdaptorModelProxyInterface, QQmlAdaptorModelProxyInterface_iid) + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/util/qqmlchangeset.cpp b/src/qml/util/qqmlchangeset.cpp new file mode 100644 index 0000000000..831cb063a5 --- /dev/null +++ b/src/qml/util/qqmlchangeset.cpp @@ -0,0 +1,621 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlchangeset_p.h" + +QT_BEGIN_NAMESPACE + + +/*! + \class QQmlChangeSet + \brief The QQmlChangeSet class stores an ordered list of notifications about + changes to a linear data set. + \internal + + QQmlChangeSet can be used to record a series of notifications about items in an indexed list + being inserted, removed, moved, and changed. Notifications in the set are re-ordered so that + all notifications of a single type are grouped together and sorted in order of ascending index, + with remove notifications preceding all others, followed by insert notification, and then + change notifications. + + Moves in a change set are represented by a remove notification paired with an insert + notification by way of a shared unique moveId. Re-ordering may result in one or both of the + paired notifications being divided, when this happens the offset member of the notification + will indicate the relative offset of the divided notification from the beginning of the + original. +*/ + +/*! + Constructs an empty change set. +*/ + +QQmlChangeSet::QQmlChangeSet() + : m_difference(0) +{ +} + +/*! + Constructs a copy of a \a changeSet. +*/ + +QQmlChangeSet::QQmlChangeSet(const QQmlChangeSet &changeSet) + : m_removes(changeSet.m_removes) + , m_inserts(changeSet.m_inserts) + , m_changes(changeSet.m_changes) + , m_difference(changeSet.m_difference) +{ +} + +/*! + Destroys a change set. +*/ + +QQmlChangeSet::~QQmlChangeSet() +{ +} + +/*! + Assigns the value of a \a changeSet to another. +*/ + +QQmlChangeSet &QQmlChangeSet::operator =(const QQmlChangeSet &changeSet) +{ + m_removes = changeSet.m_removes; + m_inserts = changeSet.m_inserts; + m_changes = changeSet.m_changes; + m_difference = changeSet.m_difference; + return *this; +} + +/*! + Appends a notification that \a count items were inserted at \a index. +*/ + +void QQmlChangeSet::insert(int index, int count) +{ + insert(QVector() << Insert(index, count)); +} + +/*! + Appends a notification that \a count items were removed at \a index. +*/ + +void QQmlChangeSet::remove(int index, int count) +{ + QVector removes; + removes.append(Remove(index, count)); + remove(&removes, 0); +} + +/*! + Appends a notification that \a count items were moved \a from one index \a to another. + + The \a moveId must be unique across the lifetime of the change set and any related + change sets. +*/ + +void QQmlChangeSet::move(int from, int to, int count, int moveId) +{ + QVector removes; + removes.append(Remove(from, count, moveId)); + QVector inserts; + inserts.append(Insert(to, count, moveId)); + remove(&removes, &inserts); + insert(inserts); +} + +/*! + Appends a notification that \a count items were changed at \a index. +*/ + +void QQmlChangeSet::change(int index, int count) +{ + QVector changes; + changes.append(Change(index, count)); + change(changes); +} + +/*! + Applies the changes in a \a changeSet to another. +*/ + +void QQmlChangeSet::apply(const QQmlChangeSet &changeSet) +{ + QVector r = changeSet.m_removes; + QVector i = changeSet.m_inserts; + QVector c = changeSet.m_changes; + remove(&r, &i); + insert(i); + change(c); +} + +/*! + Applies a list of \a removes to a change set. + + If a remove contains a moveId then any intersecting insert in the set will replace the + corresponding intersection in the optional \a inserts list. +*/ + +void QQmlChangeSet::remove(const QVector &removes, QVector *inserts) +{ + QVector r = removes; + remove(&r, inserts); +} + +void QQmlChangeSet::remove(QVector *removes, QVector *inserts) +{ + int removeCount = 0; + int insertCount = 0; + QVector::iterator insert = m_inserts.begin(); + QVector::iterator change = m_changes.begin(); + QVector::iterator rit = removes->begin(); + for (; rit != removes->end(); ++rit) { + int index = rit->index + removeCount; + int count = rit->count; + + // Decrement the accumulated remove count from the indexes of any changes prior to the + // current remove. + for (; change != m_changes.end() && change->end() < rit->index; ++change) + change->index -= removeCount; + // Remove any portion of a change notification that intersects the current remove. + for (; change != m_changes.end() && change->index > rit->end(); ++change) { + change->count -= qMin(change->end(), rit->end()) - qMax(change->index, rit->index); + if (change->count == 0) { + change = m_changes.erase(change); + } else if (rit->index < change->index) { + change->index = rit->index; + } + } + + // Decrement the accumulated remove count from the indexes of any inserts prior to the + // current remove. + for (; insert != m_inserts.end() && insert->end() <= index; ++insert) { + insertCount += insert->count; + insert->index -= removeCount; + } + + rit->index -= insertCount; + + // Remove any portion of a insert notification that intersects the current remove. + while (insert != m_inserts.end() && insert->index < index + count) { + int offset = index - insert->index; + const int difference = qMin(insert->end(), index + count) - qMax(insert->index, index); + + // If part of the remove or insert that precedes the intersection has a moveId create + // a new delta for that portion and subtract the size of that delta from the current + // one. + if (offset < 0 && rit->moveId != -1) { + rit = removes->insert(rit, Remove( + rit->index, -offset, rit->moveId, rit->offset)); + ++rit; + rit->count -= -offset; + rit->offset += -offset; + index += -offset; + count -= -offset; + removeCount += -offset; + offset = 0; + } else if (offset > 0 && insert->moveId != -1) { + insert = m_inserts.insert(insert, Insert( + insert->index - removeCount, offset, insert->moveId, insert->offset)); + ++insert; + insert->index += offset; + insert->count -= offset; + insert->offset += offset; + rit->index -= offset; + insertCount += offset; + } + + // If the current remove has a move id, find any inserts with the same move id and + // replace the corresponding sections with the insert removed from the change set. + if (rit->moveId != -1 && difference > 0 && inserts) { + for (QVector::iterator iit = inserts->begin(); iit != inserts->end(); ++iit) { + if (iit->moveId != rit->moveId + || rit->offset > iit->offset + iit->count + || iit->offset > rit->offset + difference) { + continue; + } + // If the intersecting insert starts before the replacement one create + // a new insert for the portion prior to the replacement insert. + const int overlapOffset = rit->offset - iit->offset; + if (overlapOffset > 0) { + iit = inserts->insert(iit, Insert( + iit->index, overlapOffset, iit->moveId, iit->offset)); + ++iit; + iit->index += overlapOffset; + iit->count -= overlapOffset; + iit->offset += overlapOffset; + } + if (iit->offset >= rit->offset + && iit->offset + iit->count <= rit->offset + difference) { + // If the replacement insert completely encapsulates the existing + // one just change the moveId. + iit->moveId = insert->moveId; + iit->offset = insert->offset + qMax(0, -overlapOffset); + } else { + // Create a new insertion before the intersecting one with the number of intersecting + // items and remove that number from that insert. + const int count + = qMin(iit->offset + iit->count, rit->offset + difference) + - qMax(iit->offset, rit->offset); + iit = inserts->insert(iit, Insert( + iit->index, + count, + insert->moveId, + insert->offset + qMax(0, -overlapOffset))); + ++iit; + iit->index += count; + iit->count -= count; + iit->offset += count; + } + } + } + + // Subtract the number of intersecting items from the current remove and insert. + insert->count -= difference; + insert->offset += difference; + rit->count -= difference; + rit->offset += difference; + + index += difference; + count -= difference; + removeCount += difference; + + if (insert->count == 0) { + insert = m_inserts.erase(insert); + } else if (rit->count == -offset || rit->count == 0) { + insert->index += difference; + break; + } else { + insert->index -= removeCount - difference; + rit->index -= insert->count; + insertCount += insert->count; + ++insert; + } + } + removeCount += rit->count; + } + for (; insert != m_inserts.end(); ++insert) + insert->index -= removeCount; + + removeCount = 0; + QVector::iterator remove = m_removes.begin(); + for (rit = removes->begin(); rit != removes->end(); ++rit) { + if (rit->count == 0) + continue; + // Accumulate consecutive removes into a single delta before attempting to apply. + for (QVector::iterator next = rit + 1; next != removes->end() + && next->index == rit->index + && next->moveId == -1 + && rit->moveId == -1; ++next) { + next->count += rit->count; + rit = next; + } + int index = rit->index + removeCount; + // Decrement the accumulated remove count from the indexes of any inserts prior to the + // current remove. + for (; remove != m_removes.end() && index > remove->index; ++remove) + remove->index -= removeCount; + while (remove != m_removes.end() && index + rit->count >= remove->index) { + int count = 0; + const int offset = remove->index - index; + QVector::iterator rend = remove; + for (; rend != m_removes.end() + && rit->moveId == -1 + && rend->moveId == -1 + && index + rit->count >= rend->index; ++rend) { + count += rend->count; + } + if (remove != rend) { + // Accumulate all existing non-move removes that are encapsulated by or immediately + // follow the current remove into it. + int difference = 0; + if (rend == m_removes.end()) { + difference = rit->count; + } else if (rit->index + rit->count < rend->index - removeCount) { + difference = rit->count; + } else if (rend->moveId != -1) { + difference = rend->index - removeCount - rit->index; + index += difference; + } + count += difference; + + rit->count -= difference; + removeCount += difference; + remove->index = rit->index; + remove->count = count; + remove = m_removes.erase(++remove, rend); + } else { + // Insert a remove for the portion of the unmergable current remove prior to the + // point of intersection. + if (offset > 0) { + remove = m_removes.insert(remove, Remove( + rit->index, offset, rit->moveId, rit->offset)); + ++remove; + rit->count -= offset; + rit->offset += offset; + removeCount += offset; + index += offset; + } + remove->index = rit->index; + + ++remove; + } + } + + if (rit->count > 0) { + remove = m_removes.insert(remove, *rit); + ++remove; + } + removeCount += rit->count; + } + for (; remove != m_removes.end(); ++remove) + remove->index -= removeCount; + m_difference -= removeCount; +} + +/*! + Applies a list of \a inserts to a change set. +*/ + +void QQmlChangeSet::insert(const QVector &inserts) +{ + int insertCount = 0; + QVector::iterator insert = m_inserts.begin(); + QVector::iterator change = m_changes.begin(); + for (QVector::const_iterator iit = inserts.begin(); iit != inserts.end(); ++iit) { + if (iit->count == 0) + continue; + int index = iit->index - insertCount; + + Insert current = *iit; + // Accumulate consecutive inserts into a single delta before attempting to insert. + for (QVector::const_iterator next = iit + 1; next != inserts.end() + && next->index == iit->index + iit->count + && next->moveId == -1 + && iit->moveId == -1; ++next) { + current.count += next->count; + iit = next; + } + + // Increment the index of any changes before the current insert by the accumlated insert + // count. + for (; change != m_changes.end() && change->index >= index; ++change) + change->index += insertCount; + // If the current insert index is in the middle of a change split it in two at that + // point and increment the index of the latter half. + if (change != m_changes.end() && change->index < index + iit->count) { + int offset = index - change->index; + change = m_changes.insert(change, Change(change->index + insertCount, offset)); + ++change; + change->index += iit->count + offset; + change->count -= offset; + } + + // Increment the index of any inserts before the current insert by the accumlated insert + // count. + for (; insert != m_inserts.end() && index > insert->index + insert->count; ++insert) + insert->index += insertCount; + if (insert == m_inserts.end()) { + insert = m_inserts.insert(insert, current); + ++insert; + } else { + const int offset = index - insert->index; + + if (offset < 0) { + // If the current insert is before an existing insert and not adjacent just insert + // it into the list. + insert = m_inserts.insert(insert, current); + ++insert; + } else if (iit->moveId == -1 && insert->moveId == -1) { + // If neither the current nor existing insert has a moveId add the current insert + // to the existing one. + if (offset < insert->count) { + insert->index -= current.count; + insert->count += current.count; + } else { + insert->index += insertCount; + insert->count += current.count; + ++insert; + } + } else if (offset < insert->count) { + // If either insert has a moveId then split the existing insert and insert the + // current one in the middle. + if (offset > 0) { + insert = m_inserts.insert(insert, Insert( + insert->index + insertCount, offset, insert->moveId, insert->offset)); + ++insert; + insert->index += offset; + insert->count -= offset; + insert->offset += offset; + } + insert = m_inserts.insert(insert, current); + ++insert; + } else { + insert->index += insertCount; + ++insert; + insert = m_inserts.insert(insert, current); + ++insert; + } + } + insertCount += current.count; + } + for (; insert != m_inserts.end(); ++insert) + insert->index += insertCount; + m_difference += insertCount; +} + +/*! + Applies a combined list of \a removes and \a inserts to a change set. This is equivalent + calling \l remove() followed by \l insert() with the same lists. +*/ + +void QQmlChangeSet::move(const QVector &removes, const QVector &inserts) +{ + QVector r = removes; + QVector i = inserts; + remove(&r, &i); + insert(i); +} + +/*! + Applies a list of \a changes to a change set. +*/ + +void QQmlChangeSet::change(const QVector &changes) +{ + QVector c = changes; + change(&c); +} + +void QQmlChangeSet::change(QVector *changes) +{ + QVector::iterator insert = m_inserts.begin(); + QVector::iterator change = m_changes.begin(); + for (QVector::iterator cit = changes->begin(); cit != changes->end(); ++cit) { + for (; insert != m_inserts.end() && insert->end() < cit->index; ++insert) {} + for (; insert != m_inserts.end() && insert->index < cit->end(); ++insert) { + const int offset = insert->index - cit->index; + const int count = cit->count + cit->index - insert->index - insert->count; + if (offset == 0) { + cit->index = insert->index + insert->count; + cit->count = count; + } else { + cit = changes->insert(++cit, Change(insert->index + insert->count, count)); + --cit; + cit->count = offset; + } + } + + for (; change != m_changes.end() && change->index + change->count < cit->index; ++change) {} + if (change == m_changes.end() || change->index > cit->index + cit->count) { + if (cit->count > 0) { + change = m_changes.insert(change, *cit); + ++change; + } + } else { + if (cit->index < change->index) { + change->count += change->index - cit->index; + change->index = cit->index; + } + + if (cit->index + cit->count > change->index + change->count) { + change->count = cit->index + cit->count - change->index; + QVector::iterator cbegin = change; + QVector::iterator cend = ++cbegin; + for (; cend != m_changes.end() && cend->index <= change->index + change->count; ++cend) { + if (cend->index + cend->count > change->index + change->count) + change->count = cend->index + cend->count - change->index; + } + if (cbegin != cend) { + change = m_changes.erase(cbegin, cend); + --change; + } + } + } + } +} + +/*! + Prints the contents of a change \a set to the \a debug stream. +*/ + +QDebug operator <<(QDebug debug, const QQmlChangeSet &set) +{ + debug.nospace() << "QQmlChangeSet("; + foreach (const QQmlChangeSet::Remove &remove, set.removes()) debug << remove; + foreach (const QQmlChangeSet::Insert &insert, set.inserts()) debug << insert; + foreach (const QQmlChangeSet::Change &change, set.changes()) debug << change; + return debug.nospace() << ')'; +} + +/*! + Prints a \a remove to the \a debug stream. +*/ + +QDebug operator <<(QDebug debug, const QQmlChangeSet::Remove &remove) +{ + if (remove.moveId == -1) { + return (debug.nospace() + << "Remove(" << remove.index + << ',' << remove.count + << ')').space(); + } else { + return (debug.nospace() + << "Remove(" << remove.index + << ',' << remove.count + << ',' << remove.moveId + << ',' << remove.offset + << ')').space(); + } +} + +/*! + Prints an \a insert to the \a debug stream. +*/ + +QDebug operator <<(QDebug debug, const QQmlChangeSet::Insert &insert) +{ + if (insert.moveId == -1) { + return (debug.nospace() + << "Insert(" << insert.index + << ',' << insert.count + << ')').space(); + } else { + return (debug.nospace() + << "Insert(" << insert.index + << ',' << insert.count + << ',' << insert.moveId + << ',' << insert.offset + << ')').space(); + } +} + +/*! + Prints a \a change to the \a debug stream. +*/ + +QDebug operator <<(QDebug debug, const QQmlChangeSet::Change &change) +{ + return (debug.nospace() << "Change(" << change.index << ',' << change.count << ')').space(); +} + +QT_END_NAMESPACE + diff --git a/src/qml/util/qqmlchangeset_p.h b/src/qml/util/qqmlchangeset_p.h new file mode 100644 index 0000000000..acafbd4eec --- /dev/null +++ b/src/qml/util/qqmlchangeset_p.h @@ -0,0 +1,167 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLCHANGESET_P_H +#define QQMLCHANGESET_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class Q_QML_PRIVATE_EXPORT QQmlChangeSet +{ +public: + struct MoveKey + { + MoveKey() : moveId(-1), offset(0) {} + MoveKey(int moveId, int offset) : moveId(moveId), offset(offset) {} + int moveId; + int offset; + }; + + struct Change + { + Change() : index(0), count(0), moveId(-1) {} + Change(int index, int count, int moveId = -1, int offset = 0) + : index(index), count(count), moveId(moveId), offset(offset) {} + + int index; + int count; + int moveId; + int offset; + + bool isMove() const { return moveId >= 0; } + + MoveKey moveKey(int index) const { + return MoveKey(moveId, index - Change::index + offset); } + + int start() const { return index; } + int end() const { return index + count; } + }; + + + struct Insert : public Change + { + Insert() {} + Insert(int index, int count, int moveId = -1, int offset = 0) + : Change(index, count, moveId, offset) {} + }; + + struct Remove : public Change + { + Remove() {} + Remove(int index, int count, int moveId = -1, int offset = 0) + : Change(index, count, moveId, offset) {} + }; + + QQmlChangeSet(); + QQmlChangeSet(const QQmlChangeSet &changeSet); + ~QQmlChangeSet(); + + QQmlChangeSet &operator =(const QQmlChangeSet &changeSet); + + const QVector &removes() const { return m_removes; } + const QVector &inserts() const { return m_inserts; } + const QVector &changes() const { return m_changes; } + + void insert(int index, int count); + void remove(int index, int count); + void move(int from, int to, int count, int moveId); + void change(int index, int count); + + void insert(const QVector &inserts); + void remove(const QVector &removes, QVector *inserts = 0); + void move(const QVector &removes, const QVector &inserts); + void change(const QVector &changes); + void apply(const QQmlChangeSet &changeSet); + + bool isEmpty() const { return m_removes.empty() && m_inserts.empty() && m_changes.isEmpty(); } + + void clear() + { + m_removes.clear(); + m_inserts.clear(); + m_changes.clear(); + m_difference = 0; + } + + int difference() const { return m_difference; } + +private: + void remove(QVector *removes, QVector *inserts); + void change(QVector *changes); + + QVector m_removes; + QVector m_inserts; + QVector m_changes; + int m_difference; +}; + +Q_DECLARE_TYPEINFO(QQmlChangeSet::Change, Q_PRIMITIVE_TYPE); +Q_DECLARE_TYPEINFO(QQmlChangeSet::Remove, Q_PRIMITIVE_TYPE); +Q_DECLARE_TYPEINFO(QQmlChangeSet::Insert, Q_PRIMITIVE_TYPE); +Q_DECLARE_TYPEINFO(QQmlChangeSet::MoveKey, Q_PRIMITIVE_TYPE); + +inline uint qHash(const QQmlChangeSet::MoveKey &key) { return qHash(qMakePair(key.moveId, key.offset)); } +inline bool operator ==(const QQmlChangeSet::MoveKey &l, const QQmlChangeSet::MoveKey &r) { + return l.moveId == r.moveId && l.offset == r.offset; } + +Q_QML_PRIVATE_EXPORT QDebug operator <<(QDebug debug, const QQmlChangeSet::Remove &remove); +Q_QML_PRIVATE_EXPORT QDebug operator <<(QDebug debug, const QQmlChangeSet::Insert &insert); +Q_QML_PRIVATE_EXPORT QDebug operator <<(QDebug debug, const QQmlChangeSet::Change &change); +Q_QML_PRIVATE_EXPORT QDebug operator <<(QDebug debug, const QQmlChangeSet &change); + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/util/qqmllistaccessor.cpp b/src/qml/util/qqmllistaccessor.cpp new file mode 100644 index 0000000000..2a2bd74a54 --- /dev/null +++ b/src/qml/util/qqmllistaccessor.cpp @@ -0,0 +1,138 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmllistaccessor_p.h" + +#include + +#include +#include + +// ### Remove me +#include + +QT_BEGIN_NAMESPACE + +QQmlListAccessor::QQmlListAccessor() +: m_type(Invalid) +{ +} + +QQmlListAccessor::~QQmlListAccessor() +{ +} + +QVariant QQmlListAccessor::list() const +{ + return d; +} + +void QQmlListAccessor::setList(const QVariant &v, QQmlEngine *engine) +{ + d = v; + + QQmlEnginePrivate *enginePrivate = engine?QQmlEnginePrivate::get(engine):0; + + if (!d.isValid()) { + m_type = Invalid; + } else if (d.userType() == QVariant::StringList) { + m_type = StringList; + } else if (d.userType() == QMetaType::QVariantList) { + m_type = VariantList; + } else if (d.canConvert(QVariant::Int)) { + m_type = Integer; + } else if ((!enginePrivate && QQmlMetaType::isQObject(d.userType())) || + (enginePrivate && enginePrivate->isQObject(d.userType()))) { + QObject *data = enginePrivate?enginePrivate->toQObject(v):QQmlMetaType::toQObject(v); + d = QVariant::fromValue(data); + m_type = Instance; + } else if (d.userType() == qMetaTypeId()) { + m_type = ListProperty; + } else { + m_type = Instance; + } +} + +int QQmlListAccessor::count() const +{ + switch(m_type) { + case StringList: + return qvariant_cast(d).count(); + case VariantList: + return qvariant_cast(d).count(); + case ListProperty: + return ((QQmlListReference *)d.constData())->count(); + case Instance: + return 1; + case Integer: + return d.toInt(); + default: + case Invalid: + return 0; + } +} + +QVariant QQmlListAccessor::at(int idx) const +{ + Q_ASSERT(idx >= 0 && idx < count()); + switch(m_type) { + case StringList: + return QVariant::fromValue(qvariant_cast(d).at(idx)); + case VariantList: + return qvariant_cast(d).at(idx); + case ListProperty: + return QVariant::fromValue(((QQmlListReference *)d.constData())->at(idx)); + case Instance: + return d; + case Integer: + return QVariant(idx); + default: + case Invalid: + return QVariant(); + } +} + +bool QQmlListAccessor::isValid() const +{ + return m_type != Invalid; +} + +QT_END_NAMESPACE diff --git a/src/qml/util/qqmllistaccessor_p.h b/src/qml/util/qqmllistaccessor_p.h new file mode 100644 index 0000000000..71f7542105 --- /dev/null +++ b/src/qml/util/qqmllistaccessor_p.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLLISTACCESSOR_H +#define QQMLLISTACCESSOR_H + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QQmlEngine; +class Q_AUTOTEST_EXPORT QQmlListAccessor +{ +public: + QQmlListAccessor(); + ~QQmlListAccessor(); + + QVariant list() const; + void setList(const QVariant &, QQmlEngine * = 0); + + bool isValid() const; + + int count() const; + QVariant at(int) const; + + enum Type { Invalid, StringList, VariantList, ListProperty, Instance, Integer }; + Type type() const { return m_type; } + +private: + Type m_type; + QVariant d; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QQMLLISTACCESSOR_H diff --git a/src/qml/util/qqmllistcompositor.cpp b/src/qml/util/qqmllistcompositor.cpp new file mode 100644 index 0000000000..75d2f67b51 --- /dev/null +++ b/src/qml/util/qqmllistcompositor.cpp @@ -0,0 +1,1484 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmllistcompositor_p.h" + +#include + +//#define QT_QML_VERIFY_MINIMAL +//#define QT_QML_VERIFY_INTEGRITY + +QT_BEGIN_NAMESPACE + +/*! + \class QQmlListCompositor + \brief The QQmlListCompositor class provides a lookup table for filtered, or re-ordered list + indexes. + \internal + + QQmlListCompositor is intended as an aid for developing proxy models. It doesn't however + directly proxy a list or model, instead a range of indexes from one or many lists can be + inserted into the compositor and then categorized and shuffled around and it will manage the + task of translating from an index in the combined space into an index in a particular list. + + Within a compositor indexes are categorized into groups where a group is a sub-set of the + total indexes referenced by the compositor, each with an address space ranging from 0 to + the number of indexes in the group - 1. Group memberships are independent of each other with + the one exception that items always retain the same order so if an index is moved within a + group, its position in other groups will change as well. + + The iterator classes encapsulate information about a specific position in a compositor group. + This includes a source list, the index of an item within that list and the groups that item + is a member of. The iterator for a specific position in a group can be retrieved with the + find() function and the addition and subtraction operators of the iterators can be used to + navigate to adjacent items in the same group. + + Items can be added to the compositor with the append() and insert() functions, group + membership can be changed with the setFlags() and clearFlags() functions, and the position + of items in the compositor can be changed with the move() function. Each of these functions + optionally returns a list of the changes made to indexes within each group which can then + be propagated to view so that it can correctly refresh its contents; e.g. 3 items + removed at index 6, and 5 items inserted at index 1. The notification changes are always + ordered from the start of the list to the end and accumulate, so if 5 items are removed at + index 4, one is skipped and then 3 move are removed, the changes returned are 5 items removed + at index 4, followed by 3 items removed at index 4. + + When the contents of a source list change, the mappings within the compositor can be updated + with the listItemsInserted(), listItemsRemoved(), listItemsMoved(), and listItemsChanged() + functions. Like the direct manipulation functions these too return a list of group indexes + affected by the change. If items are removed from a source list they are also removed from + any groups they belong to with the one exception being items belonging to the \l Cache group. + When items belonging to this group are removed the list, index, and other group membership + information are discarded but Cache membership is retained until explicitly removed. This + allows the cache index to be retained until cached resources for that item are actually + released. + + Internally the index mapping is stored as a list of Range objects, each has a list identifier, + a start index, a count, and a set of flags which represent group membership and some other + properties. The group index of a range is the sum of all preceding ranges that are members of + that group. To avoid the inefficiency of iterating over potentially all ranges when looking + for a specific index, each time a lookup is done the range and its indexes are cached and the + next lookup is done relative to this. This works out to near constant time in most relevant + use cases because successive index lookups are most frequently adjacent. The total number of + ranges is often quite small, which helps as well. If there is a need for faster random access + then a skip list like index may be an appropriate addition. + + \sa VisualDataModel +*/ + +#ifdef QT_QML_VERIFY_MINIMAL +#define QT_QML_VERIFY_INTEGRITY +/* + Diagnostic to verify there are no consecutive ranges, or that the compositor contains the + most compact representation possible. + + Returns false and prints a warning if any range has a starting index equal to the end + (index + count) index of the previous range, and both ranges also have the same flags and list + property. + + If there are no consecutive ranges this will return true. +*/ + +static bool qt_verifyMinimal( + const QQmlListCompositor::iterator &begin, + const QQmlListCompositor::iterator &end) +{ + bool minimal = true; + int index = 0; + + for (const QQmlListCompositor::Range *range = begin->next; range != *end; range = range->next, ++index) { + if (range->previous->list == range->list + && range->previous->flags == (range->flags & ~QQmlListCompositor::AppendFlag) + && range->previous->end() == range->index) { + qWarning() << index << "Consecutive ranges"; + qWarning() << *range->previous; + qWarning() << *range; + minimal = false; + } + } + + return minimal; +} + +#endif + +#ifdef QT_QML_VERIFY_INTEGRITY +static bool qt_printInfo(const QQmlListCompositor &compositor) +{ + qWarning() << compositor; + return true; +} + +/* + Diagnostic to verify the integrity of a compositor. + + Per range this verifies there are no invalid range combinations, that non-append ranges have + positive non-zero counts, and that list ranges have non-negative indexes. + + Accumulatively this verifies that the cached total group counts match the sum of counts + of member ranges. +*/ + +static bool qt_verifyIntegrity( + const QQmlListCompositor::iterator &begin, + const QQmlListCompositor::iterator &end, + const QQmlListCompositor::iterator &cachedIt) +{ + bool valid = true; + + int index = 0; + QQmlListCompositor::iterator it; + for (it = begin; *it != *end; *it = it->next) { + if (it->count == 0 && !it->append()) { + qWarning() << index << "Empty non-append range"; + valid = false; + } + if (it->count < 0) { + qWarning() << index << "Negative count"; + valid = false; + } + if (it->list && it->flags != QQmlListCompositor::CacheFlag && it->index < 0) { + qWarning() << index <<"Negative index"; + valid = false; + } + if (it->previous->next != it.range) { + qWarning() << index << "broken list: it->previous->next != it.range"; + valid = false; + } + if (it->next->previous != it.range) { + qWarning() << index << "broken list: it->next->previous != it.range"; + valid = false; + } + if (*it == *cachedIt) { + for (int i = 0; i < end.groupCount; ++i) { + int groupIndex = it.index[i]; + if (cachedIt->flags & (1 << i)) + groupIndex += cachedIt.offset; + if (groupIndex != cachedIt.index[i]) { + qWarning() << index + << "invalid cached index" + << QQmlListCompositor::Group(i) + << "Expected:" + << groupIndex + << "Actual" + << cachedIt.index[i] + << cachedIt; + valid = false; + } + } + } + it.incrementIndexes(it->count); + ++index; + } + + for (int i = 0; i < end.groupCount; ++i) { + if (end.index[i] != it.index[i]) { + qWarning() << "Group" << i << "count invalid. Expected:" << end.index[i] << "Actual:" << it.index[i]; + valid = false; + } + } + return valid; +} +#endif + +#if defined(QT_QML_VERIFY_MINIMAL) +# define QT_QML_VERIFY_LISTCOMPOSITOR Q_ASSERT(!(!(qt_verifyIntegrity(iterator(m_ranges.next, 0, Default, m_groupCount), m_end, m_cacheIt) \ + && qt_verifyMinimal(iterator(m_ranges.next, 0, Default, m_groupCount), m_end)) \ + && qt_printInfo(*this))); +#elif defined(QT_QML_VERIFY_INTEGRITY) +# define QT_QML_VERIFY_LISTCOMPOSITOR Q_ASSERT(!(!qt_verifyIntegrity(iterator(m_ranges.next, 0, Default, m_groupCount), m_end, m_cacheIt) \ + && qt_printInfo(*this))); +#else +# define QT_QML_VERIFY_LISTCOMPOSITOR +#endif + +//#define QT_QML_TRACE_LISTCOMPOSITOR(args) qDebug() << m_end.index[1] << m_end.index[0] << Q_FUNC_INFO args; +#define QT_QML_TRACE_LISTCOMPOSITOR(args) + +QQmlListCompositor::iterator &QQmlListCompositor::iterator::operator +=(int difference) +{ + // Update all indexes to the start of the range. + decrementIndexes(offset); + + // If the iterator group isn't a member of the current range ignore the current offset. + if (!(range->flags & groupFlag)) + offset = 0; + + offset += difference; + + // Iterate backwards looking for a range with a positive offset. + while (offset <= 0 && range->previous->flags) { + range = range->previous; + if (range->flags & groupFlag) + offset += range->count; + decrementIndexes(range->count); + } + + // Iterate forwards looking for the first range which contains both the offset and the + // iterator group. + while (range->flags && (offset >= range->count || !(range->flags & groupFlag))) { + if (range->flags & groupFlag) + offset -= range->count; + incrementIndexes(range->count); + range = range->next; + } + + // Update all the indexes to inclue the remaining offset. + incrementIndexes(offset); + + return *this; +} + +QQmlListCompositor::insert_iterator &QQmlListCompositor::insert_iterator::operator +=(int difference) +{ + iterator::operator +=(difference); + + // If the previous range contains the append flag move the iterator to the tail of the previous + // range so that appended appear after the insert position. + if (offset == 0 && range->previous->append()) { + range = range->previous; + offset = range->inGroup() ? range->count : 0; + } + + return *this; +} + + +/*! + Constructs an empty list compositor. +*/ + +QQmlListCompositor::QQmlListCompositor() + : m_end(m_ranges.next, 0, Default, 2) + , m_cacheIt(m_end) + , m_groupCount(2) + , m_defaultFlags(PrependFlag | DefaultFlag) + , m_removeFlags(AppendFlag | PrependFlag | GroupMask) + , m_moveId(0) +{ +} + +/*! + Destroys a list compositor. +*/ + +QQmlListCompositor::~QQmlListCompositor() +{ + for (Range *next, *range = m_ranges.next; range != &m_ranges; range = next) { + next = range->next; + delete range; + } +} + +/*! + Inserts a range with the given source \a list, start \a index, \a count and \a flags, in front + of the existing range \a before. +*/ + +inline QQmlListCompositor::Range *QQmlListCompositor::insert( + Range *before, void *list, int index, int count, uint flags) +{ + return new Range(before, list, index, count, flags); +} + +/*! + Erases a \a range from the compositor. + + Returns a pointer to the next range in the compositor. +*/ + +inline QQmlListCompositor::Range *QQmlListCompositor::erase( + Range *range) +{ + Range *next = range->next; + next->previous = range->previous; + next->previous->next = range->next; + delete range; + return next; +} + +/*! + Sets the number (\a count) of possible groups that items may belong to in a compositor. +*/ + +void QQmlListCompositor::setGroupCount(int count) +{ + m_groupCount = count; + m_end = iterator(&m_ranges, 0, Default, m_groupCount); + m_cacheIt = m_end; +} + +/*! + Returns the number of items that belong to a \a group. +*/ + +int QQmlListCompositor::count(Group group) const +{ + return m_end.index[group]; +} + +/*! + Returns an iterator representing the item at \a index in a \a group. + + The index must be between 0 and count(group) - 1. +*/ + +QQmlListCompositor::iterator QQmlListCompositor::find(Group group, int index) +{ + QT_QML_TRACE_LISTCOMPOSITOR(<< group << index) + Q_ASSERT(index >=0 && index < count(group)); + if (m_cacheIt == m_end) { + m_cacheIt = iterator(m_ranges.next, 0, group, m_groupCount); + m_cacheIt += index; + } else { + const int offset = index - m_cacheIt.index[group]; + m_cacheIt.setGroup(group); + m_cacheIt += offset; + } + Q_ASSERT(m_cacheIt.index[group] == index); + Q_ASSERT(m_cacheIt->inGroup(group)); + QT_QML_VERIFY_LISTCOMPOSITOR + return m_cacheIt; +} + +/*! + Returns an iterator representing the item at \a index in a \a group. + + The index must be between 0 and count(group) - 1. +*/ + +QQmlListCompositor::iterator QQmlListCompositor::find(Group group, int index) const +{ + return const_cast(this)->find(group, index); +} + +/*! + Returns an iterator representing an insert position in front of the item at \a index in a + \a group. + + The iterator for an insert position can sometimes resolve to a different Range than a regular + iterator. This is because when items are inserted on a boundary between Ranges, if the first + range has the Append flag set then the items should be inserted into that range to ensure + that the append position for the existing range remains after the insert position. + + The index must be between 0 and count(group) - 1. +*/ + +QQmlListCompositor::insert_iterator QQmlListCompositor::findInsertPosition(Group group, int index) +{ + QT_QML_TRACE_LISTCOMPOSITOR(<< group << index) + Q_ASSERT(index >=0 && index <= count(group)); + insert_iterator it; + if (m_cacheIt == m_end) { + it = iterator(m_ranges.next, 0, group, m_groupCount); + it += index; + } else { + const int offset = index - m_cacheIt.index[group]; + it = m_cacheIt; + it.setGroup(group); + it += offset; + } + Q_ASSERT(it.index[group] == index); + return it; +} + +/*! + Appends a range of \a count indexes starting at \a index from a \a list into a compositor + with the given \a flags. + + If supplied the \a inserts list will be populated with the positions of the inserted items + in each group. +*/ + +void QQmlListCompositor::append( + void *list, int index, int count, uint flags, QVector *inserts) +{ + QT_QML_TRACE_LISTCOMPOSITOR(<< list << index << count << flags) + insert(m_end, list, index, count, flags, inserts); +} + +/*! + Inserts a range of \a count indexes starting at \a index from a \a list with the given \a flags + into a \a group at index \a before. + + If supplied the \a inserts list will be populated with the positions of items inserted into + each group. +*/ + +void QQmlListCompositor::insert( + Group group, int before, void *list, int index, int count, uint flags, QVector *inserts) +{ + QT_QML_TRACE_LISTCOMPOSITOR(<< group << before << list << index << count << flags) + insert(findInsertPosition(group, before), list, index, count, flags, inserts); +} + +/*! + Inserts a range of \a count indexes starting at \a index from a \a list with the given \a flags + into a compositor at position \a before. + + If supplied the \a inserts list will be populated with the positions of items inserted into + each group. +*/ + +QQmlListCompositor::iterator QQmlListCompositor::insert( + iterator before, void *list, int index, int count, uint flags, QVector *inserts) +{ + QT_QML_TRACE_LISTCOMPOSITOR(<< before << list << index << count << flags) + if (inserts) { + inserts->append(Insert(before, count, flags & GroupMask)); + } + if (before.offset > 0) { + // Inserting into the middle of a range. Split it two and update the iterator so it's + // positioned at the start of the second half. + *before = insert( + *before, before->list, before->index, before.offset, before->flags & ~AppendFlag)->next; + before->index += before.offset; + before->count -= before.offset; + before.offset = 0; + } + + + if (!(flags & AppendFlag) && *before != m_ranges.next + && before->previous->list == list + && before->previous->flags == flags + && (!list || before->previous->end() == index)) { + // The insert arguments represent a continuation of the previous range so increment + // its count instead of inserting a new range. + before->previous->count += count; + before.incrementIndexes(count, flags); + } else { + *before = insert(*before, list, index, count, flags); + before.offset = 0; + } + + if (!(flags & AppendFlag) && before->next != &m_ranges + && before->list == before->next->list + && before->flags == before->next->flags + && (!list || before->end() == before->next->index)) { + // The current range and the next are continuous so add their counts and delete one. + before->next->index = before->index; + before->next->count += before->count; + *before = erase(*before); + } + + m_end.incrementIndexes(count, flags); + m_cacheIt = before; + QT_QML_VERIFY_LISTCOMPOSITOR + return before; +} + +/*! + Sets the given flags \a flags on \a count items belonging to \a group starting at the position + identified by \a fromGroup and the index \a from. + + If supplied the \a inserts list will be populated with insert notifications for affected groups. +*/ + +void QQmlListCompositor::setFlags( + Group fromGroup, int from, int count, Group group, int flags, QVector *inserts) +{ + QT_QML_TRACE_LISTCOMPOSITOR(<< fromGroup << from << count << group << flags) + setFlags(find(fromGroup, from), count, group, flags, inserts); +} + +/*! + Sets the given flags \a flags on \a count items belonging to \a group starting at the position + \a from. + + If supplied the \a inserts list will be populated with insert notifications for affected groups. +*/ + +void QQmlListCompositor::setFlags( + iterator from, int count, Group group, uint flags, QVector *inserts) +{ + QT_QML_TRACE_LISTCOMPOSITOR(<< from << count << flags) + if (!flags || !count) + return; + + if (from != group) { + // Skip to the next full range if the start one is not a member of the target group. + from.incrementIndexes(from->count - from.offset); + from.offset = 0; + *from = from->next; + } else if (from.offset > 0) { + // If the start position is mid range split off the portion unaffected. + *from = insert(*from, from->list, from->index, from.offset, from->flags & ~AppendFlag)->next; + from->index += from.offset; + from->count -= from.offset; + from.offset = 0; + } + + for (; count > 0; *from = from->next) { + if (from != from.group) { + // Skip ranges that are not members of the target group. + from.incrementIndexes(from->count); + continue; + } + // Find the number of items affected in the current range. + const int difference = qMin(count, from->count); + count -= difference; + + // Determine the actual changes made to the range and increment counts accordingly. + const uint insertFlags = ~from->flags & flags; + const uint setFlags = (from->flags | flags) & ~AppendFlag; + if (insertFlags && inserts) + inserts->append(Insert(from, difference, insertFlags | (from->flags & CacheFlag))); + m_end.incrementIndexes(difference, insertFlags); + from.incrementIndexes(difference, setFlags); + + if (from->previous != &m_ranges + && from->previous->list == from->list + && (!from->list || from->previous->end() == from->index) + && from->previous->flags == setFlags) { + // If the additional flags make the current range a continuation of the previous + // then move the affected items over to the previous range. + from->previous->count += difference; + from->index += difference; + from->count -= difference; + if (from->count == 0) { + // Delete the current range if it is now empty, preserving the append flag + // in the previous range. + if (from->append()) + from->previous->flags |= AppendFlag; + *from = erase(*from)->previous; + continue; + } else { + break; + } + } else if (!insertFlags) { + // No new flags, so roll onto the next range. + from.incrementIndexes(from->count - difference); + continue; + } else if (difference < from->count) { + // Create a new range with the updated flags, and remove the affected items + // from the current range. + *from = insert(*from, from->list, from->index, difference, setFlags)->next; + from->index += difference; + from->count -= difference; + } else { + // The whole range is affected so simply update the flags. + from->flags |= flags; + continue; + } + from.incrementIndexes(from->count); + } + + if (from->previous != &m_ranges + && from->previous->list == from->list + && (!from->list || from->previous->end() == from->index) + && from->previous->flags == (from->flags & ~AppendFlag)) { + // If the following range is now a continuation, merge it with its previous range. + from.offset = from->previous->count; + from->previous->count += from->count; + from->previous->flags = from->flags; + *from = erase(*from)->previous; + } + m_cacheIt = from; + QT_QML_VERIFY_LISTCOMPOSITOR +} + +/*! + Clears the given flags \a flags on \a count items belonging to \a group starting at the position + \a from. + + If supplied the \a removes list will be populated with remove notifications for affected groups. +*/ + +void QQmlListCompositor::clearFlags( + Group fromGroup, int from, int count, Group group, uint flags, QVector *removes) +{ + QT_QML_TRACE_LISTCOMPOSITOR(<< fromGroup << from << count << group << flags) + clearFlags(find(fromGroup, from), count, group, flags, removes); +} + +/*! + Clears the given flags \a flags on \a count items belonging to \a group starting at the position + identified by \a fromGroup and the index \a from. + + If supplied the \a removes list will be populated with remove notifications for affected groups. +*/ + +void QQmlListCompositor::clearFlags( + iterator from, int count, Group group, uint flags, QVector *removes) +{ + QT_QML_TRACE_LISTCOMPOSITOR(<< from << count << flags) + if (!flags || !count) + return; + + const bool clearCache = flags & CacheFlag; + + if (from != group) { + // Skip to the next full range if the start one is not a member of the target group. + from.incrementIndexes(from->count - from.offset); + from.offset = 0; + *from = from->next; + } else if (from.offset > 0) { + // If the start position is mid range split off the portion unaffected. + *from = insert(*from, from->list, from->index, from.offset, from->flags & ~AppendFlag)->next; + from->index += from.offset; + from->count -= from.offset; + from.offset = 0; + } + + for (; count > 0; *from = from->next) { + if (from != group) { + // Skip ranges that are not members of the target group. + from.incrementIndexes(from->count); + continue; + } + // Find the number of items affected in the current range. + const int difference = qMin(count, from->count); + count -= difference; + + + // Determine the actual changes made to the range and decrement counts accordingly. + const uint removeFlags = from->flags & flags & ~(AppendFlag | PrependFlag); + const uint clearedFlags = from->flags & ~(flags | AppendFlag | UnresolvedFlag); + if (removeFlags && removes) { + const int maskedFlags = clearCache + ? (removeFlags & ~CacheFlag) + : (removeFlags | (from->flags & CacheFlag)); + if (maskedFlags) + removes->append(Remove(from, difference, maskedFlags)); + } + m_end.decrementIndexes(difference, removeFlags); + from.incrementIndexes(difference, clearedFlags); + + if (from->previous != &m_ranges + && from->previous->list == from->list + && (!from->list || clearedFlags == CacheFlag || from->previous->end() == from->index) + && from->previous->flags == clearedFlags) { + // If the removed flags make the current range a continuation of the previous + // then move the affected items over to the previous range. + from->previous->count += difference; + from->index += difference; + from->count -= difference; + if (from->count == 0) { + // Delete the current range if it is now empty, preserving the append flag + if (from->append()) + from->previous->flags |= AppendFlag; + *from = erase(*from)->previous; + } else { + from.incrementIndexes(from->count); + } + } else if (difference < from->count) { + // Create a new range with the reduced flags, and remove the affected items from + // the current range. + if (clearedFlags) + *from = insert(*from, from->list, from->index, difference, clearedFlags)->next; + from->index += difference; + from->count -= difference; + from.incrementIndexes(from->count); + } else if (clearedFlags) { + // The whole range is affected so simply update the flags. + from->flags &= ~flags; + } else { + // All flags have been removed from the range so remove it. + *from = erase(*from)->previous; + } + } + + if (*from != &m_ranges && from->previous != &m_ranges + && from->previous->list == from->list + && (!from->list || from->previous->end() == from->index) + && from->previous->flags == (from->flags & ~AppendFlag)) { + // If the following range is now a continuation, merge it with its previous range. + from.offset = from->previous->count; + from->previous->count += from->count; + from->previous->flags = from->flags; + *from = erase(*from)->previous; + } + m_cacheIt = from; + QT_QML_VERIFY_LISTCOMPOSITOR +} + +bool QQmlListCompositor::verifyMoveTo( + Group fromGroup, int from, Group toGroup, int to, int count, Group group) const +{ + if (group != toGroup) { + // determine how many items from the destination group intersect with the source group. + iterator fromIt = find(fromGroup, from); + + int intersectingCount = 0; + + for (; count > 0; *fromIt = fromIt->next) { + if (*fromIt == &m_ranges) + return false; + if (!fromIt->inGroup(group)) + continue; + if (fromIt->inGroup(toGroup)) + intersectingCount += qMin(count, fromIt->count - fromIt.offset); + count -= fromIt->count - fromIt.offset; + fromIt.offset = 0; + } + count = intersectingCount; + } + + return to >= 0 && to + count <= m_end.index[toGroup]; +} + +/*! + \internal + + Moves \a count items belonging to \a moveGroup from the index \a from in \a fromGroup + to the index \a to in \a toGroup. + + If \a removes and \a inserts are not null they will be populated with per group notifications + of the items moved. + */ + +void QQmlListCompositor::move( + Group fromGroup, + int from, + Group toGroup, + int to, + int count, + Group moveGroup, + QVector *removes, + QVector *inserts) +{ + QT_QML_TRACE_LISTCOMPOSITOR(<< fromGroup << from << toGroup << to << count) + Q_ASSERT(count > 0); + Q_ASSERT(from >=0); + Q_ASSERT(verifyMoveTo(fromGroup, from, toGroup, to, count, moveGroup)); + + // Find the position of the first item to move. + iterator fromIt = find(fromGroup, from); + + if (fromIt != moveGroup) { + // If the range at the from index doesn't contain items from the move group; skip + // to the next range. + fromIt.incrementIndexes(fromIt->count - fromIt.offset); + fromIt.offset = 0; + *fromIt = fromIt->next; + } else if (fromIt.offset > 0) { + // If the range at the from index contains items from the move group and the index isn't + // at the start of the range; split the range at the index and move the iterator to start + // of the second range. + *fromIt = insert( + *fromIt, fromIt->list, fromIt->index, fromIt.offset, fromIt->flags & ~AppendFlag)->next; + fromIt->index += fromIt.offset; + fromIt->count -= fromIt.offset; + fromIt.offset = 0; + } + + // Remove count items belonging to the move group from the list. + Range movedFlags; + for (int moveId = m_moveId; count > 0;) { + if (fromIt != moveGroup) { + // Skip ranges not containing items from the move group. + fromIt.incrementIndexes(fromIt->count); + *fromIt = fromIt->next; + continue; + } + int difference = qMin(count, fromIt->count); + + // Create a new static range containing the moved items from an existing range. + new Range( + &movedFlags, + fromIt->list, + fromIt->index, + difference, + fromIt->flags & ~(PrependFlag | AppendFlag)); + // Remove moved items from the count, the existing range, and a remove notification. + if (removes) + removes->append(Remove(fromIt, difference, fromIt->flags, ++moveId)); + count -= difference; + fromIt->count -= difference; + + // If the existing range contains the prepend flag replace the removed items with + // a placeholder range for new items inserted into the source model. + int removeIndex = fromIt->index; + if (fromIt->prepend() + && fromIt->previous != &m_ranges + && fromIt->previous->flags == PrependFlag + && fromIt->previous->list == fromIt->list + && fromIt->previous->end() == fromIt->index) { + // Grow the previous range instead of creating a new one if possible. + fromIt->previous->count += difference; + } else if (fromIt->prepend()) { + *fromIt = insert(*fromIt, fromIt->list, removeIndex, difference, PrependFlag)->next; + } + fromIt->index += difference; + + if (fromIt->count == 0) { + // If the existing range has no items remaining; remove it from the list. + if (fromIt->append()) + fromIt->previous->flags |= AppendFlag; + *fromIt = erase(*fromIt); + + // If the ranges before and after the removed range can be joined, do so. + if (*fromIt != m_ranges.next && fromIt->flags == PrependFlag + && fromIt->previous != &m_ranges + && fromIt->previous->flags == PrependFlag + && fromIt->previous->list == fromIt->list + && fromIt->previous->end() == fromIt->index) { + fromIt.incrementIndexes(fromIt->count); + fromIt->previous->count += fromIt->count; + *fromIt = erase(*fromIt); + } + } else if (count > 0) { + *fromIt = fromIt->next; + } + } + + // Try and join the range following the removed items to the range preceding it. + if (*fromIt != m_ranges.next + && *fromIt != &m_ranges + && fromIt->previous->list == fromIt->list + && (!fromIt->list || fromIt->previous->end() == fromIt->index) + && fromIt->previous->flags == (fromIt->flags & ~AppendFlag)) { + if (fromIt == fromIt.group) + fromIt.offset = fromIt->previous->count; + fromIt.offset = fromIt->previous->count; + fromIt->previous->count += fromIt->count; + fromIt->previous->flags = fromIt->flags; + *fromIt = erase(*fromIt)->previous; + } + + // Find the destination position of the move. + insert_iterator toIt = fromIt; + toIt.setGroup(toGroup); + + const int difference = to - toIt.index[toGroup]; + toIt += difference; + + // If the insert position is part way through a range; split it and move the iterator to the + // start of the second range. + if (toIt.offset > 0) { + *toIt = insert(*toIt, toIt->list, toIt->index, toIt.offset, toIt->flags & ~AppendFlag)->next; + toIt->index += toIt.offset; + toIt->count -= toIt.offset; + toIt.offset = 0; + } + + // Insert the moved ranges before the insert iterator, growing the previous range if that + // is an option. + for (Range *range = movedFlags.previous; range != &movedFlags; range = range->previous) { + if (*toIt != &m_ranges + && range->list == toIt->list + && (!range->list || range->end() == toIt->index) + && range->flags == (toIt->flags & ~AppendFlag)) { + toIt->index -= range->count; + toIt->count += range->count; + } else { + *toIt = insert(*toIt, range->list, range->index, range->count, range->flags); + } + } + + // Try and join the range after the inserted ranges to the last range inserted. + if (*toIt != m_ranges.next + && toIt->previous->list == toIt->list + && (!toIt->list || (toIt->previous->end() == toIt->index && toIt->previous->flags == (toIt->flags & ~AppendFlag)))) { + toIt.offset = toIt->previous->count; + toIt->previous->count += toIt->count; + toIt->previous->flags = toIt->flags; + *toIt = erase(*toIt)->previous; + } + // Create insert notification for the ranges moved. + Insert insert(toIt, 0, 0, 0); + for (Range *next, *range = movedFlags.next; range != &movedFlags; range = next) { + insert.count = range->count; + insert.flags = range->flags; + if (inserts) { + insert.moveId = ++m_moveId; + inserts->append(insert); + } + for (int i = 0; i < m_groupCount; ++i) { + if (insert.inGroup(i)) + insert.index[i] += range->count; + } + + next = range->next; + delete range; + } + + m_cacheIt = toIt; + + QT_QML_VERIFY_LISTCOMPOSITOR +} + +/*! + Clears the contents of a compositor. +*/ + +void QQmlListCompositor::clear() +{ + QT_QML_TRACE_LISTCOMPOSITOR( ) + for (Range *range = m_ranges.next; range != &m_ranges; range = erase(range)) {} + m_end = iterator(m_ranges.next, 0, Default, m_groupCount); + m_cacheIt = m_end; +} + +void QQmlListCompositor::listItemsInserted( + QVector *translatedInsertions, + void *list, + const QVector &insertions, + const QVector *movedFlags) +{ + QT_QML_TRACE_LISTCOMPOSITOR(<< list << insertions) + for (iterator it(m_ranges.next, 0, Default, m_groupCount); *it != &m_ranges; *it = it->next) { + if (it->list != list || it->flags == CacheFlag) { + // Skip ranges that don't reference list. + it.incrementIndexes(it->count); + continue; + } else if (it->flags & MovedFlag) { + // Skip ranges that were already moved in listItemsRemoved. + it->flags &= ~MovedFlag; + it.incrementIndexes(it->count); + continue; + } + foreach (const QQmlChangeSet::Insert &insertion, insertions) { + int offset = insertion.index - it->index; + if ((offset > 0 && offset < it->count) + || (offset == 0 && it->prepend()) + || (offset == it->count && it->append())) { + // The insert index is within the current range. + if (it->prepend()) { + // The range has the prepend flag set so we insert new items into the range. + uint flags = m_defaultFlags; + if (insertion.isMove()) { + // If the insert was part of a move replace the default flags with + // the flags from the source range. + for (QVector::const_iterator move = movedFlags->begin(); + move != movedFlags->end(); + ++move) { + if (move->moveId == insertion.moveId) { + flags = move->flags; + break; + } + } + } + if (flags & ~(AppendFlag | PrependFlag)) { + // If any items are added to groups append an insert notification. + Insert translatedInsert(it, insertion.count, flags, insertion.moveId); + for (int i = 0; i < m_groupCount; ++i) { + if (it->inGroup(i)) + translatedInsert.index[i] += offset; + } + translatedInsertions->append(translatedInsert); + } + if ((it->flags & ~AppendFlag) == flags) { + // Accumulate items on the current range it its flags are the same as + // the insert flags. + it->count += insertion.count; + } else if (offset == 0 + && it->previous != &m_ranges + && it->previous->list == list + && it->previous->end() == insertion.index + && it->previous->flags == flags) { + // Attempt to append to the previous range if the insert position is at + // the start of the current range. + it->previous->count += insertion.count; + it->index += insertion.count; + it.incrementIndexes(insertion.count); + } else { + if (offset > 0) { + // Divide the current range at the insert position. + it.incrementIndexes(offset); + *it = insert(*it, it->list, it->index, offset, it->flags & ~AppendFlag)->next; + } + // Insert a new range, and increment the start index of the current range. + *it = insert(*it, it->list, insertion.index, insertion.count, flags)->next; + it.incrementIndexes(insertion.count, flags); + it->index += offset + insertion.count; + it->count -= offset; + } + m_end.incrementIndexes(insertion.count, flags); + } else { + // The range doesn't have the prepend flag set so split the range into parts; + // one before the insert position and one after the last inserted item. + if (offset > 0) { + *it = insert(*it, it->list, it->index, offset, it->flags)->next; + it->index += offset; + it->count -= offset; + } + it->index += insertion.count; + } + } else if (offset <= 0) { + // The insert position was before the current range so increment the start index. + it->index += insertion.count; + } + } + it.incrementIndexes(it->count); + } + m_cacheIt = m_end; + QT_QML_VERIFY_LISTCOMPOSITOR +} + +/*! + Updates the contents of a compositor when \a count items are inserted into a \a list at + \a index. + + This corrects the indexes of each range for that list in the compositor, splitting the range + in two if the insert index is in the middle of that range. If a range at the insert position + has the Prepend flag set then a new range will be inserted at that position with the groups + specified in defaultGroups(). If the insert index corresponds to the end of a range with + the Append flag set a new range will be inserted before the end of the append range. + + The \a translatedInsertions list is populated with insert notifications for affected + groups. +*/ + +void QQmlListCompositor::listItemsInserted( + void *list, int index, int count, QVector *translatedInsertions) +{ + QT_QML_TRACE_LISTCOMPOSITOR(<< list << index << count) + Q_ASSERT(count > 0); + + QVector insertions; + insertions.append(QQmlChangeSet::Insert(index, count)); + + listItemsInserted(translatedInsertions, list, insertions); +} + +void QQmlListCompositor::listItemsRemoved( + QVector *translatedRemovals, + void *list, + QVector *removals, + QVector *insertions, + QVector *movedFlags) +{ + QT_QML_TRACE_LISTCOMPOSITOR(<< list << *removals) + + for (iterator it(m_ranges.next, 0, Default, m_groupCount); *it != &m_ranges; *it = it->next) { + if (it->list != list || it->flags == CacheFlag) { + // Skip ranges that don't reference list. + it.incrementIndexes(it->count); + continue; + } + bool removed = false; + for (QVector::iterator removal = removals->begin(); + !removed && removal != removals->end(); + ++removal) { + int relativeIndex = removal->index - it->index; + int itemsRemoved = removal->count; + if (relativeIndex + removal->count > 0 && relativeIndex < it->count) { + // If the current range intersects the remove; remove the intersecting items. + const int offset = qMax(0, relativeIndex); + int removeCount = qMin(it->count, relativeIndex + removal->count) - offset; + it->count -= removeCount; + int removeFlags = it->flags & m_removeFlags; + Remove translatedRemoval(it, removeCount, it->flags); + for (int i = 0; i < m_groupCount; ++i) { + if (it->inGroup(i)) + translatedRemoval.index[i] += offset; + } + if (removal->isMove()) { + // If the removal was part of a move find the corresponding insert. + QVector::iterator insertion = insertions->begin(); + for (; insertion != insertions->end() && insertion->moveId != removal->moveId; + ++insertion) {} + Q_ASSERT(insertion != insertions->end()); + Q_ASSERT(insertion->count == removal->count); + + if (relativeIndex < 0) { + // If the remove started before the current range, split it and the + // corresponding insert so we're only working with intersecting part. + int splitMoveId = ++m_moveId; + removal = removals->insert(removal, QQmlChangeSet::Remove( + removal->index, -relativeIndex, splitMoveId)); + ++removal; + removal->count -= -relativeIndex; + insertion = insertions->insert(insertion, QQmlChangeSet::Insert( + insertion->index, -relativeIndex, splitMoveId)); + ++insertion; + insertion->index += -relativeIndex; + insertion->count -= -relativeIndex; + } + + if (it->prepend()) { + // If the current range has the prepend flag preserve its flags to transfer + // to its new location. + removeFlags |= it->flags & CacheFlag; + translatedRemoval.moveId = ++m_moveId; + movedFlags->append(MovedFlags(m_moveId, it->flags & ~AppendFlag)); + + if (removeCount < removal->count) { + // If the remove doesn't encompass all of the current range, + // split it and the corresponding insert. + removal = removals->insert(removal, QQmlChangeSet::Remove( + removal->index, removeCount, translatedRemoval.moveId)); + ++removal; + insertion = insertions->insert(insertion, QQmlChangeSet::Insert( + insertion->index, removeCount, translatedRemoval.moveId)); + ++insertion; + + removal->count -= removeCount; + insertion->index += removeCount; + insertion->count -= removeCount; + } else { + // If there's no need to split the move simply replace its moveId + // with that of the translated move. + removal->moveId = translatedRemoval.moveId; + insertion->moveId = translatedRemoval.moveId; + } + } else { + // If the current range doesn't have prepend flags then insert a new range + // with list indexes from the corresponding insert location. The MoveFlag + // is to notify listItemsInserted that it can skip evaluation of that range. + if (offset > 0) { + *it = insert(*it, it->list, it->index, offset, it->flags & ~AppendFlag)->next; + it->index += offset; + it->count -= offset; + it.incrementIndexes(offset); + } + if (it->previous != &m_ranges + && it->previous->list == it->list + && it->end() == insertion->index + && it->previous->flags == (it->flags | MovedFlag)) { + it->previous->count += removeCount; + } else { + *it = insert(*it, it->list, insertion->index, removeCount, it->flags | MovedFlag)->next; + } + // Clear the changed flags as the item hasn't been removed. + translatedRemoval.flags = 0; + removeFlags = 0; + } + } else if (it->inCache()) { + // If not moving and the current range has the cache flag, insert a new range + // with just the cache flag set to retain caching information for the removed + // range. + if (offset > 0) { + *it = insert(*it, it->list, it->index, offset, it->flags & ~AppendFlag)->next; + it->index += offset; + it->count -= offset; + it.incrementIndexes(offset); + } + if (it->previous != &m_ranges + && it->previous->list == it->list + && it->previous->flags == CacheFlag) { + it->previous->count += removeCount; + } else { + *it = insert(*it, it->list, -1, removeCount, CacheFlag)->next; + } + it.index[Cache] += removeCount; + } + if (removeFlags & GroupMask) + translatedRemovals->append(translatedRemoval); + m_end.decrementIndexes(removeCount, removeFlags); + if (it->count == 0 && !it->append()) { + // Erase empty non-append ranges. + *it = erase(*it)->previous; + removed = true; + } else if (relativeIndex <= 0) { + // If the remove started before the current range move the start index of + // the range to the remove index. + it->index = removal->index; + } + } else if (relativeIndex < 0) { + // If the remove was before the current range decrement the start index by the + // number of items removed. + it->index -= itemsRemoved; + + if (it->previous != &m_ranges + && it->previous->list == it->list + && it->previous->end() == it->index + && it->previous->flags == (it->flags & ~AppendFlag)) { + // Compress ranges made continuous by the removal of separating ranges. + it.decrementIndexes(it->previous->count); + it->previous->count += it->count; + it->previous->flags = it->flags; + *it = erase(*it)->previous; + } + } + } + if (it->flags == CacheFlag && it->next->flags == CacheFlag && it->next->list == it->list) { + // Compress consecutive cache only ranges. + it.index[Cache] += it->next->count; + it->count += it->next->count; + erase(it->next); + } else if (!removed) { + it.incrementIndexes(it->count); + } + } + m_cacheIt = m_end; + QT_QML_VERIFY_LISTCOMPOSITOR +} + + +/*! + Updates the contents of a compositor when \a count items are removed from a \a list at + \a index. + + Ranges that intersect the specified range are removed from the compositor and the indexes of + ranges after index + count are updated. + + The \a translatedRemovals list is populated with remove notifications for the affected + groups. +*/ + + +void QQmlListCompositor::listItemsRemoved( + void *list, int index, int count, QVector *translatedRemovals) +{ + QT_QML_TRACE_LISTCOMPOSITOR(<< list << index << count) + Q_ASSERT(count >= 0); + + QVector removals; + removals.append(QQmlChangeSet::Remove(index, count)); + listItemsRemoved(translatedRemovals, list, &removals, 0, 0); +} + +/*! + Updates the contents of a compositor when \a count items are removed from a \a list at + \a index. + + Ranges that intersect the specified range are removed from the compositor and the indexes of + ranges after index + count are updated. + + The \a translatedRemovals and translatedInserts lists are populated with move + notifications for the affected groups. +*/ + +void QQmlListCompositor::listItemsMoved( + void *list, + int from, + int to, + int count, + QVector *translatedRemovals, + QVector *translatedInsertions) +{ + QT_QML_TRACE_LISTCOMPOSITOR(<< list << from << to << count) + Q_ASSERT(count >= 0); + + QVector removals; + QVector insertions; + QVector movedFlags; + removals.append(QQmlChangeSet::Remove(from, count, 0)); + insertions.append(QQmlChangeSet::Insert(to, count, 0)); + + listItemsRemoved(translatedRemovals, list, &removals, &insertions, &movedFlags); + listItemsInserted(translatedInsertions, list, insertions, &movedFlags); +} + +void QQmlListCompositor::listItemsChanged( + QVector *translatedChanges, + void *list, + const QVector &changes) +{ + QT_QML_TRACE_LISTCOMPOSITOR(<< list << changes) + for (iterator it(m_ranges.next, 0, Default, m_groupCount); *it != &m_ranges; *it = it->next) { + if (it->list != list || it->flags == CacheFlag) { + it.incrementIndexes(it->count); + continue; + } else if (!it->inGroup()) { + continue; + } + foreach (const QQmlChangeSet::Change &change, changes) { + const int offset = change.index - it->index; + if (offset + change.count > 0 && offset < it->count) { + const int changeOffset = qMax(0, offset); + const int changeCount = qMin(it->count, offset + change.count) - changeOffset; + + Change translatedChange(it, changeCount, it->flags); + for (int i = 0; i < m_groupCount; ++i) { + if (it->inGroup(i)) + translatedChange.index[i] += changeOffset; + } + translatedChanges->append(translatedChange); + } + } + it.incrementIndexes(it->count); + } +} + + +/*! + Translates the positions of \a count changed items at \a index in a \a list. + + The \a translatedChanges list is populated with change notifications for the + affected groups. +*/ + +void QQmlListCompositor::listItemsChanged( + void *list, int index, int count, QVector *translatedChanges) +{ + QT_QML_TRACE_LISTCOMPOSITOR(<< list << index << count) + Q_ASSERT(count >= 0); + QVector changes; + changes.append(QQmlChangeSet::Change(index, count)); + listItemsChanged(translatedChanges, list, changes); +} + +void QQmlListCompositor::transition( + Group from, + Group to, + QVector *removes, + QVector *inserts) +{ + int removeCount = 0; + for (iterator it(m_ranges.next, 0, Default, m_groupCount); *it != &m_ranges; *it = it->next) { + if (it == from && it != to) { + removes->append(QQmlChangeSet::Remove(it.index[from]- removeCount, it->count)); + removeCount += it->count; + } else if (it != from && it == to) { + inserts->append(QQmlChangeSet::Insert(it.index[to], it->count)); + } + it.incrementIndexes(it->count); + } +} + +/*! + \internal + Writes the name of \a group to \a debug. +*/ + +QDebug operator <<(QDebug debug, const QQmlListCompositor::Group &group) +{ + switch (group) { + case QQmlListCompositor::Cache: return debug << "Cache"; + case QQmlListCompositor::Default: return debug << "Default"; + default: return (debug.nospace() << "Group" << int(group)).space(); + } + +} + +/*! + \internal + Writes the contents of \a range to \a debug. +*/ + +QDebug operator <<(QDebug debug, const QQmlListCompositor::Range &range) +{ + (debug.nospace() + << "Range(" + << range.list) << ' ' + << range.index << ' ' + << range.count << ' ' + << (range.isUnresolved() ? 'U' : '0') + << (range.append() ? 'A' : '0') + << (range.prepend() ? 'P' : '0'); + for (int i = QQmlListCompositor::MaximumGroupCount - 1; i >= 2; --i) + debug << (range.inGroup(i) ? '1' : '0'); + return (debug + << (range.inGroup(QQmlListCompositor::Default) ? 'D' : '0') + << (range.inGroup(QQmlListCompositor::Cache) ? 'C' : '0')); +} + +static void qt_print_indexes(QDebug &debug, int count, const int *indexes) +{ + for (int i = count - 1; i >= 0; --i) + debug << indexes[i]; +} + +/*! + \internal + Writes the contents of \a it to \a debug. +*/ + +QDebug operator <<(QDebug debug, const QQmlListCompositor::iterator &it) +{ + (debug.nospace() << "iterator(" << it.group).space() << "offset:" << it.offset; + qt_print_indexes(debug, it.groupCount, it.index); + return ((debug << **it).nospace() << ')').space(); +} + +static QDebug qt_print_change(QDebug debug, const char *name, const QQmlListCompositor::Change &change) +{ + debug.nospace() << name << '(' << change.moveId << ' ' << change.count << ' '; + for (int i = QQmlListCompositor::MaximumGroupCount - 1; i >= 2; --i) + debug << (change.inGroup(i) ? '1' : '0'); + debug << (change.inGroup(QQmlListCompositor::Default) ? 'D' : '0') + << (change.inGroup(QQmlListCompositor::Cache) ? 'C' : '0'); + int i = QQmlListCompositor::MaximumGroupCount - 1; + for (; i >= 0 && !change.inGroup(i); --i) {} + for (; i >= 0; --i) + debug << ' ' << change.index[i]; + return (debug << ')').maybeSpace(); +} + +/*! + \internal + Writes the contents of \a change to \a debug. +*/ + +QDebug operator <<(QDebug debug, const QQmlListCompositor::Change &change) +{ + return qt_print_change(debug, "Change", change); +} + +/*! + \internal + Writes the contents of \a remove to \a debug. +*/ + +QDebug operator <<(QDebug debug, const QQmlListCompositor::Remove &remove) +{ + return qt_print_change(debug, "Remove", remove); +} + +/*! + \internal + Writes the contents of \a insert to \a debug. +*/ + +QDebug operator <<(QDebug debug, const QQmlListCompositor::Insert &insert) +{ + return qt_print_change(debug, "Insert", insert); +} + +/*! + \internal + Writes the contents of \a list to \a debug. +*/ + +QDebug operator <<(QDebug debug, const QQmlListCompositor &list) +{ + int indexes[QQmlListCompositor::MaximumGroupCount]; + for (int i = 0; i < QQmlListCompositor::MaximumGroupCount; ++i) + indexes[i] = 0; + debug.nospace() << "QQmlListCompositor("; + qt_print_indexes(debug, list.m_groupCount, list.m_end.index); + for (QQmlListCompositor::Range *range = list.m_ranges.next; range != &list.m_ranges; range = range->next) { + (debug << '\n').space(); + qt_print_indexes(debug, list.m_groupCount, indexes); + debug << ' ' << *range; + + for (int i = 0; i < list.m_groupCount; ++i) { + if (range->inGroup(i)) + indexes[i] += range->count; + } + } + return (debug << ')').maybeSpace(); +} + +QT_END_NAMESPACE diff --git a/src/qml/util/qqmllistcompositor_p.h b/src/qml/util/qqmllistcompositor_p.h new file mode 100644 index 0000000000..5d87051582 --- /dev/null +++ b/src/qml/util/qqmllistcompositor_p.h @@ -0,0 +1,375 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLLISTCOMPOSITOR_P_H +#define QQMLLISTCOMPOSITOR_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +#include + +#include + +QT_BEGIN_NAMESPACE + +class Q_AUTOTEST_EXPORT QQmlListCompositor +{ +public: + enum { MinimumGroupCount = 3, MaximumGroupCount = 11 }; + + enum Group + { + Cache = 0, + Default = 1, + Persisted = 2 + }; + + enum Flag + { + CacheFlag = 1 << Cache, + DefaultFlag = 1 << Default, + PersistedFlag = 1 << Persisted, + PrependFlag = 0x10000000, + AppendFlag = 0x20000000, + UnresolvedFlag = 0x40000000, + MovedFlag = 0x80000000, + GroupMask = ~(PrependFlag | AppendFlag | UnresolvedFlag | MovedFlag | CacheFlag) + }; + + class Range + { + public: + Range() : next(this), previous(this), list(0), index(0), count(0), flags(0) {} + Range(Range *next, void *list, int index, int count, uint flags) + : next(next), previous(next->previous), list(list), index(index), count(count), flags(flags) { + next->previous = this; previous->next = this; } + + Range *next; + Range *previous; + void *list; + int index; + int count; + uint flags; + + inline int start() const { return index; } + inline int end() const { return index + count; } + + inline int groups() const { return flags & GroupMask; } + + inline bool inGroup() const { return flags & GroupMask; } + inline bool inCache() const { return flags & CacheFlag; } + inline bool inGroup(int group) const { return flags & (1 << group); } + inline bool isUnresolved() const { return flags & UnresolvedFlag; } + + inline bool prepend() const { return flags & PrependFlag; } + inline bool append() const { return flags & AppendFlag; } + }; + + class Q_AUTOTEST_EXPORT iterator + { + public: + inline iterator(); + inline iterator(const iterator &it); + inline iterator(Range *range, int offset, Group group, int groupCount); + inline ~iterator() {} + + bool operator ==(const iterator &it) const { return range == it.range && offset == it.offset; } + bool operator !=(const iterator &it) const { return range != it.range || offset != it.offset; } + + bool operator ==(Group group) const { return range->flags & (1 << group); } + bool operator !=(Group group) const { return !(range->flags & (1 << group)); } + + Range *&operator *() { return range; } + Range * const &operator *() const { return range; } + Range *operator ->() { return range; } + const Range *operator ->() const { return range; } + + iterator &operator +=(int difference); + + template T *list() const { return static_cast(range->list); } + int modelIndex() const { return range->index + offset; } + + void incrementIndexes(int difference) { incrementIndexes(difference, range->flags); } + void decrementIndexes(int difference) { decrementIndexes(difference, range->flags); } + + inline void incrementIndexes(int difference, uint flags); + inline void decrementIndexes(int difference, uint flags); + + void setGroup(Group g) { group = g; groupFlag = 1 << g; } + + Range *range; + int offset; + Group group; + int groupFlag; + int groupCount; + union { + struct { + int cacheIndex; + }; + int index[MaximumGroupCount]; + }; + }; + + class Q_AUTOTEST_EXPORT insert_iterator : public iterator + { + public: + inline insert_iterator() {} + inline insert_iterator(const iterator &it) : iterator(it) {} + inline insert_iterator(Range *, int, Group, int); + inline ~insert_iterator() {} + + insert_iterator &operator +=(int difference); + }; + + struct Change + { + inline Change() {} + inline Change(iterator it, int count, uint flags, int moveId = -1); + int count; + uint flags; + int moveId; + union { + struct { + int cacheIndex; + }; + int index[MaximumGroupCount]; + }; + + inline bool isMove() const { return moveId >= 0; } + inline bool inCache() const { return flags & CacheFlag; } + inline bool inGroup() const { return flags & GroupMask; } + inline bool inGroup(int group) const { return flags & (CacheFlag << group); } + + inline int groups() const { return flags & GroupMask; } + }; + + struct Insert : public Change + { + Insert() {} + Insert(iterator it, int count, uint flags, int moveId = -1) + : Change(it, count, flags, moveId) {} + }; + + struct Remove : public Change + { + Remove() {} + Remove(iterator it, int count, uint flags, int moveId = -1) + : Change(it, count, flags, moveId) {} + }; + + QQmlListCompositor(); + ~QQmlListCompositor(); + + int defaultGroups() const { return m_defaultFlags & ~PrependFlag; } + void setDefaultGroups(int groups) { m_defaultFlags = groups | PrependFlag; } + void setDefaultGroup(Group group) { m_defaultFlags |= (1 << group); } + void clearDefaultGroup(Group group) { m_defaultFlags &= ~(1 << group); } + void setRemoveGroups(int groups) { m_removeFlags = PrependFlag | AppendFlag | groups; } + void setGroupCount(int count); + + int count(Group group) const; + iterator find(Group group, int index); + iterator find(Group group, int index) const; + insert_iterator findInsertPosition(Group group, int index); + + const iterator &end() { return m_end; } + + void append(void *list, int index, int count, uint flags, QVector *inserts = 0); + void insert(Group group, int before, void *list, int index, int count, uint flags, QVector *inserts = 0); + iterator insert(iterator before, void *list, int index, int count, uint flags, QVector *inserts = 0); + + void setFlags(Group fromGroup, int from, int count, Group group, int flags, QVector *inserts = 0); + void setFlags(iterator from, int count, Group group, uint flags, QVector *inserts = 0); + void setFlags(Group fromGroup, int from, int count, uint flags, QVector *inserts = 0) { + setFlags(fromGroup, from, count, fromGroup, flags, inserts); } + void setFlags(iterator from, int count, uint flags, QVector *inserts = 0) { + setFlags(from, count, from.group, flags, inserts); } + + void clearFlags(Group fromGroup, int from, int count, Group group, uint flags, QVector *removals = 0); + void clearFlags(iterator from, int count, Group group, uint flags, QVector *removals = 0); + void clearFlags(Group fromGroup, int from, int count, uint flags, QVector *removals = 0) { + clearFlags(fromGroup, from, count, fromGroup, flags, removals); } + void clearFlags(iterator from, int count, uint flags, QVector *removals = 0) { + clearFlags(from, count, from.group, flags, removals); } + + bool verifyMoveTo(Group fromGroup, int from, Group toGroup, int to, int count, Group group) const; + + void move( + Group fromGroup, + int from, + Group toGroup, + int to, + int count, + Group group, + QVector *removals = 0, + QVector *inserts = 0); + void clear(); + + void listItemsInserted(void *list, int index, int count, QVector *inserts); + void listItemsRemoved(void *list, int index, int count, QVector *removals); + void listItemsMoved(void *list, int from, int to, int count, QVector *removals, QVector *inserts); + void listItemsChanged(void *list, int index, int count, QVector *changes); + + void transition( + Group from, + Group to, + QVector *removes, + QVector *inserts); + +private: + Range m_ranges; + iterator m_end; + iterator m_cacheIt; + int m_groupCount; + int m_defaultFlags; + int m_removeFlags; + int m_moveId; + + inline Range *insert(Range *before, void *list, int index, int count, uint flags); + inline Range *erase(Range *range); + + struct MovedFlags + { + MovedFlags() {} + MovedFlags(int moveId, uint flags) : moveId(moveId), flags(flags) {} + + int moveId; + uint flags; + }; + + void listItemsRemoved( + QVector *translatedRemovals, + void *list, + QVector *removals, + QVector *insertions = 0, + QVector *movedFlags = 0); + void listItemsInserted( + QVector *translatedInsertions, + void *list, + const QVector &insertions, + const QVector *movedFlags = 0); + void listItemsChanged( + QVector *translatedChanges, + void *list, + const QVector &changes); + + friend Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QQmlListCompositor &list); +}; + +Q_DECLARE_TYPEINFO(QQmlListCompositor::Change, Q_PRIMITIVE_TYPE); +Q_DECLARE_TYPEINFO(QQmlListCompositor::Remove, Q_PRIMITIVE_TYPE); +Q_DECLARE_TYPEINFO(QQmlListCompositor::Insert, Q_PRIMITIVE_TYPE); + +inline QQmlListCompositor::iterator::iterator() + : range(0), offset(0), group(Default), groupCount(0) {} +inline QQmlListCompositor::iterator::iterator(const iterator &it) + : range(it.range) + , offset(it.offset) + , group(it.group) + , groupFlag(it.groupFlag) + , groupCount(it.groupCount) +{ + for (int i = 0; i < groupCount; ++i) + index[i] = it.index[i]; +} + +inline QQmlListCompositor::iterator::iterator( + Range *range, int offset, Group group, int groupCount) + : range(range) + , offset(offset) + , group(group) + , groupFlag(1 << group) + , groupCount(groupCount) +{ + for (int i = 0; i < groupCount; ++i) + index[i] = 0; +} + +inline void QQmlListCompositor::iterator::incrementIndexes(int difference, uint flags) +{ + for (int i = 0; i < groupCount; ++i) { + if (flags & (1 << i)) + index[i] += difference; + } +} + +inline void QQmlListCompositor::iterator::decrementIndexes(int difference, uint flags) +{ + for (int i = 0; i < groupCount; ++i) { + if (flags & (1 << i)) + index[i] -= difference; + } +} + +inline QQmlListCompositor::insert_iterator::insert_iterator( + Range *range, int offset, Group group, int groupCount) + : iterator(range, offset, group, groupCount) {} + +inline QQmlListCompositor::Change::Change(iterator it, int count, uint flags, int moveId) + : count(count), flags(flags), moveId(moveId) +{ + for (int i = 0; i < MaximumGroupCount; ++i) + index[i] = it.index[i]; +} + +Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QQmlListCompositor::Group &group); +Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QQmlListCompositor::Range &range); +Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QQmlListCompositor::iterator &it); +Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QQmlListCompositor::Change &change); +Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QQmlListCompositor::Remove &remove); +Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QQmlListCompositor::Insert &insert); +Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QQmlListCompositor &list); + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/util/util.pri b/src/qml/util/util.pri index 3b121ba3cb..a9c5ffe9b7 100644 --- a/src/qml/util/util.pri +++ b/src/qml/util/util.pri @@ -1,5 +1,13 @@ SOURCES += \ + $$PWD/qqmlchangeset.cpp \ + $$PWD/qqmllistaccessor.cpp \ + $$PWD/qqmllistcompositor.cpp \ + $$PWD/qqmladaptormodel.cpp \ $$PWD/qqmlpropertymap.cpp HEADERS += \ + $$PWD/qqmlchangeset_p.h \ + $$PWD/qqmllistaccessor_p.h \ + $$PWD/qqmllistcompositor_p.h \ + $$PWD/qqmladaptormodel_p.h \ $$PWD/qqmlpropertymap.h diff --git a/src/quick/items/items.pri b/src/quick/items/items.pri index 5272a3d5f7..2183a2bc63 100644 --- a/src/quick/items/items.pri +++ b/src/quick/items/items.pri @@ -38,10 +38,6 @@ HEADERS += \ $$PWD/qquickflickable_p.h \ $$PWD/qquickflickable_p_p.h \ $$PWD/qquicklistview_p.h \ - $$PWD/qquickvisualadaptormodel_p.h \ - $$PWD/qquickvisualdatamodel_p.h \ - $$PWD/qquickvisualdatamodel_p_p.h \ - $$PWD/qquickvisualitemmodel_p.h \ $$PWD/qquickrepeater_p.h \ $$PWD/qquickrepeater_p_p.h \ $$PWD/qquickgridview_p.h \ @@ -99,9 +95,6 @@ SOURCES += \ $$PWD/qquickpincharea.cpp \ $$PWD/qquickflickable.cpp \ $$PWD/qquicklistview.cpp \ - $$PWD/qquickvisualadaptormodel.cpp \ - $$PWD/qquickvisualdatamodel.cpp \ - $$PWD/qquickvisualitemmodel.cpp \ $$PWD/qquickrepeater.cpp \ $$PWD/qquickgridview.cpp \ $$PWD/qquickpathview.cpp \ diff --git a/src/quick/items/qquickgridview.cpp b/src/quick/items/qquickgridview.cpp index fd9ce9ffcd..ea8badb584 100644 --- a/src/quick/items/qquickgridview.cpp +++ b/src/quick/items/qquickgridview.cpp @@ -40,10 +40,10 @@ ****************************************************************************/ #include "qquickgridview_p.h" -#include "qquickvisualitemmodel_p.h" #include "qquickflickable_p_p.h" #include "qquickitemview_p_p.h" +#include #include #include @@ -209,7 +209,7 @@ public: virtual void setPosition(qreal pos); virtual void layoutVisibleItems(int fromModelIndex = 0); - virtual bool applyInsertionChange(const QQuickChangeSet::Insert &insert, ChangeResult *changeResult, QList *addedItems, QList *movingIntoView); + virtual bool applyInsertionChange(const QQmlChangeSet::Insert &insert, ChangeResult *changeResult, QList *addedItems, QList *movingIntoView); virtual void translateAndTransitionItemsAfter(int afterModelIndex, const ChangeResult &insertionResult, const ChangeResult &removalResult); virtual bool needsRefillForAddedOrRemovedIndex(int index) const; @@ -2105,13 +2105,16 @@ void QQuickGridView::geometryChanged(const QRectF &newGeometry, const QRectF &ol QQuickItemView::geometryChanged(newGeometry, oldGeometry); } -void QQuickGridView::initItem(int index, QQuickItem *item) +void QQuickGridView::initItem(int index, QObject *obj) { - QQuickItemView::initItem(index, item); - QQuickGridViewAttached *attached = static_cast( - qmlAttachedPropertiesObject(item)); - if (attached) - attached->setView(this); + QQuickItemView::initItem(index, obj); + QQuickItem *item = qmlobject_cast(obj); + if (item) { + QQuickGridViewAttached *attached = static_cast( + qmlAttachedPropertiesObject(item)); + if (attached) + attached->setView(this); + } } /*! @@ -2286,7 +2289,7 @@ void QQuickGridView::moveCurrentIndexRight() } } -bool QQuickGridViewPrivate::applyInsertionChange(const QQuickChangeSet::Insert &change, ChangeResult *insertResult, QList *addedItems, QList *movingIntoView) +bool QQuickGridViewPrivate::applyInsertionChange(const QQmlChangeSet::Insert &change, ChangeResult *insertResult, QList *addedItems, QList *movingIntoView) { Q_Q(QQuickGridView); diff --git a/src/quick/items/qquickgridview_p.h b/src/quick/items/qquickgridview_p.h index 64cb814f8c..a1c0ce9389 100644 --- a/src/quick/items/qquickgridview_p.h +++ b/src/quick/items/qquickgridview_p.h @@ -50,7 +50,6 @@ QT_BEGIN_HEADER QT_BEGIN_NAMESPACE -class QQuickVisualModel; class QQuickGridViewAttached; class QQuickGridViewPrivate; class Q_AUTOTEST_EXPORT QQuickGridView : public QQuickItemView @@ -112,7 +111,7 @@ protected: virtual void viewportMoved(Qt::Orientations); virtual void keyPressEvent(QKeyEvent *); virtual void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry); - virtual void initItem(int index, QQuickItem *item); + virtual void initItem(int index, QObject *item); }; class QQuickGridViewAttached : public QQuickItemViewAttached diff --git a/src/quick/items/qquickitemsmodule.cpp b/src/quick/items/qquickitemsmodule.cpp index c04807168f..043baebd0a 100644 --- a/src/quick/items/qquickitemsmodule.cpp +++ b/src/quick/items/qquickitemsmodule.cpp @@ -57,8 +57,6 @@ #include "qquickflickable_p.h" #include "qquickflickable_p_p.h" #include "qquicklistview_p.h" -#include "qquickvisualitemmodel_p.h" -#include "qquickvisualdatamodel_p.h" #include "qquickgridview_p.h" #include "qquickpathview_p.h" #include "qquickitemviewtransition_p.h" @@ -161,9 +159,6 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor) qmlRegisterType(uri,major,minor,"TextEdit"); qmlRegisterType(uri,major,minor,"TextInput"); qmlRegisterType(uri,major,minor,"ViewSection"); - qmlRegisterType(uri,major,minor,"VisualDataModel"); - qmlRegisterType(uri,major,minor,"VisualDataGroup"); - qmlRegisterType(uri,major,minor,"VisualItemModel"); qmlRegisterType(); qmlRegisterType(); @@ -178,7 +173,6 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor) #ifndef QT_NO_VALIDATOR qmlRegisterType(); #endif - qmlRegisterType(); qmlRegisterType(); qmlRegisterType(); qRegisterMetaType("QQuickAnchorLine"); diff --git a/src/quick/items/qquickitemview.cpp b/src/quick/items/qquickitemview.cpp index b73fb8c5b6..198052b939 100644 --- a/src/quick/items/qquickitemview.cpp +++ b/src/quick/items/qquickitemview.cpp @@ -41,6 +41,7 @@ #include "qquickitemview_p_p.h" #include +#include #include "qplatformdefs.h" QT_BEGIN_NAMESPACE @@ -147,14 +148,14 @@ bool QQuickItemViewChangeSet::hasPendingChanges() const return !pendingChanges.isEmpty(); } -void QQuickItemViewChangeSet::applyChanges(const QQuickChangeSet &changeSet) +void QQuickItemViewChangeSet::applyChanges(const QQmlChangeSet &changeSet) { pendingChanges.apply(changeSet); int moveId = -1; int moveOffset = 0; - foreach (const QQuickChangeSet::Remove &r, changeSet.removes()) { + foreach (const QQmlChangeSet::Remove &r, changeSet.removes()) { itemCount -= r.count; if (moveId == -1 && newCurrentIndex >= r.index + r.count) { newCurrentIndex -= r.count; @@ -173,7 +174,7 @@ void QQuickItemViewChangeSet::applyChanges(const QQuickChangeSet &changeSet) currentChanged = true; } } - foreach (const QQuickChangeSet::Insert &i, changeSet.inserts()) { + foreach (const QQmlChangeSet::Insert &i, changeSet.inserts()) { if (moveId == -1) { if (itemCount && newCurrentIndex >= i.index) { newCurrentIndex += i.count; @@ -266,14 +267,14 @@ void QQuickItemView::setModel(const QVariant &model) if (d->modelVariant == model) return; if (d->model) { - disconnect(d->model, SIGNAL(modelUpdated(QQuickChangeSet,bool)), - this, SLOT(modelUpdated(QQuickChangeSet,bool))); - disconnect(d->model, SIGNAL(initItem(int,QQuickItem*)), this, SLOT(initItem(int,QQuickItem*))); - disconnect(d->model, SIGNAL(createdItem(int,QQuickItem*)), this, SLOT(createdItem(int,QQuickItem*))); - disconnect(d->model, SIGNAL(destroyingItem(QQuickItem*)), this, SLOT(destroyingItem(QQuickItem*))); + disconnect(d->model, SIGNAL(modelUpdated(QQmlChangeSet,bool)), + this, SLOT(modelUpdated(QQmlChangeSet,bool))); + disconnect(d->model, SIGNAL(initItem(int,QObject*)), this, SLOT(initItem(int,QObject*))); + disconnect(d->model, SIGNAL(createdItem(int,QObject*)), this, SLOT(createdItem(int,QObject*))); + disconnect(d->model, SIGNAL(destroyingItem(QObject*)), this, SLOT(destroyingItem(QObject*))); } - QQuickVisualModel *oldModel = d->model; + QQmlInstanceModel *oldModel = d->model; d->clear(); d->model = 0; @@ -281,8 +282,8 @@ void QQuickItemView::setModel(const QVariant &model) d->modelVariant = model; QObject *object = qvariant_cast(model); - QQuickVisualModel *vim = 0; - if (object && (vim = qobject_cast(object))) { + QQmlInstanceModel *vim = 0; + if (object && (vim = qobject_cast(object))) { if (d->ownModel) { delete oldModel; d->ownModel = false; @@ -290,22 +291,22 @@ void QQuickItemView::setModel(const QVariant &model) d->model = vim; } else { if (!d->ownModel) { - d->model = new QQuickVisualDataModel(qmlContext(this), this); + d->model = new QQmlDelegateModel(qmlContext(this), this); d->ownModel = true; if (isComponentComplete()) - static_cast(d->model.data())->componentComplete(); + static_cast(d->model.data())->componentComplete(); } else { d->model = oldModel; } - if (QQuickVisualDataModel *dataModel = qobject_cast(d->model)) + if (QQmlDelegateModel *dataModel = qobject_cast(d->model)) dataModel->setModel(model); } if (d->model) { d->bufferMode = QQuickItemViewPrivate::BufferBefore | QQuickItemViewPrivate::BufferAfter; - connect(d->model, SIGNAL(createdItem(int,QQuickItem*)), this, SLOT(createdItem(int,QQuickItem*))); - connect(d->model, SIGNAL(initItem(int,QQuickItem*)), this, SLOT(initItem(int,QQuickItem*))); - connect(d->model, SIGNAL(destroyingItem(QQuickItem*)), this, SLOT(destroyingItem(QQuickItem*))); + connect(d->model, SIGNAL(createdItem(int,QObject*)), this, SLOT(createdItem(int,QObject*))); + connect(d->model, SIGNAL(initItem(int,QObject*)), this, SLOT(initItem(int,QObject*))); + connect(d->model, SIGNAL(destroyingItem(QObject*)), this, SLOT(destroyingItem(QObject*))); if (isComponentComplete()) { d->updateSectionCriteria(); d->refill(); @@ -319,8 +320,8 @@ void QQuickItemView::setModel(const QVariant &model) } } - connect(d->model, SIGNAL(modelUpdated(QQuickChangeSet,bool)), - this, SLOT(modelUpdated(QQuickChangeSet,bool))); + connect(d->model, SIGNAL(modelUpdated(QQmlChangeSet,bool)), + this, SLOT(modelUpdated(QQmlChangeSet,bool))); emit countChanged(); } emit modelChanged(); @@ -330,7 +331,7 @@ QQmlComponent *QQuickItemView::delegate() const { Q_D(const QQuickItemView); if (d->model) { - if (QQuickVisualDataModel *dataModel = qobject_cast(d->model)) + if (QQmlDelegateModel *dataModel = qobject_cast(d->model)) return dataModel->delegate(); } @@ -343,10 +344,10 @@ void QQuickItemView::setDelegate(QQmlComponent *delegate) if (delegate == this->delegate()) return; if (!d->ownModel) { - d->model = new QQuickVisualDataModel(qmlContext(this)); + d->model = new QQmlDelegateModel(qmlContext(this)); d->ownModel = true; } - if (QQuickVisualDataModel *dataModel = qobject_cast(d->model)) { + if (QQmlDelegateModel *dataModel = qobject_cast(d->model)) { int oldCount = dataModel->count(); dataModel->setDelegate(delegate); if (isComponentComplete()) { @@ -371,6 +372,7 @@ void QQuickItemView::setDelegate(QQmlComponent *delegate) emit countChanged(); } emit delegateChanged(); + d->delegateValidated = false; } @@ -972,7 +974,7 @@ void QQuickItemViewPrivate::applyPendingChanges() layout(); } -int QQuickItemViewPrivate::findMoveKeyIndex(QQuickChangeSet::MoveKey key, const QVector &changes) const +int QQuickItemViewPrivate::findMoveKeyIndex(QQmlChangeSet::MoveKey key, const QVector &changes) const { for (int i=0; iforceLayoutPolish(); } -void QQuickItemView::modelUpdated(const QQuickChangeSet &changeSet, bool reset) +void QQuickItemView::modelUpdated(const QQmlChangeSet &changeSet, bool reset) { Q_D(QQuickItemView); if (reset) { @@ -1388,7 +1390,7 @@ void QQuickItemView::componentComplete() { Q_D(QQuickItemView); if (d->model && d->ownModel) - static_cast(d->model.data())->componentComplete(); + static_cast(d->model.data())->componentComplete(); QQuickFlickable::componentComplete(); @@ -1440,7 +1442,7 @@ QQuickItemViewPrivate::QQuickItemViewPrivate() , inLayout(false), inViewportMoved(false), forceLayout(false), currentIndexCleared(false) , haveHighlightRange(false), autoHighlight(true), highlightRangeStartValid(false), highlightRangeEndValid(false) , fillCacheBuffer(false), inRequest(false) - , runDelayedRemoveTransition(false) + , runDelayedRemoveTransition(false), delegateValidated(false) { bufferPause.addAnimationChangeListener(this, QAbstractAnimationJob::Completion); bufferPause.setLoopCount(1); @@ -1874,8 +1876,8 @@ bool QQuickItemViewPrivate::applyModelChanges(ChangeResult *totalInsertionResult totalInsertionResult->visiblePos = prevViewPos; totalRemovalResult->visiblePos = prevViewPos; - const QVector &removals = currentChanges.pendingChanges.removes(); - const QVector &insertions = currentChanges.pendingChanges.inserts(); + const QVector &removals = currentChanges.pendingChanges.removes(); + const QVector &insertions = currentChanges.pendingChanges.inserts(); ChangeResult insertionResult(prevViewPos); ChangeResult removalResult(prevViewPos); @@ -1895,7 +1897,7 @@ bool QQuickItemViewPrivate::applyModelChanges(ChangeResult *totalInsertionResult } } if (runDelayedRemoveTransition) { - QQuickChangeSet::Remove removal; + QQmlChangeSet::Remove removal; for (QList::Iterator it = visibleItems.begin(); it != visibleItems.end();) { FxViewItem *item = *it; if (item->index == -1 && !item->attached->delayRemove()) { @@ -1963,7 +1965,7 @@ bool QQuickItemViewPrivate::applyModelChanges(ChangeResult *totalInsertionResult // Whatever removed/moved items remain are no longer visible items. prepareRemoveTransitions(¤tChanges.removedItems); - for (QHash::Iterator it = currentChanges.removedItems.begin(); + for (QHash::Iterator it = currentChanges.removedItems.begin(); it != currentChanges.removedItems.end(); ++it) { releaseItem(it.value()); } @@ -1992,7 +1994,7 @@ bool QQuickItemViewPrivate::applyModelChanges(ChangeResult *totalInsertionResult return visibleAffected; } -bool QQuickItemViewPrivate::applyRemovalChange(const QQuickChangeSet::Remove &removal, ChangeResult *removeResult, int *removedCount) +bool QQuickItemViewPrivate::applyRemovalChange(const QQmlChangeSet::Remove &removal, ChangeResult *removeResult, int *removedCount) { Q_Q(QQuickItemView); bool visibleAffected = false; @@ -2042,7 +2044,7 @@ bool QQuickItemViewPrivate::applyRemovalChange(const QQuickChangeSet::Remove &re return visibleAffected; } -void QQuickItemViewPrivate::removeItem(FxViewItem *item, const QQuickChangeSet::Remove &removal, ChangeResult *removeResult) +void QQuickItemViewPrivate::removeItem(FxViewItem *item, const QQmlChangeSet::Remove &removal, ChangeResult *removeResult) { if (removeResult->visiblePos.isValid()) { if (item->position() < removeResult->visiblePos) @@ -2055,7 +2057,7 @@ void QQuickItemViewPrivate::removeItem(FxViewItem *item, const QQuickChangeSet:: item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::MoveTransition, true); } else { // track item so it is released later - currentChanges.removedItems.insertMulti(QQuickChangeSet::MoveKey(), item); + currentChanges.removedItems.insertMulti(QQmlChangeSet::MoveKey(), item); } if (!removeResult->changedFirstItem && item == *visibleItems.constBegin()) removeResult->changedFirstItem = true; @@ -2120,14 +2122,14 @@ void QQuickItemViewPrivate::prepareVisibleItemTransitions() visibleItems[i]->prepareTransition(transitioner, viewBounds); } -void QQuickItemViewPrivate::prepareRemoveTransitions(QHash *removedItems) +void QQuickItemViewPrivate::prepareRemoveTransitions(QHash *removedItems) { if (!transitioner) return; if (transitioner->canTransition(QQuickItemViewTransitioner::RemoveTransition, true) || transitioner->canTransition(QQuickItemViewTransitioner::RemoveTransition, false)) { - for (QHash::Iterator it = removedItems->begin(); + for (QHash::Iterator it = removedItems->begin(); it != removedItems->end(); ) { bool isRemove = it.key().moveId < 0; if (isRemove) { @@ -2197,7 +2199,20 @@ FxViewItem *QQuickItemViewPrivate::createItem(int modelIndex, bool asynchronous) requestedIndex = modelIndex; inRequest = true; - if (QQuickItem *item = model->item(modelIndex, asynchronous)) { + QObject* object = model->object(modelIndex, asynchronous); + QQuickItem *item = qmlobject_cast(object); + if (!item) { + if (object) { + model->release(object); + if (!delegateValidated) { + delegateValidated = true; + QObject* delegate = q->delegate(); + qmlInfo(delegate ? delegate : q) << q->tr("Delegate must be of Item type"); + } + } + inRequest = false; + return 0; + } else { item->setParentItem(q->contentItem()); if (requestedIndex == modelIndex) requestedIndex = -1; @@ -2212,15 +2227,13 @@ FxViewItem *QQuickItemViewPrivate::createItem(int modelIndex, bool asynchronous) inRequest = false; return viewItem; } - - inRequest = false; - return 0; } -void QQuickItemView::createdItem(int index, QQuickItem *item) +void QQuickItemView::createdItem(int index, QObject* object) { Q_D(QQuickItemView); + QQuickItem* item = qmlobject_cast(object); if (!d->inRequest) { d->unrequestedItems.insert(item, index); d->requestedIndex = -1; @@ -2235,16 +2248,21 @@ void QQuickItemView::createdItem(int index, QQuickItem *item) } } -void QQuickItemView::initItem(int, QQuickItem *item) +void QQuickItemView::initItem(int, QObject *object) { - item->setZ(1); - item->setParentItem(contentItem()); - QQuickItemPrivate::get(item)->setCulled(true); + QQuickItem* item = qmlobject_cast(object); + if (item) { + item->setZ(1); + item->setParentItem(contentItem()); + QQuickItemPrivate::get(item)->setCulled(true); + } } -void QQuickItemView::destroyingItem(QQuickItem *item) +void QQuickItemView::destroyingItem(QObject *object) { Q_D(QQuickItemView); + QQuickItem* item = qmlobject_cast(object); + item->setParentItem(0); d->unrequestedItems.remove(item); } @@ -2257,14 +2275,16 @@ bool QQuickItemViewPrivate::releaseItem(FxViewItem *item) trackedItem = 0; QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item->item); itemPrivate->removeItemChangeListener(this, QQuickItemPrivate::Geometry); - QQuickVisualModel::ReleaseFlags flags = model->release(item->item); + QQmlInstanceModel::ReleaseFlags flags = model->release(item->item); if (flags == 0) { // item was not destroyed, and we no longer reference it. QQuickItemPrivate::get(item->item)->setCulled(true); unrequestedItems.insert(item->item, model->indexOf(item->item, q)); + } else if (flags & QQmlInstanceModel::Destroyed) { + item->item->setParentItem(0); } delete item; - return flags != QQuickVisualModel::Referenced; + return flags != QQmlInstanceModel::Referenced; } QQuickItem *QQuickItemViewPrivate::createHighlightItem() diff --git a/src/quick/items/qquickitemview_p.h b/src/quick/items/qquickitemview_p.h index 82180a2e13..f94137100d 100644 --- a/src/quick/items/qquickitemview_p.h +++ b/src/quick/items/qquickitemview_p.h @@ -48,7 +48,7 @@ QT_BEGIN_HEADER QT_BEGIN_NAMESPACE -class QQuickChangeSet; +class QQmlChangeSet; class QQuickItemViewPrivate; @@ -257,10 +257,10 @@ protected: protected slots: void destroyRemoved(); - void createdItem(int index, QQuickItem *item); - virtual void initItem(int index, QQuickItem *item); - void modelUpdated(const QQuickChangeSet &changeSet, bool reset); - void destroyingItem(QQuickItem *item); + void createdItem(int index, QObject *item); + virtual void initItem(int index, QObject *item); + void modelUpdated(const QQmlChangeSet &changeSet, bool reset); + void destroyingItem(QObject *item); void animStopped(); void trackedPositionChanged(); diff --git a/src/quick/items/qquickitemview_p_p.h b/src/quick/items/qquickitemview_p_p.h index 0efc458c3d..af895fb983 100644 --- a/src/quick/items/qquickitemview_p_p.h +++ b/src/quick/items/qquickitemview_p_p.h @@ -45,9 +45,9 @@ #include "qquickitemview_p.h" #include "qquickitemviewtransition_p.h" #include "qquickflickable_p_p.h" -#include "qquickvisualdatamodel_p.h" -#include "qquickvisualitemmodel_p.h" -#include +#include +#include +#include QT_BEGIN_HEADER @@ -103,14 +103,14 @@ public: void prepare(int currentIndex, int count); void reset(); - void applyChanges(const QQuickChangeSet &changeSet); + void applyChanges(const QQmlChangeSet &changeSet); void applyBufferedChanges(const QQuickItemViewChangeSet &other); int itemCount; int newCurrentIndex; - QQuickChangeSet pendingChanges; - QHash removedItems; + QQmlChangeSet pendingChanges; + QHash removedItems; bool active : 1; bool currentChanged : 1; @@ -209,18 +209,18 @@ public: void applyPendingChanges(); bool applyModelChanges(ChangeResult *insertionResult, ChangeResult *removalResult); - bool applyRemovalChange(const QQuickChangeSet::Remove &removal, ChangeResult *changeResult, int *removedCount); - void removeItem(FxViewItem *item, const QQuickChangeSet::Remove &removal, ChangeResult *removeResult); + bool applyRemovalChange(const QQmlChangeSet::Remove &removal, ChangeResult *changeResult, int *removedCount); + void removeItem(FxViewItem *item, const QQmlChangeSet::Remove &removal, ChangeResult *removeResult); void repositionFirstItem(FxViewItem *prevVisibleItemsFirst, qreal prevVisibleItemsFirstPos, FxViewItem *prevFirstVisible, ChangeResult *insertionResult, ChangeResult *removalResult); void createTransitioner(); void prepareVisibleItemTransitions(); - void prepareRemoveTransitions(QHash *removedItems); + void prepareRemoveTransitions(QHash *removedItems); bool prepareNonVisibleItemTransition(FxViewItem *item, const QRectF &viewBounds); virtual void viewItemTransitionFinished(QQuickItemViewTransitionableItem *item); - int findMoveKeyIndex(QQuickChangeSet::MoveKey key, const QVector &changes) const; + int findMoveKeyIndex(QQmlChangeSet::MoveKey key, const QVector &changes) const; void checkVisible() const; void showVisibleItems() const; @@ -251,7 +251,7 @@ public: q->polish(); } - QQmlGuard model; + QQmlGuard model; QVariant modelVariant; int itemCount; int buffer; @@ -286,8 +286,8 @@ public: struct MovedItem { FxViewItem *item; - QQuickChangeSet::MoveKey moveKey; - MovedItem(FxViewItem *i, QQuickChangeSet::MoveKey k) + QQmlChangeSet::MoveKey moveKey; + MovedItem(FxViewItem *i, QQmlChangeSet::MoveKey k) : item(i), moveKey(k) {} }; QQuickItemViewTransitioner *transitioner; @@ -309,6 +309,7 @@ public: bool fillCacheBuffer : 1; bool inRequest : 1; bool runDelayedRemoveTransition : 1; + bool delegateValidated : 1; protected: virtual Qt::Orientation layoutOrientation() const = 0; @@ -346,7 +347,7 @@ protected: virtual void layoutVisibleItems(int fromModelIndex = 0) = 0; virtual void changedVisibleIndex(int newIndex) = 0; - virtual bool applyInsertionChange(const QQuickChangeSet::Insert &insert, ChangeResult *changeResult, + virtual bool applyInsertionChange(const QQmlChangeSet::Insert &insert, ChangeResult *changeResult, QList *newItems, QList *movingIntoView) = 0; virtual bool needsRefillForAddedOrRemovedIndex(int) const { return false; } diff --git a/src/quick/items/qquicklistview.cpp b/src/quick/items/qquicklistview.cpp index 57f1b99456..297f64176c 100644 --- a/src/quick/items/qquicklistview.cpp +++ b/src/quick/items/qquicklistview.cpp @@ -41,8 +41,8 @@ #include "qquicklistview_p.h" #include "qquickitemview_p_p.h" -#include "qquickvisualitemmodel_p.h" +#include #include #include #include @@ -106,7 +106,7 @@ public: virtual void setPosition(qreal pos); virtual void layoutVisibleItems(int fromModelIndex = 0); - virtual bool applyInsertionChange(const QQuickChangeSet::Insert &insert, ChangeResult *changeResult, QList *addedItems, QList *movingIntoView); + virtual bool applyInsertionChange(const QQmlChangeSet::Insert &insert, ChangeResult *changeResult, QList *addedItems, QList *movingIntoView); virtual void translateAndTransitionItemsAfter(int afterIndex, const ChangeResult &insertionResult, const ChangeResult &removalResult); virtual void updateSectionCriteria(); @@ -2803,13 +2803,16 @@ void QQuickListView::geometryChanged(const QRectF &newGeometry, const QRectF &ol QQuickItemView::geometryChanged(newGeometry, oldGeometry); } -void QQuickListView::initItem(int index, QQuickItem *item) +void QQuickListView::initItem(int index, QObject *object) { - QQuickItemView::initItem(index, item); - QQuickListViewAttached *attached = static_cast( - qmlAttachedPropertiesObject(item)); - if (attached) - attached->setView(this); + QQuickItemView::initItem(index, object); + QQuickItem *item = qmlobject_cast(object); + if (item) { + QQuickListViewAttached *attached = static_cast( + qmlAttachedPropertiesObject(item)); + if (attached) + attached->setView(this); + } } @@ -2867,7 +2870,7 @@ void QQuickListViewPrivate::updateSectionCriteria() } } -bool QQuickListViewPrivate::applyInsertionChange(const QQuickChangeSet::Insert &change, ChangeResult *insertResult, QList *addedItems, QList *movingIntoView) +bool QQuickListViewPrivate::applyInsertionChange(const QQmlChangeSet::Insert &change, ChangeResult *insertResult, QList *addedItems, QList *movingIntoView) { int modelIndex = change.index; int count = change.count; diff --git a/src/quick/items/qquicklistview_p.h b/src/quick/items/qquicklistview_p.h index f6c405e86b..e0ad1ffc1b 100644 --- a/src/quick/items/qquicklistview_p.h +++ b/src/quick/items/qquicklistview_p.h @@ -96,7 +96,7 @@ private: }; -class QQuickVisualModel; +class QQmlInstanceModel; class QQuickListViewAttached; class Q_AUTOTEST_EXPORT QQuickListView : public QQuickItemView { @@ -169,7 +169,7 @@ protected: virtual void viewportMoved(Qt::Orientations orient); virtual void keyPressEvent(QKeyEvent *); virtual void geometryChanged(const QRectF &newGeometry,const QRectF &oldGeometry); - virtual void initItem(int index, QQuickItem *item); + virtual void initItem(int index, QObject *item); }; class QQuickListViewAttached : public QQuickItemViewAttached diff --git a/src/quick/items/qquickpathview.cpp b/src/quick/items/qquickpathview.cpp index a7be50bc11..e9aa6985fc 100644 --- a/src/quick/items/qquickpathview.cpp +++ b/src/quick/items/qquickpathview.cpp @@ -46,8 +46,9 @@ #include #include #include -#include +#include +#include #include #include #include @@ -117,7 +118,7 @@ QQuickPathViewPrivate::QQuickPathViewPrivate() , offset(0.0), offsetAdj(0.0), mappedRange(1.0), mappedCache(0.0) , stealMouse(false), ownModel(false), interactive(true), haveHighlightRange(true) , autoHighlight(true), highlightUp(false), layoutScheduled(false) - , moving(false), flicking(false), dragging(false), inRequest(false) + , moving(false), flicking(false), dragging(false), inRequest(false), delegateValidated(false) , dragMargin(0), deceleration(100), maximumFlickVelocity(QML_FLICK_DEFAULTMAXVELOCITY) , moveOffset(this, &QQuickPathViewPrivate::setAdjustedOffset), flickDuration(0) , firstIndex(-1), pathItems(-1), requestedIndex(-1), cacheSize(0), requestedZ(0) @@ -150,8 +151,18 @@ QQuickItem *QQuickPathViewPrivate::getItem(int modelIndex, qreal z, bool async) requestedIndex = modelIndex; requestedZ = z; inRequest = true; - QQuickItem *item = model->item(modelIndex, async); - if (item) { + QObject *object = model->object(modelIndex, async); + QQuickItem *item = qmlobject_cast(object); + if (!item) { + if (object) { + model->release(object); + if (!delegateValidated) { + delegateValidated = true; + QObject* delegate = q->delegate(); + qmlInfo(delegate ? delegate : q) << q->tr("Delegate must be of Item type"); + } + } + } else { item->setParentItem(q); requestedIndex = -1; QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); @@ -161,9 +172,10 @@ QQuickItem *QQuickPathViewPrivate::getItem(int modelIndex, qreal z, bool async) return item; } -void QQuickPathView::createdItem(int index, QQuickItem *item) +void QQuickPathView::createdItem(int index, QObject *object) { Q_D(QQuickPathView); + QQuickItem *item = qmlobject_cast(object); if (d->requestedIndex != index) { qPathViewAttachedType = d->attachedType(); QQuickPathViewAttached *att = static_cast(qmlAttachedPropertiesObject(item)); @@ -181,10 +193,11 @@ void QQuickPathView::createdItem(int index, QQuickItem *item) } } -void QQuickPathView::initItem(int index, QQuickItem *item) +void QQuickPathView::initItem(int index, QObject *object) { Q_D(QQuickPathView); - if (d->requestedIndex == index) { + QQuickItem *item = qmlobject_cast(object); + if (item && d->requestedIndex == index) { QQuickItemPrivate::get(item)->setCulled(true); item->setParentItem(this); qPathViewAttachedType = d->attachedType(); @@ -209,10 +222,14 @@ void QQuickPathViewPrivate::releaseItem(QQuickItem *item) return; QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); itemPrivate->removeItemChangeListener(this, QQuickItemPrivate::Geometry); - if (model->release(item) == 0) { + QQmlInstanceModel::ReleaseFlags flags = model->release(item); + if (!flags) { // item was not destroyed, and we no longer reference it. if (QQuickPathViewAttached *att = attached(item)) att->setOnPath(false); + } else if (flags & QQmlInstanceModel::Destroyed) { + // but we still reference it + item->setParentItem(0); } } @@ -619,19 +636,19 @@ void QQuickPathView::setModel(const QVariant &model) return; if (d->model) { - qmlobject_disconnect(d->model, QQuickVisualModel, SIGNAL(modelUpdated(QQuickChangeSet,bool)), - this, QQuickPathView, SLOT(modelUpdated(QQuickChangeSet,bool))); - qmlobject_disconnect(d->model, QQuickVisualModel, SIGNAL(createdItem(int,QQuickItem*)), - this, QQuickPathView, SLOT(createdItem(int,QQuickItem*))); - qmlobject_disconnect(d->model, QQuickVisualModel, SIGNAL(initItem(int,QQuickItem*)), - this, QQuickPathView, SLOT(initItem(int,QQuickItem*))); + qmlobject_disconnect(d->model, QQmlInstanceModel, SIGNAL(modelUpdated(QQmlChangeSet,bool)), + this, QQuickPathView, SLOT(modelUpdated(QQmlChangeSet,bool))); + qmlobject_disconnect(d->model, QQmlInstanceModel, SIGNAL(createdItem(int,QObject*)), + this, QQuickPathView, SLOT(createdItem(int,QObject*))); + qmlobject_disconnect(d->model, QQmlInstanceModel, SIGNAL(initItem(int,QObject*)), + this, QQuickPathView, SLOT(initItem(int,QObject*))); d->clear(); } d->modelVariant = model; QObject *object = qvariant_cast(model); - QQuickVisualModel *vim = 0; - if (object && (vim = qobject_cast(object))) { + QQmlInstanceModel *vim = 0; + if (object && (vim = qobject_cast(object))) { if (d->ownModel) { delete d->model; d->ownModel = false; @@ -639,23 +656,23 @@ void QQuickPathView::setModel(const QVariant &model) d->model = vim; } else { if (!d->ownModel) { - d->model = new QQuickVisualDataModel(qmlContext(this)); + d->model = new QQmlDelegateModel(qmlContext(this)); d->ownModel = true; if (isComponentComplete()) - static_cast(d->model.data())->componentComplete(); + static_cast(d->model.data())->componentComplete(); } - if (QQuickVisualDataModel *dataModel = qobject_cast(d->model)) + if (QQmlDelegateModel *dataModel = qobject_cast(d->model)) dataModel->setModel(model); } int oldModelCount = d->modelCount; d->modelCount = 0; if (d->model) { - qmlobject_connect(d->model, QQuickVisualModel, SIGNAL(modelUpdated(QQuickChangeSet,bool)), - this, QQuickPathView, SLOT(modelUpdated(QQuickChangeSet,bool))); - qmlobject_connect(d->model, QQuickVisualModel, SIGNAL(createdItem(int,QQuickItem*)), - this, QQuickPathView, SLOT(createdItem(int,QQuickItem*))); - qmlobject_connect(d->model, QQuickVisualModel, SIGNAL(initItem(int,QQuickItem*)), - this, QQuickPathView, SLOT(initItem(int,QQuickItem*))); + qmlobject_connect(d->model, QQmlInstanceModel, SIGNAL(modelUpdated(QQmlChangeSet,bool)), + this, QQuickPathView, SLOT(modelUpdated(QQmlChangeSet,bool))); + qmlobject_connect(d->model, QQmlInstanceModel, SIGNAL(createdItem(int,QObject*)), + this, QQuickPathView, SLOT(createdItem(int,QObject*))); + qmlobject_connect(d->model, QQmlInstanceModel, SIGNAL(initItem(int,QObject*)), + this, QQuickPathView, SLOT(initItem(int,QObject*))); d->modelCount = d->model->count(); } if (isComponentComplete()) { @@ -1210,7 +1227,7 @@ QQmlComponent *QQuickPathView::delegate() const { Q_D(const QQuickPathView); if (d->model) { - if (QQuickVisualDataModel *dataModel = qobject_cast(d->model)) + if (QQmlDelegateModel *dataModel = qobject_cast(d->model)) return dataModel->delegate(); } @@ -1223,10 +1240,10 @@ void QQuickPathView::setDelegate(QQmlComponent *delegate) if (delegate == this->delegate()) return; if (!d->ownModel) { - d->model = new QQuickVisualDataModel(qmlContext(this)); + d->model = new QQmlDelegateModel(qmlContext(this)); d->ownModel = true; } - if (QQuickVisualDataModel *dataModel = qobject_cast(d->model)) { + if (QQmlDelegateModel *dataModel = qobject_cast(d->model)) { int oldCount = dataModel->count(); dataModel->setDelegate(delegate); d->modelCount = dataModel->count(); @@ -1234,6 +1251,7 @@ void QQuickPathView::setDelegate(QQmlComponent *delegate) if (oldCount != dataModel->count()) emit countChanged(); emit delegateChanged(); + d->delegateValidated = false; } } @@ -1821,7 +1839,7 @@ void QQuickPathView::componentComplete() { Q_D(QQuickPathView); if (d->model && d->ownModel) - static_cast(d->model.data())->componentComplete(); + static_cast(d->model.data())->componentComplete(); QQuickItem::componentComplete(); @@ -1983,7 +2001,7 @@ void QQuickPathView::refill() d->releaseItem(d->itemCache.takeLast()); } -void QQuickPathView::modelUpdated(const QQuickChangeSet &changeSet, bool reset) +void QQuickPathView::modelUpdated(const QQmlChangeSet &changeSet, bool reset) { Q_D(QQuickPathView); if (!d->model || !d->model->isValid() || !d->path || !isComponentComplete()) @@ -2004,7 +2022,7 @@ void QQuickPathView::modelUpdated(const QQuickChangeSet &changeSet, bool reset) int moveOffset; bool currentChanged = false; bool changedOffset = false; - foreach (const QQuickChangeSet::Remove &r, changeSet.removes()) { + foreach (const QQmlChangeSet::Remove &r, changeSet.removes()) { if (moveId == -1 && d->currentIndex >= r.index + r.count) { d->currentIndex -= r.count; currentChanged = true; @@ -2030,7 +2048,7 @@ void QQuickPathView::modelUpdated(const QQuickChangeSet &changeSet, bool reset) } d->modelCount -= r.count; } - foreach (const QQuickChangeSet::Insert &i, changeSet.inserts()) { + foreach (const QQmlChangeSet::Insert &i, changeSet.inserts()) { if (d->modelCount) { if (moveId == -1 && i.index <= d->currentIndex) { d->currentIndex += i.count; @@ -2082,7 +2100,7 @@ void QQuickPathView::modelUpdated(const QQuickChangeSet &changeSet, bool reset) emit countChanged(); } -void QQuickPathView::destroyingItem(QQuickItem *item) +void QQuickPathView::destroyingItem(QObject *item) { Q_UNUSED(item); } diff --git a/src/quick/items/qquickpathview_p.h b/src/quick/items/qquickpathview_p.h index 9cb6b881a0..1ee23edf5b 100644 --- a/src/quick/items/qquickpathview_p.h +++ b/src/quick/items/qquickpathview_p.h @@ -50,7 +50,7 @@ QT_BEGIN_HEADER QT_BEGIN_NAMESPACE -class QQuickChangeSet; +class QQmlChangeSet; class QQuickPathViewPrivate; class QQuickPathViewAttached; @@ -216,10 +216,10 @@ private Q_SLOTS: void refill(); void ticked(); void movementEnding(); - void modelUpdated(const QQuickChangeSet &changeSet, bool reset); - void createdItem(int index, QQuickItem *item); - void initItem(int index, QQuickItem *item); - void destroyingItem(QQuickItem *item); + void modelUpdated(const QQmlChangeSet &changeSet, bool reset); + void createdItem(int index, QObject *item); + void initItem(int index, QObject *item); + void destroyingItem(QObject *item); void pathUpdated(); private: diff --git a/src/quick/items/qquickpathview_p_p.h b/src/quick/items/qquickpathview_p_p.h index 9f504db22f..26533057d1 100644 --- a/src/quick/items/qquickpathview_p_p.h +++ b/src/quick/items/qquickpathview_p_p.h @@ -55,7 +55,6 @@ #include "qquickpathview_p.h" #include "qquickitem_p.h" -#include "qquickvisualdatamodel_p.h" #include #include @@ -63,6 +62,7 @@ #include #include +#include #include QT_BEGIN_NAMESPACE @@ -152,6 +152,7 @@ public: bool dragging : 1; bool requestedOnPath : 1; bool inRequest : 1; + bool delegateValidated : 1; QElapsedTimer timer; qint64 lastPosTime; QPointF lastPos; @@ -168,7 +169,7 @@ public: qreal requestedZ; QList items; QList itemCache; - QQmlGuard model; + QQmlGuard model; QVariant modelVariant; enum MovementReason { Other, SetIndex, Mouse }; MovementReason moveReason; diff --git a/src/quick/items/qquickrepeater.cpp b/src/quick/items/qquickrepeater.cpp index 68f70a5227..c986e469cf 100644 --- a/src/quick/items/qquickrepeater.cpp +++ b/src/quick/items/qquickrepeater.cpp @@ -41,16 +41,18 @@ #include "qquickrepeater_p.h" #include "qquickrepeater_p_p.h" -#include "qquickvisualdatamodel_p.h" #include -#include -#include +#include +#include +#include + +#include QT_BEGIN_NAMESPACE QQuickRepeaterPrivate::QQuickRepeaterPrivate() - : model(0), ownModel(false), inRequest(false), dataSourceIsObject(false), itemCount(0), createFrom(-1) + : model(0), ownModel(false), inRequest(false), dataSourceIsObject(false), delegateValidated(false), itemCount(0), createFrom(-1) { } @@ -192,18 +194,18 @@ void QQuickRepeater::setModel(const QVariant &model) clear(); if (d->model) { - disconnect(d->model, SIGNAL(modelUpdated(QQuickChangeSet,bool)), - this, SLOT(modelUpdated(QQuickChangeSet,bool))); - disconnect(d->model, SIGNAL(createdItem(int,QQuickItem*)), this, SLOT(createdItem(int,QQuickItem*))); - disconnect(d->model, SIGNAL(initItem(int,QQuickItem*)), this, SLOT(initItem(int,QQuickItem*))); -// disconnect(d->model, SIGNAL(destroyingItem(QQuickItem*)), this, SLOT(destroyingItem(QQuickItem*))); + disconnect(d->model, SIGNAL(modelUpdated(QQmlChangeSet,bool)), + this, SLOT(modelUpdated(QQmlChangeSet,bool))); + disconnect(d->model, SIGNAL(createdItem(int,QObject*)), this, SLOT(createdItem(int,QObject*))); + disconnect(d->model, SIGNAL(initItem(int,QObject*)), this, SLOT(initItem(int,QObject*))); +// disconnect(d->model, SIGNAL(destroyingItem(QObject*)), this, SLOT(destroyingItem(QObject*))); } d->dataSource = model; QObject *object = qvariant_cast(model); d->dataSourceAsObject = object; d->dataSourceIsObject = object != 0; - QQuickVisualModel *vim = 0; - if (object && (vim = qobject_cast(object))) { + QQmlInstanceModel *vim = 0; + if (object && (vim = qobject_cast(object))) { if (d->ownModel) { delete d->model; d->ownModel = false; @@ -211,20 +213,20 @@ void QQuickRepeater::setModel(const QVariant &model) d->model = vim; } else { if (!d->ownModel) { - d->model = new QQuickVisualDataModel(qmlContext(this)); + d->model = new QQmlDelegateModel(qmlContext(this)); d->ownModel = true; if (isComponentComplete()) - static_cast(d->model)->componentComplete(); + static_cast(d->model)->componentComplete(); } - if (QQuickVisualDataModel *dataModel = qobject_cast(d->model)) + if (QQmlDelegateModel *dataModel = qobject_cast(d->model)) dataModel->setModel(model); } if (d->model) { - connect(d->model, SIGNAL(modelUpdated(QQuickChangeSet,bool)), - this, SLOT(modelUpdated(QQuickChangeSet,bool))); - connect(d->model, SIGNAL(createdItem(int,QQuickItem*)), this, SLOT(createdItem(int,QQuickItem*))); - connect(d->model, SIGNAL(initItem(int,QQuickItem*)), this, SLOT(initItem(int,QQuickItem*))); -// connect(d->model, SIGNAL(destroyingItem(QQuickItem*)), this, SLOT(destroyingItem(QQuickItem*))); + connect(d->model, SIGNAL(modelUpdated(QQmlChangeSet,bool)), + this, SLOT(modelUpdated(QQmlChangeSet,bool))); + connect(d->model, SIGNAL(createdItem(int,QObject*)), this, SLOT(createdItem(int,QObject*))); + connect(d->model, SIGNAL(initItem(int,QObject*)), this, SLOT(initItem(int,QObject*))); +// connect(d->model, SIGNAL(destroyingItem(QObject*)), this, SLOT(destroyingItem(QObject*))); regenerate(); } emit modelChanged(); @@ -268,7 +270,7 @@ QQmlComponent *QQuickRepeater::delegate() const { Q_D(const QQuickRepeater); if (d->model) { - if (QQuickVisualDataModel *dataModel = qobject_cast(d->model)) + if (QQmlDelegateModel *dataModel = qobject_cast(d->model)) return dataModel->delegate(); } @@ -278,18 +280,20 @@ QQmlComponent *QQuickRepeater::delegate() const void QQuickRepeater::setDelegate(QQmlComponent *delegate) { Q_D(QQuickRepeater); - if (QQuickVisualDataModel *dataModel = qobject_cast(d->model)) + if (QQmlDelegateModel *dataModel = qobject_cast(d->model)) if (delegate == dataModel->delegate()) return; if (!d->ownModel) { - d->model = new QQuickVisualDataModel(qmlContext(this)); + d->model = new QQmlDelegateModel(qmlContext(this)); d->ownModel = true; } - if (QQuickVisualDataModel *dataModel = qobject_cast(d->model)) { + + if (QQmlDelegateModel *dataModel = qobject_cast(d->model)) { dataModel->setDelegate(delegate); regenerate(); emit delegateChanged(); + d->delegateValidated = false; } } @@ -324,7 +328,7 @@ void QQuickRepeater::componentComplete() { Q_D(QQuickRepeater); if (d->model && d->ownModel) - static_cast(d->model)->componentComplete(); + static_cast(d->model)->componentComplete(); QQuickItem::componentComplete(); regenerate(); if (d->model && d->model->count()) @@ -349,6 +353,7 @@ void QQuickRepeater::clear() QQuickItem *item = d->deletables.at(i); if (complete) emit itemRemoved(i, item); + item->setParentItem(0); d->model->release(item); } } @@ -381,8 +386,17 @@ void QQuickRepeaterPrivate::createItems() inRequest = true; for (int ii = createFrom; ii < itemCount; ++ii) { if (!deletables.at(ii)) { - QQuickItem *item = model->item(ii, false); + QObject *object = model->object(ii, false); + QQuickItem *item = qmlobject_cast(object); if (!item) { + if (object) { + model->release(object); + if (!delegateValidated) { + delegateValidated = true; + QObject* delegate = q->delegate(); + qmlInfo(delegate ? delegate : q) << q->tr("Delegate must be of Item type"); + } + } createFrom = ii; break; } @@ -406,19 +420,21 @@ void QQuickRepeaterPrivate::createItems() inRequest = false; } -void QQuickRepeater::createdItem(int, QQuickItem *) +void QQuickRepeater::createdItem(int, QObject *) { Q_D(QQuickRepeater); if (!d->inRequest) d->createItems(); } -void QQuickRepeater::initItem(int, QQuickItem *item) +void QQuickRepeater::initItem(int, QObject *object) { - item->setParentItem(parentItem()); + QQuickItem *item = qmlobject_cast(object); + if (item) + item->setParentItem(parentItem()); } -void QQuickRepeater::modelUpdated(const QQuickChangeSet &changeSet, bool reset) +void QQuickRepeater::modelUpdated(const QQmlChangeSet &changeSet, bool reset) { Q_D(QQuickRepeater); @@ -434,7 +450,7 @@ void QQuickRepeater::modelUpdated(const QQuickChangeSet &changeSet, bool reset) int difference = 0; QHash > > moved; - foreach (const QQuickChangeSet::Remove &remove, changeSet.removes()) { + foreach (const QQmlChangeSet::Remove &remove, changeSet.removes()) { int index = qMin(remove.index, d->deletables.count()); int count = qMin(remove.index + remove.count, d->deletables.count()) - index; if (remove.isMove()) { @@ -446,8 +462,10 @@ void QQuickRepeater::modelUpdated(const QQuickChangeSet &changeSet, bool reset) QQuickItem *item = d->deletables.at(index); d->deletables.remove(index); emit itemRemoved(index, item); - if (item) + if (item) { + item->setParentItem(0); d->model->release(item); + } --d->itemCount; } @@ -455,7 +473,7 @@ void QQuickRepeater::modelUpdated(const QQuickChangeSet &changeSet, bool reset) } d->createFrom = -1; - foreach (const QQuickChangeSet::Insert &insert, changeSet.inserts()) { + foreach (const QQmlChangeSet::Insert &insert, changeSet.inserts()) { int index = qMin(insert.index, d->deletables.count()); if (insert.isMove()) { QVector > items = moved.value(insert.moveId); diff --git a/src/quick/items/qquickrepeater_p.h b/src/quick/items/qquickrepeater_p.h index 17153694c9..8698b8224f 100644 --- a/src/quick/items/qquickrepeater_p.h +++ b/src/quick/items/qquickrepeater_p.h @@ -48,7 +48,7 @@ QT_BEGIN_HEADER QT_BEGIN_NAMESPACE -class QQuickChangeSet; +class QQmlChangeSet; class QQuickRepeaterPrivate; class Q_AUTOTEST_EXPORT QQuickRepeater : public QQuickItem @@ -91,9 +91,9 @@ protected: void itemChange(ItemChange change, const ItemChangeData &value); private Q_SLOTS: - void createdItem(int index, QQuickItem *item); - void initItem(int, QQuickItem *item); - void modelUpdated(const QQuickChangeSet &changeSet, bool reset); + void createdItem(int index, QObject *item); + void initItem(int, QObject *item); + void modelUpdated(const QQmlChangeSet &changeSet, bool reset); private: Q_DISABLE_COPY(QQuickRepeater) diff --git a/src/quick/items/qquickrepeater_p_p.h b/src/quick/items/qquickrepeater_p_p.h index a187aea39c..f220eb4fcb 100644 --- a/src/quick/items/qquickrepeater_p_p.h +++ b/src/quick/items/qquickrepeater_p_p.h @@ -61,7 +61,7 @@ QT_BEGIN_NAMESPACE class QQmlContext; -class QQuickVisualModel; +class QQmlInstanceModel; class QQuickRepeaterPrivate : public QQuickItemPrivate { Q_DECLARE_PUBLIC(QQuickRepeater) @@ -73,12 +73,13 @@ public: private: void createItems(); - QQuickVisualModel *model; + QQmlInstanceModel *model; QVariant dataSource; QQmlGuard dataSourceAsObject; bool ownModel : 1; bool inRequest : 1; bool dataSourceIsObject : 1; + bool delegateValidated : 1; int itemCount; int createFrom; diff --git a/src/quick/items/qquickvisualadaptormodel.cpp b/src/quick/items/qquickvisualadaptormodel.cpp deleted file mode 100644 index b57f4bbe76..0000000000 --- a/src/quick/items/qquickvisualadaptormodel.cpp +++ /dev/null @@ -1,977 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** 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, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qquickvisualadaptormodel_p.h" -#include "qquickvisualdatamodel_p_p.h" - -#include -#include -#include - -QT_BEGIN_NAMESPACE - -class QQuickVisualAdaptorModelEngineData : public QV8Engine::Deletable -{ -public: - enum - { - Index, - ModelData, - HasModelChildren, - StringCount - }; - - QQuickVisualAdaptorModelEngineData(QV8Engine *engine); - ~QQuickVisualAdaptorModelEngineData(); - - v8::Local index() { return strings->Get(Index)->ToString(); } - v8::Local modelData() { return strings->Get(ModelData)->ToString(); } - v8::Local hasModelChildren() { return strings->Get(HasModelChildren)->ToString(); } - - v8::Persistent constructorListItem; - v8::Persistent strings; -}; - -V8_DEFINE_EXTENSION(QQuickVisualAdaptorModelEngineData, engineData) - -static v8::Handle get_index(v8::Local, const v8::AccessorInfo &info) -{ - QQuickVisualDataModelItem *data = v8_resource_cast(info.This()); - V8ASSERT_TYPE(data, "Not a valid VisualData object"); - - return v8::Int32::New(data->index); -} - -template static void setModelDataType(QMetaObjectBuilder *builder, M *metaType) -{ - builder->setFlags(QMetaObjectBuilder::DynamicMetaObject); - builder->setClassName(T::staticMetaObject.className()); - builder->setSuperClass(&T::staticMetaObject); - metaType->propertyOffset = T::staticMetaObject.propertyCount(); - metaType->signalOffset = T::staticMetaObject.methodCount(); -} - -static void addProperty(QMetaObjectBuilder *builder, int propertyId, const QByteArray &propertyName, const QByteArray &propertyType) -{ - builder->addSignal("__" + QByteArray::number(propertyId) + "()"); - QMetaPropertyBuilder property = builder->addProperty( - propertyName, propertyType, propertyId); - property.setWritable(true); -} - -class VDMModelDelegateDataType; - -class QQuickVDMCachedModelData : public QQuickVisualDataModelItem -{ -public: - QQuickVDMCachedModelData( - QQuickVisualDataModelItemMetaType *metaType, - VDMModelDelegateDataType *dataType, - int index); - - int metaCall(QMetaObject::Call call, int id, void **arguments); - - virtual QVariant value(int role) const = 0; - virtual void setValue(int role, const QVariant &value) = 0; - - void setValue(const QString &role, const QVariant &value); - bool resolveIndex(const QQuickVisualAdaptorModel &model, int idx); - - static v8::Handle get_property(v8::Local, const v8::AccessorInfo &info); - static void set_property( - v8::Local, v8::Local value, const v8::AccessorInfo &info); - - VDMModelDelegateDataType *type; - QVector cachedData; -}; - -class VDMModelDelegateDataType - : public QQmlRefCount - , public QQuickVisualAdaptorModel::Accessors - , public QAbstractDynamicMetaObject -{ -public: - VDMModelDelegateDataType(QQuickVisualAdaptorModel *model) - : model(model) - , metaObject(0) - , propertyCache(0) - , propertyOffset(0) - , signalOffset(0) - , hasModelData(false) - { - } - - ~VDMModelDelegateDataType() - { - if (propertyCache) - propertyCache->release(); - free(metaObject); - - qPersistentDispose(constructor); - } - - bool notify( - const QQuickVisualAdaptorModel &, - const QList &items, - int index, - int count, - const QVector &roles) const - { - bool changed = roles.isEmpty() && !watchedRoles.isEmpty(); - if (!changed && !watchedRoles.isEmpty() && watchedRoleIds.isEmpty()) { - QList roleIds; - foreach (const QByteArray &r, watchedRoles) { - QHash::const_iterator it = roleNames.find(r); - if (it != roleNames.end()) - roleIds << it.value(); - } - const_cast(this)->watchedRoleIds = roleIds; - } - - QVector signalIndexes; - for (int i = 0; i < roles.count(); ++i) { - const int role = roles.at(i); - if (!changed && watchedRoleIds.contains(role)) - changed = true; - - int propertyId = propertyRoles.indexOf(role); - if (propertyId != -1) - signalIndexes.append(propertyId + signalOffset); - } - if (roles.isEmpty()) { - for (int propertyId = 0; propertyId < propertyRoles.count(); ++propertyId) - signalIndexes.append(propertyId + signalOffset); - } - - for (int i = 0, c = items.count(); i < c; ++i) { - QQuickVisualDataModelItem *item = items.at(i); - const int idx = item->modelIndex(); - if (idx >= index && idx < index + count) { - for (int i = 0; i < signalIndexes.count(); ++i) - QMetaObject::activate(item, signalIndexes.at(i), 0); - } - } - return changed; - } - - void replaceWatchedRoles( - QQuickVisualAdaptorModel &, - const QList &oldRoles, - const QList &newRoles) const - { - VDMModelDelegateDataType *dataType = const_cast(this); - - dataType->watchedRoleIds.clear(); - foreach (const QByteArray &oldRole, oldRoles) - dataType->watchedRoles.removeOne(oldRole); - dataType->watchedRoles += newRoles; - } - - void initializeConstructor(QQuickVisualAdaptorModelEngineData *const data) - { - constructor = qPersistentNew(v8::ObjectTemplate::New()); - constructor->SetHasExternalResource(true); - constructor->SetAccessor(data->index(), get_index); - - typedef QHash::const_iterator iterator; - for (iterator it = roleNames.constBegin(), end = roleNames.constEnd(); it != end; ++it) { - const int propertyId = propertyRoles.indexOf(it.value()); - const QByteArray &propertyName = it.key(); - - constructor->SetAccessor( - v8::String::New(propertyName.constData(), propertyName.length()), - QQuickVDMCachedModelData::get_property, - QQuickVDMCachedModelData::set_property, - v8::Int32::New(propertyId)); - } - } - - // QAbstractDynamicMetaObject - - void objectDestroyed(QObject *) - { - release(); - } - - int metaCall(QObject *object, QMetaObject::Call call, int id, void **arguments) - { - return static_cast(object)->metaCall(call, id, arguments); - } - - v8::Persistent constructor; - QList propertyRoles; - QList watchedRoleIds; - QList watchedRoles; - QHash roleNames; - QQuickVisualAdaptorModel *model; - QMetaObject *metaObject; - QQmlPropertyCache *propertyCache; - int propertyOffset; - int signalOffset; - bool hasModelData; -}; - -QQuickVDMCachedModelData::QQuickVDMCachedModelData( - QQuickVisualDataModelItemMetaType *metaType, VDMModelDelegateDataType *dataType, int index) - : QQuickVisualDataModelItem(metaType, index) - , type(dataType) -{ - if (index == -1) - cachedData.resize(type->hasModelData ? 1 : type->propertyRoles.count()); - - QObjectPrivate::get(this)->metaObject = type; - - type->addref(); - - QQmlData *qmldata = QQmlData::get(this, true); - qmldata->propertyCache = dataType->propertyCache; - qmldata->propertyCache->addref(); -} - -int QQuickVDMCachedModelData::metaCall(QMetaObject::Call call, int id, void **arguments) -{ - if (call == QMetaObject::ReadProperty && id >= type->propertyOffset) { - const int propertyIndex = id - type->propertyOffset; - if (index == -1) { - if (!cachedData.isEmpty()) { - *static_cast(arguments[0]) = cachedData.at( - type->hasModelData ? 0 : propertyIndex); - } - } else if (*type->model) { - *static_cast(arguments[0]) = value(type->propertyRoles.at(propertyIndex)); - } - return -1; - } else if (call == QMetaObject::WriteProperty && id >= type->propertyOffset) { - const int propertyIndex = id - type->propertyOffset; - if (index == -1) { - const QMetaObject *meta = metaObject(); - if (cachedData.count() > 1) { - cachedData[propertyIndex] = *static_cast(arguments[0]); - QMetaObject::activate(this, meta, propertyIndex, 0); - } else if (cachedData.count() == 1) { - cachedData[0] = *static_cast(arguments[0]); - QMetaObject::activate(this, meta, 0, 0); - QMetaObject::activate(this, meta, 1, 0); - } - } else if (*type->model) { - setValue(type->propertyRoles.at(propertyIndex), *static_cast(arguments[0])); - } - return -1; - } else { - return qt_metacall(call, id, arguments); - } -} - -void QQuickVDMCachedModelData::setValue(const QString &role, const QVariant &value) -{ - QHash::iterator it = type->roleNames.find(role.toUtf8()); - if (it != type->roleNames.end()) { - for (int i = 0; i < type->propertyRoles.count(); ++i) { - if (type->propertyRoles.at(i) == *it) { - cachedData[i] = value; - return; - } - } - } -} - -bool QQuickVDMCachedModelData::resolveIndex(const QQuickVisualAdaptorModel &, int idx) -{ - if (index == -1) { - Q_ASSERT(idx >= 0); - index = idx; - cachedData.clear(); - emit modelIndexChanged(); - const QMetaObject *meta = metaObject(); - const int propertyCount = type->propertyRoles.count(); - for (int i = 0; i < propertyCount; ++i) - QMetaObject::activate(this, meta, i, 0); - return true; - } else { - return false; - } -} - -v8::Handle QQuickVDMCachedModelData::get_property( - v8::Local, const v8::AccessorInfo &info) -{ - QQuickVisualDataModelItem *data = v8_resource_cast(info.This()); - V8ASSERT_TYPE(data, "Not a valid VisualData object"); - - QQuickVDMCachedModelData *modelData = static_cast(data); - const int propertyId = info.Data()->Int32Value(); - if (data->index == -1) { - if (!modelData->cachedData.isEmpty()) { - return data->engine->fromVariant( - modelData->cachedData.at(modelData->type->hasModelData ? 0 : propertyId)); - } - } else if (*modelData->type->model) { - return data->engine->fromVariant( - modelData->value(modelData->type->propertyRoles.at(propertyId))); - } - return v8::Undefined(); -} - -void QQuickVDMCachedModelData::set_property( - v8::Local, v8::Local value, const v8::AccessorInfo &info) -{ - QQuickVisualDataModelItem *data = v8_resource_cast(info.This()); - V8ASSERT_TYPE_SETTER(data, "Not a valid VisualData object"); - - const int propertyId = info.Data()->Int32Value(); - if (data->index == -1) { - QQuickVDMCachedModelData *modelData = static_cast(data); - if (!modelData->cachedData.isEmpty()) { - if (modelData->cachedData.count() > 1) { - modelData->cachedData[propertyId] = data->engine->toVariant(value, QVariant::Invalid); - QMetaObject::activate(data, data->metaObject(), propertyId, 0); - } else if (modelData->cachedData.count() == 1) { - modelData->cachedData[0] = data->engine->toVariant(value, QVariant::Invalid); - QMetaObject::activate(data, data->metaObject(), 0, 0); - QMetaObject::activate(data, data->metaObject(), 1, 0); - } - } - } -} - -//----------------------------------------------------------------- -// QAbstractItemModel -//----------------------------------------------------------------- - -class QQuickVDMAbstractItemModelData : public QQuickVDMCachedModelData -{ - Q_OBJECT - Q_PROPERTY(bool hasModelChildren READ hasModelChildren CONSTANT) -public: - QQuickVDMAbstractItemModelData( - QQuickVisualDataModelItemMetaType *metaType, - VDMModelDelegateDataType *dataType, - int index) - : QQuickVDMCachedModelData(metaType, dataType, index) - { - } - - bool hasModelChildren() const - { - if (index >= 0 && *type->model) { - const QAbstractItemModel * const model = type->model->aim(); - return model->hasChildren(model->index(index, 0, type->model->rootIndex)); - } else { - return false; - } - } - - QVariant value(int role) const - { - return type->model->aim()->index(index, 0, type->model->rootIndex).data(role); - } - - void setValue(int role, const QVariant &value) - { - type->model->aim()->setData( - type->model->aim()->index(index, 0, type->model->rootIndex), value, role); - } - - v8::Handle get() - { - if (type->constructor.IsEmpty()) { - QQuickVisualAdaptorModelEngineData * const data = engineData(engine); - v8::HandleScope handleScope; - v8::Context::Scope contextScope(engine->context()); - type->initializeConstructor(data); - type->constructor->SetAccessor(data->hasModelChildren(), get_hasModelChildren); - } - v8::Local data = type->constructor->NewInstance(); - data->SetExternalResource(this); - ++scriptRef; - return data; - } - - static v8::Handle get_hasModelChildren(v8::Local, const v8::AccessorInfo &info) - { - QQuickVisualDataModelItem *data = v8_resource_cast(info.This()); - V8ASSERT_TYPE(data, "Not a valid VisualData object"); - - const QQuickVisualAdaptorModel *const model = static_cast(data)->type->model; - if (data->index >= 0 && *model) { - const QAbstractItemModel * const aim = model->aim(); - return v8::Boolean::New(aim->hasChildren(aim->index(data->index, 0, model->rootIndex))); - } else { - return v8::Boolean::New(false); - } - } -}; - -class VDMAbstractItemModelDataType : public VDMModelDelegateDataType -{ -public: - VDMAbstractItemModelDataType(QQuickVisualAdaptorModel *model) - : VDMModelDelegateDataType(model) - { - } - - int count(const QQuickVisualAdaptorModel &model) const - { - return model.aim()->rowCount(model.rootIndex); - } - - void cleanup(QQuickVisualAdaptorModel &model, QQuickVisualDataModel *vdm) const - { - QAbstractItemModel * const aim = model.aim(); - if (aim && vdm) { - QObject::disconnect(aim, SIGNAL(rowsInserted(QModelIndex,int,int)), - vdm, SLOT(_q_rowsInserted(QModelIndex,int,int))); - QObject::disconnect(aim, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), - vdm, SLOT(_q_rowsAboutToBeRemoved(QModelIndex,int,int))); - QObject::disconnect(aim, SIGNAL(rowsRemoved(QModelIndex,int,int)), - vdm, SLOT(_q_rowsRemoved(QModelIndex,int,int))); - QObject::disconnect(aim, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector)), - vdm, SLOT(_q_dataChanged(QModelIndex,QModelIndex,QVector))); - QObject::disconnect(aim, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), - vdm, SLOT(_q_rowsMoved(QModelIndex,int,int,QModelIndex,int))); - QObject::disconnect(aim, SIGNAL(modelReset()), - vdm, SLOT(_q_modelReset())); - QObject::disconnect(aim, SIGNAL(layoutChanged()), - vdm, SLOT(_q_layoutChanged())); - } - - const_cast(this)->release(); - } - - QVariant value(const QQuickVisualAdaptorModel &model, int index, const QString &role) const - { - QHash::const_iterator it = roleNames.find(role.toUtf8()); - if (it != roleNames.end()) { - return model.aim()->index(index, 0, model.rootIndex).data(*it); - } else if (role == QLatin1String("hasModelChildren")) { - return QVariant(model.aim()->hasChildren(model.aim()->index(index, 0, model.rootIndex))); - } else { - return QVariant(); - } - } - - QVariant parentModelIndex(const QQuickVisualAdaptorModel &model) const - { - return model - ? QVariant::fromValue(model.aim()->parent(model.rootIndex)) - : QVariant(); - } - - QVariant modelIndex(const QQuickVisualAdaptorModel &model, int index) const - { - return model - ? QVariant::fromValue(model.aim()->index(index, 0, model.rootIndex)) - : QVariant(); - } - - bool canFetchMore(const QQuickVisualAdaptorModel &model) const - { - return model && model.aim()->canFetchMore(model.rootIndex); - } - - void fetchMore(QQuickVisualAdaptorModel &model) const - { - if (model) - model.aim()->fetchMore(model.rootIndex); - } - - QQuickVisualDataModelItem *createItem( - QQuickVisualAdaptorModel &model, - QQuickVisualDataModelItemMetaType *metaType, - QQmlEngine *engine, - int index) const - { - VDMAbstractItemModelDataType *dataType = const_cast(this); - if (!metaObject) - dataType->initializeMetaType(model, engine); - return new QQuickVDMAbstractItemModelData(metaType, dataType, index); - } - - void initializeMetaType(QQuickVisualAdaptorModel &model, QQmlEngine *engine) - { - QMetaObjectBuilder builder; - setModelDataType(&builder, this); - - const QByteArray propertyType = QByteArrayLiteral("QVariant"); - const QHash names = model.aim()->roleNames(); - for (QHash::const_iterator it = names.begin(); it != names.end(); ++it) { - const int propertyId = propertyRoles.count(); - propertyRoles.append(it.key()); - roleNames.insert(it.value(), it.key()); - addProperty(&builder, propertyId, it.value(), propertyType); - } - if (propertyRoles.count() == 1) { - hasModelData = true; - const int role = names.begin().key(); - const QByteArray propertyName = QByteArrayLiteral("modelData"); - - propertyRoles.append(role); - roleNames.insert(propertyName, role); - addProperty(&builder, 1, propertyName, propertyType); - } - - metaObject = builder.toMetaObject(); - *static_cast(this) = *metaObject; - propertyCache = new QQmlPropertyCache(engine, metaObject); - } -}; - -//----------------------------------------------------------------- -// QQuickListAccessor -//----------------------------------------------------------------- - -class QQuickVDMListAccessorData : public QQuickVisualDataModelItem -{ - Q_OBJECT - Q_PROPERTY(QVariant modelData READ modelData WRITE setModelData NOTIFY modelDataChanged) -public: - QQuickVDMListAccessorData(QQuickVisualDataModelItemMetaType *metaType, int index, const QVariant &value) - : QQuickVisualDataModelItem(metaType, index) - , cachedData(value) - { - } - - QVariant modelData() const - { - return cachedData; - } - - void setModelData(const QVariant &data) - { - if (index == -1 && data != cachedData) { - cachedData = data; - emit modelDataChanged(); - } - } - - static v8::Handle get_modelData(v8::Local, const v8::AccessorInfo &info) - { - QQuickVisualDataModelItem *data = v8_resource_cast(info.This()); - V8ASSERT_TYPE(data, "Not a valid VisualData object"); - - return data->engine->fromVariant(static_cast(data)->cachedData); - } - - static void set_modelData(v8::Local, v8::Local value, const v8::AccessorInfo &info) - { - QQuickVisualDataModelItem *data = v8_resource_cast(info.This()); - V8ASSERT_TYPE_SETTER(data, "Not a valid VisualData object"); - - static_cast(data)->setModelData( - data->engine->toVariant(value, QVariant::Invalid)); - } - - v8::Handle get() - { - v8::Local data = engineData(engine)->constructorListItem->NewInstance(); - data->SetExternalResource(this); - ++scriptRef; - return data; - } - - void setValue(const QString &role, const QVariant &value) - { - if (role == QLatin1String("modelData")) - cachedData = value; - } - - bool resolveIndex(const QQuickVisualAdaptorModel &model, int idx) - { - if (index == -1) { - index = idx; - cachedData = model.list.at(idx); - emit modelIndexChanged(); - emit modelDataChanged(); - return true; - } else { - return false; - } - } - - -Q_SIGNALS: - void modelDataChanged(); - -private: - QVariant cachedData; -}; - - -class VDMListDelegateDataType : public QQuickVisualAdaptorModel::Accessors -{ -public: - inline VDMListDelegateDataType() {} - - int count(const QQuickVisualAdaptorModel &model) const - { - return model.list.count(); - } - - QVariant value(const QQuickVisualAdaptorModel &model, int index, const QString &role) const - { - return role == QLatin1String("modelData") - ? model.list.at(index) - : QVariant(); - } - - QQuickVisualDataModelItem *createItem( - QQuickVisualAdaptorModel &model, - QQuickVisualDataModelItemMetaType *metaType, - QQmlEngine *, - int index) const - { - return new QQuickVDMListAccessorData( - metaType, - index, - index >= 0 && index < model.list.count() ? model.list.at(index) : QVariant()); - } -}; - -//----------------------------------------------------------------- -// QObject -//----------------------------------------------------------------- - -class VDMObjectDelegateDataType; -class QQuickVDMObjectData : public QQuickVisualDataModelItem, public QQuickVisualAdaptorModelProxyInterface -{ - Q_OBJECT - Q_PROPERTY(QObject *modelData READ modelData CONSTANT) - Q_INTERFACES(QQuickVisualAdaptorModelProxyInterface) -public: - QQuickVDMObjectData( - QQuickVisualDataModelItemMetaType *metaType, - VDMObjectDelegateDataType *dataType, - int index, - QObject *object); - - QObject *modelData() const { return object; } - QObject *proxiedObject() { return object; } - - QQmlGuard object; -}; - -class VDMObjectDelegateDataType : public QQmlRefCount, public QQuickVisualAdaptorModel::Accessors -{ -public: - QMetaObject *metaObject; - int propertyOffset; - int signalOffset; - bool shared; - QMetaObjectBuilder builder; - - VDMObjectDelegateDataType() - : metaObject(0) - , propertyOffset(0) - , signalOffset(0) - , shared(true) - { - } - - VDMObjectDelegateDataType(const VDMObjectDelegateDataType &type) - : QQmlRefCount() - , QQuickVisualAdaptorModel::Accessors() - , metaObject(0) - , propertyOffset(type.propertyOffset) - , signalOffset(type.signalOffset) - , shared(false) - , builder(type.metaObject, QMetaObjectBuilder::Properties - | QMetaObjectBuilder::Signals - | QMetaObjectBuilder::SuperClass - | QMetaObjectBuilder::ClassName) - { - builder.setFlags(QMetaObjectBuilder::DynamicMetaObject); - } - - ~VDMObjectDelegateDataType() - { - free(metaObject); - } - - int count(const QQuickVisualAdaptorModel &model) const - { - return model.list.count(); - } - - QVariant value(const QQuickVisualAdaptorModel &model, int index, const QString &role) const - { - if (QObject *object = model.list.at(index).value()) - return object->property(role.toUtf8()); - return QVariant(); - } - - QQuickVisualDataModelItem *createItem( - QQuickVisualAdaptorModel &model, - QQuickVisualDataModelItemMetaType *metaType, - QQmlEngine *, - int index) const - { - VDMObjectDelegateDataType *dataType = const_cast(this); - if (!metaObject) - dataType->initializeMetaType(model); - return index >= 0 && index < model.list.count() - ? new QQuickVDMObjectData(metaType, dataType, index, qvariant_cast(model.list.at(index))) - : 0; - } - - void initializeMetaType(QQuickVisualAdaptorModel &) - { - setModelDataType(&builder, this); - - metaObject = builder.toMetaObject(); - } - - void cleanup(QQuickVisualAdaptorModel &, QQuickVisualDataModel *) const - { - const_cast(this)->release(); - } -}; - -class QQuickVDMObjectDataMetaObject : public QAbstractDynamicMetaObject -{ -public: - QQuickVDMObjectDataMetaObject(QQuickVDMObjectData *data, VDMObjectDelegateDataType *type) - : m_data(data) - , m_type(type) - { - QObjectPrivate *op = QObjectPrivate::get(m_data); - *static_cast(this) = *type->metaObject; - op->metaObject = this; - m_type->addref(); - } - - ~QQuickVDMObjectDataMetaObject() - { - m_type->release(); - } - - int metaCall(QMetaObject::Call call, int id, void **arguments) - { - static const int objectPropertyOffset = QObject::staticMetaObject.propertyCount(); - if (id >= m_type->propertyOffset - && (call == QMetaObject::ReadProperty - || call == QMetaObject::WriteProperty - || call == QMetaObject::ResetProperty)) { - if (m_data->object) - QMetaObject::metacall(m_data->object, call, id - m_type->propertyOffset + objectPropertyOffset, arguments); - return -1; - } else if (id >= m_type->signalOffset && call == QMetaObject::InvokeMetaMethod) { - QMetaObject::activate(m_data, this, id - m_type->signalOffset, 0); - return -1; - } else { - return m_data->qt_metacall(call, id, arguments); - } - } - - int createProperty(const char *name, const char *) - { - if (!m_data->object) - return -1; - const QMetaObject *metaObject = m_data->object->metaObject(); - static const int objectPropertyOffset = QObject::staticMetaObject.propertyCount(); - - const int previousPropertyCount = propertyCount() - propertyOffset(); - int propertyIndex = metaObject->indexOfProperty(name); - if (propertyIndex == -1) - return -1; - if (previousPropertyCount + objectPropertyOffset == metaObject->propertyCount()) - return propertyIndex + m_type->propertyOffset - objectPropertyOffset; - - if (m_type->shared) { - VDMObjectDelegateDataType *type = m_type; - m_type = new VDMObjectDelegateDataType(*m_type); - type->release(); - } - - const int previousMethodCount = methodCount(); - int notifierId = previousMethodCount - methodOffset(); - for (int propertyId = previousPropertyCount; propertyId < metaObject->propertyCount() - objectPropertyOffset; ++propertyId) { - QMetaProperty property = metaObject->property(propertyId + objectPropertyOffset); - QMetaPropertyBuilder propertyBuilder; - if (property.hasNotifySignal()) { - m_type->builder.addSignal("__" + QByteArray::number(propertyId) + "()"); - propertyBuilder = m_type->builder.addProperty(property.name(), property.typeName(), notifierId); - ++notifierId; - } else { - propertyBuilder = m_type->builder.addProperty(property.name(), property.typeName()); - } - propertyBuilder.setWritable(property.isWritable()); - propertyBuilder.setResettable(property.isResettable()); - propertyBuilder.setConstant(property.isConstant()); - } - - if (m_type->metaObject) - free(m_type->metaObject); - m_type->metaObject = m_type->builder.toMetaObject(); - *static_cast(this) = *m_type->metaObject; - - notifierId = previousMethodCount; - for (int i = previousPropertyCount; i < metaObject->propertyCount() - objectPropertyOffset; ++i) { - QMetaProperty property = metaObject->property(i + objectPropertyOffset); - if (property.hasNotifySignal()) { - QQmlPropertyPrivate::connect( - m_data->object, property.notifySignalIndex(), m_data, notifierId); - ++notifierId; - } - } - return propertyIndex + m_type->propertyOffset - objectPropertyOffset; - } - - QQuickVDMObjectData *m_data; - VDMObjectDelegateDataType *m_type; -}; - -QQuickVDMObjectData::QQuickVDMObjectData( - QQuickVisualDataModelItemMetaType *metaType, - VDMObjectDelegateDataType *dataType, - int index, - QObject *object) - : QQuickVisualDataModelItem(metaType, index) - , object(object) -{ - new QQuickVDMObjectDataMetaObject(this, dataType); -} - -//----------------------------------------------------------------- -// QQuickVisualAdaptorModel -//----------------------------------------------------------------- - -static const QQuickVisualAdaptorModel::Accessors qt_vdm_null_accessors; -static const VDMListDelegateDataType qt_vdm_list_accessors; - -QQuickVisualAdaptorModel::Accessors::~Accessors() -{ -} - -QQuickVisualAdaptorModel::QQuickVisualAdaptorModel() - : accessors(&qt_vdm_null_accessors) -{ -} - -QQuickVisualAdaptorModel::~QQuickVisualAdaptorModel() -{ - accessors->cleanup(*this); -} - -void QQuickVisualAdaptorModel::setModel(const QVariant &variant, QQuickVisualDataModel *vdm, QQmlEngine *engine) -{ - accessors->cleanup(*this, vdm); - - list.setList(variant, engine); - - if (QObject *object = qvariant_cast(variant)) { - setObject(object); - if (QAbstractItemModel *model = qobject_cast(object)) { - accessors = new VDMAbstractItemModelDataType(this); - - qmlobject_connect(model, QAbstractItemModel, SIGNAL(rowsInserted(QModelIndex,int,int)), - vdm, QQuickVisualDataModel, SLOT(_q_rowsInserted(QModelIndex,int,int))); - qmlobject_connect(model, QAbstractItemModel, SIGNAL(rowsRemoved(QModelIndex,int,int)), - vdm, QQuickVisualDataModel, SLOT(_q_rowsRemoved(QModelIndex,int,int))); - qmlobject_connect(model, QAbstractItemModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), - vdm, QQuickVisualDataModel, SLOT(_q_rowsAboutToBeRemoved(QModelIndex,int,int))); - qmlobject_connect(model, QAbstractItemModel, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector)), - vdm, QQuickVisualDataModel, SLOT(_q_dataChanged(QModelIndex,QModelIndex,QVector))); - qmlobject_connect(model, QAbstractItemModel, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), - vdm, QQuickVisualDataModel, SLOT(_q_rowsMoved(QModelIndex,int,int,QModelIndex,int))); - qmlobject_connect(model, QAbstractItemModel, SIGNAL(modelReset()), - vdm, QQuickVisualDataModel, SLOT(_q_modelReset())); - qmlobject_connect(model, QAbstractItemModel, SIGNAL(layoutChanged()), - vdm, QQuickVisualDataModel, SLOT(_q_layoutChanged())); - } else { - accessors = new VDMObjectDelegateDataType; - } - } else if (list.type() == QQuickListAccessor::ListProperty) { - setObject(static_cast(variant.constData())->object()); - accessors = new VDMObjectDelegateDataType; - } else if (list.type() != QQuickListAccessor::Invalid) { - Q_ASSERT(list.type() != QQuickListAccessor::Instance); // Should have cast to QObject. - setObject(0); - accessors = &qt_vdm_list_accessors; - } else { - setObject(0); - accessors = &qt_vdm_null_accessors; - } -} - -void QQuickVisualAdaptorModel::invalidateModel(QQuickVisualDataModel *vdm) -{ - accessors->cleanup(*this, vdm); - accessors = &qt_vdm_null_accessors; - // Don't clear the model object as we still need the guard to clear the list variant if the - // object is destroyed. -} - -bool QQuickVisualAdaptorModel::isValid() const -{ - return accessors != &qt_vdm_null_accessors; -} - -void QQuickVisualAdaptorModel::objectDestroyed(QObject *) -{ - setModel(QVariant(), 0, 0); -} - -QQuickVisualAdaptorModelEngineData::QQuickVisualAdaptorModelEngineData(QV8Engine *) -{ - strings = qPersistentNew(v8::Array::New(StringCount)); - strings->Set(Index, v8::String::New("index")); - strings->Set(ModelData, v8::String::New("modelData")); - strings->Set(HasModelChildren, v8::String::New("hasModelChildren")); - - v8::Local listItem = v8::FunctionTemplate::New(); - listItem->InstanceTemplate()->SetHasExternalResource(true); - listItem->InstanceTemplate()->SetAccessor(index(), get_index); - listItem->InstanceTemplate()->SetAccessor( - modelData(), - QQuickVDMListAccessorData::get_modelData, - QQuickVDMListAccessorData::set_modelData); - constructorListItem = qPersistentNew(listItem->GetFunction()); -} - -QQuickVisualAdaptorModelEngineData::~QQuickVisualAdaptorModelEngineData() -{ - qPersistentDispose(constructorListItem); - qPersistentDispose(strings); -} - -QT_END_NAMESPACE - -#include diff --git a/src/quick/items/qquickvisualadaptormodel_p.h b/src/quick/items/qquickvisualadaptormodel_p.h deleted file mode 100644 index 40890f8560..0000000000 --- a/src/quick/items/qquickvisualadaptormodel_p.h +++ /dev/null @@ -1,156 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** 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, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQUICKVISUALADAPTORMODEL_P_H -#define QQUICKVISUALADAPTORMODEL_P_H - -#include - -#include "private/qquicklistaccessor_p.h" - -#include - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -class QQmlEngine; - -class QQuickVisualDataModel; -class QQuickVisualDataModelItem; -class QQuickVisualDataModelItemMetaType; - -class QQuickVisualAdaptorModel : public QQmlGuard -{ -public: - class Accessors - { - public: - inline Accessors() {} - virtual ~Accessors(); - virtual int count(const QQuickVisualAdaptorModel &) const { return 0; } - virtual void cleanup(QQuickVisualAdaptorModel &, QQuickVisualDataModel * = 0) const {} - - virtual QVariant value(const QQuickVisualAdaptorModel &, int, const QString &) const { - return QVariant(); } - - virtual QQuickVisualDataModelItem *createItem( - QQuickVisualAdaptorModel &, - QQuickVisualDataModelItemMetaType *, - QQmlEngine *, - int) const { return 0; } - - virtual bool notify( - const QQuickVisualAdaptorModel &, - const QList &, - int, - int, - const QVector &) const { return false; } - virtual void replaceWatchedRoles( - QQuickVisualAdaptorModel &, - const QList &, - const QList &) const {} - virtual QVariant parentModelIndex(const QQuickVisualAdaptorModel &) const { - return QVariant(); } - virtual QVariant modelIndex(const QQuickVisualAdaptorModel &, int) const { - return QVariant(); } - virtual bool canFetchMore(const QQuickVisualAdaptorModel &) const { return false; } - virtual void fetchMore(QQuickVisualAdaptorModel &) const {} - }; - - const Accessors *accessors; - QPersistentModelIndex rootIndex; - QQuickListAccessor list; - - QQuickVisualAdaptorModel(); - ~QQuickVisualAdaptorModel(); - - inline QVariant model() const { return list.list(); } - void setModel(const QVariant &variant, QQuickVisualDataModel *vdm, QQmlEngine *engine); - void invalidateModel(QQuickVisualDataModel *vdm); - - bool isValid() const; - - inline QAbstractItemModel *aim() { return static_cast(object()); } - inline const QAbstractItemModel *aim() const { return static_cast(object()); } - - inline int count() const { return qMax(0, accessors->count(*this)); } - inline QVariant value(int index, const QString &role) const { - return accessors->value(*this, index, role); } - inline QQuickVisualDataModelItem *createItem(QQuickVisualDataModelItemMetaType *metaType, QQmlEngine *engine, int index) { - return accessors->createItem(*this, metaType, engine, index); } - inline bool hasProxyObject() const { - return list.type() == QQuickListAccessor::Instance || list.type() == QQuickListAccessor::ListProperty; } - - inline bool notify( - const QList &items, - int index, - int count, - const QVector &roles) const { - return accessors->notify(*this, items, index, count, roles); } - inline void replaceWatchedRoles( - const QList &oldRoles, const QList &newRoles) { - accessors->replaceWatchedRoles(*this, oldRoles, newRoles); } - - inline QVariant modelIndex(int index) const { return accessors->modelIndex(*this, index); } - inline QVariant parentModelIndex() const { return accessors->parentModelIndex(*this); } - inline bool canFetchMore() const { return accessors->canFetchMore(*this); } - inline void fetchMore() { return accessors->fetchMore(*this); } - -protected: - void objectDestroyed(QObject *); -}; - -class QQuickVisualAdaptorModelProxyInterface -{ -public: - virtual ~QQuickVisualAdaptorModelProxyInterface() {} - - virtual QObject *proxiedObject() = 0; -}; - -#define QQuickVisualAdaptorModelProxyInterface_iid "org.qt-project.Qt.QQuickVisualAdaptorModelProxyInterface" - -Q_DECLARE_INTERFACE(QQuickVisualAdaptorModelProxyInterface, QQuickVisualAdaptorModelProxyInterface_iid) - -QT_END_NAMESPACE - -#endif diff --git a/src/quick/items/qquickvisualdatamodel.cpp b/src/quick/items/qquickvisualdatamodel.cpp deleted file mode 100644 index d2e50e8b8a..0000000000 --- a/src/quick/items/qquickvisualdatamodel.cpp +++ /dev/null @@ -1,3173 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** 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, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qquickvisualdatamodel_p_p.h" -#include "qquickitem.h" - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE - -class QQuickVisualDataModelEngineData : public QV8Engine::Deletable -{ -public: - enum - { - Model, - Groups, - IsUnresolved, - ItemsIndex, - PersistedItemsIndex, - InItems, - InPersistedItems, - StringCount - }; - - QQuickVisualDataModelEngineData(QV8Engine *engine); - ~QQuickVisualDataModelEngineData(); - - v8::Local array( - QV8Engine *engine, const QVector &changes); - v8::Local array( - QV8Engine *engine, const QVector &changes); - v8::Local array( - QV8Engine *engine, const QVector &changes); - - - inline v8::Local model() { return strings->Get(Model)->ToString(); } - inline v8::Local groups() { return strings->Get(Groups)->ToString(); } - inline v8::Local isUnresolved() { return strings->Get(IsUnresolved)->ToString(); } - inline v8::Local itemsIndex() { return strings->Get(ItemsIndex)->ToString(); } - inline v8::Local persistedItemsIndex() { return strings->Get(PersistedItemsIndex)->ToString(); } - inline v8::Local inItems() { return strings->Get(InItems)->ToString(); } - inline v8::Local inPersistedItems() { return strings->Get(InPersistedItems)->ToString(); } - - v8::Persistent strings; - v8::Persistent constructorChange; - v8::Persistent constructorChangeArray; -}; - -V8_DEFINE_EXTENSION(QQuickVisualDataModelEngineData, engineData) - - -void QQuickVisualDataModelPartsMetaObject::propertyCreated(int, QMetaPropertyBuilder &prop) -{ - prop.setWritable(false); -} - -QVariant QQuickVisualDataModelPartsMetaObject::initialValue(int id) -{ - QQuickVisualDataModelParts *parts = static_cast(object()); - QQuickVisualPartsModel *m = new QQuickVisualPartsModel( - parts->model, QString::fromUtf8(name(id)), parts); - parts->models.append(m); - return QVariant::fromValue(static_cast(m)); -} - -QQuickVisualDataModelParts::QQuickVisualDataModelParts(QQuickVisualDataModel *parent) -: QObject(parent), model(parent) -{ - new QQuickVisualDataModelPartsMetaObject(this); -} - -//--------------------------------------------------------------------------- - -/*! - \qmltype VisualDataModel - \instantiates QQuickVisualDataModel - \inqmlmodule QtQuick 2 - \ingroup qtquick-models - \brief Encapsulates a model and delegate - - The VisualDataModel type encapsulates a model and the delegate that will - be instantiated for items in the model. - - It is usually not necessary to create a VisualDataModel. - However, it can be useful for manipulating and accessing the \l modelIndex - when a QAbstractItemModel subclass is used as the - model. Also, VisualDataModel is used together with \l Package to - provide delegates to multiple views, and with VisualDataGroup to sort and filter - delegate items. - - The example below illustrates using a VisualDataModel with a ListView. - - \snippet qml/visualdatamodel.qml 0 -*/ - -QQuickVisualDataModelPrivate::QQuickVisualDataModelPrivate(QQmlContext *ctxt) - : m_delegate(0) - , m_cacheMetaType(0) - , m_context(ctxt) - , m_parts(0) - , m_filterGroup(QStringLiteral("items")) - , m_count(0) - , m_groupCount(Compositor::MinimumGroupCount) - , m_compositorGroup(Compositor::Cache) - , m_complete(false) - , m_delegateValidated(false) - , m_reset(false) - , m_transaction(false) - , m_incubatorCleanupScheduled(false) - , m_cacheItems(0) - , m_items(0) - , m_persistedItems(0) -{ -} - -QQuickVisualDataModelPrivate::~QQuickVisualDataModelPrivate() -{ - qDeleteAll(m_finishedIncubating); - - if (m_cacheMetaType) - m_cacheMetaType->release(); -} - -void QQuickVisualDataModelPrivate::init() -{ - Q_Q(QQuickVisualDataModel); - m_compositor.setRemoveGroups(Compositor::GroupMask & ~Compositor::PersistedFlag); - - m_items = new QQuickVisualDataGroup(QStringLiteral("items"), q, Compositor::Default, q); - m_items->setDefaultInclude(true); - m_persistedItems = new QQuickVisualDataGroup(QStringLiteral("persistedItems"), q, Compositor::Persisted, q); - QQuickVisualDataGroupPrivate::get(m_items)->emitters.insert(this); -} - -QQuickVisualDataModel::QQuickVisualDataModel() -: QQuickVisualModel(*(new QQuickVisualDataModelPrivate(0))) -{ - Q_D(QQuickVisualDataModel); - d->init(); -} - -QQuickVisualDataModel::QQuickVisualDataModel(QQmlContext *ctxt, QObject *parent) -: QQuickVisualModel(*(new QQuickVisualDataModelPrivate(ctxt)), parent) -{ - Q_D(QQuickVisualDataModel); - d->init(); -} - -QQuickVisualDataModel::~QQuickVisualDataModel() -{ - Q_D(QQuickVisualDataModel); - - foreach (QQuickVisualDataModelItem *cacheItem, d->m_cache) { - if (cacheItem->object) { - delete cacheItem->object; - - cacheItem->object = 0; - cacheItem->contextData->destroy(); - cacheItem->contextData = 0; - cacheItem->scriptRef -= 1; - } - cacheItem->groups &= ~Compositor::UnresolvedFlag; - cacheItem->objectRef = 0; - if (!cacheItem->isReferenced()) - delete cacheItem; - } -} - - -void QQuickVisualDataModel::classBegin() -{ - Q_D(QQuickVisualDataModel); - if (!d->m_context) - d->m_context = qmlContext(this); -} - -void QQuickVisualDataModel::componentComplete() -{ - Q_D(QQuickVisualDataModel); - d->m_complete = true; - - int defaultGroups = 0; - QStringList groupNames; - groupNames.append(QStringLiteral("items")); - groupNames.append(QStringLiteral("persistedItems")); - if (QQuickVisualDataGroupPrivate::get(d->m_items)->defaultInclude) - defaultGroups |= Compositor::DefaultFlag; - if (QQuickVisualDataGroupPrivate::get(d->m_persistedItems)->defaultInclude) - defaultGroups |= Compositor::PersistedFlag; - for (int i = Compositor::MinimumGroupCount; i < d->m_groupCount; ++i) { - QString name = d->m_groups[i]->name(); - if (name.isEmpty()) { - d->m_groups[i] = d->m_groups[d->m_groupCount - 1]; - --d->m_groupCount; - --i; - } else if (name.at(0).isUpper()) { - qmlInfo(d->m_groups[i]) << QQuickVisualDataGroup::tr("Group names must start with a lower case letter"); - d->m_groups[i] = d->m_groups[d->m_groupCount - 1]; - --d->m_groupCount; - --i; - } else { - groupNames.append(name); - - QQuickVisualDataGroupPrivate *group = QQuickVisualDataGroupPrivate::get(d->m_groups[i]); - group->setModel(this, Compositor::Group(i)); - if (group->defaultInclude) - defaultGroups |= (1 << i); - } - } - - d->m_cacheMetaType = new QQuickVisualDataModelItemMetaType( - QQmlEnginePrivate::getV8Engine(d->m_context->engine()), this, groupNames); - - d->m_compositor.setGroupCount(d->m_groupCount); - d->m_compositor.setDefaultGroups(defaultGroups); - d->updateFilterGroup(); - - while (!d->m_pendingParts.isEmpty()) - static_cast(d->m_pendingParts.first())->updateFilterGroup(); - - QVector inserts; - d->m_count = d->m_adaptorModel.count(); - d->m_compositor.append( - &d->m_adaptorModel, - 0, - d->m_count, - defaultGroups | Compositor::AppendFlag | Compositor::PrependFlag, - &inserts); - d->itemsInserted(inserts); - d->emitChanges(); - - if (d->m_adaptorModel.canFetchMore()) - QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest)); -} - -/*! - \qmlproperty model QtQuick2::VisualDataModel::model - This property holds the model providing data for the VisualDataModel. - - The model provides a set of data that is used to create the items - for a view. For large or dynamic datasets the model is usually - provided by a C++ model object. The C++ model object must be a \l - {QAbstractItemModel} subclass or a simple list. - - Models can also be created directly in QML, using a \l{ListModel} or - \l{XmlListModel}. - - \sa {qml-data-models}{Data Models} -*/ -QVariant QQuickVisualDataModel::model() const -{ - Q_D(const QQuickVisualDataModel); - return d->m_adaptorModel.model(); -} - -void QQuickVisualDataModel::setModel(const QVariant &model) -{ - Q_D(QQuickVisualDataModel); - - if (d->m_complete) - _q_itemsRemoved(0, d->m_count); - - d->m_adaptorModel.setModel(model, this, d->m_context->engine()); - d->m_adaptorModel.replaceWatchedRoles(QList(), d->m_watchedRoles); - for (int i = 0; d->m_parts && i < d->m_parts->models.count(); ++i) { - d->m_adaptorModel.replaceWatchedRoles( - QList(), d->m_parts->models.at(i)->watchedRoles()); - } - - if (d->m_complete) { - _q_itemsInserted(0, d->m_adaptorModel.count()); - if (d->m_adaptorModel.canFetchMore()) - QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest)); - } -} - -/*! - \qmlproperty Component QtQuick2::VisualDataModel::delegate - - The delegate provides a template defining each item instantiated by a view. - The index is exposed as an accessible \c index property. Properties of the - model are also available depending upon the type of \l {qml-data-models}{Data Model}. -*/ -QQmlComponent *QQuickVisualDataModel::delegate() const -{ - Q_D(const QQuickVisualDataModel); - return d->m_delegate; -} - -void QQuickVisualDataModel::setDelegate(QQmlComponent *delegate) -{ - Q_D(QQuickVisualDataModel); - if (d->m_transaction) { - qmlInfo(this) << tr("The delegate of a VisualDataModel cannot be changed within onUpdated."); - return; - } - bool wasValid = d->m_delegate != 0; - d->m_delegate = delegate; - d->m_delegateValidated = false; - if (wasValid && d->m_complete) { - for (int i = 1; i < d->m_groupCount; ++i) { - QQuickVisualDataGroupPrivate::get(d->m_groups[i])->changeSet.remove( - 0, d->m_compositor.count(Compositor::Group(i))); - } - } - if (d->m_complete && d->m_delegate) { - for (int i = 1; i < d->m_groupCount; ++i) { - QQuickVisualDataGroupPrivate::get(d->m_groups[i])->changeSet.insert( - 0, d->m_compositor.count(Compositor::Group(i))); - } - } - d->emitChanges(); -} - -/*! - \qmlproperty QModelIndex QtQuick2::VisualDataModel::rootIndex - - QAbstractItemModel provides a hierarchical tree of data, whereas - QML only operates on list data. \c rootIndex allows the children of - any node in a QAbstractItemModel to be provided by this model. - - This property only affects models of type QAbstractItemModel that - are hierarchical (e.g, a tree model). - - For example, here is a simple interactive file system browser. - When a directory name is clicked, the view's \c rootIndex is set to the - QModelIndex node of the clicked directory, thus updating the view to show - the new directory's contents. - - \c main.cpp: - \snippet qml/visualdatamodel_rootindex/main.cpp 0 - - \c view.qml: - \snippet qml/visualdatamodel_rootindex/view.qml 0 - - If the \l model is a QAbstractItemModel subclass, the delegate can also - reference a \c hasModelChildren property (optionally qualified by a - \e model. prefix) that indicates whether the delegate's model item has - any child nodes. - - - \sa modelIndex(), parentModelIndex() -*/ -QVariant QQuickVisualDataModel::rootIndex() const -{ - Q_D(const QQuickVisualDataModel); - return QVariant::fromValue(QModelIndex(d->m_adaptorModel.rootIndex)); -} - -void QQuickVisualDataModel::setRootIndex(const QVariant &root) -{ - Q_D(QQuickVisualDataModel); - - QModelIndex modelIndex = qvariant_cast(root); - const bool changed = d->m_adaptorModel.rootIndex != modelIndex; - if (changed || !d->m_adaptorModel.isValid()) { - const int oldCount = d->m_count; - d->m_adaptorModel.rootIndex = modelIndex; - if (!d->m_adaptorModel.isValid() && d->m_adaptorModel.aim()) // The previous root index was invalidated, so we need to reconnect the model. - d->m_adaptorModel.setModel(d->m_adaptorModel.list.list(), this, d->m_context->engine()); - if (d->m_adaptorModel.canFetchMore()) - d->m_adaptorModel.fetchMore(); - if (d->m_complete) { - const int newCount = d->m_adaptorModel.count(); - if (oldCount) - _q_itemsRemoved(0, oldCount); - if (newCount) - _q_itemsInserted(0, newCount); - } - if (changed) - emit rootIndexChanged(); - } -} - -/*! - \qmlmethod QModelIndex QtQuick2::VisualDataModel::modelIndex(int index) - - QAbstractItemModel provides a hierarchical tree of data, whereas - QML only operates on list data. This function assists in using - tree models in QML. - - Returns a QModelIndex for the specified index. - This value can be assigned to rootIndex. - - \sa rootIndex -*/ -QVariant QQuickVisualDataModel::modelIndex(int idx) const -{ - Q_D(const QQuickVisualDataModel); - return d->m_adaptorModel.modelIndex(idx); -} - -/*! - \qmlmethod QModelIndex QtQuick2::VisualDataModel::parentModelIndex() - - QAbstractItemModel provides a hierarchical tree of data, whereas - QML only operates on list data. This function assists in using - tree models in QML. - - Returns a QModelIndex for the parent of the current rootIndex. - This value can be assigned to rootIndex. - - \sa rootIndex -*/ -QVariant QQuickVisualDataModel::parentModelIndex() const -{ - Q_D(const QQuickVisualDataModel); - return d->m_adaptorModel.parentModelIndex(); -} - -/*! - \qmlproperty int QtQuick2::VisualDataModel::count -*/ - -int QQuickVisualDataModel::count() const -{ - Q_D(const QQuickVisualDataModel); - if (!d->m_delegate) - return 0; - return d->m_compositor.count(d->m_compositorGroup); -} - -QQuickVisualDataModel::ReleaseFlags QQuickVisualDataModelPrivate::release(QObject *object) -{ - QQuickVisualDataModel::ReleaseFlags stat = 0; - if (!object) - return stat; - - if (QQuickVisualDataModelItem *cacheItem = QQuickVisualDataModelItem::dataForObject(object)) { - if (cacheItem->releaseObject()) { - cacheItem->destroyObject(); - if (QQuickItem *item = qmlobject_cast(object)) - emitDestroyingItem(item); - if (cacheItem->incubationTask) { - releaseIncubator(cacheItem->incubationTask); - cacheItem->incubationTask = 0; - } - cacheItem->Dispose(); - stat |= QQuickVisualModel::Destroyed; - } else { - stat |= QQuickVisualDataModel::Referenced; - } - } - return stat; -} - -/* - Returns ReleaseStatus flags. -*/ - -QQuickVisualDataModel::ReleaseFlags QQuickVisualDataModel::release(QQuickItem *item) -{ - Q_D(QQuickVisualDataModel); - QQuickVisualModel::ReleaseFlags stat = d->release(item); - if (stat & Destroyed) - item->setParentItem(0); - return stat; -} - -// Cancel a requested async item -void QQuickVisualDataModel::cancel(int index) -{ - Q_D(QQuickVisualDataModel); - if (!d->m_delegate || index < 0 || index >= d->m_compositor.count(d->m_compositorGroup)) { - qWarning() << "VisualDataModel::cancel: index out range" << index << d->m_compositor.count(d->m_compositorGroup); - return; - } - - Compositor::iterator it = d->m_compositor.find(d->m_compositorGroup, index); - QQuickVisualDataModelItem *cacheItem = it->inCache() ? d->m_cache.at(it.cacheIndex) : 0; - if (cacheItem) { - if (cacheItem->incubationTask && !cacheItem->isObjectReferenced()) { - d->releaseIncubator(cacheItem->incubationTask); - cacheItem->incubationTask = 0; - - if (cacheItem->object) { - QObject *object = cacheItem->object; - cacheItem->destroyObject(); - if (QQuickItem *item = qmlobject_cast(object)) - d->emitDestroyingItem(item); - else if (QQuickPackage *package = qmlobject_cast(object)) - d->emitDestroyingPackage(package); - } - - cacheItem->scriptRef -= 1; - } - if (!cacheItem->isReferenced()) { - d->m_compositor.clearFlags(Compositor::Cache, it.cacheIndex, 1, Compositor::CacheFlag); - d->m_cache.removeAt(it.cacheIndex); - delete cacheItem; - Q_ASSERT(d->m_cache.count() == d->m_compositor.count(Compositor::Cache)); - } - } -} - -void QQuickVisualDataModelPrivate::group_append( - QQmlListProperty *property, QQuickVisualDataGroup *group) -{ - QQuickVisualDataModelPrivate *d = static_cast(property->data); - if (d->m_complete) - return; - if (d->m_groupCount == Compositor::MaximumGroupCount) { - qmlInfo(d->q_func()) << QQuickVisualDataModel::tr("The maximum number of supported VisualDataGroups is 8"); - return; - } - d->m_groups[d->m_groupCount] = group; - d->m_groupCount += 1; -} - -int QQuickVisualDataModelPrivate::group_count( - QQmlListProperty *property) -{ - QQuickVisualDataModelPrivate *d = static_cast(property->data); - return d->m_groupCount - 1; -} - -QQuickVisualDataGroup *QQuickVisualDataModelPrivate::group_at( - QQmlListProperty *property, int index) -{ - QQuickVisualDataModelPrivate *d = static_cast(property->data); - return index >= 0 && index < d->m_groupCount - 1 - ? d->m_groups[index + 1] - : 0; -} - -/*! - \qmlproperty list QtQuick2::VisualDataModel::groups - - This property holds a visual data model's group definitions. - - Groups define a sub-set of the items in a visual data model and can be used to filter - a model. - - For every group defined in a VisualDataModel two attached properties are added to each - delegate item. The first of the form VisualDataModel.in\e{GroupName} holds whether the - item belongs to the group and the second VisualDataModel.\e{groupName}Index holds the - index of the item in that group. - - The following example illustrates using groups to select items in a model. - - \snippet qml/visualdatagroup.qml 0 -*/ - -QQmlListProperty QQuickVisualDataModel::groups() -{ - Q_D(QQuickVisualDataModel); - return QQmlListProperty( - this, - d, - QQuickVisualDataModelPrivate::group_append, - QQuickVisualDataModelPrivate::group_count, - QQuickVisualDataModelPrivate::group_at, - 0); -} - -/*! - \qmlproperty VisualDataGroup QtQuick2::VisualDataModel::items - - This property holds visual data model's default group to which all new items are added. -*/ - -QQuickVisualDataGroup *QQuickVisualDataModel::items() -{ - Q_D(QQuickVisualDataModel); - return d->m_items; -} - -/*! - \qmlproperty VisualDataGroup QtQuick2::VisualDataModel::persistedItems - - This property holds visual data model's persisted items group. - - Items in this group are not destroyed when released by a view, instead they are persisted - until removed from the group. - - An item can be removed from the persistedItems group by setting the - VisualDataModel.inPersistedItems property to false. If the item is not referenced by a view - at that time it will be destroyed. Adding an item to this group will not create a new - instance. - - Items returned by the \l QtQuick2::VisualDataGroup::create() function are automatically added - to this group. -*/ - -QQuickVisualDataGroup *QQuickVisualDataModel::persistedItems() -{ - Q_D(QQuickVisualDataModel); - return d->m_persistedItems; -} - -/*! - \qmlproperty string QtQuick2::VisualDataModel::filterOnGroup - - This property holds the name of the group used to filter the visual data model. - - Only items which belong to this group are visible to a view. - - By default this is the \l items group. -*/ - -QString QQuickVisualDataModel::filterGroup() const -{ - Q_D(const QQuickVisualDataModel); - return d->m_filterGroup; -} - -void QQuickVisualDataModel::setFilterGroup(const QString &group) -{ - Q_D(QQuickVisualDataModel); - - if (d->m_transaction) { - qmlInfo(this) << tr("The group of a VisualDataModel cannot be changed within onChanged"); - return; - } - - if (d->m_filterGroup != group) { - d->m_filterGroup = group; - d->updateFilterGroup(); - emit filterGroupChanged(); - } -} - -void QQuickVisualDataModel::resetFilterGroup() -{ - setFilterGroup(QStringLiteral("items")); -} - -void QQuickVisualDataModelPrivate::updateFilterGroup() -{ - Q_Q(QQuickVisualDataModel); - if (!m_cacheMetaType) - return; - - QQuickListCompositor::Group previousGroup = m_compositorGroup; - m_compositorGroup = Compositor::Default; - for (int i = 1; i < m_groupCount; ++i) { - if (m_filterGroup == m_cacheMetaType->groupNames.at(i - 1)) { - m_compositorGroup = Compositor::Group(i); - break; - } - } - - QQuickVisualDataGroupPrivate::get(m_groups[m_compositorGroup])->emitters.insert(this); - if (m_compositorGroup != previousGroup) { - QVector removes; - QVector inserts; - m_compositor.transition(previousGroup, m_compositorGroup, &removes, &inserts); - - QQuickChangeSet changeSet; - changeSet.move(removes, inserts); - emit q->modelUpdated(changeSet, false); - - if (changeSet.difference() != 0) - emit q->countChanged(); - - if (m_parts) { - foreach (QQuickVisualPartsModel *model, m_parts->models) - model->updateFilterGroup(m_compositorGroup, changeSet); - } - } -} - -/*! - \qmlproperty object QtQuick2::VisualDataModel::parts - - The \a parts property selects a VisualDataModel which creates - delegates from the part named. This is used in conjunction with - the \l Package type. - - For example, the code below selects a model which creates - delegates named \e list from a \l Package: - - \code - VisualDataModel { - id: visualModel - delegate: Package { - Item { Package.name: "list" } - } - model: myModel - } - - ListView { - width: 200; height:200 - model: visualModel.parts.list - } - \endcode - - \sa Package -*/ - -QObject *QQuickVisualDataModel::parts() -{ - Q_D(QQuickVisualDataModel); - if (!d->m_parts) - d->m_parts = new QQuickVisualDataModelParts(this); - return d->m_parts; -} - -void QQuickVisualDataModelPrivate::emitCreatedPackage(QVDMIncubationTask *incubationTask, QQuickPackage *package) -{ - for (int i = 1; i < m_groupCount; ++i) - QQuickVisualDataGroupPrivate::get(m_groups[i])->createdPackage(incubationTask->index[i], package); -} - -void QQuickVisualDataModelPrivate::emitInitPackage(QVDMIncubationTask *incubationTask, QQuickPackage *package) -{ - for (int i = 1; i < m_groupCount; ++i) - QQuickVisualDataGroupPrivate::get(m_groups[i])->initPackage(incubationTask->index[i], package); -} - -void QQuickVisualDataModelPrivate::emitDestroyingPackage(QQuickPackage *package) -{ - for (int i = 1; i < m_groupCount; ++i) - QQuickVisualDataGroupPrivate::get(m_groups[i])->destroyingPackage(package); -} - -void QVDMIncubationTask::statusChanged(Status status) -{ - vdm->incubatorStatusChanged(this, status); -} - -void QQuickVisualDataModelPrivate::releaseIncubator(QVDMIncubationTask *incubationTask) -{ - Q_Q(QQuickVisualDataModel); - if (!incubationTask->isError()) - incubationTask->clear(); - m_finishedIncubating.append(incubationTask); - if (!m_incubatorCleanupScheduled) { - m_incubatorCleanupScheduled = true; - QCoreApplication::postEvent(q, new QEvent(QEvent::User)); - } -} - -void QQuickVisualDataModelPrivate::removeCacheItem(QQuickVisualDataModelItem *cacheItem) -{ - int cidx = m_cache.indexOf(cacheItem); - if (cidx >= 0) { - m_compositor.clearFlags(Compositor::Cache, cidx, 1, Compositor::CacheFlag); - m_cache.removeAt(cidx); - } - Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); -} - -void QQuickVisualDataModelPrivate::incubatorStatusChanged(QVDMIncubationTask *incubationTask, QQmlIncubator::Status status) -{ - Q_Q(QQuickVisualDataModel); - if (status != QQmlIncubator::Ready && status != QQmlIncubator::Error) - return; - - QQuickVisualDataModelItem *cacheItem = incubationTask->incubating; - cacheItem->incubationTask = 0; - incubationTask->incubating = 0; - releaseIncubator(incubationTask); - - if (status == QQmlIncubator::Ready) { - if (QQuickItem *item = qmlobject_cast(cacheItem->object)) - emitCreatedItem(incubationTask, item); - else if (QQuickPackage *package = qmlobject_cast(cacheItem->object)) - emitCreatedPackage(incubationTask, package); - } else if (status == QQmlIncubator::Error) { - qmlInfo(q, m_delegate->errors()) << "Error creating delegate"; - } - - if (!cacheItem->isObjectReferenced()) { - if (QQuickItem *item = qmlobject_cast(cacheItem->object)) - emitDestroyingItem(item); - else if (QQuickPackage *package = qmlobject_cast(cacheItem->object)) - emitDestroyingPackage(package); - delete cacheItem->object; - cacheItem->object = 0; - cacheItem->scriptRef -= 1; - cacheItem->contextData->destroy(); - cacheItem->contextData = 0; - if (!cacheItem->isReferenced()) { - removeCacheItem(cacheItem); - delete cacheItem; - } - } -} - -void QVDMIncubationTask::setInitialState(QObject *o) -{ - vdm->setInitialState(this, o); -} - -void QQuickVisualDataModelPrivate::setInitialState(QVDMIncubationTask *incubationTask, QObject *o) -{ - QQuickVisualDataModelItem *cacheItem = incubationTask->incubating; - cacheItem->object = o; - - if (QQuickItem *item = qmlobject_cast(cacheItem->object)) - emitInitItem(incubationTask, item); - else if (QQuickPackage *package = qmlobject_cast(cacheItem->object)) - emitInitPackage(incubationTask, package); -} - -QObject *QQuickVisualDataModelPrivate::object(Compositor::Group group, int index, bool asynchronous) -{ - Q_Q(QQuickVisualDataModel); - if (!m_delegate || index < 0 || index >= m_compositor.count(group)) { - qWarning() << "VisualDataModel::item: index out range" << index << m_compositor.count(group); - return 0; - } else if (!m_context->isValid()) { - return 0; - } - - Compositor::iterator it = m_compositor.find(group, index); - - QQuickVisualDataModelItem *cacheItem = it->inCache() ? m_cache.at(it.cacheIndex) : 0; - - if (!cacheItem) { - cacheItem = m_adaptorModel.createItem(m_cacheMetaType, m_context->engine(), it.modelIndex()); - if (!cacheItem) - return 0; - - cacheItem->groups = it->flags; - - m_cache.insert(it.cacheIndex, cacheItem); - m_compositor.setFlags(it, 1, Compositor::CacheFlag); - Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); - } - - // Bump the reference counts temporarily so neither the content data or the delegate object - // are deleted if incubatorStatusChanged() is called synchronously. - cacheItem->scriptRef += 1; - cacheItem->referenceObject(); - - if (cacheItem->incubationTask) { - if (!asynchronous && cacheItem->incubationTask->incubationMode() == QQmlIncubator::Asynchronous) { - // previously requested async - now needed immediately - cacheItem->incubationTask->forceCompletion(); - } - } else if (!cacheItem->object) { - QQmlContext *creationContext = m_delegate->creationContext(); - - cacheItem->scriptRef += 1; - - cacheItem->incubationTask = new QVDMIncubationTask(this, asynchronous ? QQmlIncubator::Asynchronous : QQmlIncubator::AsynchronousIfNested); - cacheItem->incubationTask->incubating = cacheItem; - cacheItem->incubationTask->clear(); - - for (int i = 1; i < m_groupCount; ++i) - cacheItem->incubationTask->index[i] = it.index[i]; - - QQmlContextData *ctxt = new QQmlContextData; - ctxt->setParent(QQmlContextData::get(creationContext ? creationContext : m_context)); - ctxt->contextObject = cacheItem; - cacheItem->contextData = ctxt; - - if (m_adaptorModel.hasProxyObject()) { - if (QQuickVisualAdaptorModelProxyInterface *proxy - = qobject_cast(cacheItem)) { - ctxt = new QQmlContextData; - ctxt->setParent(cacheItem->contextData, true); - ctxt->contextObject = proxy->proxiedObject(); - } - } - - cacheItem->incubateObject( - m_delegate, - m_context->engine(), - ctxt, - QQmlContextData::get(m_context)); - } - - if (index == m_compositor.count(group) - 1 && m_adaptorModel.canFetchMore()) - QCoreApplication::postEvent(q, new QEvent(QEvent::UpdateRequest)); - - // Remove the temporary reference count. - cacheItem->scriptRef -= 1; - if (cacheItem->object) - return cacheItem->object; - - cacheItem->releaseObject(); - if (!cacheItem->isReferenced()) { - removeCacheItem(cacheItem); - delete cacheItem; - } - - return 0; -} - -/* - If asynchronous is true or the component is being loaded asynchronously due - to an ancestor being loaded asynchronously, item() may return 0. In this - case itemCreated() will be emitted when the item is available. The item - at this stage does not have any references, so item() must be called again - to ensure a reference is held. Any call to item() which returns a valid item - must be matched by a call to release() in order to destroy the item. -*/ -QQuickItem *QQuickVisualDataModel::item(int index, bool asynchronous) -{ - Q_D(QQuickVisualDataModel); - if (!d->m_delegate || index < 0 || index >= d->m_compositor.count(d->m_compositorGroup)) { - qWarning() << "VisualDataModel::item: index out range" << index << d->m_compositor.count(d->m_compositorGroup); - return 0; - } - - QObject *object = d->object(d->m_compositorGroup, index, asynchronous); - if (!object) - return 0; - - if (QQuickItem *item = qmlobject_cast(object)) - return item; - - d->release(object); - if (!d->m_delegateValidated) { - if (object) - qmlInfo(d->m_delegate) << QQuickVisualDataModel::tr("Delegate component must be Item type."); - d->m_delegateValidated = true; - } - return 0; -} - -QString QQuickVisualDataModelPrivate::stringValue(Compositor::Group group, int index, const QString &name) -{ - Compositor::iterator it = m_compositor.find(group, index); - if (QQuickVisualAdaptorModel *model = it.list()) { - QString role = name; - int dot = name.indexOf(QLatin1Char('.')); - if (dot > 0) - role = name.left(dot); - QVariant value = model->value(it.modelIndex(), role); - while (dot > 0) { - QObject *obj = qvariant_cast(value); - if (!obj) - return QString(); - int from = dot+1; - dot = name.indexOf(QLatin1Char('.'), from); - value = obj->property(name.mid(from, dot-from).toUtf8()); - } - return value.toString(); - } - return QString(); -} - -QString QQuickVisualDataModel::stringValue(int index, const QString &name) -{ - Q_D(QQuickVisualDataModel); - return d->stringValue(d->m_compositorGroup, index, name); -} - -int QQuickVisualDataModel::indexOf(QQuickItem *item, QObject *) const -{ - Q_D(const QQuickVisualDataModel); - if (QQuickVisualDataModelItem *cacheItem = QQuickVisualDataModelItem::dataForObject(item)) - return cacheItem->groupIndex(d->m_compositorGroup); - return -1; -} - -void QQuickVisualDataModel::setWatchedRoles(QList roles) -{ - Q_D(QQuickVisualDataModel); - d->m_adaptorModel.replaceWatchedRoles(d->m_watchedRoles, roles); - d->m_watchedRoles = roles; -} - -void QQuickVisualDataModelPrivate::addGroups( - Compositor::iterator from, int count, Compositor::Group group, int groupFlags) -{ - QVector inserts; - m_compositor.setFlags(from, count, group, groupFlags, &inserts); - itemsInserted(inserts); - emitChanges(); -} - -void QQuickVisualDataModelPrivate::removeGroups( - Compositor::iterator from, int count, Compositor::Group group, int groupFlags) -{ - QVector removes; - m_compositor.clearFlags(from, count, group, groupFlags, &removes); - itemsRemoved(removes); - emitChanges(); -} - -void QQuickVisualDataModelPrivate::setGroups( - Compositor::iterator from, int count, Compositor::Group group, int groupFlags) -{ - QVector removes; - QVector inserts; - - m_compositor.setFlags(from, count, group, groupFlags, &inserts); - itemsInserted(inserts); - const int removeFlags = ~groupFlags & Compositor::GroupMask; - - from = m_compositor.find(from.group, from.index[from.group]); - m_compositor.clearFlags(from, count, group, removeFlags, &removes); - itemsRemoved(removes); - emitChanges(); -} - -bool QQuickVisualDataModel::event(QEvent *e) -{ - Q_D(QQuickVisualDataModel); - if (e->type() == QEvent::UpdateRequest) { - d->m_adaptorModel.fetchMore(); - } else if (e->type() == QEvent::User) { - d->m_incubatorCleanupScheduled = false; - qDeleteAll(d->m_finishedIncubating); - d->m_finishedIncubating.clear(); - } - return QQuickVisualModel::event(e); -} - -void QQuickVisualDataModelPrivate::itemsChanged(const QVector &changes) -{ - if (!m_delegate) - return; - - QVarLengthArray, Compositor::MaximumGroupCount> translatedChanges(m_groupCount); - - foreach (const Compositor::Change &change, changes) { - for (int i = 1; i < m_groupCount; ++i) { - if (change.inGroup(i)) { - translatedChanges[i].append(QQuickChangeSet::Change(change.index[i], change.count)); - } - } - } - - for (int i = 1; i < m_groupCount; ++i) - QQuickVisualDataGroupPrivate::get(m_groups[i])->changeSet.change(translatedChanges.at(i)); -} - -void QQuickVisualDataModel::_q_itemsChanged(int index, int count, const QVector &roles) -{ - Q_D(QQuickVisualDataModel); - if (count <= 0 || !d->m_complete) - return; - - if (d->m_adaptorModel.notify(d->m_cache, index, count, roles)) { - QVector changes; - d->m_compositor.listItemsChanged(&d->m_adaptorModel, index, count, &changes); - d->itemsChanged(changes); - d->emitChanges(); - } -} - -static void incrementIndexes(QQuickVisualDataModelItem *cacheItem, int count, const int *deltas) -{ - if (QVDMIncubationTask *incubationTask = cacheItem->incubationTask) { - for (int i = 1; i < count; ++i) - incubationTask->index[i] += deltas[i]; - } - if (QQuickVisualDataModelAttached *attached = cacheItem->attached) { - for (int i = 1; i < count; ++i) - attached->m_currentIndex[i] += deltas[i]; - } -} - -void QQuickVisualDataModelPrivate::itemsInserted( - const QVector &inserts, - QVarLengthArray, Compositor::MaximumGroupCount> *translatedInserts, - QHash > *movedItems) -{ - int cacheIndex = 0; - - int inserted[Compositor::MaximumGroupCount]; - for (int i = 1; i < m_groupCount; ++i) - inserted[i] = 0; - - foreach (const Compositor::Insert &insert, inserts) { - for (; cacheIndex < insert.cacheIndex; ++cacheIndex) - incrementIndexes(m_cache.at(cacheIndex), m_groupCount, inserted); - - for (int i = 1; i < m_groupCount; ++i) { - if (insert.inGroup(i)) { - (*translatedInserts)[i].append( - QQuickChangeSet::Insert(insert.index[i], insert.count, insert.moveId)); - inserted[i] += insert.count; - } - } - - if (!insert.inCache()) - continue; - - if (movedItems && insert.isMove()) { - QList items = movedItems->take(insert.moveId); - Q_ASSERT(items.count() == insert.count); - m_cache = m_cache.mid(0, insert.cacheIndex) + items + m_cache.mid(insert.cacheIndex); - } - if (insert.inGroup()) { - for (int offset = 0; cacheIndex < insert.cacheIndex + insert.count; ++cacheIndex, ++offset) { - QQuickVisualDataModelItem *cacheItem = m_cache.at(cacheIndex); - cacheItem->groups |= insert.flags & Compositor::GroupMask; - - if (QVDMIncubationTask *incubationTask = cacheItem->incubationTask) { - for (int i = 1; i < m_groupCount; ++i) - incubationTask->index[i] = cacheItem->groups & (1 << i) - ? insert.index[i] + offset - : insert.index[i]; - } - if (QQuickVisualDataModelAttached *attached = cacheItem->attached) { - for (int i = 1; i < m_groupCount; ++i) - attached->m_currentIndex[i] = cacheItem->groups & (1 << i) - ? insert.index[i] + offset - : insert.index[i]; - } - } - } else { - cacheIndex = insert.cacheIndex + insert.count; - } - } - for (; cacheIndex < m_cache.count(); ++cacheIndex) - incrementIndexes(m_cache.at(cacheIndex), m_groupCount, inserted); -} - -void QQuickVisualDataModelPrivate::itemsInserted(const QVector &inserts) -{ - QVarLengthArray, Compositor::MaximumGroupCount> translatedInserts(m_groupCount); - itemsInserted(inserts, &translatedInserts); - Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); - if (!m_delegate) - return; - - for (int i = 1; i < m_groupCount; ++i) - QQuickVisualDataGroupPrivate::get(m_groups[i])->changeSet.insert(translatedInserts.at(i)); -} - -void QQuickVisualDataModel::_q_itemsInserted(int index, int count) -{ - - Q_D(QQuickVisualDataModel); - if (count <= 0 || !d->m_complete) - return; - - d->m_count += count; - - for (int i = 0, c = d->m_cache.count(); i < c; ++i) { - QQuickVisualDataModelItem *item = d->m_cache.at(i); - if (item->modelIndex() >= index) - item->setModelIndex(item->modelIndex() + count); - } - - QVector inserts; - d->m_compositor.listItemsInserted(&d->m_adaptorModel, index, count, &inserts); - d->itemsInserted(inserts); - d->emitChanges(); -} - -void QQuickVisualDataModelPrivate::itemsRemoved( - const QVector &removes, - QVarLengthArray, Compositor::MaximumGroupCount> *translatedRemoves, - QHash > *movedItems) -{ - int cacheIndex = 0; - int removedCache = 0; - - int removed[Compositor::MaximumGroupCount]; - for (int i = 1; i < m_groupCount; ++i) - removed[i] = 0; - - foreach (const Compositor::Remove &remove, removes) { - for (; cacheIndex < remove.cacheIndex; ++cacheIndex) - incrementIndexes(m_cache.at(cacheIndex), m_groupCount, removed); - - for (int i = 1; i < m_groupCount; ++i) { - if (remove.inGroup(i)) { - (*translatedRemoves)[i].append( - QQuickChangeSet::Remove(remove.index[i], remove.count, remove.moveId)); - removed[i] -= remove.count; - } - } - - if (!remove.inCache()) - continue; - - if (movedItems && remove.isMove()) { - movedItems->insert(remove.moveId, m_cache.mid(remove.cacheIndex, remove.count)); - QList::iterator begin = m_cache.begin() + remove.cacheIndex; - QList::iterator end = begin + remove.count; - m_cache.erase(begin, end); - } else { - for (; cacheIndex < remove.cacheIndex + remove.count - removedCache; ++cacheIndex) { - QQuickVisualDataModelItem *cacheItem = m_cache.at(cacheIndex); - if (remove.inGroup(Compositor::Persisted) && cacheItem->objectRef == 0 && cacheItem->object) { - QObject *object = cacheItem->object; - cacheItem->destroyObject(); - if (QQuickPackage *package = qmlobject_cast(object)) - emitDestroyingPackage(package); - else if (QQuickItem *item = qmlobject_cast(object)) - emitDestroyingItem(item); - cacheItem->scriptRef -= 1; - } - if (!cacheItem->isReferenced()) { - m_compositor.clearFlags(Compositor::Cache, cacheIndex, 1, Compositor::CacheFlag); - m_cache.removeAt(cacheIndex); - delete cacheItem; - --cacheIndex; - ++removedCache; - Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); - } else if (remove.groups() == cacheItem->groups) { - cacheItem->groups = 0; - if (QVDMIncubationTask *incubationTask = cacheItem->incubationTask) { - for (int i = 1; i < m_groupCount; ++i) - incubationTask->index[i] = -1; - } - if (QQuickVisualDataModelAttached *attached = cacheItem->attached) { - for (int i = 1; i < m_groupCount; ++i) - attached->m_currentIndex[i] = -1; - } - } else { - if (QVDMIncubationTask *incubationTask = cacheItem->incubationTask) { - for (int i = 1; i < m_groupCount; ++i) { - if (remove.inGroup(i)) - incubationTask->index[i] = remove.index[i]; - } - } - if (QQuickVisualDataModelAttached *attached = cacheItem->attached) { - for (int i = 1; i < m_groupCount; ++i) { - if (remove.inGroup(i)) - attached->m_currentIndex[i] = remove.index[i]; - } - } - cacheItem->groups &= ~remove.flags; - } - } - } - } - - for (; cacheIndex < m_cache.count(); ++cacheIndex) - incrementIndexes(m_cache.at(cacheIndex), m_groupCount, removed); -} - -void QQuickVisualDataModelPrivate::itemsRemoved(const QVector &removes) -{ - QVarLengthArray, Compositor::MaximumGroupCount> translatedRemoves(m_groupCount); - itemsRemoved(removes, &translatedRemoves); - Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); - if (!m_delegate) - return; - - for (int i = 1; i < m_groupCount; ++i) - QQuickVisualDataGroupPrivate::get(m_groups[i])->changeSet.remove(translatedRemoves.at(i)); -} - -void QQuickVisualDataModel::_q_itemsRemoved(int index, int count) -{ - Q_D(QQuickVisualDataModel); - if (count <= 0|| !d->m_complete) - return; - - d->m_count -= count; - - for (int i = 0, c = d->m_cache.count(); i < c; ++i) { - QQuickVisualDataModelItem *item = d->m_cache.at(i); - if (item->modelIndex() >= index + count) - item->setModelIndex(item->modelIndex() - count); - else if (item->modelIndex() >= index) - item->setModelIndex(-1); - } - - QVector removes; - d->m_compositor.listItemsRemoved(&d->m_adaptorModel, index, count, &removes); - d->itemsRemoved(removes); - - d->emitChanges(); -} - -void QQuickVisualDataModelPrivate::itemsMoved( - const QVector &removes, const QVector &inserts) -{ - QHash > movedItems; - - QVarLengthArray, Compositor::MaximumGroupCount> translatedRemoves(m_groupCount); - itemsRemoved(removes, &translatedRemoves, &movedItems); - - QVarLengthArray, Compositor::MaximumGroupCount> translatedInserts(m_groupCount); - itemsInserted(inserts, &translatedInserts, &movedItems); - Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); - Q_ASSERT(movedItems.isEmpty()); - if (!m_delegate) - return; - - for (int i = 1; i < m_groupCount; ++i) { - QQuickVisualDataGroupPrivate::get(m_groups[i])->changeSet.move( - translatedRemoves.at(i), - translatedInserts.at(i)); - } -} - -void QQuickVisualDataModel::_q_itemsMoved(int from, int to, int count) -{ - Q_D(QQuickVisualDataModel); - if (count <= 0 || !d->m_complete) - return; - - const int minimum = qMin(from, to); - const int maximum = qMax(from, to) + count; - const int difference = from > to ? count : -count; - - for (int i = 0, c = d->m_cache.count(); i < c; ++i) { - QQuickVisualDataModelItem *item = d->m_cache.at(i); - if (item->modelIndex() >= from && item->modelIndex() < from + count) - item->setModelIndex(item->modelIndex() - from + to); - else if (item->modelIndex() >= minimum && item->modelIndex() < maximum) - item->setModelIndex(item->modelIndex() + difference); - } - - QVector removes; - QVector inserts; - d->m_compositor.listItemsMoved(&d->m_adaptorModel, from, to, count, &removes, &inserts); - d->itemsMoved(removes, inserts); - d->emitChanges(); -} - -template v8::Local -QQuickVisualDataModelPrivate::buildChangeList(const QVector &changes) -{ - v8::Local indexes = v8::Array::New(changes.count()); - v8::Local indexKey = v8::String::New("index"); - v8::Local countKey = v8::String::New("count"); - v8::Local moveIdKey = v8::String::New("moveId"); - - for (int i = 0; i < changes.count(); ++i) { - v8::Local object = v8::Object::New(); - object->Set(indexKey, v8::Integer::New(changes.at(i).index)); - object->Set(countKey, v8::Integer::New(changes.at(i).count)); - object->Set(moveIdKey, changes.at(i).moveId != -1 ? v8::Integer::New(changes.at(i).count) : v8::Undefined()); - indexes->Set(i, object); - } - return indexes; -} - -void QQuickVisualDataModelPrivate::emitModelUpdated(const QQuickChangeSet &changeSet, bool reset) -{ - Q_Q(QQuickVisualDataModel); - emit q->modelUpdated(changeSet, reset); - if (changeSet.difference() != 0) - emit q->countChanged(); -} - -void QQuickVisualDataModelPrivate::emitChanges() -{ - if (m_transaction || !m_complete || !m_context->isValid()) - return; - - m_transaction = true; - QV8Engine *engine = QQmlEnginePrivate::getV8Engine(m_context->engine()); - for (int i = 1; i < m_groupCount; ++i) - QQuickVisualDataGroupPrivate::get(m_groups[i])->emitChanges(engine); - m_transaction = false; - - const bool reset = m_reset; - m_reset = false; - for (int i = 1; i < m_groupCount; ++i) - QQuickVisualDataGroupPrivate::get(m_groups[i])->emitModelUpdated(reset); - - foreach (QQuickVisualDataModelItem *cacheItem, m_cache) { - if (cacheItem->attached) - cacheItem->attached->emitChanges(); - } -} - -void QQuickVisualDataModel::_q_modelReset() -{ - Q_D(QQuickVisualDataModel); - if (!d->m_delegate) - return; - - int oldCount = d->m_count; - d->m_adaptorModel.rootIndex = QModelIndex(); - - if (d->m_complete) { - d->m_count = d->m_adaptorModel.count(); - - for (int i = 0, c = d->m_cache.count(); i < c; ++i) { - QQuickVisualDataModelItem *item = d->m_cache.at(i); - if (item->modelIndex() != -1) - item->setModelIndex(-1); - } - - QVector removes; - QVector inserts; - if (oldCount) - d->m_compositor.listItemsRemoved(&d->m_adaptorModel, 0, oldCount, &removes); - if (d->m_count) - d->m_compositor.listItemsInserted(&d->m_adaptorModel, 0, d->m_count, &inserts); - d->itemsMoved(removes, inserts); - d->m_reset = true; - - if (d->m_adaptorModel.canFetchMore()) - d->m_adaptorModel.fetchMore(); - - d->emitChanges(); - } - emit rootIndexChanged(); -} - -void QQuickVisualDataModel::_q_rowsInserted(const QModelIndex &parent, int begin, int end) -{ - Q_D(QQuickVisualDataModel); - if (parent == d->m_adaptorModel.rootIndex) - _q_itemsInserted(begin, end - begin + 1); -} - -void QQuickVisualDataModel::_q_rowsAboutToBeRemoved(const QModelIndex &parent, int begin, int end) -{ - Q_D(QQuickVisualDataModel); - if (!d->m_adaptorModel.rootIndex.isValid()) - return; - const QModelIndex index = d->m_adaptorModel.rootIndex; - if (index.parent() == parent && index.row() >= begin && index.row() <= end) { - const int oldCount = d->m_count; - d->m_count = 0; - d->m_adaptorModel.invalidateModel(this); - - if (d->m_complete && oldCount > 0) { - QVector removes; - d->m_compositor.listItemsRemoved(&d->m_adaptorModel, 0, oldCount, &removes); - d->itemsRemoved(removes); - d->emitChanges(); - } - } -} - -void QQuickVisualDataModel::_q_rowsRemoved(const QModelIndex &parent, int begin, int end) -{ - Q_D(QQuickVisualDataModel); - if (parent == d->m_adaptorModel.rootIndex) - _q_itemsRemoved(begin, end - begin + 1); -} - -void QQuickVisualDataModel::_q_rowsMoved( - const QModelIndex &sourceParent, int sourceStart, int sourceEnd, - const QModelIndex &destinationParent, int destinationRow) -{ - Q_D(QQuickVisualDataModel); - const int count = sourceEnd - sourceStart + 1; - if (destinationParent == d->m_adaptorModel.rootIndex && sourceParent == d->m_adaptorModel.rootIndex) { - _q_itemsMoved(sourceStart, sourceStart > destinationRow ? destinationRow : destinationRow - count, count); - } else if (sourceParent == d->m_adaptorModel.rootIndex) { - _q_itemsRemoved(sourceStart, count); - } else if (destinationParent == d->m_adaptorModel.rootIndex) { - _q_itemsInserted(destinationRow, count); - } -} - -void QQuickVisualDataModel::_q_dataChanged(const QModelIndex &begin, const QModelIndex &end, const QVector &roles) -{ - Q_D(QQuickVisualDataModel); - if (begin.parent() == d->m_adaptorModel.rootIndex) - _q_itemsChanged(begin.row(), end.row() - begin.row() + 1, roles); -} - -void QQuickVisualDataModel::_q_layoutChanged() -{ - Q_D(QQuickVisualDataModel); - _q_itemsChanged(0, d->m_count, QVector()); -} - -QQuickVisualDataModelAttached *QQuickVisualDataModel::qmlAttachedProperties(QObject *obj) -{ - if (QQuickVisualDataModelItem *cacheItem = QQuickVisualDataModelItem::dataForObject(obj)) { - if (cacheItem->object == obj) { // Don't create attached item for child objects. - cacheItem->attached = new QQuickVisualDataModelAttached(cacheItem, obj); - return cacheItem->attached; - } - } - return new QQuickVisualDataModelAttached(obj); -} - -bool QQuickVisualDataModelPrivate::insert( - Compositor::insert_iterator &before, const v8::Local &object, int groups) -{ - if (!m_context->isValid()) - return false; - - QQuickVisualDataModelItem *cacheItem = m_adaptorModel.createItem(m_cacheMetaType, m_context->engine(), -1); - if (!cacheItem) - return false; - - v8::Local propertyNames = object->GetPropertyNames(); - for (uint i = 0; i < propertyNames->Length(); ++i) { - v8::Local propertyName = propertyNames->Get(i)->ToString(); - cacheItem->setValue( - m_cacheMetaType->v8Engine->toString(propertyName), - m_cacheMetaType->v8Engine->toVariant(object->Get(propertyName), QVariant::Invalid)); - } - - cacheItem->groups = groups | Compositor::UnresolvedFlag | Compositor::CacheFlag; - - // Must be before the new object is inserted into the cache or its indexes will be adjusted too. - itemsInserted(QVector() << Compositor::Insert(before, 1, cacheItem->groups & ~Compositor::CacheFlag)); - - before = m_compositor.insert(before, 0, 0, 1, cacheItem->groups); - m_cache.insert(before.cacheIndex, cacheItem); - - return true; -} - -//============================================================================ - -QQuickVisualDataModelItemMetaType::QQuickVisualDataModelItemMetaType( - QV8Engine *engine, QQuickVisualDataModel *model, const QStringList &groupNames) - : model(model) - , groupCount(groupNames.count() + 1) - , v8Engine(engine) - , metaObject(0) - , groupNames(groupNames) -{ -} - -QQuickVisualDataModelItemMetaType::~QQuickVisualDataModelItemMetaType() -{ - if (metaObject) - metaObject->release(); - qPersistentDispose(constructor); -} - -void QQuickVisualDataModelItemMetaType::initializeMetaObject() -{ - QMetaObjectBuilder builder; - builder.setFlags(QMetaObjectBuilder::DynamicMetaObject); - builder.setClassName(QQuickVisualDataModelAttached::staticMetaObject.className()); - builder.setSuperClass(&QQuickVisualDataModelAttached::staticMetaObject); - - int notifierId = 0; - for (int i = 0; i < groupNames.count(); ++i, ++notifierId) { - QString propertyName = QStringLiteral("in") + groupNames.at(i); - propertyName.replace(2, 1, propertyName.at(2).toUpper()); - builder.addSignal("__" + propertyName.toUtf8() + "Changed()"); - QMetaPropertyBuilder propertyBuilder = builder.addProperty( - propertyName.toUtf8(), "bool", notifierId); - propertyBuilder.setWritable(true); - } - for (int i = 0; i < groupNames.count(); ++i, ++notifierId) { - const QString propertyName = groupNames.at(i) + QStringLiteral("Index"); - builder.addSignal("__" + propertyName.toUtf8() + "Changed()"); - QMetaPropertyBuilder propertyBuilder = builder.addProperty( - propertyName.toUtf8(), "int", notifierId); - propertyBuilder.setWritable(true); - } - - metaObject = new QQuickVisualDataModelAttachedMetaObject(this, builder.toMetaObject()); -} - -void QQuickVisualDataModelItemMetaType::initializeConstructor() -{ - v8::HandleScope handleScope; - v8::Context::Scope contextScope(v8Engine->context()); - - QQuickVisualDataModelEngineData *data = engineData(v8Engine); - - constructor = qPersistentNew(v8::ObjectTemplate::New()); - - constructor->SetHasExternalResource(true); - constructor->SetAccessor(data->model(), get_model); - constructor->SetAccessor(data->groups(), get_groups, set_groups); - constructor->SetAccessor(data->isUnresolved(), get_member, 0, v8::Int32::New(30)); - constructor->SetAccessor(data->inItems(), get_member, set_member, v8::Int32::New(1)); - constructor->SetAccessor(data->inPersistedItems(), get_member, set_member, v8::Int32::New(2)); - constructor->SetAccessor(data->itemsIndex(), get_index, 0, v8::Int32::New(1)); - constructor->SetAccessor(data->persistedItemsIndex(), get_index, 0, v8::Int32::New(2)); - - for (int i = 2; i < groupNames.count(); ++i) { - QString propertyName = QStringLiteral("in") + groupNames.at(i); - propertyName.replace(2, 1, propertyName.at(2).toUpper()); - constructor->SetAccessor( - v8Engine->toString(propertyName), get_member, set_member, v8::Int32::New(i + 1)); - } - for (int i = 2; i < groupNames.count(); ++i) { - const QString propertyName = groupNames.at(i) + QStringLiteral("Index"); - constructor->SetAccessor( - v8Engine->toString(propertyName), get_index, 0, v8::Int32::New(i + 1)); - } -} - -int QQuickVisualDataModelItemMetaType::parseGroups(const QStringList &groups) const -{ - int groupFlags = 0; - foreach (const QString &groupName, groups) { - int index = groupNames.indexOf(groupName); - if (index != -1) - groupFlags |= 2 << index; - } - return groupFlags; -} - -int QQuickVisualDataModelItemMetaType::parseGroups(const v8::Local &groups) const -{ - int groupFlags = 0; - if (groups->IsString()) { - const QString groupName = v8Engine->toString(groups); - int index = groupNames.indexOf(groupName); - if (index != -1) - groupFlags |= 2 << index; - } else if (groups->IsArray()) { - v8::Local array = v8::Local::Cast(groups); - for (uint i = 0; i < array->Length(); ++i) { - const QString groupName = v8Engine->toString(array->Get(i)); - int index = groupNames.indexOf(groupName); - if (index != -1) - groupFlags |= 2 << index; - } - } - return groupFlags; -} - -v8::Handle QQuickVisualDataModelItemMetaType::get_model( - v8::Local, const v8::AccessorInfo &info) -{ - QQuickVisualDataModelItem *cacheItem = v8_resource_cast(info.This()); - V8ASSERT_TYPE(cacheItem, "Not a valid VisualData object"); - if (!cacheItem->metaType->model) - return v8::Undefined(); - - return cacheItem->get(); -} - -v8::Handle QQuickVisualDataModelItemMetaType::get_groups( - v8::Local, const v8::AccessorInfo &info) -{ - QQuickVisualDataModelItem *cacheItem = v8_resource_cast(info.This()); - V8ASSERT_TYPE(cacheItem, "Not a valid VisualData object"); - - QStringList groups; - for (int i = 1; i < cacheItem->metaType->groupCount; ++i) { - if (cacheItem->groups & (1 << i)) - groups.append(cacheItem->metaType->groupNames.at(i - 1)); - } - - return cacheItem->engine->fromVariant(groups); -} - -void QQuickVisualDataModelItemMetaType::set_groups( - v8::Local, v8::Local value, const v8::AccessorInfo &info) -{ - QQuickVisualDataModelItem *cacheItem = v8_resource_cast(info.This()); - V8ASSERT_TYPE_SETTER(cacheItem, "Not a valid VisualData object"); - - if (!cacheItem->metaType->model) - return; - QQuickVisualDataModelPrivate *model = QQuickVisualDataModelPrivate::get(cacheItem->metaType->model); - - const int groupFlags = model->m_cacheMetaType->parseGroups(value); - const int cacheIndex = model->m_cache.indexOf(cacheItem); - Compositor::iterator it = model->m_compositor.find(Compositor::Cache, cacheIndex); - model->setGroups(it, 1, Compositor::Cache, groupFlags); -} - -v8::Handle QQuickVisualDataModelItemMetaType::get_member( - v8::Local, const v8::AccessorInfo &info) -{ - QQuickVisualDataModelItem *cacheItem = v8_resource_cast(info.This()); - V8ASSERT_TYPE(cacheItem, "Not a valid VisualData object"); - - return v8::Boolean::New(cacheItem->groups & (1 << info.Data()->Int32Value())); -} - -void QQuickVisualDataModelItemMetaType::set_member( - v8::Local, v8::Local value, const v8::AccessorInfo &info) -{ - QQuickVisualDataModelItem *cacheItem = v8_resource_cast(info.This()); - V8ASSERT_TYPE_SETTER(cacheItem, "Not a valid VisualData object"); - - if (!cacheItem->metaType->model) - return; - QQuickVisualDataModelPrivate *model = QQuickVisualDataModelPrivate::get(cacheItem->metaType->model); - - Compositor::Group group = Compositor::Group(info.Data()->Int32Value()); - const bool member = value->BooleanValue(); - const int groupFlag = (1 << group); - if (member == ((cacheItem->groups & groupFlag) != 0)) - return; - - const int cacheIndex = model->m_cache.indexOf(cacheItem); - Compositor::iterator it = model->m_compositor.find(Compositor::Cache, cacheIndex); - if (member) - model->addGroups(it, 1, Compositor::Cache, groupFlag); - else - model->removeGroups(it, 1, Compositor::Cache, groupFlag); -} - -v8::Handle QQuickVisualDataModelItemMetaType::get_index( - v8::Local, const v8::AccessorInfo &info) -{ - QQuickVisualDataModelItem *cacheItem = v8_resource_cast(info.This()); - V8ASSERT_TYPE(cacheItem, "Not a valid VisualData object"); - - return v8::Integer::New(cacheItem->groupIndex(Compositor::Group(info.Data()->Int32Value()))); -} - - -//--------------------------------------------------------------------------- - -QQuickVisualDataModelItem::QQuickVisualDataModelItem( - QQuickVisualDataModelItemMetaType *metaType, int modelIndex) - : QV8ObjectResource(metaType->v8Engine) - , metaType(metaType) - , contextData(0) - , object(0) - , attached(0) - , incubationTask(0) - , objectRef(0) - , scriptRef(0) - , groups(0) - , index(modelIndex) -{ - metaType->addref(); -} - -QQuickVisualDataModelItem::~QQuickVisualDataModelItem() -{ - Q_ASSERT(scriptRef == 0); - Q_ASSERT(objectRef == 0); - Q_ASSERT(!object); - - if (incubationTask && metaType->model) - QQuickVisualDataModelPrivate::get(metaType->model)->releaseIncubator(incubationTask); - - metaType->release(); - -} - -void QQuickVisualDataModelItem::Dispose() -{ - --scriptRef; - if (isReferenced()) - return; - - if (metaType->model) { - QQuickVisualDataModelPrivate *model = QQuickVisualDataModelPrivate::get(metaType->model); - model->removeCacheItem(this); - } - delete this; -} - -/* - This is essentially a copy of QQmlComponent::create(); except it takes the QQmlContextData - arguments instead of QQmlContext which means we don't have to construct the rather weighty - wrapper class for every delegate item. -*/ -void QQuickVisualDataModelItem::incubateObject( - QQmlComponent *component, - QQmlEngine *engine, - QQmlContextData *context, - QQmlContextData *forContext) -{ - QQmlIncubatorPrivate *incubatorPriv = QQmlIncubatorPrivate::get(incubationTask); - QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(engine); - QQmlComponentPrivate *componentPriv = QQmlComponentPrivate::get(component); - - incubatorPriv->compiledData = componentPriv->cc; - incubatorPriv->compiledData->addref(); - incubatorPriv->vme.init( - context, - componentPriv->cc, - componentPriv->start, - componentPriv->creationContext); - - enginePriv->incubate(*incubationTask, forContext); -} - -void QQuickVisualDataModelItem::destroyObject() -{ - Q_ASSERT(object); - Q_ASSERT(contextData); - - QObjectPrivate *p = QObjectPrivate::get(object); - Q_ASSERT(p->declarativeData); - QQmlData *data = static_cast(p->declarativeData); - if (data->ownContext && data->context) - data->context->clearContext(); - object->deleteLater(); - - if (attached) { - attached->m_cacheItem = 0; - attached = 0; - } - - contextData->destroy(); - contextData = 0; - object = 0; -} - -QQuickVisualDataModelItem *QQuickVisualDataModelItem::dataForObject(QObject *object) -{ - QObjectPrivate *p = QObjectPrivate::get(object); - QQmlContextData *context = p->declarativeData - ? static_cast(p->declarativeData)->context - : 0; - for (context = context ? context->parent : 0; context; context = context->parent) { - if (QQuickVisualDataModelItem *cacheItem = qobject_cast( - context->contextObject)) { - return cacheItem; - } - } - return 0; -} - -int QQuickVisualDataModelItem::groupIndex(Compositor::Group group) -{ - if (QQuickVisualDataModelPrivate * const model = metaType->model - ? QQuickVisualDataModelPrivate::get(metaType->model) - : 0) { - return model->m_compositor.find(Compositor::Cache, model->m_cache.indexOf(this)).index[group]; - } - return -1; -} - -//--------------------------------------------------------------------------- - -QQuickVisualDataModelAttachedMetaObject::QQuickVisualDataModelAttachedMetaObject( - QQuickVisualDataModelItemMetaType *metaType, QMetaObject *metaObject) - : metaType(metaType) - , metaObject(metaObject) - , memberPropertyOffset(QQuickVisualDataModelAttached::staticMetaObject.propertyCount()) - , indexPropertyOffset(QQuickVisualDataModelAttached::staticMetaObject.propertyCount() + metaType->groupNames.count()) -{ - // Don't reference count the meta-type here as that would create a circular reference. - // Instead we rely the fact that the meta-type's reference count can't reach 0 without first - // destroying all delegates with attached objects. - *static_cast(this) = *metaObject; -} - -QQuickVisualDataModelAttachedMetaObject::~QQuickVisualDataModelAttachedMetaObject() -{ - ::free(metaObject); -} - -void QQuickVisualDataModelAttachedMetaObject::objectDestroyed(QObject *) -{ - release(); -} - -int QQuickVisualDataModelAttachedMetaObject::metaCall(QObject *object, QMetaObject::Call call, int _id, void **arguments) -{ - QQuickVisualDataModelAttached *attached = static_cast(object); - if (call == QMetaObject::ReadProperty) { - if (_id >= indexPropertyOffset) { - Compositor::Group group = Compositor::Group(_id - indexPropertyOffset + 1); - *static_cast(arguments[0]) = attached->m_currentIndex[group]; - return -1; - } else if (_id >= memberPropertyOffset) { - Compositor::Group group = Compositor::Group(_id - memberPropertyOffset + 1); - *static_cast(arguments[0]) = attached->m_cacheItem->groups & (1 << group); - return -1; - } - } else if (call == QMetaObject::WriteProperty) { - if (_id >= memberPropertyOffset) { - if (!metaType->model) - return -1; - QQuickVisualDataModelPrivate *model = QQuickVisualDataModelPrivate::get(metaType->model); - Compositor::Group group = Compositor::Group(_id - memberPropertyOffset + 1); - const int groupFlag = 1 << group; - const bool member = attached->m_cacheItem->groups & groupFlag; - if (member && !*static_cast(arguments[0])) { - Compositor::iterator it = model->m_compositor.find( - group, attached->m_currentIndex[group]); - model->removeGroups(it, 1, group, groupFlag); - } else if (!member && *static_cast(arguments[0])) { - for (int i = 1; i < metaType->groupCount; ++i) { - if (attached->m_cacheItem->groups & (1 << i)) { - Compositor::iterator it = model->m_compositor.find( - Compositor::Group(i), attached->m_currentIndex[i]); - model->addGroups(it, 1, Compositor::Group(i), groupFlag); - break; - } - } - } - return -1; - } - } - return attached->qt_metacall(call, _id, arguments); -} - -QQuickVisualDataModelAttached::QQuickVisualDataModelAttached(QObject *parent) - : m_cacheItem(0) - , m_previousGroups(0) -{ - QQml_setParent_noEvent(this, parent); -} - -QQuickVisualDataModelAttached::QQuickVisualDataModelAttached( - QQuickVisualDataModelItem *cacheItem, QObject *parent) - : m_cacheItem(cacheItem) - , m_previousGroups(cacheItem->groups) -{ - QQml_setParent_noEvent(this, parent); - if (QVDMIncubationTask *incubationTask = m_cacheItem->incubationTask) { - for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i) - m_currentIndex[i] = m_previousIndex[i] = incubationTask->index[i]; - } else { - QQuickVisualDataModelPrivate * const model = QQuickVisualDataModelPrivate::get(m_cacheItem->metaType->model); - Compositor::iterator it = model->m_compositor.find( - Compositor::Cache, model->m_cache.indexOf(m_cacheItem)); - for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i) - m_currentIndex[i] = m_previousIndex[i] = it.index[i]; - } - - if (!cacheItem->metaType->metaObject) - cacheItem->metaType->initializeMetaObject(); - - QObjectPrivate::get(this)->metaObject = cacheItem->metaType->metaObject; - cacheItem->metaType->metaObject->addref(); -} - -/*! - \qmlattachedproperty int QtQuick2::VisualDataModel::model - - This attached property holds the visual data model this delegate instance belongs to. - - It is attached to each instance of the delegate. -*/ - -QQuickVisualDataModel *QQuickVisualDataModelAttached::model() const -{ - return m_cacheItem ? m_cacheItem->metaType->model : 0; -} - -/*! - \qmlattachedproperty stringlist QtQuick2::VisualDataModel::groups - - This attached property holds the name of VisualDataGroups the item belongs to. - - It is attached to each instance of the delegate. -*/ - -QStringList QQuickVisualDataModelAttached::groups() const -{ - QStringList groups; - - if (!m_cacheItem) - return groups; - for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i) { - if (m_cacheItem->groups & (1 << i)) - groups.append(m_cacheItem->metaType->groupNames.at(i - 1)); - } - return groups; -} - -void QQuickVisualDataModelAttached::setGroups(const QStringList &groups) -{ - if (!m_cacheItem) - return; - - QQuickVisualDataModelPrivate *model = QQuickVisualDataModelPrivate::get(m_cacheItem->metaType->model); - - const int groupFlags = model->m_cacheMetaType->parseGroups(groups); - const int cacheIndex = model->m_cache.indexOf(m_cacheItem); - Compositor::iterator it = model->m_compositor.find(Compositor::Cache, cacheIndex); - model->setGroups(it, 1, Compositor::Cache, groupFlags); -} - -/*! - \qmlattachedproperty bool QtQuick2::VisualDataModel::isUnresolved - - This attached property holds whether the visual item is bound to a data model index. - Returns true if the item is not bound to the model, and false if it is. - - An unresolved item can be bound to the data model using the VisualDataGroup::resolve() - function. - - It is attached to each instance of the delegate. -*/ - -bool QQuickVisualDataModelAttached::isUnresolved() const -{ - if (!m_cacheItem) - return false; - - return m_cacheItem->groups & Compositor::UnresolvedFlag; -} - -/*! - \qmlattachedproperty int QtQuick2::VisualDataModel::inItems - - This attached property holds whether the item belongs to the default \l items VisualDataGroup. - - Changing this property will add or remove the item from the items group. - - It is attached to each instance of the delegate. -*/ - -/*! - \qmlattachedproperty int QtQuick2::VisualDataModel::itemsIndex - - This attached property holds the index of the item in the default \l items VisualDataGroup. - - It is attached to each instance of the delegate. -*/ - -/*! - \qmlattachedproperty int QtQuick2::VisualDataModel::inPersistedItems - - This attached property holds whether the item belongs to the \l persistedItems VisualDataGroup. - - Changing this property will add or remove the item from the items group. Change with caution - as removing an item from the persistedItems group will destroy the current instance if it is - not referenced by a model. - - It is attached to each instance of the delegate. -*/ - -/*! - \qmlattachedproperty int QtQuick2::VisualDataModel::persistedItemsIndex - - This attached property holds the index of the item in the \l persistedItems VisualDataGroup. - - It is attached to each instance of the delegate. -*/ - -void QQuickVisualDataModelAttached::emitChanges() -{ - const int groupChanges = m_previousGroups ^ m_cacheItem->groups; - m_previousGroups = m_cacheItem->groups; - - int indexChanges = 0; - for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i) { - if (m_previousIndex[i] != m_currentIndex[i]) { - m_previousIndex[i] = m_currentIndex[i]; - indexChanges |= (1 << i); - } - } - - int notifierId = 0; - const QMetaObject *meta = metaObject(); - for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i, ++notifierId) { - if (groupChanges & (1 << i)) - QMetaObject::activate(this, meta, notifierId, 0); - } - for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i, ++notifierId) { - if (indexChanges & (1 << i)) - QMetaObject::activate(this, meta, notifierId, 0); - } - - if (groupChanges) - emit groupsChanged(); -} - -//============================================================================ - -void QQuickVisualDataGroupPrivate::setModel(QQuickVisualDataModel *m, Compositor::Group g) -{ - Q_ASSERT(!model); - model = m; - group = g; -} - -bool QQuickVisualDataGroupPrivate::isChangedConnected() -{ - Q_Q(QQuickVisualDataGroup); - IS_SIGNAL_CONNECTED(q, QQuickVisualDataGroup, changed, (const QQmlV8Handle &,const QQmlV8Handle &)); -} - -void QQuickVisualDataGroupPrivate::emitChanges(QV8Engine *engine) -{ - Q_Q(QQuickVisualDataGroup); - if (isChangedConnected() && !changeSet.isEmpty()) { - v8::HandleScope handleScope; - v8::Context::Scope contextScope(engine->context()); - v8::Local removed = engineData(engine)->array(engine, changeSet.removes()); - v8::Local inserted = engineData(engine)->array(engine, changeSet.inserts()); - emit q->changed(QQmlV8Handle::fromHandle(removed), QQmlV8Handle::fromHandle(inserted)); - } - if (changeSet.difference() != 0) - emit q->countChanged(); -} - -void QQuickVisualDataGroupPrivate::emitModelUpdated(bool reset) -{ - for (QQuickVisualDataGroupEmitterList::iterator it = emitters.begin(); it != emitters.end(); ++it) - it->emitModelUpdated(changeSet, reset); - changeSet.clear(); -} - -void QQuickVisualDataGroupPrivate::createdPackage(int index, QQuickPackage *package) -{ - for (QQuickVisualDataGroupEmitterList::iterator it = emitters.begin(); it != emitters.end(); ++it) - it->createdPackage(index, package); -} - -void QQuickVisualDataGroupPrivate::initPackage(int index, QQuickPackage *package) -{ - for (QQuickVisualDataGroupEmitterList::iterator it = emitters.begin(); it != emitters.end(); ++it) - it->initPackage(index, package); -} - -void QQuickVisualDataGroupPrivate::destroyingPackage(QQuickPackage *package) -{ - for (QQuickVisualDataGroupEmitterList::iterator it = emitters.begin(); it != emitters.end(); ++it) - it->destroyingPackage(package); -} - -/*! - \qmltype VisualDataGroup - \instantiates QQuickVisualDataGroup - \inqmlmodule QtQuick 2 - \ingroup qtquick-models - \brief Encapsulates a filtered set of visual data items - - The VisualDataGroup type provides a means to address the model data of a VisualDataModel's - delegate items, as well as sort and filter these delegate items. - - The initial set of instantiable delegate items in a VisualDataModel is represented - by its \l {QtQuick2::VisualDataModel::items}{items} group, which normally directly reflects - the contents of the model assigned to VisualDataModel::model. This set can be changed to - the contents of any other member of VisualDataModel::groups by assigning the \l name of that - VisualDataGroup to the VisualDataModel::filterOnGroup property. - - The data of an item in a VisualDataGroup can be accessed using the get() function, which returns - information about group membership and indexes as well as model data. In combination - with the move() function this can be used to implement view sorting, with remove() to filter - items out of a view, or with setGroups() and \l Package delegates to categorize items into - different views. - - Data from models can be supplemented by inserting data directly into a VisualDataGroup - with the insert() function. This can be used to introduce mock items into a view, or - placeholder items that are later \l {resolve()}{resolved} to real model data when it becomes - available. - - Delegate items can also be be instantiated directly from a VisualDataGroup using the - create() function, making it possible to use VisualDataModel without an accompanying view - type or to cherry-pick specific items that should be instantiated irregardless of whether - they're currently within a view's visible area. - - \sa {QML Dynamic View Ordering Tutorial} -*/ - -QQuickVisualDataGroup::QQuickVisualDataGroup(QObject *parent) - : QObject(*new QQuickVisualDataGroupPrivate, parent) -{ -} - -QQuickVisualDataGroup::QQuickVisualDataGroup( - const QString &name, QQuickVisualDataModel *model, int index, QObject *parent) - : QObject(*new QQuickVisualDataGroupPrivate, parent) -{ - Q_D(QQuickVisualDataGroup); - d->name = name; - d->setModel(model, Compositor::Group(index)); -} - -QQuickVisualDataGroup::~QQuickVisualDataGroup() -{ -} - -/*! - \qmlproperty string QtQuick2::VisualDataGroup::name - - This property holds the name of the group. - - Each group in a model must have a unique name starting with a lower case letter. -*/ - -QString QQuickVisualDataGroup::name() const -{ - Q_D(const QQuickVisualDataGroup); - return d->name; -} - -void QQuickVisualDataGroup::setName(const QString &name) -{ - Q_D(QQuickVisualDataGroup); - if (d->model) - return; - if (d->name != name) { - d->name = name; - emit nameChanged(); - } -} - -/*! - \qmlproperty int QtQuick2::VisualDataGroup::count - - This property holds the number of items in the group. -*/ - -int QQuickVisualDataGroup::count() const -{ - Q_D(const QQuickVisualDataGroup); - if (!d->model) - return 0; - return QQuickVisualDataModelPrivate::get(d->model)->m_compositor.count(d->group); -} - -/*! - \qmlproperty bool QtQuick2::VisualDataGroup::includeByDefault - - This property holds whether new items are assigned to this group by default. -*/ - -bool QQuickVisualDataGroup::defaultInclude() const -{ - Q_D(const QQuickVisualDataGroup); - return d->defaultInclude; -} - -void QQuickVisualDataGroup::setDefaultInclude(bool include) -{ - Q_D(QQuickVisualDataGroup); - if (d->defaultInclude != include) { - d->defaultInclude = include; - - if (d->model) { - if (include) - QQuickVisualDataModelPrivate::get(d->model)->m_compositor.setDefaultGroup(d->group); - else - QQuickVisualDataModelPrivate::get(d->model)->m_compositor.clearDefaultGroup(d->group); - } - emit defaultIncludeChanged(); - } -} - -/*! - \qmlmethod object QtQuick2::VisualDataGroup::get(int index) - - Returns a javascript object describing the item at \a index in the group. - - The returned object contains the same information that is available to a delegate from the - VisualDataModel attached as well as the model for that item. It has the properties: - - \list - \li \b model The model data of the item. This is the same as the model context property in - a delegate - \li \b groups A list the of names of groups the item is a member of. This property can be - written to change the item's membership. - \li \b inItems Whether the item belongs to the \l {QtQuick2::VisualDataModel::items}{items} group. - Writing to this property will add or remove the item from the group. - \li \b itemsIndex The index of the item within the \l {QtQuick2::VisualDataModel::items}{items} group. - \li \b {in} Whether the item belongs to the dynamic group \e groupName. Writing to - this property will add or remove the item from the group. - \li \b {Index} The index of the item within the dynamic group \e groupName. - \li \b isUnresolved Whether the item is bound to an index in the model assigned to - VisualDataModel::model. Returns true if the item is not bound to the model, and false if it is. - \endlist -*/ - -QQmlV8Handle QQuickVisualDataGroup::get(int index) -{ - Q_D(QQuickVisualDataGroup); - if (!d->model) - return QQmlV8Handle::fromHandle(v8::Undefined());; - - QQuickVisualDataModelPrivate *model = QQuickVisualDataModelPrivate::get(d->model); - if (!model->m_context->isValid()) { - return QQmlV8Handle::fromHandle(v8::Undefined()); - } else if (index < 0 || index >= model->m_compositor.count(d->group)) { - qmlInfo(this) << tr("get: index out of range"); - return QQmlV8Handle::fromHandle(v8::Undefined()); - } - - Compositor::iterator it = model->m_compositor.find(d->group, index); - QQuickVisualDataModelItem *cacheItem = it->inCache() - ? model->m_cache.at(it.cacheIndex) - : 0; - - if (!cacheItem) { - cacheItem = model->m_adaptorModel.createItem( - model->m_cacheMetaType, model->m_context->engine(), it.modelIndex()); - if (!cacheItem) - return QQmlV8Handle::fromHandle(v8::Undefined()); - cacheItem->groups = it->flags; - - model->m_cache.insert(it.cacheIndex, cacheItem); - model->m_compositor.setFlags(it, 1, Compositor::CacheFlag); - } - - if (model->m_cacheMetaType->constructor.IsEmpty()) - model->m_cacheMetaType->initializeConstructor(); - v8::Local handle = model->m_cacheMetaType->constructor->NewInstance(); - handle->SetExternalResource(cacheItem); - ++cacheItem->scriptRef; - - return QQmlV8Handle::fromHandle(handle); -} - -bool QQuickVisualDataGroupPrivate::parseIndex( - const v8::Local &value, int *index, Compositor::Group *group) const -{ - if (value->IsInt32()) { - *index = value->Int32Value(); - return true; - } else if (value->IsObject()) { - v8::Local object = value->ToObject(); - QQuickVisualDataModelItem * const cacheItem = v8_resource_cast(object); - if (QQuickVisualDataModelPrivate *model = cacheItem && cacheItem->metaType->model - ? QQuickVisualDataModelPrivate::get(cacheItem->metaType->model) - : 0) { - *index = model->m_cache.indexOf(cacheItem); - *group = Compositor::Cache; - return true; - } - } - return false; -} - -/*! - \qmlmethod QtQuick2::VisualDataGroup::insert(int index, jsdict data, array groups = undefined) - \qmlmethod QtQuick2::VisualDataGroup::insert(jsdict data, var groups = undefined) - - Creates a new entry at \a index in a VisualDataModel with the values from \a data that - correspond to roles in the model assigned to VisualDataModel::model. - - If no index is supplied the data is appended to the model. - - The optional \a groups parameter identifies the groups the new entry should belong to, - if unspecified this is equal to the group insert was called on. - - Data inserted into a VisualDataModel can later be merged with an existing entry in - VisualDataModel::model using the \l resolve() function. This can be used to create placeholder - items that are later replaced by actual data. -*/ - -void QQuickVisualDataGroup::insert(QQmlV8Function *args) -{ - Q_D(QQuickVisualDataGroup); - QQuickVisualDataModelPrivate *model = QQuickVisualDataModelPrivate::get(d->model); - - int index = model->m_compositor.count(d->group); - Compositor::Group group = d->group; - - if (args->Length() == 0) - return; - - int i = 0; - v8::Local v = (*args)[i]; - if (d->parseIndex(v, &index, &group)) { - if (index < 0 || index > model->m_compositor.count(group)) { - qmlInfo(this) << tr("insert: index out of range"); - return; - } - if (++i == args->Length()) - return; - v = (*args)[i]; - } - - Compositor::insert_iterator before = index < model->m_compositor.count(group) - ? model->m_compositor.findInsertPosition(group, index) - : model->m_compositor.end(); - - int groups = 1 << d->group; - if (++i < args->Length()) - groups |= model->m_cacheMetaType->parseGroups((*args)[i]); - - if (v->IsArray()) { - return; - } else if (v->IsObject()) { - model->insert(before, v->ToObject(), groups); - model->emitChanges(); - } -} - -/*! - \qmlmethod QtQuick2::VisualDataGroup::create(int index) - \qmlmethod QtQuick2::VisualDataGroup::create(int index, jsdict data, array groups = undefined) - \qmlmethod QtQuick2::VisualDataGroup::create(jsdict data, array groups = undefined) - - Returns a reference to the instantiated item at \a index in the group. - - If a \a data object is provided it will be \l {insert}{inserted} at \a index and an item - referencing this new entry will be returned. The optional \a groups parameter identifies - the groups the new entry should belong to, if unspecified this is equal to the group create() - was called on. - - All items returned by create are added to the - \l {QtQuick2::VisualDataModel::persistedItems}{persistedItems} group. Items in this - group remain instantiated when not referenced by any view. -*/ - -void QQuickVisualDataGroup::create(QQmlV8Function *args) -{ - Q_D(QQuickVisualDataGroup); - if (!d->model) - return; - - if (args->Length() == 0) - return; - - QQuickVisualDataModelPrivate *model = QQuickVisualDataModelPrivate::get(d->model); - - int index = model->m_compositor.count(d->group); - Compositor::Group group = d->group; - - int i = 0; - v8::Local v = (*args)[i]; - if (d->parseIndex(v, &index, &group)) - ++i; - - if (i < args->Length() && index >= 0 && index <= model->m_compositor.count(group)) { - v = (*args)[i]; - if (v->IsObject()) { - int groups = 1 << d->group; - if (++i < args->Length()) - groups |= model->m_cacheMetaType->parseGroups((*args)[i]); - - Compositor::insert_iterator before = index < model->m_compositor.count(group) - ? model->m_compositor.findInsertPosition(group, index) - : model->m_compositor.end(); - - index = before.index[d->group]; - group = d->group; - - if (!model->insert(before, v->ToObject(), groups)) { - return; - } - } - } - if (index < 0 || index >= model->m_compositor.count(group)) { - qmlInfo(this) << tr("create: index out of range"); - return; - } - - QObject *object = model->object(group, index, false); - if (object) { - QVector inserts; - Compositor::iterator it = model->m_compositor.find(group, index); - model->m_compositor.setFlags(it, 1, d->group, Compositor::PersistedFlag, &inserts); - model->itemsInserted(inserts); - model->m_cache.at(it.cacheIndex)->releaseObject(); - } - - args->returnValue(args->engine()->newQObject(object)); - model->emitChanges(); -} - -/*! - \qmlmethod QtQuick2::VisualDataGroup::resolve(int from, int to) - - Binds an unresolved item at \a from to an item in VisualDataModel::model at index \a to. - - Unresolved items are entries whose data has been \l {insert()}{inserted} into a VisualDataGroup - instead of being derived from a VisualDataModel::model index. Resolving an item will replace - the item at the target index with the unresolved item. A resolved an item will reflect the data - of the source model at its bound index and will move when that index moves like any other item. - - If a new item is replaced in the VisualDataGroup onChanged() handler its insertion and - replacement will be communicated to views as an atomic operation, creating the appearance - that the model contents have not changed, or if the unresolved and model item are not adjacent - that the previously unresolved item has simply moved. - -*/ -void QQuickVisualDataGroup::resolve(QQmlV8Function *args) -{ - Q_D(QQuickVisualDataGroup); - if (!d->model) - return; - - QQuickVisualDataModelPrivate *model = QQuickVisualDataModelPrivate::get(d->model); - - if (args->Length() < 2) - return; - - int from = -1; - int to = -1; - Compositor::Group fromGroup = d->group; - Compositor::Group toGroup = d->group; - - v8::Local v = (*args)[0]; - if (d->parseIndex(v, &from, &fromGroup)) { - if (from < 0 || from >= model->m_compositor.count(fromGroup)) { - qmlInfo(this) << tr("resolve: from index out of range"); - return; - } - } else { - qmlInfo(this) << tr("resolve: from index invalid"); - return; - } - - v = (*args)[1]; - if (d->parseIndex(v, &to, &toGroup)) { - if (to < 0 || to >= model->m_compositor.count(toGroup)) { - qmlInfo(this) << tr("resolve: to index out of range"); - return; - } - } else { - qmlInfo(this) << tr("resolve: to index invalid"); - return; - } - - Compositor::iterator fromIt = model->m_compositor.find(fromGroup, from); - Compositor::iterator toIt = model->m_compositor.find(toGroup, to); - - if (!fromIt->isUnresolved()) { - qmlInfo(this) << tr("resolve: from is not an unresolved item"); - return; - } - if (!toIt->list) { - qmlInfo(this) << tr("resolve: to is not a model item"); - return; - } - - const int unresolvedFlags = fromIt->flags; - const int resolvedFlags = toIt->flags; - const int resolvedIndex = toIt.modelIndex(); - void * const resolvedList = toIt->list; - - QQuickVisualDataModelItem *cacheItem = model->m_cache.at(fromIt.cacheIndex); - cacheItem->groups &= ~Compositor::UnresolvedFlag; - - if (toIt.cacheIndex > fromIt.cacheIndex) - toIt.decrementIndexes(1, unresolvedFlags); - if (!toIt->inGroup(fromGroup) || toIt.index[fromGroup] > from) - from += 1; - - model->itemsMoved( - QVector() << Compositor::Remove(fromIt, 1, unresolvedFlags, 0), - QVector() << Compositor::Insert(toIt, 1, unresolvedFlags, 0)); - model->itemsInserted( - QVector() << Compositor::Insert(toIt, 1, (resolvedFlags & ~unresolvedFlags) | Compositor::CacheFlag)); - toIt.incrementIndexes(1, resolvedFlags | unresolvedFlags); - model->itemsRemoved(QVector() << Compositor::Remove(toIt, 1, resolvedFlags)); - - model->m_compositor.setFlags(toGroup, to, 1, unresolvedFlags & ~Compositor::UnresolvedFlag); - model->m_compositor.clearFlags(fromGroup, from, 1, unresolvedFlags); - - if (resolvedFlags & Compositor::CacheFlag) - model->m_compositor.insert(Compositor::Cache, toIt.cacheIndex, resolvedList, resolvedIndex, 1, Compositor::CacheFlag); - - Q_ASSERT(model->m_cache.count() == model->m_compositor.count(Compositor::Cache)); - - if (!cacheItem->isReferenced()) { - Q_ASSERT(toIt.cacheIndex == model->m_cache.indexOf(cacheItem)); - model->m_cache.removeAt(toIt.cacheIndex); - model->m_compositor.clearFlags(Compositor::Cache, toIt.cacheIndex, 1, Compositor::CacheFlag); - delete cacheItem; - Q_ASSERT(model->m_cache.count() == model->m_compositor.count(Compositor::Cache)); - } else { - cacheItem->resolveIndex(model->m_adaptorModel, resolvedIndex); - if (cacheItem->attached) - cacheItem->attached->emitUnresolvedChanged(); - } - - model->emitChanges(); -} - -/*! - \qmlmethod QtQuick2::VisualDataGroup::remove(int index, int count) - - Removes \a count items starting at \a index from the group. -*/ - -void QQuickVisualDataGroup::remove(QQmlV8Function *args) -{ - Q_D(QQuickVisualDataGroup); - if (!d->model) - return; - Compositor::Group group = d->group; - int index = -1; - int count = 1; - - if (args->Length() == 0) - return; - - int i = 0; - v8::Local v = (*args)[i]; - if (!d->parseIndex(v, &index, &group)) { - qmlInfo(this) << tr("remove: invalid index"); - return; - } - - if (++i < args->Length()) { - v = (*args)[i]; - if (v->IsInt32()) - count = v->Int32Value(); - } - - QQuickVisualDataModelPrivate *model = QQuickVisualDataModelPrivate::get(d->model); - if (index < 0 || index >= model->m_compositor.count(group)) { - qmlInfo(this) << tr("remove: index out of range"); - } else if (count != 0) { - Compositor::iterator it = model->m_compositor.find(group, index); - if (count < 0 || count > model->m_compositor.count(d->group) - it.index[d->group]) { - qmlInfo(this) << tr("remove: invalid count"); - } else { - model->removeGroups(it, count, d->group, 1 << d->group); - } - } -} - -bool QQuickVisualDataGroupPrivate::parseGroupArgs( - QQmlV8Function *args, Compositor::Group *group, int *index, int *count, int *groups) const -{ - if (!model || !QQuickVisualDataModelPrivate::get(model)->m_cacheMetaType) - return false; - - if (args->Length() < 2) - return false; - - int i = 0; - v8::Local v = (*args)[i]; - if (!parseIndex(v, index, group)) - return false; - - v = (*args)[++i]; - if (v->IsInt32()) { - *count = v->Int32Value(); - - if (++i == args->Length()) - return false; - v = (*args)[i]; - } - - *groups = QQuickVisualDataModelPrivate::get(model)->m_cacheMetaType->parseGroups(v); - - return true; -} - -/*! - \qmlmethod QtQuick2::VisualDataGroup::addGroups(int index, int count, stringlist groups) - - Adds \a count items starting at \a index to \a groups. -*/ - -void QQuickVisualDataGroup::addGroups(QQmlV8Function *args) -{ - Q_D(QQuickVisualDataGroup); - Compositor::Group group = d->group; - int index = -1; - int count = 1; - int groups = 0; - - if (!d->parseGroupArgs(args, &group, &index, &count, &groups)) - return; - - QQuickVisualDataModelPrivate *model = QQuickVisualDataModelPrivate::get(d->model); - if (index < 0 || index >= model->m_compositor.count(group)) { - qmlInfo(this) << tr("addGroups: index out of range"); - } else if (count != 0) { - Compositor::iterator it = model->m_compositor.find(group, index); - if (count < 0 || count > model->m_compositor.count(d->group) - it.index[d->group]) { - qmlInfo(this) << tr("addGroups: invalid count"); - } else { - model->addGroups(it, count, d->group, groups); - } - } -} - -/*! - \qmlmethod QtQuick2::VisualDataGroup::removeGroups(int index, int count, stringlist groups) - - Removes \a count items starting at \a index from \a groups. -*/ - -void QQuickVisualDataGroup::removeGroups(QQmlV8Function *args) -{ - Q_D(QQuickVisualDataGroup); - Compositor::Group group = d->group; - int index = -1; - int count = 1; - int groups = 0; - - if (!d->parseGroupArgs(args, &group, &index, &count, &groups)) - return; - - QQuickVisualDataModelPrivate *model = QQuickVisualDataModelPrivate::get(d->model); - if (index < 0 || index >= model->m_compositor.count(group)) { - qmlInfo(this) << tr("removeGroups: index out of range"); - } else if (count != 0) { - Compositor::iterator it = model->m_compositor.find(group, index); - if (count < 0 || count > model->m_compositor.count(d->group) - it.index[d->group]) { - qmlInfo(this) << tr("removeGroups: invalid count"); - } else { - model->removeGroups(it, count, d->group, groups); - } - } -} - -/*! - \qmlmethod QtQuick2::VisualDataGroup::setGroups(int index, int count, stringlist groups) - - Sets the \a groups \a count items starting at \a index belong to. -*/ - -void QQuickVisualDataGroup::setGroups(QQmlV8Function *args) -{ - Q_D(QQuickVisualDataGroup); - Compositor::Group group = d->group; - int index = -1; - int count = 1; - int groups = 0; - - if (!d->parseGroupArgs(args, &group, &index, &count, &groups)) - return; - - QQuickVisualDataModelPrivate *model = QQuickVisualDataModelPrivate::get(d->model); - if (index < 0 || index >= model->m_compositor.count(group)) { - qmlInfo(this) << tr("setGroups: index out of range"); - } else if (count != 0) { - Compositor::iterator it = model->m_compositor.find(group, index); - if (count < 0 || count > model->m_compositor.count(d->group) - it.index[d->group]) { - qmlInfo(this) << tr("setGroups: invalid count"); - } else { - model->setGroups(it, count, d->group, groups); - } - } -} - -/*! - \qmlmethod QtQuick2::VisualDataGroup::setGroups(int index, int count, stringlist groups) - - Sets the \a groups \a count items starting at \a index belong to. -*/ - -/*! - \qmlmethod QtQuick2::VisualDataGroup::move(var from, var to, int count) - - Moves \a count at \a from in a group \a to a new position. -*/ - -void QQuickVisualDataGroup::move(QQmlV8Function *args) -{ - Q_D(QQuickVisualDataGroup); - - if (args->Length() < 2) - return; - - Compositor::Group fromGroup = d->group; - Compositor::Group toGroup = d->group; - int from = -1; - int to = -1; - int count = 1; - - if (!d->parseIndex((*args)[0], &from, &fromGroup)) { - qmlInfo(this) << tr("move: invalid from index"); - return; - } - - if (!d->parseIndex((*args)[1], &to, &toGroup)) { - qmlInfo(this) << tr("move: invalid to index"); - return; - } - - if (args->Length() > 2) { - v8::Local v = (*args)[2]; - if (v->IsInt32()) - count = v->Int32Value(); - } - - QQuickVisualDataModelPrivate *model = QQuickVisualDataModelPrivate::get(d->model); - - if (count < 0) { - qmlInfo(this) << tr("move: invalid count"); - } else if (from < 0 || from + count > model->m_compositor.count(fromGroup)) { - qmlInfo(this) << tr("move: from index out of range"); - } else if (!model->m_compositor.verifyMoveTo(fromGroup, from, toGroup, to, count, d->group)) { - qmlInfo(this) << tr("move: to index out of range"); - } else if (count > 0) { - QVector removes; - QVector inserts; - - model->m_compositor.move(fromGroup, from, toGroup, to, count, d->group, &removes, &inserts); - model->itemsMoved(removes, inserts); - model->emitChanges(); - } - -} - -/*! - \qmlsignal QtQuick2::VisualDataGroup::onChanged(array removed, array inserted) - - This handler is called when items have been removed from or inserted into the group. - - Each object in the \a removed and \a inserted arrays has two values; the \e index of the first - item inserted or removed and a \e count of the number of consecutive items inserted or removed. - - Each index is adjusted for previous changes with all removed items preceding any inserted - items. -*/ - -//============================================================================ - -QQuickVisualPartsModel::QQuickVisualPartsModel(QQuickVisualDataModel *model, const QString &part, QObject *parent) - : QQuickVisualModel(*new QObjectPrivate, parent) - , m_model(model) - , m_part(part) - , m_compositorGroup(Compositor::Cache) - , m_inheritGroup(true) -{ - QQuickVisualDataModelPrivate *d = QQuickVisualDataModelPrivate::get(m_model); - if (d->m_cacheMetaType) { - QQuickVisualDataGroupPrivate::get(d->m_groups[1])->emitters.insert(this); - m_compositorGroup = Compositor::Default; - } else { - d->m_pendingParts.insert(this); - } -} - -QQuickVisualPartsModel::~QQuickVisualPartsModel() -{ -} - -QString QQuickVisualPartsModel::filterGroup() const -{ - if (m_inheritGroup) - return m_model->filterGroup(); - return m_filterGroup; -} - -void QQuickVisualPartsModel::setFilterGroup(const QString &group) -{ - if (QQuickVisualDataModelPrivate::get(m_model)->m_transaction) { - qmlInfo(this) << tr("The group of a VisualDataModel cannot be changed within onChanged"); - return; - } - - if (m_filterGroup != group || m_inheritGroup) { - m_filterGroup = group; - m_inheritGroup = false; - updateFilterGroup(); - - emit filterGroupChanged(); - } -} - -void QQuickVisualPartsModel::resetFilterGroup() -{ - if (!m_inheritGroup) { - m_inheritGroup = true; - updateFilterGroup(); - emit filterGroupChanged(); - } -} - -void QQuickVisualPartsModel::updateFilterGroup() -{ - QQuickVisualDataModelPrivate *model = QQuickVisualDataModelPrivate::get(m_model); - if (!model->m_cacheMetaType) - return; - - if (m_inheritGroup) { - if (m_filterGroup == model->m_filterGroup) - return; - m_filterGroup = model->m_filterGroup; - } - - QQuickListCompositor::Group previousGroup = m_compositorGroup; - m_compositorGroup = Compositor::Default; - QQuickVisualDataGroupPrivate::get(model->m_groups[Compositor::Default])->emitters.insert(this); - for (int i = 1; i < model->m_groupCount; ++i) { - if (m_filterGroup == model->m_cacheMetaType->groupNames.at(i - 1)) { - m_compositorGroup = Compositor::Group(i); - break; - } - } - - QQuickVisualDataGroupPrivate::get(model->m_groups[m_compositorGroup])->emitters.insert(this); - if (m_compositorGroup != previousGroup) { - QVector removes; - QVector inserts; - model->m_compositor.transition(previousGroup, m_compositorGroup, &removes, &inserts); - - QQuickChangeSet changeSet; - changeSet.move(removes, inserts); - if (!changeSet.isEmpty()) - emit modelUpdated(changeSet, false); - - if (changeSet.difference() != 0) - emit countChanged(); - } -} - -void QQuickVisualPartsModel::updateFilterGroup( - Compositor::Group group, const QQuickChangeSet &changeSet) -{ - if (!m_inheritGroup) - return; - - m_compositorGroup = group; - QQuickVisualDataGroupPrivate::get(QQuickVisualDataModelPrivate::get(m_model)->m_groups[m_compositorGroup])->emitters.insert(this); - - if (!changeSet.isEmpty()) - emit modelUpdated(changeSet, false); - - if (changeSet.difference() != 0) - emit countChanged(); - - emit filterGroupChanged(); -} - -int QQuickVisualPartsModel::count() const -{ - QQuickVisualDataModelPrivate *model = QQuickVisualDataModelPrivate::get(m_model); - return model->m_delegate - ? model->m_compositor.count(m_compositorGroup) - : 0; -} - -bool QQuickVisualPartsModel::isValid() const -{ - return m_model->isValid(); -} - -QQuickItem *QQuickVisualPartsModel::item(int index, bool asynchronous) -{ - QQuickVisualDataModelPrivate *model = QQuickVisualDataModelPrivate::get(m_model); - - if (!model->m_delegate || index < 0 || index >= model->m_compositor.count(m_compositorGroup)) { - qWarning() << "VisualDataModel::item: index out range" << index << model->m_compositor.count(m_compositorGroup); - return 0; - } - - QObject *object = model->object(m_compositorGroup, index, asynchronous); - - if (QQuickPackage *package = qmlobject_cast(object)) { - QObject *part = package->part(m_part); - if (!part) - return 0; - if (QQuickItem *item = qmlobject_cast(part)) { - m_packaged.insertMulti(item, package); - return item; - } - } - - model->release(object); - if (!model->m_delegateValidated) { - if (object) - qmlInfo(model->m_delegate) << tr("Delegate component must be Package type."); - model->m_delegateValidated = true; - } - - return 0; -} - -QQuickVisualModel::ReleaseFlags QQuickVisualPartsModel::release(QQuickItem *item) -{ - QQuickVisualModel::ReleaseFlags flags = 0; - - QHash::iterator it = m_packaged.find(item); - if (it != m_packaged.end()) { - QQuickPackage *package = *it; - QQuickVisualDataModelPrivate *model = QQuickVisualDataModelPrivate::get(m_model); - flags = model->release(package); - m_packaged.erase(it); - if (!m_packaged.contains(item)) - flags &= ~Referenced; - if (flags & Destroyed) - QQuickVisualDataModelPrivate::get(m_model)->emitDestroyingPackage(package); - } - return flags; -} - -QString QQuickVisualPartsModel::stringValue(int index, const QString &role) -{ - return QQuickVisualDataModelPrivate::get(m_model)->stringValue(m_compositorGroup, index, role); -} - -void QQuickVisualPartsModel::setWatchedRoles(QList roles) -{ - QQuickVisualDataModelPrivate *model = QQuickVisualDataModelPrivate::get(m_model); - model->m_adaptorModel.replaceWatchedRoles(m_watchedRoles, roles); - m_watchedRoles = roles; -} - -int QQuickVisualPartsModel::indexOf(QQuickItem *item, QObject *) const -{ - QHash::const_iterator it = m_packaged.find(item); - if (it != m_packaged.end()) { - if (QQuickVisualDataModelItem *cacheItem = QQuickVisualDataModelItem::dataForObject(*it)) - return cacheItem->groupIndex(m_compositorGroup); - } - return -1; -} - -void QQuickVisualPartsModel::createdPackage(int index, QQuickPackage *package) -{ - if (QQuickItem *item = qmlobject_cast(package->part(m_part))) - emit createdItem(index, item); -} - -void QQuickVisualPartsModel::initPackage(int index, QQuickPackage *package) -{ - if (QQuickItem *item = qmlobject_cast(package->part(m_part))) - emit initItem(index, item); -} - -void QQuickVisualPartsModel::destroyingPackage(QQuickPackage *package) -{ - if (QQuickItem *item = qmlobject_cast(package->part(m_part))) { - Q_ASSERT(!m_packaged.contains(item)); - emit destroyingItem(item); - item->setParentItem(0); - } -} - -void QQuickVisualPartsModel::emitModelUpdated(const QQuickChangeSet &changeSet, bool reset) -{ - emit modelUpdated(changeSet, reset); - if (changeSet.difference() != 0) - emit countChanged(); -} - -//============================================================================ - -v8::Handle get_change_index(v8::Local, const v8::AccessorInfo &info) -{ - return info.This()->GetInternalField(0); -} - -v8::Handle get_change_count(v8::Local, const v8::AccessorInfo &info) -{ - return info.This()->GetInternalField(1); -} - -v8::Handle get_change_moveId(v8::Local, const v8::AccessorInfo &info) -{ - return info.This()->GetInternalField(2); -} - -class QQuickVisualDataGroupChangeArray : public QV8ObjectResource -{ - V8_RESOURCE_TYPE(ChangeSetArrayType) -public: - QQuickVisualDataGroupChangeArray(QV8Engine *engine) - : QV8ObjectResource(engine) - { - } - - virtual quint32 count() const = 0; - virtual const QQuickChangeSet::Change &at(int index) const = 0; - - static v8::Handle get_change(quint32 index, const v8::AccessorInfo &info) - { - QQuickVisualDataGroupChangeArray *array = v8_resource_cast(info.This()); - V8ASSERT_TYPE(array, "Not a valid change array"); - - if (index >= array->count()) - return v8::Undefined(); - - const QQuickChangeSet::Change &change = array->at(index); - - v8::Local object = engineData(array->engine)->constructorChange->NewInstance(); - object->SetInternalField(0, v8::Int32::New(change.index)); - object->SetInternalField(1, v8::Int32::New(change.count)); - if (change.isMove()) - object->SetInternalField(2, v8::Int32::New(change.moveId)); - - return object; - } - - static v8::Handle get_length(v8::Local, const v8::AccessorInfo &info) - { - QQuickVisualDataGroupChangeArray *array = v8_resource_cast(info.This()); - V8ASSERT_TYPE(array, "Not a valid change array"); - - return v8::Integer::New(array->count()); - } - - static v8::Local constructor() - { - v8::Local changeArray = v8::FunctionTemplate::New(); - changeArray->InstanceTemplate()->SetHasExternalResource(true); - changeArray->InstanceTemplate()->SetIndexedPropertyHandler(get_change); - changeArray->InstanceTemplate()->SetAccessor(v8::String::New("length"), get_length); - return changeArray->GetFunction(); - } -}; - -class QQuickVisualDataGroupRemoveArray : public QQuickVisualDataGroupChangeArray -{ -public: - QQuickVisualDataGroupRemoveArray(QV8Engine *engine, const QVector &changes) - : QQuickVisualDataGroupChangeArray(engine) - , changes(changes) - { - } - - quint32 count() const { return changes.count(); } - const QQuickChangeSet::Change &at(int index) const { return changes.at(index); } - -private: - QVector changes; -}; - -class QQuickVisualDataGroupInsertArray : public QQuickVisualDataGroupChangeArray -{ -public: - QQuickVisualDataGroupInsertArray(QV8Engine *engine, const QVector &changes) - : QQuickVisualDataGroupChangeArray(engine) - , changes(changes) - { - } - - quint32 count() const { return changes.count(); } - const QQuickChangeSet::Change &at(int index) const { return changes.at(index); } - -private: - QVector changes; -}; - -QQuickVisualDataModelEngineData::QQuickVisualDataModelEngineData(QV8Engine *) -{ - strings = qPersistentNew(v8::Array::New(StringCount)); - strings->Set(Model, v8::String::New("model")); - strings->Set(Groups, v8::String::New("groups")); - strings->Set(IsUnresolved, v8::String::New("isUnresolved")); - strings->Set(ItemsIndex, v8::String::New("itemsIndex")); - strings->Set(PersistedItemsIndex, v8::String::New("persistedItemsIndex")); - strings->Set(InItems, v8::String::New("inItems")); - strings->Set(InPersistedItems, v8::String::New("inPersistedItems")); - - v8::Local change = v8::FunctionTemplate::New(); - change->InstanceTemplate()->SetAccessor(v8::String::New("index"), get_change_index); - change->InstanceTemplate()->SetAccessor(v8::String::New("count"), get_change_count); - change->InstanceTemplate()->SetAccessor(v8::String::New("moveId"), get_change_moveId); - change->InstanceTemplate()->SetInternalFieldCount(3); - constructorChange = qPersistentNew(change->GetFunction()); - constructorChangeArray = qPersistentNew(QQuickVisualDataGroupChangeArray::constructor()); -} - -QQuickVisualDataModelEngineData::~QQuickVisualDataModelEngineData() -{ - qPersistentDispose(strings); - qPersistentDispose(constructorChange); - qPersistentDispose(constructorChangeArray); -} - -v8::Local QQuickVisualDataModelEngineData::array( - QV8Engine *engine, const QVector &changes) -{ - v8::Local array = constructorChangeArray->NewInstance(); - array->SetExternalResource(new QQuickVisualDataGroupRemoveArray(engine, changes)); - return array; -} - -v8::Local QQuickVisualDataModelEngineData::array( - QV8Engine *engine, const QVector &changes) -{ - v8::Local array = constructorChangeArray->NewInstance(); - array->SetExternalResource(new QQuickVisualDataGroupInsertArray(engine, changes)); - return array; -} - -QT_END_NAMESPACE - diff --git a/src/quick/items/qquickvisualdatamodel_p.h b/src/quick/items/qquickvisualdatamodel_p.h deleted file mode 100644 index 03e06c82ac..0000000000 --- a/src/quick/items/qquickvisualdatamodel_p.h +++ /dev/null @@ -1,238 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** 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, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQUICKVISUALDATAMODEL_P_H -#define QQUICKVISUALDATAMODEL_P_H - -#include -#include -#include - -#include -#include - -#include -#include - -QT_BEGIN_HEADER - -Q_DECLARE_METATYPE(QModelIndex) - -QT_BEGIN_NAMESPACE - -class QQuickChangeSet; -class QQmlComponent; -class QQuickPackage; -class QQmlV8Function; -class QQuickVisualDataGroup; -class QQuickVisualDataModelAttached; -class QQuickVisualDataModelPrivate; - - -class Q_QUICK_PRIVATE_EXPORT QQuickVisualDataModel : public QQuickVisualModel, public QQmlParserStatus -{ - Q_OBJECT - Q_DECLARE_PRIVATE(QQuickVisualDataModel) - - Q_PROPERTY(QVariant model READ model WRITE setModel) - Q_PROPERTY(QQmlComponent *delegate READ delegate WRITE setDelegate) - Q_PROPERTY(QString filterOnGroup READ filterGroup WRITE setFilterGroup NOTIFY filterGroupChanged RESET resetFilterGroup) - Q_PROPERTY(QQuickVisualDataGroup *items READ items CONSTANT) - Q_PROPERTY(QQuickVisualDataGroup *persistedItems READ persistedItems CONSTANT) - Q_PROPERTY(QQmlListProperty groups READ groups CONSTANT) - Q_PROPERTY(QObject *parts READ parts CONSTANT) - Q_PROPERTY(QVariant rootIndex READ rootIndex WRITE setRootIndex NOTIFY rootIndexChanged) - Q_CLASSINFO("DefaultProperty", "delegate") - Q_INTERFACES(QQmlParserStatus) -public: - QQuickVisualDataModel(); - QQuickVisualDataModel(QQmlContext *, QObject *parent=0); - virtual ~QQuickVisualDataModel(); - - void classBegin(); - void componentComplete(); - - QVariant model() const; - void setModel(const QVariant &); - - QQmlComponent *delegate() const; - void setDelegate(QQmlComponent *); - - QVariant rootIndex() const; - void setRootIndex(const QVariant &root); - - Q_INVOKABLE QVariant modelIndex(int idx) const; - Q_INVOKABLE QVariant parentModelIndex() const; - - int count() const; - bool isValid() const { return delegate() != 0; } - QQuickItem *item(int index, bool asynchronous=false); - ReleaseFlags release(QQuickItem *item); - void cancel(int index); - virtual QString stringValue(int index, const QString &role); - virtual void setWatchedRoles(QList roles); - - int indexOf(QQuickItem *item, QObject *objectContext) const; - - QString filterGroup() const; - void setFilterGroup(const QString &group); - void resetFilterGroup(); - - QQuickVisualDataGroup *items(); - QQuickVisualDataGroup *persistedItems(); - QQmlListProperty groups(); - QObject *parts(); - - bool event(QEvent *); - - static QQuickVisualDataModelAttached *qmlAttachedProperties(QObject *obj); - -Q_SIGNALS: - void filterGroupChanged(); - void defaultGroupsChanged(); - void rootIndexChanged(); - -private Q_SLOTS: - void _q_itemsChanged(int index, int count, const QVector &roles); - void _q_itemsInserted(int index, int count); - void _q_itemsRemoved(int index, int count); - void _q_itemsMoved(int from, int to, int count); - void _q_modelReset(); - void _q_rowsInserted(const QModelIndex &,int,int); - void _q_rowsAboutToBeRemoved(const QModelIndex &parent, int begin, int end); - void _q_rowsRemoved(const QModelIndex &,int,int); - void _q_rowsMoved(const QModelIndex &, int, int, const QModelIndex &, int); - void _q_dataChanged(const QModelIndex&,const QModelIndex&,const QVector &); - void _q_layoutChanged(); - -private: - Q_DISABLE_COPY(QQuickVisualDataModel) -}; - -class QQuickVisualDataGroupPrivate; -class Q_AUTOTEST_EXPORT QQuickVisualDataGroup : public QObject -{ - Q_OBJECT - Q_PROPERTY(int count READ count NOTIFY countChanged) - Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) - Q_PROPERTY(bool includeByDefault READ defaultInclude WRITE setDefaultInclude NOTIFY defaultIncludeChanged) -public: - QQuickVisualDataGroup(QObject *parent = 0); - QQuickVisualDataGroup(const QString &name, QQuickVisualDataModel *model, int compositorType, QObject *parent = 0); - ~QQuickVisualDataGroup(); - - QString name() const; - void setName(const QString &name); - - int count() const; - - bool defaultInclude() const; - void setDefaultInclude(bool include); - - Q_INVOKABLE QQmlV8Handle get(int index); - -public Q_SLOTS: - void insert(QQmlV8Function *); - void create(QQmlV8Function *); - void resolve(QQmlV8Function *); - void remove(QQmlV8Function *); - void addGroups(QQmlV8Function *); - void removeGroups(QQmlV8Function *); - void setGroups(QQmlV8Function *); - void move(QQmlV8Function *); - -Q_SIGNALS: - void countChanged(); - void nameChanged(); - void defaultIncludeChanged(); - void changed(const QQmlV8Handle &removed, const QQmlV8Handle &inserted); -private: - Q_DECLARE_PRIVATE(QQuickVisualDataGroup) -}; - -class QQuickVisualDataModelItem; -class QQuickVisualDataModelAttachedMetaObject; -class QQuickVisualDataModelAttached : public QObject -{ - Q_OBJECT - Q_PROPERTY(QQuickVisualDataModel *model READ model CONSTANT) - Q_PROPERTY(QStringList groups READ groups WRITE setGroups NOTIFY groupsChanged) - Q_PROPERTY(bool isUnresolved READ isUnresolved NOTIFY unresolvedChanged) -public: - QQuickVisualDataModelAttached(QObject *parent); - QQuickVisualDataModelAttached(QQuickVisualDataModelItem *cacheItem, QObject *parent); - ~QQuickVisualDataModelAttached() {} - - void setCacheItem(QQuickVisualDataModelItem *item); - - QQuickVisualDataModel *model() const; - - QStringList groups() const; - void setGroups(const QStringList &groups); - - bool isUnresolved() const; - - void emitChanges(); - - void emitUnresolvedChanged() { emit unresolvedChanged(); } - -Q_SIGNALS: - void groupsChanged(); - void unresolvedChanged(); - -public: - QQuickVisualDataModelItem *m_cacheItem; - int m_previousGroups; - int m_currentIndex[QQuickListCompositor::MaximumGroupCount]; - int m_previousIndex[QQuickListCompositor::MaximumGroupCount]; - - friend class QQuickVisualDataModelAttachedMetaObject; -}; - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QQuickVisualDataModel) -QML_DECLARE_TYPEINFO(QQuickVisualDataModel, QML_HAS_ATTACHED_PROPERTIES) -QML_DECLARE_TYPE(QQuickVisualDataGroup) - -QT_END_HEADER - -#endif // QQUICKVISUALDATAMODEL_P_H diff --git a/src/quick/items/qquickvisualdatamodel_p_p.h b/src/quick/items/qquickvisualdatamodel_p_p.h deleted file mode 100644 index 89ecece064..0000000000 --- a/src/quick/items/qquickvisualdatamodel_p_p.h +++ /dev/null @@ -1,411 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** 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, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQUICKVISUALDATAMODEL_P_P_H -#define QQUICKVISUALDATAMODEL_P_P_H - -#include "qquickvisualdatamodel_p.h" - -#include "qquickvisualadaptormodel_p.h" - -#include -#include - -#include - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -QT_BEGIN_NAMESPACE - -typedef QQuickListCompositor Compositor; - -class QQuickVisualDataModelAttachedMetaObject; - -class QQuickVisualDataModelItemMetaType : public QQmlRefCount -{ -public: - QQuickVisualDataModelItemMetaType(QV8Engine *engine, QQuickVisualDataModel *model, const QStringList &groupNames); - ~QQuickVisualDataModelItemMetaType(); - - void initializeMetaObject(); - void initializeConstructor(); - - int parseGroups(const QStringList &groupNames) const; - int parseGroups(const v8::Local &groupNames) const; - - static void release_index(v8::Persistent object, void *parameter); - static void release_model(v8::Persistent object, void *parameter); - - static v8::Handle get_model(v8::Local, const v8::AccessorInfo &info); - static v8::Handle get_groups(v8::Local, const v8::AccessorInfo &info); - static void set_groups( - v8::Local, v8::Local value, const v8::AccessorInfo &info); - static v8::Handle get_member(v8::Local, const v8::AccessorInfo &info); - static void set_member( - v8::Local, v8::Local value, const v8::AccessorInfo &info); - static v8::Handle get_index(v8::Local, const v8::AccessorInfo &info); - - QQmlGuard model; - const int groupCount; - QV8Engine * const v8Engine; - QQuickVisualDataModelAttachedMetaObject *metaObject; - const QStringList groupNames; - v8::Persistent constructor; -}; - -class QQuickVisualAdaptorModel; -class QVDMIncubationTask; - -class QQuickVisualDataModelItem : public QObject, public QV8ObjectResource -{ - Q_OBJECT - Q_PROPERTY(int index READ modelIndex NOTIFY modelIndexChanged) - Q_PROPERTY(QObject *model READ modelObject CONSTANT) - V8_RESOURCE_TYPE(VisualDataItemType) -public: - QQuickVisualDataModelItem(QQuickVisualDataModelItemMetaType *metaType, int modelIndex); - ~QQuickVisualDataModelItem(); - - void referenceObject() { ++objectRef; } - bool releaseObject() { return --objectRef == 0 && !(groups & Compositor::PersistedFlag); } - bool isObjectReferenced() const { return objectRef != 0 || (groups & Compositor::PersistedFlag); } - - bool isReferenced() const { - return scriptRef - || incubationTask - || ((groups & Compositor::UnresolvedFlag) && (groups & Compositor::GroupMask)); - } - - void Dispose(); - - QObject *modelObject() { return this; } - - void incubateObject( - QQmlComponent *component, - QQmlEngine *engine, - QQmlContextData *context, - QQmlContextData *forContext); - void destroyObject(); - - static QQuickVisualDataModelItem *dataForObject(QObject *object); - - int groupIndex(Compositor::Group group); - - int modelIndex() const { return index; } - void setModelIndex(int idx) { index = idx; emit modelIndexChanged(); } - - virtual v8::Handle get() { return engine->newQObject(this); } - - virtual void setValue(const QString &role, const QVariant &value) { Q_UNUSED(role); Q_UNUSED(value); } - virtual bool resolveIndex(const QQuickVisualAdaptorModel &, int) { return false; } - - QQuickVisualDataModelItemMetaType * const metaType; - QQmlContextData *contextData; - QObject *object; - QQuickVisualDataModelAttached *attached; - QVDMIncubationTask *incubationTask; - int objectRef; - int scriptRef; - int groups; - int index; - - -Q_SIGNALS: - void modelIndexChanged(); - -protected: - void objectDestroyed(QObject *); -}; - - -class QQuickVisualDataModelPrivate; -class QVDMIncubationTask : public QQmlIncubator -{ -public: - QVDMIncubationTask(QQuickVisualDataModelPrivate *l, IncubationMode mode) - : QQmlIncubator(mode) - , incubating(0) - , vdm(l) {} - - virtual void statusChanged(Status); - virtual void setInitialState(QObject *); - - QQuickVisualDataModelItem *incubating; - QQuickVisualDataModelPrivate *vdm; - int index[QQuickListCompositor::MaximumGroupCount]; -}; - - -class QQuickVisualDataGroupEmitter -{ -public: - virtual ~QQuickVisualDataGroupEmitter() {} - virtual void emitModelUpdated(const QQuickChangeSet &changeSet, bool reset) = 0; - virtual void createdPackage(int, QQuickPackage *) {} - virtual void initPackage(int, QQuickPackage *) {} - virtual void destroyingPackage(QQuickPackage *) {} - - QIntrusiveListNode emitterNode; -}; - -typedef QIntrusiveList QQuickVisualDataGroupEmitterList; - -class QQuickVisualDataGroupPrivate : public QObjectPrivate -{ -public: - Q_DECLARE_PUBLIC(QQuickVisualDataGroup) - - QQuickVisualDataGroupPrivate() : group(Compositor::Cache), defaultInclude(false) {} - - static QQuickVisualDataGroupPrivate *get(QQuickVisualDataGroup *group) { - return static_cast(QObjectPrivate::get(group)); } - - void setModel(QQuickVisualDataModel *model, Compositor::Group group); - bool isChangedConnected(); - void emitChanges(QV8Engine *engine); - void emitModelUpdated(bool reset); - - void createdPackage(int index, QQuickPackage *package); - void initPackage(int index, QQuickPackage *package); - void destroyingPackage(QQuickPackage *package); - - bool parseIndex(const v8::Local &value, int *index, Compositor::Group *group) const; - bool parseGroupArgs( - QQmlV8Function *args, Compositor::Group *group, int *index, int *count, int *groups) const; - - Compositor::Group group; - QQmlGuard model; - QQuickVisualDataGroupEmitterList emitters; - QQuickChangeSet changeSet; - QString name; - bool defaultInclude; -}; - -class QQuickVisualDataModelParts; - -class QQuickVisualDataModelPrivate : public QObjectPrivate, public QQuickVisualDataGroupEmitter -{ - Q_DECLARE_PUBLIC(QQuickVisualDataModel) -public: - QQuickVisualDataModelPrivate(QQmlContext *); - ~QQuickVisualDataModelPrivate(); - - static QQuickVisualDataModelPrivate *get(QQuickVisualDataModel *m) { - return static_cast(QObjectPrivate::get(m)); - } - - void init(); - void connectModel(QQuickVisualAdaptorModel *model); - - QObject *object(Compositor::Group group, int index, bool asynchronous); - QQuickVisualDataModel::ReleaseFlags release(QObject *object); - QString stringValue(Compositor::Group group, int index, const QString &name); - void emitCreatedPackage(QVDMIncubationTask *incubationTask, QQuickPackage *package); - void emitInitPackage(QVDMIncubationTask *incubationTask, QQuickPackage *package); - void emitCreatedItem(QVDMIncubationTask *incubationTask, QQuickItem *item) { - emit q_func()->createdItem(incubationTask->index[m_compositorGroup], item); } - void emitInitItem(QVDMIncubationTask *incubationTask, QQuickItem *item) { - emit q_func()->initItem(incubationTask->index[m_compositorGroup], item); } - void emitDestroyingPackage(QQuickPackage *package); - void emitDestroyingItem(QQuickItem *item) { emit q_func()->destroyingItem(item); } - void removeCacheItem(QQuickVisualDataModelItem *cacheItem); - - void updateFilterGroup(); - - void addGroups(Compositor::iterator from, int count, Compositor::Group group, int groupFlags); - void removeGroups(Compositor::iterator from, int count, Compositor::Group group, int groupFlags); - void setGroups(Compositor::iterator from, int count, Compositor::Group group, int groupFlags); - - void itemsInserted( - const QVector &inserts, - QVarLengthArray, Compositor::MaximumGroupCount> *translatedInserts, - QHash > *movedItems = 0); - void itemsInserted(const QVector &inserts); - void itemsRemoved( - const QVector &removes, - QVarLengthArray, Compositor::MaximumGroupCount> *translatedRemoves, - QHash > *movedItems = 0); - void itemsRemoved(const QVector &removes); - void itemsMoved( - const QVector &removes, const QVector &inserts); - void itemsChanged(const QVector &changes); - template static v8::Local buildChangeList(const QVector &changes); - void emitChanges(); - void emitModelUpdated(const QQuickChangeSet &changeSet, bool reset); - - bool insert(Compositor::insert_iterator &before, const v8::Local &object, int groups); - - static void group_append(QQmlListProperty *property, QQuickVisualDataGroup *group); - static int group_count(QQmlListProperty *property); - static QQuickVisualDataGroup *group_at(QQmlListProperty *property, int index); - - void releaseIncubator(QVDMIncubationTask *incubationTask); - void incubatorStatusChanged(QVDMIncubationTask *incubationTask, QQmlIncubator::Status status); - void setInitialState(QVDMIncubationTask *incubationTask, QObject *o); - - QQuickVisualAdaptorModel m_adaptorModel; - QQuickListCompositor m_compositor; - QQmlComponent *m_delegate; - QQuickVisualDataModelItemMetaType *m_cacheMetaType; - QQmlContext *m_context; - QQuickVisualDataModelParts *m_parts; - QQuickVisualDataGroupEmitterList m_pendingParts; - - QList m_cache; - QList m_finishedIncubating; - QList m_watchedRoles; - - QString m_filterGroup; - - int m_count; - int m_groupCount; - - QQuickListCompositor::Group m_compositorGroup; - bool m_complete : 1; - bool m_delegateValidated : 1; - bool m_reset : 1; - bool m_transaction : 1; - bool m_incubatorCleanupScheduled : 1; - - union { - struct { - QQuickVisualDataGroup *m_cacheItems; - QQuickVisualDataGroup *m_items; - QQuickVisualDataGroup *m_persistedItems; - }; - QQuickVisualDataGroup *m_groups[Compositor::MaximumGroupCount]; - }; -}; - -class QQuickVisualPartsModel : public QQuickVisualModel, public QQuickVisualDataGroupEmitter -{ - Q_OBJECT - Q_PROPERTY(QString filterOnGroup READ filterGroup WRITE setFilterGroup NOTIFY filterGroupChanged RESET resetFilterGroup) -public: - QQuickVisualPartsModel(QQuickVisualDataModel *model, const QString &part, QObject *parent = 0); - ~QQuickVisualPartsModel(); - - QString filterGroup() const; - void setFilterGroup(const QString &group); - void resetFilterGroup(); - void updateFilterGroup(); - void updateFilterGroup(Compositor::Group group, const QQuickChangeSet &changeSet); - - int count() const; - bool isValid() const; - QQuickItem *item(int index, bool asynchronous=false); - ReleaseFlags release(QQuickItem *item); - QString stringValue(int index, const QString &role); - QList watchedRoles() const { return m_watchedRoles; } - void setWatchedRoles(QList roles); - - int indexOf(QQuickItem *item, QObject *objectContext) const; - - void emitModelUpdated(const QQuickChangeSet &changeSet, bool reset); - - void createdPackage(int index, QQuickPackage *package); - void initPackage(int index, QQuickPackage *package); - void destroyingPackage(QQuickPackage *package); - -Q_SIGNALS: - void filterGroupChanged(); - -private: - QQuickVisualDataModel *m_model; - QHash m_packaged; - QString m_part; - QString m_filterGroup; - QList m_watchedRoles; - Compositor::Group m_compositorGroup; - bool m_inheritGroup; -}; - -class QMetaPropertyBuilder; - -class QQuickVisualDataModelPartsMetaObject : public QQmlOpenMetaObject -{ -public: - QQuickVisualDataModelPartsMetaObject(QObject *parent) - : QQmlOpenMetaObject(parent) {} - - virtual void propertyCreated(int, QMetaPropertyBuilder &); - virtual QVariant initialValue(int); -}; - -class QQuickVisualDataModelParts : public QObject -{ -Q_OBJECT -public: - QQuickVisualDataModelParts(QQuickVisualDataModel *parent); - - QQuickVisualDataModel *model; - QList models; -}; - -class QQuickVisualDataModelAttachedMetaObject : public QAbstractDynamicMetaObject, public QQmlRefCount -{ -public: - QQuickVisualDataModelAttachedMetaObject( - QQuickVisualDataModelItemMetaType *metaType, QMetaObject *metaObject); - ~QQuickVisualDataModelAttachedMetaObject(); - - void objectDestroyed(QObject *); - int metaCall(QObject *, QMetaObject::Call, int _id, void **); - -private: - QQuickVisualDataModelItemMetaType * const metaType; - QMetaObject * const metaObject; - const int memberPropertyOffset; - const int indexPropertyOffset; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/quick/items/qquickvisualitemmodel.cpp b/src/quick/items/qquickvisualitemmodel.cpp deleted file mode 100644 index bdc489e2cf..0000000000 --- a/src/quick/items/qquickvisualitemmodel.cpp +++ /dev/null @@ -1,256 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** 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, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qquickvisualitemmodel_p.h" -#include "qquickitem.h" - -#include -#include -#include - -#include -#include -#include - -#include -#include - -QT_BEGIN_NAMESPACE - -QHash QQuickVisualItemModelAttached::attachedProperties; - - -class QQuickVisualItemModelPrivate : public QObjectPrivate -{ - Q_DECLARE_PUBLIC(QQuickVisualItemModel) -public: - class Item { - public: - Item(QQuickItem *i) : item(i), ref(0) {} - - void addRef() { ++ref; } - bool deref() { return --ref == 0; } - - QQuickItem *item; - int ref; - }; - - QQuickVisualItemModelPrivate() : QObjectPrivate() {} - - static void children_append(QQmlListProperty *prop, QQuickItem *item) { - static_cast(prop->data)->children.append(Item(item)); - static_cast(prop->data)->itemAppended(); - static_cast(prop->data)->emitChildrenChanged(); - } - - static int children_count(QQmlListProperty *prop) { - return static_cast(prop->data)->children.count(); - } - - static QQuickItem *children_at(QQmlListProperty *prop, int index) { - return static_cast(prop->data)->children.at(index).item; - } - - static void children_clear(QQmlListProperty *prop) { - static_cast(prop->data)->itemCleared(static_cast(prop->data)->children); - static_cast(prop->data)->children.clear(); - static_cast(prop->data)->emitChildrenChanged(); - } - - void itemAppended() { - Q_Q(QQuickVisualItemModel); - QQuickVisualItemModelAttached *attached = QQuickVisualItemModelAttached::properties(children.last().item); - attached->setIndex(children.count()-1); - QQuickChangeSet changeSet; - changeSet.insert(children.count() - 1, 1); - emit q->modelUpdated(changeSet, false); - emit q->countChanged(); - } - - void itemCleared(const QList &children) { - Q_Q(QQuickVisualItemModel); - foreach (const Item &child, children) - emit q->destroyingItem(child.item); - emit q->countChanged(); - } - - void emitChildrenChanged() { - Q_Q(QQuickVisualItemModel); - emit q->childrenChanged(); - } - - int indexOf(QQuickItem *item) const { - for (int i = 0; i < children.count(); ++i) - if (children.at(i).item == item) - return i; - return -1; - } - - - QList children; -}; - - -/*! - \qmltype VisualItemModel - \instantiates QQuickVisualItemModel - \inqmlmodule QtQuick 2 - \ingroup qtquick-models - \brief Defines items to be used added to a view - - A VisualItemModel contains the visual items to be used in a view. - When a VisualItemModel is used in a view, the view does not require - a delegate since the VisualItemModel already contains the visual - delegate (items). - - An item can determine its index within the - model via the \l{VisualItemModel::index}{index} attached property. - - The example below places three colored rectangles in a ListView. - \code - import QtQuick 2.0 - - Rectangle { - VisualItemModel { - id: itemModel - Rectangle { height: 30; width: 80; color: "red" } - Rectangle { height: 30; width: 80; color: "green" } - Rectangle { height: 30; width: 80; color: "blue" } - } - - ListView { - anchors.fill: parent - model: itemModel - } - } - \endcode - - \image visualitemmodel.png - - \sa {quick/modelviews/visualitemmodel}{VisualItemModel example} -*/ -QQuickVisualItemModel::QQuickVisualItemModel(QObject *parent) - : QQuickVisualModel(*(new QQuickVisualItemModelPrivate), parent) -{ -} - -/*! - \qmlattachedproperty int QtQuick2::VisualItemModel::index - This attached property holds the index of this delegate's item within the model. - - It is attached to each instance of the delegate. -*/ - -QQmlListProperty QQuickVisualItemModel::children() -{ - Q_D(QQuickVisualItemModel); - return QQmlListProperty(this, - d, - d->children_append, - d->children_count, - d->children_at, - d->children_clear); -} - -/*! - \qmlproperty int QtQuick2::VisualItemModel::count - - The number of items in the model. This property is readonly. -*/ -int QQuickVisualItemModel::count() const -{ - Q_D(const QQuickVisualItemModel); - return d->children.count(); -} - -bool QQuickVisualItemModel::isValid() const -{ - return true; -} - -QQuickItem *QQuickVisualItemModel::item(int index, bool) -{ - Q_D(QQuickVisualItemModel); - QQuickVisualItemModelPrivate::Item &item = d->children[index]; - item.addRef(); - if (item.ref == 1) { - emit initItem(index, item.item); - emit createdItem(index, item.item); - } - return item.item; -} - -QQuickVisualModel::ReleaseFlags QQuickVisualItemModel::release(QQuickItem *item) -{ - Q_D(QQuickVisualItemModel); - int idx = d->indexOf(item); - if (idx >= 0) { - if (d->children[idx].deref()) { - // XXX todo - the original did item->scene()->removeItem(). Why? - item->setParentItem(0); - } else { - return QQuickVisualModel::Referenced; - } - } - return 0; -} - -QString QQuickVisualItemModel::stringValue(int index, const QString &name) -{ - Q_D(QQuickVisualItemModel); - if (index < 0 || index >= d->children.count()) - return QString(); - return QQmlEngine::contextForObject(d->children.at(index).item)->contextProperty(name).toString(); -} - -int QQuickVisualItemModel::indexOf(QQuickItem *item, QObject *) const -{ - Q_D(const QQuickVisualItemModel); - return d->indexOf(item); -} - -QQuickVisualItemModelAttached *QQuickVisualItemModel::qmlAttachedProperties(QObject *obj) -{ - return QQuickVisualItemModelAttached::properties(obj); -} - -QT_END_NAMESPACE - diff --git a/src/quick/items/qquickvisualitemmodel_p.h b/src/quick/items/qquickvisualitemmodel_p.h deleted file mode 100644 index a5d50f9d38..0000000000 --- a/src/quick/items/qquickvisualitemmodel_p.h +++ /dev/null @@ -1,174 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** 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, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQUICKVISUALITEMMODEL_P_H -#define QQUICKVISUALITEMMODEL_P_H - -#include -#include -#include - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -class QQuickItem; -class QQuickChangeSet; - -class Q_QUICK_PRIVATE_EXPORT QQuickVisualModel : public QObject -{ - Q_OBJECT - - Q_PROPERTY(int count READ count NOTIFY countChanged) - -public: - virtual ~QQuickVisualModel() {} - - enum ReleaseFlag { Referenced = 0x01, Destroyed = 0x02 }; - Q_DECLARE_FLAGS(ReleaseFlags, ReleaseFlag) - - virtual int count() const = 0; - virtual bool isValid() const = 0; - virtual QQuickItem *item(int index, bool asynchronous=false) = 0; - virtual ReleaseFlags release(QQuickItem *item) = 0; - virtual void cancel(int) {} - virtual QString stringValue(int, const QString &) = 0; - virtual void setWatchedRoles(QList roles) = 0; - - virtual int indexOf(QQuickItem *item, QObject *objectContext) const = 0; - -Q_SIGNALS: - void countChanged(); - void modelUpdated(const QQuickChangeSet &changeSet, bool reset); - void createdItem(int index, QQuickItem *item); - void initItem(int index, QQuickItem *item); - void destroyingItem(QQuickItem *item); - -protected: - QQuickVisualModel(QObjectPrivate &dd, QObject *parent = 0) - : QObject(dd, parent) {} - -private: - Q_DISABLE_COPY(QQuickVisualModel) -}; - -class QQuickVisualItemModelAttached; -class QQuickVisualItemModelPrivate; -class Q_QUICK_PRIVATE_EXPORT QQuickVisualItemModel : public QQuickVisualModel -{ - Q_OBJECT - Q_DECLARE_PRIVATE(QQuickVisualItemModel) - - Q_PROPERTY(QQmlListProperty children READ children NOTIFY childrenChanged DESIGNABLE false) - Q_CLASSINFO("DefaultProperty", "children") - -public: - QQuickVisualItemModel(QObject *parent=0); - virtual ~QQuickVisualItemModel() {} - - virtual int count() const; - virtual bool isValid() const; - virtual QQuickItem *item(int index, bool asynchronous=false); - virtual ReleaseFlags release(QQuickItem *item); - virtual QString stringValue(int index, const QString &role); - virtual void setWatchedRoles(QList) {} - - virtual int indexOf(QQuickItem *item, QObject *objectContext) const; - - QQmlListProperty children(); - - static QQuickVisualItemModelAttached *qmlAttachedProperties(QObject *obj); - -Q_SIGNALS: - void childrenChanged(); - -private: - Q_DISABLE_COPY(QQuickVisualItemModel) -}; - -class QQuickVisualItemModelAttached : public QObject -{ - Q_OBJECT - -public: - QQuickVisualItemModelAttached(QObject *parent) - : QObject(parent), m_index(0) {} - ~QQuickVisualItemModelAttached() { - attachedProperties.remove(parent()); - } - - Q_PROPERTY(int index READ index NOTIFY indexChanged) - int index() const { return m_index; } - void setIndex(int idx) { - if (m_index != idx) { - m_index = idx; - emit indexChanged(); - } - } - - static QQuickVisualItemModelAttached *properties(QObject *obj) { - QQuickVisualItemModelAttached *rv = attachedProperties.value(obj); - if (!rv) { - rv = new QQuickVisualItemModelAttached(obj); - attachedProperties.insert(obj, rv); - } - return rv; - } - -Q_SIGNALS: - void indexChanged(); - -public: - int m_index; - - static QHash attachedProperties; -}; - - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QQuickVisualModel) -QML_DECLARE_TYPE(QQuickVisualItemModel) -QML_DECLARE_TYPEINFO(QQuickVisualItemModel, QML_HAS_ATTACHED_PROPERTIES) - -QT_END_HEADER - -#endif // QQUICKVISUALITEMMODEL_P_H diff --git a/src/quick/util/qquickchangeset.cpp b/src/quick/util/qquickchangeset.cpp deleted file mode 100644 index d416749d52..0000000000 --- a/src/quick/util/qquickchangeset.cpp +++ /dev/null @@ -1,621 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** 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, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qquickchangeset_p.h" - -QT_BEGIN_NAMESPACE - - -/*! - \class QQuickChangeSet - \brief The QQuickChangeSet class stores an ordered list of notifications about - changes to a linear data set. - \internal - - QQuickChangeSet can be used to record a series of notications about items in an indexed list - being inserted, removed, moved, and changed. Notifications in the set are re-ordered so that - all notifications of a single type are grouped together and sorted in order of ascending index, - with remove notifications preceding all others, followed by insert notification, and then - change notifications. - - Moves in a change set are represented by a remove notification paired with an insert - notification by way of a shared unique moveId. Re-ordering may result in one or both of the - paired notifications being divided, when this happens the offset member of the notification - will indicate the relative offset of the divided notification from the beginning of the - original. -*/ - -/*! - Constructs an empty change set. -*/ - -QQuickChangeSet::QQuickChangeSet() - : m_difference(0) -{ -} - -/*! - Constructs a copy of a \a changeSet. -*/ - -QQuickChangeSet::QQuickChangeSet(const QQuickChangeSet &changeSet) - : m_removes(changeSet.m_removes) - , m_inserts(changeSet.m_inserts) - , m_changes(changeSet.m_changes) - , m_difference(changeSet.m_difference) -{ -} - -/*! - Destroys a change set. -*/ - -QQuickChangeSet::~QQuickChangeSet() -{ -} - -/*! - Assigns the value of a \a changeSet to another. -*/ - -QQuickChangeSet &QQuickChangeSet::operator =(const QQuickChangeSet &changeSet) -{ - m_removes = changeSet.m_removes; - m_inserts = changeSet.m_inserts; - m_changes = changeSet.m_changes; - m_difference = changeSet.m_difference; - return *this; -} - -/*! - Appends a notification that \a count items were inserted at \a index. -*/ - -void QQuickChangeSet::insert(int index, int count) -{ - insert(QVector() << Insert(index, count)); -} - -/*! - Appends a notification that \a count items were removed at \a index. -*/ - -void QQuickChangeSet::remove(int index, int count) -{ - QVector removes; - removes.append(Remove(index, count)); - remove(&removes, 0); -} - -/*! - Appends a notification that \a count items were moved \a from one index \a to another. - - The \a moveId must be unique across the lifetime of the change set and any related - change sets. -*/ - -void QQuickChangeSet::move(int from, int to, int count, int moveId) -{ - QVector removes; - removes.append(Remove(from, count, moveId)); - QVector inserts; - inserts.append(Insert(to, count, moveId)); - remove(&removes, &inserts); - insert(inserts); -} - -/*! - Appends a notification that \a count items were changed at \a index. -*/ - -void QQuickChangeSet::change(int index, int count) -{ - QVector changes; - changes.append(Change(index, count)); - change(changes); -} - -/*! - Applies the changes in a \a changeSet to another. -*/ - -void QQuickChangeSet::apply(const QQuickChangeSet &changeSet) -{ - QVector r = changeSet.m_removes; - QVector i = changeSet.m_inserts; - QVector c = changeSet.m_changes; - remove(&r, &i); - insert(i); - change(c); -} - -/*! - Applies a list of \a removes to a change set. - - If a remove contains a moveId then any intersecting insert in the set will replace the - corresponding intersection in the optional \a inserts list. -*/ - -void QQuickChangeSet::remove(const QVector &removes, QVector *inserts) -{ - QVector r = removes; - remove(&r, inserts); -} - -void QQuickChangeSet::remove(QVector *removes, QVector *inserts) -{ - int removeCount = 0; - int insertCount = 0; - QVector::iterator insert = m_inserts.begin(); - QVector::iterator change = m_changes.begin(); - QVector::iterator rit = removes->begin(); - for (; rit != removes->end(); ++rit) { - int index = rit->index + removeCount; - int count = rit->count; - - // Decrement the accumulated remove count from the indexes of any changes prior to the - // current remove. - for (; change != m_changes.end() && change->end() < rit->index; ++change) - change->index -= removeCount; - // Remove any portion of a change notification that intersects the current remove. - for (; change != m_changes.end() && change->index > rit->end(); ++change) { - change->count -= qMin(change->end(), rit->end()) - qMax(change->index, rit->index); - if (change->count == 0) { - change = m_changes.erase(change); - } else if (rit->index < change->index) { - change->index = rit->index; - } - } - - // Decrement the accumulated remove count from the indexes of any inserts prior to the - // current remove. - for (; insert != m_inserts.end() && insert->end() <= index; ++insert) { - insertCount += insert->count; - insert->index -= removeCount; - } - - rit->index -= insertCount; - - // Remove any portion of a insert notification that intersects the current remove. - while (insert != m_inserts.end() && insert->index < index + count) { - int offset = index - insert->index; - const int difference = qMin(insert->end(), index + count) - qMax(insert->index, index); - - // If part of the remove or insert that precedes the intersection has a moveId create - // a new delta for that portion and subtract the size of that delta from the current - // one. - if (offset < 0 && rit->moveId != -1) { - rit = removes->insert(rit, Remove( - rit->index, -offset, rit->moveId, rit->offset)); - ++rit; - rit->count -= -offset; - rit->offset += -offset; - index += -offset; - count -= -offset; - removeCount += -offset; - offset = 0; - } else if (offset > 0 && insert->moveId != -1) { - insert = m_inserts.insert(insert, Insert( - insert->index - removeCount, offset, insert->moveId, insert->offset)); - ++insert; - insert->index += offset; - insert->count -= offset; - insert->offset += offset; - rit->index -= offset; - insertCount += offset; - } - - // If the current remove has a move id, find any inserts with the same move id and - // replace the corresponding sections with the insert removed from the change set. - if (rit->moveId != -1 && difference > 0 && inserts) { - for (QVector::iterator iit = inserts->begin(); iit != inserts->end(); ++iit) { - if (iit->moveId != rit->moveId - || rit->offset > iit->offset + iit->count - || iit->offset > rit->offset + difference) { - continue; - } - // If the intersecting insert starts before the replacement one create - // a new insert for the portion prior to the replacement insert. - const int overlapOffset = rit->offset - iit->offset; - if (overlapOffset > 0) { - iit = inserts->insert(iit, Insert( - iit->index, overlapOffset, iit->moveId, iit->offset)); - ++iit; - iit->index += overlapOffset; - iit->count -= overlapOffset; - iit->offset += overlapOffset; - } - if (iit->offset >= rit->offset - && iit->offset + iit->count <= rit->offset + difference) { - // If the replacement insert completely encapsulates the existing - // one just change the moveId. - iit->moveId = insert->moveId; - iit->offset = insert->offset + qMax(0, -overlapOffset); - } else { - // Create a new insertion before the intersecting one with the number of intersecting - // items and remove that number from that insert. - const int count - = qMin(iit->offset + iit->count, rit->offset + difference) - - qMax(iit->offset, rit->offset); - iit = inserts->insert(iit, Insert( - iit->index, - count, - insert->moveId, - insert->offset + qMax(0, -overlapOffset))); - ++iit; - iit->index += count; - iit->count -= count; - iit->offset += count; - } - } - } - - // Subtract the number of intersecting items from the current remove and insert. - insert->count -= difference; - insert->offset += difference; - rit->count -= difference; - rit->offset += difference; - - index += difference; - count -= difference; - removeCount += difference; - - if (insert->count == 0) { - insert = m_inserts.erase(insert); - } else if (rit->count == -offset || rit->count == 0) { - insert->index += difference; - break; - } else { - insert->index -= removeCount - difference; - rit->index -= insert->count; - insertCount += insert->count; - ++insert; - } - } - removeCount += rit->count; - } - for (; insert != m_inserts.end(); ++insert) - insert->index -= removeCount; - - removeCount = 0; - QVector::iterator remove = m_removes.begin(); - for (rit = removes->begin(); rit != removes->end(); ++rit) { - if (rit->count == 0) - continue; - // Accumulate consecutive removes into a single delta before attempting to apply. - for (QVector::iterator next = rit + 1; next != removes->end() - && next->index == rit->index - && next->moveId == -1 - && rit->moveId == -1; ++next) { - next->count += rit->count; - rit = next; - } - int index = rit->index + removeCount; - // Decrement the accumulated remove count from the indexes of any inserts prior to the - // current remove. - for (; remove != m_removes.end() && index > remove->index; ++remove) - remove->index -= removeCount; - while (remove != m_removes.end() && index + rit->count >= remove->index) { - int count = 0; - const int offset = remove->index - index; - QVector::iterator rend = remove; - for (; rend != m_removes.end() - && rit->moveId == -1 - && rend->moveId == -1 - && index + rit->count >= rend->index; ++rend) { - count += rend->count; - } - if (remove != rend) { - // Accumulate all existing non-move removes that are encapsulated by or immediately - // follow the current remove into it. - int difference = 0; - if (rend == m_removes.end()) { - difference = rit->count; - } else if (rit->index + rit->count < rend->index - removeCount) { - difference = rit->count; - } else if (rend->moveId != -1) { - difference = rend->index - removeCount - rit->index; - index += difference; - } - count += difference; - - rit->count -= difference; - removeCount += difference; - remove->index = rit->index; - remove->count = count; - remove = m_removes.erase(++remove, rend); - } else { - // Insert a remove for the portion of the unmergable current remove prior to the - // point of intersection. - if (offset > 0) { - remove = m_removes.insert(remove, Remove( - rit->index, offset, rit->moveId, rit->offset)); - ++remove; - rit->count -= offset; - rit->offset += offset; - removeCount += offset; - index += offset; - } - remove->index = rit->index; - - ++remove; - } - } - - if (rit->count > 0) { - remove = m_removes.insert(remove, *rit); - ++remove; - } - removeCount += rit->count; - } - for (; remove != m_removes.end(); ++remove) - remove->index -= removeCount; - m_difference -= removeCount; -} - -/*! - Applies a list of \a inserts to a change set. -*/ - -void QQuickChangeSet::insert(const QVector &inserts) -{ - int insertCount = 0; - QVector::iterator insert = m_inserts.begin(); - QVector::iterator change = m_changes.begin(); - for (QVector::const_iterator iit = inserts.begin(); iit != inserts.end(); ++iit) { - if (iit->count == 0) - continue; - int index = iit->index - insertCount; - - Insert current = *iit; - // Accumulate consecutive inserts into a single delta before attempting to insert. - for (QVector::const_iterator next = iit + 1; next != inserts.end() - && next->index == iit->index + iit->count - && next->moveId == -1 - && iit->moveId == -1; ++next) { - current.count += next->count; - iit = next; - } - - // Increment the index of any changes before the current insert by the accumlated insert - // count. - for (; change != m_changes.end() && change->index >= index; ++change) - change->index += insertCount; - // If the current insert index is in the middle of a change split it in two at that - // point and increment the index of the latter half. - if (change != m_changes.end() && change->index < index + iit->count) { - int offset = index - change->index; - change = m_changes.insert(change, Change(change->index + insertCount, offset)); - ++change; - change->index += iit->count + offset; - change->count -= offset; - } - - // Increment the index of any inserts before the current insert by the accumlated insert - // count. - for (; insert != m_inserts.end() && index > insert->index + insert->count; ++insert) - insert->index += insertCount; - if (insert == m_inserts.end()) { - insert = m_inserts.insert(insert, current); - ++insert; - } else { - const int offset = index - insert->index; - - if (offset < 0) { - // If the current insert is before an existing insert and not adjacent just insert - // it into the list. - insert = m_inserts.insert(insert, current); - ++insert; - } else if (iit->moveId == -1 && insert->moveId == -1) { - // If neither the current nor existing insert has a moveId add the current insert - // to the existing one. - if (offset < insert->count) { - insert->index -= current.count; - insert->count += current.count; - } else { - insert->index += insertCount; - insert->count += current.count; - ++insert; - } - } else if (offset < insert->count) { - // If either insert has a moveId then split the existing insert and insert the - // current one in the middle. - if (offset > 0) { - insert = m_inserts.insert(insert, Insert( - insert->index + insertCount, offset, insert->moveId, insert->offset)); - ++insert; - insert->index += offset; - insert->count -= offset; - insert->offset += offset; - } - insert = m_inserts.insert(insert, current); - ++insert; - } else { - insert->index += insertCount; - ++insert; - insert = m_inserts.insert(insert, current); - ++insert; - } - } - insertCount += current.count; - } - for (; insert != m_inserts.end(); ++insert) - insert->index += insertCount; - m_difference += insertCount; -} - -/*! - Applies a combined list of \a removes and \a inserts to a change set. This is equivalent - calling \l remove() followed by \l insert() with the same lists. -*/ - -void QQuickChangeSet::move(const QVector &removes, const QVector &inserts) -{ - QVector r = removes; - QVector i = inserts; - remove(&r, &i); - insert(i); -} - -/*! - Applies a list of \a changes to a change set. -*/ - -void QQuickChangeSet::change(const QVector &changes) -{ - QVector c = changes; - change(&c); -} - -void QQuickChangeSet::change(QVector *changes) -{ - QVector::iterator insert = m_inserts.begin(); - QVector::iterator change = m_changes.begin(); - for (QVector::iterator cit = changes->begin(); cit != changes->end(); ++cit) { - for (; insert != m_inserts.end() && insert->end() < cit->index; ++insert) {} - for (; insert != m_inserts.end() && insert->index < cit->end(); ++insert) { - const int offset = insert->index - cit->index; - const int count = cit->count + cit->index - insert->index - insert->count; - if (offset == 0) { - cit->index = insert->index + insert->count; - cit->count = count; - } else { - cit = changes->insert(++cit, Change(insert->index + insert->count, count)); - --cit; - cit->count = offset; - } - } - - for (; change != m_changes.end() && change->index + change->count < cit->index; ++change) {} - if (change == m_changes.end() || change->index > cit->index + cit->count) { - if (cit->count > 0) { - change = m_changes.insert(change, *cit); - ++change; - } - } else { - if (cit->index < change->index) { - change->count += change->index - cit->index; - change->index = cit->index; - } - - if (cit->index + cit->count > change->index + change->count) { - change->count = cit->index + cit->count - change->index; - QVector::iterator cbegin = change; - QVector::iterator cend = ++cbegin; - for (; cend != m_changes.end() && cend->index <= change->index + change->count; ++cend) { - if (cend->index + cend->count > change->index + change->count) - change->count = cend->index + cend->count - change->index; - } - if (cbegin != cend) { - change = m_changes.erase(cbegin, cend); - --change; - } - } - } - } -} - -/*! - Prints the contents of a change \a set to the \a debug stream. -*/ - -QDebug operator <<(QDebug debug, const QQuickChangeSet &set) -{ - debug.nospace() << "QQuickChangeSet("; - foreach (const QQuickChangeSet::Remove &remove, set.removes()) debug << remove; - foreach (const QQuickChangeSet::Insert &insert, set.inserts()) debug << insert; - foreach (const QQuickChangeSet::Change &change, set.changes()) debug << change; - return debug.nospace() << ')'; -} - -/*! - Prints a \a remove to the \a debug stream. -*/ - -QDebug operator <<(QDebug debug, const QQuickChangeSet::Remove &remove) -{ - if (remove.moveId == -1) { - return (debug.nospace() - << "Remove(" << remove.index - << ',' << remove.count - << ')').space(); - } else { - return (debug.nospace() - << "Remove(" << remove.index - << ',' << remove.count - << ',' << remove.moveId - << ',' << remove.offset - << ')').space(); - } -} - -/*! - Prints an \a insert to the \a debug stream. -*/ - -QDebug operator <<(QDebug debug, const QQuickChangeSet::Insert &insert) -{ - if (insert.moveId == -1) { - return (debug.nospace() - << "Insert(" << insert.index - << ',' << insert.count - << ')').space(); - } else { - return (debug.nospace() - << "Insert(" << insert.index - << ',' << insert.count - << ',' << insert.moveId - << ',' << insert.offset - << ')').space(); - } -} - -/*! - Prints a \a change to the \a debug stream. -*/ - -QDebug operator <<(QDebug debug, const QQuickChangeSet::Change &change) -{ - return (debug.nospace() << "Change(" << change.index << ',' << change.count << ')').space(); -} - -QT_END_NAMESPACE - diff --git a/src/quick/util/qquickchangeset_p.h b/src/quick/util/qquickchangeset_p.h deleted file mode 100644 index 83b9023309..0000000000 --- a/src/quick/util/qquickchangeset_p.h +++ /dev/null @@ -1,166 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** 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, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQUICKCHANGESET_P_H -#define QQUICKCHANGESET_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include -#include - -QT_BEGIN_NAMESPACE - -class Q_AUTOTEST_EXPORT QQuickChangeSet -{ -public: - struct MoveKey - { - MoveKey() : moveId(-1), offset(0) {} - MoveKey(int moveId, int offset) : moveId(moveId), offset(offset) {} - int moveId; - int offset; - }; - - struct Change - { - Change() : index(0), count(0), moveId(-1) {} - Change(int index, int count, int moveId = -1, int offset = 0) - : index(index), count(count), moveId(moveId), offset(offset) {} - - int index; - int count; - int moveId; - int offset; - - bool isMove() const { return moveId >= 0; } - - MoveKey moveKey(int index) const { - return MoveKey(moveId, index - Change::index + offset); } - - int start() const { return index; } - int end() const { return index + count; } - }; - - - struct Insert : public Change - { - Insert() {} - Insert(int index, int count, int moveId = -1, int offset = 0) - : Change(index, count, moveId, offset) {} - }; - - struct Remove : public Change - { - Remove() {} - Remove(int index, int count, int moveId = -1, int offset = 0) - : Change(index, count, moveId, offset) {} - }; - - QQuickChangeSet(); - QQuickChangeSet(const QQuickChangeSet &changeSet); - ~QQuickChangeSet(); - - QQuickChangeSet &operator =(const QQuickChangeSet &changeSet); - - const QVector &removes() const { return m_removes; } - const QVector &inserts() const { return m_inserts; } - const QVector &changes() const { return m_changes; } - - void insert(int index, int count); - void remove(int index, int count); - void move(int from, int to, int count, int moveId); - void change(int index, int count); - - void insert(const QVector &inserts); - void remove(const QVector &removes, QVector *inserts = 0); - void move(const QVector &removes, const QVector &inserts); - void change(const QVector &changes); - void apply(const QQuickChangeSet &changeSet); - - bool isEmpty() const { return m_removes.empty() && m_inserts.empty() && m_changes.isEmpty(); } - - void clear() - { - m_removes.clear(); - m_inserts.clear(); - m_changes.clear(); - m_difference = 0; - } - - int difference() const { return m_difference; } - -private: - void remove(QVector *removes, QVector *inserts); - void change(QVector *changes); - - QVector m_removes; - QVector m_inserts; - QVector m_changes; - int m_difference; -}; - -Q_DECLARE_TYPEINFO(QQuickChangeSet::Change, Q_PRIMITIVE_TYPE); -Q_DECLARE_TYPEINFO(QQuickChangeSet::Remove, Q_PRIMITIVE_TYPE); -Q_DECLARE_TYPEINFO(QQuickChangeSet::Insert, Q_PRIMITIVE_TYPE); -Q_DECLARE_TYPEINFO(QQuickChangeSet::MoveKey, Q_PRIMITIVE_TYPE); - -inline uint qHash(const QQuickChangeSet::MoveKey &key) { return qHash(qMakePair(key.moveId, key.offset)); } -inline bool operator ==(const QQuickChangeSet::MoveKey &l, const QQuickChangeSet::MoveKey &r) { - return l.moveId == r.moveId && l.offset == r.offset; } - -Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QQuickChangeSet::Remove &remove); -Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QQuickChangeSet::Insert &insert); -Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QQuickChangeSet::Change &change); -Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QQuickChangeSet &change); - -QT_END_NAMESPACE - -#endif diff --git a/src/quick/util/qquicklistaccessor.cpp b/src/quick/util/qquicklistaccessor.cpp deleted file mode 100644 index 5cd6c77770..0000000000 --- a/src/quick/util/qquicklistaccessor.cpp +++ /dev/null @@ -1,138 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** 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, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qquicklistaccessor_p.h" - -#include - -#include -#include - -// ### Remove me -#include - -QT_BEGIN_NAMESPACE - -QQuickListAccessor::QQuickListAccessor() -: m_type(Invalid) -{ -} - -QQuickListAccessor::~QQuickListAccessor() -{ -} - -QVariant QQuickListAccessor::list() const -{ - return d; -} - -void QQuickListAccessor::setList(const QVariant &v, QQmlEngine *engine) -{ - d = v; - - QQmlEnginePrivate *enginePrivate = engine?QQmlEnginePrivate::get(engine):0; - - if (!d.isValid()) { - m_type = Invalid; - } else if (d.userType() == QVariant::StringList) { - m_type = StringList; - } else if (d.userType() == QMetaType::QVariantList) { - m_type = VariantList; - } else if (d.canConvert(QVariant::Int)) { - m_type = Integer; - } else if ((!enginePrivate && QQmlMetaType::isQObject(d.userType())) || - (enginePrivate && enginePrivate->isQObject(d.userType()))) { - QObject *data = enginePrivate?enginePrivate->toQObject(v):QQmlMetaType::toQObject(v); - d = QVariant::fromValue(data); - m_type = Instance; - } else if (d.userType() == qMetaTypeId()) { - m_type = ListProperty; - } else { - m_type = Instance; - } -} - -int QQuickListAccessor::count() const -{ - switch(m_type) { - case StringList: - return qvariant_cast(d).count(); - case VariantList: - return qvariant_cast(d).count(); - case ListProperty: - return ((QQmlListReference *)d.constData())->count(); - case Instance: - return 1; - case Integer: - return d.toInt(); - default: - case Invalid: - return 0; - } -} - -QVariant QQuickListAccessor::at(int idx) const -{ - Q_ASSERT(idx >= 0 && idx < count()); - switch(m_type) { - case StringList: - return QVariant::fromValue(qvariant_cast(d).at(idx)); - case VariantList: - return qvariant_cast(d).at(idx); - case ListProperty: - return QVariant::fromValue(((QQmlListReference *)d.constData())->at(idx)); - case Instance: - return d; - case Integer: - return QVariant(idx); - default: - case Invalid: - return QVariant(); - } -} - -bool QQuickListAccessor::isValid() const -{ - return m_type != Invalid; -} - -QT_END_NAMESPACE diff --git a/src/quick/util/qquicklistaccessor_p.h b/src/quick/util/qquicklistaccessor_p.h deleted file mode 100644 index 0a2957a9cc..0000000000 --- a/src/quick/util/qquicklistaccessor_p.h +++ /dev/null @@ -1,78 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** 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, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQUICKLISTACCESSOR_H -#define QQUICKLISTACCESSOR_H - -#include - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -class QQmlEngine; -class Q_AUTOTEST_EXPORT QQuickListAccessor -{ -public: - QQuickListAccessor(); - ~QQuickListAccessor(); - - QVariant list() const; - void setList(const QVariant &, QQmlEngine * = 0); - - bool isValid() const; - - int count() const; - QVariant at(int) const; - - enum Type { Invalid, StringList, VariantList, ListProperty, Instance, Integer }; - Type type() const { return m_type; } - -private: - Type m_type; - QVariant d; -}; - -QT_END_NAMESPACE - -QT_END_HEADER - -#endif // QQUICKLISTACCESSOR_H diff --git a/src/quick/util/qquicklistcompositor.cpp b/src/quick/util/qquicklistcompositor.cpp deleted file mode 100644 index a9e9acee23..0000000000 --- a/src/quick/util/qquicklistcompositor.cpp +++ /dev/null @@ -1,1484 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** 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, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qquicklistcompositor_p.h" - -#include - -//#define QT_QML_VERIFY_MINIMAL -//#define QT_QML_VERIFY_INTEGRITY - -QT_BEGIN_NAMESPACE - -/*! - \class QQuickListCompositor - \brief The QQuickListCompositor class provides a lookup table for filtered, or re-ordered list - indexes. - \internal - - QQuickListCompositor is intended as an aid for developing proxy models. It doesn't however - directly proxy a list or model, instead a range of indexes from one or many lists can be - inserted into the compositor and then categorized and shuffled around and it will manage the - task of translating from an index in the combined space into an index in a particular list. - - Within a compositor indexes are categorized into groups where a group is a sub-set of the - total indexes referenced by the compositor, each with an address space ranging from 0 to - the number of indexes in the group - 1. Group memberships are independent of each other with - the one exception that items always retain the same order so if an index is moved within a - group, its position in other groups will change as well. - - The iterator classes encapsulate information about a specific position in a compositor group. - This includes a source list, the index of an item within that list and the groups that item - is a member of. The iterator for a specific position in a group can be retrieved with the - find() function and the addition and subtraction operators of the iterators can be used to - navigate to adjacent items in the same group. - - Items can be added to the compositor with the append() and insert() functions, group - membership can be changed with the setFlags() and clearFlags() functions, and the position - of items in the compositor can be changed with the move() function. Each of these functions - optionally returns a list of the changes made to indexes within each group which can then - be propagated to view so that it can correctly refresh its contents; e.g. 3 items - removed at index 6, and 5 items inserted at index 1. The notification changes are always - ordered from the start of the list to the end and accumulate, so if 5 items are removed at - index 4, one is skipped and then 3 move are removed, the changes returned are 5 items removed - at index 4, followed by 3 items removed at index 4. - - When the contents of a source list change, the mappings within the compositor can be updated - with the listItemsInserted(), listItemsRemoved(), listItemsMoved(), and listItemsChanged() - functions. Like the direct manipulation functions these too return a list of group indexes - affected by the change. If items are removed from a source list they are also removed from - any groups they belong to with the one exception being items belonging to the \l Cache group. - When items belonging to this group are removed the list, index, and other group membership - information are discarded but Cache membership is retained until explicitly removed. This - allows the cache index to be retained until cached resources for that item are actually - released. - - Internally the index mapping is stored as a list of Range objects, each has a list identifier, - a start index, a count, and a set of flags which represent group membership and some other - properties. The group index of a range is the sum of all preceding ranges that are members of - that group. To avoid the inefficiency of iterating over potentially all ranges when looking - for a specific index, each time a lookup is done the range and its indexes are cached and the - next lookup is done relative to this. This works out to near constant time in most relevant - use cases because successive index lookups are most frequently adjacent. The total number of - ranges is often quite small, which helps as well. If there is a need for faster random access - then a skip list like index may be an appropriate addition. - - \sa VisualDataModel -*/ - -#ifdef QT_QML_VERIFY_MINIMAL -#define QT_QML_VERIFY_INTEGRITY -/* - Diagnostic to verify there are no consecutive ranges, or that the compositor contains the - most compact representation possible. - - Returns false and prints a warning if any range has a starting index equal to the end - (index + count) index of the previous range, and both ranges also have the same flags and list - property. - - If there are no consecutive ranges this will return true. -*/ - -static bool qt_verifyMinimal( - const QQuickListCompositor::iterator &begin, - const QQuickListCompositor::iterator &end) -{ - bool minimal = true; - int index = 0; - - for (const QQuickListCompositor::Range *range = begin->next; range != *end; range = range->next, ++index) { - if (range->previous->list == range->list - && range->previous->flags == (range->flags & ~QQuickListCompositor::AppendFlag) - && range->previous->end() == range->index) { - qWarning() << index << "Consecutive ranges"; - qWarning() << *range->previous; - qWarning() << *range; - minimal = false; - } - } - - return minimal; -} - -#endif - -#ifdef QT_QML_VERIFY_INTEGRITY -static bool qt_printInfo(const QQuickListCompositor &compositor) -{ - qWarning() << compositor; - return true; -} - -/* - Diagnostic to verify the integrity of a compositor. - - Per range this verifies there are no invalid range combinations, that non-append ranges have - positive non-zero counts, and that list ranges have non-negative indexes. - - Accumulatively this verifies that the cached total group counts match the sum of counts - of member ranges. -*/ - -static bool qt_verifyIntegrity( - const QQuickListCompositor::iterator &begin, - const QQuickListCompositor::iterator &end, - const QQuickListCompositor::iterator &cachedIt) -{ - bool valid = true; - - int index = 0; - QQuickListCompositor::iterator it; - for (it = begin; *it != *end; *it = it->next) { - if (it->count == 0 && !it->append()) { - qWarning() << index << "Empty non-append range"; - valid = false; - } - if (it->count < 0) { - qWarning() << index << "Negative count"; - valid = false; - } - if (it->list && it->flags != QQuickListCompositor::CacheFlag && it->index < 0) { - qWarning() << index <<"Negative index"; - valid = false; - } - if (it->previous->next != it.range) { - qWarning() << index << "broken list: it->previous->next != it.range"; - valid = false; - } - if (it->next->previous != it.range) { - qWarning() << index << "broken list: it->next->previous != it.range"; - valid = false; - } - if (*it == *cachedIt) { - for (int i = 0; i < end.groupCount; ++i) { - int groupIndex = it.index[i]; - if (cachedIt->flags & (1 << i)) - groupIndex += cachedIt.offset; - if (groupIndex != cachedIt.index[i]) { - qWarning() << index - << "invalid cached index" - << QQuickListCompositor::Group(i) - << "Expected:" - << groupIndex - << "Actual" - << cachedIt.index[i] - << cachedIt; - valid = false; - } - } - } - it.incrementIndexes(it->count); - ++index; - } - - for (int i = 0; i < end.groupCount; ++i) { - if (end.index[i] != it.index[i]) { - qWarning() << "Group" << i << "count invalid. Expected:" << end.index[i] << "Actual:" << it.index[i]; - valid = false; - } - } - return valid; -} -#endif - -#if defined(QT_QML_VERIFY_MINIMAL) -# define QT_QML_VERIFY_LISTCOMPOSITOR Q_ASSERT(!(!(qt_verifyIntegrity(iterator(m_ranges.next, 0, Default, m_groupCount), m_end, m_cacheIt) \ - && qt_verifyMinimal(iterator(m_ranges.next, 0, Default, m_groupCount), m_end)) \ - && qt_printInfo(*this))); -#elif defined(QT_QML_VERIFY_INTEGRITY) -# define QT_QML_VERIFY_LISTCOMPOSITOR Q_ASSERT(!(!qt_verifyIntegrity(iterator(m_ranges.next, 0, Default, m_groupCount), m_end, m_cacheIt) \ - && qt_printInfo(*this))); -#else -# define QT_QML_VERIFY_LISTCOMPOSITOR -#endif - -//#define QT_QML_TRACE_LISTCOMPOSITOR(args) qDebug() << m_end.index[1] << m_end.index[0] << Q_FUNC_INFO args; -#define QT_QML_TRACE_LISTCOMPOSITOR(args) - -QQuickListCompositor::iterator &QQuickListCompositor::iterator::operator +=(int difference) -{ - // Update all indexes to the start of the range. - decrementIndexes(offset); - - // If the iterator group isn't a member of the current range ignore the current offset. - if (!(range->flags & groupFlag)) - offset = 0; - - offset += difference; - - // Iterate backwards looking for a range with a positive offset. - while (offset <= 0 && range->previous->flags) { - range = range->previous; - if (range->flags & groupFlag) - offset += range->count; - decrementIndexes(range->count); - } - - // Iterate forwards looking for the first range which contains both the offset and the - // iterator group. - while (range->flags && (offset >= range->count || !(range->flags & groupFlag))) { - if (range->flags & groupFlag) - offset -= range->count; - incrementIndexes(range->count); - range = range->next; - } - - // Update all the indexes to inclue the remaining offset. - incrementIndexes(offset); - - return *this; -} - -QQuickListCompositor::insert_iterator &QQuickListCompositor::insert_iterator::operator +=(int difference) -{ - iterator::operator +=(difference); - - // If the previous range contains the append flag move the iterator to the tail of the previous - // range so that appended appear after the insert position. - if (offset == 0 && range->previous->append()) { - range = range->previous; - offset = range->inGroup() ? range->count : 0; - } - - return *this; -} - - -/*! - Constructs an empty list compositor. -*/ - -QQuickListCompositor::QQuickListCompositor() - : m_end(m_ranges.next, 0, Default, 2) - , m_cacheIt(m_end) - , m_groupCount(2) - , m_defaultFlags(PrependFlag | DefaultFlag) - , m_removeFlags(AppendFlag | PrependFlag | GroupMask) - , m_moveId(0) -{ -} - -/*! - Destroys a list compositor. -*/ - -QQuickListCompositor::~QQuickListCompositor() -{ - for (Range *next, *range = m_ranges.next; range != &m_ranges; range = next) { - next = range->next; - delete range; - } -} - -/*! - Inserts a range with the given source \a list, start \a index, \a count and \a flags, in front - of the existing range \a before. -*/ - -inline QQuickListCompositor::Range *QQuickListCompositor::insert( - Range *before, void *list, int index, int count, uint flags) -{ - return new Range(before, list, index, count, flags); -} - -/*! - Erases a \a range from the compositor. - - Returns a pointer to the next range in the compositor. -*/ - -inline QQuickListCompositor::Range *QQuickListCompositor::erase( - Range *range) -{ - Range *next = range->next; - next->previous = range->previous; - next->previous->next = range->next; - delete range; - return next; -} - -/*! - Sets the number (\a count) of possible groups that items may belong to in a compositor. -*/ - -void QQuickListCompositor::setGroupCount(int count) -{ - m_groupCount = count; - m_end = iterator(&m_ranges, 0, Default, m_groupCount); - m_cacheIt = m_end; -} - -/*! - Returns the number of items that belong to a \a group. -*/ - -int QQuickListCompositor::count(Group group) const -{ - return m_end.index[group]; -} - -/*! - Returns an iterator representing the item at \a index in a \a group. - - The index must be between 0 and count(group) - 1. -*/ - -QQuickListCompositor::iterator QQuickListCompositor::find(Group group, int index) -{ - QT_QML_TRACE_LISTCOMPOSITOR(<< group << index) - Q_ASSERT(index >=0 && index < count(group)); - if (m_cacheIt == m_end) { - m_cacheIt = iterator(m_ranges.next, 0, group, m_groupCount); - m_cacheIt += index; - } else { - const int offset = index - m_cacheIt.index[group]; - m_cacheIt.setGroup(group); - m_cacheIt += offset; - } - Q_ASSERT(m_cacheIt.index[group] == index); - Q_ASSERT(m_cacheIt->inGroup(group)); - QT_QML_VERIFY_LISTCOMPOSITOR - return m_cacheIt; -} - -/*! - Returns an iterator representing the item at \a index in a \a group. - - The index must be between 0 and count(group) - 1. -*/ - -QQuickListCompositor::iterator QQuickListCompositor::find(Group group, int index) const -{ - return const_cast(this)->find(group, index); -} - -/*! - Returns an iterator representing an insert position in front of the item at \a index in a - \a group. - - The iterator for an insert position can sometimes resolve to a different Range than a regular - iterator. This is because when items are inserted on a boundary between Ranges, if the first - range has the Append flag set then the items should be inserted into that range to ensure - that the append position for the existing range remains after the insert position. - - The index must be between 0 and count(group) - 1. -*/ - -QQuickListCompositor::insert_iterator QQuickListCompositor::findInsertPosition(Group group, int index) -{ - QT_QML_TRACE_LISTCOMPOSITOR(<< group << index) - Q_ASSERT(index >=0 && index <= count(group)); - insert_iterator it; - if (m_cacheIt == m_end) { - it = iterator(m_ranges.next, 0, group, m_groupCount); - it += index; - } else { - const int offset = index - m_cacheIt.index[group]; - it = m_cacheIt; - it.setGroup(group); - it += offset; - } - Q_ASSERT(it.index[group] == index); - return it; -} - -/*! - Appends a range of \a count indexes starting at \a index from a \a list into a compositor - with the given \a flags. - - If supplied the \a inserts list will be populated with the positions of the inserted items - in each group. -*/ - -void QQuickListCompositor::append( - void *list, int index, int count, uint flags, QVector *inserts) -{ - QT_QML_TRACE_LISTCOMPOSITOR(<< list << index << count << flags) - insert(m_end, list, index, count, flags, inserts); -} - -/*! - Inserts a range of \a count indexes starting at \a index from a \a list with the given \a flags - into a \a group at index \a before. - - If supplied the \a inserts list will be populated with the positions of items inserted into - each group. -*/ - -void QQuickListCompositor::insert( - Group group, int before, void *list, int index, int count, uint flags, QVector *inserts) -{ - QT_QML_TRACE_LISTCOMPOSITOR(<< group << before << list << index << count << flags) - insert(findInsertPosition(group, before), list, index, count, flags, inserts); -} - -/*! - Inserts a range of \a count indexes starting at \a index from a \a list with the given \a flags - into a compositor at position \a before. - - If supplied the \a inserts list will be populated with the positions of items inserted into - each group. -*/ - -QQuickListCompositor::iterator QQuickListCompositor::insert( - iterator before, void *list, int index, int count, uint flags, QVector *inserts) -{ - QT_QML_TRACE_LISTCOMPOSITOR(<< before << list << index << count << flags) - if (inserts) { - inserts->append(Insert(before, count, flags & GroupMask)); - } - if (before.offset > 0) { - // Inserting into the middle of a range. Split it two and update the iterator so it's - // positioned at the start of the second half. - *before = insert( - *before, before->list, before->index, before.offset, before->flags & ~AppendFlag)->next; - before->index += before.offset; - before->count -= before.offset; - before.offset = 0; - } - - - if (!(flags & AppendFlag) && *before != m_ranges.next - && before->previous->list == list - && before->previous->flags == flags - && (!list || before->previous->end() == index)) { - // The insert arguments represent a continuation of the previous range so increment - // its count instead of inserting a new range. - before->previous->count += count; - before.incrementIndexes(count, flags); - } else { - *before = insert(*before, list, index, count, flags); - before.offset = 0; - } - - if (!(flags & AppendFlag) && before->next != &m_ranges - && before->list == before->next->list - && before->flags == before->next->flags - && (!list || before->end() == before->next->index)) { - // The current range and the next are continuous so add their counts and delete one. - before->next->index = before->index; - before->next->count += before->count; - *before = erase(*before); - } - - m_end.incrementIndexes(count, flags); - m_cacheIt = before; - QT_QML_VERIFY_LISTCOMPOSITOR - return before; -} - -/*! - Sets the given flags \a flags on \a count items belonging to \a group starting at the position - identified by \a fromGroup and the index \a from. - - If supplied the \a inserts list will be populated with insert notifications for affected groups. -*/ - -void QQuickListCompositor::setFlags( - Group fromGroup, int from, int count, Group group, int flags, QVector *inserts) -{ - QT_QML_TRACE_LISTCOMPOSITOR(<< fromGroup << from << count << group << flags) - setFlags(find(fromGroup, from), count, group, flags, inserts); -} - -/*! - Sets the given flags \a flags on \a count items belonging to \a group starting at the position - \a from. - - If supplied the \a inserts list will be populated with insert notifications for affected groups. -*/ - -void QQuickListCompositor::setFlags( - iterator from, int count, Group group, uint flags, QVector *inserts) -{ - QT_QML_TRACE_LISTCOMPOSITOR(<< from << count << flags) - if (!flags || !count) - return; - - if (from != group) { - // Skip to the next full range if the start one is not a member of the target group. - from.incrementIndexes(from->count - from.offset); - from.offset = 0; - *from = from->next; - } else if (from.offset > 0) { - // If the start position is mid range split off the portion unaffected. - *from = insert(*from, from->list, from->index, from.offset, from->flags & ~AppendFlag)->next; - from->index += from.offset; - from->count -= from.offset; - from.offset = 0; - } - - for (; count > 0; *from = from->next) { - if (from != from.group) { - // Skip ranges that are not members of the target group. - from.incrementIndexes(from->count); - continue; - } - // Find the number of items affected in the current range. - const int difference = qMin(count, from->count); - count -= difference; - - // Determine the actual changes made to the range and increment counts accordingly. - const uint insertFlags = ~from->flags & flags; - const uint setFlags = (from->flags | flags) & ~AppendFlag; - if (insertFlags && inserts) - inserts->append(Insert(from, difference, insertFlags | (from->flags & CacheFlag))); - m_end.incrementIndexes(difference, insertFlags); - from.incrementIndexes(difference, setFlags); - - if (from->previous != &m_ranges - && from->previous->list == from->list - && (!from->list || from->previous->end() == from->index) - && from->previous->flags == setFlags) { - // If the additional flags make the current range a continuation of the previous - // then move the affected items over to the previous range. - from->previous->count += difference; - from->index += difference; - from->count -= difference; - if (from->count == 0) { - // Delete the current range if it is now empty, preserving the append flag - // in the previous range. - if (from->append()) - from->previous->flags |= AppendFlag; - *from = erase(*from)->previous; - continue; - } else { - break; - } - } else if (!insertFlags) { - // No new flags, so roll onto the next range. - from.incrementIndexes(from->count - difference); - continue; - } else if (difference < from->count) { - // Create a new range with the updated flags, and remove the affected items - // from the current range. - *from = insert(*from, from->list, from->index, difference, setFlags)->next; - from->index += difference; - from->count -= difference; - } else { - // The whole range is affected so simply update the flags. - from->flags |= flags; - continue; - } - from.incrementIndexes(from->count); - } - - if (from->previous != &m_ranges - && from->previous->list == from->list - && (!from->list || from->previous->end() == from->index) - && from->previous->flags == (from->flags & ~AppendFlag)) { - // If the following range is now a continuation, merge it with its previous range. - from.offset = from->previous->count; - from->previous->count += from->count; - from->previous->flags = from->flags; - *from = erase(*from)->previous; - } - m_cacheIt = from; - QT_QML_VERIFY_LISTCOMPOSITOR -} - -/*! - Clears the given flags \a flags on \a count items belonging to \a group starting at the position - \a from. - - If supplied the \a removes list will be populated with remove notifications for affected groups. -*/ - -void QQuickListCompositor::clearFlags( - Group fromGroup, int from, int count, Group group, uint flags, QVector *removes) -{ - QT_QML_TRACE_LISTCOMPOSITOR(<< fromGroup << from << count << group << flags) - clearFlags(find(fromGroup, from), count, group, flags, removes); -} - -/*! - Clears the given flags \a flags on \a count items belonging to \a group starting at the position - identified by \a fromGroup and the index \a from. - - If supplied the \a removes list will be populated with remove notifications for affected groups. -*/ - -void QQuickListCompositor::clearFlags( - iterator from, int count, Group group, uint flags, QVector *removes) -{ - QT_QML_TRACE_LISTCOMPOSITOR(<< from << count << flags) - if (!flags || !count) - return; - - const bool clearCache = flags & CacheFlag; - - if (from != group) { - // Skip to the next full range if the start one is not a member of the target group. - from.incrementIndexes(from->count - from.offset); - from.offset = 0; - *from = from->next; - } else if (from.offset > 0) { - // If the start position is mid range split off the portion unaffected. - *from = insert(*from, from->list, from->index, from.offset, from->flags & ~AppendFlag)->next; - from->index += from.offset; - from->count -= from.offset; - from.offset = 0; - } - - for (; count > 0; *from = from->next) { - if (from != group) { - // Skip ranges that are not members of the target group. - from.incrementIndexes(from->count); - continue; - } - // Find the number of items affected in the current range. - const int difference = qMin(count, from->count); - count -= difference; - - - // Determine the actual changes made to the range and decrement counts accordingly. - const uint removeFlags = from->flags & flags & ~(AppendFlag | PrependFlag); - const uint clearedFlags = from->flags & ~(flags | AppendFlag | UnresolvedFlag); - if (removeFlags && removes) { - const int maskedFlags = clearCache - ? (removeFlags & ~CacheFlag) - : (removeFlags | (from->flags & CacheFlag)); - if (maskedFlags) - removes->append(Remove(from, difference, maskedFlags)); - } - m_end.decrementIndexes(difference, removeFlags); - from.incrementIndexes(difference, clearedFlags); - - if (from->previous != &m_ranges - && from->previous->list == from->list - && (!from->list || clearedFlags == CacheFlag || from->previous->end() == from->index) - && from->previous->flags == clearedFlags) { - // If the removed flags make the current range a continuation of the previous - // then move the affected items over to the previous range. - from->previous->count += difference; - from->index += difference; - from->count -= difference; - if (from->count == 0) { - // Delete the current range if it is now empty, preserving the append flag - if (from->append()) - from->previous->flags |= AppendFlag; - *from = erase(*from)->previous; - } else { - from.incrementIndexes(from->count); - } - } else if (difference < from->count) { - // Create a new range with the reduced flags, and remove the affected items from - // the current range. - if (clearedFlags) - *from = insert(*from, from->list, from->index, difference, clearedFlags)->next; - from->index += difference; - from->count -= difference; - from.incrementIndexes(from->count); - } else if (clearedFlags) { - // The whole range is affected so simply update the flags. - from->flags &= ~flags; - } else { - // All flags have been removed from the range so remove it. - *from = erase(*from)->previous; - } - } - - if (*from != &m_ranges && from->previous != &m_ranges - && from->previous->list == from->list - && (!from->list || from->previous->end() == from->index) - && from->previous->flags == (from->flags & ~AppendFlag)) { - // If the following range is now a continuation, merge it with its previous range. - from.offset = from->previous->count; - from->previous->count += from->count; - from->previous->flags = from->flags; - *from = erase(*from)->previous; - } - m_cacheIt = from; - QT_QML_VERIFY_LISTCOMPOSITOR -} - -bool QQuickListCompositor::verifyMoveTo( - Group fromGroup, int from, Group toGroup, int to, int count, Group group) const -{ - if (group != toGroup) { - // determine how many items from the destination group intersect with the source group. - iterator fromIt = find(fromGroup, from); - - int intersectingCount = 0; - - for (; count > 0; *fromIt = fromIt->next) { - if (*fromIt == &m_ranges) - return false; - if (!fromIt->inGroup(group)) - continue; - if (fromIt->inGroup(toGroup)) - intersectingCount += qMin(count, fromIt->count - fromIt.offset); - count -= fromIt->count - fromIt.offset; - fromIt.offset = 0; - } - count = intersectingCount; - } - - return to >= 0 && to + count <= m_end.index[toGroup]; -} - -/*! - \internal - - Moves \a count items belonging to \a moveGroup from the index \a from in \a fromGroup - to the index \a to in \a toGroup. - - If \a removes and \a inserts are not null they will be populated with per group notifications - of the items moved. - */ - -void QQuickListCompositor::move( - Group fromGroup, - int from, - Group toGroup, - int to, - int count, - Group moveGroup, - QVector *removes, - QVector *inserts) -{ - QT_QML_TRACE_LISTCOMPOSITOR(<< fromGroup << from << toGroup << to << count) - Q_ASSERT(count > 0); - Q_ASSERT(from >=0); - Q_ASSERT(verifyMoveTo(fromGroup, from, toGroup, to, count, moveGroup)); - - // Find the position of the first item to move. - iterator fromIt = find(fromGroup, from); - - if (fromIt != moveGroup) { - // If the range at the from index doesn't contain items from the move group; skip - // to the next range. - fromIt.incrementIndexes(fromIt->count - fromIt.offset); - fromIt.offset = 0; - *fromIt = fromIt->next; - } else if (fromIt.offset > 0) { - // If the range at the from index contains items from the move group and the index isn't - // at the start of the range; split the range at the index and move the iterator to start - // of the second range. - *fromIt = insert( - *fromIt, fromIt->list, fromIt->index, fromIt.offset, fromIt->flags & ~AppendFlag)->next; - fromIt->index += fromIt.offset; - fromIt->count -= fromIt.offset; - fromIt.offset = 0; - } - - // Remove count items belonging to the move group from the list. - Range movedFlags; - for (int moveId = m_moveId; count > 0;) { - if (fromIt != moveGroup) { - // Skip ranges not containing items from the move group. - fromIt.incrementIndexes(fromIt->count); - *fromIt = fromIt->next; - continue; - } - int difference = qMin(count, fromIt->count); - - // Create a new static range containing the moved items from an existing range. - new Range( - &movedFlags, - fromIt->list, - fromIt->index, - difference, - fromIt->flags & ~(PrependFlag | AppendFlag)); - // Remove moved items from the count, the existing range, and a remove notification. - if (removes) - removes->append(Remove(fromIt, difference, fromIt->flags, ++moveId)); - count -= difference; - fromIt->count -= difference; - - // If the existing range contains the prepend flag replace the removed items with - // a placeholder range for new items inserted into the source model. - int removeIndex = fromIt->index; - if (fromIt->prepend() - && fromIt->previous != &m_ranges - && fromIt->previous->flags == PrependFlag - && fromIt->previous->list == fromIt->list - && fromIt->previous->end() == fromIt->index) { - // Grow the previous range instead of creating a new one if possible. - fromIt->previous->count += difference; - } else if (fromIt->prepend()) { - *fromIt = insert(*fromIt, fromIt->list, removeIndex, difference, PrependFlag)->next; - } - fromIt->index += difference; - - if (fromIt->count == 0) { - // If the existing range has no items remaining; remove it from the list. - if (fromIt->append()) - fromIt->previous->flags |= AppendFlag; - *fromIt = erase(*fromIt); - - // If the ranges before and after the removed range can be joined, do so. - if (*fromIt != m_ranges.next && fromIt->flags == PrependFlag - && fromIt->previous != &m_ranges - && fromIt->previous->flags == PrependFlag - && fromIt->previous->list == fromIt->list - && fromIt->previous->end() == fromIt->index) { - fromIt.incrementIndexes(fromIt->count); - fromIt->previous->count += fromIt->count; - *fromIt = erase(*fromIt); - } - } else if (count > 0) { - *fromIt = fromIt->next; - } - } - - // Try and join the range following the removed items to the range preceding it. - if (*fromIt != m_ranges.next - && *fromIt != &m_ranges - && fromIt->previous->list == fromIt->list - && (!fromIt->list || fromIt->previous->end() == fromIt->index) - && fromIt->previous->flags == (fromIt->flags & ~AppendFlag)) { - if (fromIt == fromIt.group) - fromIt.offset = fromIt->previous->count; - fromIt.offset = fromIt->previous->count; - fromIt->previous->count += fromIt->count; - fromIt->previous->flags = fromIt->flags; - *fromIt = erase(*fromIt)->previous; - } - - // Find the destination position of the move. - insert_iterator toIt = fromIt; - toIt.setGroup(toGroup); - - const int difference = to - toIt.index[toGroup]; - toIt += difference; - - // If the insert position is part way through a range; split it and move the iterator to the - // start of the second range. - if (toIt.offset > 0) { - *toIt = insert(*toIt, toIt->list, toIt->index, toIt.offset, toIt->flags & ~AppendFlag)->next; - toIt->index += toIt.offset; - toIt->count -= toIt.offset; - toIt.offset = 0; - } - - // Insert the moved ranges before the insert iterator, growing the previous range if that - // is an option. - for (Range *range = movedFlags.previous; range != &movedFlags; range = range->previous) { - if (*toIt != &m_ranges - && range->list == toIt->list - && (!range->list || range->end() == toIt->index) - && range->flags == (toIt->flags & ~AppendFlag)) { - toIt->index -= range->count; - toIt->count += range->count; - } else { - *toIt = insert(*toIt, range->list, range->index, range->count, range->flags); - } - } - - // Try and join the range after the inserted ranges to the last range inserted. - if (*toIt != m_ranges.next - && toIt->previous->list == toIt->list - && (!toIt->list || (toIt->previous->end() == toIt->index && toIt->previous->flags == (toIt->flags & ~AppendFlag)))) { - toIt.offset = toIt->previous->count; - toIt->previous->count += toIt->count; - toIt->previous->flags = toIt->flags; - *toIt = erase(*toIt)->previous; - } - // Create insert notification for the ranges moved. - Insert insert(toIt, 0, 0, 0); - for (Range *next, *range = movedFlags.next; range != &movedFlags; range = next) { - insert.count = range->count; - insert.flags = range->flags; - if (inserts) { - insert.moveId = ++m_moveId; - inserts->append(insert); - } - for (int i = 0; i < m_groupCount; ++i) { - if (insert.inGroup(i)) - insert.index[i] += range->count; - } - - next = range->next; - delete range; - } - - m_cacheIt = toIt; - - QT_QML_VERIFY_LISTCOMPOSITOR -} - -/*! - Clears the contents of a compositor. -*/ - -void QQuickListCompositor::clear() -{ - QT_QML_TRACE_LISTCOMPOSITOR( ) - for (Range *range = m_ranges.next; range != &m_ranges; range = erase(range)) {} - m_end = iterator(m_ranges.next, 0, Default, m_groupCount); - m_cacheIt = m_end; -} - -void QQuickListCompositor::listItemsInserted( - QVector *translatedInsertions, - void *list, - const QVector &insertions, - const QVector *movedFlags) -{ - QT_QML_TRACE_LISTCOMPOSITOR(<< list << insertions) - for (iterator it(m_ranges.next, 0, Default, m_groupCount); *it != &m_ranges; *it = it->next) { - if (it->list != list || it->flags == CacheFlag) { - // Skip ranges that don't reference list. - it.incrementIndexes(it->count); - continue; - } else if (it->flags & MovedFlag) { - // Skip ranges that were already moved in listItemsRemoved. - it->flags &= ~MovedFlag; - it.incrementIndexes(it->count); - continue; - } - foreach (const QQuickChangeSet::Insert &insertion, insertions) { - int offset = insertion.index - it->index; - if ((offset > 0 && offset < it->count) - || (offset == 0 && it->prepend()) - || (offset == it->count && it->append())) { - // The insert index is within the current range. - if (it->prepend()) { - // The range has the prepend flag set so we insert new items into the range. - uint flags = m_defaultFlags; - if (insertion.isMove()) { - // If the insert was part of a move replace the default flags with - // the flags from the source range. - for (QVector::const_iterator move = movedFlags->begin(); - move != movedFlags->end(); - ++move) { - if (move->moveId == insertion.moveId) { - flags = move->flags; - break; - } - } - } - if (flags & ~(AppendFlag | PrependFlag)) { - // If any items are added to groups append an insert notification. - Insert translatedInsert(it, insertion.count, flags, insertion.moveId); - for (int i = 0; i < m_groupCount; ++i) { - if (it->inGroup(i)) - translatedInsert.index[i] += offset; - } - translatedInsertions->append(translatedInsert); - } - if ((it->flags & ~AppendFlag) == flags) { - // Accumulate items on the current range it its flags are the same as - // the insert flags. - it->count += insertion.count; - } else if (offset == 0 - && it->previous != &m_ranges - && it->previous->list == list - && it->previous->end() == insertion.index - && it->previous->flags == flags) { - // Attempt to append to the previous range if the insert position is at - // the start of the current range. - it->previous->count += insertion.count; - it->index += insertion.count; - it.incrementIndexes(insertion.count); - } else { - if (offset > 0) { - // Divide the current range at the insert position. - it.incrementIndexes(offset); - *it = insert(*it, it->list, it->index, offset, it->flags & ~AppendFlag)->next; - } - // Insert a new range, and increment the start index of the current range. - *it = insert(*it, it->list, insertion.index, insertion.count, flags)->next; - it.incrementIndexes(insertion.count, flags); - it->index += offset + insertion.count; - it->count -= offset; - } - m_end.incrementIndexes(insertion.count, flags); - } else { - // The range doesn't have the prepend flag set so split the range into parts; - // one before the insert position and one after the last inserted item. - if (offset > 0) { - *it = insert(*it, it->list, it->index, offset, it->flags)->next; - it->index += offset; - it->count -= offset; - } - it->index += insertion.count; - } - } else if (offset <= 0) { - // The insert position was before the current range so increment the start index. - it->index += insertion.count; - } - } - it.incrementIndexes(it->count); - } - m_cacheIt = m_end; - QT_QML_VERIFY_LISTCOMPOSITOR -} - -/*! - Updates the contents of a compositor when \a count items are inserted into a \a list at - \a index. - - This corrects the indexes of each range for that list in the compositor, splitting the range - in two if the insert index is in the middle of that range. If a range at the insert position - has the Prepend flag set then a new range will be inserted at that position with the groups - specified in defaultGroups(). If the insert index corresponds to the end of a range with - the Append flag set a new range will be inserted before the end of the append range. - - The \a translatedInsertions list is populated with insert notifications for affected - groups. -*/ - -void QQuickListCompositor::listItemsInserted( - void *list, int index, int count, QVector *translatedInsertions) -{ - QT_QML_TRACE_LISTCOMPOSITOR(<< list << index << count) - Q_ASSERT(count > 0); - - QVector insertions; - insertions.append(QQuickChangeSet::Insert(index, count)); - - listItemsInserted(translatedInsertions, list, insertions); -} - -void QQuickListCompositor::listItemsRemoved( - QVector *translatedRemovals, - void *list, - QVector *removals, - QVector *insertions, - QVector *movedFlags) -{ - QT_QML_TRACE_LISTCOMPOSITOR(<< list << *removals) - - for (iterator it(m_ranges.next, 0, Default, m_groupCount); *it != &m_ranges; *it = it->next) { - if (it->list != list || it->flags == CacheFlag) { - // Skip ranges that don't reference list. - it.incrementIndexes(it->count); - continue; - } - bool removed = false; - for (QVector::iterator removal = removals->begin(); - !removed && removal != removals->end(); - ++removal) { - int relativeIndex = removal->index - it->index; - int itemsRemoved = removal->count; - if (relativeIndex + removal->count > 0 && relativeIndex < it->count) { - // If the current range intersects the remove; remove the intersecting items. - const int offset = qMax(0, relativeIndex); - int removeCount = qMin(it->count, relativeIndex + removal->count) - offset; - it->count -= removeCount; - int removeFlags = it->flags & m_removeFlags; - Remove translatedRemoval(it, removeCount, it->flags); - for (int i = 0; i < m_groupCount; ++i) { - if (it->inGroup(i)) - translatedRemoval.index[i] += offset; - } - if (removal->isMove()) { - // If the removal was part of a move find the corresponding insert. - QVector::iterator insertion = insertions->begin(); - for (; insertion != insertions->end() && insertion->moveId != removal->moveId; - ++insertion) {} - Q_ASSERT(insertion != insertions->end()); - Q_ASSERT(insertion->count == removal->count); - - if (relativeIndex < 0) { - // If the remove started before the current range, split it and the - // corresponding insert so we're only working with intersecting part. - int splitMoveId = ++m_moveId; - removal = removals->insert(removal, QQuickChangeSet::Remove( - removal->index, -relativeIndex, splitMoveId)); - ++removal; - removal->count -= -relativeIndex; - insertion = insertions->insert(insertion, QQuickChangeSet::Insert( - insertion->index, -relativeIndex, splitMoveId)); - ++insertion; - insertion->index += -relativeIndex; - insertion->count -= -relativeIndex; - } - - if (it->prepend()) { - // If the current range has the prepend flag preserve its flags to transfer - // to its new location. - removeFlags |= it->flags & CacheFlag; - translatedRemoval.moveId = ++m_moveId; - movedFlags->append(MovedFlags(m_moveId, it->flags & ~AppendFlag)); - - if (removeCount < removal->count) { - // If the remove doesn't encompass all of the current range, - // split it and the corresponding insert. - removal = removals->insert(removal, QQuickChangeSet::Remove( - removal->index, removeCount, translatedRemoval.moveId)); - ++removal; - insertion = insertions->insert(insertion, QQuickChangeSet::Insert( - insertion->index, removeCount, translatedRemoval.moveId)); - ++insertion; - - removal->count -= removeCount; - insertion->index += removeCount; - insertion->count -= removeCount; - } else { - // If there's no need to split the move simply replace its moveId - // with that of the translated move. - removal->moveId = translatedRemoval.moveId; - insertion->moveId = translatedRemoval.moveId; - } - } else { - // If the current range doesn't have prepend flags then insert a new range - // with list indexes from the corresponding insert location. The MoveFlag - // is to notify listItemsInserted that it can skip evaluation of that range. - if (offset > 0) { - *it = insert(*it, it->list, it->index, offset, it->flags & ~AppendFlag)->next; - it->index += offset; - it->count -= offset; - it.incrementIndexes(offset); - } - if (it->previous != &m_ranges - && it->previous->list == it->list - && it->end() == insertion->index - && it->previous->flags == (it->flags | MovedFlag)) { - it->previous->count += removeCount; - } else { - *it = insert(*it, it->list, insertion->index, removeCount, it->flags | MovedFlag)->next; - } - // Clear the changed flags as the item hasn't been removed. - translatedRemoval.flags = 0; - removeFlags = 0; - } - } else if (it->inCache()) { - // If not moving and the current range has the cache flag, insert a new range - // with just the cache flag set to retain caching information for the removed - // range. - if (offset > 0) { - *it = insert(*it, it->list, it->index, offset, it->flags & ~AppendFlag)->next; - it->index += offset; - it->count -= offset; - it.incrementIndexes(offset); - } - if (it->previous != &m_ranges - && it->previous->list == it->list - && it->previous->flags == CacheFlag) { - it->previous->count += removeCount; - } else { - *it = insert(*it, it->list, -1, removeCount, CacheFlag)->next; - } - it.index[Cache] += removeCount; - } - if (removeFlags & GroupMask) - translatedRemovals->append(translatedRemoval); - m_end.decrementIndexes(removeCount, removeFlags); - if (it->count == 0 && !it->append()) { - // Erase empty non-append ranges. - *it = erase(*it)->previous; - removed = true; - } else if (relativeIndex <= 0) { - // If the remove started before the current range move the start index of - // the range to the remove index. - it->index = removal->index; - } - } else if (relativeIndex < 0) { - // If the remove was before the current range decrement the start index by the - // number of items removed. - it->index -= itemsRemoved; - - if (it->previous != &m_ranges - && it->previous->list == it->list - && it->previous->end() == it->index - && it->previous->flags == (it->flags & ~AppendFlag)) { - // Compress ranges made continuous by the removal of separating ranges. - it.decrementIndexes(it->previous->count); - it->previous->count += it->count; - it->previous->flags = it->flags; - *it = erase(*it)->previous; - } - } - } - if (it->flags == CacheFlag && it->next->flags == CacheFlag && it->next->list == it->list) { - // Compress consecutive cache only ranges. - it.index[Cache] += it->next->count; - it->count += it->next->count; - erase(it->next); - } else if (!removed) { - it.incrementIndexes(it->count); - } - } - m_cacheIt = m_end; - QT_QML_VERIFY_LISTCOMPOSITOR -} - - -/*! - Updates the contents of a compositor when \a count items are removed from a \a list at - \a index. - - Ranges that intersect the specified range are removed from the compositor and the indexes of - ranges after index + count are updated. - - The \a translatedRemovals list is populated with remove notifications for the affected - groups. -*/ - - -void QQuickListCompositor::listItemsRemoved( - void *list, int index, int count, QVector *translatedRemovals) -{ - QT_QML_TRACE_LISTCOMPOSITOR(<< list << index << count) - Q_ASSERT(count >= 0); - - QVector removals; - removals.append(QQuickChangeSet::Remove(index, count)); - listItemsRemoved(translatedRemovals, list, &removals, 0, 0); -} - -/*! - Updates the contents of a compositor when \a count items are removed from a \a list at - \a index. - - Ranges that intersect the specified range are removed from the compositor and the indexes of - ranges after index + count are updated. - - The \a translatedRemovals and translatedInserts lists are populated with move - notifications for the affected groups. -*/ - -void QQuickListCompositor::listItemsMoved( - void *list, - int from, - int to, - int count, - QVector *translatedRemovals, - QVector *translatedInsertions) -{ - QT_QML_TRACE_LISTCOMPOSITOR(<< list << from << to << count) - Q_ASSERT(count >= 0); - - QVector removals; - QVector insertions; - QVector movedFlags; - removals.append(QQuickChangeSet::Remove(from, count, 0)); - insertions.append(QQuickChangeSet::Insert(to, count, 0)); - - listItemsRemoved(translatedRemovals, list, &removals, &insertions, &movedFlags); - listItemsInserted(translatedInsertions, list, insertions, &movedFlags); -} - -void QQuickListCompositor::listItemsChanged( - QVector *translatedChanges, - void *list, - const QVector &changes) -{ - QT_QML_TRACE_LISTCOMPOSITOR(<< list << changes) - for (iterator it(m_ranges.next, 0, Default, m_groupCount); *it != &m_ranges; *it = it->next) { - if (it->list != list || it->flags == CacheFlag) { - it.incrementIndexes(it->count); - continue; - } else if (!it->inGroup()) { - continue; - } - foreach (const QQuickChangeSet::Change &change, changes) { - const int offset = change.index - it->index; - if (offset + change.count > 0 && offset < it->count) { - const int changeOffset = qMax(0, offset); - const int changeCount = qMin(it->count, offset + change.count) - changeOffset; - - Change translatedChange(it, changeCount, it->flags); - for (int i = 0; i < m_groupCount; ++i) { - if (it->inGroup(i)) - translatedChange.index[i] += changeOffset; - } - translatedChanges->append(translatedChange); - } - } - it.incrementIndexes(it->count); - } -} - - -/*! - Translates the positions of \a count changed items at \a index in a \a list. - - The \a translatedChanges list is populated with change notifications for the - affected groups. -*/ - -void QQuickListCompositor::listItemsChanged( - void *list, int index, int count, QVector *translatedChanges) -{ - QT_QML_TRACE_LISTCOMPOSITOR(<< list << index << count) - Q_ASSERT(count >= 0); - QVector changes; - changes.append(QQuickChangeSet::Change(index, count)); - listItemsChanged(translatedChanges, list, changes); -} - -void QQuickListCompositor::transition( - Group from, - Group to, - QVector *removes, - QVector *inserts) -{ - int removeCount = 0; - for (iterator it(m_ranges.next, 0, Default, m_groupCount); *it != &m_ranges; *it = it->next) { - if (it == from && it != to) { - removes->append(QQuickChangeSet::Remove(it.index[from]- removeCount, it->count)); - removeCount += it->count; - } else if (it != from && it == to) { - inserts->append(QQuickChangeSet::Insert(it.index[to], it->count)); - } - it.incrementIndexes(it->count); - } -} - -/*! - \internal - Writes the name of \a group to \a debug. -*/ - -QDebug operator <<(QDebug debug, const QQuickListCompositor::Group &group) -{ - switch (group) { - case QQuickListCompositor::Cache: return debug << "Cache"; - case QQuickListCompositor::Default: return debug << "Default"; - default: return (debug.nospace() << "Group" << int(group)).space(); - } - -} - -/*! - \internal - Writes the contents of \a range to \a debug. -*/ - -QDebug operator <<(QDebug debug, const QQuickListCompositor::Range &range) -{ - (debug.nospace() - << "Range(" - << range.list) << ' ' - << range.index << ' ' - << range.count << ' ' - << (range.isUnresolved() ? 'U' : '0') - << (range.append() ? 'A' : '0') - << (range.prepend() ? 'P' : '0'); - for (int i = QQuickListCompositor::MaximumGroupCount - 1; i >= 2; --i) - debug << (range.inGroup(i) ? '1' : '0'); - return (debug - << (range.inGroup(QQuickListCompositor::Default) ? 'D' : '0') - << (range.inGroup(QQuickListCompositor::Cache) ? 'C' : '0')); -} - -static void qt_print_indexes(QDebug &debug, int count, const int *indexes) -{ - for (int i = count - 1; i >= 0; --i) - debug << indexes[i]; -} - -/*! - \internal - Writes the contents of \a it to \a debug. -*/ - -QDebug operator <<(QDebug debug, const QQuickListCompositor::iterator &it) -{ - (debug.nospace() << "iterator(" << it.group).space() << "offset:" << it.offset; - qt_print_indexes(debug, it.groupCount, it.index); - return ((debug << **it).nospace() << ')').space(); -} - -static QDebug qt_print_change(QDebug debug, const char *name, const QQuickListCompositor::Change &change) -{ - debug.nospace() << name << '(' << change.moveId << ' ' << change.count << ' '; - for (int i = QQuickListCompositor::MaximumGroupCount - 1; i >= 2; --i) - debug << (change.inGroup(i) ? '1' : '0'); - debug << (change.inGroup(QQuickListCompositor::Default) ? 'D' : '0') - << (change.inGroup(QQuickListCompositor::Cache) ? 'C' : '0'); - int i = QQuickListCompositor::MaximumGroupCount - 1; - for (; i >= 0 && !change.inGroup(i); --i) {} - for (; i >= 0; --i) - debug << ' ' << change.index[i]; - return (debug << ')').maybeSpace(); -} - -/*! - \internal - Writes the contents of \a change to \a debug. -*/ - -QDebug operator <<(QDebug debug, const QQuickListCompositor::Change &change) -{ - return qt_print_change(debug, "Change", change); -} - -/*! - \internal - Writes the contents of \a remove to \a debug. -*/ - -QDebug operator <<(QDebug debug, const QQuickListCompositor::Remove &remove) -{ - return qt_print_change(debug, "Remove", remove); -} - -/*! - \internal - Writes the contents of \a insert to \a debug. -*/ - -QDebug operator <<(QDebug debug, const QQuickListCompositor::Insert &insert) -{ - return qt_print_change(debug, "Insert", insert); -} - -/*! - \internal - Writes the contents of \a list to \a debug. -*/ - -QDebug operator <<(QDebug debug, const QQuickListCompositor &list) -{ - int indexes[QQuickListCompositor::MaximumGroupCount]; - for (int i = 0; i < QQuickListCompositor::MaximumGroupCount; ++i) - indexes[i] = 0; - debug.nospace() << "QQuickListCompositor("; - qt_print_indexes(debug, list.m_groupCount, list.m_end.index); - for (QQuickListCompositor::Range *range = list.m_ranges.next; range != &list.m_ranges; range = range->next) { - (debug << '\n').space(); - qt_print_indexes(debug, list.m_groupCount, indexes); - debug << ' ' << *range; - - for (int i = 0; i < list.m_groupCount; ++i) { - if (range->inGroup(i)) - indexes[i] += range->count; - } - } - return (debug << ')').maybeSpace(); -} - -QT_END_NAMESPACE diff --git a/src/quick/util/qquicklistcompositor_p.h b/src/quick/util/qquicklistcompositor_p.h deleted file mode 100644 index b2fe69d6a2..0000000000 --- a/src/quick/util/qquicklistcompositor_p.h +++ /dev/null @@ -1,375 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** 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, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQUICKLISTCOMPOSITOR_P_H -#define QQUICKLISTCOMPOSITOR_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include -#include - -#include - -#include - -QT_BEGIN_NAMESPACE - -class Q_AUTOTEST_EXPORT QQuickListCompositor -{ -public: - enum { MinimumGroupCount = 3, MaximumGroupCount = 11 }; - - enum Group - { - Cache = 0, - Default = 1, - Persisted = 2 - }; - - enum Flag - { - CacheFlag = 1 << Cache, - DefaultFlag = 1 << Default, - PersistedFlag = 1 << Persisted, - PrependFlag = 0x10000000, - AppendFlag = 0x20000000, - UnresolvedFlag = 0x40000000, - MovedFlag = 0x80000000, - GroupMask = ~(PrependFlag | AppendFlag | UnresolvedFlag | MovedFlag | CacheFlag) - }; - - class Range - { - public: - Range() : next(this), previous(this), list(0), index(0), count(0), flags(0) {} - Range(Range *next, void *list, int index, int count, uint flags) - : next(next), previous(next->previous), list(list), index(index), count(count), flags(flags) { - next->previous = this; previous->next = this; } - - Range *next; - Range *previous; - void *list; - int index; - int count; - uint flags; - - inline int start() const { return index; } - inline int end() const { return index + count; } - - inline int groups() const { return flags & GroupMask; } - - inline bool inGroup() const { return flags & GroupMask; } - inline bool inCache() const { return flags & CacheFlag; } - inline bool inGroup(int group) const { return flags & (1 << group); } - inline bool isUnresolved() const { return flags & UnresolvedFlag; } - - inline bool prepend() const { return flags & PrependFlag; } - inline bool append() const { return flags & AppendFlag; } - }; - - class Q_AUTOTEST_EXPORT iterator - { - public: - inline iterator(); - inline iterator(const iterator &it); - inline iterator(Range *range, int offset, Group group, int groupCount); - inline ~iterator() {} - - bool operator ==(const iterator &it) const { return range == it.range && offset == it.offset; } - bool operator !=(const iterator &it) const { return range != it.range || offset != it.offset; } - - bool operator ==(Group group) const { return range->flags & (1 << group); } - bool operator !=(Group group) const { return !(range->flags & (1 << group)); } - - Range *&operator *() { return range; } - Range * const &operator *() const { return range; } - Range *operator ->() { return range; } - const Range *operator ->() const { return range; } - - iterator &operator +=(int difference); - - template T *list() const { return static_cast(range->list); } - int modelIndex() const { return range->index + offset; } - - void incrementIndexes(int difference) { incrementIndexes(difference, range->flags); } - void decrementIndexes(int difference) { decrementIndexes(difference, range->flags); } - - inline void incrementIndexes(int difference, uint flags); - inline void decrementIndexes(int difference, uint flags); - - void setGroup(Group g) { group = g; groupFlag = 1 << g; } - - Range *range; - int offset; - Group group; - int groupFlag; - int groupCount; - union { - struct { - int cacheIndex; - }; - int index[MaximumGroupCount]; - }; - }; - - class Q_AUTOTEST_EXPORT insert_iterator : public iterator - { - public: - inline insert_iterator() {} - inline insert_iterator(const iterator &it) : iterator(it) {} - inline insert_iterator(Range *, int, Group, int); - inline ~insert_iterator() {} - - insert_iterator &operator +=(int difference); - }; - - struct Change - { - inline Change() {} - inline Change(iterator it, int count, uint flags, int moveId = -1); - int count; - uint flags; - int moveId; - union { - struct { - int cacheIndex; - }; - int index[MaximumGroupCount]; - }; - - inline bool isMove() const { return moveId >= 0; } - inline bool inCache() const { return flags & CacheFlag; } - inline bool inGroup() const { return flags & GroupMask; } - inline bool inGroup(int group) const { return flags & (CacheFlag << group); } - - inline int groups() const { return flags & GroupMask; } - }; - - struct Insert : public Change - { - Insert() {} - Insert(iterator it, int count, uint flags, int moveId = -1) - : Change(it, count, flags, moveId) {} - }; - - struct Remove : public Change - { - Remove() {} - Remove(iterator it, int count, uint flags, int moveId = -1) - : Change(it, count, flags, moveId) {} - }; - - QQuickListCompositor(); - ~QQuickListCompositor(); - - int defaultGroups() const { return m_defaultFlags & ~PrependFlag; } - void setDefaultGroups(int groups) { m_defaultFlags = groups | PrependFlag; } - void setDefaultGroup(Group group) { m_defaultFlags |= (1 << group); } - void clearDefaultGroup(Group group) { m_defaultFlags &= ~(1 << group); } - void setRemoveGroups(int groups) { m_removeFlags = PrependFlag | AppendFlag | groups; } - void setGroupCount(int count); - - int count(Group group) const; - iterator find(Group group, int index); - iterator find(Group group, int index) const; - insert_iterator findInsertPosition(Group group, int index); - - const iterator &end() { return m_end; } - - void append(void *list, int index, int count, uint flags, QVector *inserts = 0); - void insert(Group group, int before, void *list, int index, int count, uint flags, QVector *inserts = 0); - iterator insert(iterator before, void *list, int index, int count, uint flags, QVector *inserts = 0); - - void setFlags(Group fromGroup, int from, int count, Group group, int flags, QVector *inserts = 0); - void setFlags(iterator from, int count, Group group, uint flags, QVector *inserts = 0); - void setFlags(Group fromGroup, int from, int count, uint flags, QVector *inserts = 0) { - setFlags(fromGroup, from, count, fromGroup, flags, inserts); } - void setFlags(iterator from, int count, uint flags, QVector *inserts = 0) { - setFlags(from, count, from.group, flags, inserts); } - - void clearFlags(Group fromGroup, int from, int count, Group group, uint flags, QVector *removals = 0); - void clearFlags(iterator from, int count, Group group, uint flags, QVector *removals = 0); - void clearFlags(Group fromGroup, int from, int count, uint flags, QVector *removals = 0) { - clearFlags(fromGroup, from, count, fromGroup, flags, removals); } - void clearFlags(iterator from, int count, uint flags, QVector *removals = 0) { - clearFlags(from, count, from.group, flags, removals); } - - bool verifyMoveTo(Group fromGroup, int from, Group toGroup, int to, int count, Group group) const; - - void move( - Group fromGroup, - int from, - Group toGroup, - int to, - int count, - Group group, - QVector *removals = 0, - QVector *inserts = 0); - void clear(); - - void listItemsInserted(void *list, int index, int count, QVector *inserts); - void listItemsRemoved(void *list, int index, int count, QVector *removals); - void listItemsMoved(void *list, int from, int to, int count, QVector *removals, QVector *inserts); - void listItemsChanged(void *list, int index, int count, QVector *changes); - - void transition( - Group from, - Group to, - QVector *removes, - QVector *inserts); - -private: - Range m_ranges; - iterator m_end; - iterator m_cacheIt; - int m_groupCount; - int m_defaultFlags; - int m_removeFlags; - int m_moveId; - - inline Range *insert(Range *before, void *list, int index, int count, uint flags); - inline Range *erase(Range *range); - - struct MovedFlags - { - MovedFlags() {} - MovedFlags(int moveId, uint flags) : moveId(moveId), flags(flags) {} - - int moveId; - uint flags; - }; - - void listItemsRemoved( - QVector *translatedRemovals, - void *list, - QVector *removals, - QVector *insertions = 0, - QVector *movedFlags = 0); - void listItemsInserted( - QVector *translatedInsertions, - void *list, - const QVector &insertions, - const QVector *movedFlags = 0); - void listItemsChanged( - QVector *translatedChanges, - void *list, - const QVector &changes); - - friend Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QQuickListCompositor &list); -}; - -Q_DECLARE_TYPEINFO(QQuickListCompositor::Change, Q_PRIMITIVE_TYPE); -Q_DECLARE_TYPEINFO(QQuickListCompositor::Remove, Q_PRIMITIVE_TYPE); -Q_DECLARE_TYPEINFO(QQuickListCompositor::Insert, Q_PRIMITIVE_TYPE); - -inline QQuickListCompositor::iterator::iterator() - : range(0), offset(0), group(Default), groupCount(0) {} -inline QQuickListCompositor::iterator::iterator(const iterator &it) - : range(it.range) - , offset(it.offset) - , group(it.group) - , groupFlag(it.groupFlag) - , groupCount(it.groupCount) -{ - for (int i = 0; i < groupCount; ++i) - index[i] = it.index[i]; -} - -inline QQuickListCompositor::iterator::iterator( - Range *range, int offset, Group group, int groupCount) - : range(range) - , offset(offset) - , group(group) - , groupFlag(1 << group) - , groupCount(groupCount) -{ - for (int i = 0; i < groupCount; ++i) - index[i] = 0; -} - -inline void QQuickListCompositor::iterator::incrementIndexes(int difference, uint flags) -{ - for (int i = 0; i < groupCount; ++i) { - if (flags & (1 << i)) - index[i] += difference; - } -} - -inline void QQuickListCompositor::iterator::decrementIndexes(int difference, uint flags) -{ - for (int i = 0; i < groupCount; ++i) { - if (flags & (1 << i)) - index[i] -= difference; - } -} - -inline QQuickListCompositor::insert_iterator::insert_iterator( - Range *range, int offset, Group group, int groupCount) - : iterator(range, offset, group, groupCount) {} - -inline QQuickListCompositor::Change::Change(iterator it, int count, uint flags, int moveId) - : count(count), flags(flags), moveId(moveId) -{ - for (int i = 0; i < MaximumGroupCount; ++i) - index[i] = it.index[i]; -} - -Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QQuickListCompositor::Group &group); -Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QQuickListCompositor::Range &range); -Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QQuickListCompositor::iterator &it); -Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QQuickListCompositor::Change &change); -Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QQuickListCompositor::Remove &remove); -Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QQuickListCompositor::Insert &insert); -Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QQuickListCompositor &list); - -QT_END_NAMESPACE - -#endif diff --git a/src/quick/util/qquickpackage.cpp b/src/quick/util/qquickpackage.cpp deleted file mode 100644 index e885524b27..0000000000 --- a/src/quick/util/qquickpackage.cpp +++ /dev/null @@ -1,198 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** 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, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qquickpackage_p.h" - -#include -#include - -QT_BEGIN_NAMESPACE - -/*! - \qmltype Package - \instantiates QQuickPackage - \inqmlmodule QtQuick 2 - \ingroup qtquick-views - \brief Specifies a collection of named items - - The Package class is used in conjunction with - VisualDataModel to enable delegates with a shared context - to be provided to multiple views. - - Any item within a Package may be assigned a name via the - \l{Package::name}{Package.name} attached property. - - The example below creates a Package containing two named items; - \e list and \e grid. The third item in the package (the \l Rectangle) is parented to whichever - delegate it should appear in. This allows an item to move - between views. - - \snippet quick/views/package/Delegate.qml 0 - - These named items are used as the delegates by the two views who - reference the special \l{VisualDataModel::parts} property to select - a model which provides the chosen delegate. - - \snippet quick/views/package/view.qml 0 - - \sa {quick/views/package}{Package example}, {quick/demos/photoviewer}{Photo Viewer example}, QtQml -*/ - -/*! - \qmlattachedproperty string QtQuick2::Package::name - This attached property holds the name of an item within a Package. -*/ - - -class QQuickPackagePrivate : public QObjectPrivate -{ -public: - QQuickPackagePrivate() {} - - struct DataGuard : public QQmlGuard - { - DataGuard(QObject *obj, QList *l) : list(l) { (QQmlGuard&)*this = obj; } - QList *list; - void objectDestroyed(QObject *) { - // we assume priv will always be destroyed after objectDestroyed calls - list->removeOne(*this); - } - }; - - QList dataList; - static void data_append(QQmlListProperty *prop, QObject *o) { - QList *list = static_cast *>(prop->data); - list->append(DataGuard(o, list)); - } - static void data_clear(QQmlListProperty *prop) { - QList *list = static_cast *>(prop->data); - list->clear(); - } - static QObject *data_at(QQmlListProperty *prop, int index) { - QList *list = static_cast *>(prop->data); - return list->at(index); - } - static int data_count(QQmlListProperty *prop) { - QList *list = static_cast *>(prop->data); - return list->count(); - } -}; - -QHash QQuickPackageAttached::attached; - -QQuickPackageAttached::QQuickPackageAttached(QObject *parent) -: QObject(parent) -{ - attached.insert(parent, this); -} - -QQuickPackageAttached::~QQuickPackageAttached() -{ - attached.remove(parent()); -} - -QString QQuickPackageAttached::name() const -{ - return _name; -} - -void QQuickPackageAttached::setName(const QString &n) -{ - _name = n; -} - -QQuickPackage::QQuickPackage(QObject *parent) - : QObject(*(new QQuickPackagePrivate), parent) -{ -} - -QQuickPackage::~QQuickPackage() -{ -} - -QQmlListProperty QQuickPackage::data() -{ - Q_D(QQuickPackage); - return QQmlListProperty(this, &d->dataList, QQuickPackagePrivate::data_append, - QQuickPackagePrivate::data_count, - QQuickPackagePrivate::data_at, - QQuickPackagePrivate::data_clear); -} - -bool QQuickPackage::hasPart(const QString &name) -{ - Q_D(QQuickPackage); - for (int ii = 0; ii < d->dataList.count(); ++ii) { - QObject *obj = d->dataList.at(ii); - QQuickPackageAttached *a = QQuickPackageAttached::attached.value(obj); - if (a && a->name() == name) - return true; - } - return false; -} - -QObject *QQuickPackage::part(const QString &name) -{ - Q_D(QQuickPackage); - if (name.isEmpty() && !d->dataList.isEmpty()) - return d->dataList.at(0); - - for (int ii = 0; ii < d->dataList.count(); ++ii) { - QObject *obj = d->dataList.at(ii); - QQuickPackageAttached *a = QQuickPackageAttached::attached.value(obj); - if (a && a->name() == name) - return obj; - } - - if (name == QLatin1String("default") && !d->dataList.isEmpty()) - return d->dataList.at(0); - - return 0; -} - -QQuickPackageAttached *QQuickPackage::qmlAttachedProperties(QObject *o) -{ - return new QQuickPackageAttached(o); -} - - - -QT_END_NAMESPACE diff --git a/src/quick/util/qquickpackage_p.h b/src/quick/util/qquickpackage_p.h deleted file mode 100644 index a777ff4a7e..0000000000 --- a/src/quick/util/qquickpackage_p.h +++ /dev/null @@ -1,96 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** 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, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQUICKPACKAGE_H -#define QQUICKPACKAGE_H - -#include - -QT_BEGIN_HEADER - -QT_BEGIN_NAMESPACE - -class QQuickPackagePrivate; -class QQuickPackageAttached; -class Q_AUTOTEST_EXPORT QQuickPackage : public QObject -{ - Q_OBJECT - Q_DECLARE_PRIVATE(QQuickPackage) - - Q_CLASSINFO("DefaultProperty", "data") - Q_PROPERTY(QQmlListProperty data READ data) - -public: - QQuickPackage(QObject *parent=0); - virtual ~QQuickPackage(); - - QQmlListProperty data(); - - QObject *part(const QString & = QString()); - bool hasPart(const QString &); - - static QQuickPackageAttached *qmlAttachedProperties(QObject *); -}; - -class QQuickPackageAttached : public QObject -{ -Q_OBJECT -Q_PROPERTY(QString name READ name WRITE setName) -public: - QQuickPackageAttached(QObject *parent); - virtual ~QQuickPackageAttached(); - - QString name() const; - void setName(const QString &n); - - static QHash attached; -private: - QString _name; -}; - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QQuickPackage) -QML_DECLARE_TYPEINFO(QQuickPackage, QML_HAS_ATTACHED_PROPERTIES) - -QT_END_HEADER - -#endif // QQUICKPACKAGE_H diff --git a/src/quick/util/qquickutilmodule.cpp b/src/quick/util/qquickutilmodule.cpp index 5591f04a57..a45ec4ef15 100644 --- a/src/quick/util/qquickutilmodule.cpp +++ b/src/quick/util/qquickutilmodule.cpp @@ -45,7 +45,6 @@ #include "qquickbehavior_p.h" #include "qquicksmoothedanimation_p.h" #include "qquickfontloader_p.h" -#include "qquickpackage_p.h" #include "qquickpropertychanges_p.h" #include "qquickspringanimation_p.h" #include "qquickstategroup_p.h" @@ -73,7 +72,6 @@ void QQuickUtilModule::defineModule() qmlRegisterType("QtQuick",2,0,"SmoothedAnimation"); qmlRegisterType("QtQuick",2,0,"FontLoader"); qmlRegisterType("QtQuick",2,0,"NumberAnimation"); - qmlRegisterType("QtQuick",2,0,"Package"); qmlRegisterType("QtQuick",2,0,"ParallelAnimation"); qmlRegisterType("QtQuick",2,0,"PauseAnimation"); qmlRegisterType("QtQuick",2,0,"PropertyAction"); diff --git a/src/quick/util/util.pri b/src/quick/util/util.pri index 7a56a95314..7f77f7f702 100644 --- a/src/quick/util/util.pri +++ b/src/quick/util/util.pri @@ -1,7 +1,6 @@ SOURCES += \ $$PWD/qquickapplication.cpp\ $$PWD/qquickutilmodule.cpp\ - $$PWD/qquickpackage.cpp \ $$PWD/qquickanimation.cpp \ $$PWD/qquicksystempalette.cpp \ $$PWD/qquickspringanimation.cpp \ @@ -13,15 +12,12 @@ SOURCES += \ $$PWD/qquickpropertychanges.cpp \ $$PWD/qquickstategroup.cpp \ $$PWD/qquicktransition.cpp \ - $$PWD/qquicklistaccessor.cpp \ $$PWD/qquicktimeline.cpp \ $$PWD/qquickpixmapcache.cpp \ $$PWD/qquickbehavior.cpp \ $$PWD/qquickfontloader.cpp \ $$PWD/qquickstyledtext.cpp \ $$PWD/qquickpath.cpp \ - $$PWD/qquickchangeset.cpp \ - $$PWD/qquicklistcompositor.cpp \ $$PWD/qquickpathinterpolator.cpp \ $$PWD/qquickimageprovider.cpp \ $$PWD/qquicksvgparser.cpp \ @@ -31,7 +27,6 @@ SOURCES += \ HEADERS += \ $$PWD/qquickapplication_p.h\ $$PWD/qquickutilmodule_p.h\ - $$PWD/qquickpackage_p.h \ $$PWD/qquickanimation_p.h \ $$PWD/qquickanimation_p_p.h \ $$PWD/qquicksystempalette_p.h \ @@ -46,7 +41,6 @@ HEADERS += \ $$PWD/qquicktransitionmanager_p_p.h \ $$PWD/qquickstategroup_p.h \ $$PWD/qquicktransition_p.h \ - $$PWD/qquicklistaccessor_p.h \ $$PWD/qquicktimeline_p_p.h \ $$PWD/qquickpixmapcache_p.h \ $$PWD/qquickbehavior_p.h \ @@ -54,8 +48,6 @@ HEADERS += \ $$PWD/qquickstyledtext_p.h \ $$PWD/qquickpath_p.h \ $$PWD/qquickpath_p_p.h \ - $$PWD/qquickchangeset_p.h \ - $$PWD/qquicklistcompositor_p.h \ $$PWD/qquickpathinterpolator_p.h \ $$PWD/qquickimageprovider.h \ $$PWD/qquicksvgparser_p.h \ diff --git a/tests/auto/qml/qml.pro b/tests/auto/qml/qml.pro index 5d62b91094..da705cefdb 100644 --- a/tests/auto/qml/qml.pro +++ b/tests/auto/qml/qml.pro @@ -42,9 +42,9 @@ PRIVATETESTS += \ qqmlvaluetypes \ qqmlvaluetypeproviders \ qqmlbinding \ - qquickchangeset \ + qqmlchangeset \ qqmlconnections \ - qquicklistcompositor \ + qqmllistcompositor \ qqmllistmodel \ qqmllistmodelworkerscript \ qquickworkerscript \ diff --git a/tests/auto/qml/qqmlchangeset/qqmlchangeset.pro b/tests/auto/qml/qqmlchangeset/qqmlchangeset.pro new file mode 100644 index 0000000000..b65e58c0b3 --- /dev/null +++ b/tests/auto/qml/qqmlchangeset/qqmlchangeset.pro @@ -0,0 +1,10 @@ +CONFIG += testcase +TARGET = tst_qqmlhangeset +macx:CONFIG -= app_bundle + +SOURCES += tst_qqmlchangeset.cpp + +CONFIG += parallel_test + +QT += core-private gui-private qml-private testlib +DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 diff --git a/tests/auto/qml/qqmlchangeset/tst_qqmlchangeset.cpp b/tests/auto/qml/qqmlchangeset/tst_qqmlchangeset.cpp new file mode 100644 index 0000000000..0f09de26d3 --- /dev/null +++ b/tests/auto/qml/qqmlchangeset/tst_qqmlchangeset.cpp @@ -0,0 +1,1574 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include +#include + +class tst_qqmlchangeset : public QObject +{ + Q_OBJECT +private slots: + void sequence_data(); + void sequence(); + + void apply_data(); + void apply(); + + void removeConsecutive_data(); + void removeConsecutive(); + void insertConsecutive_data(); + void insertConsecutive(); + + void copy(); + void debug(); + + // These create random sequences and verify a list with the reordered changes applied is the + // same as one with the unordered changes applied. +private: + void random_data(); + void random(); + +public: + enum { + MoveOp = 0, + InsertOp = -1, + RemoveOp = -2 + }; + + struct Signal + { + int index; + int count; + int to; + int moveId; + int offset; + + bool isInsert() const { return to == -1; } + bool isRemove() const { return to == -2; } + bool isChange() const { return to == -3; } + bool isMove() const { return to >= 0; } + }; + + static Signal Insert(int index, int count) { + Signal signal = { index, count, -1, -1, 0 }; return signal; } + static Signal Insert(int index, int count, int moveId, int moveOffset) { + Signal signal = { index, count, -1, moveId, moveOffset }; return signal; } + static Signal Remove(int index, int count) { + Signal signal = { index, count, -2, -1, 0 }; return signal; } + static Signal Remove(int index, int count, int moveId, int moveOffset) { + Signal signal = { index, count, -2, moveId, moveOffset }; return signal; } + static Signal Move(int from, int to, int count, int moveId) { + Signal signal = { from, count, to, moveId, 0 }; return signal; } + static Signal Change(int index, int count) { + Signal signal = { index, count, -3, -1, 0 }; return signal; } + + typedef QVector SignalList; + typedef QVector SignalListList; + + template + void move(int from, int to, int n, T *items) + { + if (from > to) { + // Only move forwards - flip if backwards moving + int tfrom = from; + int tto = to; + from = tto; + to = tto+n; + n = tfrom-tto; + } + + T replaced; + int i=0; + typename T::ConstIterator it=items->begin(); it += from+n; + for (; ibegin(); it += from; + for (; ibegin(); t += from; + for (; f != replaced.end(); ++f, ++t) + *t = *f; + } + + bool applyChanges(QVector &list, const QVector > &changes) + { + foreach (const SignalList &sl, changes) { + if (!applyChanges(list, sl)) + return false; + } + return true; + } + + bool applyChanges(QVector &list, const QVector &changes) + { + QHash removedValues; + foreach (const Signal &signal, changes) { + if (signal.isInsert()) { + if (signal.index < 0 || signal.index > list.count()) { + qDebug() << "insert out of range" << signal.index << list.count(); + return false; + } + if (signal.moveId != -1) { + QQmlChangeSet::Insert insert(signal.index, signal.count, signal.moveId, signal.offset); + for (int i = insert.start(); i < insert.end(); ++i) + list.insert(i, removedValues.take(insert.moveKey(i))); + } else { + list.insert(signal.index, signal.count, 100); + } + } else if (signal.isRemove()) { + if (signal.index < 0 || signal.index + signal.count > list.count()) { + qDebug() << "remove out of range" << signal.index << signal.count << list.count(); + return false; + } + if (signal.moveId != -1) { + QQmlChangeSet::Remove remove(signal.index, signal.count, signal.moveId, signal.offset); + for (int i = remove.start(); i < remove.end(); ++i) + removedValues.insert(remove.moveKey(i), list.at(i)); + } + list.erase(list.begin() + signal.index, list.begin() + signal.index + signal.count); + } else if (signal.isMove()) { + if (signal.index < 0 + || signal.to < 0 + || signal.index + signal.count > list.count() + || signal.to + signal.count > list.count()) { + qDebug() << "move out of range" << signal.index << signal.to << signal.count << list.count(); + return false; + } + move(signal.index, signal.to, signal.count, &list); + } else if (signal.isChange()) { + for (int i = signal.index; i < signal.index + signal.count; ++i) + list[i] = 100; + } + } + return true; + } + +}; + +bool operator ==(const tst_qqmlchangeset::Signal &left, const tst_qqmlchangeset::Signal &right) +{ + return left.index == right.index + && left.count == right.count + && left.to == right.to + && left.moveId == right.moveId + && (left.moveId == -1 || left.offset == right.offset); +} + +QT_BEGIN_NAMESPACE +bool operator ==(const QQmlChangeSet::Change &left, const QQmlChangeSet::Change &right) +{ + return left.index == right.index && left.count == right.count && left.moveId == right.moveId; +} +QT_END_NAMESPACE + +QDebug operator <<(QDebug debug, const tst_qqmlchangeset::Signal &signal) +{ + if (signal.isInsert() && signal.moveId == -1) + debug.nospace() << "Insert(" << signal.index << "," << signal.count << ")"; + else if (signal.isInsert()) + debug.nospace() << "Insert(" << signal.index << "," << signal.count << "," << signal.moveId << "," << signal.offset << ")"; + else if (signal.isRemove() && signal.moveId == -1) + debug.nospace() << "Remove(" << signal.index << "," << signal.count << ")"; + else if (signal.isRemove()) + debug.nospace() << "Remove(" << signal.index << "," << signal.count << "," << signal.moveId << "," << signal.offset << ")"; + else if (signal.isMove()) + debug.nospace() << "Move(" << signal.index << "," << signal.to << "," << signal.count << "," << signal.moveId << ")"; + else if (signal.isChange()) + debug.nospace() << "Change(" << signal.index << "," << signal.count << ")"; + return debug; +} + +Q_DECLARE_METATYPE(tst_qqmlchangeset::SignalList) +Q_DECLARE_METATYPE(tst_qqmlchangeset::SignalListList) + +#if 0 +# define VERIFY_EXPECTED_OUTPUT \ + QVector inputList; \ + for (int i = 0; i < 40; ++i) \ + inputList.append(i); \ + QVector outputList = inputList; \ + if (!applyChanges(inputList, input)) { \ + qDebug() << input; \ + qDebug() << output; \ + qDebug() << changes; \ + QVERIFY(false); \ + } else if (!applyChanges(outputList, output)) { \ + qDebug() << input; \ + qDebug() << output; \ + qDebug() << changes; \ + QVERIFY(false); \ + } else if (outputList != inputList /* || changes != output*/) { \ + qDebug() << input; \ + qDebug() << output; \ + qDebug() << changes; \ + qDebug() << inputList; \ + qDebug() << outputList; \ + QVERIFY(false); \ + } else if (changes != output) { \ + qDebug() << output; \ + qDebug() << changes; \ + QCOMPARE(outputList, inputList); \ + } +#else +# define VERIFY_EXPECTED_OUTPUT \ + if (changes != output) { \ + qDebug() << output; \ + qDebug() << changes; \ + } +#endif + +void tst_qqmlchangeset::sequence_data() +{ + QTest::addColumn("input"); + QTest::addColumn("output"); + + // Insert + QTest::newRow("i(12,5)") + << (SignalList() << Insert(12,5)) + << (SignalList() << Insert(12,5)); + QTest::newRow("i(2,3),i(12,5)") + << (SignalList() << Insert(2,3) << Insert(12,5)) + << (SignalList() << Insert(2,3) << Insert(12,5)); + QTest::newRow("i(12,5),i(2,3)") + << (SignalList() << Insert(12,5) << Insert(2,3)) + << (SignalList() << Insert(2,3) << Insert(15,5)); + QTest::newRow("i(12,5),i(12,3)") + << (SignalList() << Insert(12,5) << Insert(12,3)) + << (SignalList() << Insert(12,8)); + QTest::newRow("i(12,5),i(17,3)") + << (SignalList() << Insert(12,5) << Insert(17,3)) + << (SignalList() << Insert(12,8)); + QTest::newRow("i(12,5),i(15,3)") + << (SignalList() << Insert(12,5) << Insert(15,3)) + << (SignalList() << Insert(12,8)); + + // Remove + QTest::newRow("r(3,9)") + << (SignalList() << Remove(3,9)) + << (SignalList() << Remove(3,9)); + QTest::newRow("r(3,4),r(3,2)") + << (SignalList() << Remove(3,4) << Remove(3,2)) + << (SignalList() << Remove(3,6)); + QTest::newRow("r(4,3),r(14,5)") + << (SignalList() << Remove(4,3) << Remove(14,5)) + << (SignalList() << Remove(4,3) << Remove(14,5)); + QTest::newRow("r(14,5),r(4,3)") + << (SignalList() << Remove(14,5) << Remove(4,3)) + << (SignalList() << Remove(4,3) << Remove(11,5)); + QTest::newRow("r(4,3),r(2,9)") + << (SignalList() << Remove(4,3) << Remove(2,9)) + << (SignalList() << Remove(2,12)); + + // Move + QTest::newRow("m(8-10,2)") + << (SignalList() << Move(8,10,2,0)) + << (SignalList() << Remove(8,2,0,0) << Insert(10,2,0,0)); + + QTest::newRow("m(23-12,6),m(13-15,5)") + << (SignalList() << Move(23,12,6,0) << Move(13,15,5,1)) + << (SignalList() << Remove(23,6,0,0) << Insert(12,1,0,0) << Insert(15,5,0,1)); + QTest::newRow("m(23-12,6),m(13-15,2)") + << (SignalList() << Move(23,12,6,0) << Move(13,20,2,1)) + << (SignalList() << Remove(23,6,0,0) << Insert(12,1,0,0) << Insert(13,3,0,3) << Insert(20,2,0,1)); + QTest::newRow("m(23-12,6),m(13-2,2)") + << (SignalList() << Move(23,12,6,0) << Move(13,2,2,1)) + << (SignalList() << Remove(23,6,0,0) << Insert(2,2,0,1) << Insert(14,1,0,0) << Insert(15,3,0,3)); + QTest::newRow("m(23-12,6),m(12-6,5)") + << (SignalList() << Move(23,12,6,0) << Move(12,6,5,1)) + << (SignalList() << Remove(23,6,0,0) << Insert(6,5,0,0) << Insert(17,1,0,5)); + QTest::newRow("m(23-12,6),m(10-5,4)") + << (SignalList() << Move(23,12,6,0) << Move(10,5,4,1)) + << (SignalList() << Remove(10,2,1,0) << Remove(21,6,0,0) << Insert(5,2,1,0) << Insert(7,2,0,0) << Insert(14,4,0,2)); + QTest::newRow("m(23-12,6),m(16-5,4)") + << (SignalList() << Move(23,12,6,0) << Move(16,5,4,1)) + << (SignalList() << Remove(12,2,1,2) << Remove(21,6,0,0) << Insert(5,2,0,4) << Insert(7,2,1,2) << Insert(16,4,0,0)); + QTest::newRow("m(23-12,6),m(13-5,4)") + << (SignalList() << Move(23,12,6,0) << Move(13,5,4,1)) + << (SignalList() << Remove(23,6,0,0) << Insert(5,4,0,1) << Insert(16,1,0,0) << Insert(17,1,0,5)); + QTest::newRow("m(23-12,6),m(14-5,4)") + << (SignalList() << Move(23,12,6,0) << Move(14,5,4,1)) + << (SignalList() << Remove(23,6,0,0) << Insert(5,4,0,2) << Insert(16,2,0,0)); + QTest::newRow("m(23-12,6),m(12-5,4)") + << (SignalList() << Move(23,12,6,0) << Move(12,5,4,1)) + << (SignalList() << Remove(23,6,0,0) << Insert(5,4,0,0) << Insert(16,2,0,4)); + QTest::newRow("m(23-12,6),m(11-5,8)") + << (SignalList() << Move(23,12,6,0) << Move(11,5,8,1)) + << (SignalList() << Remove(11,1,1,0) << Remove(11,1,1,7) << Remove(21,6,0,0) << Insert(5,1,1,0) << Insert(6,6,0,0) << Insert(12,1,1,7)); + QTest::newRow("m(23-12,6),m(8-5,4)") + << (SignalList() << Move(23,12,6,0) << Move(8,5,4,1)) + << (SignalList() << Remove(8,4,1,0) << Remove(19,6,0,0) << Insert(5,4,1,0) << Insert(12,6,0,0)); + QTest::newRow("m(23-12,6),m(2-5,4)") + << (SignalList() << Move(23,12,6,0) << Move(2,5,4,1)) + << (SignalList() << Remove(2,4,1,0) << Remove(19,6,0,0) << Insert(5,4,1,0) << Insert(12,6,0,0)); + QTest::newRow("m(23-12,6),m(18-5,4)") + << (SignalList() << Move(23,12,6,0) << Move(18,5,4,1)) + << (SignalList() << Remove(12,4,1,0) << Remove(19,6,0,0) << Insert(5,4,1,0) << Insert(16,6,0,0)); + QTest::newRow("m(23-12,6),m(20-5,4)") + << (SignalList() << Move(23,12,6,0) << Move(20,5,4,1)) + << (SignalList() << Remove(14,4,1,0) << Remove(19,6,0,0) << Insert(5,4,1,0) << Insert(16,6,0,0)); + + QTest::newRow("m(23-12,6),m(5-13,11)") + << (SignalList() << Move(23,12,6,0) << Move(5,13,11,1)) + << (SignalList() << Remove(5,7,1,0) << Remove(16,6,0,0) << Insert(5,2,0,4) << Insert(13,7,1,0) << Insert(20,4,0,0)); + + QTest::newRow("m(23-12,6),m(12-23,6)") + << (SignalList() << Move(23,12,6,0) << Move(12,23,6,1)) + << (SignalList() << Remove(23,6,0,0) << Insert(23,6,0,0)); // ### These cancel out. + QTest::newRow("m(23-12,6),m(10-23,4)") + << (SignalList() << Move(23,12,6,0) << Move(10,23,4,1)) + << (SignalList() << Remove(10,2,1,0) << Remove(21,6,0,0) << Insert(10,4,0,2) << Insert(23,2,1,0) << Insert(25,2,0,0)); + QTest::newRow("m(23-12,6),m(16-23.4)") + << (SignalList() << Move(23,12,6,0) << Move(16,23,4,1)) + << (SignalList() << Remove(12,2,1,2) << Remove(21,6,0,0) << Insert(12,4,0,0) << Insert(23,2,0,4) << Insert(25,2,1,2)); + QTest::newRow("m(23-12,6),m(13-23,4)") + << (SignalList() << Move(23,12,6,0) << Move(13,23,4,1)) + << (SignalList() << Remove(23,6,0,0) << Insert(12,1,0,0) << Insert(13,1,0,5) << Insert(23,4,0,1)); + QTest::newRow("m(23-12,6),m(14-23,)") + << (SignalList() << Move(23,12,6,0) << Move(14,23,4,1)) + << (SignalList() << Remove(23,6,0,0) << Insert(12,2,0,0) << Insert(23,4,0,2)); + QTest::newRow("m(23-12,6),m(12-23,4)") + << (SignalList() << Move(23,12,6,0) << Move(12,23,4,1)) + << (SignalList() << Remove(23,6,0,0) << Insert(12,2,0,4) << Insert(23,4,0,0)); + QTest::newRow("m(23-12,6),m(11-23,8)") + << (SignalList() << Move(23,12,6,0) << Move(11,23,8,1)) + << (SignalList() << Remove(11,1,1,0) << Remove(11,1,1,7) << Remove(21,6,0,0) << Insert(23,1,1,0) << Insert(24,6,0,0) << Insert(30,1,1,7)); + QTest::newRow("m(23-12,6),m(8-23,4)") + << (SignalList() << Move(23,12,6,0) << Move(8,23,4,1)) + << (SignalList() << Remove(8,4,1,0) << Remove(19,6,0,0) << Insert(8,6,0,0) << Insert(23,4,1,0)); + QTest::newRow("m(23-12,6),m(2-23,4)") + << (SignalList() << Move(23,12,6,0) << Move(2,23,4,1)) + << (SignalList() << Remove(2,4,1,0) << Remove(19,6,0,0) << Insert(8,6,0,0) << Insert(23,4,1,0)); + QTest::newRow("m(23-12,6),m(18-23,4)") + << (SignalList() << Move(23,12,6,0) << Move(18,23,4,1)) + << (SignalList() << Remove(12,4,1,0) << Remove(19,6,0,0) << Insert(12,6,0,0) << Insert(23,4,1,0)); + QTest::newRow("m(23-12,6),m(20-23,4)") + << (SignalList() << Move(23,12,6,0) << Move(20,23,4,1)) + << (SignalList() << Remove(14,4,1,0) << Remove(19,6,0,0) << Insert(12,6,0,0) << Insert(23,4,1,0)); + + QTest::newRow("m(23-12,6),m(11-23,10)") + << (SignalList() << Move(23,12,6,0) << Move(11,23,10,1)) + << (SignalList() << Remove(11,1,1,0) << Remove(11,3,1,7) << Remove(19,6,0,0) << Insert(23,1,1,0) << Insert(24,6,0,0) << Insert(30,3,1,7)); + + QTest::newRow("m(3-9,12),m(13-5,12)") + << (SignalList() << Move(3,9,12,0) << Move(13,15,5,1)) + << (SignalList() << Remove(3,12,0,0) << Insert(9,4,0,0) << Insert(13,2,0,9) << Insert(15,5,0,4) << Insert(20,1,0,11)); + QTest::newRow("m(3-9,12),m(13-15,20)") + << (SignalList() << Move(3,9,12,0) << Move(13,15,20,1)) + << (SignalList() << Remove(3,12,0,0) << Remove(9,12,1,8) << Insert(9,4,0,0) << Insert(15,8,0,4) << Insert(23,12,1,8)); + QTest::newRow("m(3-9,12),m(13-15,2)") + << (SignalList() << Move(3,9,12,0) << Move(13,15,2,1)) + << (SignalList() << Remove(3,12,0,0) << Insert(9,4,0,0) << Insert(13,2,0,6) << Insert(15,2,0,4) << Insert(17,4,0,8)); + QTest::newRow("m(3-9,12),m(12-5,6)") + << (SignalList() << Move(3,9,12,0) << Move(12,5,6,1)) + << (SignalList() << Remove(3,12,0,0) << Insert(5,6,0,3) << Insert(15,3,0,0) << Insert(18,3,0,9)); + QTest::newRow("m(3-9,12),m(10-14,5)") + << (SignalList() << Move(3,9,12,0) << Move(10,14,5,1)) + << (SignalList() << Remove(3,12,0,0) << Insert(9,1,0,0) << Insert(10,4,0,6) << Insert(14,5,0,1) << Insert(19,2,0,10)); + QTest::newRow("m(3-9,12),m(16-20,5)") + << (SignalList() << Move(3,9,12,0) << Move(16,20,5,1)) + << (SignalList() << Remove(3,12,0,0) << Insert(9,7,0,0) << Insert(20,5,0,7)); + QTest::newRow("m(3-9,12),m(13-17,5)") + << (SignalList() << Move(3,9,12,0) << Move(13,17,5,1)) + << (SignalList() << Remove(3,12,0,0) << Insert(9,4,0,0) << Insert(13,3,0,9) << Insert(17,5,0,4)); + QTest::newRow("m(3-9,12),m(14-18,5)") + << (SignalList() << Move(3,9,12,0) << Move(14,18,5,1)) + << (SignalList() << Remove(3,12,0,0) << Insert(9,5,0,0) << Insert(14,2,0,10) << Insert(18,5,0,5)); + QTest::newRow("m(3-9,12),m(12-16,5)") + << (SignalList() << Move(3,9,12,0) << Move(12,16,5,1)) + << (SignalList() << Remove(3,12,0,0) << Insert(9,3,0,0) << Insert(12,4,0,8) << Insert(16,5,0,3)); + QTest::newRow("m(3-9,12),m(11-19,5)") + << (SignalList() << Move(3,9,12,0) << Move(11,19,5,1)) + << (SignalList() << Remove(3,12,0,0) << Insert(9,2,0,0) << Insert(11,5,0,7) << Insert(19,5,0,2)); + QTest::newRow("m(3-9,12),m(8-12,5)") + << (SignalList() << Move(3,9,12,0) << Move(8,12,5,1)) + << (SignalList() << Remove(3,12,0,0) << Remove(8,1,1,0) << Insert(8,4,0,4) << Insert(12,1,1,0) << Insert(13,4,0,0) << Insert(17,4,0,8)); + QTest::newRow("m(3-9,12),m(2-6,5)") + << (SignalList() << Move(3,9,12,0) << Move(2,6,5,1)) + << (SignalList() << Remove(2,1,1,0) << Remove(2,12,0,0) << Remove(2,4,1,1) << Insert(4,2,0,0) << Insert(6,5,1,0) << Insert(11,10,0,2)); + QTest::newRow("m(3-9,12),m(18-22,5)") + << (SignalList() << Move(3,9,12,0) << Move(18,22,5,1)) + << (SignalList() << Remove(3,12,0,0) << Remove(9,2,1,3) << Insert(9,9,0,0) << Insert(22,3,0,9) << Insert(25,2,1,3)); + QTest::newRow("m(3-9,12),m(20-24,5)") + << (SignalList() << Move(3,9,12,0) << Move(20,24,5,1)) + << (SignalList() << Remove(3,12,0,0) << Remove(9,4,1,1) << Insert(9,11,0,0) << Insert(24,1,0,11) << Insert(25,4,1,1)); + + QTest::newRow("m(3-9,12),m(5-11,8)") + << (SignalList() << Move(3,9,12,0) << Move(5,11,8,1)) + << (SignalList() << Remove(3,12,0,0) << Remove(5,4,1,0) << Insert(5,6,0,4) << Insert(11,4,1,0) << Insert(15,4,0,0) << Insert(19,2,0,10)); + + QTest::newRow("m(3-9,12),m(12-23,6)") + << (SignalList() << Move(3,9,12,0) << Move(12,23,6,1)) + << (SignalList() << Remove(3,12,0,0) << Insert(9,3,0,0) << Insert(12,3,0,9) << Insert(23,6,0,3)); + QTest::newRow("m(3-9,12),m(10-23,4)") + << (SignalList() << Move(3,9,12,0) << Move(10,23,4,1)) + << (SignalList() << Remove(3,12,0,0) << Insert(9,1,0,0) << Insert(10,7,0,5) << Insert(23,4,0,1)); + QTest::newRow("m(3-9,12),m(16-23,4)") + << (SignalList() << Move(3,9,12,0) << Move(16,23,4,1)) + << (SignalList() << Remove(3,12,0,0) << Insert(9,7,0,0) << Insert(16,1,0,11) << Insert(23,4,0,7)); + QTest::newRow("m(3-9,12),m(13-23,4)") + << (SignalList() << Move(3,9,12,0) << Move(13,23,4,1)) + << (SignalList() << Remove(3,12,0,0) << Insert(9,4,0,0) << Insert(13,4,0,8) << Insert(23,4,0,4)); + QTest::newRow("m(3-9,12),m(14-23,4)") + << (SignalList() << Move(3,9,12,0) << Move(14,23,4,1)) + << (SignalList() << Remove(3,12,0,0) << Insert(9,5,0,0) << Insert(14,3,0,9) << Insert(23,4,0,5)); + QTest::newRow("m(3-9,12),m(12-23,4)") + << (SignalList() << Move(3,9,12,0) << Move(12,23,4,1)) + << (SignalList() << Remove(3,12,0,0) << Insert(9,3,0,0) << Insert(12,5,0,7) << Insert(23,4,0,3)); + QTest::newRow("m(3-9,12),m(11-23,8)") + << (SignalList() << Move(3,9,12,0) << Move(11,23,8,1)) + << (SignalList() << Remove(3,12,0,0) << Insert(9,2,0,0) << Insert(11,2,0,10) << Insert(23,8,0,2)); + QTest::newRow("m(3-9,12),m(8-23,4)") + << (SignalList() << Move(3,9,12,0) << Move(8,23,4,1)) + << (SignalList() << Remove(3,12,0,0) << Remove(8,1,1,0) << Insert(8,9,0,3) << Insert(23,1,1,0) << Insert(24,3,0,0)); + QTest::newRow("m(3-9,12),m(2-23,4)") + << (SignalList() << Move(3,9,12,0) << Move(2,23,4,1)) + << (SignalList() << Remove(2,1,1,0) << Remove(2,12,0,0) << Remove(2,3,1,1) << Insert(5,12,0,0) << Insert(23,4,1,0)); + QTest::newRow("m(3-9,12),m(18-23,4)") + << (SignalList() << Move(3,9,12,0) << Move(18,23,4,1)) + << (SignalList() << Remove(3,12,0,0) << Remove(9,1,1,3) << Insert(9,9,0,0) << Insert(23,3,0,9) << Insert(26,1,1,3)); + QTest::newRow("m(3-9,12),m(20-23,4)") + << (SignalList() << Move(3,9,12,0) << Move(20,23,4,1)) + << (SignalList() << Remove(3,12,0,0) << Remove(9,3,1,1) << Insert(9,11,0,0) << Insert(23,1,0,11) << Insert(24,3,1,1)); + + QTest::newRow("m(3-9,12),m(11-23,10)") + << (SignalList() << Move(3,9,12,0) << Move(11,23,10,1)) + << (SignalList() << Remove(3,12,0,0) << Insert(9,2,0,0) << Insert(23,10,0,2)); + + // Change + QTest::newRow("c(4,5)") + << (SignalList() << Change(4,5)) + << (SignalList() << Change(4,5)); + QTest::newRow("c(4,5),c(12,2)") + << (SignalList() << Change(4,5) << Change(12,2)) + << (SignalList() << Change(4,5) << Change(12,2)); + QTest::newRow("c(12,2),c(4,5)") + << (SignalList() << Change(12,2) << Change(4,5)) + << (SignalList() << Change(4,5) << Change(12,2)); + QTest::newRow("c(4,5),c(2,2)") + << (SignalList() << Change(4,5) << Change(2,2)) + << (SignalList() << Change(2,7)); + QTest::newRow("c(4,5),c(9,2)") + << (SignalList() << Change(4,5) << Change(9,2)) + << (SignalList() << Change(4,7)); + QTest::newRow("c(4,5),c(3,2)") + << (SignalList() << Change(4,5) << Change(3,2)) + << (SignalList() << Change(3,6)); + QTest::newRow("c(4,5),c(8,2)") + << (SignalList() << Change(4,5) << Change(8,2)) + << (SignalList() << Change(4,6)); + QTest::newRow("c(4,5),c(3,2)") + << (SignalList() << Change(4,5) << Change(3,2)) + << (SignalList() << Change(3,6)); + QTest::newRow("c(4,5),c(2,9)") + << (SignalList() << Change(4,5) << Change(2,9)) + << (SignalList() << Change(2,9)); + QTest::newRow("c(4,5),c(12,3),c(8,6)") + << (SignalList() << Change(4,5) << Change(12,3) << Change(8,6)) + << (SignalList() << Change(4,11)); + + // Insert,then remove. + QTest::newRow("i(12,6),r(12,6)") + << (SignalList() << Insert(12,6) << Remove(12,6)) + << (SignalList()); + QTest::newRow("i(12,6),r(10,4)") + << (SignalList() << Insert(12,6) << Remove(10,4)) + << (SignalList() << Remove(10,2) << Insert(10,4)); + QTest::newRow("i(12,6),r(16,4)") + << (SignalList() << Insert(12,6) << Remove(16,4)) + << (SignalList() << Remove(12,2) << Insert(12,4)); + QTest::newRow("i(12,6),r(13,4)") + << (SignalList() << Insert(12,6) << Remove(13,4)) + << (SignalList() << Insert(12,2)); + QTest::newRow("i(12,6),r(14,4)") + << (SignalList() << Insert(12,6) << Remove(14,4)) + << (SignalList() << Insert(12,2)); + QTest::newRow("i(12,6),r(12,4)") + << (SignalList() << Insert(12,6) << Remove(12,4)) + << (SignalList() << Insert(12,2)); + QTest::newRow("i(12,6),r(11,8)") + << (SignalList() << Insert(12,6) << Remove(11,8)) + << (SignalList() << Remove(11,2)); + QTest::newRow("i(12,6),r(8,4)") + << (SignalList() << Insert(12,6) << Remove(8,4)) + << (SignalList() << Remove(8,4) << Insert(8,6)); + QTest::newRow("i(12,6),r(2,4)") + << (SignalList() << Insert(12,6) << Remove(2,4)) + << (SignalList() << Remove(2,4) << Insert(8,6)); + QTest::newRow("i(12,6),r(18,4)") + << (SignalList() << Insert(12,6) << Remove(18,4)) + << (SignalList() << Remove(12,4) << Insert(12,6)); + QTest::newRow("i(12,6),r(20,4)") + << (SignalList() << Insert(12,6) << Remove(20,4)) + << (SignalList() << Remove(14,4) << Insert(12,6)); + + // Insert,then change + QTest::newRow("i(12,6),c(12,6)") + << (SignalList() << Insert(12,6) << Change(12,6)) + << (SignalList() << Insert(12,6)); + QTest::newRow("i(12,6),c(10,6)") + << (SignalList() << Insert(12,6) << Change(10,6)) + << (SignalList() << Insert(12,6) << Change(10,2)); + QTest::newRow("i(12,6),c(16,4)") + << (SignalList() << Insert(12,6) << Change(16,4)) + << (SignalList() << Insert(12,6) << Change(18,2)); + QTest::newRow("i(12,6),c(13,4)") + << (SignalList() << Insert(12,6) << Change(13,4)) + << (SignalList() << Insert(12,6)); + QTest::newRow("i(12,6),c(14,4)") + << (SignalList() << Insert(12,6) << Change(14,4)) + << (SignalList() << Insert(12,6)); + QTest::newRow("i(12,6),c(12,4)") + << (SignalList() << Insert(12,6) << Change(12,4)) + << (SignalList() << Insert(12,6)); + QTest::newRow("i(12,6),c(11,8)") + << (SignalList() << Insert(12,6) << Change(11,8)) + << (SignalList() << Insert(12,6) << Change(11,1) << Change(18,1)); + QTest::newRow("i(12,6),c(8,4)") + << (SignalList() << Insert(12,6) << Change(8,4)) + << (SignalList() << Insert(12,6) << Change(8,4)); + QTest::newRow("i(12,6),c(2,4)") + << (SignalList() << Insert(12,6) << Change(2,4)) + << (SignalList() << Insert(12,6) << Change(2,4)); + QTest::newRow("i(12,6),c(18,4)") + << (SignalList() << Insert(12,6) << Change(18,4)) + << (SignalList() << Insert(12,6) << Change(18,4)); + QTest::newRow("i(12,6),c(20,4)") + << (SignalList() << Insert(12,6) << Change(20,4)) + << (SignalList() << Insert(12,6) << Change(20,4)); + + // Insert,then move + QTest::newRow("i(12,6),m(12-5,6)") + << (SignalList() << Insert(12,6) << Move(12,5,6,0)) + << (SignalList() << Insert(5,6)); + QTest::newRow("i(12,6),m(10-5,4)") + << (SignalList() << Insert(12,6) << Move(10,5,4,0)) + << (SignalList() << Remove(10,2,0,0) << Insert(5,2,0,0) << Insert(7,2) << Insert(14,4)); + QTest::newRow("i(12,6),m(16-5,4)") + << (SignalList() << Insert(12,6) << Move(16,5,4,0)) + << (SignalList() << Remove(12,2,0,2) << Insert(5,2) << Insert(7,2,0,2) << Insert(16,4)); + QTest::newRow("i(12,6),m(13-5,4)") + << (SignalList() << Insert(12,6) << Move(13,5,4,0)) + << (SignalList() << Insert(5,4) << Insert(16,2)); + QTest::newRow("i(12,6),m(14-5,4)") + << (SignalList() << Insert(12,6) << Move(14,5,4,0)) + << (SignalList() << Insert(5,4) << Insert(16,2)); + QTest::newRow("i(12,6),m(12-5,4)") + << (SignalList() << Insert(12,6) << Move(12,5,4,0)) + << (SignalList() << Insert(5,4) << Insert(16,2)); + QTest::newRow("i(12,6),m(11-5,8)") + << (SignalList() << Insert(12,6) << Move(11,5,8,0)) + << (SignalList() << Remove(11,1,0,0) << Remove(11,1,0,7) << Insert(5,1,0,0) << Insert(6,6) << Insert(12,1,0,7)); + QTest::newRow("i(12,6),m(8-5,4)") + << (SignalList() << Insert(12,6) << Move(8,5,4,0)) + << (SignalList() << Remove(8,4,0,0) << Insert(5,4,0,0) << Insert(12,6)); + QTest::newRow("i(12,6),m(2-5,4)") + << (SignalList() << Insert(12,6) << Move(2,5,4,0)) + << (SignalList() << Remove(2,4,0,0) << Insert(5,4,0,0) << Insert(12,6)); + QTest::newRow("i(12,6),m(18-5,4)") + << (SignalList() << Insert(12,6) << Move(18,5,4,0)) + << (SignalList() << Remove(12,4,0,0) << Insert(5,4,0,0) << Insert(16,6)); + QTest::newRow("i(12,6),m(20-5,4)") + << (SignalList() << Insert(12,6) << Move(20,5,4,0)) + << (SignalList() << Remove(14,4,0,0) << Insert(5,4,0,0) << Insert(16,6)); + + QTest::newRow("i(12,6),m(5-13,11)") + << (SignalList() << Insert(12,6) << Move(5,11,8,0)) + << (SignalList() << Remove(5,7,0,0) << Insert(5,5) << Insert(11,7,0,0) << Insert(18,1)); + + QTest::newRow("i(12,6),m(12-23,6)") + << (SignalList() << Insert(12,6) << Move(12,23,6,0)) + << (SignalList() << Insert(23,6)); + QTest::newRow("i(12,6),m(10-23,4)") + << (SignalList() << Insert(12,6) << Move(10,23,4,0)) + << (SignalList() << Remove(10,2,0,0) << Insert(10,4) << Insert(23,2,0,0) << Insert(25,2)); + QTest::newRow("i(12,6),m(16-23,4)") + << (SignalList() << Insert(12,6) << Move(16,23,4,0)) + << (SignalList() << Remove(12,2,0,2) << Insert(12,4) << Insert(23,2) << Insert(25,2,0,2)); + QTest::newRow("i(12,6),m(13-23,4)") + << (SignalList() << Insert(12,6) << Move(13,23,4,0)) + << (SignalList() << Insert(12,2) << Insert(23,4)); + QTest::newRow("i(12,6),m(14-23,4)") + << (SignalList() << Insert(12,6) << Move(14,23,4,0)) + << (SignalList() << Insert(12,2) << Insert(23,4)); + QTest::newRow("i(12,6),m(12-23,4)") + << (SignalList() << Insert(12,6) << Move(12,23,4,0)) + << (SignalList() << Insert(12,2) << Insert(23,4)); + QTest::newRow("i(12,6),m(11-23,8)") + << (SignalList() << Insert(12,6) << Move(11,23,8,0)) + << (SignalList() << Remove(11,1,0,0) << Remove(11,1,0,7) << Insert(23,1,0,0)<< Insert(24,6) << Insert(30,1,0,7)); + QTest::newRow("i(12,6),m(8-23,4)") + << (SignalList() << Insert(12,6) << Move(8,23,4,0)) + << (SignalList() << Remove(8,4,0,0) << Insert(8,6) << Insert(23,4,0,0)); + QTest::newRow("i(12,6),m(2-23,4)") + << (SignalList() << Insert(12,6) << Move(2,23,4,0)) + << (SignalList() << Remove(2,4,0,0) << Insert(8,6) << Insert(23,4,0,0)); + QTest::newRow("i(12,6),m(18-23,4)") + << (SignalList() << Insert(12,6) << Move(18,23,4,0)) + << (SignalList() << Remove(12,4,0,0) << Insert(12,6) << Insert(23,4,0,0)); + QTest::newRow("i(12,6),m(20-23,4)") + << (SignalList() << Insert(12,6) << Move(20,23,4,0)) + << (SignalList() << Remove(14,4,0,0) << Insert(12,6) << Insert(23,4,0,0)); + + QTest::newRow("i(12,6),m(11-23,10)") + << (SignalList() << Insert(12,6) << Move(11,23,10,0)) + << (SignalList() << Remove(11,1,0,0) << Remove(11,3,0,7) << Insert(23,1,0,0) << Insert(24,6) << Insert(30,3,0,7)); + + // Remove,then insert + QTest::newRow("r(12,6),i(12,6)") + << (SignalList() << Remove(12,6) << Insert(12,6)) + << (SignalList() << Remove(12,6) << Insert(12,6)); + QTest::newRow("r(12,6),i(10,4)") + << (SignalList() << Remove(12,6) << Insert(10,14)) + << (SignalList() << Remove(12,6) << Insert(10,14)); + QTest::newRow("r(12,6),i(16,4)") + << (SignalList() << Remove(12,6) << Insert(16,4)) + << (SignalList() << Remove(12,6) << Insert(16,4)); + QTest::newRow("r(12,6),i(13,4)") + << (SignalList() << Remove(12,6) << Insert(13,4)) + << (SignalList() << Remove(12,6) << Insert(13,4)); + QTest::newRow("r(12,6),i(14,4)") + << (SignalList() << Remove(12,6) << Insert(14,4)) + << (SignalList() << Remove(12,6) << Insert(14,4)); + QTest::newRow("r(12,6),i(12,4)") + << (SignalList() << Remove(12,6) << Insert(12,4)) + << (SignalList() << Remove(12,6) << Insert(12,4)); + QTest::newRow("r(12,6),i(11,8)") + << (SignalList() << Remove(12,6) << Insert(11,8)) + << (SignalList() << Remove(12,6) << Insert(11,8)); + QTest::newRow("r(12,6),i(8,4)") + << (SignalList() << Remove(12,6) << Insert(8,4)) + << (SignalList() << Remove(12,6) << Insert(8,4)); + QTest::newRow("r(12,6),i(2,4)") + << (SignalList() << Remove(12,6) << Insert(2,4)) + << (SignalList() << Remove(12,6) << Insert(2,4)); + QTest::newRow("r(12,6),i(18,4)") + << (SignalList() << Remove(12,6) << Insert(18,4)) + << (SignalList() << Remove(12,6) << Insert(18,4)); + QTest::newRow("r(12,6),i(20,4)") + << (SignalList() << Remove(12,6) << Insert(20,4)) + << (SignalList() << Remove(12,6) << Insert(20,4)); + + // Move,then insert + QTest::newRow("m(12-5,6),i(12,6)") + << (SignalList() << Move(12,5,6,0) << Insert(12,6)) + << (SignalList() << Remove(12,6,0,0) << Insert(5,6,0,0) << Insert(12,6)); + QTest::newRow("m(12-5,6),i(10,4)") + << (SignalList() << Move(12,5,6,0) << Insert(10,4)) + << (SignalList() << Remove(12,6,0,0) << Insert(5,5,0,0) << Insert(10,4) << Insert(14,1,0,5)); + QTest::newRow("m(12-5,6),i(16,4)") + << (SignalList() << Move(12,5,6,0) << Insert(16,4)) + << (SignalList() << Remove(12,6,0,0) << Insert(5,6,0,0) << Insert(16,4)); + QTest::newRow("m(12-5,6),i(13,4)") + << (SignalList() << Move(12,5,6,0) << Insert(13,4)) + << (SignalList() << Remove(12,6,0,0) << Insert(5,6,0,0) << Insert(13,4)); + QTest::newRow("m(12-5,6),i(14,4)") + << (SignalList() << Move(12,5,6,0) << Insert(14,4)) + << (SignalList() << Remove(12,6,0,0) << Insert(5,6,0,0) << Insert(14,4)); + QTest::newRow("m(12-5,6),i(12,4)") + << (SignalList() << Move(12,5,6,0) << Insert(12,4)) + << (SignalList() << Remove(12,6,0,0) << Insert(5,6,0,0) << Insert(12,4)); + QTest::newRow("m(12-5,6),i(11,8)") + << (SignalList() << Move(12,5,6,0) << Insert(11,8)) + << (SignalList() << Remove(12,6,0,0) << Insert(5,6,0,0) << Insert(11,8)); + QTest::newRow("m(12-5,6),i(8,4)") + << (SignalList() << Move(12,5,6,0) << Insert(8,4)) + << (SignalList() << Remove(12,6,0,0) << Insert(5,3,0,0) << Insert(8,4) << Insert(12,3,0,3)); + QTest::newRow("m(12-5,6),i(2,4)") + << (SignalList() << Move(12,5,6,0) << Insert(2,4)) + << (SignalList() << Remove(12,6,0,0) << Insert(2,4) << Insert(9,6,0,0)); + QTest::newRow("m(12-5,6),i(18,4)") + << (SignalList() << Move(12,5,6,0) << Insert(18,4)) + << (SignalList() << Remove(12,6,0,0) << Insert(5,6,0,0) << Insert(18,4)); + QTest::newRow("m(12-5,6),i(20,4)") + << (SignalList() << Move(12,5,6,0) << Insert(20,4)) + << (SignalList() << Remove(12,6,0,0) << Insert(5,6,0,0) << Insert(20,4)); + + QTest::newRow("m(12-23,6),i(12,6)") + << (SignalList() << Move(12,23,6,0) << Insert(12,6)) + << (SignalList() << Remove(12,6,0,0) << Insert(12,6) << Insert(29,6,0,0)); + QTest::newRow("m(12-23,6),i(10,4)") + << (SignalList() << Move(12,23,6,0) << Insert(10,4)) + << (SignalList() << Remove(12,6,0,0) << Insert(10,4) << Insert(27,6,0,0)); + QTest::newRow("m(12-23,6),i(16,4)") + << (SignalList() << Move(12,23,6,0) << Insert(16,4)) + << (SignalList() << Remove(12,6,0,0) << Insert(16,4) << Insert(27,6,0,0)); + QTest::newRow("m(12-23,6),i(13,4)") + << (SignalList() << Move(12,23,6,0) << Insert(13,4)) + << (SignalList() << Remove(12,6,0,0) << Insert(13,4) << Insert(27,6,0,0)); + QTest::newRow("m(12-23,6),i(14,4)") + << (SignalList() << Move(12,23,6,0) << Insert(14,4)) + << (SignalList() << Remove(12,6,0,0) << Insert(14,4) << Insert(27,6,0,0)); + QTest::newRow("m(12-23,6),i(12,4)") + << (SignalList() << Move(12,23,6,0) << Insert(12,4)) + << (SignalList() << Remove(12,6,0,0) << Insert(12,4) << Insert(27,6,0,0)); + QTest::newRow("m(12-23,6),i(11,8)") + << (SignalList() << Move(12,23,6,0) << Insert(11,8)) + << (SignalList() << Remove(12,6,0,0) << Insert(11,8) << Insert(31,6,0,0)); + QTest::newRow("m(12-23,6),i(8,4)") + << (SignalList() << Move(12,23,6,0) << Insert(8,4)) + << (SignalList() << Remove(12,6,0,0) << Insert(8,4) << Insert(27,6,0,0)); + QTest::newRow("m(12-23,6),i(2,4)") + << (SignalList() << Move(12,23,6,0) << Insert(2,4)) + << (SignalList() << Remove(12,6,0,0) << Insert(2,4) << Insert(27,6,0,0)); + QTest::newRow("m(12-23,6),i(18,4)") + << (SignalList() << Move(12,23,6,0) << Insert(18,4)) + << (SignalList() << Remove(12,6,0,0) << Insert(18,4) << Insert(27,6,0,0)); + QTest::newRow("m(12-23,6),i(20,4)") + << (SignalList() << Move(12,23,6,0) << Insert(20,4)) + << (SignalList() << Remove(12,6,0,0) << Insert(20,4) << Insert(27,6,0,0)); + + // Move,then remove + QTest::newRow("m(12-5,6),r(12,6)") + << (SignalList() << Move(12,5,6,0) << Remove(12,6)) + << (SignalList() << Remove(6,6) << Remove(6,6,0,0) << Insert(5,6,0,0)); + QTest::newRow("m(12-5,6),r(10,4)") + << (SignalList() << Move(12,5,6,0) << Remove(10,4)) // ### + << (SignalList() << Remove(5,3) << Remove(9,6,0,0) << Insert(5,5,0,0)); + QTest::newRow("m(12-5,6),r(16,4)") + << (SignalList() << Move(12,5,6,0) << Remove(16,4)) + << (SignalList() << Remove(10,2) << Remove(10,6,0,0) << Remove(10,2) << Insert(5,6,0,0)); + QTest::newRow("m(12-5,6),r(13,4)") + << (SignalList() << Move(12,5,6,0) << Remove(13,4)) + << (SignalList() << Remove(7,4) << Remove(8,6,0,0) << Insert(5,6,0,0)); + QTest::newRow("m(12-5,6),r(14,4)") + << (SignalList() << Move(12,5,6,0) << Remove(14,4)) + << (SignalList() << Remove(8,4) << Remove(8,6,0,0) << Insert(5,6,0,0)); + QTest::newRow("m(12-5,6),r(12,4)") + << (SignalList() << Move(12,5,6,0) << Remove(12,4)) + << (SignalList() << Remove(6,4) << Remove(8,6,0,0) << Insert(5,6,0,0)); + QTest::newRow("m(12-5,6),r(11,8)") + << (SignalList() << Move(12,5,6,0) << Remove(11,8)) + << (SignalList() << Remove(5,7) << Remove(5,6,0,0) << Remove(5,1) << Insert(5,6,0,0)); + QTest::newRow("m(12-5,6),r(8,4)") + << (SignalList() << Move(12,5,6,0) << Remove(8,4)) // ### + << (SignalList() << Remove(5,1) << Remove(11,6,0,0) << Insert(5,3,0,0)); + QTest::newRow("m(12-5,6),r(2,4)") + << (SignalList() << Move(12,5,6,0) << Remove(2,4)) + << (SignalList() << Remove(2,3) << Remove(9,6,0,0) << Insert(2,5,0,1)); + QTest::newRow("m(12-5,6),r(6,4)") + << (SignalList() << Move(12,5,6,0) << Remove(6,4)) + << (SignalList() << Remove(12,6,0,0) << Insert(5,1,0,0) << Insert(6,1,0,5)); + QTest::newRow("m(12-5,6),r(18,4)") + << (SignalList() << Move(12,5,6,0) << Remove(18,4)) + << (SignalList() << Remove(12,6,0,0) << Remove(12,4) << Insert(5,6,0,0)); + QTest::newRow("m(12-5,6),r(20,4)") + << (SignalList() << Move(12,5,6,0) << Remove(20,4)) + << (SignalList() << Remove(12,6,0,0) << Remove(14,4) << Insert(5,6,0,0)); + + QTest::newRow("m(12-23,6),r(12,6)") + << (SignalList() << Move(12,23,6,0) << Remove(12,6)) + << (SignalList() << Remove(12,6,0,0) << Remove(12,6) << Insert(17,6,0,0)); + QTest::newRow("m(12-23,6),r(10,4)") + << (SignalList() << Move(12,23,6,0) << Remove(10,4)) + << (SignalList() << Remove(10,2) << Remove(10,6,0,0) << Remove(10,2) << Insert(19,6,0,0)); + QTest::newRow("m(12-23,6),r(16,4)") + << (SignalList() << Move(12,23,6,0) << Remove(16,4)) + << (SignalList() << Remove(12,6,0,0) << Remove(16,4) << Insert(19,6,0,0)); + QTest::newRow("m(12-23,6),r(13,4)") + << (SignalList() << Move(12,23,6,0) << Remove(13,4)) + << (SignalList() << Remove(12,6,0,0) << Remove(13,4) << Insert(19,6,0,0)); + QTest::newRow("m(12-23,6),r(14,4)") + << (SignalList() << Move(12,23,6,0) << Remove(14,4)) + << (SignalList() << Remove(12,6,0,0) << Remove(14,4) << Insert(19,6,0,0)); + QTest::newRow("m(12-23,6),r(12,4)") + << (SignalList() << Move(12,23,6,0) << Remove(12,4)) + << (SignalList() << Remove(12,6,0,0) << Remove(12,4) << Insert(19,6,0,0)); + QTest::newRow("m(12-23,6),r(11,8)") + << (SignalList() << Move(12,23,6,0) << Remove(11,8)) + << (SignalList() << Remove(11,1) << Remove(11,6,0,0) << Remove(11,7) << Insert(15,6,0,0)); + QTest::newRow("m(12-23,6),r(8,4)") + << (SignalList() << Move(12,23,6,0) << Remove(8,4)) + << (SignalList() << Remove(8,4) << Remove(8,6,0,0) << Insert(19,6,0,0)); + QTest::newRow("m(12-23,6),r(2,4)") + << (SignalList() << Move(12,23,6,0) << Remove(2,4)) + << (SignalList() << Remove(2,4) << Remove(8,6,0,0) << Insert(19,6,0,0)); + QTest::newRow("m(12-23,6),r(18,4)") + << (SignalList() << Move(12,23,6,0) << Remove(18,4)) + << (SignalList() << Remove(12,6,0,0) << Remove(18,4) << Insert(19,6,0,0)); + QTest::newRow("m(12-23,6),r(20,4)") + << (SignalList() << Move(12,23,6,0) << Remove(20,4)) + << (SignalList() << Remove(12,6,0,0) << Remove(20,3) << Insert(20,5,0,1)); + + + // Complex + QTest::newRow("r(15,1),r(22,1)") + << (SignalList() << Remove(15,1) << Remove(22,1)) + << (SignalList() << Remove(15,1) << Remove(22,1)); + QTest::newRow("r(15,1),r(22,1),r(25,1)") + << (SignalList() << Remove(15,1) << Remove(22,1) << Remove(25,1)) + << (SignalList() << Remove(15,1) << Remove(22,1) << Remove(25,1)); + QTest::newRow("r(15,1),r(22,1),r(25,1),r(15,1)") + << (SignalList() << Remove(15,1) << Remove(22,1) << Remove(25,1) << Remove(15,1)) + << (SignalList() << Remove(15,2) << Remove(21,1) << Remove(24,1)); + QTest::newRow("r(15,1),r(22,1),r(25,1),r(15,1),r(13,1)") + << (SignalList() << Remove(15,1) << Remove(22,1) << Remove(25,1) << Remove(15,1) << Remove(13,1)) + << (SignalList() << Remove(13,1) << Remove(14,2) << Remove(20,1) << Remove(23,1)); + QTest::newRow("r(15,1),r(22,1),r(25,1),r(15,1),r(13,1),r(13,1)") + << (SignalList() << Remove(15,1) << Remove(22,1) << Remove(25,1) << Remove(15,1) << Remove(13,1) << Remove(13,1)) + << (SignalList() << Remove(13,4) << Remove(19,1) << Remove(22,1)); + QTest::newRow("r(15,1),r(22,1),r(25,1),r(15,1),r(13,1),r(13,1),m(12,13,1)") + << (SignalList() << Remove(15,1) << Remove(22,1) << Remove(25,1) << Remove(15,1) << Remove(13,1) << Remove(13,1) << Move(12,13,1,0)) + << (SignalList() << Remove(12,1,0,0) << Remove(12,4) << Remove(18,1) << Remove(21,1) << Insert(13,1,0,0)); + + QTest::newRow("r(12,18),r(3,0)") + << (SignalList() << Remove(12,18) << Remove(3,0)) + << (SignalList() << Remove(12,18)); + QTest::newRow("r(12,18),r(3,0),r(1,2)") + << (SignalList() << Remove(12,18) << Remove(3,0) << Remove(1,2)) + << (SignalList() << Remove(1,2) << Remove(10,18)); + QTest::newRow("r(12,18),r(3,0),r(1,2),m(4,0,11)") + << (SignalList() << Remove(12,18) << Remove(3,0) << Remove(1,2) << Move(4,0,11,0)) + << (SignalList() << Remove(1,2) << Remove(4,6,0,0) << Remove(4,18) << Remove(4,5,0,6) << Insert(0,11,0,0)); + QTest::newRow("r(12,18),r(3,0),r(1,2),m(4,0,11),r(14,3)") + << (SignalList() << Remove(12,18) << Remove(3,0) << Remove(1,2) << Move(4,0,11,0) << Remove(14,3)) + << (SignalList() << Remove(1,2) << Remove(3,1) << Remove(3,6,0,0) << Remove(3,18) << Remove(3,5,0,6) << Remove(3,2) << Insert(0,11,0,0)); + + QTest::newRow("m(9,11,14),i(16,1)") + << (SignalList() << Move(9,11,14,0) << Insert(16,1)) + << (SignalList() << Remove(9,14,0,0) << Insert(11,5,0,0) << Insert(16,1) << Insert(17,9,0,5)); + QTest::newRow("m(9,11,14),i(16,1),m(22,38,3)") + << (SignalList() << Move(9,11,14,0) << Insert(16,1) << Move(22,38,3,1)) + << (SignalList() << Remove(9,14,0,0) << Insert(11,5,0,0) << Insert(16,1) << Insert(17,5,0,5) << Insert(22,1,0,13) << Insert(38,3,0,10)); + + QTest::newRow("i(28,2),m(7,5,22)") + << (SignalList() << Insert(28,2) << Move(7,5,22,0)) + << (SignalList() << Remove(7,21,0,0) << Insert(5,21,0,0) << Insert(26,1) << Insert(29,1)); + + QTest::newRow("i(16,3),m(18,28,15)") + << (SignalList() << Insert(16,3) << Move(18,28,15,0)) + << (SignalList() << Remove(16,14,0,1) << Insert(16,2) << Insert(28,1) << Insert(29,14,0,1)); + + QTest::newRow("m(33,12,6),m(18,20,20)") + << (SignalList() << Move(22,12,6,0) << Move(18,20,20,1)) + << (SignalList() << Remove(12,10,1,0) << Remove(12,6,0,0) << Remove(12,10,1,10) + << Insert(12,6,0,0) << Insert(20,20,1,0)); + QTest::newRow("m(33,12,6),m(18,20,20),m(38,19,1)") + << (SignalList() << Move(22,12,6,0) << Move(18,20,20,1) << Move(28,19,1,2)) + << (SignalList() << Remove(12,10,1,0) << Remove(12,6,0,0) << Remove(12,10,1,10) + << Insert(12,6,0,0) << Insert(19,1,1,8) << Insert(21,8,1,0) << Insert(29,11,1,9)); + QTest::newRow("m(33,12,6),m(18,20,20),m(38,19,1),r(34,4)") + << (SignalList() << Move(22,12,6,0) << Move(18,20,20,1) << Move(28,19,1,2) << Remove(34,4)) + << (SignalList() << Remove(12,10,1,0) << Remove(12,6,0,0) << Remove(12,10,1,10) + << Insert(12,6,0,0) << Insert(19,1,1,8) << Insert(21,8,1,0) << Insert(29,5,1,9) << Insert(34,2,1,18)); + QTest::newRow("m(33,12,6),m(18,20,20),m(38,19,1),r(34,4),m(13,9,15)") + << (SignalList() << Move(22,12,6,0) << Move(18,20,20,1) << Move(28,19,1,2) << Remove(34,4) << Move(13,9,15,3)) + << (SignalList() << Remove(12,10,1,0) << Remove(12,6,0,0) << Remove(12,10,1,10) << Remove(12,1,3,5) << Remove(12,1,3,7) + << Insert(9,5,0,1) << Insert(14,1,3,5) << Insert(15,1,1,8) << Insert(16,1,3,7) << Insert(17,7,1,0) << Insert(27,1,0,0) << Insert(28,1,1,7) << Insert(29,5,1,9) << Insert(34,2,1,18)); + + QTest::newRow("i(8,5),m(14,26,14)") + << (SignalList() << Insert(8,5) << Move(14,26,14,0)) + << (SignalList() << Remove(9,14,0,0) << Insert(8,5) << Insert(26,14,0,0)); + QTest::newRow("i(8,5),m(14,26,14),r(45,0)") + << (SignalList() << Insert(8,5) << Move(14,26,14,0) << Remove(45,0)) + << (SignalList() << Remove(9,14,0,0) << Insert(8,5) << Insert(26,14,0,0)); + QTest::newRow("i(8,5),m(14,26,14),r(45,0),m(5,8,21)") + << (SignalList() << Insert(8,5) << Move(14,26,14,0) << Remove(45,0) << Move(5,8,21,1)) + << (SignalList() << Remove(5,3,1,0) << Remove(5,1,1,8) << Remove(5,14,0,0) << Remove(5,12,1,9) + << Insert(5,3,0,0) << Insert(8,3,1,0) << Insert(11,5) << Insert(16,13,1,8) << Insert(29,11,0,3)); + + QTest::newRow("i(35,1),r(5,31)") + << (SignalList() << Insert(35,1) << Remove(5,31)) + << (SignalList() << Remove(5,30)); + QTest::newRow("i(35,1),r(5,31),m(9,8,1)") + << (SignalList() << Insert(35,1) << Remove(5,31) << Move(9,8,1,0)) + << (SignalList() << Remove(5,30) << Remove(9,1,0,0) << Insert(8,1,0,0)); + QTest::newRow("i(35,1),r(5,31),m(9,8,1),i(7,2)") + << (SignalList() << Insert(35,1) << Remove(5,31) << Move(9,8,1,0) << Insert(7,2)) + << (SignalList() << Remove(5,30) << Remove(9,1,0,0) << Insert(7,2) << Insert(10,1,0,0)); + QTest::newRow("i(35,1),r(5,31),m(9,8,1),i(7,2),r(4,3)") + << (SignalList() << Insert(35,1) << Remove(5,31) << Move(9,8,1,0) << Insert(7,2) << Remove(4,3)) + << (SignalList() << Remove(4,33) << Remove(6,1,0,0) << Insert(4,2) << Insert(7,1,0,0)); + + QTest::newRow("r(37,0),r(21,1)") + << (SignalList() << Remove(37,0) << Remove(21,1)) + << (SignalList() << Remove(21,1)); + QTest::newRow("r(37,0),r(21,1),m(27,35,2)") + << (SignalList() << Remove(37,0) << Remove(21,1) << Move(27,35,2,0)) + << (SignalList() << Remove(21,1) << Remove(27,2,0,0) << Insert(35,2,0,0)); + QTest::newRow("r(37,0),r(21,1),m(27,35,2),i(31,5)") + << (SignalList() << Remove(37,0) << Remove(21,1) << Move(27,35,2,0) << Insert(31,5)) + << (SignalList() << Remove(21,1) << Remove(27,2,0,0) << Insert(31,5) << Insert(40,2,0,0)); + QTest::newRow("r(37,0),r(21,1),m(27,35,2),i(31,5),r(10,31)") + << (SignalList() << Remove(37,0) << Remove(21,1) << Move(27,35,2,0) << Insert(31,5) << Remove(10,31)) + << (SignalList() << Remove(10, 18) << Remove(10,2,0,0) << Remove(10,8) << Insert(10,1,0,1)); + + QTest::newRow("m(1,1,39),r(26,10)") + << (SignalList() << Move(1,1,39,0) << Remove(26,10)) + << (SignalList() << Remove(1,39,0,0) << Insert(1,25,0,0) << Insert(26,4,0,35)); + QTest::newRow("m(1,1,39),r(26,10),i(10,5)") + << (SignalList() << Move(1,1,39,0) << Remove(26,10) << Insert(10,5)) + << (SignalList() << Remove(1,39,0,0) << Insert(1,9,0,0) << Insert(10,5) << Insert(15,16,0,9) << Insert(31,4,0,35)); + QTest::newRow("m(1,1,39),r(26,10),i(10,5),i(27,3)") + << (SignalList() << Move(1,1,39,0) << Remove(26,10) << Insert(10,5) << Insert(27,3)) + << (SignalList() << Remove(1,39,0,0) + << Insert(1,9,0,0) << Insert(10,5) << Insert(15,12,0,9) << Insert(27,3) << Insert(30,4,0,21) << Insert(34,4,0,35)); + QTest::newRow("m(1,1,39),r(26,10),i(10,5),i(27,3),r(28,5)") + << (SignalList() << Move(1,1,39,0) << Remove(26,10) << Insert(10,5) << Insert(27,3) << Remove(28,5)) + << (SignalList() << Remove(1,39,0,0) + << Insert(1,9,0,0) << Insert(10,5) << Insert(15,12,0,9) << Insert(27,1) << Insert(28,1,0,24) << Insert(29,4,0,35)); + + QTest::newRow("i(36,4)m(25,39,5)") + << (SignalList() << Insert(36,4) << Move(25,39,5,0)) + << (SignalList() << Remove(25,5,0,0) << Insert(31,4) << Insert(39,5,0,0)); + QTest::newRow("i(36,4)m(25,39,5),i(16,5)") + << (SignalList() << Insert(36,4) << Move(25,39,5,0) << Insert(16,5)) + << (SignalList() << Remove(25,5,0,0) << Insert(16,5) << Insert(36,4) << Insert(44,5,0,0)); + QTest::newRow("i(36,4)m(25,39,5),i(16,5),i(37,5)") + << (SignalList() << Insert(36,4) << Move(25,39,5,0) << Insert(16,5) << Insert(37,5)) + << (SignalList() << Remove(25,5,0,0) << Insert(16,5) << Insert(36,9) << Insert(49,5,0,0)); + QTest::newRow("i(36,4)m(25,39,5),i(16,5),i(37,5),m(40,21,11)") + << (SignalList() << Insert(36,4) << Move(25,39,5,0) << Insert(16,5) << Insert(37,5) << Move(40,21,11,1)) + << (SignalList() << Remove(25,5,0,0) << Remove(31,4,1,5) + << Insert(16,10) << Insert(26,4,1,5) << Insert(30,2,0,0) << Insert(47,4) << Insert(51,3,0,2)); + + QTest::newRow("i(24,1),r(33,4)") + << (SignalList() << Insert(24,1) << Remove(33,4)) + << (SignalList() << Remove(32,4) << Insert(24,1)); + QTest::newRow("i(24,1),r(33,4),r(15,15)") + << (SignalList() << Insert(24,1) << Remove(33,4) << Remove(15,15)) + << (SignalList() << Remove(15,14) << Remove(18,4)); + QTest::newRow("i(24,1),r(33,4),r(15,15),m(8,10,2)") + << (SignalList() << Insert(24,1) << Remove(33,4) << Remove(15,15) << Move(8,10,2,0)) + << (SignalList() << Remove(8,2,0,0) << Remove(13,14) << Remove(16,4) << Insert(10,2,0,0)); + QTest::newRow("i(24,1),r(33,4),r(15,15),m(8,10,2),r(2,19)") + << (SignalList() << Insert(24,1) << Remove(33,4) << Remove(15,15) << Move(8,10,2,0) << Remove(2,19)) + << (SignalList() << Remove(2,6) << Remove(2,2,0,0) << Remove(2,29)); + + QTest::newRow("r(1,35),i(3,4),m(4,2,2)") + << (SignalList() << Remove(1,35) << Insert(3,4) << Move(4,2,2,0)) + << (SignalList() << Remove(1,35) <("input"); + + QTest::newRow("(r(1,35),i(3,4)),(m(4,2,2),r(7,1))") + << (SignalListList() + << (SignalList() << Remove(1,35) << Insert(3,4)) + << (SignalList() << Move(4,2,2,0) << Remove(7,1))); + + QTest::newRow("(i(30,4),m(7,28,16))(m(6,7,13),m(41,35,2))") + << (SignalListList() + << (SignalList() << Insert(30,4) << Move(7,28,16,0)) + << (SignalList() << Move(6,7,13,1) << Move(41,35,2,2))); + + QTest::newRow("(i(35,2),r(39,0))(r(25,11),m(24,8,7))") + << (SignalListList() + << (SignalList() << Insert(35,2) << Remove(39,0)) + << (SignalList() << Remove(25,11) << Move(24,8,7,0))); + + QTest::newRow("i(26,1),i(39,1),m(31,34,2),r(15,27)") + << (SignalListList() + << (SignalList() << Insert(26,1) << Insert(39,1)) + << (SignalList() << Move(31,34,2,0) << Remove(15,27))); + + QTest::newRow("i(19,1),(2,3),r(38,4),m(2,20,3)") + << (SignalListList() + << (SignalList() << Insert(19,1) << Insert(2,3)) + << (SignalList() << Remove(38,4) << Move(2,20,3,0))); + + QTest::newRow("i(4,3),i(19,1),i(31,3),m(8,10,29)") + << (SignalListList() + << (SignalList() << Insert(4,3) << Insert(19,1)) + << (SignalList() << Insert(31,3) << Move(8,10,29,0))); + + QTest::newRow("m(18,15,16),i(0,1),i(32,2),i(29,2)") + << (SignalListList() + << (SignalList() << Move(18,15,16,0) << Insert(0,1)) + << (SignalList() << Insert(32,2) << Insert(29,2))); + + QTest::newRow("i(38,5),i(12,5),i(48,3),m(28,6,6)") + << (SignalListList() + << (SignalList() << Insert(38,5) << Insert(12,5)) + << (SignalList() << Insert(48,3) << Move(28,6,6,0))); + + QTest::newRow("r(8,9),m(7,10,18),m(8,10,18),i(17,2)") + << (SignalListList() + << (SignalList() << Remove(8,9) << Move(7,10,18,0)) + << (SignalList() << Move(8,10,18,1) << Insert(17,2))); + + QTest::newRow("r(39,0),m(18,1,21),m(2,6,31),r(9,4)") + << (SignalListList() + << (SignalList() << Remove(39,0) << Move(18,1,21,0)) + << (SignalList() << Move(2,6,31,1) << Remove(9,4))); + + QTest::newRow("3*5 (5)") + << (SignalListList() + << (SignalList() << Remove(36,4) << Move(3,2,14,0) << Insert(36,3) << Remove(30,7) << Insert(3,5))); + QTest::newRow("3*5 (6)") + << (SignalListList() + << (SignalList() << Remove(36,4) << Move(3,2,14,0) << Insert(36,3) << Remove(30,7) << Insert(3,5)) + << (SignalList() << Insert(1,1))); + QTest::newRow("3*5 (7)") + << (SignalListList() + << (SignalList() << Remove(36,4) << Move(3,2,14,0) << Insert(36,3) << Remove(30,7) << Insert(3,5)) + << (SignalList() << Insert(1,1) << Move(9,32,3,1))); + QTest::newRow("3*5 (8)") + << (SignalListList() + << (SignalList() << Remove(36,4) << Move(3,2,14,0) << Insert(36,3) << Remove(30,7) << Insert(3,5)) + << (SignalList() << Insert(1,1) << Move(9,32,3,1) << Remove(22,1))); + QTest::newRow("3*5 (9)") + << (SignalListList() + << (SignalList() << Remove(36,4) << Move(3,2,14,0) << Insert(36,3) << Remove(30,7) << Insert(3,5)) + << (SignalList() << Insert(1,1) << Move(9,32,3,1) << Remove(22,1) << Insert(29,3))); + QTest::newRow("3*5 (10)") + << (SignalListList() + << (SignalList() << Remove(36,4) << Move(3,2,14,0) << Insert(36,3) << Remove(30,7) << Insert(3,5)) + << (SignalList() << Insert(1,1) << Move(9,32,3,1) << Remove(22,1) << Insert(29,3) << Move(7,15,23,2))); + QTest::newRow("3*5 (11)") + << (SignalListList() + << (SignalList() << Remove(36,4) << Move(3,2,14,0) << Insert(36,3) << Remove(30,7) << Insert(3,5)) + << (SignalList() << Insert(1,1) << Move(9,32,3,1) << Remove(22,1) << Insert(29,3) << Move(7,15,23,2)) + << (SignalList() << Move(38,23,1,3))); + QTest::newRow("3*5 (12)") + << (SignalListList() + << (SignalList() << Remove(36,4) << Move(3,2,14,0) << Insert(36,3) << Remove(30,7) << Insert(3,5)) + << (SignalList() << Insert(1,1) << Move(9,32,3,1) << Remove(22,1) << Insert(29,3) << Move(7,15,23,2)) + << (SignalList() << Move(38,23,1,3) << Move(38,31,0,4))); + QTest::newRow("3*5 (13)") + << (SignalListList() + << (SignalList() << Remove(36,4) << Move(3,2,14,0) << Insert(36,3) << Remove(30,7) << Insert(3,5)) + << (SignalList() << Insert(1,1) << Move(9,32,3,1) << Remove(22,1) << Insert(29,3) << Move(7,15,23,2)) + << (SignalList() << Move(38,23,1,3) << Move(38,31,0,4) << Remove(26,11))); + QTest::newRow("3*5 (14)") + << (SignalListList() + << (SignalList() << Remove(36,4) << Move(3,2,14,0) << Insert(36,3) << Remove(30,7) << Insert(3,5)) + << (SignalList() << Insert(1,1) << Move(9,32,3,1) << Remove(22,1) << Insert(29,3) << Move(7,15,23,2)) + << (SignalList() << Move(38,23,1,3) << Move(38,31,0,4) << Remove(26,11) << Move(5,7,18,5))); + QTest::newRow("3*5 (15)") + << (SignalListList() + << (SignalList() << Remove(36,4) << Move(3,2,14,0) << Insert(36,3) << Remove(30,7) << Insert(3,5)) + << (SignalList() << Insert(1,1) << Move(9,32,3,1) << Remove(22,1) << Insert(29,3) << Move(7,15,23,2)) + << (SignalList() << Move(38,23,1,3) << Move(38,31,0,4) << Remove(26,11) << Move(5,7,18,5) << Move(19,0,8,6))); +} + +void tst_qqmlchangeset::apply() +{ + QFETCH(SignalListList, input); + + QQmlChangeSet set; + QQmlChangeSet linearSet; + + foreach (const SignalList &list, input) { + QQmlChangeSet intermediateSet; + foreach (const Signal &signal, list) { + if (signal.isRemove()) { + intermediateSet.remove(signal.index, signal.count); + linearSet.remove(signal.index, signal.count); + } else if (signal.isInsert()) { + intermediateSet.insert(signal.index, signal.count); + linearSet.insert(signal.index, signal.count); + } else if (signal.isMove()) { + intermediateSet.move(signal.index, signal.to, signal.count, signal.moveId); + linearSet.move(signal.index, signal.to, signal.count, signal.moveId); + } + } + set.apply(intermediateSet); + } + + SignalList changes; + foreach (const QQmlChangeSet::Remove &remove, set.removes()) + changes << Remove(remove.index, remove.count, remove.moveId, remove.offset); + foreach (const QQmlChangeSet::Insert &insert, set.inserts()) + changes << Insert(insert.index, insert.count, insert.moveId, insert.offset); + + SignalList linearChanges; + foreach (const QQmlChangeSet::Remove &remove, linearSet.removes()) + linearChanges << Remove(remove.index, remove.count, remove.moveId, remove.offset); + foreach (const QQmlChangeSet::Insert &insert, linearSet.inserts()) + linearChanges << Insert(insert.index, insert.count, insert.moveId, insert.offset); + + // The output in the failing tests isn't incorrect, merely sub-optimal. + QEXPECT_FAIL("3*5 (10)", "inserts not joined when dividing space removed", Abort); + QEXPECT_FAIL("3*5 (11)", "inserts not joined when dividing space removed", Abort); + QEXPECT_FAIL("3*5 (12)", "inserts not joined when dividing space removed", Abort); + QEXPECT_FAIL("3*5 (13)", "inserts not joined when dividing space removed", Abort); + QEXPECT_FAIL("3*5 (14)", "inserts not joined when dividing space removed", Abort); + QEXPECT_FAIL("3*5 (15)", "inserts not joined when dividing space removed", Abort); + QCOMPARE(changes, linearChanges); +} + +void tst_qqmlchangeset::removeConsecutive_data() +{ + QTest::addColumn("input"); + QTest::addColumn("output"); + + QTest::newRow("at start") + << (SignalList() << Remove(0,2) << Remove(0,1) << Remove(0,5)) + << (SignalList() << Remove(0,8)); + QTest::newRow("offset") + << (SignalList() << Remove(3,2) << Remove(3,1) << Remove(3,5)) + << (SignalList() << Remove(3,8)); + QTest::newRow("with move") + << (SignalList() << Remove(0,2) << Remove(0,1,0,0) << Remove(0,5)) + << (SignalList() << Remove(0,2) << Remove(0,1,0,0) << Remove(0,5)); +} + +void tst_qqmlchangeset::removeConsecutive() +{ + QFETCH(SignalList, input); + QFETCH(SignalList, output); + + QVector removes; + foreach (const Signal &signal, input) { + QVERIFY(signal.isRemove()); + removes.append(QQmlChangeSet::Remove(signal.index, signal.count, signal.moveId, signal.offset)); + } + + QQmlChangeSet set; + set.remove(removes); + + SignalList changes; + foreach (const QQmlChangeSet::Remove &remove, set.removes()) + changes << Remove(remove.index, remove.count, remove.moveId, remove.offset); + QVERIFY(set.inserts().isEmpty()); + QVERIFY(set.changes().isEmpty()); + + VERIFY_EXPECTED_OUTPUT + QCOMPARE(changes, output); +} + +void tst_qqmlchangeset::insertConsecutive_data() +{ + QTest::addColumn("input"); + QTest::addColumn("output"); + + QTest::newRow("at start") + << (SignalList() << Insert(0,2) << Insert(2,1) << Insert(3,5)) + << (SignalList() << Insert(0,8)); + QTest::newRow("offset") + << (SignalList() << Insert(3,2) << Insert(5,1) << Insert(6,5)) + << (SignalList() << Insert(3,8)); + QTest::newRow("with move") + << (SignalList() << Insert(0,2) << Insert(2,1,0,0) << Insert(3,5)) + << (SignalList() << Insert(0,2) << Insert(2,1,0,0) << Insert(3,5)); +} + +void tst_qqmlchangeset::insertConsecutive() +{ + QFETCH(SignalList, input); + QFETCH(SignalList, output); + + QVector inserts; + foreach (const Signal &signal, input) { + QVERIFY(signal.isInsert()); + inserts.append(QQmlChangeSet::Insert(signal.index, signal.count, signal.moveId, signal.offset)); + } + + QQmlChangeSet set; + set.insert(inserts); + + SignalList changes; + foreach (const QQmlChangeSet::Insert &insert, set.inserts()) + changes << Insert(insert.index, insert.count, insert.moveId, insert.offset); + QVERIFY(set.removes().isEmpty()); + QVERIFY(set.changes().isEmpty()); + + VERIFY_EXPECTED_OUTPUT + QCOMPARE(changes, output); +} + +void tst_qqmlchangeset::copy() +{ + QQmlChangeSet changeSet; + changeSet.remove(0, 12); + changeSet.remove(5, 4); + changeSet.insert(3, 9); + changeSet.insert(15, 2); + changeSet.change(24, 8); + changeSet.move(3, 5, 9, 0); + + QQmlChangeSet copy(changeSet); + + QQmlChangeSet assign; + assign = changeSet; + + copy.move(4, 2, 5, 1); + assign.move(4, 2, 5, 1); + changeSet.move(4, 2, 5, 1); + + QCOMPARE(copy.removes(), changeSet.removes()); + QCOMPARE(copy.inserts(), changeSet.inserts()); + QCOMPARE(copy.changes(), changeSet.changes()); + QCOMPARE(copy.difference(), changeSet.difference()); + + QCOMPARE(assign.removes(), changeSet.removes()); + QCOMPARE(assign.inserts(), changeSet.inserts()); + QCOMPARE(assign.changes(), changeSet.changes()); + QCOMPARE(assign.difference(), changeSet.difference()); +} + +void tst_qqmlchangeset::debug() +{ + QQmlChangeSet changeSet; + changeSet.remove(0, 12); + changeSet.remove(5, 4); + changeSet.insert(3, 9); + changeSet.insert(15, 2); + changeSet.change(24, 8); + + QTest::ignoreMessage(QtDebugMsg, "QQmlChangeSet(Remove(0,12) Remove(5,4) Insert(3,9) Insert(15,2) Change(24,8) )"); + qDebug() << changeSet; + + changeSet.clear(); + + QTest::ignoreMessage(QtDebugMsg, "QQmlChangeSet(Remove(12,4,0,0) Insert(5,4,0,0) )"); + + changeSet.move(12, 5, 4, 0); + qDebug() << changeSet; +} + +void tst_qqmlchangeset::random_data() +{ + QTest::addColumn("seed"); + QTest::addColumn("combinations"); + QTest::addColumn("depth"); + QTest::newRow("1*5") << 32 << 1 << 5; + QTest::newRow("2*2") << 32 << 2 << 2; + QTest::newRow("3*2") << 32 << 3 << 2; + QTest::newRow("3*5") << 32 << 3 << 5; +} + +void tst_qqmlchangeset::random() +{ + QFETCH(int, seed); + QFETCH(int, combinations); + QFETCH(int, depth); + + qsrand(seed); + + int failures = 0; + for (int i = 0; i < 20000; ++i) { + QQmlChangeSet accumulatedSet; + SignalList input; + + int modelCount = 40; + int moveCount = 0; + + for (int j = 0; j < combinations; ++j) { + QQmlChangeSet set; + for (int k = 0; k < depth; ++k) { + switch (-(qrand() % 3)) { + case InsertOp: { + int index = qrand() % (modelCount + 1); + int count = qrand() % 5 + 1; + set.insert(index, count); + input.append(Insert(index, count)); + modelCount += count; + break; + } + case RemoveOp: { + const int index = qrand() % (modelCount + 1); + const int count = qrand() % (modelCount - index + 1); + set.remove(index, count); + input.append(Remove(index, count)); + modelCount -= count; + break; + } + case MoveOp: { + const int from = qrand() % (modelCount + 1); + const int count = qrand() % (modelCount - from + 1); + const int to = qrand() % (modelCount - count + 1); + const int moveId = moveCount++; + set.move(from, to, count, moveId); + input.append(Move(from, to, count, moveId)); + break; + } + default: + break; + } + } + accumulatedSet.apply(set); + } + + SignalList output; + foreach (const QQmlChangeSet::Remove &remove, accumulatedSet.removes()) + output << Remove(remove.index, remove.count, remove.moveId, remove.offset); + foreach (const QQmlChangeSet::Insert &insert, accumulatedSet.inserts()) + output << Insert(insert.index, insert.count, insert.moveId, insert.offset); + + QVector inputList; + for (int i = 0; i < 40; ++i) + inputList.append(i); + QVector outputList = inputList; + if (!applyChanges(inputList, input)) { + qDebug() << "Invalid input list"; + qDebug() << input; + qDebug() << inputList; + ++failures; + } else if (!applyChanges(outputList, output)) { + qDebug() << "Invalid output list"; + qDebug() << input; + qDebug() << output; + qDebug() << outputList; + ++failures; + } else if (outputList != inputList) { + qDebug() << "Input/output mismatch"; + qDebug() << input; + qDebug() << output; + qDebug() << inputList; + qDebug() << outputList; + ++failures; + } + } + QCOMPARE(failures, 0); +} + +QTEST_MAIN(tst_qqmlchangeset) + +#include "tst_qqmlchangeset.moc" diff --git a/tests/auto/qml/qqmllistcompositor/qqmllistcompositor.pro b/tests/auto/qml/qqmllistcompositor/qqmllistcompositor.pro new file mode 100644 index 0000000000..e6c9dc3a29 --- /dev/null +++ b/tests/auto/qml/qqmllistcompositor/qqmllistcompositor.pro @@ -0,0 +1,10 @@ +CONFIG += testcase +TARGET = tst_qqmllistcompositor +macx:CONFIG -= app_bundle + +SOURCES += tst_qqmllistcompositor.cpp + +CONFIG += parallel_test + +QT += core-private gui-private qml-private quick-private testlib +DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 diff --git a/tests/auto/qml/qqmllistcompositor/tst_qqmllistcompositor.cpp b/tests/auto/qml/qqmllistcompositor/tst_qqmllistcompositor.cpp new file mode 100644 index 0000000000..d5e85f478d --- /dev/null +++ b/tests/auto/qml/qqmllistcompositor/tst_qqmllistcompositor.cpp @@ -0,0 +1,1740 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include +#include + +template int lengthOf(const T (&)[N]) { return N; } + +typedef QQmlListCompositor C; + +struct Range +{ + Range() {} + Range(void *list, int index, int count, int flags) + : list(list), index(index), count(count), flags(flags) {} + void *list; + int index; + int count; + int flags; +}; + +template struct Array +{ + Array() : array(0), count(0) {} + template Array(const T (&array)[N]) : array(array), count(N) {} + + T operator [](int index) const { return array[index]; } + + const T *array; + int count; +}; + +typedef Array IndexArray; +typedef Array ListArray; + +typedef QVector RemoveList; +typedef QVector InsertList; +typedef QVector ChangeList; + +typedef QVector RangeList; + +Q_DECLARE_METATYPE(RangeList) +Q_DECLARE_METATYPE(RemoveList) +Q_DECLARE_METATYPE(InsertList) +Q_DECLARE_METATYPE(ChangeList) +Q_DECLARE_METATYPE(void *) +Q_DECLARE_METATYPE(IndexArray) +Q_DECLARE_METATYPE(ListArray) +Q_DECLARE_METATYPE(C::Group) + +QT_BEGIN_NAMESPACE +bool operator ==(const C::Change &left, const C::Change &right) +{ + return left.index[3] == right.index[3] + && left.index[2] == right.index[2] + && left.index[1] == right.index[1] + && left.index[0] == right.index[0] + && left.count == right.count + && left.groups() == right.groups() + && left.inCache() == right.inCache() + && (left.moveId == -1) == (right.moveId == -1); +} +QT_END_NAMESPACE + +static const C::Group Visible = C::Group(2); +static const C::Group Selection = C::Group(3); + +class tst_qqmllistcompositor : public QObject +{ + Q_OBJECT + + enum { + VisibleFlag = 0x04, + SelectionFlag = 0x08 + }; + + void populateChange( + C::Change &change, int sIndex, int vIndex, int dIndex, int cIndex, int count, int flags, int moveId) + { + change.index[Selection] = sIndex; + change.index[Visible] = vIndex; + change.index[C::Default] = dIndex; + change.index[C::Cache] = cIndex; + change.count = count; + change.flags = flags; + change.moveId = moveId; + } + + C::Remove Remove( + int sIndex, int vIndex, int dIndex, int cIndex, int count, int flags, int moveId = -1) + { + C::Remove remove; + populateChange(remove, sIndex, vIndex, dIndex, cIndex, count, flags, moveId); + return remove; + } + + C::Insert Insert( + int sIndex, int vIndex, int dIndex, int cIndex, int count, int flags, int moveId = -1) + { + C::Insert insert; + populateChange(insert, sIndex, vIndex, dIndex, cIndex, count, flags, moveId); + return insert; + } + + C::Change Change( + int sIndex, int vIndex, int dIndex, int cIndex, int count, int flags, int moveId = -1) + { + C::Change change; + populateChange(change, sIndex, vIndex, dIndex, cIndex, count, flags, moveId); + return change; + } + +private slots: + void find_data(); + void find(); + void findInsertPosition_data(); + void findInsertPosition(); + void insert(); + void clearFlags_data(); + void clearFlags(); + void setFlags_data(); + void setFlags(); + void move_data(); + void move(); + void moveFromEnd(); + void clear(); + void listItemsInserted_data(); + void listItemsInserted(); + void listItemsRemoved_data(); + void listItemsRemoved(); + void listItemsMoved_data(); + void listItemsMoved(); + void listItemsChanged_data(); + void listItemsChanged(); + void compositorDebug(); + void changeDebug(); + void groupDebug(); +}; + +void tst_qqmllistcompositor::find_data() +{ + QTest::addColumn("ranges"); + QTest::addColumn("startGroup"); + QTest::addColumn("startIndex"); + QTest::addColumn("group"); + QTest::addColumn("index"); + QTest::addColumn("selectionIndex"); + QTest::addColumn("visibleIndex"); + QTest::addColumn("defaultIndex"); + QTest::addColumn("cacheIndex"); + QTest::addColumn("rangeFlags"); + QTest::addColumn("rangeIndex"); + + int listA; void *a = &listA; + + QTest::newRow("Start") + << (RangeList() + << Range(a, 0, 1, int(C::PrependFlag | SelectionFlag | C::DefaultFlag | C::CacheFlag)) + << Range(a, 1, 1, int(C::AppendFlag | C::PrependFlag | C::CacheFlag)) + << Range(0, 0, 1, int(VisibleFlag| C::CacheFlag))) + << C::Cache << 2 + << Selection << 0 + << 0 << 0 << 0 << 0 + << uint(C::PrependFlag | SelectionFlag | C::DefaultFlag | C::CacheFlag) << 0; +} + +void tst_qqmllistcompositor::find() +{ + QFETCH(RangeList, ranges); + QFETCH(C::Group, startGroup); + QFETCH(int, startIndex); + QFETCH(C::Group, group); + QFETCH(int, index); + QFETCH(int, cacheIndex); + QFETCH(int, defaultIndex); + QFETCH(int, visibleIndex); + QFETCH(int, selectionIndex); + QFETCH(uint, rangeFlags); + QFETCH(int, rangeIndex); + + QQmlListCompositor compositor; + compositor.setGroupCount(4); + compositor.setDefaultGroups(VisibleFlag | C::DefaultFlag); + + foreach (const Range &range, ranges) + compositor.append(range.list, range.index, range.count, range.flags); + + compositor.find(startGroup, startIndex); + + QQmlListCompositor::iterator it = compositor.find(group, index); + QCOMPARE(it.index[C::Cache], cacheIndex); + QCOMPARE(it.index[C::Default], defaultIndex); + QCOMPARE(it.index[Visible], visibleIndex); + QCOMPARE(it.index[Selection], selectionIndex); + QCOMPARE(it->flags, rangeFlags); + QCOMPARE(it->index, rangeIndex); +} + +void tst_qqmllistcompositor::findInsertPosition_data() +{ + QTest::addColumn("ranges"); + QTest::addColumn("group"); + QTest::addColumn("index"); + QTest::addColumn("selectionIndex"); + QTest::addColumn("visibleIndex"); + QTest::addColumn("defaultIndex"); + QTest::addColumn("cacheIndex"); + QTest::addColumn("rangeFlags"); + QTest::addColumn("rangeIndex"); + + int listA; void *a = &listA; + + QTest::newRow("Start") + << (RangeList() + << Range(a, 0, 1, int(C::PrependFlag | SelectionFlag | C::DefaultFlag | C::CacheFlag)) + << Range(a, 1, 1, int(C::AppendFlag | C::PrependFlag | C::CacheFlag)) + << Range(0, 0, 1, int(VisibleFlag| C::CacheFlag))) + << Selection << 0 + << 0 << 0 << 0 << 0 + << uint(C::PrependFlag | SelectionFlag | C::DefaultFlag | C::CacheFlag) << 0; + QTest::newRow("1") + << (RangeList() + << Range(a, 0, 1, int(C::PrependFlag | SelectionFlag | C::DefaultFlag | C::CacheFlag)) + << Range(a, 1, 1, int(C::AppendFlag | C::PrependFlag | C::CacheFlag)) + << Range(0, 0, 1, int(VisibleFlag| C::CacheFlag))) + << Selection << 1 + << 1 << 1 << 1 << 3 + << uint(0) << 0; +} + +void tst_qqmllistcompositor::findInsertPosition() +{ + QFETCH(RangeList, ranges); + QFETCH(C::Group, group); + QFETCH(int, index); + QFETCH(int, cacheIndex); + QFETCH(int, defaultIndex); + QFETCH(int, visibleIndex); + QFETCH(int, selectionIndex); + QFETCH(uint, rangeFlags); + QFETCH(int, rangeIndex); + + QQmlListCompositor compositor; + compositor.setGroupCount(4); + compositor.setDefaultGroups(VisibleFlag | C::DefaultFlag); + + foreach (const Range &range, ranges) + compositor.append(range.list, range.index, range.count, range.flags); + + QQmlListCompositor::insert_iterator it = compositor.findInsertPosition(group, index); + + QCOMPARE(it.index[C::Cache], cacheIndex); + QCOMPARE(it.index[C::Default], defaultIndex); + QCOMPARE(it.index[Visible], visibleIndex); + QCOMPARE(it.index[Selection], selectionIndex); + QCOMPARE(it->flags, rangeFlags); + QCOMPARE(it->index, rangeIndex); +} + +void tst_qqmllistcompositor::insert() +{ + QQmlListCompositor compositor; + compositor.setGroupCount(4); + compositor.setDefaultGroups(VisibleFlag | C::DefaultFlag); + + C::iterator it; + + int listA; int *a = &listA; + int listB; int *b = &listB; + int listC; int *c = &listC; + + { + compositor.append(a, 0, 12, C::AppendFlag | C::PrependFlag | C::DefaultFlag); + const int indexes[] = {0,1,2,3,4,5,6,7,8,9,10,11}; + const int *lists[] = {a,a,a,a,a,a,a,a,a,a, a, a}; + QCOMPARE(compositor.count(C::Default), lengthOf(indexes)); + for (int i = 0; i < lengthOf(indexes); ++i) { + it = compositor.find(C::Default, i); + QCOMPARE(it.list(), lists[i]); + if (lists[i]) QCOMPARE(it.modelIndex(), indexes[i]); + } + } { + compositor.append(b, 4, 4, C::DefaultFlag); + const int indexes[] = {0,1,2,3,4,5,6,7,8,9,10,11,4,5,6,7}; + const int *lists[] = {a,a,a,a,a,a,a,a,a,a, a, a,b,b,b,b}; + QCOMPARE(compositor.count(C::Default), lengthOf(indexes)); + for (int i = 0; i < lengthOf(indexes); ++i) { + it = compositor.find(C::Default, i); + QCOMPARE(it.list(), lists[i]); + if (lists[i]) QCOMPARE(it.modelIndex(), indexes[i]); + } + } { // Insert at end. + compositor.insert( + C::Default, 16, c, 2, 2, C::DefaultFlag); + const int indexes[] = {0,1,2,3,4,5,6,7,8,9,10,11,4,5,6,7,2,3}; + const int *lists[] = {a,a,a,a,a,a,a,a,a,a, a, a,b,b,b,b,c,c}; + QCOMPARE(compositor.count(C::Default), lengthOf(indexes)); + for (int i = 0; i < lengthOf(indexes); ++i) { + it = compositor.find(C::Default, i); + QCOMPARE(it.list(), lists[i]); + if (lists[i]) QCOMPARE(it.modelIndex(), indexes[i]); + } + } { // Insert at start + compositor.insert( + C::Default, 0, c, 6, 4, C::DefaultFlag); + const int indexes[] = {6,7,8,9,0,1,2,3,4,5,6,7,8,9,10,11,4,5,6,7,2,3}; + const int *lists[] = {c,c,c,c,a,a,a,a,a,a,a,a,a,a, a, a,b,b,b,b,c,c}; + for (int i = 0; i < lengthOf(indexes); ++i) { + it = compositor.find(C::Default, i); + QCOMPARE(it.list(), lists[i]); + if (lists[i]) QCOMPARE(it.modelIndex(), indexes[i]); + } + } { // Insert after static range. + compositor.insert( + C::Default, 4, b, 0, 8, C::AppendFlag | C::PrependFlag | C::DefaultFlag); + const int indexes[] = {6,7,8,9,0,1,2,3,4,5,6,7,0,1,2,3,4,5,6,7,8,9,10,11,4,5,6,7,2,3}; + const int *lists[] = {c,c,c,c,b,b,b,b,b,b,b,b,a,a,a,a,a,a,a,a,a,a, a, a,b,b,b,b,c,c}; + QCOMPARE(compositor.count(C::Default), lengthOf(indexes)); + for (int i = 0; i < lengthOf(indexes); ++i) { + it = compositor.find(C::Default, i); + QCOMPARE(it.list(), lists[i]); + if (lists[i]) QCOMPARE(it.modelIndex(), indexes[i]); + } + } { // Insert at end of dynamic range. + compositor.insert( + C::Default, 12, c, 0, 4, C::AppendFlag | C::PrependFlag | C::DefaultFlag); + const int indexes[] = {6,7,8,9,0,1,2,3,4,5,6,7,0,1,2,3,0,1,2,3,4,5,6,7,8,9,10,11,4,5,6,7,2,3}; + const int *lists[] = {c,c,c,c,b,b,b,b,b,b,b,b,c,c,c,c,a,a,a,a,a,a,a,a,a,a, a, a,b,b,b,b,c,c}; + QCOMPARE(compositor.count(C::Default), lengthOf(indexes)); + for (int i = 0; i < lengthOf(indexes); ++i) { + it = compositor.find(C::Default, i); + QCOMPARE(it.list(), lists[i]); + if (lists[i]) QCOMPARE(it.modelIndex(), indexes[i]); + } + } { // Insert into range. + compositor.insert( + C::Default, 8, c, 0, 4, C::AppendFlag | C::PrependFlag | C::DefaultFlag); + const int indexes[] = {6,7,8,9,0,1,2,3,0,1,2,3,4,5,6,7,0,1,2,3,0,1,2,3,4,5,6,7,8,9,10,11,4,5,6,7,2,3}; + const int *lists[] = {c,c,c,c,b,b,b,b,c,c,c,c,b,b,b,b,c,c,c,c,a,a,a,a,a,a,a,a,a,a, a, a,b,b,b,b,c,c}; + QCOMPARE(compositor.count(C::Default), lengthOf(indexes)); + for (int i = 0; i < lengthOf(indexes); ++i) { + it = compositor.find(C::Default, i); + QCOMPARE(it.list(), lists[i]); + if (lists[i]) QCOMPARE(it.modelIndex(), indexes[i]); + } + } +} + +void tst_qqmllistcompositor::clearFlags_data() +{ + QTest::addColumn("ranges"); + QTest::addColumn("group"); + QTest::addColumn("index"); + QTest::addColumn("count"); + QTest::addColumn("flags"); + QTest::addColumn("expectedRemoves"); + QTest::addColumn("cacheIndexes"); + QTest::addColumn("cacheLists"); + QTest::addColumn("defaultIndexes"); + QTest::addColumn("defaultLists"); + QTest::addColumn("visibleIndexes"); + QTest::addColumn("visibleLists"); + QTest::addColumn("selectionIndexes"); + QTest::addColumn("selectionLists"); + + int listA; void *a = &listA; + + { static const int cacheIndexes[] = {0,1,2,3,4,5,6,7,8,9,10,11,0,0,0,0}; + static const void *cacheLists[] = {a,a,a,a,a,a,a,a,a,a, a, a,0,0,0,0}; + static const int defaultIndexes[] = {0,1,2,3,4,5,6,7,8,9,10,11,0,0,0,0}; + static const void *defaultLists[] = {a,a,a,a,a,a,a,a,a,a, a, a,0,0,0,0}; + static const int visibleIndexes[] = {0,1,2,3,4,5,6,7,8,9,10,11,0,0,0,0}; + static const void *visibleLists[] = {a,a,a,a,a,a,a,a,a,a, a, a,0,0,0,0}; + static const int selectionIndexes[] = {0,1,4,5,6,7,8,9,10,11,0,0,0,0}; + static const void *selectionLists[] = {a,a,a,a,a,a,a,a, a, a,0,0,0,0}; + QTest::newRow("Default, 2, 2, Selection") + << (RangeList() + << Range(a, 0, 12, int(C::AppendFlag | C::PrependFlag | SelectionFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag)) + << Range(0, 0, 4, int(SelectionFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag))) + << C::Default << 2 << 2 << int(SelectionFlag) + << (RemoveList() + << Remove(2, 2, 2, 2, 2, SelectionFlag | C::CacheFlag)) + << IndexArray(cacheIndexes) << ListArray(cacheLists) + << IndexArray(defaultIndexes) << ListArray(defaultLists) + << IndexArray(visibleIndexes) << ListArray(visibleLists) + << IndexArray(selectionIndexes) << ListArray(selectionLists); + } { static const int cacheIndexes[] = {0,1,2,3,4,5,6,7,8,9,10,11,0,0,0,0}; + static const void *cacheLists[] = {a,a,a,a,a,a,a,a,a,a, a, a,0,0,0,0}; + static const int defaultIndexes[] = {0,1,2,3,4,5,6,7,8,9,10,11,0,0,0,0}; + static const void *defaultLists[] = {a,a,a,a,a,a,a,a,a,a, a, a,0,0,0,0}; + static const int visibleIndexes[] = {0,2,3,5,6,7,8,9,10,11,0,0,0,0}; + static const void *visibleLists[] = {a,a,a,a,a,a,a,a, a, a,0,0,0,0}; + static const int selectionIndexes[] = {0,1,4,5,6,7,8,9,10,11,0,0,0,0}; + static const void *selectionLists[] = {a,a,a,a,a,a,a,a, a, a,0,0,0,0}; + QTest::newRow("Selection, 1, 2, Visible") + << (RangeList() + << Range(a, 0, 2, int(C::PrependFlag | SelectionFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag)) + << Range(a, 2, 2, int(C::PrependFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag)) + << Range(a, 4, 8, int(C::AppendFlag | C::PrependFlag | SelectionFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag)) + << Range(0, 0, 4, int(SelectionFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag))) + << Selection << 1 << 2 << int(VisibleFlag) + << (RemoveList() + << Remove(1, 1, 1, 1, 1, VisibleFlag | C::CacheFlag) + << Remove(2, 3, 4, 4, 1, VisibleFlag | C::CacheFlag)) + << IndexArray(cacheIndexes) << ListArray(cacheLists) + << IndexArray(defaultIndexes) << ListArray(defaultLists) + << IndexArray(visibleIndexes) << ListArray(visibleLists) + << IndexArray(selectionIndexes) << ListArray(selectionLists); + } { static const int cacheIndexes[] = {0,1,2,3,4,5,6,7,8,9,10,11,0,0,0,0}; + static const void *cacheLists[] = {a,a,a,a,a,a,a,a,a,a, a, a,0,0,0,0}; + static const int defaultIndexes[] = {0,1,2,3,4,5,6,7,8,9,10,11,0,0,0}; + static const void *defaultLists[] = {a,a,a,a,a,a,a,a,a,a, a, a,0,0,0}; + static const int visibleIndexes[] = {0,2,3,5,6,7,8,9,10,11,0,0,0}; + static const void *visibleLists[] = {a,a,a,a,a,a,a,a, a, a,0,0,0}; + static const int selectionIndexes[] = {0,1,4,5,6,7,8,9,10,11,0,0,0}; + static const void *selectionLists[] = {a,a,a,a,a,a,a,a, a, a,0,0,0}; + QTest::newRow("Default, 13, 1, Prepend | Selection | Visible | Default") + << (RangeList() + << Range(a, 0, 1, int(C::PrependFlag | SelectionFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag)) + << Range(a, 1, 1, int(C::PrependFlag | SelectionFlag | C::DefaultFlag | C::CacheFlag)) + << Range(a, 2, 2, int(C::PrependFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag)) + << Range(a, 4, 1, int(C::PrependFlag | SelectionFlag | C::DefaultFlag | C::CacheFlag)) + << Range(a, 5, 7, int(C::AppendFlag | C::PrependFlag | SelectionFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag)) + << Range(0, 0, 4, int(SelectionFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag))) + << C::Default << 13 << 1 << int(C::PrependFlag | SelectionFlag | VisibleFlag | C::DefaultFlag) + << (RemoveList() + << Remove(11, 11, 13, 13, 1, SelectionFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag)) + << IndexArray(cacheIndexes) << ListArray(cacheLists) + << IndexArray(defaultIndexes) << ListArray(defaultLists) + << IndexArray(visibleIndexes) << ListArray(visibleLists) + << IndexArray(selectionIndexes) << ListArray(selectionLists); + } { static const int cacheIndexes[] = {0,1,2,3,4,5,6,7,8,9,10,0}; + static const void *cacheLists[] = {a,a,a,a,a,a,a,a,a,a, a,0}; + static const int defaultIndexes[] = {0,1,2,3,4,5,6,7,8,9,10,11,0,0,0}; + static const void *defaultLists[] = {a,a,a,a,a,a,a,a,a,a, a, a,0,0,0}; + static const int visibleIndexes[] = {0,2,3,5,6,7,8,9,10,11,0,0,0}; + static const void *visibleLists[] = {a,a,a,a,a,a,a,a, a, a,0,0,0}; + static const int selectionIndexes[] = {0,1,4,5,6,7,8,9,10,11,0,0,0}; + static const void *selectionLists[] = {a,a,a,a,a,a,a,a, a, a,0,0,0}; + QTest::newRow("Cache, 11, 4, Cache") + << (RangeList() + << Range(a, 0, 1, int(C::PrependFlag | SelectionFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag)) + << Range(a, 1, 1, int(C::PrependFlag | SelectionFlag | C::DefaultFlag | C::CacheFlag)) + << Range(a, 2, 2, int(C::PrependFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag)) + << Range(a, 4, 1, int(C::PrependFlag | SelectionFlag | C::DefaultFlag | C::CacheFlag)) + << Range(a, 5, 7, int(C::AppendFlag | C::PrependFlag | SelectionFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag)) + << Range(0, 0, 1, int(C::CacheFlag)) + << Range(0, 0, 3, int(SelectionFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag))) + << C::Cache << 11 << 4 << int(C::CacheFlag) + << (RemoveList()) + << IndexArray(cacheIndexes) << ListArray(cacheLists) + << IndexArray(defaultIndexes) << ListArray(defaultLists) + << IndexArray(visibleIndexes) << ListArray(visibleLists) + << IndexArray(selectionIndexes) << ListArray(selectionLists); + } { static const int cacheIndexes[] = {0,1,2,3,4,5,6,7,8,9,10,0}; + static const void *cacheLists[] = {a,a,a,a,a,a,a,a,a,a, a,0}; + static const int defaultIndexes[] = {0,1,2,3,4,5,6,7,8,9,10,0}; + static const void *defaultLists[] = {a,a,a,a,a,a,a,a,a,a, a,0}; + static const int visibleIndexes[] = {0,2,3,5,6,7,8,9,10,0}; + static const void *visibleLists[] = {a,a,a,a,a,a,a,a, a,0}; + static const int selectionIndexes[] = {0,1,4,5,6,7,8,9,10,0}; + static const void *selectionLists[] = {a,a,a,a,a,a,a,a, a,0}; + QTest::newRow("Default, 11, 3, Default | Visible | Selection") + << (RangeList() + << Range(a, 0, 1, int(C::PrependFlag | SelectionFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag)) + << Range(a, 1, 1, int(C::PrependFlag | SelectionFlag | C::DefaultFlag | C::CacheFlag)) + << Range(a, 2, 2, int(C::PrependFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag)) + << Range(a, 4, 1, int(C::PrependFlag | SelectionFlag | C::DefaultFlag | C::CacheFlag)) + << Range(a, 5, 6, int(C::PrependFlag | SelectionFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag)) + << Range(a, 11, 1, int(C::AppendFlag | C::PrependFlag | SelectionFlag | VisibleFlag | C::DefaultFlag)) + << Range(0, 0, 2, int(SelectionFlag | VisibleFlag | C::DefaultFlag)) + << Range(0, 0, 1, int(SelectionFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag))) + << C::Default << 11 << 3 << int(C::DefaultFlag | VisibleFlag| SelectionFlag) + << (RemoveList() + << Remove(9, 9, 11, 11, 1, SelectionFlag | VisibleFlag | C::DefaultFlag) + << Remove(9, 9, 11, 11, 2, SelectionFlag | VisibleFlag | C::DefaultFlag)) + << IndexArray(cacheIndexes) << ListArray(cacheLists) + << IndexArray(defaultIndexes) << ListArray(defaultLists) + << IndexArray(visibleIndexes) << ListArray(visibleLists) + << IndexArray(selectionIndexes) << ListArray(selectionLists); + } +} + +void tst_qqmllistcompositor::clearFlags() +{ + QFETCH(RangeList, ranges); + QFETCH(C::Group, group); + QFETCH(int, index); + QFETCH(int, count); + QFETCH(int, flags); + QFETCH(RemoveList, expectedRemoves); + QFETCH(IndexArray, cacheIndexes); + QFETCH(ListArray, cacheLists); + QFETCH(IndexArray, defaultIndexes); + QFETCH(ListArray, defaultLists); + QFETCH(IndexArray, visibleIndexes); + QFETCH(ListArray, visibleLists); + QFETCH(IndexArray, selectionIndexes); + QFETCH(ListArray, selectionLists); + + QQmlListCompositor compositor; + compositor.setGroupCount(4); + compositor.setDefaultGroups(VisibleFlag | C::DefaultFlag); + + foreach (const Range &range, ranges) + compositor.append(range.list, range.index, range.count, range.flags); + + QVector removes; + compositor.clearFlags(group, index, count, flags, &removes); + + QCOMPARE(removes, expectedRemoves); + + QCOMPARE(compositor.count(C::Cache), cacheIndexes.count); + for (int i = 0; i < cacheIndexes.count; ++i) { + C::iterator it = compositor.find(C::Cache, i); + QCOMPARE(it->list, cacheLists[i]); + if (cacheLists[i]) + QCOMPARE(it.modelIndex(), cacheIndexes[i]); + } + QCOMPARE(compositor.count(C::Default), defaultIndexes.count); + for (int i = 0; i < defaultIndexes.count; ++i) { + C::iterator it = compositor.find(C::Default, i); + QCOMPARE(it->list, defaultLists[i]); + if (defaultLists[i]) + QCOMPARE(it.modelIndex(), defaultIndexes[i]); + } + QCOMPARE(compositor.count(Visible), visibleIndexes.count); + for (int i = 0; i < visibleIndexes.count; ++i) { + C::iterator it = compositor.find(Visible, i); + QCOMPARE(it->list, visibleLists[i]); + if (visibleLists[i]) + QCOMPARE(it.modelIndex(), visibleIndexes[i]); + } + QCOMPARE(compositor.count(Selection), selectionIndexes.count); + for (int i = 0; i < selectionIndexes.count; ++i) { + C::iterator it = compositor.find(Selection, i); + QCOMPARE(it->list, selectionLists[i]); + if (selectionLists[i]) + QCOMPARE(it.modelIndex(), selectionIndexes[i]); + } +} + +void tst_qqmllistcompositor::setFlags_data() +{ + QTest::addColumn("ranges"); + QTest::addColumn("group"); + QTest::addColumn("index"); + QTest::addColumn("count"); + QTest::addColumn("flags"); + QTest::addColumn("expectedInserts"); + QTest::addColumn("cacheIndexes"); + QTest::addColumn("cacheLists"); + QTest::addColumn("defaultIndexes"); + QTest::addColumn("defaultLists"); + QTest::addColumn("visibleIndexes"); + QTest::addColumn("visibleLists"); + QTest::addColumn("selectionIndexes"); + QTest::addColumn("selectionLists"); + + int listA; void *a = &listA; + + { static const int cacheIndexes[] = {0,0,0,0}; + static const void *cacheLists[] = {0,0,0,0}; + static const int defaultIndexes[] = {0,1,2,3,4,5,6,7,8,9,10,11}; + static const void *defaultLists[] = {a,a,a,a,a,a,a,a,a,a, a, a}; + QTest::newRow("Default, 2, 2, Default") + << (RangeList() + << Range(a, 0, 12, C::DefaultFlag) + << Range(0, 0, 4, C::CacheFlag)) + << C::Default << 2 << 2 << int(C::DefaultFlag) + << (InsertList()) + << IndexArray(cacheIndexes) << ListArray(cacheLists) + << IndexArray(defaultIndexes) << ListArray(defaultLists) + << IndexArray() << ListArray() + << IndexArray() << ListArray(); + } { static const int cacheIndexes[] = {0,0,0,0}; + static const void *cacheLists[] = {0,0,0,0}; + static const int defaultIndexes[] = {0,1,2,3,4,5,6,7,8,9,10,11}; + static const void *defaultLists[] = {a,a,a,a,a,a,a,a,a,a, a, a}; + static const int visibleIndexes[] = {2,3}; + static const void *visibleLists[] = {a,a}; + QTest::newRow("Default, 2, 2, Visible") + << (RangeList() + << Range(a, 0, 12, C::DefaultFlag) + << Range(0, 0, 4, C::CacheFlag)) + << C::Default << 2 << 2 << int(VisibleFlag) + << (InsertList() + << Insert(0, 0, 2, 0, 2, VisibleFlag)) + << IndexArray(cacheIndexes) << ListArray(cacheLists) + << IndexArray(defaultIndexes) << ListArray(defaultLists) + << IndexArray(visibleIndexes) << ListArray(visibleLists) + << IndexArray() << ListArray(); + } { static const int cacheIndexes[] = {3,6,0,0,0,0}; + static const void *cacheLists[] = {a,a,0,0,0,0}; + static const int defaultIndexes[] = {0,1,2,3,4,5,6,7,8,9,10,11}; + static const void *defaultLists[] = {a,a,a,a,a,a,a,a,a,a, a, a}; + static const int visibleIndexes[] = {2,3,6,7}; + static const void *visibleLists[] = {a,a,a,a}; + static const int selectionIndexes[] = {3,6}; + static const void *selectionLists[] = {a,a}; + QTest::newRow("Visible, 1, 2, Selection | Cache") + << (RangeList() + << Range(a, 0, 2, C::DefaultFlag) + << Range(a, 2, 2, VisibleFlag | C::DefaultFlag) + << Range(a, 4, 2, C::DefaultFlag) + << Range(a, 6, 2, VisibleFlag | C::DefaultFlag) + << Range(a, 8, 4, C::DefaultFlag) + << Range(0, 0, 4, C::CacheFlag)) + << Visible << 1 << 2 << int(SelectionFlag | C::CacheFlag) + << (InsertList() + << Insert(0, 1, 3, 0, 1, SelectionFlag | C::CacheFlag) + << Insert(1, 2, 6, 1, 1, SelectionFlag | C::CacheFlag)) + << IndexArray(cacheIndexes) << ListArray(cacheLists) + << IndexArray(defaultIndexes) << ListArray(defaultLists) + << IndexArray(visibleIndexes) << ListArray(visibleLists) + << IndexArray(selectionIndexes) << ListArray(selectionLists); + } { static const int cacheIndexes[] = {3,6,0,0,0,0}; + static const void *cacheLists[] = {a,a,0,0,0,0}; + static const int defaultIndexes[] = {0,1,2,3,4,5,6,7,8,9,10,11}; + static const void *defaultLists[] = {a,a,a,a,a,a,a,a,a,a, a, a}; + static const int visibleIndexes[] = {2,3,6,7,0}; + static const void *visibleLists[] = {a,a,a,a,0}; + static const int selectionIndexes[] = {3,6}; + static const void *selectionLists[] = {a,a}; + QTest::newRow("Cache, 3, 1, Visible") + << (RangeList() + << Range(a, 0, 2, C::DefaultFlag) + << Range(a, 2, 1, VisibleFlag | C::DefaultFlag) + << Range(a, 3, 1, SelectionFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag) + << Range(a, 4, 2, C::DefaultFlag) + << Range(a, 6, 1, SelectionFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag) + << Range(a, 7, 1, VisibleFlag | C::DefaultFlag) + << Range(a, 8, 4, C::DefaultFlag) + << Range(0, 0, 4, C::CacheFlag)) + << C::Cache << 3 << 1 << int(VisibleFlag) + << (InsertList() + << Insert(2, 4, 12, 3, 1, VisibleFlag | C::CacheFlag)) + << IndexArray(cacheIndexes) << ListArray(cacheLists) + << IndexArray(defaultIndexes) << ListArray(defaultLists) + << IndexArray(visibleIndexes) << ListArray(visibleLists) + << IndexArray(selectionIndexes) << ListArray(selectionLists); + } { static const int cacheIndexes[] = {0,1,2,3,4,5,6,7,8,9,10,11}; + static const void *cacheLists[] = {a,a,a,a,a,a,a,a,a,a, a, a}; + static const int defaultIndexes[] = {0,1,2,3,4,5,6,7,8,9,10,11}; + static const void *defaultLists[] = {a,a,a,a,a,a,a,a,a,a, a, a}; + static const int visibleIndexes[] = {0,1,3,4,5,6,7,8,9,10,11}; + static const void *visibleLists[] = {a,a,a,a,a,a,a,a,a, a, a}; + static const int selectionIndexes[] = {2,6,7,8,9}; + static const void *selectionLists[] = {a,a,a,a,a}; + QTest::newRow("Existing flag, sparse selection") + << (RangeList() + << Range(a, 0, 2, C::PrependFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag) + << Range(a, 2, 1, C::PrependFlag | SelectionFlag | C::DefaultFlag | C::CacheFlag) + << Range(a, 3, 3, C::PrependFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag) + << Range(a, 6, 4, C::PrependFlag | SelectionFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag) + << Range(a,10, 2, C::AppendFlag | C::PrependFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag)) + << C::Cache << 3 << 1 << int(VisibleFlag) + << InsertList() + << IndexArray(cacheIndexes) << ListArray(cacheLists) + << IndexArray(defaultIndexes) << ListArray(defaultLists) + << IndexArray(visibleIndexes) << ListArray(visibleLists) + << IndexArray(selectionIndexes) << ListArray(selectionLists); + } +} + +void tst_qqmllistcompositor::setFlags() +{ + QFETCH(RangeList, ranges); + QFETCH(C::Group, group); + QFETCH(int, index); + QFETCH(int, count); + QFETCH(int, flags); + QFETCH(InsertList, expectedInserts); + QFETCH(IndexArray, cacheIndexes); + QFETCH(ListArray, cacheLists); + QFETCH(IndexArray, defaultIndexes); + QFETCH(ListArray, defaultLists); + QFETCH(IndexArray, visibleIndexes); + QFETCH(ListArray, visibleLists); + QFETCH(IndexArray, selectionIndexes); + QFETCH(ListArray, selectionLists); + + QQmlListCompositor compositor; + compositor.setGroupCount(4); + compositor.setDefaultGroups(VisibleFlag | C::DefaultFlag); + + foreach (const Range &range, ranges) + compositor.append(range.list, range.index, range.count, range.flags); + + QVector inserts; + compositor.setFlags(group, index, count, flags, &inserts); + + QCOMPARE(inserts, expectedInserts); + + QCOMPARE(compositor.count(C::Cache), cacheIndexes.count); + for (int i = 0; i < cacheIndexes.count; ++i) { + C::iterator it = compositor.find(C::Cache, i); + QCOMPARE(it->list, cacheLists[i]); + if (cacheLists[i]) + QCOMPARE(it.modelIndex(), cacheIndexes[i]); + } + QCOMPARE(compositor.count(C::Default), defaultIndexes.count); + for (int i = 0; i < defaultIndexes.count; ++i) { + C::iterator it = compositor.find(C::Default, i); + QCOMPARE(it->list, defaultLists[i]); + if (defaultLists[i]) + QCOMPARE(it.modelIndex(), defaultIndexes[i]); + } + QCOMPARE(compositor.count(Visible), visibleIndexes.count); + for (int i = 0; i < visibleIndexes.count; ++i) { + C::iterator it = compositor.find(Visible, i); + QCOMPARE(it->list, visibleLists[i]); + if (visibleLists[i]) + QCOMPARE(it.modelIndex(), visibleIndexes[i]); + } + QCOMPARE(compositor.count(Selection), selectionIndexes.count); + for (int i = 0; i < selectionIndexes.count; ++i) { + C::iterator it = compositor.find(Selection, i); + QCOMPARE(it->list, selectionLists[i]); + if (selectionLists[i]) + QCOMPARE(it.modelIndex(), selectionIndexes[i]); + } +} + +void tst_qqmllistcompositor::move_data() +{ + QTest::addColumn("ranges"); + QTest::addColumn("fromGroup"); + QTest::addColumn("from"); + QTest::addColumn("toGroup"); + QTest::addColumn("to"); + QTest::addColumn("count"); + QTest::addColumn("expectedRemoves"); + QTest::addColumn("expectedInserts"); + QTest::addColumn("cacheIndexes"); + QTest::addColumn("cacheLists"); + QTest::addColumn("defaultIndexes"); + QTest::addColumn("defaultLists"); + QTest::addColumn("visibleIndexes"); + QTest::addColumn("visibleLists"); + QTest::addColumn("selectionIndexes"); + QTest::addColumn("selectionLists"); + + int listA; void *a = &listA; + int listB; void *b = &listB; + int listC; void *c = &listC; + + { static const int cacheIndexes[] = {0,0,0,0,2,3}; + static const void *cacheLists[] = {0,0,0,0,c,c}; + static const int defaultIndexes[] = {0,0,1,2,3,4,5,0,1,2,3,4,5,1,2,3,0,1,2,3,4,5}; + static const void *defaultLists[] = {0,a,a,a,a,a,a,b,b,b,b,b,b,0,0,0,c,c,c,c,c,c}; + QTest::newRow("15, 0, 1") + << (RangeList() + << Range(a, 0, 6, C::DefaultFlag) + << Range(b, 0, 6, C::AppendFlag | C::PrependFlag | C::DefaultFlag) + << Range(0, 0, 4, C::DefaultFlag | C::CacheFlag) + << Range(c, 0, 2, C::PrependFlag | C::DefaultFlag) + << Range(c, 2, 2, C::PrependFlag | C::DefaultFlag | C::CacheFlag) + << Range(c, 4, 2, C::AppendFlag | C::PrependFlag | C::DefaultFlag)) + << C::Default << 15 << C::Default << 0 << 1 + << (RemoveList() + << Remove(0, 0, 15, 3, 1, C::DefaultFlag | C::CacheFlag, 0)) + << (InsertList() + << Insert(0, 0, 0, 0, 1, C::DefaultFlag | C::CacheFlag, 0)) + << IndexArray(cacheIndexes) << ListArray(cacheLists) + << IndexArray(defaultIndexes) << ListArray(defaultLists) + << IndexArray() << ListArray() + << IndexArray() << ListArray(); + } { static const int cacheIndexes[] = {0,0,0,0,2,3}; + static const void *cacheLists[] = {0,0,0,0,c,c}; + static const int defaultIndexes[] = {0,1,0,1,2,3,4,5,0,1,2,3,4,5,2,3,0,1,2,3,4,5}; + static const void *defaultLists[] = {0,0,a,a,a,a,a,a,b,b,b,b,b,b,0,0,c,c,c,c,c,c}; + QTest::newRow("15, 1, 1") + << (RangeList() + << Range(0, 0, 1, C::DefaultFlag | C::CacheFlag) + << Range(a, 0, 6, C::DefaultFlag) + << Range(b, 0, 6, C::AppendFlag | C::PrependFlag | C::DefaultFlag) + << Range(0, 0, 3, C::DefaultFlag | C::CacheFlag) + << Range(c, 0, 2, C::PrependFlag | C::DefaultFlag) + << Range(c, 2, 2, C::PrependFlag | C::DefaultFlag | C::CacheFlag) + << Range(c, 4, 2, C::AppendFlag | C::PrependFlag | C::DefaultFlag)) + << C::Default << 15 << C::Default << 1 << 1 + << (RemoveList() + << Remove(0, 0, 15, 3, 1, C::DefaultFlag | C::CacheFlag, 0)) + << (InsertList() + << Insert(0, 0, 1, 1, 1, C::DefaultFlag | C::CacheFlag, 0)) + << IndexArray(cacheIndexes) << ListArray(cacheLists) + << IndexArray(defaultIndexes) << ListArray(defaultLists) + << IndexArray() << ListArray() + << IndexArray() << ListArray(); + } { static const int cacheIndexes[] = {0,0,0,0,2,3}; + static const void *cacheLists[] = {0,0,0,0,c,c}; + static const int defaultIndexes[] = {0,1,2,0,1,3,4,5,0,1,2,3,4,5,2,3,0,1,2,3,4,5}; + static const void *defaultLists[] = {a,a,a,0,0,a,a,a,b,b,b,b,b,b,0,0,c,c,c,c,c,c}; + QTest::newRow("0, 3, 2") + << (RangeList() + << Range(0, 0, 2, C::DefaultFlag | C::CacheFlag) + << Range(a, 0, 6, C::DefaultFlag) + << Range(b, 0, 6, C::AppendFlag | C::PrependFlag | C::DefaultFlag) + << Range(0, 0, 2, C::DefaultFlag | C::CacheFlag) + << Range(c, 0, 2, C::PrependFlag | C::DefaultFlag) + << Range(c, 2, 2, C::PrependFlag | C::DefaultFlag | C::CacheFlag) + << Range(c, 4, 2, C::AppendFlag | C::PrependFlag | C::DefaultFlag)) + << C::Default << 0 << C::Default << 3 << 2 + << (RemoveList() + << Remove(0, 0, 0, 0, 2, C::DefaultFlag | C::CacheFlag, 0)) + << (InsertList() + << Insert(0, 0, 3, 0, 2, C::DefaultFlag | C::CacheFlag, 0)) + << IndexArray(cacheIndexes) << ListArray(cacheLists) + << IndexArray(defaultIndexes) << ListArray(defaultLists) + << IndexArray() << ListArray() + << IndexArray() << ListArray(); + } { static const int cacheIndexes[] = {0,0,0,0,2,3}; + static const void *cacheLists[] = {0,0,0,0,c,c}; + static const int defaultIndexes[] = {0,5,0,1,2,3,4,5,0,1,0,1,2,2,3,3,4,1,2,3,4,5}; + static const void *defaultLists[] = {a,a,b,b,b,b,b,b,0,0,c,a,a,0,0,a,a,c,c,c,c,c}; + QTest::newRow("7, 1, 10") + << (RangeList() + << Range(a, 0, 3, C::DefaultFlag) + << Range(0, 0, 2, C::DefaultFlag | C::CacheFlag) + << Range(a, 3, 3, C::DefaultFlag) + << Range(b, 0, 6, C::AppendFlag | C::PrependFlag | C::DefaultFlag) + << Range(0, 0, 2, C::DefaultFlag | C::CacheFlag) + << Range(c, 0, 2, C::PrependFlag | C::DefaultFlag) + << Range(c, 2, 2, C::PrependFlag | C::DefaultFlag | C::CacheFlag) + << Range(c, 4, 2, C::AppendFlag | C::PrependFlag | C::DefaultFlag)) + << C::Default << 7 << C::Default << 1 << 10 + << (RemoveList() + << Remove(0, 0, 7, 2, 1, C::DefaultFlag, 0) + << Remove(0, 0, 7, 2, 6, C::DefaultFlag, 1) + << Remove(0, 0, 7, 2, 2, C::DefaultFlag | C::CacheFlag, 2) + << Remove(0, 0, 7, 2, 1, C::DefaultFlag, 3)) + << (InsertList() + << Insert(0, 0, 1, 0, 1, C::DefaultFlag, 0) + << Insert(0, 0, 2, 0, 6, C::DefaultFlag, 1) + << Insert(0, 0, 8, 0, 2, C::DefaultFlag | C::CacheFlag, 2) + << Insert(0, 0, 10, 2, 1, C::DefaultFlag, 3)) + << IndexArray(cacheIndexes) << ListArray(cacheLists) + << IndexArray(defaultIndexes) << ListArray(defaultLists) + << IndexArray() << ListArray() + << IndexArray() << ListArray(); + } { static const int cacheIndexes[] = {0,0,0,0,3,2}; + static const void *cacheLists[] = {0,0,0,0,c,c}; + static const int defaultIndexes[] = {0,5,0,1,2,3,4,5,0,1,0,1,2,2,3,3,4,3,4,5,1,2}; + static const void *defaultLists[] = {a,a,b,b,b,b,b,b,0,0,c,a,a,0,0,a,a,c,c,c,c,c}; + QTest::newRow("17, 20, 2") + << (RangeList() + << Range(a, 0, 1, C::DefaultFlag) + << Range(a, 5, 1, C::DefaultFlag) + << Range(b, 0, 6, C::DefaultFlag) + << Range(0, 0, 2, C::DefaultFlag | C::CacheFlag) + << Range(c, 0, 1, C::DefaultFlag) + << Range(a, 1, 2, C::DefaultFlag) + << Range(0, 0, 2, C::DefaultFlag | C::CacheFlag) + << Range(a, 3, 2, C::DefaultFlag) + << Range(b, 0, 6, C::AppendFlag | C::PrependFlag) + << Range(c, 0, 1, C::PrependFlag) + << Range(c, 1, 1, C::PrependFlag | C::DefaultFlag) + << Range(c, 2, 2, C::PrependFlag | C::DefaultFlag | C::CacheFlag) + << Range(c, 4, 2, C::AppendFlag | C::PrependFlag | C::DefaultFlag)) + << C::Default << 17 << C::Default << 20 << 2 + << (RemoveList() + << Remove(0, 0, 17, 4, 1, C::DefaultFlag, 0) + << Remove(0, 0, 17, 4, 1, C::DefaultFlag | C::CacheFlag, 1)) + << (InsertList() + << Insert(0, 0, 20, 5, 1, C::DefaultFlag, 0) + << Insert(0, 0, 21, 5, 1, C::DefaultFlag | C::CacheFlag, 1)) + << IndexArray(cacheIndexes) << ListArray(cacheLists) + << IndexArray(defaultIndexes) << ListArray(defaultLists) + << IndexArray() << ListArray() + << IndexArray() << ListArray(); + } { static const int cacheIndexes[] = {8,9,10,4,11,0,1,2,3,5,6,7}; + static const void *cacheLists[] = {a,a, a,a, a,a,a,a,a,a,a,a}; + static const int defaultIndexes[] = {8,9,10,4,11,0,1,2,3,5,6,7}; + static const void *defaultLists[] = {a,a, a,a, a,a,a,a,a,a,a,a}; + static const int visibleIndexes[] = {8,9,10,4,11,0,1,2,3,5,6,7}; + static const void *visibleLists[] = {a,a, a,a, a,a,a,a,a,a,a,a}; + QTest::newRow("3, 4, 5") + << (RangeList() + << Range(a, 8, 4, VisibleFlag | C::DefaultFlag | C::CacheFlag) + << Range(a, 0, 2, C::PrependFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag) + << Range(a, 2, 1, C::PrependFlag) + << Range(a, 2, 1, VisibleFlag | C::DefaultFlag | C::CacheFlag) + << Range(a, 3, 5, C::PrependFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag) + << Range(a, 8, 4, C::AppendFlag | C::PrependFlag)) + << C::Default << 3 << C::Default << 4 << 5 + << (RemoveList() + << Remove(0, 3, 3, 3, 1, VisibleFlag | C::DefaultFlag | C::CacheFlag, 0) + << Remove(0, 3, 3, 3, 2, VisibleFlag | C::DefaultFlag | C::CacheFlag, 1) + << Remove(0, 3, 3, 3, 1, VisibleFlag | C::DefaultFlag | C::CacheFlag, 2) + << Remove(0, 3, 3, 3, 1, VisibleFlag | C::DefaultFlag | C::CacheFlag, 3)) + << (InsertList() + << Insert(0, 4, 4, 4, 1, VisibleFlag | C::DefaultFlag | C::CacheFlag, 0) + << Insert(0, 5, 5, 5, 2, VisibleFlag | C::DefaultFlag | C::CacheFlag, 1) + << Insert(0, 7, 7, 7, 1, VisibleFlag | C::DefaultFlag | C::CacheFlag, 2) + << Insert(0, 8, 8, 8, 1, VisibleFlag | C::DefaultFlag | C::CacheFlag, 3)) + << IndexArray(cacheIndexes) << ListArray(cacheLists) + << IndexArray(defaultIndexes) << ListArray(defaultLists) + << IndexArray(visibleIndexes) << ListArray(visibleLists) + << IndexArray() << ListArray(); + } { static const int cacheIndexes[] = {0,1}; + static const void *cacheLists[] = {a,a}; + static const int defaultIndexes[] = {0,1}; + static const void *defaultLists[] = {a,a}; + QTest::newRow("0, 1, 1") + << (RangeList() + << Range(a, 0, 1, C::PrependFlag) + << Range(a, 1, 1, C::PrependFlag | C::DefaultFlag | C::CacheFlag) + << Range(a, 2, 0, C::AppendFlag | C::PrependFlag) + << Range(a, 0, 1, C::DefaultFlag | C::CacheFlag)) + << C::Default << 0 << C::Default << 1 << 1 + << (RemoveList() + << Remove(0, 0, 0, 0, 1, C::DefaultFlag | C::CacheFlag, 0)) + << (InsertList() + << Insert(0, 0, 1, 1, 1, C::DefaultFlag | C::CacheFlag, 0)) + << IndexArray(cacheIndexes) << ListArray(cacheLists) + << IndexArray(defaultIndexes) << ListArray(defaultLists) + << IndexArray() << ListArray() + << IndexArray() << ListArray(); + } { static const int cacheIndexes[] = {1,2,3,4,5,6,0,8,9}; + static const void *cacheLists[] = {a,a,a,a,a,a,a,a,a}; + static const int defaultIndexes[] = {1,2,3,4,5,6,0,8,9}; + static const void *defaultLists[] = {a,a,a,a,a,a,a,a,a}; + static const int visibleIndexes[] = {0,7,10}; + static const void *visibleLists[] = {a,a, a}; + QTest::newRow("0, 6, 3") + << (RangeList() + << Range(a, 0, 1, C::PrependFlag) + << Range(a, 1, 6, C::PrependFlag | C::DefaultFlag | C::CacheFlag) + << Range(a, 0, 1, VisibleFlag | C::DefaultFlag | C::CacheFlag) + << Range(a, 7, 1, VisibleFlag) + << Range(a,10, 1, VisibleFlag) + << Range(a, 7, 1, C::PrependFlag) + << Range(a, 8, 2, C::AppendFlag | C::PrependFlag | C::DefaultFlag | C::CacheFlag)) + << Visible << 0 << C::Default << 6 << 3 + << (RemoveList() + << Remove(0, 0, 6, 6, 1, VisibleFlag | C::DefaultFlag | C::CacheFlag, 0) + << Remove(0, 0, 6, 6, 1, VisibleFlag, 1) + << Remove(0, 0, 6, 6, 1, VisibleFlag, 2)) + << (InsertList() + << Insert(0, 0, 6, 6, 1, VisibleFlag | C::DefaultFlag | C::CacheFlag, 0) + << Insert(0, 1, 7, 7, 1, VisibleFlag, 1) + << Insert(0, 2, 7, 7, 1, VisibleFlag, 2)) + << IndexArray(cacheIndexes) << ListArray(cacheLists) + << IndexArray(defaultIndexes) << ListArray(defaultLists) + << IndexArray(visibleIndexes) << ListArray(visibleLists) + << IndexArray() << ListArray(); + } +} + +void tst_qqmllistcompositor::move() +{ + QFETCH(RangeList, ranges); + QFETCH(C::Group, fromGroup); + QFETCH(int, from); + QFETCH(C::Group, toGroup); + QFETCH(int, to); + QFETCH(int, count); + QFETCH(RemoveList, expectedRemoves); + QFETCH(InsertList, expectedInserts); + QFETCH(IndexArray, cacheIndexes); + QFETCH(ListArray, cacheLists); + QFETCH(IndexArray, defaultIndexes); + QFETCH(ListArray, defaultLists); + QFETCH(IndexArray, visibleIndexes); + QFETCH(ListArray, visibleLists); + QFETCH(IndexArray, selectionIndexes); + QFETCH(ListArray, selectionLists); + + QQmlListCompositor compositor; + compositor.setGroupCount(4); + compositor.setDefaultGroups(VisibleFlag | C::DefaultFlag); + + foreach (const Range &range, ranges) + compositor.append(range.list, range.index, range.count, range.flags); + + QVector removes; + QVector inserts; + compositor.move(fromGroup, from, toGroup, to, count, fromGroup, &removes, &inserts); + + QCOMPARE(removes, expectedRemoves); + QCOMPARE(inserts, expectedInserts); + + QCOMPARE(compositor.count(C::Cache), cacheIndexes.count); + for (int i = 0; i < cacheIndexes.count; ++i) { + C::iterator it = compositor.find(C::Cache, i); + QCOMPARE(it->list, cacheLists[i]); + if (cacheLists[i]) + QCOMPARE(it.modelIndex(), cacheIndexes[i]); + } + QCOMPARE(compositor.count(C::Default), defaultIndexes.count); + for (int i = 0; i < defaultIndexes.count; ++i) { + C::iterator it = compositor.find(C::Default, i); + QCOMPARE(it->list, defaultLists[i]); + if (defaultLists[i]) + QCOMPARE(it.modelIndex(), defaultIndexes[i]); + } + QCOMPARE(compositor.count(Visible), visibleIndexes.count); + for (int i = 0; i < visibleIndexes.count; ++i) { + C::iterator it = compositor.find(Visible, i); + QCOMPARE(it->list, visibleLists[i]); + if (visibleLists[i]) + QCOMPARE(it.modelIndex(), visibleIndexes[i]); + } + QCOMPARE(compositor.count(Selection), selectionIndexes.count); + for (int i = 0; i < selectionIndexes.count; ++i) { + C::iterator it = compositor.find(Selection, i); + QCOMPARE(it->list, selectionLists[i]); + if (selectionLists[i]) + QCOMPARE(it.modelIndex(), selectionIndexes[i]); + } +} + +void tst_qqmllistcompositor::moveFromEnd() +{ + int listA; void *a = &listA; + + QQmlListCompositor compositor; + compositor.append(a, 0, 1, C::AppendFlag | C::PrependFlag | C::DefaultFlag); + + // Moving an item anchors it to that position. + compositor.move(C::Default, 0, C::Default, 0, 1, C::Default); + + // The existing item is anchored at 0 so prepending an item to the source will append it here + QVector inserts; + compositor.listItemsInserted(a, 0, 1, &inserts); + + QCOMPARE(inserts.count(), 1); + QCOMPARE(inserts.at(0).index[1], 1); + QCOMPARE(inserts.at(0).count, 1); + + C::iterator it; + it = compositor.find(C::Default, 0); + QCOMPARE(it.modelIndex(), 1); + + it = compositor.find(C::Default, 1); + QCOMPARE(it.modelIndex(), 0); +} + +void tst_qqmllistcompositor::clear() +{ + QQmlListCompositor compositor; + compositor.setGroupCount(4); + compositor.setDefaultGroups(VisibleFlag | C::DefaultFlag); + + int listA; void *a = &listA; + int listB; void *b = &listB; + + compositor.append(a, 0, 8, C::AppendFlag | C::PrependFlag | VisibleFlag | C::DefaultFlag); + compositor.append(b, 4, 5, VisibleFlag | C::DefaultFlag); + compositor.append(0, 0, 3, VisibleFlag | C::DefaultFlag | C::CacheFlag); + + QCOMPARE(compositor.count(C::Default), 16); + QCOMPARE(compositor.count(Visible), 16); + QCOMPARE(compositor.count(C::Cache), 3); + + compositor.clear(); + QCOMPARE(compositor.count(C::Default), 0); + QCOMPARE(compositor.count(Visible), 0); + QCOMPARE(compositor.count(C::Cache), 0); +} + +void tst_qqmllistcompositor::listItemsInserted_data() +{ + QTest::addColumn("ranges"); + QTest::addColumn("list"); + QTest::addColumn("index"); + QTest::addColumn("count"); + QTest::addColumn("expectedInserts"); + QTest::addColumn("cacheIndexes"); + QTest::addColumn("defaultIndexes"); + QTest::addColumn("visibleIndexes"); + QTest::addColumn("selectionIndexes"); + + int listA; void *a = &listA; + int listB; void *b = &listB; + + { static const int defaultIndexes[] = {/*A*/0,1,5,6,/*B*/0,1,2,3,/*A*/2,3,4}; + QTest::newRow("A 10, 2") + << (RangeList() + << Range(a, 0, 2, C::PrependFlag | C::DefaultFlag) + << Range(a, 2, 3, C::PrependFlag) + << Range(a, 5, 2, C::AppendFlag | C::PrependFlag | C::DefaultFlag) + << Range(b, 0, 4, C::DefaultFlag) + << Range(a, 2, 3, C::DefaultFlag)) + << a << 10 << 2 + << InsertList() + << IndexArray() + << IndexArray(defaultIndexes) + << IndexArray() + << IndexArray(); + } { static const int defaultIndexes[] = {/*A*/0,1,5,6,/*B*/0,1,2,3,/*A*/2,3,4}; + QTest::newRow("B 10, 2") + << (RangeList() + << Range(a, 0, 2, C::PrependFlag | C::DefaultFlag) + << Range(a, 2, 3, C::PrependFlag) + << Range(a, 5, 2, C::AppendFlag | C::PrependFlag | C::DefaultFlag) + << Range(b, 0, 4, C::DefaultFlag) + << Range(a, 2, 3, C::DefaultFlag)) + << b << 10 << 2 + << InsertList() + << IndexArray() + << IndexArray(defaultIndexes) + << IndexArray() + << IndexArray(); + } { static const int defaultIndexes[] = {/*A*/0,1,2,3,7,8,/*B*/0,1,2,3,/*A*/4,5,6}; + static const int visibleIndexes[] = {/*A*/0,1}; + QTest::newRow("A 0, 2") + << (RangeList() + << Range(a, 0, 2, C::PrependFlag | C::DefaultFlag) + << Range(a, 2, 3, C::PrependFlag) + << Range(a, 5, 2, C::AppendFlag | C::PrependFlag | C::DefaultFlag) + << Range(b, 0, 4, C::DefaultFlag) + << Range(a, 2, 3, C::DefaultFlag)) + << a << 0 << 2 + << (InsertList() + << Insert(0, 0, 0, 0, 2, VisibleFlag | C::DefaultFlag)) + << IndexArray() + << IndexArray(defaultIndexes) + << IndexArray(visibleIndexes) + << IndexArray(); + } { static const int defaultIndexes[] = {/*A*/0,1,2,3,5,8,9,/*B*/0,1,2,3,/*A*/4,6,7}; + static const int visibleIndexes[] = {/*A*/0,1,5}; + QTest::newRow("A 5, 1") + << (RangeList() + << Range(a, 0, 2, C::PrependFlag | VisibleFlag | C::DefaultFlag) + << Range(a, 2, 2, C::PrependFlag | C::DefaultFlag) + << Range(a, 4, 3, C::PrependFlag) + << Range(a, 7, 2, C::AppendFlag | C::PrependFlag | C::DefaultFlag) + << Range(b, 0, 4, C::DefaultFlag) + << Range(a, 4, 3, C::DefaultFlag)) + << a << 5 << 1 + << (InsertList() + << Insert(0, 2, 4, 0, 1, VisibleFlag | C::DefaultFlag)) + << IndexArray() + << IndexArray(defaultIndexes) + << IndexArray(visibleIndexes) + << IndexArray(); + } { static const int defaultIndexes[] = {/*A*/0,1,2,3,5,8,9,10,11,/*B*/0,1,2,3,/*A*/4,6,7}; + static const int visibleIndexes[] = {/*A*/0,1,5,10,11}; + QTest::newRow("A 10, 2") + << (RangeList() + << Range(a, 0, 2, C::PrependFlag | VisibleFlag | C::DefaultFlag) + << Range(a, 2, 2, C::PrependFlag | C::DefaultFlag) + << Range(a, 4, 1, C::PrependFlag) + << Range(a, 5, 1, C::PrependFlag | VisibleFlag | C::DefaultFlag) + << Range(a, 6, 2, C::PrependFlag) + << Range(a, 8, 2, C::AppendFlag | C::PrependFlag | C::DefaultFlag) + << Range(b, 0, 4, C::DefaultFlag) + << Range(a, 4, 1, C::DefaultFlag) + << Range(a, 6, 2, C::DefaultFlag)) + << a << 10 << 2 + << (InsertList() + << Insert(0, 3, 7, 0, 2, VisibleFlag | C::DefaultFlag)) + << IndexArray() + << IndexArray(defaultIndexes) + << IndexArray(visibleIndexes) + << IndexArray(); + } { static const int cacheIndexes[] = {/*A*/0,1,-1,-1,-1,2,5,6,7,8,9}; + static const int defaultIndexes[] = {/*A*/0,1,2,3,4,5,6,7,8,9}; + static const int visibleIndexes[] = {/*A*/3,4}; + QTest::newRow("Insert after remove") + << (RangeList() + << Range(a, 0, 2, C::PrependFlag | C::DefaultFlag | C::CacheFlag) + << Range(a, 2, 3, C::CacheFlag) + << Range(a, 2, 6, C::AppendFlag | C::PrependFlag | C::DefaultFlag | C::CacheFlag)) + << a << 3 << 2 + << (InsertList() + << Insert(0, 0, 3, 6, 2, VisibleFlag | C::DefaultFlag)) + << IndexArray(cacheIndexes) + << IndexArray(defaultIndexes) + << IndexArray(visibleIndexes) + << IndexArray(); + } { static const int cacheIndexes[] = {/*A*/0,1,2,3,4}; + static const int defaultIndexes[] = {/*A*/0,1,2,3,4,5,6}; + static const int visibleIndexes[] = {/*A*/0,1,2,3,4,5,6}; + QTest::newRow("Consecutive appends") + << (RangeList() + << Range(a, 0, 5, C::PrependFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag) + << Range(a, 5, 1, C::PrependFlag | VisibleFlag | C::DefaultFlag) + << Range(a, 6, 0, C::AppendFlag | VisibleFlag | C::PrependFlag | C::DefaultFlag | C::CacheFlag)) + << a << 6 << 1 + << (InsertList() + << Insert(0, 6, 6, 5, 1, VisibleFlag | C::DefaultFlag)) + << IndexArray(cacheIndexes) + << IndexArray(defaultIndexes) + << IndexArray(visibleIndexes) + << IndexArray(); + } +} + +void tst_qqmllistcompositor::listItemsInserted() +{ + QFETCH(RangeList, ranges); + QFETCH(void *, list); + QFETCH(int, index); + QFETCH(int, count); + QFETCH(InsertList, expectedInserts); + QFETCH(IndexArray, cacheIndexes); + QFETCH(IndexArray, defaultIndexes); + QFETCH(IndexArray, visibleIndexes); + QFETCH(IndexArray, selectionIndexes); + + QQmlListCompositor compositor; + compositor.setGroupCount(4); + compositor.setDefaultGroups(VisibleFlag | C::DefaultFlag); + + foreach (const Range &range, ranges) + compositor.append(range.list, range.index, range.count, range.flags); + + QVector inserts; + compositor.listItemsInserted(list, index, count, &inserts); + + QCOMPARE(inserts, expectedInserts); + + QCOMPARE(compositor.count(C::Cache), cacheIndexes.count); + for (int i = 0; i < cacheIndexes.count; ++i) { + if (cacheIndexes[i] != -1) { + QCOMPARE(compositor.find(C::Cache, i).modelIndex(), cacheIndexes[i]); + } + } + QCOMPARE(compositor.count(C::Default), defaultIndexes.count); + for (int i = 0; i < defaultIndexes.count; ++i) { + if (defaultIndexes[i] != -1) { + QCOMPARE(compositor.find(C::Default, i).modelIndex(), defaultIndexes[i]); + } + } + QCOMPARE(compositor.count(Visible), visibleIndexes.count); + for (int i = 0; i < visibleIndexes.count; ++i) { + if (visibleIndexes[i] != -1) { + QCOMPARE(compositor.find(Visible, i).modelIndex(), visibleIndexes[i]); + } + } + QCOMPARE(compositor.count(Selection), selectionIndexes.count); + for (int i = 0; i < selectionIndexes.count; ++i) { + if (selectionIndexes[i] != -1) { + QCOMPARE(compositor.find(Selection, i).modelIndex(), selectionIndexes[i]); + } + } +} + +void tst_qqmllistcompositor::listItemsRemoved_data() +{ + QTest::addColumn("ranges"); + QTest::addColumn("list"); + QTest::addColumn("index"); + QTest::addColumn("count"); + QTest::addColumn("expectedRemoves"); + QTest::addColumn("cacheIndexes"); + QTest::addColumn("defaultIndexes"); + QTest::addColumn("visibleIndexes"); + QTest::addColumn("selectionIndexes"); + + int listA; void *a = &listA; + int listB; void *b = &listB; + + { static const int defaultIndexes[] = {/*A*/0,1,5,6,/*B*/0,1,2,3,/*A*/2,3,4}; + QTest::newRow("12, 2") + << (RangeList() + << Range(a, 0, 2, C::PrependFlag | C::DefaultFlag) + << Range(a, 2, 3, C::PrependFlag) + << Range(a, 5, 2, C::AppendFlag | C::PrependFlag | C::DefaultFlag) + << Range(b, 0, 4, C::DefaultFlag) + << Range(a, 2, 3, C::DefaultFlag)) + << a << 12 << 2 + << RemoveList() + << IndexArray() + << IndexArray(defaultIndexes) + << IndexArray() + << IndexArray(); + } { static const int defaultIndexes[] = {/*A*/0,1,/*B*/0,1,2,3,/*A*/2,3}; + QTest::newRow("4, 3") + << (RangeList() + << Range(a, 0, 2, C::PrependFlag | C::DefaultFlag) + << Range(a, 2, 3, C::PrependFlag) + << Range(a, 5, 2, C::AppendFlag | C::PrependFlag | C::DefaultFlag) + << Range(b, 0, 4, C::DefaultFlag) + << Range(a, 2, 3, C::DefaultFlag)) + << a << 4 << 3 + << (RemoveList() + << Remove(0, 0, 2, 0, 2, C::DefaultFlag) + << Remove(0, 0, 8, 0, 1, C::DefaultFlag)) + << IndexArray() + << IndexArray(defaultIndexes) + << IndexArray() + << IndexArray(); + } { static const int cacheIndexes[] = {/*A*/0,1,-1,-1,-1,2,-1,-1,3,4,5}; + static const int defaultIndexes[] = {/*A*/0,1,2,3,4,5}; + QTest::newRow("Remove after remove") + << (RangeList() + << Range(a, 0, 2, C::PrependFlag | C::DefaultFlag | C::CacheFlag) + << Range(a, 2, 3, C::CacheFlag) + << Range(a, 2, 6, C::AppendFlag | C::PrependFlag | C::DefaultFlag | C::CacheFlag)) + << a << 3 << 2 + << (RemoveList() + << Remove(0, 0, 3, 6, 2, C::DefaultFlag | C::CacheFlag)) + << IndexArray(cacheIndexes) + << IndexArray(defaultIndexes) + << IndexArray() + << IndexArray(); + } { static const int cacheIndexes[] = {/*A*/-1,-1,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,1}; + static const int defaultIndexes[] = {/*A*/0,1,2,3,4,5,6}; + QTest::newRow("Sparse remove") + << (RangeList() + << Range(a, 0, 2, C::CacheFlag) + << Range(a, 0, 1, C::DefaultFlag | C::CacheFlag) + << Range(a, 0, 1, C::CacheFlag) + << Range(a, 1, 5, C::DefaultFlag | C::CacheFlag) + << Range(a, 0, 1, C::CacheFlag) + << Range(a, 6, 2, C::DefaultFlag | C::CacheFlag) + << Range(a, 0, 1, C::CacheFlag) + << Range(a, 8, 3, C::DefaultFlag | C::CacheFlag) + << Range(a, 0, 1, C::CacheFlag) + << Range(a, 11, 1, C::DefaultFlag | C::CacheFlag) + << Range(a, 12, 5, C::DefaultFlag)) + << a << 1 << 10 + << (RemoveList() + << Remove(0, 0, 1, 4, 5, C::DefaultFlag | C::CacheFlag) + << Remove(0, 0, 1,10, 2, C::DefaultFlag | C::CacheFlag) + << Remove(0, 0, 1,13, 3, C::DefaultFlag | C::CacheFlag)) + << IndexArray(cacheIndexes) + << IndexArray(defaultIndexes) + << IndexArray() + << IndexArray(); + } +} + +void tst_qqmllistcompositor::listItemsRemoved() +{ + QFETCH(RangeList, ranges); + QFETCH(void *, list); + QFETCH(int, index); + QFETCH(int, count); + QFETCH(RemoveList, expectedRemoves); + QFETCH(IndexArray, cacheIndexes); + QFETCH(IndexArray, defaultIndexes); + QFETCH(IndexArray, visibleIndexes); + QFETCH(IndexArray, selectionIndexes); + + QQmlListCompositor compositor; + compositor.setGroupCount(4); + compositor.setDefaultGroups(VisibleFlag | C::DefaultFlag); + + foreach (const Range &range, ranges) + compositor.append(range.list, range.index, range.count, range.flags); + + QVector removes; + compositor.listItemsRemoved(list, index, count, &removes); + + QCOMPARE(removes, expectedRemoves); + + QCOMPARE(compositor.count(C::Cache), cacheIndexes.count); + for (int i = 0; i < cacheIndexes.count; ++i) { + if (cacheIndexes[i] != -1) { + QCOMPARE(compositor.find(C::Cache, i).modelIndex(), cacheIndexes[i]); + } + } + QCOMPARE(compositor.count(C::Default), defaultIndexes.count); + for (int i = 0; i < defaultIndexes.count; ++i) { + if (defaultIndexes[i] != -1) { + QCOMPARE(compositor.find(C::Default, i).modelIndex(), defaultIndexes[i]); + } + } + QCOMPARE(compositor.count(Visible), visibleIndexes.count); + for (int i = 0; i < visibleIndexes.count; ++i) { + if (visibleIndexes[i] != -1) { + QCOMPARE(compositor.find(Visible, i).modelIndex(), visibleIndexes[i]); + } + } + QCOMPARE(compositor.count(Selection), selectionIndexes.count); + for (int i = 0; i < selectionIndexes.count; ++i) { + if (selectionIndexes[i] != -1) { + QCOMPARE(compositor.find(Selection, i).modelIndex(), selectionIndexes[i]); + } + } +} + +void tst_qqmllistcompositor::listItemsMoved_data() +{ + QTest::addColumn("ranges"); + QTest::addColumn("list"); + QTest::addColumn("from"); + QTest::addColumn("to"); + QTest::addColumn("count"); + QTest::addColumn("expectedRemoves"); + QTest::addColumn("expectedInserts"); + QTest::addColumn("cacheIndexes"); + QTest::addColumn("defaultIndexes"); + QTest::addColumn("visibleIndexes"); + QTest::addColumn("selectionIndexes"); + + int listA; void *a = &listA; + int listB; void *b = &listB; + + { static const int defaultIndexes[] = {/*A*/0,2,3,4,/*B*/0,1,2,3,/*A*/5,6,1}; + QTest::newRow("4, 1, 3") + << (RangeList() + << Range(a, 0, 2, C::PrependFlag | C::DefaultFlag) + << Range(a, 2, 3, C::PrependFlag) + << Range(a, 5, 2, C::AppendFlag | C::PrependFlag | C::DefaultFlag) + << Range(b, 0, 4, C::DefaultFlag) + << Range(a, 2, 3, C::DefaultFlag)) + << a << 4 << 1 << 3 + << (RemoveList() + << Remove(0, 0, 2, 0, 2, C::DefaultFlag, 0)) + << (InsertList() + << Insert(0, 0, 1, 0, 2, C::DefaultFlag, 0)) + << IndexArray() + << IndexArray(defaultIndexes) + << IndexArray() + << IndexArray(); + } { static const int defaultIndexes[] = {/*A*/1,2,3,6,/*B*/0,1,2,3,/*A*/4,5,0}; + QTest::newRow("0, 6, 1") + << (RangeList() + << Range(a, 0, 1, C::PrependFlag | C::DefaultFlag) + << Range(a, 1, 1, C::PrependFlag) + << Range(a, 2, 3, C::PrependFlag | C::DefaultFlag) + << Range(a, 5, 2, C::PrependFlag) + << Range(a, 7, 0, C::AppendFlag | C::PrependFlag | C::DefaultFlag) + << Range(b, 0, 4, C::DefaultFlag) + << Range(a, 5, 2, C::DefaultFlag) + << Range(a, 1, 1, C::DefaultFlag)) + << a << 0 << 6 << 1 + << (RemoveList() + << Remove(0, 0, 0, 0, 1, C::DefaultFlag, 0)) + << (InsertList() + << Insert(0, 0, 3, 0, 1, C::DefaultFlag, 0)) + << IndexArray() + << IndexArray(defaultIndexes) + << IndexArray() + << IndexArray(); + } { static const int cacheIndexes[] = {/*A*/0,1,3,4}; + static const int defaultIndexes[] = {/*A*/0,1,2,3,4,5,6,7}; + QTest::newRow("6, 2, 1") + << (RangeList() + << Range(a, 0, 4, C::PrependFlag | C::DefaultFlag | C::CacheFlag) + << Range(a, 4, 4, C::AppendFlag | C::PrependFlag | C::DefaultFlag)) + << a << 6 << 2 << 1 + << (RemoveList() + << Remove(0, 0, 6, 4, 1, C::DefaultFlag, 0)) + << (InsertList() + << Insert(0, 0, 2, 2, 1, C::DefaultFlag, 0)) + << IndexArray(cacheIndexes) + << IndexArray(defaultIndexes) + << IndexArray() + << IndexArray(); + } { static const int cacheIndexes[] = {/*A*/0,1,-1,-1,-1,2,3,4,5,6,7}; + static const int defaultIndexes[] = {/*A*/0,1,2,3,4,5,6,7}; + QTest::newRow("Move after remove") + << (RangeList() + << Range(a, 0, 2, C::PrependFlag | C::DefaultFlag | C::CacheFlag) + << Range(a, 2, 3, C::CacheFlag) + << Range(a, 2, 6, C::AppendFlag | C::PrependFlag | C::DefaultFlag | C::CacheFlag)) + << a << 4 << 2 << 2 + << (RemoveList() + << Remove(0, 0, 4, 7, 2, C::DefaultFlag | C::CacheFlag, 0)) + << (InsertList() + << Insert(0, 0, 2, 5, 2, C::DefaultFlag | C::CacheFlag, 0)) + << IndexArray(cacheIndexes) + << IndexArray(defaultIndexes) + << IndexArray() + << IndexArray(); + } { static const int cacheIndexes[] = {/*A*/0,1,5,6,7,8,9,10,11,12}; + static const int defaultIndexes[] = {/*A*/0,1,2,3,4,5,6,7,8,9,10,11,12}; + QTest::newRow("Move merge tail") + << (RangeList() + << Range(a, 0, 10, C::PrependFlag | C::DefaultFlag | C::CacheFlag) + << Range(a, 10, 3, C::PrependFlag | C::DefaultFlag) + << Range(a, 13, 0, C::AppendFlag | C::PrependFlag | C::DefaultFlag | C::CacheFlag)) + << a << 8 << 0 << 5 + << (RemoveList() + << Remove(0, 0, 8, 8, 2, C::DefaultFlag | C::CacheFlag, 0) + << Remove(0, 0, 8, 8, 3, C::DefaultFlag, 1)) + << (InsertList() + << Insert(0, 0, 0, 0, 2, C::DefaultFlag | C::CacheFlag, 0) + << Insert(0, 0, 2, 2, 3, C::DefaultFlag, 1)) + << IndexArray(cacheIndexes) + << IndexArray(defaultIndexes) + << IndexArray() + << IndexArray(); + } { static const int cacheIndexes[] = {/*A*/0,1,2,3}; + static const int defaultIndexes[] = {/*A*/0,1,2,3}; + static const int selectionIndexes[] = {/*A*/3}; + QTest::newRow("Move selection") + << (RangeList() + << Range(a, 0, 2, C::PrependFlag | C::DefaultFlag | C::CacheFlag) + << Range(a, 2, 1, C::PrependFlag | SelectionFlag | C::DefaultFlag | C::CacheFlag) + << Range(a, 3, 1, C::AppendFlag | C::PrependFlag | C::DefaultFlag | C::CacheFlag)) + << a << 2 << 3 << 1 + << (RemoveList() + << Remove(0, 0, 2, 2, 1, C::PrependFlag | SelectionFlag | C::DefaultFlag | C::CacheFlag, 0)) + << (InsertList() + << Insert(0, 0, 3, 3, 1, C::PrependFlag | SelectionFlag | C::DefaultFlag | C::CacheFlag, 0)) + << IndexArray(cacheIndexes) + << IndexArray(defaultIndexes) + << IndexArray() + << IndexArray(selectionIndexes); + } { static const int cacheIndexes[] = {/*A*/0,1,2,3,4,5,8,9}; + static const int defaultIndexes[] = {/*A*/0,1,2,3,4,5,6,7,8,9,10,11}; + QTest::newRow("move mixed cached items") + << (RangeList() + << Range(a, 0, 1, C::PrependFlag | C::DefaultFlag | C::CacheFlag) + << Range(a, 1, 2, C::PrependFlag | C::DefaultFlag) + << Range(a, 3, 7, C::PrependFlag | C::DefaultFlag | C::CacheFlag) + << Range(a, 10, 2, C::PrependFlag | C::DefaultFlag)) + << a << 1 << 6 << 3 + << (RemoveList() + << Remove(0, 0, 1, 1, 2, C::PrependFlag | C::DefaultFlag, 0) + << Remove(0, 0, 1, 1, 1, C::PrependFlag | C::DefaultFlag | C::CacheFlag, 1)) + << (InsertList() + << Insert(0, 0, 6, 6, 2, C::PrependFlag | C::DefaultFlag, 0) + << Insert(0, 0, 8, 6, 1, C::PrependFlag | C::DefaultFlag | C::CacheFlag, 1)) + << IndexArray(cacheIndexes) + << IndexArray(defaultIndexes) + << IndexArray() + << IndexArray(); + } +} + +void tst_qqmllistcompositor::listItemsMoved() +{ + QFETCH(RangeList, ranges); + QFETCH(void *, list); + QFETCH(int, from); + QFETCH(int, to); + QFETCH(int, count); + QFETCH(RemoveList, expectedRemoves); + QFETCH(InsertList, expectedInserts); + QFETCH(IndexArray, cacheIndexes); + QFETCH(IndexArray, defaultIndexes); + QFETCH(IndexArray, visibleIndexes); + QFETCH(IndexArray, selectionIndexes); + + QQmlListCompositor compositor; + compositor.setGroupCount(4); + compositor.setDefaultGroups(VisibleFlag | C::DefaultFlag); + + foreach (const Range &range, ranges) + compositor.append(range.list, range.index, range.count, range.flags); + + QVector removes; + QVector inserts; + compositor.listItemsMoved(list, from, to, count, &removes, &inserts); + + QCOMPARE(removes, expectedRemoves); + QCOMPARE(inserts, expectedInserts); + + QCOMPARE(compositor.count(C::Cache), cacheIndexes.count); + for (int i = 0; i < cacheIndexes.count; ++i) { + if (cacheIndexes[i] != -1) { + QCOMPARE(compositor.find(C::Cache, i).modelIndex(), cacheIndexes[i]); + } + } + QCOMPARE(compositor.count(C::Default), defaultIndexes.count); + for (int i = 0; i < defaultIndexes.count; ++i) { + if (defaultIndexes[i] != -1) { + QCOMPARE(compositor.find(C::Default, i).modelIndex(), defaultIndexes[i]); + } + } + QCOMPARE(compositor.count(Visible), visibleIndexes.count); + for (int i = 0; i < visibleIndexes.count; ++i) { + if (visibleIndexes[i] != -1) { + QCOMPARE(compositor.find(Visible, i).modelIndex(), visibleIndexes[i]); + } + } + QCOMPARE(compositor.count(Selection), selectionIndexes.count); + for (int i = 0; i < selectionIndexes.count; ++i) { + if (selectionIndexes[i] != -1) { + QCOMPARE(compositor.find(Selection, i).modelIndex(), selectionIndexes[i]); + } + } +} + +void tst_qqmllistcompositor::listItemsChanged_data() +{ + QTest::addColumn("ranges"); + QTest::addColumn("list"); + QTest::addColumn("index"); + QTest::addColumn("count"); + QTest::addColumn("expectedChanges"); + + int listA; void *a = &listA; + int listB; void *b = &listB; + + QTest::newRow("overlapping") + << (RangeList() + << Range(a, 0, 2, C::PrependFlag | C::DefaultFlag) + << Range(a, 2, 3, C::PrependFlag) + << Range(a, 5, 2, C::AppendFlag | C::PrependFlag | C::DefaultFlag) + << Range(b, 0, 4, C::DefaultFlag) + << Range(a, 2, 3, C::DefaultFlag)) + << a << 3 << 4 + << (ChangeList() + << Change(0, 0, 2, 0, 2, C::DefaultFlag) + << Change(0, 0, 9, 0, 2, C::DefaultFlag)); + QTest::newRow("Change after remove") + << (RangeList() + << Range(a, 0, 2, C::PrependFlag | C::DefaultFlag | C::CacheFlag) + << Range(a, 2, 3, C::CacheFlag) + << Range(a, 2, 6, C::AppendFlag | C::PrependFlag | C::DefaultFlag | C::CacheFlag)) + << a << 3 << 2 + << (ChangeList() + << Change(0, 0, 3, 6, 2, C::DefaultFlag | C::CacheFlag)); +} + +void tst_qqmllistcompositor::listItemsChanged() +{ + QFETCH(RangeList, ranges); + QFETCH(void *, list); + QFETCH(int, index); + QFETCH(int, count); + QFETCH(ChangeList, expectedChanges); + + QQmlListCompositor compositor; + compositor.setGroupCount(4); + compositor.setDefaultGroups(VisibleFlag | C::DefaultFlag); + + foreach (const Range &range, ranges) + compositor.append(range.list, range.index, range.count, range.flags); + + QVector changes; + compositor.listItemsChanged(list, index, count, &changes); + + QCOMPARE(changes, expectedChanges); +} + +void tst_qqmllistcompositor::compositorDebug() +{ + void *a = (void *)0xa0; + void *b = (void *)0xb0; + + QQmlListCompositor compositor; + compositor.setGroupCount(4); + compositor.setDefaultGroups(VisibleFlag | C::DefaultFlag); + compositor.append(a, 0, 2, C::PrependFlag | C::DefaultFlag); + compositor.append(a, 2, 3, C::PrependFlag); + compositor.append(a, 5, 2, C::AppendFlag | C::PrependFlag | C::DefaultFlag); + compositor.append(b, 0, 4, C::DefaultFlag); + compositor.append(a, 2, 3, C::DefaultFlag); + + QTest::ignoreMessage(QtDebugMsg, + "QQmlListCompositor(00110\n" + " 0 0 0 0 Range(0xa0 0 2 00P000000000D0\n" + " 0 0 2 0 Range(0xa0 2 3 00P00000000000\n" + " 0 0 2 0 Range(0xa0 5 2 0AP000000000D0\n" + " 0 0 4 0 Range(0xb0 0 4 000000000000D0\n" + " 0 0 8 0 Range(0xa0 2 3 000000000000D0)"); + qDebug() << compositor; + + compositor.setFlags(C::Default, 5, 2, SelectionFlag); + + QTest::ignoreMessage(QtDebugMsg, + "QQmlListCompositor(20110\n" + " 0 0 0 0 Range(0xa0 0 2 00P000000000D0\n" + " 0 0 2 0 Range(0xa0 2 3 00P00000000000\n" + " 0 0 2 0 Range(0xa0 5 2 0AP000000000D0\n" + " 0 0 4 0 Range(0xb0 0 1 000000000000D0\n" + " 0 0 5 0 Range(0xb0 1 2 000000000010D0\n" + " 2 0 7 0 Range(0xb0 3 1 000000000000D0\n" + " 2 0 8 0 Range(0xa0 2 3 000000000000D0)"); + qDebug() << compositor; +} + +void tst_qqmllistcompositor::changeDebug() +{ + void *a = (void *)0xa0; + void *b = (void *)0xb0; + + QQmlListCompositor compositor; + compositor.setGroupCount(4); + compositor.setDefaultGroups(VisibleFlag | C::DefaultFlag); + compositor.append(a, 0, 2, C::PrependFlag | C::DefaultFlag); + compositor.append(a, 2, 3, C::PrependFlag); + compositor.append(a, 5, 2, C::AppendFlag | C::PrependFlag | C::DefaultFlag); + compositor.append(b, 0, 1, C::DefaultFlag); + compositor.append(b, 1, 2, SelectionFlag | C::DefaultFlag); + compositor.append(b, 3, 1, C::DefaultFlag); + compositor.append(a, 2, 3, C::DefaultFlag); + + + QTest::ignoreMessage(QtDebugMsg, "Change(-1 2 000000010D0 2 0 7 0)"); + qDebug() << QQmlListCompositor::Change(compositor.find(C::Default, 7), 2, SelectionFlag | C::DefaultFlag); + QTest::ignoreMessage(QtDebugMsg, "Remove(9 4 000000000D0 10 0)"); + qDebug() << QQmlListCompositor::Remove(compositor.find(C::Default, 10), 4, C::DefaultFlag, 9); + QTest::ignoreMessage(QtDebugMsg, "Insert(9 4 000000000D0 3 0)"); + qDebug() << QQmlListCompositor::Insert(compositor.find(C::Default, 3), 4, C::DefaultFlag, 9); +} + +void tst_qqmllistcompositor::groupDebug() +{ + QTest::ignoreMessage(QtDebugMsg, "Default "); + qDebug() << C::Default; + QTest::ignoreMessage(QtDebugMsg, "Cache "); + qDebug() << C::Cache; + QTest::ignoreMessage(QtDebugMsg, "Group3 "); + qDebug() << Selection; +} + +QTEST_MAIN(tst_qqmllistcompositor) + +#include "tst_qqmllistcompositor.moc" + + diff --git a/tests/auto/qml/qquickchangeset/qquickchangeset.pro b/tests/auto/qml/qquickchangeset/qquickchangeset.pro deleted file mode 100644 index fd8c87baa4..0000000000 --- a/tests/auto/qml/qquickchangeset/qquickchangeset.pro +++ /dev/null @@ -1,10 +0,0 @@ -CONFIG += testcase -TARGET = tst_qquickchangeset -macx:CONFIG -= app_bundle - -SOURCES += tst_qquickchangeset.cpp - -CONFIG += parallel_test - -QT += core-private gui-private qml-private quick-private testlib -DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 diff --git a/tests/auto/qml/qquickchangeset/tst_qquickchangeset.cpp b/tests/auto/qml/qquickchangeset/tst_qquickchangeset.cpp deleted file mode 100644 index 77286b04b4..0000000000 --- a/tests/auto/qml/qquickchangeset/tst_qquickchangeset.cpp +++ /dev/null @@ -1,1574 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** 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, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#include -#include - -class tst_qquickchangeset : public QObject -{ - Q_OBJECT -private slots: - void sequence_data(); - void sequence(); - - void apply_data(); - void apply(); - - void removeConsecutive_data(); - void removeConsecutive(); - void insertConsecutive_data(); - void insertConsecutive(); - - void copy(); - void debug(); - - // These create random sequences and verify a list with the reordered changes applied is the - // same as one with the unordered changes applied. -private: - void random_data(); - void random(); - -public: - enum { - MoveOp = 0, - InsertOp = -1, - RemoveOp = -2 - }; - - struct Signal - { - int index; - int count; - int to; - int moveId; - int offset; - - bool isInsert() const { return to == -1; } - bool isRemove() const { return to == -2; } - bool isChange() const { return to == -3; } - bool isMove() const { return to >= 0; } - }; - - static Signal Insert(int index, int count) { - Signal signal = { index, count, -1, -1, 0 }; return signal; } - static Signal Insert(int index, int count, int moveId, int moveOffset) { - Signal signal = { index, count, -1, moveId, moveOffset }; return signal; } - static Signal Remove(int index, int count) { - Signal signal = { index, count, -2, -1, 0 }; return signal; } - static Signal Remove(int index, int count, int moveId, int moveOffset) { - Signal signal = { index, count, -2, moveId, moveOffset }; return signal; } - static Signal Move(int from, int to, int count, int moveId) { - Signal signal = { from, count, to, moveId, 0 }; return signal; } - static Signal Change(int index, int count) { - Signal signal = { index, count, -3, -1, 0 }; return signal; } - - typedef QVector SignalList; - typedef QVector SignalListList; - - template - void move(int from, int to, int n, T *items) - { - if (from > to) { - // Only move forwards - flip if backwards moving - int tfrom = from; - int tto = to; - from = tto; - to = tto+n; - n = tfrom-tto; - } - - T replaced; - int i=0; - typename T::ConstIterator it=items->begin(); it += from+n; - for (; ibegin(); it += from; - for (; ibegin(); t += from; - for (; f != replaced.end(); ++f, ++t) - *t = *f; - } - - bool applyChanges(QVector &list, const QVector > &changes) - { - foreach (const SignalList &sl, changes) { - if (!applyChanges(list, sl)) - return false; - } - return true; - } - - bool applyChanges(QVector &list, const QVector &changes) - { - QHash removedValues; - foreach (const Signal &signal, changes) { - if (signal.isInsert()) { - if (signal.index < 0 || signal.index > list.count()) { - qDebug() << "insert out of range" << signal.index << list.count(); - return false; - } - if (signal.moveId != -1) { - QQuickChangeSet::Insert insert(signal.index, signal.count, signal.moveId, signal.offset); - for (int i = insert.start(); i < insert.end(); ++i) - list.insert(i, removedValues.take(insert.moveKey(i))); - } else { - list.insert(signal.index, signal.count, 100); - } - } else if (signal.isRemove()) { - if (signal.index < 0 || signal.index + signal.count > list.count()) { - qDebug() << "remove out of range" << signal.index << signal.count << list.count(); - return false; - } - if (signal.moveId != -1) { - QQuickChangeSet::Remove remove(signal.index, signal.count, signal.moveId, signal.offset); - for (int i = remove.start(); i < remove.end(); ++i) - removedValues.insert(remove.moveKey(i), list.at(i)); - } - list.erase(list.begin() + signal.index, list.begin() + signal.index + signal.count); - } else if (signal.isMove()) { - if (signal.index < 0 - || signal.to < 0 - || signal.index + signal.count > list.count() - || signal.to + signal.count > list.count()) { - qDebug() << "move out of range" << signal.index << signal.to << signal.count << list.count(); - return false; - } - move(signal.index, signal.to, signal.count, &list); - } else if (signal.isChange()) { - for (int i = signal.index; i < signal.index + signal.count; ++i) - list[i] = 100; - } - } - return true; - } - -}; - -bool operator ==(const tst_qquickchangeset::Signal &left, const tst_qquickchangeset::Signal &right) -{ - return left.index == right.index - && left.count == right.count - && left.to == right.to - && left.moveId == right.moveId - && (left.moveId == -1 || left.offset == right.offset); -} - -QT_BEGIN_NAMESPACE -bool operator ==(const QQuickChangeSet::Change &left, const QQuickChangeSet::Change &right) -{ - return left.index == right.index && left.count == right.count && left.moveId == right.moveId; -} -QT_END_NAMESPACE - -QDebug operator <<(QDebug debug, const tst_qquickchangeset::Signal &signal) -{ - if (signal.isInsert() && signal.moveId == -1) - debug.nospace() << "Insert(" << signal.index << "," << signal.count << ")"; - else if (signal.isInsert()) - debug.nospace() << "Insert(" << signal.index << "," << signal.count << "," << signal.moveId << "," << signal.offset << ")"; - else if (signal.isRemove() && signal.moveId == -1) - debug.nospace() << "Remove(" << signal.index << "," << signal.count << ")"; - else if (signal.isRemove()) - debug.nospace() << "Remove(" << signal.index << "," << signal.count << "," << signal.moveId << "," << signal.offset << ")"; - else if (signal.isMove()) - debug.nospace() << "Move(" << signal.index << "," << signal.to << "," << signal.count << "," << signal.moveId << ")"; - else if (signal.isChange()) - debug.nospace() << "Change(" << signal.index << "," << signal.count << ")"; - return debug; -} - -Q_DECLARE_METATYPE(tst_qquickchangeset::SignalList) -Q_DECLARE_METATYPE(tst_qquickchangeset::SignalListList) - -#if 0 -# define VERIFY_EXPECTED_OUTPUT \ - QVector inputList; \ - for (int i = 0; i < 40; ++i) \ - inputList.append(i); \ - QVector outputList = inputList; \ - if (!applyChanges(inputList, input)) { \ - qDebug() << input; \ - qDebug() << output; \ - qDebug() << changes; \ - QVERIFY(false); \ - } else if (!applyChanges(outputList, output)) { \ - qDebug() << input; \ - qDebug() << output; \ - qDebug() << changes; \ - QVERIFY(false); \ - } else if (outputList != inputList /* || changes != output*/) { \ - qDebug() << input; \ - qDebug() << output; \ - qDebug() << changes; \ - qDebug() << inputList; \ - qDebug() << outputList; \ - QVERIFY(false); \ - } else if (changes != output) { \ - qDebug() << output; \ - qDebug() << changes; \ - QCOMPARE(outputList, inputList); \ - } -#else -# define VERIFY_EXPECTED_OUTPUT \ - if (changes != output) { \ - qDebug() << output; \ - qDebug() << changes; \ - } -#endif - -void tst_qquickchangeset::sequence_data() -{ - QTest::addColumn("input"); - QTest::addColumn("output"); - - // Insert - QTest::newRow("i(12,5)") - << (SignalList() << Insert(12,5)) - << (SignalList() << Insert(12,5)); - QTest::newRow("i(2,3),i(12,5)") - << (SignalList() << Insert(2,3) << Insert(12,5)) - << (SignalList() << Insert(2,3) << Insert(12,5)); - QTest::newRow("i(12,5),i(2,3)") - << (SignalList() << Insert(12,5) << Insert(2,3)) - << (SignalList() << Insert(2,3) << Insert(15,5)); - QTest::newRow("i(12,5),i(12,3)") - << (SignalList() << Insert(12,5) << Insert(12,3)) - << (SignalList() << Insert(12,8)); - QTest::newRow("i(12,5),i(17,3)") - << (SignalList() << Insert(12,5) << Insert(17,3)) - << (SignalList() << Insert(12,8)); - QTest::newRow("i(12,5),i(15,3)") - << (SignalList() << Insert(12,5) << Insert(15,3)) - << (SignalList() << Insert(12,8)); - - // Remove - QTest::newRow("r(3,9)") - << (SignalList() << Remove(3,9)) - << (SignalList() << Remove(3,9)); - QTest::newRow("r(3,4),r(3,2)") - << (SignalList() << Remove(3,4) << Remove(3,2)) - << (SignalList() << Remove(3,6)); - QTest::newRow("r(4,3),r(14,5)") - << (SignalList() << Remove(4,3) << Remove(14,5)) - << (SignalList() << Remove(4,3) << Remove(14,5)); - QTest::newRow("r(14,5),r(4,3)") - << (SignalList() << Remove(14,5) << Remove(4,3)) - << (SignalList() << Remove(4,3) << Remove(11,5)); - QTest::newRow("r(4,3),r(2,9)") - << (SignalList() << Remove(4,3) << Remove(2,9)) - << (SignalList() << Remove(2,12)); - - // Move - QTest::newRow("m(8-10,2)") - << (SignalList() << Move(8,10,2,0)) - << (SignalList() << Remove(8,2,0,0) << Insert(10,2,0,0)); - - QTest::newRow("m(23-12,6),m(13-15,5)") - << (SignalList() << Move(23,12,6,0) << Move(13,15,5,1)) - << (SignalList() << Remove(23,6,0,0) << Insert(12,1,0,0) << Insert(15,5,0,1)); - QTest::newRow("m(23-12,6),m(13-15,2)") - << (SignalList() << Move(23,12,6,0) << Move(13,20,2,1)) - << (SignalList() << Remove(23,6,0,0) << Insert(12,1,0,0) << Insert(13,3,0,3) << Insert(20,2,0,1)); - QTest::newRow("m(23-12,6),m(13-2,2)") - << (SignalList() << Move(23,12,6,0) << Move(13,2,2,1)) - << (SignalList() << Remove(23,6,0,0) << Insert(2,2,0,1) << Insert(14,1,0,0) << Insert(15,3,0,3)); - QTest::newRow("m(23-12,6),m(12-6,5)") - << (SignalList() << Move(23,12,6,0) << Move(12,6,5,1)) - << (SignalList() << Remove(23,6,0,0) << Insert(6,5,0,0) << Insert(17,1,0,5)); - QTest::newRow("m(23-12,6),m(10-5,4)") - << (SignalList() << Move(23,12,6,0) << Move(10,5,4,1)) - << (SignalList() << Remove(10,2,1,0) << Remove(21,6,0,0) << Insert(5,2,1,0) << Insert(7,2,0,0) << Insert(14,4,0,2)); - QTest::newRow("m(23-12,6),m(16-5,4)") - << (SignalList() << Move(23,12,6,0) << Move(16,5,4,1)) - << (SignalList() << Remove(12,2,1,2) << Remove(21,6,0,0) << Insert(5,2,0,4) << Insert(7,2,1,2) << Insert(16,4,0,0)); - QTest::newRow("m(23-12,6),m(13-5,4)") - << (SignalList() << Move(23,12,6,0) << Move(13,5,4,1)) - << (SignalList() << Remove(23,6,0,0) << Insert(5,4,0,1) << Insert(16,1,0,0) << Insert(17,1,0,5)); - QTest::newRow("m(23-12,6),m(14-5,4)") - << (SignalList() << Move(23,12,6,0) << Move(14,5,4,1)) - << (SignalList() << Remove(23,6,0,0) << Insert(5,4,0,2) << Insert(16,2,0,0)); - QTest::newRow("m(23-12,6),m(12-5,4)") - << (SignalList() << Move(23,12,6,0) << Move(12,5,4,1)) - << (SignalList() << Remove(23,6,0,0) << Insert(5,4,0,0) << Insert(16,2,0,4)); - QTest::newRow("m(23-12,6),m(11-5,8)") - << (SignalList() << Move(23,12,6,0) << Move(11,5,8,1)) - << (SignalList() << Remove(11,1,1,0) << Remove(11,1,1,7) << Remove(21,6,0,0) << Insert(5,1,1,0) << Insert(6,6,0,0) << Insert(12,1,1,7)); - QTest::newRow("m(23-12,6),m(8-5,4)") - << (SignalList() << Move(23,12,6,0) << Move(8,5,4,1)) - << (SignalList() << Remove(8,4,1,0) << Remove(19,6,0,0) << Insert(5,4,1,0) << Insert(12,6,0,0)); - QTest::newRow("m(23-12,6),m(2-5,4)") - << (SignalList() << Move(23,12,6,0) << Move(2,5,4,1)) - << (SignalList() << Remove(2,4,1,0) << Remove(19,6,0,0) << Insert(5,4,1,0) << Insert(12,6,0,0)); - QTest::newRow("m(23-12,6),m(18-5,4)") - << (SignalList() << Move(23,12,6,0) << Move(18,5,4,1)) - << (SignalList() << Remove(12,4,1,0) << Remove(19,6,0,0) << Insert(5,4,1,0) << Insert(16,6,0,0)); - QTest::newRow("m(23-12,6),m(20-5,4)") - << (SignalList() << Move(23,12,6,0) << Move(20,5,4,1)) - << (SignalList() << Remove(14,4,1,0) << Remove(19,6,0,0) << Insert(5,4,1,0) << Insert(16,6,0,0)); - - QTest::newRow("m(23-12,6),m(5-13,11)") - << (SignalList() << Move(23,12,6,0) << Move(5,13,11,1)) - << (SignalList() << Remove(5,7,1,0) << Remove(16,6,0,0) << Insert(5,2,0,4) << Insert(13,7,1,0) << Insert(20,4,0,0)); - - QTest::newRow("m(23-12,6),m(12-23,6)") - << (SignalList() << Move(23,12,6,0) << Move(12,23,6,1)) - << (SignalList() << Remove(23,6,0,0) << Insert(23,6,0,0)); // ### These cancel out. - QTest::newRow("m(23-12,6),m(10-23,4)") - << (SignalList() << Move(23,12,6,0) << Move(10,23,4,1)) - << (SignalList() << Remove(10,2,1,0) << Remove(21,6,0,0) << Insert(10,4,0,2) << Insert(23,2,1,0) << Insert(25,2,0,0)); - QTest::newRow("m(23-12,6),m(16-23.4)") - << (SignalList() << Move(23,12,6,0) << Move(16,23,4,1)) - << (SignalList() << Remove(12,2,1,2) << Remove(21,6,0,0) << Insert(12,4,0,0) << Insert(23,2,0,4) << Insert(25,2,1,2)); - QTest::newRow("m(23-12,6),m(13-23,4)") - << (SignalList() << Move(23,12,6,0) << Move(13,23,4,1)) - << (SignalList() << Remove(23,6,0,0) << Insert(12,1,0,0) << Insert(13,1,0,5) << Insert(23,4,0,1)); - QTest::newRow("m(23-12,6),m(14-23,)") - << (SignalList() << Move(23,12,6,0) << Move(14,23,4,1)) - << (SignalList() << Remove(23,6,0,0) << Insert(12,2,0,0) << Insert(23,4,0,2)); - QTest::newRow("m(23-12,6),m(12-23,4)") - << (SignalList() << Move(23,12,6,0) << Move(12,23,4,1)) - << (SignalList() << Remove(23,6,0,0) << Insert(12,2,0,4) << Insert(23,4,0,0)); - QTest::newRow("m(23-12,6),m(11-23,8)") - << (SignalList() << Move(23,12,6,0) << Move(11,23,8,1)) - << (SignalList() << Remove(11,1,1,0) << Remove(11,1,1,7) << Remove(21,6,0,0) << Insert(23,1,1,0) << Insert(24,6,0,0) << Insert(30,1,1,7)); - QTest::newRow("m(23-12,6),m(8-23,4)") - << (SignalList() << Move(23,12,6,0) << Move(8,23,4,1)) - << (SignalList() << Remove(8,4,1,0) << Remove(19,6,0,0) << Insert(8,6,0,0) << Insert(23,4,1,0)); - QTest::newRow("m(23-12,6),m(2-23,4)") - << (SignalList() << Move(23,12,6,0) << Move(2,23,4,1)) - << (SignalList() << Remove(2,4,1,0) << Remove(19,6,0,0) << Insert(8,6,0,0) << Insert(23,4,1,0)); - QTest::newRow("m(23-12,6),m(18-23,4)") - << (SignalList() << Move(23,12,6,0) << Move(18,23,4,1)) - << (SignalList() << Remove(12,4,1,0) << Remove(19,6,0,0) << Insert(12,6,0,0) << Insert(23,4,1,0)); - QTest::newRow("m(23-12,6),m(20-23,4)") - << (SignalList() << Move(23,12,6,0) << Move(20,23,4,1)) - << (SignalList() << Remove(14,4,1,0) << Remove(19,6,0,0) << Insert(12,6,0,0) << Insert(23,4,1,0)); - - QTest::newRow("m(23-12,6),m(11-23,10)") - << (SignalList() << Move(23,12,6,0) << Move(11,23,10,1)) - << (SignalList() << Remove(11,1,1,0) << Remove(11,3,1,7) << Remove(19,6,0,0) << Insert(23,1,1,0) << Insert(24,6,0,0) << Insert(30,3,1,7)); - - QTest::newRow("m(3-9,12),m(13-5,12)") - << (SignalList() << Move(3,9,12,0) << Move(13,15,5,1)) - << (SignalList() << Remove(3,12,0,0) << Insert(9,4,0,0) << Insert(13,2,0,9) << Insert(15,5,0,4) << Insert(20,1,0,11)); - QTest::newRow("m(3-9,12),m(13-15,20)") - << (SignalList() << Move(3,9,12,0) << Move(13,15,20,1)) - << (SignalList() << Remove(3,12,0,0) << Remove(9,12,1,8) << Insert(9,4,0,0) << Insert(15,8,0,4) << Insert(23,12,1,8)); - QTest::newRow("m(3-9,12),m(13-15,2)") - << (SignalList() << Move(3,9,12,0) << Move(13,15,2,1)) - << (SignalList() << Remove(3,12,0,0) << Insert(9,4,0,0) << Insert(13,2,0,6) << Insert(15,2,0,4) << Insert(17,4,0,8)); - QTest::newRow("m(3-9,12),m(12-5,6)") - << (SignalList() << Move(3,9,12,0) << Move(12,5,6,1)) - << (SignalList() << Remove(3,12,0,0) << Insert(5,6,0,3) << Insert(15,3,0,0) << Insert(18,3,0,9)); - QTest::newRow("m(3-9,12),m(10-14,5)") - << (SignalList() << Move(3,9,12,0) << Move(10,14,5,1)) - << (SignalList() << Remove(3,12,0,0) << Insert(9,1,0,0) << Insert(10,4,0,6) << Insert(14,5,0,1) << Insert(19,2,0,10)); - QTest::newRow("m(3-9,12),m(16-20,5)") - << (SignalList() << Move(3,9,12,0) << Move(16,20,5,1)) - << (SignalList() << Remove(3,12,0,0) << Insert(9,7,0,0) << Insert(20,5,0,7)); - QTest::newRow("m(3-9,12),m(13-17,5)") - << (SignalList() << Move(3,9,12,0) << Move(13,17,5,1)) - << (SignalList() << Remove(3,12,0,0) << Insert(9,4,0,0) << Insert(13,3,0,9) << Insert(17,5,0,4)); - QTest::newRow("m(3-9,12),m(14-18,5)") - << (SignalList() << Move(3,9,12,0) << Move(14,18,5,1)) - << (SignalList() << Remove(3,12,0,0) << Insert(9,5,0,0) << Insert(14,2,0,10) << Insert(18,5,0,5)); - QTest::newRow("m(3-9,12),m(12-16,5)") - << (SignalList() << Move(3,9,12,0) << Move(12,16,5,1)) - << (SignalList() << Remove(3,12,0,0) << Insert(9,3,0,0) << Insert(12,4,0,8) << Insert(16,5,0,3)); - QTest::newRow("m(3-9,12),m(11-19,5)") - << (SignalList() << Move(3,9,12,0) << Move(11,19,5,1)) - << (SignalList() << Remove(3,12,0,0) << Insert(9,2,0,0) << Insert(11,5,0,7) << Insert(19,5,0,2)); - QTest::newRow("m(3-9,12),m(8-12,5)") - << (SignalList() << Move(3,9,12,0) << Move(8,12,5,1)) - << (SignalList() << Remove(3,12,0,0) << Remove(8,1,1,0) << Insert(8,4,0,4) << Insert(12,1,1,0) << Insert(13,4,0,0) << Insert(17,4,0,8)); - QTest::newRow("m(3-9,12),m(2-6,5)") - << (SignalList() << Move(3,9,12,0) << Move(2,6,5,1)) - << (SignalList() << Remove(2,1,1,0) << Remove(2,12,0,0) << Remove(2,4,1,1) << Insert(4,2,0,0) << Insert(6,5,1,0) << Insert(11,10,0,2)); - QTest::newRow("m(3-9,12),m(18-22,5)") - << (SignalList() << Move(3,9,12,0) << Move(18,22,5,1)) - << (SignalList() << Remove(3,12,0,0) << Remove(9,2,1,3) << Insert(9,9,0,0) << Insert(22,3,0,9) << Insert(25,2,1,3)); - QTest::newRow("m(3-9,12),m(20-24,5)") - << (SignalList() << Move(3,9,12,0) << Move(20,24,5,1)) - << (SignalList() << Remove(3,12,0,0) << Remove(9,4,1,1) << Insert(9,11,0,0) << Insert(24,1,0,11) << Insert(25,4,1,1)); - - QTest::newRow("m(3-9,12),m(5-11,8)") - << (SignalList() << Move(3,9,12,0) << Move(5,11,8,1)) - << (SignalList() << Remove(3,12,0,0) << Remove(5,4,1,0) << Insert(5,6,0,4) << Insert(11,4,1,0) << Insert(15,4,0,0) << Insert(19,2,0,10)); - - QTest::newRow("m(3-9,12),m(12-23,6)") - << (SignalList() << Move(3,9,12,0) << Move(12,23,6,1)) - << (SignalList() << Remove(3,12,0,0) << Insert(9,3,0,0) << Insert(12,3,0,9) << Insert(23,6,0,3)); - QTest::newRow("m(3-9,12),m(10-23,4)") - << (SignalList() << Move(3,9,12,0) << Move(10,23,4,1)) - << (SignalList() << Remove(3,12,0,0) << Insert(9,1,0,0) << Insert(10,7,0,5) << Insert(23,4,0,1)); - QTest::newRow("m(3-9,12),m(16-23,4)") - << (SignalList() << Move(3,9,12,0) << Move(16,23,4,1)) - << (SignalList() << Remove(3,12,0,0) << Insert(9,7,0,0) << Insert(16,1,0,11) << Insert(23,4,0,7)); - QTest::newRow("m(3-9,12),m(13-23,4)") - << (SignalList() << Move(3,9,12,0) << Move(13,23,4,1)) - << (SignalList() << Remove(3,12,0,0) << Insert(9,4,0,0) << Insert(13,4,0,8) << Insert(23,4,0,4)); - QTest::newRow("m(3-9,12),m(14-23,4)") - << (SignalList() << Move(3,9,12,0) << Move(14,23,4,1)) - << (SignalList() << Remove(3,12,0,0) << Insert(9,5,0,0) << Insert(14,3,0,9) << Insert(23,4,0,5)); - QTest::newRow("m(3-9,12),m(12-23,4)") - << (SignalList() << Move(3,9,12,0) << Move(12,23,4,1)) - << (SignalList() << Remove(3,12,0,0) << Insert(9,3,0,0) << Insert(12,5,0,7) << Insert(23,4,0,3)); - QTest::newRow("m(3-9,12),m(11-23,8)") - << (SignalList() << Move(3,9,12,0) << Move(11,23,8,1)) - << (SignalList() << Remove(3,12,0,0) << Insert(9,2,0,0) << Insert(11,2,0,10) << Insert(23,8,0,2)); - QTest::newRow("m(3-9,12),m(8-23,4)") - << (SignalList() << Move(3,9,12,0) << Move(8,23,4,1)) - << (SignalList() << Remove(3,12,0,0) << Remove(8,1,1,0) << Insert(8,9,0,3) << Insert(23,1,1,0) << Insert(24,3,0,0)); - QTest::newRow("m(3-9,12),m(2-23,4)") - << (SignalList() << Move(3,9,12,0) << Move(2,23,4,1)) - << (SignalList() << Remove(2,1,1,0) << Remove(2,12,0,0) << Remove(2,3,1,1) << Insert(5,12,0,0) << Insert(23,4,1,0)); - QTest::newRow("m(3-9,12),m(18-23,4)") - << (SignalList() << Move(3,9,12,0) << Move(18,23,4,1)) - << (SignalList() << Remove(3,12,0,0) << Remove(9,1,1,3) << Insert(9,9,0,0) << Insert(23,3,0,9) << Insert(26,1,1,3)); - QTest::newRow("m(3-9,12),m(20-23,4)") - << (SignalList() << Move(3,9,12,0) << Move(20,23,4,1)) - << (SignalList() << Remove(3,12,0,0) << Remove(9,3,1,1) << Insert(9,11,0,0) << Insert(23,1,0,11) << Insert(24,3,1,1)); - - QTest::newRow("m(3-9,12),m(11-23,10)") - << (SignalList() << Move(3,9,12,0) << Move(11,23,10,1)) - << (SignalList() << Remove(3,12,0,0) << Insert(9,2,0,0) << Insert(23,10,0,2)); - - // Change - QTest::newRow("c(4,5)") - << (SignalList() << Change(4,5)) - << (SignalList() << Change(4,5)); - QTest::newRow("c(4,5),c(12,2)") - << (SignalList() << Change(4,5) << Change(12,2)) - << (SignalList() << Change(4,5) << Change(12,2)); - QTest::newRow("c(12,2),c(4,5)") - << (SignalList() << Change(12,2) << Change(4,5)) - << (SignalList() << Change(4,5) << Change(12,2)); - QTest::newRow("c(4,5),c(2,2)") - << (SignalList() << Change(4,5) << Change(2,2)) - << (SignalList() << Change(2,7)); - QTest::newRow("c(4,5),c(9,2)") - << (SignalList() << Change(4,5) << Change(9,2)) - << (SignalList() << Change(4,7)); - QTest::newRow("c(4,5),c(3,2)") - << (SignalList() << Change(4,5) << Change(3,2)) - << (SignalList() << Change(3,6)); - QTest::newRow("c(4,5),c(8,2)") - << (SignalList() << Change(4,5) << Change(8,2)) - << (SignalList() << Change(4,6)); - QTest::newRow("c(4,5),c(3,2)") - << (SignalList() << Change(4,5) << Change(3,2)) - << (SignalList() << Change(3,6)); - QTest::newRow("c(4,5),c(2,9)") - << (SignalList() << Change(4,5) << Change(2,9)) - << (SignalList() << Change(2,9)); - QTest::newRow("c(4,5),c(12,3),c(8,6)") - << (SignalList() << Change(4,5) << Change(12,3) << Change(8,6)) - << (SignalList() << Change(4,11)); - - // Insert,then remove. - QTest::newRow("i(12,6),r(12,6)") - << (SignalList() << Insert(12,6) << Remove(12,6)) - << (SignalList()); - QTest::newRow("i(12,6),r(10,4)") - << (SignalList() << Insert(12,6) << Remove(10,4)) - << (SignalList() << Remove(10,2) << Insert(10,4)); - QTest::newRow("i(12,6),r(16,4)") - << (SignalList() << Insert(12,6) << Remove(16,4)) - << (SignalList() << Remove(12,2) << Insert(12,4)); - QTest::newRow("i(12,6),r(13,4)") - << (SignalList() << Insert(12,6) << Remove(13,4)) - << (SignalList() << Insert(12,2)); - QTest::newRow("i(12,6),r(14,4)") - << (SignalList() << Insert(12,6) << Remove(14,4)) - << (SignalList() << Insert(12,2)); - QTest::newRow("i(12,6),r(12,4)") - << (SignalList() << Insert(12,6) << Remove(12,4)) - << (SignalList() << Insert(12,2)); - QTest::newRow("i(12,6),r(11,8)") - << (SignalList() << Insert(12,6) << Remove(11,8)) - << (SignalList() << Remove(11,2)); - QTest::newRow("i(12,6),r(8,4)") - << (SignalList() << Insert(12,6) << Remove(8,4)) - << (SignalList() << Remove(8,4) << Insert(8,6)); - QTest::newRow("i(12,6),r(2,4)") - << (SignalList() << Insert(12,6) << Remove(2,4)) - << (SignalList() << Remove(2,4) << Insert(8,6)); - QTest::newRow("i(12,6),r(18,4)") - << (SignalList() << Insert(12,6) << Remove(18,4)) - << (SignalList() << Remove(12,4) << Insert(12,6)); - QTest::newRow("i(12,6),r(20,4)") - << (SignalList() << Insert(12,6) << Remove(20,4)) - << (SignalList() << Remove(14,4) << Insert(12,6)); - - // Insert,then change - QTest::newRow("i(12,6),c(12,6)") - << (SignalList() << Insert(12,6) << Change(12,6)) - << (SignalList() << Insert(12,6)); - QTest::newRow("i(12,6),c(10,6)") - << (SignalList() << Insert(12,6) << Change(10,6)) - << (SignalList() << Insert(12,6) << Change(10,2)); - QTest::newRow("i(12,6),c(16,4)") - << (SignalList() << Insert(12,6) << Change(16,4)) - << (SignalList() << Insert(12,6) << Change(18,2)); - QTest::newRow("i(12,6),c(13,4)") - << (SignalList() << Insert(12,6) << Change(13,4)) - << (SignalList() << Insert(12,6)); - QTest::newRow("i(12,6),c(14,4)") - << (SignalList() << Insert(12,6) << Change(14,4)) - << (SignalList() << Insert(12,6)); - QTest::newRow("i(12,6),c(12,4)") - << (SignalList() << Insert(12,6) << Change(12,4)) - << (SignalList() << Insert(12,6)); - QTest::newRow("i(12,6),c(11,8)") - << (SignalList() << Insert(12,6) << Change(11,8)) - << (SignalList() << Insert(12,6) << Change(11,1) << Change(18,1)); - QTest::newRow("i(12,6),c(8,4)") - << (SignalList() << Insert(12,6) << Change(8,4)) - << (SignalList() << Insert(12,6) << Change(8,4)); - QTest::newRow("i(12,6),c(2,4)") - << (SignalList() << Insert(12,6) << Change(2,4)) - << (SignalList() << Insert(12,6) << Change(2,4)); - QTest::newRow("i(12,6),c(18,4)") - << (SignalList() << Insert(12,6) << Change(18,4)) - << (SignalList() << Insert(12,6) << Change(18,4)); - QTest::newRow("i(12,6),c(20,4)") - << (SignalList() << Insert(12,6) << Change(20,4)) - << (SignalList() << Insert(12,6) << Change(20,4)); - - // Insert,then move - QTest::newRow("i(12,6),m(12-5,6)") - << (SignalList() << Insert(12,6) << Move(12,5,6,0)) - << (SignalList() << Insert(5,6)); - QTest::newRow("i(12,6),m(10-5,4)") - << (SignalList() << Insert(12,6) << Move(10,5,4,0)) - << (SignalList() << Remove(10,2,0,0) << Insert(5,2,0,0) << Insert(7,2) << Insert(14,4)); - QTest::newRow("i(12,6),m(16-5,4)") - << (SignalList() << Insert(12,6) << Move(16,5,4,0)) - << (SignalList() << Remove(12,2,0,2) << Insert(5,2) << Insert(7,2,0,2) << Insert(16,4)); - QTest::newRow("i(12,6),m(13-5,4)") - << (SignalList() << Insert(12,6) << Move(13,5,4,0)) - << (SignalList() << Insert(5,4) << Insert(16,2)); - QTest::newRow("i(12,6),m(14-5,4)") - << (SignalList() << Insert(12,6) << Move(14,5,4,0)) - << (SignalList() << Insert(5,4) << Insert(16,2)); - QTest::newRow("i(12,6),m(12-5,4)") - << (SignalList() << Insert(12,6) << Move(12,5,4,0)) - << (SignalList() << Insert(5,4) << Insert(16,2)); - QTest::newRow("i(12,6),m(11-5,8)") - << (SignalList() << Insert(12,6) << Move(11,5,8,0)) - << (SignalList() << Remove(11,1,0,0) << Remove(11,1,0,7) << Insert(5,1,0,0) << Insert(6,6) << Insert(12,1,0,7)); - QTest::newRow("i(12,6),m(8-5,4)") - << (SignalList() << Insert(12,6) << Move(8,5,4,0)) - << (SignalList() << Remove(8,4,0,0) << Insert(5,4,0,0) << Insert(12,6)); - QTest::newRow("i(12,6),m(2-5,4)") - << (SignalList() << Insert(12,6) << Move(2,5,4,0)) - << (SignalList() << Remove(2,4,0,0) << Insert(5,4,0,0) << Insert(12,6)); - QTest::newRow("i(12,6),m(18-5,4)") - << (SignalList() << Insert(12,6) << Move(18,5,4,0)) - << (SignalList() << Remove(12,4,0,0) << Insert(5,4,0,0) << Insert(16,6)); - QTest::newRow("i(12,6),m(20-5,4)") - << (SignalList() << Insert(12,6) << Move(20,5,4,0)) - << (SignalList() << Remove(14,4,0,0) << Insert(5,4,0,0) << Insert(16,6)); - - QTest::newRow("i(12,6),m(5-13,11)") - << (SignalList() << Insert(12,6) << Move(5,11,8,0)) - << (SignalList() << Remove(5,7,0,0) << Insert(5,5) << Insert(11,7,0,0) << Insert(18,1)); - - QTest::newRow("i(12,6),m(12-23,6)") - << (SignalList() << Insert(12,6) << Move(12,23,6,0)) - << (SignalList() << Insert(23,6)); - QTest::newRow("i(12,6),m(10-23,4)") - << (SignalList() << Insert(12,6) << Move(10,23,4,0)) - << (SignalList() << Remove(10,2,0,0) << Insert(10,4) << Insert(23,2,0,0) << Insert(25,2)); - QTest::newRow("i(12,6),m(16-23,4)") - << (SignalList() << Insert(12,6) << Move(16,23,4,0)) - << (SignalList() << Remove(12,2,0,2) << Insert(12,4) << Insert(23,2) << Insert(25,2,0,2)); - QTest::newRow("i(12,6),m(13-23,4)") - << (SignalList() << Insert(12,6) << Move(13,23,4,0)) - << (SignalList() << Insert(12,2) << Insert(23,4)); - QTest::newRow("i(12,6),m(14-23,4)") - << (SignalList() << Insert(12,6) << Move(14,23,4,0)) - << (SignalList() << Insert(12,2) << Insert(23,4)); - QTest::newRow("i(12,6),m(12-23,4)") - << (SignalList() << Insert(12,6) << Move(12,23,4,0)) - << (SignalList() << Insert(12,2) << Insert(23,4)); - QTest::newRow("i(12,6),m(11-23,8)") - << (SignalList() << Insert(12,6) << Move(11,23,8,0)) - << (SignalList() << Remove(11,1,0,0) << Remove(11,1,0,7) << Insert(23,1,0,0)<< Insert(24,6) << Insert(30,1,0,7)); - QTest::newRow("i(12,6),m(8-23,4)") - << (SignalList() << Insert(12,6) << Move(8,23,4,0)) - << (SignalList() << Remove(8,4,0,0) << Insert(8,6) << Insert(23,4,0,0)); - QTest::newRow("i(12,6),m(2-23,4)") - << (SignalList() << Insert(12,6) << Move(2,23,4,0)) - << (SignalList() << Remove(2,4,0,0) << Insert(8,6) << Insert(23,4,0,0)); - QTest::newRow("i(12,6),m(18-23,4)") - << (SignalList() << Insert(12,6) << Move(18,23,4,0)) - << (SignalList() << Remove(12,4,0,0) << Insert(12,6) << Insert(23,4,0,0)); - QTest::newRow("i(12,6),m(20-23,4)") - << (SignalList() << Insert(12,6) << Move(20,23,4,0)) - << (SignalList() << Remove(14,4,0,0) << Insert(12,6) << Insert(23,4,0,0)); - - QTest::newRow("i(12,6),m(11-23,10)") - << (SignalList() << Insert(12,6) << Move(11,23,10,0)) - << (SignalList() << Remove(11,1,0,0) << Remove(11,3,0,7) << Insert(23,1,0,0) << Insert(24,6) << Insert(30,3,0,7)); - - // Remove,then insert - QTest::newRow("r(12,6),i(12,6)") - << (SignalList() << Remove(12,6) << Insert(12,6)) - << (SignalList() << Remove(12,6) << Insert(12,6)); - QTest::newRow("r(12,6),i(10,4)") - << (SignalList() << Remove(12,6) << Insert(10,14)) - << (SignalList() << Remove(12,6) << Insert(10,14)); - QTest::newRow("r(12,6),i(16,4)") - << (SignalList() << Remove(12,6) << Insert(16,4)) - << (SignalList() << Remove(12,6) << Insert(16,4)); - QTest::newRow("r(12,6),i(13,4)") - << (SignalList() << Remove(12,6) << Insert(13,4)) - << (SignalList() << Remove(12,6) << Insert(13,4)); - QTest::newRow("r(12,6),i(14,4)") - << (SignalList() << Remove(12,6) << Insert(14,4)) - << (SignalList() << Remove(12,6) << Insert(14,4)); - QTest::newRow("r(12,6),i(12,4)") - << (SignalList() << Remove(12,6) << Insert(12,4)) - << (SignalList() << Remove(12,6) << Insert(12,4)); - QTest::newRow("r(12,6),i(11,8)") - << (SignalList() << Remove(12,6) << Insert(11,8)) - << (SignalList() << Remove(12,6) << Insert(11,8)); - QTest::newRow("r(12,6),i(8,4)") - << (SignalList() << Remove(12,6) << Insert(8,4)) - << (SignalList() << Remove(12,6) << Insert(8,4)); - QTest::newRow("r(12,6),i(2,4)") - << (SignalList() << Remove(12,6) << Insert(2,4)) - << (SignalList() << Remove(12,6) << Insert(2,4)); - QTest::newRow("r(12,6),i(18,4)") - << (SignalList() << Remove(12,6) << Insert(18,4)) - << (SignalList() << Remove(12,6) << Insert(18,4)); - QTest::newRow("r(12,6),i(20,4)") - << (SignalList() << Remove(12,6) << Insert(20,4)) - << (SignalList() << Remove(12,6) << Insert(20,4)); - - // Move,then insert - QTest::newRow("m(12-5,6),i(12,6)") - << (SignalList() << Move(12,5,6,0) << Insert(12,6)) - << (SignalList() << Remove(12,6,0,0) << Insert(5,6,0,0) << Insert(12,6)); - QTest::newRow("m(12-5,6),i(10,4)") - << (SignalList() << Move(12,5,6,0) << Insert(10,4)) - << (SignalList() << Remove(12,6,0,0) << Insert(5,5,0,0) << Insert(10,4) << Insert(14,1,0,5)); - QTest::newRow("m(12-5,6),i(16,4)") - << (SignalList() << Move(12,5,6,0) << Insert(16,4)) - << (SignalList() << Remove(12,6,0,0) << Insert(5,6,0,0) << Insert(16,4)); - QTest::newRow("m(12-5,6),i(13,4)") - << (SignalList() << Move(12,5,6,0) << Insert(13,4)) - << (SignalList() << Remove(12,6,0,0) << Insert(5,6,0,0) << Insert(13,4)); - QTest::newRow("m(12-5,6),i(14,4)") - << (SignalList() << Move(12,5,6,0) << Insert(14,4)) - << (SignalList() << Remove(12,6,0,0) << Insert(5,6,0,0) << Insert(14,4)); - QTest::newRow("m(12-5,6),i(12,4)") - << (SignalList() << Move(12,5,6,0) << Insert(12,4)) - << (SignalList() << Remove(12,6,0,0) << Insert(5,6,0,0) << Insert(12,4)); - QTest::newRow("m(12-5,6),i(11,8)") - << (SignalList() << Move(12,5,6,0) << Insert(11,8)) - << (SignalList() << Remove(12,6,0,0) << Insert(5,6,0,0) << Insert(11,8)); - QTest::newRow("m(12-5,6),i(8,4)") - << (SignalList() << Move(12,5,6,0) << Insert(8,4)) - << (SignalList() << Remove(12,6,0,0) << Insert(5,3,0,0) << Insert(8,4) << Insert(12,3,0,3)); - QTest::newRow("m(12-5,6),i(2,4)") - << (SignalList() << Move(12,5,6,0) << Insert(2,4)) - << (SignalList() << Remove(12,6,0,0) << Insert(2,4) << Insert(9,6,0,0)); - QTest::newRow("m(12-5,6),i(18,4)") - << (SignalList() << Move(12,5,6,0) << Insert(18,4)) - << (SignalList() << Remove(12,6,0,0) << Insert(5,6,0,0) << Insert(18,4)); - QTest::newRow("m(12-5,6),i(20,4)") - << (SignalList() << Move(12,5,6,0) << Insert(20,4)) - << (SignalList() << Remove(12,6,0,0) << Insert(5,6,0,0) << Insert(20,4)); - - QTest::newRow("m(12-23,6),i(12,6)") - << (SignalList() << Move(12,23,6,0) << Insert(12,6)) - << (SignalList() << Remove(12,6,0,0) << Insert(12,6) << Insert(29,6,0,0)); - QTest::newRow("m(12-23,6),i(10,4)") - << (SignalList() << Move(12,23,6,0) << Insert(10,4)) - << (SignalList() << Remove(12,6,0,0) << Insert(10,4) << Insert(27,6,0,0)); - QTest::newRow("m(12-23,6),i(16,4)") - << (SignalList() << Move(12,23,6,0) << Insert(16,4)) - << (SignalList() << Remove(12,6,0,0) << Insert(16,4) << Insert(27,6,0,0)); - QTest::newRow("m(12-23,6),i(13,4)") - << (SignalList() << Move(12,23,6,0) << Insert(13,4)) - << (SignalList() << Remove(12,6,0,0) << Insert(13,4) << Insert(27,6,0,0)); - QTest::newRow("m(12-23,6),i(14,4)") - << (SignalList() << Move(12,23,6,0) << Insert(14,4)) - << (SignalList() << Remove(12,6,0,0) << Insert(14,4) << Insert(27,6,0,0)); - QTest::newRow("m(12-23,6),i(12,4)") - << (SignalList() << Move(12,23,6,0) << Insert(12,4)) - << (SignalList() << Remove(12,6,0,0) << Insert(12,4) << Insert(27,6,0,0)); - QTest::newRow("m(12-23,6),i(11,8)") - << (SignalList() << Move(12,23,6,0) << Insert(11,8)) - << (SignalList() << Remove(12,6,0,0) << Insert(11,8) << Insert(31,6,0,0)); - QTest::newRow("m(12-23,6),i(8,4)") - << (SignalList() << Move(12,23,6,0) << Insert(8,4)) - << (SignalList() << Remove(12,6,0,0) << Insert(8,4) << Insert(27,6,0,0)); - QTest::newRow("m(12-23,6),i(2,4)") - << (SignalList() << Move(12,23,6,0) << Insert(2,4)) - << (SignalList() << Remove(12,6,0,0) << Insert(2,4) << Insert(27,6,0,0)); - QTest::newRow("m(12-23,6),i(18,4)") - << (SignalList() << Move(12,23,6,0) << Insert(18,4)) - << (SignalList() << Remove(12,6,0,0) << Insert(18,4) << Insert(27,6,0,0)); - QTest::newRow("m(12-23,6),i(20,4)") - << (SignalList() << Move(12,23,6,0) << Insert(20,4)) - << (SignalList() << Remove(12,6,0,0) << Insert(20,4) << Insert(27,6,0,0)); - - // Move,then remove - QTest::newRow("m(12-5,6),r(12,6)") - << (SignalList() << Move(12,5,6,0) << Remove(12,6)) - << (SignalList() << Remove(6,6) << Remove(6,6,0,0) << Insert(5,6,0,0)); - QTest::newRow("m(12-5,6),r(10,4)") - << (SignalList() << Move(12,5,6,0) << Remove(10,4)) // ### - << (SignalList() << Remove(5,3) << Remove(9,6,0,0) << Insert(5,5,0,0)); - QTest::newRow("m(12-5,6),r(16,4)") - << (SignalList() << Move(12,5,6,0) << Remove(16,4)) - << (SignalList() << Remove(10,2) << Remove(10,6,0,0) << Remove(10,2) << Insert(5,6,0,0)); - QTest::newRow("m(12-5,6),r(13,4)") - << (SignalList() << Move(12,5,6,0) << Remove(13,4)) - << (SignalList() << Remove(7,4) << Remove(8,6,0,0) << Insert(5,6,0,0)); - QTest::newRow("m(12-5,6),r(14,4)") - << (SignalList() << Move(12,5,6,0) << Remove(14,4)) - << (SignalList() << Remove(8,4) << Remove(8,6,0,0) << Insert(5,6,0,0)); - QTest::newRow("m(12-5,6),r(12,4)") - << (SignalList() << Move(12,5,6,0) << Remove(12,4)) - << (SignalList() << Remove(6,4) << Remove(8,6,0,0) << Insert(5,6,0,0)); - QTest::newRow("m(12-5,6),r(11,8)") - << (SignalList() << Move(12,5,6,0) << Remove(11,8)) - << (SignalList() << Remove(5,7) << Remove(5,6,0,0) << Remove(5,1) << Insert(5,6,0,0)); - QTest::newRow("m(12-5,6),r(8,4)") - << (SignalList() << Move(12,5,6,0) << Remove(8,4)) // ### - << (SignalList() << Remove(5,1) << Remove(11,6,0,0) << Insert(5,3,0,0)); - QTest::newRow("m(12-5,6),r(2,4)") - << (SignalList() << Move(12,5,6,0) << Remove(2,4)) - << (SignalList() << Remove(2,3) << Remove(9,6,0,0) << Insert(2,5,0,1)); - QTest::newRow("m(12-5,6),r(6,4)") - << (SignalList() << Move(12,5,6,0) << Remove(6,4)) - << (SignalList() << Remove(12,6,0,0) << Insert(5,1,0,0) << Insert(6,1,0,5)); - QTest::newRow("m(12-5,6),r(18,4)") - << (SignalList() << Move(12,5,6,0) << Remove(18,4)) - << (SignalList() << Remove(12,6,0,0) << Remove(12,4) << Insert(5,6,0,0)); - QTest::newRow("m(12-5,6),r(20,4)") - << (SignalList() << Move(12,5,6,0) << Remove(20,4)) - << (SignalList() << Remove(12,6,0,0) << Remove(14,4) << Insert(5,6,0,0)); - - QTest::newRow("m(12-23,6),r(12,6)") - << (SignalList() << Move(12,23,6,0) << Remove(12,6)) - << (SignalList() << Remove(12,6,0,0) << Remove(12,6) << Insert(17,6,0,0)); - QTest::newRow("m(12-23,6),r(10,4)") - << (SignalList() << Move(12,23,6,0) << Remove(10,4)) - << (SignalList() << Remove(10,2) << Remove(10,6,0,0) << Remove(10,2) << Insert(19,6,0,0)); - QTest::newRow("m(12-23,6),r(16,4)") - << (SignalList() << Move(12,23,6,0) << Remove(16,4)) - << (SignalList() << Remove(12,6,0,0) << Remove(16,4) << Insert(19,6,0,0)); - QTest::newRow("m(12-23,6),r(13,4)") - << (SignalList() << Move(12,23,6,0) << Remove(13,4)) - << (SignalList() << Remove(12,6,0,0) << Remove(13,4) << Insert(19,6,0,0)); - QTest::newRow("m(12-23,6),r(14,4)") - << (SignalList() << Move(12,23,6,0) << Remove(14,4)) - << (SignalList() << Remove(12,6,0,0) << Remove(14,4) << Insert(19,6,0,0)); - QTest::newRow("m(12-23,6),r(12,4)") - << (SignalList() << Move(12,23,6,0) << Remove(12,4)) - << (SignalList() << Remove(12,6,0,0) << Remove(12,4) << Insert(19,6,0,0)); - QTest::newRow("m(12-23,6),r(11,8)") - << (SignalList() << Move(12,23,6,0) << Remove(11,8)) - << (SignalList() << Remove(11,1) << Remove(11,6,0,0) << Remove(11,7) << Insert(15,6,0,0)); - QTest::newRow("m(12-23,6),r(8,4)") - << (SignalList() << Move(12,23,6,0) << Remove(8,4)) - << (SignalList() << Remove(8,4) << Remove(8,6,0,0) << Insert(19,6,0,0)); - QTest::newRow("m(12-23,6),r(2,4)") - << (SignalList() << Move(12,23,6,0) << Remove(2,4)) - << (SignalList() << Remove(2,4) << Remove(8,6,0,0) << Insert(19,6,0,0)); - QTest::newRow("m(12-23,6),r(18,4)") - << (SignalList() << Move(12,23,6,0) << Remove(18,4)) - << (SignalList() << Remove(12,6,0,0) << Remove(18,4) << Insert(19,6,0,0)); - QTest::newRow("m(12-23,6),r(20,4)") - << (SignalList() << Move(12,23,6,0) << Remove(20,4)) - << (SignalList() << Remove(12,6,0,0) << Remove(20,3) << Insert(20,5,0,1)); - - - // Complex - QTest::newRow("r(15,1),r(22,1)") - << (SignalList() << Remove(15,1) << Remove(22,1)) - << (SignalList() << Remove(15,1) << Remove(22,1)); - QTest::newRow("r(15,1),r(22,1),r(25,1)") - << (SignalList() << Remove(15,1) << Remove(22,1) << Remove(25,1)) - << (SignalList() << Remove(15,1) << Remove(22,1) << Remove(25,1)); - QTest::newRow("r(15,1),r(22,1),r(25,1),r(15,1)") - << (SignalList() << Remove(15,1) << Remove(22,1) << Remove(25,1) << Remove(15,1)) - << (SignalList() << Remove(15,2) << Remove(21,1) << Remove(24,1)); - QTest::newRow("r(15,1),r(22,1),r(25,1),r(15,1),r(13,1)") - << (SignalList() << Remove(15,1) << Remove(22,1) << Remove(25,1) << Remove(15,1) << Remove(13,1)) - << (SignalList() << Remove(13,1) << Remove(14,2) << Remove(20,1) << Remove(23,1)); - QTest::newRow("r(15,1),r(22,1),r(25,1),r(15,1),r(13,1),r(13,1)") - << (SignalList() << Remove(15,1) << Remove(22,1) << Remove(25,1) << Remove(15,1) << Remove(13,1) << Remove(13,1)) - << (SignalList() << Remove(13,4) << Remove(19,1) << Remove(22,1)); - QTest::newRow("r(15,1),r(22,1),r(25,1),r(15,1),r(13,1),r(13,1),m(12,13,1)") - << (SignalList() << Remove(15,1) << Remove(22,1) << Remove(25,1) << Remove(15,1) << Remove(13,1) << Remove(13,1) << Move(12,13,1,0)) - << (SignalList() << Remove(12,1,0,0) << Remove(12,4) << Remove(18,1) << Remove(21,1) << Insert(13,1,0,0)); - - QTest::newRow("r(12,18),r(3,0)") - << (SignalList() << Remove(12,18) << Remove(3,0)) - << (SignalList() << Remove(12,18)); - QTest::newRow("r(12,18),r(3,0),r(1,2)") - << (SignalList() << Remove(12,18) << Remove(3,0) << Remove(1,2)) - << (SignalList() << Remove(1,2) << Remove(10,18)); - QTest::newRow("r(12,18),r(3,0),r(1,2),m(4,0,11)") - << (SignalList() << Remove(12,18) << Remove(3,0) << Remove(1,2) << Move(4,0,11,0)) - << (SignalList() << Remove(1,2) << Remove(4,6,0,0) << Remove(4,18) << Remove(4,5,0,6) << Insert(0,11,0,0)); - QTest::newRow("r(12,18),r(3,0),r(1,2),m(4,0,11),r(14,3)") - << (SignalList() << Remove(12,18) << Remove(3,0) << Remove(1,2) << Move(4,0,11,0) << Remove(14,3)) - << (SignalList() << Remove(1,2) << Remove(3,1) << Remove(3,6,0,0) << Remove(3,18) << Remove(3,5,0,6) << Remove(3,2) << Insert(0,11,0,0)); - - QTest::newRow("m(9,11,14),i(16,1)") - << (SignalList() << Move(9,11,14,0) << Insert(16,1)) - << (SignalList() << Remove(9,14,0,0) << Insert(11,5,0,0) << Insert(16,1) << Insert(17,9,0,5)); - QTest::newRow("m(9,11,14),i(16,1),m(22,38,3)") - << (SignalList() << Move(9,11,14,0) << Insert(16,1) << Move(22,38,3,1)) - << (SignalList() << Remove(9,14,0,0) << Insert(11,5,0,0) << Insert(16,1) << Insert(17,5,0,5) << Insert(22,1,0,13) << Insert(38,3,0,10)); - - QTest::newRow("i(28,2),m(7,5,22)") - << (SignalList() << Insert(28,2) << Move(7,5,22,0)) - << (SignalList() << Remove(7,21,0,0) << Insert(5,21,0,0) << Insert(26,1) << Insert(29,1)); - - QTest::newRow("i(16,3),m(18,28,15)") - << (SignalList() << Insert(16,3) << Move(18,28,15,0)) - << (SignalList() << Remove(16,14,0,1) << Insert(16,2) << Insert(28,1) << Insert(29,14,0,1)); - - QTest::newRow("m(33,12,6),m(18,20,20)") - << (SignalList() << Move(22,12,6,0) << Move(18,20,20,1)) - << (SignalList() << Remove(12,10,1,0) << Remove(12,6,0,0) << Remove(12,10,1,10) - << Insert(12,6,0,0) << Insert(20,20,1,0)); - QTest::newRow("m(33,12,6),m(18,20,20),m(38,19,1)") - << (SignalList() << Move(22,12,6,0) << Move(18,20,20,1) << Move(28,19,1,2)) - << (SignalList() << Remove(12,10,1,0) << Remove(12,6,0,0) << Remove(12,10,1,10) - << Insert(12,6,0,0) << Insert(19,1,1,8) << Insert(21,8,1,0) << Insert(29,11,1,9)); - QTest::newRow("m(33,12,6),m(18,20,20),m(38,19,1),r(34,4)") - << (SignalList() << Move(22,12,6,0) << Move(18,20,20,1) << Move(28,19,1,2) << Remove(34,4)) - << (SignalList() << Remove(12,10,1,0) << Remove(12,6,0,0) << Remove(12,10,1,10) - << Insert(12,6,0,0) << Insert(19,1,1,8) << Insert(21,8,1,0) << Insert(29,5,1,9) << Insert(34,2,1,18)); - QTest::newRow("m(33,12,6),m(18,20,20),m(38,19,1),r(34,4),m(13,9,15)") - << (SignalList() << Move(22,12,6,0) << Move(18,20,20,1) << Move(28,19,1,2) << Remove(34,4) << Move(13,9,15,3)) - << (SignalList() << Remove(12,10,1,0) << Remove(12,6,0,0) << Remove(12,10,1,10) << Remove(12,1,3,5) << Remove(12,1,3,7) - << Insert(9,5,0,1) << Insert(14,1,3,5) << Insert(15,1,1,8) << Insert(16,1,3,7) << Insert(17,7,1,0) << Insert(27,1,0,0) << Insert(28,1,1,7) << Insert(29,5,1,9) << Insert(34,2,1,18)); - - QTest::newRow("i(8,5),m(14,26,14)") - << (SignalList() << Insert(8,5) << Move(14,26,14,0)) - << (SignalList() << Remove(9,14,0,0) << Insert(8,5) << Insert(26,14,0,0)); - QTest::newRow("i(8,5),m(14,26,14),r(45,0)") - << (SignalList() << Insert(8,5) << Move(14,26,14,0) << Remove(45,0)) - << (SignalList() << Remove(9,14,0,0) << Insert(8,5) << Insert(26,14,0,0)); - QTest::newRow("i(8,5),m(14,26,14),r(45,0),m(5,8,21)") - << (SignalList() << Insert(8,5) << Move(14,26,14,0) << Remove(45,0) << Move(5,8,21,1)) - << (SignalList() << Remove(5,3,1,0) << Remove(5,1,1,8) << Remove(5,14,0,0) << Remove(5,12,1,9) - << Insert(5,3,0,0) << Insert(8,3,1,0) << Insert(11,5) << Insert(16,13,1,8) << Insert(29,11,0,3)); - - QTest::newRow("i(35,1),r(5,31)") - << (SignalList() << Insert(35,1) << Remove(5,31)) - << (SignalList() << Remove(5,30)); - QTest::newRow("i(35,1),r(5,31),m(9,8,1)") - << (SignalList() << Insert(35,1) << Remove(5,31) << Move(9,8,1,0)) - << (SignalList() << Remove(5,30) << Remove(9,1,0,0) << Insert(8,1,0,0)); - QTest::newRow("i(35,1),r(5,31),m(9,8,1),i(7,2)") - << (SignalList() << Insert(35,1) << Remove(5,31) << Move(9,8,1,0) << Insert(7,2)) - << (SignalList() << Remove(5,30) << Remove(9,1,0,0) << Insert(7,2) << Insert(10,1,0,0)); - QTest::newRow("i(35,1),r(5,31),m(9,8,1),i(7,2),r(4,3)") - << (SignalList() << Insert(35,1) << Remove(5,31) << Move(9,8,1,0) << Insert(7,2) << Remove(4,3)) - << (SignalList() << Remove(4,33) << Remove(6,1,0,0) << Insert(4,2) << Insert(7,1,0,0)); - - QTest::newRow("r(37,0),r(21,1)") - << (SignalList() << Remove(37,0) << Remove(21,1)) - << (SignalList() << Remove(21,1)); - QTest::newRow("r(37,0),r(21,1),m(27,35,2)") - << (SignalList() << Remove(37,0) << Remove(21,1) << Move(27,35,2,0)) - << (SignalList() << Remove(21,1) << Remove(27,2,0,0) << Insert(35,2,0,0)); - QTest::newRow("r(37,0),r(21,1),m(27,35,2),i(31,5)") - << (SignalList() << Remove(37,0) << Remove(21,1) << Move(27,35,2,0) << Insert(31,5)) - << (SignalList() << Remove(21,1) << Remove(27,2,0,0) << Insert(31,5) << Insert(40,2,0,0)); - QTest::newRow("r(37,0),r(21,1),m(27,35,2),i(31,5),r(10,31)") - << (SignalList() << Remove(37,0) << Remove(21,1) << Move(27,35,2,0) << Insert(31,5) << Remove(10,31)) - << (SignalList() << Remove(10, 18) << Remove(10,2,0,0) << Remove(10,8) << Insert(10,1,0,1)); - - QTest::newRow("m(1,1,39),r(26,10)") - << (SignalList() << Move(1,1,39,0) << Remove(26,10)) - << (SignalList() << Remove(1,39,0,0) << Insert(1,25,0,0) << Insert(26,4,0,35)); - QTest::newRow("m(1,1,39),r(26,10),i(10,5)") - << (SignalList() << Move(1,1,39,0) << Remove(26,10) << Insert(10,5)) - << (SignalList() << Remove(1,39,0,0) << Insert(1,9,0,0) << Insert(10,5) << Insert(15,16,0,9) << Insert(31,4,0,35)); - QTest::newRow("m(1,1,39),r(26,10),i(10,5),i(27,3)") - << (SignalList() << Move(1,1,39,0) << Remove(26,10) << Insert(10,5) << Insert(27,3)) - << (SignalList() << Remove(1,39,0,0) - << Insert(1,9,0,0) << Insert(10,5) << Insert(15,12,0,9) << Insert(27,3) << Insert(30,4,0,21) << Insert(34,4,0,35)); - QTest::newRow("m(1,1,39),r(26,10),i(10,5),i(27,3),r(28,5)") - << (SignalList() << Move(1,1,39,0) << Remove(26,10) << Insert(10,5) << Insert(27,3) << Remove(28,5)) - << (SignalList() << Remove(1,39,0,0) - << Insert(1,9,0,0) << Insert(10,5) << Insert(15,12,0,9) << Insert(27,1) << Insert(28,1,0,24) << Insert(29,4,0,35)); - - QTest::newRow("i(36,4)m(25,39,5)") - << (SignalList() << Insert(36,4) << Move(25,39,5,0)) - << (SignalList() << Remove(25,5,0,0) << Insert(31,4) << Insert(39,5,0,0)); - QTest::newRow("i(36,4)m(25,39,5),i(16,5)") - << (SignalList() << Insert(36,4) << Move(25,39,5,0) << Insert(16,5)) - << (SignalList() << Remove(25,5,0,0) << Insert(16,5) << Insert(36,4) << Insert(44,5,0,0)); - QTest::newRow("i(36,4)m(25,39,5),i(16,5),i(37,5)") - << (SignalList() << Insert(36,4) << Move(25,39,5,0) << Insert(16,5) << Insert(37,5)) - << (SignalList() << Remove(25,5,0,0) << Insert(16,5) << Insert(36,9) << Insert(49,5,0,0)); - QTest::newRow("i(36,4)m(25,39,5),i(16,5),i(37,5),m(40,21,11)") - << (SignalList() << Insert(36,4) << Move(25,39,5,0) << Insert(16,5) << Insert(37,5) << Move(40,21,11,1)) - << (SignalList() << Remove(25,5,0,0) << Remove(31,4,1,5) - << Insert(16,10) << Insert(26,4,1,5) << Insert(30,2,0,0) << Insert(47,4) << Insert(51,3,0,2)); - - QTest::newRow("i(24,1),r(33,4)") - << (SignalList() << Insert(24,1) << Remove(33,4)) - << (SignalList() << Remove(32,4) << Insert(24,1)); - QTest::newRow("i(24,1),r(33,4),r(15,15)") - << (SignalList() << Insert(24,1) << Remove(33,4) << Remove(15,15)) - << (SignalList() << Remove(15,14) << Remove(18,4)); - QTest::newRow("i(24,1),r(33,4),r(15,15),m(8,10,2)") - << (SignalList() << Insert(24,1) << Remove(33,4) << Remove(15,15) << Move(8,10,2,0)) - << (SignalList() << Remove(8,2,0,0) << Remove(13,14) << Remove(16,4) << Insert(10,2,0,0)); - QTest::newRow("i(24,1),r(33,4),r(15,15),m(8,10,2),r(2,19)") - << (SignalList() << Insert(24,1) << Remove(33,4) << Remove(15,15) << Move(8,10,2,0) << Remove(2,19)) - << (SignalList() << Remove(2,6) << Remove(2,2,0,0) << Remove(2,29)); - - QTest::newRow("r(1,35),i(3,4),m(4,2,2)") - << (SignalList() << Remove(1,35) << Insert(3,4) << Move(4,2,2,0)) - << (SignalList() << Remove(1,35) <("input"); - - QTest::newRow("(r(1,35),i(3,4)),(m(4,2,2),r(7,1))") - << (SignalListList() - << (SignalList() << Remove(1,35) << Insert(3,4)) - << (SignalList() << Move(4,2,2,0) << Remove(7,1))); - - QTest::newRow("(i(30,4),m(7,28,16))(m(6,7,13),m(41,35,2))") - << (SignalListList() - << (SignalList() << Insert(30,4) << Move(7,28,16,0)) - << (SignalList() << Move(6,7,13,1) << Move(41,35,2,2))); - - QTest::newRow("(i(35,2),r(39,0))(r(25,11),m(24,8,7))") - << (SignalListList() - << (SignalList() << Insert(35,2) << Remove(39,0)) - << (SignalList() << Remove(25,11) << Move(24,8,7,0))); - - QTest::newRow("i(26,1),i(39,1),m(31,34,2),r(15,27)") - << (SignalListList() - << (SignalList() << Insert(26,1) << Insert(39,1)) - << (SignalList() << Move(31,34,2,0) << Remove(15,27))); - - QTest::newRow("i(19,1),(2,3),r(38,4),m(2,20,3)") - << (SignalListList() - << (SignalList() << Insert(19,1) << Insert(2,3)) - << (SignalList() << Remove(38,4) << Move(2,20,3,0))); - - QTest::newRow("i(4,3),i(19,1),i(31,3),m(8,10,29)") - << (SignalListList() - << (SignalList() << Insert(4,3) << Insert(19,1)) - << (SignalList() << Insert(31,3) << Move(8,10,29,0))); - - QTest::newRow("m(18,15,16),i(0,1),i(32,2),i(29,2)") - << (SignalListList() - << (SignalList() << Move(18,15,16,0) << Insert(0,1)) - << (SignalList() << Insert(32,2) << Insert(29,2))); - - QTest::newRow("i(38,5),i(12,5),i(48,3),m(28,6,6)") - << (SignalListList() - << (SignalList() << Insert(38,5) << Insert(12,5)) - << (SignalList() << Insert(48,3) << Move(28,6,6,0))); - - QTest::newRow("r(8,9),m(7,10,18),m(8,10,18),i(17,2)") - << (SignalListList() - << (SignalList() << Remove(8,9) << Move(7,10,18,0)) - << (SignalList() << Move(8,10,18,1) << Insert(17,2))); - - QTest::newRow("r(39,0),m(18,1,21),m(2,6,31),r(9,4)") - << (SignalListList() - << (SignalList() << Remove(39,0) << Move(18,1,21,0)) - << (SignalList() << Move(2,6,31,1) << Remove(9,4))); - - QTest::newRow("3*5 (5)") - << (SignalListList() - << (SignalList() << Remove(36,4) << Move(3,2,14,0) << Insert(36,3) << Remove(30,7) << Insert(3,5))); - QTest::newRow("3*5 (6)") - << (SignalListList() - << (SignalList() << Remove(36,4) << Move(3,2,14,0) << Insert(36,3) << Remove(30,7) << Insert(3,5)) - << (SignalList() << Insert(1,1))); - QTest::newRow("3*5 (7)") - << (SignalListList() - << (SignalList() << Remove(36,4) << Move(3,2,14,0) << Insert(36,3) << Remove(30,7) << Insert(3,5)) - << (SignalList() << Insert(1,1) << Move(9,32,3,1))); - QTest::newRow("3*5 (8)") - << (SignalListList() - << (SignalList() << Remove(36,4) << Move(3,2,14,0) << Insert(36,3) << Remove(30,7) << Insert(3,5)) - << (SignalList() << Insert(1,1) << Move(9,32,3,1) << Remove(22,1))); - QTest::newRow("3*5 (9)") - << (SignalListList() - << (SignalList() << Remove(36,4) << Move(3,2,14,0) << Insert(36,3) << Remove(30,7) << Insert(3,5)) - << (SignalList() << Insert(1,1) << Move(9,32,3,1) << Remove(22,1) << Insert(29,3))); - QTest::newRow("3*5 (10)") - << (SignalListList() - << (SignalList() << Remove(36,4) << Move(3,2,14,0) << Insert(36,3) << Remove(30,7) << Insert(3,5)) - << (SignalList() << Insert(1,1) << Move(9,32,3,1) << Remove(22,1) << Insert(29,3) << Move(7,15,23,2))); - QTest::newRow("3*5 (11)") - << (SignalListList() - << (SignalList() << Remove(36,4) << Move(3,2,14,0) << Insert(36,3) << Remove(30,7) << Insert(3,5)) - << (SignalList() << Insert(1,1) << Move(9,32,3,1) << Remove(22,1) << Insert(29,3) << Move(7,15,23,2)) - << (SignalList() << Move(38,23,1,3))); - QTest::newRow("3*5 (12)") - << (SignalListList() - << (SignalList() << Remove(36,4) << Move(3,2,14,0) << Insert(36,3) << Remove(30,7) << Insert(3,5)) - << (SignalList() << Insert(1,1) << Move(9,32,3,1) << Remove(22,1) << Insert(29,3) << Move(7,15,23,2)) - << (SignalList() << Move(38,23,1,3) << Move(38,31,0,4))); - QTest::newRow("3*5 (13)") - << (SignalListList() - << (SignalList() << Remove(36,4) << Move(3,2,14,0) << Insert(36,3) << Remove(30,7) << Insert(3,5)) - << (SignalList() << Insert(1,1) << Move(9,32,3,1) << Remove(22,1) << Insert(29,3) << Move(7,15,23,2)) - << (SignalList() << Move(38,23,1,3) << Move(38,31,0,4) << Remove(26,11))); - QTest::newRow("3*5 (14)") - << (SignalListList() - << (SignalList() << Remove(36,4) << Move(3,2,14,0) << Insert(36,3) << Remove(30,7) << Insert(3,5)) - << (SignalList() << Insert(1,1) << Move(9,32,3,1) << Remove(22,1) << Insert(29,3) << Move(7,15,23,2)) - << (SignalList() << Move(38,23,1,3) << Move(38,31,0,4) << Remove(26,11) << Move(5,7,18,5))); - QTest::newRow("3*5 (15)") - << (SignalListList() - << (SignalList() << Remove(36,4) << Move(3,2,14,0) << Insert(36,3) << Remove(30,7) << Insert(3,5)) - << (SignalList() << Insert(1,1) << Move(9,32,3,1) << Remove(22,1) << Insert(29,3) << Move(7,15,23,2)) - << (SignalList() << Move(38,23,1,3) << Move(38,31,0,4) << Remove(26,11) << Move(5,7,18,5) << Move(19,0,8,6))); -} - -void tst_qquickchangeset::apply() -{ - QFETCH(SignalListList, input); - - QQuickChangeSet set; - QQuickChangeSet linearSet; - - foreach (const SignalList &list, input) { - QQuickChangeSet intermediateSet; - foreach (const Signal &signal, list) { - if (signal.isRemove()) { - intermediateSet.remove(signal.index, signal.count); - linearSet.remove(signal.index, signal.count); - } else if (signal.isInsert()) { - intermediateSet.insert(signal.index, signal.count); - linearSet.insert(signal.index, signal.count); - } else if (signal.isMove()) { - intermediateSet.move(signal.index, signal.to, signal.count, signal.moveId); - linearSet.move(signal.index, signal.to, signal.count, signal.moveId); - } - } - set.apply(intermediateSet); - } - - SignalList changes; - foreach (const QQuickChangeSet::Remove &remove, set.removes()) - changes << Remove(remove.index, remove.count, remove.moveId, remove.offset); - foreach (const QQuickChangeSet::Insert &insert, set.inserts()) - changes << Insert(insert.index, insert.count, insert.moveId, insert.offset); - - SignalList linearChanges; - foreach (const QQuickChangeSet::Remove &remove, linearSet.removes()) - linearChanges << Remove(remove.index, remove.count, remove.moveId, remove.offset); - foreach (const QQuickChangeSet::Insert &insert, linearSet.inserts()) - linearChanges << Insert(insert.index, insert.count, insert.moveId, insert.offset); - - // The output in the failing tests isn't incorrect, merely sub-optimal. - QEXPECT_FAIL("3*5 (10)", "inserts not joined when dividing space removed", Abort); - QEXPECT_FAIL("3*5 (11)", "inserts not joined when dividing space removed", Abort); - QEXPECT_FAIL("3*5 (12)", "inserts not joined when dividing space removed", Abort); - QEXPECT_FAIL("3*5 (13)", "inserts not joined when dividing space removed", Abort); - QEXPECT_FAIL("3*5 (14)", "inserts not joined when dividing space removed", Abort); - QEXPECT_FAIL("3*5 (15)", "inserts not joined when dividing space removed", Abort); - QCOMPARE(changes, linearChanges); -} - -void tst_qquickchangeset::removeConsecutive_data() -{ - QTest::addColumn("input"); - QTest::addColumn("output"); - - QTest::newRow("at start") - << (SignalList() << Remove(0,2) << Remove(0,1) << Remove(0,5)) - << (SignalList() << Remove(0,8)); - QTest::newRow("offset") - << (SignalList() << Remove(3,2) << Remove(3,1) << Remove(3,5)) - << (SignalList() << Remove(3,8)); - QTest::newRow("with move") - << (SignalList() << Remove(0,2) << Remove(0,1,0,0) << Remove(0,5)) - << (SignalList() << Remove(0,2) << Remove(0,1,0,0) << Remove(0,5)); -} - -void tst_qquickchangeset::removeConsecutive() -{ - QFETCH(SignalList, input); - QFETCH(SignalList, output); - - QVector removes; - foreach (const Signal &signal, input) { - QVERIFY(signal.isRemove()); - removes.append(QQuickChangeSet::Remove(signal.index, signal.count, signal.moveId, signal.offset)); - } - - QQuickChangeSet set; - set.remove(removes); - - SignalList changes; - foreach (const QQuickChangeSet::Remove &remove, set.removes()) - changes << Remove(remove.index, remove.count, remove.moveId, remove.offset); - QVERIFY(set.inserts().isEmpty()); - QVERIFY(set.changes().isEmpty()); - - VERIFY_EXPECTED_OUTPUT - QCOMPARE(changes, output); -} - -void tst_qquickchangeset::insertConsecutive_data() -{ - QTest::addColumn("input"); - QTest::addColumn("output"); - - QTest::newRow("at start") - << (SignalList() << Insert(0,2) << Insert(2,1) << Insert(3,5)) - << (SignalList() << Insert(0,8)); - QTest::newRow("offset") - << (SignalList() << Insert(3,2) << Insert(5,1) << Insert(6,5)) - << (SignalList() << Insert(3,8)); - QTest::newRow("with move") - << (SignalList() << Insert(0,2) << Insert(2,1,0,0) << Insert(3,5)) - << (SignalList() << Insert(0,2) << Insert(2,1,0,0) << Insert(3,5)); -} - -void tst_qquickchangeset::insertConsecutive() -{ - QFETCH(SignalList, input); - QFETCH(SignalList, output); - - QVector inserts; - foreach (const Signal &signal, input) { - QVERIFY(signal.isInsert()); - inserts.append(QQuickChangeSet::Insert(signal.index, signal.count, signal.moveId, signal.offset)); - } - - QQuickChangeSet set; - set.insert(inserts); - - SignalList changes; - foreach (const QQuickChangeSet::Insert &insert, set.inserts()) - changes << Insert(insert.index, insert.count, insert.moveId, insert.offset); - QVERIFY(set.removes().isEmpty()); - QVERIFY(set.changes().isEmpty()); - - VERIFY_EXPECTED_OUTPUT - QCOMPARE(changes, output); -} - -void tst_qquickchangeset::copy() -{ - QQuickChangeSet changeSet; - changeSet.remove(0, 12); - changeSet.remove(5, 4); - changeSet.insert(3, 9); - changeSet.insert(15, 2); - changeSet.change(24, 8); - changeSet.move(3, 5, 9, 0); - - QQuickChangeSet copy(changeSet); - - QQuickChangeSet assign; - assign = changeSet; - - copy.move(4, 2, 5, 1); - assign.move(4, 2, 5, 1); - changeSet.move(4, 2, 5, 1); - - QCOMPARE(copy.removes(), changeSet.removes()); - QCOMPARE(copy.inserts(), changeSet.inserts()); - QCOMPARE(copy.changes(), changeSet.changes()); - QCOMPARE(copy.difference(), changeSet.difference()); - - QCOMPARE(assign.removes(), changeSet.removes()); - QCOMPARE(assign.inserts(), changeSet.inserts()); - QCOMPARE(assign.changes(), changeSet.changes()); - QCOMPARE(assign.difference(), changeSet.difference()); -} - -void tst_qquickchangeset::debug() -{ - QQuickChangeSet changeSet; - changeSet.remove(0, 12); - changeSet.remove(5, 4); - changeSet.insert(3, 9); - changeSet.insert(15, 2); - changeSet.change(24, 8); - - QTest::ignoreMessage(QtDebugMsg, "QQuickChangeSet(Remove(0,12) Remove(5,4) Insert(3,9) Insert(15,2) Change(24,8) )"); - qDebug() << changeSet; - - changeSet.clear(); - - QTest::ignoreMessage(QtDebugMsg, "QQuickChangeSet(Remove(12,4,0,0) Insert(5,4,0,0) )"); - - changeSet.move(12, 5, 4, 0); - qDebug() << changeSet; -} - -void tst_qquickchangeset::random_data() -{ - QTest::addColumn("seed"); - QTest::addColumn("combinations"); - QTest::addColumn("depth"); - QTest::newRow("1*5") << 32 << 1 << 5; - QTest::newRow("2*2") << 32 << 2 << 2; - QTest::newRow("3*2") << 32 << 3 << 2; - QTest::newRow("3*5") << 32 << 3 << 5; -} - -void tst_qquickchangeset::random() -{ - QFETCH(int, seed); - QFETCH(int, combinations); - QFETCH(int, depth); - - qsrand(seed); - - int failures = 0; - for (int i = 0; i < 20000; ++i) { - QQuickChangeSet accumulatedSet; - SignalList input; - - int modelCount = 40; - int moveCount = 0; - - for (int j = 0; j < combinations; ++j) { - QQuickChangeSet set; - for (int k = 0; k < depth; ++k) { - switch (-(qrand() % 3)) { - case InsertOp: { - int index = qrand() % (modelCount + 1); - int count = qrand() % 5 + 1; - set.insert(index, count); - input.append(Insert(index, count)); - modelCount += count; - break; - } - case RemoveOp: { - const int index = qrand() % (modelCount + 1); - const int count = qrand() % (modelCount - index + 1); - set.remove(index, count); - input.append(Remove(index, count)); - modelCount -= count; - break; - } - case MoveOp: { - const int from = qrand() % (modelCount + 1); - const int count = qrand() % (modelCount - from + 1); - const int to = qrand() % (modelCount - count + 1); - const int moveId = moveCount++; - set.move(from, to, count, moveId); - input.append(Move(from, to, count, moveId)); - break; - } - default: - break; - } - } - accumulatedSet.apply(set); - } - - SignalList output; - foreach (const QQuickChangeSet::Remove &remove, accumulatedSet.removes()) - output << Remove(remove.index, remove.count, remove.moveId, remove.offset); - foreach (const QQuickChangeSet::Insert &insert, accumulatedSet.inserts()) - output << Insert(insert.index, insert.count, insert.moveId, insert.offset); - - QVector inputList; - for (int i = 0; i < 40; ++i) - inputList.append(i); - QVector outputList = inputList; - if (!applyChanges(inputList, input)) { - qDebug() << "Invalid input list"; - qDebug() << input; - qDebug() << inputList; - ++failures; - } else if (!applyChanges(outputList, output)) { - qDebug() << "Invalid output list"; - qDebug() << input; - qDebug() << output; - qDebug() << outputList; - ++failures; - } else if (outputList != inputList) { - qDebug() << "Input/output mismatch"; - qDebug() << input; - qDebug() << output; - qDebug() << inputList; - qDebug() << outputList; - ++failures; - } - } - QCOMPARE(failures, 0); -} - -QTEST_MAIN(tst_qquickchangeset) - -#include "tst_qquickchangeset.moc" diff --git a/tests/auto/qml/qquicklistcompositor/qquicklistcompositor.pro b/tests/auto/qml/qquicklistcompositor/qquicklistcompositor.pro deleted file mode 100644 index 9d1d2bd237..0000000000 --- a/tests/auto/qml/qquicklistcompositor/qquicklistcompositor.pro +++ /dev/null @@ -1,10 +0,0 @@ -CONFIG += testcase -TARGET = tst_qquicklistcompositor -macx:CONFIG -= app_bundle - -SOURCES += tst_qquicklistcompositor.cpp - -CONFIG += parallel_test - -QT += core-private gui-private qml-private quick-private testlib -DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 diff --git a/tests/auto/qml/qquicklistcompositor/tst_qquicklistcompositor.cpp b/tests/auto/qml/qquicklistcompositor/tst_qquicklistcompositor.cpp deleted file mode 100644 index 5be501c94e..0000000000 --- a/tests/auto/qml/qquicklistcompositor/tst_qquicklistcompositor.cpp +++ /dev/null @@ -1,1740 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** 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, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ -#include -#include - -template int lengthOf(const T (&)[N]) { return N; } - -typedef QQuickListCompositor C; - -struct Range -{ - Range() {} - Range(void *list, int index, int count, int flags) - : list(list), index(index), count(count), flags(flags) {} - void *list; - int index; - int count; - int flags; -}; - -template struct Array -{ - Array() : array(0), count(0) {} - template Array(const T (&array)[N]) : array(array), count(N) {} - - T operator [](int index) const { return array[index]; } - - const T *array; - int count; -}; - -typedef Array IndexArray; -typedef Array ListArray; - -typedef QVector RemoveList; -typedef QVector InsertList; -typedef QVector ChangeList; - -typedef QVector RangeList; - -Q_DECLARE_METATYPE(RangeList) -Q_DECLARE_METATYPE(RemoveList) -Q_DECLARE_METATYPE(InsertList) -Q_DECLARE_METATYPE(ChangeList) -Q_DECLARE_METATYPE(void *) -Q_DECLARE_METATYPE(IndexArray) -Q_DECLARE_METATYPE(ListArray) -Q_DECLARE_METATYPE(C::Group) - -QT_BEGIN_NAMESPACE -bool operator ==(const C::Change &left, const C::Change &right) -{ - return left.index[3] == right.index[3] - && left.index[2] == right.index[2] - && left.index[1] == right.index[1] - && left.index[0] == right.index[0] - && left.count == right.count - && left.groups() == right.groups() - && left.inCache() == right.inCache() - && (left.moveId == -1) == (right.moveId == -1); -} -QT_END_NAMESPACE - -static const C::Group Visible = C::Group(2); -static const C::Group Selection = C::Group(3); - -class tst_qquicklistcompositor : public QObject -{ - Q_OBJECT - - enum { - VisibleFlag = 0x04, - SelectionFlag = 0x08 - }; - - void populateChange( - C::Change &change, int sIndex, int vIndex, int dIndex, int cIndex, int count, int flags, int moveId) - { - change.index[Selection] = sIndex; - change.index[Visible] = vIndex; - change.index[C::Default] = dIndex; - change.index[C::Cache] = cIndex; - change.count = count; - change.flags = flags; - change.moveId = moveId; - } - - C::Remove Remove( - int sIndex, int vIndex, int dIndex, int cIndex, int count, int flags, int moveId = -1) - { - C::Remove remove; - populateChange(remove, sIndex, vIndex, dIndex, cIndex, count, flags, moveId); - return remove; - } - - C::Insert Insert( - int sIndex, int vIndex, int dIndex, int cIndex, int count, int flags, int moveId = -1) - { - C::Insert insert; - populateChange(insert, sIndex, vIndex, dIndex, cIndex, count, flags, moveId); - return insert; - } - - C::Change Change( - int sIndex, int vIndex, int dIndex, int cIndex, int count, int flags, int moveId = -1) - { - C::Change change; - populateChange(change, sIndex, vIndex, dIndex, cIndex, count, flags, moveId); - return change; - } - -private slots: - void find_data(); - void find(); - void findInsertPosition_data(); - void findInsertPosition(); - void insert(); - void clearFlags_data(); - void clearFlags(); - void setFlags_data(); - void setFlags(); - void move_data(); - void move(); - void moveFromEnd(); - void clear(); - void listItemsInserted_data(); - void listItemsInserted(); - void listItemsRemoved_data(); - void listItemsRemoved(); - void listItemsMoved_data(); - void listItemsMoved(); - void listItemsChanged_data(); - void listItemsChanged(); - void compositorDebug(); - void changeDebug(); - void groupDebug(); -}; - -void tst_qquicklistcompositor::find_data() -{ - QTest::addColumn("ranges"); - QTest::addColumn("startGroup"); - QTest::addColumn("startIndex"); - QTest::addColumn("group"); - QTest::addColumn("index"); - QTest::addColumn("selectionIndex"); - QTest::addColumn("visibleIndex"); - QTest::addColumn("defaultIndex"); - QTest::addColumn("cacheIndex"); - QTest::addColumn("rangeFlags"); - QTest::addColumn("rangeIndex"); - - int listA; void *a = &listA; - - QTest::newRow("Start") - << (RangeList() - << Range(a, 0, 1, int(C::PrependFlag | SelectionFlag | C::DefaultFlag | C::CacheFlag)) - << Range(a, 1, 1, int(C::AppendFlag | C::PrependFlag | C::CacheFlag)) - << Range(0, 0, 1, int(VisibleFlag| C::CacheFlag))) - << C::Cache << 2 - << Selection << 0 - << 0 << 0 << 0 << 0 - << uint(C::PrependFlag | SelectionFlag | C::DefaultFlag | C::CacheFlag) << 0; -} - -void tst_qquicklistcompositor::find() -{ - QFETCH(RangeList, ranges); - QFETCH(C::Group, startGroup); - QFETCH(int, startIndex); - QFETCH(C::Group, group); - QFETCH(int, index); - QFETCH(int, cacheIndex); - QFETCH(int, defaultIndex); - QFETCH(int, visibleIndex); - QFETCH(int, selectionIndex); - QFETCH(uint, rangeFlags); - QFETCH(int, rangeIndex); - - QQuickListCompositor compositor; - compositor.setGroupCount(4); - compositor.setDefaultGroups(VisibleFlag | C::DefaultFlag); - - foreach (const Range &range, ranges) - compositor.append(range.list, range.index, range.count, range.flags); - - compositor.find(startGroup, startIndex); - - QQuickListCompositor::iterator it = compositor.find(group, index); - QCOMPARE(it.index[C::Cache], cacheIndex); - QCOMPARE(it.index[C::Default], defaultIndex); - QCOMPARE(it.index[Visible], visibleIndex); - QCOMPARE(it.index[Selection], selectionIndex); - QCOMPARE(it->flags, rangeFlags); - QCOMPARE(it->index, rangeIndex); -} - -void tst_qquicklistcompositor::findInsertPosition_data() -{ - QTest::addColumn("ranges"); - QTest::addColumn("group"); - QTest::addColumn("index"); - QTest::addColumn("selectionIndex"); - QTest::addColumn("visibleIndex"); - QTest::addColumn("defaultIndex"); - QTest::addColumn("cacheIndex"); - QTest::addColumn("rangeFlags"); - QTest::addColumn("rangeIndex"); - - int listA; void *a = &listA; - - QTest::newRow("Start") - << (RangeList() - << Range(a, 0, 1, int(C::PrependFlag | SelectionFlag | C::DefaultFlag | C::CacheFlag)) - << Range(a, 1, 1, int(C::AppendFlag | C::PrependFlag | C::CacheFlag)) - << Range(0, 0, 1, int(VisibleFlag| C::CacheFlag))) - << Selection << 0 - << 0 << 0 << 0 << 0 - << uint(C::PrependFlag | SelectionFlag | C::DefaultFlag | C::CacheFlag) << 0; - QTest::newRow("1") - << (RangeList() - << Range(a, 0, 1, int(C::PrependFlag | SelectionFlag | C::DefaultFlag | C::CacheFlag)) - << Range(a, 1, 1, int(C::AppendFlag | C::PrependFlag | C::CacheFlag)) - << Range(0, 0, 1, int(VisibleFlag| C::CacheFlag))) - << Selection << 1 - << 1 << 1 << 1 << 3 - << uint(0) << 0; -} - -void tst_qquicklistcompositor::findInsertPosition() -{ - QFETCH(RangeList, ranges); - QFETCH(C::Group, group); - QFETCH(int, index); - QFETCH(int, cacheIndex); - QFETCH(int, defaultIndex); - QFETCH(int, visibleIndex); - QFETCH(int, selectionIndex); - QFETCH(uint, rangeFlags); - QFETCH(int, rangeIndex); - - QQuickListCompositor compositor; - compositor.setGroupCount(4); - compositor.setDefaultGroups(VisibleFlag | C::DefaultFlag); - - foreach (const Range &range, ranges) - compositor.append(range.list, range.index, range.count, range.flags); - - QQuickListCompositor::insert_iterator it = compositor.findInsertPosition(group, index); - - QCOMPARE(it.index[C::Cache], cacheIndex); - QCOMPARE(it.index[C::Default], defaultIndex); - QCOMPARE(it.index[Visible], visibleIndex); - QCOMPARE(it.index[Selection], selectionIndex); - QCOMPARE(it->flags, rangeFlags); - QCOMPARE(it->index, rangeIndex); -} - -void tst_qquicklistcompositor::insert() -{ - QQuickListCompositor compositor; - compositor.setGroupCount(4); - compositor.setDefaultGroups(VisibleFlag | C::DefaultFlag); - - C::iterator it; - - int listA; int *a = &listA; - int listB; int *b = &listB; - int listC; int *c = &listC; - - { - compositor.append(a, 0, 12, C::AppendFlag | C::PrependFlag | C::DefaultFlag); - const int indexes[] = {0,1,2,3,4,5,6,7,8,9,10,11}; - const int *lists[] = {a,a,a,a,a,a,a,a,a,a, a, a}; - QCOMPARE(compositor.count(C::Default), lengthOf(indexes)); - for (int i = 0; i < lengthOf(indexes); ++i) { - it = compositor.find(C::Default, i); - QCOMPARE(it.list(), lists[i]); - if (lists[i]) QCOMPARE(it.modelIndex(), indexes[i]); - } - } { - compositor.append(b, 4, 4, C::DefaultFlag); - const int indexes[] = {0,1,2,3,4,5,6,7,8,9,10,11,4,5,6,7}; - const int *lists[] = {a,a,a,a,a,a,a,a,a,a, a, a,b,b,b,b}; - QCOMPARE(compositor.count(C::Default), lengthOf(indexes)); - for (int i = 0; i < lengthOf(indexes); ++i) { - it = compositor.find(C::Default, i); - QCOMPARE(it.list(), lists[i]); - if (lists[i]) QCOMPARE(it.modelIndex(), indexes[i]); - } - } { // Insert at end. - compositor.insert( - C::Default, 16, c, 2, 2, C::DefaultFlag); - const int indexes[] = {0,1,2,3,4,5,6,7,8,9,10,11,4,5,6,7,2,3}; - const int *lists[] = {a,a,a,a,a,a,a,a,a,a, a, a,b,b,b,b,c,c}; - QCOMPARE(compositor.count(C::Default), lengthOf(indexes)); - for (int i = 0; i < lengthOf(indexes); ++i) { - it = compositor.find(C::Default, i); - QCOMPARE(it.list(), lists[i]); - if (lists[i]) QCOMPARE(it.modelIndex(), indexes[i]); - } - } { // Insert at start - compositor.insert( - C::Default, 0, c, 6, 4, C::DefaultFlag); - const int indexes[] = {6,7,8,9,0,1,2,3,4,5,6,7,8,9,10,11,4,5,6,7,2,3}; - const int *lists[] = {c,c,c,c,a,a,a,a,a,a,a,a,a,a, a, a,b,b,b,b,c,c}; - for (int i = 0; i < lengthOf(indexes); ++i) { - it = compositor.find(C::Default, i); - QCOMPARE(it.list(), lists[i]); - if (lists[i]) QCOMPARE(it.modelIndex(), indexes[i]); - } - } { // Insert after static range. - compositor.insert( - C::Default, 4, b, 0, 8, C::AppendFlag | C::PrependFlag | C::DefaultFlag); - const int indexes[] = {6,7,8,9,0,1,2,3,4,5,6,7,0,1,2,3,4,5,6,7,8,9,10,11,4,5,6,7,2,3}; - const int *lists[] = {c,c,c,c,b,b,b,b,b,b,b,b,a,a,a,a,a,a,a,a,a,a, a, a,b,b,b,b,c,c}; - QCOMPARE(compositor.count(C::Default), lengthOf(indexes)); - for (int i = 0; i < lengthOf(indexes); ++i) { - it = compositor.find(C::Default, i); - QCOMPARE(it.list(), lists[i]); - if (lists[i]) QCOMPARE(it.modelIndex(), indexes[i]); - } - } { // Insert at end of dynamic range. - compositor.insert( - C::Default, 12, c, 0, 4, C::AppendFlag | C::PrependFlag | C::DefaultFlag); - const int indexes[] = {6,7,8,9,0,1,2,3,4,5,6,7,0,1,2,3,0,1,2,3,4,5,6,7,8,9,10,11,4,5,6,7,2,3}; - const int *lists[] = {c,c,c,c,b,b,b,b,b,b,b,b,c,c,c,c,a,a,a,a,a,a,a,a,a,a, a, a,b,b,b,b,c,c}; - QCOMPARE(compositor.count(C::Default), lengthOf(indexes)); - for (int i = 0; i < lengthOf(indexes); ++i) { - it = compositor.find(C::Default, i); - QCOMPARE(it.list(), lists[i]); - if (lists[i]) QCOMPARE(it.modelIndex(), indexes[i]); - } - } { // Insert into range. - compositor.insert( - C::Default, 8, c, 0, 4, C::AppendFlag | C::PrependFlag | C::DefaultFlag); - const int indexes[] = {6,7,8,9,0,1,2,3,0,1,2,3,4,5,6,7,0,1,2,3,0,1,2,3,4,5,6,7,8,9,10,11,4,5,6,7,2,3}; - const int *lists[] = {c,c,c,c,b,b,b,b,c,c,c,c,b,b,b,b,c,c,c,c,a,a,a,a,a,a,a,a,a,a, a, a,b,b,b,b,c,c}; - QCOMPARE(compositor.count(C::Default), lengthOf(indexes)); - for (int i = 0; i < lengthOf(indexes); ++i) { - it = compositor.find(C::Default, i); - QCOMPARE(it.list(), lists[i]); - if (lists[i]) QCOMPARE(it.modelIndex(), indexes[i]); - } - } -} - -void tst_qquicklistcompositor::clearFlags_data() -{ - QTest::addColumn("ranges"); - QTest::addColumn("group"); - QTest::addColumn("index"); - QTest::addColumn("count"); - QTest::addColumn("flags"); - QTest::addColumn("expectedRemoves"); - QTest::addColumn("cacheIndexes"); - QTest::addColumn("cacheLists"); - QTest::addColumn("defaultIndexes"); - QTest::addColumn("defaultLists"); - QTest::addColumn("visibleIndexes"); - QTest::addColumn("visibleLists"); - QTest::addColumn("selectionIndexes"); - QTest::addColumn("selectionLists"); - - int listA; void *a = &listA; - - { static const int cacheIndexes[] = {0,1,2,3,4,5,6,7,8,9,10,11,0,0,0,0}; - static const void *cacheLists[] = {a,a,a,a,a,a,a,a,a,a, a, a,0,0,0,0}; - static const int defaultIndexes[] = {0,1,2,3,4,5,6,7,8,9,10,11,0,0,0,0}; - static const void *defaultLists[] = {a,a,a,a,a,a,a,a,a,a, a, a,0,0,0,0}; - static const int visibleIndexes[] = {0,1,2,3,4,5,6,7,8,9,10,11,0,0,0,0}; - static const void *visibleLists[] = {a,a,a,a,a,a,a,a,a,a, a, a,0,0,0,0}; - static const int selectionIndexes[] = {0,1,4,5,6,7,8,9,10,11,0,0,0,0}; - static const void *selectionLists[] = {a,a,a,a,a,a,a,a, a, a,0,0,0,0}; - QTest::newRow("Default, 2, 2, Selection") - << (RangeList() - << Range(a, 0, 12, int(C::AppendFlag | C::PrependFlag | SelectionFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag)) - << Range(0, 0, 4, int(SelectionFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag))) - << C::Default << 2 << 2 << int(SelectionFlag) - << (RemoveList() - << Remove(2, 2, 2, 2, 2, SelectionFlag | C::CacheFlag)) - << IndexArray(cacheIndexes) << ListArray(cacheLists) - << IndexArray(defaultIndexes) << ListArray(defaultLists) - << IndexArray(visibleIndexes) << ListArray(visibleLists) - << IndexArray(selectionIndexes) << ListArray(selectionLists); - } { static const int cacheIndexes[] = {0,1,2,3,4,5,6,7,8,9,10,11,0,0,0,0}; - static const void *cacheLists[] = {a,a,a,a,a,a,a,a,a,a, a, a,0,0,0,0}; - static const int defaultIndexes[] = {0,1,2,3,4,5,6,7,8,9,10,11,0,0,0,0}; - static const void *defaultLists[] = {a,a,a,a,a,a,a,a,a,a, a, a,0,0,0,0}; - static const int visibleIndexes[] = {0,2,3,5,6,7,8,9,10,11,0,0,0,0}; - static const void *visibleLists[] = {a,a,a,a,a,a,a,a, a, a,0,0,0,0}; - static const int selectionIndexes[] = {0,1,4,5,6,7,8,9,10,11,0,0,0,0}; - static const void *selectionLists[] = {a,a,a,a,a,a,a,a, a, a,0,0,0,0}; - QTest::newRow("Selection, 1, 2, Visible") - << (RangeList() - << Range(a, 0, 2, int(C::PrependFlag | SelectionFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag)) - << Range(a, 2, 2, int(C::PrependFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag)) - << Range(a, 4, 8, int(C::AppendFlag | C::PrependFlag | SelectionFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag)) - << Range(0, 0, 4, int(SelectionFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag))) - << Selection << 1 << 2 << int(VisibleFlag) - << (RemoveList() - << Remove(1, 1, 1, 1, 1, VisibleFlag | C::CacheFlag) - << Remove(2, 3, 4, 4, 1, VisibleFlag | C::CacheFlag)) - << IndexArray(cacheIndexes) << ListArray(cacheLists) - << IndexArray(defaultIndexes) << ListArray(defaultLists) - << IndexArray(visibleIndexes) << ListArray(visibleLists) - << IndexArray(selectionIndexes) << ListArray(selectionLists); - } { static const int cacheIndexes[] = {0,1,2,3,4,5,6,7,8,9,10,11,0,0,0,0}; - static const void *cacheLists[] = {a,a,a,a,a,a,a,a,a,a, a, a,0,0,0,0}; - static const int defaultIndexes[] = {0,1,2,3,4,5,6,7,8,9,10,11,0,0,0}; - static const void *defaultLists[] = {a,a,a,a,a,a,a,a,a,a, a, a,0,0,0}; - static const int visibleIndexes[] = {0,2,3,5,6,7,8,9,10,11,0,0,0}; - static const void *visibleLists[] = {a,a,a,a,a,a,a,a, a, a,0,0,0}; - static const int selectionIndexes[] = {0,1,4,5,6,7,8,9,10,11,0,0,0}; - static const void *selectionLists[] = {a,a,a,a,a,a,a,a, a, a,0,0,0}; - QTest::newRow("Default, 13, 1, Prepend | Selection | Visible | Default") - << (RangeList() - << Range(a, 0, 1, int(C::PrependFlag | SelectionFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag)) - << Range(a, 1, 1, int(C::PrependFlag | SelectionFlag | C::DefaultFlag | C::CacheFlag)) - << Range(a, 2, 2, int(C::PrependFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag)) - << Range(a, 4, 1, int(C::PrependFlag | SelectionFlag | C::DefaultFlag | C::CacheFlag)) - << Range(a, 5, 7, int(C::AppendFlag | C::PrependFlag | SelectionFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag)) - << Range(0, 0, 4, int(SelectionFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag))) - << C::Default << 13 << 1 << int(C::PrependFlag | SelectionFlag | VisibleFlag | C::DefaultFlag) - << (RemoveList() - << Remove(11, 11, 13, 13, 1, SelectionFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag)) - << IndexArray(cacheIndexes) << ListArray(cacheLists) - << IndexArray(defaultIndexes) << ListArray(defaultLists) - << IndexArray(visibleIndexes) << ListArray(visibleLists) - << IndexArray(selectionIndexes) << ListArray(selectionLists); - } { static const int cacheIndexes[] = {0,1,2,3,4,5,6,7,8,9,10,0}; - static const void *cacheLists[] = {a,a,a,a,a,a,a,a,a,a, a,0}; - static const int defaultIndexes[] = {0,1,2,3,4,5,6,7,8,9,10,11,0,0,0}; - static const void *defaultLists[] = {a,a,a,a,a,a,a,a,a,a, a, a,0,0,0}; - static const int visibleIndexes[] = {0,2,3,5,6,7,8,9,10,11,0,0,0}; - static const void *visibleLists[] = {a,a,a,a,a,a,a,a, a, a,0,0,0}; - static const int selectionIndexes[] = {0,1,4,5,6,7,8,9,10,11,0,0,0}; - static const void *selectionLists[] = {a,a,a,a,a,a,a,a, a, a,0,0,0}; - QTest::newRow("Cache, 11, 4, Cache") - << (RangeList() - << Range(a, 0, 1, int(C::PrependFlag | SelectionFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag)) - << Range(a, 1, 1, int(C::PrependFlag | SelectionFlag | C::DefaultFlag | C::CacheFlag)) - << Range(a, 2, 2, int(C::PrependFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag)) - << Range(a, 4, 1, int(C::PrependFlag | SelectionFlag | C::DefaultFlag | C::CacheFlag)) - << Range(a, 5, 7, int(C::AppendFlag | C::PrependFlag | SelectionFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag)) - << Range(0, 0, 1, int(C::CacheFlag)) - << Range(0, 0, 3, int(SelectionFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag))) - << C::Cache << 11 << 4 << int(C::CacheFlag) - << (RemoveList()) - << IndexArray(cacheIndexes) << ListArray(cacheLists) - << IndexArray(defaultIndexes) << ListArray(defaultLists) - << IndexArray(visibleIndexes) << ListArray(visibleLists) - << IndexArray(selectionIndexes) << ListArray(selectionLists); - } { static const int cacheIndexes[] = {0,1,2,3,4,5,6,7,8,9,10,0}; - static const void *cacheLists[] = {a,a,a,a,a,a,a,a,a,a, a,0}; - static const int defaultIndexes[] = {0,1,2,3,4,5,6,7,8,9,10,0}; - static const void *defaultLists[] = {a,a,a,a,a,a,a,a,a,a, a,0}; - static const int visibleIndexes[] = {0,2,3,5,6,7,8,9,10,0}; - static const void *visibleLists[] = {a,a,a,a,a,a,a,a, a,0}; - static const int selectionIndexes[] = {0,1,4,5,6,7,8,9,10,0}; - static const void *selectionLists[] = {a,a,a,a,a,a,a,a, a,0}; - QTest::newRow("Default, 11, 3, Default | Visible | Selection") - << (RangeList() - << Range(a, 0, 1, int(C::PrependFlag | SelectionFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag)) - << Range(a, 1, 1, int(C::PrependFlag | SelectionFlag | C::DefaultFlag | C::CacheFlag)) - << Range(a, 2, 2, int(C::PrependFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag)) - << Range(a, 4, 1, int(C::PrependFlag | SelectionFlag | C::DefaultFlag | C::CacheFlag)) - << Range(a, 5, 6, int(C::PrependFlag | SelectionFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag)) - << Range(a, 11, 1, int(C::AppendFlag | C::PrependFlag | SelectionFlag | VisibleFlag | C::DefaultFlag)) - << Range(0, 0, 2, int(SelectionFlag | VisibleFlag | C::DefaultFlag)) - << Range(0, 0, 1, int(SelectionFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag))) - << C::Default << 11 << 3 << int(C::DefaultFlag | VisibleFlag| SelectionFlag) - << (RemoveList() - << Remove(9, 9, 11, 11, 1, SelectionFlag | VisibleFlag | C::DefaultFlag) - << Remove(9, 9, 11, 11, 2, SelectionFlag | VisibleFlag | C::DefaultFlag)) - << IndexArray(cacheIndexes) << ListArray(cacheLists) - << IndexArray(defaultIndexes) << ListArray(defaultLists) - << IndexArray(visibleIndexes) << ListArray(visibleLists) - << IndexArray(selectionIndexes) << ListArray(selectionLists); - } -} - -void tst_qquicklistcompositor::clearFlags() -{ - QFETCH(RangeList, ranges); - QFETCH(C::Group, group); - QFETCH(int, index); - QFETCH(int, count); - QFETCH(int, flags); - QFETCH(RemoveList, expectedRemoves); - QFETCH(IndexArray, cacheIndexes); - QFETCH(ListArray, cacheLists); - QFETCH(IndexArray, defaultIndexes); - QFETCH(ListArray, defaultLists); - QFETCH(IndexArray, visibleIndexes); - QFETCH(ListArray, visibleLists); - QFETCH(IndexArray, selectionIndexes); - QFETCH(ListArray, selectionLists); - - QQuickListCompositor compositor; - compositor.setGroupCount(4); - compositor.setDefaultGroups(VisibleFlag | C::DefaultFlag); - - foreach (const Range &range, ranges) - compositor.append(range.list, range.index, range.count, range.flags); - - QVector removes; - compositor.clearFlags(group, index, count, flags, &removes); - - QCOMPARE(removes, expectedRemoves); - - QCOMPARE(compositor.count(C::Cache), cacheIndexes.count); - for (int i = 0; i < cacheIndexes.count; ++i) { - C::iterator it = compositor.find(C::Cache, i); - QCOMPARE(it->list, cacheLists[i]); - if (cacheLists[i]) - QCOMPARE(it.modelIndex(), cacheIndexes[i]); - } - QCOMPARE(compositor.count(C::Default), defaultIndexes.count); - for (int i = 0; i < defaultIndexes.count; ++i) { - C::iterator it = compositor.find(C::Default, i); - QCOMPARE(it->list, defaultLists[i]); - if (defaultLists[i]) - QCOMPARE(it.modelIndex(), defaultIndexes[i]); - } - QCOMPARE(compositor.count(Visible), visibleIndexes.count); - for (int i = 0; i < visibleIndexes.count; ++i) { - C::iterator it = compositor.find(Visible, i); - QCOMPARE(it->list, visibleLists[i]); - if (visibleLists[i]) - QCOMPARE(it.modelIndex(), visibleIndexes[i]); - } - QCOMPARE(compositor.count(Selection), selectionIndexes.count); - for (int i = 0; i < selectionIndexes.count; ++i) { - C::iterator it = compositor.find(Selection, i); - QCOMPARE(it->list, selectionLists[i]); - if (selectionLists[i]) - QCOMPARE(it.modelIndex(), selectionIndexes[i]); - } -} - -void tst_qquicklistcompositor::setFlags_data() -{ - QTest::addColumn("ranges"); - QTest::addColumn("group"); - QTest::addColumn("index"); - QTest::addColumn("count"); - QTest::addColumn("flags"); - QTest::addColumn("expectedInserts"); - QTest::addColumn("cacheIndexes"); - QTest::addColumn("cacheLists"); - QTest::addColumn("defaultIndexes"); - QTest::addColumn("defaultLists"); - QTest::addColumn("visibleIndexes"); - QTest::addColumn("visibleLists"); - QTest::addColumn("selectionIndexes"); - QTest::addColumn("selectionLists"); - - int listA; void *a = &listA; - - { static const int cacheIndexes[] = {0,0,0,0}; - static const void *cacheLists[] = {0,0,0,0}; - static const int defaultIndexes[] = {0,1,2,3,4,5,6,7,8,9,10,11}; - static const void *defaultLists[] = {a,a,a,a,a,a,a,a,a,a, a, a}; - QTest::newRow("Default, 2, 2, Default") - << (RangeList() - << Range(a, 0, 12, C::DefaultFlag) - << Range(0, 0, 4, C::CacheFlag)) - << C::Default << 2 << 2 << int(C::DefaultFlag) - << (InsertList()) - << IndexArray(cacheIndexes) << ListArray(cacheLists) - << IndexArray(defaultIndexes) << ListArray(defaultLists) - << IndexArray() << ListArray() - << IndexArray() << ListArray(); - } { static const int cacheIndexes[] = {0,0,0,0}; - static const void *cacheLists[] = {0,0,0,0}; - static const int defaultIndexes[] = {0,1,2,3,4,5,6,7,8,9,10,11}; - static const void *defaultLists[] = {a,a,a,a,a,a,a,a,a,a, a, a}; - static const int visibleIndexes[] = {2,3}; - static const void *visibleLists[] = {a,a}; - QTest::newRow("Default, 2, 2, Visible") - << (RangeList() - << Range(a, 0, 12, C::DefaultFlag) - << Range(0, 0, 4, C::CacheFlag)) - << C::Default << 2 << 2 << int(VisibleFlag) - << (InsertList() - << Insert(0, 0, 2, 0, 2, VisibleFlag)) - << IndexArray(cacheIndexes) << ListArray(cacheLists) - << IndexArray(defaultIndexes) << ListArray(defaultLists) - << IndexArray(visibleIndexes) << ListArray(visibleLists) - << IndexArray() << ListArray(); - } { static const int cacheIndexes[] = {3,6,0,0,0,0}; - static const void *cacheLists[] = {a,a,0,0,0,0}; - static const int defaultIndexes[] = {0,1,2,3,4,5,6,7,8,9,10,11}; - static const void *defaultLists[] = {a,a,a,a,a,a,a,a,a,a, a, a}; - static const int visibleIndexes[] = {2,3,6,7}; - static const void *visibleLists[] = {a,a,a,a}; - static const int selectionIndexes[] = {3,6}; - static const void *selectionLists[] = {a,a}; - QTest::newRow("Visible, 1, 2, Selection | Cache") - << (RangeList() - << Range(a, 0, 2, C::DefaultFlag) - << Range(a, 2, 2, VisibleFlag | C::DefaultFlag) - << Range(a, 4, 2, C::DefaultFlag) - << Range(a, 6, 2, VisibleFlag | C::DefaultFlag) - << Range(a, 8, 4, C::DefaultFlag) - << Range(0, 0, 4, C::CacheFlag)) - << Visible << 1 << 2 << int(SelectionFlag | C::CacheFlag) - << (InsertList() - << Insert(0, 1, 3, 0, 1, SelectionFlag | C::CacheFlag) - << Insert(1, 2, 6, 1, 1, SelectionFlag | C::CacheFlag)) - << IndexArray(cacheIndexes) << ListArray(cacheLists) - << IndexArray(defaultIndexes) << ListArray(defaultLists) - << IndexArray(visibleIndexes) << ListArray(visibleLists) - << IndexArray(selectionIndexes) << ListArray(selectionLists); - } { static const int cacheIndexes[] = {3,6,0,0,0,0}; - static const void *cacheLists[] = {a,a,0,0,0,0}; - static const int defaultIndexes[] = {0,1,2,3,4,5,6,7,8,9,10,11}; - static const void *defaultLists[] = {a,a,a,a,a,a,a,a,a,a, a, a}; - static const int visibleIndexes[] = {2,3,6,7,0}; - static const void *visibleLists[] = {a,a,a,a,0}; - static const int selectionIndexes[] = {3,6}; - static const void *selectionLists[] = {a,a}; - QTest::newRow("Cache, 3, 1, Visible") - << (RangeList() - << Range(a, 0, 2, C::DefaultFlag) - << Range(a, 2, 1, VisibleFlag | C::DefaultFlag) - << Range(a, 3, 1, SelectionFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag) - << Range(a, 4, 2, C::DefaultFlag) - << Range(a, 6, 1, SelectionFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag) - << Range(a, 7, 1, VisibleFlag | C::DefaultFlag) - << Range(a, 8, 4, C::DefaultFlag) - << Range(0, 0, 4, C::CacheFlag)) - << C::Cache << 3 << 1 << int(VisibleFlag) - << (InsertList() - << Insert(2, 4, 12, 3, 1, VisibleFlag | C::CacheFlag)) - << IndexArray(cacheIndexes) << ListArray(cacheLists) - << IndexArray(defaultIndexes) << ListArray(defaultLists) - << IndexArray(visibleIndexes) << ListArray(visibleLists) - << IndexArray(selectionIndexes) << ListArray(selectionLists); - } { static const int cacheIndexes[] = {0,1,2,3,4,5,6,7,8,9,10,11}; - static const void *cacheLists[] = {a,a,a,a,a,a,a,a,a,a, a, a}; - static const int defaultIndexes[] = {0,1,2,3,4,5,6,7,8,9,10,11}; - static const void *defaultLists[] = {a,a,a,a,a,a,a,a,a,a, a, a}; - static const int visibleIndexes[] = {0,1,3,4,5,6,7,8,9,10,11}; - static const void *visibleLists[] = {a,a,a,a,a,a,a,a,a, a, a}; - static const int selectionIndexes[] = {2,6,7,8,9}; - static const void *selectionLists[] = {a,a,a,a,a}; - QTest::newRow("Existing flag, sparse selection") - << (RangeList() - << Range(a, 0, 2, C::PrependFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag) - << Range(a, 2, 1, C::PrependFlag | SelectionFlag | C::DefaultFlag | C::CacheFlag) - << Range(a, 3, 3, C::PrependFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag) - << Range(a, 6, 4, C::PrependFlag | SelectionFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag) - << Range(a,10, 2, C::AppendFlag | C::PrependFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag)) - << C::Cache << 3 << 1 << int(VisibleFlag) - << InsertList() - << IndexArray(cacheIndexes) << ListArray(cacheLists) - << IndexArray(defaultIndexes) << ListArray(defaultLists) - << IndexArray(visibleIndexes) << ListArray(visibleLists) - << IndexArray(selectionIndexes) << ListArray(selectionLists); - } -} - -void tst_qquicklistcompositor::setFlags() -{ - QFETCH(RangeList, ranges); - QFETCH(C::Group, group); - QFETCH(int, index); - QFETCH(int, count); - QFETCH(int, flags); - QFETCH(InsertList, expectedInserts); - QFETCH(IndexArray, cacheIndexes); - QFETCH(ListArray, cacheLists); - QFETCH(IndexArray, defaultIndexes); - QFETCH(ListArray, defaultLists); - QFETCH(IndexArray, visibleIndexes); - QFETCH(ListArray, visibleLists); - QFETCH(IndexArray, selectionIndexes); - QFETCH(ListArray, selectionLists); - - QQuickListCompositor compositor; - compositor.setGroupCount(4); - compositor.setDefaultGroups(VisibleFlag | C::DefaultFlag); - - foreach (const Range &range, ranges) - compositor.append(range.list, range.index, range.count, range.flags); - - QVector inserts; - compositor.setFlags(group, index, count, flags, &inserts); - - QCOMPARE(inserts, expectedInserts); - - QCOMPARE(compositor.count(C::Cache), cacheIndexes.count); - for (int i = 0; i < cacheIndexes.count; ++i) { - C::iterator it = compositor.find(C::Cache, i); - QCOMPARE(it->list, cacheLists[i]); - if (cacheLists[i]) - QCOMPARE(it.modelIndex(), cacheIndexes[i]); - } - QCOMPARE(compositor.count(C::Default), defaultIndexes.count); - for (int i = 0; i < defaultIndexes.count; ++i) { - C::iterator it = compositor.find(C::Default, i); - QCOMPARE(it->list, defaultLists[i]); - if (defaultLists[i]) - QCOMPARE(it.modelIndex(), defaultIndexes[i]); - } - QCOMPARE(compositor.count(Visible), visibleIndexes.count); - for (int i = 0; i < visibleIndexes.count; ++i) { - C::iterator it = compositor.find(Visible, i); - QCOMPARE(it->list, visibleLists[i]); - if (visibleLists[i]) - QCOMPARE(it.modelIndex(), visibleIndexes[i]); - } - QCOMPARE(compositor.count(Selection), selectionIndexes.count); - for (int i = 0; i < selectionIndexes.count; ++i) { - C::iterator it = compositor.find(Selection, i); - QCOMPARE(it->list, selectionLists[i]); - if (selectionLists[i]) - QCOMPARE(it.modelIndex(), selectionIndexes[i]); - } -} - -void tst_qquicklistcompositor::move_data() -{ - QTest::addColumn("ranges"); - QTest::addColumn("fromGroup"); - QTest::addColumn("from"); - QTest::addColumn("toGroup"); - QTest::addColumn("to"); - QTest::addColumn("count"); - QTest::addColumn("expectedRemoves"); - QTest::addColumn("expectedInserts"); - QTest::addColumn("cacheIndexes"); - QTest::addColumn("cacheLists"); - QTest::addColumn("defaultIndexes"); - QTest::addColumn("defaultLists"); - QTest::addColumn("visibleIndexes"); - QTest::addColumn("visibleLists"); - QTest::addColumn("selectionIndexes"); - QTest::addColumn("selectionLists"); - - int listA; void *a = &listA; - int listB; void *b = &listB; - int listC; void *c = &listC; - - { static const int cacheIndexes[] = {0,0,0,0,2,3}; - static const void *cacheLists[] = {0,0,0,0,c,c}; - static const int defaultIndexes[] = {0,0,1,2,3,4,5,0,1,2,3,4,5,1,2,3,0,1,2,3,4,5}; - static const void *defaultLists[] = {0,a,a,a,a,a,a,b,b,b,b,b,b,0,0,0,c,c,c,c,c,c}; - QTest::newRow("15, 0, 1") - << (RangeList() - << Range(a, 0, 6, C::DefaultFlag) - << Range(b, 0, 6, C::AppendFlag | C::PrependFlag | C::DefaultFlag) - << Range(0, 0, 4, C::DefaultFlag | C::CacheFlag) - << Range(c, 0, 2, C::PrependFlag | C::DefaultFlag) - << Range(c, 2, 2, C::PrependFlag | C::DefaultFlag | C::CacheFlag) - << Range(c, 4, 2, C::AppendFlag | C::PrependFlag | C::DefaultFlag)) - << C::Default << 15 << C::Default << 0 << 1 - << (RemoveList() - << Remove(0, 0, 15, 3, 1, C::DefaultFlag | C::CacheFlag, 0)) - << (InsertList() - << Insert(0, 0, 0, 0, 1, C::DefaultFlag | C::CacheFlag, 0)) - << IndexArray(cacheIndexes) << ListArray(cacheLists) - << IndexArray(defaultIndexes) << ListArray(defaultLists) - << IndexArray() << ListArray() - << IndexArray() << ListArray(); - } { static const int cacheIndexes[] = {0,0,0,0,2,3}; - static const void *cacheLists[] = {0,0,0,0,c,c}; - static const int defaultIndexes[] = {0,1,0,1,2,3,4,5,0,1,2,3,4,5,2,3,0,1,2,3,4,5}; - static const void *defaultLists[] = {0,0,a,a,a,a,a,a,b,b,b,b,b,b,0,0,c,c,c,c,c,c}; - QTest::newRow("15, 1, 1") - << (RangeList() - << Range(0, 0, 1, C::DefaultFlag | C::CacheFlag) - << Range(a, 0, 6, C::DefaultFlag) - << Range(b, 0, 6, C::AppendFlag | C::PrependFlag | C::DefaultFlag) - << Range(0, 0, 3, C::DefaultFlag | C::CacheFlag) - << Range(c, 0, 2, C::PrependFlag | C::DefaultFlag) - << Range(c, 2, 2, C::PrependFlag | C::DefaultFlag | C::CacheFlag) - << Range(c, 4, 2, C::AppendFlag | C::PrependFlag | C::DefaultFlag)) - << C::Default << 15 << C::Default << 1 << 1 - << (RemoveList() - << Remove(0, 0, 15, 3, 1, C::DefaultFlag | C::CacheFlag, 0)) - << (InsertList() - << Insert(0, 0, 1, 1, 1, C::DefaultFlag | C::CacheFlag, 0)) - << IndexArray(cacheIndexes) << ListArray(cacheLists) - << IndexArray(defaultIndexes) << ListArray(defaultLists) - << IndexArray() << ListArray() - << IndexArray() << ListArray(); - } { static const int cacheIndexes[] = {0,0,0,0,2,3}; - static const void *cacheLists[] = {0,0,0,0,c,c}; - static const int defaultIndexes[] = {0,1,2,0,1,3,4,5,0,1,2,3,4,5,2,3,0,1,2,3,4,5}; - static const void *defaultLists[] = {a,a,a,0,0,a,a,a,b,b,b,b,b,b,0,0,c,c,c,c,c,c}; - QTest::newRow("0, 3, 2") - << (RangeList() - << Range(0, 0, 2, C::DefaultFlag | C::CacheFlag) - << Range(a, 0, 6, C::DefaultFlag) - << Range(b, 0, 6, C::AppendFlag | C::PrependFlag | C::DefaultFlag) - << Range(0, 0, 2, C::DefaultFlag | C::CacheFlag) - << Range(c, 0, 2, C::PrependFlag | C::DefaultFlag) - << Range(c, 2, 2, C::PrependFlag | C::DefaultFlag | C::CacheFlag) - << Range(c, 4, 2, C::AppendFlag | C::PrependFlag | C::DefaultFlag)) - << C::Default << 0 << C::Default << 3 << 2 - << (RemoveList() - << Remove(0, 0, 0, 0, 2, C::DefaultFlag | C::CacheFlag, 0)) - << (InsertList() - << Insert(0, 0, 3, 0, 2, C::DefaultFlag | C::CacheFlag, 0)) - << IndexArray(cacheIndexes) << ListArray(cacheLists) - << IndexArray(defaultIndexes) << ListArray(defaultLists) - << IndexArray() << ListArray() - << IndexArray() << ListArray(); - } { static const int cacheIndexes[] = {0,0,0,0,2,3}; - static const void *cacheLists[] = {0,0,0,0,c,c}; - static const int defaultIndexes[] = {0,5,0,1,2,3,4,5,0,1,0,1,2,2,3,3,4,1,2,3,4,5}; - static const void *defaultLists[] = {a,a,b,b,b,b,b,b,0,0,c,a,a,0,0,a,a,c,c,c,c,c}; - QTest::newRow("7, 1, 10") - << (RangeList() - << Range(a, 0, 3, C::DefaultFlag) - << Range(0, 0, 2, C::DefaultFlag | C::CacheFlag) - << Range(a, 3, 3, C::DefaultFlag) - << Range(b, 0, 6, C::AppendFlag | C::PrependFlag | C::DefaultFlag) - << Range(0, 0, 2, C::DefaultFlag | C::CacheFlag) - << Range(c, 0, 2, C::PrependFlag | C::DefaultFlag) - << Range(c, 2, 2, C::PrependFlag | C::DefaultFlag | C::CacheFlag) - << Range(c, 4, 2, C::AppendFlag | C::PrependFlag | C::DefaultFlag)) - << C::Default << 7 << C::Default << 1 << 10 - << (RemoveList() - << Remove(0, 0, 7, 2, 1, C::DefaultFlag, 0) - << Remove(0, 0, 7, 2, 6, C::DefaultFlag, 1) - << Remove(0, 0, 7, 2, 2, C::DefaultFlag | C::CacheFlag, 2) - << Remove(0, 0, 7, 2, 1, C::DefaultFlag, 3)) - << (InsertList() - << Insert(0, 0, 1, 0, 1, C::DefaultFlag, 0) - << Insert(0, 0, 2, 0, 6, C::DefaultFlag, 1) - << Insert(0, 0, 8, 0, 2, C::DefaultFlag | C::CacheFlag, 2) - << Insert(0, 0, 10, 2, 1, C::DefaultFlag, 3)) - << IndexArray(cacheIndexes) << ListArray(cacheLists) - << IndexArray(defaultIndexes) << ListArray(defaultLists) - << IndexArray() << ListArray() - << IndexArray() << ListArray(); - } { static const int cacheIndexes[] = {0,0,0,0,3,2}; - static const void *cacheLists[] = {0,0,0,0,c,c}; - static const int defaultIndexes[] = {0,5,0,1,2,3,4,5,0,1,0,1,2,2,3,3,4,3,4,5,1,2}; - static const void *defaultLists[] = {a,a,b,b,b,b,b,b,0,0,c,a,a,0,0,a,a,c,c,c,c,c}; - QTest::newRow("17, 20, 2") - << (RangeList() - << Range(a, 0, 1, C::DefaultFlag) - << Range(a, 5, 1, C::DefaultFlag) - << Range(b, 0, 6, C::DefaultFlag) - << Range(0, 0, 2, C::DefaultFlag | C::CacheFlag) - << Range(c, 0, 1, C::DefaultFlag) - << Range(a, 1, 2, C::DefaultFlag) - << Range(0, 0, 2, C::DefaultFlag | C::CacheFlag) - << Range(a, 3, 2, C::DefaultFlag) - << Range(b, 0, 6, C::AppendFlag | C::PrependFlag) - << Range(c, 0, 1, C::PrependFlag) - << Range(c, 1, 1, C::PrependFlag | C::DefaultFlag) - << Range(c, 2, 2, C::PrependFlag | C::DefaultFlag | C::CacheFlag) - << Range(c, 4, 2, C::AppendFlag | C::PrependFlag | C::DefaultFlag)) - << C::Default << 17 << C::Default << 20 << 2 - << (RemoveList() - << Remove(0, 0, 17, 4, 1, C::DefaultFlag, 0) - << Remove(0, 0, 17, 4, 1, C::DefaultFlag | C::CacheFlag, 1)) - << (InsertList() - << Insert(0, 0, 20, 5, 1, C::DefaultFlag, 0) - << Insert(0, 0, 21, 5, 1, C::DefaultFlag | C::CacheFlag, 1)) - << IndexArray(cacheIndexes) << ListArray(cacheLists) - << IndexArray(defaultIndexes) << ListArray(defaultLists) - << IndexArray() << ListArray() - << IndexArray() << ListArray(); - } { static const int cacheIndexes[] = {8,9,10,4,11,0,1,2,3,5,6,7}; - static const void *cacheLists[] = {a,a, a,a, a,a,a,a,a,a,a,a}; - static const int defaultIndexes[] = {8,9,10,4,11,0,1,2,3,5,6,7}; - static const void *defaultLists[] = {a,a, a,a, a,a,a,a,a,a,a,a}; - static const int visibleIndexes[] = {8,9,10,4,11,0,1,2,3,5,6,7}; - static const void *visibleLists[] = {a,a, a,a, a,a,a,a,a,a,a,a}; - QTest::newRow("3, 4, 5") - << (RangeList() - << Range(a, 8, 4, VisibleFlag | C::DefaultFlag | C::CacheFlag) - << Range(a, 0, 2, C::PrependFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag) - << Range(a, 2, 1, C::PrependFlag) - << Range(a, 2, 1, VisibleFlag | C::DefaultFlag | C::CacheFlag) - << Range(a, 3, 5, C::PrependFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag) - << Range(a, 8, 4, C::AppendFlag | C::PrependFlag)) - << C::Default << 3 << C::Default << 4 << 5 - << (RemoveList() - << Remove(0, 3, 3, 3, 1, VisibleFlag | C::DefaultFlag | C::CacheFlag, 0) - << Remove(0, 3, 3, 3, 2, VisibleFlag | C::DefaultFlag | C::CacheFlag, 1) - << Remove(0, 3, 3, 3, 1, VisibleFlag | C::DefaultFlag | C::CacheFlag, 2) - << Remove(0, 3, 3, 3, 1, VisibleFlag | C::DefaultFlag | C::CacheFlag, 3)) - << (InsertList() - << Insert(0, 4, 4, 4, 1, VisibleFlag | C::DefaultFlag | C::CacheFlag, 0) - << Insert(0, 5, 5, 5, 2, VisibleFlag | C::DefaultFlag | C::CacheFlag, 1) - << Insert(0, 7, 7, 7, 1, VisibleFlag | C::DefaultFlag | C::CacheFlag, 2) - << Insert(0, 8, 8, 8, 1, VisibleFlag | C::DefaultFlag | C::CacheFlag, 3)) - << IndexArray(cacheIndexes) << ListArray(cacheLists) - << IndexArray(defaultIndexes) << ListArray(defaultLists) - << IndexArray(visibleIndexes) << ListArray(visibleLists) - << IndexArray() << ListArray(); - } { static const int cacheIndexes[] = {0,1}; - static const void *cacheLists[] = {a,a}; - static const int defaultIndexes[] = {0,1}; - static const void *defaultLists[] = {a,a}; - QTest::newRow("0, 1, 1") - << (RangeList() - << Range(a, 0, 1, C::PrependFlag) - << Range(a, 1, 1, C::PrependFlag | C::DefaultFlag | C::CacheFlag) - << Range(a, 2, 0, C::AppendFlag | C::PrependFlag) - << Range(a, 0, 1, C::DefaultFlag | C::CacheFlag)) - << C::Default << 0 << C::Default << 1 << 1 - << (RemoveList() - << Remove(0, 0, 0, 0, 1, C::DefaultFlag | C::CacheFlag, 0)) - << (InsertList() - << Insert(0, 0, 1, 1, 1, C::DefaultFlag | C::CacheFlag, 0)) - << IndexArray(cacheIndexes) << ListArray(cacheLists) - << IndexArray(defaultIndexes) << ListArray(defaultLists) - << IndexArray() << ListArray() - << IndexArray() << ListArray(); - } { static const int cacheIndexes[] = {1,2,3,4,5,6,0,8,9}; - static const void *cacheLists[] = {a,a,a,a,a,a,a,a,a}; - static const int defaultIndexes[] = {1,2,3,4,5,6,0,8,9}; - static const void *defaultLists[] = {a,a,a,a,a,a,a,a,a}; - static const int visibleIndexes[] = {0,7,10}; - static const void *visibleLists[] = {a,a, a}; - QTest::newRow("0, 6, 3") - << (RangeList() - << Range(a, 0, 1, C::PrependFlag) - << Range(a, 1, 6, C::PrependFlag | C::DefaultFlag | C::CacheFlag) - << Range(a, 0, 1, VisibleFlag | C::DefaultFlag | C::CacheFlag) - << Range(a, 7, 1, VisibleFlag) - << Range(a,10, 1, VisibleFlag) - << Range(a, 7, 1, C::PrependFlag) - << Range(a, 8, 2, C::AppendFlag | C::PrependFlag | C::DefaultFlag | C::CacheFlag)) - << Visible << 0 << C::Default << 6 << 3 - << (RemoveList() - << Remove(0, 0, 6, 6, 1, VisibleFlag | C::DefaultFlag | C::CacheFlag, 0) - << Remove(0, 0, 6, 6, 1, VisibleFlag, 1) - << Remove(0, 0, 6, 6, 1, VisibleFlag, 2)) - << (InsertList() - << Insert(0, 0, 6, 6, 1, VisibleFlag | C::DefaultFlag | C::CacheFlag, 0) - << Insert(0, 1, 7, 7, 1, VisibleFlag, 1) - << Insert(0, 2, 7, 7, 1, VisibleFlag, 2)) - << IndexArray(cacheIndexes) << ListArray(cacheLists) - << IndexArray(defaultIndexes) << ListArray(defaultLists) - << IndexArray(visibleIndexes) << ListArray(visibleLists) - << IndexArray() << ListArray(); - } -} - -void tst_qquicklistcompositor::move() -{ - QFETCH(RangeList, ranges); - QFETCH(C::Group, fromGroup); - QFETCH(int, from); - QFETCH(C::Group, toGroup); - QFETCH(int, to); - QFETCH(int, count); - QFETCH(RemoveList, expectedRemoves); - QFETCH(InsertList, expectedInserts); - QFETCH(IndexArray, cacheIndexes); - QFETCH(ListArray, cacheLists); - QFETCH(IndexArray, defaultIndexes); - QFETCH(ListArray, defaultLists); - QFETCH(IndexArray, visibleIndexes); - QFETCH(ListArray, visibleLists); - QFETCH(IndexArray, selectionIndexes); - QFETCH(ListArray, selectionLists); - - QQuickListCompositor compositor; - compositor.setGroupCount(4); - compositor.setDefaultGroups(VisibleFlag | C::DefaultFlag); - - foreach (const Range &range, ranges) - compositor.append(range.list, range.index, range.count, range.flags); - - QVector removes; - QVector inserts; - compositor.move(fromGroup, from, toGroup, to, count, fromGroup, &removes, &inserts); - - QCOMPARE(removes, expectedRemoves); - QCOMPARE(inserts, expectedInserts); - - QCOMPARE(compositor.count(C::Cache), cacheIndexes.count); - for (int i = 0; i < cacheIndexes.count; ++i) { - C::iterator it = compositor.find(C::Cache, i); - QCOMPARE(it->list, cacheLists[i]); - if (cacheLists[i]) - QCOMPARE(it.modelIndex(), cacheIndexes[i]); - } - QCOMPARE(compositor.count(C::Default), defaultIndexes.count); - for (int i = 0; i < defaultIndexes.count; ++i) { - C::iterator it = compositor.find(C::Default, i); - QCOMPARE(it->list, defaultLists[i]); - if (defaultLists[i]) - QCOMPARE(it.modelIndex(), defaultIndexes[i]); - } - QCOMPARE(compositor.count(Visible), visibleIndexes.count); - for (int i = 0; i < visibleIndexes.count; ++i) { - C::iterator it = compositor.find(Visible, i); - QCOMPARE(it->list, visibleLists[i]); - if (visibleLists[i]) - QCOMPARE(it.modelIndex(), visibleIndexes[i]); - } - QCOMPARE(compositor.count(Selection), selectionIndexes.count); - for (int i = 0; i < selectionIndexes.count; ++i) { - C::iterator it = compositor.find(Selection, i); - QCOMPARE(it->list, selectionLists[i]); - if (selectionLists[i]) - QCOMPARE(it.modelIndex(), selectionIndexes[i]); - } -} - -void tst_qquicklistcompositor::moveFromEnd() -{ - int listA; void *a = &listA; - - QQuickListCompositor compositor; - compositor.append(a, 0, 1, C::AppendFlag | C::PrependFlag | C::DefaultFlag); - - // Moving an item anchors it to that position. - compositor.move(C::Default, 0, C::Default, 0, 1, C::Default); - - // The existing item is anchored at 0 so prepending an item to the source will append it here - QVector inserts; - compositor.listItemsInserted(a, 0, 1, &inserts); - - QCOMPARE(inserts.count(), 1); - QCOMPARE(inserts.at(0).index[1], 1); - QCOMPARE(inserts.at(0).count, 1); - - C::iterator it; - it = compositor.find(C::Default, 0); - QCOMPARE(it.modelIndex(), 1); - - it = compositor.find(C::Default, 1); - QCOMPARE(it.modelIndex(), 0); -} - -void tst_qquicklistcompositor::clear() -{ - QQuickListCompositor compositor; - compositor.setGroupCount(4); - compositor.setDefaultGroups(VisibleFlag | C::DefaultFlag); - - int listA; void *a = &listA; - int listB; void *b = &listB; - - compositor.append(a, 0, 8, C::AppendFlag | C::PrependFlag | VisibleFlag | C::DefaultFlag); - compositor.append(b, 4, 5, VisibleFlag | C::DefaultFlag); - compositor.append(0, 0, 3, VisibleFlag | C::DefaultFlag | C::CacheFlag); - - QCOMPARE(compositor.count(C::Default), 16); - QCOMPARE(compositor.count(Visible), 16); - QCOMPARE(compositor.count(C::Cache), 3); - - compositor.clear(); - QCOMPARE(compositor.count(C::Default), 0); - QCOMPARE(compositor.count(Visible), 0); - QCOMPARE(compositor.count(C::Cache), 0); -} - -void tst_qquicklistcompositor::listItemsInserted_data() -{ - QTest::addColumn("ranges"); - QTest::addColumn("list"); - QTest::addColumn("index"); - QTest::addColumn("count"); - QTest::addColumn("expectedInserts"); - QTest::addColumn("cacheIndexes"); - QTest::addColumn("defaultIndexes"); - QTest::addColumn("visibleIndexes"); - QTest::addColumn("selectionIndexes"); - - int listA; void *a = &listA; - int listB; void *b = &listB; - - { static const int defaultIndexes[] = {/*A*/0,1,5,6,/*B*/0,1,2,3,/*A*/2,3,4}; - QTest::newRow("A 10, 2") - << (RangeList() - << Range(a, 0, 2, C::PrependFlag | C::DefaultFlag) - << Range(a, 2, 3, C::PrependFlag) - << Range(a, 5, 2, C::AppendFlag | C::PrependFlag | C::DefaultFlag) - << Range(b, 0, 4, C::DefaultFlag) - << Range(a, 2, 3, C::DefaultFlag)) - << a << 10 << 2 - << InsertList() - << IndexArray() - << IndexArray(defaultIndexes) - << IndexArray() - << IndexArray(); - } { static const int defaultIndexes[] = {/*A*/0,1,5,6,/*B*/0,1,2,3,/*A*/2,3,4}; - QTest::newRow("B 10, 2") - << (RangeList() - << Range(a, 0, 2, C::PrependFlag | C::DefaultFlag) - << Range(a, 2, 3, C::PrependFlag) - << Range(a, 5, 2, C::AppendFlag | C::PrependFlag | C::DefaultFlag) - << Range(b, 0, 4, C::DefaultFlag) - << Range(a, 2, 3, C::DefaultFlag)) - << b << 10 << 2 - << InsertList() - << IndexArray() - << IndexArray(defaultIndexes) - << IndexArray() - << IndexArray(); - } { static const int defaultIndexes[] = {/*A*/0,1,2,3,7,8,/*B*/0,1,2,3,/*A*/4,5,6}; - static const int visibleIndexes[] = {/*A*/0,1}; - QTest::newRow("A 0, 2") - << (RangeList() - << Range(a, 0, 2, C::PrependFlag | C::DefaultFlag) - << Range(a, 2, 3, C::PrependFlag) - << Range(a, 5, 2, C::AppendFlag | C::PrependFlag | C::DefaultFlag) - << Range(b, 0, 4, C::DefaultFlag) - << Range(a, 2, 3, C::DefaultFlag)) - << a << 0 << 2 - << (InsertList() - << Insert(0, 0, 0, 0, 2, VisibleFlag | C::DefaultFlag)) - << IndexArray() - << IndexArray(defaultIndexes) - << IndexArray(visibleIndexes) - << IndexArray(); - } { static const int defaultIndexes[] = {/*A*/0,1,2,3,5,8,9,/*B*/0,1,2,3,/*A*/4,6,7}; - static const int visibleIndexes[] = {/*A*/0,1,5}; - QTest::newRow("A 5, 1") - << (RangeList() - << Range(a, 0, 2, C::PrependFlag | VisibleFlag | C::DefaultFlag) - << Range(a, 2, 2, C::PrependFlag | C::DefaultFlag) - << Range(a, 4, 3, C::PrependFlag) - << Range(a, 7, 2, C::AppendFlag | C::PrependFlag | C::DefaultFlag) - << Range(b, 0, 4, C::DefaultFlag) - << Range(a, 4, 3, C::DefaultFlag)) - << a << 5 << 1 - << (InsertList() - << Insert(0, 2, 4, 0, 1, VisibleFlag | C::DefaultFlag)) - << IndexArray() - << IndexArray(defaultIndexes) - << IndexArray(visibleIndexes) - << IndexArray(); - } { static const int defaultIndexes[] = {/*A*/0,1,2,3,5,8,9,10,11,/*B*/0,1,2,3,/*A*/4,6,7}; - static const int visibleIndexes[] = {/*A*/0,1,5,10,11}; - QTest::newRow("A 10, 2") - << (RangeList() - << Range(a, 0, 2, C::PrependFlag | VisibleFlag | C::DefaultFlag) - << Range(a, 2, 2, C::PrependFlag | C::DefaultFlag) - << Range(a, 4, 1, C::PrependFlag) - << Range(a, 5, 1, C::PrependFlag | VisibleFlag | C::DefaultFlag) - << Range(a, 6, 2, C::PrependFlag) - << Range(a, 8, 2, C::AppendFlag | C::PrependFlag | C::DefaultFlag) - << Range(b, 0, 4, C::DefaultFlag) - << Range(a, 4, 1, C::DefaultFlag) - << Range(a, 6, 2, C::DefaultFlag)) - << a << 10 << 2 - << (InsertList() - << Insert(0, 3, 7, 0, 2, VisibleFlag | C::DefaultFlag)) - << IndexArray() - << IndexArray(defaultIndexes) - << IndexArray(visibleIndexes) - << IndexArray(); - } { static const int cacheIndexes[] = {/*A*/0,1,-1,-1,-1,2,5,6,7,8,9}; - static const int defaultIndexes[] = {/*A*/0,1,2,3,4,5,6,7,8,9}; - static const int visibleIndexes[] = {/*A*/3,4}; - QTest::newRow("Insert after remove") - << (RangeList() - << Range(a, 0, 2, C::PrependFlag | C::DefaultFlag | C::CacheFlag) - << Range(a, 2, 3, C::CacheFlag) - << Range(a, 2, 6, C::AppendFlag | C::PrependFlag | C::DefaultFlag | C::CacheFlag)) - << a << 3 << 2 - << (InsertList() - << Insert(0, 0, 3, 6, 2, VisibleFlag | C::DefaultFlag)) - << IndexArray(cacheIndexes) - << IndexArray(defaultIndexes) - << IndexArray(visibleIndexes) - << IndexArray(); - } { static const int cacheIndexes[] = {/*A*/0,1,2,3,4}; - static const int defaultIndexes[] = {/*A*/0,1,2,3,4,5,6}; - static const int visibleIndexes[] = {/*A*/0,1,2,3,4,5,6}; - QTest::newRow("Consecutive appends") - << (RangeList() - << Range(a, 0, 5, C::PrependFlag | VisibleFlag | C::DefaultFlag | C::CacheFlag) - << Range(a, 5, 1, C::PrependFlag | VisibleFlag | C::DefaultFlag) - << Range(a, 6, 0, C::AppendFlag | VisibleFlag | C::PrependFlag | C::DefaultFlag | C::CacheFlag)) - << a << 6 << 1 - << (InsertList() - << Insert(0, 6, 6, 5, 1, VisibleFlag | C::DefaultFlag)) - << IndexArray(cacheIndexes) - << IndexArray(defaultIndexes) - << IndexArray(visibleIndexes) - << IndexArray(); - } -} - -void tst_qquicklistcompositor::listItemsInserted() -{ - QFETCH(RangeList, ranges); - QFETCH(void *, list); - QFETCH(int, index); - QFETCH(int, count); - QFETCH(InsertList, expectedInserts); - QFETCH(IndexArray, cacheIndexes); - QFETCH(IndexArray, defaultIndexes); - QFETCH(IndexArray, visibleIndexes); - QFETCH(IndexArray, selectionIndexes); - - QQuickListCompositor compositor; - compositor.setGroupCount(4); - compositor.setDefaultGroups(VisibleFlag | C::DefaultFlag); - - foreach (const Range &range, ranges) - compositor.append(range.list, range.index, range.count, range.flags); - - QVector inserts; - compositor.listItemsInserted(list, index, count, &inserts); - - QCOMPARE(inserts, expectedInserts); - - QCOMPARE(compositor.count(C::Cache), cacheIndexes.count); - for (int i = 0; i < cacheIndexes.count; ++i) { - if (cacheIndexes[i] != -1) { - QCOMPARE(compositor.find(C::Cache, i).modelIndex(), cacheIndexes[i]); - } - } - QCOMPARE(compositor.count(C::Default), defaultIndexes.count); - for (int i = 0; i < defaultIndexes.count; ++i) { - if (defaultIndexes[i] != -1) { - QCOMPARE(compositor.find(C::Default, i).modelIndex(), defaultIndexes[i]); - } - } - QCOMPARE(compositor.count(Visible), visibleIndexes.count); - for (int i = 0; i < visibleIndexes.count; ++i) { - if (visibleIndexes[i] != -1) { - QCOMPARE(compositor.find(Visible, i).modelIndex(), visibleIndexes[i]); - } - } - QCOMPARE(compositor.count(Selection), selectionIndexes.count); - for (int i = 0; i < selectionIndexes.count; ++i) { - if (selectionIndexes[i] != -1) { - QCOMPARE(compositor.find(Selection, i).modelIndex(), selectionIndexes[i]); - } - } -} - -void tst_qquicklistcompositor::listItemsRemoved_data() -{ - QTest::addColumn("ranges"); - QTest::addColumn("list"); - QTest::addColumn("index"); - QTest::addColumn("count"); - QTest::addColumn("expectedRemoves"); - QTest::addColumn("cacheIndexes"); - QTest::addColumn("defaultIndexes"); - QTest::addColumn("visibleIndexes"); - QTest::addColumn("selectionIndexes"); - - int listA; void *a = &listA; - int listB; void *b = &listB; - - { static const int defaultIndexes[] = {/*A*/0,1,5,6,/*B*/0,1,2,3,/*A*/2,3,4}; - QTest::newRow("12, 2") - << (RangeList() - << Range(a, 0, 2, C::PrependFlag | C::DefaultFlag) - << Range(a, 2, 3, C::PrependFlag) - << Range(a, 5, 2, C::AppendFlag | C::PrependFlag | C::DefaultFlag) - << Range(b, 0, 4, C::DefaultFlag) - << Range(a, 2, 3, C::DefaultFlag)) - << a << 12 << 2 - << RemoveList() - << IndexArray() - << IndexArray(defaultIndexes) - << IndexArray() - << IndexArray(); - } { static const int defaultIndexes[] = {/*A*/0,1,/*B*/0,1,2,3,/*A*/2,3}; - QTest::newRow("4, 3") - << (RangeList() - << Range(a, 0, 2, C::PrependFlag | C::DefaultFlag) - << Range(a, 2, 3, C::PrependFlag) - << Range(a, 5, 2, C::AppendFlag | C::PrependFlag | C::DefaultFlag) - << Range(b, 0, 4, C::DefaultFlag) - << Range(a, 2, 3, C::DefaultFlag)) - << a << 4 << 3 - << (RemoveList() - << Remove(0, 0, 2, 0, 2, C::DefaultFlag) - << Remove(0, 0, 8, 0, 1, C::DefaultFlag)) - << IndexArray() - << IndexArray(defaultIndexes) - << IndexArray() - << IndexArray(); - } { static const int cacheIndexes[] = {/*A*/0,1,-1,-1,-1,2,-1,-1,3,4,5}; - static const int defaultIndexes[] = {/*A*/0,1,2,3,4,5}; - QTest::newRow("Remove after remove") - << (RangeList() - << Range(a, 0, 2, C::PrependFlag | C::DefaultFlag | C::CacheFlag) - << Range(a, 2, 3, C::CacheFlag) - << Range(a, 2, 6, C::AppendFlag | C::PrependFlag | C::DefaultFlag | C::CacheFlag)) - << a << 3 << 2 - << (RemoveList() - << Remove(0, 0, 3, 6, 2, C::DefaultFlag | C::CacheFlag)) - << IndexArray(cacheIndexes) - << IndexArray(defaultIndexes) - << IndexArray() - << IndexArray(); - } { static const int cacheIndexes[] = {/*A*/-1,-1,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,1}; - static const int defaultIndexes[] = {/*A*/0,1,2,3,4,5,6}; - QTest::newRow("Sparse remove") - << (RangeList() - << Range(a, 0, 2, C::CacheFlag) - << Range(a, 0, 1, C::DefaultFlag | C::CacheFlag) - << Range(a, 0, 1, C::CacheFlag) - << Range(a, 1, 5, C::DefaultFlag | C::CacheFlag) - << Range(a, 0, 1, C::CacheFlag) - << Range(a, 6, 2, C::DefaultFlag | C::CacheFlag) - << Range(a, 0, 1, C::CacheFlag) - << Range(a, 8, 3, C::DefaultFlag | C::CacheFlag) - << Range(a, 0, 1, C::CacheFlag) - << Range(a, 11, 1, C::DefaultFlag | C::CacheFlag) - << Range(a, 12, 5, C::DefaultFlag)) - << a << 1 << 10 - << (RemoveList() - << Remove(0, 0, 1, 4, 5, C::DefaultFlag | C::CacheFlag) - << Remove(0, 0, 1,10, 2, C::DefaultFlag | C::CacheFlag) - << Remove(0, 0, 1,13, 3, C::DefaultFlag | C::CacheFlag)) - << IndexArray(cacheIndexes) - << IndexArray(defaultIndexes) - << IndexArray() - << IndexArray(); - } -} - -void tst_qquicklistcompositor::listItemsRemoved() -{ - QFETCH(RangeList, ranges); - QFETCH(void *, list); - QFETCH(int, index); - QFETCH(int, count); - QFETCH(RemoveList, expectedRemoves); - QFETCH(IndexArray, cacheIndexes); - QFETCH(IndexArray, defaultIndexes); - QFETCH(IndexArray, visibleIndexes); - QFETCH(IndexArray, selectionIndexes); - - QQuickListCompositor compositor; - compositor.setGroupCount(4); - compositor.setDefaultGroups(VisibleFlag | C::DefaultFlag); - - foreach (const Range &range, ranges) - compositor.append(range.list, range.index, range.count, range.flags); - - QVector removes; - compositor.listItemsRemoved(list, index, count, &removes); - - QCOMPARE(removes, expectedRemoves); - - QCOMPARE(compositor.count(C::Cache), cacheIndexes.count); - for (int i = 0; i < cacheIndexes.count; ++i) { - if (cacheIndexes[i] != -1) { - QCOMPARE(compositor.find(C::Cache, i).modelIndex(), cacheIndexes[i]); - } - } - QCOMPARE(compositor.count(C::Default), defaultIndexes.count); - for (int i = 0; i < defaultIndexes.count; ++i) { - if (defaultIndexes[i] != -1) { - QCOMPARE(compositor.find(C::Default, i).modelIndex(), defaultIndexes[i]); - } - } - QCOMPARE(compositor.count(Visible), visibleIndexes.count); - for (int i = 0; i < visibleIndexes.count; ++i) { - if (visibleIndexes[i] != -1) { - QCOMPARE(compositor.find(Visible, i).modelIndex(), visibleIndexes[i]); - } - } - QCOMPARE(compositor.count(Selection), selectionIndexes.count); - for (int i = 0; i < selectionIndexes.count; ++i) { - if (selectionIndexes[i] != -1) { - QCOMPARE(compositor.find(Selection, i).modelIndex(), selectionIndexes[i]); - } - } -} - -void tst_qquicklistcompositor::listItemsMoved_data() -{ - QTest::addColumn("ranges"); - QTest::addColumn("list"); - QTest::addColumn("from"); - QTest::addColumn("to"); - QTest::addColumn("count"); - QTest::addColumn("expectedRemoves"); - QTest::addColumn("expectedInserts"); - QTest::addColumn("cacheIndexes"); - QTest::addColumn("defaultIndexes"); - QTest::addColumn("visibleIndexes"); - QTest::addColumn("selectionIndexes"); - - int listA; void *a = &listA; - int listB; void *b = &listB; - - { static const int defaultIndexes[] = {/*A*/0,2,3,4,/*B*/0,1,2,3,/*A*/5,6,1}; - QTest::newRow("4, 1, 3") - << (RangeList() - << Range(a, 0, 2, C::PrependFlag | C::DefaultFlag) - << Range(a, 2, 3, C::PrependFlag) - << Range(a, 5, 2, C::AppendFlag | C::PrependFlag | C::DefaultFlag) - << Range(b, 0, 4, C::DefaultFlag) - << Range(a, 2, 3, C::DefaultFlag)) - << a << 4 << 1 << 3 - << (RemoveList() - << Remove(0, 0, 2, 0, 2, C::DefaultFlag, 0)) - << (InsertList() - << Insert(0, 0, 1, 0, 2, C::DefaultFlag, 0)) - << IndexArray() - << IndexArray(defaultIndexes) - << IndexArray() - << IndexArray(); - } { static const int defaultIndexes[] = {/*A*/1,2,3,6,/*B*/0,1,2,3,/*A*/4,5,0}; - QTest::newRow("0, 6, 1") - << (RangeList() - << Range(a, 0, 1, C::PrependFlag | C::DefaultFlag) - << Range(a, 1, 1, C::PrependFlag) - << Range(a, 2, 3, C::PrependFlag | C::DefaultFlag) - << Range(a, 5, 2, C::PrependFlag) - << Range(a, 7, 0, C::AppendFlag | C::PrependFlag | C::DefaultFlag) - << Range(b, 0, 4, C::DefaultFlag) - << Range(a, 5, 2, C::DefaultFlag) - << Range(a, 1, 1, C::DefaultFlag)) - << a << 0 << 6 << 1 - << (RemoveList() - << Remove(0, 0, 0, 0, 1, C::DefaultFlag, 0)) - << (InsertList() - << Insert(0, 0, 3, 0, 1, C::DefaultFlag, 0)) - << IndexArray() - << IndexArray(defaultIndexes) - << IndexArray() - << IndexArray(); - } { static const int cacheIndexes[] = {/*A*/0,1,3,4}; - static const int defaultIndexes[] = {/*A*/0,1,2,3,4,5,6,7}; - QTest::newRow("6, 2, 1") - << (RangeList() - << Range(a, 0, 4, C::PrependFlag | C::DefaultFlag | C::CacheFlag) - << Range(a, 4, 4, C::AppendFlag | C::PrependFlag | C::DefaultFlag)) - << a << 6 << 2 << 1 - << (RemoveList() - << Remove(0, 0, 6, 4, 1, C::DefaultFlag, 0)) - << (InsertList() - << Insert(0, 0, 2, 2, 1, C::DefaultFlag, 0)) - << IndexArray(cacheIndexes) - << IndexArray(defaultIndexes) - << IndexArray() - << IndexArray(); - } { static const int cacheIndexes[] = {/*A*/0,1,-1,-1,-1,2,3,4,5,6,7}; - static const int defaultIndexes[] = {/*A*/0,1,2,3,4,5,6,7}; - QTest::newRow("Move after remove") - << (RangeList() - << Range(a, 0, 2, C::PrependFlag | C::DefaultFlag | C::CacheFlag) - << Range(a, 2, 3, C::CacheFlag) - << Range(a, 2, 6, C::AppendFlag | C::PrependFlag | C::DefaultFlag | C::CacheFlag)) - << a << 4 << 2 << 2 - << (RemoveList() - << Remove(0, 0, 4, 7, 2, C::DefaultFlag | C::CacheFlag, 0)) - << (InsertList() - << Insert(0, 0, 2, 5, 2, C::DefaultFlag | C::CacheFlag, 0)) - << IndexArray(cacheIndexes) - << IndexArray(defaultIndexes) - << IndexArray() - << IndexArray(); - } { static const int cacheIndexes[] = {/*A*/0,1,5,6,7,8,9,10,11,12}; - static const int defaultIndexes[] = {/*A*/0,1,2,3,4,5,6,7,8,9,10,11,12}; - QTest::newRow("Move merge tail") - << (RangeList() - << Range(a, 0, 10, C::PrependFlag | C::DefaultFlag | C::CacheFlag) - << Range(a, 10, 3, C::PrependFlag | C::DefaultFlag) - << Range(a, 13, 0, C::AppendFlag | C::PrependFlag | C::DefaultFlag | C::CacheFlag)) - << a << 8 << 0 << 5 - << (RemoveList() - << Remove(0, 0, 8, 8, 2, C::DefaultFlag | C::CacheFlag, 0) - << Remove(0, 0, 8, 8, 3, C::DefaultFlag, 1)) - << (InsertList() - << Insert(0, 0, 0, 0, 2, C::DefaultFlag | C::CacheFlag, 0) - << Insert(0, 0, 2, 2, 3, C::DefaultFlag, 1)) - << IndexArray(cacheIndexes) - << IndexArray(defaultIndexes) - << IndexArray() - << IndexArray(); - } { static const int cacheIndexes[] = {/*A*/0,1,2,3}; - static const int defaultIndexes[] = {/*A*/0,1,2,3}; - static const int selectionIndexes[] = {/*A*/3}; - QTest::newRow("Move selection") - << (RangeList() - << Range(a, 0, 2, C::PrependFlag | C::DefaultFlag | C::CacheFlag) - << Range(a, 2, 1, C::PrependFlag | SelectionFlag | C::DefaultFlag | C::CacheFlag) - << Range(a, 3, 1, C::AppendFlag | C::PrependFlag | C::DefaultFlag | C::CacheFlag)) - << a << 2 << 3 << 1 - << (RemoveList() - << Remove(0, 0, 2, 2, 1, C::PrependFlag | SelectionFlag | C::DefaultFlag | C::CacheFlag, 0)) - << (InsertList() - << Insert(0, 0, 3, 3, 1, C::PrependFlag | SelectionFlag | C::DefaultFlag | C::CacheFlag, 0)) - << IndexArray(cacheIndexes) - << IndexArray(defaultIndexes) - << IndexArray() - << IndexArray(selectionIndexes); - } { static const int cacheIndexes[] = {/*A*/0,1,2,3,4,5,8,9}; - static const int defaultIndexes[] = {/*A*/0,1,2,3,4,5,6,7,8,9,10,11}; - QTest::newRow("move mixed cached items") - << (RangeList() - << Range(a, 0, 1, C::PrependFlag | C::DefaultFlag | C::CacheFlag) - << Range(a, 1, 2, C::PrependFlag | C::DefaultFlag) - << Range(a, 3, 7, C::PrependFlag | C::DefaultFlag | C::CacheFlag) - << Range(a, 10, 2, C::PrependFlag | C::DefaultFlag)) - << a << 1 << 6 << 3 - << (RemoveList() - << Remove(0, 0, 1, 1, 2, C::PrependFlag | C::DefaultFlag, 0) - << Remove(0, 0, 1, 1, 1, C::PrependFlag | C::DefaultFlag | C::CacheFlag, 1)) - << (InsertList() - << Insert(0, 0, 6, 6, 2, C::PrependFlag | C::DefaultFlag, 0) - << Insert(0, 0, 8, 6, 1, C::PrependFlag | C::DefaultFlag | C::CacheFlag, 1)) - << IndexArray(cacheIndexes) - << IndexArray(defaultIndexes) - << IndexArray() - << IndexArray(); - } -} - -void tst_qquicklistcompositor::listItemsMoved() -{ - QFETCH(RangeList, ranges); - QFETCH(void *, list); - QFETCH(int, from); - QFETCH(int, to); - QFETCH(int, count); - QFETCH(RemoveList, expectedRemoves); - QFETCH(InsertList, expectedInserts); - QFETCH(IndexArray, cacheIndexes); - QFETCH(IndexArray, defaultIndexes); - QFETCH(IndexArray, visibleIndexes); - QFETCH(IndexArray, selectionIndexes); - - QQuickListCompositor compositor; - compositor.setGroupCount(4); - compositor.setDefaultGroups(VisibleFlag | C::DefaultFlag); - - foreach (const Range &range, ranges) - compositor.append(range.list, range.index, range.count, range.flags); - - QVector removes; - QVector inserts; - compositor.listItemsMoved(list, from, to, count, &removes, &inserts); - - QCOMPARE(removes, expectedRemoves); - QCOMPARE(inserts, expectedInserts); - - QCOMPARE(compositor.count(C::Cache), cacheIndexes.count); - for (int i = 0; i < cacheIndexes.count; ++i) { - if (cacheIndexes[i] != -1) { - QCOMPARE(compositor.find(C::Cache, i).modelIndex(), cacheIndexes[i]); - } - } - QCOMPARE(compositor.count(C::Default), defaultIndexes.count); - for (int i = 0; i < defaultIndexes.count; ++i) { - if (defaultIndexes[i] != -1) { - QCOMPARE(compositor.find(C::Default, i).modelIndex(), defaultIndexes[i]); - } - } - QCOMPARE(compositor.count(Visible), visibleIndexes.count); - for (int i = 0; i < visibleIndexes.count; ++i) { - if (visibleIndexes[i] != -1) { - QCOMPARE(compositor.find(Visible, i).modelIndex(), visibleIndexes[i]); - } - } - QCOMPARE(compositor.count(Selection), selectionIndexes.count); - for (int i = 0; i < selectionIndexes.count; ++i) { - if (selectionIndexes[i] != -1) { - QCOMPARE(compositor.find(Selection, i).modelIndex(), selectionIndexes[i]); - } - } -} - -void tst_qquicklistcompositor::listItemsChanged_data() -{ - QTest::addColumn("ranges"); - QTest::addColumn("list"); - QTest::addColumn("index"); - QTest::addColumn("count"); - QTest::addColumn("expectedChanges"); - - int listA; void *a = &listA; - int listB; void *b = &listB; - - QTest::newRow("overlapping") - << (RangeList() - << Range(a, 0, 2, C::PrependFlag | C::DefaultFlag) - << Range(a, 2, 3, C::PrependFlag) - << Range(a, 5, 2, C::AppendFlag | C::PrependFlag | C::DefaultFlag) - << Range(b, 0, 4, C::DefaultFlag) - << Range(a, 2, 3, C::DefaultFlag)) - << a << 3 << 4 - << (ChangeList() - << Change(0, 0, 2, 0, 2, C::DefaultFlag) - << Change(0, 0, 9, 0, 2, C::DefaultFlag)); - QTest::newRow("Change after remove") - << (RangeList() - << Range(a, 0, 2, C::PrependFlag | C::DefaultFlag | C::CacheFlag) - << Range(a, 2, 3, C::CacheFlag) - << Range(a, 2, 6, C::AppendFlag | C::PrependFlag | C::DefaultFlag | C::CacheFlag)) - << a << 3 << 2 - << (ChangeList() - << Change(0, 0, 3, 6, 2, C::DefaultFlag | C::CacheFlag)); -} - -void tst_qquicklistcompositor::listItemsChanged() -{ - QFETCH(RangeList, ranges); - QFETCH(void *, list); - QFETCH(int, index); - QFETCH(int, count); - QFETCH(ChangeList, expectedChanges); - - QQuickListCompositor compositor; - compositor.setGroupCount(4); - compositor.setDefaultGroups(VisibleFlag | C::DefaultFlag); - - foreach (const Range &range, ranges) - compositor.append(range.list, range.index, range.count, range.flags); - - QVector changes; - compositor.listItemsChanged(list, index, count, &changes); - - QCOMPARE(changes, expectedChanges); -} - -void tst_qquicklistcompositor::compositorDebug() -{ - void *a = (void *)0xa0; - void *b = (void *)0xb0; - - QQuickListCompositor compositor; - compositor.setGroupCount(4); - compositor.setDefaultGroups(VisibleFlag | C::DefaultFlag); - compositor.append(a, 0, 2, C::PrependFlag | C::DefaultFlag); - compositor.append(a, 2, 3, C::PrependFlag); - compositor.append(a, 5, 2, C::AppendFlag | C::PrependFlag | C::DefaultFlag); - compositor.append(b, 0, 4, C::DefaultFlag); - compositor.append(a, 2, 3, C::DefaultFlag); - - QTest::ignoreMessage(QtDebugMsg, - "QQuickListCompositor(00110\n" - " 0 0 0 0 Range(0xa0 0 2 00P000000000D0\n" - " 0 0 2 0 Range(0xa0 2 3 00P00000000000\n" - " 0 0 2 0 Range(0xa0 5 2 0AP000000000D0\n" - " 0 0 4 0 Range(0xb0 0 4 000000000000D0\n" - " 0 0 8 0 Range(0xa0 2 3 000000000000D0)"); - qDebug() << compositor; - - compositor.setFlags(C::Default, 5, 2, SelectionFlag); - - QTest::ignoreMessage(QtDebugMsg, - "QQuickListCompositor(20110\n" - " 0 0 0 0 Range(0xa0 0 2 00P000000000D0\n" - " 0 0 2 0 Range(0xa0 2 3 00P00000000000\n" - " 0 0 2 0 Range(0xa0 5 2 0AP000000000D0\n" - " 0 0 4 0 Range(0xb0 0 1 000000000000D0\n" - " 0 0 5 0 Range(0xb0 1 2 000000000010D0\n" - " 2 0 7 0 Range(0xb0 3 1 000000000000D0\n" - " 2 0 8 0 Range(0xa0 2 3 000000000000D0)"); - qDebug() << compositor; -} - -void tst_qquicklistcompositor::changeDebug() -{ - void *a = (void *)0xa0; - void *b = (void *)0xb0; - - QQuickListCompositor compositor; - compositor.setGroupCount(4); - compositor.setDefaultGroups(VisibleFlag | C::DefaultFlag); - compositor.append(a, 0, 2, C::PrependFlag | C::DefaultFlag); - compositor.append(a, 2, 3, C::PrependFlag); - compositor.append(a, 5, 2, C::AppendFlag | C::PrependFlag | C::DefaultFlag); - compositor.append(b, 0, 1, C::DefaultFlag); - compositor.append(b, 1, 2, SelectionFlag | C::DefaultFlag); - compositor.append(b, 3, 1, C::DefaultFlag); - compositor.append(a, 2, 3, C::DefaultFlag); - - - QTest::ignoreMessage(QtDebugMsg, "Change(-1 2 000000010D0 2 0 7 0)"); - qDebug() << QQuickListCompositor::Change(compositor.find(C::Default, 7), 2, SelectionFlag | C::DefaultFlag); - QTest::ignoreMessage(QtDebugMsg, "Remove(9 4 000000000D0 10 0)"); - qDebug() << QQuickListCompositor::Remove(compositor.find(C::Default, 10), 4, C::DefaultFlag, 9); - QTest::ignoreMessage(QtDebugMsg, "Insert(9 4 000000000D0 3 0)"); - qDebug() << QQuickListCompositor::Insert(compositor.find(C::Default, 3), 4, C::DefaultFlag, 9); -} - -void tst_qquicklistcompositor::groupDebug() -{ - QTest::ignoreMessage(QtDebugMsg, "Default "); - qDebug() << C::Default; - QTest::ignoreMessage(QtDebugMsg, "Cache "); - qDebug() << C::Cache; - QTest::ignoreMessage(QtDebugMsg, "Group3 "); - qDebug() << Selection; -} - -QTEST_MAIN(tst_qquicklistcompositor) - -#include "tst_qquicklistcompositor.moc" - - diff --git a/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp b/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp index aee4e0d119..5c0bbb1e21 100644 --- a/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp +++ b/tests/auto/quick/qquickgridview/tst_qquickgridview.cpp @@ -51,7 +51,6 @@ #include #include #include -#include #include #include "../../shared/util.h" #include "../shared/viewtestutil.h" diff --git a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp index a1b2536b0b..9bcf9d6333 100644 --- a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp +++ b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp @@ -48,7 +48,7 @@ #include #include #include -#include +#include #include #include "../../shared/util.h" #include "../shared/viewtestutil.h" @@ -2767,7 +2767,7 @@ void tst_QQuickListView::itemList() QQuickItem *contentItem = listview->contentItem(); QTRY_VERIFY(contentItem != 0); - QQuickVisualItemModel *model = window->rootObject()->findChild("itemModel"); + QQmlObjectModel *model = window->rootObject()->findChild("itemModel"); QTRY_VERIFY(model != 0); QTRY_VERIFY(model->count() == 3); @@ -2808,7 +2808,7 @@ void tst_QQuickListView::itemListFlicker() QQuickItem *contentItem = listview->contentItem(); QTRY_VERIFY(contentItem != 0); - QQuickVisualItemModel *model = window->rootObject()->findChild("itemModel"); + QQmlObjectModel *model = window->rootObject()->findChild("itemModel"); QTRY_VERIFY(model != 0); QTRY_VERIFY(model->count() == 3); @@ -4605,7 +4605,7 @@ void tst_QQuickListView::rightToLeft() QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false); - QQuickVisualItemModel *model = window->rootObject()->findChild("itemModel"); + QQmlObjectModel *model = window->rootObject()->findChild("itemModel"); QTRY_VERIFY(model != 0); QTRY_VERIFY(model->count() == 3); diff --git a/tests/auto/quick/qquickvisualdatamodel/tst_qquickvisualdatamodel.cpp b/tests/auto/quick/qquickvisualdatamodel/tst_qquickvisualdatamodel.cpp index 74c557871f..f97b0f35a6 100644 --- a/tests/auto/quick/qquickvisualdatamodel/tst_qquickvisualdatamodel.cpp +++ b/tests/auto/quick/qquickvisualdatamodel/tst_qquickvisualdatamodel.cpp @@ -52,9 +52,9 @@ #include #include #include -#include +#include #include -#include +#include #include #include #include @@ -360,21 +360,21 @@ public: int indexCreated; public Q_SLOTS: - void initItem(int index, QQuickItem *item) + void initItem(int index, QObject *item) { - itemInitialized = item; + itemInitialized = qobject_cast(item); indexInitialized = index; } - void createdItem(int index, QQuickItem *item) + void createdItem(int index, QObject *item) { - itemCreated = item; + itemCreated = qobject_cast(item); indexCreated = index; } - void destroyingItem(QQuickItem *item) + void destroyingItem(QObject *item) { - itemDestroyed = item; + itemDestroyed = qobject_cast(item); } }; @@ -452,9 +452,9 @@ private: template void get_verify( const SingleRoleModel &model, - QQuickVisualDataModel *visualModel, - QQuickVisualDataGroup *visibleItems, - QQuickVisualDataGroup *selectedItems, + QQmlDelegateModel *visualModel, + QQmlDataGroup *visibleItems, + QQmlDataGroup *selectedItems, const int (&mIndex)[N], const int (&iIndex)[N], const int (&vIndex)[N], @@ -467,7 +467,7 @@ private: QQmlEngine engine; }; -Q_DECLARE_METATYPE(QQuickChangeSet) +Q_DECLARE_METATYPE(QQmlChangeSet) template static T evaluate(QObject *scope, const QString &expression) { @@ -489,7 +489,7 @@ template <> void evaluate(QObject *scope, const QString &expression) void tst_qquickvisualdatamodel::initTestCase() { QQmlDataTest::initTestCase(); - qRegisterMetaType(); + qRegisterMetaType(); qmlRegisterType("tst_qquickvisualdatamodel", 1, 0, "SingleRoleModel"); qmlRegisterType("tst_qquickvisualdatamodel", 1, 0, "DataObject"); @@ -517,7 +517,7 @@ void tst_qquickvisualdatamodel::rootIndex() engine.rootContext()->setContextProperty("myModel", &model); - QQuickVisualDataModel *obj = qobject_cast(c.create()); + QQmlDelegateModel *obj = qobject_cast(c.create()); QVERIFY(obj != 0); QMetaObject::invokeMethod(obj, "setRoot"); @@ -611,7 +611,7 @@ void tst_qquickvisualdatamodel::childChanged() QQuickItem *contentItem = listview->contentItem(); QVERIFY(contentItem != 0); - QQuickVisualDataModel *vdm = listview->findChild("visualModel"); + QQmlDelegateModel *vdm = listview->findChild("visualModel"); vdm->setRootIndex(QVariant::fromValue(model.indexFromItem(model.item(1,0)))); QCOMPARE(listview->count(), 1); @@ -922,7 +922,7 @@ void tst_qquickvisualdatamodel::noDelegate() QQuickListView *listview = qobject_cast(view.rootObject()); QVERIFY(listview != 0); - QQuickVisualDataModel *vdm = listview->findChild("visualModel"); + QQmlDelegateModel *vdm = listview->findChild("visualModel"); QVERIFY(vdm != 0); QCOMPARE(vdm->count(), 3); @@ -1052,15 +1052,15 @@ void tst_qquickvisualdatamodel::qaimRowsMoved() SingleRoleModel model(list); engine.rootContext()->setContextProperty("myModel", &model); - QQuickVisualDataModel *obj = qobject_cast(c.create()); + QQmlDelegateModel *obj = qobject_cast(c.create()); QVERIFY(obj != 0); - QSignalSpy spy(obj, SIGNAL(modelUpdated(QQuickChangeSet,bool))); + QSignalSpy spy(obj, SIGNAL(modelUpdated(QQmlChangeSet,bool))); model.emitMove(sourceFirst, sourceLast, destinationChild); QCOMPARE(spy.count(), 1); QCOMPARE(spy[0].count(), 2); - QQuickChangeSet changeSet = spy[0][0].value(); + QQmlChangeSet changeSet = spy[0][0].value(); QCOMPARE(changeSet.removes().count(), 1); QCOMPARE(changeSet.removes().at(0).index, expectFrom); QCOMPARE(changeSet.removes().at(0).count, expectCount); @@ -1115,11 +1115,11 @@ void tst_qquickvisualdatamodel::subtreeRowsMoved() QQmlComponent component(&engine, testFileUrl("visualdatamodel.qml")); QScopedPointer object(component.create()); - QQuickVisualDataModel *vdm = qobject_cast(object.data()); + QQmlDelegateModel *vdm = qobject_cast(object.data()); QVERIFY(vdm); - QSignalSpy spy(vdm, SIGNAL(modelUpdated(QQuickChangeSet,bool))); - QQuickChangeSet changeSet; + QSignalSpy spy(vdm, SIGNAL(modelUpdated(QQmlChangeSet,bool))); + QQmlChangeSet changeSet; QCOMPARE(vdm->count(), 4); @@ -1127,7 +1127,7 @@ void tst_qquickvisualdatamodel::subtreeRowsMoved() model.move(QModelIndex(), 1, model.index(0, 0), 3, 2); QCOMPARE(vdm->count(), 2); QCOMPARE(spy.count(), 1); - changeSet = spy.last().at(0).value(); + changeSet = spy.last().at(0).value(); QCOMPARE(changeSet.removes().count(), 1); QCOMPARE(changeSet.removes().at(0).index, 1); QCOMPARE(changeSet.removes().at(0).count, 2); @@ -1137,7 +1137,7 @@ void tst_qquickvisualdatamodel::subtreeRowsMoved() model.move(model.index(0, 0), 4, QModelIndex(), 2, 1); QCOMPARE(vdm->count(), 3); QCOMPARE(spy.count(), 2); - changeSet = spy.last().at(0).value(); + changeSet = spy.last().at(0).value(); QCOMPARE(changeSet.removes().count(), 0); QCOMPARE(changeSet.inserts().count(), 1); QCOMPARE(changeSet.inserts().at(0).index, 2); @@ -1147,11 +1147,11 @@ void tst_qquickvisualdatamodel::subtreeRowsMoved() QCOMPARE(vdm->rootIndex().value(), model.index(2, 0)); QCOMPARE(vdm->count(), 3); QCOMPARE(spy.count(), 4); - changeSet = spy.at(2).at(0).value(); + changeSet = spy.at(2).at(0).value(); QCOMPARE(changeSet.removes().count(), 1); QCOMPARE(changeSet.removes().at(0).index, 0); QCOMPARE(changeSet.removes().at(0).count, 3); - changeSet = spy.last().at(0).value(); + changeSet = spy.last().at(0).value(); QCOMPARE(changeSet.inserts().count(), 1); QCOMPARE(changeSet.inserts().at(0).index, 0); QCOMPARE(changeSet.inserts().at(0).count, 3); @@ -1182,7 +1182,7 @@ void tst_qquickvisualdatamodel::subtreeRowsMoved() QCOMPARE(vdm->rootIndex().value(), QModelIndex()); QCOMPARE(vdm->count(), 0); QCOMPARE(spy.count(), 5); - changeSet = spy.last().at(0).value(); + changeSet = spy.last().at(0).value(); QCOMPARE(changeSet.removes().count(), 1); QCOMPARE(changeSet.removes().at(0).index, 0); QCOMPARE(changeSet.removes().at(0).count, 3); @@ -1192,7 +1192,7 @@ void tst_qquickvisualdatamodel::subtreeRowsMoved() QCOMPARE(vdm->rootIndex().value(), QModelIndex()); QCOMPARE(vdm->count(), 2); QCOMPARE(spy.count(), 6); - changeSet = spy.last().at(0).value(); + changeSet = spy.last().at(0).value(); QCOMPARE(changeSet.removes().count(), 0); QCOMPARE(changeSet.inserts().count(), 1); QCOMPARE(changeSet.inserts().at(0).index, 0); @@ -1211,17 +1211,17 @@ void tst_qquickvisualdatamodel::watchedRoles() QQmlComponent component(&engine, testFileUrl("visualdatamodel.qml")); QScopedPointer object(component.create()); - QQuickVisualDataModel *vdm = qobject_cast(object.data()); + QQmlDelegateModel *vdm = qobject_cast(object.data()); QVERIFY(vdm); // VisualDataModel doesn't initialize model data until the first item is requested. - QQuickItem *item = vdm->item(0); + QQuickItem *item = qobject_cast(vdm->object(0)); QVERIFY(item); vdm->release(item); QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // Ensure released items are deleted before test exits. - QSignalSpy spy(vdm, SIGNAL(modelUpdated(QQuickChangeSet,bool))); - QQuickChangeSet changeSet; + QSignalSpy spy(vdm, SIGNAL(modelUpdated(QQmlChangeSet,bool))); + QQmlChangeSet changeSet; QCOMPARE(vdm->count(), 30); @@ -1238,13 +1238,13 @@ void tst_qquickvisualdatamodel::watchedRoles() emit model.dataChanged(model.index(0), model.index(4)); QCOMPARE(spy.count(), 1); - changeSet = spy.last().at(0).value(); + changeSet = spy.last().at(0).value(); QCOMPARE(changeSet.changes().at(0).index, 0); QCOMPARE(changeSet.changes().at(0).count, 5); emit model.dataChanged(model.index(1), model.index(6), QVector() << QaimModel::Name); QCOMPARE(spy.count(), 2); - changeSet = spy.last().at(0).value(); + changeSet = spy.last().at(0).value(); QCOMPARE(changeSet.changes().at(0).index, 1); QCOMPARE(changeSet.changes().at(0).count, 6); @@ -1255,7 +1255,7 @@ void tst_qquickvisualdatamodel::watchedRoles() emit model.dataChanged(model.index(0), model.index(4)); QCOMPARE(spy.count(), 3); - changeSet = spy.last().at(0).value(); + changeSet = spy.last().at(0).value(); QCOMPARE(changeSet.changes().at(0).index, 0); QCOMPARE(changeSet.changes().at(0).count, 5); @@ -1264,7 +1264,7 @@ void tst_qquickvisualdatamodel::watchedRoles() emit model.dataChanged(model.index(8), model.index(8), QVector() << QaimModel::Number); QCOMPARE(spy.count(), 4); - changeSet = spy.last().at(0).value(); + changeSet = spy.last().at(0).value(); QCOMPARE(changeSet.changes().at(0).index, 8); QCOMPARE(changeSet.changes().at(0).count, 1); @@ -1272,19 +1272,19 @@ void tst_qquickvisualdatamodel::watchedRoles() emit model.dataChanged(model.index(0), model.index(4)); QCOMPARE(spy.count(), 5); - changeSet = spy.last().at(0).value(); + changeSet = spy.last().at(0).value(); QCOMPARE(changeSet.changes().at(0).index, 0); QCOMPARE(changeSet.changes().at(0).count, 5); emit model.dataChanged(model.index(1), model.index(6), QVector() << QaimModel::Name); QCOMPARE(spy.count(), 6); - changeSet = spy.last().at(0).value(); + changeSet = spy.last().at(0).value(); QCOMPARE(changeSet.changes().at(0).index, 1); QCOMPARE(changeSet.changes().at(0).count, 6); emit model.dataChanged(model.index(8), model.index(8), QVector() << QaimModel::Number); QCOMPARE(spy.count(), 7); - changeSet = spy.last().at(0).value(); + changeSet = spy.last().at(0).value(); QCOMPARE(changeSet.changes().at(0).index, 8); QCOMPARE(changeSet.changes().at(0).count, 1); } @@ -1301,29 +1301,29 @@ void tst_qquickvisualdatamodel::hasModelChildren() QQmlComponent component(&engine, testFileUrl("visualdatamodel.qml")); QScopedPointer object(component.create()); - QQuickVisualDataModel *vdm = qobject_cast(object.data()); + QQmlDelegateModel *vdm = qobject_cast(object.data()); QVERIFY(vdm); QCOMPARE(vdm->count(), 4); QQuickItem *item = 0; - item = vdm->item(0); + item = qobject_cast(vdm->object(0)); QVERIFY(item); QCOMPARE(item->property("modelChildren").toBool(), true); vdm->release(item); - item = vdm->item(1); + item = qobject_cast(vdm->object(1)); QVERIFY(item); QCOMPARE(item->property("modelChildren").toBool(), false); vdm->release(item); - item = vdm->item(2); + item = qobject_cast(vdm->object(2)); QVERIFY(item); QCOMPARE(item->property("modelChildren").toBool(), true); vdm->release(item); - item = vdm->item(3); + item = qobject_cast(vdm->object(3)); QVERIFY(item); QCOMPARE(item->property("modelChildren").toBool(), false); vdm->release(item); @@ -1351,14 +1351,14 @@ void tst_qquickvisualdatamodel::setValue() QQmlComponent component(&engine, testFileUrl("visualdatamodel.qml")); QScopedPointer object(component.create()); - QQuickVisualDataModel *vdm = qobject_cast(object.data()); + QQmlDelegateModel *vdm = qobject_cast(object.data()); QVERIFY(vdm); QCOMPARE(vdm->count(), 3); QQuickItem *item = 0; - item = vdm->item(0); + item = qobject_cast(vdm->object(0)); QVERIFY(item); QCOMPARE(evaluate(item, "display"), QString("Row 1 Item")); evaluate(item, "display = 'Changed Item 1'"); @@ -1412,7 +1412,7 @@ void tst_qquickvisualdatamodel::remove() QQuickItem *contentItem = listview->contentItem(); QVERIFY(contentItem != 0); - QQuickVisualDataModel *visualModel = qobject_cast(qvariant_cast(listview->model())); + QQmlDelegateModel *visualModel = qobject_cast(qvariant_cast(listview->model())); QVERIFY(visualModel); { @@ -1521,7 +1521,7 @@ void tst_qquickvisualdatamodel::move() QQuickItem *contentItem = listview->contentItem(); QVERIFY(contentItem != 0); - QQuickVisualDataModel *visualModel = qobject_cast(qvariant_cast(listview->model())); + QQmlDelegateModel *visualModel = qobject_cast(qvariant_cast(listview->model())); QVERIFY(visualModel); { @@ -1710,13 +1710,13 @@ void tst_qquickvisualdatamodel::groups() QQuickItem *contentItem = listview->contentItem(); QVERIFY(contentItem != 0); - QQuickVisualDataModel *visualModel = listview->findChild("visualModel"); + QQmlDelegateModel *visualModel = listview->findChild("visualModel"); QVERIFY(visualModel); - QQuickVisualDataGroup *visibleItems = listview->findChild("visibleItems"); + QQmlDataGroup *visibleItems = listview->findChild("visibleItems"); QVERIFY(visibleItems); - QQuickVisualDataGroup *selectedItems = listview->findChild("selectedItems"); + QQmlDataGroup *selectedItems = listview->findChild("selectedItems"); QVERIFY(selectedItems); const bool f = false; @@ -1939,9 +1939,9 @@ void tst_qquickvisualdatamodel::groups() template void tst_qquickvisualdatamodel::get_verify( const SingleRoleModel &model, - QQuickVisualDataModel *visualModel, - QQuickVisualDataGroup *visibleItems, - QQuickVisualDataGroup *selectedItems, + QQmlDelegateModel *visualModel, + QQmlDataGroup *visibleItems, + QQmlDataGroup *selectedItems, const int (&mIndex)[N], const int (&iIndex)[N], const int (&vIndex)[N], @@ -2030,13 +2030,13 @@ void tst_qquickvisualdatamodel::get() QQuickItem *contentItem = listview->contentItem(); QVERIFY(contentItem != 0); - QQuickVisualDataModel *visualModel = qobject_cast(qvariant_cast(listview->model())); + QQmlDelegateModel *visualModel = qobject_cast(qvariant_cast(listview->model())); QVERIFY(visualModel); - QQuickVisualDataGroup *visibleItems = visualModel->findChild("visibleItems"); + QQmlDataGroup *visibleItems = visualModel->findChild("visibleItems"); QVERIFY(visibleItems); - QQuickVisualDataGroup *selectedItems = visualModel->findChild("selectedItems"); + QQmlDataGroup *selectedItems = visualModel->findChild("selectedItems"); QVERIFY(selectedItems); QV8Engine *v8Engine = QQmlEnginePrivate::getV8Engine(ctxt->engine()); @@ -2154,7 +2154,7 @@ void tst_qquickvisualdatamodel::get() void tst_qquickvisualdatamodel::invalidGroups() { QUrl source = testFileUrl("groups-invalid.qml"); - QTest::ignoreMessage(QtWarningMsg, (source.toString() + ":12:9: QML VisualDataGroup: " + QQuickVisualDataGroup::tr("Group names must start with a lower case letter")).toUtf8()); + QTest::ignoreMessage(QtWarningMsg, (source.toString() + ":12:9: QML VisualDataGroup: " + QQmlDataGroup::tr("Group names must start with a lower case letter")).toUtf8()); QQmlComponent component(&engine, source); QScopedPointer object(component.create()); @@ -2326,7 +2326,7 @@ void tst_qquickvisualdatamodel::create() QQuickItem *contentItem = listview->contentItem(); QVERIFY(contentItem != 0); - QQuickVisualDataModel *visualModel = qobject_cast(qvariant_cast(listview->model())); + QQmlDelegateModel *visualModel = qobject_cast(qvariant_cast(listview->model())); QVERIFY(visualModel); QCOMPARE(listview->count(), 20); @@ -2440,7 +2440,7 @@ void tst_qquickvisualdatamodel::incompleteModel() QScopedPointer object(component.beginCreate(engine.rootContext())); - QQuickVisualDataModel *model = qobject_cast(object.data()); + QQmlDelegateModel *model = qobject_cast(object.data()); QVERIFY(model); QSignalSpy itemsSpy(model->items(), SIGNAL(countChanged())); @@ -3665,67 +3665,67 @@ void tst_qquickvisualdatamodel::warnings_data() QTest::newRow("insert < 0") << testFileUrl("listmodelproperties.qml") << QString("items.insert(-2, {\"number\": \"eight\"})") - << (": QML VisualDataGroup: " + QQuickVisualDataGroup::tr("insert: index out of range")) + << (": QML VisualDataGroup: " + QQmlDataGroup::tr("insert: index out of range")) << 4; QTest::newRow("insert > length") << testFileUrl("listmodelproperties.qml") << QString("items.insert(8, {\"number\": \"eight\"})") - << (": QML VisualDataGroup: " + QQuickVisualDataGroup::tr("insert: index out of range")) + << (": QML VisualDataGroup: " + QQmlDataGroup::tr("insert: index out of range")) << 4; QTest::newRow("create < 0") << testFileUrl("listmodelproperties.qml") << QString("items.create(-2, {\"number\": \"eight\"})") - << (": QML VisualDataGroup: " + QQuickVisualDataGroup::tr("create: index out of range")) + << (": QML VisualDataGroup: " + QQmlDataGroup::tr("create: index out of range")) << 4; QTest::newRow("create > length") << testFileUrl("listmodelproperties.qml") << QString("items.create(8, {\"number\": \"eight\"})") - << (": QML VisualDataGroup: " + QQuickVisualDataGroup::tr("create: index out of range")) + << (": QML VisualDataGroup: " + QQmlDataGroup::tr("create: index out of range")) << 4; QTest::newRow("resolve from < 0") << testFileUrl("listmodelproperties.qml") << QString("items.resolve(-2, 3)") - << (": QML VisualDataGroup: " + QQuickVisualDataGroup::tr("resolve: from index out of range")) + << (": QML VisualDataGroup: " + QQmlDataGroup::tr("resolve: from index out of range")) << 4; QTest::newRow("resolve from > length") << testFileUrl("listmodelproperties.qml") << QString("items.resolve(8, 3)") - << (": QML VisualDataGroup: " + QQuickVisualDataGroup::tr("resolve: from index out of range")) + << (": QML VisualDataGroup: " + QQmlDataGroup::tr("resolve: from index out of range")) << 4; QTest::newRow("resolve to < 0") << testFileUrl("listmodelproperties.qml") << QString("items.resolve(3, -2)") - << (": QML VisualDataGroup: " + QQuickVisualDataGroup::tr("resolve: to index out of range")) + << (": QML VisualDataGroup: " + QQmlDataGroup::tr("resolve: to index out of range")) << 4; QTest::newRow("resolve to > length") << testFileUrl("listmodelproperties.qml") << QString("items.resolve(3, 8)") - << (": QML VisualDataGroup: " + QQuickVisualDataGroup::tr("resolve: to index out of range")) + << (": QML VisualDataGroup: " + QQmlDataGroup::tr("resolve: to index out of range")) << 4; QTest::newRow("resolve from invalid index") << testFileUrl("listmodelproperties.qml") << QString("items.resolve(\"two\", 3)") - << (": QML VisualDataGroup: " + QQuickVisualDataGroup::tr("resolve: from index invalid")) + << (": QML VisualDataGroup: " + QQmlDataGroup::tr("resolve: from index invalid")) << 4; QTest::newRow("resolve to invalid index") << testFileUrl("listmodelproperties.qml") << QString("items.resolve(3, \"two\")") - << (": QML VisualDataGroup: " + QQuickVisualDataGroup::tr("resolve: to index invalid")) + << (": QML VisualDataGroup: " + QQmlDataGroup::tr("resolve: to index invalid")) << 4; QTest::newRow("resolve already resolved item") << testFileUrl("listmodelproperties.qml") << QString("items.resolve(3, 2)") - << (": QML VisualDataGroup: " + QQuickVisualDataGroup::tr("resolve: from is not an unresolved item")) + << (": QML VisualDataGroup: " + QQmlDataGroup::tr("resolve: from is not an unresolved item")) << 4; QTest::newRow("resolve already resolved item") @@ -3733,193 +3733,193 @@ void tst_qquickvisualdatamodel::warnings_data() << QString("{ items.insert(0, {\"number\": \"eight\"});" "items.insert(1, {\"number\": \"seven\"});" "items.resolve(0, 1)}") - << (": QML VisualDataGroup: " + QQuickVisualDataGroup::tr("resolve: to is not a model item")) + << (": QML VisualDataGroup: " + QQmlDataGroup::tr("resolve: to is not a model item")) << 6; QTest::newRow("remove index < 0") << testFileUrl("listmodelproperties.qml") << QString("items.remove(-2, 1)") - << (": QML VisualDataGroup: " + QQuickVisualDataGroup::tr("remove: index out of range")) + << (": QML VisualDataGroup: " + QQmlDataGroup::tr("remove: index out of range")) << 4; QTest::newRow("remove index == length") << testFileUrl("listmodelproperties.qml") << QString("items.remove(4, 1)") - << (": QML VisualDataGroup: " + QQuickVisualDataGroup::tr("remove: index out of range")) + << (": QML VisualDataGroup: " + QQmlDataGroup::tr("remove: index out of range")) << 4; QTest::newRow("remove index > length") << testFileUrl("listmodelproperties.qml") << QString("items.remove(9, 1)") - << (": QML VisualDataGroup: " + QQuickVisualDataGroup::tr("remove: index out of range")) + << (": QML VisualDataGroup: " + QQmlDataGroup::tr("remove: index out of range")) << 4; QTest::newRow("remove invalid index") << testFileUrl("listmodelproperties.qml") << QString("items.remove(\"nine\", 1)") - << (": QML VisualDataGroup: " + QQuickVisualDataGroup::tr("remove: invalid index")) + << (": QML VisualDataGroup: " + QQmlDataGroup::tr("remove: invalid index")) << 4; QTest::newRow("remove count < 0") << testFileUrl("listmodelproperties.qml") << QString("items.remove(1, -2)") - << (": QML VisualDataGroup: " + QQuickVisualDataGroup::tr("remove: invalid count")) + << (": QML VisualDataGroup: " + QQmlDataGroup::tr("remove: invalid count")) << 4; QTest::newRow("remove index + count > length") << testFileUrl("listmodelproperties.qml") << QString("items.remove(2, 4, \"selected\")") - << (": QML VisualDataGroup: " + QQuickVisualDataGroup::tr("remove: invalid count")) + << (": QML VisualDataGroup: " + QQmlDataGroup::tr("remove: invalid count")) << 4; QTest::newRow("addGroups index < 0") << testFileUrl("listmodelproperties.qml") << QString("items.addGroups(-2, 1, \"selected\")") - << (": QML VisualDataGroup: " + QQuickVisualDataGroup::tr("addGroups: index out of range")) + << (": QML VisualDataGroup: " + QQmlDataGroup::tr("addGroups: index out of range")) << 4; QTest::newRow("addGroups index == length") << testFileUrl("listmodelproperties.qml") << QString("items.addGroups(4, 1, \"selected\")") - << (": QML VisualDataGroup: " + QQuickVisualDataGroup::tr("addGroups: index out of range")) + << (": QML VisualDataGroup: " + QQmlDataGroup::tr("addGroups: index out of range")) << 4; QTest::newRow("addGroups index > length") << testFileUrl("listmodelproperties.qml") << QString("items.addGroups(9, 1, \"selected\")") - << (": QML VisualDataGroup: " + QQuickVisualDataGroup::tr("addGroups: index out of range")) + << (": QML VisualDataGroup: " + QQmlDataGroup::tr("addGroups: index out of range")) << 4; QTest::newRow("addGroups count < 0") << testFileUrl("listmodelproperties.qml") << QString("items.addGroups(1, -2, \"selected\")") - << (": QML VisualDataGroup: " + QQuickVisualDataGroup::tr("addGroups: invalid count")) + << (": QML VisualDataGroup: " + QQmlDataGroup::tr("addGroups: invalid count")) << 4; QTest::newRow("addGroups index + count > length") << testFileUrl("listmodelproperties.qml") << QString("items.addGroups(2, 4, \"selected\")") - << (": QML VisualDataGroup: " + QQuickVisualDataGroup::tr("addGroups: invalid count")) + << (": QML VisualDataGroup: " + QQmlDataGroup::tr("addGroups: invalid count")) << 4; QTest::newRow("removeGroups index < 0") << testFileUrl("listmodelproperties.qml") << QString("items.removeGroups(-2, 1, \"selected\")") - << (": QML VisualDataGroup: " + QQuickVisualDataGroup::tr("removeGroups: index out of range")) + << (": QML VisualDataGroup: " + QQmlDataGroup::tr("removeGroups: index out of range")) << 4; QTest::newRow("removeGroups index == length") << testFileUrl("listmodelproperties.qml") << QString("items.removeGroups(4, 1, \"selected\")") - << (": QML VisualDataGroup: " + QQuickVisualDataGroup::tr("removeGroups: index out of range")) + << (": QML VisualDataGroup: " + QQmlDataGroup::tr("removeGroups: index out of range")) << 4; QTest::newRow("removeGroups index > length") << testFileUrl("listmodelproperties.qml") << QString("items.removeGroups(9, 1, \"selected\")") - << (": QML VisualDataGroup: " + QQuickVisualDataGroup::tr("removeGroups: index out of range")) + << (": QML VisualDataGroup: " + QQmlDataGroup::tr("removeGroups: index out of range")) << 4; QTest::newRow("removeGroups count < 0") << testFileUrl("listmodelproperties.qml") << QString("items.removeGroups(1, -2, \"selected\")") - << (": QML VisualDataGroup: " + QQuickVisualDataGroup::tr("removeGroups: invalid count")) + << (": QML VisualDataGroup: " + QQmlDataGroup::tr("removeGroups: invalid count")) << 4; QTest::newRow("removeGroups index + count > length") << testFileUrl("listmodelproperties.qml") << QString("items.removeGroups(2, 4, \"selected\")") - << (": QML VisualDataGroup: " + QQuickVisualDataGroup::tr("removeGroups: invalid count")) + << (": QML VisualDataGroup: " + QQmlDataGroup::tr("removeGroups: invalid count")) << 4; QTest::newRow("setGroups index < 0") << testFileUrl("listmodelproperties.qml") << QString("items.setGroups(-2, 1, \"selected\")") - << (": QML VisualDataGroup: " + QQuickVisualDataGroup::tr("setGroups: index out of range")) + << (": QML VisualDataGroup: " + QQmlDataGroup::tr("setGroups: index out of range")) << 4; QTest::newRow("setGroups index == length") << testFileUrl("listmodelproperties.qml") << QString("items.setGroups(4, 1, \"selected\")") - << (": QML VisualDataGroup: " + QQuickVisualDataGroup::tr("setGroups: index out of range")) + << (": QML VisualDataGroup: " + QQmlDataGroup::tr("setGroups: index out of range")) << 4; QTest::newRow("setGroups index > length") << testFileUrl("listmodelproperties.qml") << QString("items.setGroups(9, 1, \"selected\")") - << (": QML VisualDataGroup: " + QQuickVisualDataGroup::tr("setGroups: index out of range")) + << (": QML VisualDataGroup: " + QQmlDataGroup::tr("setGroups: index out of range")) << 4; QTest::newRow("setGroups count < 0") << testFileUrl("listmodelproperties.qml") << QString("items.setGroups(1, -2, \"selected\")") - << (": QML VisualDataGroup: " + QQuickVisualDataGroup::tr("setGroups: invalid count")) + << (": QML VisualDataGroup: " + QQmlDataGroup::tr("setGroups: invalid count")) << 4; QTest::newRow("setGroups index + count > length") << testFileUrl("listmodelproperties.qml") << QString("items.setGroups(2, 4, \"selected\")") - << (": QML VisualDataGroup: " + QQuickVisualDataGroup::tr("setGroups: invalid count")) + << (": QML VisualDataGroup: " + QQmlDataGroup::tr("setGroups: invalid count")) << 4; QTest::newRow("move from < 0") << testFileUrl("listmodelproperties.qml") << QString("items.move(-2, 1, 1)") - << (": QML VisualDataGroup: " + QQuickVisualDataGroup::tr("move: from index out of range")) + << (": QML VisualDataGroup: " + QQmlDataGroup::tr("move: from index out of range")) << 4; QTest::newRow("move from == length") << testFileUrl("listmodelproperties.qml") << QString("items.move(4, 1, 1)") - << (": QML VisualDataGroup: " + QQuickVisualDataGroup::tr("move: from index out of range")) + << (": QML VisualDataGroup: " + QQmlDataGroup::tr("move: from index out of range")) << 4; QTest::newRow("move from > length") << testFileUrl("listmodelproperties.qml") << QString("items.move(9, 1, 1)") - << (": QML VisualDataGroup: " + QQuickVisualDataGroup::tr("move: from index out of range")) + << (": QML VisualDataGroup: " + QQmlDataGroup::tr("move: from index out of range")) << 4; QTest::newRow("move invalid from") << testFileUrl("listmodelproperties.qml") << QString("items.move(\"nine\", 1, 1)") - << (": QML VisualDataGroup: " + QQuickVisualDataGroup::tr("move: invalid from index")) + << (": QML VisualDataGroup: " + QQmlDataGroup::tr("move: invalid from index")) << 4; QTest::newRow("move to < 0") << testFileUrl("listmodelproperties.qml") << QString("items.move(1, -2, 1)") - << (": QML VisualDataGroup: " + QQuickVisualDataGroup::tr("move: to index out of range")) + << (": QML VisualDataGroup: " + QQmlDataGroup::tr("move: to index out of range")) << 4; QTest::newRow("move to == length") << testFileUrl("listmodelproperties.qml") << QString("items.move(1, 4, 1)") - << (": QML VisualDataGroup: " + QQuickVisualDataGroup::tr("move: to index out of range")) + << (": QML VisualDataGroup: " + QQmlDataGroup::tr("move: to index out of range")) << 4; QTest::newRow("move to > length") << testFileUrl("listmodelproperties.qml") << QString("items.move(1, 9, 1)") - << (": QML VisualDataGroup: " + QQuickVisualDataGroup::tr("move: to index out of range")) + << (": QML VisualDataGroup: " + QQmlDataGroup::tr("move: to index out of range")) << 4; QTest::newRow("move invalid to") << testFileUrl("listmodelproperties.qml") << QString("items.move(1, \"nine\", 1)") - << (": QML VisualDataGroup: " + QQuickVisualDataGroup::tr("move: invalid to index")) + << (": QML VisualDataGroup: " + QQmlDataGroup::tr("move: invalid to index")) << 4; QTest::newRow("move count < 0") << testFileUrl("listmodelproperties.qml") << QString("items.move(1, 1, -2)") - << (": QML VisualDataGroup: " + QQuickVisualDataGroup::tr("move: invalid count")) + << (": QML VisualDataGroup: " + QQmlDataGroup::tr("move: invalid count")) << 4; QTest::newRow("move from + count > length") << testFileUrl("listmodelproperties.qml") << QString("items.move(2, 1, 4)") - << (": QML VisualDataGroup: " + QQuickVisualDataGroup::tr("move: from index out of range")) + << (": QML VisualDataGroup: " + QQmlDataGroup::tr("move: from index out of range")) << 4; } @@ -3961,19 +3961,19 @@ void tst_qquickvisualdatamodel::invalidAttachment() QCOMPARE(component.errors().count(), 0); QVariant property = object->property("invalidVdm"); - QCOMPARE(property.userType(), qMetaTypeId()); - QVERIFY(!property.value()); + QCOMPARE(property.userType(), qMetaTypeId()); + QVERIFY(!property.value()); QQuickItem *item = findItem(static_cast(object.data()), "delegate"); QVERIFY(item); property = item->property("validVdm"); - QCOMPARE(property.userType(), qMetaTypeId()); - QVERIFY(property.value()); + QCOMPARE(property.userType(), qMetaTypeId()); + QVERIFY(property.value()); property = item->property("invalidVdm"); - QCOMPARE(property.userType(), qMetaTypeId()); - QVERIFY(!property.value()); + QCOMPARE(property.userType(), qMetaTypeId()); + QVERIFY(!property.value()); } void tst_qquickvisualdatamodel::asynchronousInsert_data() @@ -4005,15 +4005,15 @@ void tst_qquickvisualdatamodel::asynchronousInsert() engine.rootContext()->setContextProperty("myModel", &model); - QQuickVisualDataModel *visualModel = qobject_cast(c.create()); + QQmlDelegateModel *visualModel = qobject_cast(c.create()); QVERIFY(visualModel); ItemRequester requester; - connect(visualModel, SIGNAL(initItem(int,QQuickItem*)), &requester, SLOT(initItem(int,QQuickItem*))); - connect(visualModel, SIGNAL(createdItem(int,QQuickItem*)), &requester, SLOT(createdItem(int,QQuickItem*))); - connect(visualModel, SIGNAL(destroyingItem(QQuickItem*)), &requester, SLOT(destroyingItem(QQuickItem*))); + connect(visualModel, SIGNAL(initItem(int,QObject*)), &requester, SLOT(initItem(int,QObject*))); + connect(visualModel, SIGNAL(createdItem(int,QObject*)), &requester, SLOT(createdItem(int,QObject*))); + connect(visualModel, SIGNAL(destroyingItem(QObject*)), &requester, SLOT(destroyingItem(QObject*))); - QQuickItem *item = visualModel->item(requestIndex, true); + QQuickItem *item = qobject_cast(visualModel->object(requestIndex, true)); QVERIFY(!item); QVERIFY(!requester.itemInitialized); @@ -4025,7 +4025,7 @@ void tst_qquickvisualdatamodel::asynchronousInsert() newItems.append(qMakePair(QLatin1String("New item") + QString::number(i), QString(QLatin1String("")))); model.insertItems(insertIndex, newItems); - item = visualModel->item(completeIndex, false); + item = qobject_cast(visualModel->object(completeIndex, false)); QVERIFY(item); QCOMPARE(requester.itemInitialized, item); @@ -4070,15 +4070,15 @@ void tst_qquickvisualdatamodel::asynchronousRemove() engine.rootContext()->setContextProperty("myModel", &model); - QQuickVisualDataModel *visualModel = qobject_cast(c.create()); + QQmlDelegateModel *visualModel = qobject_cast(c.create()); QVERIFY(visualModel); ItemRequester requester; - connect(visualModel, SIGNAL(initItem(int,QQuickItem*)), &requester, SLOT(initItem(int,QQuickItem*))); - connect(visualModel, SIGNAL(createdItem(int,QQuickItem*)), &requester, SLOT(createdItem(int,QQuickItem*))); - connect(visualModel, SIGNAL(destroyingItem(QQuickItem*)), &requester, SLOT(destroyingItem(QQuickItem*))); + connect(visualModel, SIGNAL(initItem(int,QObject*)), &requester, SLOT(initItem(int,QObject*))); + connect(visualModel, SIGNAL(createdItem(int,QObject*)), &requester, SLOT(createdItem(int,QObject*))); + connect(visualModel, SIGNAL(destroyingItem(QObject*)), &requester, SLOT(destroyingItem(QObject*))); - QQuickItem *item = visualModel->item(requestIndex, true); + QQuickItem *item = qobject_cast(visualModel->object(requestIndex, true)); QVERIFY(!item); QVERIFY(!requester.itemInitialized); @@ -4098,7 +4098,7 @@ void tst_qquickvisualdatamodel::asynchronousRemove() QCOMPARE(requester.itemCreated, requester.itemInitialized); QCOMPARE(requester.itemDestroyed, requester.itemInitialized); } else { - item = visualModel->item(completeIndex, false); + item = qobject_cast(visualModel->object(completeIndex, false)); QVERIFY(item); QCOMPARE(requester.itemInitialized, item); @@ -4148,15 +4148,15 @@ void tst_qquickvisualdatamodel::asynchronousMove() engine.rootContext()->setContextProperty("myModel", &model); - QQuickVisualDataModel *visualModel = qobject_cast(c.create()); + QQmlDelegateModel *visualModel = qobject_cast(c.create()); QVERIFY(visualModel); ItemRequester requester; - connect(visualModel, SIGNAL(initItem(int,QQuickItem*)), &requester, SLOT(initItem(int,QQuickItem*))); - connect(visualModel, SIGNAL(createdItem(int,QQuickItem*)), &requester, SLOT(createdItem(int,QQuickItem*))); - connect(visualModel, SIGNAL(destroyingItem(QQuickItem*)), &requester, SLOT(destroyingItem(QQuickItem*))); + connect(visualModel, SIGNAL(initItem(int,QObject*)), &requester, SLOT(initItem(int,QObject*))); + connect(visualModel, SIGNAL(createdItem(int,QObject*)), &requester, SLOT(createdItem(int,QObject*))); + connect(visualModel, SIGNAL(destroyingItem(QObject*)), &requester, SLOT(destroyingItem(QObject*))); - QQuickItem *item = visualModel->item(requestIndex, true); + QQuickItem *item = qobject_cast(visualModel->object(requestIndex, true)); QVERIFY(!item); QVERIFY(!requester.itemInitialized); @@ -4165,7 +4165,7 @@ void tst_qquickvisualdatamodel::asynchronousMove() model.moveItems(from, to, count); - item = visualModel->item(completeIndex, false); + item = qobject_cast(visualModel->object(completeIndex, false)); QVERIFY(item); @@ -4196,10 +4196,10 @@ void tst_qquickvisualdatamodel::asynchronousCancel() engine.rootContext()->setContextProperty("myModel", &model); - QQuickVisualDataModel *visualModel = qobject_cast(c.create()); + QQmlDelegateModel *visualModel = qobject_cast(c.create()); QVERIFY(visualModel); - QQuickItem *item = visualModel->item(requestIndex, true); + QQuickItem *item = qobject_cast(visualModel->object(requestIndex, true)); QVERIFY(!item); QCOMPARE(controller.incubatingObjectCount(), 1); @@ -4221,10 +4221,10 @@ void tst_qquickvisualdatamodel::invalidContext() QQmlComponent c(&engine, testFileUrl("visualdatamodel.qml")); - QQuickVisualDataModel *visualModel = qobject_cast(c.create(context.data())); + QQmlDelegateModel *visualModel = qobject_cast(c.create(context.data())); QVERIFY(visualModel); - QQuickItem *item = visualModel->item(4, false); + QQuickItem *item = qobject_cast(visualModel->object(4, false)); QVERIFY(item); visualModel->release(item); @@ -4232,7 +4232,7 @@ void tst_qquickvisualdatamodel::invalidContext() model.insertItem(4, "new item", ""); - item = visualModel->item(4, false); + item = qobject_cast(visualModel->object(4, false)); QVERIFY(!item); } -- cgit v1.2.3 From d483a1bbaf7d3947dd18bacfa010b53300a7dbd6 Mon Sep 17 00:00:00 2001 From: Gunnar Sletta Date: Thu, 10 Jan 2013 14:06:52 +0100 Subject: Added an example of the window container embedding a QQuickView Change-Id: I451716f4a0eac2020835a9e8a0d67626b981c736 Reviewed-by: Alan Alpert --- examples/quick/embeddedinwidgets/TextBox.qml | 76 ++++++++++++++ .../quick/embeddedinwidgets/embeddedinwidgets.pro | 12 +++ .../quick/embeddedinwidgets/embeddedinwidgets.qrc | 6 ++ examples/quick/embeddedinwidgets/main.cpp | 73 +++++++++++++ examples/quick/embeddedinwidgets/main.qml | 113 +++++++++++++++++++++ examples/quick/quick.pro | 5 + 6 files changed, 285 insertions(+) create mode 100644 examples/quick/embeddedinwidgets/TextBox.qml create mode 100644 examples/quick/embeddedinwidgets/embeddedinwidgets.pro create mode 100644 examples/quick/embeddedinwidgets/embeddedinwidgets.qrc create mode 100644 examples/quick/embeddedinwidgets/main.cpp create mode 100644 examples/quick/embeddedinwidgets/main.qml diff --git a/examples/quick/embeddedinwidgets/TextBox.qml b/examples/quick/embeddedinwidgets/TextBox.qml new file mode 100644 index 0000000000..9664726305 --- /dev/null +++ b/examples/quick/embeddedinwidgets/TextBox.qml @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the demonstration applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 + +FocusScope { + id: root + + width: 100 + height: 30 + + property Item nextInFocus; + property string label; + + Rectangle { + anchors.margins: 2 + anchors.fill: parent + radius: 8 + gradient: Gradient { + GradientStop { position: 0; color: "lightgray" } + GradientStop { position: 1; color: "white" } + } + border.color: "white" + border.width: 2 + antialiasing: true + + TextInput { + anchors.fill: parent + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + focus: true + + text: root.label; + + KeyNavigation.tab: root.nextInFocus; + } + } +} diff --git a/examples/quick/embeddedinwidgets/embeddedinwidgets.pro b/examples/quick/embeddedinwidgets/embeddedinwidgets.pro new file mode 100644 index 0000000000..de47a397ab --- /dev/null +++ b/examples/quick/embeddedinwidgets/embeddedinwidgets.pro @@ -0,0 +1,12 @@ +TEMPLATE = app +QT += widgets quick + +SOURCES += main.cpp + +OTHER_FILES += main.qml TextBox.qml + +RESOURCES += \ + embeddedinwidgets.qrc + +target.path = $$[QT_INSTALL_EXAMPLES]/quick/embeddedinwidgets +INSTALLS += target diff --git a/examples/quick/embeddedinwidgets/embeddedinwidgets.qrc b/examples/quick/embeddedinwidgets/embeddedinwidgets.qrc new file mode 100644 index 0000000000..62e0ed2161 --- /dev/null +++ b/examples/quick/embeddedinwidgets/embeddedinwidgets.qrc @@ -0,0 +1,6 @@ + + + main.qml + TextBox.qml + + diff --git a/examples/quick/embeddedinwidgets/main.cpp b/examples/quick/embeddedinwidgets/main.cpp new file mode 100644 index 0000000000..7a30b277de --- /dev/null +++ b/examples/quick/embeddedinwidgets/main.cpp @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the demonstration applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include + +#include + +int main(int argc, char **argv) +{ + QApplication app(argc, argv); + + QWidget rootWidget; + + QVBoxLayout *layout = new QVBoxLayout(); + rootWidget.setLayout(layout); + + QQuickView *view = new QQuickView(); + view->setResizeMode(QQuickView::SizeRootObjectToView); + view->setSource(QUrl(QStringLiteral("qrc:///embeddedinwidgets/main.qml"))); + + QWidget *container = QWidget::createWindowContainer(view); + container->setMinimumSize(view->size()); + container->setFocusPolicy(Qt::TabFocus); + + layout->addWidget(new QLineEdit(QStringLiteral("A QLineEdit"))); + layout->addWidget(container); + layout->addWidget(new QLineEdit(QStringLiteral("A QLineEdit"))); + + rootWidget.show(); + + return app.exec(); +} diff --git a/examples/quick/embeddedinwidgets/main.qml b/examples/quick/embeddedinwidgets/main.qml new file mode 100644 index 0000000000..f5a941a258 --- /dev/null +++ b/examples/quick/embeddedinwidgets/main.qml @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the demonstration applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 + +Rectangle { + id: window + + width: 400 + height: 200 + + gradient: Gradient { + GradientStop { position: 0; color: "lightsteelblue" } + GradientStop { position: 1; color: "black" } + } + + Column { + id: column + opacity: 0.99 // work around QTBUG-29037 + + y: 50 + width: 200 + anchors.horizontalCenter: parent.horizontalCenter + + TextBox { + id: input1 + width: parent.width + height: 30 + focus: true + + label: "A QML text box.." + + nextInFocus: input2; + } + + TextBox { + id: input2 + width: parent.width + height: 30 + + label: "Another QML text box.." + + nextInFocus: input1; + } + + layer.enabled: true + layer.smooth: true + } + + ShaderEffect { + anchors.top: column.bottom + width: column.width + height: column.height; + anchors.left: column.left + + property variant source: column; + property size sourceSize: Qt.size(0.5 / column.width, 0.5 / column.height); + + fragmentShader: " + varying highp vec2 qt_TexCoord0; + uniform lowp sampler2D source; + uniform lowp vec2 sourceSize; + uniform lowp float qt_Opacity; + void main() { + + lowp vec2 tc = qt_TexCoord0 * vec2(1, -1) + vec2(0, 1); + lowp vec4 col = 0.25 * (texture2D(source, tc + sourceSize) + + texture2D(source, tc- sourceSize) + + texture2D(source, tc + sourceSize * vec2(1, -1)) + + texture2D(source, tc + sourceSize * vec2(-1, 1)) + ); + gl_FragColor = col * qt_Opacity * (1.0 - qt_TexCoord0.y) * 0.2; + }" + } +} diff --git a/examples/quick/quick.pro b/examples/quick/quick.pro index e3691a044b..2d8acb3345 100644 --- a/examples/quick/quick.pro +++ b/examples/quick/quick.pro @@ -23,6 +23,11 @@ SUBDIRS = accessibility \ particles \ demos +# Widget dependent examples +qtHaveModule(widgets) { + SUBDIRS += embeddedinwidgets +} + EXAMPLE_FILES = \ ui-components \ shared -- cgit v1.2.3 From 8b62bb86cd94287a29fd1474efe04822523600df Mon Sep 17 00:00:00 2001 From: Sergio Ahumada Date: Sun, 27 Jan 2013 00:43:59 +0100 Subject: Remove QT_{BEGIN,END}_HEADER macro usage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The macro was made empty in qtbase/ba3dc5f3b56d1fab6fe37fe7ae08096d7dc68bcb and is no longer necessary or used. Discussed-on: http://lists.qt-project.org/pipermail/development/2013-January/009284.html Change-Id: Ia07e99676e0134fde5e32880edb95e57c779a7ff Reviewed-by: Laszlo Papp Reviewed-by: Jędrzej Nowacki Reviewed-by: Alan Alpert --- src/imports/folderlistmodel/qquickfolderlistmodel.h | 4 ---- src/imports/testlib/signalspy.h | 4 ---- src/imports/testlib/testcase.h | 4 ---- src/imports/xmllistmodel/qqmlxmllistmodel_p.h | 4 ---- src/particles/qquickage_p.h | 3 --- src/particles/qquickangledirection_p.h | 3 --- src/particles/qquickcumulativedirection_p.h | 4 ---- src/particles/qquickcustomaffector_p.h | 3 --- src/particles/qquickcustomparticle_p.h | 4 ---- src/particles/qquickdirection_p.h | 3 --- src/particles/qquickellipseextruder_p.h | 3 --- src/particles/qquickfriction_p.h | 3 --- src/particles/qquickgravity_p.h | 3 --- src/particles/qquickgroupgoal_p.h | 4 ---- src/particles/qquickimageparticle_p.h | 3 --- src/particles/qquickitemparticle_p.h | 3 --- src/particles/qquickmaskextruder_p.h | 4 ---- src/particles/qquickparticleaffector_p.h | 3 --- src/particles/qquickparticleemitter_p.h | 4 ---- src/particles/qquickparticleextruder_p.h | 4 ---- src/particles/qquickparticlepainter_p.h | 3 --- src/particles/qquickparticlesmodule_p.h | 4 ---- src/particles/qquickparticlesystem_p.h | 4 ---- src/particles/qquickpointattractor_p.h | 3 --- src/particles/qquickpointdirection_p.h | 3 --- src/particles/qquickrectangleextruder_p.h | 4 ---- src/particles/qquickspritegoal_p.h | 4 ---- src/particles/qquicktargetdirection_p.h | 3 --- src/particles/qquicktrailemitter_p.h | 3 --- src/particles/qquickturbulence_p.h | 3 --- src/particles/qquickv8particledata_p.h | 4 ---- src/particles/qquickwander_p.h | 3 --- src/qml/animations/qabstractanimationjob_p.h | 4 ---- src/qml/animations/qanimationgroupjob_p.h | 4 ---- src/qml/animations/qparallelanimationgroupjob_p.h | 4 ---- src/qml/animations/qpauseanimationjob_p.h | 4 ---- src/qml/animations/qsequentialanimationgroupjob_p.h | 4 ---- src/qml/debugger/qdebugmessageservice_p.h | 4 ---- src/qml/debugger/qqmldebug.h | 4 ---- src/qml/debugger/qqmldebugserver_p.h | 4 ---- src/qml/debugger/qqmldebugserverconnection_p.h | 4 ---- src/qml/debugger/qqmldebugservice_p.h | 4 ---- src/qml/debugger/qqmldebugservice_p_p.h | 4 ---- src/qml/debugger/qqmldebugstatesdelegate_p.h | 4 ---- src/qml/debugger/qqmlinspectorinterface_p.h | 4 ---- src/qml/debugger/qqmlinspectorservice_p.h | 4 ---- src/qml/debugger/qqmlprofilerservice_p.h | 4 ---- src/qml/debugger/qv8debugservice_p.h | 4 ---- src/qml/debugger/qv8profilerservice_p.h | 4 ---- src/qml/items/qqmldelegatemodel_p.h | 4 ---- src/qml/items/qqmlobjectmodel_p.h | 4 ---- src/qml/items/qquickpackage_p.h | 4 ---- src/qml/qml/ftw/qqmlrefcount_p.h | 4 ---- src/qml/qml/qqml.h | 4 ---- src/qml/qml/qqmlaccessors_p.h | 4 ---- src/qml/qml/qqmlbind_p.h | 4 ---- src/qml/qml/qqmlcomponent.h | 4 ---- src/qml/qml/qqmlcomponentattached_p.h | 4 ---- src/qml/qml/qqmlconnections_p.h | 4 ---- src/qml/qml/qqmlcontext.h | 4 ---- src/qml/qml/qqmlcustomparser_p.h | 4 ---- src/qml/qml/qqmlengine.h | 4 ---- src/qml/qml/qqmlerror.h | 4 ---- src/qml/qml/qqmlexpression.h | 4 ---- src/qml/qml/qqmlextensioninterface.h | 4 ---- src/qml/qml/qqmlextensionplugin.h | 4 ---- src/qml/qml/qqmlfile.h | 4 ---- src/qml/qml/qqmlglobal_p.h | 4 ---- src/qml/qml/qqmlincubator.h | 4 ---- src/qml/qml/qqmlinfo.h | 4 ---- src/qml/qml/qqmllist.h | 4 ---- src/qml/qml/qqmllistmodel_p.h | 4 ---- src/qml/qml/qqmllistmodel_p_p.h | 4 ---- src/qml/qml/qqmllistmodelworkeragent_p.h | 4 ---- src/qml/qml/qqmllocale_p.h | 4 ---- src/qml/qml/qqmlmemoryprofiler_p.h | 3 --- src/qml/qml/qqmlnetworkaccessmanagerfactory.h | 4 ---- src/qml/qml/qqmlopenmetaobject_p.h | 4 ---- src/qml/qml/qqmlparserstatus.h | 4 ---- src/qml/qml/qqmlprivate.h | 4 ---- src/qml/qml/qqmlproperty.h | 4 ---- src/qml/qml/qqmlpropertyvaluesource.h | 4 ---- src/qml/qml/qqmlproxymetaobject_p.h | 4 ---- src/qml/qml/qqmlscript_p.h | 4 ---- src/qml/qml/qqmlscriptstring.h | 4 ---- src/qml/qml/qqmltimer_p.h | 4 ---- src/qml/qml/qqmltypenotavailable_p.h | 4 ---- src/qml/qml/qquickworkerscript_p.h | 4 ---- src/qml/qml/rewriter/textwriter_p.h | 3 --- src/qml/qml/v4/qv4bindings_p.h | 4 ---- src/qml/qml/v4/qv4compiler_p.h | 4 ---- src/qml/qml/v4/qv4compiler_p_p.h | 4 ---- src/qml/qml/v4/qv4instruction_p.h | 4 ---- src/qml/qml/v4/qv4ir_p.h | 4 ---- src/qml/qml/v4/qv4irbuilder_p.h | 4 ---- src/qml/qml/v4/qv4program_p.h | 4 ---- src/qml/qml/v8/qjsengine.h | 4 ---- src/qml/qml/v8/qjsvalue.h | 4 ---- src/qml/qml/v8/qjsvalueiterator.h | 4 ---- src/qml/qml/v8/qv8bindings_p.h | 4 ---- src/qml/qml/v8/qv8domerrors_p.h | 4 ---- src/qml/qml/v8/qv8sqlerrors_p.h | 4 ---- src/qml/qtqmlglobal.h | 3 --- src/qml/util/qqmladaptormodel_p.h | 2 -- src/qml/util/qqmllistaccessor_p.h | 4 ---- src/qml/util/qqmlpropertymap.h | 4 ---- src/qmltest/qtestoptions_p.h | 4 ---- src/qmltest/quicktest.h | 4 ---- src/qmltest/quicktestglobal.h | 2 -- src/quick/designer/designersupport.h | 4 ---- src/quick/designer/designerwindowmanager_p.h | 3 --- src/quick/items/context2d/qquickcanvascontext_p.h | 4 ---- src/quick/items/context2d/qquickcanvasitem_p.h | 4 ---- src/quick/items/context2d/qquickcontext2d_p.h | 4 ---- src/quick/items/context2d/qquickcontext2dcommandbuffer_p.h | 4 ---- src/quick/items/context2d/qquickcontext2dtexture_p.h | 4 ---- src/quick/items/context2d/qquickcontext2dtile_p.h | 4 ---- src/quick/items/qquickaccessibleattached_p.h | 4 ---- src/quick/items/qquickanchors_p.h | 4 ---- src/quick/items/qquickanimatedimage_p.h | 4 ---- src/quick/items/qquickanimatedsprite_p.h | 4 ---- src/quick/items/qquickborderimage_p.h | 2 -- src/quick/items/qquickdrag_p.h | 4 ---- src/quick/items/qquickdroparea_p.h | 4 ---- src/quick/items/qquickflickable_p.h | 4 ---- src/quick/items/qquickflipable_p.h | 4 ---- src/quick/items/qquickfocusscope_p.h | 4 ---- src/quick/items/qquickgridview_p.h | 4 ---- src/quick/items/qquickimage_p.h | 4 ---- src/quick/items/qquickimagebase_p.h | 4 ---- src/quick/items/qquickimplicitsizeitem_p.h | 4 ---- src/quick/items/qquickitem.h | 4 ---- src/quick/items/qquickitemanimation_p.h | 4 ---- src/quick/items/qquickitemsmodule_p.h | 4 ---- src/quick/items/qquickitemview_p.h | 4 ---- src/quick/items/qquickitemview_p_p.h | 4 ---- src/quick/items/qquickitemviewtransition_p.h | 4 ---- src/quick/items/qquicklistview_p.h | 4 ---- src/quick/items/qquickloader_p.h | 4 ---- src/quick/items/qquickmousearea_p.h | 4 ---- src/quick/items/qquickmultipointtoucharea_p.h | 4 ---- src/quick/items/qquickpainteditem.h | 4 ---- src/quick/items/qquickpathview_p.h | 3 --- src/quick/items/qquickpincharea_p.h | 4 ---- src/quick/items/qquickpositioners_p.h | 4 ---- src/quick/items/qquickrectangle_p.h | 4 ---- src/quick/items/qquickrepeater_p.h | 4 ---- src/quick/items/qquickscalegrid_p_p.h | 4 ---- src/quick/items/qquickscreen_p.h | 4 ---- src/quick/items/qquickshadereffect_p.h | 4 ---- src/quick/items/qquickshadereffectmesh_p.h | 4 ---- src/quick/items/qquickshadereffectnode_p.h | 4 ---- src/quick/items/qquickshadereffectsource_p.h | 4 ---- src/quick/items/qquicksprite_p.h | 3 --- src/quick/items/qquickspriteengine_p.h | 4 ---- src/quick/items/qquickspritesequence_p.h | 4 ---- src/quick/items/qquickstateoperations_p.h | 4 ---- src/quick/items/qquicktext_p.h | 4 ---- src/quick/items/qquicktextcontrol_p.h | 4 ---- src/quick/items/qquicktextedit_p.h | 4 ---- src/quick/items/qquicktextinput_p.h | 4 ---- src/quick/items/qquicktranslate_p.h | 4 ---- src/quick/items/qquickview.h | 4 ---- src/quick/items/qquickview_p.h | 4 ---- src/quick/items/qquickwindow.h | 4 ---- src/quick/items/qquickwindowmodule_p.h | 4 ---- src/quick/qtquick2_p.h | 4 ---- src/quick/qtquickglobal.h | 2 -- src/quick/scenegraph/coreapi/qsgdefaultrenderer_p.h | 4 ---- src/quick/scenegraph/coreapi/qsggeometry.h | 4 ---- src/quick/scenegraph/coreapi/qsgmaterial.h | 4 ---- src/quick/scenegraph/coreapi/qsgnode.h | 4 ---- src/quick/scenegraph/coreapi/qsgnodeupdater_p.h | 4 ---- src/quick/scenegraph/coreapi/qsgrenderer_p.h | 4 ---- src/quick/scenegraph/coreapi/qsgrendernode_p.h | 4 ---- src/quick/scenegraph/qsgadaptationlayer_p.h | 4 ---- src/quick/scenegraph/qsgcontext_p.h | 4 ---- src/quick/scenegraph/qsgcontextplugin_p.h | 4 ---- src/quick/scenegraph/qsgdefaultglyphnode_p.h | 4 ---- src/quick/scenegraph/qsgdefaultimagenode_p.h | 4 ---- src/quick/scenegraph/qsgdefaultrectanglenode_p.h | 4 ---- src/quick/scenegraph/qsgdistancefieldglyphnode_p.h | 4 ---- src/quick/scenegraph/qsgflashnode_p.h | 4 ---- src/quick/scenegraph/qsgrenderloop_p.h | 4 ---- src/quick/scenegraph/qsgshareddistancefieldglyphcache_p.h | 4 ---- src/quick/scenegraph/qsgthreadedrenderloop_p.h | 4 ---- src/quick/scenegraph/util/qsgdepthstencilbuffer_p.h | 4 ---- src/quick/scenegraph/util/qsgflatcolormaterial.h | 4 ---- src/quick/scenegraph/util/qsgpainternode_p.h | 4 ---- src/quick/scenegraph/util/qsgsimplematerial.h | 4 ---- src/quick/scenegraph/util/qsgsimplerectnode.h | 4 ---- src/quick/scenegraph/util/qsgsimpletexturenode.h | 4 ---- src/quick/scenegraph/util/qsgtexture.h | 4 ---- src/quick/scenegraph/util/qsgtexturematerial.h | 4 ---- src/quick/scenegraph/util/qsgtexturematerial_p.h | 4 ---- src/quick/scenegraph/util/qsgtextureprovider.h | 4 ---- src/quick/scenegraph/util/qsgvertexcolormaterial.h | 4 ---- src/quick/util/qquickanimation_p.h | 4 ---- src/quick/util/qquickanimationcontroller_p.h | 4 ---- src/quick/util/qquickapplication_p.h | 4 ---- src/quick/util/qquickbehavior_p.h | 4 ---- src/quick/util/qquickfontloader_p.h | 4 ---- src/quick/util/qquickimageprovider.h | 4 ---- src/quick/util/qquickpath_p.h | 4 ---- src/quick/util/qquickpathinterpolator_p.h | 4 ---- src/quick/util/qquickpixmapcache_p.h | 4 ---- src/quick/util/qquickpropertychanges_p.h | 4 ---- src/quick/util/qquicksmoothedanimation_p.h | 4 ---- src/quick/util/qquickspringanimation_p.h | 4 ---- src/quick/util/qquickstate_p.h | 4 ---- src/quick/util/qquickstatechangescript_p.h | 4 ---- src/quick/util/qquickstategroup_p.h | 4 ---- src/quick/util/qquicksystempalette_p.h | 4 ---- src/quick/util/qquicktransition_p.h | 4 ---- src/quick/util/qquickutilmodule_p.h | 4 ---- src/quick/util/qquickvaluetypes_p.h | 4 ---- 216 files changed, 833 deletions(-) diff --git a/src/imports/folderlistmodel/qquickfolderlistmodel.h b/src/imports/folderlistmodel/qquickfolderlistmodel.h index eb16bd05fc..03cb24d368 100644 --- a/src/imports/folderlistmodel/qquickfolderlistmodel.h +++ b/src/imports/folderlistmodel/qquickfolderlistmodel.h @@ -47,8 +47,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE @@ -165,6 +163,4 @@ private: QT_END_NAMESPACE -QT_END_HEADER - #endif // QQUICKFOLDERLISTMODEL_H diff --git a/src/imports/testlib/signalspy.h b/src/imports/testlib/signalspy.h index 27dc12ae13..a7ff89f0b0 100644 --- a/src/imports/testlib/signalspy.h +++ b/src/imports/testlib/signalspy.h @@ -46,8 +46,6 @@ #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class SignalSpy : public QQuickItem @@ -78,6 +76,4 @@ QML_DECLARE_TYPE(SignalSpy) QT_END_NAMESPACE -QT_END_HEADER - #endif diff --git a/src/imports/testlib/testcase.h b/src/imports/testlib/testcase.h index a5e393a1a4..3b767981d0 100644 --- a/src/imports/testlib/testcase.h +++ b/src/imports/testlib/testcase.h @@ -46,8 +46,6 @@ #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class TestCase : public QQuickItem @@ -89,6 +87,4 @@ QML_DECLARE_TYPE(TestCase) QT_END_NAMESPACE -QT_END_HEADER - #endif diff --git a/src/imports/xmllistmodel/qqmlxmllistmodel_p.h b/src/imports/xmllistmodel/qqmlxmllistmodel_p.h index 776ae50f64..0a11cc6e28 100644 --- a/src/imports/xmllistmodel/qqmlxmllistmodel_p.h +++ b/src/imports/xmllistmodel/qqmlxmllistmodel_p.h @@ -50,8 +50,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE @@ -207,6 +205,4 @@ QT_END_NAMESPACE QML_DECLARE_TYPE(QQuickXmlListModel) QML_DECLARE_TYPE(QQuickXmlListModelRole) -QT_END_HEADER - #endif // QQUICKXMLLISTMODEL_H diff --git a/src/particles/qquickage_p.h b/src/particles/qquickage_p.h index b9047cab9e..0839517e95 100644 --- a/src/particles/qquickage_p.h +++ b/src/particles/qquickage_p.h @@ -43,8 +43,6 @@ #define KILLAFFECTOR_H #include "qquickparticleaffector_p.h" -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickAgeAffector : public QQuickParticleAffector @@ -95,5 +93,4 @@ private: }; QT_END_NAMESPACE -QT_END_HEADER #endif // KILLAFFECTOR_H diff --git a/src/particles/qquickangledirection_p.h b/src/particles/qquickangledirection_p.h index 7ac70cef00..d89455a5e7 100644 --- a/src/particles/qquickangledirection_p.h +++ b/src/particles/qquickangledirection_p.h @@ -42,8 +42,6 @@ #ifndef QQuickANGLEDDIRECTION_H #define QQuickANGLEDDIRECTION_H #include "qquickdirection_p.h" -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickAngleDirection : public QQuickDirection @@ -127,5 +125,4 @@ qreal m_magnitudeVariation; }; QT_END_NAMESPACE -QT_END_HEADER #endif // QQuickANGLEDDIRECTION_H diff --git a/src/particles/qquickcumulativedirection_p.h b/src/particles/qquickcumulativedirection_p.h index 52e8915e12..c9aa1e8593 100644 --- a/src/particles/qquickcumulativedirection_p.h +++ b/src/particles/qquickcumulativedirection_p.h @@ -43,8 +43,6 @@ #define QQuickCUMULATIVEDIRECTION_P_H #include "qquickdirection_p.h" #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickCumulativeDirection : public QQuickDirection @@ -62,6 +60,4 @@ private: QT_END_NAMESPACE -QT_END_HEADER - #endif // QQuickCUMULATIVEDIRECTION_P_H diff --git a/src/particles/qquickcustomaffector_p.h b/src/particles/qquickcustomaffector_p.h index ad6409ab96..a4a077f864 100644 --- a/src/particles/qquickcustomaffector_p.h +++ b/src/particles/qquickcustomaffector_p.h @@ -48,8 +48,6 @@ #include "qquickparticleaffector_p.h" #include "qquickdirection_p.h" -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickCustomAffector : public QQuickParticleAffector @@ -158,5 +156,4 @@ private: }; QT_END_NAMESPACE -QT_END_HEADER #endif // CUSTOMAFFECTOR_H diff --git a/src/particles/qquickcustomparticle_p.h b/src/particles/qquickcustomparticle_p.h index 66f0fd3e6d..16c1a0c389 100644 --- a/src/particles/qquickcustomparticle_p.h +++ b/src/particles/qquickcustomparticle_p.h @@ -46,8 +46,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QSGNode; @@ -114,6 +112,4 @@ private: QT_END_NAMESPACE -QT_END_HEADER - #endif //HEADER_GUARD diff --git a/src/particles/qquickdirection_p.h b/src/particles/qquickdirection_p.h index 796be7c91c..4ceae16b35 100644 --- a/src/particles/qquickdirection_p.h +++ b/src/particles/qquickdirection_p.h @@ -45,8 +45,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickDirection : public QObject @@ -64,5 +62,4 @@ protected: }; QT_END_NAMESPACE -QT_END_HEADER #endif // VARYINGVECTOR_H diff --git a/src/particles/qquickellipseextruder_p.h b/src/particles/qquickellipseextruder_p.h index 3c65d0b792..a9425b6205 100644 --- a/src/particles/qquickellipseextruder_p.h +++ b/src/particles/qquickellipseextruder_p.h @@ -43,8 +43,6 @@ #define ELLIPSEEXTRUDER_H #include "qquickparticleextruder_p.h" -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickEllipseExtruder : public QQuickParticleExtruder @@ -79,5 +77,4 @@ private: }; QT_END_NAMESPACE -QT_END_HEADER #endif // ELLIPSEEXTRUDER_H diff --git a/src/particles/qquickfriction_p.h b/src/particles/qquickfriction_p.h index f92ed36c4b..13ba715e88 100644 --- a/src/particles/qquickfriction_p.h +++ b/src/particles/qquickfriction_p.h @@ -43,8 +43,6 @@ #define FRICTIONAFFECTOR_H #include "qquickparticleaffector_p.h" -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickFrictionAffector : public QQuickParticleAffector @@ -97,5 +95,4 @@ private: }; QT_END_NAMESPACE -QT_END_HEADER #endif // FRICTIONAFFECTOR_H diff --git a/src/particles/qquickgravity_p.h b/src/particles/qquickgravity_p.h index a34e4310b8..e6010b536e 100644 --- a/src/particles/qquickgravity_p.h +++ b/src/particles/qquickgravity_p.h @@ -43,8 +43,6 @@ #define GRAVITYAFFECTOR_H #include "qquickparticleaffector_p.h" -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickGravityAffector : public QQuickParticleAffector @@ -111,5 +109,4 @@ private: }; QT_END_NAMESPACE -QT_END_HEADER #endif // GRAVITYAFFECTOR_H diff --git a/src/particles/qquickgroupgoal_p.h b/src/particles/qquickgroupgoal_p.h index 64d1152d25..6a31b882a3 100644 --- a/src/particles/qquickgroupgoal_p.h +++ b/src/particles/qquickgroupgoal_p.h @@ -43,8 +43,6 @@ #define GROUPGOALAFFECTOR_H #include "qquickparticleaffector_p.h" -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickStochasticEngine; @@ -95,6 +93,4 @@ private: QT_END_NAMESPACE -QT_END_HEADER - #endif // GROUPGOALAFFECTOR_H diff --git a/src/particles/qquickimageparticle_p.h b/src/particles/qquickimageparticle_p.h index 55bf3530f4..daa3d5cdbc 100644 --- a/src/particles/qquickimageparticle_p.h +++ b/src/particles/qquickimageparticle_p.h @@ -48,8 +48,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class ImageMaterialData; @@ -442,5 +440,4 @@ private: }; QT_END_NAMESPACE -QT_END_HEADER #endif // ULTRAPARTICLE_H diff --git a/src/particles/qquickitemparticle_p.h b/src/particles/qquickitemparticle_p.h index df7916895f..e5c7239567 100644 --- a/src/particles/qquickitemparticle_p.h +++ b/src/particles/qquickitemparticle_p.h @@ -45,8 +45,6 @@ #include #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickVisualDataModel; @@ -137,5 +135,4 @@ QT_END_NAMESPACE QML_DECLARE_TYPEINFO(QQuickItemParticle, QML_HAS_ATTACHED_PROPERTIES) -QT_END_HEADER #endif // ITEMPARTICLE_H diff --git a/src/particles/qquickmaskextruder_p.h b/src/particles/qquickmaskextruder_p.h index 5b45559e19..5c948cc7d7 100644 --- a/src/particles/qquickmaskextruder_p.h +++ b/src/particles/qquickmaskextruder_p.h @@ -46,8 +46,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickMaskExtruder : public QQuickParticleExtruder @@ -89,6 +87,4 @@ private: QT_END_NAMESPACE -QT_END_HEADER - #endif // MASKEXTRUDER_H diff --git a/src/particles/qquickparticleaffector_p.h b/src/particles/qquickparticleaffector_p.h index fe8d21175c..2629ecc676 100644 --- a/src/particles/qquickparticleaffector_p.h +++ b/src/particles/qquickparticleaffector_p.h @@ -46,8 +46,6 @@ #include "qquickparticlesystem_p.h" #include "qquickparticleextruder_p.h" -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickParticleAffector : public QQuickItem @@ -196,5 +194,4 @@ private: }; QT_END_NAMESPACE -QT_END_HEADER #endif // PARTICLEAFFECTOR_H diff --git a/src/particles/qquickparticleemitter_p.h b/src/particles/qquickparticleemitter_p.h index 9c34d8d2f1..70de934f6d 100644 --- a/src/particles/qquickparticleemitter_p.h +++ b/src/particles/qquickparticleemitter_p.h @@ -51,8 +51,6 @@ #include #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickParticleEmitter : public QQuickItem @@ -345,6 +343,4 @@ private: QT_END_NAMESPACE -QT_END_HEADER - #endif // PARTICLEEMITTER_H diff --git a/src/particles/qquickparticleextruder_p.h b/src/particles/qquickparticleextruder_p.h index 19000ffbfd..36edc9cf06 100644 --- a/src/particles/qquickparticleextruder_p.h +++ b/src/particles/qquickparticleextruder_p.h @@ -46,8 +46,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickParticleExtruder : public QObject @@ -66,6 +64,4 @@ protected: QT_END_NAMESPACE -QT_END_HEADER - #endif // PARTICLEEXTRUDER_H diff --git a/src/particles/qquickparticlepainter_p.h b/src/particles/qquickparticlepainter_p.h index 82dda82b19..7801fddb6e 100644 --- a/src/particles/qquickparticlepainter_p.h +++ b/src/particles/qquickparticlepainter_p.h @@ -47,8 +47,6 @@ #include #include "qquickparticlesystem_p.h" -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickParticlePainter : public QQuickItem @@ -133,5 +131,4 @@ private: }; QT_END_NAMESPACE -QT_END_HEADER #endif // PARTICLE_H diff --git a/src/particles/qquickparticlesmodule_p.h b/src/particles/qquickparticlesmodule_p.h index 83f6e016ef..20b1d49b1e 100644 --- a/src/particles/qquickparticlesmodule_p.h +++ b/src/particles/qquickparticlesmodule_p.h @@ -44,8 +44,6 @@ #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class Q_QUICKPARTICLES_PRIVATE_EXPORT QQuickParticlesModule @@ -56,6 +54,4 @@ public: QT_END_NAMESPACE -QT_END_HEADER - #endif // QQUICKPARTICLESMODULE_H diff --git a/src/particles/qquickparticlesystem_p.h b/src/particles/qquickparticlesystem_p.h index ba216798f3..1177e30b8f 100644 --- a/src/particles/qquickparticlesystem_p.h +++ b/src/particles/qquickparticlesystem_p.h @@ -53,8 +53,6 @@ #include #include //For QQmlV8Handle -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickParticleSystem; @@ -378,8 +376,6 @@ private: QT_END_NAMESPACE -QT_END_HEADER - #endif // PARTICLESYSTEM_H diff --git a/src/particles/qquickpointattractor_p.h b/src/particles/qquickpointattractor_p.h index 0f2c58d632..1332591daf 100644 --- a/src/particles/qquickpointattractor_p.h +++ b/src/particles/qquickpointattractor_p.h @@ -43,8 +43,6 @@ #define ATTRACTORAFFECTOR_H #include "qquickparticleaffector_p.h" -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickAttractorAffector : public QQuickParticleAffector @@ -163,5 +161,4 @@ Proportion m_proportionalToDistance; }; QT_END_NAMESPACE -QT_END_HEADER #endif // ATTRACTORAFFECTOR_H diff --git a/src/particles/qquickpointdirection_p.h b/src/particles/qquickpointdirection_p.h index 2757588843..eae385a3b7 100644 --- a/src/particles/qquickpointdirection_p.h +++ b/src/particles/qquickpointdirection_p.h @@ -43,8 +43,6 @@ #define POINTVECTOR_H #include "qquickdirection_p.h" -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickPointDirection : public QQuickDirection @@ -129,5 +127,4 @@ private: }; QT_END_NAMESPACE -QT_END_HEADER #endif // POINTVECTOR_H diff --git a/src/particles/qquickrectangleextruder_p.h b/src/particles/qquickrectangleextruder_p.h index 3edb54bb11..e0480687a0 100644 --- a/src/particles/qquickrectangleextruder_p.h +++ b/src/particles/qquickrectangleextruder_p.h @@ -44,8 +44,6 @@ #include "qquickparticleextruder_p.h" -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickRectangleExtruder : public QQuickParticleExtruder @@ -81,6 +79,4 @@ protected: QT_END_NAMESPACE -QT_END_HEADER - #endif // RectangleEXTRUDER_H diff --git a/src/particles/qquickspritegoal_p.h b/src/particles/qquickspritegoal_p.h index 7b61f0ec36..4d5f192f0a 100644 --- a/src/particles/qquickspritegoal_p.h +++ b/src/particles/qquickspritegoal_p.h @@ -44,8 +44,6 @@ #include "qquickparticleaffector_p.h" #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickStochasticEngine; @@ -118,6 +116,4 @@ private: QT_END_NAMESPACE -QT_END_HEADER - #endif // SPRITEGOALAFFECTOR_H diff --git a/src/particles/qquicktargetdirection_p.h b/src/particles/qquicktargetdirection_p.h index aecf880506..886a560271 100644 --- a/src/particles/qquicktargetdirection_p.h +++ b/src/particles/qquicktargetdirection_p.h @@ -42,8 +42,6 @@ #ifndef DIRECTEDVECTOR_H #define DIRECTEDVECTOR_H #include "qquickdirection_p.h" -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickItem; @@ -185,5 +183,4 @@ private: }; QT_END_NAMESPACE -QT_END_HEADER #endif // DIRECTEDVECTOR_H diff --git a/src/particles/qquicktrailemitter_p.h b/src/particles/qquicktrailemitter_p.h index b57cb57e37..3ae1507f1a 100644 --- a/src/particles/qquicktrailemitter_p.h +++ b/src/particles/qquicktrailemitter_p.h @@ -44,8 +44,6 @@ #include "qquickparticleemitter_p.h" #include "qquickparticleaffector_p.h" -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickTrailEmitter : public QQuickParticleEmitter @@ -164,5 +162,4 @@ private: }; QT_END_NAMESPACE -QT_END_HEADER #endif // FOLLOWEMITTER_H diff --git a/src/particles/qquickturbulence_p.h b/src/particles/qquickturbulence_p.h index d0dd458a79..eea550ed15 100644 --- a/src/particles/qquickturbulence_p.h +++ b/src/particles/qquickturbulence_p.h @@ -44,8 +44,6 @@ #include "qquickparticleaffector_p.h" #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickParticlePainter; @@ -112,5 +110,4 @@ private: }; QT_END_NAMESPACE -QT_END_HEADER #endif // TURBULENCEAFFECTOR_H diff --git a/src/particles/qquickv8particledata_p.h b/src/particles/qquickv8particledata_p.h index 5b88dd9d8d..e3c1bf9324 100644 --- a/src/particles/qquickv8particledata_p.h +++ b/src/particles/qquickv8particledata_p.h @@ -44,8 +44,6 @@ #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickParticleData; @@ -61,7 +59,5 @@ private: QT_END_NAMESPACE -QT_END_HEADER - #endif diff --git a/src/particles/qquickwander_p.h b/src/particles/qquickwander_p.h index 54974d2d49..0f09418ea2 100644 --- a/src/particles/qquickwander_p.h +++ b/src/particles/qquickwander_p.h @@ -44,8 +44,6 @@ #include #include "qquickparticleaffector_p.h" -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE struct WanderData{ @@ -154,5 +152,4 @@ private: }; QT_END_NAMESPACE -QT_END_HEADER #endif // WANDERAFFECTOR_H diff --git a/src/qml/animations/qabstractanimationjob_p.h b/src/qml/animations/qabstractanimationjob_p.h index 29e263abe7..abf3cd6df6 100644 --- a/src/qml/animations/qabstractanimationjob_p.h +++ b/src/qml/animations/qabstractanimationjob_p.h @@ -47,8 +47,6 @@ #include #include "private/qpodvector_p.h" -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QAnimationGroupJob; @@ -235,6 +233,4 @@ Q_DECLARE_OPERATORS_FOR_FLAGS(QAbstractAnimationJob::ChangeTypes) QT_END_NAMESPACE -QT_END_HEADER - #endif // QABSTRACTANIMATIONJOB_P_H diff --git a/src/qml/animations/qanimationgroupjob_p.h b/src/qml/animations/qanimationgroupjob_p.h index 2c27e96691..0f62194656 100644 --- a/src/qml/animations/qanimationgroupjob_p.h +++ b/src/qml/animations/qanimationgroupjob_p.h @@ -44,8 +44,6 @@ #include "private/qabstractanimationjob_p.h" -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class Q_QML_PRIVATE_EXPORT QAnimationGroupJob : public QAbstractAnimationJob @@ -86,6 +84,4 @@ private: QT_END_NAMESPACE -QT_END_HEADER - #endif //QANIMATIONGROUPJOB_P_H diff --git a/src/qml/animations/qparallelanimationgroupjob_p.h b/src/qml/animations/qparallelanimationgroupjob_p.h index 5f896a8bc9..8e29402f33 100644 --- a/src/qml/animations/qparallelanimationgroupjob_p.h +++ b/src/qml/animations/qparallelanimationgroupjob_p.h @@ -44,8 +44,6 @@ #include "private/qanimationgroupjob_p.h" -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class Q_QML_PRIVATE_EXPORT QParallelAnimationGroupJob : public QAnimationGroupJob @@ -74,6 +72,4 @@ private: QT_END_NAMESPACE -QT_END_HEADER - #endif // QPARALLELANIMATIONGROUPJOB_P_H diff --git a/src/qml/animations/qpauseanimationjob_p.h b/src/qml/animations/qpauseanimationjob_p.h index 83e0553116..f84143d9bf 100644 --- a/src/qml/animations/qpauseanimationjob_p.h +++ b/src/qml/animations/qpauseanimationjob_p.h @@ -44,8 +44,6 @@ #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class Q_QML_PRIVATE_EXPORT QPauseAnimationJob : public QAbstractAnimationJob @@ -68,6 +66,4 @@ private: QT_END_NAMESPACE -QT_END_HEADER - #endif // QPAUSEANIMATIONJOB_P_H diff --git a/src/qml/animations/qsequentialanimationgroupjob_p.h b/src/qml/animations/qsequentialanimationgroupjob_p.h index fce83b094c..18dc6fcc7b 100644 --- a/src/qml/animations/qsequentialanimationgroupjob_p.h +++ b/src/qml/animations/qsequentialanimationgroupjob_p.h @@ -44,8 +44,6 @@ #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QPauseAnimationJob; @@ -101,6 +99,4 @@ private: QT_END_NAMESPACE -QT_END_HEADER - #endif //QSEQUENTIALANIMATIONGROUPJOB_P_H diff --git a/src/qml/debugger/qdebugmessageservice_p.h b/src/qml/debugger/qdebugmessageservice_p.h index 911f9fe3ed..dbd05646c9 100644 --- a/src/qml/debugger/qdebugmessageservice_p.h +++ b/src/qml/debugger/qdebugmessageservice_p.h @@ -57,8 +57,6 @@ #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QDebugMessageServicePrivate; @@ -84,6 +82,4 @@ private: QT_END_NAMESPACE -QT_END_HEADER - #endif // QDEBUGMESSAGESERVICE_P_H diff --git a/src/qml/debugger/qqmldebug.h b/src/qml/debugger/qqmldebug.h index 3232f6bb6f..d2b0220bc6 100644 --- a/src/qml/debugger/qqmldebug.h +++ b/src/qml/debugger/qqmldebug.h @@ -44,8 +44,6 @@ #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE @@ -63,6 +61,4 @@ static QQmlDebuggingEnabler qmlEnableDebuggingHelper(true); QT_END_NAMESPACE -QT_END_HEADER - #endif // QQMLDEBUG_H diff --git a/src/qml/debugger/qqmldebugserver_p.h b/src/qml/debugger/qqmldebugserver_p.h index d38b1d0147..27d54d6947 100644 --- a/src/qml/debugger/qqmldebugserver_p.h +++ b/src/qml/debugger/qqmldebugserver_p.h @@ -57,8 +57,6 @@ // We mean it. // -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE @@ -104,6 +102,4 @@ public: QT_END_NAMESPACE -QT_END_HEADER - #endif // QQMLDEBUGSERVICE_H diff --git a/src/qml/debugger/qqmldebugserverconnection_p.h b/src/qml/debugger/qqmldebugserverconnection_p.h index a622855071..82a02bb29e 100644 --- a/src/qml/debugger/qqmldebugserverconnection_p.h +++ b/src/qml/debugger/qqmldebugserverconnection_p.h @@ -56,8 +56,6 @@ // We mean it. // -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE @@ -82,6 +80,4 @@ Q_DECLARE_INTERFACE(QQmlDebugServerConnection, QQmlDebugServerConnection_iid) QT_END_NAMESPACE -QT_END_HEADER - #endif // QQMLDEBUGSERVERCONNECTION_H diff --git a/src/qml/debugger/qqmldebugservice_p.h b/src/qml/debugger/qqmldebugservice_p.h index 3e7b2f11f3..71a116f6a5 100644 --- a/src/qml/debugger/qqmldebugservice_p.h +++ b/src/qml/debugger/qqmldebugservice_p.h @@ -58,8 +58,6 @@ // We mean it. // -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE @@ -121,7 +119,5 @@ public: QT_END_NAMESPACE -QT_END_HEADER - #endif // QQMLDEBUGSERVICE_H diff --git a/src/qml/debugger/qqmldebugservice_p_p.h b/src/qml/debugger/qqmldebugservice_p_p.h index f81a8285eb..940990f628 100644 --- a/src/qml/debugger/qqmldebugservice_p_p.h +++ b/src/qml/debugger/qqmldebugservice_p_p.h @@ -56,8 +56,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE @@ -77,6 +75,4 @@ public: QT_END_NAMESPACE -QT_END_HEADER - #endif // QQMLDEBUGSERVICE_P_H diff --git a/src/qml/debugger/qqmldebugstatesdelegate_p.h b/src/qml/debugger/qqmldebugstatesdelegate_p.h index 8beb4303de..7197b8cc3b 100644 --- a/src/qml/debugger/qqmldebugstatesdelegate_p.h +++ b/src/qml/debugger/qqmldebugstatesdelegate_p.h @@ -57,8 +57,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE @@ -96,6 +94,4 @@ private: QT_END_NAMESPACE -QT_END_HEADER - #endif // QQMLDEBUGSTATESDELEGATE_P_H diff --git a/src/qml/debugger/qqmlinspectorinterface_p.h b/src/qml/debugger/qqmlinspectorinterface_p.h index 1bc25937e9..0b4020c4a7 100644 --- a/src/qml/debugger/qqmlinspectorinterface_p.h +++ b/src/qml/debugger/qqmlinspectorinterface_p.h @@ -56,8 +56,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE @@ -81,6 +79,4 @@ Q_DECLARE_INTERFACE(QQmlInspectorInterface, QQmlInspectorInterface_iid) QT_END_NAMESPACE -QT_END_HEADER - #endif // QQMLINSPECTORINTERFACE_H diff --git a/src/qml/debugger/qqmlinspectorservice_p.h b/src/qml/debugger/qqmlinspectorservice_p.h index de97e1798d..f8b2a39240 100644 --- a/src/qml/debugger/qqmlinspectorservice_p.h +++ b/src/qml/debugger/qqmlinspectorservice_p.h @@ -58,8 +58,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE @@ -96,6 +94,4 @@ private: QT_END_NAMESPACE -QT_END_HEADER - #endif // QQMLINSPECTORSERVICE_H diff --git a/src/qml/debugger/qqmlprofilerservice_p.h b/src/qml/debugger/qqmlprofilerservice_p.h index d5443aaef6..a50fb5ea08 100644 --- a/src/qml/debugger/qqmlprofilerservice_p.h +++ b/src/qml/debugger/qqmlprofilerservice_p.h @@ -64,8 +64,6 @@ #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE struct Q_AUTOTEST_EXPORT QQmlProfilerData @@ -292,7 +290,5 @@ struct QQmlCompilingProfiler { QT_END_NAMESPACE -QT_END_HEADER - #endif // QQMLPROFILERSERVICE_P_H diff --git a/src/qml/debugger/qv8debugservice_p.h b/src/qml/debugger/qv8debugservice_p.h index c93948c402..00ecf557c3 100644 --- a/src/qml/debugger/qv8debugservice_p.h +++ b/src/qml/debugger/qv8debugservice_p.h @@ -56,8 +56,6 @@ #include "qqmldebugservice_p.h" #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE @@ -100,6 +98,4 @@ private: QT_END_NAMESPACE -QT_END_HEADER - #endif // QV8DEBUGSERVICE_P_H diff --git a/src/qml/debugger/qv8profilerservice_p.h b/src/qml/debugger/qv8profilerservice_p.h index 6a8c1e8c3b..10906442f5 100644 --- a/src/qml/debugger/qv8profilerservice_p.h +++ b/src/qml/debugger/qv8profilerservice_p.h @@ -55,8 +55,6 @@ #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE @@ -115,6 +113,4 @@ private: QT_END_NAMESPACE -QT_END_HEADER - #endif // QV8PROFILERSERVICE_P_H diff --git a/src/qml/items/qqmldelegatemodel_p.h b/src/qml/items/qqmldelegatemodel_p.h index 20cc228043..0fccf5720d 100644 --- a/src/qml/items/qqmldelegatemodel_p.h +++ b/src/qml/items/qqmldelegatemodel_p.h @@ -52,8 +52,6 @@ #include #include -QT_BEGIN_HEADER - Q_DECLARE_METATYPE(QModelIndex) QT_BEGIN_NAMESPACE @@ -233,6 +231,4 @@ QML_DECLARE_TYPE(QQmlDelegateModel) QML_DECLARE_TYPEINFO(QQmlDelegateModel, QML_HAS_ATTACHED_PROPERTIES) QML_DECLARE_TYPE(QQmlDataGroup) -QT_END_HEADER - #endif // QQMLDATAMODEL_P_H diff --git a/src/qml/items/qqmlobjectmodel_p.h b/src/qml/items/qqmlobjectmodel_p.h index 5ec57bddc5..59a4a551a7 100644 --- a/src/qml/items/qqmlobjectmodel_p.h +++ b/src/qml/items/qqmlobjectmodel_p.h @@ -46,8 +46,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QObject; @@ -169,6 +167,4 @@ QML_DECLARE_TYPE(QQmlInstanceModel) QML_DECLARE_TYPE(QQmlObjectModel) QML_DECLARE_TYPEINFO(QQmlObjectModel, QML_HAS_ATTACHED_PROPERTIES) -QT_END_HEADER - #endif // QQMLINSTANCEMODEL_P_H diff --git a/src/qml/items/qquickpackage_p.h b/src/qml/items/qquickpackage_p.h index a777ff4a7e..9427c886a8 100644 --- a/src/qml/items/qquickpackage_p.h +++ b/src/qml/items/qquickpackage_p.h @@ -44,8 +44,6 @@ #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickPackagePrivate; @@ -91,6 +89,4 @@ QT_END_NAMESPACE QML_DECLARE_TYPE(QQuickPackage) QML_DECLARE_TYPEINFO(QQuickPackage, QML_HAS_ATTACHED_PROPERTIES) -QT_END_HEADER - #endif // QQUICKPACKAGE_H diff --git a/src/qml/qml/ftw/qqmlrefcount_p.h b/src/qml/qml/ftw/qqmlrefcount_p.h index 0bc3ea038a..24c3e7a2cc 100644 --- a/src/qml/qml/ftw/qqmlrefcount_p.h +++ b/src/qml/qml/ftw/qqmlrefcount_p.h @@ -56,8 +56,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE @@ -193,6 +191,4 @@ QQmlRefPointer &QQmlRefPointer::take(T *other) QT_END_NAMESPACE -QT_END_HEADER - #endif // QQMLREFCOUNT_P_H diff --git a/src/qml/qml/qqml.h b/src/qml/qml/qqml.h index 597c59eeff..877e6c16b7 100644 --- a/src/qml/qml/qqml.h +++ b/src/qml/qml/qqml.h @@ -50,8 +50,6 @@ #include #include -QT_BEGIN_HEADER - #define QML_VERSION 0x020000 #define QML_VERSION_STR "2.0" @@ -470,6 +468,4 @@ QT_END_NAMESPACE QML_DECLARE_TYPE(QObject) Q_DECLARE_METATYPE(QVariant) -QT_END_HEADER - #endif // QQML_H diff --git a/src/qml/qml/qqmlaccessors_p.h b/src/qml/qml/qqmlaccessors_p.h index 24b548c58c..6df624eaf1 100644 --- a/src/qml/qml/qqmlaccessors_p.h +++ b/src/qml/qml/qqmlaccessors_p.h @@ -52,8 +52,6 @@ #include #endif -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QObject; @@ -167,6 +165,4 @@ QQmlAccessorProperties::Properties::Properties() QT_END_NAMESPACE -QT_END_HEADER - #endif // QQMLACCESSORS_P_H diff --git a/src/qml/qml/qqmlbind_p.h b/src/qml/qml/qqmlbind_p.h index 04dc72a4c0..1e29c257f0 100644 --- a/src/qml/qml/qqmlbind_p.h +++ b/src/qml/qml/qqmlbind_p.h @@ -46,8 +46,6 @@ #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQmlBindPrivate; @@ -91,6 +89,4 @@ QT_END_NAMESPACE QML_DECLARE_TYPE(QQmlBind) -QT_END_HEADER - #endif diff --git a/src/qml/qml/qqmlcomponent.h b/src/qml/qml/qqmlcomponent.h index e2d49b32db..aefad475b4 100644 --- a/src/qml/qml/qqmlcomponent.h +++ b/src/qml/qml/qqmlcomponent.h @@ -49,8 +49,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE @@ -139,6 +137,4 @@ Q_DECLARE_METATYPE(QQmlComponent::Status) QML_DECLARE_TYPE(QQmlComponent) QML_DECLARE_TYPEINFO(QQmlComponent, QML_HAS_ATTACHED_PROPERTIES) -QT_END_HEADER - #endif // QQMLCOMPONENT_H diff --git a/src/qml/qml/qqmlcomponentattached_p.h b/src/qml/qml/qqmlcomponentattached_p.h index 9901821b94..3c27e795c6 100644 --- a/src/qml/qml/qqmlcomponentattached_p.h +++ b/src/qml/qml/qqmlcomponentattached_p.h @@ -45,8 +45,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE @@ -80,6 +78,4 @@ private: QT_END_NAMESPACE -QT_END_HEADER - #endif // QQMLCOMPONENTATTACHED_P_H diff --git a/src/qml/qml/qqmlconnections_p.h b/src/qml/qml/qqmlconnections_p.h index 15fba24df8..9bc668e5f4 100644 --- a/src/qml/qml/qqmlconnections_p.h +++ b/src/qml/qml/qqmlconnections_p.h @@ -48,8 +48,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQmlBoundSignal; @@ -95,6 +93,4 @@ QT_END_NAMESPACE QML_DECLARE_TYPE(QQmlConnections) -QT_END_HEADER - #endif diff --git a/src/qml/qml/qqmlcontext.h b/src/qml/qml/qqmlcontext.h index 5477b46e92..e191807cf4 100644 --- a/src/qml/qml/qqmlcontext.h +++ b/src/qml/qml/qqmlcontext.h @@ -48,8 +48,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE @@ -107,6 +105,4 @@ QT_END_NAMESPACE Q_DECLARE_METATYPE(QList) -QT_END_HEADER - #endif // QQMLCONTEXT_H diff --git a/src/qml/qml/qqmlcustomparser_p.h b/src/qml/qml/qqmlcustomparser_p.h index 3114c52870..7a3fd47b46 100644 --- a/src/qml/qml/qqmlcustomparser_p.h +++ b/src/qml/qml/qqmlcustomparser_p.h @@ -61,8 +61,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE @@ -163,6 +161,4 @@ QT_END_NAMESPACE Q_DECLARE_METATYPE(QQmlCustomParserProperty) Q_DECLARE_METATYPE(QQmlCustomParserNode) -QT_END_HEADER - #endif diff --git a/src/qml/qml/qqmlengine.h b/src/qml/qml/qqmlengine.h index 76e6ce1d75..9433fcbcb3 100644 --- a/src/qml/qml/qqmlengine.h +++ b/src/qml/qml/qqmlengine.h @@ -49,8 +49,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE @@ -157,6 +155,4 @@ private: QT_END_NAMESPACE -QT_END_HEADER - #endif // QQMLENGINE_H diff --git a/src/qml/qml/qqmlerror.h b/src/qml/qml/qqmlerror.h index c95e0c50b9..cea9ee4cc0 100644 --- a/src/qml/qml/qqmlerror.h +++ b/src/qml/qml/qqmlerror.h @@ -47,8 +47,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE @@ -84,6 +82,4 @@ Q_DECLARE_TYPEINFO(QQmlError, Q_MOVABLE_TYPE); QT_END_NAMESPACE -QT_END_HEADER - #endif // QQMLERROR_H diff --git a/src/qml/qml/qqmlexpression.h b/src/qml/qml/qqmlexpression.h index 8c3bf38581..b04abc1b98 100644 --- a/src/qml/qml/qqmlexpression.h +++ b/src/qml/qml/qqmlexpression.h @@ -48,8 +48,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE @@ -113,7 +111,5 @@ private: QT_END_NAMESPACE -QT_END_HEADER - #endif // QQMLEXPRESSION_H diff --git a/src/qml/qml/qqmlextensioninterface.h b/src/qml/qml/qqmlextensioninterface.h index 35facb0333..be2939d366 100644 --- a/src/qml/qml/qqmlextensioninterface.h +++ b/src/qml/qml/qqmlextensioninterface.h @@ -45,8 +45,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE @@ -76,6 +74,4 @@ Q_DECLARE_INTERFACE(QQmlExtensionInterface, QQmlExtensionInterface_iid) QT_END_NAMESPACE -QT_END_HEADER - #endif // QQMLEXTENSIONINTERFACE_H diff --git a/src/qml/qml/qqmlextensionplugin.h b/src/qml/qml/qqmlextensionplugin.h index a7f5da137a..8568565f22 100644 --- a/src/qml/qml/qqmlextensionplugin.h +++ b/src/qml/qml/qqmlextensionplugin.h @@ -46,8 +46,6 @@ #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE @@ -72,6 +70,4 @@ private: QT_END_NAMESPACE -QT_END_HEADER - #endif // QQMLEXTENSIONPLUGIN_H diff --git a/src/qml/qml/qqmlfile.h b/src/qml/qml/qqmlfile.h index 777bc0db7b..5e7a42d6bd 100644 --- a/src/qml/qml/qqmlfile.h +++ b/src/qml/qml/qqmlfile.h @@ -44,8 +44,6 @@ #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QUrl; @@ -119,6 +117,4 @@ private: QT_END_NAMESPACE -QT_END_HEADER - #endif // QQMLFILE_H diff --git a/src/qml/qml/qqmlglobal_p.h b/src/qml/qml/qqmlglobal_p.h index 2631fc459f..0ce026a558 100644 --- a/src/qml/qml/qqmlglobal_p.h +++ b/src/qml/qml/qqmlglobal_p.h @@ -48,8 +48,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE @@ -316,6 +314,4 @@ Q_AUTOTEST_EXPORT QQmlGuiProvider *QQml_guiProvider(); QT_END_NAMESPACE -QT_END_HEADER - #endif // QQMLGLOBAL_H diff --git a/src/qml/qml/qqmlincubator.h b/src/qml/qml/qqmlincubator.h index bbb232bfa2..4d3287a394 100644 --- a/src/qml/qml/qqmlincubator.h +++ b/src/qml/qml/qqmlincubator.h @@ -45,8 +45,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE @@ -125,6 +123,4 @@ private: QT_END_NAMESPACE -QT_END_HEADER - #endif // QQMLINCUBATOR_H diff --git a/src/qml/qml/qqmlinfo.h b/src/qml/qml/qqmlinfo.h index c2b2cb353e..16fca9428e 100644 --- a/src/qml/qml/qqmlinfo.h +++ b/src/qml/qml/qqmlinfo.h @@ -46,8 +46,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE @@ -98,6 +96,4 @@ Q_QML_EXPORT QQmlInfo qmlInfo(const QObject *me, const QList &errors) QT_END_NAMESPACE -QT_END_HEADER - #endif // QQMLINFO_H diff --git a/src/qml/qml/qqmllist.h b/src/qml/qml/qqmllist.h index d2425bc6bf..a27a2da9ae 100644 --- a/src/qml/qml/qqmllist.h +++ b/src/qml/qml/qqmllist.h @@ -46,8 +46,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE @@ -150,6 +148,4 @@ QT_END_NAMESPACE Q_DECLARE_METATYPE(QQmlListReference) -QT_END_HEADER - #endif // QQMLLIST_H diff --git a/src/qml/qml/qqmllistmodel_p.h b/src/qml/qml/qqmllistmodel_p.h index 04a6c230f6..70477115bd 100644 --- a/src/qml/qml/qqmllistmodel_p.h +++ b/src/qml/qml/qqmllistmodel_p.h @@ -55,8 +55,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE @@ -197,6 +195,4 @@ QT_END_NAMESPACE QML_DECLARE_TYPE(QQmlListModel) QML_DECLARE_TYPE(QQmlListElement) -QT_END_HEADER - #endif // QQMLLISTMODEL_H diff --git a/src/qml/qml/qqmllistmodel_p_p.h b/src/qml/qml/qqmllistmodel_p_p.h index d48edec0bf..f2720c6c39 100644 --- a/src/qml/qml/qqmllistmodel_p_p.h +++ b/src/qml/qml/qqmllistmodel_p_p.h @@ -58,8 +58,6 @@ #include "qqmlopenmetaobject_p.h" #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE @@ -376,7 +374,5 @@ QT_END_NAMESPACE Q_DECLARE_METATYPE(ListModel *); -QT_END_HEADER - #endif // QQUICKLISTMODEL_P_P_H diff --git a/src/qml/qml/qqmllistmodelworkeragent_p.h b/src/qml/qml/qqmllistmodelworkeragent_p.h index 9471d6663f..614017069c 100644 --- a/src/qml/qml/qqmllistmodelworkeragent_p.h +++ b/src/qml/qml/qqmllistmodelworkeragent_p.h @@ -60,8 +60,6 @@ #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE @@ -154,7 +152,5 @@ QT_END_NAMESPACE Q_DECLARE_METATYPE(QQmlListModelWorkerAgent::VariantRef) -QT_END_HEADER - #endif // QQUICKLISTMODELWORKERAGENT_P_H diff --git a/src/qml/qml/qqmllocale_p.h b/src/qml/qml/qqmllocale_p.h index 8015ca2bbe..25f98d9168 100644 --- a/src/qml/qml/qqmllocale_p.h +++ b/src/qml/qml/qqmllocale_p.h @@ -49,8 +49,6 @@ #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE @@ -133,6 +131,4 @@ private: QT_END_NAMESPACE -QT_END_HEADER - #endif diff --git a/src/qml/qml/qqmlmemoryprofiler_p.h b/src/qml/qml/qqmlmemoryprofiler_p.h index d0d96f20f9..dfc26aa8fb 100644 --- a/src/qml/qml/qqmlmemoryprofiler_p.h +++ b/src/qml/qml/qqmlmemoryprofiler_p.h @@ -44,7 +44,6 @@ #include -QT_BEGIN_HEADER QT_BEGIN_NAMESPACE class QUrl; @@ -76,6 +75,4 @@ public: #define QML_MEMORY_SCOPE_STRING(s) QQmlMemoryScope _qml_memory_scope(s) QT_END_NAMESPACE -QT_END_HEADER - #endif // QQMLMEMORYPROFILER_H diff --git a/src/qml/qml/qqmlnetworkaccessmanagerfactory.h b/src/qml/qml/qqmlnetworkaccessmanagerfactory.h index 6a79118c50..4c12e00c09 100644 --- a/src/qml/qml/qqmlnetworkaccessmanagerfactory.h +++ b/src/qml/qml/qqmlnetworkaccessmanagerfactory.h @@ -45,8 +45,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE @@ -61,6 +59,4 @@ public: QT_END_NAMESPACE -QT_END_HEADER - #endif // QQMLNETWORKACCESSMANAGERFACTORY_H diff --git a/src/qml/qml/qqmlopenmetaobject_p.h b/src/qml/qml/qqmlopenmetaobject_p.h index fd442e4ad9..a8c5744078 100644 --- a/src/qml/qml/qqmlopenmetaobject_p.h +++ b/src/qml/qml/qqmlopenmetaobject_p.h @@ -50,8 +50,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE @@ -130,6 +128,4 @@ private: QT_END_NAMESPACE -QT_END_HEADER - #endif // QQMLOPENMETAOBJECT_H diff --git a/src/qml/qml/qqmlparserstatus.h b/src/qml/qml/qqmlparserstatus.h index 253fe64022..d3447e7752 100644 --- a/src/qml/qml/qqmlparserstatus.h +++ b/src/qml/qml/qqmlparserstatus.h @@ -45,8 +45,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE @@ -72,6 +70,4 @@ Q_DECLARE_INTERFACE(QQmlParserStatus, QQmlParserStatus_iid) QT_END_NAMESPACE -QT_END_HEADER - #endif // QQMLPARSERSTATUS_H diff --git a/src/qml/qml/qqmlprivate.h b/src/qml/qml/qqmlprivate.h index e548f860be..4d345fad23 100644 --- a/src/qml/qml/qqmlprivate.h +++ b/src/qml/qml/qqmlprivate.h @@ -58,8 +58,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE @@ -265,6 +263,4 @@ namespace QQmlPrivate QT_END_NAMESPACE -QT_END_HEADER - #endif // QQMLPRIVATE_H diff --git a/src/qml/qml/qqmlproperty.h b/src/qml/qml/qqmlproperty.h index 0079fed09a..c3322c0048 100644 --- a/src/qml/qml/qqmlproperty.h +++ b/src/qml/qml/qqmlproperty.h @@ -45,8 +45,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE @@ -140,6 +138,4 @@ Q_DECLARE_TYPEINFO(QQmlProperty, Q_MOVABLE_TYPE); QT_END_NAMESPACE -QT_END_HEADER - #endif // QQMLPROPERTY_H diff --git a/src/qml/qml/qqmlpropertyvaluesource.h b/src/qml/qml/qqmlpropertyvaluesource.h index 38f06d0b02..ccf40eb6f8 100644 --- a/src/qml/qml/qqmlpropertyvaluesource.h +++ b/src/qml/qml/qqmlpropertyvaluesource.h @@ -45,8 +45,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE @@ -65,6 +63,4 @@ Q_DECLARE_INTERFACE(QQmlPropertyValueSource, QQmlPropertyValueSource_iid) QT_END_NAMESPACE -QT_END_HEADER - #endif // QQMLPROPERTYVALUESOURCE_H diff --git a/src/qml/qml/qqmlproxymetaobject_p.h b/src/qml/qml/qqmlproxymetaobject_p.h index 9f9c346d9f..c7b266835d 100644 --- a/src/qml/qml/qqmlproxymetaobject_p.h +++ b/src/qml/qml/qqmlproxymetaobject_p.h @@ -61,8 +61,6 @@ #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE @@ -93,7 +91,5 @@ private: QT_END_NAMESPACE -QT_END_HEADER - #endif // QQMLPROXYMETAOBJECT_P_H diff --git a/src/qml/qml/qqmlscript_p.h b/src/qml/qml/qqmlscript_p.h index 20cec3703d..54e7a67b65 100644 --- a/src/qml/qml/qqmlscript_p.h +++ b/src/qml/qml/qqmlscript_p.h @@ -62,8 +62,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE @@ -520,6 +518,4 @@ QT_END_NAMESPACE Q_DECLARE_METATYPE(QQmlScript::Variant) -QT_END_HEADER - #endif // QQMLSCRIPT_P_H diff --git a/src/qml/qml/qqmlscriptstring.h b/src/qml/qml/qqmlscriptstring.h index fc732877ab..5421ef95fc 100644 --- a/src/qml/qml/qqmlscriptstring.h +++ b/src/qml/qml/qqmlscriptstring.h @@ -47,8 +47,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE @@ -86,7 +84,5 @@ QT_END_NAMESPACE Q_DECLARE_METATYPE(QQmlScriptString) -QT_END_HEADER - #endif // QQMLSCRIPTSTRING_H diff --git a/src/qml/qml/qqmltimer_p.h b/src/qml/qml/qqmltimer_p.h index ff6d6207ff..c625522851 100644 --- a/src/qml/qml/qqmltimer_p.h +++ b/src/qml/qml/qqmltimer_p.h @@ -48,8 +48,6 @@ #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQmlTimerPrivate; @@ -106,6 +104,4 @@ QT_END_NAMESPACE QML_DECLARE_TYPE(QQmlTimer) -QT_END_HEADER - #endif diff --git a/src/qml/qml/qqmltypenotavailable_p.h b/src/qml/qml/qqmltypenotavailable_p.h index f2e829e2ac..2529db3e5f 100644 --- a/src/qml/qml/qqmltypenotavailable_p.h +++ b/src/qml/qml/qqmltypenotavailable_p.h @@ -44,8 +44,6 @@ #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE @@ -59,6 +57,4 @@ QT_END_NAMESPACE QML_DECLARE_TYPE(QQmlTypeNotAvailable) -QT_END_HEADER - #endif // QQMLTYPENOTAVAILABLE_H diff --git a/src/qml/qml/qquickworkerscript_p.h b/src/qml/qml/qquickworkerscript_p.h index e643751953..d568e58d0e 100644 --- a/src/qml/qml/qquickworkerscript_p.h +++ b/src/qml/qml/qquickworkerscript_p.h @@ -60,8 +60,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE @@ -125,6 +123,4 @@ QT_END_NAMESPACE QML_DECLARE_TYPE(QQuickWorkerScript) -QT_END_HEADER - #endif // QQUICKWORKERSCRIPT_P_H diff --git a/src/qml/qml/rewriter/textwriter_p.h b/src/qml/qml/rewriter/textwriter_p.h index 5c22a62a02..fdfd955d9f 100644 --- a/src/qml/qml/rewriter/textwriter_p.h +++ b/src/qml/qml/rewriter/textwriter_p.h @@ -47,7 +47,6 @@ #include #include -QT_BEGIN_HEADER QT_QML_BEGIN_NAMESPACE namespace QQmlJS { @@ -92,6 +91,4 @@ public: } // end of namespace QQmlJS QT_QML_END_NAMESPACE -QT_END_HEADER - #endif // TEXTWRITER_H diff --git a/src/qml/qml/v4/qv4bindings_p.h b/src/qml/qml/v4/qv4bindings_p.h index 6ea548c642..adb05ba1f4 100644 --- a/src/qml/qml/v4/qv4bindings_p.h +++ b/src/qml/qml/v4/qv4bindings_p.h @@ -59,8 +59,6 @@ #include "private/qv4instruction_p.h" #include "private/qpointervaluepair_p.h" -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE struct QV4Program; @@ -170,7 +168,5 @@ private: QT_END_NAMESPACE -QT_END_HEADER - #endif // QV4BINDINGS_P_H diff --git a/src/qml/qml/v4/qv4compiler_p.h b/src/qml/qml/v4/qv4compiler_p.h index c5175b2bbd..5b6cee2a55 100644 --- a/src/qml/qml/v4/qv4compiler_p.h +++ b/src/qml/qml/v4/qv4compiler_p.h @@ -61,8 +61,6 @@ Q_DECLARE_METATYPE(v8::Handle) -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQmlTypeNameCache; @@ -103,7 +101,5 @@ private: QT_END_NAMESPACE -QT_END_HEADER - #endif // QV4COMPILER_P_H diff --git a/src/qml/qml/v4/qv4compiler_p_p.h b/src/qml/qml/v4/qv4compiler_p_p.h index c2d6b8c362..58ec521a97 100644 --- a/src/qml/qml/v4/qv4compiler_p_p.h +++ b/src/qml/qml/v4/qv4compiler_p_p.h @@ -59,8 +59,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE template @@ -243,7 +241,5 @@ private: QT_END_NAMESPACE -QT_END_HEADER - #endif // QV4COMPILER_P_P_H diff --git a/src/qml/qml/v4/qv4instruction_p.h b/src/qml/qml/v4/qv4instruction_p.h index be3b4bef1e..9797abf69d 100644 --- a/src/qml/qml/v4/qv4instruction_p.h +++ b/src/qml/qml/v4/qv4instruction_p.h @@ -60,8 +60,6 @@ #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE #define FOR_EACH_V4_INSTR(F) \ @@ -481,7 +479,5 @@ private: QT_END_NAMESPACE -QT_END_HEADER - #endif // QV4INSTRUCTION_P_H diff --git a/src/qml/qml/v4/qv4ir_p.h b/src/qml/qml/v4/qv4ir_p.h index 08cfe43091..701f76d9e4 100644 --- a/src/qml/qml/v4/qv4ir_p.h +++ b/src/qml/qml/v4/qv4ir_p.h @@ -69,8 +69,6 @@ # undef CONST #endif -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QTextStream; @@ -614,6 +612,4 @@ private: QT_END_NAMESPACE -QT_END_HEADER - #endif // QV4IR_P_H diff --git a/src/qml/qml/v4/qv4irbuilder_p.h b/src/qml/qml/v4/qv4irbuilder_p.h index dc0d62f438..86baae463d 100644 --- a/src/qml/qml/v4/qv4irbuilder_p.h +++ b/src/qml/qml/v4/qv4irbuilder_p.h @@ -46,8 +46,6 @@ #include "qv4ir_p.h" -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QV4IRBuilder : public QQmlJS::AST::Visitor @@ -238,6 +236,4 @@ private: QT_END_NAMESPACE -QT_END_HEADER - #endif // QV4IRBUILDER_P_H diff --git a/src/qml/qml/v4/qv4program_p.h b/src/qml/qml/v4/qv4program_p.h index d04ada487e..fb23e863af 100644 --- a/src/qml/qml/v4/qv4program_p.h +++ b/src/qml/qml/v4/qv4program_p.h @@ -60,8 +60,6 @@ # pragma warning( disable : 4200 ) #endif -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE struct QV4Program { @@ -126,7 +124,5 @@ QV4Program::BindingReferenceList *QV4Program::signalTable(int signalIndex) const QT_END_NAMESPACE -QT_END_HEADER - #endif // QV4PROGRAM_P_H diff --git a/src/qml/qml/v8/qjsengine.h b/src/qml/qml/v8/qjsengine.h index 23ee289d6c..0a575f84e9 100644 --- a/src/qml/qml/v8/qjsengine.h +++ b/src/qml/qml/v8/qjsengine.h @@ -49,8 +49,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE @@ -137,6 +135,4 @@ inline QVariant qjsvalue_cast(const QJSValue &value) QT_END_NAMESPACE -QT_END_HEADER - #endif // QJSENGINE_H diff --git a/src/qml/qml/v8/qjsvalue.h b/src/qml/qml/v8/qjsvalue.h index 0a57e3533e..efd52ce880 100644 --- a/src/qml/qml/v8/qjsvalue.h +++ b/src/qml/qml/v8/qjsvalue.h @@ -48,8 +48,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE @@ -157,6 +155,4 @@ QT_END_NAMESPACE Q_DECLARE_METATYPE(QJSValue) -QT_END_HEADER - #endif diff --git a/src/qml/qml/v8/qjsvalueiterator.h b/src/qml/qml/v8/qjsvalueiterator.h index e9a67738ab..b4f90a44b7 100644 --- a/src/qml/qml/v8/qjsvalueiterator.h +++ b/src/qml/qml/v8/qjsvalueiterator.h @@ -46,8 +46,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE @@ -77,6 +75,4 @@ private: QT_END_NAMESPACE -QT_END_HEADER - #endif // QSCRIPTVALUEITERATOR_H diff --git a/src/qml/qml/v8/qv8bindings_p.h b/src/qml/qml/v8/qv8bindings_p.h index f3e62faa3b..98b367ac72 100644 --- a/src/qml/qml/v8/qv8bindings_p.h +++ b/src/qml/qml/v8/qv8bindings_p.h @@ -61,8 +61,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQmlCompiledData; @@ -158,8 +156,6 @@ void QV8Bindings::release() QT_END_NAMESPACE -QT_END_HEADER - #endif // QV8BINDINGS_P_H diff --git a/src/qml/qml/v8/qv8domerrors_p.h b/src/qml/qml/v8/qv8domerrors_p.h index 0542863c32..8fd1ccb6d6 100644 --- a/src/qml/qml/v8/qv8domerrors_p.h +++ b/src/qml/qml/v8/qv8domerrors_p.h @@ -55,8 +55,6 @@ #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE // From DOM-Level-3-Core spec // http://www.w3.org/TR/DOM-Level-3-Core/core.html @@ -89,6 +87,4 @@ void qt_add_domexceptions(QV8Engine *engine); QT_END_NAMESPACE -QT_END_HEADER - #endif // QV8DOMERRORS_P_H diff --git a/src/qml/qml/v8/qv8sqlerrors_p.h b/src/qml/qml/v8/qv8sqlerrors_p.h index b2ffbf9233..8a612d69ab 100644 --- a/src/qml/qml/v8/qv8sqlerrors_p.h +++ b/src/qml/qml/v8/qv8sqlerrors_p.h @@ -55,8 +55,6 @@ #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE #define SQLEXCEPTION_UNKNOWN_ERR 1 #define SQLEXCEPTION_DATABASE_ERR 2 @@ -72,6 +70,4 @@ void qt_add_sqlexceptions(QV8Engine *engine); QT_END_NAMESPACE -QT_END_HEADER - #endif // QV8SQLERRORS_P_H diff --git a/src/qml/qtqmlglobal.h b/src/qml/qtqmlglobal.h index e0668dfb90..07a4136e3a 100644 --- a/src/qml/qtqmlglobal.h +++ b/src/qml/qtqmlglobal.h @@ -44,7 +44,6 @@ #include -QT_BEGIN_HEADER QT_BEGIN_NAMESPACE #ifndef QT_STATIC @@ -58,6 +57,4 @@ QT_BEGIN_NAMESPACE #endif QT_END_NAMESPACE -QT_END_HEADER - #endif // QTQMLGLOBAL_H diff --git a/src/qml/util/qqmladaptormodel_p.h b/src/qml/util/qqmladaptormodel_p.h index 6e9af8ebda..399350d725 100644 --- a/src/qml/util/qqmladaptormodel_p.h +++ b/src/qml/util/qqmladaptormodel_p.h @@ -48,8 +48,6 @@ #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQmlEngine; diff --git a/src/qml/util/qqmllistaccessor_p.h b/src/qml/util/qqmllistaccessor_p.h index 71f7542105..8f3fb41186 100644 --- a/src/qml/util/qqmllistaccessor_p.h +++ b/src/qml/util/qqmllistaccessor_p.h @@ -44,8 +44,6 @@ #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQmlEngine; @@ -73,6 +71,4 @@ private: QT_END_NAMESPACE -QT_END_HEADER - #endif // QQMLLISTACCESSOR_H diff --git a/src/qml/util/qqmlpropertymap.h b/src/qml/util/qqmlpropertymap.h index cd3516a907..c327fd8ce4 100644 --- a/src/qml/util/qqmlpropertymap.h +++ b/src/qml/util/qqmlpropertymap.h @@ -49,8 +49,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE @@ -100,6 +98,4 @@ private: QT_END_NAMESPACE -QT_END_HEADER - #endif diff --git a/src/qmltest/qtestoptions_p.h b/src/qmltest/qtestoptions_p.h index 36706b2e31..ac2d428b4e 100644 --- a/src/qmltest/qtestoptions_p.h +++ b/src/qmltest/qtestoptions_p.h @@ -47,8 +47,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE @@ -61,6 +59,4 @@ namespace QTest QT_END_NAMESPACE -QT_END_HEADER - #endif diff --git a/src/qmltest/quicktest.h b/src/qmltest/quicktest.h index d20d71aec4..d6ffcf86a8 100644 --- a/src/qmltest/quicktest.h +++ b/src/qmltest/quicktest.h @@ -44,8 +44,6 @@ #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE Q_QUICK_TEST_EXPORT int quick_test_main(int argc, char **argv, const char *name, const char *sourceDir); @@ -82,6 +80,4 @@ Q_QUICK_TEST_EXPORT int quick_test_main(int argc, char **argv, const char *name, QT_END_NAMESPACE -QT_END_HEADER - #endif diff --git a/src/qmltest/quicktestglobal.h b/src/qmltest/quicktestglobal.h index d9a3b001d3..32461fd9a7 100644 --- a/src/qmltest/quicktestglobal.h +++ b/src/qmltest/quicktestglobal.h @@ -44,7 +44,6 @@ #include -QT_BEGIN_HEADER QT_BEGIN_NAMESPACE #ifndef QT_STATIC @@ -58,6 +57,5 @@ QT_BEGIN_NAMESPACE #endif QT_END_NAMESPACE -QT_END_HEADER #endif diff --git a/src/quick/designer/designersupport.h b/src/quick/designer/designersupport.h index 63809c3c3b..c93cffa7fa 100644 --- a/src/quick/designer/designersupport.h +++ b/src/quick/designer/designersupport.h @@ -59,8 +59,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickItem; @@ -157,6 +155,4 @@ private: QT_END_NAMESPACE -QT_END_HEADER - #endif // DESIGNERSUPPORT_H diff --git a/src/quick/designer/designerwindowmanager_p.h b/src/quick/designer/designerwindowmanager_p.h index 878d236314..02aacf06bd 100644 --- a/src/quick/designer/designerwindowmanager_p.h +++ b/src/quick/designer/designerwindowmanager_p.h @@ -60,8 +60,6 @@ #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickWindow; @@ -103,5 +101,4 @@ private: QT_END_NAMESPACE -QT_END_HEADER #endif // DESIGNERWINDOWMANAGER_P_H diff --git a/src/quick/items/context2d/qquickcanvascontext_p.h b/src/quick/items/context2d/qquickcanvascontext_p.h index bfea0e5d5b..d15f5d945d 100644 --- a/src/quick/items/context2d/qquickcanvascontext_p.h +++ b/src/quick/items/context2d/qquickcanvascontext_p.h @@ -46,8 +46,6 @@ #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickCanvasItem; @@ -83,6 +81,4 @@ Q_SIGNALS: QT_END_NAMESPACE -QT_END_HEADER - #endif //QQUICKCANVASCONTEXT_P_H diff --git a/src/quick/items/context2d/qquickcanvasitem_p.h b/src/quick/items/context2d/qquickcanvasitem_p.h index d2a907554e..c53e4f952a 100644 --- a/src/quick/items/context2d/qquickcanvasitem_p.h +++ b/src/quick/items/context2d/qquickcanvasitem_p.h @@ -47,8 +47,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickCanvasContext; @@ -207,6 +205,4 @@ QT_END_NAMESPACE QML_DECLARE_TYPE(QQuickCanvasItem) -QT_END_HEADER - #endif //QQUICKCANVASITEM_P_H diff --git a/src/quick/items/context2d/qquickcontext2d_p.h b/src/quick/items/context2d/qquickcontext2d_p.h index 8d96ab579b..24f5c44f18 100644 --- a/src/quick/items/context2d/qquickcontext2d_p.h +++ b/src/quick/items/context2d/qquickcontext2d_p.h @@ -62,8 +62,6 @@ #include #endif -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickContext2DCommandBuffer; @@ -257,6 +255,4 @@ public: QT_END_NAMESPACE QML_DECLARE_TYPE(QQuickContext2D) -QT_END_HEADER - #endif // QQUICKCONTEXT2D_P_H diff --git a/src/quick/items/context2d/qquickcontext2dcommandbuffer_p.h b/src/quick/items/context2d/qquickcontext2dcommandbuffer_p.h index 0247a9e286..ff0a3a4e20 100644 --- a/src/quick/items/context2d/qquickcontext2dcommandbuffer_p.h +++ b/src/quick/items/context2d/qquickcontext2dcommandbuffer_p.h @@ -45,8 +45,6 @@ #include #include "qquickcontext2d_p.h" -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickCanvasItem; @@ -266,8 +264,6 @@ private: QMutex queueLock; }; -QT_END_HEADER - QT_END_NAMESPACE #endif // QQUICKCONTEXT2DCOMMANDBUFFER_P_H diff --git a/src/quick/items/context2d/qquickcontext2dtexture_p.h b/src/quick/items/context2d/qquickcontext2dtexture_p.h index 8fe3168533..df96a0eda1 100644 --- a/src/quick/items/context2d/qquickcontext2dtexture_p.h +++ b/src/quick/items/context2d/qquickcontext2dtexture_p.h @@ -53,8 +53,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickContext2DTile; @@ -180,8 +178,6 @@ private: QSGPlainTexture* m_texture; }; -QT_END_HEADER - QT_END_NAMESPACE #endif // QQUICKCONTEXT2DTEXTURE_P_H diff --git a/src/quick/items/context2d/qquickcontext2dtile_p.h b/src/quick/items/context2d/qquickcontext2dtile_p.h index 60415ee63d..22cbbe6a30 100644 --- a/src/quick/items/context2d/qquickcontext2dtile_p.h +++ b/src/quick/items/context2d/qquickcontext2dtile_p.h @@ -45,8 +45,6 @@ #include "qquickcontext2d_p.h" #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickContext2DTexture; @@ -103,8 +101,6 @@ public: private: QImage m_image; }; -QT_END_HEADER - QT_END_NAMESPACE #endif // QQUICKCONTEXT2DTILE_P_H diff --git a/src/quick/items/qquickaccessibleattached_p.h b/src/quick/items/qquickaccessibleattached_p.h index 15261232b2..2124356a70 100644 --- a/src/quick/items/qquickaccessibleattached_p.h +++ b/src/quick/items/qquickaccessibleattached_p.h @@ -52,8 +52,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE @@ -165,8 +163,6 @@ QT_END_NAMESPACE QML_DECLARE_TYPE(QQuickAccessibleAttached) QML_DECLARE_TYPEINFO(QQuickAccessibleAttached, QML_HAS_ATTACHED_PROPERTIES) -QT_END_HEADER - #endif // QT_NO_ACCESSIBILITY #endif diff --git a/src/quick/items/qquickanchors_p.h b/src/quick/items/qquickanchors_p.h index df3577c42d..1f18961caf 100644 --- a/src/quick/items/qquickanchors_p.h +++ b/src/quick/items/qquickanchors_p.h @@ -48,8 +48,6 @@ #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickItem; @@ -200,6 +198,4 @@ QT_END_NAMESPACE QML_DECLARE_TYPE(QQuickAnchors) -QT_END_HEADER - #endif // QQUICKANCHORS_P_H diff --git a/src/quick/items/qquickanimatedimage_p.h b/src/quick/items/qquickanimatedimage_p.h index 83bae59af8..2775023d4e 100644 --- a/src/quick/items/qquickanimatedimage_p.h +++ b/src/quick/items/qquickanimatedimage_p.h @@ -46,8 +46,6 @@ #ifndef QT_NO_MOVIE -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QMovie; @@ -108,8 +106,6 @@ QT_END_NAMESPACE QML_DECLARE_TYPE(QQuickAnimatedImage) -QT_END_HEADER - #endif // QT_NO_MOVIE #endif // QQUICKANIMATEDIMAGE_P_H diff --git a/src/quick/items/qquickanimatedsprite_p.h b/src/quick/items/qquickanimatedsprite_p.h index 47dd51c3ee..6d0a821a47 100644 --- a/src/quick/items/qquickanimatedsprite_p.h +++ b/src/quick/items/qquickanimatedsprite_p.h @@ -46,8 +46,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QSGContext; @@ -378,6 +376,4 @@ private: QT_END_NAMESPACE -QT_END_HEADER - #endif // QQUICKANIMATEDSPRITE_P_H diff --git a/src/quick/items/qquickborderimage_p.h b/src/quick/items/qquickborderimage_p.h index 1cc86bc832..2ba69a383d 100644 --- a/src/quick/items/qquickborderimage_p.h +++ b/src/quick/items/qquickborderimage_p.h @@ -44,7 +44,6 @@ #include "qquickimagebase_p.h" -QT_BEGIN_HEADER QT_BEGIN_NAMESPACE class QQuickScaleGrid; @@ -102,6 +101,5 @@ private: QT_END_NAMESPACE QML_DECLARE_TYPE(QQuickBorderImage) -QT_END_HEADER #endif // QQUICKBORDERIMAGE_P_H diff --git a/src/quick/items/qquickdrag_p.h b/src/quick/items/qquickdrag_p.h index 5e1448aab6..c1835d9504 100644 --- a/src/quick/items/qquickdrag_p.h +++ b/src/quick/items/qquickdrag_p.h @@ -51,8 +51,6 @@ #ifndef QT_NO_DRAGANDDROP -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickItem; @@ -205,8 +203,6 @@ private: QT_END_NAMESPACE -QT_END_HEADER - #endif // QT_NO_DRAGANDDROP #endif diff --git a/src/quick/items/qquickdroparea_p.h b/src/quick/items/qquickdroparea_p.h index 14921a2d28..bfc3f922b5 100644 --- a/src/quick/items/qquickdroparea_p.h +++ b/src/quick/items/qquickdroparea_p.h @@ -51,8 +51,6 @@ #ifndef QT_NO_DRAGANDDROP -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickDropAreaPrivate; @@ -162,8 +160,6 @@ QT_END_NAMESPACE QML_DECLARE_TYPE(QQuickDropEvent) QML_DECLARE_TYPE(QQuickDropArea) -QT_END_HEADER - #endif // QT_NO_DRAGANDDROP #endif // QQUICKDROPAREA_P_H diff --git a/src/quick/items/qquickflickable_p.h b/src/quick/items/qquickflickable_p.h index 2e16f11882..5d36ea01ac 100644 --- a/src/quick/items/qquickflickable_p.h +++ b/src/quick/items/qquickflickable_p.h @@ -45,8 +45,6 @@ #include "qquickitem.h" #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickFlickablePrivate; @@ -278,6 +276,4 @@ QT_END_NAMESPACE QML_DECLARE_TYPE(QQuickFlickable) -QT_END_HEADER - #endif // QQUICKFLICKABLE_P_H diff --git a/src/quick/items/qquickflipable_p.h b/src/quick/items/qquickflipable_p.h index 1bc6f4113d..795dc4f809 100644 --- a/src/quick/items/qquickflipable_p.h +++ b/src/quick/items/qquickflipable_p.h @@ -48,8 +48,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickFlipablePrivate; @@ -96,6 +94,4 @@ QT_END_NAMESPACE QML_DECLARE_TYPE(QQuickFlipable) -QT_END_HEADER - #endif // QQUICKFLIPABLE_P_H diff --git a/src/quick/items/qquickfocusscope_p.h b/src/quick/items/qquickfocusscope_p.h index d72eae0805..965c7538bf 100644 --- a/src/quick/items/qquickfocusscope_p.h +++ b/src/quick/items/qquickfocusscope_p.h @@ -44,8 +44,6 @@ #include "qquickitem.h" -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class Q_AUTOTEST_EXPORT QQuickFocusScope : public QQuickItem @@ -60,6 +58,4 @@ QT_END_NAMESPACE QML_DECLARE_TYPE(QQuickFocusScope) -QT_END_HEADER - #endif // QQUICKFOCUSSCOPE_P_H diff --git a/src/quick/items/qquickgridview_p.h b/src/quick/items/qquickgridview_p.h index a1c0ce9389..d98de1050f 100644 --- a/src/quick/items/qquickgridview_p.h +++ b/src/quick/items/qquickgridview_p.h @@ -46,8 +46,6 @@ #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickGridViewAttached; @@ -144,6 +142,4 @@ QT_END_NAMESPACE QML_DECLARE_TYPE(QQuickGridView) QML_DECLARE_TYPEINFO(QQuickGridView, QML_HAS_ATTACHED_PROPERTIES) -QT_END_HEADER - #endif // QQUICKGRIDVIEW_P_H diff --git a/src/quick/items/qquickimage_p.h b/src/quick/items/qquickimage_p.h index 9437b47635..b1f27a64ba 100644 --- a/src/quick/items/qquickimage_p.h +++ b/src/quick/items/qquickimage_p.h @@ -45,8 +45,6 @@ #include "qquickimagebase_p.h" #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickImagePrivate; @@ -114,6 +112,4 @@ private: QT_END_NAMESPACE QML_DECLARE_TYPE(QQuickImage) -QT_END_HEADER - #endif // QQUICKIMAGE_P_H diff --git a/src/quick/items/qquickimagebase_p.h b/src/quick/items/qquickimagebase_p.h index f5ed69129d..388873b4d3 100644 --- a/src/quick/items/qquickimagebase_p.h +++ b/src/quick/items/qquickimagebase_p.h @@ -45,8 +45,6 @@ #include "qquickimplicitsizeitem_p.h" #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickImageBasePrivate; @@ -114,6 +112,4 @@ private: QT_END_NAMESPACE -QT_END_HEADER - #endif // QQUICKIMAGEBASE_P_H diff --git a/src/quick/items/qquickimplicitsizeitem_p.h b/src/quick/items/qquickimplicitsizeitem_p.h index 6e306d8375..54b94dd183 100644 --- a/src/quick/items/qquickimplicitsizeitem_p.h +++ b/src/quick/items/qquickimplicitsizeitem_p.h @@ -45,8 +45,6 @@ #include "qquickpainteditem.h" #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickImplicitSizeItemPrivate; @@ -70,6 +68,4 @@ private: QT_END_NAMESPACE -QT_END_HEADER - #endif // QQUICKIMPLICITSIZEITEM_H diff --git a/src/quick/items/qquickitem.h b/src/quick/items/qquickitem.h index b3ac9339a6..e9c817a580 100644 --- a/src/quick/items/qquickitem.h +++ b/src/quick/items/qquickitem.h @@ -52,8 +52,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickItem; @@ -446,6 +444,4 @@ QT_END_NAMESPACE QML_DECLARE_TYPE(QQuickItem) QML_DECLARE_TYPE(QQuickTransform) -QT_END_HEADER - #endif // QQUICKITEM_H diff --git a/src/quick/items/qquickitemanimation_p.h b/src/quick/items/qquickitemanimation_p.h index 1dfce5233c..796f6c8ade 100644 --- a/src/quick/items/qquickitemanimation_p.h +++ b/src/quick/items/qquickitemanimation_p.h @@ -46,8 +46,6 @@ #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickParentAnimationPrivate; @@ -198,6 +196,4 @@ QML_DECLARE_TYPE(QQuickParentAnimation) QML_DECLARE_TYPE(QQuickAnchorAnimation) QML_DECLARE_TYPE(QQuickPathAnimation) -QT_END_HEADER - #endif // QQUICKITEMANIMATION_H diff --git a/src/quick/items/qquickitemsmodule_p.h b/src/quick/items/qquickitemsmodule_p.h index d21f182cc6..bbc7c5c9e2 100644 --- a/src/quick/items/qquickitemsmodule_p.h +++ b/src/quick/items/qquickitemsmodule_p.h @@ -44,8 +44,6 @@ #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickItemsModule @@ -56,7 +54,5 @@ public: QT_END_NAMESPACE -QT_END_HEADER - #endif // QQUICKITEMSMODULE_P_H diff --git a/src/quick/items/qquickitemview_p.h b/src/quick/items/qquickitemview_p.h index f94137100d..b0f910680a 100644 --- a/src/quick/items/qquickitemview_p.h +++ b/src/quick/items/qquickitemview_p.h @@ -44,8 +44,6 @@ #include "qquickflickable_p.h" -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQmlChangeSet; @@ -352,7 +350,5 @@ public: QT_END_NAMESPACE -QT_END_HEADER - #endif // QQUICKITEMVIEW_P_H diff --git a/src/quick/items/qquickitemview_p_p.h b/src/quick/items/qquickitemview_p_p.h index af895fb983..1fa933ebd1 100644 --- a/src/quick/items/qquickitemview_p_p.h +++ b/src/quick/items/qquickitemview_p_p.h @@ -50,8 +50,6 @@ #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE @@ -364,6 +362,4 @@ protected: QT_END_NAMESPACE -QT_END_HEADER - #endif // QQUICKITEMVIEW_P_P_H diff --git a/src/quick/items/qquickitemviewtransition_p.h b/src/quick/items/qquickitemviewtransition_p.h index 2667971fd7..4fb4386c57 100644 --- a/src/quick/items/qquickitemviewtransition_p.h +++ b/src/quick/items/qquickitemviewtransition_p.h @@ -44,8 +44,6 @@ #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickItem; @@ -211,6 +209,4 @@ QT_END_NAMESPACE QML_DECLARE_TYPE(QQuickViewTransitionAttached) QML_DECLARE_TYPEINFO(QQuickViewTransitionAttached, QML_HAS_ATTACHED_PROPERTIES) -QT_END_HEADER - #endif // QQUICKITEMVIEWTRANSITION_P_P_H diff --git a/src/quick/items/qquicklistview_p.h b/src/quick/items/qquicklistview_p.h index e0ad1ffc1b..3e766f8068 100644 --- a/src/quick/items/qquicklistview_p.h +++ b/src/quick/items/qquicklistview_p.h @@ -46,8 +46,6 @@ #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickListView; @@ -205,6 +203,4 @@ QML_DECLARE_TYPEINFO(QQuickListView, QML_HAS_ATTACHED_PROPERTIES) QML_DECLARE_TYPE(QQuickListView) QML_DECLARE_TYPE(QQuickViewSection) -QT_END_HEADER - #endif // QQUICKLISTVIEW_P_H diff --git a/src/quick/items/qquickloader_p.h b/src/quick/items/qquickloader_p.h index ebf9e16c1e..6a69ccd32f 100644 --- a/src/quick/items/qquickloader_p.h +++ b/src/quick/items/qquickloader_p.h @@ -44,8 +44,6 @@ #include "qquickimplicitsizeitem_p.h" -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickLoaderPrivate; @@ -115,6 +113,4 @@ QT_END_NAMESPACE QML_DECLARE_TYPE(QQuickLoader) -QT_END_HEADER - #endif // QQUICKLOADER_P_H diff --git a/src/quick/items/qquickmousearea_p.h b/src/quick/items/qquickmousearea_p.h index eb49535d95..ad15167b10 100644 --- a/src/quick/items/qquickmousearea_p.h +++ b/src/quick/items/qquickmousearea_p.h @@ -46,8 +46,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickMouseEvent; @@ -252,6 +250,4 @@ QML_DECLARE_TYPEINFO(QQuickDrag, QML_HAS_ATTACHED_PROPERTIES) #endif QML_DECLARE_TYPE(QQuickMouseArea) -QT_END_HEADER - #endif // QQUICKMOUSEAREA_P_H diff --git a/src/quick/items/qquickmultipointtoucharea_p.h b/src/quick/items/qquickmultipointtoucharea_p.h index 1ab934e06d..e2ae5ed24e 100644 --- a/src/quick/items/qquickmultipointtoucharea_p.h +++ b/src/quick/items/qquickmultipointtoucharea_p.h @@ -50,8 +50,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickMultiPointTouchArea; @@ -271,6 +269,4 @@ QML_DECLARE_TYPE(QQuickTouchPoint) QML_DECLARE_TYPE(QQuickGrabGestureEvent) QML_DECLARE_TYPE(QQuickMultiPointTouchArea) -QT_END_HEADER - #endif // QQUICKMULTIPOINTTOUCHAREA_H diff --git a/src/quick/items/qquickpainteditem.h b/src/quick/items/qquickpainteditem.h index d2d7a54467..88924003b8 100644 --- a/src/quick/items/qquickpainteditem.h +++ b/src/quick/items/qquickpainteditem.h @@ -45,8 +45,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickPaintedItemPrivate; @@ -125,6 +123,4 @@ Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickPaintedItem::PerformanceHints) QT_END_NAMESPACE -QT_END_HEADER - #endif // QQUICKPAINTEDITEM_P_H diff --git a/src/quick/items/qquickpathview_p.h b/src/quick/items/qquickpathview_p.h index 1ee23edf5b..998707aa41 100644 --- a/src/quick/items/qquickpathview_p.h +++ b/src/quick/items/qquickpathview_p.h @@ -46,8 +46,6 @@ #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQmlChangeSet; @@ -281,6 +279,5 @@ QT_END_NAMESPACE QML_DECLARE_TYPE(QQuickPathView) QML_DECLARE_TYPEINFO(QQuickPathView, QML_HAS_ATTACHED_PROPERTIES) -QT_END_HEADER #endif // QQUICKPATHVIEW_P_H diff --git a/src/quick/items/qquickpincharea_p.h b/src/quick/items/qquickpincharea_p.h index 2f22b4e4a7..4fc77d7f9c 100644 --- a/src/quick/items/qquickpincharea_p.h +++ b/src/quick/items/qquickpincharea_p.h @@ -44,8 +44,6 @@ #include "qquickitem.h" -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class Q_AUTOTEST_EXPORT QQuickPinch : public QObject @@ -301,7 +299,5 @@ QML_DECLARE_TYPE(QQuickPinch) QML_DECLARE_TYPE(QQuickPinchEvent) QML_DECLARE_TYPE(QQuickPinchArea) -QT_END_HEADER - #endif // QQUICKPINCHAREA_H diff --git a/src/quick/items/qquickpositioners_p.h b/src/quick/items/qquickpositioners_p.h index 784efea510..aeb722dd60 100644 --- a/src/quick/items/qquickpositioners_p.h +++ b/src/quick/items/qquickpositioners_p.h @@ -51,8 +51,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickBasePositionerPrivate; @@ -318,6 +316,4 @@ QML_DECLARE_TYPE(QQuickFlow) QML_DECLARE_TYPE(QQuickBasePositioner) QML_DECLARE_TYPEINFO(QQuickBasePositioner, QML_HAS_ATTACHED_PROPERTIES) -QT_END_HEADER - #endif // QQUICKPOSITIONERS_P_H diff --git a/src/quick/items/qquickrectangle_p.h b/src/quick/items/qquickrectangle_p.h index 911c3fdf8b..2ad144aafd 100644 --- a/src/quick/items/qquickrectangle_p.h +++ b/src/quick/items/qquickrectangle_p.h @@ -48,8 +48,6 @@ #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class Q_AUTOTEST_EXPORT QQuickPen : public QObject @@ -178,6 +176,4 @@ QML_DECLARE_TYPE(QQuickGradientStop) QML_DECLARE_TYPE(QQuickGradient) QML_DECLARE_TYPE(QQuickRectangle) -QT_END_HEADER - #endif // QQUICKRECTANGLE_P_H diff --git a/src/quick/items/qquickrepeater_p.h b/src/quick/items/qquickrepeater_p.h index 8698b8224f..be9ed7266d 100644 --- a/src/quick/items/qquickrepeater_p.h +++ b/src/quick/items/qquickrepeater_p.h @@ -44,8 +44,6 @@ #include "qquickitem.h" -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQmlChangeSet; @@ -104,6 +102,4 @@ QT_END_NAMESPACE QML_DECLARE_TYPE(QQuickRepeater) -QT_END_HEADER - #endif // QQUICKREPEATER_P_H diff --git a/src/quick/items/qquickscalegrid_p_p.h b/src/quick/items/qquickscalegrid_p_p.h index b10785f60e..264c444229 100644 --- a/src/quick/items/qquickscalegrid_p_p.h +++ b/src/quick/items/qquickscalegrid_p_p.h @@ -50,8 +50,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickScaleGrid : public QObject @@ -126,6 +124,4 @@ QT_END_NAMESPACE QML_DECLARE_TYPE(QQuickScaleGrid) -QT_END_HEADER - #endif // QQUICKSCALEGRID_P_P_H diff --git a/src/quick/items/qquickscreen_p.h b/src/quick/items/qquickscreen_p.h index 014fc0709b..98f38b7154 100644 --- a/src/quick/items/qquickscreen_p.h +++ b/src/quick/items/qquickscreen_p.h @@ -47,8 +47,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE @@ -104,6 +102,4 @@ QT_END_NAMESPACE QML_DECLARE_TYPEINFO(QQuickScreen, QML_HAS_ATTACHED_PROPERTIES) -QT_END_HEADER - #endif diff --git a/src/quick/items/qquickshadereffect_p.h b/src/quick/items/qquickshadereffect_p.h index 6abb41b737..5c3dcf4e41 100644 --- a/src/quick/items/qquickshadereffect_p.h +++ b/src/quick/items/qquickshadereffect_p.h @@ -52,8 +52,6 @@ #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE const char *qtPositionAttributeName(); @@ -193,6 +191,4 @@ private: QT_END_NAMESPACE -QT_END_HEADER - #endif // QQUICKSHADEREFFECT_P_H diff --git a/src/quick/items/qquickshadereffectmesh_p.h b/src/quick/items/qquickshadereffectmesh_p.h index 15f4d02c12..9c19b8e6e6 100644 --- a/src/quick/items/qquickshadereffectmesh_p.h +++ b/src/quick/items/qquickshadereffectmesh_p.h @@ -51,8 +51,6 @@ #ifndef QQUICKSHADEREFFECTMESH_P_H #define QQUICKSHADEREFFECTMESH_P_H -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QSGGeometry; @@ -101,6 +99,4 @@ inline QColor qt_premultiply_color(const QColor &c) QT_END_NAMESPACE -QT_END_HEADER - #endif // QQUICKSHADEREFFECTMESH_P_H diff --git a/src/quick/items/qquickshadereffectnode_p.h b/src/quick/items/qquickshadereffectnode_p.h index db21d06f58..a6854526fa 100644 --- a/src/quick/items/qquickshadereffectnode_p.h +++ b/src/quick/items/qquickshadereffectnode_p.h @@ -51,8 +51,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE struct QQuickShaderEffectMaterialKey { @@ -147,6 +145,4 @@ private Q_SLOTS: QT_END_NAMESPACE -QT_END_HEADER - #endif // QQUICKSHADEREFFECTNODE_P_H diff --git a/src/quick/items/qquickshadereffectsource_p.h b/src/quick/items/qquickshadereffectsource_p.h index 5c9427adde..57aba314aa 100644 --- a/src/quick/items/qquickshadereffectsource_p.h +++ b/src/quick/items/qquickshadereffectsource_p.h @@ -55,8 +55,6 @@ #define QSG_DEBUG_FBO_OVERLAY -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QSGNode; @@ -257,6 +255,4 @@ private: QT_END_NAMESPACE -QT_END_HEADER - #endif // QQUICKSHADEREFFECTSOURCE_P_H diff --git a/src/quick/items/qquicksprite_p.h b/src/quick/items/qquicksprite_p.h index dce28e888b..a85059e8d6 100644 --- a/src/quick/items/qquicksprite_p.h +++ b/src/quick/items/qquicksprite_p.h @@ -50,8 +50,6 @@ #include "qquickspriteengine_p.h" #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickSprite : public QQuickStochasticState @@ -316,5 +314,4 @@ private: }; QT_END_NAMESPACE -QT_END_HEADER #endif // QQUICKSPRITE_P_H diff --git a/src/quick/items/qquickspriteengine_p.h b/src/quick/items/qquickspriteengine_p.h index 612a2b4c2d..768b5e4a6e 100644 --- a/src/quick/items/qquickspriteengine_p.h +++ b/src/quick/items/qquickspriteengine_p.h @@ -53,8 +53,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickSprite; @@ -323,6 +321,4 @@ inline int spriteCount(QQmlListProperty *p) QT_END_NAMESPACE -QT_END_HEADER - #endif // QQUICKSPRITEENGINE_P_H diff --git a/src/quick/items/qquickspritesequence_p.h b/src/quick/items/qquickspritesequence_p.h index b07d177745..b954a4464f 100644 --- a/src/quick/items/qquickspritesequence_p.h +++ b/src/quick/items/qquickspritesequence_p.h @@ -45,8 +45,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QSGContext; @@ -145,6 +143,4 @@ private: QT_END_NAMESPACE -QT_END_HEADER - #endif // QQUICKSPRITESEQUENCE_P_H diff --git a/src/quick/items/qquickstateoperations_p.h b/src/quick/items/qquickstateoperations_p.h index d43420f266..9ee0d591da 100644 --- a/src/quick/items/qquickstateoperations_p.h +++ b/src/quick/items/qquickstateoperations_p.h @@ -49,8 +49,6 @@ #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickParentChangePrivate; @@ -213,7 +211,5 @@ QML_DECLARE_TYPE(QQuickParentChange) QML_DECLARE_TYPE(QQuickAnchorSet) QML_DECLARE_TYPE(QQuickAnchorChanges) -QT_END_HEADER - #endif // QQUICKSTATEOPERATIONS_P_H diff --git a/src/quick/items/qquicktext_p.h b/src/quick/items/qquicktext_p.h index ba0260c5c7..03b436b3fb 100644 --- a/src/quick/items/qquicktext_p.h +++ b/src/quick/items/qquicktext_p.h @@ -46,8 +46,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickTextPrivate; @@ -295,6 +293,4 @@ QT_END_NAMESPACE QML_DECLARE_TYPE(QQuickText) QML_DECLARE_TYPE(QQuickTextLine) -QT_END_HEADER - #endif // QQUICKTEXT_P_H diff --git a/src/quick/items/qquicktextcontrol_p.h b/src/quick/items/qquicktextcontrol_p.h index 84d6fcce52..94eae81f4e 100644 --- a/src/quick/items/qquicktextcontrol_p.h +++ b/src/quick/items/qquicktextcontrol_p.h @@ -63,8 +63,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE @@ -196,6 +194,4 @@ private: QT_END_NAMESPACE -QT_END_HEADER - #endif // QQuickTextControl_H diff --git a/src/quick/items/qquicktextedit_p.h b/src/quick/items/qquicktextedit_p.h index e88ca16c0e..0538270cd4 100644 --- a/src/quick/items/qquicktextedit_p.h +++ b/src/quick/items/qquicktextedit_p.h @@ -46,8 +46,6 @@ #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickTextEditPrivate; @@ -351,6 +349,4 @@ QT_END_NAMESPACE QML_DECLARE_TYPE(QQuickTextEdit) -QT_END_HEADER - #endif // QQUICKTEXTEDIT_P_H diff --git a/src/quick/items/qquicktextinput_p.h b/src/quick/items/qquicktextinput_p.h index 83bc125dee..602e9dc87b 100644 --- a/src/quick/items/qquicktextinput_p.h +++ b/src/quick/items/qquicktextinput_p.h @@ -46,8 +46,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickTextInputPrivate; @@ -415,6 +413,4 @@ QML_DECLARE_TYPE(QQuickDoubleValidator) QML_DECLARE_TYPE(QRegExpValidator) #endif -QT_END_HEADER - #endif // QQUICKTEXTINPUT_P_H diff --git a/src/quick/items/qquicktranslate_p.h b/src/quick/items/qquicktranslate_p.h index 8c3438c937..4181f5af79 100644 --- a/src/quick/items/qquicktranslate_p.h +++ b/src/quick/items/qquicktranslate_p.h @@ -46,8 +46,6 @@ #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickTranslatePrivate; @@ -154,6 +152,4 @@ QT_END_NAMESPACE QML_DECLARE_TYPE(QQuickTranslate) -QT_END_HEADER - #endif diff --git a/src/quick/items/qquickview.h b/src/quick/items/qquickview.h index 10bad4c615..3e8883ddfb 100644 --- a/src/quick/items/qquickview.h +++ b/src/quick/items/qquickview.h @@ -46,8 +46,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQmlEngine; @@ -115,6 +113,4 @@ private: QT_END_NAMESPACE -QT_END_HEADER - #endif // QQUICKVIEW_H diff --git a/src/quick/items/qquickview_p.h b/src/quick/items/qquickview_p.h index 4b78d8b270..43b17e5bed 100644 --- a/src/quick/items/qquickview_p.h +++ b/src/quick/items/qquickview_p.h @@ -55,8 +55,6 @@ #include "qquickitemchangelistener_p.h" -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQmlContext; @@ -100,6 +98,4 @@ public: QT_END_NAMESPACE -QT_END_HEADER - #endif // QQUICKVIEW_P_H diff --git a/src/quick/items/qquickwindow.h b/src/quick/items/qquickwindow.h index a98f3a695f..959c52b37c 100644 --- a/src/quick/items/qquickwindow.h +++ b/src/quick/items/qquickwindow.h @@ -48,8 +48,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickItem; @@ -173,7 +171,5 @@ QT_END_NAMESPACE Q_DECLARE_METATYPE(QQuickWindow *) -QT_END_HEADER - #endif // QQUICKWINDOW_H diff --git a/src/quick/items/qquickwindowmodule_p.h b/src/quick/items/qquickwindowmodule_p.h index 10f0c630b9..4afc45751f 100644 --- a/src/quick/items/qquickwindowmodule_p.h +++ b/src/quick/items/qquickwindowmodule_p.h @@ -44,8 +44,6 @@ #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE @@ -57,6 +55,4 @@ public: QT_END_NAMESPACE -QT_END_HEADER - #endif diff --git a/src/quick/qtquick2_p.h b/src/quick/qtquick2_p.h index ad624e66a8..47f4939820 100644 --- a/src/quick/qtquick2_p.h +++ b/src/quick/qtquick2_p.h @@ -44,8 +44,6 @@ #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class Q_QUICK_PRIVATE_EXPORT QQmlQtQuick2Module @@ -56,6 +54,4 @@ public: QT_END_NAMESPACE -QT_END_HEADER - #endif // QTQUICK2_P_H diff --git a/src/quick/qtquickglobal.h b/src/quick/qtquickglobal.h index 7ac75b2682..4cbeb4a00b 100644 --- a/src/quick/qtquickglobal.h +++ b/src/quick/qtquickglobal.h @@ -44,7 +44,6 @@ #include -QT_BEGIN_HEADER QT_BEGIN_NAMESPACE #ifndef QT_STATIC @@ -58,6 +57,5 @@ QT_BEGIN_NAMESPACE #endif QT_END_NAMESPACE -QT_END_HEADER #endif // QTQUICKGLOBAL_H diff --git a/src/quick/scenegraph/coreapi/qsgdefaultrenderer_p.h b/src/quick/scenegraph/coreapi/qsgdefaultrenderer_p.h index 5bd6eeeb1e..645ba93b80 100644 --- a/src/quick/scenegraph/coreapi/qsgdefaultrenderer_p.h +++ b/src/quick/scenegraph/coreapi/qsgdefaultrenderer_p.h @@ -47,8 +47,6 @@ #include #include "qsgrendernode_p.h" -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class Q_QUICK_PRIVATE_EXPORT QSGDefaultRenderer : public QSGRenderer @@ -91,6 +89,4 @@ private: QT_END_NAMESPACE -QT_END_HEADER - #endif // QMLRENDERER_H diff --git a/src/quick/scenegraph/coreapi/qsggeometry.h b/src/quick/scenegraph/coreapi/qsggeometry.h index c9b44e86b0..679b773fc8 100644 --- a/src/quick/scenegraph/coreapi/qsggeometry.h +++ b/src/quick/scenegraph/coreapi/qsggeometry.h @@ -46,8 +46,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QSGGeometryData; @@ -295,6 +293,4 @@ int QSGGeometry::sizeOfIndex() const QT_END_NAMESPACE -QT_END_HEADER - #endif // QSGGEOMETRY_H diff --git a/src/quick/scenegraph/coreapi/qsgmaterial.h b/src/quick/scenegraph/coreapi/qsgmaterial.h index 062311e2e3..5effdbfda6 100644 --- a/src/quick/scenegraph/coreapi/qsgmaterial.h +++ b/src/quick/scenegraph/coreapi/qsgmaterial.h @@ -45,8 +45,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QSGMaterial; @@ -143,6 +141,4 @@ Q_DECLARE_OPERATORS_FOR_FLAGS(QSGMaterialShader::RenderState::DirtyStates) QT_END_NAMESPACE -QT_END_HEADER - #endif diff --git a/src/quick/scenegraph/coreapi/qsgnode.h b/src/quick/scenegraph/coreapi/qsgnode.h index 82bb66fa77..522546d8e8 100644 --- a/src/quick/scenegraph/coreapi/qsgnode.h +++ b/src/quick/scenegraph/coreapi/qsgnode.h @@ -47,8 +47,6 @@ #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE //#define QML_RUNTIME_TESTING @@ -335,6 +333,4 @@ Q_DECLARE_OPERATORS_FOR_FLAGS(QSGNode::Flags) QT_END_NAMESPACE -QT_END_HEADER - #endif // NODE_H diff --git a/src/quick/scenegraph/coreapi/qsgnodeupdater_p.h b/src/quick/scenegraph/coreapi/qsgnodeupdater_p.h index 88eb814766..7e57bf5bbf 100644 --- a/src/quick/scenegraph/coreapi/qsgnodeupdater_p.h +++ b/src/quick/scenegraph/coreapi/qsgnodeupdater_p.h @@ -45,8 +45,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QSGNode; @@ -96,6 +94,4 @@ protected: QT_END_NAMESPACE -QT_END_HEADER - #endif // NODEUPDATER_P_H diff --git a/src/quick/scenegraph/coreapi/qsgrenderer_p.h b/src/quick/scenegraph/coreapi/qsgrenderer_p.h index 92072e2e3e..5dd53547d1 100644 --- a/src/quick/scenegraph/coreapi/qsgrenderer_p.h +++ b/src/quick/scenegraph/coreapi/qsgrenderer_p.h @@ -55,8 +55,6 @@ #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QSGMaterialShader; @@ -241,6 +239,4 @@ private: QT_END_NAMESPACE -QT_END_HEADER - #endif // RENDERER_H diff --git a/src/quick/scenegraph/coreapi/qsgrendernode_p.h b/src/quick/scenegraph/coreapi/qsgrendernode_p.h index 6c3e03b8b3..1f1bc23123 100644 --- a/src/quick/scenegraph/coreapi/qsgrendernode_p.h +++ b/src/quick/scenegraph/coreapi/qsgrendernode_p.h @@ -56,8 +56,6 @@ #include "qsgnode.h" #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class Q_QUICK_PRIVATE_EXPORT QSGRenderNode : public QSGNode @@ -110,6 +108,4 @@ Q_DECLARE_OPERATORS_FOR_FLAGS(QSGRenderNode::StateFlags) QT_END_NAMESPACE -QT_END_HEADER - #endif diff --git a/src/quick/scenegraph/qsgadaptationlayer_p.h b/src/quick/scenegraph/qsgadaptationlayer_p.h index 8b37deb1c1..85c1d2e6ff 100644 --- a/src/quick/scenegraph/qsgadaptationlayer_p.h +++ b/src/quick/scenegraph/qsgadaptationlayer_p.h @@ -59,8 +59,6 @@ // ### remove #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QSGNode; @@ -292,6 +290,4 @@ inline bool QSGDistanceFieldGlyphCache::containsGlyph(glyph_t glyph) QT_END_NAMESPACE -QT_END_HEADER - #endif diff --git a/src/quick/scenegraph/qsgcontext_p.h b/src/quick/scenegraph/qsgcontext_p.h index cdc83d1dee..b069c53dd3 100644 --- a/src/quick/scenegraph/qsgcontext_p.h +++ b/src/quick/scenegraph/qsgcontext_p.h @@ -54,8 +54,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE @@ -140,6 +138,4 @@ signals: QT_END_NAMESPACE -QT_END_HEADER - #endif // QSGCONTEXT_H diff --git a/src/quick/scenegraph/qsgcontextplugin_p.h b/src/quick/scenegraph/qsgcontextplugin_p.h index 036bc69c66..be3987d823 100644 --- a/src/quick/scenegraph/qsgcontextplugin_p.h +++ b/src/quick/scenegraph/qsgcontextplugin_p.h @@ -47,8 +47,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QSGContext; @@ -84,6 +82,4 @@ public: QT_END_NAMESPACE -QT_END_HEADER - #endif // QSGCONTEXTPLUGIN_H diff --git a/src/quick/scenegraph/qsgdefaultglyphnode_p.h b/src/quick/scenegraph/qsgdefaultglyphnode_p.h index 8f764b2d91..a1cd381968 100644 --- a/src/quick/scenegraph/qsgdefaultglyphnode_p.h +++ b/src/quick/scenegraph/qsgdefaultglyphnode_p.h @@ -45,8 +45,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QGlyphs; @@ -80,6 +78,4 @@ private: QT_END_NAMESPACE -QT_END_HEADER - #endif // DEFAULT_GLYPHNODE_H diff --git a/src/quick/scenegraph/qsgdefaultimagenode_p.h b/src/quick/scenegraph/qsgdefaultimagenode_p.h index 07cfa2c925..d2b3baae79 100644 --- a/src/quick/scenegraph/qsgdefaultimagenode_p.h +++ b/src/quick/scenegraph/qsgdefaultimagenode_p.h @@ -46,8 +46,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class SmoothTextureMaterial : public QSGTextureMaterial @@ -103,6 +101,4 @@ private: QT_END_NAMESPACE -QT_END_HEADER - #endif diff --git a/src/quick/scenegraph/qsgdefaultrectanglenode_p.h b/src/quick/scenegraph/qsgdefaultrectanglenode_p.h index f42a1fa14a..e4560784fd 100644 --- a/src/quick/scenegraph/qsgdefaultrectanglenode_p.h +++ b/src/quick/scenegraph/qsgdefaultrectanglenode_p.h @@ -47,8 +47,6 @@ #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QSGContext; @@ -104,6 +102,4 @@ private: QT_END_NAMESPACE -QT_END_HEADER - #endif diff --git a/src/quick/scenegraph/qsgdistancefieldglyphnode_p.h b/src/quick/scenegraph/qsgdistancefieldglyphnode_p.h index bb423820b4..b75a123d34 100644 --- a/src/quick/scenegraph/qsgdistancefieldglyphnode_p.h +++ b/src/quick/scenegraph/qsgdistancefieldglyphnode_p.h @@ -47,8 +47,6 @@ #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QSGContext; @@ -112,8 +110,6 @@ private: uint m_dirtyMaterial: 1; }; -QT_END_HEADER - QT_END_NAMESPACE #endif // DISTANCEFIELD_GLYPHNODE_H diff --git a/src/quick/scenegraph/qsgflashnode_p.h b/src/quick/scenegraph/qsgflashnode_p.h index 84ea734a25..d8d2ef4a24 100644 --- a/src/quick/scenegraph/qsgflashnode_p.h +++ b/src/quick/scenegraph/qsgflashnode_p.h @@ -44,8 +44,6 @@ #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QSGFlashNode : public QSGSimpleRectNode @@ -61,7 +59,5 @@ private: QT_END_NAMESPACE -QT_END_HEADER - #endif // QSGFLASHNODE_H diff --git a/src/quick/scenegraph/qsgrenderloop_p.h b/src/quick/scenegraph/qsgrenderloop_p.h index fc705f48e8..2ec6de9411 100644 --- a/src/quick/scenegraph/qsgrenderloop_p.h +++ b/src/quick/scenegraph/qsgrenderloop_p.h @@ -45,8 +45,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickWindow; @@ -86,6 +84,4 @@ private: QT_END_NAMESPACE -QT_END_HEADER - #endif // QSGRenderLoop_P_H diff --git a/src/quick/scenegraph/qsgshareddistancefieldglyphcache_p.h b/src/quick/scenegraph/qsgshareddistancefieldglyphcache_p.h index 0286408116..ac613c16f4 100644 --- a/src/quick/scenegraph/qsgshareddistancefieldglyphcache_p.h +++ b/src/quick/scenegraph/qsgshareddistancefieldglyphcache_p.h @@ -46,8 +46,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QPlatformSharedGraphicsCache; @@ -132,6 +130,4 @@ private: QT_END_NAMESPACE -QT_END_HEADER - #endif // QSGSHAREDDISTANCEFIELDGLYPHCACHE_H diff --git a/src/quick/scenegraph/qsgthreadedrenderloop_p.h b/src/quick/scenegraph/qsgthreadedrenderloop_p.h index fd3ab657d3..4c297f500a 100644 --- a/src/quick/scenegraph/qsgthreadedrenderloop_p.h +++ b/src/quick/scenegraph/qsgthreadedrenderloop_p.h @@ -48,8 +48,6 @@ #include "qsgrenderloop_p.h" -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QSGRenderThread; @@ -120,6 +118,4 @@ private: QT_END_NAMESPACE -QT_END_HEADER - #endif // QSGTHREADEDRENDERLOOP_P_H diff --git a/src/quick/scenegraph/util/qsgdepthstencilbuffer_p.h b/src/quick/scenegraph/util/qsgdepthstencilbuffer_p.h index 479c8e2d1f..f3fb92cbc5 100644 --- a/src/quick/scenegraph/util/qsgdepthstencilbuffer_p.h +++ b/src/quick/scenegraph/util/qsgdepthstencilbuffer_p.h @@ -48,8 +48,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QSGDepthStencilBufferManager; @@ -137,6 +135,4 @@ extern uint qHash(const QSGDepthStencilBuffer::Format &format); QT_END_NAMESPACE -QT_END_HEADER - #endif diff --git a/src/quick/scenegraph/util/qsgflatcolormaterial.h b/src/quick/scenegraph/util/qsgflatcolormaterial.h index 97b8c14a9b..27cf256603 100644 --- a/src/quick/scenegraph/util/qsgflatcolormaterial.h +++ b/src/quick/scenegraph/util/qsgflatcolormaterial.h @@ -45,8 +45,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class Q_QUICK_EXPORT QSGFlatColorMaterial : public QSGMaterial @@ -67,6 +65,4 @@ private: QT_END_NAMESPACE -QT_END_HEADER - #endif // FLATCOLORMATERIAL_H diff --git a/src/quick/scenegraph/util/qsgpainternode_p.h b/src/quick/scenegraph/util/qsgpainternode_p.h index 8c799810e4..387ca9bf88 100644 --- a/src/quick/scenegraph/util/qsgpainternode_p.h +++ b/src/quick/scenegraph/util/qsgpainternode_p.h @@ -50,8 +50,6 @@ #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QOpenGLFramebufferObject; @@ -151,8 +149,6 @@ private: bool m_dirtyTexture; }; -QT_END_HEADER - QT_END_NAMESPACE #endif // QSGPAINTERNODE_P_H diff --git a/src/quick/scenegraph/util/qsgsimplematerial.h b/src/quick/scenegraph/util/qsgsimplematerial.h index 25ac93b2c5..0a733e346a 100644 --- a/src/quick/scenegraph/util/qsgsimplematerial.h +++ b/src/quick/scenegraph/util/qsgsimplematerial.h @@ -44,8 +44,6 @@ #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE template @@ -215,7 +213,5 @@ Q_INLINE_TEMPLATE void QSGSimpleMaterialShader::updateState(const RenderS QT_END_NAMESPACE -QT_END_HEADER - #endif diff --git a/src/quick/scenegraph/util/qsgsimplerectnode.h b/src/quick/scenegraph/util/qsgsimplerectnode.h index 2808354089..856e6793bc 100644 --- a/src/quick/scenegraph/util/qsgsimplerectnode.h +++ b/src/quick/scenegraph/util/qsgsimplerectnode.h @@ -45,8 +45,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class Q_QUICK_EXPORT QSGSimpleRectNode : public QSGGeometryNode @@ -70,6 +68,4 @@ private: QT_END_NAMESPACE -QT_END_HEADER - #endif // SOLIDRECTNODE_H diff --git a/src/quick/scenegraph/util/qsgsimpletexturenode.h b/src/quick/scenegraph/util/qsgsimpletexturenode.h index a2ce80802a..ffd10210ae 100644 --- a/src/quick/scenegraph/util/qsgsimpletexturenode.h +++ b/src/quick/scenegraph/util/qsgsimpletexturenode.h @@ -46,8 +46,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class Q_QUICK_EXPORT QSGSimpleTextureNode : public QSGGeometryNode @@ -75,6 +73,4 @@ private: QT_END_NAMESPACE -QT_END_HEADER - #endif // QSGSIMPLETEXTURENODE_H diff --git a/src/quick/scenegraph/util/qsgtexture.h b/src/quick/scenegraph/util/qsgtexture.h index 9d91d4e81c..ace29cd9a0 100644 --- a/src/quick/scenegraph/util/qsgtexture.h +++ b/src/quick/scenegraph/util/qsgtexture.h @@ -46,8 +46,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QSGTexturePrivate; @@ -127,6 +125,4 @@ public: QT_END_NAMESPACE -QT_END_HEADER - #endif diff --git a/src/quick/scenegraph/util/qsgtexturematerial.h b/src/quick/scenegraph/util/qsgtexturematerial.h index 7fc06c7927..cbc4f7d51f 100644 --- a/src/quick/scenegraph/util/qsgtexturematerial.h +++ b/src/quick/scenegraph/util/qsgtexturematerial.h @@ -45,8 +45,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class Q_QUICK_EXPORT QSGOpaqueTextureMaterial : public QSGMaterial @@ -94,6 +92,4 @@ public: QT_END_NAMESPACE -QT_END_HEADER - #endif // TEXTUREMATERIAL_H diff --git a/src/quick/scenegraph/util/qsgtexturematerial_p.h b/src/quick/scenegraph/util/qsgtexturematerial_p.h index 346004be35..a07cb7925e 100644 --- a/src/quick/scenegraph/util/qsgtexturematerial_p.h +++ b/src/quick/scenegraph/util/qsgtexturematerial_p.h @@ -45,8 +45,6 @@ #include "qsgtexturematerial.h" #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class Q_QUICK_PRIVATE_EXPORT QSGOpaqueTextureMaterialShader : public QSGMaterialShader @@ -81,6 +79,4 @@ protected: QT_END_NAMESPACE -QT_END_HEADER - #endif // QSGTEXTUREMATERIAL_P_H diff --git a/src/quick/scenegraph/util/qsgtextureprovider.h b/src/quick/scenegraph/util/qsgtextureprovider.h index f4925612f9..608b3c6e0f 100644 --- a/src/quick/scenegraph/util/qsgtextureprovider.h +++ b/src/quick/scenegraph/util/qsgtextureprovider.h @@ -45,8 +45,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class Q_QUICK_EXPORT QSGTextureProvider : public QObject @@ -61,6 +59,4 @@ Q_SIGNALS: QT_END_NAMESPACE -QT_END_HEADER - #endif diff --git a/src/quick/scenegraph/util/qsgvertexcolormaterial.h b/src/quick/scenegraph/util/qsgvertexcolormaterial.h index 7e1eb43193..a3ffa48c31 100644 --- a/src/quick/scenegraph/util/qsgvertexcolormaterial.h +++ b/src/quick/scenegraph/util/qsgvertexcolormaterial.h @@ -44,8 +44,6 @@ #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class Q_QUICK_EXPORT QSGVertexColorMaterial : public QSGMaterial @@ -62,6 +60,4 @@ protected: QT_END_NAMESPACE -QT_END_HEADER - #endif // VERTEXCOLORMATERIAL_H diff --git a/src/quick/util/qquickanimation_p.h b/src/quick/util/qquickanimation_p.h index cb0da6112a..82d1ba269e 100644 --- a/src/quick/util/qquickanimation_p.h +++ b/src/quick/util/qquickanimation_p.h @@ -54,8 +54,6 @@ #include "private/qabstractanimationjob_p.h" #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickAbstractAnimationPrivate; @@ -460,6 +458,4 @@ QML_DECLARE_TYPE(QQuickParallelAnimation) QML_DECLARE_TYPE(QQuickVector3dAnimation) QML_DECLARE_TYPE(QQuickRotationAnimation) -QT_END_HEADER - #endif // QQUICKANIMATION_H diff --git a/src/quick/util/qquickanimationcontroller_p.h b/src/quick/util/qquickanimationcontroller_p.h index 0fe3888afb..5286e77534 100644 --- a/src/quick/util/qquickanimationcontroller_p.h +++ b/src/quick/util/qquickanimationcontroller_p.h @@ -45,8 +45,6 @@ #include #include "qquickanimation_p.h" -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickAnimationControllerPrivate; @@ -89,6 +87,4 @@ QT_END_NAMESPACE QML_DECLARE_TYPE(QQuickAnimationController) -QT_END_HEADER - #endif // QQUICKANIMATIONCONTROLLER_H diff --git a/src/quick/util/qquickapplication_p.h b/src/quick/util/qquickapplication_p.h index aa46a6a7e9..2eef59b608 100644 --- a/src/quick/util/qquickapplication_p.h +++ b/src/quick/util/qquickapplication_p.h @@ -46,8 +46,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE @@ -81,6 +79,4 @@ QT_END_NAMESPACE QML_DECLARE_TYPE(QQuickApplication) -QT_END_HEADER - #endif // QQUICKAPPLICATION_P_H diff --git a/src/quick/util/qquickbehavior_p.h b/src/quick/util/qquickbehavior_p.h index a93b597a6b..0a49891cc0 100644 --- a/src/quick/util/qquickbehavior_p.h +++ b/src/quick/util/qquickbehavior_p.h @@ -47,8 +47,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickAbstractAnimation; @@ -88,6 +86,4 @@ QT_END_NAMESPACE QML_DECLARE_TYPE(QQuickBehavior) -QT_END_HEADER - #endif // QQUICKBEHAVIOR_H diff --git a/src/quick/util/qquickfontloader_p.h b/src/quick/util/qquickfontloader_p.h index 9337f5c2d7..1488ffe734 100644 --- a/src/quick/util/qquickfontloader_p.h +++ b/src/quick/util/qquickfontloader_p.h @@ -47,8 +47,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickFontLoaderPrivate; @@ -89,7 +87,5 @@ QT_END_NAMESPACE QML_DECLARE_TYPE(QQuickFontLoader) -QT_END_HEADER - #endif // QQUICKFONTLOADER_H diff --git a/src/quick/util/qquickimageprovider.h b/src/quick/util/qquickimageprovider.h index 75c2e80a34..2735c9ac78 100644 --- a/src/quick/util/qquickimageprovider.h +++ b/src/quick/util/qquickimageprovider.h @@ -47,8 +47,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE @@ -87,6 +85,4 @@ private: QT_END_NAMESPACE -QT_END_HEADER - #endif // QQUICKIMAGEPROVIDER_H diff --git a/src/quick/util/qquickpath_p.h b/src/quick/util/qquickpath_p.h index 545e98556b..71545851e4 100644 --- a/src/quick/util/qquickpath_p.h +++ b/src/quick/util/qquickpath_p.h @@ -50,8 +50,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickCurve; @@ -446,6 +444,4 @@ QML_DECLARE_TYPE(QQuickPathSvg) QML_DECLARE_TYPE(QQuickPathPercent) QML_DECLARE_TYPE(QQuickPath) -QT_END_HEADER - #endif // QQUICKPATH_H diff --git a/src/quick/util/qquickpathinterpolator_p.h b/src/quick/util/qquickpathinterpolator_p.h index f5414e028a..b66af408db 100644 --- a/src/quick/util/qquickpathinterpolator_p.h +++ b/src/quick/util/qquickpathinterpolator_p.h @@ -45,8 +45,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickPath; @@ -93,6 +91,4 @@ QT_END_NAMESPACE QML_DECLARE_TYPE(QQuickPathInterpolator) -QT_END_HEADER - #endif // QQUICKPATHINTERPOLATOR_P_H diff --git a/src/quick/util/qquickpixmapcache_p.h b/src/quick/util/qquickpixmapcache_p.h index a6c2a7c1f3..3fefa35c0b 100644 --- a/src/quick/util/qquickpixmapcache_p.h +++ b/src/quick/util/qquickpixmapcache_p.h @@ -51,8 +51,6 @@ #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQmlEngine; @@ -139,6 +137,4 @@ Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickPixmap::Options) QT_END_NAMESPACE -QT_END_HEADER - #endif // QQUICKPIXMAPCACHE_H diff --git a/src/quick/util/qquickpropertychanges_p.h b/src/quick/util/qquickpropertychanges_p.h index 95320afd6d..674dfa7c4c 100644 --- a/src/quick/util/qquickpropertychanges_p.h +++ b/src/quick/util/qquickpropertychanges_p.h @@ -45,8 +45,6 @@ #include "qquickstatechangescript_p.h" #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickPropertyChangesPrivate; @@ -105,6 +103,4 @@ QT_END_NAMESPACE QML_DECLARE_TYPE(QQuickPropertyChanges) -QT_END_HEADER - #endif // QQUICKPROPERTYCHANGES_H diff --git a/src/quick/util/qquicksmoothedanimation_p.h b/src/quick/util/qquicksmoothedanimation_p.h index 644ebf2a7b..7bf8b6c063 100644 --- a/src/quick/util/qquicksmoothedanimation_p.h +++ b/src/quick/util/qquicksmoothedanimation_p.h @@ -47,8 +47,6 @@ #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQmlProperty; @@ -95,6 +93,4 @@ QT_END_NAMESPACE QML_DECLARE_TYPE(QQuickSmoothedAnimation) -QT_END_HEADER - #endif // QQUICKSMOOTHEDANIMATION_H diff --git a/src/quick/util/qquickspringanimation_p.h b/src/quick/util/qquickspringanimation_p.h index f43d6a4002..14968b770c 100644 --- a/src/quick/util/qquickspringanimation_p.h +++ b/src/quick/util/qquickspringanimation_p.h @@ -47,8 +47,6 @@ #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickSpringAnimationPrivate; @@ -102,6 +100,4 @@ QT_END_NAMESPACE QML_DECLARE_TYPE(QQuickSpringAnimation) -QT_END_HEADER - #endif // QQUICKSPRINGANIMATION_H diff --git a/src/quick/util/qquickstate_p.h b/src/quick/util/qquickstate_p.h index e9324f4390..6a7b1b345f 100644 --- a/src/quick/util/qquickstate_p.h +++ b/src/quick/util/qquickstate_p.h @@ -48,8 +48,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickActionEvent; @@ -205,6 +203,4 @@ QT_END_NAMESPACE QML_DECLARE_TYPE(QQuickStateOperation) QML_DECLARE_TYPE(QQuickState) -QT_END_HEADER - #endif // QQUICKSTATE_H diff --git a/src/quick/util/qquickstatechangescript_p.h b/src/quick/util/qquickstatechangescript_p.h index 8d3e3804f9..3d8e139fe7 100644 --- a/src/quick/util/qquickstatechangescript_p.h +++ b/src/quick/util/qquickstatechangescript_p.h @@ -45,8 +45,6 @@ #include "qquickstate_p.h" #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickStateChangeScriptPrivate; @@ -80,6 +78,4 @@ QT_END_NAMESPACE QML_DECLARE_TYPE(QQuickStateChangeScript) -QT_END_HEADER - #endif // QQUICKSTATEOPERATIONS_H diff --git a/src/quick/util/qquickstategroup_p.h b/src/quick/util/qquickstategroup_p.h index ccd928aacf..b8e34237b7 100644 --- a/src/quick/util/qquickstategroup_p.h +++ b/src/quick/util/qquickstategroup_p.h @@ -44,8 +44,6 @@ #include "qquickstate_p.h" -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickStateGroupPrivate; @@ -90,6 +88,4 @@ QT_END_NAMESPACE QML_DECLARE_TYPE(QQuickStateGroup) -QT_END_HEADER - #endif // QQUICKSTATEGROUP_H diff --git a/src/quick/util/qquicksystempalette_p.h b/src/quick/util/qquicksystempalette_p.h index c8267b677a..6ff31829e3 100644 --- a/src/quick/util/qquicksystempalette_p.h +++ b/src/quick/util/qquicksystempalette_p.h @@ -47,8 +47,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickSystemPalettePrivate; @@ -115,6 +113,4 @@ QT_END_NAMESPACE QML_DECLARE_TYPE(QQuickSystemPalette) -QT_END_HEADER - #endif // QQUICKSYSTEMPALETTE_H diff --git a/src/quick/util/qquicktransition_p.h b/src/quick/util/qquicktransition_p.h index 6ed2ad63cf..831aec025f 100644 --- a/src/quick/util/qquicktransition_p.h +++ b/src/quick/util/qquicktransition_p.h @@ -48,8 +48,6 @@ #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickAbstractAnimation; @@ -127,6 +125,4 @@ QT_END_NAMESPACE QML_DECLARE_TYPE(QQuickTransition) -QT_END_HEADER - #endif // QQUICKTRANSITION_H diff --git a/src/quick/util/qquickutilmodule_p.h b/src/quick/util/qquickutilmodule_p.h index 5a7406b8e7..360480a34a 100644 --- a/src/quick/util/qquickutilmodule_p.h +++ b/src/quick/util/qquickutilmodule_p.h @@ -44,8 +44,6 @@ #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class QQuickUtilModule @@ -56,6 +54,4 @@ public: QT_END_NAMESPACE -QT_END_HEADER - #endif // QQUICKUTILMODULE_H diff --git a/src/quick/util/qquickvaluetypes_p.h b/src/quick/util/qquickvaluetypes_p.h index 557cbd4642..2f8756fb01 100644 --- a/src/quick/util/qquickvaluetypes_p.h +++ b/src/quick/util/qquickvaluetypes_p.h @@ -54,8 +54,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE namespace QQuickValueTypes { @@ -365,6 +363,4 @@ private: QT_END_NAMESPACE -QT_END_HEADER - #endif // QQUICKVALUETYPES_P_H -- cgit v1.2.3 From 233e83b20512a5e3748542f1c279a5c7ec6310ad Mon Sep 17 00:00:00 2001 From: Fabian Bumberger Date: Mon, 14 Jan 2013 15:00:41 +0100 Subject: Allow aligning items in a grid This change introduces two new properties, horizontalItemAlignment and verticalItemAlignment to a Grid element. This gives the user the possibility to align the items. Change-Id: I7322a689f1bbc1da342bd618f6c30dd8c139ee29 Reviewed-by: Alan Alpert --- src/quick/doc/images/gridLayout_aligncenter.png | Bin 0 -> 476 bytes src/quick/doc/images/gridLayout_aligntop.png | Bin 0 -> 472 bytes src/quick/doc/images/gridLayout_aligntopleft.png | Bin 0 -> 477 bytes src/quick/items/qquickitemsmodule.cpp | 2 + src/quick/items/qquickpositioners.cpp | 111 +++++++++++++++++- src/quick/items/qquickpositioners_p.h | 24 ++++ .../auto/quick/qquickpositioners/data/gridtest.qml | 6 +- .../qquickpositioners/tst_qquickpositioners.cpp | 126 +++++++++++++++++++++ 8 files changed, 266 insertions(+), 3 deletions(-) create mode 100644 src/quick/doc/images/gridLayout_aligncenter.png create mode 100644 src/quick/doc/images/gridLayout_aligntop.png create mode 100644 src/quick/doc/images/gridLayout_aligntopleft.png diff --git a/src/quick/doc/images/gridLayout_aligncenter.png b/src/quick/doc/images/gridLayout_aligncenter.png new file mode 100644 index 0000000000..f31ed25e79 Binary files /dev/null and b/src/quick/doc/images/gridLayout_aligncenter.png differ diff --git a/src/quick/doc/images/gridLayout_aligntop.png b/src/quick/doc/images/gridLayout_aligntop.png new file mode 100644 index 0000000000..5b752b1d19 Binary files /dev/null and b/src/quick/doc/images/gridLayout_aligntop.png differ diff --git a/src/quick/doc/images/gridLayout_aligntopleft.png b/src/quick/doc/images/gridLayout_aligntopleft.png new file mode 100644 index 0000000000..5a4a2fe211 Binary files /dev/null and b/src/quick/doc/images/gridLayout_aligntopleft.png differ diff --git a/src/quick/items/qquickitemsmodule.cpp b/src/quick/items/qquickitemsmodule.cpp index 043baebd0a..71c23111d5 100644 --- a/src/quick/items/qquickitemsmodule.cpp +++ b/src/quick/items/qquickitemsmodule.cpp @@ -222,6 +222,8 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor) #ifndef QT_NO_ACCESSIBILITY qmlRegisterUncreatableType("QtQuick", 2, 0, "Accessible",QQuickAccessibleAttached::tr("Accessible is only available via attached properties")); #endif + + qmlRegisterType(uri, 2, 1, "Grid"); } void QQuickItemsModule::defineModule() diff --git a/src/quick/items/qquickpositioners.cpp b/src/quick/items/qquickpositioners.cpp index 2b03ed2dc4..df80e77bfa 100644 --- a/src/quick/items/qquickpositioners.cpp +++ b/src/quick/items/qquickpositioners.cpp @@ -1102,6 +1102,8 @@ QQuickGrid::QQuickGrid(QQuickItem *parent) , m_useRowSpacing(false) , m_useColumnSpacing(false) , m_flow(LeftToRight) + , m_hItemAlign(AlignLeft) + , m_vItemAlign(AlignTop) { } @@ -1248,6 +1250,7 @@ void QQuickGrid::setLayoutDirection(Qt::LayoutDirection layoutDirection) prePositioning(); emit layoutDirectionChanged(); emit effectiveLayoutDirectionChanged(); + emit effectiveHorizontalAlignmentChanged(effectiveHAlign()); } } @@ -1266,6 +1269,97 @@ Qt::LayoutDirection QQuickGrid::effectiveLayoutDirection() const return QQuickBasePositionerPrivate::getEffectiveLayoutDirection(this); } +/*! + \qmlproperty enumeration QtQuick2::Grid::horizontalItmeAlignment + \qmlproperty enumeration QtQuick2::Grid::verticalItemAlignment + \qmlproperty enumeration QtQuick2::Grid::effectiveHorizontalItemAlignment + + Sets the horizontal and vertical alignment of items in the Grid. By default, + the items are vertically aligned to the top. Horizontal + alignment follows the layoutDirection of the Grid, for example when having a layoutDirection + from LeftToRight, the items will be aligned on the left. + + The valid values for \c horizontalItemAlignment are, \c Grid.AlignLeft, \c Grid.AlignRight and + \c Grid.AlignHCenter. + + The valid values for \c verticalItemAlignment are \c Grid.AlignTop, \c Grid.AlignBottom + and \c Grid.AlignVCenter. + + The below images show three examples of how to align items. + + \table + \row + \li + \li \inlineimage gridLayout_aligntopleft.png + \li \inlineimage gridLayout_aligntop.png + \li \inlineimage gridLayout_aligncenter.png + \row + \li Horizontal alignment + \li AlignLeft + \li AlignHCenter + \li AlignHCenter + \row + \li Vertical alignment + \li AlignTop + \li AlignTop + \li AlignVCenter + \endtable + + + When mirroring the layout using either the attached property LayoutMirroring::enabled or + by setting the layoutDirection, the horizontal alignment of items will be mirrored as well. + However, the property \c horizontalItemAlignment will remain unchanged. + To query the effective horizontal alignment of items, use the read-only property + \c effectiveHorizontalItemAlignment. + + \sa Grid::layoutDirection, {LayoutMirroring}{LayoutMirroring} +*/ +QQuickGrid::HAlignment QQuickGrid::hItemAlign() const +{ + return m_hItemAlign; +} +void QQuickGrid::setHItemAlign(HAlignment align) +{ + if (m_hItemAlign != align) { + m_hItemAlign = align; + prePositioning(); + emit horizontalAlignmentChanged(align); + emit effectiveHorizontalAlignmentChanged(effectiveHAlign()); + } +} + +QQuickGrid::HAlignment QQuickGrid::effectiveHAlign() const +{ + HAlignment effectiveAlignment = m_hItemAlign; + if (effectiveLayoutDirection() == Qt::RightToLeft) { + switch (hItemAlign()) { + case AlignLeft: + effectiveAlignment = AlignRight; + break; + case AlignRight: + effectiveAlignment = AlignLeft; + break; + default: + break; + } + } + return effectiveAlignment; +} + + +QQuickGrid::VAlignment QQuickGrid::vItemAlign() const +{ + return m_vItemAlign; +} +void QQuickGrid::setVItemAlign(VAlignment align) +{ + if (m_vItemAlign != align) { + m_vItemAlign = align; + prePositioning(); + emit verticalAlignmentChanged(align); + } +} + void QQuickGrid::doPositioning(QSizeF *contentSize) { //Precondition: All items in the positioned list have a valid item pointer and should be positioned @@ -1362,9 +1456,22 @@ void QQuickGrid::doPositioning(QSizeF *contentSize) for (int i = 0; i < positionedItems.count(); ++i) { PositionedItem &child = positionedItems[i]; qreal childXOffset = xoffset; + + if (effectiveHAlign() == AlignRight) + childXOffset += maxColWidth[curCol] - child.item->width(); + else if (hItemAlign() == AlignHCenter) + childXOffset += (maxColWidth[curCol] - child.item->width())/2.0; + if (!d->isLeftToRight()) - childXOffset -= child.item->width(); - positionItem(childXOffset, yoffset, &child); + childXOffset -= maxColWidth[curCol]; + + qreal alignYOffset = yoffset; + if (m_vItemAlign == AlignVCenter) + alignYOffset += (maxRowHeight[curRow] - child.item->height())/2.0; + else if (m_vItemAlign == AlignBottom) + alignYOffset += maxRowHeight[curRow] - child.item->height(); + + positionItem(childXOffset, alignYOffset, &child); if (m_flow == LeftToRight) { if (d->isLeftToRight()) diff --git a/src/quick/items/qquickpositioners_p.h b/src/quick/items/qquickpositioners_p.h index aeb722dd60..1ff5a7a74c 100644 --- a/src/quick/items/qquickpositioners_p.h +++ b/src/quick/items/qquickpositioners_p.h @@ -220,6 +220,9 @@ class Q_AUTOTEST_EXPORT QQuickGrid : public QQuickBasePositioner Q_PROPERTY(Flow flow READ flow WRITE setFlow NOTIFY flowChanged) Q_PROPERTY(Qt::LayoutDirection layoutDirection READ layoutDirection WRITE setLayoutDirection NOTIFY layoutDirectionChanged) Q_PROPERTY(Qt::LayoutDirection effectiveLayoutDirection READ effectiveLayoutDirection NOTIFY effectiveLayoutDirectionChanged) + Q_PROPERTY(HAlignment horizontalItemAlignment READ hItemAlign WRITE setHItemAlign NOTIFY horizontalAlignmentChanged REVISION 1) + Q_PROPERTY(HAlignment effectiveHorizontalItemAlignment READ effectiveHAlign NOTIFY effectiveHorizontalAlignmentChanged REVISION 1) + Q_PROPERTY(VAlignment verticalItemAlignment READ vItemAlign WRITE setVItemAlign NOTIFY verticalAlignmentChanged REVISION 1) public: QQuickGrid(QQuickItem *parent=0); @@ -247,6 +250,22 @@ public: void setLayoutDirection (Qt::LayoutDirection); Qt::LayoutDirection effectiveLayoutDirection() const; + Q_ENUMS(HAlignment) + Q_ENUMS(VAlignment) + enum HAlignment { AlignLeft = Qt::AlignLeft, + AlignRight = Qt::AlignRight, + AlignHCenter = Qt::AlignHCenter}; + enum VAlignment { AlignTop = Qt::AlignTop, + AlignBottom = Qt::AlignBottom, + AlignVCenter = Qt::AlignVCenter }; + + HAlignment hItemAlign() const; + void setHItemAlign(HAlignment align); + HAlignment effectiveHAlign() const; + + VAlignment vItemAlign() const; + void setVItemAlign(VAlignment align); + Q_SIGNALS: void rowsChanged(); void columnsChanged(); @@ -255,6 +274,9 @@ Q_SIGNALS: void effectiveLayoutDirectionChanged(); void rowSpacingChanged(); void columnSpacingChanged(); + Q_REVISION(1) void horizontalAlignmentChanged(HAlignment alignment); + Q_REVISION(1) void effectiveHorizontalAlignmentChanged(HAlignment alignment); + Q_REVISION(1) void verticalAlignmentChanged(VAlignment alignment); protected: virtual void doPositioning(QSizeF *contentSize); @@ -268,6 +290,8 @@ private: bool m_useRowSpacing; bool m_useColumnSpacing; Flow m_flow; + HAlignment m_hItemAlign; + VAlignment m_vItemAlign; Q_DISABLE_COPY(QQuickGrid) }; diff --git a/tests/auto/quick/qquickpositioners/data/gridtest.qml b/tests/auto/quick/qquickpositioners/data/gridtest.qml index 50bec1377b..fbe0b22b15 100644 --- a/tests/auto/quick/qquickpositioners/data/gridtest.qml +++ b/tests/auto/quick/qquickpositioners/data/gridtest.qml @@ -1,11 +1,15 @@ -import QtQuick 2.0 +import QtQuick 2.1 Item { width: 640 height: 480 property bool testRightToLeft: false + property int testHAlignment: Grid.AlignLeft; + property int testVAlignment: Grid.AlignTop; Grid { layoutDirection: testRightToLeft ? Qt.RightToLeft : Qt.LeftToRight + horizontalItemAlignment: testHAlignment + verticalItemAlignment: testVAlignment objectName: "grid" columns: 3 Rectangle { diff --git a/tests/auto/quick/qquickpositioners/tst_qquickpositioners.cpp b/tests/auto/quick/qquickpositioners/tst_qquickpositioners.cpp index 98999e540a..9cceaab5dd 100644 --- a/tests/auto/quick/qquickpositioners/tst_qquickpositioners.cpp +++ b/tests/auto/quick/qquickpositioners/tst_qquickpositioners.cpp @@ -78,6 +78,8 @@ private slots: void test_grid_animated(); void test_grid_animated_rightToLeft(); void test_grid_zero_columns(); + void test_grid_H_alignment(); + void test_grid_V_alignment(); void test_propertychanges(); void test_repeater(); void test_flow(); @@ -1418,6 +1420,130 @@ void tst_qquickpositioners::test_grid_zero_columns() delete window; } +void tst_qquickpositioners::test_grid_H_alignment() +{ + QQuickView *window = createView(testFile("gridtest.qml")); + + window->rootObject()->setProperty("testHAlignment", QQuickGrid::AlignHCenter); + + QQuickRectangle *one = window->rootObject()->findChild("one"); + QVERIFY(one != 0); + QQuickRectangle *two = window->rootObject()->findChild("two"); + QVERIFY(two != 0); + QQuickRectangle *three = window->rootObject()->findChild("three"); + QVERIFY(three != 0); + QQuickRectangle *four = window->rootObject()->findChild("four"); + QVERIFY(four != 0); + QQuickRectangle *five = window->rootObject()->findChild("five"); + QVERIFY(five != 0); + + QCOMPARE(one->x(), 0.0); + QCOMPARE(one->y(), 0.0); + QCOMPARE(two->x(), 50.0); + QCOMPARE(two->y(), 0.0); + QCOMPARE(three->x(), 70.0); + QCOMPARE(three->y(), 0.0); + QCOMPARE(four->x(), 0.0); + QCOMPARE(four->y(), 50.0); + QCOMPARE(five->x(), 55.0); + QCOMPARE(five->y(), 50.0); + + QQuickItem *grid = window->rootObject()->findChild("grid"); + QCOMPARE(grid->width(), 100.0); + QCOMPARE(grid->height(), 100.0); + + window->rootObject()->setProperty("testHAlignment", QQuickGrid::AlignRight); + + QCOMPARE(one->x(), 0.0); + QCOMPARE(one->y(), 0.0); + QCOMPARE(two->x(), 50.0); + QCOMPARE(two->y(), 0.0); + QCOMPARE(three->x(), 70.0); + QCOMPARE(three->y(), 0.0); + QCOMPARE(four->x(), 0.0); + QCOMPARE(four->y(), 50.0); + QCOMPARE(five->x(), 60.0); + QCOMPARE(five->y(), 50.0); + QCOMPARE(grid->width(), 100.0); + QCOMPARE(grid->height(), 100.0); + + window->rootObject()->setProperty("testRightToLeft", true); + + QCOMPARE(one->x(), 50.0); + QCOMPARE(one->y(), 0.0); + QCOMPARE(two->x(), 30.0); + QCOMPARE(two->y(), 0.0); + QCOMPARE(three->x(), 0.0); + QCOMPARE(three->y(), 0.0); + QCOMPARE(four->x(), 50.0); + QCOMPARE(four->y(), 50.0); + QCOMPARE(five->x(), 30.0); + QCOMPARE(five->y(), 50.0); + QCOMPARE(grid->width(), 100.0); + QCOMPARE(grid->height(), 100.0); + + window->rootObject()->setProperty("testHAlignment", QQuickGrid::AlignHCenter); + + QCOMPARE(one->x(), 50.0); + QCOMPARE(one->y(), 0.0); + QCOMPARE(two->x(), 30.0); + QCOMPARE(two->y(), 0.0); + QCOMPARE(three->x(), 0.0); + QCOMPARE(three->y(), 0.0); + QCOMPARE(four->x(), 50.0); + QCOMPARE(four->y(), 50.0); + QCOMPARE(five->x(), 35.0); + QCOMPARE(five->y(), 50.0); + QCOMPARE(grid->width(), 100.0); + QCOMPARE(grid->height(), 100.0); + + delete window; +} + +void tst_qquickpositioners::test_grid_V_alignment() +{ + QQuickView *window = createView(testFile("gridtest.qml")); + + window->rootObject()->setProperty("testVAlignment", QQuickGrid::AlignVCenter); + + QQuickRectangle *one = window->rootObject()->findChild("one"); + QVERIFY(one != 0); + QQuickRectangle *two = window->rootObject()->findChild("two"); + QVERIFY(two != 0); + QQuickRectangle *three = window->rootObject()->findChild("three"); + QVERIFY(three != 0); + QQuickRectangle *four = window->rootObject()->findChild("four"); + QVERIFY(four != 0); + QQuickRectangle *five = window->rootObject()->findChild("five"); + QVERIFY(five != 0); + + QCOMPARE(one->x(), 0.0); + QCOMPARE(one->y(), 0.0); + QCOMPARE(two->x(), 50.0); + QCOMPARE(two->y(), 0.0); + QCOMPARE(three->x(), 70.0); + QCOMPARE(three->y(), 15.0); + QCOMPARE(four->x(), 0.0); + QCOMPARE(four->y(), 50.0); + QCOMPARE(five->x(), 50.0); + QCOMPARE(five->y(), 70.0); + + window->rootObject()->setProperty("testVAlignment", QQuickGrid::AlignBottom); + + QCOMPARE(one->x(), 0.0); + QCOMPARE(one->y(), 0.0); + QCOMPARE(two->x(), 50.0); + QCOMPARE(two->y(), 0.0); + QCOMPARE(three->x(), 70.0); + QCOMPARE(three->y(), 30.0); + QCOMPARE(four->x(), 0.0); + QCOMPARE(four->y(), 50.0); + QCOMPARE(five->x(), 50.0); + QCOMPARE(five->y(), 90.0); + + delete window; +} + void tst_qquickpositioners::test_propertychanges() { QQuickView *window = createView(testFile("propertychangestest.qml")); -- cgit v1.2.3 From f814eb831a8fb3d7eb958b72142ab4979ed76599 Mon Sep 17 00:00:00 2001 From: Kai Koehne Date: Mon, 28 Jan 2013 12:11:38 +0100 Subject: Debugger: Remove symbian specific plugin Symbian isn't supported any more. We only support debugging via TCP. Change-Id: Ie7be06eeacd4206c4c14178e24c1b33d35fc8a0c Reviewed-by: Aurindam Jana --- src/plugins/qmltooling/qmldbg_ost/qmldbg_ost.pro | 15 -- src/plugins/qmltooling/qmldbg_ost/qmlostplugin.cpp | 150 -------------- src/plugins/qmltooling/qmldbg_ost/qmlostplugin.h | 82 -------- src/plugins/qmltooling/qmldbg_ost/qostdevice.cpp | 220 --------------------- src/plugins/qmltooling/qmldbg_ost/qostdevice.h | 77 -------- src/plugins/qmltooling/qmldbg_ost/usbostcomm.h | 191 ------------------ 6 files changed, 735 deletions(-) delete mode 100644 src/plugins/qmltooling/qmldbg_ost/qmldbg_ost.pro delete mode 100644 src/plugins/qmltooling/qmldbg_ost/qmlostplugin.cpp delete mode 100644 src/plugins/qmltooling/qmldbg_ost/qmlostplugin.h delete mode 100644 src/plugins/qmltooling/qmldbg_ost/qostdevice.cpp delete mode 100644 src/plugins/qmltooling/qmldbg_ost/qostdevice.h delete mode 100644 src/plugins/qmltooling/qmldbg_ost/usbostcomm.h diff --git a/src/plugins/qmltooling/qmldbg_ost/qmldbg_ost.pro b/src/plugins/qmltooling/qmldbg_ost/qmldbg_ost.pro deleted file mode 100644 index 6172ee95cf..0000000000 --- a/src/plugins/qmltooling/qmldbg_ost/qmldbg_ost.pro +++ /dev/null @@ -1,15 +0,0 @@ -TARGET = qmldbg_ost -QT += qml network - -PLUGIN_TYPE = qmltooling -PLUGIN_CLASS_NAME = QmlOstPlugin -load(qt_plugin) - -SOURCES += \ - qmlostplugin.cpp \ - qostdevice.cpp - -HEADERS += \ - qmlostplugin.h \ - qostdevice.h \ - usbostcomm.h diff --git a/src/plugins/qmltooling/qmldbg_ost/qmlostplugin.cpp b/src/plugins/qmltooling/qmldbg_ost/qmlostplugin.cpp deleted file mode 100644 index bab33f7f44..0000000000 --- a/src/plugins/qmltooling/qmldbg_ost/qmlostplugin.cpp +++ /dev/null @@ -1,150 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** 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, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qmlostplugin.h" -#include "qostdevice.h" - -#include -#include - -QT_BEGIN_NAMESPACE - -static const TInt KQmlOstProtocolId = 0x94; - -class QmlOstPluginPrivate { -public: - QmlOstPluginPrivate(); - - QOstDevice *ost; - QPacketProtocol *protocol; - QQmlDebugServer *debugServer; -}; - -QmlOstPluginPrivate::QmlOstPluginPrivate() : - ost(0), - protocol(0), - debugServer(0) -{ -} - -QmlOstPlugin::QmlOstPlugin() : - d_ptr(new QmlOstPluginPrivate) -{ -} - -QmlOstPlugin::~QmlOstPlugin() -{ - delete d_ptr; -} - -void QmlOstPlugin::setServer(QQmlDebugServer *server) -{ - Q_D(QmlOstPlugin); - d->debugServer = server; -} - -bool QmlOstPlugin::isConnected() const -{ - Q_D(const QmlOstPlugin); - return d->ost && d->ost->isOpen(); -} - -void QmlOstPlugin::send(const QByteArray &message) -{ - Q_D(QmlOstPlugin); - - if (!isConnected()) - return; - - QPacket pack; - pack.writeRawData(message.data(), message.length()); - - d->protocol->send(pack); - //d->socket->flush(); -} - -void QmlOstPlugin::disconnect() -{ - Q_D(QmlOstPlugin); - - delete d->protocol; - d->protocol = 0; -} - -bool QmlOstPlugin::waitForMessage() -{ - Q_D(QmlOstPlugin); - return d->protocol->waitForReadyRead(-1); -} - -void QmlOstPlugin::setPort(int port, bool block, const QString &hostaddress) -{ - Q_UNUSED(port); - Q_UNUSED(block); - Q_UNUSED(hostaddress); - - Q_D(QmlOstPlugin); - - d->ost = new QOstDevice(this); - bool ok = d->ost->open(KQmlOstProtocolId); - if (!ok) { - if (d->ost->errorString().length()) - qDebug("Error from QOstDevice: %s", qPrintable(d->ost->errorString())); - qWarning("QML Debugger: Unable to listen to OST."); // This message is part of the signalling - do not change the format! - return; - } - d->protocol = new QPacketProtocol(d->ost, this); - QObject::connect(d->protocol, SIGNAL(readyRead()), this, SLOT(readyRead())); - qDebug("QML Debugger: Waiting for connection via OST."); // This message is part of the signalling - do not change the format! -} - -void QmlOstPlugin::readyRead() -{ - Q_D(QmlOstPlugin); - QPacket packet = d->protocol->read(); - - QByteArray content = packet.data(); - d->debugServer->receiveMessage(content); -} - -Q_EXPORT_PLUGIN2(qmlostplugin, QmlOstPlugin) - -QT_END_NAMESPACE diff --git a/src/plugins/qmltooling/qmldbg_ost/qmlostplugin.h b/src/plugins/qmltooling/qmldbg_ost/qmlostplugin.h deleted file mode 100644 index a31cdb6b2c..0000000000 --- a/src/plugins/qmltooling/qmldbg_ost/qmlostplugin.h +++ /dev/null @@ -1,82 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** 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, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QMLOSTPLUGIN_H -#define QMLOSTPLUGIN_H - -#include -#include - -QT_BEGIN_NAMESPACE - -class QQmlDebugServer; -class QmlOstPluginPrivate; - -class QmlOstPlugin : public QObject, public QQmlDebugServerConnection -{ - Q_OBJECT - Q_DECLARE_PRIVATE(QmlOstPlugin) - Q_DISABLE_COPY(QmlOstPlugin) - Q_INTERFACES(QQmlDebugServerConnection) - - -public: - QmlOstPlugin(); - ~QmlOstPlugin(); - - void setServer(QQmlDebugServer *server); - void setPort(int port, bool bock, const QString &hostaddress); - - bool isConnected() const; - void send(const QByteArray &message); - void disconnect(); - bool waitForMessage(); - -private Q_SLOTS: - void readyRead(); - -private: - QmlOstPluginPrivate *d_ptr; -}; - -QT_END_NAMESPACE - -#endif // QMLOSTPLUGIN_H diff --git a/src/plugins/qmltooling/qmldbg_ost/qostdevice.cpp b/src/plugins/qmltooling/qmldbg_ost/qostdevice.cpp deleted file mode 100644 index e01041bcfb..0000000000 --- a/src/plugins/qmltooling/qmldbg_ost/qostdevice.cpp +++ /dev/null @@ -1,220 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** 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, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qostdevice.h" -#include - -#include "usbostcomm.h" - -class QOstDevicePrivate : public CActive -{ - QOstDevice* q_ptr; - Q_DECLARE_PUBLIC(QOstDevice) - -public: - QOstDevicePrivate() : CActive(CActive::EPriorityStandard) { - CActiveScheduler::Add(this); - } - ~QOstDevicePrivate() { - Cancel(); - } - - TInt& AoFlags() { return ((TInt*)&iStatus)[1]; } - -private: - void RunL(); - void DoCancel(); - -private: - RUsbOstComm ost; - TBuf8<4096> readBuf; - QByteArray dataBuf; - TBool inReadyRead; -}; - -QOstDevice::QOstDevice(QObject *parent) : - QIODevice(parent), d_ptr(new QOstDevicePrivate) -{ - d_ptr->q_ptr = this; -} - -QOstDevice::~QOstDevice() -{ - close(); - delete d_ptr; -} - -bool QOstDevice::open(int ostProtocolId) -{ - if (isOpen()) - return false; - - Q_D(QOstDevice); - TInt err = d->ost.Connect(); - if (!err) err = d->ost.Open(); - const TVersion KRequiredVersion(1,1,0); - TVersion version = d->ost.Version(); - if (version.iMajor < KRequiredVersion.iMajor || - (version.iMajor == KRequiredVersion.iMajor && version.iMinor < KRequiredVersion.iMinor)) { - setErrorString("CODA version too old. At least version 4.0.18 (without TRK) is required."); - return false; - } - - if (!err) err = d->ost.RegisterProtocolID((TOstProtIds)ostProtocolId, EFalse); - if (!err) { - d->ost.ReadMessage(d->iStatus, d->readBuf); - d->SetActive(); - return QIODevice::open(ReadWrite | Unbuffered); - } - return false; -} - -void QOstDevicePrivate::RunL() -{ - Q_Q(QOstDevice); - //qDebug("QOstDevice received %d bytes q=%x", readBuf.Size(), q); - if (iStatus == KErrNone) { - QByteArray data = QByteArray::fromRawData((const char*)readBuf.Ptr(), readBuf.Size()); - dataBuf.append(data); - - readBuf.Zero(); - ost.ReadMessage(iStatus, readBuf); - SetActive(); - - if (!inReadyRead) { - inReadyRead = true; - emit q->readyRead(); - inReadyRead = false; - } - } else { - q->setErrorString(QString("Error %1 from RUsbOstComm::ReadMessage()").arg(iStatus.Int())); - } - //qDebug("-QOstDevicePrivate RunL"); -} - -void QOstDevicePrivate::DoCancel() -{ - ost.ReadCancel(); -} - -void QOstDevice::close() -{ - Q_D(QOstDevice); - QIODevice::close(); - d->Cancel(); - // RDbgTrcComm::Close isn't safe to call when not open, sigh - if (d->ost.Handle()) { - d->ost.Close(); - } -} - -qint64 QOstDevice::readData(char *data, qint64 maxSize) -{ - Q_D(QOstDevice); - if (d->dataBuf.length() == 0 && !d->IsActive()) - return -1; - qint64 available = qMin(maxSize, (qint64)d->dataBuf.length()); - memcpy(data, d->dataBuf.constData(), available); - d->dataBuf.remove(0, available); - return available; -} - -static const TInt KMaxOstPacketLen = 4096; - -qint64 QOstDevice::writeData(const char *data, qint64 maxSize) -{ - Q_D(QOstDevice); - TPtrC8 ptr((const TUint8*)data, (TInt)maxSize); - while (ptr.Length()) { - TPtrC8 fragment = ptr.Left(qMin(ptr.Length(), KMaxOstPacketLen)); - //qDebug("QOstDevice writing %d bytes", fragment.Length()); - TRequestStatus stat; - d->ost.WriteMessage(stat, fragment); - User::WaitForRequest(stat); - if (stat.Int() != KErrNone) { - setErrorString(QString("Error %1 from RUsbOstComm::WriteMessage()").arg(stat.Int())); - return -1; - } - ptr.Set(ptr.Mid(fragment.Length())); - } - emit bytesWritten(maxSize); //TODO does it matter this is emitted synchronously? - //qDebug("QOstDevice wrote %d bytes", ptr.Size()); - return maxSize; -} - -qint64 QOstDevice::bytesAvailable() const -{ - Q_D(const QOstDevice); - return d->dataBuf.length(); -} - -bool QOstDevice::waitForReadyRead(int msecs) -{ - Q_D(QOstDevice); - if (msecs >= 0) { - RTimer timer; - TInt err = timer.CreateLocal(); - if (err) return false; - TRequestStatus timeoutStat; - timer.After(timeoutStat, msecs*1000); - User::WaitForRequest(timeoutStat, d->iStatus); - if (timeoutStat != KRequestPending) { - // Timed out - timer.Close(); - return false; - } else { - // We got data, so cancel timer - timer.Cancel(); - User::WaitForRequest(timeoutStat); - timer.Close(); - // And drop through - } - } else { - // Just wait forever for data - User::WaitForRequest(d->iStatus); - } - - // If we get here we have data - TInt err = d->iStatus.Int(); - d->AoFlags() &= ~3; // This is necessary to clean up the scheduler as you're not supposed to bypass it like this - TRAP_IGNORE(d->RunL()); - return err == KErrNone; -} diff --git a/src/plugins/qmltooling/qmldbg_ost/qostdevice.h b/src/plugins/qmltooling/qmldbg_ost/qostdevice.h deleted file mode 100644 index 0ddcdb0e61..0000000000 --- a/src/plugins/qmltooling/qmldbg_ost/qostdevice.h +++ /dev/null @@ -1,77 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** 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, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QOSTDEVICE_H -#define QOSTDEVICE_H - -#include - -QT_BEGIN_NAMESPACE - -class QOstDevicePrivate; - -class QOstDevice : public QIODevice -{ - Q_OBJECT - Q_DECLARE_PRIVATE(QOstDevice) - Q_DISABLE_COPY(QOstDevice) - -public: - explicit QOstDevice(QObject *parent=0); - ~QOstDevice(); - - bool open(int ostProtocolId); - void close(); - - bool waitForReadyRead(int msecs); - qint64 bytesAvailable() const; - -protected: - qint64 readData(char *data, qint64 maxSize); - qint64 writeData(const char *data, qint64 maxSize); - -private: - QOstDevicePrivate* d_ptr; -}; - -QT_END_NAMESPACE - -#endif // QOSTDEVICE_H diff --git a/src/plugins/qmltooling/qmldbg_ost/usbostcomm.h b/src/plugins/qmltooling/qmldbg_ost/usbostcomm.h deleted file mode 100644 index 6fa47bfc07..0000000000 --- a/src/plugins/qmltooling/qmldbg_ost/usbostcomm.h +++ /dev/null @@ -1,191 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** 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, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef USBHOSTCOMM_H -#define USBHOSTCOMM_H - -// Based on the official usbostrouter header, modified to remove dependancy on -// the client DLL - -#include - -typedef int TOstProtIds; - -class RUsbOstComm : public RSessionBase -{ -public: - RUsbOstComm(); - TInt Connect(); - TInt Disconnect(); - TInt Open(); - TInt Close(); - TInt RegisterProtocolID(TOstProtIds aId, TBool aNeedHeader); - void ReadMessage(TRequestStatus& aStatus, TDes8& aDes); - TInt ReadCancel(); - void WriteMessage(TRequestStatus& aStatus, const TDesC8& aDes, TBool aHasHeader=EFalse); - TVersion Version() const; - -private: - enum TUsbOstCmdCode - { - EUsbOstCmdCodeFirst, - EUsbOstCmdConnect, - EUsbOstCmdDisconnect, - EUsbOstCmdCodeGetAcmConfig, - EUsbOstCmdCodeSetAcmConfig, - EUsbOstCmdCodeOpen, - EUsbOstCmdCodeClose, - EUsbOstCmdCodeRegisterId, - EUsbOstCmdCodeRegisterIds, - EUsbOstCmdCodeUnRegisterId, - EUsbOstCmdCodeUnRegisterIds, - EUsbOstCmdCodeReadMsg, - EUsbOstCmdCodeReadCancel, - EUsbOstCmdCodeWriteMsg, - EUsbOstCmdCodeWriteCancel, - EUsbOstCmdCodeLast - }; -}; - -RUsbOstComm::RUsbOstComm() -{ -} - -TInt RUsbOstComm::Connect() -{ - _LIT(KUsbOstServerName, "!UsbOstRouter"); - _LIT(KUsbOstServerImageName, "usbostrouter"); - const TUid KUsbOstServerUid = { 0x200170BE }; - TInt startupAttempts = 2; - for(;;) { - TInt ret = CreateSession(KUsbOstServerName, TVersion(1,0,0)); - if (ret != KErrNotFound && ret != KErrServerTerminated) { - return ret; - } - - if (startupAttempts-- == 0) { - return ret; - } - - RProcess server; - ret = server.Create(KUsbOstServerImageName, KNullDesC, KUsbOstServerUid); - if (ret != KErrNone) - return ret; - - TRequestStatus serverDiedRequestStatus; - server.Rendezvous(serverDiedRequestStatus); - - if (serverDiedRequestStatus != KRequestPending) { - // Abort startup - server.Kill(KErrNone); - } else { - // Logon OK - start the server - server.Resume(); - } - User::WaitForRequest(serverDiedRequestStatus); - ret = (server.ExitType() == EExitPanic) ? KErrGeneral : serverDiedRequestStatus.Int(); - server.Close(); - - if (ret != KErrNone && ret != KErrAlreadyExists) { - return ret; - } - } -} - -TInt RUsbOstComm::Disconnect() -{ - return SendReceive(EUsbOstCmdDisconnect); -} - -TInt RUsbOstComm::Open() -{ - return SendReceive(EUsbOstCmdCodeOpen); -} - -TInt RUsbOstComm::Close() -{ - TInt err = SendReceive(EUsbOstCmdCodeClose); - RHandleBase::Close(); - return err; -} - -TInt RUsbOstComm::RegisterProtocolID(const TOstProtIds aId, TBool aNeedHeader) -{ - TIpcArgs args(aId, aNeedHeader); - return SendReceive(EUsbOstCmdCodeRegisterId, args); -} - -void RUsbOstComm::ReadMessage(TRequestStatus& aStatus, TDes8& aDes) -{ - TIpcArgs args(aDes.MaxLength(), &aDes); - SendReceive(EUsbOstCmdCodeReadMsg, args, aStatus); -} - -TInt RUsbOstComm::ReadCancel() -{ - return SendReceive(EUsbOstCmdCodeReadCancel); -} - -void RUsbOstComm::WriteMessage(TRequestStatus& aStatus, const TDesC8& aDes, TBool aHasHeader) -{ - TIpcArgs args(aHasHeader, aDes.Length(), &aDes); - SendReceive(EUsbOstCmdCodeWriteMsg, args, aStatus); -} - -typedef TVersion (*TVersionFunction)(const RUsbOstComm*); -const TInt KVersionOrdinal = 17; - -TVersion RUsbOstComm::Version() const -{ - // This function has to go to the DLL, unfortunately - TVersion result; // Return 0.0.0 on any error - RLibrary lib; - TInt err = lib.Load(_L("usbostcomm")); - if (err) return result; - - TLibraryFunction fn = lib.Lookup(KVersionOrdinal); - if (fn) - result = ((TVersionFunction)fn)(this); - lib.Close(); - return result; -} - -#endif //USBHOSTCOMM_H -- cgit v1.2.3 From 3e3c59b2c05463eea9a9ad99125aac909e70a996 Mon Sep 17 00:00:00 2001 From: Gunnar Sletta Date: Wed, 30 Jan 2013 11:13:09 +0100 Subject: Added to QML_RENDERER_TIMING logic to capture most render bottlenecks. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The typical bottlenecks during rendering are usually compiling shader programs, uploading textures and preparing glyphs, so add profiling data around them. Change-Id: I9c330a0f6b769836d303a035b2c0dce832842aec Reviewed-by: Samuel Rødal --- src/quick/scenegraph/coreapi/qsgrenderer.cpp | 17 +++--- src/quick/scenegraph/qsgadaptationlayer.cpp | 29 ++++++++++ src/quick/scenegraph/qsgcontext.cpp | 16 ++++++ src/quick/scenegraph/qsgthreadedrenderloop.cpp | 23 ++++---- src/quick/scenegraph/util/qsgtexture.cpp | 77 +++++++++++++++++++++++++- 5 files changed, 140 insertions(+), 22 deletions(-) diff --git a/src/quick/scenegraph/coreapi/qsgrenderer.cpp b/src/quick/scenegraph/coreapi/qsgrenderer.cpp index 6f9d380eb5..02eec70952 100644 --- a/src/quick/scenegraph/coreapi/qsgrenderer.cpp +++ b/src/quick/scenegraph/coreapi/qsgrenderer.cpp @@ -60,8 +60,7 @@ QT_BEGIN_NAMESPACE -#define QSG_RENDERER_TIMING -#ifdef QSG_RENDERER_TIMING +#ifndef QSG_NO_RENDERER_TIMING static bool qsg_render_timing = !qgetenv("QML_RENDERER_TIMING").isEmpty(); static QTime frameTimer; static int preprocessTime; @@ -237,7 +236,7 @@ void QSGRenderer::renderScene(const QSGBindable &bindable) m_is_rendering = true; -#ifdef QSG_RENDERER_TIMING +#ifndef QSG_NO_RENDERER_TIMING if (qsg_render_timing) frameTimer.start(); int bindTime = 0; @@ -248,7 +247,7 @@ void QSGRenderer::renderScene(const QSGBindable &bindable) preprocess(); bindable.bind(); -#ifdef QSG_RENDERER_TIMING +#ifndef QSG_NO_RENDERER_TIMING if (qsg_render_timing) bindTime = frameTimer.elapsed(); #endif @@ -269,7 +268,7 @@ void QSGRenderer::renderScene(const QSGBindable &bindable) #endif render(); -#ifdef QSG_RENDERER_TIMING +#ifndef QSG_NO_RENDERER_TIMING if (qsg_render_timing) renderTime = frameTimer.elapsed(); #endif @@ -289,9 +288,9 @@ void QSGRenderer::renderScene(const QSGBindable &bindable) m_index_buffer_bound = false; } -#ifdef QSG_RENDERER_TIMING +#ifndef QSG_NO_RENDERER_TIMING if (qsg_render_timing) { - printf(" - Breakdown of frametime: preprocess=%d, updates=%d, binding=%d, render=%d, total=%d\n", + printf(" - Breakdown of render time: preprocess=%d, updates=%d, binding=%d, render=%d, total=%d\n", preprocessTime, updatePassTime - preprocessTime, bindTime - updatePassTime, @@ -379,7 +378,7 @@ void QSGRenderer::preprocess() } } -#ifdef QSG_RENDERER_TIMING +#ifndef QSG_NO_RENDERER_TIMING if (qsg_render_timing) preprocessTime = frameTimer.elapsed(); #endif @@ -387,7 +386,7 @@ void QSGRenderer::preprocess() nodeUpdater()->setToplevelOpacity(context()->renderAlpha()); nodeUpdater()->updateStates(m_root_node); -#ifdef QSG_RENDERER_TIMING +#ifndef QSG_NO_RENDERER_TIMING if (qsg_render_timing) updatePassTime = frameTimer.elapsed(); #endif diff --git a/src/quick/scenegraph/qsgadaptationlayer.cpp b/src/quick/scenegraph/qsgadaptationlayer.cpp index 87b13d86d3..4e8bafbe95 100644 --- a/src/quick/scenegraph/qsgadaptationlayer.cpp +++ b/src/quick/scenegraph/qsgadaptationlayer.cpp @@ -48,8 +48,14 @@ #include #include +#include + QT_BEGIN_NAMESPACE +#ifndef QSG_NO_RENDERER_TIMING +static bool qsg_render_timing = !qgetenv("QML_RENDERER_TIMING").isEmpty(); +static QElapsedTimer qsg_render_timer; +#endif QSGDistanceFieldGlyphCache::Texture QSGDistanceFieldGlyphCache::s_emptyTexture; @@ -155,6 +161,11 @@ void QSGDistanceFieldGlyphCache::update() if (m_pendingGlyphs.isEmpty()) return; +#ifndef QSG_NO_RENDERER_TIMING + if (qsg_render_timing) + qsg_render_timer.start(); +#endif + QHash distanceFields; for (int i = 0; i < m_pendingGlyphs.size(); ++i) { @@ -164,9 +175,27 @@ void QSGDistanceFieldGlyphCache::update() distanceFields.insert(glyphIndex, distanceField); } +#ifndef QSG_NO_RENDERER_TIMING + int renderTime = 0; + int count = m_pendingGlyphs.size(); + if (qsg_render_timing) + renderTime = qsg_render_timer.elapsed(); +#endif + m_pendingGlyphs.reset(); storeGlyphs(distanceFields); + +#ifndef QSG_NO_RENDERER_TIMING + if (qsg_render_timing) { + printf(" - glyphs: count=%d, render=%d, store=%d, total=%d\n", + count, + renderTime, + (int) qsg_render_timer.elapsed() - renderTime, + (int) qsg_render_timer.elapsed()); + + } +#endif } void QSGDistanceFieldGlyphCache::setGlyphsPosition(const QList &glyphs) diff --git a/src/quick/scenegraph/qsgcontext.cpp b/src/quick/scenegraph/qsgcontext.cpp index c9ac190e86..e7e10ec2f6 100644 --- a/src/quick/scenegraph/qsgcontext.cpp +++ b/src/quick/scenegraph/qsgcontext.cpp @@ -71,6 +71,12 @@ DEFINE_BOOL_CONFIG_OPTION(qmlFlashMode, QML_FLASH_MODE) DEFINE_BOOL_CONFIG_OPTION(qmlTranslucentMode, QML_TRANSLUCENT_MODE) DEFINE_BOOL_CONFIG_OPTION(qmlDisableDistanceField, QML_DISABLE_DISTANCEFIELD) + +#ifndef QSG_NO_RENDERER_TIMING +static bool qsg_render_timing = !qgetenv("QML_RENDERER_TIMING").isEmpty(); +static QElapsedTimer qsg_renderer_timer; +#endif + /* Comments about this class from Gunnar: @@ -463,11 +469,21 @@ QSGMaterialShader *QSGContext::prepareMaterial(QSGMaterial *material) if (shader) return shader; +#ifndef QSG_NO_RENDERER_TIMING + if (qsg_render_timing) + qsg_renderer_timer.start(); +#endif + shader = material->createShader(); shader->compile(); shader->initialize(); d->materials[type] = shader; +#ifndef QSG_NO_RENDERER_TIMING + if (qsg_render_timing) + printf(" - compiling material: %dms\n", (int) qsg_renderer_timer.elapsed()); +#endif + return shader; } diff --git a/src/quick/scenegraph/qsgthreadedrenderloop.cpp b/src/quick/scenegraph/qsgthreadedrenderloop.cpp index 5ec6de2f04..4e8050dd62 100644 --- a/src/quick/scenegraph/qsgthreadedrenderloop.cpp +++ b/src/quick/scenegraph/qsgthreadedrenderloop.cpp @@ -133,8 +133,7 @@ static inline int qsgrl_animation_interval() { } -#define QQUICK_WINDOW_TIMING -#ifdef QQUICK_WINDOW_TIMING +#ifndef QSG_NO_WINDOW_TIMING static bool qquick_window_timing = !qgetenv("QML_WINDOW_TIMING").isEmpty(); static QTime threadTimer; static int syncTime; @@ -537,7 +536,7 @@ void QSGRenderThread::sync() void QSGRenderThread::syncAndRender() { -#ifdef QQUICK_WINDOW_TIMING +#ifndef QSG_NO_WINDOW_TIMING if (qquick_window_timing) sinceLastTime = threadTimer.restart(); #endif @@ -556,7 +555,7 @@ void QSGRenderThread::syncAndRender() sync(); } -#ifdef QQUICK_WINDOW_TIMING +#ifndef QSG_NO_WINDOW_TIMING if (qquick_window_timing) syncTime = threadTimer.elapsed(); #endif @@ -570,7 +569,7 @@ void QSGRenderThread::syncAndRender() } gl->makeCurrent(w.window); d->renderSceneGraph(w.size); -#ifdef QQUICK_WINDOW_TIMING +#ifndef QSG_NO_WINDOW_TIMING if (qquick_window_timing && i == 0) renderTime = threadTimer.elapsed(); #endif @@ -579,7 +578,7 @@ void QSGRenderThread::syncAndRender() } RLDEBUG(" Render: - rendering done"); -#ifdef QQUICK_WINDOW_TIMING +#ifndef QSG_NO_WINDOW_TIMING if (qquick_window_timing) qDebug("window Time: sinceLast=%d, sync=%d, first render=%d, after final swap=%d", sinceLastTime, @@ -885,10 +884,10 @@ void QSGThreadedRenderLoop::polishAndSync() if (!anyoneShowing()) return; -#ifdef QQUICK_WINDOW_TIMING +#ifndef QSG_NO_WINDOW_TIMING QElapsedTimer timer; - int polishTime; - int waitTime; + int polishTime = 0; + int waitTime = 0; if (qquick_window_timing) timer.start(); #endif @@ -899,7 +898,7 @@ void QSGThreadedRenderLoop::polishAndSync() QQuickWindowPrivate *d = QQuickWindowPrivate::get(w.window); d->polishItems(); } -#ifdef QQUICK_WINDOW_TIMING +#ifndef QSG_NO_WINDOW_TIMING if (qquick_window_timing) polishTime = timer.elapsed(); #endif @@ -916,7 +915,7 @@ void QSGThreadedRenderLoop::polishAndSync() QCoreApplication::postEvent(m_thread, event); RLDEBUG("GUI: - wait for sync..."); -#ifdef QQUICK_WINDOW_TIMING +#ifndef QSG_NO_WINDOW_TIMING if (qquick_window_timing) waitTime = timer.elapsed(); #endif @@ -925,7 +924,7 @@ void QSGThreadedRenderLoop::polishAndSync() m_thread->mutex.unlock(); RLDEBUG("GUI: - unlocked after sync..."); -#ifdef QQUICK_WINDOW_TIMING +#ifndef QSG_NO_WINDOW_TIMING if (qquick_window_timing) qDebug(" - polish=%d, wait=%d, sync=%d", polishTime, waitTime - polishTime, int(timer.elapsed() - waitTime)); #endif diff --git a/src/quick/scenegraph/util/qsgtexture.cpp b/src/quick/scenegraph/util/qsgtexture.cpp index 16cc46113d..c6a5672469 100644 --- a/src/quick/scenegraph/util/qsgtexture.cpp +++ b/src/quick/scenegraph/util/qsgtexture.cpp @@ -65,6 +65,12 @@ #include #endif +#ifndef QSG_NO_RENDERER_TIMING +static bool qsg_render_timing = !qgetenv("QML_RENDERER_TIMING").isEmpty(); +static QElapsedTimer qsg_renderer_timer; +#endif + + QT_BEGIN_NAMESPACE inline static bool isPowerOfTwo(int x) @@ -596,14 +602,30 @@ void QSGPlainTexture::bind() m_dirty_texture = false; +#ifndef QSG_NO_RENDERER_TIMING + if (qsg_render_timing) + qsg_renderer_timer.start(); +#endif if (m_image.isNull()) { - if (m_texture_id && m_owns_texture) + if (m_texture_id && m_owns_texture) { glDeleteTextures(1, &m_texture_id); +#ifndef QSG_NO_RENDERER_TIMING + if (qsg_render_timing) { + printf(" - texture deleted in %dms (size: %dx%d)\n", + (int) qsg_renderer_timer.elapsed(), + m_texture_size.width(), + m_texture_size.height()); + } +#endif + } m_texture_id = 0; m_texture_size = QSize(); m_has_mipmaps = false; m_has_alpha = false; + + + return; } @@ -611,6 +633,12 @@ void QSGPlainTexture::bind() glGenTextures(1, &m_texture_id); glBindTexture(GL_TEXTURE_2D, m_texture_id); +#ifndef QSG_NO_RENDERER_TIMING + int bindTime = 0; + if (qsg_render_timing) + bindTime = qsg_renderer_timer.elapsed(); +#endif + // ### TODO: check for out-of-memory situations... int w = m_image.width(); int h = m_image.height(); @@ -619,21 +647,68 @@ void QSGPlainTexture::bind() ? m_image : m_image.convertToFormat(QImage::Format_ARGB32_Premultiplied); +#ifndef QSG_NO_RENDERER_TIMING + int convertTime = 0; + if (qsg_render_timing) + convertTime = qsg_renderer_timer.elapsed(); +#endif + updateBindOptions(m_dirty_bind_options); #ifdef QT_OPENGL_ES qsg_swizzleBGRAToRGBA(&tmp); +#ifndef QSG_NO_RENDERER_TIMING + int swizzleTime = 0; + if (qsg_render_timing) + swizzleTime = qsg_renderer_timer.elapsed(); +#endif glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, tmp.constBits()); #else glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_BGRA, GL_UNSIGNED_BYTE, tmp.constBits()); #endif +#ifndef QSG_NO_RENDERER_TIMING + int uploadTime = 0; + if (qsg_render_timing) + uploadTime = qsg_renderer_timer.elapsed(); +#endif + + if (m_has_mipmaps) { QOpenGLContext *ctx = QOpenGLContext::currentContext(); ctx->functions()->glGenerateMipmap(GL_TEXTURE_2D); m_mipmaps_generated = true; } +#ifndef QSG_NO_RENDERER_TIMING + int mipmapTime = 0; + if (qsg_render_timing) { + mipmapTime = qsg_renderer_timer.elapsed(); + +#ifdef QT_OPENGL_ES + printf(" - plaintexture(%dx%d) bind=%d, convert=%d, swizzle=%d, upload=%d, mipmap=%d, total=%d\n", + m_texture_size.width(), m_texture_size.height(), + bindTime, + convertTime - bindTime, + swizzleTime - convertTime, + uploadTime - swizzleTime, + mipmapTime - uploadTime, + (int) qsg_renderer_timer.elapsed()); +#else + printf(" - plaintexture(%dx%d): bind=%d, convert=%d, upload=%d, mipmap=%d, total=%d\n", + m_texture_size.width(), m_texture_size.height(), + bindTime, + convertTime - bindTime, + uploadTime - convertTime, + mipmapTime - uploadTime, + (int) qsg_renderer_timer.elapsed()); +#endif + + } + +#endif + + m_texture_size = QSize(w, h); m_texture_rect = QRectF(0, 0, 1, 1); -- cgit v1.2.3 From 4e58e01f409104003ab4359cb4cac706b332c896 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Tue, 29 Jan 2013 12:53:41 +0100 Subject: Window can have a transparent/translucent color Change-Id: I1e39b8537dfa00a327b6c735336872cf302c3229 Reviewed-by: Gunnar Sletta --- src/quick/items/qquickwindow.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index bf82423ca9..086865aec3 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -2815,6 +2815,14 @@ void QQuickWindow::setColor(const QColor &color) if (color == d->clearColor) return; + if (color.alpha() != d->clearColor.alpha()) { + QSurfaceFormat fmt = format(); + if (color.alpha() < 255) + fmt.setAlphaBufferSize(8); + else + fmt.setAlphaBufferSize(-1); + setFormat(fmt); + } d->clearColor = color; emit colorChanged(color); d->dirtyItem(contentItem()); -- cgit v1.2.3 From 08a83c42ad441044c669c909fecd80529d02df5a Mon Sep 17 00:00:00 2001 From: Gunnar Sletta Date: Mon, 28 Jan 2013 08:59:43 +0100 Subject: Reenable tst_QQuickItem::simpleFocus and fix warnings. As a result of 1512835ee1425a3e874d2f2dd2b01f1a1ea7b763, the simpleFocus test now passes on Mac and can be reneabled. I also took the liberty of making sure windows have a valid geometry, so that we don't get tons of warnings. Task-number: QTBUG-24094 Change-Id: I5b8bc82f3f5397110f6e487898d511e810bae59d Reviewed-by: Alan Alpert --- tests/auto/quick/qquickitem/tst_qquickitem.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/auto/quick/qquickitem/tst_qquickitem.cpp b/tests/auto/quick/qquickitem/tst_qquickitem.cpp index 2d8db6ef17..a7343f686f 100644 --- a/tests/auto/quick/qquickitem/tst_qquickitem.cpp +++ b/tests/auto/quick/qquickitem/tst_qquickitem.cpp @@ -175,6 +175,8 @@ private: }; void ensureFocus(QWindow *w) { + if (w->width() <=0 || w->height() <= 0) + w->setGeometry(100, 100, 400, 300); w->show(); w->requestActivate(); QTest::qWaitForWindowActive(w); @@ -270,10 +272,6 @@ void tst_qquickitem::simpleFocus() QQuickWindow window; ensureFocus(&window); -#ifdef Q_OS_MAC - QSKIP("QTBUG-24094: fails on Mac OS X 10.7"); -#endif - QTRY_VERIFY(QGuiApplication::focusWindow() == &window); QQuickItem *l1c1 = new TestItem(window.contentItem()); -- cgit v1.2.3 From 271b12790f9459287e909016e5a56b6bc07f493c Mon Sep 17 00:00:00 2001 From: Alberto Mardegan Date: Thu, 27 Dec 2012 11:41:08 +0100 Subject: FolderListModel: use QDateTime for file times Make the "fileModified" and "fileAccessed" roles return times as a QDateTime, and not as an already formatted string. Change-Id: I1c114477a31e65007ad7bac87b11cb8b0dc7de01 Reviewed-by: Alan Alpert --- src/imports/folderlistmodel/qquickfolderlistmodel.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/imports/folderlistmodel/qquickfolderlistmodel.cpp b/src/imports/folderlistmodel/qquickfolderlistmodel.cpp index 6cb014a5ad..3e3a824648 100644 --- a/src/imports/folderlistmodel/qquickfolderlistmodel.cpp +++ b/src/imports/folderlistmodel/qquickfolderlistmodel.cpp @@ -334,10 +334,10 @@ QVariant QQuickFolderListModel::data(const QModelIndex &index, int role) const rv = d->data.at(index.row()).size(); break; case FileLastModifiedRole: - rv = d->data.at(index.row()).lastModified().date().toString(Qt::ISODate) + " " + d->data.at(index.row()).lastModified().time().toString(); + rv = d->data.at(index.row()).lastModified(); break; case FileLastReadRole: - rv = d->data.at(index.row()).lastRead().date().toString(Qt::ISODate) + " " + d->data.at(index.row()).lastRead().time().toString(); + rv = d->data.at(index.row()).lastRead(); break; case FileIsDirRole: rv = d->data.at(index.row()).isDir(); -- cgit v1.2.3 From b7c3d7b4d50055d0f8534a9ca73df56c888d77a1 Mon Sep 17 00:00:00 2001 From: Michael Brasser Date: Fri, 1 Feb 2013 07:33:15 -0600 Subject: Stabilize tst_qquicktextinput::remoteCursorDelegate() In some cases QCOMPARE(component.status(), QQmlComponent::Loading); (line 2800) is failing -- it appears to be Ready immediately. Add a delay to ensure it always hits Loading before Ready. Change-Id: I975f3b3ea6004f178a7b0fb21193dcbdaf483157 Reviewed-by: Gunnar Sletta --- tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp b/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp index 3debfbe234..2714ce47f6 100644 --- a/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp +++ b/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp @@ -2775,7 +2775,7 @@ void tst_qquicktextinput::cursorDelegate() void tst_qquicktextinput::remoteCursorDelegate() { TestHTTPServer server(SERVER_PORT); - server.serveDirectory(dataDirectory()); + server.serveDirectory(dataDirectory(), TestHTTPServer::Delay); QQuickView view; -- cgit v1.2.3 From 8b50a9f00f6f5b693d18c59ff15968e2b902ebf8 Mon Sep 17 00:00:00 2001 From: Michael Brasser Date: Fri, 25 Jan 2013 21:00:15 -0600 Subject: Dereference the QImage once the corresponding texture is bound. We do not require a reference here, as the texture is only bound once. It is the texture factory that is reused (when QQuickWIndow::setPersistentSceneGraph(false) is set and the window is hidden and then re-exposed). With this patch it becomes trivial to create a custom QSGContextPlugin with a texture factory that does not retain the QImage, freeing up memory. This is useful in the case of an embedded system with only a single window that is never hidden or re-exposed. Change-Id: I1cfa6efc3a2e9e641b456bf90c55d563061757b8 Reviewed-by: Gunnar Sletta --- src/quick/scenegraph/util/qsgpainternode.cpp | 2 +- src/quick/scenegraph/util/qsgtexture.cpp | 3 +++ src/quick/scenegraph/util/qsgtexture_p.h | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/quick/scenegraph/util/qsgpainternode.cpp b/src/quick/scenegraph/util/qsgpainternode.cpp index 2d536ea547..a6b0c328e6 100644 --- a/src/quick/scenegraph/util/qsgpainternode.cpp +++ b/src/quick/scenegraph/util/qsgpainternode.cpp @@ -70,7 +70,7 @@ static inline int qt_next_power_of_two(int v) QSGPainterTexture::QSGPainterTexture() : QSGPlainTexture() { - + m_retain_image = true; } #ifdef QT_OPENGL_ES diff --git a/src/quick/scenegraph/util/qsgtexture.cpp b/src/quick/scenegraph/util/qsgtexture.cpp index c6a5672469..e06f075f1b 100644 --- a/src/quick/scenegraph/util/qsgtexture.cpp +++ b/src/quick/scenegraph/util/qsgtexture.cpp @@ -519,6 +519,7 @@ QSGPlainTexture::QSGPlainTexture() , m_dirty_bind_options(false) , m_owns_texture(true) , m_mipmaps_generated(false) + , m_retain_image(false) { } @@ -713,6 +714,8 @@ void QSGPlainTexture::bind() m_texture_rect = QRectF(0, 0, 1, 1); m_dirty_bind_options = false; + if (!m_retain_image) + m_image = QImage(); } diff --git a/src/quick/scenegraph/util/qsgtexture_p.h b/src/quick/scenegraph/util/qsgtexture_p.h index ed1d782c35..6430a93ed8 100644 --- a/src/quick/scenegraph/util/qsgtexture_p.h +++ b/src/quick/scenegraph/util/qsgtexture_p.h @@ -112,6 +112,7 @@ protected: uint m_dirty_bind_options : 1; uint m_owns_texture : 1; uint m_mipmaps_generated : 1; + uint m_retain_image: 1; }; QT_END_NAMESPACE -- cgit v1.2.3 From ed761c779b2127b520033a89273085d98f1e9c31 Mon Sep 17 00:00:00 2001 From: Gunnar Sletta Date: Fri, 1 Feb 2013 10:49:01 +0100 Subject: Use the optimal image format for the sprites Change-Id: Ib6cfbc0e5c743d21c265fad926aac29a601f3b0a Reviewed-by: Alan Alpert --- src/quick/items/qquickspriteengine.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/quick/items/qquickspriteengine.cpp b/src/quick/items/qquickspriteengine.cpp index 6dc4cf4436..4c7be3bce9 100644 --- a/src/quick/items/qquickspriteengine.cpp +++ b/src/quick/items/qquickspriteengine.cpp @@ -429,7 +429,7 @@ QImage QQuickSpriteEngine::assembledImage() } //maxFrames is max number in a line of the texture - QImage image(w, h, QImage::Format_ARGB32); + QImage image(w, h, QImage::Format_ARGB32_Premultiplied); image.fill(0); QPainter p(&image); int y = 0; -- cgit v1.2.3 From f626f32c91781ee3fe3aaa7209114efb9d6e3e7c Mon Sep 17 00:00:00 2001 From: Michael Brasser Date: Fri, 1 Feb 2013 09:01:43 -0600 Subject: Add ETC1 texture provider example. This code was removed as a labs import in commit 732a86a11dbc20079e33c289af72762ce0125ced. Bring it back as an example. Change-Id: I057d721117dc75256c4a7229c4f8fad8dc9ac6b2 Reviewed-by: Gunnar Sletta --- examples/quick/textureprovider/Label.qml | 49 ++++++ examples/quick/textureprovider/etcprovider.cpp | 193 +++++++++++++++++++++ examples/quick/textureprovider/etcprovider.h | 80 +++++++++ examples/quick/textureprovider/images/qt-logo.jpg | Bin 0 -> 19365 bytes examples/quick/textureprovider/images/qt-logo.pkm | Bin 0 -> 32784 bytes examples/quick/textureprovider/main.cpp | 56 ++++++ examples/quick/textureprovider/textureprovider.pro | 13 ++ examples/quick/textureprovider/textureprovider.qml | 65 +++++++ examples/quick/textureprovider/textureprovider.qrc | 8 + 9 files changed, 464 insertions(+) create mode 100644 examples/quick/textureprovider/Label.qml create mode 100644 examples/quick/textureprovider/etcprovider.cpp create mode 100644 examples/quick/textureprovider/etcprovider.h create mode 100644 examples/quick/textureprovider/images/qt-logo.jpg create mode 100644 examples/quick/textureprovider/images/qt-logo.pkm create mode 100644 examples/quick/textureprovider/main.cpp create mode 100644 examples/quick/textureprovider/textureprovider.pro create mode 100644 examples/quick/textureprovider/textureprovider.qml create mode 100644 examples/quick/textureprovider/textureprovider.qrc diff --git a/examples/quick/textureprovider/Label.qml b/examples/quick/textureprovider/Label.qml new file mode 100644 index 0000000000..990ac247dc --- /dev/null +++ b/examples/quick/textureprovider/Label.qml @@ -0,0 +1,49 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 + +Text { + font.pixelSize: 26 + style: Text.Outline; styleColor: "white" + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: parent.bottom + anchors.bottomMargin: 20 +} diff --git a/examples/quick/textureprovider/etcprovider.cpp b/examples/quick/textureprovider/etcprovider.cpp new file mode 100644 index 0000000000..0bb072ffaf --- /dev/null +++ b/examples/quick/textureprovider/etcprovider.cpp @@ -0,0 +1,193 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the demonstration applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "etcprovider.h" + +#include +#include +#include + +//#define ETC_DEBUG + +#ifndef GL_ETC1_RGB8_OES + #define GL_ETC1_RGB8_OES 0x8d64 +#endif + +typedef struct { + char aName[6]; + unsigned short iBlank; + /* NB: Beware endianness issues here. */ + unsigned char iPaddedWidthMSB; + unsigned char iPaddedWidthLSB; + unsigned char iPaddedHeightMSB; + unsigned char iPaddedHeightLSB; + unsigned char iWidthMSB; + unsigned char iWidthLSB; + unsigned char iHeightMSB; + unsigned char iHeightLSB; +} ETCHeader; + +unsigned short getWidth(ETCHeader *pHeader) +{ + return (pHeader->iWidthMSB << 8) | pHeader->iWidthLSB; +} + +unsigned short getHeight(ETCHeader *pHeader) +{ + return (pHeader->iHeightMSB << 8) | pHeader->iHeightLSB; +} + +unsigned short getPaddedWidth(ETCHeader *pHeader) +{ + return (pHeader->iPaddedWidthMSB << 8) | pHeader->iPaddedWidthLSB; +} + +unsigned short getPaddedHeight(ETCHeader *pHeader) +{ + return (pHeader->iPaddedHeightMSB << 8) | pHeader->iPaddedHeightLSB; +} + +EtcTexture::EtcTexture() + : m_texture_id(0) +{ + +} + +EtcTexture::~EtcTexture() +{ + if (m_texture_id) + glDeleteTextures(1, &m_texture_id); +} + +void EtcTexture::bind() +{ + if (m_texture_id) { + glBindTexture(GL_TEXTURE_2D, m_texture_id); + return; + } + + glGenTextures(1, &m_texture_id); + glBindTexture(GL_TEXTURE_2D, m_texture_id); + +#ifdef ETC_DEBUG + qDebug() << "glCompressedTexImage2D, width: " << m_size.width() << "height" << m_size.height() << + "paddedWidth: " << m_paddedSize.width() << "paddedHeight: " << m_paddedSize.height(); +#endif + + QOpenGLContext *ctx = QOpenGLContext::currentContext(); + Q_ASSERT(ctx != 0); + ctx->functions()->glCompressedTexImage2D(GL_TEXTURE_2D, 0, GL_ETC1_RGB8_OES, + m_size.width(), m_size.height(), 0, + (m_paddedSize.width() * m_paddedSize.height()) >> 1, + m_data.data() + 16); + + // Gracefully fail in case of an error... + GLuint error = glGetError(); + if (error != GL_NO_ERROR) { + qDebug () << "glCompressedTexImage2D for compressed texture failed, error: " << error; + glBindTexture(GL_TEXTURE_2D, 0); + glDeleteTextures(1, &m_texture_id); + m_texture_id = 0; + return; + } + + updateBindOptions(true); +} + +class QEtcTextureFactory : public QQuickTextureFactory +{ +public: + QByteArray m_data; + QSize m_size; + QSize m_paddedSize; + + QSize textureSize() const { return m_size; } + int textureByteCount() const { return m_data.size(); } + + QSGTexture *createTexture(QQuickWindow *) const { + EtcTexture *texture = new EtcTexture; + texture->m_data = m_data; + texture->m_size = m_size; + texture->m_paddedSize = m_paddedSize; + return texture; + } +}; + +QQuickTextureFactory *EtcProvider::requestTexture(const QString &id, QSize *size, const QSize &requestedSize) +{ + Q_UNUSED(requestedSize); + QEtcTextureFactory *ret = 0; + + size->setHeight(0); + size->setWidth(0); + + // resolve paths relative to qrc file + QFile file(QLatin1String(":/textureprovider/") + id); +#ifdef ETC_DEBUG + qDebug() << "requestTexture opening file: " << id; +#endif + if (file.open(QIODevice::ReadOnly)) { + ret = new QEtcTextureFactory; + ret->m_data = file.readAll(); + if (!ret->m_data.isEmpty()) { + ETCHeader *pETCHeader = NULL; + pETCHeader = (ETCHeader *)ret->m_data.data(); + size->setHeight(getHeight(pETCHeader)); + size->setWidth(getWidth(pETCHeader)); + ret->m_size = *size; + ret->m_paddedSize.setHeight(getPaddedHeight(pETCHeader)); + ret->m_paddedSize.setWidth(getPaddedWidth(pETCHeader)); + } + else { + delete ret; + ret = 0; + } + } + +#ifdef ETC_DEBUG + if (ret) + qDebug() << "requestTexture returning: " << ret->m_data.length() << ", bytes; width: " << size->width() << ", height: " << size->height(); + else + qDebug () << "File not found."; +#endif + + return ret; +} diff --git a/examples/quick/textureprovider/etcprovider.h b/examples/quick/textureprovider/etcprovider.h new file mode 100644 index 0000000000..4215ea297b --- /dev/null +++ b/examples/quick/textureprovider/etcprovider.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the demonstration applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef ETCPROVIDER_H +#define ETCPROVIDER_H + +#include +#include +#include + +class EtcProvider : public QQuickImageProvider +{ +public: + EtcProvider() + : QQuickImageProvider(QQuickImageProvider::Texture) + {} + + QQuickTextureFactory *requestTexture(const QString &id, QSize *size, const QSize &requestedSize); +}; + +class EtcTexture : public QSGTexture +{ + Q_OBJECT +public: + EtcTexture(); + ~EtcTexture(); + + void bind(); + + QSize textureSize() const { return m_size; } + int textureId() const { return m_texture_id; } + + bool hasAlphaChannel() const { return false; } + bool hasMipmaps() const { return false; } + + QByteArray m_data; + QSize m_size; + QSize m_paddedSize; + GLuint m_texture_id; +}; + +#endif // ETCPROVIDER_H diff --git a/examples/quick/textureprovider/images/qt-logo.jpg b/examples/quick/textureprovider/images/qt-logo.jpg new file mode 100644 index 0000000000..32d151c2c5 Binary files /dev/null and b/examples/quick/textureprovider/images/qt-logo.jpg differ diff --git a/examples/quick/textureprovider/images/qt-logo.pkm b/examples/quick/textureprovider/images/qt-logo.pkm new file mode 100644 index 0000000000..3329031ab2 Binary files /dev/null and b/examples/quick/textureprovider/images/qt-logo.pkm differ diff --git a/examples/quick/textureprovider/main.cpp b/examples/quick/textureprovider/main.cpp new file mode 100644 index 0000000000..d9d4fb0d38 --- /dev/null +++ b/examples/quick/textureprovider/main.cpp @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +#include "etcprovider.h" + +int main(int argc, char* argv[]) +{ + QGuiApplication app(argc,argv); + QQuickView view; + + view.engine()->addImageProvider("etc", new EtcProvider()); + + view.setSource(QUrl("qrc:///textureprovider/textureprovider.qml")); + view.show(); + return app.exec(); +} diff --git a/examples/quick/textureprovider/textureprovider.pro b/examples/quick/textureprovider/textureprovider.pro new file mode 100644 index 0000000000..5d9825f7ac --- /dev/null +++ b/examples/quick/textureprovider/textureprovider.pro @@ -0,0 +1,13 @@ +TEMPLATE = app + +QT += quick qml + +HEADERS += etcprovider.h + +SOURCES += main.cpp \ + etcprovider.cpp + +RESOURCES += textureprovider.qrc + +target.path = $$[QT_INSTALL_EXAMPLES]/quick/textureprovider +INSTALLS += target diff --git a/examples/quick/textureprovider/textureprovider.qml b/examples/quick/textureprovider/textureprovider.qml new file mode 100644 index 0000000000..9854bef229 --- /dev/null +++ b/examples/quick/textureprovider/textureprovider.qml @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 + +Row { + width: 512 + height: 256 + + Image { + source: "images/qt-logo.jpg" + + Label { text: "Original Image" } + } + + Image { + source: "image://etc/images/qt-logo.pkm" + + Label { id: clabel; text: "Compressed Texture" } + Text { + text: "(requires ETC1 support)" + anchors.horizontalCenter: clabel.horizontalCenter + anchors.top: clabel.bottom + font.pixelSize: 14 + color: "white" + } + } +} diff --git a/examples/quick/textureprovider/textureprovider.qrc b/examples/quick/textureprovider/textureprovider.qrc new file mode 100644 index 0000000000..77a568ba1d --- /dev/null +++ b/examples/quick/textureprovider/textureprovider.qrc @@ -0,0 +1,8 @@ + + + images/qt-logo.jpg + images/qt-logo.pkm + textureprovider.qml + Label.qml + + -- cgit v1.2.3 From 8f774ffb954c219d503e99c94b62e754a7e6e435 Mon Sep 17 00:00:00 2001 From: Gunnar Sletta Date: Tue, 5 Feb 2013 13:45:24 +0100 Subject: Warn if the OpenGL context lacks depth/stencil when requested. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I thought this was a serious bug in the renderer, but it turned out to be qmlscene just overriding the surface format without requesting the needed depth and stencil buffers. An easy mistake to make, hence introduce the warning. Task-number: QTBUG-29037 Change-Id: I59d09d1b2a139e3add851777ff2eeb6c4efb47ec Reviewed-by: Samuel Rødal --- src/quick/scenegraph/qsgcontext.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/quick/scenegraph/qsgcontext.cpp b/src/quick/scenegraph/qsgcontext.cpp index e7e10ec2f6..705b12c4d4 100644 --- a/src/quick/scenegraph/qsgcontext.cpp +++ b/src/quick/scenegraph/qsgcontext.cpp @@ -249,6 +249,14 @@ void QSGContext::initialize(QOpenGLContext *context) { Q_D(QSGContext); + // Sanity check the surface format, in case it was overridden by the application + QSurfaceFormat requested = defaultSurfaceFormat(); + QSurfaceFormat actual = context->format(); + if (requested.depthBufferSize() > 0 && actual.depthBufferSize() <= 0) + qWarning("QSGContext::initialize: depth buffer support missing, expect rendering errors"); + if (requested.stencilBufferSize() > 0 && actual.stencilBufferSize() <= 0) + qWarning("QSGContext::initialize: stencil buffer support missing, expect rendering errors"); + Q_ASSERT(!d->gl); d->gl = context; -- cgit v1.2.3 From a51d867ff269a6d2a703a96bc6eff113893d13d8 Mon Sep 17 00:00:00 2001 From: Gunnar Sletta Date: Fri, 1 Feb 2013 12:38:26 +0100 Subject: Respect the default format from the scene graph adaptation layer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I080c25ed8b8cc8b743043b5aa348927749c1f0eb Reviewed-by: Samuel Rødal --- tools/qmlscene/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/qmlscene/main.cpp b/tools/qmlscene/main.cpp index 090d173f9f..1e7c6e13de 100644 --- a/tools/qmlscene/main.cpp +++ b/tools/qmlscene/main.cpp @@ -501,7 +501,7 @@ int main(int argc, char ** argv) } if (window) { - QSurfaceFormat surfaceFormat; + QSurfaceFormat surfaceFormat = window->requestedFormat(); if (options.multisample) surfaceFormat.setSamples(16); if (options.transparent) { -- cgit v1.2.3 From a81a24e01b1fe68e8ddce65a2d6187d6542cc6e0 Mon Sep 17 00:00:00 2001 From: Gunnar Sletta Date: Tue, 5 Feb 2013 14:30:31 +0100 Subject: Enable sorting of opaque items again in the default renderer. This was disabled a long while back because we thought the code was broken, and it turned out the problem was that our sort function had a bug. b570d384c0704ea12364e353493eeb6f1ae34cd3 fixed the actual problem, so now we reenable it. Task-number: QTBUG-28563 Change-Id: I4032df82a1fd4cf7f775c8c64302c18239879fea Reviewed-by: Michael Brasser --- src/quick/scenegraph/coreapi/qsgdefaultrenderer.cpp | 6 +++--- src/quick/scenegraph/coreapi/qsgdefaultrenderer_p.h | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/quick/scenegraph/coreapi/qsgdefaultrenderer.cpp b/src/quick/scenegraph/coreapi/qsgdefaultrenderer.cpp index e662c2bcba..1642f7f0b2 100644 --- a/src/quick/scenegraph/coreapi/qsgdefaultrenderer.cpp +++ b/src/quick/scenegraph/coreapi/qsgdefaultrenderer.cpp @@ -131,7 +131,6 @@ QSGDefaultRenderer::QSGDefaultRenderer(QSGContext *context) , m_transparentNodes(64) , m_renderGroups(4) , m_rebuild_lists(false) - , m_needs_sorting(false) , m_sort_front_to_back(false) , m_render_node_added(false) , m_currentRenderOrder(1) @@ -212,6 +211,8 @@ void QSGDefaultRenderer::render() m_currentProgram = 0; m_currentMatrix = 0; + bool sortNodes = m_rebuild_lists; + if (m_rebuild_lists) { m_opaqueNodes.reset(); m_transparentNodes.reset(); @@ -228,7 +229,7 @@ void QSGDefaultRenderer::render() int debugtimeLists = debugTimer.elapsed(); #endif - if (m_needs_sorting) { + if (sortNodes) { if (!m_opaqueNodes.isEmpty()) { bool (*lessThan)(QSGNode *, QSGNode *); lessThan = m_sort_front_to_back ? nodeLessThanWithRenderOrder : nodeLessThan; @@ -240,7 +241,6 @@ void QSGDefaultRenderer::render() start = end; } } - m_needs_sorting = false; } #ifdef RENDERER_DEBUG diff --git a/src/quick/scenegraph/coreapi/qsgdefaultrenderer_p.h b/src/quick/scenegraph/coreapi/qsgdefaultrenderer_p.h index 645ba93b80..ae759c0b24 100644 --- a/src/quick/scenegraph/coreapi/qsgdefaultrenderer_p.h +++ b/src/quick/scenegraph/coreapi/qsgdefaultrenderer_p.h @@ -76,7 +76,6 @@ private: QDataBuffer m_renderGroups; bool m_rebuild_lists; - bool m_needs_sorting; bool m_sort_front_to_back; bool m_render_node_added; int m_currentRenderOrder; -- cgit v1.2.3 From f4536754b49d1aa39843dfa24c88c903a535ff35 Mon Sep 17 00:00:00 2001 From: hjk Date: Wed, 6 Feb 2013 10:28:13 +0100 Subject: Qml: Use QString::append(QStringRef) overload in ProcessAST::asString Change-Id: Ifc9d276e0d4f9e2ff9a89c7edbe34a04ab080774 Reviewed-by: Kai Koehne --- src/qml/qml/qqmlscript.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qml/qml/qqmlscript.cpp b/src/qml/qml/qqmlscript.cpp index 2f8750ee0f..613ff24f20 100644 --- a/src/qml/qml/qqmlscript.cpp +++ b/src/qml/qml/qqmlscript.cpp @@ -651,7 +651,7 @@ QString ProcessAST::asString(AST::UiQualifiedId *node) const QString s; for (AST::UiQualifiedId *it = node; it; it = it->next) { - s.append(it->name.toString()); + s.append(it->name); if (it->next) s.append(QLatin1Char('.')); -- cgit v1.2.3 From 40a977771a62b6c89a055d5fda2a92a12828473b Mon Sep 17 00:00:00 2001 From: hjk Date: Wed, 6 Feb 2013 10:50:56 +0100 Subject: Use constFind instead of find in QQmlMetaType::qmlType There is no need to detach when only checking for the presence of an item. Change-Id: I59a0aadb74b4613b019882bcf67e84e68df18a21 Reviewed-by: Kai Koehne --- src/qml/qml/qqmlmetatype.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index e4f51474b9..2a2d0b3879 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -1531,7 +1531,7 @@ QQmlType *QQmlMetaType::qmlType(const QHashedStringRef &name, const QHashedStrin QReadLocker lock(metaTypeDataLock()); QQmlMetaTypeData *data = metaTypeData(); - QQmlMetaTypeData::Names::ConstIterator it = data->nameToType.find(name); + QQmlMetaTypeData::Names::ConstIterator it = data->nameToType.constFind(name); while (it != data->nameToType.end() && it.key() == name) { // XXX version_major<0 just a kludge for QQmlPropertyPrivate::initProperty if (version_major < 0 || (*it)->availableInVersion(module, version_major,version_minor)) @@ -1565,7 +1565,7 @@ QQmlType *QQmlMetaType::qmlType(const QMetaObject *metaObject, const QHashedStri QReadLocker lock(metaTypeDataLock()); QQmlMetaTypeData *data = metaTypeData(); - QQmlMetaTypeData::MetaObjects::const_iterator it = data->metaObjectToType.find(metaObject); + QQmlMetaTypeData::MetaObjects::const_iterator it = data->metaObjectToType.constFind(metaObject); while (it != data->metaObjectToType.end() && it.key() == metaObject) { QQmlType *t = *it; if (version_major < 0 || t->availableInVersion(module, version_major,version_minor)) -- cgit v1.2.3 From 2150eaa2c49fa5f996aa69980945522cf0a0f281 Mon Sep 17 00:00:00 2001 From: Gunnar Sletta Date: Sat, 2 Feb 2013 10:56:03 +0100 Subject: Avoid swizzling on OpenGL ES when possible Add support for APPLE_texture_format_BGRA8888, IMG_texture_format_BGRA8888, EXT_texture_format_BGRA8888 and EXT_bgra. The apple one acts just like the desktop EXT_bgra one, so they need slightly different handling than the ES extensions. This change also has the benefit that we no longer have a dedicated ES path. Change-Id: Ibf6dbf7548ca16a1bada61c677181d8f809c69a2 Reviewed-by: Gunnar Sletta --- src/quick/scenegraph/util/qsgtexture.cpp | 42 +++++++++++++++++++------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/src/quick/scenegraph/util/qsgtexture.cpp b/src/quick/scenegraph/util/qsgtexture.cpp index e06f075f1b..87ef91b212 100644 --- a/src/quick/scenegraph/util/qsgtexture.cpp +++ b/src/quick/scenegraph/util/qsgtexture.cpp @@ -70,6 +70,10 @@ static bool qsg_render_timing = !qgetenv("QML_RENDERER_TIMING").isEmpty(); static QElapsedTimer qsg_renderer_timer; #endif +#ifndef GL_BGRA +#define GL_BGRA 0x80E1 +#endif + QT_BEGIN_NAMESPACE @@ -530,7 +534,6 @@ QSGPlainTexture::~QSGPlainTexture() glDeleteTextures(1, &m_texture_id); } -#ifdef QT_OPENGL_ES void qsg_swizzleBGRAToRGBA(QImage *image) { const int width = image->width(); @@ -541,7 +544,6 @@ void qsg_swizzleBGRAToRGBA(QImage *image) p[x] = ((p[x] << 16) & 0xff0000) | ((p[x] >> 16) & 0xff) | (p[x] & 0xff00ff00); } } -#endif void QSGPlainTexture::setImage(const QImage &image) { @@ -656,17 +658,31 @@ void QSGPlainTexture::bind() updateBindOptions(m_dirty_bind_options); + GLenum externalFormat = GL_RGBA; + GLenum internalFormat = GL_RGBA; + + const char *extensions = (const char *) glGetString(GL_EXTENSIONS); + if (strstr(extensions, "GL_EXT_bgra")) { + externalFormat = GL_BGRA; #ifdef QT_OPENGL_ES + internalFormat = GL_BGRA; +#endif + } else if (strstr(extensions, "GL_APPLE_texture_format_BGRA8888")) { + externalFormat = GL_BGRA; + } else if (strstr(extensions, "GL_EXT_texture_format_BGRA8888") + || strstr(extensions, "GL_IMG_texture_format_BGRA8888")) { + externalFormat = GL_BGRA; + internalFormat = GL_BGRA; + } else { qsg_swizzleBGRAToRGBA(&tmp); + } + #ifndef QSG_NO_RENDERER_TIMING int swizzleTime = 0; if (qsg_render_timing) swizzleTime = qsg_renderer_timer.elapsed(); #endif - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, tmp.constBits()); -#else - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_BGRA, GL_UNSIGNED_BYTE, tmp.constBits()); -#endif + glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, w, h, 0, externalFormat, GL_UNSIGNED_BYTE, tmp.constBits()); #ifndef QSG_NO_RENDERER_TIMING int uploadTime = 0; @@ -686,24 +702,16 @@ void QSGPlainTexture::bind() if (qsg_render_timing) { mipmapTime = qsg_renderer_timer.elapsed(); -#ifdef QT_OPENGL_ES - printf(" - plaintexture(%dx%d) bind=%d, convert=%d, swizzle=%d, upload=%d, mipmap=%d, total=%d\n", + printf(" - plaintexture(%dx%d) bind=%d, convert=%d, swizzle=%d (%s->%s), upload=%d, mipmap=%d, total=%d\n", m_texture_size.width(), m_texture_size.height(), bindTime, convertTime - bindTime, swizzleTime - convertTime, + externalFormat == GL_BGRA ? "BGRA" : "RGBA", + internalFormat == GL_BGRA ? "BGRA" : "RGBA", uploadTime - swizzleTime, mipmapTime - uploadTime, (int) qsg_renderer_timer.elapsed()); -#else - printf(" - plaintexture(%dx%d): bind=%d, convert=%d, upload=%d, mipmap=%d, total=%d\n", - m_texture_size.width(), m_texture_size.height(), - bindTime, - convertTime - bindTime, - uploadTime - convertTime, - mipmapTime - uploadTime, - (int) qsg_renderer_timer.elapsed()); -#endif } -- cgit v1.2.3 From b2e916eb871f6dd91ac3f2c69e856107edb1efd5 Mon Sep 17 00:00:00 2001 From: Alan Alpert Date: Wed, 16 Jan 2013 10:13:00 -0800 Subject: Add \qmlmodule commands for QtQml and QtQuick Change-Id: I07a961bb2c5e39bfef134264e5f24974e1501361 Reviewed-by: Jerome Pasion --- src/qml/doc/src/qmltypereference.qdoc | 8 ++++---- src/quick/doc/src/qmltypereference.qdoc | 13 +++++++++++++ 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/qml/doc/src/qmltypereference.qdoc b/src/qml/doc/src/qmltypereference.qdoc index 203bdd3eb9..cce8d108d8 100644 --- a/src/qml/doc/src/qmltypereference.qdoc +++ b/src/qml/doc/src/qmltypereference.qdoc @@ -25,9 +25,10 @@ ** ****************************************************************************/ /*! -\page qtqml-typereference-topic.html -\title QML Types Provided By The QtQml Module -\brief List of QML types provided by the QtQml module +\qmlmodule QtQml 2 +\title Qt Qml QML Types +\ingroup qmlmodules +\brief List of QML types provided by the Qt QML module The \c QtQml module provides the definition and implementation of various convenience types which can be used with the QML language, including some @@ -153,7 +154,6 @@ run-time: } } \endcode - */ /*! diff --git a/src/quick/doc/src/qmltypereference.qdoc b/src/quick/doc/src/qmltypereference.qdoc index 9604b927a8..a627cfe7b5 100644 --- a/src/quick/doc/src/qmltypereference.qdoc +++ b/src/quick/doc/src/qmltypereference.qdoc @@ -284,6 +284,19 @@ Data Storage */ +/*! +\qmlmodule QtQuick 2 +\brief The QtQuick 2 module provides graphical primitives for use in QML. + +The QtQuick 2 module provides graphical primitive types. They can be used with the following import +\code +import QtQuick 2.1 +\endcode + +For a more detailed listing of types in the QtQuick 2 import, see the \l{qtquick-qmltypereference.html}{QtQuick 2 type reference page}. +For more details about the QtQuick 2 module, see the \l{QtQuick} module page. +*/ + /*! \qmlbasictype color \ingroup qtquickbasictypes -- cgit v1.2.3 From eb9036b09728080f09d17f95876aaedddc5fe749 Mon Sep 17 00:00:00 2001 From: Gunnar Sletta Date: Wed, 6 Feb 2013 11:41:12 +0100 Subject: Stabilize tst_qquickpositioners Change-Id: I41e37493d5e1a6569beb5a26d6f2033a546af4b4 Reviewed-by: Alan Alpert --- .../qquickpositioners/tst_qquickpositioners.cpp | 125 ++++++++------------- 1 file changed, 45 insertions(+), 80 deletions(-) diff --git a/tests/auto/quick/qquickpositioners/tst_qquickpositioners.cpp b/tests/auto/quick/qquickpositioners/tst_qquickpositioners.cpp index 9cceaab5dd..1f472a3f46 100644 --- a/tests/auto/quick/qquickpositioners/tst_qquickpositioners.cpp +++ b/tests/auto/quick/qquickpositioners/tst_qquickpositioners.cpp @@ -290,7 +290,7 @@ tst_qquickpositioners::tst_qquickpositioners() void tst_qquickpositioners::test_horizontal() { - QQuickView *window = createView(testFile("horizontal.qml")); + QScopedPointer window(createView(testFile("horizontal.qml"))); window->rootObject()->setProperty("testRightToLeft", false); @@ -313,13 +313,11 @@ void tst_qquickpositioners::test_horizontal() QQuickItem *row = window->rootObject()->findChild("row"); QCOMPARE(row->width(), 110.0); QCOMPARE(row->height(), 50.0); - - delete window; } void tst_qquickpositioners::test_horizontal_rtl() { - QQuickView *window = createView(testFile("horizontal.qml")); + QScopedPointer window(createView(testFile("horizontal.qml"))); window->rootObject()->setProperty("testRightToLeft", true); @@ -352,12 +350,11 @@ void tst_qquickpositioners::test_horizontal_rtl() QCOMPARE(three->x(), 90.0); QCOMPARE(three->y(), 0.0); - delete window; } void tst_qquickpositioners::test_horizontal_spacing() { - QQuickView *window = createView(testFile("horizontal-spacing.qml")); + QScopedPointer window(createView(testFile("horizontal-spacing.qml"))); window->rootObject()->setProperty("testRightToLeft", false); @@ -381,12 +378,11 @@ void tst_qquickpositioners::test_horizontal_spacing() QCOMPARE(row->width(), 130.0); QCOMPARE(row->height(), 50.0); - delete window; } void tst_qquickpositioners::test_horizontal_spacing_rightToLeft() { - QQuickView *window = createView(testFile("horizontal-spacing.qml")); + QScopedPointer window(createView(testFile("horizontal-spacing.qml"))); window->rootObject()->setProperty("testRightToLeft", true); @@ -410,12 +406,11 @@ void tst_qquickpositioners::test_horizontal_spacing_rightToLeft() QCOMPARE(row->width(), 130.0); QCOMPARE(row->height(), 50.0); - delete window; } void tst_qquickpositioners::test_horizontal_animated() { - QQuickView *window = createView(testFile("horizontal-animated.qml"), false); + QScopedPointer window(createView(testFile("horizontal-animated.qml"), false)); window->rootObject()->setProperty("testRightToLeft", false); @@ -433,7 +428,7 @@ void tst_qquickpositioners::test_horizontal_animated() QCOMPARE(two->x(), -100.0); QCOMPARE(three->x(), -100.0); - QVERIFY(QTest::qWaitForWindowExposed(window)); //It may not relayout until the next frame, so it needs to be drawn + QVERIFY(QTest::qWaitForWindowExposed(window.data())); //It may not relayout until the next frame, so it needs to be drawn QQuickItem *row = window->rootObject()->findChild("row"); QVERIFY(row); @@ -464,12 +459,11 @@ void tst_qquickpositioners::test_horizontal_animated() QTRY_COMPARE(two->x(), 50.0); QTRY_COMPARE(three->x(), 100.0); - delete window; } void tst_qquickpositioners::test_horizontal_animated_rightToLeft() { - QQuickView *window = createView(testFile("horizontal-animated.qml"), false); + QScopedPointer window(createView(testFile("horizontal-animated.qml"), false)); window->rootObject()->setProperty("testRightToLeft", true); @@ -487,7 +481,7 @@ void tst_qquickpositioners::test_horizontal_animated_rightToLeft() QCOMPARE(two->x(), -100.0); QCOMPARE(three->x(), -100.0); - QVERIFY(QTest::qWaitForWindowExposed(window)); //It may not relayout until the next frame, so it needs to be drawn + QVERIFY(QTest::qWaitForWindowExposed(window.data())); //It may not relayout until the next frame, so it needs to be drawn QQuickItem *row = window->rootObject()->findChild("row"); QVERIFY(row); @@ -520,12 +514,11 @@ void tst_qquickpositioners::test_horizontal_animated_rightToLeft() QTRY_COMPARE(one->x(), 100.0); QTRY_COMPARE(two->x(), 50.0); - delete window; } void tst_qquickpositioners::test_horizontal_animated_disabled() { - QQuickView *window = createView(testFile("horizontal-animated-disabled.qml")); + QScopedPointer window(createView(testFile("horizontal-animated-disabled.qml"))); QQuickRectangle *one = window->rootObject()->findChild("one"); QVERIFY(one != 0); @@ -558,7 +551,6 @@ void tst_qquickpositioners::test_horizontal_animated_disabled() QTRY_COMPARE(two->x(), 50.0); QTRY_COMPARE(three->x(), 100.0); - delete window; } void tst_qquickpositioners::populateTransitions(const QString &positionerObjectName) @@ -578,7 +570,8 @@ void tst_qquickpositioners::populateTransitions(const QString &positionerObjectN QaimModel model_targetItems_transitionFrom; QaimModel model_displacedItems_transitionVia; - QQuickView *window = QQuickViewTestUtil::createView(); + QScopedPointer window(QQuickViewTestUtil::createView()); + QQmlContext *ctxt = window->rootContext(); ctxt->setContextProperty("usePopulateTransition", usePopulateTransition); ctxt->setContextProperty("enableAddTransition", true); @@ -594,6 +587,7 @@ void tst_qquickpositioners::populateTransitions(const QString &positionerObjectN QQuickItem *positioner = window->rootObject()->findChild(positionerObjectName); QVERIFY(positioner); window->show(); + QTest::qWaitForWindowExposed(window.data()); qApp->processEvents(); if (!dynamicallyPopulate && usePopulateTransition) { @@ -629,8 +623,6 @@ void tst_qquickpositioners::populateTransitions(const QString &positionerObjectN model.insertItem(0, "new item", ""); QTRY_COMPARE(window->rootObject()->property("addTransitionsDone").toInt(), 1); QTRY_COMPARE(window->rootObject()->property("populateTransitionsDone").toInt(), 0); - - delete window; } void tst_qquickpositioners::populateTransitions_data() @@ -659,7 +651,7 @@ void tst_qquickpositioners::addTransitions(const QString &positionerObjectName) QaimModel model_targetItems_transitionFrom; QaimModel model_displacedItems_transitionVia; - QQuickView *window = QQuickViewTestUtil::createView(); + QScopedPointer window(QQuickViewTestUtil::createView()); QQmlContext *ctxt = window->rootContext(); ctxt->setContextProperty("usePopulateTransition", QVariant(false)); ctxt->setContextProperty("enableAddTransition", QVariant(true)); @@ -669,6 +661,7 @@ void tst_qquickpositioners::addTransitions(const QString &positionerObjectName) ctxt->setContextProperty("displacedItems_transitionVia", displacedItems_transitionVia); window->setSource(testFileUrl("transitions.qml")); window->show(); + QTest::qWaitForWindowExposed(window.data()); qApp->processEvents(); QQuickItem *positioner = window->rootObject()->findChild(positionerObjectName); @@ -735,8 +728,6 @@ void tst_qquickpositioners::addTransitions(const QString &positionerObjectName) } checkItemPositions(positioner, &model, window->rootObject()->property("incrementalSize").toInt()); - - delete window; } void tst_qquickpositioners::addTransitions_data() @@ -772,7 +763,7 @@ void tst_qquickpositioners::moveTransitions(const QString &positionerObjectName) QaimModel model_targetItems_transitionFrom; QaimModel model_displacedItems_transitionVia; - QQuickView *window = QQuickViewTestUtil::createView(); + QScopedPointer window(QQuickViewTestUtil::createView()); QQmlContext *ctxt = window->rootContext(); ctxt->setContextProperty("usePopulateTransition", QVariant(false)); ctxt->setContextProperty("enableAddTransition", QVariant(false)); @@ -782,6 +773,7 @@ void tst_qquickpositioners::moveTransitions(const QString &positionerObjectName) ctxt->setContextProperty("displacedItems_transitionVia", displacedItems_transitionVia); window->setSource(testFileUrl("transitions.qml")); window->show(); + QTest::qWaitForWindowExposed(window.data()); qApp->processEvents(); QList > expectedDisplacedValues = expectedDisplacedIndexes.getModelDataValues(model); @@ -844,8 +836,6 @@ void tst_qquickpositioners::moveTransitions(const QString &positionerObjectName) } checkItemPositions(positioner, &model, window->rootObject()->property("incrementalSize").toInt()); - - delete window; } void tst_qquickpositioners::moveTransitions_data() @@ -931,7 +921,7 @@ void tst_qquickpositioners::checkItemPositions(QQuickItem *positioner, QaimModel void tst_qquickpositioners::test_vertical() { - QQuickView *window = createView(testFile("vertical.qml")); + QScopedPointer window(createView(testFile("vertical.qml"))); QQuickRectangle *one = window->rootObject()->findChild("one"); QVERIFY(one != 0); @@ -954,12 +944,11 @@ void tst_qquickpositioners::test_vertical() QCOMPARE(column->height(), 80.0); QCOMPARE(column->width(), 50.0); - delete window; } void tst_qquickpositioners::test_vertical_spacing() { - QQuickView *window = createView(testFile("vertical-spacing.qml")); + QScopedPointer window(createView(testFile("vertical-spacing.qml"))); QQuickRectangle *one = window->rootObject()->findChild("one"); QVERIFY(one != 0); @@ -981,12 +970,11 @@ void tst_qquickpositioners::test_vertical_spacing() QCOMPARE(column->height(), 100.0); QCOMPARE(column->width(), 50.0); - delete window; } void tst_qquickpositioners::test_vertical_animated() { - QQuickView *window = createView(testFile("vertical-animated.qml"), false); + QScopedPointer window(createView(testFile("vertical-animated.qml"), false)); //Note that they animate in QQuickRectangle *one = window->rootObject()->findChild("one"); @@ -1001,7 +989,7 @@ void tst_qquickpositioners::test_vertical_animated() QVERIFY(three != 0); QCOMPARE(three->y(), -100.0); - QVERIFY(QTest::qWaitForWindowExposed(window)); //It may not relayout until the next frame, so it needs to be drawn + QVERIFY(QTest::qWaitForWindowExposed(window.data())); //It may not relayout until the next frame, so it needs to be drawn QQuickItem *column = window->rootObject()->findChild("column"); QVERIFY(column); @@ -1031,12 +1019,11 @@ void tst_qquickpositioners::test_vertical_animated() QTRY_COMPARE(two->y(), 50.0); QTRY_COMPARE(three->y(), 100.0); - delete window; } void tst_qquickpositioners::test_grid() { - QQuickView *window = createView(testFile("gridtest.qml")); + QScopedPointer window(createView(testFile("gridtest.qml"))); QQuickRectangle *one = window->rootObject()->findChild("one"); QVERIFY(one != 0); @@ -1065,12 +1052,11 @@ void tst_qquickpositioners::test_grid() QCOMPARE(grid->width(), 100.0); QCOMPARE(grid->height(), 100.0); - delete window; } void tst_qquickpositioners::test_grid_topToBottom() { - QQuickView *window = createView(testFile("grid-toptobottom.qml")); + QScopedPointer window(createView(testFile("grid-toptobottom.qml"))); QQuickRectangle *one = window->rootObject()->findChild("one"); QVERIFY(one != 0); @@ -1099,12 +1085,11 @@ void tst_qquickpositioners::test_grid_topToBottom() QCOMPARE(grid->width(), 100.0); QCOMPARE(grid->height(), 120.0); - delete window; } void tst_qquickpositioners::test_grid_rightToLeft() { - QQuickView *window = createView(testFile("gridtest.qml")); + QScopedPointer window(createView(testFile("gridtest.qml"))); window->rootObject()->setProperty("testRightToLeft", true); @@ -1148,12 +1133,11 @@ void tst_qquickpositioners::test_grid_rightToLeft() QCOMPARE(five->x(), 140.0); QCOMPARE(five->y(), 50.0); - delete window; } void tst_qquickpositioners::test_grid_spacing() { - QQuickView *window = createView(testFile("grid-spacing.qml")); + QScopedPointer window(createView(testFile("grid-spacing.qml"))); QQuickRectangle *one = window->rootObject()->findChild("one"); QVERIFY(one != 0); @@ -1181,12 +1165,11 @@ void tst_qquickpositioners::test_grid_spacing() QCOMPARE(grid->width(), 128.0); QCOMPARE(grid->height(), 104.0); - delete window; } void tst_qquickpositioners::test_grid_row_column_spacing() { - QQuickView *window = createView(testFile("grid-row-column-spacing.qml")); + QScopedPointer window(createView(testFile("grid-row-column-spacing.qml"))); QQuickRectangle *one = window->rootObject()->findChild("one"); QVERIFY(one != 0); @@ -1214,12 +1197,11 @@ void tst_qquickpositioners::test_grid_row_column_spacing() QCOMPARE(grid->width(), 142.0); QCOMPARE(grid->height(), 107.0); - delete window; } void tst_qquickpositioners::test_grid_animated() { - QQuickView *window = createView(testFile("grid-animated.qml"), false); + QScopedPointer window(createView(testFile("grid-animated.qml"), false)); window->rootObject()->setProperty("testRightToLeft", false); @@ -1249,7 +1231,7 @@ void tst_qquickpositioners::test_grid_animated() QCOMPARE(five->x(), -100.0); QCOMPARE(five->y(), -100.0); - QVERIFY(QTest::qWaitForWindowExposed(window)); //It may not relayout until the next frame, so it needs to be drawn + QVERIFY(QTest::qWaitForWindowExposed(window.data())); //It may not relayout until the next frame, so it needs to be drawn QQuickItem *grid = window->rootObject()->findChild("grid"); QVERIFY(grid); @@ -1299,12 +1281,11 @@ void tst_qquickpositioners::test_grid_animated() QTRY_COMPARE(five->x(), 50.0); QTRY_COMPARE(five->y(), 50.0); - delete window; } void tst_qquickpositioners::test_grid_animated_rightToLeft() { - QQuickView *window = createView(testFile("grid-animated.qml"), false); + QScopedPointer window(createView(testFile("grid-animated.qml"), false)); window->rootObject()->setProperty("testRightToLeft", true); @@ -1334,7 +1315,7 @@ void tst_qquickpositioners::test_grid_animated_rightToLeft() QCOMPARE(five->x(), -100.0); QCOMPARE(five->y(), -100.0); - QVERIFY(QTest::qWaitForWindowExposed(window)); //It may not relayout until the next frame, so it needs to be drawn + QVERIFY(QTest::qWaitForWindowExposed(window.data())); //It may not relayout until the next frame, so it needs to be drawn QQuickItem *grid = window->rootObject()->findChild("grid"); QVERIFY(grid); @@ -1384,12 +1365,11 @@ void tst_qquickpositioners::test_grid_animated_rightToLeft() QTRY_COMPARE(five->x(), 50.0); QTRY_COMPARE(five->y(), 50.0); - delete window; } void tst_qquickpositioners::test_grid_zero_columns() { - QQuickView *window = createView(testFile("gridzerocolumns.qml")); + QScopedPointer window(createView(testFile("gridzerocolumns.qml"))); QQuickRectangle *one = window->rootObject()->findChild("one"); QVERIFY(one != 0); @@ -1417,12 +1397,11 @@ void tst_qquickpositioners::test_grid_zero_columns() QCOMPARE(grid->width(), 170.0); QCOMPARE(grid->height(), 60.0); - delete window; } void tst_qquickpositioners::test_grid_H_alignment() { - QQuickView *window = createView(testFile("gridtest.qml")); + QScopedPointer window(createView(testFile("gridtest.qml"))); window->rootObject()->setProperty("testHAlignment", QQuickGrid::AlignHCenter); @@ -1497,12 +1476,11 @@ void tst_qquickpositioners::test_grid_H_alignment() QCOMPARE(grid->width(), 100.0); QCOMPARE(grid->height(), 100.0); - delete window; } void tst_qquickpositioners::test_grid_V_alignment() { - QQuickView *window = createView(testFile("gridtest.qml")); + QScopedPointer window(createView(testFile("gridtest.qml"))); window->rootObject()->setProperty("testVAlignment", QQuickGrid::AlignVCenter); @@ -1541,12 +1519,11 @@ void tst_qquickpositioners::test_grid_V_alignment() QCOMPARE(five->x(), 50.0); QCOMPARE(five->y(), 90.0); - delete window; } void tst_qquickpositioners::test_propertychanges() { - QQuickView *window = createView(testFile("propertychangestest.qml")); + QScopedPointer window(createView(testFile("propertychangestest.qml"))); QQuickGrid *grid = qobject_cast(window->rootObject()); QVERIFY(grid != 0); @@ -1600,12 +1577,11 @@ void tst_qquickpositioners::test_propertychanges() QCOMPARE(columnsSpy.count(),2); QCOMPARE(rowsSpy.count(),2); - delete window; } void tst_qquickpositioners::test_repeater() { - QQuickView *window = createView(testFile("repeatertest.qml")); + QScopedPointer window(createView(testFile("repeatertest.qml"))); QQuickRectangle *one = findItem(window->contentItem(), "one"); QVERIFY(one != 0); @@ -1623,12 +1599,11 @@ void tst_qquickpositioners::test_repeater() QCOMPARE(three->x(), 100.0); QCOMPARE(three->y(), 0.0); - delete window; } void tst_qquickpositioners::test_flow() { - QQuickView *window = createView(testFile("flowtest.qml")); + QScopedPointer window(createView(testFile("flowtest.qml"))); window->rootObject()->setProperty("testRightToLeft", false); @@ -1659,12 +1634,11 @@ void tst_qquickpositioners::test_flow() QCOMPARE(flow->width(), 90.0); QCOMPARE(flow->height(), 120.0); - delete window; } void tst_qquickpositioners::test_flow_rightToLeft() { - QQuickView *window = createView(testFile("flowtest.qml")); + QScopedPointer window(createView(testFile("flowtest.qml"))); window->rootObject()->setProperty("testRightToLeft", true); @@ -1695,12 +1669,11 @@ void tst_qquickpositioners::test_flow_rightToLeft() QCOMPARE(flow->width(), 90.0); QCOMPARE(flow->height(), 120.0); - delete window; } void tst_qquickpositioners::test_flow_topToBottom() { - QQuickView *window = createView(testFile("flowtest-toptobottom.qml")); + QScopedPointer window(createView(testFile("flowtest-toptobottom.qml"))); window->rootObject()->setProperty("testRightToLeft", false); @@ -1748,12 +1721,11 @@ void tst_qquickpositioners::test_flow_topToBottom() QCOMPARE(five->x(), 40.0); QCOMPARE(five->y(), 50.0); - delete window; } void tst_qquickpositioners::test_flow_resize() { - QQuickView *window = createView(testFile("flowtest.qml")); + QScopedPointer window(createView(testFile("flowtest.qml"))); QQuickItem *root = qobject_cast(window->rootObject()); QVERIFY(root); @@ -1782,12 +1754,11 @@ void tst_qquickpositioners::test_flow_resize() QTRY_COMPARE(five->x(), 50.0); QTRY_COMPARE(five->y(), 50.0); - delete window; } void tst_qquickpositioners::test_flow_resize_rightToLeft() { - QQuickView *window = createView(testFile("flowtest.qml")); + QScopedPointer window(createView(testFile("flowtest.qml"))); QQuickItem *root = qobject_cast(window->rootObject()); QVERIFY(root); @@ -1816,12 +1787,11 @@ void tst_qquickpositioners::test_flow_resize_rightToLeft() QCOMPARE(five->x(), 65.0); QCOMPARE(five->y(), 50.0); - delete window; } void tst_qquickpositioners::test_flow_implicit_resize() { - QQuickView *window = createView(testFile("flow-testimplicitsize.qml")); + QScopedPointer window(createView(testFile("flow-testimplicitsize.qml"))); QVERIFY(window->rootObject() != 0); QQuickFlow *flow = window->rootObject()->findChild("flow"); @@ -1845,7 +1815,6 @@ void tst_qquickpositioners::test_flow_implicit_resize() QCOMPARE(flow->width(), 220.0); QCOMPARE(flow->height(), 50.0); - delete window; } void tst_qquickpositioners::test_conflictinganchors() @@ -1962,10 +1931,10 @@ void tst_qquickpositioners::test_mirroring() objectNames << "one" << "two" << "three" << "four" << "five"; foreach (const QString qmlFile, qmlFiles) { - QQuickView *windowA = createView(testFile(qmlFile)); + QScopedPointer windowA(createView(testFile(qmlFile))); QQuickItem *rootA = qobject_cast(windowA->rootObject()); - QQuickView *windowB = createView(testFile(qmlFile)); + QScopedPointer windowB(createView(testFile(qmlFile))); QQuickItem *rootB = qobject_cast(windowB->rootObject()); rootA->setProperty("testRightToLeft", true); // layoutDirection: Qt.RightToLeft @@ -2007,15 +1976,13 @@ void tst_qquickpositioners::test_mirroring() QQuickItem *itemB = rootB->findChild(objectName); QTRY_COMPARE(itemA->x(), itemB->x()); } - delete windowA; - delete windowB; } } void tst_qquickpositioners::test_allInvisible() { //QTBUG-19361 - QQuickView *window = createView(testFile("allInvisible.qml")); + QScopedPointer window(createView(testFile("allInvisible.qml"))); QQuickItem *root = qobject_cast(window->rootObject()); QVERIFY(root); @@ -2034,7 +2001,7 @@ void tst_qquickpositioners::test_attachedproperties() { QFETCH(QString, filename); - QQuickView *window = createView(filename); + QScopedPointer window(createView(filename)); QVERIFY(window->rootObject() != 0); QQuickRectangle *greenRect = window->rootObject()->findChild("greenRect"); @@ -2066,7 +2033,6 @@ void tst_qquickpositioners::test_attachedproperties() isLast = yellowRect->property("isLastItem").toBool(); QVERIFY(isLast == true); - delete window; } void tst_qquickpositioners::test_attachedproperties_data() @@ -2081,7 +2047,7 @@ void tst_qquickpositioners::test_attachedproperties_data() void tst_qquickpositioners::test_attachedproperties_dynamic() { - QQuickView *window = createView(testFile("attachedproperties-dynamic.qml")); + QScopedPointer window(createView(testFile("attachedproperties-dynamic.qml"))); QVERIFY(window->rootObject() != 0); QQuickRow *row = window->rootObject()->findChild("pos"); @@ -2132,7 +2098,6 @@ void tst_qquickpositioners::test_attachedproperties_dynamic() QTRY_VERIFY(rect1->property("firstItem").toBool() == false); QTRY_VERIFY(rect1->property("lastItem").toBool() == true); - delete window; } QQuickView *tst_qquickpositioners::createView(const QString &filename, bool wait) -- cgit v1.2.3 From 4f8537ff8c9427705e3587861a62fe81cf3e503b Mon Sep 17 00:00:00 2001 From: Gunnar Sletta Date: Tue, 5 Feb 2013 12:11:27 +0100 Subject: Make sure deleteLater gets run at the "correct" time. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We have a number of places where we delete scene graph objects with deleteLater() and change the scene graph. The timing of when the deleteLater happens is very important as we need to complete the updates to the scene graph first. In this particular case, a QQuickShaderEffectSource was released, and an update was scheduled but because animations were running we were already beginning to render a new frame before the update was handled, causing a crash. The only safe place to run deferred deletes is after we have performed a proper sync with the item scene. Task-number: QTBUG-29485 Change-Id: I6e93d4e6276fe82d3f4c818781b188e03c46e510 Reviewed-by: Samuel Rødal --- src/quick/scenegraph/qsgthreadedrenderloop.cpp | 132 ++++++++++++++++++++++--- 1 file changed, 116 insertions(+), 16 deletions(-) diff --git a/src/quick/scenegraph/qsgthreadedrenderloop.cpp b/src/quick/scenegraph/qsgthreadedrenderloop.cpp index 4e8050dd62..b11da22268 100644 --- a/src/quick/scenegraph/qsgthreadedrenderloop.cpp +++ b/src/quick/scenegraph/qsgthreadedrenderloop.cpp @@ -43,6 +43,7 @@ #include #include #include +#include #include #include @@ -184,6 +185,12 @@ const QEvent::Type WM_Grab = QEvent::Type(QEvent::User + 9); // Passed by the RT to the RL to trigger animations to be advanced. const QEvent::Type WM_AdvanceAnimations = QEvent::Type(QEvent::User + 10); +// Passed by the RT to the RL when animations start +const QEvent::Type WM_AnimationsStarted = QEvent::Type(QEvent::User + 11); + +// Passed by the RT to the RL when animations stop +const QEvent::Type WM_AnimationsStopped = QEvent::Type(QEvent::User + 12); + template T *windowFor(const QList list, QQuickWindow *window) { for (int i=0; i +{ +public: + QSGRenderThreadEventQueue() + : waiting(false) + { + } + + void addEvent(QEvent *e) { + mutex.lock(); + enqueue(e); + if (waiting) + condition.wakeOne(); + mutex.unlock(); + } + + QEvent *takeEvent(bool wait) { + mutex.lock(); + if (size() == 0 && wait) { + waiting = true; + condition.wait(&mutex); + waiting = false; + } + QEvent *e = dequeue(); + mutex.unlock(); + return e; + } + + bool hasMoreEvents() { + mutex.lock(); + bool has = !isEmpty(); + mutex.unlock(); + return has; + } + +private: + QMutex mutex; + QWaitCondition condition; + bool waiting; +}; + + class QSGRenderThread : public QThread { Q_OBJECT @@ -253,6 +302,7 @@ public: , shouldExit(false) , allowMainThreadProcessing(true) , animationRequestsPending(0) + , stopEventProcessing(false) { sg->moveToThread(this); } @@ -270,17 +320,21 @@ public: void requestRepaint() { if (sleeping) - exit(); + stopEventProcessing = true; if (m_windows.size() > 0) pendingUpdate |= RepaintRequest; } + void processEventsAndWaitForMore(); + void processEvents(); + void postEvent(QEvent *e); + public slots: void animationStarted() { RLDEBUG(" Render: animationStarted()"); animationRunning = true; if (sleeping) - exit(); + stopEventProcessing = true; } void animationStopped() { @@ -320,6 +374,10 @@ public: QSize size; }; QList m_windows; + + // Local event queue stuff... + bool stopEventProcessing; + QSGRenderThreadEventQueue eventQueue; }; bool QSGRenderThread::event(QEvent *e) @@ -353,14 +411,14 @@ bool QSGRenderThread::event(QEvent *e) } if (sleeping && m_windows.size()) - exit(); + stopEventProcessing = true; return true; } case WM_RequestSync: RLDEBUG(" Render: WM_RequestSync"); if (sleeping) - exit(); + stopEventProcessing = true; if (m_windows.size() > 0) pendingUpdate |= SyncRequest; return true; @@ -382,7 +440,7 @@ bool QSGRenderThread::event(QEvent *e) invalidateOpenGL(wme->window, wme->inDestructor); shouldExit = !gl; if (sleeping) - exit(); + stopEventProcessing = true; } else { RLDEBUG1(" Render: - not releasing anything because we have active windows..."); } @@ -414,6 +472,14 @@ bool QSGRenderThread::event(QEvent *e) return true; } + case WM_AnimationsStarted: + animationStarted(); + break; + + case WM_AnimationsStopped: + animationStopped(); + break; + default: break; } @@ -531,6 +597,8 @@ void QSGRenderThread::sync() waitCondition.wakeOne(); mutex.unlock(); + + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); } @@ -559,6 +627,7 @@ void QSGRenderThread::syncAndRender() if (qquick_window_timing) syncTime = threadTimer.elapsed(); #endif + RLDEBUG(" Render: - rendering starting"); for (int i=0; i(m_windows.at(i)); @@ -588,6 +657,38 @@ void QSGRenderThread::syncAndRender() #endif } + + +void QSGRenderThread::postEvent(QEvent *e) +{ + eventQueue.addEvent(e); +} + + + +void QSGRenderThread::processEvents() +{ + RLDEBUG1(" Render: processEvents()"); + while (eventQueue.hasMoreEvents()) { + QEvent *e = eventQueue.takeEvent(false); + event(e); + delete e; + } + RLDEBUG1(" Render: - done with processEvents()"); +} + +void QSGRenderThread::processEventsAndWaitForMore() +{ + RLDEBUG1(" Render: processEventsAndWaitForMore()"); + stopEventProcessing = false; + while (!stopEventProcessing) { + QEvent *e = eventQueue.takeEvent(true); + event(e); + delete e; + } + RLDEBUG1(" Render: - done with processEventsAndWaitForMore()"); +} + void QSGRenderThread::run() { RLDEBUG1(" Render: run()"); @@ -601,14 +702,14 @@ void QSGRenderThread::run() syncAndRender(); } + processEvents(); QCoreApplication::processEvents(); - QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); if (!shouldExit && ((!animationRunning && pendingUpdate == 0) || m_windows.size() == 0)) { RLDEBUG(" Render: enter event loop (going to sleep)"); sleeping = true; - exec(); + processEventsAndWaitForMore(); sleeping = false; } @@ -630,8 +731,6 @@ QSGThreadedRenderLoop::QSGThreadedRenderLoop() m_exhaust_delay = get_env_int("QML_EXHAUST_DELAY", 5); - connect(m_animation_driver, SIGNAL(started()), m_thread, SLOT(animationStarted())); - connect(m_animation_driver, SIGNAL(stopped()), m_thread, SLOT(animationStopped())); connect(m_animation_driver, SIGNAL(started()), this, SLOT(animationStarted())); connect(m_animation_driver, SIGNAL(stopped()), this, SLOT(animationStopped())); @@ -664,6 +763,7 @@ void QSGThreadedRenderLoop::animationStarted() RLDEBUG("GUI: animationStarted()"); if (!anyoneShowing() && m_animation_timer == 0) m_animation_timer = startTimer(qsgrl_animation_interval()); + m_thread->postEvent(new QEvent(WM_AnimationsStarted)); } void QSGThreadedRenderLoop::animationStopped() @@ -673,6 +773,7 @@ void QSGThreadedRenderLoop::animationStopped() killTimer(m_animation_timer); m_animation_timer = 0; } + m_thread->postEvent(new QEvent(WM_AnimationsStopped)); } @@ -763,7 +864,7 @@ void QSGThreadedRenderLoop::handleExposure(QQuickWindow *window) if (!window->handle()) window->create(); - QCoreApplication::postEvent(m_thread, new WMExposeEvent(window)); + m_thread->postEvent(new WMExposeEvent(window)); // Start render thread if it is not running if (!m_thread->isRunning()) { @@ -798,7 +899,7 @@ void QSGThreadedRenderLoop::handleObscurity(QQuickWindow *window) { RLDEBUG1("GUI: handleObscurity"); if (m_thread->isRunning()) - QCoreApplication::postEvent(m_thread, new WMWindowEvent(window, WM_Obscure)); + m_thread->postEvent(new WMWindowEvent(window, WM_Obscure)); if (!anyoneShowing() && m_animation_driver->isRunning() && m_animation_timer == 0) { m_animation_timer = startTimer(qsgrl_animation_interval()); @@ -871,7 +972,7 @@ void QSGThreadedRenderLoop::releaseResources(QQuickWindow *window, bool inDestru m_thread->mutex.lock(); if (m_thread->isRunning() && !m_thread->shouldExit) { RLDEBUG1("GUI: - posting release request to render thread"); - QCoreApplication::postEvent(m_thread, new WMTryReleaseEvent(window, inDestructor)); + m_thread->postEvent(new WMTryReleaseEvent(window, inDestructor)); m_thread->waitCondition.wait(&m_thread->mutex); } m_thread->mutex.unlock(); @@ -913,7 +1014,7 @@ void QSGThreadedRenderLoop::polishAndSync() m_thread->guiIsLocked = true; QEvent *event = new QEvent(WM_RequestSync); - QCoreApplication::postEvent(m_thread, event); + m_thread->postEvent(event); RLDEBUG("GUI: - wait for sync..."); #ifndef QSG_NO_WINDOW_TIMING if (qquick_window_timing) @@ -1005,7 +1106,7 @@ QImage QSGThreadedRenderLoop::grab(QQuickWindow *window) QImage result; m_thread->mutex.lock(); RLDEBUG1("GUI: - locking, posting grab event"); - QCoreApplication::postEvent(m_thread, new WMGrabEvent(window, &result)); + m_thread->postEvent(new WMGrabEvent(window, &result)); m_thread->waitCondition.wait(&m_thread->mutex); RLDEBUG1("GUI: - locking, grab done, unlocking"); m_thread->mutex.unlock(); @@ -1032,8 +1133,7 @@ void QSGThreadedRenderLoop::resize(QQuickWindow *w, const QSize &size) return; RLDEBUG("GUI: - posting resize event..."); - WMResizeEvent *e = new WMResizeEvent(w, size); - QCoreApplication::postEvent(m_thread, e); + m_thread->postEvent(new WMResizeEvent(w, size)); polishAndSync(); } -- cgit v1.2.3 From edb924b93c55dc5576849b2e93ba55fc7ee0df63 Mon Sep 17 00:00:00 2001 From: Gunnar Sletta Date: Mon, 11 Feb 2013 11:14:04 +0100 Subject: Do pixel conversion on the pixmap decoder thread when possible MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Id991bee5c0a8b260fc1ca4395402970c6a722b5d Reviewed-by: Samuel Rødal --- src/quick/util/qquickpixmapcache.cpp | 11 +++++++++++ src/quick/util/qquickpixmapcache_p.h | 6 +----- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/quick/util/qquickpixmapcache.cpp b/src/quick/util/qquickpixmapcache.cpp index dda2fbe2b0..7a9bea3aa0 100644 --- a/src/quick/util/qquickpixmapcache.cpp +++ b/src/quick/util/qquickpixmapcache.cpp @@ -91,6 +91,17 @@ static inline QString imageId(const QUrl &url) return url.toString(QUrl::RemoveScheme | QUrl::RemoveAuthority).mid(1); } +QQuickDefaultTextureFactory::QQuickDefaultTextureFactory(const QImage &image) +{ + if (image.format() == QImage::Format_ARGB32_Premultiplied + || image.format() == QImage::Format_RGB32) { + im = image; + } else { + im = image.convertToFormat(QImage::Format_ARGB32_Premultiplied); + } +} + + QSGTexture *QQuickDefaultTextureFactory::createTexture(QQuickWindow *) const { QSGPlainTexture *t = new QSGPlainTexture(); diff --git a/src/quick/util/qquickpixmapcache_p.h b/src/quick/util/qquickpixmapcache_p.h index 3fefa35c0b..08dba8044f 100644 --- a/src/quick/util/qquickpixmapcache_p.h +++ b/src/quick/util/qquickpixmapcache_p.h @@ -61,11 +61,7 @@ class QQuickDefaultTextureFactory : public QQuickTextureFactory { Q_OBJECT public: - QQuickDefaultTextureFactory(const QImage &i) - : im(i) - { - } - + QQuickDefaultTextureFactory(const QImage &i); QSGTexture *createTexture(QQuickWindow *window) const; QSize textureSize() const { return im.size(); } int textureByteCount() const { return im.byteCount(); } -- cgit v1.2.3 From 536c9481170c7cd84e5e78edf99ebc943d439b1a Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Tue, 5 Feb 2013 18:11:34 +0100 Subject: QQuickItem::childAt(x, y) takes child transformations into account Task-number: QTBUG-28706 Change-Id: Ib1738eac1319361398b1898db341d6e4be0fe5fd Reviewed-by: Shawn Rutledge --- src/quick/items/qquickitem.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index e87f870eb8..26af6953e1 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -3808,14 +3808,15 @@ void QQuickItem::forceActiveFocus() */ QQuickItem *QQuickItem::childAt(qreal x, qreal y) const { - // XXX todo - should this include transform etc.? const QList children = childItems(); for (int i = children.count()-1; i >= 0; --i) { QQuickItem *child = children.at(i); - if (child->isVisible() && child->x() <= x - && child->x() + child->width() >= x - && child->y() <= y - && child->y() + child->height() >= y) + // Map coordinates to the child element's coordinate space + QPointF point = mapToItem(child, QPointF(x, y)); + if (child->isVisible() && point.x() >= 0 + && child->width() >= point.x() + && point.y() >= 0 + && child->height() >= point.y()) return child; } return 0; -- cgit v1.2.3 From d3fa421bd0958fbcea63941a48227fb3664d4d52 Mon Sep 17 00:00:00 2001 From: Maurice Kalinowski Date: Wed, 13 Feb 2013 10:02:54 +0100 Subject: only instantiate variable when required variable is only used for debugging purposes, where debugging part is inside preprocessor check. Creation of the object should be there as well. Change-Id: I6c9e842ad05d8e45f6d67f5e5dedebc896cda157 Reviewed-by: Gunnar Sletta --- src/quick/scenegraph/coreapi/qsgdefaultrenderer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/quick/scenegraph/coreapi/qsgdefaultrenderer.cpp b/src/quick/scenegraph/coreapi/qsgdefaultrenderer.cpp index 1642f7f0b2..448ec55a82 100644 --- a/src/quick/scenegraph/coreapi/qsgdefaultrenderer.cpp +++ b/src/quick/scenegraph/coreapi/qsgdefaultrenderer.cpp @@ -135,8 +135,8 @@ QSGDefaultRenderer::QSGDefaultRenderer(QSGContext *context) , m_render_node_added(false) , m_currentRenderOrder(1) { - QStringList args = qApp->arguments(); #if defined(QML_RUNTIME_TESTING) + QStringList args = qApp->arguments(); m_render_opaque_nodes = !args.contains(QLatin1String("--no-opaque-nodes")); m_render_alpha_nodes = !args.contains(QLatin1String("--no-alpha-nodes")); #endif -- cgit v1.2.3 From 5e6b2bf68b2338ad9424b48193b860a70addd022 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Mon, 14 Jan 2013 18:12:50 +0100 Subject: Add QQmlPlatform (exposed as Qt.platform) Change-Id: If34603fd8fbd907bc063b52ea31848eabdb49f61 Reviewed-by: Gabriel de Dietrich Reviewed-by: Alan Alpert --- src/qml/qml/qml.pri | 6 ++-- src/qml/qml/qqmlengine.cpp | 31 +++++++++++++++++ src/qml/qml/qqmlplatform.cpp | 83 ++++++++++++++++++++++++++++++++++++++++++++ src/qml/qml/qqmlplatform_p.h | 74 +++++++++++++++++++++++++++++++++++++++ src/qml/qml/v8/qv8engine.cpp | 13 +++++++ src/qml/qml/v8/qv8engine_p.h | 2 ++ 6 files changed, 207 insertions(+), 2 deletions(-) create mode 100644 src/qml/qml/qqmlplatform.cpp create mode 100644 src/qml/qml/qqmlplatform_p.h diff --git a/src/qml/qml/qml.pri b/src/qml/qml/qml.pri index 87fe47750f..53094db59d 100644 --- a/src/qml/qml/qml.pri +++ b/src/qml/qml/qml.pri @@ -55,7 +55,8 @@ SOURCES += \ $$PWD/qqmlmemoryprofiler.cpp \ $$PWD/qqmlconnections.cpp \ $$PWD/qqmltimer.cpp \ - $$PWD/qqmlbind.cpp + $$PWD/qqmlbind.cpp \ + $$PWD/qqmlplatform.cpp HEADERS += \ $$PWD/qqmlglobal_p.h \ @@ -131,7 +132,8 @@ HEADERS += \ $$PWD/qqmlmemoryprofiler_p.h \ $$PWD/qqmlconnections_p.h \ $$PWD/qqmltimer_p.h \ - $$PWD/qqmlbind_p.h + $$PWD/qqmlbind_p.h \ + $$PWD/qqmlplatform_p.h include(parser/parser.pri) diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index 5657cacf7d..074ea51ce3 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -92,6 +92,7 @@ #include "qqmlbind_p.h" #include "qqmlconnections_p.h" #include "qqmltimer_p.h" +#include "qqmlplatform_p.h" #include #include #include @@ -366,6 +367,36 @@ The following functions are also on the Qt object. \endlist */ +/*! + \qmlproperty object Qt::platform + \since QtQml 2.1 + + The \c platform object provides info about the underlying platform. + + Its properties are: + + \table + \row + \li \c platform.os + \li + + This read-only property contains the name of the operating system. + + Possible values are: + + \list + \li \c "android" - Android + \li \c "blackberry" - BlackBerry OS + \li \c "ios" - Apple iOS + \li \c "linux" - Linux + \li \c "mac" - Mac OS X + \li \c "unix" - Other Unix-based OS + \li \c "windows" - Windows + \li \c "wince" - Windows CE + \endlist + \endtable +*/ + /*! \qmlproperty object Qt::application \since QtQuick 1.1 diff --git a/src/qml/qml/qqmlplatform.cpp b/src/qml/qml/qqmlplatform.cpp new file mode 100644 index 0000000000..21145be89e --- /dev/null +++ b/src/qml/qml/qqmlplatform.cpp @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlplatform_p.h" + +QT_BEGIN_NAMESPACE + +/* + This object and its properties are documented as part of the Qt object, + in qqmlengine.cpp +*/ + +QQmlPlatform::QQmlPlatform(QObject *parent) + : QObject(parent) +{ +} + +QQmlPlatform::~QQmlPlatform() +{ +} + +QString QQmlPlatform::os() +{ +#if defined(Q_OS_LINUX_ANDROID) + return QLatin1String("android"); +#elif defined(Q_OS_BLACKBERRY) + return QLatin1String("blackberry"); +#elif defined(Q_OS_IOS) + return QLatin1String("ios"); +#elif defined(Q_OS_MAC) + return QLatin1String("mac"); +#elif defined(Q_OS_WINCE) + return QLatin1String("wince"); +#elif defined(Q_OS_WIN) + return QLatin1String("windows"); +#elif defined(Q_OS_LINUX) + return QLatin1String("linux"); +#elif defined(Q_OS_UNIX) + return QLatin1String("unix"); +#else + return QLatin1String("unknown"); +#endif +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlplatform_p.h b/src/qml/qml/qqmlplatform_p.h new file mode 100644 index 0000000000..a70e21ffbc --- /dev/null +++ b/src/qml/qml/qqmlplatform_p.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLPLATFORM_P_H +#define QQMLPLATFORM_P_H + +#include +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class Q_QML_PRIVATE_EXPORT QQmlPlatform : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString os READ os CONSTANT) + +public: + explicit QQmlPlatform(QObject *parent = 0); + virtual ~QQmlPlatform(); + + static QString os(); + +private: + Q_DISABLE_COPY(QQmlPlatform) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQmlPlatform) + +QT_END_HEADER + +#endif // QQMLPLATFORM_P_H diff --git a/src/qml/qml/v8/qv8engine.cpp b/src/qml/qml/v8/qv8engine.cpp index 2619c1a484..15611f3428 100644 --- a/src/qml/qml/v8/qv8engine.cpp +++ b/src/qml/qml/v8/qv8engine.cpp @@ -54,6 +54,7 @@ #include #include #include +#include #include "qscript_impl_p.h" #include "qv8domerrors_p.h" @@ -125,6 +126,7 @@ QV8Engine::QV8Engine(QJSEngine* qq, ContextOwnership ownership) , m_ownsV8Context(ownership == CreateNewContext) , m_xmlHttpRequestData(0) , m_listModelData(0) + , m_platform(0) , m_application(0) { QML_MEMORY_SCOPE_STRING("QV8Engine::QV8Engine"); @@ -629,6 +631,7 @@ void QV8Engine::initializeGlobal(v8::Handle global) qt->Set(v8::String::New("binding"), V8FUNCTION(binding, this)); if (m_engine) { + qt->SetAccessor(v8::String::New("platform"), getPlatform, 0, v8::External::New(this)); qt->SetAccessor(v8::String::New("application"), getApplication, 0, v8::External::New(this)); #ifndef QT_NO_IM qt->SetAccessor(v8::String::New("inputMethod"), getInputMethod, 0, v8::External::New(this)); @@ -1446,6 +1449,16 @@ int QV8Engine::consoleCountHelper(const QString &file, quint16 line, quint16 col return number; } +v8::Handle QV8Engine::getPlatform(v8::Local, const v8::AccessorInfo &info) +{ + QV8Engine *engine = reinterpret_cast(v8::External::Unwrap(info.Data())); + if (!engine->m_platform) { + // Only allocate a platform object once + engine->m_platform = new QQmlPlatform(engine->m_engine); + } + return engine->newQObject(engine->m_platform); +} + v8::Handle QV8Engine::getApplication(v8::Local, const v8::AccessorInfo &info) { QV8Engine *engine = reinterpret_cast(v8::External::Unwrap(info.Data())); diff --git a/src/qml/qml/v8/qv8engine_p.h b/src/qml/qml/v8/qv8engine_p.h index 43c978c757..9c3eb22e1c 100644 --- a/src/qml/qml/v8/qv8engine_p.h +++ b/src/qml/qml/v8/qv8engine_p.h @@ -427,6 +427,7 @@ public: void addRelationshipForGC(QObject *object, v8::Persistent handle); void addRelationshipForGC(QObject *object, QObject *other); + static v8::Handle getPlatform(v8::Local property, const v8::AccessorInfo &info); static v8::Handle getApplication(v8::Local property, const v8::AccessorInfo &info); #ifndef QT_NO_IM static v8::Handle getInputMethod(v8::Local property, const v8::AccessorInfo &info); @@ -480,6 +481,7 @@ protected: QHash m_consoleCount; + QObject *m_platform; QObject *m_application; QVariant toBasicVariant(v8::Handle); -- cgit v1.2.3 From 42f230fb9bc04d32774f05e9358604720662490a Mon Sep 17 00:00:00 2001 From: Michael Brasser Date: Thu, 14 Feb 2013 21:18:59 -0600 Subject: Ensure EtcTexture always provides a valid textureId. Change-Id: I1e7e8095593838f0fc1d78d1cb5a146787f748a7 Reviewed-by: Gunnar Sletta --- examples/quick/textureprovider/etcprovider.cpp | 15 ++++++++++++--- examples/quick/textureprovider/etcprovider.h | 3 ++- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/examples/quick/textureprovider/etcprovider.cpp b/examples/quick/textureprovider/etcprovider.cpp index 0bb072ffaf..507965ca9a 100644 --- a/examples/quick/textureprovider/etcprovider.cpp +++ b/examples/quick/textureprovider/etcprovider.cpp @@ -86,7 +86,7 @@ unsigned short getPaddedHeight(ETCHeader *pHeader) } EtcTexture::EtcTexture() - : m_texture_id(0) + : m_texture_id(0), m_uploaded(false) { } @@ -97,14 +97,22 @@ EtcTexture::~EtcTexture() glDeleteTextures(1, &m_texture_id); } +int EtcTexture::textureId() const +{ + if (m_texture_id == 0) + glGenTextures(1, &const_cast(this)->m_texture_id); + return m_texture_id; +} + void EtcTexture::bind() { - if (m_texture_id) { + if (m_uploaded && m_texture_id) { glBindTexture(GL_TEXTURE_2D, m_texture_id); return; } - glGenTextures(1, &m_texture_id); + if (m_texture_id == 0) + glGenTextures(1, &m_texture_id); glBindTexture(GL_TEXTURE_2D, m_texture_id); #ifdef ETC_DEBUG @@ -129,6 +137,7 @@ void EtcTexture::bind() return; } + m_uploaded = true; updateBindOptions(true); } diff --git a/examples/quick/textureprovider/etcprovider.h b/examples/quick/textureprovider/etcprovider.h index 08418f8b4b..24570e90e9 100644 --- a/examples/quick/textureprovider/etcprovider.h +++ b/examples/quick/textureprovider/etcprovider.h @@ -67,7 +67,7 @@ public: void bind(); QSize textureSize() const { return m_size; } - int textureId() const { return m_texture_id; } + int textureId() const; bool hasAlphaChannel() const { return false; } bool hasMipmaps() const { return false; } @@ -76,6 +76,7 @@ public: QSize m_size; QSize m_paddedSize; GLuint m_texture_id; + bool m_uploaded; }; #endif // ETCPROVIDER_H -- cgit v1.2.3 From af8c15951886ffb10e1ea8a0631e3ea7e29da483 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Wed, 13 Feb 2013 14:08:19 +0100 Subject: FolderListModel: prevent assertion failure when filter selects 0 files If you do nameFilters = "*.foo" and there are no matching files, there was an assertion failure due to inserting rows from 0 to -1: ASSERT: "last >= first" in file itemmodels/qabstractitemmodel.cpp Change-Id: I4a3642906a4588bebc699b8d99438726676fb4ca Reviewed-by: Shawn Rutledge --- src/imports/folderlistmodel/qquickfolderlistmodel.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/imports/folderlistmodel/qquickfolderlistmodel.cpp b/src/imports/folderlistmodel/qquickfolderlistmodel.cpp index 3e3a824648..421e215066 100644 --- a/src/imports/folderlistmodel/qquickfolderlistmodel.cpp +++ b/src/imports/folderlistmodel/qquickfolderlistmodel.cpp @@ -159,8 +159,10 @@ void QQuickFolderListModelPrivate::_q_directoryUpdated(const QString &directory, data = list; q->beginRemoveRows(parent, fromIndex, toIndex); q->endRemoveRows(); - q->beginInsertRows(parent, fromIndex, list.size()-1); - q->endInsertRows(); + if (list.size() > 0) { + q->beginInsertRows(parent, fromIndex, list.size()-1); + q->endInsertRows(); + } emit q->rowCountChanged(); } else if (data.size() < list.size()) { //qDebug() << "File added. FromIndex: " << fromIndex << " toIndex: " << toIndex << " list size: " << list.size(); -- cgit v1.2.3 From fa6499d3607f87d79ed58307cef4af49313140a6 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Fri, 15 Feb 2013 14:44:50 +0100 Subject: QQuickPlatform: remove QT_{BEGIN,END}_HEADER macro usage Change-Id: I219517d740fa7385e923a9e09cb7e241378f8576 Reviewed-by: Gabriel de Dietrich --- src/qml/qml/qqmlplatform_p.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/qml/qml/qqmlplatform_p.h b/src/qml/qml/qqmlplatform_p.h index a70e21ffbc..7a212d5bdf 100644 --- a/src/qml/qml/qqmlplatform_p.h +++ b/src/qml/qml/qqmlplatform_p.h @@ -46,8 +46,6 @@ #include #include -QT_BEGIN_HEADER - QT_BEGIN_NAMESPACE class Q_QML_PRIVATE_EXPORT QQmlPlatform : public QObject @@ -69,6 +67,4 @@ QT_END_NAMESPACE QML_DECLARE_TYPE(QQmlPlatform) -QT_END_HEADER - #endif // QQMLPLATFORM_P_H -- cgit v1.2.3 From 58471eb3f267dbee728b1c13f87458f2ee509bfa Mon Sep 17 00:00:00 2001 From: Gunnar Sletta Date: Mon, 18 Feb 2013 09:45:24 +0100 Subject: GUI render loop did not render on expose, nor clean up properly. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I6f9ab43ad6d149295487d9f69ceb0131cd142776 Reviewed-by: Samuel Rødal --- src/quick/scenegraph/qsgrenderloop.cpp | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/quick/scenegraph/qsgrenderloop.cpp b/src/quick/scenegraph/qsgrenderloop.cpp index 9117b2cffa..83e8d3f17a 100644 --- a/src/quick/scenegraph/qsgrenderloop.cpp +++ b/src/quick/scenegraph/qsgrenderloop.cpp @@ -194,15 +194,24 @@ void QSGGuiThreadRenderLoop::hide(QQuickWindow *window) cd->cleanupNodesOnShutdown(); if (m_windows.size() == 0) { - sg->invalidate(); - delete gl; - gl = 0; + if (!cd->persistentSceneGraph) { + sg->invalidate(); + if (!cd->persistentGLContext) { + delete gl; + gl = 0; + } + } } } void QSGGuiThreadRenderLoop::windowDestroyed(QQuickWindow *window) { hide(window); + if (m_windows.size() == 0) { + sg->invalidate(); + delete gl; + gl = 0; + } } void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window) @@ -297,7 +306,7 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window) void QSGGuiThreadRenderLoop::exposureChanged(QQuickWindow *window) { if (window->isExposed()) - maybeUpdate(window); + renderWindow(window); } QImage QSGGuiThreadRenderLoop::grab(QQuickWindow *window) -- cgit v1.2.3 From 4d3a64c5e65a781acb4acf4ba641456da28bd1e4 Mon Sep 17 00:00:00 2001 From: Alan Alpert <416365416c@gmail.com> Date: Sun, 16 Dec 2012 21:24:14 -0800 Subject: Add Composite Types to QQmlType When a composite type is loaded from a QML file, it now generates a QQmlType entry in QQmlMetaTypeData. Change-Id: I9b127dff7955456aacb25138fa6ea8efb7bb9221 Reviewed-by: Christopher Adams Reviewed-by: Alan Alpert --- src/qml/doc/src/documents/definetypes.qdoc | 2 +- src/qml/qml/qqmlcompileddata.cpp | 2 +- src/qml/qml/qqmlcompiler.cpp | 47 ++--- src/qml/qml/qqmlengine.cpp | 4 +- src/qml/qml/qqmlengine_p.h | 4 +- src/qml/qml/qqmlimport.cpp | 107 +++++++----- src/qml/qml/qqmlimport_p.h | 5 +- src/qml/qml/qqmlmetatype.cpp | 190 +++++++++++++++------ src/qml/qml/qqmlmetatype_p.h | 14 +- src/qml/qml/qqmlprivate.h | 14 +- src/qml/qml/qqmltypeloader.cpp | 15 +- .../auto/qml/qqmllanguage/data/CompositeType5.qml | 2 + .../auto/qml/qqmllanguage/data/CompositeType6.qml | 2 + .../qqmllanguage/data/assignCompositeToType.qml | 8 + tests/auto/qml/qqmlmetatype/data/CompositeType.qml | 5 + tests/auto/qml/qqmlmetatype/data/ImplicitType.qml | 5 + .../qqmlmetatype/data/testImplicitComposite.qml | 3 + tests/auto/qml/qqmlmetatype/qqmlmetatype.pro | 3 + tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp | 62 ++++++- 19 files changed, 354 insertions(+), 140 deletions(-) create mode 100644 tests/auto/qml/qqmllanguage/data/CompositeType5.qml create mode 100644 tests/auto/qml/qqmllanguage/data/CompositeType6.qml create mode 100644 tests/auto/qml/qqmlmetatype/data/CompositeType.qml create mode 100644 tests/auto/qml/qqmlmetatype/data/ImplicitType.qml create mode 100644 tests/auto/qml/qqmlmetatype/data/testImplicitComposite.qml diff --git a/src/qml/doc/src/documents/definetypes.qdoc b/src/qml/doc/src/documents/definetypes.qdoc index 97a841ccb8..b9df6a4381 100644 --- a/src/qml/doc/src/documents/definetypes.qdoc +++ b/src/qml/doc/src/documents/definetypes.qdoc @@ -34,7 +34,7 @@ One of the core features of QML is that it enables QML object types to be easily \section1 Defining an Object Type with a QML File -To create an object type, a QML document should be placed into a text file named as \e .qml where \e is the desired name of the type, beginning with an uppercase letter. This document is then automatically recognized by the engine as a definition of a QML type. Additionally, a type defined in this manner is automatically made available to other QML files within the same directory as the engine searches within the immediate directory when resolving QML type names. +To create an object type, a QML document should be placed into a text file named as \e .qml where \e is the desired name of the type, which must be comprised of alphanumeric characters or underscores and beginning with an uppercase letter. This document is then automatically recognized by the engine as a definition of a QML type. Additionally, a type defined in this manner is automatically made available to other QML files within the same directory as the engine searches within the immediate directory when resolving QML type names. For example, below is a document that declares a \l Rectangle with a child \l MouseArea. The document has been saved to file named \c SquareButton.qml: diff --git a/src/qml/qml/qqmlcompileddata.cpp b/src/qml/qml/qqmlcompileddata.cpp index 62150b1af1..7279762565 100644 --- a/src/qml/qml/qqmlcompileddata.cpp +++ b/src/qml/qml/qqmlcompileddata.cpp @@ -105,7 +105,7 @@ void QQmlCompiledData::destroy() QQmlCompiledData::~QQmlCompiledData() { if (isRegisteredWithEngine) - QQmlEnginePrivate::get(engine)->unregisterCompositeType(this); + QQmlEnginePrivate::get(engine)->unregisterInternalCompositeType(this); clear(); diff --git a/src/qml/qml/qqmlcompiler.cpp b/src/qml/qml/qqmlcompiler.cpp index 7a65515634..a5f1cc564f 100644 --- a/src/qml/qml/qqmlcompiler.cpp +++ b/src/qml/qml/qqmlcompiler.cpp @@ -815,7 +815,10 @@ bool QQmlCompiler::compile(QQmlEngine *engine, const QQmlTypeData::TypeReference &tref = resolvedTypes.at(ii); QQmlScript::TypeReference *parserRef = referencedTypes.at(ii); - if (tref.type) { + if (tref.typeData) { //QML-based type + ref.component = tref.typeData->compiledData(); + ref.component->addref(); + } else if (tref.type) {//C++-based type ref.type = tref.type; if (!ref.type->isCreatable()) { QString err = ref.type->noCreationReason(); @@ -823,21 +826,18 @@ bool QQmlCompiler::compile(QQmlEngine *engine, err = tr( "Element is not creatable."); COMPILE_EXCEPTION(parserRef->firstUse, err); } - + if (ref.type->containsRevisionedAttributes()) { QQmlError cacheError; ref.typePropertyCache = enginePrivate->cache(ref.type, resolvedTypes.at(ii).minorVersion, cacheError); - if (!ref.typePropertyCache) + if (!ref.typePropertyCache) COMPILE_EXCEPTION(parserRef->firstUse, cacheError.description()); ref.typePropertyCache->addref(); } - - } else if (tref.typeData) { - ref.component = tref.typeData->compiledData(); - ref.component->addref(); } + out->types << ref; } @@ -944,7 +944,7 @@ void QQmlCompiler::compileTree(QQmlScript::Object *tree) Q_ASSERT(tree->metatype); if (!tree->synthdata.isEmpty()) { - enginePrivate->registerCompositeType(output); + enginePrivate->registerInternalCompositeType(output); } else if (output->types.at(tree->type).component) { output->metaTypeId = output->types.at(tree->type).component->metaTypeId; output->listMetaTypeId = output->types.at(tree->type).component->listMetaTypeId; @@ -954,7 +954,7 @@ void QQmlCompiler::compileTree(QQmlScript::Object *tree) output->listMetaTypeId = output->types.at(tree->type).type->qListTypeId(); } if (!tree->synthdata.isEmpty()) - enginePrivate->registerCompositeType(output); + enginePrivate->registerInternalCompositeType(output); } static bool QStringList_contains(const QStringList &list, const QHashedStringRef &string) @@ -1752,7 +1752,7 @@ bool QQmlCompiler::buildProperty(QQmlScript::Property *prop, QQmlType *type = 0; QQmlImportNamespace *typeNamespace = 0; - unit->imports().resolveType(prop->name(), &type, 0, 0, 0, &typeNamespace); + unit->imports().resolveType(prop->name(), &type, 0, 0, &typeNamespace); if (typeNamespace) { COMPILE_CHECK(buildPropertyInNamespace(typeNamespace, prop, obj, @@ -1874,7 +1874,7 @@ bool QQmlCompiler::buildPropertyInNamespace(QQmlImportNamespace *ns, // Setup attached property data QQmlType *type = 0; - unit->imports().resolveType(ns, prop->name(), &type, 0, 0, 0); + unit->imports().resolveType(ns, prop->name(), &type, 0, 0); if (!type || !type->attachedPropertiesType()) COMPILE_EXCEPTION(prop, tr("Non-existent attached object")); @@ -2572,10 +2572,12 @@ bool QQmlCompiler::testQualifiedEnumAssignment(QQmlScript::Property *prop, } QQmlType *type = 0; - unit->imports().resolveType(typeName, &type, 0, 0, 0, 0); + unit->imports().resolveType(typeName, &type, 0, 0, 0); if (!type && typeName != QLatin1String("Qt")) return true; + if (type && type->isComposite()) //No enums on composite types + return true; int value = 0; bool ok = false; @@ -2619,7 +2621,7 @@ int QQmlCompiler::evaluateEnum(const QHashedStringRef &scope, const QByteArray& if (scope != QLatin1String("Qt")) { QQmlType *type = 0; - unit->imports().resolveType(scope, &type, 0, 0, 0, 0); + unit->imports().resolveType(scope, &type, 0, 0, 0); return type ? type->enumValue(QHashedCStringRef(enumValue.constData(), enumValue.length()), ok) : -1; } @@ -2636,7 +2638,7 @@ int QQmlCompiler::evaluateEnum(const QHashedStringRef &scope, const QByteArray& const QMetaObject *QQmlCompiler::resolveType(const QString& name) const { QQmlType *qmltype = 0; - if (!unit->imports().resolveType(name, &qmltype, 0, 0, 0, 0)) + if (!unit->imports().resolveType(name, &qmltype, 0, 0, 0)) return 0; if (!qmltype) return 0; @@ -3024,12 +3026,11 @@ bool QQmlCompiler::buildDynamicMeta(QQmlScript::Object *obj, DynamicMetaMode mod // lazily resolved type Q_ASSERT(s->parameterTypes.at(i) == Object::DynamicProperty::Custom); QQmlType *qmltype = 0; - QString url; - if (!unit->imports().resolveType(s->parameterTypeNames.at(i).toString(), &qmltype, &url, 0, 0, 0)) + if (!unit->imports().resolveType(s->parameterTypeNames.at(i).toString(), &qmltype, 0, 0, 0)) COMPILE_EXCEPTION(s, tr("Invalid signal parameter type: %1").arg(s->parameterTypeNames.at(i).toString())); - if (!qmltype) { - QQmlTypeData *tdata = enginePrivate->typeLoader.getType(QUrl(url)); + if (qmltype->isComposite()) { + QQmlTypeData *tdata = enginePrivate->typeLoader.getType(qmltype->sourceUrl()); Q_ASSERT(tdata); Q_ASSERT(tdata->isComplete()); @@ -3121,12 +3122,12 @@ bool QQmlCompiler::buildDynamicMeta(QQmlScript::Object *obj, DynamicMetaMode mod p->type == Object::DynamicProperty::Custom); QQmlType *qmltype = 0; - QString url; - if (!unit->imports().resolveType(p->customType.toString(), &qmltype, &url, 0, 0, 0)) + if (!unit->imports().resolveType(p->customType.toString(), &qmltype, 0, 0, 0)) COMPILE_EXCEPTION(p, tr("Invalid property type")); - if (!qmltype) { - QQmlTypeData *tdata = enginePrivate->typeLoader.getType(QUrl(url)); + Q_ASSERT(qmltype); + if (qmltype->isComposite()) { + QQmlTypeData *tdata = enginePrivate->typeLoader.getType(qmltype->sourceUrl()); Q_ASSERT(tdata); Q_ASSERT(tdata->isComplete()); @@ -3885,7 +3886,7 @@ QQmlType *QQmlCompiler::toQmlType(QQmlScript::Object *from) type = QQmlMetaType::qmlType(mo); mo = mo->superClass(); } - return type; + return type; } QStringList QQmlCompiler::deferredProperties(QQmlScript::Object *obj) diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index 074ea51ce3..b0b6f45fa9 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -2062,7 +2062,7 @@ QQmlPropertyCache *QQmlEnginePrivate::rawPropertyCacheForType(int t) } } -void QQmlEnginePrivate::registerCompositeType(QQmlCompiledData *data) +void QQmlEnginePrivate::registerInternalCompositeType(QQmlCompiledData *data) { QByteArray name = data->rootPropertyCache->className(); @@ -2097,7 +2097,7 @@ void QQmlEnginePrivate::registerCompositeType(QQmlCompiledData *data) m_compositeTypes.insert(ptr_type, data); } -void QQmlEnginePrivate::unregisterCompositeType(QQmlCompiledData *data) +void QQmlEnginePrivate::unregisterInternalCompositeType(QQmlCompiledData *data) { int ptr_type = data->metaTypeId; int lst_type = data->listMetaTypeId; diff --git a/src/qml/qml/qqmlengine_p.h b/src/qml/qml/qqmlengine_p.h index 43bcc0390f..b5af0bb7cd 100644 --- a/src/qml/qml/qqmlengine_p.h +++ b/src/qml/qml/qqmlengine_p.h @@ -235,8 +235,8 @@ public: QQmlMetaObject metaObjectForType(int) const; QQmlPropertyCache *propertyCacheForType(int); QQmlPropertyCache *rawPropertyCacheForType(int); - void registerCompositeType(QQmlCompiledData *); - void unregisterCompositeType(QQmlCompiledData *); + void registerInternalCompositeType(QQmlCompiledData *); + void unregisterInternalCompositeType(QQmlCompiledData *); bool isTypeLoaded(const QUrl &url) const; bool isScriptLoaded(const QUrl &url) const; diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp index 52d07b23ec..95b59f93f4 100644 --- a/src/qml/qml/qqmlimport.cpp +++ b/src/qml/qml/qqmlimport.cpp @@ -124,6 +124,39 @@ bool isPathAbsolute(const QString &path) #endif } +// If the type does not already exist as a file import, add the type and return the new type +QQmlType *getTypeForUrl(const QString &urlString, const QHashedStringRef& typeName, QList *errors) +{ + QUrl url(urlString); + QQmlType *ret = QQmlMetaType::qmlType(url); + if (!ret) { //QQmlType not yet existing for composite type + int dot = typeName.indexOf(QLatin1Char('.')); + QHashedStringRef unqualifiedtype = dot < 0 ? typeName : QHashedStringRef(typeName.constData() + dot + 1, typeName.length() - dot - 1); + + //XXX: The constData of the string ref is pointing somewhere unsafe in qmlregister, so we need to create a temporary copy + QByteArray buf(unqualifiedtype.toUtf8().constData()); + + QQmlPrivate::RegisterCompositeType reg = { + url, + "", //Empty URI indicates loaded via file imports + -1, + -1, + buf.constData() + }; + ret = QQmlMetaType::qmlTypeFromIndex(QQmlPrivate::qmlregister(QQmlPrivate::CompositeRegistration, ®)); + } + if (!ret) {//Usually when a type name is "found" but invalid + //qDebug() << ret << urlString << QQmlMetaType::qmlType(url); + if (!errors) // Cannot list errors properly, just quit + qFatal(QQmlMetaType::typeRegistrationFailures().join('\n').toLatin1().constData()); + QQmlError error; + error.setDescription(QQmlMetaType::typeRegistrationFailures().join('\n')); + errors->prepend(error); + } + return ret; + +} + } typedef QMap StringStringMap; @@ -150,8 +183,7 @@ public: static QQmlDirScripts getVersionedScripts(const QQmlDirScripts &qmldirscripts, int vmaj, int vmin); bool resolveType(QQmlTypeLoader *typeLoader, const QHashedStringRef &type, - int *vmajor, int *vminor, - QQmlType** type_return, QString* url_return, + int *vmajor, int *vminor, QQmlType** type_return, QString *base = 0, bool *typeRecursionDetected = 0) const; }; QList imports; @@ -159,8 +191,7 @@ public: Import *findImport(const QString &uri); bool resolveType(QQmlTypeLoader *typeLoader, const QHashedStringRef& type, - int *vmajor, int *vminor, - QQmlType** type_return, QString* url_return, + int *vmajor, int *vminor, QQmlType** type_return, QString *base = 0, QList *errors = 0); // Prefix when used as a qualified import. Otherwise empty. @@ -194,8 +225,7 @@ public: QList *errors); bool resolveType(const QHashedStringRef &type, int *vmajor, int *vminor, - QQmlType** type_return, QString* url_return, - QList *errors); + QQmlType** type_return, QList *errors); QUrl baseUrl; QString base; @@ -379,9 +409,8 @@ QString QQmlImports::completeQmldirPath(const QString &uri, const QString &base, The given (namespace qualified) \a type is resolved to either \list - \li a QQmlImportNamespace stored at \a ns_return, - \li a QQmlType stored at \a type_return, or - \li a component located at \a url_return. + \li a QQmlImportNamespace stored at \a ns_return, or + \li a QQmlType stored at \a type_return, \endlist If any return pointer is 0, the corresponding search is not done. @@ -389,7 +418,7 @@ QString QQmlImports::completeQmldirPath(const QString &uri, const QString &base, \sa addFileImport(), addLibraryImport */ bool QQmlImports::resolveType(const QHashedStringRef &type, - QQmlType** type_return, QString* url_return, int *vmaj, int *vmin, + QQmlType** type_return, int *vmaj, int *vmin, QQmlImportNamespace** ns_return, QList *errors) const { QQmlImportNamespace* ns = d->findQualifiedNamespace(type); @@ -398,19 +427,16 @@ bool QQmlImports::resolveType(const QHashedStringRef &type, *ns_return = ns; return true; } - if (type_return || url_return) { - if (d->resolveType(type,vmaj,vmin,type_return,url_return, errors)) { + if (type_return) { + if (d->resolveType(type,vmaj,vmin,type_return, errors)) { if (qmlImportTrace()) { #define RESOLVE_TYPE_DEBUG qDebug().nospace() << "QQmlImports(" << qPrintable(baseUrl().toString()) \ << ')' << "::resolveType: " << type.toString() << " => " - if (type_return && *type_return && url_return && !url_return->isEmpty()) - RESOLVE_TYPE_DEBUG << (*type_return)->typeName() << ' ' << *url_return << " TYPE/URL"; - if (type_return && *type_return) + if (type_return && *type_return && (*type_return)->isComposite()) + RESOLVE_TYPE_DEBUG << (*type_return)->typeName() << ' ' << (*type_return)->sourceUrl() << " TYPE/URL"; + else if (type_return && *type_return) RESOLVE_TYPE_DEBUG << (*type_return)->typeName() << " TYPE"; - if (url_return && !url_return->isEmpty()) - RESOLVE_TYPE_DEBUG << *url_return << " URL"; - #undef RESOLVE_TYPE_DEBUG } return true; @@ -469,23 +495,21 @@ QQmlDirScripts QQmlImportNamespace::Import::getVersionedScripts(const QQmlDirScr \internal Searching \e only in the namespace \a ns (previously returned in a call to - resolveType(), \a type is found and returned to either - a QQmlType stored at \a type_return, or - a component located at \a url_return. + resolveType(), \a type is found and returned to + a QQmlType stored at \a type_return. If the type is from a QML file, the returned + type will be a CompositeType. - If either return pointer is 0, the corresponding search is not done. + If the return pointer is 0, the corresponding search is not done. */ bool QQmlImports::resolveType(QQmlImportNamespace* ns, const QHashedStringRef &type, - QQmlType** type_return, QString* url_return, - int *vmaj, int *vmin) const + QQmlType** type_return, int *vmaj, int *vmin) const { - return ns->resolveType(d->typeLoader,type,vmaj,vmin,type_return,url_return); + return ns->resolveType(d->typeLoader,type,vmaj,vmin,type_return); } bool QQmlImportNamespace::Import::resolveType(QQmlTypeLoader *typeLoader, const QHashedStringRef& type, int *vmajor, int *vminor, - QQmlType** type_return, QString* url_return, - QString *base, bool *typeRecursionDetected) const + QQmlType** type_return, QString *base, bool *typeRecursionDetected) const { if (majversion >= 0 && minversion >= 0) { QQmlType *t = QQmlMetaType::qmlType(type, uri, majversion, minversion); @@ -530,9 +554,9 @@ bool QQmlImportNamespace::Import::resolveType(QQmlTypeLoader *typeLoader, } if (candidate != end) { - if (url_return) - *url_return = componentUrl; - return true; + if (type_return) + *type_return = getTypeForUrl(componentUrl, type, 0); + return (*type_return != 0); } } else if (!isLibrary) { QString qmlUrl = url + QString::fromRawData(type.constData(), type.length()) + dotqml_string; @@ -550,9 +574,9 @@ bool QQmlImportNamespace::Import::resolveType(QQmlTypeLoader *typeLoader, if (typeRecursionDetected) *typeRecursionDetected = true; } else { - if (url_return) - *url_return = qmlUrl; - return true; + if (type_return) + *type_return = getTypeForUrl(qmlUrl, type, 0); + return (*type_return) != 0; } } } @@ -561,8 +585,7 @@ bool QQmlImportNamespace::Import::resolveType(QQmlTypeLoader *typeLoader, } bool QQmlImportsPrivate::resolveType(const QHashedStringRef& type, int *vmajor, int *vminor, - QQmlType** type_return, QString* url_return, - QList *errors) + QQmlType** type_return, QList *errors) { QQmlImportNamespace *s = 0; int dot = type.indexOf(Dot); @@ -591,12 +614,12 @@ bool QQmlImportsPrivate::resolveType(const QHashedStringRef& type, int *vmajor, } QHashedStringRef unqualifiedtype = dot < 0 ? type : QHashedStringRef(type.constData()+dot+1, type.length()-dot-1); if (s) { - if (s->resolveType(typeLoader,unqualifiedtype,vmajor,vminor,type_return,url_return, &base, errors)) + if (s->resolveType(typeLoader,unqualifiedtype,vmajor,vminor,type_return, &base, errors)) return true; - if (s->imports.count() == 1 && !s->imports.at(0)->isLibrary && url_return && s != &unqualifiedset) { + if (s->imports.count() == 1 && !s->imports.at(0)->isLibrary && type_return && s != &unqualifiedset) { // qualified, and only 1 url - *url_return = resolveLocalUrl(s->imports.at(0)->url, unqualifiedtype.toString() + QLatin1String(".qml")); - return true; + *type_return = getTypeForUrl(resolveLocalUrl(s->imports.at(0)->url, unqualifiedtype.toString() + QLatin1String(".qml")), type, errors); + return (*type_return != 0); } } @@ -615,18 +638,18 @@ QQmlImportNamespace::Import *QQmlImportNamespace::findImport(const QString &uri) bool QQmlImportNamespace::resolveType(QQmlTypeLoader *typeLoader, const QHashedStringRef &type, int *vmajor, int *vminor, QQmlType** type_return, - QString* url_return, QString *base, QList *errors) + QString *base, QList *errors) { bool typeRecursionDetected = false; for (int i=0; iresolveType(typeLoader, type, vmajor, vminor, type_return, url_return, + if (import->resolveType(typeLoader, type, vmajor, vminor, type_return, base, &typeRecursionDetected)) { if (qmlCheckTypes()) { // check for type clashes for (int j = i+1; jresolveType(typeLoader, type, vmajor, vminor, 0, 0, base)) { + if (import2->resolveType(typeLoader, type, vmajor, vminor, 0, base)) { if (errors) { QString u1 = import->url; QString u2 = import2->url; diff --git a/src/qml/qml/qqmlimport_p.h b/src/qml/qml/qqmlimport_p.h index ad4b3dfee9..140ca6695e 100644 --- a/src/qml/qml/qqmlimport_p.h +++ b/src/qml/qml/qqmlimport_p.h @@ -85,14 +85,13 @@ public: QUrl baseUrl() const; bool resolveType(const QHashedStringRef &type, - QQmlType** type_return, QString* url_return, + QQmlType** type_return, int *version_major, int *version_minor, QQmlImportNamespace** ns_return, QList *errors = 0) const; bool resolveType(QQmlImportNamespace*, const QHashedStringRef& type, - QQmlType** type_return, QString* url_return, - int *version_major, int *version_minor) const; + QQmlType** type_return, int *version_major, int *version_minor) const; bool addImplicitImport(QQmlImportDatabase *importDb, QList *errors); diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index 2a2d0b3879..372475d85c 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -75,6 +75,8 @@ struct QQmlMetaTypeData Ids idToType; typedef QHash Names; Names nameToType; + typedef QHash Files; //For file imported composite types only + Files urlToType; typedef QHash MetaObjects; MetaObjects metaObjectToType; typedef QHash StringConverters; @@ -178,9 +180,15 @@ public: QQmlType::SingletonInstanceInfo *singletonInstanceInfo; }; + struct QQmlCompositeTypeData + { + QUrl url; + }; + union extraData { QQmlCppTypeData* cd; QQmlSingletonTypeData* sd; + QQmlCompositeTypeData* fd; } extraData; const char *iid; @@ -292,6 +300,9 @@ QQmlTypePrivate::QQmlTypePrivate(QQmlType::RegistrationType type) case QQmlType::InterfaceType: extraData.cd = 0; break; + case QQmlType::CompositeType: + extraData.fd = new QQmlCompositeTypeData; + break; default: qFatal("QQmlTypePrivate Internal Error."); } } @@ -307,6 +318,9 @@ QQmlTypePrivate::~QQmlTypePrivate() delete extraData.sd->singletonInstanceInfo; delete extraData.sd; break; + case QQmlType::CompositeType: + delete extraData.fd; + break; default: //Also InterfaceType, because it has no extra data break; } @@ -389,6 +403,19 @@ QQmlType::QQmlType(int index, const QString &elementName, const QQmlPrivate::Reg d->extraData.cd->extMetaObject = type.extensionMetaObject; } +QQmlType::QQmlType(int index, const QString &elementName, const QQmlPrivate::RegisterCompositeType &type) +: d(new QQmlTypePrivate(CompositeType)) +{ + d->index = index; + d->elementName = elementName; + + d->module = moduleFromUtf8(type.uri); + d->version_maj = type.versionMajor; + d->version_min = type.versionMinor; + + d->extraData.fd->url = type.url; +} + QQmlType::~QQmlType() { delete d; @@ -751,6 +778,11 @@ bool QQmlType::isInterface() const return d->regType == InterfaceType; } +bool QQmlType::isComposite() const +{ + return d->regType == CompositeType; +} + int QQmlType::typeId() const { return d->typeId; @@ -848,6 +880,13 @@ int QQmlType::index() const return d->index; } +QUrl QQmlType::sourceUrl() const +{ + if (d->regType != CompositeType) + return QUrl(); + return d->extraData.fd->url; +} + int QQmlType::enumValue(const QHashedStringRef &name, bool *ok) const { Q_ASSERT(ok); @@ -1074,6 +1113,8 @@ QString registrationTypeString(QQmlType::RegistrationType typeType) typeStr = QStringLiteral("element"); else if (typeType == QQmlType::SingletonType) typeStr = QStringLiteral("singleton type"); + else + typeStr = QStringLiteral("type"); return typeStr; } @@ -1083,7 +1124,7 @@ bool checkRegistration(QQmlType::RegistrationType typeType, QQmlMetaTypeData *da if (!typeName.isEmpty()) { int typeNameLen = typeName.length(); for (int ii = 0; ii < typeNameLen; ++ii) { - if (!typeName.at(ii).isLetterOrNumber()) { + if (!(typeName.at(ii).isLetterOrNumber() || typeName.at(ii) == '_')) { QString failure(QCoreApplication::translate("qmlRegisterType", "Invalid QML %1 name \"%2\"")); data->typeRegistrationFailures.append(failure.arg(registrationTypeString(typeType)).arg(typeName)); return false; @@ -1116,46 +1157,59 @@ bool checkRegistration(QQmlType::RegistrationType typeType, QQmlMetaTypeData *da return true; } -int registerType(const QQmlPrivate::RegisterType &type) +// NOTE: caller must hold a QWriteLocker on "data" +void addTypeToData(QQmlType* type, QQmlMetaTypeData *data) { - QWriteLocker lock(metaTypeDataLock()); - QQmlMetaTypeData *data = metaTypeData(); - QString elementName = QString::fromUtf8(type.elementName); - if (!checkRegistration(QQmlType::CppType, data, type.uri, elementName)) - return -1; - - int index = data->types.count(); - - QQmlType *dtype = new QQmlType(index, elementName, type); - - data->types.append(dtype); - data->idToType.insert(dtype->typeId(), dtype); - if (dtype->qListTypeId()) data->idToType.insert(dtype->qListTypeId(), dtype); + if (!type->elementName().isEmpty()) + data->nameToType.insertMulti(type->elementName(), type); - if (!dtype->elementName().isEmpty()) - data->nameToType.insertMulti(dtype->elementName(), dtype); + if (type->baseMetaObject()) + data->metaObjectToType.insertMulti(type->baseMetaObject(), type); - data->metaObjectToType.insertMulti(dtype->baseMetaObject(), dtype); + if (type->typeId()) { + data->idToType.insert(type->typeId(), type); + if (data->objects.size() <= type->typeId()) + data->objects.resize(type->typeId() + 16); + data->objects.setBit(type->typeId(), true); + } - if (data->objects.size() <= type.typeId) - data->objects.resize(type.typeId + 16); - if (data->lists.size() <= type.listId) - data->lists.resize(type.listId + 16); - data->objects.setBit(type.typeId, true); - if (type.listId) data->lists.setBit(type.listId, true); + if (type->qListTypeId()) { + if (data->lists.size() <= type->qListTypeId()) + data->lists.resize(type->qListTypeId() + 16); + data->lists.setBit(type->qListTypeId(), true); + data->idToType.insert(type->qListTypeId(), type); + } - if (!dtype->module().isEmpty()) { - const QHashedString &mod = dtype->module(); + if (!type->module().isEmpty()) { + const QHashedString &mod = type->module(); - QQmlMetaTypeData::VersionedUri versionedUri(mod, type.versionMajor); + QQmlMetaTypeData::VersionedUri versionedUri(mod, type->majorVersion()); QQmlTypeModule *module = data->uriToModule.value(versionedUri); if (!module) { module = new QQmlTypeModule; module->d->uri = versionedUri; data->uriToModule.insert(versionedUri, module); } - module->d->add(dtype); + module->d->add(type); } +} + +int registerType(const QQmlPrivate::RegisterType &type) +{ + QWriteLocker lock(metaTypeDataLock()); + QQmlMetaTypeData *data = metaTypeData(); + QString elementName = QString::fromUtf8(type.elementName); + if (!checkRegistration(QQmlType::CppType, data, type.uri, elementName)) + return -1; + + int index = data->types.count(); + + QQmlType *dtype = new QQmlType(index, elementName, type); + + data->types.append(dtype); + addTypeToData(dtype, data); + if (!type.typeId) + data->idToType.insert(dtype->typeId(), dtype); return index; } @@ -1173,32 +1227,31 @@ int registerSingletonType(const QQmlPrivate::RegisterSingletonType &type) QQmlType *dtype = new QQmlType(index, typeName, type); data->types.append(dtype); - data->idToType.insert(dtype->typeId(), dtype); + addTypeToData(dtype, data); - if (!dtype->elementName().isEmpty()) - data->nameToType.insertMulti(dtype->elementName(), dtype); + return index; +} - if (dtype->baseMetaObject()) - data->metaObjectToType.insertMulti(dtype->baseMetaObject(), dtype); +int registerCompositeType(const QQmlPrivate::RegisterCompositeType &type) +{ + // Assumes URL is absolute and valid. Checking of user input should happen before the URL enters type. + QWriteLocker lock(metaTypeDataLock()); + QQmlMetaTypeData *data = metaTypeData(); + QString typeName = QString::fromUtf8(type.typeName); + bool fileImport = false; + if (*(type.uri) == '\0') + fileImport = true; + if (!checkRegistration(QQmlType::CompositeType, data, fileImport?0:type.uri, typeName)) + return -1; - if (type.typeId) { - if (data->objects.size() <= type.typeId) - data->objects.resize(type.typeId + 16); - data->objects.setBit(type.typeId, true); - } + int index = data->types.count(); - if (!dtype->module().isEmpty()) { - const QHashedString &mod = dtype->module(); + QQmlType *dtype = new QQmlType(index, typeName, type); + data->types.append(dtype); + addTypeToData(dtype, data); - QQmlMetaTypeData::VersionedUri versionedUri(mod, type.versionMajor); - QQmlTypeModule *module = data->uriToModule.value(versionedUri); - if (!module) { - module = new QQmlTypeModule; - module->d->uri = versionedUri; - data->uriToModule.insert(versionedUri, module); - } - module->d->add(dtype); - } + if (fileImport) + data->urlToType.insertMulti(type.url, dtype); return index; } @@ -1218,6 +1271,8 @@ int QQmlPrivate::qmlregister(RegistrationType type, void *data) return registerAutoParentFunction(*reinterpret_cast(data)); } else if (type == SingletonRegistration) { return registerSingletonType(*reinterpret_cast(data)); + } else if (type == CompositeRegistration) { + return registerCompositeType(*reinterpret_cast(data)); } return -1; } @@ -1534,7 +1589,7 @@ QQmlType *QQmlMetaType::qmlType(const QHashedStringRef &name, const QHashedStrin QQmlMetaTypeData::Names::ConstIterator it = data->nameToType.constFind(name); while (it != data->nameToType.end() && it.key() == name) { // XXX version_major<0 just a kludge for QQmlPropertyPrivate::initProperty - if (version_major < 0 || (*it)->availableInVersion(module, version_major,version_minor)) + if (version_major < 0 || module.isEmpty() || (*it)->availableInVersion(module, version_major,version_minor)) return (*it); ++it; } @@ -1568,7 +1623,7 @@ QQmlType *QQmlMetaType::qmlType(const QMetaObject *metaObject, const QHashedStri QQmlMetaTypeData::MetaObjects::const_iterator it = data->metaObjectToType.constFind(metaObject); while (it != data->metaObjectToType.end() && it.key() == metaObject) { QQmlType *t = *it; - if (version_major < 0 || t->availableInVersion(module, version_major,version_minor)) + if (version_major < 0 || module.isEmpty() || t->availableInVersion(module, version_major,version_minor)) return t; ++it; } @@ -1592,6 +1647,39 @@ QQmlType *QQmlMetaType::qmlType(int userType) return 0; } +/*! + Returns the type (if any) that corresponds to the given \a url in the set of + composite types added through file imports. + + Returns null if no such type is registered. +*/ +QQmlType *QQmlMetaType::qmlType(const QUrl &url) +{ + QReadLocker lock(metaTypeDataLock()); + QQmlMetaTypeData *data = metaTypeData(); + + QQmlType *type = data->urlToType.value(url); + if (type && type->sourceUrl() == url) + return type; + else + return 0; +} + +/*! + Returns the type (if any) with the given \a index in the global type store. + This is for use when you just got the index back from a qmlRegister function. + Returns null if the index is out of bounds. +*/ +QQmlType *QQmlMetaType::qmlTypeFromIndex(int idx) +{ + QReadLocker lock(metaTypeDataLock()); + QQmlMetaTypeData *data = metaTypeData(); + + if (idx < 0 || idx >= data->types.count()) + return 0; + return data->types[idx]; +} + /*! Returns the list of registered QML type names. */ diff --git a/src/qml/qml/qqmlmetatype_p.h b/src/qml/qml/qqmlmetatype_p.h index 8d3aad519e..497afffb5d 100644 --- a/src/qml/qml/qqmlmetatype_p.h +++ b/src/qml/qml/qqmlmetatype_p.h @@ -84,6 +84,8 @@ public: static QQmlType *qmlType(const QMetaObject *); static QQmlType *qmlType(const QMetaObject *metaObject, const QHashedStringRef &module, int version_major, int version_minor); static QQmlType *qmlType(int); + static QQmlType *qmlType(const QUrl &url); + static QQmlType *qmlTypeFromIndex(int); static QMetaProperty defaultProperty(const QMetaObject *); static QMetaProperty defaultProperty(QObject *); @@ -164,6 +166,7 @@ public: bool isSingleton() const; bool isInterface() const; + bool isComposite() const; int typeId() const; int qListTypeId() const; @@ -206,6 +209,7 @@ public: QHash qobjectApis; }; SingletonInstanceInfo *singletonInstanceInfo() const; + QUrl sourceUrl() const; int enumValue(const QHashedStringRef &, bool *ok) const; int enumValue(const QHashedCStringRef &, bool *ok) const; @@ -218,17 +222,19 @@ private: enum RegistrationType { CppType = 0, SingletonType = 1, - InterfaceType = 2 - // In the near future, we should register all types via QQmlType, including Composite types. + InterfaceType = 2, + CompositeType = 3 }; friend QString registrationTypeString(RegistrationType); friend bool checkRegistration(RegistrationType, QQmlMetaTypeData *, const char *, const QString &); friend int registerType(const QQmlPrivate::RegisterType &); friend int registerSingletonType(const QQmlPrivate::RegisterSingletonType &); friend int registerInterface(const QQmlPrivate::RegisterInterface &); + friend int registerCompositeType(const QQmlPrivate::RegisterCompositeType &); QQmlType(int, const QQmlPrivate::RegisterInterface &); QQmlType(int, const QString &, const QQmlPrivate::RegisterSingletonType &); QQmlType(int, const QString &, const QQmlPrivate::RegisterType &); + QQmlType(int, const QString &, const QQmlPrivate::RegisterCompositeType &); ~QQmlType(); QQmlTypePrivate *d; @@ -250,8 +256,8 @@ public: QList singletonTypes(int) const; private: - friend int registerType(const QQmlPrivate::RegisterType &); - friend int registerSingletonType(const QQmlPrivate::RegisterSingletonType &); + //Used by register functions and creates the QQmlTypeModule for them + friend void addTypeToData(QQmlType* type, QQmlMetaTypeData *data); friend struct QQmlMetaTypeData; QQmlTypeModule(); diff --git a/src/qml/qml/qqmlprivate.h b/src/qml/qml/qqmlprivate.h index 4d345fad23..570435ae81 100644 --- a/src/qml/qml/qqmlprivate.h +++ b/src/qml/qml/qqmlprivate.h @@ -57,6 +57,7 @@ #include #include +#include QT_BEGIN_NAMESPACE @@ -251,11 +252,20 @@ namespace QQmlPrivate // If this is extended ensure "version" is bumped!!! }; + struct RegisterCompositeType { + const QUrl &url; + const char *uri; + int versionMajor; + int versionMinor; + const char *typeName; + }; + enum RegistrationType { - TypeRegistration = 0, + TypeRegistration = 0, InterfaceRegistration = 1, AutoParentRegistration = 2, - SingletonRegistration = 3 + SingletonRegistration = 3, + CompositeRegistration = 4 }; int Q_QML_EXPORT qmlregister(RegistrationType, void *); diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index aa7a2d95c7..5910f60667 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -2135,12 +2135,12 @@ void QQmlTypeData::resolveTypes() TypeReference ref; QString url; - int majorVersion; - int minorVersion; + int majorVersion = -1; + int minorVersion = -1; QQmlImportNamespace *typeNamespace = 0; QList errors; - if (!m_imports.resolveType(parserRef->name, &ref.type, &url, &majorVersion, &minorVersion, + if (!m_imports.resolveType(parserRef->name, &ref.type, &majorVersion, &minorVersion, &typeNamespace, &errors) || typeNamespace) { // Known to not be a type: // - known to be a namespace (Namespace {}) @@ -2169,13 +2169,12 @@ void QQmlTypeData::resolveTypes() return; } - if (ref.type) { - ref.majorVersion = majorVersion; - ref.minorVersion = minorVersion; - } else { - ref.typeData = typeLoader()->getType(QUrl(url)); + if (ref.type->isComposite()) { + ref.typeData = typeLoader()->getType(ref.type->sourceUrl()); addDependency(ref.typeData); } + ref.majorVersion = majorVersion; + ref.minorVersion = minorVersion; Q_ASSERT(parserRef->firstUse); ref.location = parserRef->firstUse->location.start; diff --git a/tests/auto/qml/qqmllanguage/data/CompositeType5.qml b/tests/auto/qml/qqmllanguage/data/CompositeType5.qml new file mode 100644 index 0000000000..564468ce90 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/CompositeType5.qml @@ -0,0 +1,2 @@ +CompositeType { +} diff --git a/tests/auto/qml/qqmllanguage/data/CompositeType6.qml b/tests/auto/qml/qqmllanguage/data/CompositeType6.qml new file mode 100644 index 0000000000..a8a2af9318 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/CompositeType6.qml @@ -0,0 +1,2 @@ +CompositeType2 { +} diff --git a/tests/auto/qml/qqmllanguage/data/assignCompositeToType.qml b/tests/auto/qml/qqmllanguage/data/assignCompositeToType.qml index 717cd84536..4034849df8 100644 --- a/tests/auto/qml/qqmllanguage/data/assignCompositeToType.qml +++ b/tests/auto/qml/qqmllanguage/data/assignCompositeToType.qml @@ -8,6 +8,10 @@ QtObject { property QtObject myProperty4 property MyQmlObject myProperty5 property MyQmlObject myProperty6 + property CompositeType myProperty7 + property CompositeType myProperty8 + property CompositeType2 myProperty9 + property CompositeType2 myPropertyA myProperty: CompositeType {} myProperty2: CompositeType2 {} @@ -15,4 +19,8 @@ QtObject { myProperty4: CompositeType4 {} myProperty5: CompositeType2 {} myProperty6: CompositeType4 {} + myProperty7: CompositeType {} + myProperty8: CompositeType5 {} + myProperty9: CompositeType2 {} + myPropertyA: CompositeType6 {} } diff --git a/tests/auto/qml/qqmlmetatype/data/CompositeType.qml b/tests/auto/qml/qqmlmetatype/data/CompositeType.qml new file mode 100644 index 0000000000..bc2abca42b --- /dev/null +++ b/tests/auto/qml/qqmlmetatype/data/CompositeType.qml @@ -0,0 +1,5 @@ +import QtQml 2.0 + +QtObject { + property int foo +} diff --git a/tests/auto/qml/qqmlmetatype/data/ImplicitType.qml b/tests/auto/qml/qqmlmetatype/data/ImplicitType.qml new file mode 100644 index 0000000000..ca2bcef5bf --- /dev/null +++ b/tests/auto/qml/qqmlmetatype/data/ImplicitType.qml @@ -0,0 +1,5 @@ +import QtQml 2.0 + +QtObject { + property int bar +} diff --git a/tests/auto/qml/qqmlmetatype/data/testImplicitComposite.qml b/tests/auto/qml/qqmlmetatype/data/testImplicitComposite.qml new file mode 100644 index 0000000000..f838c6995f --- /dev/null +++ b/tests/auto/qml/qqmlmetatype/data/testImplicitComposite.qml @@ -0,0 +1,3 @@ +import "." + +ImplicitType{} diff --git a/tests/auto/qml/qqmlmetatype/qqmlmetatype.pro b/tests/auto/qml/qqmlmetatype/qqmlmetatype.pro index b24f006bad..a9a6a32a2b 100644 --- a/tests/auto/qml/qqmlmetatype/qqmlmetatype.pro +++ b/tests/auto/qml/qqmlmetatype/qqmlmetatype.pro @@ -3,6 +3,9 @@ TARGET = tst_qqmlmetatype SOURCES += tst_qqmlmetatype.cpp macx:CONFIG -= app_bundle +TESTDATA = data/* +include (../../shared/util.pri) + CONFIG += parallel_test QT += core-private gui-private qml-private testlib v8-private DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 diff --git a/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp b/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp index d5dd364e25..03d2f0beb0 100644 --- a/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp +++ b/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp @@ -41,12 +41,16 @@ #include #include +#include +#include +#include #include #include #include +#include "../../shared/util.h" -class tst_qqmlmetatype : public QObject +class tst_qqmlmetatype : public QQmlDataTest { Q_OBJECT public: @@ -60,6 +64,8 @@ private slots: void qmlPropertyValueInterceptorCast(); void qmlType(); void invalidQmlTypeName(); + void registrationType(); + void compositeType(); void isList(); @@ -77,6 +83,13 @@ public: }; QML_DECLARE_TYPE(TestType); +QObject *testTypeProvider(QQmlEngine *engine, QJSEngine *scriptEngine) +{ + Q_UNUSED(engine); + Q_UNUSED(scriptEngine); + return new TestType(); +} + class ParserStatusTestType : public QObject, public QQmlParserStatus { Q_OBJECT @@ -108,10 +121,19 @@ QML_DECLARE_TYPE(ValueInterceptorTestType); void tst_qqmlmetatype::initTestCase() { + QQmlDataTest::initTestCase(); qmlRegisterType("Test", 1, 0, "TestType"); + qmlRegisterSingletonType("Test", 1, 0, "TestTypeSingleton", testTypeProvider); qmlRegisterType("Test", 1, 0, "ParserStatusTestType"); qmlRegisterType("Test", 1, 0, "ValueSourceTestType"); qmlRegisterType("Test", 1, 0, "ValueInterceptorTestType"); + + QUrl testTypeUrl(testFileUrl("CompositeType.qml")); + //TODO: Replace this with public API version when added + QQmlPrivate::RegisterCompositeType regStruct = { + testTypeUrl,"Test", 1, 0, "TestTypeComposite" + }; + QQmlPrivate::qmlregister(QQmlPrivate::CompositeRegistration, ®Struct); } void tst_qqmlmetatype::qmlParserStatusCast() @@ -228,6 +250,44 @@ void tst_qqmlmetatype::defaultObject() QCOMPARE(QString(QQmlMetaType::defaultProperty(&t).name()), QString("foo")); } +void tst_qqmlmetatype::registrationType() +{ + QQmlType *type = QQmlMetaType::qmlType(QString("TestType"), QString("Test"), 1, 0); + QVERIFY(type); + QVERIFY(!type->isInterface()); + QVERIFY(!type->isSingleton()); + QVERIFY(!type->isComposite()); + + type = QQmlMetaType::qmlType(QString("TestTypeSingleton"), QString("Test"), 1, 0); + QVERIFY(type); + QVERIFY(!type->isInterface()); + QVERIFY(type->isSingleton()); + QVERIFY(!type->isComposite()); + + type = QQmlMetaType::qmlType(QString("TestTypeComposite"), QString("Test"), 1, 0); + QVERIFY(type); + QVERIFY(!type->isInterface()); + QVERIFY(!type->isSingleton()); + QVERIFY(type->isComposite()); +} + +void tst_qqmlmetatype::compositeType() +{ + QQmlEngine engine; + + //Loading the test file also loads all composite types it imports + QQmlComponent c(&engine, testFileUrl("testImplicitComposite.qml")); + QObject* obj = c.create(); + QVERIFY(obj); + + QQmlType *type = QQmlMetaType::qmlType(QString("ImplicitType"), QString(""), 1, 0); + QVERIFY(type); + QVERIFY(type->module() == QLatin1String("")); + QVERIFY(type->elementName() == QLatin1String("ImplicitType")); + QCOMPARE(type->qmlTypeName(), QLatin1String("ImplicitType")); + QCOMPARE(type->sourceUrl(), testFileUrl("ImplicitType.qml")); +} + QTEST_MAIN(tst_qqmlmetatype) #include "tst_qqmlmetatype.moc" -- cgit v1.2.3 From d0491c87a9f0f2dadc9ef0e0a267b8c241c08b41 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Wed, 13 Feb 2013 17:42:56 +0100 Subject: qmlplugindump: don't crash if a composite type was a registered This doesn't actually result in putting any information about the composite type into the output, though. Change-Id: I160702578f0acd22eff98e8afa288b3c9fb20076 Reviewed-by: Alan Alpert --- tools/qmlplugindump/main.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tools/qmlplugindump/main.cpp b/tools/qmlplugindump/main.cpp index b5ee08ddd3..e05c77cbfa 100644 --- a/tools/qmlplugindump/main.cpp +++ b/tools/qmlplugindump/main.cpp @@ -184,11 +184,13 @@ QSet collectReachableMetaObjects(QQmlEngine *engine, const QHash > extensions; foreach (const QQmlType *ty, QQmlMetaType::qmlTypes()) { - qmlTypesByCppName[ty->metaObject()->className()].insert(ty); - if (ty->isExtendedType()) { - extensions[ty->typeName()].insert(ty->metaObject()->className()); + if (!ty->isComposite()) { + qmlTypesByCppName[ty->metaObject()->className()].insert(ty); + if (ty->isExtendedType()) + extensions[ty->typeName()].insert(ty->metaObject()->className()); + collectReachableMetaObjects(ty, &metas); } - collectReachableMetaObjects(ty, &metas); + // TODO actually handle composite types } // Adjust exports of the base object if there are extensions. -- cgit v1.2.3 From 99ad368cf810723643ae76bba6a1adba3321b18f Mon Sep 17 00:00:00 2001 From: Alan Alpert Date: Thu, 24 Jan 2013 12:13:57 -0800 Subject: Add qmlRegisterType for Composite Types This is equivalent functionality to registering a composite type in a qmldir file, a type name in a versioned module is associated with a given file. This function now allows that to be done easily from C++. Change-Id: I1cf44b92c3ad7fee593f4f84773c35b56253e628 Reviewed-by: Shawn Rutledge --- src/qml/doc/src/qmlfunctions.qdoc | 17 +++++++++++++++++ src/qml/qml/qqml.h | 21 +++++++++++++++++++++ .../data/badCompositeRegistration.1.errors.txt | 2 ++ .../data/badCompositeRegistration.1.qml | 3 +++ .../data/badCompositeRegistration.2.errors.txt | 2 ++ .../data/badCompositeRegistration.2.qml | 3 +++ .../qqmllanguage/data/registeredCompositeType.qml | 3 +++ tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp | 19 +++++++++++++++++++ tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp | 6 +----- 9 files changed, 71 insertions(+), 5 deletions(-) create mode 100644 tests/auto/qml/qqmllanguage/data/badCompositeRegistration.1.errors.txt create mode 100644 tests/auto/qml/qqmllanguage/data/badCompositeRegistration.1.qml create mode 100644 tests/auto/qml/qqmllanguage/data/badCompositeRegistration.2.errors.txt create mode 100644 tests/auto/qml/qqmllanguage/data/badCompositeRegistration.2.qml create mode 100644 tests/auto/qml/qqmllanguage/data/registeredCompositeType.qml diff --git a/src/qml/doc/src/qmlfunctions.qdoc b/src/qml/doc/src/qmlfunctions.qdoc index da62ddab6a..81b8fd594f 100644 --- a/src/qml/doc/src/qmlfunctions.qdoc +++ b/src/qml/doc/src/qmlfunctions.qdoc @@ -380,3 +380,20 @@ \endcode */ + +/*! + \fn int qmlRegisterType(const QUrl &url, const char *uri, int versionMajor, int versionMinor, const char *qmlName); + \relates QQmlEngine + + This function registers a type in the QML system with the name \a qmlName, in the library imported from \a uri having the + version number composed from \a versionMajor and \a versionMinor. The type is defined by the QML file located at \a url. The + url must be an absolute URL, i.e. url.isRelative() == false. + + Normally QML files can be loaded as types directly from other QML files, or using a qmldir file. This function allows + registration of files to types from a C++ module, such as when the type mapping needs to be procedurally determined at startup. + + #include to use this function. + + Returns non-zero if the registration was sucessful. +*/ + diff --git a/src/qml/qml/qqml.h b/src/qml/qml/qqml.h index 877e6c16b7..7e6e0d1d36 100644 --- a/src/qml/qml/qqml.h +++ b/src/qml/qml/qqml.h @@ -49,6 +49,7 @@ #include #include +#include #define QML_VERSION 0x020000 #define QML_VERSION_STR "2.0" @@ -463,6 +464,26 @@ inline int qmlRegisterSingletonType(const char *uri, int versionMajor, int versi return QQmlPrivate::qmlregister(QQmlPrivate::SingletonRegistration, &api); } + +inline int qmlRegisterType(const QUrl &url, const char *uri, int versionMajor, int versionMinor, const char *qmlName) +{ + if (url.isRelative()) { + // User input check must go here, because QQmlPrivate::qmlregister is also used internally for composite types + qWarning() << "qmlRegisterType requires absolute URLs."; + return 0; + } + + QQmlPrivate::RegisterCompositeType type = { + url, + uri, + versionMajor, + versionMinor, + qmlName + }; + + return QQmlPrivate::qmlregister(QQmlPrivate::CompositeRegistration, &type); +} + QT_END_NAMESPACE QML_DECLARE_TYPE(QObject) diff --git a/tests/auto/qml/qqmllanguage/data/badCompositeRegistration.1.errors.txt b/tests/auto/qml/qqmllanguage/data/badCompositeRegistration.1.errors.txt new file mode 100644 index 0000000000..7a75447a62 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/badCompositeRegistration.1.errors.txt @@ -0,0 +1,2 @@ +3:1:Type RegisteredCompositeType2 unavailable +-1:-1:File not found diff --git a/tests/auto/qml/qqmllanguage/data/badCompositeRegistration.1.qml b/tests/auto/qml/qqmllanguage/data/badCompositeRegistration.1.qml new file mode 100644 index 0000000000..915a6138d9 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/badCompositeRegistration.1.qml @@ -0,0 +1,3 @@ +import Test 1.0 + +RegisteredCompositeType2 {} diff --git a/tests/auto/qml/qqmllanguage/data/badCompositeRegistration.2.errors.txt b/tests/auto/qml/qqmllanguage/data/badCompositeRegistration.2.errors.txt new file mode 100644 index 0000000000..0272619404 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/badCompositeRegistration.2.errors.txt @@ -0,0 +1,2 @@ +3:1:Type RegisteredCompositeType3 unavailable +1:1:Expected a qualified name id diff --git a/tests/auto/qml/qqmllanguage/data/badCompositeRegistration.2.qml b/tests/auto/qml/qqmllanguage/data/badCompositeRegistration.2.qml new file mode 100644 index 0000000000..43d7f3ee95 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/badCompositeRegistration.2.qml @@ -0,0 +1,3 @@ +import Test 1.0 + +RegisteredCompositeType3 {} diff --git a/tests/auto/qml/qqmllanguage/data/registeredCompositeType.qml b/tests/auto/qml/qqmllanguage/data/registeredCompositeType.qml new file mode 100644 index 0000000000..f633a14fa7 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/registeredCompositeType.qml @@ -0,0 +1,3 @@ +import Test 1.0 + +RegisteredCompositeType {} diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index 222548da29..3121d10265 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -139,6 +139,7 @@ private slots: void registrationOrder(); void readonly(); void receivers(); + void registeredCompositeType(); void basicRemote_data(); void basicRemote(); @@ -467,6 +468,9 @@ void tst_qqmllanguage::errors_data() QTest::newRow("invalidTypeName.4") << "invalidTypeName.4.qml" << "invalidTypeName.4.errors.txt" << false; QTest::newRow("Major version isolation") << "majorVersionIsolation.qml" << "majorVersionIsolation.errors.txt" << false; + + QTest::newRow("badCompositeRegistration.1") << "badCompositeRegistration.1.qml" << "badCompositeRegistration.1.errors.txt" << false; + QTest::newRow("badCompositeRegistration.2") << "badCompositeRegistration.2.qml" << "badCompositeRegistration.2.errors.txt" << false; } @@ -2771,6 +2775,10 @@ void tst_qqmllanguage::initTestCase() QQmlMetaType::registerCustomStringConverter(qMetaTypeId(), myCustomVariantTypeConverter); registerTypes(); + // Registered here because it uses testFileUrl + qmlRegisterType(testFileUrl("CompositeType.qml"), "Test", 1, 0, "RegisteredCompositeType"); + qmlRegisterType(testFileUrl("CompositeType.DoesNotExist.qml"), "Test", 1, 0, "RegisteredCompositeType2"); + qmlRegisterType(testFileUrl("invalidRoot.1.qml"), "Test", 1, 0, "RegisteredCompositeType3"); // Registering the TestType class in other modules should have no adverse effects qmlRegisterType("org.qtproject.TestPre", 1, 0, "Test"); @@ -2915,6 +2923,17 @@ void tst_qqmllanguage::receivers() delete o; } +void tst_qqmllanguage::registeredCompositeType() +{ + QQmlComponent component(&engine, testFileUrl("registeredCompositeType.qml")); + + VERIFY_ERRORS(0); + QObject *o = component.create(); + QVERIFY(o != 0); + + delete o; +} + // QTBUG-18268 void tst_qqmllanguage::remoteLoadCrash() { diff --git a/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp b/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp index 03d2f0beb0..90023222d3 100644 --- a/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp +++ b/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp @@ -129,11 +129,7 @@ void tst_qqmlmetatype::initTestCase() qmlRegisterType("Test", 1, 0, "ValueInterceptorTestType"); QUrl testTypeUrl(testFileUrl("CompositeType.qml")); - //TODO: Replace this with public API version when added - QQmlPrivate::RegisterCompositeType regStruct = { - testTypeUrl,"Test", 1, 0, "TestTypeComposite" - }; - QQmlPrivate::qmlregister(QQmlPrivate::CompositeRegistration, ®Struct); + qmlRegisterType(testTypeUrl, "Test", 1, 0, "TestTypeComposite"); } void tst_qqmlmetatype::qmlParserStatusCast() -- cgit v1.2.3 From 0dc1c0554465d4e2546b75254371f3b8b01e957d Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Mon, 11 Feb 2013 19:07:02 +0100 Subject: Make the base URL available to QQmlExtensionPlugin This is for the benefit of any plugin which needs to interact with the QML files or related assets which are also located in its import directory. Change-Id: Id23ec279b4d017bd3f620c3a7916dac9f9ac29bc Reviewed-by: Alan Alpert --- src/qml/qml/qml.pri | 3 +- src/qml/qml/qqmlextensionplugin.cpp | 9 ++++- src/qml/qml/qqmlextensionplugin.h | 13 ++++--- src/qml/qml/qqmlextensionplugin_p.h | 74 +++++++++++++++++++++++++++++++++++++ src/qml/qml/qqmlimport.cpp | 6 +++ 5 files changed, 98 insertions(+), 7 deletions(-) create mode 100644 src/qml/qml/qqmlextensionplugin_p.h diff --git a/src/qml/qml/qml.pri b/src/qml/qml/qml.pri index 53094db59d..d3abd84fc8 100644 --- a/src/qml/qml/qml.pri +++ b/src/qml/qml/qml.pri @@ -133,7 +133,8 @@ HEADERS += \ $$PWD/qqmlconnections_p.h \ $$PWD/qqmltimer_p.h \ $$PWD/qqmlbind_p.h \ - $$PWD/qqmlplatform_p.h + $$PWD/qqmlplatform_p.h \ + $$PWD/qqmlextensionplugin_p.h include(parser/parser.pri) diff --git a/src/qml/qml/qqmlextensionplugin.cpp b/src/qml/qml/qqmlextensionplugin.cpp index bbc0fed768..edef5a91cd 100644 --- a/src/qml/qml/qqmlextensionplugin.cpp +++ b/src/qml/qml/qqmlextensionplugin.cpp @@ -40,6 +40,7 @@ ****************************************************************************/ #include "qqmlextensionplugin.h" +#include "qqmlextensionplugin_p.h" QT_BEGIN_NAMESPACE @@ -144,7 +145,7 @@ QT_BEGIN_NAMESPACE explicitly. */ QQmlExtensionPlugin::QQmlExtensionPlugin(QObject *parent) - : QObject(parent) + : QObject(*(new QQmlExtensionPluginPrivate), parent) { } @@ -155,6 +156,12 @@ QQmlExtensionPlugin::~QQmlExtensionPlugin() { } +QUrl QQmlExtensionPlugin::baseUrl() const +{ + Q_D(const QQmlExtensionPlugin); + return d->baseUrl; +} + /*! \fn void QQmlExtensionPlugin::initializeEngine(QQmlEngine *engine, const char *uri) diff --git a/src/qml/qml/qqmlextensionplugin.h b/src/qml/qml/qqmlextensionplugin.h index 8568565f22..170c7915a5 100644 --- a/src/qml/qml/qqmlextensionplugin.h +++ b/src/qml/qml/qqmlextensionplugin.h @@ -43,28 +43,31 @@ #define QQMLEXTENSIONPLUGIN_H #include - +#include #include QT_BEGIN_NAMESPACE - class QQmlEngine; +class QQmlExtensionPluginPrivate; -class Q_QML_EXPORT QQmlExtensionPlugin : public QObject, - public QQmlExtensionInterface +class Q_QML_EXPORT QQmlExtensionPlugin + : public QObject + , public QQmlExtensionInterface { Q_OBJECT + Q_DECLARE_PRIVATE(QQmlExtensionPlugin) Q_INTERFACES(QQmlExtensionInterface) Q_INTERFACES(QQmlTypesExtensionInterface) public: explicit QQmlExtensionPlugin(QObject *parent = 0); ~QQmlExtensionPlugin(); + QUrl baseUrl() const; + virtual void registerTypes(const char *uri) = 0; virtual void initializeEngine(QQmlEngine *engine, const char *uri); -private: Q_DISABLE_COPY(QQmlExtensionPlugin) }; diff --git a/src/qml/qml/qqmlextensionplugin_p.h b/src/qml/qml/qqmlextensionplugin_p.h new file mode 100644 index 0000000000..747bc78efb --- /dev/null +++ b/src/qml/qml/qqmlextensionplugin_p.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLEXTENSIONPLUGIN_P_H +#define QQMLEXTENSIONPLUGIN_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include "qqmlextensionplugin.h" + +QT_BEGIN_NAMESPACE + +class QQmlExtensionPluginPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QQmlExtensionPlugin) + +public: + static QQmlExtensionPluginPrivate* get(QQmlExtensionPlugin *e) { return e->d_func(); } + + QUrl baseUrl; + +}; + +QT_END_NAMESPACE + +#endif // QQMLEXTENSIONPLUGIN_P_H diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp index 95b59f93f4..5374f2d1ad 100644 --- a/src/qml/qml/qqmlimport.cpp +++ b/src/qml/qml/qqmlimport.cpp @@ -49,6 +49,8 @@ #include #include #include +#include +#include #include #include #include @@ -1683,6 +1685,10 @@ bool QQmlImportDatabase::importPlugin(const QString &filePath, const QString &ur QQmlMetaType::setTypeRegistrationNamespace(typeNamespace); + if (QQmlExtensionPlugin *plugin = qobject_cast(instance)) { + // Set the directory, not the library file itself + QQmlExtensionPluginPrivate::get(plugin)->baseUrl = QUrl::fromLocalFile(fileInfo.absolutePath()); + } iface->registerTypes(moduleId); registrationFailures = QQmlMetaType::typeRegistrationFailures(); -- cgit v1.2.3 From aef7dad91640bb495b655012dbc7214a79195dff Mon Sep 17 00:00:00 2001 From: Gunnar Sletta Date: Fri, 15 Feb 2013 15:35:04 +0100 Subject: Improved animations in the new render loop MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To advance animations in line with vsync, we used a dedicated event from the rendering thread which we fired immediately after sync. This is a bit elaborate as we know in Gui when sync is complete and we can just animate there and then. This means we can remove all animation logic from the rendering thread, making it simpler. I also updated the syncAndRender pass so that it does not render anything if the scene graph reported no changes during the sync pass. This will prevent non-visual animations and property updates from triggering render passes which will save quite a few cycles. Change-Id: I62bb5484f0673f99abe726fca5a9b424f6b0a317 Reviewed-by: Eskil Abrahamsen Blomfeldt Reviewed-by: Samuel Rødal --- src/quick/scenegraph/qsgthreadedrenderloop.cpp | 182 ++++++++++----------- src/quick/scenegraph/qsgthreadedrenderloop_p.h | 5 +- tests/auto/quick/qquickwindow/data/focus.qml | 4 + .../quick/qquickwindow/data/ownershipRootItem.qml | 2 +- tests/auto/quick/qquickwindow/tst_qquickwindow.cpp | 74 +++++++++ 5 files changed, 168 insertions(+), 99 deletions(-) diff --git a/src/quick/scenegraph/qsgthreadedrenderloop.cpp b/src/quick/scenegraph/qsgthreadedrenderloop.cpp index b11da22268..eebcad72d7 100644 --- a/src/quick/scenegraph/qsgthreadedrenderloop.cpp +++ b/src/quick/scenegraph/qsgthreadedrenderloop.cpp @@ -103,10 +103,12 @@ QT_BEGIN_NAMESPACE #endif #if defined (QSG_RENDER_LOOP_DEBUG_FULL) -# define RLDEBUG1(x) qDebug("%s : %4d - %s", __FILE__, __LINE__, x); -# define RLDEBUG(x) qDebug("%s : %4d - %s", __FILE__, __LINE__, x); +QElapsedTimer qsgrl_timer; +# define RLDEBUG1(x) qDebug("(%6d) %s : %4d - %s", (int) qsgrl_timer.elapsed(), __FILE__, __LINE__, x); +# define RLDEBUG(x) qDebug("(%6d) %s : %4d - %s", (int) qsgrl_timer.elapsed(), __FILE__, __LINE__, x); #elif defined (QSG_RENDER_LOOP_DEBUG_BASIC) -# define RLDEBUG1(x) qDebug("%s : %4d - %s", __FILE__, __LINE__, x); +QElapsedTimer qsgrl_timer; +# define RLDEBUG1(x) qDebug("(%6d) %s : %4d - %s", (int) qsgrl_timer.elapsed(), __FILE__, __LINE__, x); # define RLDEBUG(x) #else # define RLDEBUG1(x) @@ -182,15 +184,6 @@ const QEvent::Type WM_UpdateLater = QEvent::Type(QEvent::User + 8); // called. const QEvent::Type WM_Grab = QEvent::Type(QEvent::User + 9); -// Passed by the RT to the RL to trigger animations to be advanced. -const QEvent::Type WM_AdvanceAnimations = QEvent::Type(QEvent::User + 10); - -// Passed by the RT to the RL when animations start -const QEvent::Type WM_AnimationsStarted = QEvent::Type(QEvent::User + 11); - -// Passed by the RT to the RL when animations stop -const QEvent::Type WM_AnimationsStopped = QEvent::Type(QEvent::User + 12); - template T *windowFor(const QList list, QQuickWindow *window) { for (int i=0; imoveToThread(this); + vsyncDelta = QGuiApplication::primaryScreen()->refreshRate(); } @@ -330,16 +322,9 @@ public: void postEvent(QEvent *e); public slots: - void animationStarted() { - RLDEBUG(" Render: animationStarted()"); - animationRunning = true; - if (sleeping) - stopEventProcessing = true; - } - - void animationStopped() { - RLDEBUG(" Render: animationStopped()"); - animationRunning = false; + void sceneGraphChanged() { + RLDEBUG(" Render: sceneGraphChanged()"); + syncResultedInChanges = true; } public: @@ -356,13 +341,12 @@ public: uint pendingUpdate : 2; uint sleeping : 1; - uint animationRunning : 1; + uint syncResultedInChanges : 1; volatile bool guiIsLocked; volatile bool shouldExit; - volatile bool allowMainThreadProcessing; - volatile int animationRequestsPending; + float vsyncDelta; QMutex mutex; QWaitCondition waitCondition; @@ -472,12 +456,10 @@ bool QSGRenderThread::event(QEvent *e) return true; } - case WM_AnimationsStarted: - animationStarted(); - break; - - case WM_AnimationsStopped: - animationStopped(); + case WM_RequestRepaint: + // When GUI posts this event, it is followed by a polishAndSync, so we mustn't + // exit the event loop yet. + pendingUpdate |= RepaintRequest; break; default: @@ -590,7 +572,13 @@ void QSGRenderThread::sync() } gl->makeCurrent(w.window); QQuickWindowPrivate *d = QQuickWindowPrivate::get(w.window); + bool hadRenderer = d->renderer != 0; d->syncSceneGraph(); + if (!hadRenderer && d->renderer) { + RLDEBUG(" Render: - renderer was created, hooking up changed signal"); + syncResultedInChanges = true; + connect(d->renderer, SIGNAL(sceneGraphChanged()), this, SLOT(sceneGraphChanged()), Qt::DirectConnection); + } } RLDEBUG(" Render: - unlocking after sync"); @@ -608,21 +596,28 @@ void QSGRenderThread::syncAndRender() if (qquick_window_timing) sinceLastTime = threadTimer.restart(); #endif + QElapsedTimer waitTimer; + waitTimer.start(); + RLDEBUG(" Render: syncAndRender()"); - // This animate request will get there after the sync - if (animationRunning && animationRequestsPending < 2) { - RLDEBUG(" Render: - posting animate to gui.."); - ++animationRequestsPending; - QCoreApplication::postEvent(wm, new QEvent(WM_AdvanceAnimations)); + syncResultedInChanges = false; - } + bool repaintRequested = pendingUpdate & RepaintRequest; if (pendingUpdate & SyncRequest) { RLDEBUG(" Render: - update pending, doing sync"); sync(); } + if (!syncResultedInChanges && !(repaintRequested)) { + RLDEBUG(" Render: - no changes, rendering aborted"); + int waitTime = vsyncDelta - (int) waitTimer.elapsed(); + if (waitTime > 0) + msleep(waitTime); + return; + } + #ifndef QSG_NO_WINDOW_TIMING if (qquick_window_timing) syncTime = threadTimer.elapsed(); @@ -668,25 +663,25 @@ void QSGRenderThread::postEvent(QEvent *e) void QSGRenderThread::processEvents() { - RLDEBUG1(" Render: processEvents()"); + RLDEBUG(" Render: processEvents()"); while (eventQueue.hasMoreEvents()) { QEvent *e = eventQueue.takeEvent(false); event(e); delete e; } - RLDEBUG1(" Render: - done with processEvents()"); + RLDEBUG(" Render: - done with processEvents()"); } void QSGRenderThread::processEventsAndWaitForMore() { - RLDEBUG1(" Render: processEventsAndWaitForMore()"); + RLDEBUG(" Render: processEventsAndWaitForMore()"); stopEventProcessing = false; while (!stopEventProcessing) { QEvent *e = eventQueue.takeEvent(true); event(e); delete e; } - RLDEBUG1(" Render: - done with processEventsAndWaitForMore()"); + RLDEBUG(" Render: - done with processEventsAndWaitForMore()"); } void QSGRenderThread::run() @@ -706,7 +701,7 @@ void QSGRenderThread::run() QCoreApplication::processEvents(); if (!shouldExit - && ((!animationRunning && pendingUpdate == 0) || m_windows.size() == 0)) { + && (pendingUpdate == 0 || m_windows.size() == 0)) { RLDEBUG(" Render: enter event loop (going to sleep)"); sleeping = true; processEventsAndWaitForMore(); @@ -723,7 +718,12 @@ void QSGRenderThread::run() QSGThreadedRenderLoop::QSGThreadedRenderLoop() : m_animation_timer(0) , m_update_timer(0) + , m_sync_triggered_update(false) { +#if defined(QSG_RENDER_LOOP_DEBUG_BASIC) || defined (QSG_RENDER_LOOP_DEBUG_FULL) + qsgrl_timer.start(); +#endif + m_thread = new QSGRenderThread(this); m_thread->moveToThread(m_thread); @@ -738,6 +738,14 @@ QSGThreadedRenderLoop::QSGThreadedRenderLoop() RLDEBUG1("GUI: QSGThreadedRenderLoop() created"); } +void QSGThreadedRenderLoop::maybePostPolishRequest() +{ + if (m_update_timer == 0) { + RLDEBUG("GUI: - posting update"); + m_update_timer = startTimer(m_exhaust_delay, Qt::PreciseTimer); + } +} + QAnimationDriver *QSGThreadedRenderLoop::animationDriver() const { return m_animation_driver; @@ -763,7 +771,7 @@ void QSGThreadedRenderLoop::animationStarted() RLDEBUG("GUI: animationStarted()"); if (!anyoneShowing() && m_animation_timer == 0) m_animation_timer = startTimer(qsgrl_animation_interval()); - m_thread->postEvent(new QEvent(WM_AnimationsStarted)); + maybePostPolishRequest(); } void QSGThreadedRenderLoop::animationStopped() @@ -773,7 +781,6 @@ void QSGThreadedRenderLoop::animationStopped() killTimer(m_animation_timer); m_animation_timer = 0; } - m_thread->postEvent(new QEvent(WM_AnimationsStopped)); } @@ -790,7 +797,6 @@ void QSGThreadedRenderLoop::show(QQuickWindow *window) Window win; win.window = window; - win.pendingUpdate = false; m_windows << win; } @@ -869,7 +875,6 @@ void QSGThreadedRenderLoop::handleExposure(QQuickWindow *window) // Start render thread if it is not running if (!m_thread->isRunning()) { m_thread->shouldExit = false; - m_thread->animationRunning = m_animation_driver->isRunning(); RLDEBUG1("GUI: - starting render thread..."); m_thread->start(); @@ -919,27 +924,19 @@ void QSGThreadedRenderLoop::maybeUpdate(QQuickWindow *window) RLDEBUG("GUI: maybeUpdate..."); Window *w = windowFor(m_windows, window); - if (!w || w->pendingUpdate || !m_thread->isRunning()) { + if (!w || !m_thread->isRunning()) { return; } // Call this function from the Gui thread later as startTimer cannot be // called from the render thread. if (QThread::currentThread() == m_thread) { - RLDEBUG("GUI: - on render thread, posting update later"); - QCoreApplication::postEvent(this, new WMWindowEvent(window, WM_UpdateLater)); + RLDEBUG("GUI: - on render thread, will update later.."); + m_sync_triggered_update = true; return; } - - w->pendingUpdate = true; - - if (m_update_timer > 0) { - return; - } - - RLDEBUG("GUI: - posting update"); - m_update_timer = startTimer(m_animation_driver->isRunning() ? m_exhaust_delay : 0, Qt::PreciseTimer); + maybePostPolishRequest(); } /*! @@ -956,6 +953,7 @@ void QSGThreadedRenderLoop::update(QQuickWindow *window) } RLDEBUG("Gui: update called"); + m_thread->postEvent(new QEvent(WM_RequestRepaint)); maybeUpdate(window); } @@ -985,6 +983,8 @@ void QSGThreadedRenderLoop::polishAndSync() if (!anyoneShowing()) return; + RLDEBUG("GUI: polishAndSync()"); + #ifndef QSG_NO_WINDOW_TIMING QElapsedTimer timer; int polishTime = 0; @@ -992,7 +992,7 @@ void QSGThreadedRenderLoop::polishAndSync() if (qquick_window_timing) timer.start(); #endif - RLDEBUG("GUI: polishAndSync()"); + // Polish as the last thing we do before we allow the sync to take place for (int i=0; imutex.lock(); m_thread->guiIsLocked = true; - QEvent *event = new QEvent(WM_RequestSync); + m_thread->postEvent(new QEvent(WM_RequestSync)); - m_thread->postEvent(event); RLDEBUG("GUI: - wait for sync..."); #ifndef QSG_NO_WINDOW_TIMING if (qquick_window_timing) @@ -1025,9 +1021,27 @@ void QSGThreadedRenderLoop::polishAndSync() m_thread->mutex.unlock(); RLDEBUG("GUI: - unlocked after sync..."); +#ifndef QSG_NO_WINDOW_TIMING + int syncTime = timer.elapsed(); +#endif + + killTimer(m_update_timer); + m_update_timer = 0; + + if (m_animation_driver->isRunning()) { + RLDEBUG("GUI: - animations advancing"); + m_animation_driver->advance(); + RLDEBUG("GUI: - animations done"); + + // We need to trigger another sync to keep animations running... + maybePostPolishRequest(); + } else if (m_sync_triggered_update) { + maybePostPolishRequest(); + } + #ifndef QSG_NO_WINDOW_TIMING if (qquick_window_timing) - qDebug(" - polish=%d, wait=%d, sync=%d", polishTime, waitTime - polishTime, int(timer.elapsed() - waitTime)); + qDebug(" - polish=%d, wait=%d, sync=%d -- animations=%d", polishTime, waitTime - polishTime, syncTime - waitTime, int(timer.elapsed() - syncTime)); #endif } @@ -1037,40 +1051,14 @@ bool QSGThreadedRenderLoop::event(QEvent *e) case QEvent::Timer: if (static_cast(e)->timerId() == m_animation_timer) { - RLDEBUG("Gui: QEvent::Timer -> non-visual animation"); + RLDEBUG("GUI: QEvent::Timer -> non-visual animation"); m_animation_driver->advance(); } else if (static_cast(e)->timerId() == m_update_timer) { - RLDEBUG("Gui: QEvent::Timer -> polishAndSync()"); - killTimer(m_update_timer); - m_update_timer = 0; + RLDEBUG("GUI: QEvent::Timer -> Polish & Sync"); polishAndSync(); } return true; - case WM_UpdateLater: { - QQuickWindow *window = static_cast(e)->window; - // The window might have gone away... - if (windowFor(m_windows, window)) - maybeUpdate(window); - return true; } - - case WM_AdvanceAnimations: - --m_thread->animationRequestsPending; - RLDEBUG("GUI: WM_AdvanceAnimations"); - if (m_animation_driver->isRunning()) { -#ifdef QQUICK_CANVAS_TIMING - QElapsedTimer timer; - timer.start(); -#endif - m_animation_driver->advance(); - RLDEBUG("GUI: - animations advanced.."); -#ifdef QQUICK_CANVAS_TIMING - if (qquick_canvas_timing) - qDebug(" - animation: %d", (int) timer.elapsed()); -#endif - } - return true; - default: break; } diff --git a/src/quick/scenegraph/qsgthreadedrenderloop_p.h b/src/quick/scenegraph/qsgthreadedrenderloop_p.h index 4c297f500a..63b2b442e6 100644 --- a/src/quick/scenegraph/qsgthreadedrenderloop_p.h +++ b/src/quick/scenegraph/qsgthreadedrenderloop_p.h @@ -96,13 +96,14 @@ private: bool anyoneShowing(); void initialize(); + void maybePostPolishRequest(); + void waitForReleaseComplete(); void polishAndSync(); struct Window { QQuickWindow *window; - uint pendingUpdate : 1; }; QSGRenderThread *m_thread; @@ -112,6 +113,8 @@ private: int m_animation_timer; int m_update_timer; int m_exhaust_delay; + + bool m_sync_triggered_update; }; diff --git a/tests/auto/quick/qquickwindow/data/focus.qml b/tests/auto/quick/qquickwindow/data/focus.qml index 901f2fcf2e..899b999cdc 100644 --- a/tests/auto/quick/qquickwindow/data/focus.qml +++ b/tests/auto/quick/qquickwindow/data/focus.qml @@ -2,6 +2,10 @@ import QtQuick 2.0 import QtQuick.Window 2.0 as Window Window.Window { + + width: 400 + height: 300 + Item { objectName: "item1" } diff --git a/tests/auto/quick/qquickwindow/data/ownershipRootItem.qml b/tests/auto/quick/qquickwindow/data/ownershipRootItem.qml index 955304e317..03400ba673 100644 --- a/tests/auto/quick/qquickwindow/data/ownershipRootItem.qml +++ b/tests/auto/quick/qquickwindow/data/ownershipRootItem.qml @@ -10,6 +10,6 @@ Window.Window { RootItemAccessor { id:accessor objectName:"accessor" - Component.onCompleted:accessor.rootItem(); + Component.onCompleted:accessor.contentItem(); } } diff --git a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp index 3e907a5204..858653a613 100644 --- a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp +++ b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp @@ -284,8 +284,11 @@ private slots: void constantUpdates(); + void constantUpdatesOnWindow_data(); + void constantUpdatesOnWindow(); void mouseFiltering(); void headless(); + void noUpdateWhenNothingChanges(); void touchEvent_basic(); void touchEvent_propagation(); @@ -336,6 +339,55 @@ void tst_qquickwindow::constantUpdates() QTRY_VERIFY(item.iterations > 60); } +void tst_qquickwindow::constantUpdatesOnWindow_data() +{ + QTest::addColumn("blockedGui"); + QTest::addColumn("signal"); + + QQuickWindow window; + window.setGeometry(100, 100, 300, 200); + window.show(); + QTest::qWaitForWindowExposed(&window); + bool threaded = window.openglContext()->thread() != QGuiApplication::instance()->thread(); + + if (threaded) { + QTest::newRow("blocked, beforeSync") << true << QByteArray(SIGNAL(beforeSynchronizing())); + QTest::newRow("blocked, beforeRender") << true << QByteArray(SIGNAL(beforeRendering())); + QTest::newRow("blocked, afterRender") << true << QByteArray(SIGNAL(afterRendering())); + QTest::newRow("blocked, swapped") << true << QByteArray(SIGNAL(frameSwapped())); + } + QTest::newRow("unblocked, beforeSync") << false << QByteArray(SIGNAL(beforeSynchronizing())); + QTest::newRow("unblocked, beforeRender") << false << QByteArray(SIGNAL(beforeRendering())); + QTest::newRow("unblocked, afterRender") << false << QByteArray(SIGNAL(afterRendering())); + QTest::newRow("unblocked, swapped") << false << QByteArray(SIGNAL(frameSwapped())); +} + +void tst_qquickwindow::constantUpdatesOnWindow() +{ + QFETCH(bool, blockedGui); + QFETCH(QByteArray, signal); + + QQuickWindow window; + window.setGeometry(100, 100, 300, 200); + + connect(&window, signal.constData(), &window, SLOT(update()), Qt::DirectConnection); + window.show(); + QTRY_VERIFY(window.isExposed()); + + QSignalSpy catcher(&window, SIGNAL(frameSwapped())); + if (blockedGui) + QTest::qSleep(1000); + else { + window.update(); + QTest::qWait(1000); + } + window.hide(); + + // We should expect 60, but under loaded conditions we could be skipping + // frames, so don't expect too much. + QVERIFY(catcher.size() > 10); +} + void tst_qquickwindow::touchEvent_basic() { TestTouchItem::clearMousePressCounter(); @@ -991,6 +1043,28 @@ void tst_qquickwindow::headless() QCOMPARE(originalContent, newContent); } +void tst_qquickwindow::noUpdateWhenNothingChanges() +{ + QQuickWindow window; + window.setGeometry(100, 100, 300, 200); + + QQuickRectangle rect(window.contentItem()); + + window.show(); + QTRY_VERIFY(window.isExposed()); + + if (window.openglContext()->thread() == QGuiApplication::instance()->thread()) { + QSKIP("Only threaded renderloop implements this feature"); + return; + } + + QSignalSpy spy(&window, SIGNAL(frameSwapped())); + rect.update(); + QTest::qWait(500); + + QCOMPARE(spy.size(), 0); +} + void tst_qquickwindow::focusObject() { QQmlEngine engine; -- cgit v1.2.3 From b7a1d6e041d98e9fb296fb7618c5a82f5b86a442 Mon Sep 17 00:00:00 2001 From: Gunnar Sletta Date: Tue, 19 Feb 2013 10:47:27 +0100 Subject: Let rounded rectangles be antialiased by default MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This was the behavior in Qt Quick 1.1, so we should keep it Change-Id: I5c6968225e20b06f550c52741fff571088b0100b Reviewed-by: Samuel Rødal --- src/quick/items/qquickrectangle.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/quick/items/qquickrectangle.cpp b/src/quick/items/qquickrectangle.cpp index 2a8c1a3bb9..3c3bd8897b 100644 --- a/src/quick/items/qquickrectangle.cpp +++ b/src/quick/items/qquickrectangle.cpp @@ -475,7 +475,7 @@ QSGNode *QQuickRectangle::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData } rectangle->setRadius(d->radius); - rectangle->setAntialiasing(d->antialiasing); + rectangle->setAntialiasing(d->antialiasing || d->radius > 0); QGradientStops stops; if (d->gradient) { -- cgit v1.2.3 From 8e949e741bab0ee084444bb176a57ad61deb0f3f Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Fri, 15 Feb 2013 18:25:47 +0100 Subject: Add QQuickTextDocument Adding QQuickTextDocument as a way to access the QTextDocument of QQuickTextEdit from C++. This makes it possible to use a QSyntaxHighlighter from C++ without exposing more than one single qml property in the TextEdit. Change-Id: If1790b591493adcb0b68aef0c8ca706e00450657 Reviewed-by: Alan Alpert Reviewed-by: Jens Bache-Wiig --- src/quick/items/items.pri | 2 + src/quick/items/qquickitemsmodule.cpp | 4 + src/quick/items/qquicktextdocument.cpp | 71 ++++++++++++++++++ src/quick/items/qquicktextdocument.h | 86 +++++++++++++++++++++ src/quick/items/qquicktextedit.cpp | 20 +++++ src/quick/items/qquicktextedit_p.h | 5 ++ src/quick/items/qquicktextedit_p_p.h | 2 + tests/auto/quick/qquicktextdocument/data/text.qml | 6 ++ .../qquicktextdocument/qquicktextdocument.pro | 15 ++++ .../qquicktextdocument/tst_qquicktextdocument.cpp | 87 ++++++++++++++++++++++ tests/auto/quick/quick.pro | 1 + 11 files changed, 299 insertions(+) create mode 100644 src/quick/items/qquicktextdocument.cpp create mode 100644 src/quick/items/qquicktextdocument.h create mode 100644 tests/auto/quick/qquicktextdocument/data/text.qml create mode 100644 tests/auto/quick/qquicktextdocument/qquicktextdocument.pro create mode 100644 tests/auto/quick/qquicktextdocument/tst_qquicktextdocument.cpp diff --git a/src/quick/items/items.pri b/src/quick/items/items.pri index 2183a2bc63..fe406ecaac 100644 --- a/src/quick/items/items.pri +++ b/src/quick/items/items.pri @@ -21,6 +21,7 @@ HEADERS += \ $$PWD/qquicktextinput_p_p.h \ $$PWD/qquicktextcontrol_p.h \ $$PWD/qquicktextcontrol_p_p.h \ + $$PWD/qquicktextdocument.h \ $$PWD/qquicktextedit_p.h \ $$PWD/qquicktextedit_p_p.h \ $$PWD/qquicktextutil_p.h \ @@ -85,6 +86,7 @@ SOURCES += \ $$PWD/qquicktextnode.cpp \ $$PWD/qquicktextinput.cpp \ $$PWD/qquicktextcontrol.cpp \ + $$PWD/qquicktextdocument.cpp \ $$PWD/qquicktextedit.cpp \ $$PWD/qquicktextutil.cpp \ $$PWD/qquickimagebase.cpp \ diff --git a/src/quick/items/qquickitemsmodule.cpp b/src/quick/items/qquickitemsmodule.cpp index 71c23111d5..5321f5e588 100644 --- a/src/quick/items/qquickitemsmodule.cpp +++ b/src/quick/items/qquickitemsmodule.cpp @@ -49,6 +49,7 @@ #include "qquicktext_p.h" #include "qquicktextinput_p.h" #include "qquicktextedit_p.h" +#include "qquicktextdocument.h" #include "qquickimage_p.h" #include "qquickborderimage_p.h" #include "qquickscalegrid_p_p.h" @@ -178,6 +179,9 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor) qRegisterMetaType("QQuickAnchorLine"); QQmlMetaType::setQQuickAnchorLineCompareFunction(compareQQuickAnchorLines); + qmlRegisterType(); + + qmlRegisterUncreatableType(uri,major,minor,"KeyNavigation",QQuickKeyNavigationAttached::tr("KeyNavigation is only available via attached properties")); qmlRegisterUncreatableType(uri,major,minor,"Keys",QQuickKeysAttached::tr("Keys is only available via attached properties")); qmlRegisterUncreatableType(uri,major,minor,"LayoutMirroring", QQuickLayoutMirroringAttached::tr("LayoutMirroring is only available via attached properties")); diff --git a/src/quick/items/qquicktextdocument.cpp b/src/quick/items/qquicktextdocument.cpp new file mode 100644 index 0000000000..e29e48c5d0 --- /dev/null +++ b/src/quick/items/qquicktextdocument.cpp @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquicktextdocument.h" + +#include "qquicktextedit_p.h" +#include "qquicktextedit_p_p.h" +#include "qquicktext_p_p.h" + +QT_BEGIN_NAMESPACE + +class QQuickTextDocumentPrivate : public QObjectPrivate +{ +public: + QPointer document; +}; + +QQuickTextDocument::QQuickTextDocument(QQuickItem *parent) + : QObject(*(new QQuickTextDocumentPrivate), parent) +{ + Q_D(QQuickTextDocument); + Q_ASSERT(parent); + Q_ASSERT(qobject_cast(parent)); + d->document = QPointer(qobject_cast(parent)->d_func()->document); +} + +QTextDocument* QQuickTextDocument::textDocument() const +{ + Q_D(const QQuickTextDocument); + return d->document.data(); +} + +QT_END_NAMESPACE diff --git a/src/quick/items/qquicktextdocument.h b/src/quick/items/qquicktextdocument.h new file mode 100644 index 0000000000..25d3bbeaf0 --- /dev/null +++ b/src/quick/items/qquicktextdocument.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKTEXTDOCUMENT_H +#define QQUICKTEXTDOCUMENT_H + +#include +#include + +QT_BEGIN_NAMESPACE + +/*! + \class QQuickTextDocument + \since 5.1 + \brief The QQuickTextDocument class provides access to the QTextDocument of QQuickTextEdit + \inmodule QtQuick + + This class provides access to the QTextDocument of QQuickTextEdit elements. + This is provided to allow usage of the \l{Rich Text Processing} functionalities of Qt. + You are not allowed to modify the document, but it can be used to output content, for example with \l{QTextDocumentWriter}), + or provide additional formatting, for example with \l{QSyntaxHighlighter}. + + The class has to be used from C++ directly, using the property of the \l TextEdit. + + Warning: The QTextDocument provided is used internally by QtQuick elements to provide text manipulation primitives. + You are not allowed to perform any modification of the internal state of the QTextDocument. If you do, the element + in question may stop functioning or crash. +*/ + +class QQuickTextDocumentPrivate; +class Q_QUICK_EXPORT QQuickTextDocument : public QObject +{ + Q_OBJECT + +public: + QQuickTextDocument(QQuickItem *parent); + QTextDocument *textDocument() const; + +private: + Q_DISABLE_COPY(QQuickTextDocument) + Q_DECLARE_PRIVATE(QQuickTextDocument) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickTextDocument) + +#endif diff --git a/src/quick/items/qquicktextedit.cpp b/src/quick/items/qquicktextedit.cpp index d543b6bd41..e051b5202c 100644 --- a/src/quick/items/qquicktextedit.cpp +++ b/src/quick/items/qquicktextedit.cpp @@ -61,6 +61,7 @@ #include #include +#include "qquicktextdocument.h" QT_BEGIN_NAMESPACE @@ -2179,4 +2180,23 @@ void QQuickTextEdit::remove(int start, int end) d->control->updateCursorRectangle(false); } +/*! + \qmlproperty TextDocument QtQuick2::TextEdit::textDocument + \since 5.1 + + Returns the QQuickTextDocument of this TextEdit. + It can be used to implement syntax highlighting using + \l QSyntaxHighlighter. + + \sa QQuickTextDocument +*/ + +QQuickTextDocument *QQuickTextEdit::textDocument() +{ + Q_D(QQuickTextEdit); + if (!d->quickDocument) + d->quickDocument = new QQuickTextDocument(this); + return d->quickDocument; +} + QT_END_NAMESPACE diff --git a/src/quick/items/qquicktextedit_p.h b/src/quick/items/qquicktextedit_p.h index 0538270cd4..744a7e290d 100644 --- a/src/quick/items/qquicktextedit_p.h +++ b/src/quick/items/qquicktextedit_p.h @@ -48,6 +48,7 @@ QT_BEGIN_NAMESPACE +class QQuickTextDocument; class QQuickTextEditPrivate; class Q_QUICK_PRIVATE_EXPORT QQuickTextEdit : public QQuickImplicitSizeItem { @@ -99,6 +100,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickTextEdit : public QQuickImplicitSizeItem #endif Q_PROPERTY(QUrl baseUrl READ baseUrl WRITE setBaseUrl RESET resetBaseUrl NOTIFY baseUrlChanged) Q_PROPERTY(RenderType renderType READ renderType WRITE setRenderType NOTIFY renderTypeChanged) + Q_PROPERTY(QQuickTextDocument *textDocument READ textDocument FINAL) public: QQuickTextEdit(QQuickItem *parent=0); @@ -247,6 +249,8 @@ public: Q_INVOKABLE QString getText(int start, int end) const; Q_INVOKABLE QString getFormattedText(int start, int end) const; + QQuickTextDocument *textDocument(); + Q_SIGNALS: void textChanged(); void contentSizeChanged(); @@ -339,6 +343,7 @@ protected: QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *updatePaintNodeData); friend class QQuickTextUtil; + friend class QQuickTextDocument; private: Q_DISABLE_COPY(QQuickTextEdit) diff --git a/src/quick/items/qquicktextedit_p_p.h b/src/quick/items/qquicktextedit_p_p.h index 597874d864..e4819e4d2d 100644 --- a/src/quick/items/qquicktextedit_p_p.h +++ b/src/quick/items/qquicktextedit_p_p.h @@ -73,6 +73,7 @@ public: QQuickTextEditPrivate() : color(QRgb(0xFF000000)), selectionColor(QRgb(0xFF000080)), selectedTextColor(QRgb(0xFFFFFFFF)) , textMargin(0.0), xoff(0), yoff(0), font(sourceFont), cursorComponent(0), cursorItem(0), document(0), control(0) + , quickDocument(0) , lastSelectionStart(0), lastSelectionEnd(0), lineCount(0) , hAlign(QQuickTextEdit::AlignLeft), vAlign(QQuickTextEdit::AlignTop) , format(QQuickTextEdit::PlainText), wrapMode(QQuickTextEdit::NoWrap) @@ -124,6 +125,7 @@ public: QQuickItem* cursorItem; QQuickTextDocumentWithImageResources *document; QQuickTextControl *control; + QQuickTextDocument *quickDocument; int lastSelectionStart; int lastSelectionEnd; diff --git a/tests/auto/quick/qquicktextdocument/data/text.qml b/tests/auto/quick/qquicktextdocument/data/text.qml new file mode 100644 index 0000000000..43a8c6bb82 --- /dev/null +++ b/tests/auto/quick/qquicktextdocument/data/text.qml @@ -0,0 +1,6 @@ +import QtQuick 2.1 + +TextEdit { + text: "" +} + diff --git a/tests/auto/quick/qquicktextdocument/qquicktextdocument.pro b/tests/auto/quick/qquicktextdocument/qquicktextdocument.pro new file mode 100644 index 0000000000..e6bfdbd099 --- /dev/null +++ b/tests/auto/quick/qquicktextdocument/qquicktextdocument.pro @@ -0,0 +1,15 @@ +CONFIG += testcase +TARGET = tst_qquicktextdocument +macx:CONFIG -= app_bundle + +SOURCES += tst_qquicktextdocument.cpp + +include (../../shared/util.pri) + +TESTDATA = data/* + +CONFIG += parallel_test + +QT += core-private gui-private qml-private quick-private testlib +DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 + diff --git a/tests/auto/quick/qquicktextdocument/tst_qquicktextdocument.cpp b/tests/auto/quick/qquicktextdocument/tst_qquicktextdocument.cpp new file mode 100644 index 0000000000..717496cf0e --- /dev/null +++ b/tests/auto/quick/qquicktextdocument/tst_qquicktextdocument.cpp @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../../shared/util.h" + +class tst_qquicktextdocument : public QQmlDataTest +{ + Q_OBJECT +private slots: + void textDocumentWriter(); +}; + +QString text = QStringLiteral("foo bar"); + +void tst_qquicktextdocument::textDocumentWriter() +{ + QQmlEngine e; + QQmlComponent c(&e, testFileUrl("text.qml")); + QObject* o = c.create(); + QVERIFY(o); + QQuickTextEdit *edit = qobject_cast(o); + QVERIFY(edit); + + QQuickTextDocument* quickDocument = qobject_cast(edit->property("textDocument").value()); + QVERIFY(quickDocument->textDocument() != 0); + + QBuffer output; + output.open(QBuffer::ReadWrite); + QVERIFY(output.buffer().isEmpty()); + + edit->setProperty("text", QVariant(text)); + QTextDocumentWriter writer(&output, "plaintext"); + QVERIFY(writer.write(quickDocument->textDocument())); + QCOMPARE(output.buffer(), text.toLatin1()); + delete o; +} + +QTEST_MAIN(tst_qquicktextdocument) + +#include "tst_qquicktextdocument.moc" diff --git a/tests/auto/quick/quick.pro b/tests/auto/quick/quick.pro index 3ed6ca8c99..8ce17a10d0 100644 --- a/tests/auto/quick/quick.pro +++ b/tests/auto/quick/quick.pro @@ -60,6 +60,7 @@ QUICKTESTS = \ qquickshadereffect \ qquickspritesequence \ qquicktext \ + qquicktextdocument \ qquicktextedit \ qquicktextinput \ qquickvisualdatamodel \ -- cgit v1.2.3 From bbc9008f077bccd811483608d9a436cce3e923f5 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Mon, 18 Feb 2013 16:48:24 +0100 Subject: QQuickScreen is exposed as an attached property on Window too Change-Id: I13ee75247cbe6238de1b58cf0005c39fc4314e96 Reviewed-by: Alan Alpert --- src/quick/items/qquickscreen.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/quick/items/qquickscreen.cpp b/src/quick/items/qquickscreen.cpp index 7c7d8a6eab..3ab93e6d0f 100644 --- a/src/quick/items/qquickscreen.cpp +++ b/src/quick/items/qquickscreen.cpp @@ -112,6 +112,10 @@ QQuickScreenAttached::QQuickScreenAttached(QObject* attachee) if (m_attachee->window()) //It might not be assigned to a window yet windowChanged(m_attachee->window()); + } else { + QQuickWindow *window = qobject_cast(attachee); + if (window) + windowChanged(window); } } -- cgit v1.2.3 From 8c8404f8153508106f65efb43110ba85c05e73bc Mon Sep 17 00:00:00 2001 From: Gunnar Sletta Date: Wed, 20 Feb 2013 14:21:53 +0100 Subject: Don't print leak warnings on exit by default Task-number: QTBUG-29780 Change-Id: I6f35253dbec6346af239c0ab341caad9f4f9b862 Reviewed-by: Jens Bache-Wiig --- src/quick/items/qquickitem.cpp | 24 ++++++++++++++++-------- src/quick/scenegraph/coreapi/qsgmaterial.cpp | 24 ++++++++++++++++-------- src/quick/scenegraph/coreapi/qsgnode.cpp | 21 +++++++++++++-------- src/quick/scenegraph/util/qsgtexture.cpp | 12 +++++++++--- 4 files changed, 54 insertions(+), 27 deletions(-) diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index 26af6953e1..ad8a9d90af 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -78,6 +78,10 @@ QT_BEGIN_NAMESPACE +#ifndef QT_NO_DEBUG +static bool qsg_leak_check = !qgetenv("QML_LEAK_CHECK").isEmpty(); +#endif + #ifdef FOCUS_DEBUG void printFocusTree(QQuickItem *item, QQuickItem *scope = 0, int depth = 1); void printFocusTree(QQuickItem *item, QQuickItem *scope, int depth) @@ -1965,9 +1969,11 @@ static void qt_print_item_count() QQuickItem::~QQuickItem() { #ifndef QT_NO_DEBUG - --qt_item_count; - if (qt_item_count < 0) - qDebug("Item destroyed after qt_print_item_count() was called."); + if (qsg_leak_check) { + --qt_item_count; + if (qt_item_count < 0) + qDebug("Item destroyed after qt_print_item_count() was called."); + } #endif Q_D(QQuickItem); @@ -2546,11 +2552,13 @@ QQuickItemPrivate::~QQuickItemPrivate() void QQuickItemPrivate::init(QQuickItem *parent) { #ifndef QT_NO_DEBUG - ++qt_item_count; - static bool atexit_registered = false; - if (!atexit_registered) { - atexit(qt_print_item_count); - atexit_registered = true; + if (qsg_leak_check) { + ++qt_item_count; + static bool atexit_registered = false; + if (!atexit_registered) { + atexit(qt_print_item_count); + atexit_registered = true; + } } #endif diff --git a/src/quick/scenegraph/coreapi/qsgmaterial.cpp b/src/quick/scenegraph/coreapi/qsgmaterial.cpp index 0e40a01311..f678504344 100644 --- a/src/quick/scenegraph/coreapi/qsgmaterial.cpp +++ b/src/quick/scenegraph/coreapi/qsgmaterial.cpp @@ -44,6 +44,10 @@ QT_BEGIN_NAMESPACE +#ifndef QT_NO_DEBUG +static bool qsg_leak_check = !qgetenv("QML_LEAK_CHECK").isEmpty(); +#endif + /*! \group qtquick-scenegraph-materials \title Qt Quick Scene Graph Material Classes @@ -538,11 +542,13 @@ QSGMaterial::QSGMaterial() : m_flags(0) { #ifndef QT_NO_DEBUG - ++qt_material_count; - static bool atexit_registered = false; - if (!atexit_registered) { - atexit(qt_print_material_count); - atexit_registered = true; + if (qsg_leak_check) { + ++qt_material_count; + static bool atexit_registered = false; + if (!atexit_registered) { + atexit(qt_print_material_count); + atexit_registered = true; + } } #endif } @@ -555,9 +561,11 @@ QSGMaterial::QSGMaterial() QSGMaterial::~QSGMaterial() { #ifndef QT_NO_DEBUG - --qt_material_count; - if (qt_material_count < 0) - qDebug("Material destroyed after qt_print_material_count() was called."); + if (qsg_leak_check) { + --qt_material_count; + if (qt_material_count < 0) + qDebug("Material destroyed after qt_print_material_count() was called."); + } #endif } diff --git a/src/quick/scenegraph/coreapi/qsgnode.cpp b/src/quick/scenegraph/coreapi/qsgnode.cpp index 5995dc862d..6a22e0e7f9 100644 --- a/src/quick/scenegraph/coreapi/qsgnode.cpp +++ b/src/quick/scenegraph/coreapi/qsgnode.cpp @@ -49,6 +49,7 @@ QT_BEGIN_NAMESPACE #ifndef QT_NO_DEBUG +static bool qsg_leak_check = !qgetenv("QML_LEAK_CHECK").isEmpty(); static int qt_node_count = 0; static void qt_print_node_count() @@ -271,11 +272,13 @@ QSGNode::QSGNode(NodeType type) void QSGNode::init() { #ifndef QT_NO_DEBUG - ++qt_node_count; - static bool atexit_registered = false; - if (!atexit_registered) { - atexit(qt_print_node_count); - atexit_registered = true; + if (qsg_leak_check) { + ++qt_node_count; + static bool atexit_registered = false; + if (!atexit_registered) { + atexit(qt_print_node_count); + atexit_registered = true; + } } #endif } @@ -289,9 +292,11 @@ void QSGNode::init() QSGNode::~QSGNode() { #ifndef QT_NO_DEBUG - --qt_node_count; - if (qt_node_count < 0) - qDebug("Node destroyed after qt_print_node_count() was called."); + if (qsg_leak_check) { + --qt_node_count; + if (qt_node_count < 0) + qDebug("Node destroyed after qt_print_node_count() was called."); + } #endif destroy(); } diff --git a/src/quick/scenegraph/util/qsgtexture.cpp b/src/quick/scenegraph/util/qsgtexture.cpp index 87ef91b212..48927ce5e1 100644 --- a/src/quick/scenegraph/util/qsgtexture.cpp +++ b/src/quick/scenegraph/util/qsgtexture.cpp @@ -65,6 +65,10 @@ #include #endif +#ifndef QT_NO_DEBUG +static bool qsg_leak_check = !qgetenv("QML_LEAK_CHECK").isEmpty(); +#endif + #ifndef QSG_NO_RENDERER_TIMING static bool qsg_render_timing = !qgetenv("QML_RENDERER_TIMING").isEmpty(); static QElapsedTimer qsg_renderer_timer; @@ -182,7 +186,7 @@ static void qt_debug_remove_texture(QSGTexture* texture) --qt_debug_texture_count; if (qt_debug_texture_count < 0) - qDebug("Material destroyed after qt_debug_print_texture_count() was called."); + qDebug("Texture destroyed after qt_debug_print_texture_count() was called."); } #endif // QT_NO_DEBUG @@ -269,7 +273,8 @@ QSGTexture::QSGTexture() : QObject(*(new QSGTexturePrivate)) { #ifndef QT_NO_DEBUG - qt_debug_add_texture(this); + if (qsg_leak_check) + qt_debug_add_texture(this); #endif } @@ -279,7 +284,8 @@ QSGTexture::QSGTexture() QSGTexture::~QSGTexture() { #ifndef QT_NO_DEBUG - qt_debug_remove_texture(this); + if (qsg_leak_check) + qt_debug_remove_texture(this); #endif } -- cgit v1.2.3 From cf53bf9ae6716613c120cc4a0202c69bd59eba1e Mon Sep 17 00:00:00 2001 From: Gunnar Sletta Date: Wed, 20 Feb 2013 19:39:11 +0100 Subject: Make sure exposeEvents trigger a render pass MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We needs this on non-compositing window managers to trigger repaints on partial updates. Change-Id: Ied5f3e854173c5e00ad7e1222aeb66eb9c96158c Reviewed-by: Samuel Rødal --- src/quick/scenegraph/qsgrenderloop.cpp | 4 +++- src/quick/scenegraph/qsgthreadedrenderloop.cpp | 2 ++ tests/auto/quick/qquickwindow/tst_qquickwindow.cpp | 24 ++++++++++++++++++++++ 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/quick/scenegraph/qsgrenderloop.cpp b/src/quick/scenegraph/qsgrenderloop.cpp index 83e8d3f17a..85ef549397 100644 --- a/src/quick/scenegraph/qsgrenderloop.cpp +++ b/src/quick/scenegraph/qsgrenderloop.cpp @@ -305,8 +305,10 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window) void QSGGuiThreadRenderLoop::exposureChanged(QQuickWindow *window) { - if (window->isExposed()) + if (window->isExposed()) { + m_windows[window].updatePending = true; renderWindow(window); + } } QImage QSGGuiThreadRenderLoop::grab(QQuickWindow *window) diff --git a/src/quick/scenegraph/qsgthreadedrenderloop.cpp b/src/quick/scenegraph/qsgthreadedrenderloop.cpp index eebcad72d7..fb26f543c3 100644 --- a/src/quick/scenegraph/qsgthreadedrenderloop.cpp +++ b/src/quick/scenegraph/qsgthreadedrenderloop.cpp @@ -372,6 +372,8 @@ bool QSGRenderThread::event(QEvent *e) RLDEBUG1(" Render: WM_Expose"); WMExposeEvent *se = static_cast(e); + pendingUpdate |= RepaintRequest; + if (windowFor(m_windows, se->window)) { RLDEBUG1(" Render: - window already added..."); return true; diff --git a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp index 858653a613..b2e07e4c13 100644 --- a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp +++ b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp @@ -320,6 +320,8 @@ private slots: void showHideAnimate(); + void testExpose(); + #ifndef QT_NO_CURSOR void cursor(); #endif @@ -1375,6 +1377,28 @@ void tst_qquickwindow::showHideAnimate() QTRY_VERIFY(created->opacity() < 0.5); } +void tst_qquickwindow::testExpose() +{ + QQuickWindow window; + window.setGeometry(100, 100, 300, 200); + + window.show(); + QTRY_VERIFY(window.isExposed()); + + QSignalSpy swapSpy(&window, SIGNAL(frameSwapped())); + + // exhaust pending exposes, as some platforms send us plenty + // while showing the first time + QTest::qWait(1000); + while (swapSpy.size() != 0) { + swapSpy.clear(); + QTest::qWait(100); + } + + QWindowSystemInterface::handleExposeEvent(&window, QRegion(10, 10, 20, 20)); + QTRY_COMPARE(swapSpy.size(), 1); +} + QTEST_MAIN(tst_qquickwindow) #include "tst_qquickwindow.moc" -- cgit v1.2.3 From 33334c2ac36b2706df25dca6d2055b87088cdb7d Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Wed, 20 Feb 2013 15:24:50 +0100 Subject: Improved documentation in the Screen attached property It can attach to the Window now too, so needs some clarification about when to use it that way. The orientation properties also need more description to avoid confusion. Change-Id: Ib537422f6001d8b64de176e60a6433b2e25551b9 Reviewed-by: Gabriel de Dietrich --- src/quick/items/qquickscreen.cpp | 46 ++++++++++++++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 9 deletions(-) diff --git a/src/quick/items/qquickscreen.cpp b/src/quick/items/qquickscreen.cpp index 3ab93e6d0f..33a831acad 100644 --- a/src/quick/items/qquickscreen.cpp +++ b/src/quick/items/qquickscreen.cpp @@ -54,20 +54,29 @@ QT_BEGIN_NAMESPACE \instantiates QQuickScreenAttached \inqmlmodule QtQuick.Window 2 \ingroup qtquick-visual-utility - \brief The Screen attached object provides information about the Screen an Item is displayed on. + \brief The Screen attached object provides information about the Screen an Item or Window is displayed on. - The Screen attached object is only valid inside Item or Item derived types, after component completion. - Inside these items it refers to the screen that the item is currently being displayed on. + The Screen attached object is valid inside Item or Item derived types, + after component completion. Inside these items it refers to the screen that + the item is currently being displayed on. + + The attached object is also valid inside Window or Window derived types, + after component completion. In that case it refers to the screen where the + Window was created. It is generally better to access the Screen from the + relevant Item instead, because on a multi-screen desktop computer, the user + can drag a Window into a position where it spans across multiple screens. + In that case some Items will be on one screen, and others on a different + screen. To use this type, you will need to import the module with the following line: \code import QtQuick.Window 2.0 \endcode + It is a separate import in order to allow you to have a QML environment + without access to window system features. - Note that the Screen type is not valid at Component.onCompleted, because the Item has not been displayed on - a screen by this time. - - Restricting this import will allow you to have a QML environment without access to window system features. + Note that the Screen type is not valid at Component.onCompleted, because + the Item or Window has not been displayed on a screen by this time. */ /*! @@ -86,13 +95,32 @@ QT_BEGIN_NAMESPACE \qmlattachedproperty Qt::ScreenOrientation QtQuick.Window2::Screen::primaryOrientation \readonly - This contains the primary orientation of the screen. + This contains the primary orientation of the screen. If the + screen's height is greater than its width, then the orientation is + Qt.PortraitOrientation; otherwise it is Qt.LandscapeOrientation. + + If you are designing an application which changes its layout depending on + device orientation, you probably want to use primaryOrientation to + determine the layout. That is because on a desktop computer, you can expect + primaryOrientation to change when the user rotates the screen via the + operating system's control panel, even if the computer does not contain an + accelerometer. Likewise on most handheld computers which do have + accelerometers, the operating system will rotate the whole screen + automatically, so again you will see the primaryOrientation change. */ /*! \qmlattachedproperty Qt::ScreenOrientation QtQuick.Window2::Screen::orientation \readonly - This contains the current orientation of the screen. + This contains the current orientation of the screen, from the accelerometer + (if any). On a desktop computer, this value typically does not change. + + If primaryOrientation == orientation, it means that the screen + automatically rotates all content which is displayed, depending on how you + hold it. But if orientation changes while primaryOrientation does NOT + change, then probably you are using a device which does not rotate its own + display. In that case you may need to use \l Item.rotation or + \l Item.transform to rotate your content. */ /*! \qmlattachedmethod int QtQuick.Window2::Screen::angleBetween(Qt::ScreenOrientation a, Qt::ScreenOrientation b) -- cgit v1.2.3 From f289fdc53e85bf0f2002070fa883f5c01bae1232 Mon Sep 17 00:00:00 2001 From: Jens Bache-Wiig Date: Sun, 24 Feb 2013 15:47:38 +0100 Subject: Various improvements to Canvas examples MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I noticed these examples were rather "aestetically challenged" and decided to make it a test case for our WIP style guidelines. - Use consistent margins (12 px) - Use proposed color palettes - Use proposed fonts and header styles I created a new Slider graphic and moved this into the shared folder as I think it is useful for other examples. I removed a lot of unused files which seem to have been added but never actually used. I also found several bugs in our implementation, including not scaling or rotation around the correct origin. In many cases I simplified the examples, removing variables/sliders where they did not add significantly to the example itself. Change-Id: Ie09da33deaf56a3ec45a2031b87a24a8602e994a Reviewed-by: Samuel Rødal Reviewed-by: Jerome Pasion Reviewed-by: Jens Bache-Wiig --- examples/quick/canvas/bezierCurve/bezierCurve.qml | 146 ++++++++------- examples/quick/canvas/canvas.qrc | 17 -- examples/quick/canvas/clip/clip.qml | 182 +++++++++--------- examples/quick/canvas/contents/Button.qml | 87 --------- examples/quick/canvas/contents/ScrollBar.qml | 74 -------- examples/quick/canvas/contents/Slider.qml | 115 ------------ examples/quick/canvas/contents/TitleBar.qml | 70 ------- examples/quick/canvas/contents/ToolBar.qml | 69 ------- .../canvas/contents/images/button-pressed.png | Bin 571 -> 0 bytes examples/quick/canvas/contents/images/button.png | Bin 564 -> 0 bytes examples/quick/canvas/contents/images/default.svg | 82 -------- examples/quick/canvas/contents/images/gloss.png | Bin 1236 -> 0 bytes examples/quick/canvas/contents/images/lineedit.png | Bin 1415 -> 0 bytes examples/quick/canvas/contents/images/lineedit.sci | 5 - examples/quick/canvas/contents/images/quit.png | Bin 2369 -> 0 bytes examples/quick/canvas/contents/images/stripes.png | Bin 257 -> 0 bytes examples/quick/canvas/contents/images/titlebar.png | Bin 1436 -> 0 bytes examples/quick/canvas/contents/images/titlebar.sci | 5 - .../quick/canvas/contents/images/toolbutton.png | Bin 2550 -> 0 bytes .../quick/canvas/contents/images/toolbutton.sci | 5 - .../canvas/quadraticCurveTo/quadraticCurveTo.qml | 158 ++++++++-------- examples/quick/canvas/roundedrect/roundedrect.qml | 142 +++++++------- examples/quick/canvas/smile/smile.qml | 149 +++++++-------- examples/quick/canvas/squircle/squircle.qml | 207 ++++++++++----------- examples/quick/canvas/tiger/tiger.qml | 146 ++++++++------- examples/quick/shared/Slider.qml | 117 ++++++++++++ examples/quick/shared/images/slider_handle.png | Bin 0 -> 887 bytes examples/quick/shared/qmldir | 1 + examples/quick/shared/shared.qrc | 2 + 29 files changed, 691 insertions(+), 1088 deletions(-) delete mode 100644 examples/quick/canvas/contents/Button.qml delete mode 100644 examples/quick/canvas/contents/ScrollBar.qml delete mode 100644 examples/quick/canvas/contents/Slider.qml delete mode 100644 examples/quick/canvas/contents/TitleBar.qml delete mode 100644 examples/quick/canvas/contents/ToolBar.qml delete mode 100644 examples/quick/canvas/contents/images/button-pressed.png delete mode 100644 examples/quick/canvas/contents/images/button.png delete mode 100644 examples/quick/canvas/contents/images/default.svg delete mode 100755 examples/quick/canvas/contents/images/gloss.png delete mode 100755 examples/quick/canvas/contents/images/lineedit.png delete mode 100644 examples/quick/canvas/contents/images/lineedit.sci delete mode 100755 examples/quick/canvas/contents/images/quit.png delete mode 100755 examples/quick/canvas/contents/images/stripes.png delete mode 100755 examples/quick/canvas/contents/images/titlebar.png delete mode 100644 examples/quick/canvas/contents/images/titlebar.sci delete mode 100755 examples/quick/canvas/contents/images/toolbutton.png delete mode 100644 examples/quick/canvas/contents/images/toolbutton.sci create mode 100644 examples/quick/shared/Slider.qml create mode 100644 examples/quick/shared/images/slider_handle.png diff --git a/examples/quick/canvas/bezierCurve/bezierCurve.qml b/examples/quick/canvas/bezierCurve/bezierCurve.qml index 386d1d73d3..281ca9399e 100644 --- a/examples/quick/canvas/bezierCurve/bezierCurve.qml +++ b/examples/quick/canvas/bezierCurve/bezierCurve.qml @@ -40,84 +40,88 @@ import QtQuick 2.0 import "../contents" +import "../../shared" + Item { - id:container - width:320 - height:480 + id:container + width: 320 + height: 480 - Column { - spacing:5 - anchors.fill:parent - Text { font.pointSize:15; text:"Bezier Curve"; anchors.horizontalCenter:parent.horizontalCenter} + Column { + spacing:5 + anchors.fill: parent + anchors.topMargin: 12 - Canvas { - id:canvas - width:320 - height:280 - property string strokeStyle:"red" - property string fillStyle:"red" - property int lineWidth:lineWidthCtrl.value - property bool fill:true - property bool stroke:true - property real alpha:alphaCtrl.value - property real scaleX : scaleXCtrl.value - property real scaleY : scaleYCtrl.value - property real rotate : rotateCtrl.value - antialiasing: true + Text { + font.pointSize: 24 + font.bold: true + text: "Bezier Curve" + anchors.horizontalCenter: parent.horizontalCenter + color: "#777" + } - Behavior on scaleX { SpringAnimation { spring: 2; damping: 0.2; loops:Animation.Infinite } } - Behavior on scaleY { SpringAnimation { spring: 2; damping: 0.2; loops:Animation.Infinite} } - Behavior on rotate { SpringAnimation { spring: 2; damping: 0.2; loops:Animation.Infinite} } + Canvas { + id:canvas + width:320 + height:280 + property color strokeStyle: Qt.darker(fillStyle, 1.4) + property color fillStyle: "#b40000" // red + property int lineWidth: lineWidthCtrl.value + property bool fill: true + property bool stroke: true + property real alpha: 1.0 + property real scale : scaleCtrl.value + property real rotate : rotateCtrl.value + antialiasing: true - onLineWidthChanged:requestPaint(); - onFillChanged:requestPaint(); - onStrokeChanged:requestPaint(); - onAlphaChanged:requestPaint(); - onScaleXChanged:requestPaint(); - onScaleYChanged:requestPaint(); - onRotateChanged:requestPaint(); + onLineWidthChanged:requestPaint(); + onFillChanged:requestPaint(); + onStrokeChanged:requestPaint(); + onScaleChanged:requestPaint(); + onRotateChanged:requestPaint(); - onPaint: { - var ctx = canvas.getContext('2d'); - ctx.save(); - ctx.clearRect(0, 0, canvas.width, canvas.height); - ctx.globalAlpha = canvas.alpha; - ctx.strokeStyle = canvas.strokeStyle; - ctx.fillStyle = canvas.fillStyle; - ctx.lineWidth = canvas.lineWidth; - ctx.scale(canvas.scaleX, canvas.scaleY); - ctx.rotate(canvas.rotate); - //! [0] - ctx.beginPath(); - ctx.moveTo(75,40); - ctx.bezierCurveTo(75,37,70,25,50,25); - ctx.bezierCurveTo(20,25,20,62.5,20,62.5); - ctx.bezierCurveTo(20,80,40,102,75,120); - ctx.bezierCurveTo(110,102,130,80,130,62.5); - ctx.bezierCurveTo(130,62.5,130,25,100,25); - ctx.bezierCurveTo(85,25,75,37,75,40); - ctx.closePath(); - //! [0] - if (canvas.fill) - ctx.fill(); - if (canvas.stroke) - ctx.stroke(); - ctx.restore(); - } - } + onPaint: { + var ctx = canvas.getContext('2d'); + var originX = 85 + var originY = 75 + ctx.save(); + ctx.clearRect(0, 0, canvas.width, canvas.height); + ctx.translate(originX, originX); + ctx.globalAlpha = canvas.alpha; + ctx.strokeStyle = canvas.strokeStyle; + ctx.fillStyle = canvas.fillStyle; + ctx.lineWidth = canvas.lineWidth; - Rectangle { - id:controls - width:320 - height:150 - Column { - spacing:3 - Slider {id:lineWidthCtrl; width:300; height:20; min:1; max:10; init:2; name:"Line width"} - Slider {id:scaleXCtrl; width:300; height:20; min:0.1; max:10; init:1; name:"ScaleX"} - Slider {id:scaleYCtrl; width:300; height:20; min:0.1; max:10; init:1; name:"ScaleY"} - Slider {id:rotateCtrl; width:300; height:20; min:0; max:Math.PI*2; init:0; name:"Rotate"} - Slider {id:alphaCtrl; width:300; height:20; min:0; max:1; init:1; name:"Alpha"} + ctx.translate(originX, originY) + ctx.scale(canvas.scale, canvas.scale); + ctx.rotate(canvas.rotate); + ctx.translate(-originX, -originY) + + //! [0] + ctx.beginPath(); + ctx.moveTo(75,40); + ctx.bezierCurveTo(75,37,70,25,50,25); + ctx.bezierCurveTo(20,25,20,62.5,20,62.5); + ctx.bezierCurveTo(20,80,40,102,75,120); + ctx.bezierCurveTo(110,102,130,80,130,62.5); + ctx.bezierCurveTo(130,62.5,130,25,100,25); + ctx.bezierCurveTo(85,25,75,37,75,40); + ctx.closePath(); + //! [0] + if (canvas.fill) + ctx.fill(); + if (canvas.stroke) + ctx.stroke(); + ctx.restore(); + } } } - } + Column { + id: controls + anchors.bottom: parent.bottom + anchors.bottomMargin: 12 + Slider {id: lineWidthCtrl; min: 1; max: 10; init: 2; name: "Outline"} + Slider {id: scaleCtrl; min: 0.1; max: 10; init: 1; name: "Scale"} + Slider {id: rotateCtrl; min: 0; max: Math.PI*2; init: 0; name: "Rotate"} + } } diff --git a/examples/quick/canvas/canvas.qrc b/examples/quick/canvas/canvas.qrc index 06d39f3624..cfdfc844b9 100644 --- a/examples/quick/canvas/canvas.qrc +++ b/examples/quick/canvas/canvas.qrc @@ -3,24 +3,7 @@ canvas.qml bezierCurve/bezierCurve.qml clip/clip.qml - contents/Button.qml contents/qt-logo.png - contents/ScrollBar.qml - contents/Slider.qml - contents/TitleBar.qml - contents/ToolBar.qml - contents/images/button-pressed.png - contents/images/button.png - contents/images/default.svg - contents/images/gloss.png - contents/images/lineedit.png - contents/images/lineedit.sci - contents/images/quit.png - contents/images/stripes.png - contents/images/titlebar.png - contents/images/titlebar.sci - contents/images/toolbutton.png - contents/images/toolbutton.sci quadraticCurveTo/quadraticCurveTo.qml roundedrect/roundedrect.qml smile/smile.qml diff --git a/examples/quick/canvas/clip/clip.qml b/examples/quick/canvas/clip/clip.qml index 1e9728ff8d..71def05d20 100644 --- a/examples/quick/canvas/clip/clip.qml +++ b/examples/quick/canvas/clip/clip.qml @@ -40,111 +40,109 @@ import QtQuick 2.0 import "../contents" +import "../../shared" + Item { - id:container - width:320 - height:480 + id:container + width: 320 + height: 480 - Column { - spacing:5 - anchors.fill:parent - Text { font.pointSize:15; text:"Makes squircle icon with clip"; anchors.horizontalCenter:parent.horizontalCenter} - Canvas { - id:canvas - width:320 - height:280 - property string strokeStyle:"blue" - property string fillStyle:"steelblue" - property int lineWidth:2 - property int nSize:nCtrl.value - property real radius:rCtrl.value - property bool fill:true - property bool stroke:false - property real px:xCtrl.value - property real py:yCtrl.value - property real alpha:alphaCtrl.value - property string imagefile:"../contents/qt-logo.png" - antialiasing: true - Component.onCompleted:loadImage(canvas.imagefile); + Column { + spacing:5 + anchors.fill: parent + anchors.topMargin: 12 + Text { + font.pointSize: 24 + font.bold: true + text: "Squircle with Clip" + anchors.horizontalCenter: parent.horizontalCenter + color: "#777" + } + Canvas { + id: canvas + width: 320 + height: 280 + property color strokeStyle: Qt.darker(fillStyle, 1.2) + property color fillStyle:"#14aaff" // green + property int lineWidth:2 + property int nSize:nCtrl.value + property real radius:rCtrl.value + property bool fill:true + property bool stroke:false + property real px: width/2 + property real py: height/2 + 20 + property real alpha: 1.0 + property string imagefile:"../contents/qt-logo.png" + antialiasing: true + Component.onCompleted:loadImage(canvas.imagefile); - onAlphaChanged:requestPaint(); - onRadiusChanged:requestPaint(); - onLineWidthChanged:requestPaint(); - onNSizeChanged:requestPaint(); - onFillChanged:requestPaint(); - onStrokeChanged:requestPaint(); - onPxChanged:requestPaint(); - onPyChanged:requestPaint(); - - onImageLoaded : requestPaint(); + onRadiusChanged:requestPaint(); + onLineWidthChanged:requestPaint(); + onNSizeChanged:requestPaint(); + onFillChanged:requestPaint(); + onStrokeChanged:requestPaint(); + onImageLoaded : requestPaint(); + onPaint: squcirle(); - onPaint: squcirle(); + function squcirle() { + var ctx = canvas.getContext("2d"); + var N = canvas.nSize; + var R = canvas.radius; - function squcirle() { - var ctx = canvas.getContext("2d"); - var N = canvas.nSize; - var R = canvas.radius; + N=Math.abs(N); + var M=N; + if (N>100) M=100; + if (N<0.00000000001) M=0.00000000001; - N=Math.abs(N); - var M=N; - if (N>100) M=100; - if (N<0.00000000001) M=0.00000000001; + ctx.save(); + ctx.globalAlpha = canvas.alpha; + ctx.fillStyle = "white"; + ctx.fillRect(0, 0, canvas.width, canvas.height); - ctx.save(); - ctx.globalAlpha =canvas.alpha; - ctx.fillStyle = "gray"; - ctx.fillRect(0, 0, canvas.width, canvas.height); + ctx.strokeStyle = canvas.strokeStyle; + ctx.fillStyle = canvas.fillStyle; + ctx.lineWidth = canvas.lineWidth; - ctx.strokeStyle = canvas.strokeStyle; - ctx.fillStyle = canvas.fillStyle; - ctx.lineWidth = canvas.lineWidth; + ctx.beginPath(); + var i = 0, x, y; + for (i=0; i<(2*R+1); i++){ + x = Math.round(i-R) + canvas.px; + y = Math.round(Math.pow(Math.abs(Math.pow(R,M)-Math.pow(Math.abs(i-R),M)),1/M)) + canvas.py; - ctx.beginPath(); - var i = 0, x, y; - for (i=0; i<(2*R+1); i++){ - x = Math.round(i-R) + canvas.px; - y = Math.round(Math.pow(Math.abs(Math.pow(R,M)-Math.pow(Math.abs(i-R),M)),1/M)) + canvas.py; + if (i == 0) + ctx.moveTo(x, y); + else + ctx.lineTo(x, y); + } - if (i == 0) - ctx.moveTo(x, y); - else - ctx.lineTo(x, y); - } + for (i=(2*R); i<(4*R+1); i++){ + x =Math.round(3*R-i)+canvas.px; + y = Math.round(-Math.pow(Math.abs(Math.pow(R,M)-Math.pow(Math.abs(3*R-i),M)),1/M)) + canvas.py; + ctx.lineTo(x, y); + } + ctx.closePath(); + if (canvas.stroke) { + ctx.stroke(); + } - for (i=(2*R); i<(4*R+1); i++){ - x =Math.round(3*R-i)+canvas.px; - y = Math.round(-Math.pow(Math.abs(Math.pow(R,M)-Math.pow(Math.abs(3*R-i),M)),1/M)) + canvas.py; - ctx.lineTo(x, y); - } - ctx.closePath(); - if (canvas.stroke) { - ctx.stroke(); - } + if (canvas.fill) { + ctx.fill(); + } - if (canvas.fill) { - ctx.fill(); - } - //! [0] - ctx.clip(); + //! [0] + ctx.clip(); + ctx.drawImage(canvas.imagefile, 0, 0); + //! [0] - ctx.drawImage(canvas.imagefile, 0, 0); - //! [0] - ctx.restore(); - } - } - - Rectangle { - id:controls - width:320 - height:150 - Column { - spacing:3 - Slider {id:nCtrl; width:300; height:20; min:1; max:10; init:4; name:"N"} - Slider {id:rCtrl; width:300; height:20; min:30; max:180; init:100; name:"Radius"} - Slider {id:xCtrl; width:300; height:20; min:50; max:300; init:180; name:"X"} - Slider {id:yCtrl; width:300; height:20; min:30; max:300; init:220; name:"Y"} - Slider {id:alphaCtrl; width:300; height:20; min:0; max:1; init:1; name:"Alpha"} + ctx.restore(); + } } } - } + Column { + id: controls + anchors.bottom: parent.bottom + anchors.bottomMargin: 12 + Slider {id: nCtrl; min: 1; max: 10; init: 4; name:"N"} + Slider {id: rCtrl; min: 30; max: 180; init: 80; name:"Radius"} + } } diff --git a/examples/quick/canvas/contents/Button.qml b/examples/quick/canvas/contents/Button.qml deleted file mode 100644 index efdf4eae76..0000000000 --- a/examples/quick/canvas/contents/Button.qml +++ /dev/null @@ -1,87 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the examples of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names -** of its contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -import QtQuick 2.0 - -Item { - id: container - - signal clicked - - property string text - width: buttonText.width + 28 - height: buttonText.height + 14 - - BorderImage { - id: buttonImage - source: "images/toolbutton.sci" - width: container.width - 10 - height: container.height - } - BorderImage { - id: pressed - opacity: 0 - source: "images/toolbutton.sci" - width: container.width - 10 - height: container.height - } - MouseArea { - id: mouseRegion - anchors.fill: buttonImage - onClicked: { container.clicked(); } - } - Text { - id: buttonText - color: "white" - anchors.centerIn: buttonImage - font.bold: true - font.pointSize: 15 - text: container.text - style: Text.Raised - styleColor: "black" - } - states: [ - State { - name: "Pressed" - when: mouseRegion.pressed == true - PropertyChanges { target: pressed; opacity: 1 } - } - ] -} diff --git a/examples/quick/canvas/contents/ScrollBar.qml b/examples/quick/canvas/contents/ScrollBar.qml deleted file mode 100644 index 2c51e0fc48..0000000000 --- a/examples/quick/canvas/contents/ScrollBar.qml +++ /dev/null @@ -1,74 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the Qt Mobility Components. -** -** $QT_BEGIN_LICENSE:BSD$ -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names -** of its contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -import QtQuick 2.0 - -Item { - id: scrollBar - // The properties that define the scrollbar's state. - // position and pageSize are in the range 0.0 - 1.0. They are relative to the - // height of the page, i.e. a pageSize of 0.5 means that you can see 50% - // of the height of the view. - // orientation can be either 'Vertical' or 'Horizontal' - property real position - property real pageSize - property string orientation : "Vertical" - property alias bgColor: background.color - property alias fgColor: thumb.color - - // A light, semi-transparent background - Rectangle { - id: background - radius: orientation == 'Vertical' ? (width/2 - 1) : (height/2 - 1) - color: "white"; opacity: 0.3 - anchors.fill: parent - } - // Size the bar to the required size, depending upon the orientation. - Rectangle { - id: thumb - opacity: 0.7 - color: "black" - radius: orientation == 'Vertical' ? (width/2 - 1) : (height/2 - 1) - x: orientation == 'Vertical' ? 1 : (scrollBar.position * (scrollBar.width-2) + 1) - y: orientation == 'Vertical' ? (scrollBar.position * (scrollBar.height-2) + 1) : 1 - width: orientation == 'Vertical' ? (parent.width-2) : (scrollBar.pageSize * (scrollBar.width-2)) - height: orientation == 'Vertical' ? (scrollBar.pageSize * (scrollBar.height-2)) : (parent.height-2) - } -} diff --git a/examples/quick/canvas/contents/Slider.qml b/examples/quick/canvas/contents/Slider.qml deleted file mode 100644 index e0fed95ee8..0000000000 --- a/examples/quick/canvas/contents/Slider.qml +++ /dev/null @@ -1,115 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQuick module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** 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, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -import QtQuick 2.0 - -Item { - id:slider - property real min:0 - property real max:1 - property real value: min + (max - min) * (bar.x / (foo.width - bar.width)) - property real init:min+(max-min)/2 - property string name:"Slider" - - Component.onCompleted: setValue(init) - function setValue(v) { - if (min < max) - bar.x = v/(max - min) * (foo.width - bar.width); - } - Rectangle { - id:sliderName - anchors.left:parent.left - height: childrenRect.height - width:childrenRect.width - anchors.verticalCenter:parent.verticalCenter - Text { - text:slider.name - font.pointSize:12 - } - } - Item { - id: foo - height: 6 - width: parent.width - 4 - sliderName.width - anchors.verticalCenter:parent.verticalCenter - anchors.left:sliderName.right - anchors.leftMargin:5 - Rectangle { - height: parent.height - anchors.left: parent.left - anchors.right: bar.horizontalCenter - color: "blue" - radius: 3 - } - Rectangle { - height: parent.height - anchors.left: bar.horizontalCenter - anchors.right: parent.right - color: "gray" - radius: 3 - } - Rectangle { - anchors.fill: parent - color: "transparent" - radius: 3 - border.width: 2 - border.color: "black" - } - - Rectangle { - id: bar - y: -7 - width: 20 - height: 20 - radius: 15 - color: "white" - border.width: 2 - border.color: "black" - MouseArea { - anchors.fill: parent - drag.target: parent - drag.axis: Drag.XAxis - drag.minimumX: 0 - drag.maximumX: foo.width - parent.width - } - } - } -} diff --git a/examples/quick/canvas/contents/TitleBar.qml b/examples/quick/canvas/contents/TitleBar.qml deleted file mode 100644 index 1698a2a98a..0000000000 --- a/examples/quick/canvas/contents/TitleBar.qml +++ /dev/null @@ -1,70 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the examples of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names -** of its contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -import QtQuick 2.0 - -Item { - id: titleBar - property string title: "" - - BorderImage { source: "images/titlebar.sci"; width: parent.width; height: parent.height + 14; y: -7 } - - Image { - id: quitButton - anchors.left: parent.left//; anchors.leftMargin: 0 - anchors.verticalCenter: parent.verticalCenter - source: "images/quit.png" - MouseArea { - anchors.fill: parent - onClicked: Qt.quit() - } - } - - Text { - id: categoryText - anchors { - left: quitButton.right; right: parent.right; //leftMargin: 10; rightMargin: 10 - verticalCenter: parent.verticalCenter - } - elide: Text.ElideLeft - text: title - font.bold: true; font.pointSize: 20; color: "White"; style: Text.Raised; styleColor: "Black" - } -} diff --git a/examples/quick/canvas/contents/ToolBar.qml b/examples/quick/canvas/contents/ToolBar.qml deleted file mode 100644 index 005a6b144e..0000000000 --- a/examples/quick/canvas/contents/ToolBar.qml +++ /dev/null @@ -1,69 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the examples of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names -** of its contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -import QtQuick 2.0 - -Item { - id: toolbar - - property variant labels - signal buttonClicked(int index) - - BorderImage { - source: "images/titlebar.sci" - width: parent.width - height: parent.height + 14 - y: -7 - } - - Row { - y: 3 - anchors.horizontalCenter: parent.horizontalCenter - Repeater { - model: toolbar.labels - delegate: - Button { - text: modelData - onClicked: toolbar.buttonClicked(model.index) - } - } - } - -} diff --git a/examples/quick/canvas/contents/images/button-pressed.png b/examples/quick/canvas/contents/images/button-pressed.png deleted file mode 100644 index e434d327f2..0000000000 Binary files a/examples/quick/canvas/contents/images/button-pressed.png and /dev/null differ diff --git a/examples/quick/canvas/contents/images/button.png b/examples/quick/canvas/contents/images/button.png deleted file mode 100644 index 56a63ce641..0000000000 Binary files a/examples/quick/canvas/contents/images/button.png and /dev/null differ diff --git a/examples/quick/canvas/contents/images/default.svg b/examples/quick/canvas/contents/images/default.svg deleted file mode 100644 index 248199cc4e..0000000000 --- a/examples/quick/canvas/contents/images/default.svg +++ /dev/null @@ -1,82 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - weather-clear - January 2006 - - - Ryan Collier (pseudo) - - - - - http://www.tango-project.org - - - http://www.pseudocode.org - - - weather - applet - notification - - - - - - Garrett LeSage - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/examples/quick/canvas/contents/images/gloss.png b/examples/quick/canvas/contents/images/gloss.png deleted file mode 100755 index 5d370cd93d..0000000000 Binary files a/examples/quick/canvas/contents/images/gloss.png and /dev/null differ diff --git a/examples/quick/canvas/contents/images/lineedit.png b/examples/quick/canvas/contents/images/lineedit.png deleted file mode 100755 index 2cc38dc35b..0000000000 Binary files a/examples/quick/canvas/contents/images/lineedit.png and /dev/null differ diff --git a/examples/quick/canvas/contents/images/lineedit.sci b/examples/quick/canvas/contents/images/lineedit.sci deleted file mode 100644 index 054bff78be..0000000000 --- a/examples/quick/canvas/contents/images/lineedit.sci +++ /dev/null @@ -1,5 +0,0 @@ -border.left: 10 -border.top: 10 -border.bottom: 10 -border.right: 10 -source: lineedit.png diff --git a/examples/quick/canvas/contents/images/quit.png b/examples/quick/canvas/contents/images/quit.png deleted file mode 100755 index 5bda1b6e0d..0000000000 Binary files a/examples/quick/canvas/contents/images/quit.png and /dev/null differ diff --git a/examples/quick/canvas/contents/images/stripes.png b/examples/quick/canvas/contents/images/stripes.png deleted file mode 100755 index 9f36727ea4..0000000000 Binary files a/examples/quick/canvas/contents/images/stripes.png and /dev/null differ diff --git a/examples/quick/canvas/contents/images/titlebar.png b/examples/quick/canvas/contents/images/titlebar.png deleted file mode 100755 index 51c90082d0..0000000000 Binary files a/examples/quick/canvas/contents/images/titlebar.png and /dev/null differ diff --git a/examples/quick/canvas/contents/images/titlebar.sci b/examples/quick/canvas/contents/images/titlebar.sci deleted file mode 100644 index 0418d94cd6..0000000000 --- a/examples/quick/canvas/contents/images/titlebar.sci +++ /dev/null @@ -1,5 +0,0 @@ -border.left: 10 -border.top: 12 -border.bottom: 12 -border.right: 10 -source: titlebar.png diff --git a/examples/quick/canvas/contents/images/toolbutton.png b/examples/quick/canvas/contents/images/toolbutton.png deleted file mode 100755 index 11310013ee..0000000000 Binary files a/examples/quick/canvas/contents/images/toolbutton.png and /dev/null differ diff --git a/examples/quick/canvas/contents/images/toolbutton.sci b/examples/quick/canvas/contents/images/toolbutton.sci deleted file mode 100644 index 9e4f965307..0000000000 --- a/examples/quick/canvas/contents/images/toolbutton.sci +++ /dev/null @@ -1,5 +0,0 @@ -border.left: 15 -border.top: 4 -border.bottom: 4 -border.right: 15 -source: toolbutton.png diff --git a/examples/quick/canvas/quadraticCurveTo/quadraticCurveTo.qml b/examples/quick/canvas/quadraticCurveTo/quadraticCurveTo.qml index 9e03c03b98..0756d339ff 100644 --- a/examples/quick/canvas/quadraticCurveTo/quadraticCurveTo.qml +++ b/examples/quick/canvas/quadraticCurveTo/quadraticCurveTo.qml @@ -40,91 +40,97 @@ import QtQuick 2.0 import "../contents" +import "../../shared" + Item { - id:container - width:320 - height:480 + id:container + width:320 + height:480 - Column { - spacing:5 - anchors.fill:parent - Text { font.pointSize:15; text:"Quadratic Curve"; anchors.horizontalCenter:parent.horizontalCenter} + Column { + spacing:5 + anchors.fill: parent + anchors.topMargin: 12 - Canvas { - id:canvas - width:320 - height:280 - property string strokeStyle:"steelblue" - property string fillStyle:"yellow" - property int lineWidth:lineWidthCtrl.value - property bool fill:true - property bool stroke:true - property real alpha:alphaCtrl.value - property real scaleX : scaleXCtrl.value - property real scaleY : scaleYCtrl.value - property real rotate : rotateCtrl.value - antialiasing: true + Text { + font.pointSize: 24 + font.bold: true + text: "Quadratic Curve" + anchors.horizontalCenter: parent.horizontalCenter + color: "#777" + } - onLineWidthChanged:requestPaint(); - onFillChanged:requestPaint(); - onStrokeChanged:requestPaint(); - onAlphaChanged:requestPaint(); - onScaleXChanged:requestPaint(); - onScaleYChanged:requestPaint(); - onRotateChanged:requestPaint(); - Behavior on scaleX { SpringAnimation { spring: 2; damping: 0.2; loops:Animation.Infinite } } - Behavior on scaleY { SpringAnimation { spring: 2; damping: 0.2; loops:Animation.Infinite} } - Behavior on rotate { SpringAnimation { spring: 2; damping: 0.2; loops:Animation.Infinite} } + Canvas { + id: canvas + width: 320 + height: 280 - onPaint: { - var ctx = canvas.getContext('2d'); - ctx.save(); - ctx.clearRect(0, 0, canvas.width, canvas.height); - ctx.globalAlpha = canvas.alpha; - ctx.strokeStyle = canvas.strokeStyle; - ctx.fillStyle = canvas.fillStyle; - ctx.lineWidth = canvas.lineWidth; - ctx.scale(canvas.scaleX, canvas.scaleY); - ctx.rotate(canvas.rotate); - // ![0] - ctx.beginPath(); - ctx.moveTo(75,25); - ctx.quadraticCurveTo(25,25,25,62.5); - ctx.quadraticCurveTo(25,100,50,100); - ctx.quadraticCurveTo(50,120,30,125); - ctx.quadraticCurveTo(60,120,65,100); - ctx.quadraticCurveTo(125,100,125,62.5); - ctx.quadraticCurveTo(125,25,75,25); - ctx.closePath(); - // ![0] - if (canvas.fill) - ctx.fill(); - if (canvas.stroke) - ctx.stroke(); + property color strokeStyle: Qt.darker(fillStyle, 1.3) + property color fillStyle: "#14aaff" // blue + property int lineWidth: lineWidthCtrl.value + property bool fill: true + property bool stroke: true + property real alpha: 1.0 + property real scale : scaleCtrl.value + property real rotate : rotateCtrl.value + antialiasing: true - // ![1] - ctx.fillStyle="green"; - ctx.font = "Bold 15px"; + onLineWidthChanged:requestPaint(); + onFillChanged:requestPaint(); + onStrokeChanged:requestPaint(); + onScaleChanged:requestPaint(); + onRotateChanged:requestPaint(); - ctx.fillText("QML酷毙了", 30, 60); - // ![1] - ctx.restore(); - } - } + onPaint: { + var ctx = canvas.getContext('2d'); + var originX = 75 + var originY = 75 + + ctx.save(); + ctx.clearRect(0, 0, canvas.width, canvas.height); + ctx.translate(originX, originX); + ctx.strokeStyle = canvas.strokeStyle; + ctx.fillStyle = canvas.fillStyle; + ctx.lineWidth = canvas.lineWidth; + + ctx.translate(originX, originY) + ctx.rotate(canvas.rotate); + ctx.scale(canvas.scale, canvas.scale); + ctx.translate(-originX, -originY) - Rectangle { - id:controls - width:320 - height:150 - Column { - spacing:3 - Slider {id:lineWidthCtrl; width:300; height:20; min:1; max:10; init:2; name:"Line width"} - Slider {id:scaleXCtrl; width:300; height:20; min:0.1; max:10; init:1; name:"ScaleX"} - Slider {id:scaleYCtrl; width:300; height:20; min:0.1; max:10; init:1; name:"ScaleY"} - Slider {id:rotateCtrl; width:300; height:20; min:0; max:Math.PI*2; init:0; name:"Rotate"} - Slider {id:alphaCtrl; width:300; height:20; min:0; max:1; init:1; name:"Alpha"} + // ![0] + ctx.beginPath(); + ctx.moveTo(75,25); + ctx.quadraticCurveTo(25,25,25,62.5); + ctx.quadraticCurveTo(25,100,50,100); + ctx.quadraticCurveTo(50,120,30,125); + ctx.quadraticCurveTo(60,120,65,100); + ctx.quadraticCurveTo(125,100,125,62.5); + ctx.quadraticCurveTo(125,25,75,25); + ctx.closePath(); + // ![0] + + if (canvas.fill) + ctx.fill(); + if (canvas.stroke) + ctx.stroke(); + + ctx.restore(); + + // ![1] + ctx.fillStyle = "white"; + ctx.font = "Bold 17px"; + ctx.fillText("Qt Quick", 110, 140); + // ![1] + } } } - } + Column { + anchors.bottom: parent.bottom + anchors.bottomMargin: 12 + Slider {id:lineWidthCtrl; min:1; max:10; init:2; name:"Outline"} + Slider {id:scaleCtrl; min:0.1; max:10; init:1; name:"Scale"} + Slider {id:rotateCtrl; min:0; max:Math.PI*2; init:0; name:"Rotate"} + } } diff --git a/examples/quick/canvas/roundedrect/roundedrect.qml b/examples/quick/canvas/roundedrect/roundedrect.qml index 42142cef19..5a0a77425a 100644 --- a/examples/quick/canvas/roundedrect/roundedrect.qml +++ b/examples/quick/canvas/roundedrect/roundedrect.qml @@ -40,84 +40,82 @@ import QtQuick 2.0 import "../contents" +import "../../shared" + Item { - id:container - width:320 - height:480 + id:container + width: 320 + height: 480 - Column { - spacing:5 - anchors.fill:parent - Text { font.pointSize:15; text:"Rounded rectangle"; anchors.horizontalCenter:parent.horizontalCenter} - Canvas { - id:canvas - width:320 - height:280 - antialiasing: true + Column { + spacing: 6 + anchors.fill: parent + anchors.topMargin: 12 + Text { + font.pointSize: 24 + font.bold: true + text: "Rounded rectangle" + anchors.horizontalCenter: parent.horizontalCenter + color: "#777" + } + Canvas { + id: canvas + width: 320 + height: 280 + antialiasing: true - property int radius: rCtrl.value - property int rectx: rxCtrl.value - property int recty: ryCtrl.value - property int rectWidth: width - 2*rectx - property int rectHeight: height - 2*recty - property string strokeStyle:"blue" - property string fillStyle:"steelblue" - property int lineWidth:lineWidthCtrl.value - property bool fill:true - property bool stroke:true - property real alpha:alphaCtrl.value + property int radius: rCtrl.value + property int rectx: 60 + property int recty: 60 + property int rectWidth: width - 2*rectx + property int rectHeight: height - 2*recty + property color strokeStyle: Qt.darker(fillStyle, 1.4) + property color fillStyle: "#ae32a0" // purple + property int lineWidth: lineWidthCtrl.value + property bool fill: true + property bool stroke: true + property real alpha: 1.0 - onLineWidthChanged:requestPaint(); - onFillChanged:requestPaint(); - onStrokeChanged:requestPaint(); - onRadiusChanged:requestPaint(); - onRectxChanged:requestPaint(); - onRectyChanged:requestPaint(); - onAlphaChanged:requestPaint(); + onLineWidthChanged:requestPaint(); + onFillChanged:requestPaint(); + onStrokeChanged:requestPaint(); + onRadiusChanged:requestPaint(); - onPaint: { - var ctx = getContext("2d"); - ctx.save(); - ctx.clearRect(0,0,canvas.width, canvas.height); - ctx.strokeStyle = canvas.strokeStyle; - ctx.lineWidth = canvas.lineWidth - ctx.fillStyle = canvas.fillStyle - ctx.globalAlpha = canvas.alpha - ctx.beginPath(); - ctx.moveTo(rectx+radius,recty); // top side - ctx.lineTo(rectx+rectWidth-radius,recty); - // draw top right corner - ctx.arcTo(rectx+rectWidth,recty,rectx+rectWidth,recty+radius,radius); - ctx.lineTo(rectx+rectWidth,recty+rectHeight-radius); // right side - // draw bottom right corner - ctx.arcTo(rectx+rectWidth,recty+rectHeight,rectx+rectWidth-radius,recty+rectHeight,radius); - ctx.lineTo(rectx+radius,recty+rectHeight); // bottom side - // draw bottom left corner - ctx.arcTo(rectx,recty+rectHeight,rectx,recty+rectHeight-radius,radius); - ctx.lineTo(rectx,recty+radius); // left side - // draw top left corner - ctx.arcTo(rectx,recty,rectx+radius,recty,radius); - ctx.closePath(); - if (canvas.fill) - ctx.fill(); - if (canvas.stroke) - ctx.stroke(); - ctx.restore(); + onPaint: { + var ctx = getContext("2d"); + ctx.save(); + ctx.clearRect(0,0,canvas.width, canvas.height); + ctx.strokeStyle = canvas.strokeStyle; + ctx.lineWidth = canvas.lineWidth + ctx.fillStyle = canvas.fillStyle + ctx.globalAlpha = canvas.alpha + ctx.beginPath(); + ctx.moveTo(rectx+radius,recty); // top side + ctx.lineTo(rectx+rectWidth-radius,recty); + // draw top right corner + ctx.arcTo(rectx+rectWidth,recty,rectx+rectWidth,recty+radius,radius); + ctx.lineTo(rectx+rectWidth,recty+rectHeight-radius); // right side + // draw bottom right corner + ctx.arcTo(rectx+rectWidth,recty+rectHeight,rectx+rectWidth-radius,recty+rectHeight,radius); + ctx.lineTo(rectx+radius,recty+rectHeight); // bottom side + // draw bottom left corner + ctx.arcTo(rectx,recty+rectHeight,rectx,recty+rectHeight-radius,radius); + ctx.lineTo(rectx,recty+radius); // left side + // draw top left corner + ctx.arcTo(rectx,recty,rectx+radius,recty,radius); + ctx.closePath(); + if (canvas.fill) + ctx.fill(); + if (canvas.stroke) + ctx.stroke(); + ctx.restore(); + } } } - - Rectangle { - id:controls - width:320 - height:150 - Column { - spacing:3 - Slider {id:lineWidthCtrl; width:300; height:20; min:1; max:10; init:2; name:"Line width"} - Slider {id:rxCtrl; width:300; height:20; min:5; max:30; init:10; name:"rectx"} - Slider {id:ryCtrl; width:300; height:20; min:5; max:30; init:10; name:"recty"} - Slider {id:rCtrl; width:300; height:20; min:10; max:100; init:40; name:"Radius"} - Slider {id:alphaCtrl; width:300; height:20; min:0; max:1; init:1; name:"Alpha"} - } + Column { + anchors.bottom: parent.bottom + anchors.bottomMargin: 12 + Slider {id: lineWidthCtrl ; min: 1 ; max: 10; init: 2 ; name: "Outline"} + Slider {id: rCtrl ; min: 10 ; max: 80 ; init: 40 ; name: "Radius"} } - } } diff --git a/examples/quick/canvas/smile/smile.qml b/examples/quick/canvas/smile/smile.qml index c8b01b305a..0251b1b101 100644 --- a/examples/quick/canvas/smile/smile.qml +++ b/examples/quick/canvas/smile/smile.qml @@ -40,87 +40,90 @@ import QtQuick 2.0 import "../contents" +import "../../shared" + Item { - id:container - width:320 - height:480 + id: container + width: 320 + height: 480 - Column { - spacing:5 - anchors.fill:parent - Text { font.pointSize:15; text:"Smile with arcs"; anchors.horizontalCenter:parent.horizontalCenter} + Column { + spacing: 6 + anchors.fill: parent + anchors.topMargin: 12 - Canvas { - id:canvas - width:320 - height:280 - antialiasing: true + Text { + font.pointSize: 24 + font.bold: true + text: "Smile with arcs" + anchors.horizontalCenter: parent.horizontalCenter + color: "#777" + } - property string strokeStyle:"green" - property string fillStyle:"yellow" - property int lineWidth:lineWidthCtrl.value - property bool fill:true - property bool stroke:true - property real alpha:alphaCtrl.value - property real scaleX : scaleXCtrl.value - property real scaleY : scaleYCtrl.value - property real rotate : rotateCtrl.value + Canvas { + id: canvas + width: 320 + height: parent.height - controls.height + antialiasing: true - onLineWidthChanged:requestPaint(); - onFillChanged:requestPaint(); - onStrokeChanged:requestPaint(); - onAlphaChanged:requestPaint(); - onScaleXChanged:requestPaint(); - onScaleYChanged:requestPaint(); - onRotateChanged:requestPaint(); + property color strokeStyle: Qt.darker(fillStyle, 1.6) + property color fillStyle: "#e0c31e" // yellow + property int lineWidth: lineWidthCtrl.value + property bool fill: true + property bool stroke: true + property real scale : scaleCtrl.value + property real rotate : rotateCtrl.value - Behavior on scaleX { SpringAnimation { spring: 2; damping: 0.2; loops:Animation.Infinite } } - Behavior on scaleY { SpringAnimation { spring: 2; damping: 0.2; loops:Animation.Infinite} } - Behavior on rotate { SpringAnimation { spring: 2; damping: 0.2; loops:Animation.Infinite} } + onLineWidthChanged:requestPaint(); + onFillChanged:requestPaint(); + onStrokeChanged:requestPaint(); + onScaleChanged:requestPaint(); + onRotateChanged:requestPaint(); - onPaint: { - var ctx = canvas.getContext('2d'); - ctx.save(); - ctx.clearRect(0, 0, canvas.width, canvas.height); - ctx.globalAlpha = canvas.alpha; - ctx.strokeStyle = canvas.strokeStyle; - ctx.fillStyle = canvas.fillStyle; - ctx.lineWidth = canvas.lineWidth; - ctx.scale(canvas.scaleX, canvas.scaleY); - ctx.rotate(canvas.rotate); - ctx.beginPath(); - ctx.moveTo(75 + 50 * Math.cos(0), - 75 - 50 * Math.sin(Math.PI*2)); - ctx.arc(75,75,50,0,Math.PI*2,true); // Outer circle - ctx.moveTo(75,70); - ctx.arc(75,70,35,0,Math.PI,false); // Mouth (clockwise) - ctx.moveTo(60,65); - ctx.arc(60,65,5,0,Math.PI*2,true); // Left eye - ctx.moveTo(90 + 5 * Math.cos(0), - 65 - 5 * Math.sin(Math.PI*2)); - ctx.moveTo(90,65); - ctx.arc(90,65,5,0,Math.PI*2,true); // Right eye - ctx.closePath(); - if (canvas.fill) - ctx.fill(); - if (canvas.stroke) - ctx.stroke(); - ctx.restore(); - } - } + onPaint: { + var ctx = canvas.getContext('2d'); + var originX = 85 + var originY = 75 + ctx.save(); + ctx.clearRect(0, 0, canvas.width, canvas.height); + ctx.translate(originX, originX); + ctx.globalAlpha = canvas.alpha; + ctx.strokeStyle = canvas.strokeStyle; + ctx.fillStyle = canvas.fillStyle; + ctx.lineWidth = canvas.lineWidth; + + ctx.translate(originX, originY) + ctx.scale(canvas.scale, canvas.scale); + ctx.rotate(canvas.rotate); + ctx.translate(-originX, -originY) - Rectangle { - id:controls - width:320 - height:150 - Column { - spacing:3 - Slider {id:lineWidthCtrl; width:300; height:20; min:1; max:10; init:2; name:"Line width"} - Slider {id:scaleXCtrl; width:300; height:20; min:0.1; max:10; init:1; name:"ScaleX"} - Slider {id:scaleYCtrl; width:300; height:20; min:0.1; max:10; init:1; name:"ScaleY"} - Slider {id:rotateCtrl; width:300; height:20; min:0; max:Math.PI*2; init:0; name:"Rotate"} - Slider {id:alphaCtrl; width:300; height:20; min:0; max:1; init:1; name:"Alpha"} + ctx.beginPath(); + ctx.moveTo(75 + 50 * Math.cos(0), + 75 - 50 * Math.sin(Math.PI*2)); + ctx.arc(75,75,50,0,Math.PI*2,true); // Outer circle + ctx.moveTo(75,70); + ctx.arc(75,70,35,0,Math.PI,false); // Mouth (clockwise) + ctx.moveTo(60,65); + ctx.arc(60,65,5,0,Math.PI*2,true); // Left eye + ctx.moveTo(90 + 5 * Math.cos(0), + 65 - 5 * Math.sin(Math.PI*2)); + ctx.moveTo(90,65); + ctx.arc(90,65,5,0,Math.PI*2,true); // Right eye + ctx.closePath(); + if (canvas.fill) + ctx.fill(); + if (canvas.stroke) + ctx.stroke(); + ctx.restore(); + } } } - } + Column { + id: controls + anchors.bottom: parent.bottom + anchors.bottomMargin: 12 + Slider {id: lineWidthCtrl ; min: 1 ; max: 10 ; init: 2 ; name: "Outline"} + Slider {id: scaleCtrl ; min: 0.1 ; max: 10 ; init: 1 ; name: "Scale"} + Slider {id: rotateCtrl ; min: 0 ; max: Math.PI*2 ; init: 0 ; name: "Rotate"} + } } diff --git a/examples/quick/canvas/squircle/squircle.qml b/examples/quick/canvas/squircle/squircle.qml index 3044816479..4e5c5a49fc 100644 --- a/examples/quick/canvas/squircle/squircle.qml +++ b/examples/quick/canvas/squircle/squircle.qml @@ -40,114 +40,111 @@ import QtQuick 2.0 import "../contents" +import "../../shared" + Item { - id:container - width:320 - height:480 - - Column { - spacing:5 - anchors.fill:parent - Text { font.pointSize:15; text:"Squircles"; anchors.horizontalCenter:parent.horizontalCenter} - Image { - anchors.horizontalCenter:parent.horizontalCenter - source:"squircle.png" - width:250 - height:25 - } - Canvas { - id:canvas - width:320 - height:250 - antialiasing: true - - property string strokeStyle:"blue" - property string fillStyle:"steelblue" - property int lineWidth:2 - property int nSize:nCtrl.value - property real radius:rCtrl.value - property bool fill:true - property bool stroke:false - property real px:xCtrl.value - property real py:yCtrl.value - property real alpha:alphaCtrl.value - - onAlphaChanged:requestPaint(); - onRadiusChanged:requestPaint(); - onLineWidthChanged:requestPaint(); - onNSizeChanged:requestPaint(); - onFillChanged:requestPaint(); - onStrokeChanged:requestPaint(); - onPxChanged:requestPaint(); - onPyChanged:requestPaint(); - - - onPaint: squcirle(); - - function squcirle() { - var ctx = canvas.getContext("2d"); - var N = canvas.nSize; - var R = canvas.radius; - - N=Math.abs(N); - var M=N; - if (N>100) M=100; - if (N<0.00000000001) M=0.00000000001; - - ctx.save(); - ctx.globalAlpha =canvas.alpha; - ctx.fillStyle = "gray"; - ctx.fillRect(0, 0, canvas.width, canvas.height); - - ctx.strokeStyle = canvas.strokeStyle; - ctx.fillStyle = canvas.fillStyle; - ctx.lineWidth = canvas.lineWidth; - - ctx.beginPath(); - var i = 0, x, y; - for (i=0; i<(2*R+1); i++){ - x = Math.round(i-R) + canvas.px; - y = Math.round(Math.pow(Math.abs(Math.pow(R,M)-Math.pow(Math.abs(i-R),M)),1/M)) + canvas.py; - - if (i == 0) - ctx.moveTo(x, y); - else - ctx.lineTo(x, y); - } - - for (i=(2*R); i<(4*R+1); i++){ - x =Math.round(3*R-i)+canvas.px; - y = Math.round(-Math.pow(Math.abs(Math.pow(R,M)-Math.pow(Math.abs(3*R-i),M)),1/M)) + canvas.py; - ctx.lineTo(x, y); - } - ctx.closePath(); - if (canvas.stroke) { - ctx.stroke(); - } - - if (canvas.fill) { - ctx.fill(); - } - - ctx.fillStyle = "yellow"; - ctx.font = "Helvetica 16px"; - ctx.fillText("|X-" + Math.round(canvas.px) + "|^" + N + " + |Y-"+Math.round(canvas.py)+"|^" + N + " = |" + Math.round(R) + "|^" + N, canvas.px - 125, canvas.py); - ctx.restore(); + id: container + width: 320 + height: 480 + + Column { + spacing: 6 + anchors.fill: parent + anchors.topMargin: 12 + Text { + font.pointSize: 24 + font.bold: true + text: "Squircles" + anchors.horizontalCenter: parent.horizontalCenter + color: "#777" } - } - Rectangle { - id:controls - width:320 - height:150 - Column { - spacing:3 - Slider {id:nCtrl; width:300; height:20; min:1; max:10; init:4; name:"N"} - Slider {id:rCtrl; width:300; height:20; min:30; max:180; init:100; name:"Radius"} - Slider {id:xCtrl; width:300; height:20; min:50; max:300; init:180; name:"X"} - Slider {id:yCtrl; width:300; height:20; min:30; max:300; init:220; name:"Y"} - Slider {id:alphaCtrl; width:300; height:20; min:0; max:1; init:1; name:"Alpha"} + Image { + anchors.horizontalCenter: parent.horizontalCenter + source: "squircle.png" + width: 250 + height: 25 } + + Canvas { + id: canvas + width: 320 + height: 250 + antialiasing: true + + property color strokeStyle: Qt.darker(fillStyle, 1.2) + property color fillStyle: "#6400aa" + + property int lineWidth: 2 + property int nSize: nCtrl.value + property real radius: rCtrl.value + property bool fill: true + property bool stroke: false + property real px: width/2 + property real py: height/2 + 10 + property real alpha: 1.0 + + onRadiusChanged: requestPaint(); + onLineWidthChanged: requestPaint(); + onNSizeChanged: requestPaint(); + onFillChanged: requestPaint(); + onStrokeChanged: requestPaint(); + + onPaint: squcirle(); + + function squcirle() { + var ctx = canvas.getContext("2d"); + var N = canvas.nSize; + var R = canvas.radius; + + N=Math.abs(N); + var M=N; + if (N>100) M=100; + if (N<0.00000000001) M=0.00000000001; + + ctx.save(); + ctx.globalAlpha =canvas.alpha; + ctx.fillStyle = "white"; + ctx.fillRect(0, 0, canvas.width, canvas.height); + + ctx.strokeStyle = canvas.strokeStyle; + ctx.fillStyle = canvas.fillStyle; + ctx.lineWidth = canvas.lineWidth; + + ctx.beginPath(); + var i = 0, x, y; + for (i=0; i<(2*R+1); i++){ + x = Math.round(i-R) + canvas.px; + y = Math.round(Math.pow(Math.abs(Math.pow(R,M)-Math.pow(Math.abs(i-R),M)),1/M)) + canvas.py; + + if (i == 0) + ctx.moveTo(x, y); + else + ctx.lineTo(x, y); + } + + for (i=(2*R); i<(4*R+1); i++){ + x =Math.round(3*R-i)+canvas.px; + y = Math.round(-Math.pow(Math.abs(Math.pow(R,M)-Math.pow(Math.abs(3*R-i),M)),1/M)) + canvas.py; + ctx.lineTo(x, y); + } + ctx.closePath(); + if (canvas.stroke) { + ctx.stroke(); + } + + if (canvas.fill) { + ctx.fill(); + } + ctx.restore(); + } + } + + } + Column { + anchors.bottom: parent.bottom + anchors.bottomMargin: 12 + Slider {id: nCtrl ; min: 1 ; max: 10 ; init: 2 ; name: "N"} + Slider {id: rCtrl ; min: 30 ; max: 180 ; init: 60 ; name: "Radius"} } - } } diff --git a/examples/quick/canvas/tiger/tiger.qml b/examples/quick/canvas/tiger/tiger.qml index 8b629eecf3..94a7d2dc3d 100644 --- a/examples/quick/canvas/tiger/tiger.qml +++ b/examples/quick/canvas/tiger/tiger.qml @@ -40,89 +40,95 @@ import QtQuick 2.0 import "../contents" +import "../../shared" import "tiger.js" as Tiger + Item { - id:container - width:320 - height:480 + id: container + width: 320 + height: 480 - Column { - spacing:5 - anchors.fill:parent - Text { font.pointSize:15; text:"Tiger with SVG path"; anchors.horizontalCenter:parent.horizontalCenter} + Column { + spacing: 6 + anchors.fill: parent + anchors.topMargin: 12 - Canvas { - id:canvas - width:320 - height:280 - antialiasing: true - property string strokeStyle:"steelblue" - property string fillStyle:"yellow" - property bool fill:true - property bool stroke:true - property real alpha:alphaCtrl.value - property real scaleX : scaleXCtrl.value - property real scaleY : scaleYCtrl.value - property real rotate : rotateCtrl.value - property int frame:0 + Text { + font.pointSize: 24 + font.bold: true + text: "Tiger with SVG path" + anchors.horizontalCenter: parent.horizontalCenter + color: "#777" + } - onFillChanged: requestPaint(); - onStrokeChanged: requestPaint(); - onAlphaChanged: requestPaint(); - onScaleXChanged: requestPaint(); - onScaleYChanged: requestPaint(); - onRotateChanged: requestPaint(); + Canvas { + id: canvas + width: 320 + height: 280 + antialiasing: true - onPainted : { - canvas.frame++; - if (canvas.frame < Tiger.tiger.length) - requestPaint(); - } - onPaint: { - var ctx = canvas.getContext('2d'); - ctx.save(); - ctx.clearRect(0, 0, canvas.width, canvas.height); - ctx.globalAlpha = canvas.alpha; - ctx.scale(canvas.scaleX, canvas.scaleY); - ctx.rotate(canvas.rotate); - ctx.globalCompositeOperation = "source-over"; - ctx.translate(canvas.width/2, canvas.height/2); - ctx.strokeStyle = Qt.rgba(.3, .3, .3,1); - ctx.lineWidth = 1; + property string strokeStyle: "steelblue" + property string fillStyle: "yellow" + property bool fill: true + property bool stroke: true + property real alpha: alphaCtrl.value + property real scale: scaleCtrl.value + property real rotate: rotateCtrl.value + property int frame: 0 - //! [0] - for (var i = 0; i < canvas.frame && i < Tiger.tiger.length; i++) { - if (Tiger.tiger[i].width != undefined) - ctx.lineWidth = Tiger.tiger[i].width; + onFillChanged: requestPaint(); + onStrokeChanged: requestPaint(); + onAlphaChanged: requestPaint(); + onScaleChanged: requestPaint(); + onRotateChanged: requestPaint(); - if (Tiger.tiger[i].path != undefined) - ctx.path = Tiger.tiger[i].path; + onPaint: { + var ctx = canvas.getContext('2d'); + var originX = canvas.width/2 + 30 + var originY = canvas.height/2 + 60 - if (Tiger.tiger[i].fill != undefined) { - ctx.fillStyle = Tiger.tiger[i].fill; - ctx.fill(); - } + ctx.save(); + ctx.clearRect(0, 0, canvas.width, canvas.height); + ctx.globalAlpha = canvas.alpha; + ctx.globalCompositeOperation = "source-over"; + + ctx.translate(originX, originY) + ctx.scale(canvas.scale, canvas.scale); + ctx.rotate(canvas.rotate); + ctx.translate(-originX, -originY) - if (Tiger.tiger[i].stroke != undefined) { - ctx.strokeStyle = Tiger.tiger[i].stroke; - ctx.stroke(); + ctx.strokeStyle = Qt.rgba(.3, .3, .3,1); + ctx.lineWidth = 1; + + //! [0] + for (var i = 0; i < Tiger.tiger.length; i++) { + if (Tiger.tiger[i].width != undefined) + ctx.lineWidth = Tiger.tiger[i].width; + + if (Tiger.tiger[i].path != undefined) + ctx.path = Tiger.tiger[i].path; + + if (Tiger.tiger[i].fill != undefined) { + ctx.fillStyle = Tiger.tiger[i].fill; + ctx.fill(); + } + + if (Tiger.tiger[i].stroke != undefined) { + ctx.strokeStyle = Tiger.tiger[i].stroke; + ctx.stroke(); + } } + //! [0] + ctx.restore(); } - //! [0] - ctx.restore(); } } - Rectangle { - id:controls - width:320 - height:150 - Column { - spacing:3 - Slider {id:scaleXCtrl; width:300; height:20; min:0.1; max:10; init:0.5; name:"ScaleX"} - Slider {id:scaleYCtrl; width:300; height:20; min:0.1; max:10; init:0.5; name:"ScaleY"} - Slider {id:rotateCtrl; width:300; height:20; min:0; max:Math.PI*2; init:0; name:"Rotate"} - Slider {id:alphaCtrl; width:300; height:20; min:0; max:1; init:1; name:"Alpha"} - } + Column { + id: controls + anchors.bottom: parent.bottom + anchors.bottomMargin: 12 + Slider {id: scaleCtrl ; min: 0.1 ; max: 1 ; init: 0.3 ; name: "Scale"} + Slider {id: rotateCtrl ; min: 0 ; max: Math.PI*2 ; init: 0 ; name: "Rotate"} + Slider {id: alphaCtrl ; min: 0 ; max: 1 ; init: 1 ; name: "Alpha"} } - } } diff --git a/examples/quick/shared/Slider.qml b/examples/quick/shared/Slider.qml new file mode 100644 index 0000000000..d310a25bd5 --- /dev/null +++ b/examples/quick/shared/Slider.qml @@ -0,0 +1,117 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 + +Item { + id: slider + height: 26 + width: 320 + + property real min: 0 + property real max: 1 + property real value: min + (max - min) * mousearea.value + property real init: min+(max-min)/2 + property string name: "Slider" + property color color: "#0066cc" + + Component.onCompleted: setValue(init) + function setValue(v) { + if (min < max) + handle.x = Math.round( v / (max - min) * + (mousearea.drag.maximumX - mousearea.drag.minimumX) + + mousearea.drag.minimumX); + } + Rectangle { + id:sliderName + anchors.left: parent.left + anchors.leftMargin: 16 + height: childrenRect.height + width: Math.max(44, childrenRect.width) + anchors.verticalCenter: parent.verticalCenter + Text { + text: slider.name + ":" + font.pointSize: 12 + color: "#333" + } + } + + Rectangle{ + id: foo + width: parent.width - 8 - sliderName.width + color: "#eee" + height: 7 + radius: 3 + antialiasing: true + border.color: Qt.darker(color, 1.2) + anchors.left: sliderName.right + anchors.right: parent.right + anchors.leftMargin: 10 + anchors.rightMargin: 24 + anchors.verticalCenter: parent.verticalCenter + + Rectangle { + height: parent.height + anchors.left: parent.left + anchors.right: handle.horizontalCenter + color: slider.color + radius: 3 + border.width: 1 + border.color: Qt.darker(color, 1.3) + opacity: 0.8 + } + Image { + id: handle + source: "images/slider_handle.png" + anchors.verticalCenter: parent.verticalCenter + MouseArea { + id: mousearea + anchors.fill: parent + anchors.margins: -4 + drag.target: parent + drag.axis: Drag.XAxis + drag.minimumX: Math.round(-handle.width / 2 + 3) + drag.maximumX: Math.round(foo.width - handle.width/2 - 3) + property real value: (handle.x - drag.minimumX) / (drag.maximumX - drag.minimumX) + } + } + } +} diff --git a/examples/quick/shared/images/slider_handle.png b/examples/quick/shared/images/slider_handle.png new file mode 100644 index 0000000000..63c518be7d Binary files /dev/null and b/examples/quick/shared/images/slider_handle.png differ diff --git a/examples/quick/shared/qmldir b/examples/quick/shared/qmldir index 2f1e56aefb..106654bbf0 100644 --- a/examples/quick/shared/qmldir +++ b/examples/quick/shared/qmldir @@ -1,3 +1,4 @@ Button 2.0 Button.qml LauncherList 2.0 LauncherList.qml SimpleLauncherDelegate 2.0 SimpleLauncherDelegate.qml +Slider 2.0 Slider.qml diff --git a/examples/quick/shared/shared.qrc b/examples/quick/shared/shared.qrc index 0c9f39e7b0..fba2d3eb82 100644 --- a/examples/quick/shared/shared.qrc +++ b/examples/quick/shared/shared.qrc @@ -3,6 +3,8 @@ LauncherList.qml SimpleLauncherDelegate.qml Button.qml + Slider.qml + images/slider_handle.png images/back.png images/next.png -- cgit v1.2.3 From 98e00abc19ad46072f7c8303e50f9748d36a925e Mon Sep 17 00:00:00 2001 From: Gunnar Sletta Date: Tue, 26 Feb 2013 13:35:46 +0100 Subject: Change Canvas to use Image and antialiasing by default. Change-Id: I3033cba7c2f9e1dd9886e8f92ece1a2f43f60c01 Reviewed-by: Jens Bache-Wiig --- src/quick/items/context2d/qquickcanvasitem.cpp | 3 ++- tests/auto/quick/qquickcanvasitem/data/CanvasComponent.qml | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/quick/items/context2d/qquickcanvasitem.cpp b/src/quick/items/context2d/qquickcanvasitem.cpp index 7c91193623..ea49d58af3 100644 --- a/src/quick/items/context2d/qquickcanvasitem.cpp +++ b/src/quick/items/context2d/qquickcanvasitem.cpp @@ -190,9 +190,10 @@ QQuickCanvasItemPrivate::QQuickCanvasItemPrivate() , hasCanvasWindow(false) , available(false) , contextInitialized(false) - , renderTarget(QQuickCanvasItem::FramebufferObject) + , renderTarget(QQuickCanvasItem::Image) , renderStrategy(QQuickCanvasItem::Cooperative) { + antialiasing = true; } QQuickCanvasItemPrivate::~QQuickCanvasItemPrivate() diff --git a/tests/auto/quick/qquickcanvasitem/data/CanvasComponent.qml b/tests/auto/quick/qquickcanvasitem/data/CanvasComponent.qml index b9e8d75f5a..000888a36f 100644 --- a/tests/auto/quick/qquickcanvasitem/data/CanvasComponent.qml +++ b/tests/auto/quick/qquickcanvasitem/data/CanvasComponent.qml @@ -5,6 +5,7 @@ Component { id:canvas Canvas { id:c + antialiasing: false; width:100;height:100 onPaint :{} //this line is needed for some tests (make sure onPaint handler always called property alias paintCount:spyPaint.count @@ -27,4 +28,4 @@ Component { SignalSpy {id: spyImageLoaded;target:c;signalName: "imageLoaded"} SignalSpy {id: spyAvailableChanged;target:c;signalName: "availableChanged"} } -} \ No newline at end of file +} -- cgit v1.2.3 From c324edd5923a86548e385fa6f16361e1f085e4c4 Mon Sep 17 00:00:00 2001 From: Gunnar Sletta Date: Mon, 25 Feb 2013 17:22:58 +0100 Subject: Avoid ambiguous include guards in SG classes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These include guards stem from the olden days before the QSG prefix and many of them are conflict prone, which can lead to pretty bisarrer errors for the user. Change-Id: I0e76c68a588404b6e82be9ecc2e7afa3b9a48e78 Reviewed-by: Samuel Rødal --- src/quick/scenegraph/coreapi/qsgdefaultrenderer_p.h | 4 ++-- src/quick/scenegraph/coreapi/qsgmaterial.h | 4 ++-- src/quick/scenegraph/coreapi/qsgnode.h | 6 +++--- src/quick/scenegraph/coreapi/qsgnodeupdater_p.h | 6 +++--- src/quick/scenegraph/coreapi/qsgrenderer_p.h | 6 +++--- src/quick/scenegraph/qsgadaptationlayer_p.h | 4 ++-- src/quick/scenegraph/qsgdefaultglyphnode_p.h | 6 +++--- src/quick/scenegraph/qsgdefaultglyphnode_p_p.h | 6 +++--- src/quick/scenegraph/qsgdefaultimagenode_p.h | 4 ++-- src/quick/scenegraph/qsgdefaultrectanglenode_p.h | 4 ++-- src/quick/scenegraph/qsgdistancefieldglyphnode_p.h | 6 +++--- src/quick/scenegraph/qsgdistancefieldglyphnode_p_p.h | 6 +++--- src/quick/scenegraph/util/qsgareaallocator_p.h | 4 ++-- src/quick/scenegraph/util/qsgflatcolormaterial.h | 4 ++-- src/quick/scenegraph/util/qsgsimplerectnode.h | 4 ++-- src/quick/scenegraph/util/qsgtexturematerial.h | 6 +++--- src/quick/scenegraph/util/qsgvertexcolormaterial.h | 4 ++-- 17 files changed, 42 insertions(+), 42 deletions(-) diff --git a/src/quick/scenegraph/coreapi/qsgdefaultrenderer_p.h b/src/quick/scenegraph/coreapi/qsgdefaultrenderer_p.h index ae759c0b24..6fba0d18bc 100644 --- a/src/quick/scenegraph/coreapi/qsgdefaultrenderer_p.h +++ b/src/quick/scenegraph/coreapi/qsgdefaultrenderer_p.h @@ -39,8 +39,8 @@ ** ****************************************************************************/ -#ifndef QMLRENDERER_H -#define QMLRENDERER_H +#ifndef QSGDEFAULTRENDERER_P_H +#define QSGDEFAULTRENDERER_P_H #include "qsgrenderer_p.h" diff --git a/src/quick/scenegraph/coreapi/qsgmaterial.h b/src/quick/scenegraph/coreapi/qsgmaterial.h index 5effdbfda6..50e9c77d4e 100644 --- a/src/quick/scenegraph/coreapi/qsgmaterial.h +++ b/src/quick/scenegraph/coreapi/qsgmaterial.h @@ -39,8 +39,8 @@ ** ****************************************************************************/ -#ifndef MATERIAL_H -#define MATERIAL_H +#ifndef QSGMATERIAL_H +#define QSGMATERIAL_H #include #include diff --git a/src/quick/scenegraph/coreapi/qsgnode.h b/src/quick/scenegraph/coreapi/qsgnode.h index 522546d8e8..3fa2f7fc04 100644 --- a/src/quick/scenegraph/coreapi/qsgnode.h +++ b/src/quick/scenegraph/coreapi/qsgnode.h @@ -39,8 +39,8 @@ ** ****************************************************************************/ -#ifndef NODE_H -#define NODE_H +#ifndef QSGNODE_H +#define QSGNODE_H #include #include @@ -333,4 +333,4 @@ Q_DECLARE_OPERATORS_FOR_FLAGS(QSGNode::Flags) QT_END_NAMESPACE -#endif // NODE_H +#endif // QSGNODE_H diff --git a/src/quick/scenegraph/coreapi/qsgnodeupdater_p.h b/src/quick/scenegraph/coreapi/qsgnodeupdater_p.h index 7e57bf5bbf..eb612ff877 100644 --- a/src/quick/scenegraph/coreapi/qsgnodeupdater_p.h +++ b/src/quick/scenegraph/coreapi/qsgnodeupdater_p.h @@ -39,8 +39,8 @@ ** ****************************************************************************/ -#ifndef NODEUPDATER_P_H -#define NODEUPDATER_P_H +#ifndef QSGNODEUPDATER_P_H +#define QSGNODEUPDATER_P_H #include #include @@ -94,4 +94,4 @@ protected: QT_END_NAMESPACE -#endif // NODEUPDATER_P_H +#endif diff --git a/src/quick/scenegraph/coreapi/qsgrenderer_p.h b/src/quick/scenegraph/coreapi/qsgrenderer_p.h index 5dd53547d1..844e8b1d8c 100644 --- a/src/quick/scenegraph/coreapi/qsgrenderer_p.h +++ b/src/quick/scenegraph/coreapi/qsgrenderer_p.h @@ -39,8 +39,8 @@ ** ****************************************************************************/ -#ifndef RENDERER_H -#define RENDERER_H +#ifndef QSGRENDERER_P_H +#define QSGRENDERER_P_H #include #include @@ -239,4 +239,4 @@ private: QT_END_NAMESPACE -#endif // RENDERER_H +#endif diff --git a/src/quick/scenegraph/qsgadaptationlayer_p.h b/src/quick/scenegraph/qsgadaptationlayer_p.h index 85c1d2e6ff..cc22bfa61f 100644 --- a/src/quick/scenegraph/qsgadaptationlayer_p.h +++ b/src/quick/scenegraph/qsgadaptationlayer_p.h @@ -39,8 +39,8 @@ ** ****************************************************************************/ -#ifndef ADAPTATIONINTERFACES_H -#define ADAPTATIONINTERFACES_H +#ifndef QSGADAPTATIONLAYER_P_H +#define QSGADAPTATIONLAYER_P_H #include #include diff --git a/src/quick/scenegraph/qsgdefaultglyphnode_p.h b/src/quick/scenegraph/qsgdefaultglyphnode_p.h index a1cd381968..a3d5d7c3ae 100644 --- a/src/quick/scenegraph/qsgdefaultglyphnode_p.h +++ b/src/quick/scenegraph/qsgdefaultglyphnode_p.h @@ -39,8 +39,8 @@ ** ****************************************************************************/ -#ifndef DEFAULT_GLYPHNODE_H -#define DEFAULT_GLYPHNODE_H +#ifndef QSGDEFAULTGLYPHNODE_P_H +#define QSGDEFAULTGLYPHNODE_P_H #include #include @@ -78,4 +78,4 @@ private: QT_END_NAMESPACE -#endif // DEFAULT_GLYPHNODE_H +#endif diff --git a/src/quick/scenegraph/qsgdefaultglyphnode_p_p.h b/src/quick/scenegraph/qsgdefaultglyphnode_p_p.h index efa96a1805..263523221e 100644 --- a/src/quick/scenegraph/qsgdefaultglyphnode_p_p.h +++ b/src/quick/scenegraph/qsgdefaultglyphnode_p_p.h @@ -39,8 +39,8 @@ ** ****************************************************************************/ -#ifndef TEXTMASKMATERIAL_H -#define TEXTMASKMATERIAL_H +#ifndef QSGDEFAULTGLYPHNODE_P_P_H +#define QSGDEFAULTGLYPHNODE_P_P_H #include #include @@ -93,4 +93,4 @@ private: QT_END_NAMESPACE -#endif // TEXTMASKMATERIAL_H +#endif diff --git a/src/quick/scenegraph/qsgdefaultimagenode_p.h b/src/quick/scenegraph/qsgdefaultimagenode_p.h index d2b3baae79..7d299faee3 100644 --- a/src/quick/scenegraph/qsgdefaultimagenode_p.h +++ b/src/quick/scenegraph/qsgdefaultimagenode_p.h @@ -40,8 +40,8 @@ ****************************************************************************/ -#ifndef DEFAULT_PIXMAPNODE_H -#define DEFAULT_PIXMAPNODE_H +#ifndef QSGDEFAULTIMAGENODE_P_H +#define QSGDEFAULTIMAGENODE_P_H #include #include diff --git a/src/quick/scenegraph/qsgdefaultrectanglenode_p.h b/src/quick/scenegraph/qsgdefaultrectanglenode_p.h index e4560784fd..24bdbb3d34 100644 --- a/src/quick/scenegraph/qsgdefaultrectanglenode_p.h +++ b/src/quick/scenegraph/qsgdefaultrectanglenode_p.h @@ -40,8 +40,8 @@ ****************************************************************************/ -#ifndef DEFAULT_RECTANGLENODE_H -#define DEFAULT_RECTANGLENODE_H +#ifndef QSGDEFAULTRECTANGLENODE_P_H +#define QSGDEFAULTRECTANGLENODE_P_H #include diff --git a/src/quick/scenegraph/qsgdistancefieldglyphnode_p.h b/src/quick/scenegraph/qsgdistancefieldglyphnode_p.h index b75a123d34..4f03ab52e2 100644 --- a/src/quick/scenegraph/qsgdistancefieldglyphnode_p.h +++ b/src/quick/scenegraph/qsgdistancefieldglyphnode_p.h @@ -39,8 +39,8 @@ ** ****************************************************************************/ -#ifndef DISTANCEFIELD_GLYPHNODE_H -#define DISTANCEFIELD_GLYPHNODE_H +#ifndef QSGDISTANCEFIELDGLYPHNODE_P_H +#define QSGDISTANCEFIELDGLYPHNODE_P_H #include #include @@ -112,4 +112,4 @@ private: QT_END_NAMESPACE -#endif // DISTANCEFIELD_GLYPHNODE_H +#endif diff --git a/src/quick/scenegraph/qsgdistancefieldglyphnode_p_p.h b/src/quick/scenegraph/qsgdistancefieldglyphnode_p_p.h index fe66776896..d6aa38affa 100644 --- a/src/quick/scenegraph/qsgdistancefieldglyphnode_p_p.h +++ b/src/quick/scenegraph/qsgdistancefieldglyphnode_p_p.h @@ -39,8 +39,8 @@ ** ****************************************************************************/ -#ifndef DISTANCEFIELDTEXTMATERIAL_H -#define DISTANCEFIELDTEXTMATERIAL_H +#ifndef QSGDISTANCEFIELDGLYPHNODE_P_P_H +#define QSGDISTANCEFIELDGLYPHNODE_P_P_H #include #include "qsgdistancefieldglyphnode_p.h" @@ -141,4 +141,4 @@ public: QT_END_NAMESPACE -#endif // DISTANCEFIELDTEXTMATERIAL_H +#endif diff --git a/src/quick/scenegraph/util/qsgareaallocator_p.h b/src/quick/scenegraph/util/qsgareaallocator_p.h index 4ec628288b..1255001d0c 100644 --- a/src/quick/scenegraph/util/qsgareaallocator_p.h +++ b/src/quick/scenegraph/util/qsgareaallocator_p.h @@ -39,8 +39,8 @@ ** ****************************************************************************/ -#ifndef AREAALLOCATOR_H -#define AREAALLOCATOR_H +#ifndef QSGAREAALLOCATOR_P_H +#define QSGAREAALLOCATOR_P_H #include #include diff --git a/src/quick/scenegraph/util/qsgflatcolormaterial.h b/src/quick/scenegraph/util/qsgflatcolormaterial.h index 27cf256603..f0a3a3741d 100644 --- a/src/quick/scenegraph/util/qsgflatcolormaterial.h +++ b/src/quick/scenegraph/util/qsgflatcolormaterial.h @@ -39,8 +39,8 @@ ** ****************************************************************************/ -#ifndef FLATCOLORMATERIAL_H -#define FLATCOLORMATERIAL_H +#ifndef QSGFLATCOLORMATERIAL_H +#define QSGFLATCOLORMATERIAL_H #include #include diff --git a/src/quick/scenegraph/util/qsgsimplerectnode.h b/src/quick/scenegraph/util/qsgsimplerectnode.h index 856e6793bc..bed2d98078 100644 --- a/src/quick/scenegraph/util/qsgsimplerectnode.h +++ b/src/quick/scenegraph/util/qsgsimplerectnode.h @@ -39,8 +39,8 @@ ** ****************************************************************************/ -#ifndef SOLIDRECTNODE_H -#define SOLIDRECTNODE_H +#ifndef QSGSIMPLERECTNODE_H +#define QSGSIMPLERECTNODE_H #include #include diff --git a/src/quick/scenegraph/util/qsgtexturematerial.h b/src/quick/scenegraph/util/qsgtexturematerial.h index cbc4f7d51f..ee4f7cc2e4 100644 --- a/src/quick/scenegraph/util/qsgtexturematerial.h +++ b/src/quick/scenegraph/util/qsgtexturematerial.h @@ -39,8 +39,8 @@ ** ****************************************************************************/ -#ifndef TEXTUREMATERIAL_H -#define TEXTUREMATERIAL_H +#ifndef QSGTEXTUREMATERIAL_H +#define QSGTEXTUREMATERIAL_H #include #include @@ -92,4 +92,4 @@ public: QT_END_NAMESPACE -#endif // TEXTUREMATERIAL_H +#endif diff --git a/src/quick/scenegraph/util/qsgvertexcolormaterial.h b/src/quick/scenegraph/util/qsgvertexcolormaterial.h index a3ffa48c31..81da0650f1 100644 --- a/src/quick/scenegraph/util/qsgvertexcolormaterial.h +++ b/src/quick/scenegraph/util/qsgvertexcolormaterial.h @@ -39,8 +39,8 @@ ** ****************************************************************************/ -#ifndef VERTEXCOLORMATERIAL_H -#define VERTEXCOLORMATERIAL_H +#ifndef QSGVERTEXCOLORMATERIAL_H +#define QSGVERTEXCOLORMATERIAL_H #include -- cgit v1.2.3 From af00d212755c2bf6e1542afc66eb706a869071c8 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Tue, 26 Feb 2013 15:20:46 +0100 Subject: Allow shortcut overrides. When delivering shortcut events, make it possible for the current active focus item to override the focus. The use case is having an edit/copy menu (ctrl+c) and a text edit. When the text edit is focused, it should override the global shortcut. Change-Id: Ide5b94832289c32762ef7304cdc94c107301d579 Reviewed-by: Gabriel de Dietrich --- src/quick/items/qquickwindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 9acd24cae3..caf60dc30f 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -1189,7 +1189,7 @@ void QQuickWindow::keyPressEvent(QKeyEvent *e) #ifndef QT_NO_SHORTCUT // Try looking for a Shortcut before sending key events - if (QGuiApplicationPrivate::instance()->shortcutMap.tryShortcutEvent(this, e)) + if (QGuiApplicationPrivate::instance()->shortcutMap.tryShortcutEvent(this->activeFocusItem(), e)) return; #endif -- cgit v1.2.3 From fa130d2e20b1b0d8048bff154243b9ddce0d1236 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Fri, 1 Mar 2013 15:49:09 +0100 Subject: skip failing constantUpdatesOnWindow test for now This test tries to render some frames and count them; but even 10 frames in one second seems to be too many to expect on the CI Macs. Change-Id: Id9cd700499101ca10afeddee89bc183fef7d30d3 Reviewed-by: Gunnar Sletta --- tests/auto/quick/qquickwindow/tst_qquickwindow.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp index b2e07e4c13..2d3c8f7ca7 100644 --- a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp +++ b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp @@ -366,6 +366,7 @@ void tst_qquickwindow::constantUpdatesOnWindow_data() void tst_qquickwindow::constantUpdatesOnWindow() { + QSKIP("This test fails frequently on the present overworked CI mac machines"); QFETCH(bool, blockedGui); QFETCH(QByteArray, signal); -- cgit v1.2.3 From 9ae561d7fe7513fe682b48dfdda3932f2533b829 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Thu, 28 Feb 2013 12:41:03 -0800 Subject: Fix the QtQml's use of plugins for value type providers When the QtQuick library is loaded, it runs a global constructor that installs the QtQuick value providers. But those providers are never uninstalled when the library is unloaded, resulting in a dangling pointer stored in QtQml's singly-linked list of value providers. Since cafb02911a29b98ac2652fde64e95870e70fd547, QLibrary will unload plugins after inspecting their plugin metadata on Mac and Windows. If QtQml is trying to load a plugin that links to QtQuick (in particular, *the* qtquick2plugin plugin), it would cause the value provider to go stale. To make matters worse, it's quite likely that the plugin would get loaded soon after, at the same address in memory, which causes the valueTypeProvider list to become cyclic. Change-Id: I6f4db5475ceeaba766d9e9c78f86266c9a65806a Reviewed-by: Gunnar Sletta --- src/qml/qml/qqmlglobal.cpp | 33 +++++++++++++++++++++++++++++++-- src/qml/qml/qqmlglobal_p.h | 1 + 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/qml/qml/qqmlglobal.cpp b/src/qml/qml/qqmlglobal.cpp index 5d4b2a567b..379a168110 100644 --- a/src/qml/qml/qqmlglobal.cpp +++ b/src/qml/qml/qqmlglobal.cpp @@ -54,6 +54,7 @@ QQmlValueTypeProvider::QQmlValueTypeProvider() QQmlValueTypeProvider::~QQmlValueTypeProvider() { + QQml_removeValueTypeProvider(this); } QQmlValueType *QQmlValueTypeProvider::createValueType(int type) @@ -266,13 +267,13 @@ bool QQmlValueTypeProvider::store(int, const void *, void *, size_t) { return fa bool QQmlValueTypeProvider::read(int, const void *, size_t, int, void *) { return false; } bool QQmlValueTypeProvider::write(int, const void *, void *, size_t) { return false; } +Q_GLOBAL_STATIC(QQmlValueTypeProvider, nullValueTypeProvider) static QQmlValueTypeProvider *valueTypeProvider = 0; static QQmlValueTypeProvider **getValueTypeProvider(void) { if (valueTypeProvider == 0) { - static QQmlValueTypeProvider nullValueTypeProvider; - valueTypeProvider = &nullValueTypeProvider; + valueTypeProvider = nullValueTypeProvider; } return &valueTypeProvider; @@ -285,6 +286,34 @@ Q_QML_PRIVATE_EXPORT void QQml_addValueTypeProvider(QQmlValueTypeProvider *newPr *providerPtr = newProvider; } +Q_QML_PRIVATE_EXPORT void QQml_removeValueTypeProvider(QQmlValueTypeProvider *oldProvider) +{ + if (oldProvider == nullValueTypeProvider) { + // don't remove the null provider + // we get here when the QtQml library is being unloaded + return; + } + + // the only entry with next = 0 is the null provider + Q_ASSERT(oldProvider->next); + + QQmlValueTypeProvider *prev = valueTypeProvider; + if (prev == oldProvider) { + valueTypeProvider = oldProvider->next; + return; + } + + // singly-linked list removal + for ( ; prev; prev = prev->next) { + if (prev->next != oldProvider) + continue; // this is not the provider you're looking for + prev->next = oldProvider->next; + return; + } + + qWarning("QQml_removeValueTypeProvider: was asked to remove provider %p but it was not found", oldProvider); +} + Q_AUTOTEST_EXPORT QQmlValueTypeProvider *QQml_valueTypeProvider(void) { static QQmlValueTypeProvider **providerPtr = getValueTypeProvider(); diff --git a/src/qml/qml/qqmlglobal_p.h b/src/qml/qml/qqmlglobal_p.h index 0ce026a558..f6b2c81536 100644 --- a/src/qml/qml/qqmlglobal_p.h +++ b/src/qml/qml/qqmlglobal_p.h @@ -271,6 +271,7 @@ private: virtual bool write(int, const void *, void *, size_t); friend Q_QML_PRIVATE_EXPORT void QQml_addValueTypeProvider(QQmlValueTypeProvider *); + friend Q_QML_PRIVATE_EXPORT void QQml_removeValueTypeProvider(QQmlValueTypeProvider *); QQmlValueTypeProvider *next; }; -- cgit v1.2.3 From b1f8c84eb5ddaea9dcbe58ed174106802080ff21 Mon Sep 17 00:00:00 2001 From: Gunnar Sletta Date: Tue, 26 Feb 2013 14:56:43 +0100 Subject: Fix docs for Canvas.renderStrategy, the default is Cooperative MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task-number: QTBUG-29833 Change-Id: I3ee680d2decb01ff514852b2445617e6ab1b1480 Reviewed-by: Samuel Rødal --- src/quick/items/context2d/qquickcanvasitem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/quick/items/context2d/qquickcanvasitem.cpp b/src/quick/items/context2d/qquickcanvasitem.cpp index ea49d58af3..9d9ddd6ef0 100644 --- a/src/quick/items/context2d/qquickcanvasitem.cpp +++ b/src/quick/items/context2d/qquickcanvasitem.cpp @@ -246,7 +246,7 @@ QQuickCanvasItemPrivate::~QQuickCanvasItemPrivate() results in faster rendering. The default render target is Canvas.Image and the default renderStrategy is - Canvas.Threaded. + Canvas.Cooperative. \section1 Tiled Canvas The Canvas item supports tiled rendering by setting \l canvasSize, \l tileSize -- cgit v1.2.3 From 15e58df3386b5438f6ae114a51ad4a258085e70b Mon Sep 17 00:00:00 2001 From: Gunnar Sletta Date: Wed, 27 Feb 2013 08:44:47 +0100 Subject: Added examples on how to use FBOs with scene graph. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I also cleaned up the naming of the other scene graph examples a bit, so that they sort together in the samples list and have a more obvious naming scheme. Task-number: QTBUG-29548 Change-Id: I455eacb02c06058a6d49e12e4f1813ec80b655f6 Reviewed-by: Samuel Rødal --- .../customgeometry/doc/src/customgeometry.qdoc | 4 +- .../openglunderqml/doc/src/openglunderqml.qdoc | 2 +- examples/quick/scenegraph/scenegraph.pro | 7 +- examples/quick/scenegraph/shared/logorenderer.cpp | 258 +++++++++++++++++++++ examples/quick/scenegraph/shared/logorenderer.h | 77 ++++++ .../simplematerial/doc/src/simplematerial.qdoc | 2 +- .../doc/images/textureinsgnode-example.jpg | Bin 0 -> 25863 bytes .../textureinsgnode/doc/src/textureinsgnode.qdoc | 36 +++ .../scenegraph/textureinsgnode/fboinsgrenderer.cpp | 129 +++++++++++ .../scenegraph/textureinsgnode/fboinsgrenderer.h | 59 +++++ examples/quick/scenegraph/textureinsgnode/main.cpp | 59 +++++ examples/quick/scenegraph/textureinsgnode/main.qml | 132 +++++++++++ .../scenegraph/textureinsgnode/textureinsgnode.pro | 16 ++ .../scenegraph/textureinsgnode/textureinsgnode.qrc | 5 + .../doc/images/textureinthread-example.jpg | Bin 0 -> 33269 bytes .../textureinthread/doc/src/textureinthread.qdoc | 36 +++ examples/quick/scenegraph/textureinthread/main.cpp | 59 +++++ examples/quick/scenegraph/textureinthread/main.qml | 132 +++++++++++ .../scenegraph/textureinthread/textureinthread.pro | 16 ++ .../scenegraph/textureinthread/textureinthread.qrc | 5 + .../scenegraph/textureinthread/threadrenderer.cpp | 257 ++++++++++++++++++++ .../scenegraph/textureinthread/threadrenderer.h | 62 +++++ .../doc/src/concepts/visualcanvas/scenegraph.qdoc | 26 +-- src/quick/items/qquickwindow.cpp | 2 +- src/quick/scenegraph/coreapi/qsggeometry.cpp | 2 +- src/quick/scenegraph/util/qsgsimplematerial.cpp | 2 +- src/quick/scenegraph/util/qsgtexture.cpp | 2 + 27 files changed, 1365 insertions(+), 22 deletions(-) create mode 100644 examples/quick/scenegraph/shared/logorenderer.cpp create mode 100644 examples/quick/scenegraph/shared/logorenderer.h create mode 100644 examples/quick/scenegraph/textureinsgnode/doc/images/textureinsgnode-example.jpg create mode 100644 examples/quick/scenegraph/textureinsgnode/doc/src/textureinsgnode.qdoc create mode 100644 examples/quick/scenegraph/textureinsgnode/fboinsgrenderer.cpp create mode 100644 examples/quick/scenegraph/textureinsgnode/fboinsgrenderer.h create mode 100644 examples/quick/scenegraph/textureinsgnode/main.cpp create mode 100644 examples/quick/scenegraph/textureinsgnode/main.qml create mode 100644 examples/quick/scenegraph/textureinsgnode/textureinsgnode.pro create mode 100644 examples/quick/scenegraph/textureinsgnode/textureinsgnode.qrc create mode 100644 examples/quick/scenegraph/textureinthread/doc/images/textureinthread-example.jpg create mode 100644 examples/quick/scenegraph/textureinthread/doc/src/textureinthread.qdoc create mode 100644 examples/quick/scenegraph/textureinthread/main.cpp create mode 100644 examples/quick/scenegraph/textureinthread/main.qml create mode 100644 examples/quick/scenegraph/textureinthread/textureinthread.pro create mode 100644 examples/quick/scenegraph/textureinthread/textureinthread.qrc create mode 100644 examples/quick/scenegraph/textureinthread/threadrenderer.cpp create mode 100644 examples/quick/scenegraph/textureinthread/threadrenderer.h diff --git a/examples/quick/scenegraph/customgeometry/doc/src/customgeometry.qdoc b/examples/quick/scenegraph/customgeometry/doc/src/customgeometry.qdoc index cc034e34ba..d99deb754f 100644 --- a/examples/quick/scenegraph/customgeometry/doc/src/customgeometry.qdoc +++ b/examples/quick/scenegraph/customgeometry/doc/src/customgeometry.qdoc @@ -27,11 +27,11 @@ /*! \example quick/scenegraph/customgeometry - \title Custom Geometry Example + \title Scene Graph - Custom Geometry \ingroup qtquickexamples \brief Shows how to implement a custom geometry in the Qt Quick Scene Graph. - \brief The custom geometry example shows how to create a QQuickItem which + The custom geometry example shows how to create a QQuickItem which uses the scene graph API to build a custom geometry for the scene graph. It does this by creating a BezierCurve item which is made part of the CustomGeometry module and makes use of this in a QML diff --git a/examples/quick/scenegraph/openglunderqml/doc/src/openglunderqml.qdoc b/examples/quick/scenegraph/openglunderqml/doc/src/openglunderqml.qdoc index 5ded717e5f..49f1a825e1 100644 --- a/examples/quick/scenegraph/openglunderqml/doc/src/openglunderqml.qdoc +++ b/examples/quick/scenegraph/openglunderqml/doc/src/openglunderqml.qdoc @@ -27,7 +27,7 @@ /*! \example quick/scenegraph/openglunderqml - \title OpenGL Under QML + \title Scene Graph - OpenGL Under QML \ingroup qtquickexamples \brief Shows how to render OpenGL under a Qt Quick scene. diff --git a/examples/quick/scenegraph/scenegraph.pro b/examples/quick/scenegraph/scenegraph.pro index 88b8d03dc3..3ddb216615 100644 --- a/examples/quick/scenegraph/scenegraph.pro +++ b/examples/quick/scenegraph/scenegraph.pro @@ -1,2 +1,7 @@ TEMPLATE = subdirs -SUBDIRS += customgeometry simplematerial openglunderqml +SUBDIRS += \ + customgeometry \ + openglunderqml \ + simplematerial \ + textureinsgnode \ + textureinthread \ diff --git a/examples/quick/scenegraph/shared/logorenderer.cpp b/examples/quick/scenegraph/shared/logorenderer.cpp new file mode 100644 index 0000000000..4c5927b543 --- /dev/null +++ b/examples/quick/scenegraph/shared/logorenderer.cpp @@ -0,0 +1,258 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "logorenderer.h" +#include +#include +#include + +LogoRenderer::LogoRenderer() +{ +} + +LogoRenderer::~LogoRenderer() +{ +} + + +void LogoRenderer::paintQtLogo() +{ + program1.enableAttributeArray(normalAttr1); + program1.enableAttributeArray(vertexAttr1); + program1.setAttributeArray(vertexAttr1, vertices.constData()); + program1.setAttributeArray(normalAttr1, normals.constData()); + glDrawArrays(GL_TRIANGLES, 0, vertices.size()); + program1.disableAttributeArray(normalAttr1); + program1.disableAttributeArray(vertexAttr1); +} + + +void LogoRenderer::initialize() +{ + glClearColor(0.1f, 0.1f, 0.2f, 1.0f); + + QOpenGLShader *vshader1 = new QOpenGLShader(QOpenGLShader::Vertex, &program1); + const char *vsrc1 = + "attribute highp vec4 vertex;\n" + "attribute mediump vec3 normal;\n" + "uniform mediump mat4 matrix;\n" + "varying mediump vec4 color;\n" + "void main(void)\n" + "{\n" + " vec3 toLight = normalize(vec3(0.0, 0.3, 1.0));\n" + " float angle = max(dot(normal, toLight), 0.0);\n" + " vec3 col = vec3(0.40, 1.0, 0.0);\n" + " color = vec4(col * 0.2 + col * 0.8 * angle, 1.0);\n" + " color = clamp(color, 0.0, 1.0);\n" + " gl_Position = matrix * vertex;\n" + "}\n"; + vshader1->compileSourceCode(vsrc1); + + QOpenGLShader *fshader1 = new QOpenGLShader(QOpenGLShader::Fragment, &program1); + const char *fsrc1 = + "varying mediump vec4 color;\n" + "void main(void)\n" + "{\n" + " gl_FragColor = color;\n" + "}\n"; + fshader1->compileSourceCode(fsrc1); + + program1.addShader(vshader1); + program1.addShader(fshader1); + program1.link(); + + vertexAttr1 = program1.attributeLocation("vertex"); + normalAttr1 = program1.attributeLocation("normal"); + matrixUniform1 = program1.uniformLocation("matrix"); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + + m_fAngle = 0; + m_fScale = 1; + createGeometry(); +} + +void LogoRenderer::render() +{ + glDepthMask(true); + + glClearColor(0.5f, 0.5f, 0.7f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + + glFrontFace(GL_CW); + glCullFace(GL_FRONT); + glEnable(GL_CULL_FACE); + glEnable(GL_DEPTH_TEST); + + QMatrix4x4 modelview; + modelview.rotate(m_fAngle, 0.0f, 1.0f, 0.0f); + modelview.rotate(m_fAngle, 1.0f, 0.0f, 0.0f); + modelview.rotate(m_fAngle, 0.0f, 0.0f, 1.0f); + modelview.scale(m_fScale); + modelview.translate(0.0f, -0.2f, 0.0f); + + program1.bind(); + program1.setUniformValue(matrixUniform1, modelview); + paintQtLogo(); + program1.release(); + + glDisable(GL_DEPTH_TEST); + glDisable(GL_CULL_FACE); + + m_fAngle += 1.0f; +} + +void LogoRenderer::createGeometry() +{ + vertices.clear(); + normals.clear(); + + qreal x1 = +0.06f; + qreal y1 = -0.14f; + qreal x2 = +0.14f; + qreal y2 = -0.06f; + qreal x3 = +0.08f; + qreal y3 = +0.00f; + qreal x4 = +0.30f; + qreal y4 = +0.22f; + + quad(x1, y1, x2, y2, y2, x2, y1, x1); + quad(x3, y3, x4, y4, y4, x4, y3, x3); + + extrude(x1, y1, x2, y2); + extrude(x2, y2, y2, x2); + extrude(y2, x2, y1, x1); + extrude(y1, x1, x1, y1); + extrude(x3, y3, x4, y4); + extrude(x4, y4, y4, x4); + extrude(y4, x4, y3, x3); + + const qreal Pi = 3.14159f; + const int NumSectors = 100; + + for (int i = 0; i < NumSectors; ++i) { + qreal angle1 = (i * 2 * Pi) / NumSectors; + qreal x5 = 0.30 * sin(angle1); + qreal y5 = 0.30 * cos(angle1); + qreal x6 = 0.20 * sin(angle1); + qreal y6 = 0.20 * cos(angle1); + + qreal angle2 = ((i + 1) * 2 * Pi) / NumSectors; + qreal x7 = 0.20 * sin(angle2); + qreal y7 = 0.20 * cos(angle2); + qreal x8 = 0.30 * sin(angle2); + qreal y8 = 0.30 * cos(angle2); + + quad(x5, y5, x6, y6, x7, y7, x8, y8); + + extrude(x6, y6, x7, y7); + extrude(x8, y8, x5, y5); + } + + for (int i = 0;i < vertices.size();i++) + vertices[i] *= 2.0f; +} + +void LogoRenderer::quad(qreal x1, qreal y1, qreal x2, qreal y2, qreal x3, qreal y3, qreal x4, qreal y4) +{ + vertices << QVector3D(x1, y1, -0.05f); + vertices << QVector3D(x2, y2, -0.05f); + vertices << QVector3D(x4, y4, -0.05f); + + vertices << QVector3D(x3, y3, -0.05f); + vertices << QVector3D(x4, y4, -0.05f); + vertices << QVector3D(x2, y2, -0.05f); + + QVector3D n = QVector3D::normal + (QVector3D(x2 - x1, y2 - y1, 0.0f), QVector3D(x4 - x1, y4 - y1, 0.0f)); + + normals << n; + normals << n; + normals << n; + + normals << n; + normals << n; + normals << n; + + vertices << QVector3D(x4, y4, 0.05f); + vertices << QVector3D(x2, y2, 0.05f); + vertices << QVector3D(x1, y1, 0.05f); + + vertices << QVector3D(x2, y2, 0.05f); + vertices << QVector3D(x4, y4, 0.05f); + vertices << QVector3D(x3, y3, 0.05f); + + n = QVector3D::normal + (QVector3D(x2 - x4, y2 - y4, 0.0f), QVector3D(x1 - x4, y1 - y4, 0.0f)); + + normals << n; + normals << n; + normals << n; + + normals << n; + normals << n; + normals << n; +} + +void LogoRenderer::extrude(qreal x1, qreal y1, qreal x2, qreal y2) +{ + vertices << QVector3D(x1, y1, +0.05f); + vertices << QVector3D(x2, y2, +0.05f); + vertices << QVector3D(x1, y1, -0.05f); + + vertices << QVector3D(x2, y2, -0.05f); + vertices << QVector3D(x1, y1, -0.05f); + vertices << QVector3D(x2, y2, +0.05f); + + QVector3D n = QVector3D::normal + (QVector3D(x2 - x1, y2 - y1, 0.0f), QVector3D(0.0f, 0.0f, -0.1f)); + + normals << n; + normals << n; + normals << n; + + normals << n; + normals << n; + normals << n; +} diff --git a/examples/quick/scenegraph/shared/logorenderer.h b/examples/quick/scenegraph/shared/logorenderer.h new file mode 100644 index 0000000000..48fbf87203 --- /dev/null +++ b/examples/quick/scenegraph/shared/logorenderer.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef LOGORENDERER_H +#define LOGORENDERER_H + +#include +#include +#include + +#include +#include + +class LogoRenderer { + +public: + LogoRenderer(); + ~LogoRenderer(); + + void render(); + void initialize(); + +private: + + qreal m_fAngle; + qreal m_fScale; + + void paintQtLogo(); + void createGeometry(); + void quad(qreal x1, qreal y1, qreal x2, qreal y2, qreal x3, qreal y3, qreal x4, qreal y4); + void extrude(qreal x1, qreal y1, qreal x2, qreal y2); + + QVector vertices; + QVector normals; + QOpenGLShaderProgram program1; + int vertexAttr1; + int normalAttr1; + int matrixUniform1; +}; +#endif diff --git a/examples/quick/scenegraph/simplematerial/doc/src/simplematerial.qdoc b/examples/quick/scenegraph/simplematerial/doc/src/simplematerial.qdoc index 20d244fe99..97cc4677d5 100644 --- a/examples/quick/scenegraph/simplematerial/doc/src/simplematerial.qdoc +++ b/examples/quick/scenegraph/simplematerial/doc/src/simplematerial.qdoc @@ -27,7 +27,7 @@ /*! \example quick/scenegraph/simplematerial - \title Simple Material Example + \title Scene Graph - Simple Material \ingroup qtquickexamples \brief Shows how to define a scene graph material to fill a shape. diff --git a/examples/quick/scenegraph/textureinsgnode/doc/images/textureinsgnode-example.jpg b/examples/quick/scenegraph/textureinsgnode/doc/images/textureinsgnode-example.jpg new file mode 100644 index 0000000000..306b8bab20 Binary files /dev/null and b/examples/quick/scenegraph/textureinsgnode/doc/images/textureinsgnode-example.jpg differ diff --git a/examples/quick/scenegraph/textureinsgnode/doc/src/textureinsgnode.qdoc b/examples/quick/scenegraph/textureinsgnode/doc/src/textureinsgnode.qdoc new file mode 100644 index 0000000000..b24b0bd97b --- /dev/null +++ b/examples/quick/scenegraph/textureinsgnode/doc/src/textureinsgnode.qdoc @@ -0,0 +1,36 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: http://www.gnu.org/copyleft/fdl.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \example quick/scenegraph/textureinsgnode + \title Scene Graph - Rendering FBOs + \ingroup qtquickexamples + + \brief Shows how to use FramebufferObjects with Qt Quick. + + \image textureinsgnode-example.jpg + */ diff --git a/examples/quick/scenegraph/textureinsgnode/fboinsgrenderer.cpp b/examples/quick/scenegraph/textureinsgnode/fboinsgrenderer.cpp new file mode 100644 index 0000000000..67df392555 --- /dev/null +++ b/examples/quick/scenegraph/textureinsgnode/fboinsgrenderer.cpp @@ -0,0 +1,129 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "fboinsgrenderer.h" +#include "logorenderer.h" + +#include + +#include +#include + + + + +class TextureNode : public QObject, public QSGSimpleTextureNode +{ + Q_OBJECT + +public: + TextureNode(QQuickWindow *window) + : m_fbo(0) + , m_texture(0) + , m_window(window) + , m_logoRenderer(0) + { + connect(m_window, SIGNAL(beforeRendering()), this, SLOT(renderFBO())); + } + + ~TextureNode() + { + delete m_texture; + delete m_fbo; + delete m_logoRenderer; + } + +public slots: + void renderFBO() + { + QSize size = rect().size().toSize(); + + if (!m_fbo) { + + QOpenGLFramebufferObjectFormat format; + format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil); + m_fbo = new QOpenGLFramebufferObject(size, format); + m_texture = m_window->createTextureFromId(m_fbo->texture(), size); + m_logoRenderer = new LogoRenderer(); + m_logoRenderer->initialize(); + setTexture(m_texture); + } + + m_fbo->bind(); + + glViewport(0, 0, size.width(), size.height()); + + m_logoRenderer->render(); + + m_fbo->bindDefault(); + + m_window->update(); + } + +private: + QOpenGLFramebufferObject *m_fbo; + QSGTexture *m_texture; + QQuickWindow *m_window; + + LogoRenderer *m_logoRenderer; +}; + + + +FboInSGRenderer::FboInSGRenderer() +{ + setFlag(ItemHasContents, true); +} + + +QSGNode *FboInSGRenderer::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) +{ + // Don't bother with resize and such, just recreate the node from scratch + // when geometry changes. + if (oldNode) + delete oldNode; + + TextureNode *node = new TextureNode(window()); + node->setRect(boundingRect()); + + return node; +} + +#include "fboinsgrenderer.moc" diff --git a/examples/quick/scenegraph/textureinsgnode/fboinsgrenderer.h b/examples/quick/scenegraph/textureinsgnode/fboinsgrenderer.h new file mode 100644 index 0000000000..42e10aef6d --- /dev/null +++ b/examples/quick/scenegraph/textureinsgnode/fboinsgrenderer.h @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef FBOINSGRENDERER_H +#define FBOINSGRENDERER_H + +#include + + +class FboInSGRenderer : public QQuickItem +{ + Q_OBJECT + +public: + FboInSGRenderer(); + +protected: + QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *); + +}; + +#endif diff --git a/examples/quick/scenegraph/textureinsgnode/main.cpp b/examples/quick/scenegraph/textureinsgnode/main.cpp new file mode 100644 index 0000000000..8fa6f6814f --- /dev/null +++ b/examples/quick/scenegraph/textureinsgnode/main.cpp @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include + +#include "fboinsgrenderer.h" + +int main(int argc, char **argv) +{ + QGuiApplication app(argc, argv); + + qmlRegisterType("SceneGraphRendering", 1, 0, "Renderer"); + + QQuickView view; + view.setResizeMode(QQuickView::SizeRootObjectToView); + view.setSource(QUrl("qrc:///scenegraph/textureinsgnode/main.qml")); + view.show(); + + return app.exec(); +} diff --git a/examples/quick/scenegraph/textureinsgnode/main.qml b/examples/quick/scenegraph/textureinsgnode/main.qml new file mode 100644 index 0000000000..6f6d7d1c1a --- /dev/null +++ b/examples/quick/scenegraph/textureinsgnode/main.qml @@ -0,0 +1,132 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 + +import SceneGraphRendering 1.0 + +Item { + width: 400 + height: 400 + + // The checkers background + ShaderEffect { + id: tileBackground + anchors.fill: parent + + property real tileSize: 16 + property color color1: Qt.rgba(0.9, 0.9, 0.9, 1); + property color color2: Qt.rgba(0.85, 0.85, 0.85, 1); + + property size pixelSize: Qt.size(width / tileSize, height / tileSize); + + fragmentShader: + " + uniform lowp vec4 color1; + uniform lowp vec4 color2; + uniform highp vec2 pixelSize; + varying highp vec2 qt_TexCoord0; + void main() { + highp vec2 tc = sign(sin(3.14152 * qt_TexCoord0 * pixelSize)); + if (tc.x != tc.y) + gl_FragColor = color1; + else + gl_FragColor = color2; + } + " + } + + Renderer { + id: renderer + anchors.fill: parent + anchors.margins: 10 + + // The transform is just to show something interesting.. + transform: [ + Rotation { id: rotation; axis.x: 0; axis.z: 0; axis.y: 1; angle: 0; origin.x: renderer.width / 2; origin.y: renderer.height / 2; }, + Translate { id: txOut; x: -renderer.width / 2; y: -renderer.height / 2 }, + Scale { id: scale; }, + Translate { id: txIn; x: renderer.width / 2; y: renderer.height / 2 } + ] + } + + // Just to show something interesting + SequentialAnimation { + PauseAnimation { duration: 5000 } + ParallelAnimation { + NumberAnimation { target: scale; property: "xScale"; to: 0.6; duration: 1000; easing.type: Easing.InOutBack } + NumberAnimation { target: scale; property: "yScale"; to: 0.6; duration: 1000; easing.type: Easing.InOutBack } + } + NumberAnimation { target: rotation; property: "angle"; to: 80; duration: 1000; easing.type: Easing.InOutCubic } + NumberAnimation { target: rotation; property: "angle"; to: -80; duration: 1000; easing.type: Easing.InOutCubic } + NumberAnimation { target: rotation; property: "angle"; to: 0; duration: 1000; easing.type: Easing.InOutCubic } + NumberAnimation { target: renderer; property: "opacity"; to: 0.5; duration: 1000; easing.type: Easing.InOutCubic } + PauseAnimation { duration: 1000 } + NumberAnimation { target: renderer; property: "opacity"; to: 0.8; duration: 1000; easing.type: Easing.InOutCubic } + ParallelAnimation { + NumberAnimation { target: scale; property: "xScale"; to: 1; duration: 1000; easing.type: Easing.InOutBack } + NumberAnimation { target: scale; property: "yScale"; to: 1; duration: 1000; easing.type: Easing.InOutBack } + } + running: true + loops: Animation.Infinite + } + + Rectangle { + id: labelFrame + anchors.margins: -10 + radius: 5 + color: "white" + border.color: "black" + opacity: 0.8 + anchors.fill: label + } + + Text { + id: label + anchors.bottom: renderer.bottom + anchors.left: renderer.left + anchors.right: renderer.right + anchors.margins: 20 + wrapMode: Text.WordWrap + text: "The blue rectangle with the vintage 'Q' is an FBO, rendered by the application on the scene graph rendering thread. It is displayed using a QSGSimpleTextureNode." + } + + +} diff --git a/examples/quick/scenegraph/textureinsgnode/textureinsgnode.pro b/examples/quick/scenegraph/textureinsgnode/textureinsgnode.pro new file mode 100644 index 0000000000..238e20a553 --- /dev/null +++ b/examples/quick/scenegraph/textureinsgnode/textureinsgnode.pro @@ -0,0 +1,16 @@ +QT += qml quick + +HEADERS += fboinsgrenderer.h +SOURCES += fboinsgrenderer.cpp main.cpp + +INCLUDEPATH += ../shared +HEADERS += ../shared/logorenderer.h +SOURCES += ../shared/logorenderer.cpp + +RESOURCES += textureinsgnode.qrc + +target.path = $$[QT_INSTALL_EXAMPLES]/quick/scenegraph/textureinsgnode +INSTALLS += target + +OTHER_FILES += \ + main.qml diff --git a/examples/quick/scenegraph/textureinsgnode/textureinsgnode.qrc b/examples/quick/scenegraph/textureinsgnode/textureinsgnode.qrc new file mode 100644 index 0000000000..9ecf0ada1c --- /dev/null +++ b/examples/quick/scenegraph/textureinsgnode/textureinsgnode.qrc @@ -0,0 +1,5 @@ + + + main.qml + + diff --git a/examples/quick/scenegraph/textureinthread/doc/images/textureinthread-example.jpg b/examples/quick/scenegraph/textureinthread/doc/images/textureinthread-example.jpg new file mode 100644 index 0000000000..59134e07c4 Binary files /dev/null and b/examples/quick/scenegraph/textureinthread/doc/images/textureinthread-example.jpg differ diff --git a/examples/quick/scenegraph/textureinthread/doc/src/textureinthread.qdoc b/examples/quick/scenegraph/textureinthread/doc/src/textureinthread.qdoc new file mode 100644 index 0000000000..476605b268 --- /dev/null +++ b/examples/quick/scenegraph/textureinthread/doc/src/textureinthread.qdoc @@ -0,0 +1,36 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: http://www.gnu.org/copyleft/fdl.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \example quick/scenegraph/textureinthread + \title Scene Graph - Rendering FBOs in a thread + \ingroup qtquickexamples + + \brief Shows how to use FramebufferObjects in a thread together with Qt Quick. + + \image textureinthread-example.jpg + */ diff --git a/examples/quick/scenegraph/textureinthread/main.cpp b/examples/quick/scenegraph/textureinthread/main.cpp new file mode 100644 index 0000000000..e415d254a1 --- /dev/null +++ b/examples/quick/scenegraph/textureinthread/main.cpp @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include + +#include "threadrenderer.h" + +int main(int argc, char **argv) +{ + QGuiApplication app(argc, argv); + + qmlRegisterType("SceneGraphRendering", 1, 0, "Renderer"); + + QQuickView view; + view.setResizeMode(QQuickView::SizeRootObjectToView); + view.setSource(QUrl("qrc:///scenegraph/textureinsgnode/main.qml")); + view.show(); + + return app.exec(); +} diff --git a/examples/quick/scenegraph/textureinthread/main.qml b/examples/quick/scenegraph/textureinthread/main.qml new file mode 100644 index 0000000000..4493ec46fd --- /dev/null +++ b/examples/quick/scenegraph/textureinthread/main.qml @@ -0,0 +1,132 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 + +import SceneGraphRendering 1.0 + +Item { + width: 400 + height: 400 + + // The checkers background + ShaderEffect { + id: tileBackground + anchors.fill: parent + + property real tileSize: 16 + property color color1: Qt.rgba(0.9, 0.9, 0.9, 1); + property color color2: Qt.rgba(0.85, 0.85, 0.85, 1); + + property size pixelSize: Qt.size(width / tileSize, height / tileSize); + + fragmentShader: + " + uniform lowp vec4 color1; + uniform lowp vec4 color2; + uniform highp vec2 pixelSize; + varying highp vec2 qt_TexCoord0; + void main() { + highp vec2 tc = sign(sin(3.14152 * qt_TexCoord0 * pixelSize)); + if (tc.x != tc.y) + gl_FragColor = color1; + else + gl_FragColor = color2; + } + " + } + + Renderer { + id: renderer + anchors.fill: parent + anchors.margins: 10 + + // The transform is just to show something interesting.. + transform: [ + Rotation { id: rotation; axis.x: 0; axis.z: 0; axis.y: 1; angle: 0; origin.x: renderer.width / 2; origin.y: renderer.height / 2; }, + Translate { id: txOut; x: -renderer.width / 2; y: -renderer.height / 2 }, + Scale { id: scale; }, + Translate { id: txIn; x: renderer.width / 2; y: renderer.height / 2 } + ] + } + + // Just to show something interesting + SequentialAnimation { + PauseAnimation { duration: 5000 } + ParallelAnimation { + NumberAnimation { target: scale; property: "xScale"; to: 0.6; duration: 1000; easing.type: Easing.InOutBack } + NumberAnimation { target: scale; property: "yScale"; to: 0.6; duration: 1000; easing.type: Easing.InOutBack } + } + NumberAnimation { target: rotation; property: "angle"; to: 80; duration: 1000; easing.type: Easing.InOutCubic } + NumberAnimation { target: rotation; property: "angle"; to: -80; duration: 1000; easing.type: Easing.InOutCubic } + NumberAnimation { target: rotation; property: "angle"; to: 0; duration: 1000; easing.type: Easing.InOutCubic } + NumberAnimation { target: renderer; property: "opacity"; to: 0.5; duration: 1000; easing.type: Easing.InOutCubic } + PauseAnimation { duration: 1000 } + NumberAnimation { target: renderer; property: "opacity"; to: 0.8; duration: 1000; easing.type: Easing.InOutCubic } + ParallelAnimation { + NumberAnimation { target: scale; property: "xScale"; to: 1; duration: 1000; easing.type: Easing.InOutBack } + NumberAnimation { target: scale; property: "yScale"; to: 1; duration: 1000; easing.type: Easing.InOutBack } + } + running: true + loops: Animation.Infinite + } + + Rectangle { + id: labelFrame + anchors.margins: -10 + radius: 5 + color: "white" + border.color: "black" + opacity: 0.8 + anchors.fill: label + } + + Text { + id: label + anchors.bottom: renderer.bottom + anchors.left: renderer.left + anchors.right: renderer.right + anchors.margins: 20 + wrapMode: Text.WordWrap + text: "The blue rectangle with the vintage 'Q' is an FBO, rendered by the application in a dedicated background thread. The background thread juggles two FBOs, one that is being rendered to and one for displaying. The texture to display is posted to the scene graph and displayed using a QSGSimpleTextureNode." + } + + +} diff --git a/examples/quick/scenegraph/textureinthread/textureinthread.pro b/examples/quick/scenegraph/textureinthread/textureinthread.pro new file mode 100644 index 0000000000..b48c2a1863 --- /dev/null +++ b/examples/quick/scenegraph/textureinthread/textureinthread.pro @@ -0,0 +1,16 @@ +QT += quick + +HEADERS += threadrenderer.h +SOURCES += threadrenderer.cpp main.cpp + +INCLUDEPATH += ../shared +HEADERS += ../shared/logorenderer.h +SOURCES += ../shared/logorenderer.cpp + +RESOURCES += textureinthread.qrc + +target.path = $$[QT_INSTALL_EXAMPLES]/quick/scenegraph/textureinthread +INSTALLS += target + +OTHER_FILES += \ + main.qml diff --git a/examples/quick/scenegraph/textureinthread/textureinthread.qrc b/examples/quick/scenegraph/textureinthread/textureinthread.qrc new file mode 100644 index 0000000000..9ecf0ada1c --- /dev/null +++ b/examples/quick/scenegraph/textureinthread/textureinthread.qrc @@ -0,0 +1,5 @@ + + + main.qml + + diff --git a/examples/quick/scenegraph/textureinthread/threadrenderer.cpp b/examples/quick/scenegraph/textureinthread/threadrenderer.cpp new file mode 100644 index 0000000000..90b6b49880 --- /dev/null +++ b/examples/quick/scenegraph/textureinthread/threadrenderer.cpp @@ -0,0 +1,257 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "threadrenderer.h" +#include "logorenderer.h" + +#include +#include + +#include +#include + +#include +#include + + +/* + * The render thread shares a context with the scene graph and will + * render into two separate FBOs, one to use for display and one + * to use for rendering + */ +class RenderThread : public QThread +{ + Q_OBJECT +public: + RenderThread(const QSize &size) + : m_renderFbo(0) + , m_displayFbo(0) + , m_logoRenderer(0) + , m_size(size) + { + // Since we're using queued connections, we need affinity to the rendering thread. + moveToThread(this); + + // Set up the QOpenGLContext to use for rendering in this thread. It is sharing + // memory space with the GL context of the scene graph. This constructor is called + // during updatePaintNode, so we are currently on the scene graph thread with the + // scene graph's OpenGL context current. + QOpenGLContext *current = QOpenGLContext::currentContext(); + m_context = new QOpenGLContext(); + m_context->setShareContext(current); + m_context->setFormat(current->format()); + m_context->create(); + m_context->moveToThread(this); + + // We need a non-visible surface to make current... + m_fakeSurface = new QWindow(); + m_fakeSurface->setGeometry(0, 0, 64, 64); + m_fakeSurface->setSurfaceType(QWindow::OpenGLSurface); + m_fakeSurface->setFormat(current->format()); + m_fakeSurface->create(); + } + +public slots: + void renderNext() + { + m_context->makeCurrent(m_fakeSurface); + + if (!m_renderFbo) { + // Initialize the buffers and renderer + QOpenGLFramebufferObjectFormat format; + format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil); + m_renderFbo = new QOpenGLFramebufferObject(m_size, format); + m_displayFbo = new QOpenGLFramebufferObject(m_size, format); + m_logoRenderer = new LogoRenderer(); + m_logoRenderer->initialize(); + } + + m_renderFbo->bind(); + glViewport(0, 0, m_size.width(), m_size.height()); + + m_logoRenderer->render(); + + // We need to flush the contents to the FBO before posting + // the texture to the other thread, otherwise, we might + // get unexpected results. + glFlush(); + + m_renderFbo->bindDefault(); + qSwap(m_renderFbo, m_displayFbo); + + emit textureReady(m_displayFbo->texture(), m_size); + } + +signals: + void textureReady(int id, const QSize &size); + +private: + QOpenGLFramebufferObject *m_renderFbo; + QOpenGLFramebufferObject *m_displayFbo; + + LogoRenderer *m_logoRenderer; + + QWindow *m_fakeSurface; + QOpenGLContext *m_context; + QSize m_size; +}; + + + +class TextureNode : public QObject, public QSGSimpleTextureNode +{ + Q_OBJECT + +public: + TextureNode(QQuickWindow *window) + : m_id(0) + , m_size(0, 0) + , m_texture(0) + , m_window(window) + { + // Our texture node must have a texture, so use the default 0 texture. + m_texture = m_window->createTextureFromId(0, QSize(1, 1)); + setTexture(m_texture); + setFiltering(QSGTexture::Linear); + } + + ~TextureNode() + { + delete m_texture; + } + +signals: + void textureInUse(); + void pendingNewTexture(); + +public slots: + + // This function gets called on the FBO rendering thread and will store the + // texture id and size and schedule an update on the window. + void newTexture(int id, const QSize &size) { + m_mutex.lock(); + m_id = id; + m_size = size; + m_mutex.unlock(); + + // We cannot call QQuickWindow::update directly here, as this is only allowed + // from the rendering thread or GUI thread. + emit pendingNewTexture(); + } + + + // Before the scene graph starts to render, we update to the pending texture + void prepareNode() { + m_mutex.lock(); + int newId = m_id; + QSize size = m_size; + m_id = 0; + m_mutex.unlock(); + if (newId) { + delete m_texture; + m_texture = m_window->createTextureFromId(newId, size); + setTexture(m_texture); + + // This will notify the rendering thread that the texture is now being rendered + // and it can start rendering to the other one. + emit textureInUse(); + } + } + +private: + + int m_id; + QSize m_size; + + QMutex m_mutex; + + QSGTexture *m_texture; + QQuickWindow *m_window; +}; + + + +ThreadRenderer::ThreadRenderer() +{ + setFlag(ItemHasContents, true); +} + + + +QSGNode *ThreadRenderer::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) +{ + TextureNode *node = static_cast(oldNode); + + if (!node) { + node = new TextureNode(window()); + m_renderThread = new RenderThread(QSize(512, 512)); + + /* Set up connections to get the production of FBO textures in sync with vsync on the + * rendering thread. + * + * When a new texture is ready on the rendering thread, we use a direct connection to + * the texture node to let it know a new texture can be used. The node will then + * emit pendingNewTexture which we bind to QQuickWindow::update to schedule a redraw. + * + * When the scene graph starts rendering the next frame, the prepareNode() function + * is used to update the node with the new texture. Once it completes, it emits + * textureInUse() which we connect to the FBO rendering thread's renderNext() to have + * it start producing content into its current "back buffer". + * + * This FBO rendering pipeline is throttled by vsync on the scene graph rendering thread. + */ + connect(m_renderThread, SIGNAL(textureReady(int,QSize)), node, SLOT(newTexture(int,QSize)), Qt::DirectConnection); + connect(node, SIGNAL(pendingNewTexture()), window(), SLOT(update()), Qt::QueuedConnection); + connect(window(), SIGNAL(beforeRendering()), node, SLOT(prepareNode()), Qt::DirectConnection); + connect(node, SIGNAL(textureInUse()), m_renderThread, SLOT(renderNext()), Qt::QueuedConnection); + + // Start the render thread and enter let it process events. + m_renderThread->start(); + + // Get the production of FBO textures started.. + QMetaObject::invokeMethod(m_renderThread, "renderNext", Qt::QueuedConnection); + } + + node->setRect(boundingRect()); + + return node; +} + +#include "threadrenderer.moc" diff --git a/examples/quick/scenegraph/textureinthread/threadrenderer.h b/examples/quick/scenegraph/textureinthread/threadrenderer.h new file mode 100644 index 0000000000..f12e6404e5 --- /dev/null +++ b/examples/quick/scenegraph/textureinthread/threadrenderer.h @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef THREADRENDERER_H +#define THREADRENDERER_H + +#include + +class RenderThread; + +class ThreadRenderer : public QQuickItem +{ + Q_OBJECT + +public: + ThreadRenderer(); + +protected: + QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *); + +private: + RenderThread *m_renderThread; +}; + +#endif diff --git a/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc b/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc index 099c7eb443..40d77c3d9b 100644 --- a/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc +++ b/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc @@ -106,7 +106,7 @@ during the updatePaintNode() call. The rule of thumb is to only use classes with the "QSG" prefix inside the QQuickItem::updatePaintNode() function. -For more details, see the \l {Custom Geometry Example}. +For more details, see the \l {Scene Graph - Custom Geometry}. \section3 Preprocessing @@ -140,7 +140,7 @@ type. Below is a complete list of material classes: \annotatedlist{qtquick-scenegraph-materials} -For more details, see the \l {Simple Material Example} +For more details, see the \l {Scene Graph - Simple Material} \section2 Convenience Nodes @@ -280,21 +280,19 @@ needed to perform the rendering. The downside is that Qt Quick decides when to call the signals and this is the only time the OpenGL application is allowed to draw. -The \l {OpenGL Under QML} example gives an example on how to use use -these signals. - +The \l {Scene Graph - OpenGL Under QML} example gives an example on +how to use use these signals. The other alternative is to create a FramebufferObject, render into it and use the result as a textured node in the scene graph, for instance -using a QSGSimpleTextureNode. A simple way of doing the same is to use -a QQuickPaintedItem with QQuickPaintedItem::FramebufferObject as -render target and by calling QPainter::beginNativePainting() before -the OpenGL rendering and QPainter::endNativePainting() after. When -OpenGL content is integrated with a texture and FramebufferObject, the -application has more control over when the content is rendered. For -instance, the application can create a second QOpenGLContext on the -GUI thread which shares memory with the scene graph's OpenGL context -and drive the rendering manually. +using a QSGSimpleTextureNode. The \l {Scene Graph - Rendering FBOs} +and \l {Scene Graph - Rendering FBOs in a thread} examples show how +this can be done in an optimal manner. + +A simple way of doing the same is to use a QQuickPaintedItem with +QQuickPaintedItem::FramebufferObject as render target and by calling +QPainter::beginNativePainting() before the OpenGL rendering and +QPainter::endNativePainting() after. \warning When mixing OpenGL content with scene graph rendering, it is important the application does not leave the OpenGL context in a state diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index caf60dc30f..efc67c0437 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -909,7 +909,7 @@ void QQuickWindowPrivate::cleanup(QSGNode *n) scene graph and its OpenGL context being deleted. The sceneGraphInvalidated() signal will be emitted when this happens. - \sa {OpenGL Under QML} + \sa {Scene Graph - OpenGL Under QML} */ diff --git a/src/quick/scenegraph/coreapi/qsggeometry.cpp b/src/quick/scenegraph/coreapi/qsggeometry.cpp index 27d4ed413a..818b9b26aa 100644 --- a/src/quick/scenegraph/coreapi/qsggeometry.cpp +++ b/src/quick/scenegraph/coreapi/qsggeometry.cpp @@ -284,7 +284,7 @@ const QSGGeometry::AttributeSet &QSGGeometry::defaultAttributes_ColoredPoint2D() setIndexDataPattern() functions. Whether this hint is respected or not is implementation specific. - \sa QSGGeometryNode, {Custom Geometry Example} + \sa QSGGeometryNode, {Scene Graph - Custom Geometry} */ diff --git a/src/quick/scenegraph/util/qsgsimplematerial.cpp b/src/quick/scenegraph/util/qsgsimplematerial.cpp index bed1b710ca..ee7a272fbe 100644 --- a/src/quick/scenegraph/util/qsgsimplematerial.cpp +++ b/src/quick/scenegraph/util/qsgsimplematerial.cpp @@ -142,7 +142,7 @@ the unique QSGSimpleMaterialShader implementation must be instantiated with a unique C++ type. - \sa {Simple Material Example} + \sa {Scene Graph - Simple Material} */ /*! diff --git a/src/quick/scenegraph/util/qsgtexture.cpp b/src/quick/scenegraph/util/qsgtexture.cpp index 48927ce5e1..1a8c69a474 100644 --- a/src/quick/scenegraph/util/qsgtexture.cpp +++ b/src/quick/scenegraph/util/qsgtexture.cpp @@ -232,6 +232,8 @@ static void qt_debug_remove_texture(QSGTexture* texture) If the texture is used in such a way that atlas is not preferable, the function removedFromAtlas() can be used to extract a non-atlassed copy. + + \sa {Scene Graph - Rendering FBOs}, {Scene Graph - Rendering FBOs in a thread} */ /*! -- cgit v1.2.3 From 84c41e4c7e22774e18d1462fcf4ca907a960900c Mon Sep 17 00:00:00 2001 From: Gunnar Sletta Date: Tue, 26 Feb 2013 16:56:22 +0100 Subject: Do not print out leaked pixmaps by default Change-Id: Id112def9710efa943ffc5350a25d40d4c4f463a1 Reviewed-by: Jens Bache-Wiig --- src/quick/util/qquickpixmapcache.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/quick/util/qquickpixmapcache.cpp b/src/quick/util/qquickpixmapcache.cpp index 7a9bea3aa0..0033be82be 100644 --- a/src/quick/util/qquickpixmapcache.cpp +++ b/src/quick/util/qquickpixmapcache.cpp @@ -78,6 +78,11 @@ QT_BEGIN_NAMESPACE + +#ifndef QT_NO_DEBUG +static bool qsg_leak_check = !qgetenv("QML_LEAK_CHECK").isEmpty(); +#endif + // The cache limit describes the maximum "junk" in the cache. static int cache_limit = 2048 * 1024; // 2048 KB cache limit for embedded in qpixmapcache.cpp @@ -741,7 +746,9 @@ QQuickPixmapStore::~QQuickPixmapStore() { m_destroying = true; +#ifndef QT_NO_DEBUG int leakedPixmaps = 0; +#endif QList cachedData = m_cache.values(); // Prevent unreferencePixmap() from assuming it needs to kick @@ -753,7 +760,9 @@ QQuickPixmapStore::~QQuickPixmapStore() foreach (QQuickPixmapData* pixmap, cachedData) { int currRefCount = pixmap->refCount; if (currRefCount) { +#ifndef QT_NO_DEBUG leakedPixmaps++; +#endif while (currRefCount > 0) { pixmap->release(); currRefCount--; @@ -766,8 +775,10 @@ QQuickPixmapStore::~QQuickPixmapStore() shrinkCache(20); } - if (leakedPixmaps) +#ifndef QT_NO_DEBUG + if (leakedPixmaps && qsg_leak_check) qDebug("Number of leaked pixmaps: %i", leakedPixmaps); +#endif } void QQuickPixmapStore::unreferencePixmap(QQuickPixmapData *data) -- cgit v1.2.3 From b45f742dd8c533cff0767d2dfbe68ea9fd19a522 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Tue, 8 Jan 2013 17:30:49 +0100 Subject: adding QtQuick.Dialogs, with FileDialog implementation This will obsolete the QFileDialogItem in desktop components and be available to all QtQuick applications. The QML FileDialog type is dynamically defined in the plugin by detecting which implementation will work on the current platform. Change-Id: I073c7a84bff6c02cf592dc46822a5a4b9c9bcaea Reviewed-by: Jens Bache-Wiig --- examples/quick/dialogs/FileDialogs.qml | 164 +++++++++ examples/quick/shared/CheckBox.qml | 102 ++++++ examples/quick/shared/ToolButton.qml | 93 ++++++ examples/quick/shared/qmldir | 2 + examples/quick/shared/quick_shared.qrc | 2 + examples/quick/shared/shared.qrc | 2 + src/imports/dialogs/DefaultFileDialog.qml | 366 +++++++++++++++++++++ src/imports/dialogs/WidgetFileDialog.qml | 45 +++ src/imports/dialogs/dialogs.pro | 30 ++ src/imports/dialogs/images/folder.png | Bin 0 -> 1841 bytes src/imports/dialogs/images/titlebar.png | Bin 0 -> 1436 bytes src/imports/dialogs/images/titlebar.sci | 5 + src/imports/dialogs/images/up.png | Bin 0 -> 662 bytes src/imports/dialogs/plugin.cpp | 140 ++++++++ src/imports/dialogs/plugins.qmltypes | 72 ++++ src/imports/dialogs/qml/Button.qml | 90 +++++ src/imports/dialogs/qml/TextField.qml | 77 +++++ src/imports/dialogs/qml/qmldir | 2 + src/imports/dialogs/qmldir | 2 + src/imports/dialogs/qquickabstractfiledialog.cpp | 233 +++++++++++++ src/imports/dialogs/qquickabstractfiledialog_p.h | 144 ++++++++ src/imports/dialogs/qquickfiledialog.cpp | 230 +++++++++++++ src/imports/dialogs/qquickfiledialog_p.h | 99 ++++++ src/imports/dialogs/qquickplatformfiledialog.cpp | 314 ++++++++++++++++++ src/imports/dialogs/qquickplatformfiledialog_p.h | 78 +++++ src/imports/imports.pro | 3 + src/imports/widgets/plugins.qmltypes | 72 ++++ src/imports/widgets/qmldir | 3 + src/imports/widgets/qquickqfiledialog.cpp | 199 +++++++++++ src/imports/widgets/qquickqfiledialog_p.h | 78 +++++ src/imports/widgets/widgets.pro | 17 + src/imports/widgets/widgetsplugin.cpp | 83 +++++ .../auto/quick/dialogs/data/RectWithFileDialog.qml | 33 ++ tests/auto/quick/dialogs/dialogs.pro | 17 + tests/auto/quick/dialogs/tst_dialogs.cpp | 156 +++++++++ tests/auto/quick/quick.pro | 1 + 36 files changed, 2954 insertions(+) create mode 100644 examples/quick/dialogs/FileDialogs.qml create mode 100644 examples/quick/shared/CheckBox.qml create mode 100644 examples/quick/shared/ToolButton.qml create mode 100644 src/imports/dialogs/DefaultFileDialog.qml create mode 100644 src/imports/dialogs/WidgetFileDialog.qml create mode 100644 src/imports/dialogs/dialogs.pro create mode 100644 src/imports/dialogs/images/folder.png create mode 100644 src/imports/dialogs/images/titlebar.png create mode 100644 src/imports/dialogs/images/titlebar.sci create mode 100644 src/imports/dialogs/images/up.png create mode 100644 src/imports/dialogs/plugin.cpp create mode 100644 src/imports/dialogs/plugins.qmltypes create mode 100644 src/imports/dialogs/qml/Button.qml create mode 100644 src/imports/dialogs/qml/TextField.qml create mode 100644 src/imports/dialogs/qml/qmldir create mode 100644 src/imports/dialogs/qmldir create mode 100644 src/imports/dialogs/qquickabstractfiledialog.cpp create mode 100644 src/imports/dialogs/qquickabstractfiledialog_p.h create mode 100644 src/imports/dialogs/qquickfiledialog.cpp create mode 100644 src/imports/dialogs/qquickfiledialog_p.h create mode 100644 src/imports/dialogs/qquickplatformfiledialog.cpp create mode 100644 src/imports/dialogs/qquickplatformfiledialog_p.h create mode 100644 src/imports/widgets/plugins.qmltypes create mode 100644 src/imports/widgets/qmldir create mode 100644 src/imports/widgets/qquickqfiledialog.cpp create mode 100644 src/imports/widgets/qquickqfiledialog_p.h create mode 100644 src/imports/widgets/widgets.pro create mode 100644 src/imports/widgets/widgetsplugin.cpp create mode 100644 tests/auto/quick/dialogs/data/RectWithFileDialog.qml create mode 100644 tests/auto/quick/dialogs/dialogs.pro create mode 100644 tests/auto/quick/dialogs/tst_dialogs.cpp diff --git a/examples/quick/dialogs/FileDialogs.qml b/examples/quick/dialogs/FileDialogs.qml new file mode 100644 index 0000000000..3a987ff86f --- /dev/null +++ b/examples/quick/dialogs/FileDialogs.qml @@ -0,0 +1,164 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtQuick.Dialogs 1.0 +import "../shared" + +Rectangle { + + width: 580 + height: 360 + color: "#444444" + + Rectangle { + id: toolbar + width: parent.width + height: 40 + border.color: "black" + gradient: Gradient { + GradientStop { position: 0.0; color: "#444444" } + GradientStop { position: 0.3; color: "grey" } + GradientStop { position: 0.85; color: "grey" } + GradientStop { position: 1.0; color: "white" } + } + Row { + spacing: 6 + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + anchors.leftMargin: 8 + height: parent.height - 6 + width: parent.width + ToolButton { + text: "Open" + anchors.verticalCenter: parent.verticalCenter + onClicked: fileDialog.open() + } + ToolButton { + text: "Close" + anchors.verticalCenter: parent.verticalCenter + onClicked: fileDialog.close() + } + ToolButton { + text: "/tmp" + anchors.verticalCenter: parent.verticalCenter + // TODO: QTBUG-29814 This isn't portable, but we don't expose QDir::tempPath to QML yet. + onClicked: fileDialog.folder = "/tmp" + } + } + } + + FileDialog { + id: fileDialog + visible: fileDialogVisible.checked + modality: fileDialogModal.checked ? Qt.WindowModal : Qt.NonModal + title: fileDialogSelectFolder.checked ? "Choose a folder" : + (fileDialogSelectMultiple.checked ? "Choose some files" : "Choose a file") + selectExisting: fileDialogSelectExisting.checked + selectMultiple: fileDialogSelectMultiple.checked + selectFolder: fileDialogSelectFolder.checked + nameFilters: [ "Image files (*.png *.jpg)", "All files (*)" ] + selectedNameFilter: "All files (*)" + onAccepted: { console.log("Accepted: " + fileUrls) } + onRejected: { console.log("Rejected") } + } + + Column { + anchors.left: parent.left + anchors.right: parent.right + anchors.top: toolbar.bottom + anchors.margins: 8 + spacing: 8 + Text { + color: "white" + font.bold: true + text: "File dialog properties:" + } + CheckBox { + id: fileDialogModal + text: "Modal" + checked: true + Binding on checked { value: fileDialog.modality != Qt.NonModal } + } + CheckBox { + id: fileDialogSelectFolder + text: "Select Folder" + Binding on checked { value: fileDialog.selectFolder } + } + CheckBox { + id: fileDialogSelectExisting + text: "Select Existing Files" + checked: true + Binding on checked { value: fileDialog.selectExisting } + } + CheckBox { + id: fileDialogSelectMultiple + text: "Select Multiple Files" + Binding on checked { value: fileDialog.selectMultiple } + } + CheckBox { + id: fileDialogVisible + text: "Visible" + Binding on checked { value: fileDialog.visible } + } + Text { + color: "#EEEEDD" + text: "current view folder: " + fileDialog.folder + } + Text { + color: "#EEEEDD" + text: "name filters: {" + fileDialog.nameFilters + "}; current filter: " + fileDialog.selectedNameFilter + width: parent.width + wrapMode: Text.Wrap + } + Text { + color: "#EEEEDD" + text: "chosen files: " + fileDialog.fileUrls + width: parent.width + wrapMode: Text.Wrap + } + Text { + color: "#EEEEDD" + text: "chosen single path: " + fileDialog.fileUrl + width: parent.width + wrapMode: Text.Wrap + } + } +} diff --git a/examples/quick/shared/CheckBox.qml b/examples/quick/shared/CheckBox.qml new file mode 100644 index 0000000000..1d29430934 --- /dev/null +++ b/examples/quick/shared/CheckBox.qml @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 + +Item { + height: label.height + 4 + width: label.width + height + 4 + property alias text: label.text + property bool checked + property alias pressed: mouseArea.pressed + Rectangle { + antialiasing: true + border.color: "white" + color: "transparent" + anchors.fill: gradientRect + anchors.rightMargin: 1 + anchors.bottomMargin: 1 + radius: 3 + } + Rectangle { + border.color: "black" + anchors.fill: gradientRect + anchors.leftMargin: 1 + anchors.topMargin: 1 + radius: 3 + } + Rectangle { + id: gradientRect + gradient: Gradient { + GradientStop { position: 0.0; color: mouseArea.pressed ? "darkgrey" : "#CCCCCC" } + GradientStop { position: 0.6; color: "#887766" } + GradientStop { position: 1.0; color: mouseArea.pressed ? "white" : "#333333" } + } + anchors.verticalCenter: parent.verticalCenter + height: parent.height + width: height + anchors.margins: 1 + radius: 3 + } + Text { + id: theX + anchors.centerIn: gradientRect + text: checked ? "✓" : "" + } + Text { + anchors.centerIn: gradientRect + anchors.horizontalCenterOffset: 0.5 + anchors.verticalCenterOffset: 0.5 + color: "white" + text: theX.text + } + Text { + id: label + color: "#EEEEDD" + anchors.left: gradientRect.right + anchors.leftMargin: 6 + anchors.verticalCenter: parent.verticalCenter + } + MouseArea { + id: mouseArea + anchors.fill: parent + onClicked: parent.checked = !parent.checked + } +} diff --git a/examples/quick/shared/ToolButton.qml b/examples/quick/shared/ToolButton.qml new file mode 100644 index 0000000000..1af0d13dcf --- /dev/null +++ b/examples/quick/shared/ToolButton.qml @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 + +Item { + height: parent.height - 4 + width: height * 2 + anchors.verticalCenter: parent.verticalCenter + property alias text: label.text + property string tooltip + signal clicked + Rectangle { + antialiasing: true + border.color: "white" + color: "transparent" + anchors.fill: parent + anchors.rightMargin: 1 + anchors.bottomMargin: 1 + radius: 3 + } + Rectangle { + border.color: "black" + anchors.fill: parent + anchors.leftMargin: 1 + anchors.topMargin: 1 + radius: 3 + } + Rectangle { + gradient: Gradient { + GradientStop { position: 0.0; color: mouseArea.pressed ? "darkgrey" : "#CCCCCC" } + GradientStop { position: 0.6; color: "#887766" } + GradientStop { position: 1.0; color: mouseArea.pressed ? "white" : "#333333" } + } + anchors.fill: parent + anchors.margins: 1 + radius: 3 + } + Text { + id: label + anchors.centerIn: parent + } + Text { + anchors.centerIn: parent + anchors.horizontalCenterOffset: 0.5 + anchors.verticalCenterOffset: 0.5 + color: "white" + text: label.text + } + + MouseArea { + id: mouseArea + anchors.fill: parent + onClicked: parent.clicked() + } +} diff --git a/examples/quick/shared/qmldir b/examples/quick/shared/qmldir index 106654bbf0..68bd573f85 100644 --- a/examples/quick/shared/qmldir +++ b/examples/quick/shared/qmldir @@ -1,4 +1,6 @@ Button 2.0 Button.qml +CheckBox 2.1 CheckBox.qml LauncherList 2.0 LauncherList.qml SimpleLauncherDelegate 2.0 SimpleLauncherDelegate.qml Slider 2.0 Slider.qml +ToolButton 2.1 ToolButton.qml diff --git a/examples/quick/shared/quick_shared.qrc b/examples/quick/shared/quick_shared.qrc index 74a964e5ef..aa6a761e0d 100644 --- a/examples/quick/shared/quick_shared.qrc +++ b/examples/quick/shared/quick_shared.qrc @@ -3,6 +3,8 @@ LauncherList.qml SimpleLauncherDelegate.qml Button.qml + CheckBox.qml + ToolButton.qml images/back.png images/next.png diff --git a/examples/quick/shared/shared.qrc b/examples/quick/shared/shared.qrc index fba2d3eb82..cbc782f2c7 100644 --- a/examples/quick/shared/shared.qrc +++ b/examples/quick/shared/shared.qrc @@ -5,6 +5,8 @@ Button.qml Slider.qml images/slider_handle.png + CheckBox.qml + ToolButton.qml images/back.png images/next.png diff --git a/src/imports/dialogs/DefaultFileDialog.qml b/src/imports/dialogs/DefaultFileDialog.qml new file mode 100644 index 0000000000..80bf6b5cb1 --- /dev/null +++ b/src/imports/dialogs/DefaultFileDialog.qml @@ -0,0 +1,366 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.1 +import QtQuick.Dialogs 1.0 +import Qt.labs.folderlistmodel 2.0 +import "qml" + +AbstractFileDialog { + id: root + onVisibleChanged: { + if (visible) { + selectedIndices = [] + lastClickedIdx = -1 + currentPathField.visible = false + } + } + + property bool showFocusHighlight: false + property real textX: 28 + property SystemPalette palette + property var selectedIndices: [] + property int lastClickedIdx: -1 + folder: urlToPath(view.model.folder) + + function dirDown(path) { + view.model.folder = path + lastClickedIdx = -1 + selectedIndices = [] + } + function dirUp() { + view.model.folder = view.model.parentFolder + lastClickedIdx = -1 + selectedIndices = [] + } + function up(extend) { + if (view.currentIndex > 0) + --view.currentIndex + else + view.currentIndex = 0 + if (extend) { + if (selectedIndices.indexOf(view.currentIndex) < 0) { + var selCopy = selectedIndices + selCopy.push(view.currentIndex) + selectedIndices = selCopy + } + } else + selectedIndices = [view.currentIndex] + } + function down(extend) { + if (view.currentIndex < view.model.count - 1) + ++view.currentIndex + else + view.currentIndex = view.model.count - 1 + if (extend) { + if (selectedIndices.indexOf(view.currentIndex) < 0) { + var selCopy = selectedIndices + selCopy.push(view.currentIndex) + selectedIndices = selCopy + } + } else + selectedIndices = [view.currentIndex] + } + function acceptSelection() { + clearSelection() + if (selectFolder && selectedIndices.length == 0) + addSelection(folder) + else { + selectedIndices.map(function(idx) { + if (view.model.isFolder(idx)) { + if (selectFolder) + addSelection(view.model.get(idx, "filePath")) + } else { + if (!selectFolder) + addSelection(view.model.get(idx, "filePath")) + } + }) + } + accept() + } + + Rectangle { + width: 480 // TODO: QTBUG-29817 geometry from AbstractFileDialog + height: 320 + id: window + color: palette.window + anchors.centerIn: Qt.application.supportsMultipleWindows ? null : parent + + SystemPalette { id: palette } + + Component { + id: folderDelegate + Rectangle { + id: wrapper + function launch() { + if (view.model.isFolder(index)) { + dirDown(filePath) + } else { + root.acceptSelection() + } + } + width: window.width + height: nameText.implicitHeight * 1.5 + color: "transparent" + Rectangle { + id: itemHighlight + visible: root.selectedIndices.indexOf(index) >= 0 + anchors.fill: parent + color: palette.highlight + } + Image { + id: icon + source: "images/folder.png" + height: wrapper.height - y * 2; width: height + x: (root.textX - width) / 2 + y: 2 + visible: view.model.isFolder(index) + } + Text { + id: nameText + anchors.fill: parent; verticalAlignment: Text.AlignVCenter + text: fileName + anchors.leftMargin: root.textX + color: itemHighlight.visible ? palette.highlightedText : palette.windowText + elide: Text.ElideRight + } + MouseArea { + id: mouseRegion + anchors.fill: parent + onDoubleClicked: { + selectedIndices = [index] + root.lastClickedIdx = index + launch() + } + onClicked: { + view.currentIndex = index + if (mouse.modifiers & Qt.ControlModifier && root.selectMultiple) { + // modifying the contents of selectedIndices doesn't notify, + // so we have to re-assign the variable + var selCopy = selectedIndices + var existingIdx = selCopy.indexOf(index) + if (existingIdx >= 0) + selCopy.splice(existingIdx, 1) + else + selCopy.push(index) + selectedIndices = selCopy + } else if (mouse.modifiers & Qt.ShiftModifier && root.selectMultiple) { + if (root.lastClickedIdx >= 0) { + var sel = [] + if (index > lastClickedIdx) { + for (var i = root.lastClickedIdx; i <= index; i++) + sel.push(i) + } else { + for (var i = root.lastClickedIdx; i >= index; i--) + sel.push(i) + } + selectedIndices = sel + } + } else { + selectedIndices = [index] + root.lastClickedIdx = index + } + } + } + } + } + + ListView { + id: view + anchors.top: titleBar.bottom + anchors.bottom: bottomBar.top + clip: true + x: 0 + width: parent.width + model: FolderListModel { } + delegate: folderDelegate + highlight: Rectangle { + color: "transparent" + border.color: palette.midlight + } + highlightMoveDuration: 0 + highlightMoveVelocity: -1 + focus: !currentPathField.visible + Keys.onPressed: { + event.accepted = true + switch (event.key) { + case Qt.Key_Up: + root.up(event.modifiers & Qt.ShiftModifier && root.selectMultiple) + break + case Qt.Key_Down: + root.down(event.modifiers & Qt.ShiftModifier && root.selectMultiple) + break + case Qt.Key_Left: + root.dirUp() + break + case Qt.Key_Return: + case Qt.Key_Select: + case Qt.Key_Right: + if (view.currentItem) + view.currentItem.launch() + else + root.acceptSelection() + break + default: + // do nothing + event.accepted = false + break + } + } + } + + MouseArea { + anchors.fill: view + enabled: currentPathField.visible + onClicked: currentPathField.visible = false + } + + + Item { + id: titleBar + width: parent.width + height: currentPathField.height * 1.5 + BorderImage { + source: "images/titlebar.sci" + anchors.fill: parent + anchors.topMargin: -7 + anchors.bottomMargin: -7 + } + Rectangle { + id: upButton + width: root.textX + height: titleBar.height + color: "transparent" + + Image { + id: upButtonImage + anchors.centerIn: parent; source: "images/up.png" + } + MouseArea { id: upRegion; anchors.centerIn: parent + width: 56 + height: parent.height + onClicked: if (view.model.parentFolder != "") dirUp() + } + states: [ + State { + name: "pressed" + when: upRegion.pressed + PropertyChanges { target: upButton; color: palette.highlight } + } + ] + } + Text { + id: currentPathText + anchors.left: parent.left; anchors.right: parent.right; anchors.verticalCenter: parent.verticalCenter + anchors.leftMargin: textX; anchors.rightMargin: 4 + text: root.urlToPath(view.model.folder) + color: "white" + elide: Text.ElideLeft; horizontalAlignment: Text.AlignRight; verticalAlignment: Text.AlignVCenter + MouseArea { + anchors.fill: parent + onClicked: currentPathField.visible = true + } + } + TextField { + id: currentPathField + anchors.left: parent.left; anchors.right: parent.right; anchors.verticalCenter: parent.verticalCenter + anchors.leftMargin: textX; anchors.rightMargin: 4 + visible: false + focus: visible + text: root.urlToPath(view.model.folder) + onAccepted: { + root.clearSelection() + if (root.addSelection(text)) + root.accept() + else + view.model.folder = root.pathFolder(text) + } + onDownPressed: currentPathField.visible = false + } + } + Rectangle { + id: bottomBar + width: parent.width + height: buttonRow.height + buttonRow.spacing * 2 + anchors.bottom: parent.bottom + gradient: Gradient { + GradientStop { position: 0.0; color: palette.dark } + GradientStop { position: 0.3; color: palette.mid } + GradientStop { position: 0.85; color: palette.mid } + GradientStop { position: 1.0; color: palette.light } + } + + Row { + id: buttonRow + anchors.right: parent.right + anchors.rightMargin: spacing + anchors.verticalCenter: parent.verticalCenter + spacing: 4 + TextField { + id: filterField + text: root.selectedNameFilter + visible: !selectFolder + width: bottomBar.width - cancelButton.width - okButton.width - parent.spacing * 5 + anchors.verticalCenter: parent.verticalCenter + onAccepted: { + root.selectNameFilter(text) + view.model.nameFilters = text + } + } + Button { + id: cancelButton + text: "Cancel" + onClicked: root.reject() + } + Button { + id: okButton + text: "OK" + onClicked: { + if (view.model.isFolder(view.currentIndex) && !selectFolder) + dirDown(view.model.get(view.currentIndex, "filePath")) + else + root.acceptSelection() + } + } + } + } + } +} diff --git a/src/imports/dialogs/WidgetFileDialog.qml b/src/imports/dialogs/WidgetFileDialog.qml new file mode 100644 index 0000000000..837c7f8a57 --- /dev/null +++ b/src/imports/dialogs/WidgetFileDialog.qml @@ -0,0 +1,45 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.1 +import QtQuick.PrivateWidgets 1.0 + +QtFileDialog { } diff --git a/src/imports/dialogs/dialogs.pro b/src/imports/dialogs/dialogs.pro new file mode 100644 index 0000000000..1a8ec05b85 --- /dev/null +++ b/src/imports/dialogs/dialogs.pro @@ -0,0 +1,30 @@ +CXX_MODULE = qml +TARGET = dialogplugin +TARGETPATH = QtQuick/Dialogs +IMPORT_VERSION = 1.0 + +SOURCES += \ + plugin.cpp \ + qquickabstractfiledialog.cpp \ + qquickplatformfiledialog.cpp \ + qquickfiledialog.cpp + +HEADERS += \ + qquickabstractfiledialog_p.h \ + qquickplatformfiledialog_p.h \ + qquickfiledialog_p.h + +QML_FILES += \ + DefaultFileDialog.qml \ + WidgetFileDialog.qml \ + qml/Button.qml \ + qml/TextField.qml \ + qml/qmldir \ + images/folder.png \ + images/titlebar.png \ + images/titlebar.sci \ + images/up.png + +QT += quick-private gui-private core-private + +load(qml_plugin) diff --git a/src/imports/dialogs/images/folder.png b/src/imports/dialogs/images/folder.png new file mode 100644 index 0000000000..e53e2ad464 Binary files /dev/null and b/src/imports/dialogs/images/folder.png differ diff --git a/src/imports/dialogs/images/titlebar.png b/src/imports/dialogs/images/titlebar.png new file mode 100644 index 0000000000..51c90082d0 Binary files /dev/null and b/src/imports/dialogs/images/titlebar.png differ diff --git a/src/imports/dialogs/images/titlebar.sci b/src/imports/dialogs/images/titlebar.sci new file mode 100644 index 0000000000..0418d94cd6 --- /dev/null +++ b/src/imports/dialogs/images/titlebar.sci @@ -0,0 +1,5 @@ +border.left: 10 +border.top: 12 +border.bottom: 12 +border.right: 10 +source: titlebar.png diff --git a/src/imports/dialogs/images/up.png b/src/imports/dialogs/images/up.png new file mode 100644 index 0000000000..b05f8025d0 Binary files /dev/null and b/src/imports/dialogs/images/up.png differ diff --git a/src/imports/dialogs/plugin.cpp b/src/imports/dialogs/plugin.cpp new file mode 100644 index 0000000000..46bd0dcb9d --- /dev/null +++ b/src/imports/dialogs/plugin.cpp @@ -0,0 +1,140 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include "qquickfiledialog_p.h" +#include "qquickabstractfiledialog_p.h" +#include "qquickplatformfiledialog_p.h" +#include + +//#define PURE_QML_ONLY + +QT_BEGIN_NAMESPACE + +/*! + \qmlmodule QtQuick.Dialogs 1 + \title Qt Quick Dialog QML Types + \ingroup qmlmodules + \brief Provides QML types for standard file, color picker and message dialogs + + This QML module contains types for creating and interacting with system dialogs. + + To use the types in this module, import the module with the following line: + + \code + import QtQuick.Dialogs 1.0 + \endcode +*/ + +class QtQuick2DialogsPlugin : public QQmlExtensionPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface/1.0") + +public: + QtQuick2DialogsPlugin() : QQmlExtensionPlugin() { } + + virtual void initializeEngine(QQmlEngine *, const char *uri) { + bool needQml = false; + QDir qmlDir(baseUrl().toLocalFile()); + // If there is no support for native dialogs on the platform, we need to + // either re-use QFileDialog, or register a QML implementation instead. +#ifdef PURE_QML_ONLY + needQml = true; +#else + if (!QGuiApplicationPrivate::platformTheme()->usePlatformNativeDialog(QPlatformTheme::FileDialog)) { + needQml = true; + // If there is not a QApplication, there's no point in trying to load + // widget-based dialogs, because a runtime error will occur. + if (QCoreApplication::instance()->metaObject()->className() == QLatin1String("QApplication")) { + // Test whether PrivateWidgets can load. It's not currently possible + // to use the given engine for that, so we need to create a temporary one. + // That doesn't work in registerTypes either, which is why it's done here. + QString dialogQmlPath(qmlDir.filePath("WidgetFileDialog.qml")); + QQmlEngine tempEngine; + QQmlComponent widgetDialogComponent(&tempEngine); + QFile widgetDialogQmlFile(dialogQmlPath); + widgetDialogQmlFile.open(QIODevice::ReadOnly); + widgetDialogComponent.setData(widgetDialogQmlFile.readAll(), QUrl()); + + switch (widgetDialogComponent.status()) { + case QQmlComponent::Ready: + needQml = (qmlRegisterType(QUrl::fromLocalFile(dialogQmlPath), uri, 1, 0, "FileDialog") < 0); + // returns -1 unless we omit the module from qmldir, because otherwise + // QtQuick.Dialogs is already a protected namespace + // after the qmldir having been parsed. (QQmlImportDatabase::importPlugin) + // But omitting the module from qmldir results in this warning: + // "Module 'QtQuick.Dialogs' does not contain a module identifier directive - + // it cannot be protected from external registrations." + // TODO register all types in registerTypes, to avoid the warning + // But, in that case we cannot check for existence by trying to instantiate the component. + // So it will have to just look for a file (qmldir?) and assume + // that whatever modules are installed are also in working order. + break; + default: + break; + } + } + } +#endif + if (needQml) { + QString dialogQmlPath = qmlDir.filePath("DefaultFileDialog.qml"); + qmlRegisterType(uri, 1, 0, "AbstractFileDialog"); // implementation wrapper + // qDebug() << "registering FileDialog as " << dialogQmlPath << "success?" << + qmlRegisterType(QUrl::fromLocalFile(dialogQmlPath), uri, 1, 0, "FileDialog"); + } + } + + virtual void registerTypes(const char *uri) { + Q_ASSERT(QLatin1String(uri) == QLatin1String("QtQuick.Dialogs")); + +#ifndef PURE_QML_ONLY + // Prefer the QPA file dialog helper if the platform supports it + if (QGuiApplicationPrivate::platformTheme()->usePlatformNativeDialog(QPlatformTheme::FileDialog)) + qmlRegisterType(uri, 1, 0, "FileDialog"); +#endif + } +}; + +QT_END_NAMESPACE + +#include "plugin.moc" diff --git a/src/imports/dialogs/plugins.qmltypes b/src/imports/dialogs/plugins.qmltypes new file mode 100644 index 0000000000..faf68de909 --- /dev/null +++ b/src/imports/dialogs/plugins.qmltypes @@ -0,0 +1,72 @@ +import QtQuick.tooling 1.1 + +// This file describes the plugin-supplied types contained in the library. +// It is used for QML tooling purposes only. +// +// This file was auto-generated with the command 'qmlplugindump QtQuick.Dialogs 1.0'. + +Module { + Component { + name: "QQuickAbstractFileDialog" + prototype: "QObject" + Property { name: "visible"; type: "bool" } + Property { name: "modality"; type: "Qt::WindowModality" } + Property { name: "title"; type: "string" } + Property { name: "selectExisting"; type: "bool" } + Property { name: "selectMultiple"; type: "bool" } + Property { name: "selectFolder"; type: "bool" } + Property { name: "folder"; type: "string" } + Property { name: "nameFilters"; type: "QStringList" } + Property { name: "selectedNameFilter"; type: "string" } + Property { name: "filePath"; type: "string"; isReadonly: true } + Property { name: "filePaths"; type: "QStringList"; isReadonly: true } + Signal { name: "visibilityChanged" } + Signal { name: "filterSelected" } + Signal { name: "fileModeChanged" } + Signal { name: "accepted" } + Signal { name: "rejected" } + Method { name: "open" } + Method { name: "close" } + Method { + name: "setVisible" + Parameter { name: "v"; type: "bool" } + } + Method { + name: "setModality" + Parameter { name: "m"; type: "Qt::WindowModality" } + } + Method { + name: "setTitle" + Parameter { name: "t"; type: "string" } + } + Method { + name: "setSelectExisting" + Parameter { name: "s"; type: "bool" } + } + Method { + name: "setSelectMultiple" + Parameter { name: "s"; type: "bool" } + } + Method { + name: "setSelectFolder" + Parameter { name: "s"; type: "bool" } + } + Method { + name: "setFolder" + Parameter { name: "f"; type: "string" } + } + Method { + name: "setNameFilters" + Parameter { name: "f"; type: "QStringList" } + } + Method { + name: "selectNameFilter" + Parameter { name: "f"; type: "string" } + } + } + Component { + name: "QQuickQFileDialog" + prototype: "QQuickAbstractFileDialog" + exports: ["QtQuick.PrivateWidgets/QtFileDialog 1.0"] + } +} diff --git a/src/imports/dialogs/qml/Button.qml b/src/imports/dialogs/qml/Button.qml new file mode 100644 index 0000000000..42ee9f79e8 --- /dev/null +++ b/src/imports/dialogs/qml/Button.qml @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.1 + +Item { + height: label.implicitHeight * 2 + width: Math.max(label.implicitWidth * 1.2, height * 2.5); + anchors.verticalCenter: parent.verticalCenter + property alias text: label.text + property string tooltip + signal clicked + SystemPalette { id: palette } + Rectangle { + antialiasing: true + border.color: mouseArea.pressed ? palette.highlight : palette.light + color: "transparent" + anchors.fill: parent + anchors.rightMargin: 1 + anchors.bottomMargin: 1 + radius: 3 + } + Rectangle { + border.color: palette.dark + anchors.fill: parent + anchors.leftMargin: 1 + anchors.topMargin: 1 + radius: 3 + } + Rectangle { + gradient: Gradient { + GradientStop { position: 0.0; color: mouseArea.pressed ? palette.dark : palette.light } + GradientStop { position: 0.2; color: palette.button } + GradientStop { position: 0.8; color: palette.button } + GradientStop { position: 1.0; color: mouseArea.pressed ? palette.light : palette.dark } + } + anchors.fill: parent + anchors.margins: 1 + radius: 3 + } + Text { + id: label + anchors.centerIn: parent + color: palette.buttonText + } + + MouseArea { + id: mouseArea + anchors.fill: parent + onClicked: parent.clicked() + } +} diff --git a/src/imports/dialogs/qml/TextField.qml b/src/imports/dialogs/qml/TextField.qml new file mode 100644 index 0000000000..204eef71b9 --- /dev/null +++ b/src/imports/dialogs/qml/TextField.qml @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.1 + +Item { + id: root + + property alias textInput: textInput + property alias text: textInput.text + signal accepted + signal downPressed + + SystemPalette { id: palette } + height: textInput.implicitHeight + 4 + Rectangle { + id: rect + anchors.fill: parent + anchors.leftMargin: -radius + border.color: palette.light + radius: height / 4 + antialiasing: true + gradient: Gradient { + GradientStop { position: 0.0; color: palette.dark } + GradientStop { position: 0.2; color: palette.button } + GradientStop { position: 0.8; color: palette.button } + GradientStop { position: 1.0; color: palette.light } + } + } + + TextInput { + id: textInput + color: palette.text + anchors.fill: parent + verticalAlignment: Text.AlignVCenter + onAccepted: root.accepted() + Keys.onDownPressed: root.downPressed() + } +} diff --git a/src/imports/dialogs/qml/qmldir b/src/imports/dialogs/qml/qmldir new file mode 100644 index 0000000000..85575c738a --- /dev/null +++ b/src/imports/dialogs/qml/qmldir @@ -0,0 +1,2 @@ +Button 1.0 Button.qml +TextField 1.0 TextField.qml diff --git a/src/imports/dialogs/qmldir b/src/imports/dialogs/qmldir new file mode 100644 index 0000000000..50867d7361 --- /dev/null +++ b/src/imports/dialogs/qmldir @@ -0,0 +1,2 @@ +plugin dialogplugin +typeinfo plugins.qmltypes diff --git a/src/imports/dialogs/qquickabstractfiledialog.cpp b/src/imports/dialogs/qquickabstractfiledialog.cpp new file mode 100644 index 0000000000..905cb5d030 --- /dev/null +++ b/src/imports/dialogs/qquickabstractfiledialog.cpp @@ -0,0 +1,233 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickabstractfiledialog_p.h" +#include "qquickitem.h" + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +QQuickAbstractFileDialog::QQuickAbstractFileDialog(QObject *parent) + : QObject(parent) + , m_dlgHelper(0) + , m_parentWindow(0) + , m_options(QSharedPointer(new QFileDialogOptions())) + , m_visible(false) + , m_modality(Qt::WindowModal) + , m_selectExisting(true) + , m_selectMultiple(false) + , m_selectFolder(false) +{ +} + +QQuickAbstractFileDialog::~QQuickAbstractFileDialog() +{ +} + +void QQuickAbstractFileDialog::setVisible(bool v) +{ + if (m_visible == v) return; + m_visible = v; + if (helper()) { + if (v) { + helper()->setOptions(m_options); + helper()->setFilter(); + m_visible = helper()->show(Qt::Dialog, m_modality, parentWindow()); + emit filterSelected(); + } else { + helper()->hide(); + } + } + + emit visibilityChanged(); +} + +void QQuickAbstractFileDialog::setModality(Qt::WindowModality m) +{ + if (m_modality == m) return; + m_modality = m; + emit modalityChanged(); +} + +void QQuickAbstractFileDialog::setTitle(QString t) +{ + if (m_options->windowTitle() == t) return; + m_options->setWindowTitle(t); + emit titleChanged(); +} + +void QQuickAbstractFileDialog::setSelectExisting(bool selectExisting) +{ + if (selectExisting == m_selectExisting) return; + m_selectExisting = selectExisting; + updateModes(); +} + +void QQuickAbstractFileDialog::setSelectMultiple(bool selectMultiple) +{ + if (selectMultiple == m_selectMultiple) return; + m_selectMultiple = selectMultiple; + updateModes(); +} + +void QQuickAbstractFileDialog::setSelectFolder(bool selectFolder) +{ + if (selectFolder == m_selectFolder) return; + m_selectFolder = selectFolder; + updateModes(); +} + +QString QQuickAbstractFileDialog::folder() +{ + if (m_dlgHelper && !m_dlgHelper->directory().isEmpty()) + return m_dlgHelper->directory(); + return m_options->initialDirectory(); +} + +void QQuickAbstractFileDialog::setFolder(QString f) +{ + if (m_dlgHelper) + m_dlgHelper->setDirectory(f); + m_options->setInitialDirectory(f); + emit folderChanged(); +} + +void QQuickAbstractFileDialog::setNameFilters(const QStringList &f) +{ + m_options->setNameFilters(f); + if (f.isEmpty()) + selectNameFilter(QString()); + else if (!f.contains(selectedNameFilter())) + selectNameFilter(f.first()); + emit nameFiltersChanged(); +} + +QString QQuickAbstractFileDialog::selectedNameFilter() +{ + QString ret; + if (m_dlgHelper) + ret = m_dlgHelper->selectedNameFilter(); + if (ret.isEmpty()) + return m_options->initiallySelectedNameFilter(); + return ret; +} + +void QQuickAbstractFileDialog::selectNameFilter(QString f) +{ + // This should work whether the dialog is currently being shown already, or ahead of time. + m_options->setInitiallySelectedNameFilter(f); + if (m_dlgHelper) + m_dlgHelper->selectNameFilter(f); + emit filterSelected(); +} + +QUrl QQuickAbstractFileDialog::fileUrl() +{ + QList urls = fileUrls(); + return (urls.count() == 1) ? urls[0] : QUrl(); +} + +QList QQuickAbstractFileDialog::fileUrls() +{ + QList ret; + if (m_dlgHelper) + foreach (QString path, m_dlgHelper->selectedFiles()) + ret << QUrl::fromLocalFile(path); + return ret; +} + +void QQuickAbstractFileDialog::accept() +{ + setVisible(false); + emit accepted(); +} + +void QQuickAbstractFileDialog::reject() +{ + setVisible(false); + emit rejected(); +} + +void QQuickAbstractFileDialog::visibleChanged(bool v) +{ + m_visible = v; + emit visibilityChanged(); +} + +void QQuickAbstractFileDialog::updateModes() +{ + // The 4 possible modes are AnyFile, ExistingFile, Directory, ExistingFiles + // Assume AnyFile until we find a reason to the contrary + QFileDialogOptions::FileMode mode = QFileDialogOptions::AnyFile; + + if (m_selectFolder) { + mode = QFileDialogOptions::Directory; + m_options->setOption(QFileDialogOptions::ShowDirsOnly); + m_selectMultiple = false; + m_selectExisting = true; + setNameFilters(QStringList()); + } else if (m_selectExisting) { + mode = m_selectMultiple ? + QFileDialogOptions::ExistingFiles : QFileDialogOptions::ExistingFile; + m_options->setOption(QFileDialogOptions::ShowDirsOnly, false); + } else if (m_selectMultiple) { + m_selectExisting = true; + } + if (!m_selectExisting) + m_selectMultiple = false; + m_options->setFileMode(mode); + m_options->setAcceptMode(m_selectExisting ? + QFileDialogOptions::AcceptOpen : QFileDialogOptions::AcceptSave); + emit fileModeChanged(); +} + +QQuickWindow *QQuickAbstractFileDialog::parentWindow() +{ + QQuickItem *parentItem = qobject_cast(parent()); + if (parentItem) + m_parentWindow = parentItem->window(); + return m_parentWindow; +} + +QT_END_NAMESPACE diff --git a/src/imports/dialogs/qquickabstractfiledialog_p.h b/src/imports/dialogs/qquickabstractfiledialog_p.h new file mode 100644 index 0000000000..e9565108d6 --- /dev/null +++ b/src/imports/dialogs/qquickabstractfiledialog_p.h @@ -0,0 +1,144 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKABSTRACTFILEDIALOG_P_H +#define QQUICKABSTRACTFILEDIALOG_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QQuickAbstractFileDialog : public QObject +{ + Q_OBJECT + Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibilityChanged) + Q_PROPERTY(Qt::WindowModality modality READ modality WRITE setModality NOTIFY modalityChanged) + Q_PROPERTY(QString title READ title WRITE setTitle NOTIFY titleChanged) + Q_PROPERTY(bool selectExisting READ selectExisting WRITE setSelectExisting NOTIFY fileModeChanged) + Q_PROPERTY(bool selectMultiple READ selectMultiple WRITE setSelectMultiple NOTIFY fileModeChanged) + Q_PROPERTY(bool selectFolder READ selectFolder WRITE setSelectFolder NOTIFY fileModeChanged) + Q_PROPERTY(QString folder READ folder WRITE setFolder NOTIFY folderChanged) + Q_PROPERTY(QStringList nameFilters READ nameFilters WRITE setNameFilters NOTIFY nameFiltersChanged) + Q_PROPERTY(QString selectedNameFilter READ selectedNameFilter WRITE selectNameFilter NOTIFY filterSelected) + Q_PROPERTY(QUrl fileUrl READ fileUrl NOTIFY accepted) + Q_PROPERTY(QList fileUrls READ fileUrls NOTIFY accepted) + // TODO: QTBUG-29817: provide x y width and height (after QPlatformDialogHelper provides it) + +public: + QQuickAbstractFileDialog(QObject *parent = 0); + virtual ~QQuickAbstractFileDialog(); + + bool isVisible() const { return m_visible; } + Qt::WindowModality modality() const { return m_modality; } + QString title() const { return m_options->windowTitle(); } + bool selectExisting() const { return m_selectExisting; } + bool selectMultiple() const { return m_selectMultiple; } + bool selectFolder() const { return m_selectFolder; } + QString folder(); + QStringList nameFilters() const { return m_options->nameFilters(); } + QString selectedNameFilter(); + QUrl fileUrl(); + virtual QList fileUrls(); + +public Q_SLOTS: + void open() { setVisible(true); } + void close() { setVisible(false); } + virtual void setVisible(bool v); + void setModality(Qt::WindowModality m); + void setTitle(QString t); + void setSelectExisting(bool s); + void setSelectMultiple(bool s); + void setSelectFolder(bool s); + void setFolder(QString f); + void setNameFilters(const QStringList &f); + void selectNameFilter(QString f); + +Q_SIGNALS: + void visibilityChanged(); + void modalityChanged(); + void titleChanged(); + void folderChanged(); + void nameFiltersChanged(); + void filterSelected(); + void fileModeChanged(); + void accepted(); + void rejected(); + +protected Q_SLOTS: + void accept(); + void reject(); + void visibleChanged(bool v); + +protected: + virtual QPlatformFileDialogHelper *helper() = 0; + void updateModes(); + QQuickWindow *parentWindow(); + +protected: + QPlatformFileDialogHelper *m_dlgHelper; + QQuickWindow *m_parentWindow; + QSharedPointer m_options; + bool m_visible; + Qt::WindowModality m_modality; + bool m_selectExisting; + bool m_selectMultiple; + bool m_selectFolder; + + Q_DISABLE_COPY(QQuickAbstractFileDialog) +}; + +QT_END_NAMESPACE + +#endif // QQUICKABSTRACTFILEDIALOG_P_H diff --git a/src/imports/dialogs/qquickfiledialog.cpp b/src/imports/dialogs/qquickfiledialog.cpp new file mode 100644 index 0000000000..89b8b4cc5b --- /dev/null +++ b/src/imports/dialogs/qquickfiledialog.cpp @@ -0,0 +1,230 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickfiledialog_p.h" +#include +#include +#include + +QT_BEGIN_NAMESPACE + +/*! + \qmltype AbstractFileDialog + \instantiates QQuickFileDialog + \inqmlmodule QtQuick.Dialogs 1 + \ingroup qtquick-visual + \brief API wrapper for QML file dialog implementations + \since 5.1 + \internal + + AbstractFileDialog provides only the API for implementing a file dialog. + The implementation (e.g. a Window or Item) can be provided as \l implementation, + which is the default property (the only allowed child element). +*/ + +/*! + \qmlsignal QtQuick::Dialogs::AbstractFileDialog::accepted + + The \a accepted signal is emitted by \l accept(). +*/ + +/*! + \qmlsignal QtQuick::Dialogs::AbstractFileDialog::rejected + + The \a accepted signal is emitted by \l reject(). +*/ + +/*! + \class QQuickFileDialog + \inmodule QtQuick.Dialogs + \internal + + The QQuickFileDialog class is a concrete subclass of \l + QQuickAbstractFileDialog, but it is abstract from the QML perspective + because it needs to enclose a graphical implementation. It exists in order + to provide accessors and helper functions which the QML implementation will + need. + + \since 5.1 +*/ + +/*! + Constructs a file dialog wrapper with parent window \a parent. +*/ +QQuickFileDialog::QQuickFileDialog(QObject *parent) + : QQuickAbstractFileDialog(parent) + , m_implementation(0) + , m_dialogWindow(0) +{ +} + + +/*! + Destroys the file dialog wrapper. +*/ +QQuickFileDialog::~QQuickFileDialog() +{ +} + +QList QQuickFileDialog::fileUrls() +{ + QList ret; + foreach (QString path, m_selections) + ret << QUrl::fromLocalFile(path); + return ret; +} + +/*! + \qmlproperty bool AbstractFileDialog::visible + + This property holds whether the dialog is visible. By default this is false. +*/ +void QQuickFileDialog::setVisible(bool v) +{ + if (m_visible == v) return; + m_visible = v; + // For a pure QML implementation, there is no helper. + // But m_implementation is probably either an Item or a Window at this point. + if (!m_dialogWindow) { + m_dialogWindow = qobject_cast(m_implementation); + if (!m_dialogWindow) { + QQuickItem *dlgItem = qobject_cast(m_implementation); + if (dlgItem) { + m_dialogWindow = dlgItem->window(); + // An Item-based dialog implementation doesn't come with a window, so + // we have to instantiate one iff the platform allows it. + if (!m_dialogWindow && QGuiApplicationPrivate::platformIntegration()-> + hasCapability(QPlatformIntegration::MultipleWindows)) { + QQuickWindow *win = new QQuickWindow; + m_dialogWindow = win; + dlgItem->setParentItem(win->contentItem()); + m_dialogWindow->setMinimumSize(QSize(dlgItem->width(), dlgItem->height())); + } + + QQuickItem *parentItem = qobject_cast(parent()); + // qDebug() << "item implementation" << dlgItem << "has window" << m_dialogWindow << "and parent" << parentItem; + + // If the platform does not support multiple windows, but the dialog is + // implemented as an Item, then just reparent it and make it visible. + // TODO QTBUG-29818: put it into a fake Item-based window, when we have a reusable self-decorated one. + if (parentItem && !m_dialogWindow) + dlgItem->setParentItem(parentItem); + } + } + if (m_dialogWindow) + connect(m_dialogWindow, SIGNAL(visibleChanged(bool)), this, SLOT(visibleChanged(bool))); + } + if (m_dialogWindow) { + if (v) { + m_dialogWindow->setTransientParent(parentWindow()); + m_dialogWindow->setTitle(title()); + m_dialogWindow->setModality(m_modality); + } + m_dialogWindow->setVisible(v); + } + emit visibilityChanged(); +} + +/*! + \qmlproperty bool AbstractFileDialog::filePaths + + A list of files to be populated as the user chooses. +*/ + +/*! + \brief Clears \l filePaths +*/ +void QQuickFileDialog::clearSelection() +{ + m_selections.clear(); +} + +/*! + \brief Adds one file to \l filePaths + + \l path should be given as an absolute file system path. If it is given as a + file:// URL, it will be converted to a path. Returns true on success, + false if the given path is not valid given the current setting properties. +*/ +bool QQuickFileDialog::addSelection(QString path) +{ + if (path.startsWith("file:")) + path = QUrl(path).toLocalFile(); + QFileInfo info(path); + if (info.exists() && ((info.isDir() && m_selectFolder) || !info.isDir())) { + if (m_selectFolder) + m_selections.append(pathFolder(path).toLocalFile()); + else + m_selections.append(path); + return true; + } + return false; +} + +/*! + \brief get a file's directory as a URL + + If \a path points to a directory, just convert it to a URL. + If \a path points to a file, convert the file's directory to a URL. +*/ +QUrl QQuickFileDialog::pathFolder(const QString &path) +{ + QFileInfo info(path); + if (info.exists() && info.isDir()) + return QUrl::fromLocalFile(path); + return QUrl::fromLocalFile(QFileInfo(path).absolutePath()); +} + +/*! + \qmlproperty QObject AbstractFileDialog::implementation + + The QML object which implements the actual file dialog. Should be either a + \l Window or an \l Item. +*/ +void QQuickFileDialog::setImplementation(QObject *obj) +{ + m_implementation = obj; + if (m_dialogWindow) + disconnect(this, SLOT(visibleChanged(bool))); + m_dialogWindow = 0; +} + +QT_END_NAMESPACE diff --git a/src/imports/dialogs/qquickfiledialog_p.h b/src/imports/dialogs/qquickfiledialog_p.h new file mode 100644 index 0000000000..0a660b316b --- /dev/null +++ b/src/imports/dialogs/qquickfiledialog_p.h @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKFILEDIALOG_P_H +#define QQUICKFILEDIALOG_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qquickabstractfiledialog_p.h" + +QT_BEGIN_NAMESPACE + +class QQuickFileDialog : public QQuickAbstractFileDialog +{ + Q_OBJECT + Q_PROPERTY(QObject* implementation READ implementation WRITE setImplementation DESIGNABLE false) + Q_CLASSINFO("DefaultProperty", "implementation") // AbstractFileDialog in QML can have only one child + +public: + explicit QQuickFileDialog(QObject *parent = 0); + ~QQuickFileDialog(); + QObject* implementation() { return m_implementation; } + virtual QList fileUrls(); + +signals: + +public Q_SLOTS: + void setImplementation(QObject* obj); + virtual void setVisible(bool v); + + void clearSelection(); + bool addSelection(QString path); + +protected: + virtual QPlatformFileDialogHelper *helper() { return 0; } + Q_INVOKABLE QString urlToPath(const QUrl &url) { return url.toLocalFile(); } + Q_INVOKABLE QUrl pathToUrl(const QString &path) { return QUrl::fromLocalFile(path); } + Q_INVOKABLE QUrl pathFolder(const QString &path); + +private: + QObject *m_implementation; + QWindow *m_dialogWindow; + QStringList m_selections; + + Q_DISABLE_COPY(QQuickFileDialog) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickFileDialog *) + +#endif // QQUICKFILEDIALOG_P_H diff --git a/src/imports/dialogs/qquickplatformfiledialog.cpp b/src/imports/dialogs/qquickplatformfiledialog.cpp new file mode 100644 index 0000000000..f2bd279d7b --- /dev/null +++ b/src/imports/dialogs/qquickplatformfiledialog.cpp @@ -0,0 +1,314 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickplatformfiledialog_p.h" +#include "qquickitem.h" + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +/*! + \qmltype FileDialog + \instantiates QQuickPlatformFileDialog + \inqmlmodule QtQuick.Dialogs 1 + \ingroup qtquick-visual + \brief Dialog component for choosing files from a local filesystem. + \since 5.1 + + FileDialog provides a basic file chooser: it allows the user to select + existing files and/or directories, or create new filenames. The dialog is + initially invisible. You need to set the properties as desired first, then + set \l visible to true or call \l open(). + + Here is a minimal example to open a file dialog and exit after the user + chooses a file: + + \qml + import QtQuick 2.0 + import QtQuick.Dialogs 1.0 + + FileDialog { + id: fileDialog + title: "Please choose a file" + onAccepted: { + console.log("You chose: " + fileDialog.filePaths) + Qt.quit() + } + onRejected: { + console.log("Cancelled") + Qt.quit() + } + Component.onCompleted: visible = true + } + \endqml + + A FileDialog window is automatically transient for its parent window. So + whether you declare the dialog inside an \l Item or inside a \l Window, the + dialog will appear centered over the window containing the item, or over + the Window that you declared. + + The implementation of FileDialog will be a platform file dialog if + possible. If that isn't possible, then it will try to instantiate a + \l QFileDialog. If that also isn't possible, then it will fall back to a QML + implementation, DefaultFileDialog.qml. In that case you can customize the + appearance by editing this file. DefaultFileDialog.qml contains a Rectangle + to hold the dialog's contents, because certain embedded systems do not + support multiple top-level windows. When the dialog becomes visible, it + will automatically be wrapped in a Window if possible, or simply reparented + on top of the main window if there can only be one window. +*/ + +/*! + \qmlsignal QtQuick::Dialogs::FileDialog::accepted + + The \a accepted signal is emitted when the user has finished using the + dialog. You can then inspect the \a filePath or \a filePaths properties to + get the selection. + + Example: + + \qml + FileDialog { + onAccepted: { console.log("Selected file: " + filePath) } + } + \endqml +*/ + +/*! + \qmlsignal QtQuick::Dialogs::FileDialog::rejected + + The \a rejected signal is emitted when the user has dismissed the dialog, + either by closing the dialog window or by pressing the Cancel button. +*/ + +/*! + \class QQuickPlatformFileDialog + \inmodule QtQuick.Dialogs + \internal + + \brief The QQuickPlatformFileDialog class provides a file dialog + + The dialog is implemented via the QPlatformFileDialogHelper when possible; + otherwise it falls back to a QFileDialog or a QML implementation. + + \since 5.1 +*/ + +/*! + Constructs a file dialog with parent window \a parent. +*/ +QQuickPlatformFileDialog::QQuickPlatformFileDialog(QObject *parent) : + QQuickAbstractFileDialog(parent) +{ +} + +/*! + Destroys the file dialog. +*/ +QQuickPlatformFileDialog::~QQuickPlatformFileDialog() +{ + if (m_dlgHelper) + m_dlgHelper->hide(); + delete m_dlgHelper; +} + +QPlatformFileDialogHelper *QQuickPlatformFileDialog::helper() +{ + QQuickItem *parentItem = qobject_cast(parent()); + if (parentItem) + m_parentWindow = parentItem->window(); + + if ( !m_dlgHelper && QGuiApplicationPrivate::platformTheme()-> + usePlatformNativeDialog(QPlatformTheme::FileDialog) ) { + m_dlgHelper = static_cast(QGuiApplicationPrivate::platformTheme() + ->createPlatformDialogHelper(QPlatformTheme::FileDialog)); + if (!m_dlgHelper) + return m_dlgHelper; + connect(m_dlgHelper, SIGNAL(directoryEntered(QString)), this, SIGNAL(folderChanged())); + connect(m_dlgHelper, SIGNAL(filterSelected(QString)), this, SIGNAL(filterSelected())); + connect(m_dlgHelper, SIGNAL(accept()), this, SLOT(accept())); + connect(m_dlgHelper, SIGNAL(reject()), this, SLOT(reject())); + } + + return m_dlgHelper; +} + +/*! + \qmlproperty bool FileDialog::visible + + This property holds whether the dialog is visible. By default this is + false. + + \sa modality +*/ + +/*! + \qmlproperty Qt::WindowModality FileDialog::modality + + Whether the dialog should be shown modal with respect to the window + containing the dialog's parent Item, modal with respect to the whole + application, or non-modal. + + By default it is \l WindowModal. + + Modality does not mean that there are any blocking calls to wait for the + dialog to be accepted or rejected; it's only that the user will be + prevented from interacting with the parent window and/or the application + windows at the same time. You probably need to write an onAccepted handler + to actually load or save the chosen file. +*/ + +/*! + \qmlmethod void FileDialog::open() + + Shows the dialog to the user. It is equivalent to setting \l visible to + true. +*/ + +/*! + \qmlmethod void FileDialog::close() + + Closes the dialog. +*/ + +/*! + \qmlproperty string FileDialog::title + + The title of the dialog window. +*/ + +/*! + \qmlproperty bool FileDialog::selectExisting + + Whether only existing files or directories can be selected. + + By default, this property is true. This property must be set to the desired + value before opening the dialog. Setting this property to false implies + that the dialog is for naming a file to which to save something, or naming + a folder to be created; therefore \l selectMultiple must be false. +*/ + +/*! + \qmlproperty bool FileDialog::selectMultiple + + Whether more than one filename can be selected. + + By default, this property is false. This property must be set to the + desired value before opening the dialog. Setting this property to true + implies that \l selectExisting must be true. +*/ + +/*! + \qmlproperty bool FileDialog::selectFolder + + Whether the selected item should be a folder. + + By default, this property is false. This property must be set to the + desired value before opening the dialog. Setting this property to true + implies that \l selectMultiple must be false and \l selectExisting must be + true. +*/ + +/*! + \qmlproperty string FileDialog::folder + + The path to the currently selected folder. Setting this property before + invoking open() will cause the file browser to be initially positioned on + the specified folder. + + The value of this property is also updated after the dialog is closed. + + By default, this property is false. +*/ + +/*! + \qmlproperty list FileDialog::nameFilters + + A list of strings to be used as file name filters. Each string can be a + space-separated list of filters; filters may include the ? and * wildcards. + The list of filters can also be enclosed in parentheses and a textual + description of the filter can be provided. + + For example: + + \qml + FileDialog { + nameFilters: [ "Image files (*.jpg *.png)", "All files (*)" ] + } + \endqml + + \note Directories are not excluded by filters. + \sa selectedNameFilter +*/ + +/*! + \qmlproperty string FileDialog::selectedNameFilter + + Which of the \l nameFilters is currently selected. + + This property can be set before the dialog is visible, to set the default + name filter, and can also be set while the dialog is visible to set the + current name filter. It is also updated when the user selects a different + filter. +*/ + +/*! + \qmlproperty string FileDialog::filePath + + The path of the file which was selected by the user. + + \note This property is set only if exactly one file was selected. In all + other cases, it will return an empty string. + + \sa filePaths +*/ + +/*! + \qmlproperty list FileDialog::filePaths + + The list of file paths which were selected by the user. +*/ + +QT_END_NAMESPACE diff --git a/src/imports/dialogs/qquickplatformfiledialog_p.h b/src/imports/dialogs/qquickplatformfiledialog_p.h new file mode 100644 index 0000000000..3559543319 --- /dev/null +++ b/src/imports/dialogs/qquickplatformfiledialog_p.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKPLATFORMFILEDIALOG_P_H +#define QQUICKPLATFORMFILEDIALOG_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qquickabstractfiledialog_p.h" + +QT_BEGIN_NAMESPACE + +class QQuickPlatformFileDialog : public QQuickAbstractFileDialog +{ + Q_OBJECT + +public: + QQuickPlatformFileDialog(QObject *parent = 0); + virtual ~QQuickPlatformFileDialog(); + +protected: + QPlatformFileDialogHelper *helper(); + + Q_DISABLE_COPY(QQuickPlatformFileDialog) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickPlatformFileDialog *) + +#endif // QQUICKPLATFORMFILEDIALOG_P_H diff --git a/src/imports/imports.pro b/src/imports/imports.pro index 72888b2c71..e411a4f097 100644 --- a/src/imports/imports.pro +++ b/src/imports/imports.pro @@ -9,7 +9,10 @@ qtHaveModule(quick) { qtquick2 \ particles \ window \ + dialogs \ testlib } qtHaveModule(xmlpatterns) : SUBDIRS += xmllistmodel + +qtHaveModule(widgets) : SUBDIRS += widgets diff --git a/src/imports/widgets/plugins.qmltypes b/src/imports/widgets/plugins.qmltypes new file mode 100644 index 0000000000..f9fe1f722c --- /dev/null +++ b/src/imports/widgets/plugins.qmltypes @@ -0,0 +1,72 @@ +import QtQuick.tooling 1.1 + +// This file describes the plugin-supplied types contained in the library. +// It is used for QML tooling purposes only. +// +// This file was auto-generated with the command 'qmlplugindump QtQuick.PrivateWidgets 1.0'. + +Module { + Component { + name: "QQuickAbstractFileDialog" + prototype: "QObject" + Property { name: "visible"; type: "bool" } + Property { name: "modality"; type: "Qt::WindowModality" } + Property { name: "title"; type: "string" } + Property { name: "selectExisting"; type: "bool" } + Property { name: "selectMultiple"; type: "bool" } + Property { name: "selectFolder"; type: "bool" } + Property { name: "folder"; type: "string" } + Property { name: "nameFilters"; type: "QStringList" } + Property { name: "selectedNameFilter"; type: "string" } + Property { name: "filePath"; type: "string"; isReadonly: true } + Property { name: "filePaths"; type: "QStringList"; isReadonly: true } + Signal { name: "visibilityChanged" } + Signal { name: "filterSelected" } + Signal { name: "fileModeChanged" } + Signal { name: "accepted" } + Signal { name: "rejected" } + Method { name: "open" } + Method { name: "close" } + Method { + name: "setVisible" + Parameter { name: "v"; type: "bool" } + } + Method { + name: "setModality" + Parameter { name: "m"; type: "Qt::WindowModality" } + } + Method { + name: "setTitle" + Parameter { name: "t"; type: "string" } + } + Method { + name: "setSelectExisting" + Parameter { name: "s"; type: "bool" } + } + Method { + name: "setSelectMultiple" + Parameter { name: "s"; type: "bool" } + } + Method { + name: "setSelectFolder" + Parameter { name: "s"; type: "bool" } + } + Method { + name: "setFolder" + Parameter { name: "f"; type: "string" } + } + Method { + name: "setNameFilters" + Parameter { name: "f"; type: "QStringList" } + } + Method { + name: "selectNameFilter" + Parameter { name: "f"; type: "string" } + } + } + Component { + name: "QQuickQFileDialog" + prototype: "QQuickAbstractFileDialog" + exports: ["QtFileDialog 1.0"] + } +} diff --git a/src/imports/widgets/qmldir b/src/imports/widgets/qmldir new file mode 100644 index 0000000000..16c84424f2 --- /dev/null +++ b/src/imports/widgets/qmldir @@ -0,0 +1,3 @@ +module QtQuick.PrivateWidgets +plugin widgetsplugin +typeinfo plugins.qmltypes diff --git a/src/imports/widgets/qquickqfiledialog.cpp b/src/imports/widgets/qquickqfiledialog.cpp new file mode 100644 index 0000000000..672c6d1bf4 --- /dev/null +++ b/src/imports/widgets/qquickqfiledialog.cpp @@ -0,0 +1,199 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickqfiledialog_p.h" +#include "qquickitem.h" + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QFileDialogHelper : public QPlatformFileDialogHelper +{ +public: + QFileDialogHelper() : + QPlatformFileDialogHelper() + { + connect(&m_dialog, SIGNAL(currentChanged(const QString&)), this, SIGNAL(currentChanged(const QString&))); + connect(&m_dialog, SIGNAL(directoryEntered(const QString&)), this, SIGNAL(directoryEntered(const QString&))); + connect(&m_dialog, SIGNAL(fileSelected(const QString&)), this, SIGNAL(fileSelected(const QString&))); + connect(&m_dialog, SIGNAL(filesSelected(const QStringList&)), this, SIGNAL(filesSelected(const QStringList&))); + connect(&m_dialog, SIGNAL(filterSelected(const QString&)), this, SIGNAL(filterSelected(const QString&))); + connect(&m_dialog, SIGNAL(accepted()), this, SIGNAL(accept())); + connect(&m_dialog, SIGNAL(rejected()), this, SIGNAL(reject())); + } + + virtual bool defaultNameFilterDisables() const { return true; } + virtual void setDirectory(const QString &dir) { m_dialog.setDirectory(dir); } + virtual QString directory() const { return m_dialog.directory().absolutePath(); } + virtual void selectFile(const QString &f) { m_dialog.selectFile(f); } + virtual QStringList selectedFiles() const { return m_dialog.selectedFiles(); } + + virtual void setFilter() { + m_dialog.setWindowTitle(QPlatformFileDialogHelper::options()->windowTitle()); + if (QPlatformFileDialogHelper::options()->isLabelExplicitlySet(QFileDialogOptions::LookIn)) + m_dialog.setLabelText(m_dialog.LookIn, QPlatformFileDialogHelper::options()->labelText(QFileDialogOptions::LookIn)); + if (QPlatformFileDialogHelper::options()->isLabelExplicitlySet(QFileDialogOptions::FileName)) + m_dialog.setLabelText(m_dialog.FileName, QPlatformFileDialogHelper::options()->labelText(QFileDialogOptions::FileName)); + if (QPlatformFileDialogHelper::options()->isLabelExplicitlySet(QFileDialogOptions::FileType)) + m_dialog.setLabelText(m_dialog.FileType, QPlatformFileDialogHelper::options()->labelText(QFileDialogOptions::FileType)); + if (QPlatformFileDialogHelper::options()->isLabelExplicitlySet(QFileDialogOptions::Accept)) + m_dialog.setLabelText(m_dialog.Accept, QPlatformFileDialogHelper::options()->labelText(QFileDialogOptions::Accept)); + if (QPlatformFileDialogHelper::options()->isLabelExplicitlySet(QFileDialogOptions::Reject)) + m_dialog.setLabelText(m_dialog.Reject, QPlatformFileDialogHelper::options()->labelText(QFileDialogOptions::Reject)); + m_dialog.setFilter(QPlatformFileDialogHelper::options()->filter()); + m_dialog.setNameFilters(QPlatformFileDialogHelper::options()->nameFilters()); + m_dialog.selectNameFilter(QPlatformFileDialogHelper::options()->initiallySelectedNameFilter()); + m_dialog.setFileMode(QFileDialog::FileMode(QPlatformFileDialogHelper::options()->fileMode())); + m_dialog.setOptions((QFileDialog::Options)((int)(QPlatformFileDialogHelper::options()->options()))); + m_dialog.setAcceptMode(QFileDialog::AcceptMode(QPlatformFileDialogHelper::options()->acceptMode())); + } + + virtual void selectNameFilter(const QString &f) { m_dialog.selectNameFilter(f); } + virtual QString selectedNameFilter() const { return m_dialog.selectedNameFilter(); } + virtual void exec() { m_dialog.exec(); } + + virtual bool show(Qt::WindowFlags f, Qt::WindowModality m, QWindow *parent) { + m_dialog.windowHandle()->setTransientParent(parent); + m_dialog.windowHandle()->setFlags(f); + m_dialog.setWindowModality(m); + m_dialog.show(); + return m_dialog.isVisible(); + } + + virtual void hide() { m_dialog.hide(); } + +private: + QFileDialog m_dialog; +}; + +/*! + \qmltype QtFileDialog + \instantiates QQuickQFileDialog + \inqmlmodule QtQuick.PrivateWidgets 1 + \ingroup qtquick-visual + \brief Dialog component for choosing files from a local filesystem. + \since 5.1 + \internal + + QtFileDialog provides a means to instantiate and manage a QFileDialog. + It is not recommended to be used directly; it is an implementation + detail of \l FileDialog in the \l QtQuick.Dialogs module. + + To use this type, you will need to import the module with the following line: + \code + import QtQuick.PrivateWidgets 1.0 + \endcode +*/ + +/*! + \qmlsignal QtQuick::Dialogs::FileDialog::accepted + + The \a accepted signal is emitted when the user has finished using the + dialog. You can then inspect the \a filePath or \a filePaths properties to + get the selection. + + Example: + + \qml + FileDialog { + onAccepted: { console.log("Selected file: " + filePath) } + } + \endqml +*/ + +/*! + \qmlsignal QtQuick::Dialogs::FileDialog::rejected + + The \a rejected signal is emitted when the user has dismissed the dialog, + either by closing the dialog window or by pressing the Cancel button. +*/ + +/*! + \class QQuickQFileDialog + \inmodule QtQuick.PrivateWidgets + \internal + + \brief The QQuickQFileDialog class is a wrapper for a QFileDialog. + + \since 5.1 +*/ + +/*! + Constructs a file dialog with parent window \a parent. +*/ +QQuickQFileDialog::QQuickQFileDialog(QObject *parent) + : QQuickAbstractFileDialog(parent) +{ +} + +/*! + Destroys the file dialog. +*/ +QQuickQFileDialog::~QQuickQFileDialog() +{ + if (m_dlgHelper) + m_dlgHelper->hide(); + delete m_dlgHelper; +} + +QPlatformFileDialogHelper *QQuickQFileDialog::helper() +{ + QQuickItem *parentItem = qobject_cast(parent()); + if (parentItem) + m_parentWindow = parentItem->window(); + + if (!m_dlgHelper) { + m_dlgHelper = new QFileDialogHelper(); + connect(m_dlgHelper, SIGNAL(directoryEntered(QString)), this, SIGNAL(folderChanged())); + connect(m_dlgHelper, SIGNAL(filterSelected(QString)), this, SIGNAL(filterSelected())); + connect(m_dlgHelper, SIGNAL(accept()), this, SLOT(accept())); + connect(m_dlgHelper, SIGNAL(reject()), this, SLOT(reject())); + } + + return m_dlgHelper; +} + +QT_END_NAMESPACE diff --git a/src/imports/widgets/qquickqfiledialog_p.h b/src/imports/widgets/qquickqfiledialog_p.h new file mode 100644 index 0000000000..73067f796c --- /dev/null +++ b/src/imports/widgets/qquickqfiledialog_p.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKQFILEDIALOG_P_H +#define QQUICKQFILEDIALOG_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "../dialogs/qquickabstractfiledialog_p.h" + +QT_BEGIN_NAMESPACE + +class QQuickQFileDialog : public QQuickAbstractFileDialog +{ + Q_OBJECT + +public: + QQuickQFileDialog(QObject *parent = 0); + virtual ~QQuickQFileDialog(); + +protected: + QPlatformFileDialogHelper *helper(); + + Q_DISABLE_COPY(QQuickQFileDialog) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickQFileDialog *) + +#endif // QQUICKQFILEDIALOG_P_H diff --git a/src/imports/widgets/widgets.pro b/src/imports/widgets/widgets.pro new file mode 100644 index 0000000000..57281178a0 --- /dev/null +++ b/src/imports/widgets/widgets.pro @@ -0,0 +1,17 @@ +CXX_MODULE = qml +TARGET = widgetsplugin +TARGETPATH = QtQuick/PrivateWidgets +IMPORT_VERSION = 1.0 + +SOURCES += \ + widgetsplugin.cpp \ + qquickqfiledialog.cpp \ + ../dialogs/qquickabstractfiledialog.cpp + +HEADERS += \ + qquickqfiledialog_p.h \ + ../dialogs/qquickabstractfiledialog_p.h + +QT += quick-private gui-private core-private qml-private v8-private widgets + +load(qml_plugin) diff --git a/src/imports/widgets/widgetsplugin.cpp b/src/imports/widgets/widgetsplugin.cpp new file mode 100644 index 0000000000..6d3a56ca27 --- /dev/null +++ b/src/imports/widgets/widgetsplugin.cpp @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include "qquickqfiledialog_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \qmlmodule QtQuick.PrivateWidgets 1 + \title QWidget QML Types + \ingroup qmlmodules + \brief Provides QML types for certain QWidgets + \internal + + This QML module contains types which should not be depended upon in QtQuick + applications, but are available if the Widgets module is linked. It is + recommended to load components from this module conditionally, if at all, + and to provide fallback implementations in case they fail to load. + + \code + import QtQuick.PrivateWidgets 1.0 + \endcode + + \since 5.1 +*/ + +class QtQuick2PrivateWidgetsPlugin : public QQmlExtensionPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface/1.0") + +public: + virtual void registerTypes(const char *uri) + { + Q_ASSERT(QLatin1String(uri) == QLatin1String("QtQuick.PrivateWidgets")); + + qmlRegisterType(uri, 1, 0, "QtFileDialog"); + } +}; + +QT_END_NAMESPACE + +#include "widgetsplugin.moc" diff --git a/tests/auto/quick/dialogs/data/RectWithFileDialog.qml b/tests/auto/quick/dialogs/data/RectWithFileDialog.qml new file mode 100644 index 0000000000..ca7ecc948a --- /dev/null +++ b/tests/auto/quick/dialogs/data/RectWithFileDialog.qml @@ -0,0 +1,33 @@ +import QtQuick 2.0 +import QtQuick.Dialogs 1.0 + +Rectangle { + width: 1024 + height: 320 + property alias fileDialog: fileDialog + property alias label: label + property alias mouseArea: mouseArea + + FileDialog { + id: fileDialog + title: "Choose some files" + selectMultiple: true + nameFilters: [ "QML files (*.qml)", "All files (*)" ] + selectedNameFilter: "QML files (*.qml)" + onAccepted: label.text = fileDialog.filePaths + } + + MouseArea { + id: mouseArea + anchors.fill: parent + onClicked: fileDialog.visible = !fileDialog.visible + } + + Text { + id: label + text: "Click to open a file dialog" + wrapMode: Text.Wrap + anchors.fill: parent + anchors.margins: 10 + } +} diff --git a/tests/auto/quick/dialogs/dialogs.pro b/tests/auto/quick/dialogs/dialogs.pro new file mode 100644 index 0000000000..d28c623a1f --- /dev/null +++ b/tests/auto/quick/dialogs/dialogs.pro @@ -0,0 +1,17 @@ +CONFIG += testcase +TARGET = tst_dialogs +SOURCES += tst_dialogs.cpp + +include (../../shared/util.pri) + +macx:CONFIG -= app_bundle + +CONFIG += parallel_test +QT += core-private gui-private qml-private quick-private v8-private testlib + +TESTDATA = data/* + +OTHER_FILES += \ + data/FileDialog.qml \ + +DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 diff --git a/tests/auto/quick/dialogs/tst_dialogs.cpp b/tests/auto/quick/dialogs/tst_dialogs.cpp new file mode 100644 index 0000000000..9f0dc8ec18 --- /dev/null +++ b/tests/auto/quick/dialogs/tst_dialogs.cpp @@ -0,0 +1,156 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include "../../shared/util.h" +#include +#include +#include + +class tst_dialogs : public QQmlDataTest +{ + Q_OBJECT +public: + +private slots: + void initTestCase() + { + QQmlDataTest::initTestCase(); + } + + // FileDialog + void fileDialogDefaultModality(); + void fileDialogNonModal(); + void fileDialogNameFilters(); + +private: +}; + +void tst_dialogs::fileDialogDefaultModality() +{ + QQuickView *window = new QQuickView; + QScopedPointer cleanup(window); + + window->setSource(testFileUrl("RectWithFileDialog.qml")); + window->setGeometry(240,240,1024,320); + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window)); + + // Click to show + QObject *dlg = qvariant_cast(window->rootObject()->property("fileDialog")); + QSignalSpy spyVisibilityChanged(dlg, SIGNAL(visibilityChanged())); + QTest::mouseClick(window, Qt::LeftButton, 0, QPoint(1000, 100)); // show + QTRY_VERIFY(spyVisibilityChanged.count() > 0); + int visibilityChangedCount = spyVisibilityChanged.count(); + // Can't hide by clicking the main window, because dialog is modal. + QTest::mouseClick(window, Qt::LeftButton, 0, QPoint(1000, 100)); +#ifdef Q_OS_MAC + /* + On the Mac, if you send an event directly to a window, the modal dialog + doesn't block the event, so the window will process it normally. This + is a different code path compared to having a user click the mouse and + generate a native event; in that case the OS does the filtering itself, + and Qt will not even see the event. But simulating real events in the + test framework is generally unstable. So there isn't a good way to test + modality on the mac. + */ + QSKIP("Modality test doesn't work on Mac OS"); +#endif + // So we expect no change in visibility. + QCOMPARE(spyVisibilityChanged.count(), visibilityChangedCount); + + QCOMPARE(dlg->property("visible").toBool(), true); + QMetaObject::invokeMethod(dlg, "close"); + QTRY_VERIFY(spyVisibilityChanged.count() > visibilityChangedCount); + visibilityChangedCount = spyVisibilityChanged.count(); + QCOMPARE(dlg->property("visible").toBool(), false); + QMetaObject::invokeMethod(dlg, "open"); + QTRY_VERIFY(spyVisibilityChanged.count() > visibilityChangedCount); + QCOMPARE(dlg->property("visible").toBool(), true); + QCOMPARE(dlg->property("modality").toInt(), (int)Qt::WindowModal); +} + +void tst_dialogs::fileDialogNonModal() +{ + QQuickView *window = new QQuickView; + QScopedPointer cleanup(window); + + window->setSource(testFileUrl("RectWithFileDialog.qml")); + window->setGeometry(240,240,1024,320); + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window)); + + // Click to toggle visibility + QObject *dlg = qvariant_cast(window->rootObject()->property("fileDialog")); + dlg->setProperty("modality", QVariant((int)Qt::NonModal)); + QSignalSpy spyVisibilityChanged(dlg, SIGNAL(visibilityChanged())); + QTest::mouseClick(window, Qt::LeftButton, 0, QPoint(1000, 100)); // show + int visibilityChangedCount = spyVisibilityChanged.count(); + QTRY_VERIFY(visibilityChangedCount > 0); + QCOMPARE(dlg->property("visible").toBool(), true); + QTest::mouseClick(window, Qt::LeftButton, 0, QPoint(1000, 100)); // hide + QTRY_VERIFY(spyVisibilityChanged.count() > visibilityChangedCount); + QCOMPARE(dlg->property("visible").toBool(), false); + QCOMPARE(dlg->property("modality").toInt(), (int)Qt::NonModal); +} + +void tst_dialogs::fileDialogNameFilters() +{ + QQuickView *window = new QQuickView; + QScopedPointer cleanup(window); + + window->setSource(testFileUrl("RectWithFileDialog.qml")); + window->setGeometry(240,240,1024,320); + window->show(); + QVERIFY(QTest::qWaitForWindowExposed(window)); + + QObject *dlg = qvariant_cast(window->rootObject()->property("fileDialog")); + QStringList filters; + filters << "QML files (*.qml)"; + filters << "Image files (*.jpg, *.png, *.gif)"; + filters << "All files (*)"; + dlg->setProperty("nameFilters", QVariant(filters)); + QCOMPARE(dlg->property("selectedNameFilter").toString(), filters.first()); +} + +QTEST_MAIN(tst_dialogs) + +#include "tst_dialogs.moc" diff --git a/tests/auto/quick/quick.pro b/tests/auto/quick/quick.pro index 8ce17a10d0..b969927b8f 100644 --- a/tests/auto/quick/quick.pro +++ b/tests/auto/quick/quick.pro @@ -68,6 +68,7 @@ QUICKTESTS = \ qquickcanvasitem \ qquickscreen \ touchmouse \ + dialogs \ SUBDIRS += $$PUBLICTESTS -- cgit v1.2.3 From 3b507d2c48a1637966a6e7537af7763824f0bef8 Mon Sep 17 00:00:00 2001 From: Paul Olav Tvete Date: Wed, 27 Feb 2013 12:53:23 +0100 Subject: Enablers for input methods on Android Change-Id: Ic26722cf530e7a787e7a8f92b645e3c4883d9822 Reviewed-by: Eskil Abrahamsen Blomfeldt Reviewed-by: Lars Knoll --- src/quick/items/qquicktextedit.cpp | 9 ++++++++- src/quick/items/qquicktextedit_p_p.h | 4 ++++ src/quick/items/qquicktextinput.cpp | 2 +- src/quick/items/qquickwindow.cpp | 9 +++++++-- 4 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/quick/items/qquicktextedit.cpp b/src/quick/items/qquicktextedit.cpp index e051b5202c..015f52cc46 100644 --- a/src/quick/items/qquicktextedit.cpp +++ b/src/quick/items/qquicktextedit.cpp @@ -635,6 +635,13 @@ void QQuickTextEditPrivate::mirrorChange() } } +#ifndef QT_NO_IM +Qt::InputMethodHints QQuickTextEditPrivate::effectiveInputMethodHints() const +{ + return inputMethodHints | Qt::ImhMultiLine; +} +#endif + QQuickTextEdit::VAlignment QQuickTextEdit::vAlign() const { Q_D(const QQuickTextEdit); @@ -1632,7 +1639,7 @@ QVariant QQuickTextEdit::inputMethodQuery(Qt::InputMethodQuery property) const v = (bool)(flags() & ItemAcceptsInputMethod); break; case Qt::ImHints: - v = (int)inputMethodHints(); + v = (int)d->effectiveInputMethodHints(); break; default: v = d->control->inputMethodQuery(property); diff --git a/src/quick/items/qquicktextedit_p_p.h b/src/quick/items/qquicktextedit_p_p.h index e4819e4d2d..5306c979cf 100644 --- a/src/quick/items/qquicktextedit_p_p.h +++ b/src/quick/items/qquicktextedit_p_p.h @@ -106,6 +106,10 @@ public: void setNativeCursorEnabled(bool enabled) { control->setCursorWidth(enabled ? 1 : 0); } +#ifndef QT_NO_IM + Qt::InputMethodHints effectiveInputMethodHints() const; +#endif + QColor color; QColor selectionColor; QColor selectedTextColor; diff --git a/src/quick/items/qquicktextinput.cpp b/src/quick/items/qquicktextinput.cpp index f9de3d25e7..1c65c37516 100644 --- a/src/quick/items/qquicktextinput.cpp +++ b/src/quick/items/qquicktextinput.cpp @@ -1910,7 +1910,7 @@ QVariant QQuickTextInput::inputMethodQuery(Qt::InputMethodQuery property) const else return QVariant(d->selectionStart()); default: - return QVariant(); + return QQuickItem::inputMethodQuery(property); } } #endif // QT_NO_IM diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index efc67c0437..2e8f58b849 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -77,8 +77,11 @@ void QQuickWindowPrivate::updateFocusItemTransform() Q_Q(QQuickWindow); #ifndef QT_NO_IM QQuickItem *focus = q->activeFocusItem(); - if (focus && qApp->focusObject() == focus) - qApp->inputMethod()->setInputItemTransform(QQuickItemPrivate::get(focus)->itemToWindowTransform()); + if (focus && qApp->focusObject() == focus) { + QQuickItemPrivate *focusPrivate = QQuickItemPrivate::get(focus); + qApp->inputMethod()->setInputItemTransform(focusPrivate->itemToWindowTransform()); + qApp->inputMethod()->setInputItemRectangle(QRectF(0, 0, focusPrivate->width, focusPrivate->height)); + } #endif } @@ -690,6 +693,7 @@ void QQuickWindowPrivate::setFocusInScope(QQuickItem *scope, QQuickItem *item, F } afi = afi->parentItem(); } + updateFocusItemTransform(); QFocusEvent event(QEvent::FocusIn, Qt::OtherFocusReason); q->sendEvent(newActiveFocusItem, &event); @@ -771,6 +775,7 @@ void QQuickWindowPrivate::clearFocusInScope(QQuickItem *scope, QQuickItem *item, if (newActiveFocusItem) { Q_ASSERT(newActiveFocusItem == scope); activeFocusItem = scope; + updateFocusItemTransform(); QFocusEvent event(QEvent::FocusIn, Qt::OtherFocusReason); q->sendEvent(newActiveFocusItem, &event); -- cgit v1.2.3 From 1c451b40aee66a38ca3d61e5beec4ae8c986c8ed Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Mon, 4 Mar 2013 12:52:11 +0100 Subject: pressedCanceledOnWindowDeactivate pops up a second window MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit in order to cause the first one to be deactivated. Task-number: QTBUG-29953 Change-Id: I7fec66b07976b2afc78941d39c593f99ea484522 Reviewed-by: Samuel Rødal --- tests/auto/quick/qquickmousearea/data/pressedCanceled.qml | 9 ++++++++- tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp | 8 +++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/tests/auto/quick/qquickmousearea/data/pressedCanceled.qml b/tests/auto/quick/qquickmousearea/data/pressedCanceled.qml index 231436d0f2..14630b8962 100644 --- a/tests/auto/quick/qquickmousearea/data/pressedCanceled.qml +++ b/tests/auto/quick/qquickmousearea/data/pressedCanceled.qml @@ -1,4 +1,5 @@ import QtQuick 2.0 +import QtQuick.Window 2.0 Rectangle { id: root @@ -7,6 +8,12 @@ Rectangle { property bool pressed:mouse.pressed property bool canceled: false property bool released: false + property alias secondWindow: secondWindow + + Window { + id: secondWindow + x: root.x + root.width + } MouseArea { id: mouse @@ -15,4 +22,4 @@ Rectangle { onCanceled: {root.canceled = true} onReleased: {root.released = true; root.canceled = false} } -} \ No newline at end of file +} diff --git a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp index 327abbebd5..6ee79b06a2 100644 --- a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp +++ b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp @@ -583,16 +583,14 @@ void tst_QQuickMouseArea::pressedCanceledOnWindowDeactivate() QVERIFY(!window->rootObject()->property("canceled").toBool()); QVERIFY(!window->rootObject()->property("released").toBool()); - QTest::qWait(200); + QWindow *secondWindow = qvariant_cast(window->rootObject()->property("secondWindow")); + secondWindow->setProperty("visible", true); + QTest::qWaitForWindowActive(secondWindow); - QEvent windowDeactivateEvent(QEvent::WindowDeactivate); - QGuiApplication::sendEvent(window, &windowDeactivateEvent); QVERIFY(!window->rootObject()->property("pressed").toBool()); QVERIFY(window->rootObject()->property("canceled").toBool()); QVERIFY(!window->rootObject()->property("released").toBool()); - QTest::qWait(200); - //press again QGuiApplication::sendEvent(window, &pressEvent); QVERIFY(window->rootObject()->property("pressed").toBool()); -- cgit v1.2.3 From 037c0d5d597d2e7d35e74ea1de42d4ecf16fbc6a Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Mon, 4 Mar 2013 13:58:57 +0100 Subject: PinchArea autotest doesn't depend on window activation; QScopedPointer The QQuickView pointer is guarded by a QScopedPointer to make sure it will be deleted if the test fails. And because we don't depend on window activation, the test should be parallelizable. Change-Id: I33a5dcff037087d9752b264eb067196c2a5be535 Reviewed-by: Gunnar Sletta --- .../auto/quick/qquickpincharea/qquickpincharea.pro | 1 + .../quick/qquickpincharea/tst_qquickpincharea.cpp | 29 +++++++--------------- 2 files changed, 10 insertions(+), 20 deletions(-) diff --git a/tests/auto/quick/qquickpincharea/qquickpincharea.pro b/tests/auto/quick/qquickpincharea/qquickpincharea.pro index df3b14d8c0..970ce48851 100644 --- a/tests/auto/quick/qquickpincharea/qquickpincharea.pro +++ b/tests/auto/quick/qquickpincharea/qquickpincharea.pro @@ -1,4 +1,5 @@ CONFIG += testcase +CONFIG += parallel_test TARGET = tst_qquickpincharea macx:CONFIG -= app_bundle diff --git a/tests/auto/quick/qquickpincharea/tst_qquickpincharea.cpp b/tests/auto/quick/qquickpincharea/tst_qquickpincharea.cpp index ba1db0e7cf..18595133d0 100644 --- a/tests/auto/quick/qquickpincharea/tst_qquickpincharea.cpp +++ b/tests/auto/quick/qquickpincharea/tst_qquickpincharea.cpp @@ -84,10 +84,9 @@ void tst_QQuickPinchArea::cleanupTestCase() } void tst_QQuickPinchArea::pinchProperties() { - QQuickView *window = createView(); + QScopedPointer window(createView()); window->setSource(testFileUrl("pinchproperties.qml")); window->show(); - window->requestActivate(); QVERIFY(window->rootObject() != 0); QQuickPinchArea *pinchArea = window->rootObject()->findChild("pincharea"); @@ -195,8 +194,6 @@ void tst_QQuickPinchArea::pinchProperties() QCOMPARE(rotMinSpy.count(),1); QCOMPARE(rotMaxSpy.count(),1); - - delete window; } QTouchEvent::TouchPoint makeTouchPoint(int id, QPoint p, QQuickView *v, QQuickItem *i) @@ -211,10 +208,10 @@ QTouchEvent::TouchPoint makeTouchPoint(int id, QPoint p, QQuickView *v, QQuickIt void tst_QQuickPinchArea::scale() { QQuickView *window = createView(); + QScopedPointer scope(window); window->setSource(testFileUrl("pinchproperties.qml")); window->show(); - window->requestActivate(); - QVERIFY(QTest::qWaitForWindowActive(window)); + QVERIFY(QTest::qWaitForWindowExposed(window)); QVERIFY(window->rootObject() != 0); qApp->processEvents(); @@ -267,17 +264,15 @@ void tst_QQuickPinchArea::scale() pinchSequence.release(0, p1, window).release(1, p2, window).commit(); } QVERIFY(!root->property("pinchActive").toBool()); - - delete window; } void tst_QQuickPinchArea::pan() { QQuickView *window = createView(); + QScopedPointer scope(window); window->setSource(testFileUrl("pinchproperties.qml")); window->show(); - window->requestActivate(); - QVERIFY(QTest::qWaitForWindowActive(window)); + QVERIFY(QTest::qWaitForWindowExposed(window)); QVERIFY(window->rootObject() != 0); qApp->processEvents(); @@ -327,18 +322,16 @@ void tst_QQuickPinchArea::pan() QTest::touchEvent(window, device).release(0, p1, window).release(1, p2, window); QVERIFY(!root->property("pinchActive").toBool()); - - delete window; } // test pinch, release one point, touch again to continue pinch void tst_QQuickPinchArea::retouch() { QQuickView *window = createView(); + QScopedPointer scope(window); window->setSource(testFileUrl("pinchproperties.qml")); window->show(); - window->requestActivate(); - QVERIFY(QTest::qWaitForWindowActive(window)); + QVERIFY(QTest::qWaitForWindowExposed(window)); QVERIFY(window->rootObject() != 0); qApp->processEvents(); @@ -413,8 +406,6 @@ void tst_QQuickPinchArea::retouch() QCOMPARE(startedSpy.count(), 2); QCOMPARE(finishedSpy.count(), 1); } - - delete window; } void tst_QQuickPinchArea::transformedPinchArea_data() @@ -449,10 +440,10 @@ void tst_QQuickPinchArea::transformedPinchArea() QFETCH(bool, shouldPinch); QQuickView *view = createView(); + QScopedPointer scope(view); view->setSource(testFileUrl("transformedPinchArea.qml")); view->show(); - view->requestActivate(); - QVERIFY(QTest::qWaitForWindowActive(view)); + QVERIFY(QTest::qWaitForWindowExposed(view)); QVERIFY(view->rootObject() != 0); qApp->processEvents(); @@ -475,8 +466,6 @@ void tst_QQuickPinchArea::transformedPinchArea() pinchSequence.release(0, p1, view).release(1, p2, view).commit(); QCOMPARE(pinchArea->property("pinching").toBool(), false); } - - delete view; } QQuickView *tst_QQuickPinchArea::createView() -- cgit v1.2.3 From 5e88373c188d9afc88a09ed11c635b59a92614fe Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Mon, 4 Mar 2013 14:01:02 +0100 Subject: Autotests w CONFIG+=parallel_test don't depend on activation or focus If the test does requestActivate() or relies on isFocusWindow(), the test cannot be parallel_test. Also, a lot of the tests don't actually need the window to be active, only exposed; and waiting only for exposed is likely to make them more stable. Change-Id: I0845b9b12ddf7f0c8906d9738a3e26d46ab98820 Reviewed-by: Gunnar Sletta --- .../tst_qquickanimatedimage.cpp | 3 +- .../tst_qquickanimatedsprite.cpp | 3 +- .../quick/qquickflickable/tst_qquickflickable.cpp | 26 ------------ tests/auto/quick/qquickimage/tst_qquickimage.cpp | 6 +-- .../quick/qquickitemlayer/tst_qquickitemlayer.cpp | 2 +- .../quick/qquickmousearea/tst_qquickmousearea.cpp | 49 ++++++++++------------ .../tst_qquickmultipointtoucharea.cpp | 3 +- tests/auto/quick/qquickstates/tst_qquickstates.cpp | 3 +- tests/auto/quick/qquickwindow/qquickwindow.pro | 1 - tests/auto/quick/touchmouse/tst_touchmouse.cpp | 3 +- 10 files changed, 31 insertions(+), 68 deletions(-) diff --git a/tests/auto/quick/qquickanimatedimage/tst_qquickanimatedimage.cpp b/tests/auto/quick/qquickanimatedimage/tst_qquickanimatedimage.cpp index aad1327e6f..201287b2a8 100644 --- a/tests/auto/quick/qquickanimatedimage/tst_qquickanimatedimage.cpp +++ b/tests/auto/quick/qquickanimatedimage/tst_qquickanimatedimage.cpp @@ -152,9 +152,8 @@ void tst_qquickanimatedimage::mirror_running() QQuickView window; window.setSource(testFileUrl("hearts.qml")); - window.requestActivate(); window.show(); - QTest::qWaitForWindowActive(&window); + QTest::qWaitForWindowExposed(&window); QQuickAnimatedImage *anim = qobject_cast(window.rootObject()); QVERIFY(anim); diff --git a/tests/auto/quick/qquickanimatedsprite/tst_qquickanimatedsprite.cpp b/tests/auto/quick/qquickanimatedsprite/tst_qquickanimatedsprite.cpp index 7847268d94..34a1614ac5 100644 --- a/tests/auto/quick/qquickanimatedsprite/tst_qquickanimatedsprite.cpp +++ b/tests/auto/quick/qquickanimatedsprite/tst_qquickanimatedsprite.cpp @@ -93,8 +93,7 @@ void tst_qquickanimatedsprite::test_frameChangedSignal() window->setSource(testFileUrl("frameChange.qml")); window->show(); - window->requestActivate(); - QVERIFY(QTest::qWaitForWindowActive(window)); + QVERIFY(QTest::qWaitForWindowExposed(window)); QVERIFY(window->rootObject()); QQuickAnimatedSprite* sprite = window->rootObject()->findChild("sprite"); diff --git a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp index 875ade45b8..d643f91366 100644 --- a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp +++ b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp @@ -214,8 +214,6 @@ void tst_qquickflickable::rebound() window->setSource(testFileUrl("rebound.qml")); window->show(); QVERIFY(QTest::qWaitForWindowExposed(window.data())); - window->requestActivate(); - QVERIFY(QTest::qWaitForWindowActive(window.data())); QVERIFY(window->rootObject() != 0); QQuickFlickable *flickable = qobject_cast(window->rootObject()); @@ -355,8 +353,6 @@ void tst_qquickflickable::pressDelay() window->setSource(testFileUrl("pressDelay.qml")); window->show(); QVERIFY(QTest::qWaitForWindowExposed(window.data())); - window->requestActivate(); - QVERIFY(QTest::qWaitForWindowActive(window.data())); QVERIFY(window->rootObject() != 0); QQuickFlickable *flickable = qobject_cast(window->rootObject()); @@ -396,8 +392,6 @@ void tst_qquickflickable::nestedPressDelay() window->setSource(testFileUrl("nestedPressDelay.qml")); window->show(); QVERIFY(QTest::qWaitForWindowExposed(window.data())); - window->requestActivate(); - QVERIFY(QTest::qWaitForWindowActive(window.data())); QVERIFY(window->rootObject() != 0); QQuickFlickable *outer = qobject_cast(window->rootObject()); @@ -554,8 +548,6 @@ void tst_qquickflickable::wheel() window->setSource(testFileUrl("wheel.qml")); window->show(); QVERIFY(QTest::qWaitForWindowExposed(window.data())); - window->requestActivate(); - QVERIFY(QTest::qWaitForWindowActive(window.data())); QVERIFY(window->rootObject() != 0); QQuickFlickable *flick = window->rootObject()->findChild("flick"); @@ -622,8 +614,6 @@ void tst_qquickflickable::movingAndFlicking() window->setSource(testFileUrl("flickable03.qml")); window->show(); QVERIFY(QTest::qWaitForWindowExposed(window.data())); - window->requestActivate(); - QVERIFY(QTest::qWaitForWindowActive(window.data())); QVERIFY(window->rootObject() != 0); QQuickFlickable *flickable = qobject_cast(window->rootObject()); @@ -781,8 +771,6 @@ void tst_qquickflickable::movingAndDragging() window->setSource(testFileUrl("flickable03.qml")); window->show(); QVERIFY(QTest::qWaitForWindowExposed(window.data())); - window->requestActivate(); - QVERIFY(QTest::qWaitForWindowActive(window.data())); QVERIFY(window->rootObject() != 0); QQuickFlickable *flickable = qobject_cast(window->rootObject()); @@ -947,8 +935,6 @@ void tst_qquickflickable::flickOnRelease() window->setSource(testFileUrl("flickable03.qml")); window->show(); QVERIFY(QTest::qWaitForWindowExposed(window.data())); - window->requestActivate(); - QVERIFY(QTest::qWaitForWindowActive(window.data())); QVERIFY(window->rootObject() != 0); QQuickFlickable *flickable = qobject_cast(window->rootObject()); @@ -983,8 +969,6 @@ void tst_qquickflickable::pressWhileFlicking() window->setSource(testFileUrl("flickable03.qml")); window->show(); QVERIFY(QTest::qWaitForWindowExposed(window.data())); - window->requestActivate(); - QVERIFY(QTest::qWaitForWindowActive(window.data())); QVERIFY(window->rootObject() != 0); QQuickFlickable *flickable = qobject_cast(window->rootObject()); @@ -1035,8 +1019,6 @@ void tst_qquickflickable::disabled() window->setSource(testFileUrl("disabled.qml")); window->show(); QVERIFY(QTest::qWaitForWindowExposed(window.data())); - window->requestActivate(); - QVERIFY(QTest::qWaitForWindowActive(window.data())); QVERIFY(window->rootObject() != 0); QQuickFlickable *flick = window->rootObject()->findChild("flickable"); @@ -1065,8 +1047,6 @@ void tst_qquickflickable::flickVelocity() window->setSource(testFileUrl("flickable03.qml")); window->show(); QVERIFY(QTest::qWaitForWindowExposed(window.data())); - window->requestActivate(); - QVERIFY(QTest::qWaitForWindowActive(window.data())); QVERIFY(window->rootObject() != 0); QQuickFlickable *flickable = qobject_cast(window->rootObject()); @@ -1167,8 +1147,6 @@ void tst_qquickflickable::cancelOnMouseGrab() window->setSource(testFileUrl("cancel.qml")); window->show(); QVERIFY(QTest::qWaitForWindowExposed(window.data())); - window->requestActivate(); - QVERIFY(QTest::qWaitForWindowActive(window.data())); QVERIFY(window->rootObject() != 0); QQuickFlickable *flickable = qobject_cast(window->rootObject()); @@ -1204,8 +1182,6 @@ void tst_qquickflickable::clickAndDragWhenTransformed() view->setSource(testFileUrl("transformedFlickable.qml")); view->show(); QVERIFY(QTest::qWaitForWindowExposed(view.data())); - view->requestActivate(); - QVERIFY(QTest::qWaitForWindowActive(view.data())); QVERIFY(view->rootObject() != 0); QQuickFlickable *flickable = view->rootObject()->findChild("flickable"); @@ -1255,8 +1231,6 @@ void tst_qquickflickable::flickTwiceUsingTouches() QQuickView *window = new QQuickView; window->setSource(testFileUrl("longList.qml")); window->show(); - window->requestActivate(); - QVERIFY(QTest::qWaitForWindowActive(window)); QVERIFY(window->rootObject() != 0); QQuickFlickable *flickable = qobject_cast(window->rootObject()); diff --git a/tests/auto/quick/qquickimage/tst_qquickimage.cpp b/tests/auto/quick/qquickimage/tst_qquickimage.cpp index 51ac5c640a..675c8f9350 100644 --- a/tests/auto/quick/qquickimage/tst_qquickimage.cpp +++ b/tests/auto/quick/qquickimage/tst_qquickimage.cpp @@ -315,8 +315,7 @@ void tst_qquickimage::mirror() obj->setFillMode(fillMode); obj->setProperty("mirror", true); window->show(); - window->requestActivate(); - QVERIFY(QTest::qWaitForWindowActive(window)); + QVERIFY(QTest::qWaitForWindowExposed(window)); QImage screenshot = window->grabWindow(); screenshots[fillMode] = screenshot; @@ -500,8 +499,7 @@ void tst_qquickimage::tiling_QTBUG_6716() QQuickView view(testFileUrl(source)); view.show(); - view.requestActivate(); - QVERIFY(QTest::qWaitForWindowActive(&view)); + QVERIFY(QTest::qWaitForWindowExposed(&view)); QQuickImage *tiling = findItem(view.rootObject(), "tiling"); diff --git a/tests/auto/quick/qquickitemlayer/tst_qquickitemlayer.cpp b/tests/auto/quick/qquickitemlayer/tst_qquickitemlayer.cpp index 9387264eb0..4f103bd1fa 100644 --- a/tests/auto/quick/qquickitemlayer/tst_qquickitemlayer.cpp +++ b/tests/auto/quick/qquickitemlayer/tst_qquickitemlayer.cpp @@ -266,7 +266,7 @@ void tst_QQuickItemLayer::layerVisibility() view.show(); - QTest::qWaitForWindowActive(&view); + QTest::qWaitForWindowExposed(&view); QImage fb = view.grabWindow(); uint pixel = fb.pixel(0, 0); diff --git a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp index 6ee79b06a2..3c08b16168 100644 --- a/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp +++ b/tests/auto/quick/qquickmousearea/tst_qquickmousearea.cpp @@ -132,7 +132,7 @@ void tst_QQuickMouseArea::dragProperties() window->setSource(testFileUrl("dragproperties.qml")); window->show(); - window->requestActivate(); + QTest::qWaitForWindowExposed(window); QVERIFY(window->rootObject() != 0); QQuickMouseArea *mouseRegion = window->rootObject()->findChild("mouseregion"); @@ -218,7 +218,7 @@ void tst_QQuickMouseArea::resetDrag() window->rootContext()->setContextProperty("haveTarget", QVariant(true)); window->setSource(testFileUrl("dragreset.qml")); window->show(); - window->requestActivate(); + QTest::qWaitForWindowExposed(window); QVERIFY(window->rootObject() != 0); QQuickMouseArea *mouseRegion = window->rootObject()->findChild("mouseregion"); @@ -250,7 +250,7 @@ void tst_QQuickMouseArea::dragging() window->setSource(testFileUrl("dragging.qml")); window->show(); - window->requestActivate(); + QTest::qWaitForWindowExposed(window); QVERIFY(QTest::qWaitForWindowExposed(window)); QVERIFY(window->rootObject() != 0); @@ -303,8 +303,7 @@ void tst_QQuickMouseArea::invalidDrag() window->setSource(testFileUrl("dragging.qml")); window->show(); - window->requestActivate(); - QTest::qWait(20); + QTest::qWaitForWindowExposed(window); QVERIFY(window->rootObject() != 0); QQuickMouseArea *mouseRegion = window->rootObject()->findChild("mouseregion"); @@ -355,8 +354,7 @@ void tst_QQuickMouseArea::setDragOnPressed() window->setSource(testFileUrl("setDragOnPressed.qml")); window->show(); - window->requestActivate(); - QTest::qWait(20); + QTest::qWaitForWindowExposed(window); QVERIFY(window->rootObject() != 0); QQuickMouseArea *mouseArea = qobject_cast(window->rootObject()); @@ -410,7 +408,7 @@ void tst_QQuickMouseArea::updateMouseAreaPosOnClick() QQuickView *window = createView(); window->setSource(testFileUrl("updateMousePosOnClick.qml")); window->show(); - window->requestActivate(); + QTest::qWaitForWindowExposed(window); QVERIFY(window->rootObject() != 0); QQuickMouseArea *mouseRegion = window->rootObject()->findChild("mouseregion"); @@ -439,7 +437,7 @@ void tst_QQuickMouseArea::updateMouseAreaPosOnResize() QQuickView *window = createView(); window->setSource(testFileUrl("updateMousePosOnResize.qml")); window->show(); - window->requestActivate(); + QTest::qWaitForWindowExposed(window); QVERIFY(window->rootObject() != 0); QQuickMouseArea *mouseRegion = window->rootObject()->findChild("mouseregion"); @@ -476,7 +474,7 @@ void tst_QQuickMouseArea::noOnClickedWithPressAndHold() QQuickView *window = createView(); window->setSource(testFileUrl("clickandhold.qml")); window->show(); - window->requestActivate(); + QTest::qWaitForWindowExposed(window); QVERIFY(window->rootObject() != 0); QQuickMouseArea *mouseArea = qobject_cast(window->rootObject()->children().first()); QVERIFY(mouseArea); @@ -509,7 +507,7 @@ void tst_QQuickMouseArea::noOnClickedWithPressAndHold() QQuickView *window = createView(); window->setSource(testFileUrl("noclickandhold.qml")); window->show(); - window->requestActivate(); + QTest::qWaitForWindowExposed(window); QVERIFY(window->rootObject() != 0); QMouseEvent pressEvent(QEvent::MouseButtonPress, QPoint(100, 100), Qt::LeftButton, Qt::LeftButton, 0); @@ -533,7 +531,7 @@ void tst_QQuickMouseArea::onMousePressRejected() QQuickView *window = createView(); window->setSource(testFileUrl("rejectEvent.qml")); window->show(); - window->requestActivate(); + QTest::qWaitForWindowExposed(window); QVERIFY(window->rootObject() != 0); QVERIFY(window->rootObject()->property("enabled").toBool()); @@ -570,7 +568,7 @@ void tst_QQuickMouseArea::pressedCanceledOnWindowDeactivate() QQuickView *window = createView(); window->setSource(testFileUrl("pressedCanceled.qml")); window->show(); - window->requestActivate(); + QTest::qWaitForWindowExposed(window); QVERIFY(window->rootObject() != 0); QVERIFY(!window->rootObject()->property("pressed").toBool()); QVERIFY(!window->rootObject()->property("canceled").toBool()); @@ -617,7 +615,7 @@ void tst_QQuickMouseArea::doubleClick() QQuickView *window = createView(); window->setSource(testFileUrl("doubleclick.qml")); window->show(); - window->requestActivate(); + QTest::qWaitForWindowExposed(window); QVERIFY(window->rootObject() != 0); QQuickMouseArea *mouseArea = window->rootObject()->findChild("mousearea"); @@ -655,7 +653,7 @@ void tst_QQuickMouseArea::clickTwice() QQuickView *window = createView(); window->setSource(testFileUrl("clicktwice.qml")); window->show(); - window->requestActivate(); + QTest::qWaitForWindowExposed(window); QVERIFY(window->rootObject() != 0); QQuickMouseArea *mouseArea = window->rootObject()->findChild("mousearea"); @@ -692,7 +690,7 @@ void tst_QQuickMouseArea::invalidClick() QQuickView *window = createView(); window->setSource(testFileUrl("doubleclick.qml")); window->show(); - window->requestActivate(); + QTest::qWaitForWindowExposed(window); QVERIFY(window->rootObject() != 0); QQuickMouseArea *mouseArea = window->rootObject()->findChild("mousearea"); @@ -726,7 +724,7 @@ void tst_QQuickMouseArea::pressedOrdering() QQuickView *window = createView(); window->setSource(testFileUrl("pressedOrdering.qml")); window->show(); - window->requestActivate(); + QTest::qWaitForWindowExposed(window); QVERIFY(window->rootObject() != 0); QCOMPARE(window->rootObject()->property("value").toString(), QLatin1String("base")); @@ -754,7 +752,7 @@ void tst_QQuickMouseArea::preventStealing() window->setSource(testFileUrl("preventstealing.qml")); window->show(); - window->requestActivate(); + QTest::qWaitForWindowExposed(window); QVERIFY(window->rootObject() != 0); QQuickFlickable *flickable = qobject_cast(window->rootObject()); @@ -818,7 +816,7 @@ void tst_QQuickMouseArea::clickThrough() QQuickView *window = createView(); window->setSource(testFileUrl("clickThrough.qml")); window->show(); - window->requestActivate(); + QTest::qWaitForWindowExposed(window); QVERIFY(window->rootObject() != 0); QTest::mousePress(window, Qt::LeftButton, 0, QPoint(100,100)); @@ -853,7 +851,7 @@ void tst_QQuickMouseArea::clickThrough() window = createView(); window->setSource(testFileUrl("clickThrough2.qml")); window->show(); - window->requestActivate(); + QTest::qWaitForWindowExposed(window); QVERIFY(window->rootObject() != 0); QTest::mousePress(window, Qt::LeftButton, 0, QPoint(100,100)); @@ -1012,8 +1010,7 @@ void tst_QQuickMouseArea::disableAfterPress() QQuickView *window = createView(); window->setSource(testFileUrl("dragging.qml")); window->show(); - window->requestActivate(); - QTest::qWait(20); + QTest::qWaitForWindowExposed(window); QVERIFY(window->rootObject() != 0); QQuickMouseArea *mouseArea = window->rootObject()->findChild("mouseregion"); @@ -1162,7 +1159,7 @@ void tst_QQuickMouseArea::transformedMouseArea() QQuickView *window = createView(); window->setSource(testFileUrl("transformedMouseArea.qml")); window->show(); - window->requestActivate(); + QTest::qWaitForWindowExposed(window); QVERIFY(window->rootObject() != 0); QQuickMouseArea *mouseArea = window->rootObject()->findChild("mouseArea"); @@ -1280,7 +1277,7 @@ void tst_QQuickMouseArea::pressedMultipleButtons() QQuickView *view = createView(); view->setSource(testFileUrl("simple.qml")); view->show(); - view->requestActivate(); + QTest::qWaitForWindowExposed(view); QVERIFY(view->rootObject() != 0); QQuickMouseArea *mouseArea = view->rootObject()->findChild("mousearea"); @@ -1317,7 +1314,7 @@ void tst_QQuickMouseArea::changeAxis() view->setSource(testFileUrl("changeAxis.qml")); view->show(); - view->requestActivate(); + QTest::qWaitForWindowExposed(view); QTRY_VERIFY(view->rootObject() != 0); QQuickMouseArea *mouseRegion = view->rootObject()->findChild("mouseregion"); @@ -1409,7 +1406,7 @@ void tst_QQuickMouseArea::moveAndReleaseWithoutPress() window->setSource(testFileUrl("moveAndReleaseWithoutPress.qml")); window->show(); - window->requestActivate(); + QTest::qWaitForWindowExposed(window); QVERIFY(QTest::qWaitForWindowExposed(window)); QObject *root = window->rootObject(); diff --git a/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp b/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp index 73c2cf68dd..663d02d921 100644 --- a/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp +++ b/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp @@ -851,8 +851,7 @@ QQuickView *tst_QQuickMultiPointTouchArea::createAndShowView(const QString &file QQuickView *window = new QQuickView(0); window->setSource(testFileUrl(file)); window->show(); - window->requestActivate(); - QTest::qWaitForWindowActive(window); + QTest::qWaitForWindowExposed(window); return window; } diff --git a/tests/auto/quick/qquickstates/tst_qquickstates.cpp b/tests/auto/quick/qquickstates/tst_qquickstates.cpp index 726d3a6e75..e165b559f7 100644 --- a/tests/auto/quick/qquickstates/tst_qquickstates.cpp +++ b/tests/auto/quick/qquickstates/tst_qquickstates.cpp @@ -1009,9 +1009,8 @@ void tst_qquickstates::anchorRewindBug() view->setSource(testFileUrl("anchorRewindBug.qml")); view->show(); - view->requestActivate(); - QVERIFY(QTest::qWaitForWindowActive(view)); + QVERIFY(QTest::qWaitForWindowExposed(view)); QQuickRectangle *rect = qobject_cast(view->rootObject()); QVERIFY(rect != 0); diff --git a/tests/auto/quick/qquickwindow/qquickwindow.pro b/tests/auto/quick/qquickwindow/qquickwindow.pro index 59d96429db..b1fc5cd4f2 100644 --- a/tests/auto/quick/qquickwindow/qquickwindow.pro +++ b/tests/auto/quick/qquickwindow/qquickwindow.pro @@ -6,7 +6,6 @@ include (../../shared/util.pri) macx:CONFIG -= app_bundle -CONFIG += parallel_test QT += core-private gui-private qml-private quick-private v8-private testlib TESTDATA = data/* diff --git a/tests/auto/quick/touchmouse/tst_touchmouse.cpp b/tests/auto/quick/touchmouse/tst_touchmouse.cpp index caad2539be..d33bf3991c 100644 --- a/tests/auto/quick/touchmouse/tst_touchmouse.cpp +++ b/tests/auto/quick/touchmouse/tst_touchmouse.cpp @@ -824,8 +824,7 @@ void tst_TouchMouse::mouseOnFlickableOnPinch() QQuickView *window = createView(); window->setSource(testFileUrl("mouseonflickableonpinch.qml")); window->show(); - window->requestActivate(); - QVERIFY(QTest::qWaitForWindowActive(window)); + QVERIFY(QTest::qWaitForWindowExposed(window)); QVERIFY(window->rootObject() != 0); QRect windowRect = QRect(window->position(), window->size()); QCursor::setPos(windowRect.center()); -- cgit v1.2.3 From 0ba44aef85284fc976417e36118501f74a006806 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Mon, 25 Jun 2012 20:06:12 +0200 Subject: Added photosurface example to demo PinchArea/MouseArea together It seems to be important to put the MouseArea inside the PinchArea in order to support simultaneous drag/rotate/pinch. Change-Id: Ic3ec6a131bd2d4c7f8a8560cd9c3015a717fef20 Reviewed-by: Gunnar Sletta --- examples/quick/demos/demos.pro | 1 + .../doc/images/qml-photosurface-example-small.png | Bin 0 -> 47271 bytes .../demos/photosurface/doc/src/photosurface.qdoc | 37 ++++++ examples/quick/demos/photosurface/photosurface.qml | 138 +++++++++++++++++++++ .../demos/photosurface/photosurface.qmlproject | 20 +++ 5 files changed, 196 insertions(+) create mode 100644 examples/quick/demos/photosurface/doc/images/qml-photosurface-example-small.png create mode 100644 examples/quick/demos/photosurface/doc/src/photosurface.qdoc create mode 100644 examples/quick/demos/photosurface/photosurface.qml create mode 100644 examples/quick/demos/photosurface/photosurface.qmlproject diff --git a/examples/quick/demos/demos.pro b/examples/quick/demos/demos.pro index 474b5b1bc0..67128a905f 100644 --- a/examples/quick/demos/demos.pro +++ b/examples/quick/demos/demos.pro @@ -8,4 +8,5 @@ SUBDIRS = samegame \ EXAMPLE_FILES = \ clocks \ photoviewer \ + photosurface \ rssnews diff --git a/examples/quick/demos/photosurface/doc/images/qml-photosurface-example-small.png b/examples/quick/demos/photosurface/doc/images/qml-photosurface-example-small.png new file mode 100644 index 0000000000..a0cd823999 Binary files /dev/null and b/examples/quick/demos/photosurface/doc/images/qml-photosurface-example-small.png differ diff --git a/examples/quick/demos/photosurface/doc/src/photosurface.qdoc b/examples/quick/demos/photosurface/doc/src/photosurface.qdoc new file mode 100644 index 0000000000..343acce14a --- /dev/null +++ b/examples/quick/demos/photosurface/doc/src/photosurface.qdoc @@ -0,0 +1,37 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: http://www.gnu.org/copyleft/fdl.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \title QML Demo - Photo Surface + \example demos/photosurface + \brief This is the typical touch photo-shuffling example written in QML. + \image qml-photosurface-example-small.png + \ingroup qtquickexamples + + This example demonstrates how to handle dragging, rotation and + pinch zooming within the same item using a PinchArea containing a MouseArea. +*/ diff --git a/examples/quick/demos/photosurface/photosurface.qml b/examples/quick/demos/photosurface/photosurface.qml new file mode 100644 index 0000000000..80957180d0 --- /dev/null +++ b/examples/quick/demos/photosurface/photosurface.qml @@ -0,0 +1,138 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +import QtQuick 2.0 +import QtQuick.Dialogs 1.0 +import Qt.labs.folderlistmodel 1.0 + +Rectangle { + id: root + width: 1024; height: 600 + color: "black" + visible: true + property int highestZ: 0 + property real defaultSize: 200 + + FileDialog { + id: fileDialog + title: "Choose a folder with some images" + selectFolder: true + onAccepted: { folderModel.folder = filePaths[0] + "/" } + } + + Repeater { + model: FolderListModel { + id: folderModel + objectName: "folderModel" + showDirs: false + nameFilters: ["*.png", "*.jpg", "*.gif"] + } + Rectangle { + id: photoFrame + width: image.width * image.scale + 20 + height: image.height * image.scale + 20 + border.color: "black" + border.width: 2 + smooth: true + antialiasing: true + x: Math.random() * root.width - defaultSize + y: Math.random() * root.height - defaultSize + rotation: Math.random() * 13 - 6 + Image { + id: image + anchors.centerIn: parent + fillMode: Image.PreserveAspectFit + source: folderModel.folder + fileName + scale: defaultSize / Math.max(sourceSize.width, sourceSize.height) + antialiasing: true + } + PinchArea { + anchors.fill: parent + property real initialScale + property real initialRotation + onPinchStarted: { + initialScale = image.scale + initialRotation = parent.rotation + } + onPinchUpdated: { + image.scale = initialScale * pinch.scale; + parent.rotation = initialRotation + pinch.rotation + if (Math.abs(photoFrame.rotation) < 4) + parent.rotation = 0; + } + onPinchFinished: photoFrame.border.color = "black"; + MouseArea { + id: dragArea + hoverEnabled: true + anchors.fill: parent + drag.target: photoFrame + onPressed: photoFrame.z = ++root.highestZ; + onEntered: photoFrame.border.color = "red"; + onExited: photoFrame.border.color = "black"; + onWheel: { + if (wheel.modifiers & Qt.ControlModifier) { + photoFrame.rotation += wheel.angleDelta.y / 120 * 5; + if (Math.abs(photoFrame.rotation) < 4) + photoFrame.rotation = 0; + } else { + photoFrame.rotation += wheel.angleDelta.x / 120; + if (Math.abs(photoFrame.rotation) < 0.6) + photoFrame.rotation = 0; + var scaleBefore = image.scale; + image.scale += image.scale * wheel.angleDelta.y / 120 / 10; + photoFrame.x -= image.width * (image.scale - scaleBefore) / 2.0; + photoFrame.y -= image.height * (image.scale - scaleBefore) / 2.0; + } + } + } + } + } + } + Text { + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.margins: 10 + color: "darkgrey" + text: "On a touchscreen: use two fingers to zoom and rotate, one finger to drag\n" + + "With a mouse: drag normally, use the vertical wheel to zoom, horizontal wheel to rotate, or hold Ctrl while using the vertical wheel to rotate" + } + + Component.onCompleted: fileDialog.open() +} diff --git a/examples/quick/demos/photosurface/photosurface.qmlproject b/examples/quick/demos/photosurface/photosurface.qmlproject new file mode 100644 index 0000000000..ae2065f59c --- /dev/null +++ b/examples/quick/demos/photosurface/photosurface.qmlproject @@ -0,0 +1,20 @@ +import QmlProject 1.1 + +Project { + mainFile: "photosurface.qml" + + /* Include .qml, .js, and image files from current directory and subdirectories */ + QmlFiles { + directory: "." + } + JavaScriptFiles { + directory: "." + } + ImageFiles { + directory: "." + } + Files { + filter: "*.ts" + directory: "i18n" + } +} -- cgit v1.2.3 From 5be0938e28b656fecd5b2cb6a56de1716ae79c70 Mon Sep 17 00:00:00 2001 From: Alan Alpert Date: Thu, 28 Feb 2013 10:24:47 -0800 Subject: Fix relative URL in parallax example Samegame demo moved a while back, this didn't get updated. Change-Id: Ib34a09f606528e1e53650bbbd4d19787b1864bcb Reviewed-by: Jens Bache-Wiig --- examples/quick/views/parallax/parallax.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/quick/views/parallax/parallax.qml b/examples/quick/views/parallax/parallax.qml index 4ec482f581..ddcafc1ee1 100644 --- a/examples/quick/views/parallax/parallax.qml +++ b/examples/quick/views/parallax/parallax.qml @@ -69,7 +69,7 @@ Rectangle { anchors { top: parent.top; topMargin: 10; horizontalCenter: parent.horizontalCenter } width: 300; height: 400 clip: true; - source: "../../../demos/samegame/samegame.qml" + source: "../../demos/samegame/samegame.qml" } } } -- cgit v1.2.3 From e65a7e2f9955bb873c569fcb7e40dd645d9846c2 Mon Sep 17 00:00:00 2001 From: Alan Alpert Date: Thu, 28 Feb 2013 09:13:23 -0800 Subject: Work with synchronous replies from a custom QNetworkAccessManager Forward port of e5783b79887299d094 from QtQuick 1. Task-number: QTBUG-27723 Change-Id: I4332dd72bb9d65167307c1ac5ce24e93b14cff5f Reviewed-by: Peter Hartmann --- src/qml/qml/qqmltypeloader.cpp | 27 +++++++++++---- tests/auto/qml/qqmlengine/tst_qqmlengine.cpp | 49 ++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 6 deletions(-) diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index 5910f60667..9547204760 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -124,6 +124,7 @@ public: public slots: void finished(); void downloadProgress(qint64, qint64); + void manualFinished(QNetworkReply*); private: QQmlDataLoader *l; @@ -183,6 +184,14 @@ void QQmlDataLoaderNetworkReplyProxy::downloadProgress(qint64 bytesReceived, qin l->networkReplyProgress(reply, bytesReceived, bytesTotal); } +// This function is for when you want to shortcut the signals and call directly +void QQmlDataLoaderNetworkReplyProxy::manualFinished(QNetworkReply *reply) +{ + qint64 replySize = reply->size(); + l->networkReplyProgress(reply, replySize, replySize); + l->networkReplyFinished(reply); +} + /*! \class QQmlDataBlob @@ -1008,17 +1017,23 @@ void QQmlDataLoader::loadThread(QQmlDataBlob *blob) } else { QNetworkReply *reply = m_thread->networkAccessManager()->get(QNetworkRequest(blob->m_url)); - QObject *nrp = m_thread->networkReplyProxy(); - QObject::connect(reply, SIGNAL(downloadProgress(qint64,qint64)), - nrp, SLOT(downloadProgress(qint64,qint64))); - QObject::connect(reply, SIGNAL(finished()), - nrp, SLOT(finished())); + QQmlDataLoaderNetworkReplyProxy *nrp = m_thread->networkReplyProxy(); + blob->addref(); m_networkReplies.insert(reply, blob); + + if (reply->isFinished()) { + nrp->manualFinished(reply); + } else { + QObject::connect(reply, SIGNAL(downloadProgress(qint64,qint64)), + nrp, SLOT(downloadProgress(qint64,qint64))); + QObject::connect(reply, SIGNAL(finished()), + nrp, SLOT(finished())); + } + #ifdef DATABLOB_DEBUG qWarning("QQmlDataBlob: requested %s", qPrintable(blob->url().toString())); #endif - blob->addref(); } } diff --git a/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp b/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp index 4e1ac22337..9177ff58f7 100644 --- a/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp +++ b/tests/auto/qml/qqmlengine/tst_qqmlengine.cpp @@ -48,6 +48,7 @@ #include #include #include +#include #include #include #include @@ -63,6 +64,7 @@ public: private slots: void rootContext(); void networkAccessManager(); + void synchronousNetworkAccessManager(); void baseUrl(); void contextForObject(); void offlineStoragePath(); @@ -127,6 +129,53 @@ void tst_qqmlengine::networkAccessManager() delete engine; } +class ImmediateReply : public QNetworkReply { + + Q_OBJECT + +public: + ImmediateReply() { + setFinished(true); + } + virtual qint64 readData(char* , qint64 ) { + return 0; + } + virtual void abort() { } +}; + +class ImmediateManager : public QNetworkAccessManager { + + Q_OBJECT + +public: + ImmediateManager(QObject *parent = 0) : QNetworkAccessManager(parent) { + } + + QNetworkReply *createRequest(Operation, const QNetworkRequest & , QIODevice * outgoingData = 0) { + Q_UNUSED(outgoingData); + return new ImmediateReply; + } +}; + +class ImmediateFactory : public QQmlNetworkAccessManagerFactory { + +public: + QNetworkAccessManager *create(QObject *) { + return new ImmediateManager; + } +}; + +void tst_qqmlengine::synchronousNetworkAccessManager() +{ + ImmediateFactory factory; + QQmlEngine engine; + engine.setNetworkAccessManagerFactory(&factory); + QQmlComponent c(&engine, QUrl("myScheme://test.qml")); + // reply is finished, so should not be in loading state. + QVERIFY(!c.isLoading()); +} + + void tst_qqmlengine::baseUrl() { QQmlEngine engine; -- cgit v1.2.3 From 707bbe5dea9d7398b205124a54422f2fafb6f151 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Morten=20Johan=20S=C3=B8rvig?= Date: Tue, 5 Mar 2013 08:55:34 +0100 Subject: Load "@2x" images on high-dpi "retina" systems. Check for the existence of a "@2x" file when loading an image from a local file url. For example, Image { source = "foo.png" } will load "foo@2x.png" if that file exists. Change-Id: I37688d035bc9eb412d54be0eb758dd52ebfa5d90 Reviewed-by: Jens Bache-Wiig --- src/quick/util/qquickpixmapcache.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/quick/util/qquickpixmapcache.cpp b/src/quick/util/qquickpixmapcache.cpp index 0033be82be..d5ef4b767f 100644 --- a/src/quick/util/qquickpixmapcache.cpp +++ b/src/quick/util/qquickpixmapcache.cpp @@ -1020,6 +1020,17 @@ static QQuickPixmapData* createPixmapDataSync(QQuickPixmap *declarativePixmap, Q if (localFile.isEmpty()) return 0; + // check for "retina" high-dpi and use @2x file if it exixts + if (qApp->devicePixelRatio() > 1) { + int dotIndex = localFile.lastIndexOf(QStringLiteral(".")); + if (dotIndex != -1) { + QString retinaFile = localFile; + retinaFile.insert(dotIndex, "@2x"); + if (QFile(retinaFile).exists()) + localFile = retinaFile; + } + } + QFile f(localFile); QSize readSize; QString errorString; -- cgit v1.2.3 From 425403d062bad165629b1420be96ab42c20c28c7 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Mon, 4 Mar 2013 21:08:12 +0100 Subject: Remove extra semicolon. Change-Id: I5830f8dd2c636ad8653506c04f8ea1a9ac000f9a Reviewed-by: Gabriel de Dietrich --- src/qml/qml/qqmlguard_p.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qml/qml/qqmlguard_p.h b/src/qml/qml/qqmlguard_p.h index cef4c8fb8d..455f5c93a8 100644 --- a/src/qml/qml/qqmlguard_p.h +++ b/src/qml/qml/qqmlguard_p.h @@ -201,7 +201,7 @@ template T *QQmlGuard::object() const { return static_cast(o); -}; +} template void QQmlGuard::setObject(T *g) -- cgit v1.2.3 From 5bfe13b64f46d7b737769154ca1c3fd85e627c05 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Tue, 5 Mar 2013 00:23:23 +0100 Subject: Fix crash for keypress without active focus. If there was no active focus item we need to use tryShortcutEvent with the window as target. Change-Id: I420925781bbbda79fe2a62a5774230ae77c2cd34 Reviewed-by: Liang Qi --- src/quick/items/qquickwindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 2e8f58b849..2e717a9bf0 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -1194,7 +1194,7 @@ void QQuickWindow::keyPressEvent(QKeyEvent *e) #ifndef QT_NO_SHORTCUT // Try looking for a Shortcut before sending key events - if (QGuiApplicationPrivate::instance()->shortcutMap.tryShortcutEvent(this->activeFocusItem(), e)) + if (QGuiApplicationPrivate::instance()->shortcutMap.tryShortcutEvent(focusObject(), e)) return; #endif -- cgit v1.2.3 From 42477ced635c3c20c41a6b37fc499b65950e75c3 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Wed, 9 Jan 2013 15:51:04 +0100 Subject: Accessibility: Adapt memory management for Qt 5.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The model was changed to cache interfaces, so do not try to delete them. Change-Id: I5a97a6aca38036803c62d90988bb6d02196f0b84 Reviewed-by: Jan Arve Sæther --- .../accessible/quick/qaccessiblequickview.cpp | 2 +- src/plugins/accessible/shared/qqmlaccessible.cpp | 1 - .../qquickaccessible/tst_qquickaccessible.cpp | 86 ++++++++++------------ 3 files changed, 41 insertions(+), 48 deletions(-) diff --git a/src/plugins/accessible/quick/qaccessiblequickview.cpp b/src/plugins/accessible/quick/qaccessiblequickview.cpp index caf4b5df53..4251a80b33 100644 --- a/src/plugins/accessible/quick/qaccessiblequickview.cpp +++ b/src/plugins/accessible/quick/qaccessiblequickview.cpp @@ -133,7 +133,7 @@ static QQuickItem *childAt_helper(QQuickItem *item, int x, int y) return 0; } - QScopedPointer accessibleInterface(QAccessible::queryAccessibleInterface(item)); + QAccessibleInterface *accessibleInterface = QAccessible::queryAccessibleInterface(item); // this item has no Accessible attached property if (!accessibleInterface) return 0; diff --git a/src/plugins/accessible/shared/qqmlaccessible.cpp b/src/plugins/accessible/shared/qqmlaccessible.cpp index a3f6a733c8..4abf80a60c 100644 --- a/src/plugins/accessible/shared/qqmlaccessible.cpp +++ b/src/plugins/accessible/shared/qqmlaccessible.cpp @@ -87,7 +87,6 @@ QAccessibleInterface *QQmlAccessible::childAt(int x, int y) const if (childIface->rect().contains(x, y)) return childIface; } - delete childIface; } return 0; } diff --git a/tests/auto/quick/qquickaccessible/tst_qquickaccessible.cpp b/tests/auto/quick/qquickaccessible/tst_qquickaccessible.cpp index 46141a946e..d0bb075f4e 100644 --- a/tests/auto/quick/qquickaccessible/tst_qquickaccessible.cpp +++ b/tests/auto/quick/qquickaccessible/tst_qquickaccessible.cpp @@ -55,8 +55,6 @@ #include "../../shared/util.h" -typedef QSharedPointer QAI; - #define EXPECT(cond) \ do { \ if (!errorAt && !(cond)) { \ @@ -82,14 +80,11 @@ static int verifyHierarchy(QAccessibleInterface *iface) // navigate Ancestor... QAccessibleInterface *parent = if2->parent(); EXPECT(iface->object() == parent->object()); - delete parent; // verify children... if (!errorAt) errorAt = verifyHierarchy(if2); - delete if2; } - delete middleChild; --treelevel; return errorAt; @@ -149,7 +144,6 @@ void tst_QQuickAccessible::commonTests() QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(view); QVERIFY(iface); - delete iface; delete view; } @@ -274,7 +268,7 @@ void tst_QQuickAccessible::quickAttachedProperties() void tst_QQuickAccessible::basicPropertiesTest() { - QAI app = QAI(QAccessible::queryAccessibleInterface(qApp)); + QAccessibleInterface *app = QAccessible::queryAccessibleInterface(qApp); QCOMPARE(app->childCount(), 0); QQuickView *window = new QQuickView(); @@ -282,19 +276,19 @@ void tst_QQuickAccessible::basicPropertiesTest() window->show(); QCOMPARE(app->childCount(), 1); - QAI iface = QAI(QAccessible::queryAccessibleInterface(window)); - QVERIFY(iface.data()); + QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(window); + QVERIFY(iface); QCOMPARE(iface->childCount(), 1); - QAI item = QAI(iface->child(0)); - QVERIFY(item.data()); + QAccessibleInterface *item = iface->child(0); + QVERIFY(item); QCOMPARE(item->childCount(), 2); QCOMPARE(item->rect().size(), QSize(400, 400)); QCOMPARE(item->role(), QAccessible::Pane); - QCOMPARE(iface->indexOfChild(item.data()), 0); + QCOMPARE(iface->indexOfChild(item), 0); - QAI text = QAI(item->child(0)); - QVERIFY(text.data()); + QAccessibleInterface *text = item->child(0); + QVERIFY(text); QCOMPARE(text->childCount(), 0); QCOMPARE(text->text(QAccessible::Name), QLatin1String("Hello Accessibility")); @@ -302,10 +296,10 @@ void tst_QQuickAccessible::basicPropertiesTest() QCOMPARE(text->rect().x(), item->rect().x() + 100); QCOMPARE(text->rect().y(), item->rect().y() + 20); QCOMPARE(text->role(), QAccessible::StaticText); - QCOMPARE(item->indexOfChild(text.data()), 0); + QCOMPARE(item->indexOfChild(text), 0); - QAI text2 = QAI(item->child(1)); - QVERIFY(text2.data()); + QAccessibleInterface *text2 = item->child(1); + QVERIFY(text2); QCOMPARE(text2->childCount(), 0); QCOMPARE(text2->text(QAccessible::Name), QLatin1String("The Hello 2 accessible text")); @@ -313,22 +307,22 @@ void tst_QQuickAccessible::basicPropertiesTest() QCOMPARE(text2->rect().x(), item->rect().x() + 100); QCOMPARE(text2->rect().y(), item->rect().y() + 40); QCOMPARE(text2->role(), QAccessible::StaticText); - QCOMPARE(item->indexOfChild(text2.data()), 1); + QCOMPARE(item->indexOfChild(text2), 1); - QCOMPARE(iface->indexOfChild(text2.data()), -1); - QCOMPARE(text2->indexOfChild(item.data()), -1); + QCOMPARE(iface->indexOfChild(text2), -1); + QCOMPARE(text2->indexOfChild(item), -1); delete window; } -QAI topLevelChildAt(QAccessibleInterface *iface, int x, int y) +QAccessibleInterface *topLevelChildAt(QAccessibleInterface *iface, int x, int y) { - QAI child = QAI(iface->childAt(x, y)); + QAccessibleInterface *child = iface->childAt(x, y); if (!child) - return QAI(); + return 0; - QAI childOfChild; - while (childOfChild = QAI(child->childAt(x, y))) { + QAccessibleInterface *childOfChild; + while (childOfChild = child->childAt(x, y)) { child = childOfChild; } return child; @@ -340,45 +334,45 @@ void tst_QQuickAccessible::hitTest() window->setSource(testFileUrl("hittest.qml")); window->show(); - QAI windowIface = QAI(QAccessible::queryAccessibleInterface(window)); - QVERIFY(windowIface.data()); - QAI rootItem = QAI(windowIface->child(0)); + QAccessibleInterface *windowIface = QAccessible::queryAccessibleInterface(window); + QVERIFY(windowIface); + QAccessibleInterface *rootItem = windowIface->child(0); QRect rootRect = rootItem->rect(); // check the root item from app - QAI appIface = QAI(QAccessible::queryAccessibleInterface(qApp)); + QAccessibleInterface *appIface = QAccessible::queryAccessibleInterface(qApp); QVERIFY(appIface); - QAI itemHit(appIface->childAt(rootRect.x() + 200, rootRect.y() + 50)); + QAccessibleInterface *itemHit(appIface->childAt(rootRect.x() + 200, rootRect.y() + 50)); QVERIFY(itemHit); QCOMPARE(rootRect, itemHit->rect()); // hit rect1 - QAI rect1(rootItem->child(0)); + QAccessibleInterface *rect1(rootItem->child(0)); QRect rect1Rect = rect1->rect(); - QAI rootItemIface = QAI(rootItem->childAt(rect1Rect.x() + 10, rect1Rect.y() + 10)); + QAccessibleInterface *rootItemIface = rootItem->childAt(rect1Rect.x() + 10, rect1Rect.y() + 10); QVERIFY(rootItemIface); QCOMPARE(rect1Rect, rootItemIface->rect()); QCOMPARE(rootItemIface->text(QAccessible::Name), QLatin1String("rect1")); // should also work from top level (app) - QAI app(QAccessible::queryAccessibleInterface(qApp)); - QAI itemHit2(topLevelChildAt(app.data(), rect1Rect.x() + 10, rect1Rect.y() + 10)); + QAccessibleInterface *app(QAccessible::queryAccessibleInterface(qApp)); + QAccessibleInterface *itemHit2(topLevelChildAt(app, rect1Rect.x() + 10, rect1Rect.y() + 10)); QVERIFY(itemHit2); QCOMPARE(itemHit2->rect(), rect1Rect); QCOMPARE(itemHit2->text(QAccessible::Name), QLatin1String("rect1")); // hit rect201 - QAI rect2(rootItem->child(1)); + QAccessibleInterface *rect2(rootItem->child(1)); QVERIFY(rect2); // FIXME: This is seems broken on mac // QCOMPARE(rect2->rect().translated(rootItem->rect().x(), rootItem->rect().y()), QRect(0, 50, 100, 100)); - QAI rect20(rect2->child(0)); + QAccessibleInterface *rect20(rect2->child(0)); QVERIFY(rect20); - QAI rect201(rect20->child(1)); + QAccessibleInterface *rect201(rect20->child(1)); QVERIFY(rect201); QRect rect201Rect = rect201->rect(); - rootItemIface = QAI(windowIface->childAt(rect201Rect.x() + 20, rect201Rect.y() + 20)); + rootItemIface = windowIface->childAt(rect201Rect.x() + 20, rect201Rect.y() + 20); QVERIFY(rootItemIface); QCOMPARE(rootItemIface->rect(), rect201Rect); QCOMPARE(rootItemIface->text(QAccessible::Name), QLatin1String("rect201")); @@ -392,29 +386,29 @@ void tst_QQuickAccessible::checkableTest() window->setSource(testFileUrl("checkbuttons.qml")); window->show(); - QAI iface = QAI(QAccessible::queryAccessibleInterface(window)); - QVERIFY(iface.data()); - QAI root = QAI(iface->child(0)); + QAccessibleInterface *iface = QAccessible::queryAccessibleInterface(window); + QVERIFY(iface); + QAccessibleInterface *root = iface->child(0); - QAI button1 = QAI(root->child(0)); + QAccessibleInterface *button1 = root->child(0); QCOMPARE(button1->role(), QAccessible::Button); QVERIFY(!(button1->state().checked)); QVERIFY(!(button1->state().checkable)); - QAI button2 = QAI(root->child(1)); + QAccessibleInterface *button2 = root->child(1); QVERIFY(!(button2->state().checked)); QVERIFY(button2->state().checkable); - QAI button3 = QAI(root->child(2)); + QAccessibleInterface *button3 = root->child(2); QVERIFY(button3->state().checked); QVERIFY(button3->state().checkable); - QAI checkBox1 = QAI(root->child(3)); + QAccessibleInterface *checkBox1 = root->child(3); QCOMPARE(checkBox1->role(), QAccessible::CheckBox); QVERIFY((checkBox1->state().checked)); QVERIFY(checkBox1->state().checkable); - QAI checkBox2 = QAI(root->child(4)); + QAccessibleInterface *checkBox2 = root->child(4); QVERIFY(!(checkBox2->state().checked)); QVERIFY(checkBox2->state().checkable); } -- cgit v1.2.3 From 23b7579e4d82e2c0a840b55adf7817ffb6b4389c Mon Sep 17 00:00:00 2001 From: Paul Olav Tvete Date: Mon, 4 Mar 2013 13:51:32 +0100 Subject: Android support: build fixes Change-Id: If829341b0baef7b253a386a195d3b5e4238b8103 Reviewed-by: Eskil Abrahamsen Blomfeldt Reviewed-by: Oswald Buddenhagen --- src/qml/qml/qqmlplatform.cpp | 2 +- src/quick/items/context2d/qquickcontext2d.cpp | 2 +- src/quick/quick.pro | 3 +++ src/quick/scenegraph/util/qsgtexture.cpp | 2 +- 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/qml/qml/qqmlplatform.cpp b/src/qml/qml/qqmlplatform.cpp index 21145be89e..de48f60f56 100644 --- a/src/qml/qml/qqmlplatform.cpp +++ b/src/qml/qml/qqmlplatform.cpp @@ -59,7 +59,7 @@ QQmlPlatform::~QQmlPlatform() QString QQmlPlatform::os() { -#if defined(Q_OS_LINUX_ANDROID) +#if defined(Q_OS_ANDROID) return QLatin1String("android"); #elif defined(Q_OS_BLACKBERRY) return QLatin1String("blackberry"); diff --git a/src/quick/items/context2d/qquickcontext2d.cpp b/src/quick/items/context2d/qquickcontext2d.cpp index 17115ae8c4..9c73c7d191 100644 --- a/src/quick/items/context2d/qquickcontext2d.cpp +++ b/src/quick/items/context2d/qquickcontext2d.cpp @@ -63,7 +63,7 @@ #include #include -#if defined(Q_OS_QNX) || defined(Q_OS_LINUX_ANDROID) +#if defined(Q_OS_QNX) || defined(Q_OS_ANDROID) #include #endif diff --git a/src/quick/quick.pro b/src/quick/quick.pro index c4fceac33d..eedc71de92 100644 --- a/src/quick/quick.pro +++ b/src/quick/quick.pro @@ -14,6 +14,9 @@ exists("qqml_enable_gcov") { QMAKE_DOCS = $$PWD/doc/qtquick.qdocconf +ANDROID_LIB_DEPENDENCY_REPLACEMENTS = \ + "plugins/platforms/android/libqtforandroid.so:plugins/platforms/android/libqtforandroidGL.so" + load(qt_module) include(util/util.pri) diff --git a/src/quick/scenegraph/util/qsgtexture.cpp b/src/quick/scenegraph/util/qsgtexture.cpp index 1a8c69a474..cd95d9f445 100644 --- a/src/quick/scenegraph/util/qsgtexture.cpp +++ b/src/quick/scenegraph/util/qsgtexture.cpp @@ -48,7 +48,7 @@ #include #include -#if defined(Q_OS_LINUX) && !defined(Q_OS_LINUX_ANDROID) +#if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID) #define CAN_BACKTRACE_EXECINFO #endif -- cgit v1.2.3 From dfefcba06efc0ad239cb7f9ad5d9a5f9540841db Mon Sep 17 00:00:00 2001 From: Alan Alpert Date: Mon, 18 Feb 2013 21:07:15 -0800 Subject: Add core application functionality to Qt.application in QML This exposes some information to QML which is available on the QCoreApplication instance. A future change should make it possible to restrict this for use in scripting environments (which should not have access to the QCoreApplication). That has been left out of this change because proper support for such restrictions is not yet in place. Change-Id: Ica144fcfb0b42fa6df8d0cb1c7c03eb97282b489 Reviewed-by: Lars Knoll Reviewed-by: Kai Koehne --- src/qml/qml/qqmlengine.cpp | 20 ++++++++++- src/qml/qml/qqmlglobal.cpp | 53 ++++++++++++++++++++++++++-- src/qml/qml/qqmlglobal_p.h | 47 ++++++++++++++++++++++++ src/quick/util/qquickapplication.cpp | 5 +-- src/quick/util/qquickapplication_p.h | 3 +- tests/auto/qml/qqmlglobal/tst_qqmlglobal.cpp | 8 ++--- 6 files changed, 124 insertions(+), 12 deletions(-) diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index b0b6f45fa9..ddc6f0e9b9 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -437,13 +437,31 @@ The following functions are also on the Qt object. \li Qt.RightToLeft - Text and graphics elements should be positioned from right to left. \endlist - + \row + \li \c application.arguments + \li This is a string list of the arguments the executable was invoked with. + \row + \li \c application.name + \li This is the application name set on the QCoreApplication instance. This property can be written + to in order to set the application name. + \row + \li \c application.version + \li This is the application version set on the QCoreApplication instance. This property can be written + to in order to set the application name. \endtable + The object also has one signal, aboutToQuit(), which is the same as \l QCoreApplication::aboutToQuit(). + The following example uses the \c application object to indicate whether the application is currently active: \snippet qml/application.qml document + + Note that when using QML without a QGuiApplication, the following properties will be undefined: + \list + \li application.active + \li application.layoutDirection + \endlist */ /*! diff --git a/src/qml/qml/qqmlglobal.cpp b/src/qml/qml/qqmlglobal.cpp index 379a168110..607ee4d0ac 100644 --- a/src/qml/qml/qqmlglobal.cpp +++ b/src/qml/qml/qqmlglobal.cpp @@ -44,6 +44,7 @@ #include #include #include +#include QT_BEGIN_NAMESPACE @@ -357,7 +358,7 @@ Q_AUTOTEST_EXPORT QQmlColorProvider *QQml_colorProvider(void) QQmlGuiProvider::~QQmlGuiProvider() {} -QObject *QQmlGuiProvider::application(QObject *) { return 0; } +QObject *QQmlGuiProvider::application(QObject *) { return new QQmlApplication(); } QStringList QQmlGuiProvider::fontFamilies() { return QStringList(); } bool QQmlGuiProvider::openUrlExternally(QUrl &) { return false; } @@ -383,8 +384,7 @@ Q_QML_PRIVATE_EXPORT QQmlGuiProvider *QQml_setGuiProvider(QQmlGuiProvider *newPr static QQmlGuiProvider **getGuiProvider(void) { if (guiProvider == 0) { - qWarning() << "Warning: QQml_guiProvider: no GUI provider has been set!"; - static QQmlGuiProvider nullGuiProvider; + static QQmlGuiProvider nullGuiProvider; //Still provides an application with no GUI support guiProvider = &nullGuiProvider; } @@ -397,4 +397,51 @@ Q_AUTOTEST_EXPORT QQmlGuiProvider *QQml_guiProvider(void) return *providerPtr; } +//Docs in qqmlengine.cpp +QQmlApplication::QQmlApplication(QObject *parent) + : QObject(*(new QQmlApplicationPrivate),parent) +{ + connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()), + this, SIGNAL(aboutToQuit())); +} + +QQmlApplication::QQmlApplication(QQmlApplicationPrivate &dd, QObject *parent) + : QObject(dd, parent) +{ + connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()), + this, SIGNAL(aboutToQuit())); +} + +QStringList QQmlApplication::args() +{ + Q_D(QQmlApplication); + if (!d->argsInit) { + d->argsInit = true; + d->args = QCoreApplication::arguments(); + } + return d->args; +} + +QString QQmlApplication::name() const +{ + return QCoreApplication::instance()->applicationName(); +} + +QString QQmlApplication::version() const +{ + return QCoreApplication::instance()->applicationVersion(); +} + +void QQmlApplication::setName(const QString &arg) +{ + QCoreApplication::instance()->setApplicationName(arg); + emit nameChanged(); //Note that we don't get notified if it's changed from C++ +} + +void QQmlApplication::setVersion(const QString &arg) +{ + QCoreApplication::instance()->setApplicationVersion(arg); + emit versionChanged(); //Note that we don't get notified if it's changed from C++ +} + QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlglobal_p.h b/src/qml/qml/qqmlglobal_p.h index f6b2c81536..6ca54f65cd 100644 --- a/src/qml/qml/qqmlglobal_p.h +++ b/src/qml/qml/qqmlglobal_p.h @@ -313,6 +313,53 @@ public: Q_QML_PRIVATE_EXPORT QQmlGuiProvider *QQml_setGuiProvider(QQmlGuiProvider *); Q_AUTOTEST_EXPORT QQmlGuiProvider *QQml_guiProvider(); +class QQmlApplicationPrivate; + +class Q_QML_PRIVATE_EXPORT QQmlApplication : public QObject +{ + //Application level logic, subclassed by QtQuick if available via QQmlGuiProvider + Q_OBJECT + Q_PROPERTY(QStringList arguments READ args CONSTANT) + Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) + Q_PROPERTY(QString version READ version WRITE setVersion NOTIFY versionChanged) +public: + QQmlApplication(QObject* parent=0); + + QStringList args(); + + QString name() const; + QString version() const; + +public Q_SLOTS: + void setName(const QString &arg); + void setVersion(const QString &arg); + +Q_SIGNALS: + void aboutToQuit(); + + void nameChanged(); + void versionChanged(); + +protected: + QQmlApplication(QQmlApplicationPrivate &dd, QObject* parent=0); + +private: + Q_DISABLE_COPY(QQmlApplication); + Q_DECLARE_PRIVATE(QQmlApplication); +}; + +class QQmlApplicationPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QQmlApplication) +public: + QQmlApplicationPrivate() { + argsInit = false; + } + + bool argsInit; + QStringList args; +}; + QT_END_NAMESPACE #endif // QQMLGLOBAL_H diff --git a/src/quick/util/qquickapplication.cpp b/src/quick/util/qquickapplication.cpp index 1e2a421bcb..bc8b724a00 100644 --- a/src/quick/util/qquickapplication.cpp +++ b/src/quick/util/qquickapplication.cpp @@ -46,10 +46,11 @@ #include #include #include +#include QT_BEGIN_NAMESPACE -class QQuickApplicationPrivate : public QObjectPrivate +class QQuickApplicationPrivate : public QQmlApplicationPrivate { Q_DECLARE_PUBLIC(QQuickApplication) public: @@ -70,7 +71,7 @@ private: */ QQuickApplication::QQuickApplication(QObject *parent) - : QObject(*new QQuickApplicationPrivate(), parent) + : QQmlApplication(*new QQuickApplicationPrivate(), parent) { if (qApp) { qApp->installEventFilter(this); diff --git a/src/quick/util/qquickapplication_p.h b/src/quick/util/qquickapplication_p.h index 2eef59b608..cccc024282 100644 --- a/src/quick/util/qquickapplication_p.h +++ b/src/quick/util/qquickapplication_p.h @@ -44,13 +44,14 @@ #include #include +#include #include QT_BEGIN_NAMESPACE class QQuickApplicationPrivate; -class Q_AUTOTEST_EXPORT QQuickApplication : public QObject +class Q_AUTOTEST_EXPORT QQuickApplication : public QQmlApplication { Q_OBJECT Q_PROPERTY(bool active READ active NOTIFY activeChanged) diff --git a/tests/auto/qml/qqmlglobal/tst_qqmlglobal.cpp b/tests/auto/qml/qqmlglobal/tst_qqmlglobal.cpp index 293eccbf64..793da64734 100644 --- a/tests/auto/qml/qqmlglobal/tst_qqmlglobal.cpp +++ b/tests/auto/qml/qqmlglobal/tst_qqmlglobal.cpp @@ -54,7 +54,7 @@ private slots: void initTestCase(); void colorProviderWarning(); - void guiProviderWarning(); + void noGuiProviderWarning(); }; void tst_qqmlglobal::initTestCase() @@ -68,11 +68,9 @@ void tst_qqmlglobal::colorProviderWarning() QQml_colorProvider(); } -void tst_qqmlglobal::guiProviderWarning() +void tst_qqmlglobal::noGuiProviderWarning() { - const QLatin1String expected("Warning: QQml_guiProvider: no GUI provider has been set! "); - QTest::ignoreMessage(QtWarningMsg, expected.data()); - QQml_guiProvider(); + QVERIFY(QQml_guiProvider()); //No GUI provider, so a default non-zero application instance is returned. } QTEST_MAIN(tst_qqmlglobal) -- cgit v1.2.3 From e4128d24bb7030fe571767731e160c7f58cd77e8 Mon Sep 17 00:00:00 2001 From: Alan Alpert Date: Mon, 25 Feb 2013 14:38:07 -0800 Subject: Remove spurious include While it's discouraged to use the entire module headers inside Qt, this particular example also compiles without the line. Change-Id: I75b6fc09e11d00df2f19f5db9b0209ed4cd82844 Reviewed-by: Laszlo Papp Reviewed-by: Shawn Rutledge --- src/qml/qml/qqmlbundle.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/qml/qml/qqmlbundle.cpp b/src/qml/qml/qqmlbundle.cpp index 62925ced1e..273462aa25 100644 --- a/src/qml/qml/qqmlbundle.cpp +++ b/src/qml/qml/qqmlbundle.cpp @@ -40,7 +40,6 @@ ****************************************************************************/ #include "qqmlbundle_p.h" -#include #include #include -- cgit v1.2.3 From 89578efc5213b6e73f2551036e217d7fe9f5e2eb Mon Sep 17 00:00:00 2001 From: Gunnar Sletta Date: Thu, 7 Mar 2013 08:49:36 +0100 Subject: Disable more unstable tests Change-Id: I54db72466ca50d65dd39e9c8e25d3a366cb737ea Reviewed-by: Shawn Rutledge Reviewed-by: Eskil Abrahamsen Blomfeldt --- tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp b/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp index 8e63b6207f..02475eb431 100644 --- a/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp +++ b/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp @@ -2774,6 +2774,7 @@ void tst_qquicktextinput::cursorDelegate() void tst_qquicktextinput::remoteCursorDelegate() { + QSKIP("This test is unstable"); TestHTTPServer server(SERVER_PORT); server.serveDirectory(dataDirectory(), TestHTTPServer::Delay); @@ -2807,6 +2808,7 @@ void tst_qquicktextinput::remoteCursorDelegate() void tst_qquicktextinput::cursorVisible() { + QSKIP("This test is unstable"); QQuickTextInput input; input.componentComplete(); QSignalSpy spy(&input, SIGNAL(cursorVisibleChanged(bool))); -- cgit v1.2.3 From b659615a088dfce560ccf7f1933b0e02915c2eda Mon Sep 17 00:00:00 2001 From: Gunnar Sletta Date: Wed, 6 Mar 2013 11:12:44 +0100 Subject: Disable qv8profilerservice test. It is very unstable Change-Id: Ic7fada8ca457b15fb62a47d2137de7d6f8b1711a Reviewed-by: Eskil Abrahamsen Blomfeldt --- tests/auto/qml/debugger/qv8profilerservice/qv8profilerservice.pro | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/auto/qml/debugger/qv8profilerservice/qv8profilerservice.pro b/tests/auto/qml/debugger/qv8profilerservice/qv8profilerservice.pro index 8780f64946..dc6f4c5038 100644 --- a/tests/auto/qml/debugger/qv8profilerservice/qv8profilerservice.pro +++ b/tests/auto/qml/debugger/qv8profilerservice/qv8profilerservice.pro @@ -13,3 +13,4 @@ TESTDATA = data/* QT += qml testlib gui-private DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 +CONFIG+=insignificant_test -- cgit v1.2.3 From e70f21152aa8b2459c903db0cf4ca30bde5bd178 Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Tue, 5 Mar 2013 14:21:04 +0100 Subject: Fix rendering of subpixel positioned native text To get accurate sampling of the native text rendering, we use nearest filtering. This, however, does not work well when we're sampling in the middle of pixels and the result is that with text which is positioned at 0.5 offsets, we will sample the wrong pixels and get artifacts from this. The main use case for native rendered text is unrotated and unscaled text, so we fix this use case by pixel-aligning the translation factor. Done-with: Samuel Task-number: QTBUG-30022 Change-Id: I6911196d6ff491dca3b329c42da1c6dd7263cff0 Reviewed-by: Gunnar Sletta --- src/quick/scenegraph/coreapi/qsgmaterial.cpp | 10 ++++++++++ src/quick/scenegraph/coreapi/qsgmaterial.h | 1 + src/quick/scenegraph/qsgdefaultglyphnode_p.cpp | 26 +++++++++++++++++++++++--- 3 files changed, 34 insertions(+), 3 deletions(-) diff --git a/src/quick/scenegraph/coreapi/qsgmaterial.cpp b/src/quick/scenegraph/coreapi/qsgmaterial.cpp index f678504344..11ce987959 100644 --- a/src/quick/scenegraph/coreapi/qsgmaterial.cpp +++ b/src/quick/scenegraph/coreapi/qsgmaterial.cpp @@ -442,6 +442,16 @@ QMatrix4x4 QSGMaterialShader::RenderState::modelViewMatrix() const return static_cast(m_data)->currentModelViewMatrix(); } +/*! + Returns the projection matrix. + */ + +QMatrix4x4 QSGMaterialShader::RenderState::projectionMatrix() const +{ + Q_ASSERT(m_data); + return static_cast(m_data)->currentProjectionMatrix(); +} + /*! diff --git a/src/quick/scenegraph/coreapi/qsgmaterial.h b/src/quick/scenegraph/coreapi/qsgmaterial.h index 50e9c77d4e..238bf83111 100644 --- a/src/quick/scenegraph/coreapi/qsgmaterial.h +++ b/src/quick/scenegraph/coreapi/qsgmaterial.h @@ -69,6 +69,7 @@ public: float opacity() const; QMatrix4x4 combinedMatrix() const; QMatrix4x4 modelViewMatrix() const; + QMatrix4x4 projectionMatrix() const; QRect viewportRect() const; QRect deviceRect() const; float determinant() const; diff --git a/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp b/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp index 6b8f9927c4..b65686e628 100644 --- a/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp +++ b/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp @@ -178,7 +178,7 @@ void QSGTextMaskMaterialData::updateState(const RenderState &state, QSGMaterial 1.0 / material->cacheTextureHeight())); glBindTexture(GL_TEXTURE_2D, material->texture()->textureId()); - // Set the mag/min filters to be linear. We only need to do this when the texture + // Set the mag/min filters to be nearest. We only need to do this when the texture // has been recreated. if (updated) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); @@ -186,8 +186,28 @@ void QSGTextMaskMaterialData::updateState(const RenderState &state, QSGMaterial } } - if (state.isMatrixDirty()) - program()->setUniformValue(m_matrix_id, state.combinedMatrix()); + if (state.isMatrixDirty()) { + QMatrix4x4 transform = state.modelViewMatrix(); + qreal xTranslation = transform(0, 3); + qreal yTranslation = transform(1, 3); + + // Remove translation and check identity to see if matrix is only translating. + // If it is, we can round the translation to make sure the text is pixel aligned, + // which is the only thing that works with GL_NEAREST filtering. Adding rotations + // and scales to native rendered text is not a prioritized use case, since the + // default rendering type is designed for that. + transform(0, 3) = 0.0; + transform(1, 3) = 0.0; + if (transform.isIdentity()) { + transform(0, 3) = qRound(xTranslation); + transform(1, 3) = qRound(yTranslation); + + transform = state.projectionMatrix() * transform; + program()->setUniformValue(m_matrix_id, transform); + } else { + program()->setUniformValue(m_matrix_id, state.combinedMatrix()); + } + } } QSGTextMaskMaterial::QSGTextMaskMaterial(const QRawFont &font) -- cgit v1.2.3 From bd6ae5e5931b98b781847fd67f7c5b570b2b61c0 Mon Sep 17 00:00:00 2001 From: Caroline Chao Date: Tue, 5 Mar 2013 12:00:19 +0100 Subject: Focus reason When trying to do for example text handling it becomes obvious that focus handling is not proper. A mouse click focus should de-select text, while a window change should preserve the selection. Re-introduce focus reason. Change-Id: I3322c976437cba68938d7c9188e549bdb499fa5a Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickitem.cpp | 42 ++++++++++++++++++++++++++++---------- src/quick/items/qquickitem.h | 2 ++ src/quick/items/qquicktextedit.cpp | 2 +- src/quick/items/qquickwindow.cpp | 20 +++++++++--------- src/quick/items/qquickwindow_p.h | 4 ++-- 5 files changed, 46 insertions(+), 24 deletions(-) diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index ad8a9d90af..8f68f6ff3f 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -2083,7 +2083,7 @@ void QQuickItem::setParentItem(QQuickItem *parentItem) while (!scopeItem->isFocusScope() && scopeItem->parentItem()) scopeItem = scopeItem->parentItem(); if (d->window) { - QQuickWindowPrivate::get(d->window)->clearFocusInScope(scopeItem, scopeFocusedItem, + QQuickWindowPrivate::get(d->window)->clearFocusInScope(scopeItem, scopeFocusedItem, Qt::OtherFocusReason, QQuickWindowPrivate::DontChangeFocusProperty); if (scopeFocusedItem != this) QQuickItemPrivate::get(scopeFocusedItem)->updateSubFocusItem(this, true); @@ -2146,7 +2146,7 @@ void QQuickItem::setParentItem(QQuickItem *parentItem) emit scopeFocusedItem->focusChanged(false); } else { if (d->window) { - QQuickWindowPrivate::get(d->window)->setFocusInScope(scopeItem, scopeFocusedItem, + QQuickWindowPrivate::get(d->window)->setFocusInScope(scopeItem, scopeFocusedItem, Qt::OtherFocusReason, QQuickWindowPrivate::DontChangeFocusProperty); } else { QQuickItemPrivate::get(scopeFocusedItem)->updateSubFocusItem(scopeItem, true); @@ -3772,29 +3772,44 @@ void QQuickItem::mapToItem(QQmlV8Function *args) const /*! \qmlmethod QtQuick2::Item::forceActiveFocus() + \overload Forces active focus on the item. This method sets focus on the item and ensures that all ancestor FocusScope objects in the object hierarchy are also given \l focus. + The reason for the focus change will be \a Qt::OtherFocusReason. Use + the overloaded method to specify the focus reason to enable better + handling of the focus change. + \sa activeFocus */ +void QQuickItem::forceActiveFocus() +{ + forceActiveFocus(Qt::OtherFocusReason); +} + /*! - Forces active focus on the item. + \qmlmethod QtQuick2::Item::forceActiveFocus(Qt::FocusReason reason) + + Forces active focus on the item with the given \a reason. This method sets focus on the item and ensures that all ancestor FocusScope objects in the object hierarchy are also given \l focus. - \sa activeFocus + \since 5.1 + + \sa activeFocus, Qt::FocusReason */ -void QQuickItem::forceActiveFocus() + +void QQuickItem::forceActiveFocus(Qt::FocusReason reason) { - setFocus(true); + setFocus(true, reason); QQuickItem *parent = parentItem(); while (parent) { if (parent->flags() & QQuickItem::ItemIsFocusScope) { - parent->setFocus(true); + parent->setFocus(true, reason); } parent = parent->parentItem(); } @@ -5081,7 +5096,7 @@ void QQuickItemPrivate::setEffectiveEnableRecur(QQuickItem *scope, bool newEffec q->ungrabMouse(); if (scope && !effectiveEnable && activeFocus) { windowPriv->clearFocusInScope( - scope, q, QQuickWindowPrivate::DontChangeFocusProperty | QQuickWindowPrivate::DontChangeSubFocusItem); + scope, q, Qt::OtherFocusReason, QQuickWindowPrivate::DontChangeFocusProperty | QQuickWindowPrivate::DontChangeSubFocusItem); } } @@ -5092,7 +5107,7 @@ void QQuickItemPrivate::setEffectiveEnableRecur(QQuickItem *scope, bool newEffec if (window && scope && effectiveEnable && focus) { QQuickWindowPrivate::get(window)->setFocusInScope( - scope, q, QQuickWindowPrivate::DontChangeFocusProperty | QQuickWindowPrivate::DontChangeSubFocusItem); + scope, q, Qt::OtherFocusReason, QQuickWindowPrivate::DontChangeFocusProperty | QQuickWindowPrivate::DontChangeSubFocusItem); } emit q->enabledChanged(); @@ -6005,6 +6020,11 @@ bool QQuickItem::hasFocus() const } void QQuickItem::setFocus(bool focus) +{ + setFocus(focus, Qt::OtherFocusReason); +} + +void QQuickItem::setFocus(bool focus, Qt::FocusReason reason) { Q_D(QQuickItem); if (d->focus == focus) @@ -6017,9 +6037,9 @@ void QQuickItem::setFocus(bool focus) scope = scope->parentItem(); if (d->window) { if (focus) - QQuickWindowPrivate::get(d->window)->setFocusInScope(scope, this); + QQuickWindowPrivate::get(d->window)->setFocusInScope(scope, this, reason); else - QQuickWindowPrivate::get(d->window)->clearFocusInScope(scope, this); + QQuickWindowPrivate::get(d->window)->clearFocusInScope(scope, this, reason); } else { // do the focus changes from setFocusInScope/clearFocusInScope that are // unrelated to a window diff --git a/src/quick/items/qquickitem.h b/src/quick/items/qquickitem.h index e9c817a580..71681698b9 100644 --- a/src/quick/items/qquickitem.h +++ b/src/quick/items/qquickitem.h @@ -274,6 +274,7 @@ public: bool hasActiveFocus() const; bool hasFocus() const; void setFocus(bool); + void setFocus(bool focus, Qt::FocusReason reason); bool isFocusScope() const; QQuickItem *scopedFocusItem() const; @@ -318,6 +319,7 @@ public: Q_INVOKABLE void mapFromItem(QQmlV8Function*) const; Q_INVOKABLE void mapToItem(QQmlV8Function*) const; Q_INVOKABLE void forceActiveFocus(); + Q_INVOKABLE void forceActiveFocus(Qt::FocusReason reason); Q_INVOKABLE QQuickItem *childAt(qreal x, qreal y) const; #ifndef QT_NO_IM diff --git a/src/quick/items/qquicktextedit.cpp b/src/quick/items/qquicktextedit.cpp index 015f52cc46..4ddb992fa6 100644 --- a/src/quick/items/qquicktextedit.cpp +++ b/src/quick/items/qquicktextedit.cpp @@ -1539,7 +1539,7 @@ void QQuickTextEdit::mousePressEvent(QMouseEvent *event) d->control->processEvent(event, QPointF(-d->xoff, -d->yoff)); if (d->focusOnPress){ bool hadActiveFocus = hasActiveFocus(); - forceActiveFocus(); + forceActiveFocus(Qt::MouseFocusReason); // re-open input panel on press if already focused #ifndef QT_NO_IM if (hasActiveFocus() && hadActiveFocus && !isReadOnly()) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 2e717a9bf0..1732251cf2 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -224,17 +224,17 @@ void QQuickWindow::hideEvent(QHideEvent *) } /*! \reimp */ -void QQuickWindow::focusOutEvent(QFocusEvent *) +void QQuickWindow::focusOutEvent(QFocusEvent *ev) { Q_D(QQuickWindow); - d->contentItem->setFocus(false); + d->contentItem->setFocus(false, ev->reason()); } /*! \reimp */ -void QQuickWindow::focusInEvent(QFocusEvent *) +void QQuickWindow::focusInEvent(QFocusEvent *ev) { Q_D(QQuickWindow); - d->contentItem->setFocus(true); + d->contentItem->setFocus(true, ev->reason()); d->updateFocusItemTransform(); } @@ -608,7 +608,7 @@ void QQuickWindowPrivate::translateTouchEvent(QTouchEvent *touchEvent) touchEvent->setTouchPoints(touchPoints); } -void QQuickWindowPrivate::setFocusInScope(QQuickItem *scope, QQuickItem *item, FocusOptions options) +void QQuickWindowPrivate::setFocusInScope(QQuickItem *scope, QQuickItem *item, Qt::FocusReason reason, FocusOptions options) { Q_Q(QQuickWindow); @@ -648,7 +648,7 @@ void QQuickWindowPrivate::setFocusInScope(QQuickItem *scope, QQuickItem *item, F #endif activeFocusItem = 0; - QFocusEvent event(QEvent::FocusOut, Qt::OtherFocusReason); + QFocusEvent event(QEvent::FocusOut, reason); q->sendEvent(oldActiveFocusItem, &event); QQuickItem *afi = oldActiveFocusItem; @@ -695,7 +695,7 @@ void QQuickWindowPrivate::setFocusInScope(QQuickItem *scope, QQuickItem *item, F } updateFocusItemTransform(); - QFocusEvent event(QEvent::FocusIn, Qt::OtherFocusReason); + QFocusEvent event(QEvent::FocusIn, reason); q->sendEvent(newActiveFocusItem, &event); } @@ -705,7 +705,7 @@ void QQuickWindowPrivate::setFocusInScope(QQuickItem *scope, QQuickItem *item, F notifyFocusChangesRecur(changed.data(), changed.count() - 1); } -void QQuickWindowPrivate::clearFocusInScope(QQuickItem *scope, QQuickItem *item, FocusOptions options) +void QQuickWindowPrivate::clearFocusInScope(QQuickItem *scope, QQuickItem *item, Qt::FocusReason reason, FocusOptions options) { Q_Q(QQuickWindow); @@ -745,7 +745,7 @@ void QQuickWindowPrivate::clearFocusInScope(QQuickItem *scope, QQuickItem *item, #endif activeFocusItem = 0; - QFocusEvent event(QEvent::FocusOut, Qt::OtherFocusReason); + QFocusEvent event(QEvent::FocusOut, reason); q->sendEvent(oldActiveFocusItem, &event); QQuickItem *afi = oldActiveFocusItem; @@ -777,7 +777,7 @@ void QQuickWindowPrivate::clearFocusInScope(QQuickItem *scope, QQuickItem *item, activeFocusItem = scope; updateFocusItemTransform(); - QFocusEvent event(QEvent::FocusIn, Qt::OtherFocusReason); + QFocusEvent event(QEvent::FocusIn, reason); q->sendEvent(newActiveFocusItem, &event); } diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h index f272f30f8e..ab772ca2bc 100644 --- a/src/quick/items/qquickwindow_p.h +++ b/src/quick/items/qquickwindow_p.h @@ -163,8 +163,8 @@ public: }; Q_DECLARE_FLAGS(FocusOptions, FocusOption) - void setFocusInScope(QQuickItem *scope, QQuickItem *item, FocusOptions = 0); - void clearFocusInScope(QQuickItem *scope, QQuickItem *item, FocusOptions = 0); + void setFocusInScope(QQuickItem *scope, QQuickItem *item, Qt::FocusReason reason, FocusOptions = 0); + void clearFocusInScope(QQuickItem *scope, QQuickItem *item, Qt::FocusReason reason, FocusOptions = 0); static void notifyFocusChangesRecur(QQuickItem **item, int remaining); void updateFocusItemTransform(); -- cgit v1.2.3 From ffd48e1ff75f03fb4d684eeb5a47a526cc1b0d47 Mon Sep 17 00:00:00 2001 From: Caroline Chao Date: Tue, 5 Mar 2013 11:04:26 +0100 Subject: TextInput: Handle focus event directly Use focusInEvent and focusOutEvent instead of changeItem to handle focus event. (One benefit is the focus events have the focus reason and changeItem doesn't). Change-Id: I164820f375f0ffddc529d59565a3e448b84c6042 Reviewed-by: Frederik Gladhorn --- src/quick/items/qquicktextinput.cpp | 54 +++++++++++++++++++---------------- src/quick/items/qquicktextinput_p.h | 2 +- src/quick/items/qquicktextinput_p_p.h | 2 +- 3 files changed, 31 insertions(+), 27 deletions(-) diff --git a/src/quick/items/qquicktextinput.cpp b/src/quick/items/qquicktextinput.cpp index 1c65c37516..67664add78 100644 --- a/src/quick/items/qquicktextinput.cpp +++ b/src/quick/items/qquicktextinput.cpp @@ -2477,40 +2477,44 @@ void QQuickTextInput::moveCursorSelection(int pos, SelectionMode mode) void QQuickTextInput::focusInEvent(QFocusEvent *event) { - Q_D(const QQuickTextInput); -#ifndef QT_NO_IM - if (d->focusOnPress && !d->m_readOnly) - qGuiApp->inputMethod()->show(); -#endif + Q_D(QQuickTextInput); + d->handleFocusEvent(event); QQuickImplicitSizeItem::focusInEvent(event); } -void QQuickTextInput::itemChange(ItemChange change, const ItemChangeData &value) +void QQuickTextInputPrivate::handleFocusEvent(QFocusEvent *event) { - Q_D(QQuickTextInput); - if (change == ItemActiveFocusHasChanged) { - bool hasFocus = value.boolValue; - setCursorVisible(hasFocus); - if (!hasFocus && (d->m_passwordEchoEditing || d->m_passwordEchoTimer.isActive())) { - d->updatePasswordEchoEditing(false);//QQuickTextInputPrivate sets it on key events, but doesn't deal with focus events - } - - if (!hasFocus) { - if (!d->persistentSelection) - d->deselect(); + Q_Q(QQuickTextInput); + bool focus = event->gotFocus(); + q->setCursorVisible(focus); + if (focus) { + q->q_updateAlignment(); #ifndef QT_NO_IM - disconnect(qApp->inputMethod(), SIGNAL(inputDirectionChanged(Qt::LayoutDirection)), - this, SLOT(q_updateAlignment())); + if (focusOnPress && !m_readOnly) + qGuiApp->inputMethod()->show(); + q->connect(qApp->inputMethod(), SIGNAL(inputDirectionChanged(Qt::LayoutDirection)), + q, SLOT(q_updateAlignment())); #endif - } else { - q_updateAlignment(); + } else { + if ((m_passwordEchoEditing || m_passwordEchoTimer.isActive())) { + updatePasswordEchoEditing(false);//QQuickTextInputPrivate sets it on key events, but doesn't deal with focus events + } + + if (!persistentSelection) + deselect(); + #ifndef QT_NO_IM - connect(qApp->inputMethod(), SIGNAL(inputDirectionChanged(Qt::LayoutDirection)), - this, SLOT(q_updateAlignment())); + q->disconnect(qApp->inputMethod(), SIGNAL(inputDirectionChanged(Qt::LayoutDirection)), + q, SLOT(q_updateAlignment())); #endif - } } - QQuickItem::itemChange(change, value); +} + +void QQuickTextInput::focusOutEvent(QFocusEvent *event) +{ + Q_D(QQuickTextInput); + d->handleFocusEvent(event); + QQuickImplicitSizeItem::focusOutEvent(event); } #ifndef QT_NO_IM diff --git a/src/quick/items/qquicktextinput_p.h b/src/quick/items/qquicktextinput_p.h index 602e9dc87b..e66d9b4964 100644 --- a/src/quick/items/qquicktextinput_p.h +++ b/src/quick/items/qquicktextinput_p.h @@ -332,9 +332,9 @@ protected: #endif void mouseUngrabEvent(); bool event(QEvent *e); + void focusOutEvent(QFocusEvent *event); void focusInEvent(QFocusEvent *event); void timerEvent(QTimerEvent *event); - virtual void itemChange(ItemChange, const ItemChangeData &); QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data); public Q_SLOTS: diff --git a/src/quick/items/qquicktextinput_p_p.h b/src/quick/items/qquicktextinput_p_p.h index 2b3809799b..57eff175a2 100644 --- a/src/quick/items/qquicktextinput_p_p.h +++ b/src/quick/items/qquicktextinput_p_p.h @@ -158,7 +158,7 @@ public: #endif void hideCursor(); void showCursor(); - + void handleFocusEvent(QFocusEvent *event); struct MaskInputData { enum Casemode { NoCaseMode, Upper, Lower }; -- cgit v1.2.3 From 04592dae5067b87b39e1ae9387e522fb394159d6 Mon Sep 17 00:00:00 2001 From: Caroline Chao Date: Tue, 5 Mar 2013 11:11:02 +0100 Subject: TextInput: Update text deselection on focus out event The text selection should not be cleared when the focus out event received has one the following reasons: - Qt::ActiveWindowFocusReason - Qt::PopupFocusReason When the user opens a menu or navigates to another window, the eventual selection should not be clearer. This also makes the behavior consistent with TextEdit. Change-Id: Ibc6242cb2f8207cf5281925c8e20b88394f21eea Reviewed-by: Frederik Gladhorn --- src/quick/items/qquicktextinput.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/quick/items/qquicktextinput.cpp b/src/quick/items/qquicktextinput.cpp index 67664add78..58cc68495c 100644 --- a/src/quick/items/qquicktextinput.cpp +++ b/src/quick/items/qquicktextinput.cpp @@ -2500,7 +2500,10 @@ void QQuickTextInputPrivate::handleFocusEvent(QFocusEvent *event) updatePasswordEchoEditing(false);//QQuickTextInputPrivate sets it on key events, but doesn't deal with focus events } - if (!persistentSelection) + if (event->reason() != Qt::ActiveWindowFocusReason + && event->reason() != Qt::PopupFocusReason + && hasSelectedText() + && !persistentSelection) deselect(); #ifndef QT_NO_IM -- cgit v1.2.3 From 962609bef1aaa61125700ceb5e5e25c243113a0a Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Mon, 4 Mar 2013 21:26:13 +0100 Subject: Handle focus events directly in TextEdit. Instead of relying on the item change where we don't get focus reason, use focusInEvent and focusOutEvent. Change-Id: I2db7d81c67c65595b929fdcedc568af360831c5c Reviewed-by: Caroline Chao --- src/quick/items/qquicktextedit.cpp | 55 ++++++++++++++++++------------------ src/quick/items/qquicktextedit_p.h | 3 +- src/quick/items/qquicktextedit_p_p.h | 1 + 3 files changed, 30 insertions(+), 29 deletions(-) diff --git a/src/quick/items/qquicktextedit.cpp b/src/quick/items/qquicktextedit.cpp index 4ddb992fa6..9d28bc7963 100644 --- a/src/quick/items/qquicktextedit.cpp +++ b/src/quick/items/qquicktextedit.cpp @@ -1601,30 +1601,7 @@ void QQuickTextEdit::inputMethodEvent(QInputMethodEvent *event) if (wasComposing != isInputMethodComposing()) emit inputMethodComposingChanged(); } -#endif // QT_NO_IM -void QQuickTextEdit::itemChange(ItemChange change, const ItemChangeData &value) -{ - Q_D(QQuickTextEdit); - if (change == ItemActiveFocusHasChanged) { - setCursorVisible(value.boolValue); - QFocusEvent focusEvent(value.boolValue ? QEvent::FocusIn : QEvent::FocusOut); - d->control->processEvent(&focusEvent, QPointF(-d->xoff, -d->yoff)); - if (value.boolValue) { - q_updateAlignment(); -#ifndef QT_NO_IM - connect(qApp->inputMethod(), SIGNAL(inputDirectionChanged(Qt::LayoutDirection)), - this, SLOT(q_updateAlignment())); - } else { - disconnect(qApp->inputMethod(), SIGNAL(inputDirectionChanged(Qt::LayoutDirection)), - this, SLOT(q_updateAlignment())); -#endif - } - } - QQuickItem::itemChange(change, value); -} - -#ifndef QT_NO_IM /*! \overload Returns the value of the given \a property. @@ -2070,12 +2047,36 @@ void QQuickTextEditPrivate::updateDefaultTextOption() void QQuickTextEdit::focusInEvent(QFocusEvent *event) { - Q_D(const QQuickTextEdit); + Q_D(QQuickTextEdit); + d->handleFocusEvent(event); + QQuickImplicitSizeItem::focusInEvent(event); +} + +void QQuickTextEdit::focusOutEvent(QFocusEvent *event) +{ + Q_D(QQuickTextEdit); + d->handleFocusEvent(event); + QQuickImplicitSizeItem::focusOutEvent(event); +} + +void QQuickTextEditPrivate::handleFocusEvent(QFocusEvent *event) +{ + Q_Q(QQuickTextEdit); + bool focus = event->type() == QEvent::FocusIn; + q->setCursorVisible(focus); + control->processEvent(event, QPointF(-xoff, -yoff)); + if (focus) { + q->q_updateAlignment(); #ifndef QT_NO_IM - if (d->focusOnPress && !isReadOnly()) - qGuiApp->inputMethod()->show(); + if (focusOnPress && !q->isReadOnly()) + qGuiApp->inputMethod()->show(); + q->connect(qApp->inputMethod(), SIGNAL(inputDirectionChanged(Qt::LayoutDirection)), + q, SLOT(q_updateAlignment())); + } else { + q->disconnect(qApp->inputMethod(), SIGNAL(inputDirectionChanged(Qt::LayoutDirection)), + q, SLOT(q_updateAlignment())); #endif - QQuickImplicitSizeItem::focusInEvent(event); + } } void QQuickTextEdit::q_canPasteChanged() diff --git a/src/quick/items/qquicktextedit_p.h b/src/quick/items/qquicktextedit_p.h index 744a7e290d..149d26f98e 100644 --- a/src/quick/items/qquicktextedit_p.h +++ b/src/quick/items/qquicktextedit_p.h @@ -329,6 +329,7 @@ protected: void keyPressEvent(QKeyEvent *); void keyReleaseEvent(QKeyEvent *); void focusInEvent(QFocusEvent *event); + void focusOutEvent(QFocusEvent *event); // mouse filter? void mousePressEvent(QMouseEvent *event); @@ -338,8 +339,6 @@ protected: #ifndef QT_NO_IM void inputMethodEvent(QInputMethodEvent *e); #endif - virtual void itemChange(ItemChange, const ItemChangeData &); - QSGNode *updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *updatePaintNodeData); friend class QQuickTextUtil; diff --git a/src/quick/items/qquicktextedit_p_p.h b/src/quick/items/qquicktextedit_p_p.h index 5306c979cf..f65af3d2d3 100644 --- a/src/quick/items/qquicktextedit_p_p.h +++ b/src/quick/items/qquicktextedit_p_p.h @@ -105,6 +105,7 @@ public: Qt::LayoutDirection textDirection(const QString &text) const; void setNativeCursorEnabled(bool enabled) { control->setCursorWidth(enabled ? 1 : 0); } + void handleFocusEvent(QFocusEvent *event); #ifndef QT_NO_IM Qt::InputMethodHints effectiveInputMethodHints() const; -- cgit v1.2.3 From c4cbb1249fa7cb754de4603f5ff606aa94fafbd5 Mon Sep 17 00:00:00 2001 From: Caroline Chao Date: Tue, 5 Mar 2013 14:56:10 +0100 Subject: Tests: add autotests for TextEdit and TextInput Check the selection is not cleared when the control receives a focus out event with one of the following reason: - Qt::ActiveWindowFocusReason - Qt::PopupFocusReason Change-Id: I38f4a4cba5e769f19de4e327d03be57a8d1d36a7 Reviewed-by: Frederik Gladhorn --- .../qquicktextedit/data/focusOutSelection.qml | 35 +++++++++++++++++ .../quick/qquicktextedit/tst_qquicktextedit.cpp | 43 +++++++++++++++++++++ .../quick/qquicktextinput/tst_qquicktextinput.cpp | 44 ++++++++++++++++++++++ 3 files changed, 122 insertions(+) create mode 100644 tests/auto/quick/qquicktextedit/data/focusOutSelection.qml diff --git a/tests/auto/quick/qquicktextedit/data/focusOutSelection.qml b/tests/auto/quick/qquicktextedit/data/focusOutSelection.qml new file mode 100644 index 0000000000..91a6ac900d --- /dev/null +++ b/tests/auto/quick/qquicktextedit/data/focusOutSelection.qml @@ -0,0 +1,35 @@ +import QtQuick 2.0 + +Rectangle { + id: focusoutwindow + + width: 100 + height: 150 + + property alias text1: text1 + property alias text2: text2 + + TextEdit { + x: 20 + y: 30 + id: text1 + text: "text 1" + height: 20 + width: 50 + selectByMouse: true + activeFocusOnPress: true + objectName: "text1" + } + + TextEdit { + x: 20 + y: 80 + id: text2 + text: "text 2" + height: 20 + width: 50 + selectByMouse: true + activeFocusOnPress: true + objectName: "text2" + } +} diff --git a/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp b/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp index c29c6c05d6..e4821e80a6 100644 --- a/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp +++ b/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp @@ -117,6 +117,7 @@ private slots: void color(); void textMargin(); void persistentSelection(); + void selectionOnFocusOut(); void focusOnPress(); void selection(); void isRightToLeft_data(); @@ -1163,6 +1164,48 @@ void tst_qquicktextedit::persistentSelection() } +void tst_qquicktextedit::selectionOnFocusOut() +{ + QQuickView window(testFileUrl("focusOutSelection.qml")); + window.show(); + window.requestActivate(); + QTest::qWaitForWindowActive(&window); + + QPoint p1(25, 35); + QPoint p2(25, 85); + + QQuickTextEdit *edit1 = window.rootObject()->findChild("text1"); + QQuickTextEdit *edit2 = window.rootObject()->findChild("text2"); + + QTest::mouseClick(&window, Qt::LeftButton, 0, p1); + QVERIFY(edit1->hasActiveFocus()); + QVERIFY(!edit2->hasActiveFocus()); + + edit1->selectAll(); + QCOMPARE(edit1->selectedText(), QLatin1String("text 1")); + + QTest::mouseClick(&window, Qt::LeftButton, 0, p2); + + QCOMPARE(edit1->selectedText(), QLatin1String("")); + QVERIFY(!edit1->hasActiveFocus()); + QVERIFY(edit2->hasActiveFocus()); + + edit2->selectAll(); + QCOMPARE(edit2->selectedText(), QLatin1String("text 2")); + + + edit2->setFocus(false, Qt::ActiveWindowFocusReason); + QVERIFY(!edit2->hasActiveFocus()); + QCOMPARE(edit2->selectedText(), QLatin1String("text 2")); + + edit2->setFocus(true); + QVERIFY(edit2->hasActiveFocus()); + + edit2->setFocus(false, Qt::PopupFocusReason); + QVERIFY(!edit2->hasActiveFocus()); + QCOMPARE(edit2->selectedText(), QLatin1String("text 2")); +} + void tst_qquicktextedit::focusOnPress() { QString componentStr = diff --git a/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp b/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp index 02475eb431..61cac47370 100644 --- a/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp +++ b/tests/auto/quick/qquicktextinput/tst_qquicktextinput.cpp @@ -167,6 +167,7 @@ private slots: void openInputPanel(); void setHAlignClearCache(); void focusOutClearSelection(); + void focusOutNotClearSelection(); void echoMode(); void passwordEchoDelay(); @@ -3497,6 +3498,49 @@ void tst_qquicktextinput::focusOutClearSelection() QTRY_COMPARE(input.selectedText(), QLatin1String("")); } +void tst_qquicktextinput::focusOutNotClearSelection() +{ + QQuickView view; + QQuickTextInput input; + input.setText(QLatin1String("Hello world")); + input.setFocus(true); + input.setParentItem(view.contentItem()); + input.componentComplete(); + view.show(); + view.requestActivate(); + QTest::qWaitForWindowActive(&view); + + QVERIFY(input.hasActiveFocus()); + input.select(2,5); + QTRY_COMPARE(input.selectedText(), QLatin1String("llo")); + + // The selection should not be cleared when the focus + // out event has one of the following reason: + // Qt::ActiveWindowFocusReason + // Qt::PopupFocusReason + + input.setFocus(false, Qt::ActiveWindowFocusReason); + QGuiApplication::processEvents(); + QTRY_COMPARE(input.selectedText(), QLatin1String("llo")); + QTRY_COMPARE(input.hasActiveFocus(), false); + + input.setFocus(true); + QTRY_COMPARE(input.hasActiveFocus(), true); + + input.setFocus(false, Qt::PopupFocusReason); + QGuiApplication::processEvents(); + QTRY_COMPARE(input.selectedText(), QLatin1String("llo")); + QTRY_COMPARE(input.hasActiveFocus(), false); + + input.setFocus(true); + QTRY_COMPARE(input.hasActiveFocus(), true); + + input.setFocus(false, Qt::OtherFocusReason); + QGuiApplication::processEvents(); + QTRY_COMPARE(input.selectedText(), QLatin1String("")); + QTRY_COMPARE(input.hasActiveFocus(), false); +} + void tst_qquicktextinput::geometrySignals() { QQmlComponent component(&engine, testFileUrl("geometrySignals.qml")); -- cgit v1.2.3 From 709764190778f50d5b7dbbb8a63d0ce0ebc2259d Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Thu, 7 Mar 2013 12:08:48 +0100 Subject: Fix warnings about overloaded virtual functions in tests (CLANG). MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I1f4bda77d0520766813201726f53b439b6094a24 Reviewed-by: Jędrzej Nowacki --- tests/auto/qml/qqmlnotifier/tst_qqmlnotifier.cpp | 4 ++-- tests/auto/quick/touchmouse/tst_touchmouse.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/auto/qml/qqmlnotifier/tst_qqmlnotifier.cpp b/tests/auto/qml/qqmlnotifier/tst_qqmlnotifier.cpp index ae37b6a4fb..fa3b190826 100644 --- a/tests/auto/qml/qqmlnotifier/tst_qqmlnotifier.cpp +++ b/tests/auto/qml/qqmlnotifier/tst_qqmlnotifier.cpp @@ -161,7 +161,7 @@ public: private slots: void initTestCase() Q_DECL_OVERRIDE; void cleanupTestCase(); - void connectNotify(); + void testConnectNotify(); void removeV4Binding(); void removeV4Binding2(); @@ -215,7 +215,7 @@ void tst_qqmlnotifier::cleanupTestCase() exportedObject = 0; } -void tst_qqmlnotifier::connectNotify() +void tst_qqmlnotifier::testConnectNotify() { createObjects(); diff --git a/tests/auto/quick/touchmouse/tst_touchmouse.cpp b/tests/auto/quick/touchmouse/tst_touchmouse.cpp index d33bf3991c..15a4f0cc27 100644 --- a/tests/auto/quick/touchmouse/tst_touchmouse.cpp +++ b/tests/auto/quick/touchmouse/tst_touchmouse.cpp @@ -148,7 +148,7 @@ private slots: void initTestCase(); void simpleTouchEvent(); - void eventFilter(); + void testEventFilter(); void mouse(); void touchOverMouse(); void mouseOverTouch(); @@ -303,7 +303,7 @@ void tst_TouchMouse::simpleTouchEvent() delete window; } -void tst_TouchMouse::eventFilter() +void tst_TouchMouse::testEventFilter() { // // install event filter on item and see that it can grab events // QQuickView *window = createView(); -- cgit v1.2.3 From 92d99ca182f702afb40865e25dd773890e44bc2d Mon Sep 17 00:00:00 2001 From: Debao Zhang Date: Wed, 6 Mar 2013 16:57:44 +0800 Subject: Fix MSVC warning C4819 under Chinese locale warning C4819: The file contains a character that cannot be represented in the current code page (936) Change-Id: I53dc345e03b0ec6f951bd2763bc1bab4aef63d04 Reviewed-by: Laszlo Papp Reviewed-by: Friedemann Kleint Reviewed-by: Joerg Bornemann --- src/quick/items/qquickitem.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index 8f68f6ff3f..62bc70bb5a 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -3998,8 +3998,8 @@ void QQuickItemPrivate::setState(const QString &state) This property holds the name of the current state of the item. - If the item is in its default state — that is, no explicit state has been - set — then this property holds an empty string. Likewise, you can return + If the item is in its default state, that is, no explicit state has been + set, then this property holds an empty string. Likewise, you can return an item to its default state by setting this property to an empty string. \sa {Qt Quick States} @@ -4009,8 +4009,8 @@ void QQuickItemPrivate::setState(const QString &state) This property holds the name of the current state of the item. - If the item is in its default state — that is, no explicit state has been - set — then this property holds an empty string. Likewise, you can return + If the item is in its default state, that is, no explicit state has been + set, then this property holds an empty string. Likewise, you can return an item to its default state by setting this property to an empty string. \sa {Qt Quick States} -- cgit v1.2.3 From 943fbf9449a264aaefac722cf1c5694e1238fcb0 Mon Sep 17 00:00:00 2001 From: Gunnar Sletta Date: Thu, 28 Feb 2013 12:28:16 +0100 Subject: Mark geometry dirty to signal scene graph that a repaint is needed. Change-Id: Ic230ffe43b690e3f1e0e7290c4c9571f60bd34b7 Reviewed-by: Yoann Lopes Reviewed-by: Frederik Gladhorn --- examples/quick/scenegraph/customgeometry/beziercurve.cpp | 2 ++ examples/quick/scenegraph/customgeometry/main.cpp | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/quick/scenegraph/customgeometry/beziercurve.cpp b/examples/quick/scenegraph/customgeometry/beziercurve.cpp index 4d1cb5b670..d8d1a189e5 100644 --- a/examples/quick/scenegraph/customgeometry/beziercurve.cpp +++ b/examples/quick/scenegraph/customgeometry/beziercurve.cpp @@ -159,8 +159,10 @@ QSGNode *BezierCurve::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) vertices[i].set(x, y); } + node->markDirty(QSGNode::DirtyGeometry); //! [8] + //! [9] return node; } diff --git a/examples/quick/scenegraph/customgeometry/main.cpp b/examples/quick/scenegraph/customgeometry/main.cpp index 5df68e87cb..0da803f8dc 100644 --- a/examples/quick/scenegraph/customgeometry/main.cpp +++ b/examples/quick/scenegraph/customgeometry/main.cpp @@ -52,7 +52,7 @@ int main(int argc, char **argv) qmlRegisterType("CustomGeometry", 1, 0, "BezierCurve"); QQuickView view; - QSurfaceFormat format; + QSurfaceFormat format = view.format(); format.setSamples(16); view.setFormat(format); view.setSource(QUrl("qrc:///scenegraph/customgeometry/main.qml")); -- cgit v1.2.3 From 17be956554654d5b5869767457da585375bd3e9f Mon Sep 17 00:00:00 2001 From: Gunnar Sletta Date: Thu, 7 Mar 2013 08:23:07 +0100 Subject: Remove comment about performance in qquickitem's clip property. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I2d1b9a86251466445d506969d779308e2d7b92bb Reviewed-by: Samuel Rødal --- src/quick/items/qquickitem.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index 62bc70bb5a..53773553ee 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -3007,8 +3007,6 @@ QList QQuickItem::childItems() const If clipping is enabled, an item will clip its own painting, as well as the painting of its children, to its bounding rectangle. - - Non-rectangular clipping regions are not supported for performance reasons. */ /*! \property QQuickItem::clip @@ -3018,8 +3016,6 @@ QList QQuickItem::childItems() const as the painting of its children, to its bounding rectangle. If you set clipping during an item's paint operation, remember to re-set it to prevent clipping the rest of your scene. - - Non-rectangular clipping regions are not supported for performance reasons. */ bool QQuickItem::clip() const { -- cgit v1.2.3 From a508650fb6e2e850734efdcce154b83bdad4df67 Mon Sep 17 00:00:00 2001 From: Peter Varga Date: Mon, 4 Mar 2013 17:21:08 +0100 Subject: Replace deprecated V8 functions The External::Wrap and External::Unwrap functions became deprecated in the mainline V8. Replace them as it is officially proposed. Change-Id: Ieac1da53997da440a909939d86c98f17a124c068 Reviewed-by: Lars Knoll --- src/particles/qquickv8particledata.cpp | 2 +- src/qml/qml/qqmlxmlhttprequest.cpp | 56 +++++++++++++-------------- src/qml/qml/v8/qv8engine.cpp | 6 +-- src/qml/qml/v8/qv8engine_p.h | 6 +-- src/qml/qml/v8/qv8qobjectwrapper.cpp | 6 +-- src/quick/items/context2d/qquickcontext2d.cpp | 46 +++++++++++----------- 6 files changed, 61 insertions(+), 61 deletions(-) diff --git a/src/particles/qquickv8particledata.cpp b/src/particles/qquickv8particledata.cpp index a485e7449d..44f992c163 100644 --- a/src/particles/qquickv8particledata.cpp +++ b/src/particles/qquickv8particledata.cpp @@ -386,7 +386,7 @@ static void particleData_set_ ## VARIABLE (v8::Local, v8::Localdatum-> SETTER ( value->NumberValue() );\ } -#define REGISTER_ACCESSOR(FT, ENGINE, VARIABLE, NAME) FT ->PrototypeTemplate()->SetAccessor( v8::String::New( #NAME ), particleData_get_ ## VARIABLE , particleData_set_ ## VARIABLE , v8::External::Wrap(ENGINE)) +#define REGISTER_ACCESSOR(FT, ENGINE, VARIABLE, NAME) FT ->PrototypeTemplate()->SetAccessor( v8::String::New( #NAME ), particleData_get_ ## VARIABLE , particleData_set_ ## VARIABLE , v8::External::New(ENGINE)) COLOR_GETTER_AND_SETTER(r, red) COLOR_GETTER_AND_SETTER(g, green) diff --git a/src/qml/qml/qqmlxmlhttprequest.cpp b/src/qml/qml/qqmlxmlhttprequest.cpp index 9bd769f157..80f5deccdb 100644 --- a/src/qml/qml/qqmlxmlhttprequest.cpp +++ b/src/qml/qml/qqmlxmlhttprequest.cpp @@ -496,25 +496,25 @@ v8::Handle Node::prototype(QV8Engine *engine) if (d->nodePrototype.IsEmpty()) { d->nodePrototype = qPersistentNew(v8::Object::New()); d->nodePrototype->SetAccessor(v8::String::New("nodeName"), nodeName, - 0, v8::External::Wrap(engine)); + 0, v8::External::New(engine)); d->nodePrototype->SetAccessor(v8::String::New("nodeValue"), nodeValue, - 0, v8::External::Wrap(engine)); + 0, v8::External::New(engine)); d->nodePrototype->SetAccessor(v8::String::New("nodeType"), nodeType, - 0, v8::External::Wrap(engine)); + 0, v8::External::New(engine)); d->nodePrototype->SetAccessor(v8::String::New("parentNode"), parentNode, - 0, v8::External::Wrap(engine)); + 0, v8::External::New(engine)); d->nodePrototype->SetAccessor(v8::String::New("childNodes"), childNodes, - 0, v8::External::Wrap(engine)); + 0, v8::External::New(engine)); d->nodePrototype->SetAccessor(v8::String::New("firstChild"), firstChild, - 0, v8::External::Wrap(engine)); + 0, v8::External::New(engine)); d->nodePrototype->SetAccessor(v8::String::New("lastChild"), lastChild, - 0, v8::External::Wrap(engine)); + 0, v8::External::New(engine)); d->nodePrototype->SetAccessor(v8::String::New("previousSibling"), previousSibling, - 0, v8::External::Wrap(engine)); + 0, v8::External::New(engine)); d->nodePrototype->SetAccessor(v8::String::New("nextSibling"), nextSibling, - 0, v8::External::Wrap(engine)); + 0, v8::External::New(engine)); d->nodePrototype->SetAccessor(v8::String::New("attributes"), attributes, - 0, v8::External::Wrap(engine)); + 0, v8::External::New(engine)); engine->freezeObject(d->nodePrototype); } return d->nodePrototype; @@ -564,7 +564,7 @@ v8::Handle Element::prototype(QV8Engine *engine) d->elementPrototype = qPersistentNew(v8::Object::New()); d->elementPrototype->SetPrototype(Node::prototype(engine)); d->elementPrototype->SetAccessor(v8::String::New("tagName"), nodeName, - 0, v8::External::Wrap(engine)); + 0, v8::External::New(engine)); engine->freezeObject(d->elementPrototype); } return d->elementPrototype; @@ -577,11 +577,11 @@ v8::Handle Attr::prototype(QV8Engine *engine) d->attrPrototype = qPersistentNew(v8::Object::New()); d->attrPrototype->SetPrototype(Node::prototype(engine)); d->attrPrototype->SetAccessor(v8::String::New("name"), name, - 0, v8::External::Wrap(engine)); + 0, v8::External::New(engine)); d->attrPrototype->SetAccessor(v8::String::New("value"), value, - 0, v8::External::Wrap(engine)); + 0, v8::External::New(engine)); d->attrPrototype->SetAccessor(v8::String::New("ownerElement"), ownerElement, - 0, v8::External::Wrap(engine)); + 0, v8::External::New(engine)); engine->freezeObject(d->attrPrototype); } return d->attrPrototype; @@ -630,9 +630,9 @@ v8::Handle CharacterData::prototype(QV8Engine *engine) d->characterDataPrototype = qPersistentNew(v8::Object::New()); d->characterDataPrototype->SetPrototype(Node::prototype(engine)); d->characterDataPrototype->SetAccessor(v8::String::New("data"), nodeValue, - 0, v8::External::Wrap(engine)); + 0, v8::External::New(engine)); d->characterDataPrototype->SetAccessor(v8::String::New("length"), length, - 0, v8::External::Wrap(engine)); + 0, v8::External::New(engine)); engine->freezeObject(d->characterDataPrototype); } return d->characterDataPrototype; @@ -663,9 +663,9 @@ v8::Handle Text::prototype(QV8Engine *engine) d->textPrototype = qPersistentNew(v8::Object::New()); d->textPrototype->SetPrototype(CharacterData::prototype(engine)); d->textPrototype->SetAccessor(v8::String::New("isElementContentWhitespace"), isElementContentWhitespace, - 0, v8::External::Wrap(engine)); + 0, v8::External::New(engine)); d->textPrototype->SetAccessor(v8::String::New("wholeText"), wholeText, - 0, v8::External::Wrap(engine)); + 0, v8::External::New(engine)); engine->freezeObject(d->textPrototype); } return d->textPrototype; @@ -689,13 +689,13 @@ v8::Handle Document::prototype(QV8Engine *engine) d->documentPrototype = qPersistentNew(v8::Object::New()); d->documentPrototype->SetPrototype(Node::prototype(engine)); d->documentPrototype->SetAccessor(v8::String::New("xmlVersion"), xmlVersion, - 0, v8::External::Wrap(engine)); + 0, v8::External::New(engine)); d->documentPrototype->SetAccessor(v8::String::New("xmlEncoding"), xmlEncoding, - 0, v8::External::Wrap(engine)); + 0, v8::External::New(engine)); d->documentPrototype->SetAccessor(v8::String::New("xmlStandalone"), xmlStandalone, - 0, v8::External::Wrap(engine)); + 0, v8::External::New(engine)); d->documentPrototype->SetAccessor(v8::String::New("documentElement"), documentElement, - 0, v8::External::Wrap(engine)); + 0, v8::External::New(engine)); engine->freezeObject(d->documentPrototype); } return d->documentPrototype; @@ -854,9 +854,9 @@ v8::Handle NamedNodeMap::prototype(QV8Engine *engine) QQmlXMLHttpRequestData *d = xhrdata(engine); if (d->namedNodeMapPrototype.IsEmpty()) { v8::Local ot = v8::ObjectTemplate::New(); - ot->SetAccessor(v8::String::New("length"), length, 0, v8::External::Wrap(engine)); - ot->SetIndexedPropertyHandler(indexed, 0, 0, 0, 0, v8::External::Wrap(engine)); - ot->SetFallbackPropertyHandler(named, 0, 0, 0, 0, v8::External::Wrap(engine)); + ot->SetAccessor(v8::String::New("length"), length, 0, v8::External::New(engine)); + ot->SetIndexedPropertyHandler(indexed, 0, 0, 0, 0, v8::External::New(engine)); + ot->SetFallbackPropertyHandler(named, 0, 0, 0, 0, v8::External::New(engine)); d->namedNodeMapPrototype = qPersistentNew(ot->NewInstance()); engine->freezeObject(d->namedNodeMapPrototype); } @@ -903,8 +903,8 @@ v8::Handle NodeList::prototype(QV8Engine *engine) QQmlXMLHttpRequestData *d = xhrdata(engine); if (d->nodeListPrototype.IsEmpty()) { v8::Local ot = v8::ObjectTemplate::New(); - ot->SetAccessor(v8::String::New("length"), length, 0, v8::External::Wrap(engine)); - ot->SetIndexedPropertyHandler(indexed, 0, 0, 0, 0, v8::External::Wrap(engine)); + ot->SetAccessor(v8::String::New("length"), length, 0, v8::External::New(engine)); + ot->SetIndexedPropertyHandler(indexed, 0, 0, 0, 0, v8::External::New(engine)); d->nodeListPrototype = qPersistentNew(ot->NewInstance()); engine->freezeObject(d->nodeListPrototype); } @@ -1790,7 +1790,7 @@ void *qt_add_qmlxmlhttprequest(QV8Engine *engine) // XMLHttpRequest v8::Local xmlhttprequest = v8::FunctionTemplate::New(qmlxmlhttprequest_new, - v8::External::Wrap(engine)); + v8::External::New(engine)); xmlhttprequest->InstanceTemplate()->SetHasExternalResource(true); // Methods diff --git a/src/qml/qml/v8/qv8engine.cpp b/src/qml/qml/v8/qv8engine.cpp index 15611f3428..2f14b62618 100644 --- a/src/qml/qml/v8/qv8engine.cpp +++ b/src/qml/qml/v8/qv8engine.cpp @@ -1451,7 +1451,7 @@ int QV8Engine::consoleCountHelper(const QString &file, quint16 line, quint16 col v8::Handle QV8Engine::getPlatform(v8::Local, const v8::AccessorInfo &info) { - QV8Engine *engine = reinterpret_cast(v8::External::Unwrap(info.Data())); + QV8Engine *engine = reinterpret_cast(v8::External::Cast(*info.Data())->Value()); if (!engine->m_platform) { // Only allocate a platform object once engine->m_platform = new QQmlPlatform(engine->m_engine); @@ -1461,7 +1461,7 @@ v8::Handle QV8Engine::getPlatform(v8::Local, const v8::Ac v8::Handle QV8Engine::getApplication(v8::Local, const v8::AccessorInfo &info) { - QV8Engine *engine = reinterpret_cast(v8::External::Unwrap(info.Data())); + QV8Engine *engine = reinterpret_cast(v8::External::Cast(*info.Data())->Value()); if (!engine->m_application) { // Only allocate an application object once engine->m_application = QQml_guiProvider()->application(engine->m_engine); @@ -1472,7 +1472,7 @@ v8::Handle QV8Engine::getApplication(v8::Local, const v8: #ifndef QT_NO_IM v8::Handle QV8Engine::getInputMethod(v8::Local, const v8::AccessorInfo &info) { - QV8Engine *engine = reinterpret_cast(v8::External::Unwrap(info.Data())); + QV8Engine *engine = reinterpret_cast(v8::External::Cast(*info.Data())->Value()); return engine->newQObject(QQml_guiProvider()->inputMethod(), CppOwnership); } #endif diff --git a/src/qml/qml/v8/qv8engine_p.h b/src/qml/qml/v8/qv8engine_p.h index 9c3eb22e1c..090ffaa353 100644 --- a/src/qml/qml/v8/qv8engine_p.h +++ b/src/qml/qml/v8/qv8engine_p.h @@ -102,8 +102,8 @@ QT_BEGIN_NAMESPACE // a handle, qFatal() is called. // #define QML_GLOBAL_HANDLE_DEBUGGING -#define V8ENGINE() ((QV8Engine *)v8::External::Unwrap(args.Data())) -#define V8FUNCTION(function, engine) v8::FunctionTemplate::New(function, v8::External::Wrap((QV8Engine*)engine))->GetFunction() +#define V8ENGINE() ((QV8Engine *)v8::External::Cast(*args.Data())->Value()) +#define V8FUNCTION(function, engine) v8::FunctionTemplate::New(function, v8::External::New((QV8Engine*)engine))->GetFunction() #define V8THROW_ERROR(string) { \ v8::ThrowException(v8::Exception::Error(v8::String::New(string))); \ return v8::Handle(); \ @@ -112,7 +112,7 @@ QT_BEGIN_NAMESPACE v8::ThrowException(v8::Exception::TypeError(v8::String::New(string))); \ return v8::Handle(); \ } -#define V8ENGINE_ACCESSOR() ((QV8Engine *)v8::External::Unwrap(info.Data())); +#define V8ENGINE_ACCESSOR() ((QV8Engine *)v8::External::Cast(*info.Data())->Value()); #define V8THROW_ERROR_SETTER(string) { \ v8::ThrowException(v8::Exception::Error(v8::String::New(string))); \ return; \ diff --git a/src/qml/qml/v8/qv8qobjectwrapper.cpp b/src/qml/qml/v8/qv8qobjectwrapper.cpp index 4539401a3b..0982f177d3 100644 --- a/src/qml/qml/v8/qv8qobjectwrapper.cpp +++ b/src/qml/qml/v8/qv8qobjectwrapper.cpp @@ -277,7 +277,7 @@ static v8::Handle GenericValueGetter(v8::Local, const v8: if (QQmlData::wasDeleted(object)) return v8::Undefined(); QQmlPropertyData *property = - (QQmlPropertyData *)v8::External::Unwrap(info.Data()); + (QQmlPropertyData *)v8::External::Cast(*info.Data())->Value(); QQmlEngine *engine = resource->engine->engine(); QQmlEnginePrivate *ep = engine?QQmlEnginePrivate::get(engine):0; @@ -894,7 +894,7 @@ static void FastValueSetter(v8::Local, v8::Local value, QObject *object = resource->object; QQmlPropertyData *property = - (QQmlPropertyData *)v8::External::Unwrap(info.Data()); + (QQmlPropertyData *)v8::External::Cast(*info.Data())->Value(); int index = property->coreIndex; @@ -1029,7 +1029,7 @@ v8::Local QQmlPropertyCache::newQObject(QObject *object, QV8Engine * // this type and the property accessor checks if the object is 0 (deleted) before // dereferencing the pointer. ft->InstanceTemplate()->SetAccessor(engine->toString(iter.key()), fastgetter, fastsetter, - v8::External::Wrap(property)); + v8::External::New(property)); } } diff --git a/src/quick/items/context2d/qquickcontext2d.cpp b/src/quick/items/context2d/qquickcontext2d.cpp index 9c73c7d191..995d951a8a 100644 --- a/src/quick/items/context2d/qquickcontext2d.cpp +++ b/src/quick/items/context2d/qquickcontext2d.cpp @@ -3479,7 +3479,7 @@ QQuickContext2DEngineData::QQuickContext2DEngineData(QV8Engine *engine) v8::Local ft = v8::FunctionTemplate::New(); ft->InstanceTemplate()->SetHasExternalResource(true); - ft->PrototypeTemplate()->SetAccessor(v8::String::New("canvas"), ctx2d_canvas, 0, v8::External::Wrap(engine)); + ft->PrototypeTemplate()->SetAccessor(v8::String::New("canvas"), ctx2d_canvas, 0, v8::External::New(engine)); ft->PrototypeTemplate()->Set(v8::String::New("restore"), V8FUNCTION(ctx2d_restore, engine)); ft->PrototypeTemplate()->Set(v8::String::New("reset"), V8FUNCTION(ctx2d_reset, engine)); ft->PrototypeTemplate()->Set(v8::String::New("save"), V8FUNCTION(ctx2d_save, engine)); @@ -3490,24 +3490,24 @@ QQuickContext2DEngineData::QQuickContext2DEngineData(QV8Engine *engine) ft->PrototypeTemplate()->Set(v8::String::New("transform"), V8FUNCTION(ctx2d_transform, engine)); ft->PrototypeTemplate()->Set(v8::String::New("translate"), V8FUNCTION(ctx2d_translate, engine)); ft->PrototypeTemplate()->Set(v8::String::New("shear"), V8FUNCTION(ctx2d_shear, engine)); - ft->InstanceTemplate()->SetAccessor(v8::String::New("globalAlpha"), ctx2d_globalAlpha, ctx2d_globalAlpha_set, v8::External::Wrap(engine)); - ft->InstanceTemplate()->SetAccessor(v8::String::New("globalCompositeOperation"), ctx2d_globalCompositeOperation, ctx2d_globalCompositeOperation_set, v8::External::Wrap(engine)); - ft->InstanceTemplate()->SetAccessor(v8::String::New("fillRule"), ctx2d_fillRule, ctx2d_fillRule_set, v8::External::Wrap(engine)); - ft->InstanceTemplate()->SetAccessor(v8::String::New("fillStyle"), ctx2d_fillStyle, ctx2d_fillStyle_set, v8::External::Wrap(engine)); - ft->InstanceTemplate()->SetAccessor(v8::String::New("strokeStyle"), ctx2d_strokeStyle, ctx2d_strokeStyle_set, v8::External::Wrap(engine)); + ft->InstanceTemplate()->SetAccessor(v8::String::New("globalAlpha"), ctx2d_globalAlpha, ctx2d_globalAlpha_set, v8::External::New(engine)); + ft->InstanceTemplate()->SetAccessor(v8::String::New("globalCompositeOperation"), ctx2d_globalCompositeOperation, ctx2d_globalCompositeOperation_set, v8::External::New(engine)); + ft->InstanceTemplate()->SetAccessor(v8::String::New("fillRule"), ctx2d_fillRule, ctx2d_fillRule_set, v8::External::New(engine)); + ft->InstanceTemplate()->SetAccessor(v8::String::New("fillStyle"), ctx2d_fillStyle, ctx2d_fillStyle_set, v8::External::New(engine)); + ft->InstanceTemplate()->SetAccessor(v8::String::New("strokeStyle"), ctx2d_strokeStyle, ctx2d_strokeStyle_set, v8::External::New(engine)); ft->PrototypeTemplate()->Set(v8::String::New("createLinearGradient"), V8FUNCTION(ctx2d_createLinearGradient, engine)); ft->PrototypeTemplate()->Set(v8::String::New("createRadialGradient"), V8FUNCTION(ctx2d_createRadialGradient, engine)); ft->PrototypeTemplate()->Set(v8::String::New("createConicalGradient"), V8FUNCTION(ctx2d_createConicalGradient, engine)); ft->PrototypeTemplate()->Set(v8::String::New("createPattern"), V8FUNCTION(ctx2d_createPattern, engine)); - ft->InstanceTemplate()->SetAccessor(v8::String::New("lineCap"), ctx2d_lineCap, ctx2d_lineCap_set, v8::External::Wrap(engine)); - ft->InstanceTemplate()->SetAccessor(v8::String::New("lineJoin"), ctx2d_lineJoin, ctx2d_lineJoin_set, v8::External::Wrap(engine)); - ft->InstanceTemplate()->SetAccessor(v8::String::New("lineWidth"), ctx2d_lineWidth, ctx2d_lineWidth_set, v8::External::Wrap(engine)); - ft->InstanceTemplate()->SetAccessor(v8::String::New("miterLimit"), ctx2d_miterLimit, ctx2d_miterLimit_set, v8::External::Wrap(engine)); - ft->InstanceTemplate()->SetAccessor(v8::String::New("shadowBlur"), ctx2d_shadowBlur, ctx2d_shadowBlur_set, v8::External::Wrap(engine)); - ft->InstanceTemplate()->SetAccessor(v8::String::New("shadowColor"), ctx2d_shadowColor, ctx2d_shadowColor_set, v8::External::Wrap(engine)); - ft->InstanceTemplate()->SetAccessor(v8::String::New("shadowOffsetX"), ctx2d_shadowOffsetX, ctx2d_shadowOffsetX_set, v8::External::Wrap(engine)); - ft->InstanceTemplate()->SetAccessor(v8::String::New("shadowOffsetY"), ctx2d_shadowOffsetY, ctx2d_shadowOffsetY_set, v8::External::Wrap(engine)); - ft->InstanceTemplate()->SetAccessor(v8::String::New("path"), ctx2d_path, ctx2d_path_set, v8::External::Wrap(engine)); + ft->InstanceTemplate()->SetAccessor(v8::String::New("lineCap"), ctx2d_lineCap, ctx2d_lineCap_set, v8::External::New(engine)); + ft->InstanceTemplate()->SetAccessor(v8::String::New("lineJoin"), ctx2d_lineJoin, ctx2d_lineJoin_set, v8::External::New(engine)); + ft->InstanceTemplate()->SetAccessor(v8::String::New("lineWidth"), ctx2d_lineWidth, ctx2d_lineWidth_set, v8::External::New(engine)); + ft->InstanceTemplate()->SetAccessor(v8::String::New("miterLimit"), ctx2d_miterLimit, ctx2d_miterLimit_set, v8::External::New(engine)); + ft->InstanceTemplate()->SetAccessor(v8::String::New("shadowBlur"), ctx2d_shadowBlur, ctx2d_shadowBlur_set, v8::External::New(engine)); + ft->InstanceTemplate()->SetAccessor(v8::String::New("shadowColor"), ctx2d_shadowColor, ctx2d_shadowColor_set, v8::External::New(engine)); + ft->InstanceTemplate()->SetAccessor(v8::String::New("shadowOffsetX"), ctx2d_shadowOffsetX, ctx2d_shadowOffsetX_set, v8::External::New(engine)); + ft->InstanceTemplate()->SetAccessor(v8::String::New("shadowOffsetY"), ctx2d_shadowOffsetY, ctx2d_shadowOffsetY_set, v8::External::New(engine)); + ft->InstanceTemplate()->SetAccessor(v8::String::New("path"), ctx2d_path, ctx2d_path_set, v8::External::New(engine)); ft->PrototypeTemplate()->Set(v8::String::New("clearRect"), V8FUNCTION(ctx2d_clearRect, engine)); ft->PrototypeTemplate()->Set(v8::String::New("fillRect"), V8FUNCTION(ctx2d_fillRect, engine)); ft->PrototypeTemplate()->Set(v8::String::New("strokeRect"), V8FUNCTION(ctx2d_strokeRect, engine)); @@ -3530,9 +3530,9 @@ QQuickContext2DEngineData::QQuickContext2DEngineData(QV8Engine *engine) ft->PrototypeTemplate()->Set(v8::String::New("drawFocusRing"), V8FUNCTION(ctx2d_drawFocusRing, engine)); ft->PrototypeTemplate()->Set(v8::String::New("caretBlinkRate"), V8FUNCTION(ctx2d_caretBlinkRate, engine)); ft->PrototypeTemplate()->Set(v8::String::New("setCaretSelectionRect"), V8FUNCTION(ctx2d_setCaretSelectionRect, engine)); - ft->InstanceTemplate()->SetAccessor(v8::String::New("font"), ctx2d_font, ctx2d_font_set, v8::External::Wrap(engine)); - ft->InstanceTemplate()->SetAccessor(v8::String::New("textAlign"), ctx2d_textAlign, ctx2d_textAlign_set, v8::External::Wrap(engine)); - ft->InstanceTemplate()->SetAccessor(v8::String::New("textBaseline"), ctx2d_textBaseline, ctx2d_textBaseline_set, v8::External::Wrap(engine)); + ft->InstanceTemplate()->SetAccessor(v8::String::New("font"), ctx2d_font, ctx2d_font_set, v8::External::New(engine)); + ft->InstanceTemplate()->SetAccessor(v8::String::New("textAlign"), ctx2d_textAlign, ctx2d_textAlign_set, v8::External::New(engine)); + ft->InstanceTemplate()->SetAccessor(v8::String::New("textBaseline"), ctx2d_textBaseline, ctx2d_textBaseline_set, v8::External::New(engine)); ft->PrototypeTemplate()->Set(v8::String::New("fillText"), V8FUNCTION(ctx2d_fillText, engine)); ft->PrototypeTemplate()->Set(v8::String::New("measureText"), V8FUNCTION(ctx2d_measureText, engine)); ft->PrototypeTemplate()->Set(v8::String::New("strokeText"), V8FUNCTION(ctx2d_strokeText, engine)); @@ -3554,14 +3554,14 @@ QQuickContext2DEngineData::QQuickContext2DEngineData(QV8Engine *engine) v8::Local ftPixelArray = v8::FunctionTemplate::New(); ftPixelArray->InstanceTemplate()->SetHasExternalResource(true); - ftPixelArray->InstanceTemplate()->SetAccessor(v8::String::New("length"), ctx2d_pixelArray_length, 0, v8::External::Wrap(engine)); - ftPixelArray->InstanceTemplate()->SetIndexedPropertyHandler(ctx2d_pixelArray_indexed, ctx2d_pixelArray_indexed_set, 0, 0, 0, v8::External::Wrap(engine)); + ftPixelArray->InstanceTemplate()->SetAccessor(v8::String::New("length"), ctx2d_pixelArray_length, 0, v8::External::New(engine)); + ftPixelArray->InstanceTemplate()->SetIndexedPropertyHandler(ctx2d_pixelArray_indexed, ctx2d_pixelArray_indexed_set, 0, 0, 0, v8::External::New(engine)); constructorPixelArray = qPersistentNew(ftPixelArray->GetFunction()); v8::Local ftImageData = v8::FunctionTemplate::New(); - ftImageData->InstanceTemplate()->SetAccessor(v8::String::New("width"), ctx2d_imageData_width, 0, v8::External::Wrap(engine)); - ftImageData->InstanceTemplate()->SetAccessor(v8::String::New("height"), ctx2d_imageData_height, 0, v8::External::Wrap(engine)); - ftImageData->InstanceTemplate()->SetAccessor(v8::String::New("data"), ctx2d_imageData_data, 0, v8::External::Wrap(engine)); + ftImageData->InstanceTemplate()->SetAccessor(v8::String::New("width"), ctx2d_imageData_width, 0, v8::External::New(engine)); + ftImageData->InstanceTemplate()->SetAccessor(v8::String::New("height"), ctx2d_imageData_height, 0, v8::External::New(engine)); + ftImageData->InstanceTemplate()->SetAccessor(v8::String::New("data"), ctx2d_imageData_data, 0, v8::External::New(engine)); ftImageData->InstanceTemplate()->SetInternalFieldCount(1); constructorImageData = qPersistentNew(ftImageData->GetFunction()); } -- cgit v1.2.3 From b4673e69a0522fca9c592f1dab1734738b1443a8 Mon Sep 17 00:00:00 2001 From: Guenter Schwann Date: Wed, 27 Feb 2013 15:52:03 +0100 Subject: Fix displacement transition bug for horizontal case Task-number: QTBUG-29944 Change-Id: I23381f7a1d2c8d3c6df007b5b11c12b0db3bb1e9 Reviewed-by: Gunnar Sletta --- src/quick/items/qquickitemview.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/quick/items/qquickitemview.cpp b/src/quick/items/qquickitemview.cpp index 198052b939..b9d508ec9d 100644 --- a/src/quick/items/qquickitemview.cpp +++ b/src/quick/items/qquickitemview.cpp @@ -2117,7 +2117,7 @@ void QQuickItemViewPrivate::prepareVisibleItemTransitions() return; // must call for every visible item to init or discard transitions - QRectF viewBounds(0, position(), q->width(), q->height()); + QRectF viewBounds(q->contentX(), q->contentY(), q->width(), q->height()); for (int i=0; iprepareTransition(transitioner, viewBounds); } -- cgit v1.2.3 From 2f9d0346f00e37725f0f1bce1318501a25c5e438 Mon Sep 17 00:00:00 2001 From: Liang Qi Date: Fri, 22 Feb 2013 13:44:06 +0100 Subject: Support activeFocusOnTab in QQuickItem Add activeFocusOnTab as property to QQuickItem. Setting the property allows automatic keyboard navigation between all elements that have it set. This key event handler will only be called after the QML key handlers, such as KeyNavigation and Keys, and the C++ key event handlers, such as keyPressEvent function. Algorithm is most done by Frederik Gladhorn, in cooperation with Gabriel de Dietrich. Done-with: Frederik Gladhorn Done-with: Gabriel de Dietrich Change-Id: I8b58be9c20d113661fe85d27bdb1af84340d9de5 Reviewed-by: Alan Alpert (Personal) <416365416c@gmail.com> Reviewed-by: Frederik Gladhorn --- .../quick/keyinteraction/focus/Core/GridMenu.qml | 3 +- .../quick/keyinteraction/focus/Core/TabMenu.qml | 104 +++++++ examples/quick/keyinteraction/focus/focus.qml | 46 ++- examples/quick/keyinteraction/keyinteraction.qrc | 1 + src/quick/items/qquickitem.cpp | 157 ++++++++++ src/quick/items/qquickitem.h | 5 + src/quick/items/qquickitem_p.h | 6 +- .../quick/qquickitem2/data/activeFocusOnTab.qml | 136 +++++++++ .../quick/qquickitem2/data/activeFocusOnTab3.qml | 250 ++++++++++++++++ tests/auto/quick/qquickitem2/tst_qquickitem.cpp | 329 +++++++++++++++++++++ 10 files changed, 1023 insertions(+), 14 deletions(-) create mode 100644 examples/quick/keyinteraction/focus/Core/TabMenu.qml create mode 100644 tests/auto/quick/qquickitem2/data/activeFocusOnTab.qml create mode 100644 tests/auto/quick/qquickitem2/data/activeFocusOnTab3.qml diff --git a/examples/quick/keyinteraction/focus/Core/GridMenu.qml b/examples/quick/keyinteraction/focus/Core/GridMenu.qml index 3dd0637344..e18ec1f0d3 100644 --- a/examples/quick/keyinteraction/focus/Core/GridMenu.qml +++ b/examples/quick/keyinteraction/focus/Core/GridMenu.qml @@ -45,7 +45,7 @@ FocusScope { onActiveFocusChanged: { if (activeFocus) - mainView.state = "" + mainView.state = "showGridViews" } Rectangle { @@ -63,6 +63,7 @@ FocusScope { focus: true model: 12 + KeyNavigation.up: tabMenu KeyNavigation.down: listMenu KeyNavigation.left: contextMenu diff --git a/examples/quick/keyinteraction/focus/Core/TabMenu.qml b/examples/quick/keyinteraction/focus/Core/TabMenu.qml new file mode 100644 index 0000000000..5eea611b44 --- /dev/null +++ b/examples/quick/keyinteraction/focus/Core/TabMenu.qml @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 + +FocusScope { + onActiveFocusChanged: { + if (activeFocus) + mainView.state = "showTabViews" + } + + Rectangle { + anchors.fill: parent + clip: true + gradient: Gradient { + GradientStop { position: 0.0; color: "#193441" } + GradientStop { position: 1.0; color: Qt.darker("#193441") } + } + + Row { + id: tabView + anchors.fill: parent; anchors.leftMargin: 20; anchors.rightMargin: 20 + Repeater { + activeFocusOnTab: false + model: 5 + Item { + id: container + width: 152; height: 152 + activeFocusOnTab: true + focus: true + + KeyNavigation.up: listMenu + KeyNavigation.down: gridMenu + + Rectangle { + id: content + color: "transparent" + antialiasing: true + anchors.fill: parent; anchors.margins: 20; radius: 10 + + Rectangle { color: "#91AA9D"; anchors.fill: parent; anchors.margins: 3; radius: 8; antialiasing: true } + Image { source: "images/qt-logo.png"; anchors.centerIn: parent } + } + + MouseArea { + id: mouseArea + anchors.fill: parent + hoverEnabled: true + + onClicked: { + container.forceActiveFocus() + } + } + + states: State { + name: "active"; when: container.activeFocus + PropertyChanges { target: content; color: "#FCFFF5"; scale: 1.1 } + } + + transitions: Transition { + NumberAnimation { properties: "scale"; duration: 100 } + } + } + } + } + } +} diff --git a/examples/quick/keyinteraction/focus/focus.qml b/examples/quick/keyinteraction/focus/focus.qml index 193b3d7b3a..91fb146e40 100644 --- a/examples/quick/keyinteraction/focus/focus.qml +++ b/examples/quick/keyinteraction/focus/focus.qml @@ -44,7 +44,7 @@ import "Core" Rectangle { id: window - width: 800; height: 480 + width: 800; height: 640 color: "#3E606F" FocusScope { @@ -53,17 +53,24 @@ Rectangle { width: parent.width; height: parent.height focus: true - GridMenu { - id: gridMenu - width: parent.width; height: 320 + TabMenu { + id: tabMenu + y: 160; width: parent.width; height: 160 focus: true - interactive: parent.activeFocus + activeFocusOnTab: true + } + + GridMenu { + id: gridMenu + y: 320; width: parent.width; height: 320 + activeFocusOnTab: true } ListMenu { id: listMenu - y: 320; width: parent.width; height: 320 + y: 640; width: parent.width; height: 320 + activeFocusOnTab: true } Rectangle { @@ -73,11 +80,28 @@ Rectangle { opacity: 0 } - states: State { - name: "showListViews" - PropertyChanges { target: gridMenu; y: -160 } - PropertyChanges { target: listMenu; y: 160 } - } + states: [ + State { + name: "showTabViews" + PropertyChanges { target: tabMenu; y: 160 } + PropertyChanges { target: gridMenu; y: 320 } + PropertyChanges { target: listMenu; y: 640 } + }, + + State { + name: "showGridViews" + PropertyChanges { target: tabMenu; y: 0 } + PropertyChanges { target: gridMenu; y: 160 } + PropertyChanges { target: listMenu; y: 480 } + }, + + State { + name: "showListViews" + PropertyChanges { target: tabMenu; y: -160 } + PropertyChanges { target: gridMenu; y: 0 } + PropertyChanges { target: listMenu; y: 320 } + } + ] transitions: Transition { NumberAnimation { properties: "y"; duration: 600; easing.type: Easing.OutQuint } diff --git a/examples/quick/keyinteraction/keyinteraction.qrc b/examples/quick/keyinteraction/keyinteraction.qrc index 21bde4472c..c719654835 100644 --- a/examples/quick/keyinteraction/keyinteraction.qrc +++ b/examples/quick/keyinteraction/keyinteraction.qrc @@ -8,5 +8,6 @@ focus/Core/GridMenu.qml focus/Core/ListMenu.qml focus/Core/ListViewDelegate.qml + focus/Core/TabMenu.qml diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index 53773553ee..67c4fd2140 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -1862,6 +1862,11 @@ void QQuickItemPrivate::updateSubFocusItem(QQuickItem *scope, bool focus) \internal */ +/*! + \fn void QQuickItem::activeFocusOnTabChanged(bool) + \internal +*/ + /*! \fn void QQuickItem::childrenChanged() \internal @@ -2022,6 +2027,93 @@ QQuickItem::~QQuickItem() delete d->_stateGroup; d->_stateGroup = 0; } +/*! + \internal + \brief QQuickItemPrivate::focusNextPrev focuses the next/prev item in the tab-focus-chain + \param item The item that currently has the focus + \param forward The direction + \return Whether the next item in the focus chain is found or not + + If \a next is true, the next item visited will be in depth-first order relative to \a item. + If \a next is false, the next item visited will be in reverse depth-first order relative to \a item. +*/ +bool QQuickItemPrivate::focusNextPrev(QQuickItem *item, bool forward) +{ + Q_ASSERT(item); + Q_ASSERT(item->activeFocusOnTab()); + + QQuickItem *from = 0; + if (forward) { + from = item->parentItem(); + } else { + if (!item->childItems().isEmpty()) + from = item->childItems().first(); + else + from = item->parentItem(); + } + bool skip = false; + QQuickItem *current = item; + do { + skip = false; + QQuickItem *last = current; + + bool hasChildren = !current->childItems().isEmpty() && current->isEnabled() && current->isVisible(); + + // coming from parent: check children + if (hasChildren && from == current->parentItem()) { + if (forward) { + current = current->childItems().first(); + } else { + current = current->childItems().last(); + if (!current->childItems().isEmpty()) + skip = true; + } + } else if (hasChildren && forward && from != current->childItems().last()) { + // not last child going forwards + int nextChild = current->childItems().indexOf(from) + 1; + current = current->childItems().at(nextChild); + } else if (hasChildren && !forward && from != current->childItems().first()) { + // not first child going backwards + int prevChild = current->childItems().indexOf(from) - 1; + current = current->childItems().at(prevChild); + if (!current->childItems().isEmpty()) + skip = true; + // back to the parent + } else if (current->parentItem()) { + current = current->parentItem(); + // we would evaluate the parent twice, thus we skip + if (forward) { + skip = true; + } else if (!forward && !current->childItems().isEmpty()) { + if (last != current->childItems().first()) { + skip = true; + } else if (last == current->childItems().first()) { + if (current->isFocusScope() && current->activeFocusOnTab() && current->hasActiveFocus()) + skip = true; + } + } + } else if (hasChildren) { + // Wrap around after checking all items forward + if (forward) { + current = current->childItems().first(); + } else { + current = current->childItems().last(); + if (!current->childItems().isEmpty()) + skip = true; + } + } + + from = last; + } while (skip || !current->activeFocusOnTab() || !current->isEnabled() || !current->isVisible()); + + if (current == item) + return false; + + current->forceActiveFocus(); + + return true; +} + /*! \qmlproperty Item QtQuick2::Item::parent This property holds the visual parent of the item. @@ -2522,6 +2614,7 @@ QQuickItemPrivate::QQuickItemPrivate() , isAccessible(false) , culled(false) , hasCursor(false) + , activeFocusOnTab(false) , dirtyAttributes(0) , nextDirtyItem(0) , prevDirtyItem(0) @@ -4170,6 +4263,23 @@ void QQuickItemPrivate::deliverKeyEvent(QKeyEvent *e) else extra->keyHandler->keyReleased(e, true); } + + if (e->isAccepted()) + return; + + //only care about KeyPress now + if (q->activeFocusOnTab() && e->type() == QEvent::KeyPress) { + bool res = false; + if (!(e->modifiers() & (Qt::ControlModifier | Qt::AltModifier))) { //### Add MetaModifier? + if (e->key() == Qt::Key_Backtab + || (e->key() == Qt::Key_Tab && (e->modifiers() & Qt::ShiftModifier))) + res = QQuickItemPrivate::focusNextPrev(q, false); + else if (e->key() == Qt::Key_Tab) + res = QQuickItemPrivate::focusNextPrev(q, true); + if (res) + e->setAccepted(true); + } + } } #ifndef QT_NO_IM @@ -5331,6 +5441,53 @@ void QQuickItem::setSmooth(bool smooth) emit smoothChanged(smooth); } +/*! + \qmlproperty bool QtQuick2::Item::activeFocusOnTab + + This property holds whether the item wants to be in tab focus + chain. By default this is set to false. + + The tab focus chain traverses elements by visiting first the + parent, and then its children in the order they occur in the + children property. Pressing the tab key on an item in the tab + focus chain will move keyboard focus to the next item in the + chain. Pressing BackTab (normally Shift+Tab) will move focus + to the previous item. + + To set up a manual tab focus chain, see \l KeyNavigation. Tab + key events used by Keys or KeyNavigation have precedence over + focus chain behavior, ignore the events in other key handlers + to allow it to propagate. +*/ +/*! + \property QQuickItem::activeFocusOnTab + + This property holds whether the item wants to be in tab focus + chain. By default this is set to false. +*/ +bool QQuickItem::activeFocusOnTab() const +{ + Q_D(const QQuickItem); + return d->activeFocusOnTab; +} +void QQuickItem::setActiveFocusOnTab(bool activeFocusOnTab) +{ + Q_D(QQuickItem); + if (d->activeFocusOnTab == activeFocusOnTab) + return; + + if (window()) { + if ((this == window()->activeFocusItem()) && !activeFocusOnTab) { + qWarning("QQuickItem: Cannot set activeFocusOnTab to false once item is the active focus item."); + return; + } + } + + d->activeFocusOnTab = activeFocusOnTab; + + emit activeFocusOnTabChanged(activeFocusOnTab); +} + /*! \qmlproperty bool QtQuick2::Item::antialiasing diff --git a/src/quick/items/qquickitem.h b/src/quick/items/qquickitem.h index 71681698b9..c37bc10bdd 100644 --- a/src/quick/items/qquickitem.h +++ b/src/quick/items/qquickitem.h @@ -132,6 +132,7 @@ class Q_QUICK_EXPORT QQuickItem : public QObject, public QQmlParserStatus Q_PROPERTY(bool focus READ hasFocus WRITE setFocus NOTIFY focusChanged FINAL) Q_PROPERTY(bool activeFocus READ hasActiveFocus NOTIFY activeFocusChanged FINAL) + Q_PROPERTY(bool activeFocusOnTab READ activeFocusOnTab WRITE setActiveFocusOnTab NOTIFY activeFocusOnTabChanged FINAL) Q_PROPERTY(qreal rotation READ rotation WRITE setRotation NOTIFY rotationChanged) Q_PROPERTY(qreal scale READ scale WRITE setScale NOTIFY scaleChanged) @@ -261,6 +262,9 @@ public: bool smooth() const; void setSmooth(bool); + bool activeFocusOnTab() const; + void setActiveFocusOnTab(bool); + bool antialiasing() const; void setAntialiasing(bool); @@ -345,6 +349,7 @@ Q_SIGNALS: void stateChanged(const QString &); void focusChanged(bool); void activeFocusChanged(bool); + void activeFocusOnTabChanged(bool); void parentChanged(QQuickItem *); void transformOriginChanged(TransformOrigin); void smoothChanged(bool); diff --git a/src/quick/items/qquickitem_p.h b/src/quick/items/qquickitem_p.h index 8f6865b40b..4bd9d82c20 100644 --- a/src/quick/items/qquickitem_p.h +++ b/src/quick/items/qquickitem_p.h @@ -396,8 +396,8 @@ public: bool antialiasing:1; bool focus:1; bool activeFocus:1; - bool notifiedFocus:1; // Bit 16 + bool notifiedFocus:1; bool notifiedActiveFocus:1; bool filtersChildMouseEvents:1; bool explicitVisible:1; @@ -413,8 +413,8 @@ public: bool isAccessible:1; bool culled:1; bool hasCursor:1; - // bool dummy:1 // Bit 32 + bool activeFocusOnTab:1; enum DirtyType { TransformOrigin = 0x00000001, @@ -484,6 +484,8 @@ public: QTransform itemToWindowTransform() const; void itemToParentTransform(QTransform &) const; + static bool focusNextPrev(QQuickItem *item, bool forward); + qreal x; qreal y; qreal width; diff --git a/tests/auto/quick/qquickitem2/data/activeFocusOnTab.qml b/tests/auto/quick/qquickitem2/data/activeFocusOnTab.qml new file mode 100644 index 0000000000..e064b41efe --- /dev/null +++ b/tests/auto/quick/qquickitem2/data/activeFocusOnTab.qml @@ -0,0 +1,136 @@ +import QtQuick 2.0 + +Item { + id: main + objectName: "main" + width: 800 + height: 600 + focus: true + Component.onCompleted: button12.focus = true + Item { + id: sub1 + objectName: "sub1" + width: 230 + height: 600 + anchors.top: parent.top + anchors.left: parent.left + Item { + id: button11 + objectName: "button11" + width: 100 + height: 50 + activeFocusOnTab: true + Rectangle { + anchors.fill: parent + color: parent.activeFocus ? "red" : "black" + } + + anchors.top: parent.top + anchors.topMargin: 100 + } + Item { + id: button12 + objectName: "button12" + activeFocusOnTab: true + Rectangle { + anchors.fill: parent + color: parent.activeFocus ? "red" : "black" + } + width: 100 + height: 50 + + anchors.top: button11.bottom + anchors.bottomMargin: 100 + } + Item { + id: button13 + objectName: "button13" + enabled: false + activeFocusOnTab: true + Rectangle { + anchors.fill: parent + color: parent.activeFocus ? "red" : "black" + } + width: 100 + height: 50 + + anchors.top: button12.bottom + anchors.bottomMargin: 100 + } + Item { + id: button14 + objectName: "button14" + visible: false + activeFocusOnTab: true + Rectangle { + anchors.fill: parent + color: parent.activeFocus ? "red" : "black" + } + width: 100 + height: 50 + + anchors.top: button12.bottom + anchors.bottomMargin: 100 + } + } + Item { + id: sub2 + objectName: "sub2" + activeFocusOnTab: true + width: 230 + height: 600 + anchors.top: parent.top + anchors.left: sub1.right + Item { + id: button21 + objectName: "button21" + width: 100 + height: 50 + activeFocusOnTab: true + Rectangle { + anchors.fill: parent + color: parent.activeFocus ? "red" : "black" + } + + anchors.top: parent.top + anchors.topMargin: 100 + } + Item { + id: button22 + objectName: "button22" + width: 100 + height: 50 + activeFocusOnTab: true + Rectangle { + anchors.fill: parent + color: parent.activeFocus ? "red" : "black" + } + + anchors.top: button21.bottom + anchors.bottomMargin: 100 + } + } + Item { + id: sub3 + objectName: "sub3" + width: 230 + height: 600 + anchors.top: parent.top + anchors.left: sub2.right + TextEdit { + id: edit + objectName: "edit" + width: 230 + height: 400 + readOnly: false + activeFocusOnTab: true + wrapMode: TextEdit.Wrap + textFormat: TextEdit.RichText + + text: "aaa\n" + +"bbb\n" + +"ccc\n" + +"ddd\n" + } + } +} diff --git a/tests/auto/quick/qquickitem2/data/activeFocusOnTab3.qml b/tests/auto/quick/qquickitem2/data/activeFocusOnTab3.qml new file mode 100644 index 0000000000..00fb82d59e --- /dev/null +++ b/tests/auto/quick/qquickitem2/data/activeFocusOnTab3.qml @@ -0,0 +1,250 @@ +import QtQuick 2.0 + +Item { + id: main + objectName: "main" + width: 400 + height: 700 + focus: true + Component.onCompleted: button1.focus = true + Item { + id: sub1 + objectName: "sub1" + activeFocusOnTab: false + width: 100 + height: 50 + anchors.top: parent.top + Item { + id: button1 + objectName: "button1" + width: 100 + height: 50 + activeFocusOnTab: true + Rectangle { + anchors.fill: parent + color: parent.activeFocus ? "red" : "black" + } + } + } + Row { + id: row_repeater + objectName: "row_repeater" + activeFocusOnTab: false + anchors.top: sub1.bottom + Repeater { + activeFocusOnTab: false + model: 3 + Rectangle { + activeFocusOnTab: true + width: 100; height: 40 + border.width: 1 + color: activeFocus ? "red" : "yellow" + } + } + } + Item { + id: sub2 + objectName: "sub2" + activeFocusOnTab: false + anchors.top: row_repeater.bottom + width: 100 + height: 50 + Item { + id: button2 + objectName: "button2" + width: 100 + height: 50 + activeFocusOnTab: true + Rectangle { + anchors.fill: parent + color: parent.activeFocus ? "red" : "black" + } + } + } + Row { + id: row + objectName: "row" + activeFocusOnTab: false + anchors.top: sub2.bottom + Rectangle { activeFocusOnTab: true; color: activeFocus ? "red" : "yellow"; width: 50; height: 50 } + Rectangle { activeFocusOnTab: true; color: activeFocus ? "red" : "green"; width: 20; height: 50 } + Rectangle { activeFocusOnTab: true; color: activeFocus ? "red" : "blue"; width: 50; height: 20 } + } + Item { + id: sub3 + objectName: "sub3" + activeFocusOnTab: false + anchors.top: row.bottom + width: 100 + height: 50 + Item { + id: button3 + objectName: "button3" + width: 100 + height: 50 + activeFocusOnTab: true + Rectangle { + anchors.fill: parent + color: parent.activeFocus ? "red" : "black" + } + } + } + Flow { + id: flow + objectName: "flow" + activeFocusOnTab: false + anchors.top: sub3.bottom + width: parent.width + anchors.margins: 4 + spacing: 10 + Rectangle { activeFocusOnTab: true; color: activeFocus ? "red" : "yellow"; width: 50; height: 50 } + Rectangle { activeFocusOnTab: true; color: activeFocus ? "red" : "green"; width: 20; height: 50 } + Rectangle { activeFocusOnTab: true; color: activeFocus ? "red" : "blue"; width: 50; height: 20 } + } + Item { + id: sub4 + objectName: "sub4" + activeFocusOnTab: false + anchors.top: flow.bottom + width: 100 + height: 50 + Item { + id: button4 + objectName: "button4" + width: 100 + height: 50 + activeFocusOnTab: true + Rectangle { + anchors.fill: parent + color: parent.activeFocus ? "red" : "black" + } + } + } + FocusScope { + id: focusscope + objectName: "focusscope" + activeFocusOnTab: false + anchors.top: sub4.bottom + height: 40 + Row { + id: row_focusscope + objectName: "row_focusscope" + activeFocusOnTab: false + anchors.fill: parent + Repeater { + activeFocusOnTab: false + model: 3 + Rectangle { + activeFocusOnTab: true + width: 100; height: 40 + border.width: 1 + color: activeFocus ? "red" : "yellow" + } + } + } + } + Item { + id: sub5 + objectName: "sub5" + activeFocusOnTab: false + anchors.top: focusscope.bottom + width: 100 + height: 50 + Item { + id: button5 + objectName: "button5" + width: 100 + height: 50 + activeFocusOnTab: true + Rectangle { + anchors.fill: parent + color: parent.activeFocus ? "red" : "black" + } + } + } + FocusScope { + id: focusscope2 + objectName: "focusscope2" + activeFocusOnTab: true + anchors.top: sub5.bottom + height: 40 + Row { + id: row_focusscope2 + objectName: "row_focusscope2" + activeFocusOnTab: false + anchors.fill: parent + Repeater { + activeFocusOnTab: false + model: 3 + Rectangle { + activeFocusOnTab: true + focus: true + width: 100; height: 40 + border.width: 1 + color: activeFocus ? "red" : "yellow" + } + } + } + } + Item { + id: sub6 + objectName: "sub6" + activeFocusOnTab: false + anchors.top: focusscope2.bottom + width: 100 + height: 50 + Item { + id: button6 + objectName: "button6" + width: 100 + height: 50 + activeFocusOnTab: true + Rectangle { + anchors.fill: parent + color: parent.activeFocus ? "red" : "black" + } + } + } + FocusScope { + id: focusscope3 + objectName: "focusscope3" + activeFocusOnTab: true + anchors.top: sub6.bottom + height: 40 + Row { + id: row_focusscope3 + objectName: "row_focusscope3" + activeFocusOnTab: false + anchors.fill: parent + Repeater { + activeFocusOnTab: false + model: 3 + Rectangle { + activeFocusOnTab: true + width: 100; height: 40 + border.width: 1 + color: activeFocus ? "red" : "yellow" + } + } + } + } + Item { + id: sub7 + objectName: "sub7" + activeFocusOnTab: false + anchors.top: focusscope3.bottom + width: 100 + height: 50 + Item { + id: button7 + objectName: "button7" + width: 100 + height: 50 + activeFocusOnTab: true + Rectangle { + anchors.fill: parent + color: parent.activeFocus ? "red" : "black" + } + } + } +} diff --git a/tests/auto/quick/qquickitem2/tst_qquickitem.cpp b/tests/auto/quick/qquickitem2/tst_qquickitem.cpp index c82372c287..668b5e2945 100644 --- a/tests/auto/quick/qquickitem2/tst_qquickitem.cpp +++ b/tests/auto/quick/qquickitem2/tst_qquickitem.cpp @@ -64,6 +64,10 @@ private slots: void initTestCase(); void cleanup(); + void activeFocusOnTab(); + void activeFocusOnTab2(); + void activeFocusOnTab3(); + void keys(); void keysProcessingOrder(); void keysim(); @@ -273,6 +277,331 @@ void tst_QQuickItem::cleanup() inputMethodPrivate->testContext = 0; } +void tst_QQuickItem::activeFocusOnTab() +{ + QQuickView *window = new QQuickView(0); + window->setBaseSize(QSize(800,600)); + + window->setSource(testFileUrl("activeFocusOnTab.qml")); + window->show(); + window->requestActivate(); + QVERIFY(QTest::qWaitForWindowActive(window)); + QVERIFY(QGuiApplication::focusWindow() == window); + + // original: button12 + QQuickItem *item = findItem(window->rootObject(), "button12"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // Tab: button12->sub2 + QKeyEvent key(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1); + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + + item = findItem(window->rootObject(), "sub2"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // Tab: sub2->button21 + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1); + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + + item = findItem(window->rootObject(), "button21"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // Tab: button21->button22 + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1); + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + + item = findItem(window->rootObject(), "button22"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // Tab: button22->edit + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1); + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + + item = findItem(window->rootObject(), "edit"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // BackTab: edit->button22 + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1); + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + + item = findItem(window->rootObject(), "button22"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // BackTab: button22->button21 + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1); + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + + item = findItem(window->rootObject(), "button21"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // BackTab: button21->sub2 + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1); + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + + item = findItem(window->rootObject(), "sub2"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // BackTab: sub2->button12 + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1); + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + + item = findItem(window->rootObject(), "button12"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // BackTab: button12->button11 + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1); + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + + item = findItem(window->rootObject(), "button11"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // BackTab: button11->edit + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1); + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + + item = findItem(window->rootObject(), "edit"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + delete window; +} + +void tst_QQuickItem::activeFocusOnTab2() +{ + QQuickView *window = new QQuickView(0); + window->setBaseSize(QSize(800,600)); + + window->setSource(testFileUrl("activeFocusOnTab.qml")); + window->show(); + window->requestActivate(); + QVERIFY(QTest::qWaitForWindowActive(window)); + QVERIFY(QGuiApplication::focusWindow() == window); + + // original: button12 + QQuickItem *item = findItem(window->rootObject(), "button12"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // BackTab: button12->button11 + QKeyEvent key(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1); + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + + item = findItem(window->rootObject(), "button11"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // BackTab: button11->edit + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1); + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + + item = findItem(window->rootObject(), "edit"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + delete window; +} + +void tst_QQuickItem::activeFocusOnTab3() +{ + QQuickView *window = new QQuickView(0); + window->setBaseSize(QSize(800,600)); + + window->setSource(testFileUrl("activeFocusOnTab3.qml")); + window->show(); + window->requestActivate(); + QVERIFY(QTest::qWaitForWindowActive(window)); + QVERIFY(QGuiApplication::focusWindow() == window); + + // original: button1 + QQuickItem *item = findItem(window->rootObject(), "button1"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // 4 Tabs: button1->button2, through a repeater + QKeyEvent key(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1);; + for (int i = 0; i < 4; ++i) { + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + } + + item = findItem(window->rootObject(), "button2"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // 4 Tabs: button2->button3, through a row + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1);; + for (int i = 0; i < 4; ++i) { + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + } + + item = findItem(window->rootObject(), "button3"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // 4 Tabs: button3->button4, through a flow + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1);; + for (int i = 0; i < 4; ++i) { + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + } + + item = findItem(window->rootObject(), "button4"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // 4 Tabs: button4->button5, through a focusscope + // parent is activeFocusOnTab:false, one of children is focus:true + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1);; + for (int i = 0; i < 4; ++i) { + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + } + + item = findItem(window->rootObject(), "button5"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // 4 Tabs: button5->button6, through a focusscope + // parent is activeFocusOnTab:true, one of children is focus:true + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1);; + for (int i = 0; i < 4; ++i) { + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + } + + item = findItem(window->rootObject(), "button6"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // 5 Tabs: button6->button7, through a focusscope + // parent is activeFocusOnTab:true, none of children is focus:true + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1);; + for (int i = 0; i < 5; ++i) { + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + } + + item = findItem(window->rootObject(), "button7"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // 4 BackTabs: button7->button6, through a focusscope + // parent is activeFocusOnTab:true, one of children got focus:true in previous code + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1); + for (int i = 0; i < 4; ++i) { + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + } + + item = findItem(window->rootObject(), "button6"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // 4 Tabs: button6->button7, through a focusscope + // parent is activeFocusOnTab:true, one of children got focus:true in previous code + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier, "", false, 1);; + for (int i = 0; i < 4; ++i) { + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + } + + item = findItem(window->rootObject(), "button7"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // 4 BackTabs: button7->button6, through a focusscope + // parent is activeFocusOnTab:true, one of children got focus:true in previous code + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1); + for (int i = 0; i < 4; ++i) { + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + } + + item = findItem(window->rootObject(), "button6"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // 4 BackTabs: button6->button5, through a focusscope(parent is activeFocusOnTab: false) + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1); + for (int i = 0; i < 4; ++i) { + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + } + + item = findItem(window->rootObject(), "button5"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // 4 BackTabs: button5->button4, through a focusscope(parent is activeFocusOnTab: false) + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1); + for (int i = 0; i < 4; ++i) { + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + } + + item = findItem(window->rootObject(), "button4"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // 4 BackTabs: button4->button3, through a flow + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1); + for (int i = 0; i < 4; ++i) { + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + } + + item = findItem(window->rootObject(), "button3"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // 4 BackTabs: button3->button2, through a row + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1); + for (int i = 0; i < 4; ++i) { + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + } + + item = findItem(window->rootObject(), "button2"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + // 4 BackTabs: button2->button1, through a repeater + key = QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::ShiftModifier, "", false, 1); + for (int i = 0; i < 4; ++i) { + QGuiApplication::sendEvent(window, &key); + QVERIFY(key.isAccepted()); + } + + item = findItem(window->rootObject(), "button1"); + QVERIFY(item); + QVERIFY(item->hasActiveFocus()); + + delete window; +} + void tst_QQuickItem::keys() { QQuickView *window = new QQuickView(0); -- cgit v1.2.3 From 7c7207b4722d5da981bb53b6c68ecd42338e01e1 Mon Sep 17 00:00:00 2001 From: Gunnar Sletta Date: Fri, 8 Mar 2013 19:45:07 +0100 Subject: Create the sg render thread's opengl context on the GUI thread MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I710d80ebb4e5a12fda1f58f54089b0e72268dfcf Reviewed-by: Samuel Rødal --- src/quick/scenegraph/qsgthreadedrenderloop.cpp | 46 +++++++------------------- 1 file changed, 12 insertions(+), 34 deletions(-) diff --git a/src/quick/scenegraph/qsgthreadedrenderloop.cpp b/src/quick/scenegraph/qsgthreadedrenderloop.cpp index fb26f543c3..55b27dc151 100644 --- a/src/quick/scenegraph/qsgthreadedrenderloop.cpp +++ b/src/quick/scenegraph/qsgthreadedrenderloop.cpp @@ -523,37 +523,6 @@ void QSGRenderThread::invalidateOpenGL(QQuickWindow *window, bool inDestructor) } } -void QSGRenderThread::initializeOpenGL() -{ - RLDEBUG1(" Render: initializeOpenGL()"); - QWindow *win = m_windows.at(0).window; - bool temp = false; - - // Workaround for broken expose logic... We should not get an - // expose when the size of a window is invalid, but we sometimes do. - // On Mac this leads to harmless, yet annoying, console warnings - if (m_windows.at(0).size.isEmpty()) { - temp = true; - win = new QWindow(); - win->setFormat(m_windows.at(0).window->requestedFormat()); - win->setSurfaceType(QWindow::OpenGLSurface); - win->setGeometry(0, 0, 64, 64); - win->create(); - } - - gl = new QOpenGLContext(); - // Pick up the surface format from one of them - gl->setFormat(win->requestedFormat()); - gl->create(); - if (!gl->makeCurrent(win)) - qWarning("QQuickWindow: makeCurrent() failed..."); - sg->initialize(gl); - - if (temp) { - delete win; - } -} - /*! Enters the mutex lock to make sure GUI is blocking and performs sync, then wakes GUI. @@ -692,10 +661,10 @@ void QSGRenderThread::run() while (!shouldExit) { if (m_windows.size() > 0) { - if (!gl) - initializeOpenGL(); - if (!sg->isReady()) + if (!sg->isReady()) { + gl->makeCurrent(m_windows.at(0).window); sg->initialize(gl); + } syncAndRender(); } @@ -879,6 +848,15 @@ void QSGThreadedRenderLoop::handleExposure(QQuickWindow *window) m_thread->shouldExit = false; RLDEBUG1("GUI: - starting render thread..."); + + if (!m_thread->gl) { + QOpenGLContext *ctx = new QOpenGLContext(); + ctx->setFormat(window->requestedFormat()); + ctx->create(); + ctx->moveToThread(m_thread); + m_thread->gl = ctx; + } + m_thread->start(); } else { -- cgit v1.2.3 From 687603406a1f548eec57b031aafe5bb31eaae278 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Mon, 11 Mar 2013 11:05:44 +0100 Subject: correct misspelling Canceled Change-Id: Ie080e5be4af0cf60cab56e906f82fb6e7b32c6e3 Reviewed-by: Jens Bache-Wiig --- src/imports/dialogs/qquickplatformfiledialog.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/imports/dialogs/qquickplatformfiledialog.cpp b/src/imports/dialogs/qquickplatformfiledialog.cpp index f2bd279d7b..fbc385f61d 100644 --- a/src/imports/dialogs/qquickplatformfiledialog.cpp +++ b/src/imports/dialogs/qquickplatformfiledialog.cpp @@ -77,7 +77,7 @@ QT_BEGIN_NAMESPACE Qt.quit() } onRejected: { - console.log("Cancelled") + console.log("Canceled") Qt.quit() } Component.onCompleted: visible = true -- cgit v1.2.3 From 74878ba417e7db9fe8f68a55d69a3cc7ee804422 Mon Sep 17 00:00:00 2001 From: Peter Varga Date: Fri, 1 Mar 2013 14:39:59 +0100 Subject: Use official V8 string manipulation methods in QML The QtJSBackend's String::GetCharacter and String::Equals methods are Qt specific and they are not supported by official V8. These methods can be replaced by more "V8-friendly" implementations in QtDeclarative. Thus the mentioned methods can be removed from QtJSBackend to reduce the difference between QtJSBackend and the official V8 source. Change-Id: I5590ca62dc667e64a7f54a7e47a02d350ba0c077 Reviewed-by: Lars Knoll --- src/qml/qml/ftw/qhashedstring_p.h | 10 +++++++--- src/qml/qml/qqmllocale.cpp | 7 +++++-- src/qml/qml/v8/qv8engine_p.h | 6 ++++-- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/qml/qml/ftw/qhashedstring_p.h b/src/qml/qml/ftw/qhashedstring_p.h index 5fc8443ecd..cdf0717e3a 100644 --- a/src/qml/qml/ftw/qhashedstring_p.h +++ b/src/qml/qml/ftw/qhashedstring_p.h @@ -262,8 +262,9 @@ public: inline uint16_t *utf16Data() const { return (uint16_t *)strData->data(); } inline bool equals(v8::Handle string) const { - return isQString()?string->Equals(utf16Data(), length): - string->Equals(cStrData(), length); + v8::Local data = isQString() ? v8::String::New(utf16Data(), length) + : v8::String::New(cStrData(), length); + return string->Equals(data); } inline bool symbolEquals(const QHashedV8String &string) const { @@ -1183,8 +1184,11 @@ QString QHashedV8String::toString() const QString result; result.reserve(m_hash.length); + v8::String::Value value(m_string); + Q_ASSERT(*value != NULL); + uint16_t* string = *value; for (int i = 0; i < m_hash.length; ++i) - result.append(m_string->GetCharacter(i)); + result.append(string[i]); return result; } diff --git a/src/qml/qml/qqmllocale.cpp b/src/qml/qml/qqmllocale.cpp index e1ac75f5f0..c9ce4773c3 100644 --- a/src/qml/qml/qqmllocale.cpp +++ b/src/qml/qml/qqmllocale.cpp @@ -464,8 +464,11 @@ v8::Handle QQmlNumberExtension::toLocaleString(const v8::Arguments& a if (!args[1]->IsString()) V8THROW_ERROR("Locale: Number.toLocaleString(): Invalid arguments"); v8::Local fs = args[1]->ToString(); - if (!fs.IsEmpty() && fs->Length()) - format = fs->GetCharacter(0); + if (!fs.IsEmpty() && fs->Length()) { + v8::String::Value value(fs); + Q_ASSERT(*value != NULL); + format = **value; + } } int prec = 2; if (args.Length() > 2) { diff --git a/src/qml/qml/v8/qv8engine_p.h b/src/qml/qml/v8/qv8engine_p.h index 090ffaa353..5ae0963178 100644 --- a/src/qml/qml/v8/qv8engine_p.h +++ b/src/qml/qml/v8/qv8engine_p.h @@ -629,8 +629,10 @@ v8::Handle QV8Engine::bindingFlagKey() const // unqualified name in QV8ContextWrapper. bool QV8Engine::startsWithUpper(v8::Handle string) { - uint16_t c = string->GetCharacter(0); - return (c >= 'A' && c <= 'Z') || + v8::String::Value value(string); + Q_ASSERT(*value != NULL); + uint16_t c = **value; + return (c >= 'A' && c <= 'Z') || (c > 127 && QChar::category(c) == QChar::Letter_Uppercase); } -- cgit v1.2.3 From f9bf67af85728f5207b02a3f99d950af83020bc3 Mon Sep 17 00:00:00 2001 From: Michael Brasser Date: Mon, 4 Mar 2013 20:20:15 -0600 Subject: Introduce QML_FBO_FLUSH_BEFORE_DETACH to work around FBO issue. On some AMD hardware, detaching the depth attachment immediately after rendering to an FBO will cause rendering issues. Adding an explicit glFlush before detaching seems to work around the issue. Task-number: QTBUG-29265 Change-Id: I97c7b87c1c3d3a69a4d6228dd9459745710c5f4c Reviewed-by: Gunnar Sletta --- src/quick/items/qquickshadereffectsource.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/quick/items/qquickshadereffectsource.cpp b/src/quick/items/qquickshadereffectsource.cpp index bdfef7ca88..cab3692f5a 100644 --- a/src/quick/items/qquickshadereffectsource.cpp +++ b/src/quick/items/qquickshadereffectsource.cpp @@ -53,6 +53,7 @@ QT_BEGIN_NAMESPACE DEFINE_BOOL_CONFIG_OPTION(qmlFboOverlay, QML_FBO_OVERLAY) +DEFINE_BOOL_CONFIG_OPTION(qmlFboFlushBeforeDetach, QML_FBO_FLUSH_BEFORE_DETACH) namespace { @@ -75,6 +76,8 @@ namespace BindableFbo::~BindableFbo() { + if (qmlFboFlushBeforeDetach()) + glFlush(); if (m_depthStencil) m_depthStencil->detach(); } -- cgit v1.2.3 From 856bb2908e42e874e7340f414e9decbe972f8793 Mon Sep 17 00:00:00 2001 From: Yoann Lopes Date: Fri, 8 Mar 2013 14:28:21 +0100 Subject: Fixed incorrect vsync delta time in QSGThreadedRenderLoop. Change-Id: I32bb720e9c9aa0278959dd64e5e1c449bdace7d1 Reviewed-by: Gunnar Sletta --- src/quick/scenegraph/qsgthreadedrenderloop.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/quick/scenegraph/qsgthreadedrenderloop.cpp b/src/quick/scenegraph/qsgthreadedrenderloop.cpp index 55b27dc151..95409a04f7 100644 --- a/src/quick/scenegraph/qsgthreadedrenderloop.cpp +++ b/src/quick/scenegraph/qsgthreadedrenderloop.cpp @@ -296,7 +296,7 @@ public: , stopEventProcessing(false) { sg->moveToThread(this); - vsyncDelta = QGuiApplication::primaryScreen()->refreshRate(); + vsyncDelta = qsgrl_animation_interval(); } -- cgit v1.2.3 From fd4a811af84331813b2264229155d32885d4463e Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Tue, 12 Mar 2013 13:38:11 +0100 Subject: Disable accessibility temporarily. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is required to update headers in QtBase. Change-Id: I373a1f13dcde8ce1cafeedb5cd2269a0f8f3a670 Reviewed-by: Jan Arve Sæther --- src/plugins/plugins.pro | 6 +++--- tests/auto/quick/quick.pro | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/plugins/plugins.pro b/src/plugins/plugins.pro index 9ef8c7ab72..db5bbad7f8 100644 --- a/src/plugins/plugins.pro +++ b/src/plugins/plugins.pro @@ -1,5 +1,5 @@ TEMPLATE = subdirs SUBDIRS += qmltooling -contains(QT_CONFIG, accessibility) { - SUBDIRS += accessible -} +#contains(QT_CONFIG, accessibility) { +# SUBDIRS += accessible +#} diff --git a/tests/auto/quick/quick.pro b/tests/auto/quick/quick.pro index 81d4fd3348..6cb091b04a 100644 --- a/tests/auto/quick/quick.pro +++ b/tests/auto/quick/quick.pro @@ -31,7 +31,7 @@ PRIVATETESTS += \ !qtHaveModule(xmlpatterns): PRIVATETESTS -= qquickxmllistmodel QUICKTESTS = \ - qquickaccessible \ +# qquickaccessible \ qquickanchors \ qquickanimatedimage \ qquickanimatedsprite \ -- cgit v1.2.3 From 0de74e919182122b2dde62d179685bb1e3cb05bc Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Thu, 7 Mar 2013 15:47:27 +0100 Subject: Fix QString usage in createPixmapDataSync(). MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Warning introduced by 707bbe5dea9d7398b205124a54422f2fafb6f151 . Change-Id: I20f0da00ea519cc2ec82a1d13f1887c099a3947c Reviewed-by: Morten Johan Sørvig --- src/quick/util/qquickpixmapcache.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/quick/util/qquickpixmapcache.cpp b/src/quick/util/qquickpixmapcache.cpp index d5ef4b767f..c5968c2bc1 100644 --- a/src/quick/util/qquickpixmapcache.cpp +++ b/src/quick/util/qquickpixmapcache.cpp @@ -1022,10 +1022,10 @@ static QQuickPixmapData* createPixmapDataSync(QQuickPixmap *declarativePixmap, Q // check for "retina" high-dpi and use @2x file if it exixts if (qApp->devicePixelRatio() > 1) { - int dotIndex = localFile.lastIndexOf(QStringLiteral(".")); + const int dotIndex = localFile.lastIndexOf(QLatin1Char('.')); if (dotIndex != -1) { QString retinaFile = localFile; - retinaFile.insert(dotIndex, "@2x"); + retinaFile.insert(dotIndex, QStringLiteral("@2x")); if (QFile(retinaFile).exists()) localFile = retinaFile; } -- cgit v1.2.3 From c460a83e26c6ade557dac9cf30df6a95cb1aadd2 Mon Sep 17 00:00:00 2001 From: Bernd Weimer Date: Thu, 7 Mar 2013 16:07:35 +0100 Subject: Fixed some auto tests In the listmodel test an error description was not converted to a string. In the pathview test, number literals are always interpreted as double, but qreal can also be float, which lead to failing tests. Constructor usage prevents "expected data of type 'float', got 'double'". Change-Id: I0c760486de0ba4df6a5e9a33e9528ca7d7a1a63e Reviewed-by: Alan Alpert --- tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp | 2 +- .../quick/qquickpathview/tst_qquickpathview.cpp | 104 ++++++++++----------- 2 files changed, 53 insertions(+), 53 deletions(-) diff --git a/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp b/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp index 22229febf3..eec312ec4f 100644 --- a/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp +++ b/tests/auto/qml/qqmllistmodel/tst_qqmllistmodel.cpp @@ -973,7 +973,7 @@ void tst_qqmllistmodel::property_changes() expr.setExpression(script_change); expr.evaluate(); - QVERIFY2(!expr.hasError(), QTest::toString(expr.error())); + QVERIFY2(!expr.hasError(), QTest::toString(expr.error().toString())); // test the object returned by get() emits the correct signals QCOMPARE(connectionsObject->property("gotSignal").toBool(), itemsChanged); diff --git a/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp b/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp index f52939e628..e805a6b074 100644 --- a/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp +++ b/tests/auto/quick/qquickpathview/tst_qquickpathview.cpp @@ -310,34 +310,34 @@ void tst_QQuickPathView::insertModel_data() // We have 8 items, with currentIndex == 4 QTest::newRow("insert after current") - << int(QQuickPathView::StrictlyEnforceRange) << 6 << 1 << 5. << 4; + << int(QQuickPathView::StrictlyEnforceRange) << 6 << 1 << qreal(5.) << 4; QTest::newRow("insert before current") - << int(QQuickPathView::StrictlyEnforceRange) << 2 << 1 << 4. << 5; + << int(QQuickPathView::StrictlyEnforceRange) << 2 << 1 << qreal(4.)<< 5; QTest::newRow("insert multiple after current") - << int(QQuickPathView::StrictlyEnforceRange) << 5 << 2 << 6. << 4; + << int(QQuickPathView::StrictlyEnforceRange) << 5 << 2 << qreal(6.) << 4; QTest::newRow("insert multiple before current") - << int(QQuickPathView::StrictlyEnforceRange) << 1 << 2 << 4. << 6; + << int(QQuickPathView::StrictlyEnforceRange) << 1 << 2 << qreal(4.) << 6; QTest::newRow("insert at end") - << int(QQuickPathView::StrictlyEnforceRange) << 8 << 1 << 5. << 4; + << int(QQuickPathView::StrictlyEnforceRange) << 8 << 1 << qreal(5.) << 4; QTest::newRow("insert at beginning") - << int(QQuickPathView::StrictlyEnforceRange) << 0 << 1 << 4. << 5; + << int(QQuickPathView::StrictlyEnforceRange) << 0 << 1 << qreal(4.) << 5; QTest::newRow("insert at current") - << int(QQuickPathView::StrictlyEnforceRange) << 4 << 1 << 4. << 5; + << int(QQuickPathView::StrictlyEnforceRange) << 4 << 1 << qreal(4.) << 5; QTest::newRow("no range - insert after current") - << int(QQuickPathView::NoHighlightRange) << 6 << 1 << 5. << 4; + << int(QQuickPathView::NoHighlightRange) << 6 << 1 << qreal(5.) << 4; QTest::newRow("no range - insert before current") - << int(QQuickPathView::NoHighlightRange) << 2 << 1 << 4. << 5; + << int(QQuickPathView::NoHighlightRange) << 2 << 1 << qreal(4.) << 5; QTest::newRow("no range - insert multiple after current") - << int(QQuickPathView::NoHighlightRange) << 5 << 2 << 6. << 4; + << int(QQuickPathView::NoHighlightRange) << 5 << 2 << qreal(6.) << 4; QTest::newRow("no range - insert multiple before current") - << int(QQuickPathView::NoHighlightRange) << 1 << 2 << 4. << 6; + << int(QQuickPathView::NoHighlightRange) << 1 << 2 << qreal(4.) << 6; QTest::newRow("no range - insert at end") - << int(QQuickPathView::NoHighlightRange) << 8 << 1 << 5. << 4; + << int(QQuickPathView::NoHighlightRange) << 8 << 1 << qreal(5.) << 4; QTest::newRow("no range - insert at beginning") - << int(QQuickPathView::NoHighlightRange) << 0 << 1 << 4. << 5; + << int(QQuickPathView::NoHighlightRange) << 0 << 1 << qreal(4.) << 5; QTest::newRow("no range - insert at current") - << int(QQuickPathView::NoHighlightRange) << 4 << 1 << 4. << 5; + << int(QQuickPathView::NoHighlightRange) << 4 << 1 << qreal(4.) << 5; } void tst_QQuickPathView::insertModel() @@ -398,38 +398,38 @@ void tst_QQuickPathView::removeModel_data() // We have 8 items, with currentIndex == 4 QTest::newRow("remove after current") - << int(QQuickPathView::StrictlyEnforceRange) << 6 << 1 << 3. << 4; + << int(QQuickPathView::StrictlyEnforceRange) << 6 << 1 << qreal(3.) << 4; QTest::newRow("remove before current") - << int(QQuickPathView::StrictlyEnforceRange) << 2 << 1 << 4. << 3; + << int(QQuickPathView::StrictlyEnforceRange) << 2 << 1 << qreal(4.) << 3; QTest::newRow("remove multiple after current") - << int(QQuickPathView::StrictlyEnforceRange) << 5 << 2 << 2. << 4; + << int(QQuickPathView::StrictlyEnforceRange) << 5 << 2 << qreal(2.) << 4; QTest::newRow("remove multiple before current") - << int(QQuickPathView::StrictlyEnforceRange) << 1 << 2 << 4. << 2; + << int(QQuickPathView::StrictlyEnforceRange) << 1 << 2 << qreal(4.) << 2; QTest::newRow("remove last") - << int(QQuickPathView::StrictlyEnforceRange) << 7 << 1 << 3. << 4; + << int(QQuickPathView::StrictlyEnforceRange) << 7 << 1 << qreal(3.) << 4; QTest::newRow("remove first") - << int(QQuickPathView::StrictlyEnforceRange) << 0 << 1 << 4. << 3; + << int(QQuickPathView::StrictlyEnforceRange) << 0 << 1 << qreal(4.) << 3; QTest::newRow("remove current") - << int(QQuickPathView::StrictlyEnforceRange) << 4 << 1 << 3. << 4; + << int(QQuickPathView::StrictlyEnforceRange) << 4 << 1 << qreal(3.) << 4; QTest::newRow("remove all") - << int(QQuickPathView::StrictlyEnforceRange) << 0 << 8 << 0. << 0; + << int(QQuickPathView::StrictlyEnforceRange) << 0 << 8 << qreal(0.) << 0; QTest::newRow("no range - remove after current") - << int(QQuickPathView::NoHighlightRange) << 6 << 1 << 3. << 4; + << int(QQuickPathView::NoHighlightRange) << 6 << 1 << qreal(3.) << 4; QTest::newRow("no range - remove before current") - << int(QQuickPathView::NoHighlightRange) << 2 << 1 << 4. << 3; + << int(QQuickPathView::NoHighlightRange) << 2 << 1 << qreal(4.) << 3; QTest::newRow("no range - remove multiple after current") - << int(QQuickPathView::NoHighlightRange) << 5 << 2 << 2. << 4; + << int(QQuickPathView::NoHighlightRange) << 5 << 2 << qreal(2.) << 4; QTest::newRow("no range - remove multiple before current") - << int(QQuickPathView::NoHighlightRange) << 1 << 2 << 4. << 2; + << int(QQuickPathView::NoHighlightRange) << 1 << 2 << qreal(4.) << 2; QTest::newRow("no range - remove last") - << int(QQuickPathView::NoHighlightRange) << 7 << 1 << 3. << 4; + << int(QQuickPathView::NoHighlightRange) << 7 << 1 << qreal(3.) << 4; QTest::newRow("no range - remove first") - << int(QQuickPathView::NoHighlightRange) << 0 << 1 << 4. << 3; + << int(QQuickPathView::NoHighlightRange) << 0 << 1 << qreal(4.) << 3; QTest::newRow("no range - remove current offset") - << int(QQuickPathView::NoHighlightRange) << 4 << 1 << 4. << 4; + << int(QQuickPathView::NoHighlightRange) << 4 << 1 << qreal(4.) << 4; QTest::newRow("no range - remove all") - << int(QQuickPathView::NoHighlightRange) << 0 << 8 << 0. << 0; + << int(QQuickPathView::NoHighlightRange) << 0 << 8 << qreal(0.) << 0; } void tst_QQuickPathView::removeModel() @@ -489,40 +489,40 @@ void tst_QQuickPathView::moveModel_data() // We have 8 items, with currentIndex == 4 QTest::newRow("move after current") - << int(QQuickPathView::StrictlyEnforceRange) << 5 << 6 << 1 << 4. << 4; + << int(QQuickPathView::StrictlyEnforceRange) << 5 << 6 << 1 << qreal(4.) << 4; QTest::newRow("move before current") - << int(QQuickPathView::StrictlyEnforceRange) << 2 << 3 << 1 << 4. << 4; + << int(QQuickPathView::StrictlyEnforceRange) << 2 << 3 << 1 << qreal(4.) << 4; QTest::newRow("move before current to after") - << int(QQuickPathView::StrictlyEnforceRange) << 2 << 6 << 1 << 5. << 3; + << int(QQuickPathView::StrictlyEnforceRange) << 2 << 6 << 1 << qreal(5.) << 3; QTest::newRow("move multiple after current") - << int(QQuickPathView::StrictlyEnforceRange) << 5 << 6 << 2 << 4. << 4; + << int(QQuickPathView::StrictlyEnforceRange) << 5 << 6 << 2 << qreal(4.) << 4; QTest::newRow("move multiple before current") - << int(QQuickPathView::StrictlyEnforceRange) << 0 << 1 << 2 << 4. << 4; + << int(QQuickPathView::StrictlyEnforceRange) << 0 << 1 << 2 << qreal(4.) << 4; QTest::newRow("move before current to end") - << int(QQuickPathView::StrictlyEnforceRange) << 2 << 7 << 1 << 5. << 3; + << int(QQuickPathView::StrictlyEnforceRange) << 2 << 7 << 1 << qreal(5.) << 3; QTest::newRow("move last to beginning") - << int(QQuickPathView::StrictlyEnforceRange) << 7 << 0 << 1 << 3. << 5; + << int(QQuickPathView::StrictlyEnforceRange) << 7 << 0 << 1 << qreal(3.) << 5; QTest::newRow("move current") - << int(QQuickPathView::StrictlyEnforceRange) << 4 << 6 << 1 << 2. << 6; + << int(QQuickPathView::StrictlyEnforceRange) << 4 << 6 << 1 << qreal(2.) << 6; QTest::newRow("no range - move after current") - << int(QQuickPathView::NoHighlightRange) << 5 << 6 << 1 << 4. << 4; + << int(QQuickPathView::NoHighlightRange) << 5 << 6 << 1 << qreal(4.) << 4; QTest::newRow("no range - move before current") - << int(QQuickPathView::NoHighlightRange) << 2 << 3 << 1 << 4. << 4; + << int(QQuickPathView::NoHighlightRange) << 2 << 3 << 1 << qreal(4.) << 4; QTest::newRow("no range - move before current to after") - << int(QQuickPathView::NoHighlightRange) << 2 << 6 << 1 << 5. << 3; + << int(QQuickPathView::NoHighlightRange) << 2 << 6 << 1 << qreal(5.) << 3; QTest::newRow("no range - move multiple after current") - << int(QQuickPathView::NoHighlightRange) << 5 << 6 << 2 << 4. << 4; + << int(QQuickPathView::NoHighlightRange) << 5 << 6 << 2 << qreal(4.) << 4; QTest::newRow("no range - move multiple before current") - << int(QQuickPathView::NoHighlightRange) << 0 << 1 << 2 << 4. << 4; + << int(QQuickPathView::NoHighlightRange) << 0 << 1 << 2 << qreal(4.) << 4; QTest::newRow("no range - move before current to end") - << int(QQuickPathView::NoHighlightRange) << 2 << 7 << 1 << 5. << 3; + << int(QQuickPathView::NoHighlightRange) << 2 << 7 << 1 << qreal(5.) << 3; QTest::newRow("no range - move last to beginning") - << int(QQuickPathView::NoHighlightRange) << 7 << 0 << 1 << 3. << 5; + << int(QQuickPathView::NoHighlightRange) << 7 << 0 << 1 << qreal(3.) << 5; QTest::newRow("no range - move current") - << int(QQuickPathView::NoHighlightRange) << 4 << 6 << 1 << 4. << 6; + << int(QQuickPathView::NoHighlightRange) << 4 << 6 << 1 << qreal(4.) << 6; QTest::newRow("no range - move multiple incl. current") - << int(QQuickPathView::NoHighlightRange) << 0 << 1 << 5 << 4. << 5; + << int(QQuickPathView::NoHighlightRange) << 0 << 1 << 5 << qreal(4.) << 5; } void tst_QQuickPathView::moveModel() @@ -1998,11 +1998,11 @@ void tst_QQuickPathView::indexAt_itemAt_data() QTest::addColumn("y"); QTest::addColumn("index"); - QTest::newRow("Item 0 - 585, 95") << 585. << 95. << 0; - QTest::newRow("Item 0 - 660, 165") << 660. << 165. << 0; - QTest::newRow("No Item a - 580, 95") << 580. << 95. << -1; - QTest::newRow("No Item b - 585, 85") << 585. << 85. << -1; - QTest::newRow("Item 7 - 360, 200") << 360. << 200. << 7; + QTest::newRow("Item 0 - 585, 95") << qreal(585.) << qreal(95.) << 0; + QTest::newRow("Item 0 - 660, 165") << qreal(660.) << qreal(165.) << 0; + QTest::newRow("No Item a - 580, 95") << qreal(580.) << qreal(95.) << -1; + QTest::newRow("No Item b - 585, 85") << qreal(585.) << qreal(85.) << -1; + QTest::newRow("Item 7 - 360, 200") << qreal(360.) << qreal(200.) << 7; } void tst_QQuickPathView::cacheItemCount() -- cgit v1.2.3 From fa8a4beb157f82e02cf8471ade7da0faee7a9fa1 Mon Sep 17 00:00:00 2001 From: Alan Alpert Date: Tue, 26 Feb 2013 13:50:51 -0800 Subject: Fix remote image loading for AnimatedSprite The Sprite generated behind the scenes had no QmlEngine associated, and the engine is needed by QQuickPixmap for async loading. Task-number: QTBUG-28086 Change-Id: Ibf3b03c54b339fe8f44201dc6fcb507e5274bbec Reviewed-by: Gunnar Sletta --- src/quick/items/qquickanimatedsprite.cpp | 4 ++-- src/quick/items/qquicksprite.cpp | 11 +++++++++-- src/quick/items/qquickspriteengine.cpp | 1 + 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/quick/items/qquickanimatedsprite.cpp b/src/quick/items/qquickanimatedsprite.cpp index 343cac5d5c..f09e9bb90e 100644 --- a/src/quick/items/qquickanimatedsprite.cpp +++ b/src/quick/items/qquickanimatedsprite.cpp @@ -365,7 +365,7 @@ QQuickAnimatedSprite::QQuickAnimatedSprite(QQuickItem *parent) : QQuickItem(parent) , m_node(0) , m_material(0) - , m_sprite(new QQuickSprite) + , m_sprite(new QQuickSprite(this)) , m_spriteEngine(0) , m_curFrame(0) , m_pleaseReset(false) @@ -540,7 +540,7 @@ QSGGeometryNode* QQuickAnimatedSprite::buildNode() m_material = new QQuickAnimatedSpriteMaterial(); - QImage image = m_spriteEngine->assembledImage(); + QImage image = m_spriteEngine->assembledImage(); //Engine prints errors if there are any if (image.isNull()) return 0; m_sheetSize = QSizeF(image.size()); diff --git a/src/quick/items/qquicksprite.cpp b/src/quick/items/qquicksprite.cpp index 13593384d8..b4138308f6 100644 --- a/src/quick/items/qquicksprite.cpp +++ b/src/quick/items/qquicksprite.cpp @@ -254,8 +254,15 @@ int QQuickSprite::variedDuration() const //Deals with precedence when multiple d void QQuickSprite::startImageLoading() { m_pix.clear(this); - if (!m_source.isEmpty()) - m_pix.load(qmlEngine(this), m_source); + if (!m_source.isEmpty()) { + QQmlEngine *e = qmlEngine(this); + if (!e) { //If not created in QML, you must set the QObject parent to the QML element so this can work + e = qmlEngine(parent()); + if (!e) + qWarning() << "QQuickSprite: Cannot find QQmlEngine - this class is only for use in QML and may not work"; + } + m_pix.load(e, m_source); + } } QT_END_NAMESPACE diff --git a/src/quick/items/qquickspriteengine.cpp b/src/quick/items/qquickspriteengine.cpp index 4c7be3bce9..aa93d31cf7 100644 --- a/src/quick/items/qquickspriteengine.cpp +++ b/src/quick/items/qquickspriteengine.cpp @@ -328,6 +328,7 @@ QQuickPixmap::Status QQuickSpriteEngine::status()//Composed status of all Sprite null = loading = ready = 0; foreach (QQuickSprite* s, m_sprites) { switch (s->m_pix.status()) { + // ### Maybe add an error message here, because this null shouldn't be reached but when it does, the image fails without an error message. case QQuickPixmap::Null : null++; break; case QQuickPixmap::Loading : loading++; break; case QQuickPixmap::Error : return QQuickPixmap::Error; -- cgit v1.2.3 From 700910d938c75c978f765ec8438f81e70676999a Mon Sep 17 00:00:00 2001 From: Alan Alpert Date: Wed, 23 Jan 2013 14:08:58 -0800 Subject: Add QtQml.Models module The moved Model classes can now be exposed in a QtQml import. To keep the QtQml import resticted to more core functionality, they are being exposed in a plugin module. Change-Id: I0a84642a72c7c9bbf9b6ffd2a6c33549f8e61c29 Reviewed-by: Alan Alpert Reviewed-by: Lars Knoll --- .../photoviewer/PhotoViewerCore/AlbumDelegate.qml | 3 +- examples/quick/demos/photoviewer/photoviewer.qml | 3 +- examples/quick/demos/stocqt/stocqt.qml | 3 +- .../quick/draganddrop/doc/src/draganddrop.qdoc | 2 +- examples/quick/draganddrop/views/gridview.qml | 5 +- examples/quick/shadereffects/shadereffects.qml | 3 +- .../dynamicview/dynamicview3/dynamicview.qml | 7 +- .../dynamicview/dynamicview4/dynamicview.qml | 7 +- .../tutorials/gettingStartedQml/core/MenuBar.qml | 3 +- .../gettingStartedQml/parts/part2/MenuBar.qml | 3 +- .../gettingStartedQml/parts/part3/MenuBar.qml | 3 +- .../gettingStartedQml/parts/part4/MenuBar.qml | 3 +- .../gettingStartedQml/parts/part5/core/MenuBar.qml | 3 +- examples/quick/views/doc/src/views.qdoc | 4 +- examples/quick/views/objectmodel/objectmodel.qml | 118 ++++++ examples/quick/views/package/view.qml | 3 +- .../quick/views/parallax/content/ParallaxView.qml | 3 +- examples/quick/views/views.qml | 37 +- examples/quick/views/views.qrc | 2 +- .../quick/views/visualdatamodel/dragselection.qml | 19 +- examples/quick/views/visualdatamodel/slideshow.qml | 11 +- .../views/visualitemmodel/visualitemmodel.qml | 117 ------ src/imports/imports.pro | 3 +- src/imports/models/models.pro | 11 + src/imports/models/plugin.cpp | 85 +++++ src/imports/models/qmldir | 2 + src/qml/items/items.pri | 6 +- src/qml/items/qqmldelegatemodel.cpp | 395 +++++++++++---------- src/qml/items/qqmldelegatemodel_p.h | 28 +- src/qml/items/qqmldelegatemodel_p_p.h | 38 +- src/qml/items/qqmlmodelsmodule.cpp | 60 ++++ src/qml/items/qqmlmodelsmodule_p.h | 57 +++ src/qml/items/qqmlobjectmodel.cpp | 40 ++- src/qml/qml/qqmlengine.cpp | 18 +- src/qml/qml/qqmllistmodel.cpp | 21 ++ tests/auto/qml/qml.pro | 1 + tests/auto/qml/qtqmlmodules/data/base.qml | 14 + tests/auto/qml/qtqmlmodules/data/models.qml | 15 + tests/auto/qml/qtqmlmodules/data/unavailable.qml | 35 ++ tests/auto/qml/qtqmlmodules/qtqmlmodules.pro | 12 + tests/auto/qml/qtqmlmodules/tst_qtqmlmodules.cpp | 95 +++++ .../tst_qquickvisualdatamodel.cpp | 104 +++--- 42 files changed, 942 insertions(+), 460 deletions(-) create mode 100644 examples/quick/views/objectmodel/objectmodel.qml delete mode 100644 examples/quick/views/visualitemmodel/visualitemmodel.qml create mode 100644 src/imports/models/models.pro create mode 100644 src/imports/models/plugin.cpp create mode 100644 src/imports/models/qmldir create mode 100644 src/qml/items/qqmlmodelsmodule.cpp create mode 100644 src/qml/items/qqmlmodelsmodule_p.h create mode 100644 tests/auto/qml/qtqmlmodules/data/base.qml create mode 100644 tests/auto/qml/qtqmlmodules/data/models.qml create mode 100644 tests/auto/qml/qtqmlmodules/data/unavailable.qml create mode 100644 tests/auto/qml/qtqmlmodules/qtqmlmodules.pro create mode 100644 tests/auto/qml/qtqmlmodules/tst_qtqmlmodules.cpp diff --git a/examples/quick/demos/photoviewer/PhotoViewerCore/AlbumDelegate.qml b/examples/quick/demos/photoviewer/PhotoViewerCore/AlbumDelegate.qml index 4bd5e33ae3..d94231c9b5 100644 --- a/examples/quick/demos/photoviewer/PhotoViewerCore/AlbumDelegate.qml +++ b/examples/quick/demos/photoviewer/PhotoViewerCore/AlbumDelegate.qml @@ -41,6 +41,7 @@ import QtQuick 2.0 import QtQuick.XmlListModel 2.0 +import QtQml.Models 2.1 Component { id: albumDelegate @@ -69,7 +70,7 @@ Component { Package.name: 'album' id: albumWrapper; width: 210; height: 220 - VisualDataModel { + DelegateModel { id: visualModel; delegate: PhotoDelegate { } model: RssModel { id: rssModel; tags: tag } } diff --git a/examples/quick/demos/photoviewer/photoviewer.qml b/examples/quick/demos/photoviewer/photoviewer.qml index fa7c83f74e..df344accd5 100644 --- a/examples/quick/demos/photoviewer/photoviewer.qml +++ b/examples/quick/demos/photoviewer/photoviewer.qml @@ -40,6 +40,7 @@ ****************************************************************************/ import QtQuick 2.0 +import QtQml.Models 2.1 import "PhotoViewerCore" Rectangle { @@ -58,7 +59,7 @@ Rectangle { ListElement { tag: "Prague" } } - VisualDataModel { id: albumVisualModel; model: photosModel; delegate: AlbumDelegate {} } + DelegateModel { id: albumVisualModel; model: photosModel; delegate: AlbumDelegate {} } GridView { id: albumView; width: parent.width; height: parent.height; cellWidth: 210; cellHeight: 220 diff --git a/examples/quick/demos/stocqt/stocqt.qml b/examples/quick/demos/stocqt/stocqt.qml index ec353737fe..9bcffd972f 100644 --- a/examples/quick/demos/stocqt/stocqt.qml +++ b/examples/quick/demos/stocqt/stocqt.qml @@ -39,6 +39,7 @@ ****************************************************************************/ import QtQuick 2.0 +import QtQml.Models 2.1 import "./content" ListView { @@ -66,7 +67,7 @@ ListView { } } - model: VisualItemModel { + model: ObjectModel { StockListView { id: listView width: root.width diff --git a/examples/quick/draganddrop/doc/src/draganddrop.qdoc b/examples/quick/draganddrop/doc/src/draganddrop.qdoc index 1844c3b4eb..ad32cbcfcd 100644 --- a/examples/quick/draganddrop/doc/src/draganddrop.qdoc +++ b/examples/quick/draganddrop/doc/src/draganddrop.qdoc @@ -49,7 +49,7 @@ \section2 GridView adds drag and drop to a GridView, allowing you to reorder the list. - It uses a VisualDataModel to move a delegate item to the position of another item + It uses a DelegateModel to move a delegate item to the position of another item it is dragged over. \snippet quick/draganddrop/views/gridview.qml 0 diff --git a/examples/quick/draganddrop/views/gridview.qml b/examples/quick/draganddrop/views/gridview.qml index 4df265e8af..f2c9c75e42 100644 --- a/examples/quick/draganddrop/views/gridview.qml +++ b/examples/quick/draganddrop/views/gridview.qml @@ -39,6 +39,7 @@ ****************************************************************************/ import QtQuick 2.0 +import QtQml.Models 2.1 GridView { id: root @@ -50,7 +51,7 @@ GridView { } //! [0] - model: VisualDataModel { + model: DelegateModel { //! [0] id: visualModel model: ListModel { @@ -84,7 +85,7 @@ GridView { delegate: MouseArea { id: delegateRoot - property int visualIndex: VisualDataModel.itemsIndex + property int visualIndex: DelegateModel.itemsIndex width: 80; height: 80 drag.target: icon diff --git a/examples/quick/shadereffects/shadereffects.qml b/examples/quick/shadereffects/shadereffects.qml index 77de356eac..a8f0469b44 100644 --- a/examples/quick/shadereffects/shadereffects.qml +++ b/examples/quick/shadereffects/shadereffects.qml @@ -40,6 +40,7 @@ ****************************************************************************/ import QtQuick 2.0 +import QtQml.Models 2.1 import "content" Rectangle { @@ -85,7 +86,7 @@ Rectangle { height: 140 clip: true snapMode: ListView.SnapOneItem - model: VisualItemModel { + model: ObjectModel { Text { width: 160 height: 140 diff --git a/examples/quick/tutorials/dynamicview/dynamicview3/dynamicview.qml b/examples/quick/tutorials/dynamicview/dynamicview3/dynamicview.qml index 09c69df54d..7f353f075a 100644 --- a/examples/quick/tutorials/dynamicview/dynamicview3/dynamicview.qml +++ b/examples/quick/tutorials/dynamicview/dynamicview3/dynamicview.qml @@ -39,6 +39,7 @@ ****************************************************************************/ import QtQuick 2.0 +import QtQml.Models 2.1 //![0] Rectangle { id: root @@ -111,8 +112,8 @@ Rectangle { onEntered: { visualModel.items.move( - drag.source.VisualDataModel.itemsIndex, - dragArea.VisualDataModel.itemsIndex) + drag.source.DelegateModel.itemsIndex, + dragArea.DelegateModel.itemsIndex) } } //![3] @@ -120,7 +121,7 @@ Rectangle { } //![2] //![4] - VisualDataModel { + DelegateModel { id: visualModel model: PetsModel {} diff --git a/examples/quick/tutorials/dynamicview/dynamicview4/dynamicview.qml b/examples/quick/tutorials/dynamicview/dynamicview4/dynamicview.qml index 6ff3f7eb96..82639c2016 100644 --- a/examples/quick/tutorials/dynamicview/dynamicview4/dynamicview.qml +++ b/examples/quick/tutorials/dynamicview/dynamicview4/dynamicview.qml @@ -39,6 +39,7 @@ ****************************************************************************/ import QtQuick 2.0 +import QtQml.Models 2.1 Rectangle { id: root @@ -112,14 +113,14 @@ Rectangle { onEntered: { visualModel.items.move( - drag.source.VisualDataModel.itemsIndex, - dragArea.VisualDataModel.itemsIndex) + drag.source.DelegateModel.itemsIndex, + dragArea.DelegateModel.itemsIndex) } } } } //![0] - VisualDataModel { + DelegateModel { id: visualModel //![4] property var lessThan: [ diff --git a/examples/quick/tutorials/gettingStartedQml/core/MenuBar.qml b/examples/quick/tutorials/gettingStartedQml/core/MenuBar.qml index c6edcab836..b53fc1b45a 100644 --- a/examples/quick/tutorials/gettingStartedQml/core/MenuBar.qml +++ b/examples/quick/tutorials/gettingStartedQml/core/MenuBar.qml @@ -39,6 +39,7 @@ ****************************************************************************/ import QtQuick 2.0 +import QtQml.Models 2.1 Rectangle { id: menuBar @@ -128,7 +129,7 @@ Rectangle { } } //a list of visual items already have delegates handling their display - VisualItemModel { + ObjectModel { id: menuListModel FileMenu { diff --git a/examples/quick/tutorials/gettingStartedQml/parts/part2/MenuBar.qml b/examples/quick/tutorials/gettingStartedQml/parts/part2/MenuBar.qml index 929052f0ae..63b2a1c190 100644 --- a/examples/quick/tutorials/gettingStartedQml/parts/part2/MenuBar.qml +++ b/examples/quick/tutorials/gettingStartedQml/parts/part2/MenuBar.qml @@ -39,6 +39,7 @@ ****************************************************************************/ import QtQuick 2.0 +import QtQml.Models 2.1 Rectangle { id: menuBar @@ -99,7 +100,7 @@ Rectangle { } //a list of visual items already have delegates handling their display - VisualItemModel{ + ObjectModel{ id: menuListModel FileMenu{ diff --git a/examples/quick/tutorials/gettingStartedQml/parts/part3/MenuBar.qml b/examples/quick/tutorials/gettingStartedQml/parts/part3/MenuBar.qml index 929052f0ae..63b2a1c190 100644 --- a/examples/quick/tutorials/gettingStartedQml/parts/part3/MenuBar.qml +++ b/examples/quick/tutorials/gettingStartedQml/parts/part3/MenuBar.qml @@ -39,6 +39,7 @@ ****************************************************************************/ import QtQuick 2.0 +import QtQml.Models 2.1 Rectangle { id: menuBar @@ -99,7 +100,7 @@ Rectangle { } //a list of visual items already have delegates handling their display - VisualItemModel{ + ObjectModel{ id: menuListModel FileMenu{ diff --git a/examples/quick/tutorials/gettingStartedQml/parts/part4/MenuBar.qml b/examples/quick/tutorials/gettingStartedQml/parts/part4/MenuBar.qml index 39b74e9d49..7bb1d2d30e 100644 --- a/examples/quick/tutorials/gettingStartedQml/parts/part4/MenuBar.qml +++ b/examples/quick/tutorials/gettingStartedQml/parts/part4/MenuBar.qml @@ -39,6 +39,7 @@ ****************************************************************************/ import QtQuick 2.0 +import QtQml.Models 2.1 Rectangle { id: menuBar @@ -105,7 +106,7 @@ Rectangle { } //a list of visual items already have delegates handling their display - VisualItemModel{ + ObjectModel{ id: menuListModel FileMenu{ diff --git a/examples/quick/tutorials/gettingStartedQml/parts/part5/core/MenuBar.qml b/examples/quick/tutorials/gettingStartedQml/parts/part5/core/MenuBar.qml index 7ffa90c8f4..3600be7868 100644 --- a/examples/quick/tutorials/gettingStartedQml/parts/part5/core/MenuBar.qml +++ b/examples/quick/tutorials/gettingStartedQml/parts/part5/core/MenuBar.qml @@ -39,6 +39,7 @@ ****************************************************************************/ import QtQuick 2.0 +import QtQml.Models 2.1 Rectangle { id: menuBar @@ -129,7 +130,7 @@ Rectangle { } } //a list of visual items already have delegates handling their display - VisualItemModel{ + ObjectModel{ id: menuListModel FileMenu{ diff --git a/examples/quick/views/doc/src/views.qdoc b/examples/quick/views/doc/src/views.qdoc index e48a66514b..658e51c8fc 100644 --- a/examples/quick/views/doc/src/views.qdoc +++ b/examples/quick/views/doc/src/views.qdoc @@ -71,12 +71,12 @@ \snippet quick/views/package/Delegate.qml 0 - A VisualDataModel allows the individual views to access their specific items from + A DelegateModel allows the individual views to access their specific items from the shared package delegate. \snippet quick/views/package/view.qml 0 - \section2 VisualItemModel uses a VisualItemModel for the model instead of a ListModel. + \section2 ObjectModel uses an ObjectModel for the model instead of a ListModel. \snippet quick/views/visualitemmodel/visualitemmodel.qml 0 */ diff --git a/examples/quick/views/objectmodel/objectmodel.qml b/examples/quick/views/objectmodel/objectmodel.qml new file mode 100644 index 0000000000..b790053c82 --- /dev/null +++ b/examples/quick/views/objectmodel/objectmodel.qml @@ -0,0 +1,118 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// This example demonstrates placing items in a view using +// an ObjectModel + +import QtQuick 2.0 +import QtQml.Models 2.1 + +Rectangle { + id: root + color: "lightgray" + width: 320 + height: 480 + property bool printDestruction: false + +//! [0] + ObjectModel { + id: itemModel + + Rectangle { + width: view.width; height: view.height + color: "#FFFEF0" + Text { text: "Page 1"; font.bold: true; anchors.centerIn: parent } + + Component.onDestruction: if (printDestruction) print("destroyed 1") + } + Rectangle { + width: view.width; height: view.height + color: "#F0FFF7" + Text { text: "Page 2"; font.bold: true; anchors.centerIn: parent } + + Component.onDestruction: if (printDestruction) print("destroyed 2") + } + Rectangle { + width: view.width; height: view.height + color: "#F4F0FF" + Text { text: "Page 3"; font.bold: true; anchors.centerIn: parent } + + Component.onDestruction: if (printDestruction) print("destroyed 3") + } + } + + ListView { + id: view + anchors { fill: parent; bottomMargin: 30 } + model: itemModel + preferredHighlightBegin: 0; preferredHighlightEnd: 0 + highlightRangeMode: ListView.StrictlyEnforceRange + orientation: ListView.Horizontal + snapMode: ListView.SnapOneItem; flickDeceleration: 2000 + cacheBuffer: 200 + } +//! [0] + Rectangle { + width: root.width; height: 30 + anchors { top: view.bottom; bottom: parent.bottom } + color: "gray" + + Row { + anchors.centerIn: parent + spacing: 20 + + Repeater { + model: itemModel.count + + Rectangle { + width: 5; height: 5 + radius: 3 + color: view.currentIndex == index ? "blue" : "white" + + MouseArea { + width: 20; height: 20 + anchors.centerIn: parent + onClicked: view.currentIndex = index + } + } + } + } + } +} diff --git a/examples/quick/views/package/view.qml b/examples/quick/views/package/view.qml index 58ae5a4bf8..820eb4068c 100644 --- a/examples/quick/views/package/view.qml +++ b/examples/quick/views/package/view.qml @@ -39,6 +39,7 @@ ****************************************************************************/ import QtQuick 2.0 +import QtQml.Models 2.1 Rectangle { id: root @@ -64,7 +65,7 @@ Rectangle { ListElement { display: "Eight" } } //![0] - VisualDataModel { + DelegateModel { id: visualModel delegate: Delegate {} model: myModel diff --git a/examples/quick/views/parallax/content/ParallaxView.qml b/examples/quick/views/parallax/content/ParallaxView.qml index 5bfed1a297..e8c67c7434 100644 --- a/examples/quick/views/parallax/content/ParallaxView.qml +++ b/examples/quick/views/parallax/content/ParallaxView.qml @@ -39,6 +39,7 @@ ****************************************************************************/ import QtQuick 2.0 +import QtQml.Models 2.1 Item { id: root @@ -63,7 +64,7 @@ Item { orientation: Qt.Horizontal boundsBehavior: Flickable.DragOverBounds - model: VisualItemModel { id: visualModel } + model: ObjectModel { id: visualModel } highlightRangeMode: ListView.StrictlyEnforceRange snapMode: ListView.SnapOneItem diff --git a/examples/quick/views/views.qml b/examples/quick/views/views.qml index d1dda2a777..0724d11456 100644 --- a/examples/quick/views/views.qml +++ b/examples/quick/views/views.qml @@ -39,6 +39,7 @@ ****************************************************************************/ import QtQuick 2.0 +import QtQml.Models 2.1 import "../shared" as Examples /*! @@ -51,50 +52,50 @@ import "../shared" as Examples are focused on the views side, which is the visual representation of the data in the models. \section2 GridView and PathView demonstrate usage of these elements to display views. - \snippet examples/quick/modelviews/gridview/gridview-example.qml 0 + \snippet examples/quick/views/gridview/gridview-example.qml 0 \section2 Dynamic List demonstrates animation of runtime additions and removals to a ListView. The ListView.onAdd signal handler runs an animation when new items are added to the view, and the ListView.onRemove another when they are removed. - \snippet examples/quick/modelviews/listview/dynamiclist.qml 0 - \snippet examples/quick/modelviews/listview/dynamiclist.qml 1 + \snippet examples/quick/views/listview/dynamiclist.qml 0 + \snippet examples/quick/views/listview/dynamiclist.qml 1 \section2 Expanding Delegates demonstrates delegates that expand when activated. It has a complex delegate the size and appearance of which can change, displacing other items in the view. - \snippet examples/quick/modelviews/listview/expandingdelegates.qml 0 - \snippet examples/quick/modelviews/listview/expandingdelegates.qml 1 - \snippet examples/quick/modelviews/listview/expandingdelegates.qml 2 - \snippet examples/quick/modelviews/listview/expandingdelegates.qml 3 + \snippet examples/quick/views/listview/expandingdelegates.qml 0 + \snippet examples/quick/views/listview/expandingdelegates.qml 1 + \snippet examples/quick/views/listview/expandingdelegates.qml 2 + \snippet examples/quick/views/listview/expandingdelegates.qml 3 \section2 Highlight demonstrates adding a custom highlight to a ListView. - \snippet examples/quick/modelviews/listview/highlight.qml 0 + \snippet examples/quick/views/listview/highlight.qml 0 \section2 Highlight Ranges shows the three different highlight range modes of ListView. - \snippet examples/quick/modelviews/listview/highlightranges.qml 0 - \snippet examples/quick/modelviews/listview/highlightranges.qml 1 - \snippet examples/quick/modelviews/listview/highlightranges.qml 2 + \snippet examples/quick/views/listview/highlightranges.qml 0 + \snippet examples/quick/views/listview/highlightranges.qml 1 + \snippet examples/quick/views/listview/highlightranges.qml 2 \section2 Sections demonstrates the various section headers and footers available to ListView. - \snippet examples/quick/modelviews/listview/sections.qml 0 + \snippet examples/quick/views/listview/sections.qml 0 \section2 Packages demonstrates using Packages to transition delegates between two views. It has a Package which defines delegate items for each view and an item that can be transferred between delegates. - \snippet examples/quick/modelviews/package/Delegate.qml 0 + \snippet examples/quick/views/package/Delegate.qml 0 - A VisualDataModel allows the individual views to access their specific items from + A DelegateModel allows the individual views to access their specific items from the shared package delegate. - \snippet examples/quick/modelviews/package/view.qml 0 + \snippet examples/quick/views/package/view.qml 0 - \section2 VisualItemModel uses a VisualItemModel for the model instead of a ListModel. + \section2 ObjectModel uses a ObjectModel for the model instead of a ListModel. - \snippet examples/quick/modelviews/visualitemmodel/visualitemmodel.qml 0 + \snippet examples/quick/views/objectmodel/objectmodel.qml 0 */ Item { @@ -112,7 +113,7 @@ import "../shared" as Examples addExample("Sections", "ListView section headers and footers", Qt.resolvedUrl("listview/sections.qml")) addExample("Packages", "Transitions between a ListView and GridView", Qt.resolvedUrl("package/view.qml")) addExample("PathView", "A simple PathView", Qt.resolvedUrl("pathview/pathview-example.qml")) - addExample("VisualItemModel", "Using a VisualItemModel", Qt.resolvedUrl("visualitemmodel/visualitemmodel.qml")) + addExample("ObjectModel", "Using a ObjectModel", Qt.resolvedUrl("objectmodel/objectmodel.qml")) } } } diff --git a/examples/quick/views/views.qrc b/examples/quick/views/views.qrc index e35f128202..434fa788bf 100644 --- a/examples/quick/views/views.qrc +++ b/examples/quick/views/views.qrc @@ -63,7 +63,7 @@ pathview/pics/VideoPlayer_48.png visualdatamodel/slideshow.qml visualdatamodel/dragselection.qml - visualitemmodel/visualitemmodel.qml + objectmodel/objectmodel.qml views.qml diff --git a/examples/quick/views/visualdatamodel/dragselection.qml b/examples/quick/views/visualdatamodel/dragselection.qml index ec80cc7c16..5578268961 100644 --- a/examples/quick/views/visualdatamodel/dragselection.qml +++ b/examples/quick/views/visualdatamodel/dragselection.qml @@ -39,6 +39,7 @@ ****************************************************************************/ import QtQuick 2.0 +import QtQml.Models 2.1 Item { id: root @@ -59,7 +60,7 @@ Item { width: 64 height: 64 - enabled: packageRoot.VisualDataModel.inSelected + enabled: packageRoot.DelegateModel.inSelected drag.target: draggable @@ -83,7 +84,7 @@ Item { } DropArea { anchors.fill: parent - onEntered: selectedItems.move(0, visualModel.items.get(packageRoot.VisualDataModel.itemsIndex), selectedItems.count) + onEntered: selectedItems.move(0, visualModel.items.get(packageRoot.DelegateModel.itemsIndex), selectedItems.count) } } Item { @@ -112,7 +113,7 @@ Item { border.width: 2 border.color: "#007423" - state: root.dragging && packageRoot.VisualDataModel.inSelected ? "selected" : "visible" + state: root.dragging && packageRoot.DelegateModel.inSelected ? "selected" : "visible" Text { anchors.fill: parent @@ -126,7 +127,7 @@ Item { Rectangle { anchors { right: parent.right; top: parent.top; margins: 3 } width: 12; height: 12 - color: packageRoot.VisualDataModel.inSelected ? "black" : "white" + color: packageRoot.DelegateModel.inSelected ? "black" : "white" radius: 6 border.color: "white" @@ -134,7 +135,7 @@ Item { MouseArea { anchors.fill: parent - onClicked: packageRoot.VisualDataModel.inSelected = !packageRoot.VisualDataModel.inSelected + onClicked: packageRoot.DelegateModel.inSelected = !packageRoot.DelegateModel.inSelected } } @@ -142,19 +143,19 @@ Item { State { name: "selected" ParentChange { target: content; parent: selectionContainer; x: 3; y: 3 } - PropertyChanges { target: packageRoot; VisualDataModel.inItems: visibleContainer.drag.active } + PropertyChanges { target: packageRoot; DelegateModel.inItems: visibleContainer.drag.active } PropertyChanges { target: gradientStart; color: "#017423" } PropertyChanges { target: gradientStart; color: "#007423" } }, State { name: "visible" - PropertyChanges { target: packageRoot; VisualDataModel.inItems: true } + PropertyChanges { target: packageRoot; DelegateModel.inItems: true } ParentChange { target: content; parent: visibleContainer; x: 3; y: 3 } PropertyChanges { target: gradientStart; color: "#8AC953" } PropertyChanges { target: gradientStart; color: "#8BC953" } } ] transitions: Transition { - PropertyAction { target: packageRoot; properties: "VisualDataModel.inItems" } + PropertyAction { target: packageRoot; properties: "DelegateModel.inItems" } ParentAnimation { target: content NumberAnimation { target: content; properties: "x,y"; duration: 500 } @@ -165,7 +166,7 @@ Item { } } - VisualDataModel { + DelegateModel { id: visualModel model: 35 delegate: packageDelegate diff --git a/examples/quick/views/visualdatamodel/slideshow.qml b/examples/quick/views/visualdatamodel/slideshow.qml index 77fe9809d4..d3a4013503 100644 --- a/examples/quick/views/visualdatamodel/slideshow.qml +++ b/examples/quick/views/visualdatamodel/slideshow.qml @@ -40,6 +40,7 @@ import QtQuick 2.0 import QtQuick.XmlListModel 2.0 +import QtQml.Models 2.1 Rectangle { id: root @@ -50,7 +51,7 @@ Rectangle { color: "black" - VisualDataModel { + DelegateModel { id: visualModel model: XmlListModel { @@ -97,14 +98,14 @@ Rectangle { name: "inDisplay"; ParentChange { target: image; parent: imageContainer; x: 75; y: 75; width: 150; height: 150 } PropertyChanges { target: image; z: 2 } - PropertyChanges { target: delegateItem; VisualDataModel.inItems: false } + PropertyChanges { target: delegateItem; DelegateModel.inItems: false } }, State { when: root.displayItem !== delegateItem name: "inList"; ParentChange { target: image; parent: delegateItem; x: 2; y: 2; width: 75; height: 75 } PropertyChanges { target: image; z: 1 } - PropertyChanges { target: delegateItem; VisualDataModel.inItems: true } + PropertyChanges { target: delegateItem; DelegateModel.inItems: true } } ] @@ -112,7 +113,7 @@ Rectangle { Transition { from: "inList" SequentialAnimation { - PropertyAction { target: delegateItem; property: "VisualDataModel.inPersistedItems"; value: true } + PropertyAction { target: delegateItem; property: "DelegateModel.inPersistedItems"; value: true } ParentAnimation { target: image; via: root @@ -126,7 +127,7 @@ Rectangle { target: image NumberAnimation { target: image; properties: "x,y,width,height"; duration: 1000 } } - PropertyAction { target: delegateItem; property: "VisualDataModel.inPersistedItems"; value: false } + PropertyAction { target: delegateItem; property: "DelegateModel.inPersistedItems"; value: false } } } ] diff --git a/examples/quick/views/visualitemmodel/visualitemmodel.qml b/examples/quick/views/visualitemmodel/visualitemmodel.qml deleted file mode 100644 index 90adf39b86..0000000000 --- a/examples/quick/views/visualitemmodel/visualitemmodel.qml +++ /dev/null @@ -1,117 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the examples of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names -** of its contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -// This example demonstrates placing items in a view using -// a VisualItemModel - -import QtQuick 2.0 - -Rectangle { - id: root - color: "lightgray" - width: 320 - height: 480 - property bool printDestruction: false - -//! [0] - VisualItemModel { - id: itemModel - - Rectangle { - width: view.width; height: view.height - color: "#FFFEF0" - Text { text: "Page 1"; font.bold: true; anchors.centerIn: parent } - - Component.onDestruction: if (printDestruction) print("destroyed 1") - } - Rectangle { - width: view.width; height: view.height - color: "#F0FFF7" - Text { text: "Page 2"; font.bold: true; anchors.centerIn: parent } - - Component.onDestruction: if (printDestruction) print("destroyed 2") - } - Rectangle { - width: view.width; height: view.height - color: "#F4F0FF" - Text { text: "Page 3"; font.bold: true; anchors.centerIn: parent } - - Component.onDestruction: if (printDestruction) print("destroyed 3") - } - } - - ListView { - id: view - anchors { fill: parent; bottomMargin: 30 } - model: itemModel - preferredHighlightBegin: 0; preferredHighlightEnd: 0 - highlightRangeMode: ListView.StrictlyEnforceRange - orientation: ListView.Horizontal - snapMode: ListView.SnapOneItem; flickDeceleration: 2000 - cacheBuffer: 200 - } -//! [0] - Rectangle { - width: root.width; height: 30 - anchors { top: view.bottom; bottom: parent.bottom } - color: "gray" - - Row { - anchors.centerIn: parent - spacing: 20 - - Repeater { - model: itemModel.count - - Rectangle { - width: 5; height: 5 - radius: 3 - color: view.currentIndex == index ? "blue" : "white" - - MouseArea { - width: 20; height: 20 - anchors.centerIn: parent - onClicked: view.currentIndex = index - } - } - } - } - } -} diff --git a/src/imports/imports.pro b/src/imports/imports.pro index e411a4f097..733c7c47bd 100644 --- a/src/imports/imports.pro +++ b/src/imports/imports.pro @@ -2,7 +2,8 @@ TEMPLATE = subdirs SUBDIRS += \ folderlistmodel \ - localstorage + localstorage \ + models qtHaveModule(quick) { SUBDIRS += \ diff --git a/src/imports/models/models.pro b/src/imports/models/models.pro new file mode 100644 index 0000000000..e96d4b9a44 --- /dev/null +++ b/src/imports/models/models.pro @@ -0,0 +1,11 @@ +CXX_MODULE = qml +TARGET = modelsplugin +TARGETPATH = QtQml/Models.2 +IMPORT_VERSION = 2.1 + +SOURCES += \ + plugin.cpp + +QT += qml-private + +load(qml_plugin) diff --git a/src/imports/models/plugin.cpp b/src/imports/models/plugin.cpp new file mode 100644 index 0000000000..127c781d6c --- /dev/null +++ b/src/imports/models/plugin.cpp @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Research In Motion. +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include + +QT_BEGIN_NAMESPACE + +/*! + \qmlmodule QtQml.Models 2 + \title Qt Qml Model QML Types + \ingroup qmlmodules + \brief Provides QML types for data models + \since 5.1 + + This QML module contains types for defining data models in QML. + + To use the types in this module, import the module with the following line: + + \code + import QtQml.Models 2.1 + \endcode + + Note that QtQml.Models module started at version 2.1 to match the version of the parent module. +*/ + + + +//![class decl] +class QtQmlModelsPlugin : public QQmlExtensionPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface/1.0") +public: + virtual void registerTypes(const char *uri) + { + Q_ASSERT(QLatin1String(uri) == QLatin1String("QtQml.Models")); + Q_UNUSED(uri); + QQmlModelsModule::defineModule(); + } +}; +//![class decl] + +QT_END_NAMESPACE + +#include "plugin.moc" diff --git a/src/imports/models/qmldir b/src/imports/models/qmldir new file mode 100644 index 0000000000..9cad5ef734 --- /dev/null +++ b/src/imports/models/qmldir @@ -0,0 +1,2 @@ +module QtQml.Models +plugin modelsplugin diff --git a/src/qml/items/items.pri b/src/qml/items/items.pri index 0fa489f4e1..f5224f0acc 100644 --- a/src/qml/items/items.pri +++ b/src/qml/items/items.pri @@ -1,10 +1,12 @@ SOURCES += \ $$PWD/qquickpackage.cpp \ $$PWD/qqmldelegatemodel.cpp \ - $$PWD/qqmlobjectmodel.cpp + $$PWD/qqmlobjectmodel.cpp \ + $$PWD/qqmlmodelsmodule.cpp HEADERS += \ $$PWD/qquickpackage_p.h \ $$PWD/qqmldelegatemodel_p.h \ $$PWD/qqmldelegatemodel_p_p.h \ - $$PWD/qqmlobjectmodel_p.h + $$PWD/qqmlobjectmodel_p.h \ + $$PWD/qqmlmodelsmodule_p.h diff --git a/src/qml/items/qqmldelegatemodel.cpp b/src/qml/items/qqmldelegatemodel.cpp index 131b539d92..efbd98bdbc 100644 --- a/src/qml/items/qqmldelegatemodel.cpp +++ b/src/qml/items/qqmldelegatemodel.cpp @@ -126,16 +126,36 @@ QQmlDelegateModelParts::QQmlDelegateModelParts(QQmlDelegateModel *parent) \brief Encapsulates a model and delegate The VisualDataModel type encapsulates a model and the delegate that will + be instantiated for items in a model. + + This type is provided by QtQuick 2 for compatibility reasons. The same implementation + is now primarily available as DelegateModel in the QtQml.Models module. + + \sa {QtQml.Models2::DelegateModel} +*/ +/*! + \qmltype DelegateModel + \instantiates QQmlDelegateModel + \inqmlmodule QtQml.Models 2 + \brief Encapsulates a model and delegate + + The DelegateModel type encapsulates a model and the delegate that will + be instantiated for items in the model. + + This element is also available as DelegateModel in the QtQuick module. For full details, + see the \l DelegateModel documentation. + + The DelegateModel type encapsulates a model and the delegate that will be instantiated for items in the model. - It is usually not necessary to create a VisualDataModel. + It is usually not necessary to create a DelegateModel. However, it can be useful for manipulating and accessing the \l modelIndex when a QAbstractItemModel subclass is used as the - model. Also, VisualDataModel is used together with \l Package to - provide delegates to multiple views, and with VisualDataGroup to sort and filter + model. Also, DelegateModel is used together with \l Package to + provide delegates to multiple views, and with DelegateModelGroup to sort and filter delegate items. - The example below illustrates using a VisualDataModel with a ListView. + The example below illustrates using a DelegateModel with a ListView. \snippet qml/visualdatamodel.qml 0 */ @@ -173,10 +193,10 @@ void QQmlDelegateModelPrivate::init() Q_Q(QQmlDelegateModel); m_compositor.setRemoveGroups(Compositor::GroupMask & ~Compositor::PersistedFlag); - m_items = new QQmlDataGroup(QStringLiteral("items"), q, Compositor::Default, q); + m_items = new QQmlDelegateModelGroup(QStringLiteral("items"), q, Compositor::Default, q); m_items->setDefaultInclude(true); - m_persistedItems = new QQmlDataGroup(QStringLiteral("persistedItems"), q, Compositor::Persisted, q); - QQmlDataGroupPrivate::get(m_items)->emitters.insert(this); + m_persistedItems = new QQmlDelegateModelGroup(QStringLiteral("persistedItems"), q, Compositor::Persisted, q); + QQmlDelegateModelGroupPrivate::get(m_items)->emitters.insert(this); } QQmlDelegateModel::QQmlDelegateModel() @@ -230,9 +250,9 @@ void QQmlDelegateModel::componentComplete() QStringList groupNames; groupNames.append(QStringLiteral("items")); groupNames.append(QStringLiteral("persistedItems")); - if (QQmlDataGroupPrivate::get(d->m_items)->defaultInclude) + if (QQmlDelegateModelGroupPrivate::get(d->m_items)->defaultInclude) defaultGroups |= Compositor::DefaultFlag; - if (QQmlDataGroupPrivate::get(d->m_persistedItems)->defaultInclude) + if (QQmlDelegateModelGroupPrivate::get(d->m_persistedItems)->defaultInclude) defaultGroups |= Compositor::PersistedFlag; for (int i = Compositor::MinimumGroupCount; i < d->m_groupCount; ++i) { QString name = d->m_groups[i]->name(); @@ -241,14 +261,14 @@ void QQmlDelegateModel::componentComplete() --d->m_groupCount; --i; } else if (name.at(0).isUpper()) { - qmlInfo(d->m_groups[i]) << QQmlDataGroup::tr("Group names must start with a lower case letter"); + qmlInfo(d->m_groups[i]) << QQmlDelegateModelGroup::tr("Group names must start with a lower case letter"); d->m_groups[i] = d->m_groups[d->m_groupCount - 1]; --d->m_groupCount; --i; } else { groupNames.append(name); - QQmlDataGroupPrivate *group = QQmlDataGroupPrivate::get(d->m_groups[i]); + QQmlDelegateModelGroupPrivate *group = QQmlDelegateModelGroupPrivate::get(d->m_groups[i]); group->setModel(this, Compositor::Group(i)); if (group->defaultInclude) defaultGroups |= (1 << i); @@ -281,8 +301,8 @@ void QQmlDelegateModel::componentComplete() } /*! - \qmlproperty model QtQuick2::VisualDataModel::model - This property holds the model providing data for the VisualDataModel. + \qmlproperty model QtQml.Models2::DelegateModel::model + This property holds the model providing data for the DelegateModel. The model provides a set of data that is used to create the items for a view. For large or dynamic datasets the model is usually @@ -322,7 +342,7 @@ void QQmlDelegateModel::setModel(const QVariant &model) } /*! - \qmlproperty Component QtQuick2::VisualDataModel::delegate + \qmlproperty Component QtQml.Models2::DelegateModel::delegate The delegate provides a template defining each item instantiated by a view. The index is exposed as an accessible \c index property. Properties of the @@ -338,7 +358,7 @@ void QQmlDelegateModel::setDelegate(QQmlComponent *delegate) { Q_D(QQmlDelegateModel); if (d->m_transaction) { - qmlInfo(this) << tr("The delegate of a VisualDataModel cannot be changed within onUpdated."); + qmlInfo(this) << tr("The delegate of a DelegateModel cannot be changed within onUpdated."); return; } bool wasValid = d->m_delegate != 0; @@ -346,13 +366,13 @@ void QQmlDelegateModel::setDelegate(QQmlComponent *delegate) d->m_delegateValidated = false; if (wasValid && d->m_complete) { for (int i = 1; i < d->m_groupCount; ++i) { - QQmlDataGroupPrivate::get(d->m_groups[i])->changeSet.remove( + QQmlDelegateModelGroupPrivate::get(d->m_groups[i])->changeSet.remove( 0, d->m_compositor.count(Compositor::Group(i))); } } if (d->m_complete && d->m_delegate) { for (int i = 1; i < d->m_groupCount; ++i) { - QQmlDataGroupPrivate::get(d->m_groups[i])->changeSet.insert( + QQmlDelegateModelGroupPrivate::get(d->m_groups[i])->changeSet.insert( 0, d->m_compositor.count(Compositor::Group(i))); } } @@ -360,7 +380,7 @@ void QQmlDelegateModel::setDelegate(QQmlComponent *delegate) } /*! - \qmlproperty QModelIndex QtQuick2::VisualDataModel::rootIndex + \qmlproperty QModelIndex QtQml.Models2::DelegateModel::rootIndex QAbstractItemModel provides a hierarchical tree of data, whereas QML only operates on list data. \c rootIndex allows the children of @@ -420,7 +440,7 @@ void QQmlDelegateModel::setRootIndex(const QVariant &root) } /*! - \qmlmethod QModelIndex QtQuick2::VisualDataModel::modelIndex(int index) + \qmlmethod QModelIndex QtQml.Models2::DelegateModel::modelIndex(int index) QAbstractItemModel provides a hierarchical tree of data, whereas QML only operates on list data. This function assists in using @@ -438,7 +458,7 @@ QVariant QQmlDelegateModel::modelIndex(int idx) const } /*! - \qmlmethod QModelIndex QtQuick2::VisualDataModel::parentModelIndex() + \qmlmethod QModelIndex QtQml.Models2::DelegateModel::parentModelIndex() QAbstractItemModel provides a hierarchical tree of data, whereas QML only operates on list data. This function assists in using @@ -456,7 +476,7 @@ QVariant QQmlDelegateModel::parentModelIndex() const } /*! - \qmlproperty int QtQuick2::VisualDataModel::count + \qmlproperty int QtQml.Models2::DelegateModel::count */ int QQmlDelegateModel::count() const @@ -506,7 +526,7 @@ void QQmlDelegateModel::cancel(int index) { Q_D(QQmlDelegateModel); if (!d->m_delegate || index < 0 || index >= d->m_compositor.count(d->m_compositorGroup)) { - qWarning() << "VisualDataModel::cancel: index out range" << index << d->m_compositor.count(d->m_compositorGroup); + qWarning() << "DelegateModel::cancel: index out range" << index << d->m_compositor.count(d->m_compositorGroup); return; } @@ -538,13 +558,13 @@ void QQmlDelegateModel::cancel(int index) } void QQmlDelegateModelPrivate::group_append( - QQmlListProperty *property, QQmlDataGroup *group) + QQmlListProperty *property, QQmlDelegateModelGroup *group) { QQmlDelegateModelPrivate *d = static_cast(property->data); if (d->m_complete) return; if (d->m_groupCount == Compositor::MaximumGroupCount) { - qmlInfo(d->q_func()) << QQmlDelegateModel::tr("The maximum number of supported VisualDataGroups is 8"); + qmlInfo(d->q_func()) << QQmlDelegateModel::tr("The maximum number of supported DelegateModelGroups is 8"); return; } d->m_groups[d->m_groupCount] = group; @@ -552,14 +572,14 @@ void QQmlDelegateModelPrivate::group_append( } int QQmlDelegateModelPrivate::group_count( - QQmlListProperty *property) + QQmlListProperty *property) { QQmlDelegateModelPrivate *d = static_cast(property->data); return d->m_groupCount - 1; } -QQmlDataGroup *QQmlDelegateModelPrivate::group_at( - QQmlListProperty *property, int index) +QQmlDelegateModelGroup *QQmlDelegateModelPrivate::group_at( + QQmlListProperty *property, int index) { QQmlDelegateModelPrivate *d = static_cast(property->data); return index >= 0 && index < d->m_groupCount - 1 @@ -568,16 +588,16 @@ QQmlDataGroup *QQmlDelegateModelPrivate::group_at( } /*! - \qmlproperty list QtQuick2::VisualDataModel::groups + \qmlproperty list QtQml.Models2::DelegateModel::groups This property holds a visual data model's group definitions. Groups define a sub-set of the items in a visual data model and can be used to filter a model. - For every group defined in a VisualDataModel two attached properties are added to each - delegate item. The first of the form VisualDataModel.in\e{GroupName} holds whether the - item belongs to the group and the second VisualDataModel.\e{groupName}Index holds the + For every group defined in a DelegateModel two attached properties are added to each + delegate item. The first of the form DelegateModel.in\e{GroupName} holds whether the + item belongs to the group and the second DelegateModel.\e{groupName}Index holds the index of the item in that group. The following example illustrates using groups to select items in a model. @@ -585,10 +605,10 @@ QQmlDataGroup *QQmlDelegateModelPrivate::group_at( \snippet qml/visualdatagroup.qml 0 */ -QQmlListProperty QQmlDelegateModel::groups() +QQmlListProperty QQmlDelegateModel::groups() { Q_D(QQmlDelegateModel); - return QQmlListProperty( + return QQmlListProperty( this, d, QQmlDelegateModelPrivate::group_append, @@ -598,19 +618,19 @@ QQmlListProperty QQmlDelegateModel::groups() } /*! - \qmlproperty VisualDataGroup QtQuick2::VisualDataModel::items + \qmlproperty DelegateModelGroup QtQml.Models2::DelegateModel::items This property holds visual data model's default group to which all new items are added. */ -QQmlDataGroup *QQmlDelegateModel::items() +QQmlDelegateModelGroup *QQmlDelegateModel::items() { Q_D(QQmlDelegateModel); return d->m_items; } /*! - \qmlproperty VisualDataGroup QtQuick2::VisualDataModel::persistedItems + \qmlproperty DelegateModelGroup QtQml.Models2::DelegateModel::persistedItems This property holds visual data model's persisted items group. @@ -618,22 +638,22 @@ QQmlDataGroup *QQmlDelegateModel::items() until removed from the group. An item can be removed from the persistedItems group by setting the - VisualDataModel.inPersistedItems property to false. If the item is not referenced by a view + DelegateModel.inPersistedItems property to false. If the item is not referenced by a view at that time it will be destroyed. Adding an item to this group will not create a new instance. - Items returned by the \l QtQuick2::VisualDataGroup::create() function are automatically added + Items returned by the \l QtQml.Models2::DelegateModelGroup::create() function are automatically added to this group. */ -QQmlDataGroup *QQmlDelegateModel::persistedItems() +QQmlDelegateModelGroup *QQmlDelegateModel::persistedItems() { Q_D(QQmlDelegateModel); return d->m_persistedItems; } /*! - \qmlproperty string QtQuick2::VisualDataModel::filterOnGroup + \qmlproperty string QtQml.Models2::DelegateModel::filterOnGroup This property holds the name of the group used to filter the visual data model. @@ -653,7 +673,7 @@ void QQmlDelegateModel::setFilterGroup(const QString &group) Q_D(QQmlDelegateModel); if (d->m_transaction) { - qmlInfo(this) << tr("The group of a VisualDataModel cannot be changed within onChanged"); + qmlInfo(this) << tr("The group of a DelegateModel cannot be changed within onChanged"); return; } @@ -684,7 +704,7 @@ void QQmlDelegateModelPrivate::updateFilterGroup() } } - QQmlDataGroupPrivate::get(m_groups[m_compositorGroup])->emitters.insert(this); + QQmlDelegateModelGroupPrivate::get(m_groups[m_compositorGroup])->emitters.insert(this); if (m_compositorGroup != previousGroup) { QVector removes; QVector inserts; @@ -705,9 +725,9 @@ void QQmlDelegateModelPrivate::updateFilterGroup() } /*! - \qmlproperty object QtQuick2::VisualDataModel::parts + \qmlproperty object QtQml.Models2::DelegateModel::parts - The \a parts property selects a VisualDataModel which creates + The \a parts property selects a DelegateModel which creates delegates from the part named. This is used in conjunction with the \l Package type. @@ -715,7 +735,7 @@ void QQmlDelegateModelPrivate::updateFilterGroup() delegates named \e list from a \l Package: \code - VisualDataModel { + DelegateModel { id: visualModel delegate: Package { Item { Package.name: "list" } @@ -743,19 +763,19 @@ QObject *QQmlDelegateModel::parts() void QQmlDelegateModelPrivate::emitCreatedPackage(QQDMIncubationTask *incubationTask, QQuickPackage *package) { for (int i = 1; i < m_groupCount; ++i) - QQmlDataGroupPrivate::get(m_groups[i])->createdPackage(incubationTask->index[i], package); + QQmlDelegateModelGroupPrivate::get(m_groups[i])->createdPackage(incubationTask->index[i], package); } void QQmlDelegateModelPrivate::emitInitPackage(QQDMIncubationTask *incubationTask, QQuickPackage *package) { for (int i = 1; i < m_groupCount; ++i) - QQmlDataGroupPrivate::get(m_groups[i])->initPackage(incubationTask->index[i], package); + QQmlDelegateModelGroupPrivate::get(m_groups[i])->initPackage(incubationTask->index[i], package); } void QQmlDelegateModelPrivate::emitDestroyingPackage(QQuickPackage *package) { for (int i = 1; i < m_groupCount; ++i) - QQmlDataGroupPrivate::get(m_groups[i])->destroyingPackage(package); + QQmlDelegateModelGroupPrivate::get(m_groups[i])->destroyingPackage(package); } void QQDMIncubationTask::statusChanged(Status status) @@ -842,7 +862,7 @@ QObject *QQmlDelegateModelPrivate::object(Compositor::Group group, int index, bo { Q_Q(QQmlDelegateModel); if (!m_delegate || index < 0 || index >= m_compositor.count(group)) { - qWarning() << "VisualDataModel::item: index out range" << index << m_compositor.count(group); + qWarning() << "DelegateModel::item: index out range" << index << m_compositor.count(group); return 0; } else if (!m_context->isValid()) { return 0; @@ -936,7 +956,7 @@ QObject *QQmlDelegateModel::object(int index, bool asynchronous) { Q_D(QQmlDelegateModel); if (!d->m_delegate || index < 0 || index >= d->m_compositor.count(d->m_compositorGroup)) { - qWarning() << "VisualDataModel::item: index out range" << index << d->m_compositor.count(d->m_compositorGroup); + qWarning() << "DelegateModel::item: index out range" << index << d->m_compositor.count(d->m_compositorGroup); return 0; } @@ -1053,7 +1073,7 @@ void QQmlDelegateModelPrivate::itemsChanged(const QVector &c } for (int i = 1; i < m_groupCount; ++i) - QQmlDataGroupPrivate::get(m_groups[i])->changeSet.change(translatedChanges.at(i)); + QQmlDelegateModelGroupPrivate::get(m_groups[i])->changeSet.change(translatedChanges.at(i)); } void QQmlDelegateModel::_q_itemsChanged(int index, int count, const QVector &roles) @@ -1148,7 +1168,7 @@ void QQmlDelegateModelPrivate::itemsInserted(const QVector & return; for (int i = 1; i < m_groupCount; ++i) - QQmlDataGroupPrivate::get(m_groups[i])->changeSet.insert(translatedInserts.at(i)); + QQmlDelegateModelGroupPrivate::get(m_groups[i])->changeSet.insert(translatedInserts.at(i)); } void QQmlDelegateModel::_q_itemsInserted(int index, int count) @@ -1265,7 +1285,7 @@ void QQmlDelegateModelPrivate::itemsRemoved(const QVector &r return; for (int i = 1; i < m_groupCount; ++i) - QQmlDataGroupPrivate::get(m_groups[i])->changeSet.remove(translatedRemoves.at(i)); + QQmlDelegateModelGroupPrivate::get(m_groups[i])->changeSet.remove(translatedRemoves.at(i)); } void QQmlDelegateModel::_q_itemsRemoved(int index, int count) @@ -1307,7 +1327,7 @@ void QQmlDelegateModelPrivate::itemsMoved( return; for (int i = 1; i < m_groupCount; ++i) { - QQmlDataGroupPrivate::get(m_groups[i])->changeSet.move( + QQmlDelegateModelGroupPrivate::get(m_groups[i])->changeSet.move( translatedRemoves.at(i), translatedInserts.at(i)); } @@ -1372,13 +1392,13 @@ void QQmlDelegateModelPrivate::emitChanges() m_transaction = true; QV8Engine *engine = QQmlEnginePrivate::getV8Engine(m_context->engine()); for (int i = 1; i < m_groupCount; ++i) - QQmlDataGroupPrivate::get(m_groups[i])->emitChanges(engine); + QQmlDelegateModelGroupPrivate::get(m_groups[i])->emitChanges(engine); m_transaction = false; const bool reset = m_reset; m_reset = false; for (int i = 1; i < m_groupCount; ++i) - QQmlDataGroupPrivate::get(m_groups[i])->emitModelUpdated(reset); + QQmlDelegateModelGroupPrivate::get(m_groups[i])->emitModelUpdated(reset); foreach (QQmlDelegateModelItem *cacheItem, m_cache) { if (cacheItem->attached) @@ -1931,7 +1951,7 @@ QQmlDelegateModelAttached::QQmlDelegateModelAttached( } /*! - \qmlattachedproperty int QtQuick2::VisualDataModel::model + \qmlattachedproperty int QtQml.Models2::DelegateModel::model This attached property holds the visual data model this delegate instance belongs to. @@ -1944,9 +1964,9 @@ QQmlDelegateModel *QQmlDelegateModelAttached::model() const } /*! - \qmlattachedproperty stringlist QtQuick2::VisualDataModel::groups + \qmlattachedproperty stringlist QtQml.Models2::DelegateModel::groups - This attached property holds the name of VisualDataGroups the item belongs to. + This attached property holds the name of DelegateModelGroups the item belongs to. It is attached to each instance of the delegate. */ @@ -1978,12 +1998,12 @@ void QQmlDelegateModelAttached::setGroups(const QStringList &groups) } /*! - \qmlattachedproperty bool QtQuick2::VisualDataModel::isUnresolved + \qmlattachedproperty bool QtQml.Models2::DelegateModel::isUnresolved This attached property holds whether the visual item is bound to a data model index. Returns true if the item is not bound to the model, and false if it is. - An unresolved item can be bound to the data model using the VisualDataGroup::resolve() + An unresolved item can be bound to the data model using the DelegateModelGroup::resolve() function. It is attached to each instance of the delegate. @@ -1998,9 +2018,9 @@ bool QQmlDelegateModelAttached::isUnresolved() const } /*! - \qmlattachedproperty int QtQuick2::VisualDataModel::inItems + \qmlattachedproperty int QtQml.Models2::DelegateModel::inItems - This attached property holds whether the item belongs to the default \l items VisualDataGroup. + This attached property holds whether the item belongs to the default \l items DelegateModelGroup. Changing this property will add or remove the item from the items group. @@ -2008,17 +2028,17 @@ bool QQmlDelegateModelAttached::isUnresolved() const */ /*! - \qmlattachedproperty int QtQuick2::VisualDataModel::itemsIndex + \qmlattachedproperty int QtQml.Models2::DelegateModel::itemsIndex - This attached property holds the index of the item in the default \l items VisualDataGroup. + This attached property holds the index of the item in the default \l items DelegateModelGroup. It is attached to each instance of the delegate. */ /*! - \qmlattachedproperty int QtQuick2::VisualDataModel::inPersistedItems + \qmlattachedproperty int QtQml.Models2::DelegateModel::inPersistedItems - This attached property holds whether the item belongs to the \l persistedItems VisualDataGroup. + This attached property holds whether the item belongs to the \l persistedItems DelegateModelGroup. Changing this property will add or remove the item from the items group. Change with caution as removing an item from the persistedItems group will destroy the current instance if it is @@ -2028,9 +2048,9 @@ bool QQmlDelegateModelAttached::isUnresolved() const */ /*! - \qmlattachedproperty int QtQuick2::VisualDataModel::persistedItemsIndex + \qmlattachedproperty int QtQml.Models2::DelegateModel::persistedItemsIndex - This attached property holds the index of the item in the \l persistedItems VisualDataGroup. + This attached property holds the index of the item in the \l persistedItems DelegateModelGroup. It is attached to each instance of the delegate. */ @@ -2065,22 +2085,22 @@ void QQmlDelegateModelAttached::emitChanges() //============================================================================ -void QQmlDataGroupPrivate::setModel(QQmlDelegateModel *m, Compositor::Group g) +void QQmlDelegateModelGroupPrivate::setModel(QQmlDelegateModel *m, Compositor::Group g) { Q_ASSERT(!model); model = m; group = g; } -bool QQmlDataGroupPrivate::isChangedConnected() +bool QQmlDelegateModelGroupPrivate::isChangedConnected() { - Q_Q(QQmlDataGroup); - IS_SIGNAL_CONNECTED(q, QQmlDataGroup, changed, (const QQmlV8Handle &,const QQmlV8Handle &)); + Q_Q(QQmlDelegateModelGroup); + IS_SIGNAL_CONNECTED(q, QQmlDelegateModelGroup, changed, (const QQmlV8Handle &,const QQmlV8Handle &)); } -void QQmlDataGroupPrivate::emitChanges(QV8Engine *engine) +void QQmlDelegateModelGroupPrivate::emitChanges(QV8Engine *engine) { - Q_Q(QQmlDataGroup); + Q_Q(QQmlDelegateModelGroup); if (isChangedConnected() && !changeSet.isEmpty()) { v8::HandleScope handleScope; v8::Context::Scope contextScope(engine->context()); @@ -2092,101 +2112,116 @@ void QQmlDataGroupPrivate::emitChanges(QV8Engine *engine) emit q->countChanged(); } -void QQmlDataGroupPrivate::emitModelUpdated(bool reset) +void QQmlDelegateModelGroupPrivate::emitModelUpdated(bool reset) { - for (QQmlDataGroupEmitterList::iterator it = emitters.begin(); it != emitters.end(); ++it) + for (QQmlDelegateModelGroupEmitterList::iterator it = emitters.begin(); it != emitters.end(); ++it) it->emitModelUpdated(changeSet, reset); changeSet.clear(); } -void QQmlDataGroupPrivate::createdPackage(int index, QQuickPackage *package) +void QQmlDelegateModelGroupPrivate::createdPackage(int index, QQuickPackage *package) { - for (QQmlDataGroupEmitterList::iterator it = emitters.begin(); it != emitters.end(); ++it) + for (QQmlDelegateModelGroupEmitterList::iterator it = emitters.begin(); it != emitters.end(); ++it) it->createdPackage(index, package); } -void QQmlDataGroupPrivate::initPackage(int index, QQuickPackage *package) +void QQmlDelegateModelGroupPrivate::initPackage(int index, QQuickPackage *package) { - for (QQmlDataGroupEmitterList::iterator it = emitters.begin(); it != emitters.end(); ++it) + for (QQmlDelegateModelGroupEmitterList::iterator it = emitters.begin(); it != emitters.end(); ++it) it->initPackage(index, package); } -void QQmlDataGroupPrivate::destroyingPackage(QQuickPackage *package) +void QQmlDelegateModelGroupPrivate::destroyingPackage(QQuickPackage *package) { - for (QQmlDataGroupEmitterList::iterator it = emitters.begin(); it != emitters.end(); ++it) + for (QQmlDelegateModelGroupEmitterList::iterator it = emitters.begin(); it != emitters.end(); ++it) it->destroyingPackage(package); } /*! - \qmltype VisualDataGroup - \instantiates QQmlDataGroup + \qmltype DelegateModelGroup + \instantiates QQmlDelegateModelGroup \inqmlmodule QtQuick 2 \ingroup qtquick-models \brief Encapsulates a filtered set of visual data items - The VisualDataGroup type provides a means to address the model data of a VisualDataModel's + The DelegateModelGroup type provides a means to address the model data of a DelegateModel's delegate items, as well as sort and filter these delegate items. - The initial set of instantiable delegate items in a VisualDataModel is represented - by its \l {QtQuick2::VisualDataModel::items}{items} group, which normally directly reflects - the contents of the model assigned to VisualDataModel::model. This set can be changed to - the contents of any other member of VisualDataModel::groups by assigning the \l name of that - VisualDataGroup to the VisualDataModel::filterOnGroup property. + The initial set of instantiable delegate items in a DelegateModel is represented + by its \l {QtQml.Models2::DelegateModel::items}{items} group, which normally directly reflects + the contents of the model assigned to DelegateModel::model. This set can be changed to + the contents of any other member of DelegateModel::groups by assigning the \l name of that + DelegateModelGroup to the DelegateModel::filterOnGroup property. - The data of an item in a VisualDataGroup can be accessed using the get() function, which returns + The data of an item in a DelegateModelGroup can be accessed using the get() function, which returns information about group membership and indexes as well as model data. In combination with the move() function this can be used to implement view sorting, with remove() to filter items out of a view, or with setGroups() and \l Package delegates to categorize items into different views. - Data from models can be supplemented by inserting data directly into a VisualDataGroup + Data from models can be supplemented by inserting data directly into a DelegateModelGroup with the insert() function. This can be used to introduce mock items into a view, or placeholder items that are later \l {resolve()}{resolved} to real model data when it becomes available. - Delegate items can also be be instantiated directly from a VisualDataGroup using the - create() function, making it possible to use VisualDataModel without an accompanying view + Delegate items can also be be instantiated directly from a DelegateModelGroup using the + create() function, making it possible to use DelegateModel without an accompanying view type or to cherry-pick specific items that should be instantiated irregardless of whether they're currently within a view's visible area. \sa {QML Dynamic View Ordering Tutorial} */ +/*! + \qmltype DelegateModelGroup + \instantiates QQmlDelegateModelGroup + \inqmlmodule QtQml.Models 2 + \brief Encapsulates a filtered set of visual data items + + The DelegateModelGroup type provides a means to address the model data of a DelegateModel's + delegate items, as well as sort and filter these delegate items. + + This element is also available as DelegateModelGroup in the QtQuick module. For full details, + see the \l DelegateModelGroup documentation. + + \sa {QtQuick::DelegateModelGroup} +*/ + -QQmlDataGroup::QQmlDataGroup(QObject *parent) - : QObject(*new QQmlDataGroupPrivate, parent) +QQmlDelegateModelGroup::QQmlDelegateModelGroup(QObject *parent) + : QObject(*new QQmlDelegateModelGroupPrivate, parent) { } -QQmlDataGroup::QQmlDataGroup( +QQmlDelegateModelGroup::QQmlDelegateModelGroup( const QString &name, QQmlDelegateModel *model, int index, QObject *parent) - : QObject(*new QQmlDataGroupPrivate, parent) + : QObject(*new QQmlDelegateModelGroupPrivate, parent) { - Q_D(QQmlDataGroup); + Q_D(QQmlDelegateModelGroup); d->name = name; d->setModel(model, Compositor::Group(index)); } -QQmlDataGroup::~QQmlDataGroup() +QQmlDelegateModelGroup::~QQmlDelegateModelGroup() { } /*! - \qmlproperty string QtQuick2::VisualDataGroup::name + \qmlproperty string QtQml.Models2::DelegateModelGroup::name This property holds the name of the group. Each group in a model must have a unique name starting with a lower case letter. */ -QString QQmlDataGroup::name() const +QString QQmlDelegateModelGroup::name() const { - Q_D(const QQmlDataGroup); + Q_D(const QQmlDelegateModelGroup); return d->name; } -void QQmlDataGroup::setName(const QString &name) +void QQmlDelegateModelGroup::setName(const QString &name) { - Q_D(QQmlDataGroup); + Q_D(QQmlDelegateModelGroup); if (d->model) return; if (d->name != name) { @@ -2196,34 +2231,34 @@ void QQmlDataGroup::setName(const QString &name) } /*! - \qmlproperty int QtQuick2::VisualDataGroup::count + \qmlproperty int QtQml.Models2::DelegateModelGroup::count This property holds the number of items in the group. */ -int QQmlDataGroup::count() const +int QQmlDelegateModelGroup::count() const { - Q_D(const QQmlDataGroup); + Q_D(const QQmlDelegateModelGroup); if (!d->model) return 0; return QQmlDelegateModelPrivate::get(d->model)->m_compositor.count(d->group); } /*! - \qmlproperty bool QtQuick2::VisualDataGroup::includeByDefault + \qmlproperty bool QtQml.Models2::DelegateModelGroup::includeByDefault This property holds whether new items are assigned to this group by default. */ -bool QQmlDataGroup::defaultInclude() const +bool QQmlDelegateModelGroup::defaultInclude() const { - Q_D(const QQmlDataGroup); + Q_D(const QQmlDelegateModelGroup); return d->defaultInclude; } -void QQmlDataGroup::setDefaultInclude(bool include) +void QQmlDelegateModelGroup::setDefaultInclude(bool include) { - Q_D(QQmlDataGroup); + Q_D(QQmlDelegateModelGroup); if (d->defaultInclude != include) { d->defaultInclude = include; @@ -2238,32 +2273,32 @@ void QQmlDataGroup::setDefaultInclude(bool include) } /*! - \qmlmethod object QtQuick2::VisualDataGroup::get(int index) + \qmlmethod object QtQml.Models2::DelegateModelGroup::get(int index) Returns a javascript object describing the item at \a index in the group. The returned object contains the same information that is available to a delegate from the - VisualDataModel attached as well as the model for that item. It has the properties: + DelegateModel attached as well as the model for that item. It has the properties: \list \li \b model The model data of the item. This is the same as the model context property in a delegate \li \b groups A list the of names of groups the item is a member of. This property can be written to change the item's membership. - \li \b inItems Whether the item belongs to the \l {QtQuick2::VisualDataModel::items}{items} group. + \li \b inItems Whether the item belongs to the \l {QtQml.Models2::DelegateModel::items}{items} group. Writing to this property will add or remove the item from the group. - \li \b itemsIndex The index of the item within the \l {QtQuick2::VisualDataModel::items}{items} group. + \li \b itemsIndex The index of the item within the \l {QtQml.Models2::DelegateModel::items}{items} group. \li \b {in} Whether the item belongs to the dynamic group \e groupName. Writing to this property will add or remove the item from the group. \li \b {Index} The index of the item within the dynamic group \e groupName. \li \b isUnresolved Whether the item is bound to an index in the model assigned to - VisualDataModel::model. Returns true if the item is not bound to the model, and false if it is. + DelegateModel::model. Returns true if the item is not bound to the model, and false if it is. \endlist */ -QQmlV8Handle QQmlDataGroup::get(int index) +QQmlV8Handle QQmlDelegateModelGroup::get(int index) { - Q_D(QQmlDataGroup); + Q_D(QQmlDelegateModelGroup); if (!d->model) return QQmlV8Handle::fromHandle(v8::Undefined());; @@ -2300,7 +2335,7 @@ QQmlV8Handle QQmlDataGroup::get(int index) return QQmlV8Handle::fromHandle(handle); } -bool QQmlDataGroupPrivate::parseIndex( +bool QQmlDelegateModelGroupPrivate::parseIndex( const v8::Local &value, int *index, Compositor::Group *group) const { if (value->IsInt32()) { @@ -2321,25 +2356,25 @@ bool QQmlDataGroupPrivate::parseIndex( } /*! - \qmlmethod QtQuick2::VisualDataGroup::insert(int index, jsdict data, array groups = undefined) - \qmlmethod QtQuick2::VisualDataGroup::insert(jsdict data, var groups = undefined) + \qmlmethod QtQml.Models2::DelegateModelGroup::insert(int index, jsdict data, array groups = undefined) + \qmlmethod QtQml.Models2::DelegateModelGroup::insert(jsdict data, var groups = undefined) - Creates a new entry at \a index in a VisualDataModel with the values from \a data that - correspond to roles in the model assigned to VisualDataModel::model. + Creates a new entry at \a index in a DelegateModel with the values from \a data that + correspond to roles in the model assigned to DelegateModel::model. If no index is supplied the data is appended to the model. The optional \a groups parameter identifies the groups the new entry should belong to, if unspecified this is equal to the group insert was called on. - Data inserted into a VisualDataModel can later be merged with an existing entry in - VisualDataModel::model using the \l resolve() function. This can be used to create placeholder + Data inserted into a DelegateModel can later be merged with an existing entry in + DelegateModel::model using the \l resolve() function. This can be used to create placeholder items that are later replaced by actual data. */ -void QQmlDataGroup::insert(QQmlV8Function *args) +void QQmlDelegateModelGroup::insert(QQmlV8Function *args) { - Q_D(QQmlDataGroup); + Q_D(QQmlDelegateModelGroup); QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); int index = model->m_compositor.count(d->group); @@ -2377,9 +2412,9 @@ void QQmlDataGroup::insert(QQmlV8Function *args) } /*! - \qmlmethod QtQuick2::VisualDataGroup::create(int index) - \qmlmethod QtQuick2::VisualDataGroup::create(int index, jsdict data, array groups = undefined) - \qmlmethod QtQuick2::VisualDataGroup::create(jsdict data, array groups = undefined) + \qmlmethod QtQml.Models2::DelegateModelGroup::create(int index) + \qmlmethod QtQml.Models2::DelegateModelGroup::create(int index, jsdict data, array groups = undefined) + \qmlmethod QtQml.Models2::DelegateModelGroup::create(jsdict data, array groups = undefined) Returns a reference to the instantiated item at \a index in the group. @@ -2389,13 +2424,13 @@ void QQmlDataGroup::insert(QQmlV8Function *args) was called on. All items returned by create are added to the - \l {QtQuick2::VisualDataModel::persistedItems}{persistedItems} group. Items in this + \l {QtQml.Models2::DelegateModel::persistedItems}{persistedItems} group. Items in this group remain instantiated when not referenced by any view. */ -void QQmlDataGroup::create(QQmlV8Function *args) +void QQmlDelegateModelGroup::create(QQmlV8Function *args) { - Q_D(QQmlDataGroup); + Q_D(QQmlDelegateModelGroup); if (!d->model) return; @@ -2450,24 +2485,24 @@ void QQmlDataGroup::create(QQmlV8Function *args) } /*! - \qmlmethod QtQuick2::VisualDataGroup::resolve(int from, int to) + \qmlmethod QtQml.Models2::DelegateModelGroup::resolve(int from, int to) - Binds an unresolved item at \a from to an item in VisualDataModel::model at index \a to. + Binds an unresolved item at \a from to an item in DelegateModel::model at index \a to. - Unresolved items are entries whose data has been \l {insert()}{inserted} into a VisualDataGroup - instead of being derived from a VisualDataModel::model index. Resolving an item will replace + Unresolved items are entries whose data has been \l {insert()}{inserted} into a DelegateModelGroup + instead of being derived from a DelegateModel::model index. Resolving an item will replace the item at the target index with the unresolved item. A resolved an item will reflect the data of the source model at its bound index and will move when that index moves like any other item. - If a new item is replaced in the VisualDataGroup onChanged() handler its insertion and + If a new item is replaced in the DelegateModelGroup onChanged() handler its insertion and replacement will be communicated to views as an atomic operation, creating the appearance that the model contents have not changed, or if the unresolved and model item are not adjacent that the previously unresolved item has simply moved. */ -void QQmlDataGroup::resolve(QQmlV8Function *args) +void QQmlDelegateModelGroup::resolve(QQmlV8Function *args) { - Q_D(QQmlDataGroup); + Q_D(QQmlDelegateModelGroup); if (!d->model) return; @@ -2560,14 +2595,14 @@ void QQmlDataGroup::resolve(QQmlV8Function *args) } /*! - \qmlmethod QtQuick2::VisualDataGroup::remove(int index, int count) + \qmlmethod QtQml.Models2::DelegateModelGroup::remove(int index, int count) Removes \a count items starting at \a index from the group. */ -void QQmlDataGroup::remove(QQmlV8Function *args) +void QQmlDelegateModelGroup::remove(QQmlV8Function *args) { - Q_D(QQmlDataGroup); + Q_D(QQmlDelegateModelGroup); if (!d->model) return; Compositor::Group group = d->group; @@ -2603,7 +2638,7 @@ void QQmlDataGroup::remove(QQmlV8Function *args) } } -bool QQmlDataGroupPrivate::parseGroupArgs( +bool QQmlDelegateModelGroupPrivate::parseGroupArgs( QQmlV8Function *args, Compositor::Group *group, int *index, int *count, int *groups) const { if (!model || !QQmlDelegateModelPrivate::get(model)->m_cacheMetaType) @@ -2632,14 +2667,14 @@ bool QQmlDataGroupPrivate::parseGroupArgs( } /*! - \qmlmethod QtQuick2::VisualDataGroup::addGroups(int index, int count, stringlist groups) + \qmlmethod QtQml.Models2::DelegateModelGroup::addGroups(int index, int count, stringlist groups) Adds \a count items starting at \a index to \a groups. */ -void QQmlDataGroup::addGroups(QQmlV8Function *args) +void QQmlDelegateModelGroup::addGroups(QQmlV8Function *args) { - Q_D(QQmlDataGroup); + Q_D(QQmlDelegateModelGroup); Compositor::Group group = d->group; int index = -1; int count = 1; @@ -2662,14 +2697,14 @@ void QQmlDataGroup::addGroups(QQmlV8Function *args) } /*! - \qmlmethod QtQuick2::VisualDataGroup::removeGroups(int index, int count, stringlist groups) + \qmlmethod QtQml.Models2::DelegateModelGroup::removeGroups(int index, int count, stringlist groups) Removes \a count items starting at \a index from \a groups. */ -void QQmlDataGroup::removeGroups(QQmlV8Function *args) +void QQmlDelegateModelGroup::removeGroups(QQmlV8Function *args) { - Q_D(QQmlDataGroup); + Q_D(QQmlDelegateModelGroup); Compositor::Group group = d->group; int index = -1; int count = 1; @@ -2692,14 +2727,14 @@ void QQmlDataGroup::removeGroups(QQmlV8Function *args) } /*! - \qmlmethod QtQuick2::VisualDataGroup::setGroups(int index, int count, stringlist groups) + \qmlmethod QtQml.Models2::DelegateModelGroup::setGroups(int index, int count, stringlist groups) Sets the \a groups \a count items starting at \a index belong to. */ -void QQmlDataGroup::setGroups(QQmlV8Function *args) +void QQmlDelegateModelGroup::setGroups(QQmlV8Function *args) { - Q_D(QQmlDataGroup); + Q_D(QQmlDelegateModelGroup); Compositor::Group group = d->group; int index = -1; int count = 1; @@ -2722,20 +2757,20 @@ void QQmlDataGroup::setGroups(QQmlV8Function *args) } /*! - \qmlmethod QtQuick2::VisualDataGroup::setGroups(int index, int count, stringlist groups) + \qmlmethod QtQml.Models2::DelegateModelGroup::setGroups(int index, int count, stringlist groups) Sets the \a groups \a count items starting at \a index belong to. */ /*! - \qmlmethod QtQuick2::VisualDataGroup::move(var from, var to, int count) + \qmlmethod QtQml.Models2::DelegateModelGroup::move(var from, var to, int count) Moves \a count at \a from in a group \a to a new position. */ -void QQmlDataGroup::move(QQmlV8Function *args) +void QQmlDelegateModelGroup::move(QQmlV8Function *args) { - Q_D(QQmlDataGroup); + Q_D(QQmlDelegateModelGroup); if (args->Length() < 2) return; @@ -2782,7 +2817,7 @@ void QQmlDataGroup::move(QQmlV8Function *args) } /*! - \qmlsignal QtQuick2::VisualDataGroup::onChanged(array removed, array inserted) + \qmlsignal QtQml.Models2::DelegateModelGroup::onChanged(array removed, array inserted) This handler is called when items have been removed from or inserted into the group. @@ -2804,7 +2839,7 @@ QQmlPartsModel::QQmlPartsModel(QQmlDelegateModel *model, const QString &part, QO { QQmlDelegateModelPrivate *d = QQmlDelegateModelPrivate::get(m_model); if (d->m_cacheMetaType) { - QQmlDataGroupPrivate::get(d->m_groups[1])->emitters.insert(this); + QQmlDelegateModelGroupPrivate::get(d->m_groups[1])->emitters.insert(this); m_compositorGroup = Compositor::Default; } else { d->m_pendingParts.insert(this); @@ -2825,7 +2860,7 @@ QString QQmlPartsModel::filterGroup() const void QQmlPartsModel::setFilterGroup(const QString &group) { if (QQmlDelegateModelPrivate::get(m_model)->m_transaction) { - qmlInfo(this) << tr("The group of a VisualDataModel cannot be changed within onChanged"); + qmlInfo(this) << tr("The group of a DelegateModel cannot be changed within onChanged"); return; } @@ -2861,7 +2896,7 @@ void QQmlPartsModel::updateFilterGroup() QQmlListCompositor::Group previousGroup = m_compositorGroup; m_compositorGroup = Compositor::Default; - QQmlDataGroupPrivate::get(model->m_groups[Compositor::Default])->emitters.insert(this); + QQmlDelegateModelGroupPrivate::get(model->m_groups[Compositor::Default])->emitters.insert(this); for (int i = 1; i < model->m_groupCount; ++i) { if (m_filterGroup == model->m_cacheMetaType->groupNames.at(i - 1)) { m_compositorGroup = Compositor::Group(i); @@ -2869,7 +2904,7 @@ void QQmlPartsModel::updateFilterGroup() } } - QQmlDataGroupPrivate::get(model->m_groups[m_compositorGroup])->emitters.insert(this); + QQmlDelegateModelGroupPrivate::get(model->m_groups[m_compositorGroup])->emitters.insert(this); if (m_compositorGroup != previousGroup) { QVector removes; QVector inserts; @@ -2892,7 +2927,7 @@ void QQmlPartsModel::updateFilterGroup( return; m_compositorGroup = group; - QQmlDataGroupPrivate::get(QQmlDelegateModelPrivate::get(m_model)->m_groups[m_compositorGroup])->emitters.insert(this); + QQmlDelegateModelGroupPrivate::get(QQmlDelegateModelPrivate::get(m_model)->m_groups[m_compositorGroup])->emitters.insert(this); if (!changeSet.isEmpty()) emit modelUpdated(changeSet, false); @@ -2921,7 +2956,7 @@ QObject *QQmlPartsModel::object(int index, bool asynchronous) QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_model); if (!model->m_delegate || index < 0 || index >= model->m_compositor.count(m_compositorGroup)) { - qWarning() << "VisualDataModel::item: index out range" << index << model->m_compositor.count(m_compositorGroup); + qWarning() << "DelegateModel::item: index out range" << index << model->m_compositor.count(m_compositorGroup); return 0; } @@ -3026,11 +3061,11 @@ v8::Handle get_change_moveId(v8::Local, const v8::Accesso return info.This()->GetInternalField(2); } -class QQmlDataGroupChangeArray : public QV8ObjectResource +class QQmlDelegateModelGroupChangeArray : public QV8ObjectResource { V8_RESOURCE_TYPE(ChangeSetArrayType) public: - QQmlDataGroupChangeArray(QV8Engine *engine) + QQmlDelegateModelGroupChangeArray(QV8Engine *engine) : QV8ObjectResource(engine) { } @@ -3040,7 +3075,7 @@ public: static v8::Handle get_change(quint32 index, const v8::AccessorInfo &info) { - QQmlDataGroupChangeArray *array = v8_resource_cast(info.This()); + QQmlDelegateModelGroupChangeArray *array = v8_resource_cast(info.This()); V8ASSERT_TYPE(array, "Not a valid change array"); if (index >= array->count()) @@ -3059,7 +3094,7 @@ public: static v8::Handle get_length(v8::Local, const v8::AccessorInfo &info) { - QQmlDataGroupChangeArray *array = v8_resource_cast(info.This()); + QQmlDelegateModelGroupChangeArray *array = v8_resource_cast(info.This()); V8ASSERT_TYPE(array, "Not a valid change array"); return v8::Integer::New(array->count()); @@ -3075,11 +3110,11 @@ public: } }; -class QQmlDataGroupRemoveArray : public QQmlDataGroupChangeArray +class QQmlDelegateModelGroupRemoveArray : public QQmlDelegateModelGroupChangeArray { public: - QQmlDataGroupRemoveArray(QV8Engine *engine, const QVector &changes) - : QQmlDataGroupChangeArray(engine) + QQmlDelegateModelGroupRemoveArray(QV8Engine *engine, const QVector &changes) + : QQmlDelegateModelGroupChangeArray(engine) , changes(changes) { } @@ -3091,11 +3126,11 @@ private: QVector changes; }; -class QQmlDataGroupInsertArray : public QQmlDataGroupChangeArray +class QQmlDelegateModelGroupInsertArray : public QQmlDelegateModelGroupChangeArray { public: - QQmlDataGroupInsertArray(QV8Engine *engine, const QVector &changes) - : QQmlDataGroupChangeArray(engine) + QQmlDelegateModelGroupInsertArray(QV8Engine *engine, const QVector &changes) + : QQmlDelegateModelGroupChangeArray(engine) , changes(changes) { } @@ -3124,7 +3159,7 @@ QQmlDelegateModelEngineData::QQmlDelegateModelEngineData(QV8Engine *) change->InstanceTemplate()->SetAccessor(v8::String::New("moveId"), get_change_moveId); change->InstanceTemplate()->SetInternalFieldCount(3); constructorChange = qPersistentNew(change->GetFunction()); - constructorChangeArray = qPersistentNew(QQmlDataGroupChangeArray::constructor()); + constructorChangeArray = qPersistentNew(QQmlDelegateModelGroupChangeArray::constructor()); } QQmlDelegateModelEngineData::~QQmlDelegateModelEngineData() @@ -3138,7 +3173,7 @@ v8::Local QQmlDelegateModelEngineData::array( QV8Engine *engine, const QVector &changes) { v8::Local array = constructorChangeArray->NewInstance(); - array->SetExternalResource(new QQmlDataGroupRemoveArray(engine, changes)); + array->SetExternalResource(new QQmlDelegateModelGroupRemoveArray(engine, changes)); return array; } @@ -3146,7 +3181,7 @@ v8::Local QQmlDelegateModelEngineData::array( QV8Engine *engine, const QVector &changes) { v8::Local array = constructorChangeArray->NewInstance(); - array->SetExternalResource(new QQmlDataGroupInsertArray(engine, changes)); + array->SetExternalResource(new QQmlDelegateModelGroupInsertArray(engine, changes)); return array; } diff --git a/src/qml/items/qqmldelegatemodel_p.h b/src/qml/items/qqmldelegatemodel_p.h index 0fccf5720d..5702c59787 100644 --- a/src/qml/items/qqmldelegatemodel_p.h +++ b/src/qml/items/qqmldelegatemodel_p.h @@ -60,7 +60,7 @@ class QQmlChangeSet; class QQmlComponent; class QQuickPackage; class QQmlV8Function; -class QQmlDataGroup; +class QQmlDelegateModelGroup; class QQmlDelegateModelAttached; class QQmlDelegateModelPrivate; @@ -73,9 +73,9 @@ class Q_QML_PRIVATE_EXPORT QQmlDelegateModel : public QQmlInstanceModel, public Q_PROPERTY(QVariant model READ model WRITE setModel) Q_PROPERTY(QQmlComponent *delegate READ delegate WRITE setDelegate) Q_PROPERTY(QString filterOnGroup READ filterGroup WRITE setFilterGroup NOTIFY filterGroupChanged RESET resetFilterGroup) - Q_PROPERTY(QQmlDataGroup *items READ items CONSTANT) //TODO : worth renaming? - Q_PROPERTY(QQmlDataGroup *persistedItems READ persistedItems CONSTANT) - Q_PROPERTY(QQmlListProperty groups READ groups CONSTANT) + Q_PROPERTY(QQmlDelegateModelGroup *items READ items CONSTANT) //TODO : worth renaming? + Q_PROPERTY(QQmlDelegateModelGroup *persistedItems READ persistedItems CONSTANT) + Q_PROPERTY(QQmlListProperty groups READ groups CONSTANT) Q_PROPERTY(QObject *parts READ parts CONSTANT) Q_PROPERTY(QVariant rootIndex READ rootIndex WRITE setRootIndex NOTIFY rootIndexChanged) Q_CLASSINFO("DefaultProperty", "delegate") @@ -114,9 +114,9 @@ public: void setFilterGroup(const QString &group); void resetFilterGroup(); - QQmlDataGroup *items(); - QQmlDataGroup *persistedItems(); - QQmlListProperty groups(); + QQmlDelegateModelGroup *items(); + QQmlDelegateModelGroup *persistedItems(); + QQmlListProperty groups(); QObject *parts(); bool event(QEvent *); @@ -145,17 +145,17 @@ private: Q_DISABLE_COPY(QQmlDelegateModel) }; -class QQmlDataGroupPrivate; -class Q_QML_PRIVATE_EXPORT QQmlDataGroup : public QObject +class QQmlDelegateModelGroupPrivate; +class Q_QML_PRIVATE_EXPORT QQmlDelegateModelGroup : public QObject { Q_OBJECT Q_PROPERTY(int count READ count NOTIFY countChanged) Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) Q_PROPERTY(bool includeByDefault READ defaultInclude WRITE setDefaultInclude NOTIFY defaultIncludeChanged) public: - QQmlDataGroup(QObject *parent = 0); - QQmlDataGroup(const QString &name, QQmlDelegateModel *model, int compositorType, QObject *parent = 0); - ~QQmlDataGroup(); + QQmlDelegateModelGroup(QObject *parent = 0); + QQmlDelegateModelGroup(const QString &name, QQmlDelegateModel *model, int compositorType, QObject *parent = 0); + ~QQmlDelegateModelGroup(); QString name() const; void setName(const QString &name); @@ -183,7 +183,7 @@ Q_SIGNALS: void defaultIncludeChanged(); void changed(const QQmlV8Handle &removed, const QQmlV8Handle &inserted); private: - Q_DECLARE_PRIVATE(QQmlDataGroup) + Q_DECLARE_PRIVATE(QQmlDelegateModelGroup) }; class QQmlDelegateModelItem; @@ -229,6 +229,6 @@ QT_END_NAMESPACE QML_DECLARE_TYPE(QQmlDelegateModel) QML_DECLARE_TYPEINFO(QQmlDelegateModel, QML_HAS_ATTACHED_PROPERTIES) -QML_DECLARE_TYPE(QQmlDataGroup) +QML_DECLARE_TYPE(QQmlDelegateModelGroup) #endif // QQMLDATAMODEL_P_H diff --git a/src/qml/items/qqmldelegatemodel_p_p.h b/src/qml/items/qqmldelegatemodel_p_p.h index 0ac7285cab..68242f433d 100644 --- a/src/qml/items/qqmldelegatemodel_p_p.h +++ b/src/qml/items/qqmldelegatemodel_p_p.h @@ -183,10 +183,10 @@ public: }; -class QQmlDataGroupEmitter +class QQmlDelegateModelGroupEmitter { public: - virtual ~QQmlDataGroupEmitter() {} + virtual ~QQmlDelegateModelGroupEmitter() {} virtual void emitModelUpdated(const QQmlChangeSet &changeSet, bool reset) = 0; virtual void createdPackage(int, QQuickPackage *) {} virtual void initPackage(int, QQuickPackage *) {} @@ -195,17 +195,17 @@ public: QIntrusiveListNode emitterNode; }; -typedef QIntrusiveList QQmlDataGroupEmitterList; +typedef QIntrusiveList QQmlDelegateModelGroupEmitterList; -class QQmlDataGroupPrivate : public QObjectPrivate +class QQmlDelegateModelGroupPrivate : public QObjectPrivate { public: - Q_DECLARE_PUBLIC(QQmlDataGroup) + Q_DECLARE_PUBLIC(QQmlDelegateModelGroup) - QQmlDataGroupPrivate() : group(Compositor::Cache), defaultInclude(false) {} + QQmlDelegateModelGroupPrivate() : group(Compositor::Cache), defaultInclude(false) {} - static QQmlDataGroupPrivate *get(QQmlDataGroup *group) { - return static_cast(QObjectPrivate::get(group)); } + static QQmlDelegateModelGroupPrivate *get(QQmlDelegateModelGroup *group) { + return static_cast(QObjectPrivate::get(group)); } void setModel(QQmlDelegateModel *model, Compositor::Group group); bool isChangedConnected(); @@ -222,7 +222,7 @@ public: Compositor::Group group; QQmlGuard model; - QQmlDataGroupEmitterList emitters; + QQmlDelegateModelGroupEmitterList emitters; QQmlChangeSet changeSet; QString name; bool defaultInclude; @@ -230,7 +230,7 @@ public: class QQmlDelegateModelParts; -class QQmlDelegateModelPrivate : public QObjectPrivate, public QQmlDataGroupEmitter +class QQmlDelegateModelPrivate : public QObjectPrivate, public QQmlDelegateModelGroupEmitter { Q_DECLARE_PUBLIC(QQmlDelegateModel) public: @@ -282,9 +282,9 @@ public: bool insert(Compositor::insert_iterator &before, const v8::Local &object, int groups); - static void group_append(QQmlListProperty *property, QQmlDataGroup *group); - static int group_count(QQmlListProperty *property); - static QQmlDataGroup *group_at(QQmlListProperty *property, int index); + static void group_append(QQmlListProperty *property, QQmlDelegateModelGroup *group); + static int group_count(QQmlListProperty *property); + static QQmlDelegateModelGroup *group_at(QQmlListProperty *property, int index); void releaseIncubator(QQDMIncubationTask *incubationTask); void incubatorStatusChanged(QQDMIncubationTask *incubationTask, QQmlIncubator::Status status); @@ -296,7 +296,7 @@ public: QQmlDelegateModelItemMetaType *m_cacheMetaType; QQmlContext *m_context; QQmlDelegateModelParts *m_parts; - QQmlDataGroupEmitterList m_pendingParts; + QQmlDelegateModelGroupEmitterList m_pendingParts; QList m_cache; QList m_finishedIncubating; @@ -316,15 +316,15 @@ public: union { struct { - QQmlDataGroup *m_cacheItems; - QQmlDataGroup *m_items; - QQmlDataGroup *m_persistedItems; + QQmlDelegateModelGroup *m_cacheItems; + QQmlDelegateModelGroup *m_items; + QQmlDelegateModelGroup *m_persistedItems; }; - QQmlDataGroup *m_groups[Compositor::MaximumGroupCount]; + QQmlDelegateModelGroup *m_groups[Compositor::MaximumGroupCount]; }; }; -class QQmlPartsModel : public QQmlInstanceModel, public QQmlDataGroupEmitter +class QQmlPartsModel : public QQmlInstanceModel, public QQmlDelegateModelGroupEmitter { Q_OBJECT Q_PROPERTY(QString filterOnGroup READ filterGroup WRITE setFilterGroup NOTIFY filterGroupChanged RESET resetFilterGroup) diff --git a/src/qml/items/qqmlmodelsmodule.cpp b/src/qml/items/qqmlmodelsmodule.cpp new file mode 100644 index 0000000000..4f6b0a5580 --- /dev/null +++ b/src/qml/items/qqmlmodelsmodule.cpp @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Research In Motion. +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlmodelsmodule_p.h" +#include +#include +#include + +QT_BEGIN_NAMESPACE + +void QQmlModelsModule::defineModule() +{ + const char uri[] = "QtQml.Models"; + + qmlRegisterType(uri, 2, 1, "ListElement"); + qmlRegisterCustomType(uri, 2, 1, "ListModel", new QQmlListModelParser); + qmlRegisterType(uri, 2, 1, "DelegateModel"); + qmlRegisterType(uri, 2, 1, "DelegateModelGroup"); + qmlRegisterType(uri, 2, 1, "ObjectModel"); +} + +QT_END_NAMESPACE diff --git a/src/qml/items/qqmlmodelsmodule_p.h b/src/qml/items/qqmlmodelsmodule_p.h new file mode 100644 index 0000000000..6e72dadf8b --- /dev/null +++ b/src/qml/items/qqmlmodelsmodule_p.h @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Research In Motion. +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLMODELSMODULE_H +#define QQMLMODELSMODULE_H + +#include + +QT_BEGIN_NAMESPACE + +class Q_QML_PRIVATE_EXPORT QQmlModelsModule +{ +public: + static void defineModule(); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/items/qqmlobjectmodel.cpp b/src/qml/items/qqmlobjectmodel.cpp index 913fa79719..7f7bf92fa3 100644 --- a/src/qml/items/qqmlobjectmodel.cpp +++ b/src/qml/items/qqmlobjectmodel.cpp @@ -129,26 +129,26 @@ public: /*! - \qmltype VisualItemModel + \qmltype ObjectModel \instantiates QQmlObjectModel - \inqmlmodule QtQuick 2 + \inqmlmodule QtQml.Models 2 \ingroup qtquick-models - \brief Defines items to be used added to a view + \brief Defines a set of items to be used as a model - A VisualItemModel contains the visual items to be used in a view. - When a VisualItemModel is used in a view, the view does not require - a delegate since the VisualItemModel already contains the visual + A ObjectModel contains the visual items to be used in a view. + When a ObjectModel is used in a view, the view does not require + a delegate since the ObjectModel already contains the visual delegate (items). An item can determine its index within the - model via the \l{VisualItemModel::index}{index} attached property. + model via the \l{ObjectModel::index}{index} attached property. The example below places three colored rectangles in a ListView. \code import QtQuick 2.0 Rectangle { - VisualItemModel { + ObjectModel { id: itemModel Rectangle { height: 30; width: 80; color: "red" } Rectangle { height: 30; width: 80; color: "green" } @@ -164,15 +164,33 @@ public: \image visualitemmodel.png - \sa {quick/modelviews/visualitemmodel}{VisualItemModel example} + \sa {quick/views/objectmodel}{ObjectModel example} +*/ +/*! + \qmltype VisualItemModel + \instantiates QQmlObjectModel + \inqmlmodule QtQuick 2 + \brief Defines a set of objects to be used as a model + + The VisualItemModel type encapsulates contains the objects to be used + as a model. + + This element is now primarily available as ObjectModel in the QtQml.Models module. + VisualItemModel continues to be provided, with the same implementation, in QtQuick for + compatibility reasons. + + For full details about the type, see the \l ObjectModel documentation. + + \sa {QtQml.Models2::ObjectModel} */ + QQmlObjectModel::QQmlObjectModel(QObject *parent) : QQmlInstanceModel(*(new QQmlObjectModelPrivate), parent) { } /*! - \qmlattachedproperty int QtQuick2::VisualItemModel::index + \qmlattachedproperty int QtQml.Models2::ObjectModel::index This attached property holds the index of this delegate's item within the model. It is attached to each instance of the delegate. @@ -190,7 +208,7 @@ QQmlListProperty QQmlObjectModel::children() } /*! - \qmlproperty int QtQuick2::VisualItemModel::count + \qmlproperty int QtQml.Models2::ObjectModel::count The number of items in the model. This property is readonly. */ diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index ddc6f0e9b9..5bb9ac7df6 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -55,7 +55,6 @@ #include "qqmlxmlhttprequest_p.h" #include "qqmlscriptstring.h" #include "qqmlglobal_p.h" -#include "qqmllistmodel_p.h" #include "qquickworkerscript_p.h" #include "qqmlcomponent_p.h" #include "qqmlnetworkaccessmanagerfactory.h" @@ -89,10 +88,11 @@ #include -#include "qqmlbind_p.h" -#include "qqmlconnections_p.h" -#include "qqmltimer_p.h" -#include "qqmlplatform_p.h" +#include +#include +#include +#include +#include #include #include #include @@ -184,20 +184,20 @@ void QQmlEnginePrivate::registerBaseTypes(const char *uri, int versionMajor, int qmlRegisterType(uri, versionMajor, versionMinor,"Connections"); qmlRegisterType(uri, versionMajor, versionMinor,"Timer"); qmlRegisterCustomType(uri, versionMajor, versionMinor,"Connections", new QQmlConnectionsParser); - qmlRegisterType(uri, versionMajor, versionMinor, "ListElement"); - qmlRegisterCustomType(uri, versionMajor, versionMinor, "ListModel", new QQmlListModelParser); + qmlRegisterType(); } // These QtQuick types' implementation resides in the QtQml module void QQmlEnginePrivate::registerQtQuick2Types(const char *uri, int versionMajor, int versionMinor) { + qmlRegisterType(uri, versionMajor, versionMinor, "ListElement"); // Now in QtQml.Models, here for compatibility + qmlRegisterCustomType(uri, versionMajor, versionMinor, "ListModel", new QQmlListModelParser); // Now in QtQml.Models, here for compatibility qmlRegisterType(uri, versionMajor, versionMinor, "WorkerScript"); qmlRegisterType(uri, versionMajor, versionMinor, "Package"); qmlRegisterType(uri, versionMajor, versionMinor, "VisualDataModel"); - qmlRegisterType(uri, versionMajor, versionMinor, "VisualDataGroup"); + qmlRegisterType(uri, versionMajor, versionMinor, "VisualDataGroup"); qmlRegisterType(uri, versionMajor, versionMinor, "VisualItemModel"); - qmlRegisterType(); } void QQmlEnginePrivate::defineQtQuick2Module() diff --git a/src/qml/qml/qqmllistmodel.cpp b/src/qml/qml/qqmllistmodel.cpp index 5d64d37768..254eb58962 100644 --- a/src/qml/qml/qqmllistmodel.cpp +++ b/src/qml/qml/qqmllistmodel.cpp @@ -1422,6 +1422,17 @@ QQmlListModelParser::ListInstruction *QQmlListModelParser::ListModelData::instru return (QQmlListModelParser::ListInstruction *)((char *)this + sizeof(ListModelData)); } +/*! + \qmltype ListModel + \instantiates QQmlListModel + \inqmlmodule QtQml.Models 2 + \brief Defines a free-form list data source + + The ListModel is a simple container of ListElement definitions, each containing data roles. + The contents can be defined dynamically, or explicitly in QML. + + This type is also available in the QtQuick 2 import. For full documentation, see \l QtQuick2::ListModel +*/ /*! \qmltype ListModel \instantiates QQmlListModel @@ -2521,6 +2532,16 @@ bool QQmlListModelParser::definesEmptyList(const QString &s) } +/*! + \qmltype ListElement + \instantiates QQmlListElement + \inqmlmodule QtQml.Models 2 + \brief Defines a data item in a ListModel + + List elements are defined inside ListModel definitions, and represent items in a list. + + This type is also available in the QtQuick 2 import. For full documentation, see \l QtQuick2::ListElement +*/ /*! \qmltype ListElement \instantiates QQmlListElement diff --git a/tests/auto/qml/qml.pro b/tests/auto/qml/qml.pro index da705cefdb..e278d29162 100644 --- a/tests/auto/qml/qml.pro +++ b/tests/auto/qml/qml.pro @@ -24,6 +24,7 @@ PUBLICTESTS += \ qqmltranslation \ qqmlxmlhttprequest \ qqmlparser \ + qtqmlmodules \ qquickfolderlistmodel PRIVATETESTS += \ diff --git a/tests/auto/qml/qtqmlmodules/data/base.qml b/tests/auto/qml/qtqmlmodules/data/base.qml new file mode 100644 index 0000000000..942b270572 --- /dev/null +++ b/tests/auto/qml/qtqmlmodules/data/base.qml @@ -0,0 +1,14 @@ +import QtQml 2.0 + +QtObject { + property bool success: { + prop1 != undefined && + prop2 != undefined && + prop3 != undefined && + prop4 != undefined + } + property Component prop1: Component { QtObject {}} + property Timer prop2: Timer {} + property Connections prop3: Connections{} + property Binding prop4: Binding{} +} diff --git a/tests/auto/qml/qtqmlmodules/data/models.qml b/tests/auto/qml/qtqmlmodules/data/models.qml new file mode 100644 index 0000000000..d253565932 --- /dev/null +++ b/tests/auto/qml/qtqmlmodules/data/models.qml @@ -0,0 +1,15 @@ +import QtQml 2.0 +import QtQml.Models 2.1 + +QtObject { + property bool success: { + prop1 != undefined && + prop2 != undefined && + prop3 != undefined && + prop4 != undefined + } + property DelegateModelGroup prop1: DelegateModelGroup{} + property DelegateModel prop2: DelegateModel{} + property ObjectModel prop3: ObjectModel{} + property ListModel prop4: ListModel{ListElement{dummy: true}} +} diff --git a/tests/auto/qml/qtqmlmodules/data/unavailable.qml b/tests/auto/qml/qtqmlmodules/data/unavailable.qml new file mode 100644 index 0000000000..5841e3f677 --- /dev/null +++ b/tests/auto/qml/qtqmlmodules/data/unavailable.qml @@ -0,0 +1,35 @@ +import QtQml 2.0 + +QtObject { + id: root + property bool success: false; + Component.onCompleted: { + var strings = [ + "QtObject{}", + "Binding{}", + "Connections{}", + "Timer{}", + "Component{QtObject{}}", + "ListModel{ListElement{}}", + "ObjectModel{QtObject{}}", + "import QtQml 2.0 Item{}", + "import QtQml 2.0 ListModel{}", + "import QtQml 2.0 ObjectModel{}" + ]; + var idx; + for (idx in strings) { + var errored = false; + var item; + try { + item = Qt.createQmlObject(strings[idx], root); + } catch (err) { + errored = true; + } + if (!errored) { + console.log("It worked? ", item); + return; + } + } + root.success = true; + } +} diff --git a/tests/auto/qml/qtqmlmodules/qtqmlmodules.pro b/tests/auto/qml/qtqmlmodules/qtqmlmodules.pro new file mode 100644 index 0000000000..36ece8d7e0 --- /dev/null +++ b/tests/auto/qml/qtqmlmodules/qtqmlmodules.pro @@ -0,0 +1,12 @@ +CONFIG += testcase +TARGET = tst_qtqmlmodules +SOURCES += tst_qtqmlmodules.cpp + +include (../../shared/util.pri) + +macx:CONFIG -= app_bundle + +TESTDATA = data/* + +QT += core-private v8-private qml-private testlib gui gui-private +DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 diff --git a/tests/auto/qml/qtqmlmodules/tst_qtqmlmodules.cpp b/tests/auto/qml/qtqmlmodules/tst_qtqmlmodules.cpp new file mode 100644 index 0000000000..1e98c53694 --- /dev/null +++ b/tests/auto/qml/qtqmlmodules/tst_qtqmlmodules.cpp @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Research in Motion. +** Contact: http://www.qt-project.org/legal +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include +#include "../../shared/util.h" + +class tst_qtqmlmodules : public QQmlDataTest +{ + Q_OBJECT +public: + tst_qtqmlmodules() {} + +private slots: + void baseTypes(); + void modelsTypes(); + void unavailableTypes(); +}; + +void tst_qtqmlmodules::baseTypes() +{ + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("base.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + QVERIFY(object->property("success").toBool()); + + delete object; +} + +void tst_qtqmlmodules::modelsTypes() +{ + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("models.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + QVERIFY(object->property("success").toBool()); + + delete object; +} + +void tst_qtqmlmodules::unavailableTypes() +{ + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("unavailable.qml")); + QObject *object = component.create(); + QVERIFY(object != 0); + QVERIFY(object->property("success").toBool()); + + delete object; +} + +QTEST_MAIN(tst_qtqmlmodules) + +#include "tst_qtqmlmodules.moc" diff --git a/tests/auto/quick/qquickvisualdatamodel/tst_qquickvisualdatamodel.cpp b/tests/auto/quick/qquickvisualdatamodel/tst_qquickvisualdatamodel.cpp index f97b0f35a6..d16bf81d88 100644 --- a/tests/auto/quick/qquickvisualdatamodel/tst_qquickvisualdatamodel.cpp +++ b/tests/auto/quick/qquickvisualdatamodel/tst_qquickvisualdatamodel.cpp @@ -453,8 +453,8 @@ private: template void get_verify( const SingleRoleModel &model, QQmlDelegateModel *visualModel, - QQmlDataGroup *visibleItems, - QQmlDataGroup *selectedItems, + QQmlDelegateModelGroup *visibleItems, + QQmlDelegateModelGroup *selectedItems, const int (&mIndex)[N], const int (&iIndex)[N], const int (&vIndex)[N], @@ -1713,10 +1713,10 @@ void tst_qquickvisualdatamodel::groups() QQmlDelegateModel *visualModel = listview->findChild("visualModel"); QVERIFY(visualModel); - QQmlDataGroup *visibleItems = listview->findChild("visibleItems"); + QQmlDelegateModelGroup *visibleItems = listview->findChild("visibleItems"); QVERIFY(visibleItems); - QQmlDataGroup *selectedItems = listview->findChild("selectedItems"); + QQmlDelegateModelGroup *selectedItems = listview->findChild("selectedItems"); QVERIFY(selectedItems); const bool f = false; @@ -1940,8 +1940,8 @@ void tst_qquickvisualdatamodel::groups() template void tst_qquickvisualdatamodel::get_verify( const SingleRoleModel &model, QQmlDelegateModel *visualModel, - QQmlDataGroup *visibleItems, - QQmlDataGroup *selectedItems, + QQmlDelegateModelGroup *visibleItems, + QQmlDelegateModelGroup *selectedItems, const int (&mIndex)[N], const int (&iIndex)[N], const int (&vIndex)[N], @@ -2033,10 +2033,10 @@ void tst_qquickvisualdatamodel::get() QQmlDelegateModel *visualModel = qobject_cast(qvariant_cast(listview->model())); QVERIFY(visualModel); - QQmlDataGroup *visibleItems = visualModel->findChild("visibleItems"); + QQmlDelegateModelGroup *visibleItems = visualModel->findChild("visibleItems"); QVERIFY(visibleItems); - QQmlDataGroup *selectedItems = visualModel->findChild("selectedItems"); + QQmlDelegateModelGroup *selectedItems = visualModel->findChild("selectedItems"); QVERIFY(selectedItems); QV8Engine *v8Engine = QQmlEnginePrivate::getV8Engine(ctxt->engine()); @@ -2154,7 +2154,7 @@ void tst_qquickvisualdatamodel::get() void tst_qquickvisualdatamodel::invalidGroups() { QUrl source = testFileUrl("groups-invalid.qml"); - QTest::ignoreMessage(QtWarningMsg, (source.toString() + ":12:9: QML VisualDataGroup: " + QQmlDataGroup::tr("Group names must start with a lower case letter")).toUtf8()); + QTest::ignoreMessage(QtWarningMsg, (source.toString() + ":12:9: QML VisualDataGroup: " + QQmlDelegateModelGroup::tr("Group names must start with a lower case letter")).toUtf8()); QQmlComponent component(&engine, source); QScopedPointer object(component.create()); @@ -3665,67 +3665,67 @@ void tst_qquickvisualdatamodel::warnings_data() QTest::newRow("insert < 0") << testFileUrl("listmodelproperties.qml") << QString("items.insert(-2, {\"number\": \"eight\"})") - << (": QML VisualDataGroup: " + QQmlDataGroup::tr("insert: index out of range")) + << (": QML VisualDataGroup: " + QQmlDelegateModelGroup::tr("insert: index out of range")) << 4; QTest::newRow("insert > length") << testFileUrl("listmodelproperties.qml") << QString("items.insert(8, {\"number\": \"eight\"})") - << (": QML VisualDataGroup: " + QQmlDataGroup::tr("insert: index out of range")) + << (": QML VisualDataGroup: " + QQmlDelegateModelGroup::tr("insert: index out of range")) << 4; QTest::newRow("create < 0") << testFileUrl("listmodelproperties.qml") << QString("items.create(-2, {\"number\": \"eight\"})") - << (": QML VisualDataGroup: " + QQmlDataGroup::tr("create: index out of range")) + << (": QML VisualDataGroup: " + QQmlDelegateModelGroup::tr("create: index out of range")) << 4; QTest::newRow("create > length") << testFileUrl("listmodelproperties.qml") << QString("items.create(8, {\"number\": \"eight\"})") - << (": QML VisualDataGroup: " + QQmlDataGroup::tr("create: index out of range")) + << (": QML VisualDataGroup: " + QQmlDelegateModelGroup::tr("create: index out of range")) << 4; QTest::newRow("resolve from < 0") << testFileUrl("listmodelproperties.qml") << QString("items.resolve(-2, 3)") - << (": QML VisualDataGroup: " + QQmlDataGroup::tr("resolve: from index out of range")) + << (": QML VisualDataGroup: " + QQmlDelegateModelGroup::tr("resolve: from index out of range")) << 4; QTest::newRow("resolve from > length") << testFileUrl("listmodelproperties.qml") << QString("items.resolve(8, 3)") - << (": QML VisualDataGroup: " + QQmlDataGroup::tr("resolve: from index out of range")) + << (": QML VisualDataGroup: " + QQmlDelegateModelGroup::tr("resolve: from index out of range")) << 4; QTest::newRow("resolve to < 0") << testFileUrl("listmodelproperties.qml") << QString("items.resolve(3, -2)") - << (": QML VisualDataGroup: " + QQmlDataGroup::tr("resolve: to index out of range")) + << (": QML VisualDataGroup: " + QQmlDelegateModelGroup::tr("resolve: to index out of range")) << 4; QTest::newRow("resolve to > length") << testFileUrl("listmodelproperties.qml") << QString("items.resolve(3, 8)") - << (": QML VisualDataGroup: " + QQmlDataGroup::tr("resolve: to index out of range")) + << (": QML VisualDataGroup: " + QQmlDelegateModelGroup::tr("resolve: to index out of range")) << 4; QTest::newRow("resolve from invalid index") << testFileUrl("listmodelproperties.qml") << QString("items.resolve(\"two\", 3)") - << (": QML VisualDataGroup: " + QQmlDataGroup::tr("resolve: from index invalid")) + << (": QML VisualDataGroup: " + QQmlDelegateModelGroup::tr("resolve: from index invalid")) << 4; QTest::newRow("resolve to invalid index") << testFileUrl("listmodelproperties.qml") << QString("items.resolve(3, \"two\")") - << (": QML VisualDataGroup: " + QQmlDataGroup::tr("resolve: to index invalid")) + << (": QML VisualDataGroup: " + QQmlDelegateModelGroup::tr("resolve: to index invalid")) << 4; QTest::newRow("resolve already resolved item") << testFileUrl("listmodelproperties.qml") << QString("items.resolve(3, 2)") - << (": QML VisualDataGroup: " + QQmlDataGroup::tr("resolve: from is not an unresolved item")) + << (": QML VisualDataGroup: " + QQmlDelegateModelGroup::tr("resolve: from is not an unresolved item")) << 4; QTest::newRow("resolve already resolved item") @@ -3733,193 +3733,193 @@ void tst_qquickvisualdatamodel::warnings_data() << QString("{ items.insert(0, {\"number\": \"eight\"});" "items.insert(1, {\"number\": \"seven\"});" "items.resolve(0, 1)}") - << (": QML VisualDataGroup: " + QQmlDataGroup::tr("resolve: to is not a model item")) + << (": QML VisualDataGroup: " + QQmlDelegateModelGroup::tr("resolve: to is not a model item")) << 6; QTest::newRow("remove index < 0") << testFileUrl("listmodelproperties.qml") << QString("items.remove(-2, 1)") - << (": QML VisualDataGroup: " + QQmlDataGroup::tr("remove: index out of range")) + << (": QML VisualDataGroup: " + QQmlDelegateModelGroup::tr("remove: index out of range")) << 4; QTest::newRow("remove index == length") << testFileUrl("listmodelproperties.qml") << QString("items.remove(4, 1)") - << (": QML VisualDataGroup: " + QQmlDataGroup::tr("remove: index out of range")) + << (": QML VisualDataGroup: " + QQmlDelegateModelGroup::tr("remove: index out of range")) << 4; QTest::newRow("remove index > length") << testFileUrl("listmodelproperties.qml") << QString("items.remove(9, 1)") - << (": QML VisualDataGroup: " + QQmlDataGroup::tr("remove: index out of range")) + << (": QML VisualDataGroup: " + QQmlDelegateModelGroup::tr("remove: index out of range")) << 4; QTest::newRow("remove invalid index") << testFileUrl("listmodelproperties.qml") << QString("items.remove(\"nine\", 1)") - << (": QML VisualDataGroup: " + QQmlDataGroup::tr("remove: invalid index")) + << (": QML VisualDataGroup: " + QQmlDelegateModelGroup::tr("remove: invalid index")) << 4; QTest::newRow("remove count < 0") << testFileUrl("listmodelproperties.qml") << QString("items.remove(1, -2)") - << (": QML VisualDataGroup: " + QQmlDataGroup::tr("remove: invalid count")) + << (": QML VisualDataGroup: " + QQmlDelegateModelGroup::tr("remove: invalid count")) << 4; QTest::newRow("remove index + count > length") << testFileUrl("listmodelproperties.qml") << QString("items.remove(2, 4, \"selected\")") - << (": QML VisualDataGroup: " + QQmlDataGroup::tr("remove: invalid count")) + << (": QML VisualDataGroup: " + QQmlDelegateModelGroup::tr("remove: invalid count")) << 4; QTest::newRow("addGroups index < 0") << testFileUrl("listmodelproperties.qml") << QString("items.addGroups(-2, 1, \"selected\")") - << (": QML VisualDataGroup: " + QQmlDataGroup::tr("addGroups: index out of range")) + << (": QML VisualDataGroup: " + QQmlDelegateModelGroup::tr("addGroups: index out of range")) << 4; QTest::newRow("addGroups index == length") << testFileUrl("listmodelproperties.qml") << QString("items.addGroups(4, 1, \"selected\")") - << (": QML VisualDataGroup: " + QQmlDataGroup::tr("addGroups: index out of range")) + << (": QML VisualDataGroup: " + QQmlDelegateModelGroup::tr("addGroups: index out of range")) << 4; QTest::newRow("addGroups index > length") << testFileUrl("listmodelproperties.qml") << QString("items.addGroups(9, 1, \"selected\")") - << (": QML VisualDataGroup: " + QQmlDataGroup::tr("addGroups: index out of range")) + << (": QML VisualDataGroup: " + QQmlDelegateModelGroup::tr("addGroups: index out of range")) << 4; QTest::newRow("addGroups count < 0") << testFileUrl("listmodelproperties.qml") << QString("items.addGroups(1, -2, \"selected\")") - << (": QML VisualDataGroup: " + QQmlDataGroup::tr("addGroups: invalid count")) + << (": QML VisualDataGroup: " + QQmlDelegateModelGroup::tr("addGroups: invalid count")) << 4; QTest::newRow("addGroups index + count > length") << testFileUrl("listmodelproperties.qml") << QString("items.addGroups(2, 4, \"selected\")") - << (": QML VisualDataGroup: " + QQmlDataGroup::tr("addGroups: invalid count")) + << (": QML VisualDataGroup: " + QQmlDelegateModelGroup::tr("addGroups: invalid count")) << 4; QTest::newRow("removeGroups index < 0") << testFileUrl("listmodelproperties.qml") << QString("items.removeGroups(-2, 1, \"selected\")") - << (": QML VisualDataGroup: " + QQmlDataGroup::tr("removeGroups: index out of range")) + << (": QML VisualDataGroup: " + QQmlDelegateModelGroup::tr("removeGroups: index out of range")) << 4; QTest::newRow("removeGroups index == length") << testFileUrl("listmodelproperties.qml") << QString("items.removeGroups(4, 1, \"selected\")") - << (": QML VisualDataGroup: " + QQmlDataGroup::tr("removeGroups: index out of range")) + << (": QML VisualDataGroup: " + QQmlDelegateModelGroup::tr("removeGroups: index out of range")) << 4; QTest::newRow("removeGroups index > length") << testFileUrl("listmodelproperties.qml") << QString("items.removeGroups(9, 1, \"selected\")") - << (": QML VisualDataGroup: " + QQmlDataGroup::tr("removeGroups: index out of range")) + << (": QML VisualDataGroup: " + QQmlDelegateModelGroup::tr("removeGroups: index out of range")) << 4; QTest::newRow("removeGroups count < 0") << testFileUrl("listmodelproperties.qml") << QString("items.removeGroups(1, -2, \"selected\")") - << (": QML VisualDataGroup: " + QQmlDataGroup::tr("removeGroups: invalid count")) + << (": QML VisualDataGroup: " + QQmlDelegateModelGroup::tr("removeGroups: invalid count")) << 4; QTest::newRow("removeGroups index + count > length") << testFileUrl("listmodelproperties.qml") << QString("items.removeGroups(2, 4, \"selected\")") - << (": QML VisualDataGroup: " + QQmlDataGroup::tr("removeGroups: invalid count")) + << (": QML VisualDataGroup: " + QQmlDelegateModelGroup::tr("removeGroups: invalid count")) << 4; QTest::newRow("setGroups index < 0") << testFileUrl("listmodelproperties.qml") << QString("items.setGroups(-2, 1, \"selected\")") - << (": QML VisualDataGroup: " + QQmlDataGroup::tr("setGroups: index out of range")) + << (": QML VisualDataGroup: " + QQmlDelegateModelGroup::tr("setGroups: index out of range")) << 4; QTest::newRow("setGroups index == length") << testFileUrl("listmodelproperties.qml") << QString("items.setGroups(4, 1, \"selected\")") - << (": QML VisualDataGroup: " + QQmlDataGroup::tr("setGroups: index out of range")) + << (": QML VisualDataGroup: " + QQmlDelegateModelGroup::tr("setGroups: index out of range")) << 4; QTest::newRow("setGroups index > length") << testFileUrl("listmodelproperties.qml") << QString("items.setGroups(9, 1, \"selected\")") - << (": QML VisualDataGroup: " + QQmlDataGroup::tr("setGroups: index out of range")) + << (": QML VisualDataGroup: " + QQmlDelegateModelGroup::tr("setGroups: index out of range")) << 4; QTest::newRow("setGroups count < 0") << testFileUrl("listmodelproperties.qml") << QString("items.setGroups(1, -2, \"selected\")") - << (": QML VisualDataGroup: " + QQmlDataGroup::tr("setGroups: invalid count")) + << (": QML VisualDataGroup: " + QQmlDelegateModelGroup::tr("setGroups: invalid count")) << 4; QTest::newRow("setGroups index + count > length") << testFileUrl("listmodelproperties.qml") << QString("items.setGroups(2, 4, \"selected\")") - << (": QML VisualDataGroup: " + QQmlDataGroup::tr("setGroups: invalid count")) + << (": QML VisualDataGroup: " + QQmlDelegateModelGroup::tr("setGroups: invalid count")) << 4; QTest::newRow("move from < 0") << testFileUrl("listmodelproperties.qml") << QString("items.move(-2, 1, 1)") - << (": QML VisualDataGroup: " + QQmlDataGroup::tr("move: from index out of range")) + << (": QML VisualDataGroup: " + QQmlDelegateModelGroup::tr("move: from index out of range")) << 4; QTest::newRow("move from == length") << testFileUrl("listmodelproperties.qml") << QString("items.move(4, 1, 1)") - << (": QML VisualDataGroup: " + QQmlDataGroup::tr("move: from index out of range")) + << (": QML VisualDataGroup: " + QQmlDelegateModelGroup::tr("move: from index out of range")) << 4; QTest::newRow("move from > length") << testFileUrl("listmodelproperties.qml") << QString("items.move(9, 1, 1)") - << (": QML VisualDataGroup: " + QQmlDataGroup::tr("move: from index out of range")) + << (": QML VisualDataGroup: " + QQmlDelegateModelGroup::tr("move: from index out of range")) << 4; QTest::newRow("move invalid from") << testFileUrl("listmodelproperties.qml") << QString("items.move(\"nine\", 1, 1)") - << (": QML VisualDataGroup: " + QQmlDataGroup::tr("move: invalid from index")) + << (": QML VisualDataGroup: " + QQmlDelegateModelGroup::tr("move: invalid from index")) << 4; QTest::newRow("move to < 0") << testFileUrl("listmodelproperties.qml") << QString("items.move(1, -2, 1)") - << (": QML VisualDataGroup: " + QQmlDataGroup::tr("move: to index out of range")) + << (": QML VisualDataGroup: " + QQmlDelegateModelGroup::tr("move: to index out of range")) << 4; QTest::newRow("move to == length") << testFileUrl("listmodelproperties.qml") << QString("items.move(1, 4, 1)") - << (": QML VisualDataGroup: " + QQmlDataGroup::tr("move: to index out of range")) + << (": QML VisualDataGroup: " + QQmlDelegateModelGroup::tr("move: to index out of range")) << 4; QTest::newRow("move to > length") << testFileUrl("listmodelproperties.qml") << QString("items.move(1, 9, 1)") - << (": QML VisualDataGroup: " + QQmlDataGroup::tr("move: to index out of range")) + << (": QML VisualDataGroup: " + QQmlDelegateModelGroup::tr("move: to index out of range")) << 4; QTest::newRow("move invalid to") << testFileUrl("listmodelproperties.qml") << QString("items.move(1, \"nine\", 1)") - << (": QML VisualDataGroup: " + QQmlDataGroup::tr("move: invalid to index")) + << (": QML VisualDataGroup: " + QQmlDelegateModelGroup::tr("move: invalid to index")) << 4; QTest::newRow("move count < 0") << testFileUrl("listmodelproperties.qml") << QString("items.move(1, 1, -2)") - << (": QML VisualDataGroup: " + QQmlDataGroup::tr("move: invalid count")) + << (": QML VisualDataGroup: " + QQmlDelegateModelGroup::tr("move: invalid count")) << 4; QTest::newRow("move from + count > length") << testFileUrl("listmodelproperties.qml") << QString("items.move(2, 1, 4)") - << (": QML VisualDataGroup: " + QQmlDataGroup::tr("move: from index out of range")) + << (": QML VisualDataGroup: " + QQmlDelegateModelGroup::tr("move: from index out of range")) << 4; } -- cgit v1.2.3 From 28113c7c8b74f957be812b61b861efc5bac12c3e Mon Sep 17 00:00:00 2001 From: Oswald Buddenhagen Date: Tue, 12 Mar 2013 20:26:13 +0100 Subject: don't use CamelCase includes they are not available during bootstrap. Change-Id: I5e941f22cd2b9fa622730ba0bf8389394f6a30a5 Reviewed-by: Thiago Macieira Reviewed-by: Alan Alpert (Personal) <416365416c@gmail.com> --- src/qml/qml/parser/qqmljs.g | 17 +++++++++-------- src/qml/qml/parser/qqmljsast_p.h | 2 +- src/qml/qml/parser/qqmljsengine_p.cpp | 6 +++--- src/qml/qml/parser/qqmljsengine_p.h | 4 ++-- src/qml/qml/parser/qqmljslexer.cpp | 6 +++--- src/qml/qml/parser/qqmljslexer_p.h | 3 ++- src/qml/qml/parser/qqmljsparser.cpp | 12 ++++++------ src/qml/qml/parser/qqmljsparser_p.h | 4 ++-- 8 files changed, 28 insertions(+), 26 deletions(-) diff --git a/src/qml/qml/parser/qqmljs.g b/src/qml/qml/parser/qqmljs.g index b4307909ea..ff4f54374b 100644 --- a/src/qml/qml/parser/qqmljs.g +++ b/src/qml/qml/parser/qqmljs.g @@ -127,16 +127,16 @@ ** ****************************************************************************/ -#include -#include - -#include - #include "qqmljsengine_p.h" #include "qqmljslexer_p.h" #include "qqmljsast_p.h" #include "qqmljsmemorypool_p.h" +#include +#include + +#include + ./ /:/**************************************************************************** @@ -212,8 +212,8 @@ #include "qqmljsast_p.h" #include "qqmljsengine_p.h" -#include -#include +#include +#include QT_QML_BEGIN_NAMESPACE @@ -391,7 +391,8 @@ protected: /. #include "qqmljsparser_p.h" -#include + +#include // // W A R N I N G diff --git a/src/qml/qml/parser/qqmljsast_p.h b/src/qml/qml/parser/qqmljsast_p.h index 4c4af9ce9c..01a872f1e8 100644 --- a/src/qml/qml/parser/qqmljsast_p.h +++ b/src/qml/qml/parser/qqmljsast_p.h @@ -57,7 +57,7 @@ #include "qqmljsglobal_p.h" #include "qqmljsmemorypool_p.h" -#include +#include QT_QML_BEGIN_NAMESPACE diff --git a/src/qml/qml/parser/qqmljsengine_p.cpp b/src/qml/qml/parser/qqmljsengine_p.cpp index 9a61a62e3b..7dc3b6f6cb 100644 --- a/src/qml/qml/parser/qqmljsengine_p.cpp +++ b/src/qml/qml/parser/qqmljsengine_p.cpp @@ -42,9 +42,9 @@ #include "qqmljsengine_p.h" #include "qqmljsglobal_p.h" -#include -#include -#include +#include +#include +#include QT_QML_BEGIN_NAMESPACE diff --git a/src/qml/qml/parser/qqmljsengine_p.h b/src/qml/qml/parser/qqmljsengine_p.h index f1729c0526..4f58e7f8ea 100644 --- a/src/qml/qml/parser/qqmljsengine_p.h +++ b/src/qml/qml/parser/qqmljsengine_p.h @@ -57,8 +57,8 @@ #include "qqmljsastfwd_p.h" #include "qqmljsmemorypool_p.h" -#include -#include +#include +#include QT_QML_BEGIN_NAMESPACE diff --git a/src/qml/qml/parser/qqmljslexer.cpp b/src/qml/qml/parser/qqmljslexer.cpp index 9175efe054..edd85ec878 100644 --- a/src/qml/qml/parser/qqmljslexer.cpp +++ b/src/qml/qml/parser/qqmljslexer.cpp @@ -44,9 +44,9 @@ #include "qqmljsmemorypool_p.h" #include "qqmljskeywords_p.h" -#include -#include -#include +#include +#include +#include QT_BEGIN_NAMESPACE Q_CORE_EXPORT double qstrtod(const char *s00, char const **se, bool *ok); diff --git a/src/qml/qml/parser/qqmljslexer_p.h b/src/qml/qml/parser/qqmljslexer_p.h index e7e15cbf94..e1b51da92b 100644 --- a/src/qml/qml/parser/qqmljslexer_p.h +++ b/src/qml/qml/parser/qqmljslexer_p.h @@ -55,7 +55,8 @@ #include "qqmljsglobal_p.h" #include "qqmljsgrammar_p.h" -#include + +#include QT_QML_BEGIN_NAMESPACE diff --git a/src/qml/qml/parser/qqmljsparser.cpp b/src/qml/qml/parser/qqmljsparser.cpp index bc03b57525..a0fa7a4711 100644 --- a/src/qml/qml/parser/qqmljsparser.cpp +++ b/src/qml/qml/parser/qqmljsparser.cpp @@ -39,20 +39,20 @@ ** ****************************************************************************/ -#include -#include - -#include - #include "qqmljsengine_p.h" #include "qqmljslexer_p.h" #include "qqmljsast_p.h" #include "qqmljsmemorypool_p.h" +#include +#include + +#include #include "qqmljsparser_p.h" -#include + +#include // // W A R N I N G diff --git a/src/qml/qml/parser/qqmljsparser_p.h b/src/qml/qml/parser/qqmljsparser_p.h index 7becd5f5c8..1b13690547 100644 --- a/src/qml/qml/parser/qqmljsparser_p.h +++ b/src/qml/qml/parser/qqmljsparser_p.h @@ -71,8 +71,8 @@ #include "qqmljsast_p.h" #include "qqmljsengine_p.h" -#include -#include +#include +#include QT_QML_BEGIN_NAMESPACE -- cgit v1.2.3 From aa8c2f93d89cabdd7708b9bbb8d45d824e88a71f Mon Sep 17 00:00:00 2001 From: Gunnar Sletta Date: Wed, 13 Mar 2013 07:36:30 +0100 Subject: Abort rendering when QOpenGLContext::create/makeCurrent fails. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task-number: QTBUG-30158 Change-Id: Ic8239fe6f074c989e4474d46042e1a82796b4908 Reviewed-by: Samuel Rødal --- src/quick/scenegraph/qsgrenderloop.cpp | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/quick/scenegraph/qsgrenderloop.cpp b/src/quick/scenegraph/qsgrenderloop.cpp index 85ef549397..a55658bf71 100644 --- a/src/quick/scenegraph/qsgrenderloop.cpp +++ b/src/quick/scenegraph/qsgrenderloop.cpp @@ -245,20 +245,28 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window) return; } + bool current = false; + if (!gl) { gl = new QOpenGLContext(); gl->setFormat(masterWindow->requestedFormat()); - gl->create(); - if (!gl->makeCurrent(masterWindow)) - qWarning("QQuickWindow: makeCurrent() failed..."); - sg->initialize(gl); + if (!gl->create()) { + delete gl; + gl = 0; + } + current = gl->makeCurrent(masterWindow); + if (current) + sg->initialize(gl); } else { - gl->makeCurrent(masterWindow); + current = gl->makeCurrent(masterWindow); } bool alsoSwap = data.updatePending; data.updatePending = false; + if (!current) + return; + QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window); cd->polishItems(); -- cgit v1.2.3 From 02ac995b882fc839fac940a12461f80af1dc2d21 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Tue, 5 Mar 2013 09:26:45 +0100 Subject: Documentation and versioning for new Window properties Even some of the properties that existed in 5.0 were not documented. Change-Id: I25a14b9b19425a6c792d06bc41983e9abd1d17d0 Task-number: QTBUG-29807 Reviewed-by: J-P Nurmi --- .../doc/images/screen-and-window-dimensions.jpg | Bin 0 -> 50403 bytes src/quick/items/qquickwindow.cpp | 127 ++++++++++++++++++++- src/quick/items/qquickwindowmodule.cpp | 2 + 3 files changed, 126 insertions(+), 3 deletions(-) create mode 100644 src/quick/doc/images/screen-and-window-dimensions.jpg diff --git a/src/quick/doc/images/screen-and-window-dimensions.jpg b/src/quick/doc/images/screen-and-window-dimensions.jpg new file mode 100644 index 0000000000..98c8b06df6 Binary files /dev/null and b/src/quick/doc/images/screen-and-window-dimensions.jpg differ diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 1732251cf2..53b731b3f1 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -833,11 +833,11 @@ void QQuickWindowPrivate::cleanup(QSGNode *n) \brief Creates a new top-level window The Window object creates a new top-level window for a QtQuick scene. It automatically sets up the - window for use with QtQuick 2.0 graphical types. + window for use with QtQuick 2.x graphical types. To use this type, you will need to import the module with the following line: \code - import QtQuick.Window 2.0 + import QtQuick.Window 2.1 \endcode Restricting this import will allow you to have a QML environment without access to window system features. @@ -2850,7 +2850,7 @@ QColor QQuickWindow::color() const */ /*! - \qmlproperty string QtQuick.Window2::Window::modality + \qmlproperty Qt::WindowModality QtQuick.Window2::Window::modality The modality of the window. @@ -2859,6 +2859,127 @@ QColor QQuickWindow::color() const and Qt.ApplicationModal. */ +/*! + \qmlproperty Qt::WindowFlags QtQuick.Window2::Window::flags + + The window flags of the window. + + The window flags control the window's appearance in the windowing system, + whether it's a dialog, popup, or a regular window, and whether it should + have a title bar, etc. + + The flags which you read from this property might differ from the ones + that you set if the requested flags could not be fulfilled. + */ + +/*! + \qmlproperty int QtQuick.Window2::Window::x + \qmlproperty int QtQuick.Window2::Window::y + \qmlproperty int QtQuick.Window2::Window::width + \qmlproperty int QtQuick.Window2::Window::height + + Defines the window's position and size. + + The (x,y) position is relative to the \l Screen if there is only one, + or to the virtual desktop (arrangement of multiple screens). + + \qml + Window { x: 100; y: 100; width: 100; height: 100 } + \endqml + + \image screen-and-window-dimensions.jpg + */ + +/*! + \qmlproperty int QtQuick.Window2::Window::minimumWidth + \qmlproperty int QtQuick.Window2::Window::minimumHeight + \since Qt 5.1 + + Defines the window's minimum size. + + This is a hint to the window manager to prevent resizing below the specified + width and height. + */ + +/*! + \qmlproperty int QtQuick.Window2::Window::maximumWidth + \qmlproperty int QtQuick.Window2::Window::maximumHeight + \since Qt 5.1 + + Defines the window's maximum size. + + This is a hint to the window manager to prevent resizing above the specified + width and height. + */ + +/*! + \qmlproperty bool QtQuick.Window2::Window::visible + + Whether the window is visible on the screen. + + Setting visible to false is the same as setting \l visibility to Hidden. + + \sa visibility + */ + +/*! + \qmlproperty QWindow::Visibility QtQuick.Window2::Window::visibility + + The screen-occupation state of the window. + + Visibility is whether the window should appear in the windowing system as + normal, minimized, maximized, fullscreen or hidden. + + To set the visibility to AutomaticVisibility means to give the window a + default visible state, which might be fullscreen or windowed depending on + the platform. However when reading the visibility property you will always + get the actual state, never AutomaticVisibility. + + When a window is not visible its visibility is Hidden, and setting + visibility to Hidden is the same as setting \l visible to false. + + \sa visible + \since Qt 5.1 + */ + +/*! + \qmlproperty Qt::ScreenOrientation QtQuick.Window2::Window::contentOrientation + + This is a hint to the window manager in case it needs to display + additional content like popups, dialogs, status bars, or similar + in relation to the window. + + The recommended orientation is \l Screen.orientation, but + an application doesn't have to support all possible orientations, + and thus can opt to ignore the current screen orientation. + + The difference between the window and the content orientation + determines how much to rotate the content by. + + The default value is Qt::PrimaryOrientation. + + \sa Screen + + \since Qt 5.1 + */ + +/*! + \qmlproperty real QtQuick.Window2::Window::opacity + + The opacity of the window. + + If the windowing system supports window opacity, this can be used to fade the + window in and out, or to make it semitransparent. + + A value of 1.0 or above is treated as fully opaque, whereas a value of 0.0 or below + is treated as fully transparent. Values inbetween represent varying levels of + translucency between the two extremes. + + The default value is 1.0. + + \since Qt 5.1 + */ + #include "moc_qquickwindow.cpp" QT_END_NAMESPACE diff --git a/src/quick/items/qquickwindowmodule.cpp b/src/quick/items/qquickwindowmodule.cpp index 8d2d583607..f826a53a29 100644 --- a/src/quick/items/qquickwindowmodule.cpp +++ b/src/quick/items/qquickwindowmodule.cpp @@ -50,6 +50,8 @@ void QQuickWindowModule::defineModule() const char uri[] = "QtQuick.Window"; qmlRegisterType(uri, 2, 0, "Window"); + qmlRegisterRevision(uri, 2, 1); + qmlRegisterType(uri, 2, 1, "Window"); qmlRegisterUncreatableType(uri, 2, 0, "Screen", QStringLiteral("Screen can only be used via the attached property.")); } -- cgit v1.2.3 From ddb4935823e9682cbe9fa03869ee5d1771c15feb Mon Sep 17 00:00:00 2001 From: Thomas McGuire Date: Wed, 13 Mar 2013 10:43:51 +0100 Subject: Fix usages of \since for QML items \since uses the QML import, not the Qt version. When adding a new property, it needs a REVISION argument and the type needs to be registered again for the new version. Change-Id: I2e636e9d26c8e989729eadad2ef73a836c35caa1 Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickitem.cpp | 2 +- src/quick/items/qquickitemsmodule.cpp | 1 + src/quick/items/qquickpositioners.cpp | 4 ++-- src/quick/items/qquicktextedit.cpp | 2 +- src/quick/items/qquicktextedit_p.h | 2 +- 5 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index 67c4fd2140..022aaa7e3b 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -3887,7 +3887,7 @@ void QQuickItem::forceActiveFocus() This method sets focus on the item and ensures that all ancestor FocusScope objects in the object hierarchy are also given \l focus. - \since 5.1 + \since QtQuick 2.1 \sa activeFocus, Qt::FocusReason */ diff --git a/src/quick/items/qquickitemsmodule.cpp b/src/quick/items/qquickitemsmodule.cpp index 5321f5e588..20c35f42f2 100644 --- a/src/quick/items/qquickitemsmodule.cpp +++ b/src/quick/items/qquickitemsmodule.cpp @@ -228,6 +228,7 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor) #endif qmlRegisterType(uri, 2, 1, "Grid"); + qmlRegisterType(uri, 2, 1, "TextEdit"); } void QQuickItemsModule::defineModule() diff --git a/src/quick/items/qquickpositioners.cpp b/src/quick/items/qquickpositioners.cpp index df80e77bfa..4a74c0bfba 100644 --- a/src/quick/items/qquickpositioners.cpp +++ b/src/quick/items/qquickpositioners.cpp @@ -1180,7 +1180,7 @@ void QQuickGrid::setFlow(Flow flow) By default this property is not set. \sa columnSpacing - \since QtQuick2.0 + \since QtQuick 2.0 */ void QQuickGrid::setRowSpacing(const qreal rowSpacing) { @@ -1202,7 +1202,7 @@ void QQuickGrid::setRowSpacing(const qreal rowSpacing) By default this property is not set. \sa rowSpacing - \since QtQuick2.0 + \since QtQuick 2.0 */ void QQuickGrid::setColumnSpacing(const qreal columnSpacing) { diff --git a/src/quick/items/qquicktextedit.cpp b/src/quick/items/qquicktextedit.cpp index 9d28bc7963..0fe5458c34 100644 --- a/src/quick/items/qquicktextedit.cpp +++ b/src/quick/items/qquicktextedit.cpp @@ -2190,7 +2190,7 @@ void QQuickTextEdit::remove(int start, int end) /*! \qmlproperty TextDocument QtQuick2::TextEdit::textDocument - \since 5.1 + \since QtQuick 2.1 Returns the QQuickTextDocument of this TextEdit. It can be used to implement syntax highlighting using diff --git a/src/quick/items/qquicktextedit_p.h b/src/quick/items/qquicktextedit_p.h index 149d26f98e..255c8ac670 100644 --- a/src/quick/items/qquicktextedit_p.h +++ b/src/quick/items/qquicktextedit_p.h @@ -100,7 +100,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickTextEdit : public QQuickImplicitSizeItem #endif Q_PROPERTY(QUrl baseUrl READ baseUrl WRITE setBaseUrl RESET resetBaseUrl NOTIFY baseUrlChanged) Q_PROPERTY(RenderType renderType READ renderType WRITE setRenderType NOTIFY renderTypeChanged) - Q_PROPERTY(QQuickTextDocument *textDocument READ textDocument FINAL) + Q_PROPERTY(QQuickTextDocument *textDocument READ textDocument FINAL REVISION 1) public: QQuickTextEdit(QQuickItem *parent=0); -- cgit v1.2.3 From 8fee64d8d6dd1d33523fa91830304a29a8d89f05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Nowacki?= Date: Tue, 12 Mar 2013 11:03:51 +0100 Subject: Polish usage of a QElapsedTime instance in QSGThreadedRenderLoop The patch should make valgrind happy, by not using an uninitialized memory. Change-Id: Iec7f16c56f250dd121a37f03da4cfc5d9e5c0742 Reviewed-by: Gunnar Sletta --- src/quick/scenegraph/qsgthreadedrenderloop.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/quick/scenegraph/qsgthreadedrenderloop.cpp b/src/quick/scenegraph/qsgthreadedrenderloop.cpp index 95409a04f7..ef50d5a89d 100644 --- a/src/quick/scenegraph/qsgthreadedrenderloop.cpp +++ b/src/quick/scenegraph/qsgthreadedrenderloop.cpp @@ -969,6 +969,7 @@ void QSGThreadedRenderLoop::polishAndSync() QElapsedTimer timer; int polishTime = 0; int waitTime = 0; + int syncTime; if (qquick_window_timing) timer.start(); #endif @@ -1002,7 +1003,8 @@ void QSGThreadedRenderLoop::polishAndSync() RLDEBUG("GUI: - unlocked after sync..."); #ifndef QSG_NO_WINDOW_TIMING - int syncTime = timer.elapsed(); + if (qquick_window_timing) + syncTime = timer.elapsed(); #endif killTimer(m_update_timer); -- cgit v1.2.3 From a1a7679028eda395d74cd1247a8c3ed46ac3bef1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20R=C3=B8dal?= Date: Wed, 13 Mar 2013 16:10:06 +0100 Subject: Compile fix. Some compilers don't allow the first argument to printf and similar functions to be a non-literal string. Change-Id: Idd11ae6679d5c0585b5d10b76c991dcfdb4f65da Reviewed-by: Gunnar Sletta --- src/qml/qml/qqmlimport.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp index 5374f2d1ad..d3ec9e67f7 100644 --- a/src/qml/qml/qqmlimport.cpp +++ b/src/qml/qml/qqmlimport.cpp @@ -150,7 +150,7 @@ QQmlType *getTypeForUrl(const QString &urlString, const QHashedStringRef& typeNa if (!ret) {//Usually when a type name is "found" but invalid //qDebug() << ret << urlString << QQmlMetaType::qmlType(url); if (!errors) // Cannot list errors properly, just quit - qFatal(QQmlMetaType::typeRegistrationFailures().join('\n').toLatin1().constData()); + qFatal("%s", QQmlMetaType::typeRegistrationFailures().join('\n').toLatin1().constData()); QQmlError error; error.setDescription(QQmlMetaType::typeRegistrationFailures().join('\n')); errors->prepend(error); -- cgit v1.2.3 From c64ab7db9856488a216e063052c8aeb6d36628a8 Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Mon, 4 Mar 2013 21:45:24 +0100 Subject: Add some docs, improve scope of var. Change-Id: I62e0b3bb4882f58ba58b3c8d71679e4ad2dbc5a3 Reviewed-by: Caroline Chao --- src/quick/items/qquickitem.cpp | 5 +++++ src/quick/items/qquickwindow.cpp | 7 ++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index 022aaa7e3b..0b3d91ffe5 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -1534,6 +1534,11 @@ void QQuickItemPrivate::setAccessibleFlagAndListener() } } +/*! +Clears all sub focus items from \a scope. +If \a focus is true, sets the scope's subFocusItem +to be this item. +*/ void QQuickItemPrivate::updateSubFocusItem(QQuickItem *scope, bool focus) { Q_Q(QQuickItem); diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 53b731b3f1..48e2aceebe 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -608,6 +608,11 @@ void QQuickWindowPrivate::translateTouchEvent(QTouchEvent *touchEvent) touchEvent->setTouchPoints(touchPoints); } +/*! +Set the focus inside \a scope to be \a item. +If the scope contains the active focus item, it will be changed to \a item. +Calls notifyFocusChangesRecur for all changed items. +*/ void QQuickWindowPrivate::setFocusInScope(QQuickItem *scope, QQuickItem *item, Qt::FocusReason reason, FocusOptions options) { Q_Q(QQuickWindow); @@ -627,13 +632,13 @@ void QQuickWindowPrivate::setFocusInScope(QQuickItem *scope, QQuickItem *item, Q QQuickItemPrivate *scopePrivate = scope ? QQuickItemPrivate::get(scope) : 0; QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); - QQuickItem *oldActiveFocusItem = 0; QQuickItem *newActiveFocusItem = 0; QVarLengthArray changed; // Does this change the active focus? if (item == contentItem || (scopePrivate->activeFocus && item->isEnabled())) { + QQuickItem *oldActiveFocusItem = 0; oldActiveFocusItem = activeFocusItem; newActiveFocusItem = item; while (newActiveFocusItem->isFocusScope() -- cgit v1.2.3 From 2050601125eeb72950a7d718b5136d16c898f8e6 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Fri, 8 Mar 2013 16:44:32 +0100 Subject: Cosmetic changes in common controls for examples Use system palette colors. SimpleLauncherDelegate has its own "button" rather than using the shared one, which really wasn't suitable for any other purpose. So now example apps can use Button, and ToolButton isn't necessary. Change-Id: I632397f36b96a26c32a86301ddacb85d5c3221f0 Reviewed-by: Jens Bache-Wiig --- examples/quick/dialogs/FileDialogs.qml | 36 ++++----- examples/quick/shared/Button.qml | 60 +++++++-------- examples/quick/shared/CheckBox.qml | 92 +++++++++++----------- examples/quick/shared/SimpleLauncherDelegate.qml | 45 ++++++++++- examples/quick/shared/ToolButton.qml | 93 ----------------------- examples/quick/shared/images/checkmark.png | Bin 0 -> 809 bytes examples/quick/shared/qmldir | 1 - examples/quick/shared/quick_shared.qrc | 1 - examples/quick/shared/shared.qrc | 1 - 9 files changed, 131 insertions(+), 198 deletions(-) delete mode 100644 examples/quick/shared/ToolButton.qml create mode 100644 examples/quick/shared/images/checkmark.png diff --git a/examples/quick/dialogs/FileDialogs.qml b/examples/quick/dialogs/FileDialogs.qml index 3a987ff86f..a6df29bb37 100644 --- a/examples/quick/dialogs/FileDialogs.qml +++ b/examples/quick/dialogs/FileDialogs.qml @@ -46,19 +46,15 @@ Rectangle { width: 580 height: 360 - color: "#444444" + color: palette.window + SystemPalette { id: palette } Rectangle { id: toolbar width: parent.width height: 40 - border.color: "black" - gradient: Gradient { - GradientStop { position: 0.0; color: "#444444" } - GradientStop { position: 0.3; color: "grey" } - GradientStop { position: 0.85; color: "grey" } - GradientStop { position: 1.0; color: "white" } - } + color: Qt.darker(palette.window, 1.1) + border.color: Qt.darker(palette.window, 1.3) Row { spacing: 6 anchors.verticalCenter: parent.verticalCenter @@ -66,17 +62,17 @@ Rectangle { anchors.leftMargin: 8 height: parent.height - 6 width: parent.width - ToolButton { + Button { text: "Open" anchors.verticalCenter: parent.verticalCenter onClicked: fileDialog.open() } - ToolButton { + Button { text: "Close" anchors.verticalCenter: parent.verticalCenter onClicked: fileDialog.close() } - ToolButton { + Button { text: "/tmp" anchors.verticalCenter: parent.verticalCenter // TODO: QTBUG-29814 This isn't portable, but we don't expose QDir::tempPath to QML yet. @@ -107,7 +103,7 @@ Rectangle { anchors.margins: 8 spacing: 8 Text { - color: "white" + color: palette.windowText font.bold: true text: "File dialog properties:" } @@ -139,23 +135,29 @@ Rectangle { Binding on checked { value: fileDialog.visible } } Text { - color: "#EEEEDD" + color: palette.windowText text: "current view folder: " + fileDialog.folder } Text { - color: "#EEEEDD" - text: "name filters: {" + fileDialog.nameFilters + "}; current filter: " + fileDialog.selectedNameFilter + color: palette.windowText + text: "name filters: {" + fileDialog.nameFilters + "}" + width: parent.width + wrapMode: Text.Wrap + } + Text { + color: palette.windowText + text: "current filter:" + fileDialog.selectedNameFilter width: parent.width wrapMode: Text.Wrap } Text { - color: "#EEEEDD" + color: palette.windowText text: "chosen files: " + fileDialog.fileUrls width: parent.width wrapMode: Text.Wrap } Text { - color: "#EEEEDD" + color: palette.windowText text: "chosen single path: " + fileDialog.fileUrl width: parent.width wrapMode: Text.Wrap diff --git a/examples/quick/shared/Button.qml b/examples/quick/shared/Button.qml index 9bbc01ac01..9942a17b5c 100644 --- a/examples/quick/shared/Button.qml +++ b/examples/quick/shared/Button.qml @@ -43,14 +43,31 @@ import QtQuick 2.0 Item { id: container - property string text: "Button" - property string subText: "" + property alias text: buttonLabel.text + property alias label: buttonLabel signal clicked property alias containsMouse: mouseArea.containsMouse property alias pressed: mouseArea.pressed - implicitHeight: col.height - height: implicitHeight - width: buttonLabel.width + 20 + implicitHeight: buttonLabel.implicitHeight + implicitWidth: buttonLabel.implicitWidth + height: buttonLabel.implicitHeight + 12 + width: Math.max(80, implicitWidth + 8) + + SystemPalette { id: palette } + + Rectangle { + id: frame + anchors.fill: parent + color: palette.button + gradient: Gradient { + GradientStop { position: 0.0; color: mouseArea.pressed ? Qt.darker(palette.button, 1.3) : palette.button } + GradientStop { position: 1.0; color: Qt.darker(palette.button, 1.3) } + } + antialiasing: true + radius: 5 + border.color: Qt.darker(palette.button, 1.5) + border.width: 1 + } MouseArea { id: mouseArea @@ -59,33 +76,12 @@ Item { hoverEnabled: true } - Column { - spacing: 2 - id: col - anchors.verticalCenter: parent.verticalCenter + Text { + id: buttonLabel width: parent.width - Text { - id: buttonLabel - anchors.left: parent.left - anchors.leftMargin: 10 - anchors.right: parent.right - anchors.rightMargin: 10 - text: container.text - color: "black" - font.pixelSize: 22 - wrapMode: Text.WrapAtWordBoundaryOrAnywhere - styleColor: "white" - style: Text.Raised - - } - Text { - id: buttonLabel2 - anchors.left: parent.left - anchors.leftMargin: 10 - text: container.subText - wrapMode: Text.WrapAtWordBoundaryOrAnywhere - color: "#666" - font.pixelSize: 12 - } + horizontalAlignment: Text.Center + text: container.text + color: palette.buttonText + anchors.verticalCenter: parent.verticalCenter } } diff --git a/examples/quick/shared/CheckBox.qml b/examples/quick/shared/CheckBox.qml index 1d29430934..a3a22b7723 100644 --- a/examples/quick/shared/CheckBox.qml +++ b/examples/quick/shared/CheckBox.qml @@ -41,62 +41,56 @@ import QtQuick 2.0 Item { - height: label.height + 4 - width: label.width + height + 4 + id: root + implicitHeight: frame.height + implicitWidth: row.implicitWidth + width: implicitWidth + height: implicitHeight property alias text: label.text property bool checked property alias pressed: mouseArea.pressed - Rectangle { - antialiasing: true - border.color: "white" - color: "transparent" - anchors.fill: gradientRect - anchors.rightMargin: 1 - anchors.bottomMargin: 1 - radius: 3 - } - Rectangle { - border.color: "black" - anchors.fill: gradientRect - anchors.leftMargin: 1 - anchors.topMargin: 1 - radius: 3 - } - Rectangle { - id: gradientRect - gradient: Gradient { - GradientStop { position: 0.0; color: mouseArea.pressed ? "darkgrey" : "#CCCCCC" } - GradientStop { position: 0.6; color: "#887766" } - GradientStop { position: 1.0; color: mouseArea.pressed ? "white" : "#333333" } - } - anchors.verticalCenter: parent.verticalCenter - height: parent.height - width: height - anchors.margins: 1 - radius: 3 - } - Text { - id: theX - anchors.centerIn: gradientRect - text: checked ? "✓" : "" - } - Text { - anchors.centerIn: gradientRect - anchors.horizontalCenterOffset: 0.5 - anchors.verticalCenterOffset: 0.5 - color: "white" - text: theX.text - } - Text { - id: label - color: "#EEEEDD" - anchors.left: gradientRect.right - anchors.leftMargin: 6 + signal clicked + + SystemPalette { id: palette } + + Row { + id: row anchors.verticalCenter: parent.verticalCenter + spacing: 6 + Rectangle { + id: frame + gradient: Gradient { + GradientStop { position: 0.0; color: mouseArea.pressed ? Qt.darker(palette.button, 1.3) : palette.button } + GradientStop { position: 1.0; color: Qt.darker(palette.button, 1.3) } + } + height: label.implicitHeight * 1.5 + width: height + anchors.margins: 1 + radius: 3 + antialiasing: true + border.color: Qt.darker(palette.button, 1.5) + Image { + id: theX + source: "images/checkmark.png" + anchors.fill: frame + anchors.margins: frame.width / 5 + fillMode: Image.PreserveAspectFit + smooth: true + visible: checked + } + } + Text { + id: label + color: palette.text + anchors.verticalCenter: frame.verticalCenter + } } MouseArea { id: mouseArea anchors.fill: parent - onClicked: parent.checked = !parent.checked + onClicked: { + parent.checked = !parent.checked + parent.clicked() + } } } diff --git a/examples/quick/shared/SimpleLauncherDelegate.qml b/examples/quick/shared/SimpleLauncherDelegate.qml index e89126690f..48dd34fd41 100644 --- a/examples/quick/shared/SimpleLauncherDelegate.qml +++ b/examples/quick/shared/SimpleLauncherDelegate.qml @@ -68,15 +68,52 @@ Rectangle { anchors.rightMargin: 16 } - Button { + Item { id: button anchors.top: parent.top anchors.left: parent.left anchors.bottom: parent.bottom anchors.right:image.left - text: name - subText: description - onClicked: exampleItem.exampleUrl = url; + implicitHeight: col.height + height: implicitHeight + width: buttonLabel.width + 20 + + MouseArea { + id: mouseArea + anchors.fill: parent + onClicked: exampleItem.exampleUrl = url + hoverEnabled: true + } + + Column { + spacing: 2 + id: col + anchors.verticalCenter: parent.verticalCenter + width: parent.width + Text { + id: buttonLabel + anchors.left: parent.left + anchors.leftMargin: 10 + anchors.right: parent.right + anchors.rightMargin: 10 + text: name + color: "black" + font.pixelSize: 22 + wrapMode: Text.WrapAtWordBoundaryOrAnywhere + styleColor: "white" + style: Text.Raised + + } + Text { + id: buttonLabel2 + anchors.left: parent.left + anchors.leftMargin: 10 + text: description + wrapMode: Text.WrapAtWordBoundaryOrAnywhere + color: "#666" + font.pixelSize: 12 + } + } } Rectangle { diff --git a/examples/quick/shared/ToolButton.qml b/examples/quick/shared/ToolButton.qml deleted file mode 100644 index 1af0d13dcf..0000000000 --- a/examples/quick/shared/ToolButton.qml +++ /dev/null @@ -1,93 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the examples of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:BSD$ -** You may use this file under the terms of the BSD license as follows: -** -** "Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions are -** met: -** * Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** * Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in -** the documentation and/or other materials provided with the -** distribution. -** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names -** of its contributors may be used to endorse or promote products derived -** from this software without specific prior written permission. -** -** -** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -import QtQuick 2.0 - -Item { - height: parent.height - 4 - width: height * 2 - anchors.verticalCenter: parent.verticalCenter - property alias text: label.text - property string tooltip - signal clicked - Rectangle { - antialiasing: true - border.color: "white" - color: "transparent" - anchors.fill: parent - anchors.rightMargin: 1 - anchors.bottomMargin: 1 - radius: 3 - } - Rectangle { - border.color: "black" - anchors.fill: parent - anchors.leftMargin: 1 - anchors.topMargin: 1 - radius: 3 - } - Rectangle { - gradient: Gradient { - GradientStop { position: 0.0; color: mouseArea.pressed ? "darkgrey" : "#CCCCCC" } - GradientStop { position: 0.6; color: "#887766" } - GradientStop { position: 1.0; color: mouseArea.pressed ? "white" : "#333333" } - } - anchors.fill: parent - anchors.margins: 1 - radius: 3 - } - Text { - id: label - anchors.centerIn: parent - } - Text { - anchors.centerIn: parent - anchors.horizontalCenterOffset: 0.5 - anchors.verticalCenterOffset: 0.5 - color: "white" - text: label.text - } - - MouseArea { - id: mouseArea - anchors.fill: parent - onClicked: parent.clicked() - } -} diff --git a/examples/quick/shared/images/checkmark.png b/examples/quick/shared/images/checkmark.png new file mode 100644 index 0000000000..821aafccdd Binary files /dev/null and b/examples/quick/shared/images/checkmark.png differ diff --git a/examples/quick/shared/qmldir b/examples/quick/shared/qmldir index 68bd573f85..cc4eb3c793 100644 --- a/examples/quick/shared/qmldir +++ b/examples/quick/shared/qmldir @@ -3,4 +3,3 @@ CheckBox 2.1 CheckBox.qml LauncherList 2.0 LauncherList.qml SimpleLauncherDelegate 2.0 SimpleLauncherDelegate.qml Slider 2.0 Slider.qml -ToolButton 2.1 ToolButton.qml diff --git a/examples/quick/shared/quick_shared.qrc b/examples/quick/shared/quick_shared.qrc index aa6a761e0d..ee706712dd 100644 --- a/examples/quick/shared/quick_shared.qrc +++ b/examples/quick/shared/quick_shared.qrc @@ -4,7 +4,6 @@ SimpleLauncherDelegate.qml Button.qml CheckBox.qml - ToolButton.qml images/back.png images/next.png diff --git a/examples/quick/shared/shared.qrc b/examples/quick/shared/shared.qrc index cbc782f2c7..0b574ac879 100644 --- a/examples/quick/shared/shared.qrc +++ b/examples/quick/shared/shared.qrc @@ -6,7 +6,6 @@ Slider.qml images/slider_handle.png CheckBox.qml - ToolButton.qml images/back.png images/next.png -- cgit v1.2.3 From 78b3aed3264439cd95ca23a47e53e15b769a58ae Mon Sep 17 00:00:00 2001 From: Alan Alpert Date: Wed, 23 Jan 2013 15:17:27 -0800 Subject: Move QML types to types folder The QtQml module has gain a bunch of QML types cluttering up the qml folder. Moving them to types organizes them a bit better. Change-Id: I570884c00f4abc48f4f1aea048bf002bc70223f3 Reviewed-by: Gabriel de Dietrich Reviewed-by: Lars Knoll --- src/qml/items/items.pri | 12 - src/qml/items/qqmldelegatemodel.cpp | 3189 ---------------------------- src/qml/items/qqmldelegatemodel_p.h | 234 -- src/qml/items/qqmldelegatemodel_p_p.h | 411 ---- src/qml/items/qqmlmodelsmodule.cpp | 60 - src/qml/items/qqmlmodelsmodule_p.h | 57 - src/qml/items/qqmlobjectmodel.cpp | 269 --- src/qml/items/qqmlobjectmodel_p.h | 170 -- src/qml/items/qquickpackage.cpp | 198 -- src/qml/items/qquickpackage_p.h | 92 - src/qml/qml.pro | 2 +- src/qml/qml/qml.pri | 19 +- src/qml/qml/qqmlbind.cpp | 310 --- src/qml/qml/qqmlbind_p.h | 92 - src/qml/qml/qqmlconnections.cpp | 321 --- src/qml/qml/qqmlconnections_p.h | 96 - src/qml/qml/qqmlengine.cpp | 2 +- src/qml/qml/qqmllistmodel.cpp | 2588 ---------------------- src/qml/qml/qqmllistmodel_p.h | 198 -- src/qml/qml/qqmllistmodel_p_p.h | 378 ---- src/qml/qml/qqmllistmodelworkeragent.cpp | 259 --- src/qml/qml/qqmllistmodelworkeragent_p.h | 156 -- src/qml/qml/qqmltimer.cpp | 328 --- src/qml/qml/qqmltimer_p.h | 107 - src/qml/qml/qquickworkerscript.cpp | 740 ------- src/qml/qml/qquickworkerscript_p.h | 126 -- src/qml/types/qqmlbind.cpp | 310 +++ src/qml/types/qqmlbind_p.h | 92 + src/qml/types/qqmlconnections.cpp | 321 +++ src/qml/types/qqmlconnections_p.h | 96 + src/qml/types/qqmldelegatemodel.cpp | 3189 ++++++++++++++++++++++++++++ src/qml/types/qqmldelegatemodel_p.h | 234 ++ src/qml/types/qqmldelegatemodel_p_p.h | 411 ++++ src/qml/types/qqmllistmodel.cpp | 2588 ++++++++++++++++++++++ src/qml/types/qqmllistmodel_p.h | 198 ++ src/qml/types/qqmllistmodel_p_p.h | 378 ++++ src/qml/types/qqmllistmodelworkeragent.cpp | 259 +++ src/qml/types/qqmllistmodelworkeragent_p.h | 156 ++ src/qml/types/qqmlmodelsmodule.cpp | 60 + src/qml/types/qqmlmodelsmodule_p.h | 57 + src/qml/types/qqmlobjectmodel.cpp | 269 +++ src/qml/types/qqmlobjectmodel_p.h | 170 ++ src/qml/types/qqmltimer.cpp | 328 +++ src/qml/types/qqmltimer_p.h | 107 + src/qml/types/qquickpackage.cpp | 198 ++ src/qml/types/qquickpackage_p.h | 92 + src/qml/types/qquickworkerscript.cpp | 740 +++++++ src/qml/types/qquickworkerscript_p.h | 126 ++ src/qml/types/types.pri | 26 + 49 files changed, 10410 insertions(+), 10409 deletions(-) delete mode 100644 src/qml/items/items.pri delete mode 100644 src/qml/items/qqmldelegatemodel.cpp delete mode 100644 src/qml/items/qqmldelegatemodel_p.h delete mode 100644 src/qml/items/qqmldelegatemodel_p_p.h delete mode 100644 src/qml/items/qqmlmodelsmodule.cpp delete mode 100644 src/qml/items/qqmlmodelsmodule_p.h delete mode 100644 src/qml/items/qqmlobjectmodel.cpp delete mode 100644 src/qml/items/qqmlobjectmodel_p.h delete mode 100644 src/qml/items/qquickpackage.cpp delete mode 100644 src/qml/items/qquickpackage_p.h delete mode 100644 src/qml/qml/qqmlbind.cpp delete mode 100644 src/qml/qml/qqmlbind_p.h delete mode 100644 src/qml/qml/qqmlconnections.cpp delete mode 100644 src/qml/qml/qqmlconnections_p.h delete mode 100644 src/qml/qml/qqmllistmodel.cpp delete mode 100644 src/qml/qml/qqmllistmodel_p.h delete mode 100644 src/qml/qml/qqmllistmodel_p_p.h delete mode 100644 src/qml/qml/qqmllistmodelworkeragent.cpp delete mode 100644 src/qml/qml/qqmllistmodelworkeragent_p.h delete mode 100644 src/qml/qml/qqmltimer.cpp delete mode 100644 src/qml/qml/qqmltimer_p.h delete mode 100644 src/qml/qml/qquickworkerscript.cpp delete mode 100644 src/qml/qml/qquickworkerscript_p.h create mode 100644 src/qml/types/qqmlbind.cpp create mode 100644 src/qml/types/qqmlbind_p.h create mode 100644 src/qml/types/qqmlconnections.cpp create mode 100644 src/qml/types/qqmlconnections_p.h create mode 100644 src/qml/types/qqmldelegatemodel.cpp create mode 100644 src/qml/types/qqmldelegatemodel_p.h create mode 100644 src/qml/types/qqmldelegatemodel_p_p.h create mode 100644 src/qml/types/qqmllistmodel.cpp create mode 100644 src/qml/types/qqmllistmodel_p.h create mode 100644 src/qml/types/qqmllistmodel_p_p.h create mode 100644 src/qml/types/qqmllistmodelworkeragent.cpp create mode 100644 src/qml/types/qqmllistmodelworkeragent_p.h create mode 100644 src/qml/types/qqmlmodelsmodule.cpp create mode 100644 src/qml/types/qqmlmodelsmodule_p.h create mode 100644 src/qml/types/qqmlobjectmodel.cpp create mode 100644 src/qml/types/qqmlobjectmodel_p.h create mode 100644 src/qml/types/qqmltimer.cpp create mode 100644 src/qml/types/qqmltimer_p.h create mode 100644 src/qml/types/qquickpackage.cpp create mode 100644 src/qml/types/qquickpackage_p.h create mode 100644 src/qml/types/qquickworkerscript.cpp create mode 100644 src/qml/types/qquickworkerscript_p.h create mode 100644 src/qml/types/types.pri diff --git a/src/qml/items/items.pri b/src/qml/items/items.pri deleted file mode 100644 index f5224f0acc..0000000000 --- a/src/qml/items/items.pri +++ /dev/null @@ -1,12 +0,0 @@ -SOURCES += \ - $$PWD/qquickpackage.cpp \ - $$PWD/qqmldelegatemodel.cpp \ - $$PWD/qqmlobjectmodel.cpp \ - $$PWD/qqmlmodelsmodule.cpp - -HEADERS += \ - $$PWD/qquickpackage_p.h \ - $$PWD/qqmldelegatemodel_p.h \ - $$PWD/qqmldelegatemodel_p_p.h \ - $$PWD/qqmlobjectmodel_p.h \ - $$PWD/qqmlmodelsmodule_p.h diff --git a/src/qml/items/qqmldelegatemodel.cpp b/src/qml/items/qqmldelegatemodel.cpp deleted file mode 100644 index efbd98bdbc..0000000000 --- a/src/qml/items/qqmldelegatemodel.cpp +++ /dev/null @@ -1,3189 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** 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, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qqmldelegatemodel_p_p.h" - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE - -class QQmlDelegateModelEngineData : public QV8Engine::Deletable -{ -public: - enum - { - Model, - Groups, - IsUnresolved, - ItemsIndex, - PersistedItemsIndex, - InItems, - InPersistedItems, - StringCount - }; - - QQmlDelegateModelEngineData(QV8Engine *engine); - ~QQmlDelegateModelEngineData(); - - v8::Local array( - QV8Engine *engine, const QVector &changes); - v8::Local array( - QV8Engine *engine, const QVector &changes); - v8::Local array( - QV8Engine *engine, const QVector &changes); - - - inline v8::Local model() { return strings->Get(Model)->ToString(); } - inline v8::Local groups() { return strings->Get(Groups)->ToString(); } - inline v8::Local isUnresolved() { return strings->Get(IsUnresolved)->ToString(); } - inline v8::Local itemsIndex() { return strings->Get(ItemsIndex)->ToString(); } - inline v8::Local persistedItemsIndex() { return strings->Get(PersistedItemsIndex)->ToString(); } - inline v8::Local inItems() { return strings->Get(InItems)->ToString(); } - inline v8::Local inPersistedItems() { return strings->Get(InPersistedItems)->ToString(); } - - v8::Persistent strings; - v8::Persistent constructorChange; - v8::Persistent constructorChangeArray; -}; - -V8_DEFINE_EXTENSION(QQmlDelegateModelEngineData, engineData) - - -void QQmlDelegateModelPartsMetaObject::propertyCreated(int, QMetaPropertyBuilder &prop) -{ - prop.setWritable(false); -} - -QVariant QQmlDelegateModelPartsMetaObject::initialValue(int id) -{ - QQmlDelegateModelParts *parts = static_cast(object()); - QQmlPartsModel *m = new QQmlPartsModel( - parts->model, QString::fromUtf8(name(id)), parts); - parts->models.append(m); - return QVariant::fromValue(static_cast(m)); -} - -QQmlDelegateModelParts::QQmlDelegateModelParts(QQmlDelegateModel *parent) -: QObject(parent), model(parent) -{ - new QQmlDelegateModelPartsMetaObject(this); -} - -//--------------------------------------------------------------------------- - -/*! - \qmltype VisualDataModel - \instantiates QQmlDelegateModel - \inqmlmodule QtQuick 2 - \ingroup qtquick-models - \brief Encapsulates a model and delegate - - The VisualDataModel type encapsulates a model and the delegate that will - be instantiated for items in a model. - - This type is provided by QtQuick 2 for compatibility reasons. The same implementation - is now primarily available as DelegateModel in the QtQml.Models module. - - \sa {QtQml.Models2::DelegateModel} -*/ -/*! - \qmltype DelegateModel - \instantiates QQmlDelegateModel - \inqmlmodule QtQml.Models 2 - \brief Encapsulates a model and delegate - - The DelegateModel type encapsulates a model and the delegate that will - be instantiated for items in the model. - - This element is also available as DelegateModel in the QtQuick module. For full details, - see the \l DelegateModel documentation. - - The DelegateModel type encapsulates a model and the delegate that will - be instantiated for items in the model. - - It is usually not necessary to create a DelegateModel. - However, it can be useful for manipulating and accessing the \l modelIndex - when a QAbstractItemModel subclass is used as the - model. Also, DelegateModel is used together with \l Package to - provide delegates to multiple views, and with DelegateModelGroup to sort and filter - delegate items. - - The example below illustrates using a DelegateModel with a ListView. - - \snippet qml/visualdatamodel.qml 0 -*/ - -QQmlDelegateModelPrivate::QQmlDelegateModelPrivate(QQmlContext *ctxt) - : m_delegate(0) - , m_cacheMetaType(0) - , m_context(ctxt) - , m_parts(0) - , m_filterGroup(QStringLiteral("items")) - , m_count(0) - , m_groupCount(Compositor::MinimumGroupCount) - , m_compositorGroup(Compositor::Cache) - , m_complete(false) - , m_delegateValidated(false) - , m_reset(false) - , m_transaction(false) - , m_incubatorCleanupScheduled(false) - , m_cacheItems(0) - , m_items(0) - , m_persistedItems(0) -{ -} - -QQmlDelegateModelPrivate::~QQmlDelegateModelPrivate() -{ - qDeleteAll(m_finishedIncubating); - - if (m_cacheMetaType) - m_cacheMetaType->release(); -} - -void QQmlDelegateModelPrivate::init() -{ - Q_Q(QQmlDelegateModel); - m_compositor.setRemoveGroups(Compositor::GroupMask & ~Compositor::PersistedFlag); - - m_items = new QQmlDelegateModelGroup(QStringLiteral("items"), q, Compositor::Default, q); - m_items->setDefaultInclude(true); - m_persistedItems = new QQmlDelegateModelGroup(QStringLiteral("persistedItems"), q, Compositor::Persisted, q); - QQmlDelegateModelGroupPrivate::get(m_items)->emitters.insert(this); -} - -QQmlDelegateModel::QQmlDelegateModel() -: QQmlInstanceModel(*(new QQmlDelegateModelPrivate(0))) -{ - Q_D(QQmlDelegateModel); - d->init(); -} - -QQmlDelegateModel::QQmlDelegateModel(QQmlContext *ctxt, QObject *parent) -: QQmlInstanceModel(*(new QQmlDelegateModelPrivate(ctxt)), parent) -{ - Q_D(QQmlDelegateModel); - d->init(); -} - -QQmlDelegateModel::~QQmlDelegateModel() -{ - Q_D(QQmlDelegateModel); - - foreach (QQmlDelegateModelItem *cacheItem, d->m_cache) { - if (cacheItem->object) { - delete cacheItem->object; - - cacheItem->object = 0; - cacheItem->contextData->destroy(); - cacheItem->contextData = 0; - cacheItem->scriptRef -= 1; - } - cacheItem->groups &= ~Compositor::UnresolvedFlag; - cacheItem->objectRef = 0; - if (!cacheItem->isReferenced()) - delete cacheItem; - } -} - - -void QQmlDelegateModel::classBegin() -{ - Q_D(QQmlDelegateModel); - if (!d->m_context) - d->m_context = qmlContext(this); -} - -void QQmlDelegateModel::componentComplete() -{ - Q_D(QQmlDelegateModel); - d->m_complete = true; - - int defaultGroups = 0; - QStringList groupNames; - groupNames.append(QStringLiteral("items")); - groupNames.append(QStringLiteral("persistedItems")); - if (QQmlDelegateModelGroupPrivate::get(d->m_items)->defaultInclude) - defaultGroups |= Compositor::DefaultFlag; - if (QQmlDelegateModelGroupPrivate::get(d->m_persistedItems)->defaultInclude) - defaultGroups |= Compositor::PersistedFlag; - for (int i = Compositor::MinimumGroupCount; i < d->m_groupCount; ++i) { - QString name = d->m_groups[i]->name(); - if (name.isEmpty()) { - d->m_groups[i] = d->m_groups[d->m_groupCount - 1]; - --d->m_groupCount; - --i; - } else if (name.at(0).isUpper()) { - qmlInfo(d->m_groups[i]) << QQmlDelegateModelGroup::tr("Group names must start with a lower case letter"); - d->m_groups[i] = d->m_groups[d->m_groupCount - 1]; - --d->m_groupCount; - --i; - } else { - groupNames.append(name); - - QQmlDelegateModelGroupPrivate *group = QQmlDelegateModelGroupPrivate::get(d->m_groups[i]); - group->setModel(this, Compositor::Group(i)); - if (group->defaultInclude) - defaultGroups |= (1 << i); - } - } - - d->m_cacheMetaType = new QQmlDelegateModelItemMetaType( - QQmlEnginePrivate::getV8Engine(d->m_context->engine()), this, groupNames); - - d->m_compositor.setGroupCount(d->m_groupCount); - d->m_compositor.setDefaultGroups(defaultGroups); - d->updateFilterGroup(); - - while (!d->m_pendingParts.isEmpty()) - static_cast(d->m_pendingParts.first())->updateFilterGroup(); - - QVector inserts; - d->m_count = d->m_adaptorModel.count(); - d->m_compositor.append( - &d->m_adaptorModel, - 0, - d->m_count, - defaultGroups | Compositor::AppendFlag | Compositor::PrependFlag, - &inserts); - d->itemsInserted(inserts); - d->emitChanges(); - - if (d->m_adaptorModel.canFetchMore()) - QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest)); -} - -/*! - \qmlproperty model QtQml.Models2::DelegateModel::model - This property holds the model providing data for the DelegateModel. - - The model provides a set of data that is used to create the items - for a view. For large or dynamic datasets the model is usually - provided by a C++ model object. The C++ model object must be a \l - {QAbstractItemModel} subclass or a simple list. - - Models can also be created directly in QML, using a \l{ListModel} or - \l{XmlListModel}. - - \sa {qml-data-models}{Data Models} -*/ -QVariant QQmlDelegateModel::model() const -{ - Q_D(const QQmlDelegateModel); - return d->m_adaptorModel.model(); -} - -void QQmlDelegateModel::setModel(const QVariant &model) -{ - Q_D(QQmlDelegateModel); - - if (d->m_complete) - _q_itemsRemoved(0, d->m_count); - - d->m_adaptorModel.setModel(model, this, d->m_context->engine()); - d->m_adaptorModel.replaceWatchedRoles(QList(), d->m_watchedRoles); - for (int i = 0; d->m_parts && i < d->m_parts->models.count(); ++i) { - d->m_adaptorModel.replaceWatchedRoles( - QList(), d->m_parts->models.at(i)->watchedRoles()); - } - - if (d->m_complete) { - _q_itemsInserted(0, d->m_adaptorModel.count()); - if (d->m_adaptorModel.canFetchMore()) - QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest)); - } -} - -/*! - \qmlproperty Component QtQml.Models2::DelegateModel::delegate - - The delegate provides a template defining each item instantiated by a view. - The index is exposed as an accessible \c index property. Properties of the - model are also available depending upon the type of \l {qml-data-models}{Data Model}. -*/ -QQmlComponent *QQmlDelegateModel::delegate() const -{ - Q_D(const QQmlDelegateModel); - return d->m_delegate; -} - -void QQmlDelegateModel::setDelegate(QQmlComponent *delegate) -{ - Q_D(QQmlDelegateModel); - if (d->m_transaction) { - qmlInfo(this) << tr("The delegate of a DelegateModel cannot be changed within onUpdated."); - return; - } - bool wasValid = d->m_delegate != 0; - d->m_delegate = delegate; - d->m_delegateValidated = false; - if (wasValid && d->m_complete) { - for (int i = 1; i < d->m_groupCount; ++i) { - QQmlDelegateModelGroupPrivate::get(d->m_groups[i])->changeSet.remove( - 0, d->m_compositor.count(Compositor::Group(i))); - } - } - if (d->m_complete && d->m_delegate) { - for (int i = 1; i < d->m_groupCount; ++i) { - QQmlDelegateModelGroupPrivate::get(d->m_groups[i])->changeSet.insert( - 0, d->m_compositor.count(Compositor::Group(i))); - } - } - d->emitChanges(); -} - -/*! - \qmlproperty QModelIndex QtQml.Models2::DelegateModel::rootIndex - - QAbstractItemModel provides a hierarchical tree of data, whereas - QML only operates on list data. \c rootIndex allows the children of - any node in a QAbstractItemModel to be provided by this model. - - This property only affects models of type QAbstractItemModel that - are hierarchical (e.g, a tree model). - - For example, here is a simple interactive file system browser. - When a directory name is clicked, the view's \c rootIndex is set to the - QModelIndex node of the clicked directory, thus updating the view to show - the new directory's contents. - - \c main.cpp: - \snippet qml/visualdatamodel_rootindex/main.cpp 0 - - \c view.qml: - \snippet qml/visualdatamodel_rootindex/view.qml 0 - - If the \l model is a QAbstractItemModel subclass, the delegate can also - reference a \c hasModelChildren property (optionally qualified by a - \e model. prefix) that indicates whether the delegate's model item has - any child nodes. - - - \sa modelIndex(), parentModelIndex() -*/ -QVariant QQmlDelegateModel::rootIndex() const -{ - Q_D(const QQmlDelegateModel); - return QVariant::fromValue(QModelIndex(d->m_adaptorModel.rootIndex)); -} - -void QQmlDelegateModel::setRootIndex(const QVariant &root) -{ - Q_D(QQmlDelegateModel); - - QModelIndex modelIndex = qvariant_cast(root); - const bool changed = d->m_adaptorModel.rootIndex != modelIndex; - if (changed || !d->m_adaptorModel.isValid()) { - const int oldCount = d->m_count; - d->m_adaptorModel.rootIndex = modelIndex; - if (!d->m_adaptorModel.isValid() && d->m_adaptorModel.aim()) // The previous root index was invalidated, so we need to reconnect the model. - d->m_adaptorModel.setModel(d->m_adaptorModel.list.list(), this, d->m_context->engine()); - if (d->m_adaptorModel.canFetchMore()) - d->m_adaptorModel.fetchMore(); - if (d->m_complete) { - const int newCount = d->m_adaptorModel.count(); - if (oldCount) - _q_itemsRemoved(0, oldCount); - if (newCount) - _q_itemsInserted(0, newCount); - } - if (changed) - emit rootIndexChanged(); - } -} - -/*! - \qmlmethod QModelIndex QtQml.Models2::DelegateModel::modelIndex(int index) - - QAbstractItemModel provides a hierarchical tree of data, whereas - QML only operates on list data. This function assists in using - tree models in QML. - - Returns a QModelIndex for the specified index. - This value can be assigned to rootIndex. - - \sa rootIndex -*/ -QVariant QQmlDelegateModel::modelIndex(int idx) const -{ - Q_D(const QQmlDelegateModel); - return d->m_adaptorModel.modelIndex(idx); -} - -/*! - \qmlmethod QModelIndex QtQml.Models2::DelegateModel::parentModelIndex() - - QAbstractItemModel provides a hierarchical tree of data, whereas - QML only operates on list data. This function assists in using - tree models in QML. - - Returns a QModelIndex for the parent of the current rootIndex. - This value can be assigned to rootIndex. - - \sa rootIndex -*/ -QVariant QQmlDelegateModel::parentModelIndex() const -{ - Q_D(const QQmlDelegateModel); - return d->m_adaptorModel.parentModelIndex(); -} - -/*! - \qmlproperty int QtQml.Models2::DelegateModel::count -*/ - -int QQmlDelegateModel::count() const -{ - Q_D(const QQmlDelegateModel); - if (!d->m_delegate) - return 0; - return d->m_compositor.count(d->m_compositorGroup); -} - -QQmlDelegateModel::ReleaseFlags QQmlDelegateModelPrivate::release(QObject *object) -{ - QQmlDelegateModel::ReleaseFlags stat = 0; - if (!object) - return stat; - - if (QQmlDelegateModelItem *cacheItem = QQmlDelegateModelItem::dataForObject(object)) { - if (cacheItem->releaseObject()) { - cacheItem->destroyObject(); - emitDestroyingItem(object); - if (cacheItem->incubationTask) { - releaseIncubator(cacheItem->incubationTask); - cacheItem->incubationTask = 0; - } - cacheItem->Dispose(); - stat |= QQmlInstanceModel::Destroyed; - } else { - stat |= QQmlDelegateModel::Referenced; - } - } - return stat; -} - -/* - Returns ReleaseStatus flags. -*/ - -QQmlDelegateModel::ReleaseFlags QQmlDelegateModel::release(QObject *item) -{ - Q_D(QQmlDelegateModel); - QQmlInstanceModel::ReleaseFlags stat = d->release(item); - return stat; -} - -// Cancel a requested async item -void QQmlDelegateModel::cancel(int index) -{ - Q_D(QQmlDelegateModel); - if (!d->m_delegate || index < 0 || index >= d->m_compositor.count(d->m_compositorGroup)) { - qWarning() << "DelegateModel::cancel: index out range" << index << d->m_compositor.count(d->m_compositorGroup); - return; - } - - Compositor::iterator it = d->m_compositor.find(d->m_compositorGroup, index); - QQmlDelegateModelItem *cacheItem = it->inCache() ? d->m_cache.at(it.cacheIndex) : 0; - if (cacheItem) { - if (cacheItem->incubationTask && !cacheItem->isObjectReferenced()) { - d->releaseIncubator(cacheItem->incubationTask); - cacheItem->incubationTask = 0; - - if (cacheItem->object) { - QObject *object = cacheItem->object; - cacheItem->destroyObject(); - if (QQuickPackage *package = qmlobject_cast(object)) - d->emitDestroyingPackage(package); - else - d->emitDestroyingItem(object); - } - - cacheItem->scriptRef -= 1; - } - if (!cacheItem->isReferenced()) { - d->m_compositor.clearFlags(Compositor::Cache, it.cacheIndex, 1, Compositor::CacheFlag); - d->m_cache.removeAt(it.cacheIndex); - delete cacheItem; - Q_ASSERT(d->m_cache.count() == d->m_compositor.count(Compositor::Cache)); - } - } -} - -void QQmlDelegateModelPrivate::group_append( - QQmlListProperty *property, QQmlDelegateModelGroup *group) -{ - QQmlDelegateModelPrivate *d = static_cast(property->data); - if (d->m_complete) - return; - if (d->m_groupCount == Compositor::MaximumGroupCount) { - qmlInfo(d->q_func()) << QQmlDelegateModel::tr("The maximum number of supported DelegateModelGroups is 8"); - return; - } - d->m_groups[d->m_groupCount] = group; - d->m_groupCount += 1; -} - -int QQmlDelegateModelPrivate::group_count( - QQmlListProperty *property) -{ - QQmlDelegateModelPrivate *d = static_cast(property->data); - return d->m_groupCount - 1; -} - -QQmlDelegateModelGroup *QQmlDelegateModelPrivate::group_at( - QQmlListProperty *property, int index) -{ - QQmlDelegateModelPrivate *d = static_cast(property->data); - return index >= 0 && index < d->m_groupCount - 1 - ? d->m_groups[index + 1] - : 0; -} - -/*! - \qmlproperty list QtQml.Models2::DelegateModel::groups - - This property holds a visual data model's group definitions. - - Groups define a sub-set of the items in a visual data model and can be used to filter - a model. - - For every group defined in a DelegateModel two attached properties are added to each - delegate item. The first of the form DelegateModel.in\e{GroupName} holds whether the - item belongs to the group and the second DelegateModel.\e{groupName}Index holds the - index of the item in that group. - - The following example illustrates using groups to select items in a model. - - \snippet qml/visualdatagroup.qml 0 -*/ - -QQmlListProperty QQmlDelegateModel::groups() -{ - Q_D(QQmlDelegateModel); - return QQmlListProperty( - this, - d, - QQmlDelegateModelPrivate::group_append, - QQmlDelegateModelPrivate::group_count, - QQmlDelegateModelPrivate::group_at, - 0); -} - -/*! - \qmlproperty DelegateModelGroup QtQml.Models2::DelegateModel::items - - This property holds visual data model's default group to which all new items are added. -*/ - -QQmlDelegateModelGroup *QQmlDelegateModel::items() -{ - Q_D(QQmlDelegateModel); - return d->m_items; -} - -/*! - \qmlproperty DelegateModelGroup QtQml.Models2::DelegateModel::persistedItems - - This property holds visual data model's persisted items group. - - Items in this group are not destroyed when released by a view, instead they are persisted - until removed from the group. - - An item can be removed from the persistedItems group by setting the - DelegateModel.inPersistedItems property to false. If the item is not referenced by a view - at that time it will be destroyed. Adding an item to this group will not create a new - instance. - - Items returned by the \l QtQml.Models2::DelegateModelGroup::create() function are automatically added - to this group. -*/ - -QQmlDelegateModelGroup *QQmlDelegateModel::persistedItems() -{ - Q_D(QQmlDelegateModel); - return d->m_persistedItems; -} - -/*! - \qmlproperty string QtQml.Models2::DelegateModel::filterOnGroup - - This property holds the name of the group used to filter the visual data model. - - Only items which belong to this group are visible to a view. - - By default this is the \l items group. -*/ - -QString QQmlDelegateModel::filterGroup() const -{ - Q_D(const QQmlDelegateModel); - return d->m_filterGroup; -} - -void QQmlDelegateModel::setFilterGroup(const QString &group) -{ - Q_D(QQmlDelegateModel); - - if (d->m_transaction) { - qmlInfo(this) << tr("The group of a DelegateModel cannot be changed within onChanged"); - return; - } - - if (d->m_filterGroup != group) { - d->m_filterGroup = group; - d->updateFilterGroup(); - emit filterGroupChanged(); - } -} - -void QQmlDelegateModel::resetFilterGroup() -{ - setFilterGroup(QStringLiteral("items")); -} - -void QQmlDelegateModelPrivate::updateFilterGroup() -{ - Q_Q(QQmlDelegateModel); - if (!m_cacheMetaType) - return; - - QQmlListCompositor::Group previousGroup = m_compositorGroup; - m_compositorGroup = Compositor::Default; - for (int i = 1; i < m_groupCount; ++i) { - if (m_filterGroup == m_cacheMetaType->groupNames.at(i - 1)) { - m_compositorGroup = Compositor::Group(i); - break; - } - } - - QQmlDelegateModelGroupPrivate::get(m_groups[m_compositorGroup])->emitters.insert(this); - if (m_compositorGroup != previousGroup) { - QVector removes; - QVector inserts; - m_compositor.transition(previousGroup, m_compositorGroup, &removes, &inserts); - - QQmlChangeSet changeSet; - changeSet.move(removes, inserts); - emit q->modelUpdated(changeSet, false); - - if (changeSet.difference() != 0) - emit q->countChanged(); - - if (m_parts) { - foreach (QQmlPartsModel *model, m_parts->models) - model->updateFilterGroup(m_compositorGroup, changeSet); - } - } -} - -/*! - \qmlproperty object QtQml.Models2::DelegateModel::parts - - The \a parts property selects a DelegateModel which creates - delegates from the part named. This is used in conjunction with - the \l Package type. - - For example, the code below selects a model which creates - delegates named \e list from a \l Package: - - \code - DelegateModel { - id: visualModel - delegate: Package { - Item { Package.name: "list" } - } - model: myModel - } - - ListView { - width: 200; height:200 - model: visualModel.parts.list - } - \endcode - - \sa Package -*/ - -QObject *QQmlDelegateModel::parts() -{ - Q_D(QQmlDelegateModel); - if (!d->m_parts) - d->m_parts = new QQmlDelegateModelParts(this); - return d->m_parts; -} - -void QQmlDelegateModelPrivate::emitCreatedPackage(QQDMIncubationTask *incubationTask, QQuickPackage *package) -{ - for (int i = 1; i < m_groupCount; ++i) - QQmlDelegateModelGroupPrivate::get(m_groups[i])->createdPackage(incubationTask->index[i], package); -} - -void QQmlDelegateModelPrivate::emitInitPackage(QQDMIncubationTask *incubationTask, QQuickPackage *package) -{ - for (int i = 1; i < m_groupCount; ++i) - QQmlDelegateModelGroupPrivate::get(m_groups[i])->initPackage(incubationTask->index[i], package); -} - -void QQmlDelegateModelPrivate::emitDestroyingPackage(QQuickPackage *package) -{ - for (int i = 1; i < m_groupCount; ++i) - QQmlDelegateModelGroupPrivate::get(m_groups[i])->destroyingPackage(package); -} - -void QQDMIncubationTask::statusChanged(Status status) -{ - vdm->incubatorStatusChanged(this, status); -} - -void QQmlDelegateModelPrivate::releaseIncubator(QQDMIncubationTask *incubationTask) -{ - Q_Q(QQmlDelegateModel); - if (!incubationTask->isError()) - incubationTask->clear(); - m_finishedIncubating.append(incubationTask); - if (!m_incubatorCleanupScheduled) { - m_incubatorCleanupScheduled = true; - QCoreApplication::postEvent(q, new QEvent(QEvent::User)); - } -} - -void QQmlDelegateModelPrivate::removeCacheItem(QQmlDelegateModelItem *cacheItem) -{ - int cidx = m_cache.indexOf(cacheItem); - if (cidx >= 0) { - m_compositor.clearFlags(Compositor::Cache, cidx, 1, Compositor::CacheFlag); - m_cache.removeAt(cidx); - } - Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); -} - -void QQmlDelegateModelPrivate::incubatorStatusChanged(QQDMIncubationTask *incubationTask, QQmlIncubator::Status status) -{ - Q_Q(QQmlDelegateModel); - if (status != QQmlIncubator::Ready && status != QQmlIncubator::Error) - return; - - QQmlDelegateModelItem *cacheItem = incubationTask->incubating; - cacheItem->incubationTask = 0; - incubationTask->incubating = 0; - releaseIncubator(incubationTask); - - if (status == QQmlIncubator::Ready) { - if (QQuickPackage *package = qmlobject_cast(cacheItem->object)) - emitCreatedPackage(incubationTask, package); - else - emitCreatedItem(incubationTask, cacheItem->object); - } else if (status == QQmlIncubator::Error) { - qmlInfo(q, m_delegate->errors()) << "Error creating delegate"; - } - - if (!cacheItem->isObjectReferenced()) { - if (QQuickPackage *package = qmlobject_cast(cacheItem->object)) - emitDestroyingPackage(package); - else - emitDestroyingItem(cacheItem->object); - delete cacheItem->object; - cacheItem->object = 0; - cacheItem->scriptRef -= 1; - cacheItem->contextData->destroy(); - cacheItem->contextData = 0; - if (!cacheItem->isReferenced()) { - removeCacheItem(cacheItem); - delete cacheItem; - } - } -} - -void QQDMIncubationTask::setInitialState(QObject *o) -{ - vdm->setInitialState(this, o); -} - -void QQmlDelegateModelPrivate::setInitialState(QQDMIncubationTask *incubationTask, QObject *o) -{ - QQmlDelegateModelItem *cacheItem = incubationTask->incubating; - cacheItem->object = o; - - if (QQuickPackage *package = qmlobject_cast(cacheItem->object)) - emitInitPackage(incubationTask, package); - else - emitInitItem(incubationTask, cacheItem->object); -} - -QObject *QQmlDelegateModelPrivate::object(Compositor::Group group, int index, bool asynchronous) -{ - Q_Q(QQmlDelegateModel); - if (!m_delegate || index < 0 || index >= m_compositor.count(group)) { - qWarning() << "DelegateModel::item: index out range" << index << m_compositor.count(group); - return 0; - } else if (!m_context->isValid()) { - return 0; - } - - Compositor::iterator it = m_compositor.find(group, index); - - QQmlDelegateModelItem *cacheItem = it->inCache() ? m_cache.at(it.cacheIndex) : 0; - - if (!cacheItem) { - cacheItem = m_adaptorModel.createItem(m_cacheMetaType, m_context->engine(), it.modelIndex()); - if (!cacheItem) - return 0; - - cacheItem->groups = it->flags; - - m_cache.insert(it.cacheIndex, cacheItem); - m_compositor.setFlags(it, 1, Compositor::CacheFlag); - Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); - } - - // Bump the reference counts temporarily so neither the content data or the delegate object - // are deleted if incubatorStatusChanged() is called synchronously. - cacheItem->scriptRef += 1; - cacheItem->referenceObject(); - - if (cacheItem->incubationTask) { - if (!asynchronous && cacheItem->incubationTask->incubationMode() == QQmlIncubator::Asynchronous) { - // previously requested async - now needed immediately - cacheItem->incubationTask->forceCompletion(); - } - } else if (!cacheItem->object) { - QQmlContext *creationContext = m_delegate->creationContext(); - - cacheItem->scriptRef += 1; - - cacheItem->incubationTask = new QQDMIncubationTask(this, asynchronous ? QQmlIncubator::Asynchronous : QQmlIncubator::AsynchronousIfNested); - cacheItem->incubationTask->incubating = cacheItem; - cacheItem->incubationTask->clear(); - - for (int i = 1; i < m_groupCount; ++i) - cacheItem->incubationTask->index[i] = it.index[i]; - - QQmlContextData *ctxt = new QQmlContextData; - ctxt->setParent(QQmlContextData::get(creationContext ? creationContext : m_context)); - ctxt->contextObject = cacheItem; - cacheItem->contextData = ctxt; - - if (m_adaptorModel.hasProxyObject()) { - if (QQmlAdaptorModelProxyInterface *proxy - = qobject_cast(cacheItem)) { - ctxt = new QQmlContextData; - ctxt->setParent(cacheItem->contextData, true); - ctxt->contextObject = proxy->proxiedObject(); - } - } - - cacheItem->incubateObject( - m_delegate, - m_context->engine(), - ctxt, - QQmlContextData::get(m_context)); - } - - if (index == m_compositor.count(group) - 1 && m_adaptorModel.canFetchMore()) - QCoreApplication::postEvent(q, new QEvent(QEvent::UpdateRequest)); - - // Remove the temporary reference count. - cacheItem->scriptRef -= 1; - if (cacheItem->object) - return cacheItem->object; - - cacheItem->releaseObject(); - if (!cacheItem->isReferenced()) { - removeCacheItem(cacheItem); - delete cacheItem; - } - - return 0; -} - -/* - If asynchronous is true or the component is being loaded asynchronously due - to an ancestor being loaded asynchronously, item() may return 0. In this - case itemCreated() will be emitted when the item is available. The item - at this stage does not have any references, so item() must be called again - to ensure a reference is held. Any call to item() which returns a valid item - must be matched by a call to release() in order to destroy the item. -*/ -QObject *QQmlDelegateModel::object(int index, bool asynchronous) -{ - Q_D(QQmlDelegateModel); - if (!d->m_delegate || index < 0 || index >= d->m_compositor.count(d->m_compositorGroup)) { - qWarning() << "DelegateModel::item: index out range" << index << d->m_compositor.count(d->m_compositorGroup); - return 0; - } - - QObject *object = d->object(d->m_compositorGroup, index, asynchronous); - if (!object) - return 0; - - return object; -} - -QString QQmlDelegateModelPrivate::stringValue(Compositor::Group group, int index, const QString &name) -{ - Compositor::iterator it = m_compositor.find(group, index); - if (QQmlAdaptorModel *model = it.list()) { - QString role = name; - int dot = name.indexOf(QLatin1Char('.')); - if (dot > 0) - role = name.left(dot); - QVariant value = model->value(it.modelIndex(), role); - while (dot > 0) { - QObject *obj = qvariant_cast(value); - if (!obj) - return QString(); - int from = dot+1; - dot = name.indexOf(QLatin1Char('.'), from); - value = obj->property(name.mid(from, dot-from).toUtf8()); - } - return value.toString(); - } - return QString(); -} - -QString QQmlDelegateModel::stringValue(int index, const QString &name) -{ - Q_D(QQmlDelegateModel); - return d->stringValue(d->m_compositorGroup, index, name); -} - -int QQmlDelegateModel::indexOf(QObject *item, QObject *) const -{ - Q_D(const QQmlDelegateModel); - if (QQmlDelegateModelItem *cacheItem = QQmlDelegateModelItem::dataForObject(item)) - return cacheItem->groupIndex(d->m_compositorGroup); - return -1; -} - -void QQmlDelegateModel::setWatchedRoles(QList roles) -{ - Q_D(QQmlDelegateModel); - d->m_adaptorModel.replaceWatchedRoles(d->m_watchedRoles, roles); - d->m_watchedRoles = roles; -} - -void QQmlDelegateModelPrivate::addGroups( - Compositor::iterator from, int count, Compositor::Group group, int groupFlags) -{ - QVector inserts; - m_compositor.setFlags(from, count, group, groupFlags, &inserts); - itemsInserted(inserts); - emitChanges(); -} - -void QQmlDelegateModelPrivate::removeGroups( - Compositor::iterator from, int count, Compositor::Group group, int groupFlags) -{ - QVector removes; - m_compositor.clearFlags(from, count, group, groupFlags, &removes); - itemsRemoved(removes); - emitChanges(); -} - -void QQmlDelegateModelPrivate::setGroups( - Compositor::iterator from, int count, Compositor::Group group, int groupFlags) -{ - QVector removes; - QVector inserts; - - m_compositor.setFlags(from, count, group, groupFlags, &inserts); - itemsInserted(inserts); - const int removeFlags = ~groupFlags & Compositor::GroupMask; - - from = m_compositor.find(from.group, from.index[from.group]); - m_compositor.clearFlags(from, count, group, removeFlags, &removes); - itemsRemoved(removes); - emitChanges(); -} - -bool QQmlDelegateModel::event(QEvent *e) -{ - Q_D(QQmlDelegateModel); - if (e->type() == QEvent::UpdateRequest) { - d->m_adaptorModel.fetchMore(); - } else if (e->type() == QEvent::User) { - d->m_incubatorCleanupScheduled = false; - qDeleteAll(d->m_finishedIncubating); - d->m_finishedIncubating.clear(); - } - return QQmlInstanceModel::event(e); -} - -void QQmlDelegateModelPrivate::itemsChanged(const QVector &changes) -{ - if (!m_delegate) - return; - - QVarLengthArray, Compositor::MaximumGroupCount> translatedChanges(m_groupCount); - - foreach (const Compositor::Change &change, changes) { - for (int i = 1; i < m_groupCount; ++i) { - if (change.inGroup(i)) { - translatedChanges[i].append(QQmlChangeSet::Change(change.index[i], change.count)); - } - } - } - - for (int i = 1; i < m_groupCount; ++i) - QQmlDelegateModelGroupPrivate::get(m_groups[i])->changeSet.change(translatedChanges.at(i)); -} - -void QQmlDelegateModel::_q_itemsChanged(int index, int count, const QVector &roles) -{ - Q_D(QQmlDelegateModel); - if (count <= 0 || !d->m_complete) - return; - - if (d->m_adaptorModel.notify(d->m_cache, index, count, roles)) { - QVector changes; - d->m_compositor.listItemsChanged(&d->m_adaptorModel, index, count, &changes); - d->itemsChanged(changes); - d->emitChanges(); - } -} - -static void incrementIndexes(QQmlDelegateModelItem *cacheItem, int count, const int *deltas) -{ - if (QQDMIncubationTask *incubationTask = cacheItem->incubationTask) { - for (int i = 1; i < count; ++i) - incubationTask->index[i] += deltas[i]; - } - if (QQmlDelegateModelAttached *attached = cacheItem->attached) { - for (int i = 1; i < count; ++i) - attached->m_currentIndex[i] += deltas[i]; - } -} - -void QQmlDelegateModelPrivate::itemsInserted( - const QVector &inserts, - QVarLengthArray, Compositor::MaximumGroupCount> *translatedInserts, - QHash > *movedItems) -{ - int cacheIndex = 0; - - int inserted[Compositor::MaximumGroupCount]; - for (int i = 1; i < m_groupCount; ++i) - inserted[i] = 0; - - foreach (const Compositor::Insert &insert, inserts) { - for (; cacheIndex < insert.cacheIndex; ++cacheIndex) - incrementIndexes(m_cache.at(cacheIndex), m_groupCount, inserted); - - for (int i = 1; i < m_groupCount; ++i) { - if (insert.inGroup(i)) { - (*translatedInserts)[i].append( - QQmlChangeSet::Insert(insert.index[i], insert.count, insert.moveId)); - inserted[i] += insert.count; - } - } - - if (!insert.inCache()) - continue; - - if (movedItems && insert.isMove()) { - QList items = movedItems->take(insert.moveId); - Q_ASSERT(items.count() == insert.count); - m_cache = m_cache.mid(0, insert.cacheIndex) + items + m_cache.mid(insert.cacheIndex); - } - if (insert.inGroup()) { - for (int offset = 0; cacheIndex < insert.cacheIndex + insert.count; ++cacheIndex, ++offset) { - QQmlDelegateModelItem *cacheItem = m_cache.at(cacheIndex); - cacheItem->groups |= insert.flags & Compositor::GroupMask; - - if (QQDMIncubationTask *incubationTask = cacheItem->incubationTask) { - for (int i = 1; i < m_groupCount; ++i) - incubationTask->index[i] = cacheItem->groups & (1 << i) - ? insert.index[i] + offset - : insert.index[i]; - } - if (QQmlDelegateModelAttached *attached = cacheItem->attached) { - for (int i = 1; i < m_groupCount; ++i) - attached->m_currentIndex[i] = cacheItem->groups & (1 << i) - ? insert.index[i] + offset - : insert.index[i]; - } - } - } else { - cacheIndex = insert.cacheIndex + insert.count; - } - } - for (; cacheIndex < m_cache.count(); ++cacheIndex) - incrementIndexes(m_cache.at(cacheIndex), m_groupCount, inserted); -} - -void QQmlDelegateModelPrivate::itemsInserted(const QVector &inserts) -{ - QVarLengthArray, Compositor::MaximumGroupCount> translatedInserts(m_groupCount); - itemsInserted(inserts, &translatedInserts); - Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); - if (!m_delegate) - return; - - for (int i = 1; i < m_groupCount; ++i) - QQmlDelegateModelGroupPrivate::get(m_groups[i])->changeSet.insert(translatedInserts.at(i)); -} - -void QQmlDelegateModel::_q_itemsInserted(int index, int count) -{ - - Q_D(QQmlDelegateModel); - if (count <= 0 || !d->m_complete) - return; - - d->m_count += count; - - for (int i = 0, c = d->m_cache.count(); i < c; ++i) { - QQmlDelegateModelItem *item = d->m_cache.at(i); - if (item->modelIndex() >= index) - item->setModelIndex(item->modelIndex() + count); - } - - QVector inserts; - d->m_compositor.listItemsInserted(&d->m_adaptorModel, index, count, &inserts); - d->itemsInserted(inserts); - d->emitChanges(); -} - -void QQmlDelegateModelPrivate::itemsRemoved( - const QVector &removes, - QVarLengthArray, Compositor::MaximumGroupCount> *translatedRemoves, - QHash > *movedItems) -{ - int cacheIndex = 0; - int removedCache = 0; - - int removed[Compositor::MaximumGroupCount]; - for (int i = 1; i < m_groupCount; ++i) - removed[i] = 0; - - foreach (const Compositor::Remove &remove, removes) { - for (; cacheIndex < remove.cacheIndex; ++cacheIndex) - incrementIndexes(m_cache.at(cacheIndex), m_groupCount, removed); - - for (int i = 1; i < m_groupCount; ++i) { - if (remove.inGroup(i)) { - (*translatedRemoves)[i].append( - QQmlChangeSet::Remove(remove.index[i], remove.count, remove.moveId)); - removed[i] -= remove.count; - } - } - - if (!remove.inCache()) - continue; - - if (movedItems && remove.isMove()) { - movedItems->insert(remove.moveId, m_cache.mid(remove.cacheIndex, remove.count)); - QList::iterator begin = m_cache.begin() + remove.cacheIndex; - QList::iterator end = begin + remove.count; - m_cache.erase(begin, end); - } else { - for (; cacheIndex < remove.cacheIndex + remove.count - removedCache; ++cacheIndex) { - QQmlDelegateModelItem *cacheItem = m_cache.at(cacheIndex); - if (remove.inGroup(Compositor::Persisted) && cacheItem->objectRef == 0 && cacheItem->object) { - QObject *object = cacheItem->object; - cacheItem->destroyObject(); - if (QQuickPackage *package = qmlobject_cast(object)) - emitDestroyingPackage(package); - else - emitDestroyingItem(object); - cacheItem->scriptRef -= 1; - } - if (!cacheItem->isReferenced()) { - m_compositor.clearFlags(Compositor::Cache, cacheIndex, 1, Compositor::CacheFlag); - m_cache.removeAt(cacheIndex); - delete cacheItem; - --cacheIndex; - ++removedCache; - Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); - } else if (remove.groups() == cacheItem->groups) { - cacheItem->groups = 0; - if (QQDMIncubationTask *incubationTask = cacheItem->incubationTask) { - for (int i = 1; i < m_groupCount; ++i) - incubationTask->index[i] = -1; - } - if (QQmlDelegateModelAttached *attached = cacheItem->attached) { - for (int i = 1; i < m_groupCount; ++i) - attached->m_currentIndex[i] = -1; - } - } else { - if (QQDMIncubationTask *incubationTask = cacheItem->incubationTask) { - for (int i = 1; i < m_groupCount; ++i) { - if (remove.inGroup(i)) - incubationTask->index[i] = remove.index[i]; - } - } - if (QQmlDelegateModelAttached *attached = cacheItem->attached) { - for (int i = 1; i < m_groupCount; ++i) { - if (remove.inGroup(i)) - attached->m_currentIndex[i] = remove.index[i]; - } - } - cacheItem->groups &= ~remove.flags; - } - } - } - } - - for (; cacheIndex < m_cache.count(); ++cacheIndex) - incrementIndexes(m_cache.at(cacheIndex), m_groupCount, removed); -} - -void QQmlDelegateModelPrivate::itemsRemoved(const QVector &removes) -{ - QVarLengthArray, Compositor::MaximumGroupCount> translatedRemoves(m_groupCount); - itemsRemoved(removes, &translatedRemoves); - Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); - if (!m_delegate) - return; - - for (int i = 1; i < m_groupCount; ++i) - QQmlDelegateModelGroupPrivate::get(m_groups[i])->changeSet.remove(translatedRemoves.at(i)); -} - -void QQmlDelegateModel::_q_itemsRemoved(int index, int count) -{ - Q_D(QQmlDelegateModel); - if (count <= 0|| !d->m_complete) - return; - - d->m_count -= count; - - for (int i = 0, c = d->m_cache.count(); i < c; ++i) { - QQmlDelegateModelItem *item = d->m_cache.at(i); - if (item->modelIndex() >= index + count) - item->setModelIndex(item->modelIndex() - count); - else if (item->modelIndex() >= index) - item->setModelIndex(-1); - } - - QVector removes; - d->m_compositor.listItemsRemoved(&d->m_adaptorModel, index, count, &removes); - d->itemsRemoved(removes); - - d->emitChanges(); -} - -void QQmlDelegateModelPrivate::itemsMoved( - const QVector &removes, const QVector &inserts) -{ - QHash > movedItems; - - QVarLengthArray, Compositor::MaximumGroupCount> translatedRemoves(m_groupCount); - itemsRemoved(removes, &translatedRemoves, &movedItems); - - QVarLengthArray, Compositor::MaximumGroupCount> translatedInserts(m_groupCount); - itemsInserted(inserts, &translatedInserts, &movedItems); - Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); - Q_ASSERT(movedItems.isEmpty()); - if (!m_delegate) - return; - - for (int i = 1; i < m_groupCount; ++i) { - QQmlDelegateModelGroupPrivate::get(m_groups[i])->changeSet.move( - translatedRemoves.at(i), - translatedInserts.at(i)); - } -} - -void QQmlDelegateModel::_q_itemsMoved(int from, int to, int count) -{ - Q_D(QQmlDelegateModel); - if (count <= 0 || !d->m_complete) - return; - - const int minimum = qMin(from, to); - const int maximum = qMax(from, to) + count; - const int difference = from > to ? count : -count; - - for (int i = 0, c = d->m_cache.count(); i < c; ++i) { - QQmlDelegateModelItem *item = d->m_cache.at(i); - if (item->modelIndex() >= from && item->modelIndex() < from + count) - item->setModelIndex(item->modelIndex() - from + to); - else if (item->modelIndex() >= minimum && item->modelIndex() < maximum) - item->setModelIndex(item->modelIndex() + difference); - } - - QVector removes; - QVector inserts; - d->m_compositor.listItemsMoved(&d->m_adaptorModel, from, to, count, &removes, &inserts); - d->itemsMoved(removes, inserts); - d->emitChanges(); -} - -template v8::Local -QQmlDelegateModelPrivate::buildChangeList(const QVector &changes) -{ - v8::Local indexes = v8::Array::New(changes.count()); - v8::Local indexKey = v8::String::New("index"); - v8::Local countKey = v8::String::New("count"); - v8::Local moveIdKey = v8::String::New("moveId"); - - for (int i = 0; i < changes.count(); ++i) { - v8::Local object = v8::Object::New(); - object->Set(indexKey, v8::Integer::New(changes.at(i).index)); - object->Set(countKey, v8::Integer::New(changes.at(i).count)); - object->Set(moveIdKey, changes.at(i).moveId != -1 ? v8::Integer::New(changes.at(i).count) : v8::Undefined()); - indexes->Set(i, object); - } - return indexes; -} - -void QQmlDelegateModelPrivate::emitModelUpdated(const QQmlChangeSet &changeSet, bool reset) -{ - Q_Q(QQmlDelegateModel); - emit q->modelUpdated(changeSet, reset); - if (changeSet.difference() != 0) - emit q->countChanged(); -} - -void QQmlDelegateModelPrivate::emitChanges() -{ - if (m_transaction || !m_complete || !m_context->isValid()) - return; - - m_transaction = true; - QV8Engine *engine = QQmlEnginePrivate::getV8Engine(m_context->engine()); - for (int i = 1; i < m_groupCount; ++i) - QQmlDelegateModelGroupPrivate::get(m_groups[i])->emitChanges(engine); - m_transaction = false; - - const bool reset = m_reset; - m_reset = false; - for (int i = 1; i < m_groupCount; ++i) - QQmlDelegateModelGroupPrivate::get(m_groups[i])->emitModelUpdated(reset); - - foreach (QQmlDelegateModelItem *cacheItem, m_cache) { - if (cacheItem->attached) - cacheItem->attached->emitChanges(); - } -} - -void QQmlDelegateModel::_q_modelReset() -{ - Q_D(QQmlDelegateModel); - if (!d->m_delegate) - return; - - int oldCount = d->m_count; - d->m_adaptorModel.rootIndex = QModelIndex(); - - if (d->m_complete) { - d->m_count = d->m_adaptorModel.count(); - - for (int i = 0, c = d->m_cache.count(); i < c; ++i) { - QQmlDelegateModelItem *item = d->m_cache.at(i); - if (item->modelIndex() != -1) - item->setModelIndex(-1); - } - - QVector removes; - QVector inserts; - if (oldCount) - d->m_compositor.listItemsRemoved(&d->m_adaptorModel, 0, oldCount, &removes); - if (d->m_count) - d->m_compositor.listItemsInserted(&d->m_adaptorModel, 0, d->m_count, &inserts); - d->itemsMoved(removes, inserts); - d->m_reset = true; - - if (d->m_adaptorModel.canFetchMore()) - d->m_adaptorModel.fetchMore(); - - d->emitChanges(); - } - emit rootIndexChanged(); -} - -void QQmlDelegateModel::_q_rowsInserted(const QModelIndex &parent, int begin, int end) -{ - Q_D(QQmlDelegateModel); - if (parent == d->m_adaptorModel.rootIndex) - _q_itemsInserted(begin, end - begin + 1); -} - -void QQmlDelegateModel::_q_rowsAboutToBeRemoved(const QModelIndex &parent, int begin, int end) -{ - Q_D(QQmlDelegateModel); - if (!d->m_adaptorModel.rootIndex.isValid()) - return; - const QModelIndex index = d->m_adaptorModel.rootIndex; - if (index.parent() == parent && index.row() >= begin && index.row() <= end) { - const int oldCount = d->m_count; - d->m_count = 0; - d->m_adaptorModel.invalidateModel(this); - - if (d->m_complete && oldCount > 0) { - QVector removes; - d->m_compositor.listItemsRemoved(&d->m_adaptorModel, 0, oldCount, &removes); - d->itemsRemoved(removes); - d->emitChanges(); - } - } -} - -void QQmlDelegateModel::_q_rowsRemoved(const QModelIndex &parent, int begin, int end) -{ - Q_D(QQmlDelegateModel); - if (parent == d->m_adaptorModel.rootIndex) - _q_itemsRemoved(begin, end - begin + 1); -} - -void QQmlDelegateModel::_q_rowsMoved( - const QModelIndex &sourceParent, int sourceStart, int sourceEnd, - const QModelIndex &destinationParent, int destinationRow) -{ - Q_D(QQmlDelegateModel); - const int count = sourceEnd - sourceStart + 1; - if (destinationParent == d->m_adaptorModel.rootIndex && sourceParent == d->m_adaptorModel.rootIndex) { - _q_itemsMoved(sourceStart, sourceStart > destinationRow ? destinationRow : destinationRow - count, count); - } else if (sourceParent == d->m_adaptorModel.rootIndex) { - _q_itemsRemoved(sourceStart, count); - } else if (destinationParent == d->m_adaptorModel.rootIndex) { - _q_itemsInserted(destinationRow, count); - } -} - -void QQmlDelegateModel::_q_dataChanged(const QModelIndex &begin, const QModelIndex &end, const QVector &roles) -{ - Q_D(QQmlDelegateModel); - if (begin.parent() == d->m_adaptorModel.rootIndex) - _q_itemsChanged(begin.row(), end.row() - begin.row() + 1, roles); -} - -void QQmlDelegateModel::_q_layoutChanged() -{ - Q_D(QQmlDelegateModel); - _q_itemsChanged(0, d->m_count, QVector()); -} - -QQmlDelegateModelAttached *QQmlDelegateModel::qmlAttachedProperties(QObject *obj) -{ - if (QQmlDelegateModelItem *cacheItem = QQmlDelegateModelItem::dataForObject(obj)) { - if (cacheItem->object == obj) { // Don't create attached item for child objects. - cacheItem->attached = new QQmlDelegateModelAttached(cacheItem, obj); - return cacheItem->attached; - } - } - return new QQmlDelegateModelAttached(obj); -} - -bool QQmlDelegateModelPrivate::insert( - Compositor::insert_iterator &before, const v8::Local &object, int groups) -{ - if (!m_context->isValid()) - return false; - - QQmlDelegateModelItem *cacheItem = m_adaptorModel.createItem(m_cacheMetaType, m_context->engine(), -1); - if (!cacheItem) - return false; - - v8::Local propertyNames = object->GetPropertyNames(); - for (uint i = 0; i < propertyNames->Length(); ++i) { - v8::Local propertyName = propertyNames->Get(i)->ToString(); - cacheItem->setValue( - m_cacheMetaType->v8Engine->toString(propertyName), - m_cacheMetaType->v8Engine->toVariant(object->Get(propertyName), QVariant::Invalid)); - } - - cacheItem->groups = groups | Compositor::UnresolvedFlag | Compositor::CacheFlag; - - // Must be before the new object is inserted into the cache or its indexes will be adjusted too. - itemsInserted(QVector() << Compositor::Insert(before, 1, cacheItem->groups & ~Compositor::CacheFlag)); - - before = m_compositor.insert(before, 0, 0, 1, cacheItem->groups); - m_cache.insert(before.cacheIndex, cacheItem); - - return true; -} - -//============================================================================ - -QQmlDelegateModelItemMetaType::QQmlDelegateModelItemMetaType( - QV8Engine *engine, QQmlDelegateModel *model, const QStringList &groupNames) - : model(model) - , groupCount(groupNames.count() + 1) - , v8Engine(engine) - , metaObject(0) - , groupNames(groupNames) -{ -} - -QQmlDelegateModelItemMetaType::~QQmlDelegateModelItemMetaType() -{ - if (metaObject) - metaObject->release(); - qPersistentDispose(constructor); -} - -void QQmlDelegateModelItemMetaType::initializeMetaObject() -{ - QMetaObjectBuilder builder; - builder.setFlags(QMetaObjectBuilder::DynamicMetaObject); - builder.setClassName(QQmlDelegateModelAttached::staticMetaObject.className()); - builder.setSuperClass(&QQmlDelegateModelAttached::staticMetaObject); - - int notifierId = 0; - for (int i = 0; i < groupNames.count(); ++i, ++notifierId) { - QString propertyName = QStringLiteral("in") + groupNames.at(i); - propertyName.replace(2, 1, propertyName.at(2).toUpper()); - builder.addSignal("__" + propertyName.toUtf8() + "Changed()"); - QMetaPropertyBuilder propertyBuilder = builder.addProperty( - propertyName.toUtf8(), "bool", notifierId); - propertyBuilder.setWritable(true); - } - for (int i = 0; i < groupNames.count(); ++i, ++notifierId) { - const QString propertyName = groupNames.at(i) + QStringLiteral("Index"); - builder.addSignal("__" + propertyName.toUtf8() + "Changed()"); - QMetaPropertyBuilder propertyBuilder = builder.addProperty( - propertyName.toUtf8(), "int", notifierId); - propertyBuilder.setWritable(true); - } - - metaObject = new QQmlDelegateModelAttachedMetaObject(this, builder.toMetaObject()); -} - -void QQmlDelegateModelItemMetaType::initializeConstructor() -{ - v8::HandleScope handleScope; - v8::Context::Scope contextScope(v8Engine->context()); - - QQmlDelegateModelEngineData *data = engineData(v8Engine); - - constructor = qPersistentNew(v8::ObjectTemplate::New()); - - constructor->SetHasExternalResource(true); - constructor->SetAccessor(data->model(), get_model); - constructor->SetAccessor(data->groups(), get_groups, set_groups); - constructor->SetAccessor(data->isUnresolved(), get_member, 0, v8::Int32::New(30)); - constructor->SetAccessor(data->inItems(), get_member, set_member, v8::Int32::New(1)); - constructor->SetAccessor(data->inPersistedItems(), get_member, set_member, v8::Int32::New(2)); - constructor->SetAccessor(data->itemsIndex(), get_index, 0, v8::Int32::New(1)); - constructor->SetAccessor(data->persistedItemsIndex(), get_index, 0, v8::Int32::New(2)); - - for (int i = 2; i < groupNames.count(); ++i) { - QString propertyName = QStringLiteral("in") + groupNames.at(i); - propertyName.replace(2, 1, propertyName.at(2).toUpper()); - constructor->SetAccessor( - v8Engine->toString(propertyName), get_member, set_member, v8::Int32::New(i + 1)); - } - for (int i = 2; i < groupNames.count(); ++i) { - const QString propertyName = groupNames.at(i) + QStringLiteral("Index"); - constructor->SetAccessor( - v8Engine->toString(propertyName), get_index, 0, v8::Int32::New(i + 1)); - } -} - -int QQmlDelegateModelItemMetaType::parseGroups(const QStringList &groups) const -{ - int groupFlags = 0; - foreach (const QString &groupName, groups) { - int index = groupNames.indexOf(groupName); - if (index != -1) - groupFlags |= 2 << index; - } - return groupFlags; -} - -int QQmlDelegateModelItemMetaType::parseGroups(const v8::Local &groups) const -{ - int groupFlags = 0; - if (groups->IsString()) { - const QString groupName = v8Engine->toString(groups); - int index = groupNames.indexOf(groupName); - if (index != -1) - groupFlags |= 2 << index; - } else if (groups->IsArray()) { - v8::Local array = v8::Local::Cast(groups); - for (uint i = 0; i < array->Length(); ++i) { - const QString groupName = v8Engine->toString(array->Get(i)); - int index = groupNames.indexOf(groupName); - if (index != -1) - groupFlags |= 2 << index; - } - } - return groupFlags; -} - -v8::Handle QQmlDelegateModelItemMetaType::get_model( - v8::Local, const v8::AccessorInfo &info) -{ - QQmlDelegateModelItem *cacheItem = v8_resource_cast(info.This()); - V8ASSERT_TYPE(cacheItem, "Not a valid VisualData object"); - if (!cacheItem->metaType->model) - return v8::Undefined(); - - return cacheItem->get(); -} - -v8::Handle QQmlDelegateModelItemMetaType::get_groups( - v8::Local, const v8::AccessorInfo &info) -{ - QQmlDelegateModelItem *cacheItem = v8_resource_cast(info.This()); - V8ASSERT_TYPE(cacheItem, "Not a valid VisualData object"); - - QStringList groups; - for (int i = 1; i < cacheItem->metaType->groupCount; ++i) { - if (cacheItem->groups & (1 << i)) - groups.append(cacheItem->metaType->groupNames.at(i - 1)); - } - - return cacheItem->engine->fromVariant(groups); -} - -void QQmlDelegateModelItemMetaType::set_groups( - v8::Local, v8::Local value, const v8::AccessorInfo &info) -{ - QQmlDelegateModelItem *cacheItem = v8_resource_cast(info.This()); - V8ASSERT_TYPE_SETTER(cacheItem, "Not a valid VisualData object"); - - if (!cacheItem->metaType->model) - return; - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(cacheItem->metaType->model); - - const int groupFlags = model->m_cacheMetaType->parseGroups(value); - const int cacheIndex = model->m_cache.indexOf(cacheItem); - Compositor::iterator it = model->m_compositor.find(Compositor::Cache, cacheIndex); - model->setGroups(it, 1, Compositor::Cache, groupFlags); -} - -v8::Handle QQmlDelegateModelItemMetaType::get_member( - v8::Local, const v8::AccessorInfo &info) -{ - QQmlDelegateModelItem *cacheItem = v8_resource_cast(info.This()); - V8ASSERT_TYPE(cacheItem, "Not a valid VisualData object"); - - return v8::Boolean::New(cacheItem->groups & (1 << info.Data()->Int32Value())); -} - -void QQmlDelegateModelItemMetaType::set_member( - v8::Local, v8::Local value, const v8::AccessorInfo &info) -{ - QQmlDelegateModelItem *cacheItem = v8_resource_cast(info.This()); - V8ASSERT_TYPE_SETTER(cacheItem, "Not a valid VisualData object"); - - if (!cacheItem->metaType->model) - return; - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(cacheItem->metaType->model); - - Compositor::Group group = Compositor::Group(info.Data()->Int32Value()); - const bool member = value->BooleanValue(); - const int groupFlag = (1 << group); - if (member == ((cacheItem->groups & groupFlag) != 0)) - return; - - const int cacheIndex = model->m_cache.indexOf(cacheItem); - Compositor::iterator it = model->m_compositor.find(Compositor::Cache, cacheIndex); - if (member) - model->addGroups(it, 1, Compositor::Cache, groupFlag); - else - model->removeGroups(it, 1, Compositor::Cache, groupFlag); -} - -v8::Handle QQmlDelegateModelItemMetaType::get_index( - v8::Local, const v8::AccessorInfo &info) -{ - QQmlDelegateModelItem *cacheItem = v8_resource_cast(info.This()); - V8ASSERT_TYPE(cacheItem, "Not a valid VisualData object"); - - return v8::Integer::New(cacheItem->groupIndex(Compositor::Group(info.Data()->Int32Value()))); -} - - -//--------------------------------------------------------------------------- - -QQmlDelegateModelItem::QQmlDelegateModelItem( - QQmlDelegateModelItemMetaType *metaType, int modelIndex) - : QV8ObjectResource(metaType->v8Engine) - , metaType(metaType) - , contextData(0) - , object(0) - , attached(0) - , incubationTask(0) - , objectRef(0) - , scriptRef(0) - , groups(0) - , index(modelIndex) -{ - metaType->addref(); -} - -QQmlDelegateModelItem::~QQmlDelegateModelItem() -{ - Q_ASSERT(scriptRef == 0); - Q_ASSERT(objectRef == 0); - Q_ASSERT(!object); - - if (incubationTask && metaType->model) - QQmlDelegateModelPrivate::get(metaType->model)->releaseIncubator(incubationTask); - - metaType->release(); - -} - -void QQmlDelegateModelItem::Dispose() -{ - --scriptRef; - if (isReferenced()) - return; - - if (metaType->model) { - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(metaType->model); - model->removeCacheItem(this); - } - delete this; -} - -/* - This is essentially a copy of QQmlComponent::create(); except it takes the QQmlContextData - arguments instead of QQmlContext which means we don't have to construct the rather weighty - wrapper class for every delegate item. -*/ -void QQmlDelegateModelItem::incubateObject( - QQmlComponent *component, - QQmlEngine *engine, - QQmlContextData *context, - QQmlContextData *forContext) -{ - QQmlIncubatorPrivate *incubatorPriv = QQmlIncubatorPrivate::get(incubationTask); - QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(engine); - QQmlComponentPrivate *componentPriv = QQmlComponentPrivate::get(component); - - incubatorPriv->compiledData = componentPriv->cc; - incubatorPriv->compiledData->addref(); - incubatorPriv->vme.init( - context, - componentPriv->cc, - componentPriv->start, - componentPriv->creationContext); - - enginePriv->incubate(*incubationTask, forContext); -} - -void QQmlDelegateModelItem::destroyObject() -{ - Q_ASSERT(object); - Q_ASSERT(contextData); - - QObjectPrivate *p = QObjectPrivate::get(object); - Q_ASSERT(p->declarativeData); - QQmlData *data = static_cast(p->declarativeData); - if (data->ownContext && data->context) - data->context->clearContext(); - object->deleteLater(); - - if (attached) { - attached->m_cacheItem = 0; - attached = 0; - } - - contextData->destroy(); - contextData = 0; - object = 0; -} - -QQmlDelegateModelItem *QQmlDelegateModelItem::dataForObject(QObject *object) -{ - QObjectPrivate *p = QObjectPrivate::get(object); - QQmlContextData *context = p->declarativeData - ? static_cast(p->declarativeData)->context - : 0; - for (context = context ? context->parent : 0; context; context = context->parent) { - if (QQmlDelegateModelItem *cacheItem = qobject_cast( - context->contextObject)) { - return cacheItem; - } - } - return 0; -} - -int QQmlDelegateModelItem::groupIndex(Compositor::Group group) -{ - if (QQmlDelegateModelPrivate * const model = metaType->model - ? QQmlDelegateModelPrivate::get(metaType->model) - : 0) { - return model->m_compositor.find(Compositor::Cache, model->m_cache.indexOf(this)).index[group]; - } - return -1; -} - -//--------------------------------------------------------------------------- - -QQmlDelegateModelAttachedMetaObject::QQmlDelegateModelAttachedMetaObject( - QQmlDelegateModelItemMetaType *metaType, QMetaObject *metaObject) - : metaType(metaType) - , metaObject(metaObject) - , memberPropertyOffset(QQmlDelegateModelAttached::staticMetaObject.propertyCount()) - , indexPropertyOffset(QQmlDelegateModelAttached::staticMetaObject.propertyCount() + metaType->groupNames.count()) -{ - // Don't reference count the meta-type here as that would create a circular reference. - // Instead we rely the fact that the meta-type's reference count can't reach 0 without first - // destroying all delegates with attached objects. - *static_cast(this) = *metaObject; -} - -QQmlDelegateModelAttachedMetaObject::~QQmlDelegateModelAttachedMetaObject() -{ - ::free(metaObject); -} - -void QQmlDelegateModelAttachedMetaObject::objectDestroyed(QObject *) -{ - release(); -} - -int QQmlDelegateModelAttachedMetaObject::metaCall(QObject *object, QMetaObject::Call call, int _id, void **arguments) -{ - QQmlDelegateModelAttached *attached = static_cast(object); - if (call == QMetaObject::ReadProperty) { - if (_id >= indexPropertyOffset) { - Compositor::Group group = Compositor::Group(_id - indexPropertyOffset + 1); - *static_cast(arguments[0]) = attached->m_currentIndex[group]; - return -1; - } else if (_id >= memberPropertyOffset) { - Compositor::Group group = Compositor::Group(_id - memberPropertyOffset + 1); - *static_cast(arguments[0]) = attached->m_cacheItem->groups & (1 << group); - return -1; - } - } else if (call == QMetaObject::WriteProperty) { - if (_id >= memberPropertyOffset) { - if (!metaType->model) - return -1; - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(metaType->model); - Compositor::Group group = Compositor::Group(_id - memberPropertyOffset + 1); - const int groupFlag = 1 << group; - const bool member = attached->m_cacheItem->groups & groupFlag; - if (member && !*static_cast(arguments[0])) { - Compositor::iterator it = model->m_compositor.find( - group, attached->m_currentIndex[group]); - model->removeGroups(it, 1, group, groupFlag); - } else if (!member && *static_cast(arguments[0])) { - for (int i = 1; i < metaType->groupCount; ++i) { - if (attached->m_cacheItem->groups & (1 << i)) { - Compositor::iterator it = model->m_compositor.find( - Compositor::Group(i), attached->m_currentIndex[i]); - model->addGroups(it, 1, Compositor::Group(i), groupFlag); - break; - } - } - } - return -1; - } - } - return attached->qt_metacall(call, _id, arguments); -} - -QQmlDelegateModelAttached::QQmlDelegateModelAttached(QObject *parent) - : m_cacheItem(0) - , m_previousGroups(0) -{ - QQml_setParent_noEvent(this, parent); -} - -QQmlDelegateModelAttached::QQmlDelegateModelAttached( - QQmlDelegateModelItem *cacheItem, QObject *parent) - : m_cacheItem(cacheItem) - , m_previousGroups(cacheItem->groups) -{ - QQml_setParent_noEvent(this, parent); - if (QQDMIncubationTask *incubationTask = m_cacheItem->incubationTask) { - for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i) - m_currentIndex[i] = m_previousIndex[i] = incubationTask->index[i]; - } else { - QQmlDelegateModelPrivate * const model = QQmlDelegateModelPrivate::get(m_cacheItem->metaType->model); - Compositor::iterator it = model->m_compositor.find( - Compositor::Cache, model->m_cache.indexOf(m_cacheItem)); - for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i) - m_currentIndex[i] = m_previousIndex[i] = it.index[i]; - } - - if (!cacheItem->metaType->metaObject) - cacheItem->metaType->initializeMetaObject(); - - QObjectPrivate::get(this)->metaObject = cacheItem->metaType->metaObject; - cacheItem->metaType->metaObject->addref(); -} - -/*! - \qmlattachedproperty int QtQml.Models2::DelegateModel::model - - This attached property holds the visual data model this delegate instance belongs to. - - It is attached to each instance of the delegate. -*/ - -QQmlDelegateModel *QQmlDelegateModelAttached::model() const -{ - return m_cacheItem ? m_cacheItem->metaType->model : 0; -} - -/*! - \qmlattachedproperty stringlist QtQml.Models2::DelegateModel::groups - - This attached property holds the name of DelegateModelGroups the item belongs to. - - It is attached to each instance of the delegate. -*/ - -QStringList QQmlDelegateModelAttached::groups() const -{ - QStringList groups; - - if (!m_cacheItem) - return groups; - for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i) { - if (m_cacheItem->groups & (1 << i)) - groups.append(m_cacheItem->metaType->groupNames.at(i - 1)); - } - return groups; -} - -void QQmlDelegateModelAttached::setGroups(const QStringList &groups) -{ - if (!m_cacheItem) - return; - - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_cacheItem->metaType->model); - - const int groupFlags = model->m_cacheMetaType->parseGroups(groups); - const int cacheIndex = model->m_cache.indexOf(m_cacheItem); - Compositor::iterator it = model->m_compositor.find(Compositor::Cache, cacheIndex); - model->setGroups(it, 1, Compositor::Cache, groupFlags); -} - -/*! - \qmlattachedproperty bool QtQml.Models2::DelegateModel::isUnresolved - - This attached property holds whether the visual item is bound to a data model index. - Returns true if the item is not bound to the model, and false if it is. - - An unresolved item can be bound to the data model using the DelegateModelGroup::resolve() - function. - - It is attached to each instance of the delegate. -*/ - -bool QQmlDelegateModelAttached::isUnresolved() const -{ - if (!m_cacheItem) - return false; - - return m_cacheItem->groups & Compositor::UnresolvedFlag; -} - -/*! - \qmlattachedproperty int QtQml.Models2::DelegateModel::inItems - - This attached property holds whether the item belongs to the default \l items DelegateModelGroup. - - Changing this property will add or remove the item from the items group. - - It is attached to each instance of the delegate. -*/ - -/*! - \qmlattachedproperty int QtQml.Models2::DelegateModel::itemsIndex - - This attached property holds the index of the item in the default \l items DelegateModelGroup. - - It is attached to each instance of the delegate. -*/ - -/*! - \qmlattachedproperty int QtQml.Models2::DelegateModel::inPersistedItems - - This attached property holds whether the item belongs to the \l persistedItems DelegateModelGroup. - - Changing this property will add or remove the item from the items group. Change with caution - as removing an item from the persistedItems group will destroy the current instance if it is - not referenced by a model. - - It is attached to each instance of the delegate. -*/ - -/*! - \qmlattachedproperty int QtQml.Models2::DelegateModel::persistedItemsIndex - - This attached property holds the index of the item in the \l persistedItems DelegateModelGroup. - - It is attached to each instance of the delegate. -*/ - -void QQmlDelegateModelAttached::emitChanges() -{ - const int groupChanges = m_previousGroups ^ m_cacheItem->groups; - m_previousGroups = m_cacheItem->groups; - - int indexChanges = 0; - for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i) { - if (m_previousIndex[i] != m_currentIndex[i]) { - m_previousIndex[i] = m_currentIndex[i]; - indexChanges |= (1 << i); - } - } - - int notifierId = 0; - const QMetaObject *meta = metaObject(); - for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i, ++notifierId) { - if (groupChanges & (1 << i)) - QMetaObject::activate(this, meta, notifierId, 0); - } - for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i, ++notifierId) { - if (indexChanges & (1 << i)) - QMetaObject::activate(this, meta, notifierId, 0); - } - - if (groupChanges) - emit groupsChanged(); -} - -//============================================================================ - -void QQmlDelegateModelGroupPrivate::setModel(QQmlDelegateModel *m, Compositor::Group g) -{ - Q_ASSERT(!model); - model = m; - group = g; -} - -bool QQmlDelegateModelGroupPrivate::isChangedConnected() -{ - Q_Q(QQmlDelegateModelGroup); - IS_SIGNAL_CONNECTED(q, QQmlDelegateModelGroup, changed, (const QQmlV8Handle &,const QQmlV8Handle &)); -} - -void QQmlDelegateModelGroupPrivate::emitChanges(QV8Engine *engine) -{ - Q_Q(QQmlDelegateModelGroup); - if (isChangedConnected() && !changeSet.isEmpty()) { - v8::HandleScope handleScope; - v8::Context::Scope contextScope(engine->context()); - v8::Local removed = engineData(engine)->array(engine, changeSet.removes()); - v8::Local inserted = engineData(engine)->array(engine, changeSet.inserts()); - emit q->changed(QQmlV8Handle::fromHandle(removed), QQmlV8Handle::fromHandle(inserted)); - } - if (changeSet.difference() != 0) - emit q->countChanged(); -} - -void QQmlDelegateModelGroupPrivate::emitModelUpdated(bool reset) -{ - for (QQmlDelegateModelGroupEmitterList::iterator it = emitters.begin(); it != emitters.end(); ++it) - it->emitModelUpdated(changeSet, reset); - changeSet.clear(); -} - -void QQmlDelegateModelGroupPrivate::createdPackage(int index, QQuickPackage *package) -{ - for (QQmlDelegateModelGroupEmitterList::iterator it = emitters.begin(); it != emitters.end(); ++it) - it->createdPackage(index, package); -} - -void QQmlDelegateModelGroupPrivate::initPackage(int index, QQuickPackage *package) -{ - for (QQmlDelegateModelGroupEmitterList::iterator it = emitters.begin(); it != emitters.end(); ++it) - it->initPackage(index, package); -} - -void QQmlDelegateModelGroupPrivate::destroyingPackage(QQuickPackage *package) -{ - for (QQmlDelegateModelGroupEmitterList::iterator it = emitters.begin(); it != emitters.end(); ++it) - it->destroyingPackage(package); -} - -/*! - \qmltype DelegateModelGroup - \instantiates QQmlDelegateModelGroup - \inqmlmodule QtQuick 2 - \ingroup qtquick-models - \brief Encapsulates a filtered set of visual data items - - The DelegateModelGroup type provides a means to address the model data of a DelegateModel's - delegate items, as well as sort and filter these delegate items. - - The initial set of instantiable delegate items in a DelegateModel is represented - by its \l {QtQml.Models2::DelegateModel::items}{items} group, which normally directly reflects - the contents of the model assigned to DelegateModel::model. This set can be changed to - the contents of any other member of DelegateModel::groups by assigning the \l name of that - DelegateModelGroup to the DelegateModel::filterOnGroup property. - - The data of an item in a DelegateModelGroup can be accessed using the get() function, which returns - information about group membership and indexes as well as model data. In combination - with the move() function this can be used to implement view sorting, with remove() to filter - items out of a view, or with setGroups() and \l Package delegates to categorize items into - different views. - - Data from models can be supplemented by inserting data directly into a DelegateModelGroup - with the insert() function. This can be used to introduce mock items into a view, or - placeholder items that are later \l {resolve()}{resolved} to real model data when it becomes - available. - - Delegate items can also be be instantiated directly from a DelegateModelGroup using the - create() function, making it possible to use DelegateModel without an accompanying view - type or to cherry-pick specific items that should be instantiated irregardless of whether - they're currently within a view's visible area. - - \sa {QML Dynamic View Ordering Tutorial} -*/ -/*! - \qmltype DelegateModelGroup - \instantiates QQmlDelegateModelGroup - \inqmlmodule QtQml.Models 2 - \brief Encapsulates a filtered set of visual data items - - The DelegateModelGroup type provides a means to address the model data of a DelegateModel's - delegate items, as well as sort and filter these delegate items. - - This element is also available as DelegateModelGroup in the QtQuick module. For full details, - see the \l DelegateModelGroup documentation. - - \sa {QtQuick::DelegateModelGroup} -*/ - - -QQmlDelegateModelGroup::QQmlDelegateModelGroup(QObject *parent) - : QObject(*new QQmlDelegateModelGroupPrivate, parent) -{ -} - -QQmlDelegateModelGroup::QQmlDelegateModelGroup( - const QString &name, QQmlDelegateModel *model, int index, QObject *parent) - : QObject(*new QQmlDelegateModelGroupPrivate, parent) -{ - Q_D(QQmlDelegateModelGroup); - d->name = name; - d->setModel(model, Compositor::Group(index)); -} - -QQmlDelegateModelGroup::~QQmlDelegateModelGroup() -{ -} - -/*! - \qmlproperty string QtQml.Models2::DelegateModelGroup::name - - This property holds the name of the group. - - Each group in a model must have a unique name starting with a lower case letter. -*/ - -QString QQmlDelegateModelGroup::name() const -{ - Q_D(const QQmlDelegateModelGroup); - return d->name; -} - -void QQmlDelegateModelGroup::setName(const QString &name) -{ - Q_D(QQmlDelegateModelGroup); - if (d->model) - return; - if (d->name != name) { - d->name = name; - emit nameChanged(); - } -} - -/*! - \qmlproperty int QtQml.Models2::DelegateModelGroup::count - - This property holds the number of items in the group. -*/ - -int QQmlDelegateModelGroup::count() const -{ - Q_D(const QQmlDelegateModelGroup); - if (!d->model) - return 0; - return QQmlDelegateModelPrivate::get(d->model)->m_compositor.count(d->group); -} - -/*! - \qmlproperty bool QtQml.Models2::DelegateModelGroup::includeByDefault - - This property holds whether new items are assigned to this group by default. -*/ - -bool QQmlDelegateModelGroup::defaultInclude() const -{ - Q_D(const QQmlDelegateModelGroup); - return d->defaultInclude; -} - -void QQmlDelegateModelGroup::setDefaultInclude(bool include) -{ - Q_D(QQmlDelegateModelGroup); - if (d->defaultInclude != include) { - d->defaultInclude = include; - - if (d->model) { - if (include) - QQmlDelegateModelPrivate::get(d->model)->m_compositor.setDefaultGroup(d->group); - else - QQmlDelegateModelPrivate::get(d->model)->m_compositor.clearDefaultGroup(d->group); - } - emit defaultIncludeChanged(); - } -} - -/*! - \qmlmethod object QtQml.Models2::DelegateModelGroup::get(int index) - - Returns a javascript object describing the item at \a index in the group. - - The returned object contains the same information that is available to a delegate from the - DelegateModel attached as well as the model for that item. It has the properties: - - \list - \li \b model The model data of the item. This is the same as the model context property in - a delegate - \li \b groups A list the of names of groups the item is a member of. This property can be - written to change the item's membership. - \li \b inItems Whether the item belongs to the \l {QtQml.Models2::DelegateModel::items}{items} group. - Writing to this property will add or remove the item from the group. - \li \b itemsIndex The index of the item within the \l {QtQml.Models2::DelegateModel::items}{items} group. - \li \b {in} Whether the item belongs to the dynamic group \e groupName. Writing to - this property will add or remove the item from the group. - \li \b {Index} The index of the item within the dynamic group \e groupName. - \li \b isUnresolved Whether the item is bound to an index in the model assigned to - DelegateModel::model. Returns true if the item is not bound to the model, and false if it is. - \endlist -*/ - -QQmlV8Handle QQmlDelegateModelGroup::get(int index) -{ - Q_D(QQmlDelegateModelGroup); - if (!d->model) - return QQmlV8Handle::fromHandle(v8::Undefined());; - - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); - if (!model->m_context->isValid()) { - return QQmlV8Handle::fromHandle(v8::Undefined()); - } else if (index < 0 || index >= model->m_compositor.count(d->group)) { - qmlInfo(this) << tr("get: index out of range"); - return QQmlV8Handle::fromHandle(v8::Undefined()); - } - - Compositor::iterator it = model->m_compositor.find(d->group, index); - QQmlDelegateModelItem *cacheItem = it->inCache() - ? model->m_cache.at(it.cacheIndex) - : 0; - - if (!cacheItem) { - cacheItem = model->m_adaptorModel.createItem( - model->m_cacheMetaType, model->m_context->engine(), it.modelIndex()); - if (!cacheItem) - return QQmlV8Handle::fromHandle(v8::Undefined()); - cacheItem->groups = it->flags; - - model->m_cache.insert(it.cacheIndex, cacheItem); - model->m_compositor.setFlags(it, 1, Compositor::CacheFlag); - } - - if (model->m_cacheMetaType->constructor.IsEmpty()) - model->m_cacheMetaType->initializeConstructor(); - v8::Local handle = model->m_cacheMetaType->constructor->NewInstance(); - handle->SetExternalResource(cacheItem); - ++cacheItem->scriptRef; - - return QQmlV8Handle::fromHandle(handle); -} - -bool QQmlDelegateModelGroupPrivate::parseIndex( - const v8::Local &value, int *index, Compositor::Group *group) const -{ - if (value->IsInt32()) { - *index = value->Int32Value(); - return true; - } else if (value->IsObject()) { - v8::Local object = value->ToObject(); - QQmlDelegateModelItem * const cacheItem = v8_resource_cast(object); - if (QQmlDelegateModelPrivate *model = cacheItem && cacheItem->metaType->model - ? QQmlDelegateModelPrivate::get(cacheItem->metaType->model) - : 0) { - *index = model->m_cache.indexOf(cacheItem); - *group = Compositor::Cache; - return true; - } - } - return false; -} - -/*! - \qmlmethod QtQml.Models2::DelegateModelGroup::insert(int index, jsdict data, array groups = undefined) - \qmlmethod QtQml.Models2::DelegateModelGroup::insert(jsdict data, var groups = undefined) - - Creates a new entry at \a index in a DelegateModel with the values from \a data that - correspond to roles in the model assigned to DelegateModel::model. - - If no index is supplied the data is appended to the model. - - The optional \a groups parameter identifies the groups the new entry should belong to, - if unspecified this is equal to the group insert was called on. - - Data inserted into a DelegateModel can later be merged with an existing entry in - DelegateModel::model using the \l resolve() function. This can be used to create placeholder - items that are later replaced by actual data. -*/ - -void QQmlDelegateModelGroup::insert(QQmlV8Function *args) -{ - Q_D(QQmlDelegateModelGroup); - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); - - int index = model->m_compositor.count(d->group); - Compositor::Group group = d->group; - - if (args->Length() == 0) - return; - - int i = 0; - v8::Local v = (*args)[i]; - if (d->parseIndex(v, &index, &group)) { - if (index < 0 || index > model->m_compositor.count(group)) { - qmlInfo(this) << tr("insert: index out of range"); - return; - } - if (++i == args->Length()) - return; - v = (*args)[i]; - } - - Compositor::insert_iterator before = index < model->m_compositor.count(group) - ? model->m_compositor.findInsertPosition(group, index) - : model->m_compositor.end(); - - int groups = 1 << d->group; - if (++i < args->Length()) - groups |= model->m_cacheMetaType->parseGroups((*args)[i]); - - if (v->IsArray()) { - return; - } else if (v->IsObject()) { - model->insert(before, v->ToObject(), groups); - model->emitChanges(); - } -} - -/*! - \qmlmethod QtQml.Models2::DelegateModelGroup::create(int index) - \qmlmethod QtQml.Models2::DelegateModelGroup::create(int index, jsdict data, array groups = undefined) - \qmlmethod QtQml.Models2::DelegateModelGroup::create(jsdict data, array groups = undefined) - - Returns a reference to the instantiated item at \a index in the group. - - If a \a data object is provided it will be \l {insert}{inserted} at \a index and an item - referencing this new entry will be returned. The optional \a groups parameter identifies - the groups the new entry should belong to, if unspecified this is equal to the group create() - was called on. - - All items returned by create are added to the - \l {QtQml.Models2::DelegateModel::persistedItems}{persistedItems} group. Items in this - group remain instantiated when not referenced by any view. -*/ - -void QQmlDelegateModelGroup::create(QQmlV8Function *args) -{ - Q_D(QQmlDelegateModelGroup); - if (!d->model) - return; - - if (args->Length() == 0) - return; - - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); - - int index = model->m_compositor.count(d->group); - Compositor::Group group = d->group; - - int i = 0; - v8::Local v = (*args)[i]; - if (d->parseIndex(v, &index, &group)) - ++i; - - if (i < args->Length() && index >= 0 && index <= model->m_compositor.count(group)) { - v = (*args)[i]; - if (v->IsObject()) { - int groups = 1 << d->group; - if (++i < args->Length()) - groups |= model->m_cacheMetaType->parseGroups((*args)[i]); - - Compositor::insert_iterator before = index < model->m_compositor.count(group) - ? model->m_compositor.findInsertPosition(group, index) - : model->m_compositor.end(); - - index = before.index[d->group]; - group = d->group; - - if (!model->insert(before, v->ToObject(), groups)) { - return; - } - } - } - if (index < 0 || index >= model->m_compositor.count(group)) { - qmlInfo(this) << tr("create: index out of range"); - return; - } - - QObject *object = model->object(group, index, false); - if (object) { - QVector inserts; - Compositor::iterator it = model->m_compositor.find(group, index); - model->m_compositor.setFlags(it, 1, d->group, Compositor::PersistedFlag, &inserts); - model->itemsInserted(inserts); - model->m_cache.at(it.cacheIndex)->releaseObject(); - } - - args->returnValue(args->engine()->newQObject(object)); - model->emitChanges(); -} - -/*! - \qmlmethod QtQml.Models2::DelegateModelGroup::resolve(int from, int to) - - Binds an unresolved item at \a from to an item in DelegateModel::model at index \a to. - - Unresolved items are entries whose data has been \l {insert()}{inserted} into a DelegateModelGroup - instead of being derived from a DelegateModel::model index. Resolving an item will replace - the item at the target index with the unresolved item. A resolved an item will reflect the data - of the source model at its bound index and will move when that index moves like any other item. - - If a new item is replaced in the DelegateModelGroup onChanged() handler its insertion and - replacement will be communicated to views as an atomic operation, creating the appearance - that the model contents have not changed, or if the unresolved and model item are not adjacent - that the previously unresolved item has simply moved. - -*/ -void QQmlDelegateModelGroup::resolve(QQmlV8Function *args) -{ - Q_D(QQmlDelegateModelGroup); - if (!d->model) - return; - - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); - - if (args->Length() < 2) - return; - - int from = -1; - int to = -1; - Compositor::Group fromGroup = d->group; - Compositor::Group toGroup = d->group; - - v8::Local v = (*args)[0]; - if (d->parseIndex(v, &from, &fromGroup)) { - if (from < 0 || from >= model->m_compositor.count(fromGroup)) { - qmlInfo(this) << tr("resolve: from index out of range"); - return; - } - } else { - qmlInfo(this) << tr("resolve: from index invalid"); - return; - } - - v = (*args)[1]; - if (d->parseIndex(v, &to, &toGroup)) { - if (to < 0 || to >= model->m_compositor.count(toGroup)) { - qmlInfo(this) << tr("resolve: to index out of range"); - return; - } - } else { - qmlInfo(this) << tr("resolve: to index invalid"); - return; - } - - Compositor::iterator fromIt = model->m_compositor.find(fromGroup, from); - Compositor::iterator toIt = model->m_compositor.find(toGroup, to); - - if (!fromIt->isUnresolved()) { - qmlInfo(this) << tr("resolve: from is not an unresolved item"); - return; - } - if (!toIt->list) { - qmlInfo(this) << tr("resolve: to is not a model item"); - return; - } - - const int unresolvedFlags = fromIt->flags; - const int resolvedFlags = toIt->flags; - const int resolvedIndex = toIt.modelIndex(); - void * const resolvedList = toIt->list; - - QQmlDelegateModelItem *cacheItem = model->m_cache.at(fromIt.cacheIndex); - cacheItem->groups &= ~Compositor::UnresolvedFlag; - - if (toIt.cacheIndex > fromIt.cacheIndex) - toIt.decrementIndexes(1, unresolvedFlags); - if (!toIt->inGroup(fromGroup) || toIt.index[fromGroup] > from) - from += 1; - - model->itemsMoved( - QVector() << Compositor::Remove(fromIt, 1, unresolvedFlags, 0), - QVector() << Compositor::Insert(toIt, 1, unresolvedFlags, 0)); - model->itemsInserted( - QVector() << Compositor::Insert(toIt, 1, (resolvedFlags & ~unresolvedFlags) | Compositor::CacheFlag)); - toIt.incrementIndexes(1, resolvedFlags | unresolvedFlags); - model->itemsRemoved(QVector() << Compositor::Remove(toIt, 1, resolvedFlags)); - - model->m_compositor.setFlags(toGroup, to, 1, unresolvedFlags & ~Compositor::UnresolvedFlag); - model->m_compositor.clearFlags(fromGroup, from, 1, unresolvedFlags); - - if (resolvedFlags & Compositor::CacheFlag) - model->m_compositor.insert(Compositor::Cache, toIt.cacheIndex, resolvedList, resolvedIndex, 1, Compositor::CacheFlag); - - Q_ASSERT(model->m_cache.count() == model->m_compositor.count(Compositor::Cache)); - - if (!cacheItem->isReferenced()) { - Q_ASSERT(toIt.cacheIndex == model->m_cache.indexOf(cacheItem)); - model->m_cache.removeAt(toIt.cacheIndex); - model->m_compositor.clearFlags(Compositor::Cache, toIt.cacheIndex, 1, Compositor::CacheFlag); - delete cacheItem; - Q_ASSERT(model->m_cache.count() == model->m_compositor.count(Compositor::Cache)); - } else { - cacheItem->resolveIndex(model->m_adaptorModel, resolvedIndex); - if (cacheItem->attached) - cacheItem->attached->emitUnresolvedChanged(); - } - - model->emitChanges(); -} - -/*! - \qmlmethod QtQml.Models2::DelegateModelGroup::remove(int index, int count) - - Removes \a count items starting at \a index from the group. -*/ - -void QQmlDelegateModelGroup::remove(QQmlV8Function *args) -{ - Q_D(QQmlDelegateModelGroup); - if (!d->model) - return; - Compositor::Group group = d->group; - int index = -1; - int count = 1; - - if (args->Length() == 0) - return; - - int i = 0; - v8::Local v = (*args)[i]; - if (!d->parseIndex(v, &index, &group)) { - qmlInfo(this) << tr("remove: invalid index"); - return; - } - - if (++i < args->Length()) { - v = (*args)[i]; - if (v->IsInt32()) - count = v->Int32Value(); - } - - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); - if (index < 0 || index >= model->m_compositor.count(group)) { - qmlInfo(this) << tr("remove: index out of range"); - } else if (count != 0) { - Compositor::iterator it = model->m_compositor.find(group, index); - if (count < 0 || count > model->m_compositor.count(d->group) - it.index[d->group]) { - qmlInfo(this) << tr("remove: invalid count"); - } else { - model->removeGroups(it, count, d->group, 1 << d->group); - } - } -} - -bool QQmlDelegateModelGroupPrivate::parseGroupArgs( - QQmlV8Function *args, Compositor::Group *group, int *index, int *count, int *groups) const -{ - if (!model || !QQmlDelegateModelPrivate::get(model)->m_cacheMetaType) - return false; - - if (args->Length() < 2) - return false; - - int i = 0; - v8::Local v = (*args)[i]; - if (!parseIndex(v, index, group)) - return false; - - v = (*args)[++i]; - if (v->IsInt32()) { - *count = v->Int32Value(); - - if (++i == args->Length()) - return false; - v = (*args)[i]; - } - - *groups = QQmlDelegateModelPrivate::get(model)->m_cacheMetaType->parseGroups(v); - - return true; -} - -/*! - \qmlmethod QtQml.Models2::DelegateModelGroup::addGroups(int index, int count, stringlist groups) - - Adds \a count items starting at \a index to \a groups. -*/ - -void QQmlDelegateModelGroup::addGroups(QQmlV8Function *args) -{ - Q_D(QQmlDelegateModelGroup); - Compositor::Group group = d->group; - int index = -1; - int count = 1; - int groups = 0; - - if (!d->parseGroupArgs(args, &group, &index, &count, &groups)) - return; - - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); - if (index < 0 || index >= model->m_compositor.count(group)) { - qmlInfo(this) << tr("addGroups: index out of range"); - } else if (count != 0) { - Compositor::iterator it = model->m_compositor.find(group, index); - if (count < 0 || count > model->m_compositor.count(d->group) - it.index[d->group]) { - qmlInfo(this) << tr("addGroups: invalid count"); - } else { - model->addGroups(it, count, d->group, groups); - } - } -} - -/*! - \qmlmethod QtQml.Models2::DelegateModelGroup::removeGroups(int index, int count, stringlist groups) - - Removes \a count items starting at \a index from \a groups. -*/ - -void QQmlDelegateModelGroup::removeGroups(QQmlV8Function *args) -{ - Q_D(QQmlDelegateModelGroup); - Compositor::Group group = d->group; - int index = -1; - int count = 1; - int groups = 0; - - if (!d->parseGroupArgs(args, &group, &index, &count, &groups)) - return; - - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); - if (index < 0 || index >= model->m_compositor.count(group)) { - qmlInfo(this) << tr("removeGroups: index out of range"); - } else if (count != 0) { - Compositor::iterator it = model->m_compositor.find(group, index); - if (count < 0 || count > model->m_compositor.count(d->group) - it.index[d->group]) { - qmlInfo(this) << tr("removeGroups: invalid count"); - } else { - model->removeGroups(it, count, d->group, groups); - } - } -} - -/*! - \qmlmethod QtQml.Models2::DelegateModelGroup::setGroups(int index, int count, stringlist groups) - - Sets the \a groups \a count items starting at \a index belong to. -*/ - -void QQmlDelegateModelGroup::setGroups(QQmlV8Function *args) -{ - Q_D(QQmlDelegateModelGroup); - Compositor::Group group = d->group; - int index = -1; - int count = 1; - int groups = 0; - - if (!d->parseGroupArgs(args, &group, &index, &count, &groups)) - return; - - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); - if (index < 0 || index >= model->m_compositor.count(group)) { - qmlInfo(this) << tr("setGroups: index out of range"); - } else if (count != 0) { - Compositor::iterator it = model->m_compositor.find(group, index); - if (count < 0 || count > model->m_compositor.count(d->group) - it.index[d->group]) { - qmlInfo(this) << tr("setGroups: invalid count"); - } else { - model->setGroups(it, count, d->group, groups); - } - } -} - -/*! - \qmlmethod QtQml.Models2::DelegateModelGroup::setGroups(int index, int count, stringlist groups) - - Sets the \a groups \a count items starting at \a index belong to. -*/ - -/*! - \qmlmethod QtQml.Models2::DelegateModelGroup::move(var from, var to, int count) - - Moves \a count at \a from in a group \a to a new position. -*/ - -void QQmlDelegateModelGroup::move(QQmlV8Function *args) -{ - Q_D(QQmlDelegateModelGroup); - - if (args->Length() < 2) - return; - - Compositor::Group fromGroup = d->group; - Compositor::Group toGroup = d->group; - int from = -1; - int to = -1; - int count = 1; - - if (!d->parseIndex((*args)[0], &from, &fromGroup)) { - qmlInfo(this) << tr("move: invalid from index"); - return; - } - - if (!d->parseIndex((*args)[1], &to, &toGroup)) { - qmlInfo(this) << tr("move: invalid to index"); - return; - } - - if (args->Length() > 2) { - v8::Local v = (*args)[2]; - if (v->IsInt32()) - count = v->Int32Value(); - } - - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); - - if (count < 0) { - qmlInfo(this) << tr("move: invalid count"); - } else if (from < 0 || from + count > model->m_compositor.count(fromGroup)) { - qmlInfo(this) << tr("move: from index out of range"); - } else if (!model->m_compositor.verifyMoveTo(fromGroup, from, toGroup, to, count, d->group)) { - qmlInfo(this) << tr("move: to index out of range"); - } else if (count > 0) { - QVector removes; - QVector inserts; - - model->m_compositor.move(fromGroup, from, toGroup, to, count, d->group, &removes, &inserts); - model->itemsMoved(removes, inserts); - model->emitChanges(); - } - -} - -/*! - \qmlsignal QtQml.Models2::DelegateModelGroup::onChanged(array removed, array inserted) - - This handler is called when items have been removed from or inserted into the group. - - Each object in the \a removed and \a inserted arrays has two values; the \e index of the first - item inserted or removed and a \e count of the number of consecutive items inserted or removed. - - Each index is adjusted for previous changes with all removed items preceding any inserted - items. -*/ - -//============================================================================ - -QQmlPartsModel::QQmlPartsModel(QQmlDelegateModel *model, const QString &part, QObject *parent) - : QQmlInstanceModel(*new QObjectPrivate, parent) - , m_model(model) - , m_part(part) - , m_compositorGroup(Compositor::Cache) - , m_inheritGroup(true) -{ - QQmlDelegateModelPrivate *d = QQmlDelegateModelPrivate::get(m_model); - if (d->m_cacheMetaType) { - QQmlDelegateModelGroupPrivate::get(d->m_groups[1])->emitters.insert(this); - m_compositorGroup = Compositor::Default; - } else { - d->m_pendingParts.insert(this); - } -} - -QQmlPartsModel::~QQmlPartsModel() -{ -} - -QString QQmlPartsModel::filterGroup() const -{ - if (m_inheritGroup) - return m_model->filterGroup(); - return m_filterGroup; -} - -void QQmlPartsModel::setFilterGroup(const QString &group) -{ - if (QQmlDelegateModelPrivate::get(m_model)->m_transaction) { - qmlInfo(this) << tr("The group of a DelegateModel cannot be changed within onChanged"); - return; - } - - if (m_filterGroup != group || m_inheritGroup) { - m_filterGroup = group; - m_inheritGroup = false; - updateFilterGroup(); - - emit filterGroupChanged(); - } -} - -void QQmlPartsModel::resetFilterGroup() -{ - if (!m_inheritGroup) { - m_inheritGroup = true; - updateFilterGroup(); - emit filterGroupChanged(); - } -} - -void QQmlPartsModel::updateFilterGroup() -{ - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_model); - if (!model->m_cacheMetaType) - return; - - if (m_inheritGroup) { - if (m_filterGroup == model->m_filterGroup) - return; - m_filterGroup = model->m_filterGroup; - } - - QQmlListCompositor::Group previousGroup = m_compositorGroup; - m_compositorGroup = Compositor::Default; - QQmlDelegateModelGroupPrivate::get(model->m_groups[Compositor::Default])->emitters.insert(this); - for (int i = 1; i < model->m_groupCount; ++i) { - if (m_filterGroup == model->m_cacheMetaType->groupNames.at(i - 1)) { - m_compositorGroup = Compositor::Group(i); - break; - } - } - - QQmlDelegateModelGroupPrivate::get(model->m_groups[m_compositorGroup])->emitters.insert(this); - if (m_compositorGroup != previousGroup) { - QVector removes; - QVector inserts; - model->m_compositor.transition(previousGroup, m_compositorGroup, &removes, &inserts); - - QQmlChangeSet changeSet; - changeSet.move(removes, inserts); - if (!changeSet.isEmpty()) - emit modelUpdated(changeSet, false); - - if (changeSet.difference() != 0) - emit countChanged(); - } -} - -void QQmlPartsModel::updateFilterGroup( - Compositor::Group group, const QQmlChangeSet &changeSet) -{ - if (!m_inheritGroup) - return; - - m_compositorGroup = group; - QQmlDelegateModelGroupPrivate::get(QQmlDelegateModelPrivate::get(m_model)->m_groups[m_compositorGroup])->emitters.insert(this); - - if (!changeSet.isEmpty()) - emit modelUpdated(changeSet, false); - - if (changeSet.difference() != 0) - emit countChanged(); - - emit filterGroupChanged(); -} - -int QQmlPartsModel::count() const -{ - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_model); - return model->m_delegate - ? model->m_compositor.count(m_compositorGroup) - : 0; -} - -bool QQmlPartsModel::isValid() const -{ - return m_model->isValid(); -} - -QObject *QQmlPartsModel::object(int index, bool asynchronous) -{ - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_model); - - if (!model->m_delegate || index < 0 || index >= model->m_compositor.count(m_compositorGroup)) { - qWarning() << "DelegateModel::item: index out range" << index << model->m_compositor.count(m_compositorGroup); - return 0; - } - - QObject *object = model->object(m_compositorGroup, index, asynchronous); - - if (QQuickPackage *package = qmlobject_cast(object)) { - QObject *part = package->part(m_part); - if (!part) - return 0; - m_packaged.insertMulti(part, package); - return part; - } - - model->release(object); - if (!model->m_delegateValidated) { - if (object) - qmlInfo(model->m_delegate) << tr("Delegate component must be Package type."); - model->m_delegateValidated = true; - } - - return 0; -} - -QQmlInstanceModel::ReleaseFlags QQmlPartsModel::release(QObject *item) -{ - QQmlInstanceModel::ReleaseFlags flags = 0; - - QHash::iterator it = m_packaged.find(item); - if (it != m_packaged.end()) { - QQuickPackage *package = *it; - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_model); - flags = model->release(package); - m_packaged.erase(it); - if (!m_packaged.contains(item)) - flags &= ~Referenced; - if (flags & Destroyed) - QQmlDelegateModelPrivate::get(m_model)->emitDestroyingPackage(package); - } - return flags; -} - -QString QQmlPartsModel::stringValue(int index, const QString &role) -{ - return QQmlDelegateModelPrivate::get(m_model)->stringValue(m_compositorGroup, index, role); -} - -void QQmlPartsModel::setWatchedRoles(QList roles) -{ - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_model); - model->m_adaptorModel.replaceWatchedRoles(m_watchedRoles, roles); - m_watchedRoles = roles; -} - -int QQmlPartsModel::indexOf(QObject *item, QObject *) const -{ - QHash::const_iterator it = m_packaged.find(item); - if (it != m_packaged.end()) { - if (QQmlDelegateModelItem *cacheItem = QQmlDelegateModelItem::dataForObject(*it)) - return cacheItem->groupIndex(m_compositorGroup); - } - return -1; -} - -void QQmlPartsModel::createdPackage(int index, QQuickPackage *package) -{ - emit createdItem(index, package->part(m_part)); -} - -void QQmlPartsModel::initPackage(int index, QQuickPackage *package) -{ - emit initItem(index, package->part(m_part)); -} - -void QQmlPartsModel::destroyingPackage(QQuickPackage *package) -{ - QObject *item = package->part(m_part); - Q_ASSERT(!m_packaged.contains(item)); - emit destroyingItem(item); -} - -void QQmlPartsModel::emitModelUpdated(const QQmlChangeSet &changeSet, bool reset) -{ - emit modelUpdated(changeSet, reset); - if (changeSet.difference() != 0) - emit countChanged(); -} - -//============================================================================ - -v8::Handle get_change_index(v8::Local, const v8::AccessorInfo &info) -{ - return info.This()->GetInternalField(0); -} - -v8::Handle get_change_count(v8::Local, const v8::AccessorInfo &info) -{ - return info.This()->GetInternalField(1); -} - -v8::Handle get_change_moveId(v8::Local, const v8::AccessorInfo &info) -{ - return info.This()->GetInternalField(2); -} - -class QQmlDelegateModelGroupChangeArray : public QV8ObjectResource -{ - V8_RESOURCE_TYPE(ChangeSetArrayType) -public: - QQmlDelegateModelGroupChangeArray(QV8Engine *engine) - : QV8ObjectResource(engine) - { - } - - virtual quint32 count() const = 0; - virtual const QQmlChangeSet::Change &at(int index) const = 0; - - static v8::Handle get_change(quint32 index, const v8::AccessorInfo &info) - { - QQmlDelegateModelGroupChangeArray *array = v8_resource_cast(info.This()); - V8ASSERT_TYPE(array, "Not a valid change array"); - - if (index >= array->count()) - return v8::Undefined(); - - const QQmlChangeSet::Change &change = array->at(index); - - v8::Local object = engineData(array->engine)->constructorChange->NewInstance(); - object->SetInternalField(0, v8::Int32::New(change.index)); - object->SetInternalField(1, v8::Int32::New(change.count)); - if (change.isMove()) - object->SetInternalField(2, v8::Int32::New(change.moveId)); - - return object; - } - - static v8::Handle get_length(v8::Local, const v8::AccessorInfo &info) - { - QQmlDelegateModelGroupChangeArray *array = v8_resource_cast(info.This()); - V8ASSERT_TYPE(array, "Not a valid change array"); - - return v8::Integer::New(array->count()); - } - - static v8::Local constructor() - { - v8::Local changeArray = v8::FunctionTemplate::New(); - changeArray->InstanceTemplate()->SetHasExternalResource(true); - changeArray->InstanceTemplate()->SetIndexedPropertyHandler(get_change); - changeArray->InstanceTemplate()->SetAccessor(v8::String::New("length"), get_length); - return changeArray->GetFunction(); - } -}; - -class QQmlDelegateModelGroupRemoveArray : public QQmlDelegateModelGroupChangeArray -{ -public: - QQmlDelegateModelGroupRemoveArray(QV8Engine *engine, const QVector &changes) - : QQmlDelegateModelGroupChangeArray(engine) - , changes(changes) - { - } - - quint32 count() const { return changes.count(); } - const QQmlChangeSet::Change &at(int index) const { return changes.at(index); } - -private: - QVector changes; -}; - -class QQmlDelegateModelGroupInsertArray : public QQmlDelegateModelGroupChangeArray -{ -public: - QQmlDelegateModelGroupInsertArray(QV8Engine *engine, const QVector &changes) - : QQmlDelegateModelGroupChangeArray(engine) - , changes(changes) - { - } - - quint32 count() const { return changes.count(); } - const QQmlChangeSet::Change &at(int index) const { return changes.at(index); } - -private: - QVector changes; -}; - -QQmlDelegateModelEngineData::QQmlDelegateModelEngineData(QV8Engine *) -{ - strings = qPersistentNew(v8::Array::New(StringCount)); - strings->Set(Model, v8::String::New("model")); - strings->Set(Groups, v8::String::New("groups")); - strings->Set(IsUnresolved, v8::String::New("isUnresolved")); - strings->Set(ItemsIndex, v8::String::New("itemsIndex")); - strings->Set(PersistedItemsIndex, v8::String::New("persistedItemsIndex")); - strings->Set(InItems, v8::String::New("inItems")); - strings->Set(InPersistedItems, v8::String::New("inPersistedItems")); - - v8::Local change = v8::FunctionTemplate::New(); - change->InstanceTemplate()->SetAccessor(v8::String::New("index"), get_change_index); - change->InstanceTemplate()->SetAccessor(v8::String::New("count"), get_change_count); - change->InstanceTemplate()->SetAccessor(v8::String::New("moveId"), get_change_moveId); - change->InstanceTemplate()->SetInternalFieldCount(3); - constructorChange = qPersistentNew(change->GetFunction()); - constructorChangeArray = qPersistentNew(QQmlDelegateModelGroupChangeArray::constructor()); -} - -QQmlDelegateModelEngineData::~QQmlDelegateModelEngineData() -{ - qPersistentDispose(strings); - qPersistentDispose(constructorChange); - qPersistentDispose(constructorChangeArray); -} - -v8::Local QQmlDelegateModelEngineData::array( - QV8Engine *engine, const QVector &changes) -{ - v8::Local array = constructorChangeArray->NewInstance(); - array->SetExternalResource(new QQmlDelegateModelGroupRemoveArray(engine, changes)); - return array; -} - -v8::Local QQmlDelegateModelEngineData::array( - QV8Engine *engine, const QVector &changes) -{ - v8::Local array = constructorChangeArray->NewInstance(); - array->SetExternalResource(new QQmlDelegateModelGroupInsertArray(engine, changes)); - return array; -} - -QT_END_NAMESPACE - diff --git a/src/qml/items/qqmldelegatemodel_p.h b/src/qml/items/qqmldelegatemodel_p.h deleted file mode 100644 index 5702c59787..0000000000 --- a/src/qml/items/qqmldelegatemodel_p.h +++ /dev/null @@ -1,234 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** 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, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQMLDATAMODEL_P_H -#define QQMLDATAMODEL_P_H - -#include -#include -#include - -#include -#include - -#include -#include - -Q_DECLARE_METATYPE(QModelIndex) - -QT_BEGIN_NAMESPACE - -class QQmlChangeSet; -class QQmlComponent; -class QQuickPackage; -class QQmlV8Function; -class QQmlDelegateModelGroup; -class QQmlDelegateModelAttached; -class QQmlDelegateModelPrivate; - - -class Q_QML_PRIVATE_EXPORT QQmlDelegateModel : public QQmlInstanceModel, public QQmlParserStatus -{ - Q_OBJECT - Q_DECLARE_PRIVATE(QQmlDelegateModel) - - Q_PROPERTY(QVariant model READ model WRITE setModel) - Q_PROPERTY(QQmlComponent *delegate READ delegate WRITE setDelegate) - Q_PROPERTY(QString filterOnGroup READ filterGroup WRITE setFilterGroup NOTIFY filterGroupChanged RESET resetFilterGroup) - Q_PROPERTY(QQmlDelegateModelGroup *items READ items CONSTANT) //TODO : worth renaming? - Q_PROPERTY(QQmlDelegateModelGroup *persistedItems READ persistedItems CONSTANT) - Q_PROPERTY(QQmlListProperty groups READ groups CONSTANT) - Q_PROPERTY(QObject *parts READ parts CONSTANT) - Q_PROPERTY(QVariant rootIndex READ rootIndex WRITE setRootIndex NOTIFY rootIndexChanged) - Q_CLASSINFO("DefaultProperty", "delegate") - Q_INTERFACES(QQmlParserStatus) -public: - QQmlDelegateModel(); - QQmlDelegateModel(QQmlContext *, QObject *parent=0); - virtual ~QQmlDelegateModel(); - - void classBegin(); - void componentComplete(); - - QVariant model() const; - void setModel(const QVariant &); - - QQmlComponent *delegate() const; - void setDelegate(QQmlComponent *); - - QVariant rootIndex() const; - void setRootIndex(const QVariant &root); - - Q_INVOKABLE QVariant modelIndex(int idx) const; - Q_INVOKABLE QVariant parentModelIndex() const; - - int count() const; - bool isValid() const { return delegate() != 0; } - QObject *object(int index, bool asynchronous=false); - ReleaseFlags release(QObject *object); - void cancel(int index); - virtual QString stringValue(int index, const QString &role); - virtual void setWatchedRoles(QList roles); - - int indexOf(QObject *object, QObject *objectContext) const; - - QString filterGroup() const; - void setFilterGroup(const QString &group); - void resetFilterGroup(); - - QQmlDelegateModelGroup *items(); - QQmlDelegateModelGroup *persistedItems(); - QQmlListProperty groups(); - QObject *parts(); - - bool event(QEvent *); - - static QQmlDelegateModelAttached *qmlAttachedProperties(QObject *obj); - -Q_SIGNALS: - void filterGroupChanged(); - void defaultGroupsChanged(); - void rootIndexChanged(); - -private Q_SLOTS: - void _q_itemsChanged(int index, int count, const QVector &roles); - void _q_itemsInserted(int index, int count); - void _q_itemsRemoved(int index, int count); - void _q_itemsMoved(int from, int to, int count); - void _q_modelReset(); - void _q_rowsInserted(const QModelIndex &,int,int); - void _q_rowsAboutToBeRemoved(const QModelIndex &parent, int begin, int end); - void _q_rowsRemoved(const QModelIndex &,int,int); - void _q_rowsMoved(const QModelIndex &, int, int, const QModelIndex &, int); - void _q_dataChanged(const QModelIndex&,const QModelIndex&,const QVector &); - void _q_layoutChanged(); - -private: - Q_DISABLE_COPY(QQmlDelegateModel) -}; - -class QQmlDelegateModelGroupPrivate; -class Q_QML_PRIVATE_EXPORT QQmlDelegateModelGroup : public QObject -{ - Q_OBJECT - Q_PROPERTY(int count READ count NOTIFY countChanged) - Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) - Q_PROPERTY(bool includeByDefault READ defaultInclude WRITE setDefaultInclude NOTIFY defaultIncludeChanged) -public: - QQmlDelegateModelGroup(QObject *parent = 0); - QQmlDelegateModelGroup(const QString &name, QQmlDelegateModel *model, int compositorType, QObject *parent = 0); - ~QQmlDelegateModelGroup(); - - QString name() const; - void setName(const QString &name); - - int count() const; - - bool defaultInclude() const; - void setDefaultInclude(bool include); - - Q_INVOKABLE QQmlV8Handle get(int index); - -public Q_SLOTS: - void insert(QQmlV8Function *); - void create(QQmlV8Function *); - void resolve(QQmlV8Function *); - void remove(QQmlV8Function *); - void addGroups(QQmlV8Function *); - void removeGroups(QQmlV8Function *); - void setGroups(QQmlV8Function *); - void move(QQmlV8Function *); - -Q_SIGNALS: - void countChanged(); - void nameChanged(); - void defaultIncludeChanged(); - void changed(const QQmlV8Handle &removed, const QQmlV8Handle &inserted); -private: - Q_DECLARE_PRIVATE(QQmlDelegateModelGroup) -}; - -class QQmlDelegateModelItem; -class QQmlDelegateModelAttachedMetaObject; -class QQmlDelegateModelAttached : public QObject -{ - Q_OBJECT - Q_PROPERTY(QQmlDelegateModel *model READ model CONSTANT) - Q_PROPERTY(QStringList groups READ groups WRITE setGroups NOTIFY groupsChanged) - Q_PROPERTY(bool isUnresolved READ isUnresolved NOTIFY unresolvedChanged) -public: - QQmlDelegateModelAttached(QObject *parent); - QQmlDelegateModelAttached(QQmlDelegateModelItem *cacheItem, QObject *parent); - ~QQmlDelegateModelAttached() {} - - void setCacheItem(QQmlDelegateModelItem *item); - - QQmlDelegateModel *model() const; - - QStringList groups() const; - void setGroups(const QStringList &groups); - - bool isUnresolved() const; - - void emitChanges(); - - void emitUnresolvedChanged() { emit unresolvedChanged(); } - -Q_SIGNALS: - void groupsChanged(); - void unresolvedChanged(); - -public: - QQmlDelegateModelItem *m_cacheItem; - int m_previousGroups; - int m_currentIndex[QQmlListCompositor::MaximumGroupCount]; - int m_previousIndex[QQmlListCompositor::MaximumGroupCount]; - - friend class QQmlDelegateModelAttachedMetaObject; -}; - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QQmlDelegateModel) -QML_DECLARE_TYPEINFO(QQmlDelegateModel, QML_HAS_ATTACHED_PROPERTIES) -QML_DECLARE_TYPE(QQmlDelegateModelGroup) - -#endif // QQMLDATAMODEL_P_H diff --git a/src/qml/items/qqmldelegatemodel_p_p.h b/src/qml/items/qqmldelegatemodel_p_p.h deleted file mode 100644 index 68242f433d..0000000000 --- a/src/qml/items/qqmldelegatemodel_p_p.h +++ /dev/null @@ -1,411 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** 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, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQMLDATAMODEL_P_P_H -#define QQMLDATAMODEL_P_P_H - -#include "qqmldelegatemodel_p.h" - - -#include -#include - -#include -#include - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -QT_BEGIN_NAMESPACE - -typedef QQmlListCompositor Compositor; - -class QQmlDelegateModelAttachedMetaObject; - -class QQmlDelegateModelItemMetaType : public QQmlRefCount -{ -public: - QQmlDelegateModelItemMetaType(QV8Engine *engine, QQmlDelegateModel *model, const QStringList &groupNames); - ~QQmlDelegateModelItemMetaType(); - - void initializeMetaObject(); - void initializeConstructor(); - - int parseGroups(const QStringList &groupNames) const; - int parseGroups(const v8::Local &groupNames) const; - - static void release_index(v8::Persistent object, void *parameter); - static void release_model(v8::Persistent object, void *parameter); - - static v8::Handle get_model(v8::Local, const v8::AccessorInfo &info); - static v8::Handle get_groups(v8::Local, const v8::AccessorInfo &info); - static void set_groups( - v8::Local, v8::Local value, const v8::AccessorInfo &info); - static v8::Handle get_member(v8::Local, const v8::AccessorInfo &info); - static void set_member( - v8::Local, v8::Local value, const v8::AccessorInfo &info); - static v8::Handle get_index(v8::Local, const v8::AccessorInfo &info); - - QQmlGuard model; - const int groupCount; - QV8Engine * const v8Engine; - QQmlDelegateModelAttachedMetaObject *metaObject; - const QStringList groupNames; - v8::Persistent constructor; -}; - -class QQmlAdaptorModel; -class QQDMIncubationTask; - -class QQmlDelegateModelItem : public QObject, public QV8ObjectResource -{ - Q_OBJECT - Q_PROPERTY(int index READ modelIndex NOTIFY modelIndexChanged) - Q_PROPERTY(QObject *model READ modelObject CONSTANT) - V8_RESOURCE_TYPE(VisualDataItemType) -public: - QQmlDelegateModelItem(QQmlDelegateModelItemMetaType *metaType, int modelIndex); - ~QQmlDelegateModelItem(); - - void referenceObject() { ++objectRef; } - bool releaseObject() { return --objectRef == 0 && !(groups & Compositor::PersistedFlag); } - bool isObjectReferenced() const { return objectRef != 0 || (groups & Compositor::PersistedFlag); } - - bool isReferenced() const { - return scriptRef - || incubationTask - || ((groups & Compositor::UnresolvedFlag) && (groups & Compositor::GroupMask)); - } - - void Dispose(); - - QObject *modelObject() { return this; } - - void incubateObject( - QQmlComponent *component, - QQmlEngine *engine, - QQmlContextData *context, - QQmlContextData *forContext); - void destroyObject(); - - static QQmlDelegateModelItem *dataForObject(QObject *object); - - int groupIndex(Compositor::Group group); - - int modelIndex() const { return index; } - void setModelIndex(int idx) { index = idx; emit modelIndexChanged(); } - - virtual v8::Handle get() { return engine->newQObject(this); } - - virtual void setValue(const QString &role, const QVariant &value) { Q_UNUSED(role); Q_UNUSED(value); } - virtual bool resolveIndex(const QQmlAdaptorModel &, int) { return false; } - - QQmlDelegateModelItemMetaType * const metaType; - QQmlContextData *contextData; - QObject *object; - QQmlDelegateModelAttached *attached; - QQDMIncubationTask *incubationTask; - int objectRef; - int scriptRef; - int groups; - int index; - - -Q_SIGNALS: - void modelIndexChanged(); - -protected: - void objectDestroyed(QObject *); -}; - - -class QQmlDelegateModelPrivate; -class QQDMIncubationTask : public QQmlIncubator -{ -public: - QQDMIncubationTask(QQmlDelegateModelPrivate *l, IncubationMode mode) - : QQmlIncubator(mode) - , incubating(0) - , vdm(l) {} - - virtual void statusChanged(Status); - virtual void setInitialState(QObject *); - - QQmlDelegateModelItem *incubating; - QQmlDelegateModelPrivate *vdm; - int index[QQmlListCompositor::MaximumGroupCount]; -}; - - -class QQmlDelegateModelGroupEmitter -{ -public: - virtual ~QQmlDelegateModelGroupEmitter() {} - virtual void emitModelUpdated(const QQmlChangeSet &changeSet, bool reset) = 0; - virtual void createdPackage(int, QQuickPackage *) {} - virtual void initPackage(int, QQuickPackage *) {} - virtual void destroyingPackage(QQuickPackage *) {} - - QIntrusiveListNode emitterNode; -}; - -typedef QIntrusiveList QQmlDelegateModelGroupEmitterList; - -class QQmlDelegateModelGroupPrivate : public QObjectPrivate -{ -public: - Q_DECLARE_PUBLIC(QQmlDelegateModelGroup) - - QQmlDelegateModelGroupPrivate() : group(Compositor::Cache), defaultInclude(false) {} - - static QQmlDelegateModelGroupPrivate *get(QQmlDelegateModelGroup *group) { - return static_cast(QObjectPrivate::get(group)); } - - void setModel(QQmlDelegateModel *model, Compositor::Group group); - bool isChangedConnected(); - void emitChanges(QV8Engine *engine); - void emitModelUpdated(bool reset); - - void createdPackage(int index, QQuickPackage *package); - void initPackage(int index, QQuickPackage *package); - void destroyingPackage(QQuickPackage *package); - - bool parseIndex(const v8::Local &value, int *index, Compositor::Group *group) const; - bool parseGroupArgs( - QQmlV8Function *args, Compositor::Group *group, int *index, int *count, int *groups) const; - - Compositor::Group group; - QQmlGuard model; - QQmlDelegateModelGroupEmitterList emitters; - QQmlChangeSet changeSet; - QString name; - bool defaultInclude; -}; - -class QQmlDelegateModelParts; - -class QQmlDelegateModelPrivate : public QObjectPrivate, public QQmlDelegateModelGroupEmitter -{ - Q_DECLARE_PUBLIC(QQmlDelegateModel) -public: - QQmlDelegateModelPrivate(QQmlContext *); - ~QQmlDelegateModelPrivate(); - - static QQmlDelegateModelPrivate *get(QQmlDelegateModel *m) { - return static_cast(QObjectPrivate::get(m)); - } - - void init(); - void connectModel(QQmlAdaptorModel *model); - - QObject *object(Compositor::Group group, int index, bool asynchronous); - QQmlDelegateModel::ReleaseFlags release(QObject *object); - QString stringValue(Compositor::Group group, int index, const QString &name); - void emitCreatedPackage(QQDMIncubationTask *incubationTask, QQuickPackage *package); - void emitInitPackage(QQDMIncubationTask *incubationTask, QQuickPackage *package); - void emitCreatedItem(QQDMIncubationTask *incubationTask, QObject *item) { - emit q_func()->createdItem(incubationTask->index[m_compositorGroup], item); } - void emitInitItem(QQDMIncubationTask *incubationTask, QObject *item) { - emit q_func()->initItem(incubationTask->index[m_compositorGroup], item); } - void emitDestroyingPackage(QQuickPackage *package); - void emitDestroyingItem(QObject *item) { emit q_func()->destroyingItem(item); } - void removeCacheItem(QQmlDelegateModelItem *cacheItem); - - void updateFilterGroup(); - - void addGroups(Compositor::iterator from, int count, Compositor::Group group, int groupFlags); - void removeGroups(Compositor::iterator from, int count, Compositor::Group group, int groupFlags); - void setGroups(Compositor::iterator from, int count, Compositor::Group group, int groupFlags); - - void itemsInserted( - const QVector &inserts, - QVarLengthArray, Compositor::MaximumGroupCount> *translatedInserts, - QHash > *movedItems = 0); - void itemsInserted(const QVector &inserts); - void itemsRemoved( - const QVector &removes, - QVarLengthArray, Compositor::MaximumGroupCount> *translatedRemoves, - QHash > *movedItems = 0); - void itemsRemoved(const QVector &removes); - void itemsMoved( - const QVector &removes, const QVector &inserts); - void itemsChanged(const QVector &changes); - template static v8::Local buildChangeList(const QVector &changes); - void emitChanges(); - void emitModelUpdated(const QQmlChangeSet &changeSet, bool reset); - - bool insert(Compositor::insert_iterator &before, const v8::Local &object, int groups); - - static void group_append(QQmlListProperty *property, QQmlDelegateModelGroup *group); - static int group_count(QQmlListProperty *property); - static QQmlDelegateModelGroup *group_at(QQmlListProperty *property, int index); - - void releaseIncubator(QQDMIncubationTask *incubationTask); - void incubatorStatusChanged(QQDMIncubationTask *incubationTask, QQmlIncubator::Status status); - void setInitialState(QQDMIncubationTask *incubationTask, QObject *o); - - QQmlAdaptorModel m_adaptorModel; - QQmlListCompositor m_compositor; - QQmlComponent *m_delegate; - QQmlDelegateModelItemMetaType *m_cacheMetaType; - QQmlContext *m_context; - QQmlDelegateModelParts *m_parts; - QQmlDelegateModelGroupEmitterList m_pendingParts; - - QList m_cache; - QList m_finishedIncubating; - QList m_watchedRoles; - - QString m_filterGroup; - - int m_count; - int m_groupCount; - - QQmlListCompositor::Group m_compositorGroup; - bool m_complete : 1; - bool m_delegateValidated : 1; - bool m_reset : 1; - bool m_transaction : 1; - bool m_incubatorCleanupScheduled : 1; - - union { - struct { - QQmlDelegateModelGroup *m_cacheItems; - QQmlDelegateModelGroup *m_items; - QQmlDelegateModelGroup *m_persistedItems; - }; - QQmlDelegateModelGroup *m_groups[Compositor::MaximumGroupCount]; - }; -}; - -class QQmlPartsModel : public QQmlInstanceModel, public QQmlDelegateModelGroupEmitter -{ - Q_OBJECT - Q_PROPERTY(QString filterOnGroup READ filterGroup WRITE setFilterGroup NOTIFY filterGroupChanged RESET resetFilterGroup) -public: - QQmlPartsModel(QQmlDelegateModel *model, const QString &part, QObject *parent = 0); - ~QQmlPartsModel(); - - QString filterGroup() const; - void setFilterGroup(const QString &group); - void resetFilterGroup(); - void updateFilterGroup(); - void updateFilterGroup(Compositor::Group group, const QQmlChangeSet &changeSet); - - int count() const; - bool isValid() const; - QObject *object(int index, bool asynchronous=false); - ReleaseFlags release(QObject *item); - QString stringValue(int index, const QString &role); - QList watchedRoles() const { return m_watchedRoles; } - void setWatchedRoles(QList roles); - - int indexOf(QObject *item, QObject *objectContext) const; - - void emitModelUpdated(const QQmlChangeSet &changeSet, bool reset); - - void createdPackage(int index, QQuickPackage *package); - void initPackage(int index, QQuickPackage *package); - void destroyingPackage(QQuickPackage *package); - -Q_SIGNALS: - void filterGroupChanged(); - -private: - QQmlDelegateModel *m_model; - QHash m_packaged; - QString m_part; - QString m_filterGroup; - QList m_watchedRoles; - Compositor::Group m_compositorGroup; - bool m_inheritGroup; -}; - -class QMetaPropertyBuilder; - -class QQmlDelegateModelPartsMetaObject : public QQmlOpenMetaObject -{ -public: - QQmlDelegateModelPartsMetaObject(QObject *parent) - : QQmlOpenMetaObject(parent) {} - - virtual void propertyCreated(int, QMetaPropertyBuilder &); - virtual QVariant initialValue(int); -}; - -class QQmlDelegateModelParts : public QObject -{ -Q_OBJECT -public: - QQmlDelegateModelParts(QQmlDelegateModel *parent); - - QQmlDelegateModel *model; - QList models; -}; - -class QQmlDelegateModelAttachedMetaObject : public QAbstractDynamicMetaObject, public QQmlRefCount -{ -public: - QQmlDelegateModelAttachedMetaObject( - QQmlDelegateModelItemMetaType *metaType, QMetaObject *metaObject); - ~QQmlDelegateModelAttachedMetaObject(); - - void objectDestroyed(QObject *); - int metaCall(QObject *, QMetaObject::Call, int _id, void **); - -private: - QQmlDelegateModelItemMetaType * const metaType; - QMetaObject * const metaObject; - const int memberPropertyOffset; - const int indexPropertyOffset; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/qml/items/qqmlmodelsmodule.cpp b/src/qml/items/qqmlmodelsmodule.cpp deleted file mode 100644 index 4f6b0a5580..0000000000 --- a/src/qml/items/qqmlmodelsmodule.cpp +++ /dev/null @@ -1,60 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Research In Motion. -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** 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, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qqmlmodelsmodule_p.h" -#include -#include -#include - -QT_BEGIN_NAMESPACE - -void QQmlModelsModule::defineModule() -{ - const char uri[] = "QtQml.Models"; - - qmlRegisterType(uri, 2, 1, "ListElement"); - qmlRegisterCustomType(uri, 2, 1, "ListModel", new QQmlListModelParser); - qmlRegisterType(uri, 2, 1, "DelegateModel"); - qmlRegisterType(uri, 2, 1, "DelegateModelGroup"); - qmlRegisterType(uri, 2, 1, "ObjectModel"); -} - -QT_END_NAMESPACE diff --git a/src/qml/items/qqmlmodelsmodule_p.h b/src/qml/items/qqmlmodelsmodule_p.h deleted file mode 100644 index 6e72dadf8b..0000000000 --- a/src/qml/items/qqmlmodelsmodule_p.h +++ /dev/null @@ -1,57 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Research In Motion. -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** 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, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQMLMODELSMODULE_H -#define QQMLMODELSMODULE_H - -#include - -QT_BEGIN_NAMESPACE - -class Q_QML_PRIVATE_EXPORT QQmlModelsModule -{ -public: - static void defineModule(); -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/qml/items/qqmlobjectmodel.cpp b/src/qml/items/qqmlobjectmodel.cpp deleted file mode 100644 index 7f7bf92fa3..0000000000 --- a/src/qml/items/qqmlobjectmodel.cpp +++ /dev/null @@ -1,269 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** 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, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qqmlobjectmodel_p.h" - -#include -#include -#include - -#include -#include -#include - -#include -#include - -QT_BEGIN_NAMESPACE - -QHash QQmlObjectModelAttached::attachedProperties; - - -class QQmlObjectModelPrivate : public QObjectPrivate -{ - Q_DECLARE_PUBLIC(QQmlObjectModel) -public: - class Item { - public: - Item(QObject *i) : item(i), ref(0) {} - - void addRef() { ++ref; } - bool deref() { return --ref == 0; } - - QObject *item; - int ref; - }; - - QQmlObjectModelPrivate() : QObjectPrivate() {} - - static void children_append(QQmlListProperty *prop, QObject *item) { - static_cast(prop->data)->children.append(Item(item)); - static_cast(prop->data)->itemAppended(); - static_cast(prop->data)->emitChildrenChanged(); - } - - static int children_count(QQmlListProperty *prop) { - return static_cast(prop->data)->children.count(); - } - - static QObject *children_at(QQmlListProperty *prop, int index) { - return static_cast(prop->data)->children.at(index).item; - } - - static void children_clear(QQmlListProperty *prop) { - static_cast(prop->data)->itemCleared(static_cast(prop->data)->children); - static_cast(prop->data)->children.clear(); - static_cast(prop->data)->emitChildrenChanged(); - } - - void itemAppended() { - Q_Q(QQmlObjectModel); - QQmlObjectModelAttached *attached = QQmlObjectModelAttached::properties(children.last().item); - attached->setIndex(children.count()-1); - QQmlChangeSet changeSet; - changeSet.insert(children.count() - 1, 1); - emit q->modelUpdated(changeSet, false); - emit q->countChanged(); - } - - void itemCleared(const QList &children) { - Q_Q(QQmlObjectModel); - foreach (const Item &child, children) - emit q->destroyingItem(child.item); - emit q->countChanged(); - } - - void emitChildrenChanged() { - Q_Q(QQmlObjectModel); - emit q->childrenChanged(); - } - - int indexOf(QObject *item) const { - for (int i = 0; i < children.count(); ++i) - if (children.at(i).item == item) - return i; - return -1; - } - - - QList children; -}; - - -/*! - \qmltype ObjectModel - \instantiates QQmlObjectModel - \inqmlmodule QtQml.Models 2 - \ingroup qtquick-models - \brief Defines a set of items to be used as a model - - A ObjectModel contains the visual items to be used in a view. - When a ObjectModel is used in a view, the view does not require - a delegate since the ObjectModel already contains the visual - delegate (items). - - An item can determine its index within the - model via the \l{ObjectModel::index}{index} attached property. - - The example below places three colored rectangles in a ListView. - \code - import QtQuick 2.0 - - Rectangle { - ObjectModel { - id: itemModel - Rectangle { height: 30; width: 80; color: "red" } - Rectangle { height: 30; width: 80; color: "green" } - Rectangle { height: 30; width: 80; color: "blue" } - } - - ListView { - anchors.fill: parent - model: itemModel - } - } - \endcode - - \image visualitemmodel.png - - \sa {quick/views/objectmodel}{ObjectModel example} -*/ -/*! - \qmltype VisualItemModel - \instantiates QQmlObjectModel - \inqmlmodule QtQuick 2 - \brief Defines a set of objects to be used as a model - - The VisualItemModel type encapsulates contains the objects to be used - as a model. - - This element is now primarily available as ObjectModel in the QtQml.Models module. - VisualItemModel continues to be provided, with the same implementation, in QtQuick for - compatibility reasons. - - For full details about the type, see the \l ObjectModel documentation. - - \sa {QtQml.Models2::ObjectModel} -*/ - -QQmlObjectModel::QQmlObjectModel(QObject *parent) - : QQmlInstanceModel(*(new QQmlObjectModelPrivate), parent) -{ -} - -/*! - \qmlattachedproperty int QtQml.Models2::ObjectModel::index - This attached property holds the index of this delegate's item within the model. - - It is attached to each instance of the delegate. -*/ - -QQmlListProperty QQmlObjectModel::children() -{ - Q_D(QQmlObjectModel); - return QQmlListProperty(this, - d, - d->children_append, - d->children_count, - d->children_at, - d->children_clear); -} - -/*! - \qmlproperty int QtQml.Models2::ObjectModel::count - - The number of items in the model. This property is readonly. -*/ -int QQmlObjectModel::count() const -{ - Q_D(const QQmlObjectModel); - return d->children.count(); -} - -bool QQmlObjectModel::isValid() const -{ - return true; -} - -QObject *QQmlObjectModel::object(int index, bool) -{ - Q_D(QQmlObjectModel); - QQmlObjectModelPrivate::Item &item = d->children[index]; - item.addRef(); - if (item.ref == 1) { - emit initItem(index, item.item); - emit createdItem(index, item.item); - } - return item.item; -} - -QQmlInstanceModel::ReleaseFlags QQmlObjectModel::release(QObject *item) -{ - Q_D(QQmlObjectModel); - int idx = d->indexOf(item); - if (idx >= 0) { - if (!d->children[idx].deref()) - return QQmlInstanceModel::Referenced; - } - return 0; -} - -QString QQmlObjectModel::stringValue(int index, const QString &name) -{ - Q_D(QQmlObjectModel); - if (index < 0 || index >= d->children.count()) - return QString(); - return QQmlEngine::contextForObject(d->children.at(index).item)->contextProperty(name).toString(); -} - -int QQmlObjectModel::indexOf(QObject *item, QObject *) const -{ - Q_D(const QQmlObjectModel); - return d->indexOf(item); -} - -QQmlObjectModelAttached *QQmlObjectModel::qmlAttachedProperties(QObject *obj) -{ - return QQmlObjectModelAttached::properties(obj); -} - -QT_END_NAMESPACE - diff --git a/src/qml/items/qqmlobjectmodel_p.h b/src/qml/items/qqmlobjectmodel_p.h deleted file mode 100644 index 59a4a551a7..0000000000 --- a/src/qml/items/qqmlobjectmodel_p.h +++ /dev/null @@ -1,170 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** 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, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQMLINSTANCEMODEL_P_H -#define QQMLINSTANCEMODEL_P_H - -#include -#include -#include - -QT_BEGIN_NAMESPACE - -class QObject; -class QQmlChangeSet; - -class Q_QML_PRIVATE_EXPORT QQmlInstanceModel : public QObject -{ - Q_OBJECT - - Q_PROPERTY(int count READ count NOTIFY countChanged) - -public: - virtual ~QQmlInstanceModel() {} - - enum ReleaseFlag { Referenced = 0x01, Destroyed = 0x02 }; - Q_DECLARE_FLAGS(ReleaseFlags, ReleaseFlag) - - virtual int count() const = 0; - virtual bool isValid() const = 0; - virtual QObject *object(int index, bool asynchronous=false) = 0; - virtual ReleaseFlags release(QObject *object) = 0; - virtual void cancel(int) {} - virtual QString stringValue(int, const QString &) = 0; - virtual void setWatchedRoles(QList roles) = 0; - - virtual int indexOf(QObject *object, QObject *objectContext) const = 0; - -Q_SIGNALS: - void countChanged(); - void modelUpdated(const QQmlChangeSet &changeSet, bool reset); - void createdItem(int index, QObject *object); - void initItem(int index, QObject *object); - void destroyingItem(QObject *object); - -protected: - QQmlInstanceModel(QObjectPrivate &dd, QObject *parent = 0) - : QObject(dd, parent) {} - -private: - Q_DISABLE_COPY(QQmlInstanceModel) -}; - -class QQmlObjectModelAttached; -class QQmlObjectModelPrivate; -class Q_QML_PRIVATE_EXPORT QQmlObjectModel : public QQmlInstanceModel -{ - Q_OBJECT - Q_DECLARE_PRIVATE(QQmlObjectModel) - - Q_PROPERTY(QQmlListProperty children READ children NOTIFY childrenChanged DESIGNABLE false) - Q_CLASSINFO("DefaultProperty", "children") - -public: - QQmlObjectModel(QObject *parent=0); - virtual ~QQmlObjectModel() {} - - virtual int count() const; - virtual bool isValid() const; - virtual QObject *object(int index, bool asynchronous=false); - virtual ReleaseFlags release(QObject *object); - virtual QString stringValue(int index, const QString &role); - virtual void setWatchedRoles(QList) {} - - virtual int indexOf(QObject *object, QObject *objectContext) const; - - QQmlListProperty children(); - - static QQmlObjectModelAttached *qmlAttachedProperties(QObject *obj); - -Q_SIGNALS: - void childrenChanged(); - -private: - Q_DISABLE_COPY(QQmlObjectModel) -}; - -class QQmlObjectModelAttached : public QObject -{ - Q_OBJECT - -public: - QQmlObjectModelAttached(QObject *parent) - : QObject(parent), m_index(0) {} - ~QQmlObjectModelAttached() { - attachedProperties.remove(parent()); - } - - Q_PROPERTY(int index READ index NOTIFY indexChanged) - int index() const { return m_index; } - void setIndex(int idx) { - if (m_index != idx) { - m_index = idx; - emit indexChanged(); - } - } - - static QQmlObjectModelAttached *properties(QObject *obj) { - QQmlObjectModelAttached *rv = attachedProperties.value(obj); - if (!rv) { - rv = new QQmlObjectModelAttached(obj); - attachedProperties.insert(obj, rv); - } - return rv; - } - -Q_SIGNALS: - void indexChanged(); - -public: - int m_index; - - static QHash attachedProperties; -}; - - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QQmlInstanceModel) -QML_DECLARE_TYPE(QQmlObjectModel) -QML_DECLARE_TYPEINFO(QQmlObjectModel, QML_HAS_ATTACHED_PROPERTIES) - -#endif // QQMLINSTANCEMODEL_P_H diff --git a/src/qml/items/qquickpackage.cpp b/src/qml/items/qquickpackage.cpp deleted file mode 100644 index e885524b27..0000000000 --- a/src/qml/items/qquickpackage.cpp +++ /dev/null @@ -1,198 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** 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, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qquickpackage_p.h" - -#include -#include - -QT_BEGIN_NAMESPACE - -/*! - \qmltype Package - \instantiates QQuickPackage - \inqmlmodule QtQuick 2 - \ingroup qtquick-views - \brief Specifies a collection of named items - - The Package class is used in conjunction with - VisualDataModel to enable delegates with a shared context - to be provided to multiple views. - - Any item within a Package may be assigned a name via the - \l{Package::name}{Package.name} attached property. - - The example below creates a Package containing two named items; - \e list and \e grid. The third item in the package (the \l Rectangle) is parented to whichever - delegate it should appear in. This allows an item to move - between views. - - \snippet quick/views/package/Delegate.qml 0 - - These named items are used as the delegates by the two views who - reference the special \l{VisualDataModel::parts} property to select - a model which provides the chosen delegate. - - \snippet quick/views/package/view.qml 0 - - \sa {quick/views/package}{Package example}, {quick/demos/photoviewer}{Photo Viewer example}, QtQml -*/ - -/*! - \qmlattachedproperty string QtQuick2::Package::name - This attached property holds the name of an item within a Package. -*/ - - -class QQuickPackagePrivate : public QObjectPrivate -{ -public: - QQuickPackagePrivate() {} - - struct DataGuard : public QQmlGuard - { - DataGuard(QObject *obj, QList *l) : list(l) { (QQmlGuard&)*this = obj; } - QList *list; - void objectDestroyed(QObject *) { - // we assume priv will always be destroyed after objectDestroyed calls - list->removeOne(*this); - } - }; - - QList dataList; - static void data_append(QQmlListProperty *prop, QObject *o) { - QList *list = static_cast *>(prop->data); - list->append(DataGuard(o, list)); - } - static void data_clear(QQmlListProperty *prop) { - QList *list = static_cast *>(prop->data); - list->clear(); - } - static QObject *data_at(QQmlListProperty *prop, int index) { - QList *list = static_cast *>(prop->data); - return list->at(index); - } - static int data_count(QQmlListProperty *prop) { - QList *list = static_cast *>(prop->data); - return list->count(); - } -}; - -QHash QQuickPackageAttached::attached; - -QQuickPackageAttached::QQuickPackageAttached(QObject *parent) -: QObject(parent) -{ - attached.insert(parent, this); -} - -QQuickPackageAttached::~QQuickPackageAttached() -{ - attached.remove(parent()); -} - -QString QQuickPackageAttached::name() const -{ - return _name; -} - -void QQuickPackageAttached::setName(const QString &n) -{ - _name = n; -} - -QQuickPackage::QQuickPackage(QObject *parent) - : QObject(*(new QQuickPackagePrivate), parent) -{ -} - -QQuickPackage::~QQuickPackage() -{ -} - -QQmlListProperty QQuickPackage::data() -{ - Q_D(QQuickPackage); - return QQmlListProperty(this, &d->dataList, QQuickPackagePrivate::data_append, - QQuickPackagePrivate::data_count, - QQuickPackagePrivate::data_at, - QQuickPackagePrivate::data_clear); -} - -bool QQuickPackage::hasPart(const QString &name) -{ - Q_D(QQuickPackage); - for (int ii = 0; ii < d->dataList.count(); ++ii) { - QObject *obj = d->dataList.at(ii); - QQuickPackageAttached *a = QQuickPackageAttached::attached.value(obj); - if (a && a->name() == name) - return true; - } - return false; -} - -QObject *QQuickPackage::part(const QString &name) -{ - Q_D(QQuickPackage); - if (name.isEmpty() && !d->dataList.isEmpty()) - return d->dataList.at(0); - - for (int ii = 0; ii < d->dataList.count(); ++ii) { - QObject *obj = d->dataList.at(ii); - QQuickPackageAttached *a = QQuickPackageAttached::attached.value(obj); - if (a && a->name() == name) - return obj; - } - - if (name == QLatin1String("default") && !d->dataList.isEmpty()) - return d->dataList.at(0); - - return 0; -} - -QQuickPackageAttached *QQuickPackage::qmlAttachedProperties(QObject *o) -{ - return new QQuickPackageAttached(o); -} - - - -QT_END_NAMESPACE diff --git a/src/qml/items/qquickpackage_p.h b/src/qml/items/qquickpackage_p.h deleted file mode 100644 index 9427c886a8..0000000000 --- a/src/qml/items/qquickpackage_p.h +++ /dev/null @@ -1,92 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** 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, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQUICKPACKAGE_H -#define QQUICKPACKAGE_H - -#include - -QT_BEGIN_NAMESPACE - -class QQuickPackagePrivate; -class QQuickPackageAttached; -class Q_AUTOTEST_EXPORT QQuickPackage : public QObject -{ - Q_OBJECT - Q_DECLARE_PRIVATE(QQuickPackage) - - Q_CLASSINFO("DefaultProperty", "data") - Q_PROPERTY(QQmlListProperty data READ data) - -public: - QQuickPackage(QObject *parent=0); - virtual ~QQuickPackage(); - - QQmlListProperty data(); - - QObject *part(const QString & = QString()); - bool hasPart(const QString &); - - static QQuickPackageAttached *qmlAttachedProperties(QObject *); -}; - -class QQuickPackageAttached : public QObject -{ -Q_OBJECT -Q_PROPERTY(QString name READ name WRITE setName) -public: - QQuickPackageAttached(QObject *parent); - virtual ~QQuickPackageAttached(); - - QString name() const; - void setName(const QString &n); - - static QHash attached; -private: - QString _name; -}; - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QQuickPackage) -QML_DECLARE_TYPEINFO(QQuickPackage, QML_HAS_ATTACHED_PROPERTIES) - -#endif // QQUICKPACKAGE_H diff --git a/src/qml/qml.pro b/src/qml/qml.pro index 08aa369ac3..8dbe0c65d1 100644 --- a/src/qml/qml.pro +++ b/src/qml/qml.pro @@ -25,4 +25,4 @@ include(util/util.pri) include(qml/qml.pri) include(debugger/debugger.pri) include(animations/animations.pri) -include(items/items.pri) +include(types/types.pri) diff --git a/src/qml/qml/qml.pri b/src/qml/qml/qml.pri index d3abd84fc8..a660abe7d7 100644 --- a/src/qml/qml/qml.pri +++ b/src/qml/qml/qml.pri @@ -1,12 +1,9 @@ SOURCES += \ $$PWD/qqmlinstruction.cpp \ - $$PWD/qqmllistmodel.cpp \ - $$PWD/qqmllistmodelworkeragent.cpp \ $$PWD/qqmlopenmetaobject.cpp \ $$PWD/qqmlvmemetaobject.cpp \ $$PWD/qqmlengine.cpp \ $$PWD/qqmlexpression.cpp \ - $$PWD/qqmlbinding.cpp \ $$PWD/qqmlproperty.cpp \ $$PWD/qqmlcomponent.cpp \ $$PWD/qqmlincubator.cpp \ @@ -38,7 +35,6 @@ SOURCES += \ $$PWD/qqmltypenotavailable.cpp \ $$PWD/qqmltypenamecache.cpp \ $$PWD/qqmlscriptstring.cpp \ - $$PWD/qquickworkerscript.cpp \ $$PWD/qqmlnetworkaccessmanagerfactory.cpp \ $$PWD/qqmldirparser.cpp \ $$PWD/qqmlextensionplugin.cpp \ @@ -53,21 +49,15 @@ SOURCES += \ $$PWD/qqmlfile.cpp \ $$PWD/qqmlbundle.cpp \ $$PWD/qqmlmemoryprofiler.cpp \ - $$PWD/qqmlconnections.cpp \ - $$PWD/qqmltimer.cpp \ - $$PWD/qqmlbind.cpp \ - $$PWD/qqmlplatform.cpp + $$PWD/qqmlplatform.cpp \ + $$PWD/qqmlbinding.cpp HEADERS += \ $$PWD/qqmlglobal_p.h \ $$PWD/qqmlinstruction_p.h \ - $$PWD/qqmllistmodel_p.h\ - $$PWD/qqmllistmodel_p_p.h\ - $$PWD/qqmllistmodelworkeragent_p.h \ $$PWD/qqmlopenmetaobject_p.h \ $$PWD/qqmlvmemetaobject_p.h \ $$PWD/qqml.h \ - $$PWD/qqmlbinding_p.h \ $$PWD/qqmlproperty.h \ $$PWD/qqmlcomponent.h \ $$PWD/qqmlcomponent_p.h \ @@ -112,7 +102,6 @@ HEADERS += \ $$PWD/qqmltypenotavailable_p.h \ $$PWD/qqmltypenamecache_p.h \ $$PWD/qqmlscriptstring.h \ - $$PWD/qquickworkerscript_p.h \ $$PWD/qqmlguard_p.h \ $$PWD/qqmlnetworkaccessmanagerfactory.h \ $$PWD/qqmldirparser_p.h \ @@ -130,10 +119,8 @@ HEADERS += \ $$PWD/qqmlfile.h \ $$PWD/qqmlbundle_p.h \ $$PWD/qqmlmemoryprofiler_p.h \ - $$PWD/qqmlconnections_p.h \ - $$PWD/qqmltimer_p.h \ - $$PWD/qqmlbind_p.h \ $$PWD/qqmlplatform_p.h \ + $$PWD/qqmlbinding_p.h \ $$PWD/qqmlextensionplugin_p.h diff --git a/src/qml/qml/qqmlbind.cpp b/src/qml/qml/qqmlbind.cpp deleted file mode 100644 index fcb3079891..0000000000 --- a/src/qml/qml/qqmlbind.cpp +++ /dev/null @@ -1,310 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** 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, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qqmlbind_p.h" - -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include - -#include - -QT_BEGIN_NAMESPACE - -class QQmlBindPrivate : public QObjectPrivate -{ -public: - QQmlBindPrivate() : componentComplete(true), obj(0), prevBind(0) {} - ~QQmlBindPrivate() { if (prevBind) prevBind->destroy(); } - - QQmlNullableValue when; - bool componentComplete; - QQmlGuard obj; - QString propName; - QQmlNullableValue value; - QQmlProperty prop; - QQmlAbstractBinding *prevBind; -}; - - -/*! - \qmltype Binding - \instantiates QQmlBind - \inqmlmodule QtQml 2 - \ingroup qtquick-interceptors - \brief Enables the arbitrary creation of property bindings - - \section1 Binding to an inaccessible property - - Sometimes it is necessary to bind to a property of an object that wasn't - directly instantiated by QML - generally a property of a class exported - to QML by C++. In these cases, regular property binding doesn't work. Binding - allows you to bind any value to any property. - - For example, imagine a C++ application that maps an "app.enteredText" - property into QML. You could use Binding to update the enteredText property - like this. - \code - TextEdit { id: myTextField; text: "Please type here..." } - Binding { target: app; property: "enteredText"; value: myTextField.text } - \endcode - Whenever the text in the TextEdit is updated, the C++ property will be - updated also. - - \section1 "Single-branch" conditional binding - - In some circumstances you may want to control the value of a property - only when a certain condition is true (and relinquish control in all - other circumstances). This often isn't possible to accomplish with a direct - binding, as you need to supply values for all possible branches. - - \code - // produces warning: "Unable to assign [undefined] to double value" - value: if (mouse.pressed) mouse.mouseX - \endcode - - The above example will produce a warning whenever we release the mouse, as the value - of the binding is undefined when the mouse isn't pressed. We can use the Binding - type to rewrite the above code and avoid the warning. - - \qml - Binding on value { - when: mouse.pressed - value: mouse.mouseX - } - \endqml - - The Binding type will also restore any previously set direct bindings on - the property. In that sense, it functions much like a simplified State. - - \qml - // this is equivalent to the above Binding - State { - name: "pressed" - when: mouse.pressed - PropertyChanges { - target: obj - value: mouse.mouseX - } - } - \endqml - - If the binding target or binding property is changed, the bound value is - immediately pushed onto the new target. - - \sa QtQml -*/ -QQmlBind::QQmlBind(QObject *parent) - : QObject(*(new QQmlBindPrivate), parent) -{ -} - -QQmlBind::~QQmlBind() -{ -} - -/*! - \qmlproperty bool QtQml2::Binding::when - - This property holds when the binding is active. - This should be set to an expression that evaluates to true when you want the binding to be active. - - \code - Binding { - target: contactName; property: 'text' - value: name; when: list.ListView.isCurrentItem - } - \endcode - - When the binding becomes inactive again, any direct bindings that were previously - set on the property will be restored. -*/ -bool QQmlBind::when() const -{ - Q_D(const QQmlBind); - return d->when; -} - -void QQmlBind::setWhen(bool v) -{ - Q_D(QQmlBind); - if (!d->when.isNull && d->when == v) - return; - - d->when = v; - eval(); -} - -/*! - \qmlproperty Object QtQml2::Binding::target - - The object to be updated. -*/ -QObject *QQmlBind::object() -{ - Q_D(const QQmlBind); - return d->obj; -} - -void QQmlBind::setObject(QObject *obj) -{ - Q_D(QQmlBind); - if (d->obj && d->when.isValid() && d->when) { - /* if we switch the object at runtime, we need to restore the - previous binding on the old object before continuing */ - d->when = false; - eval(); - d->when = true; - } - d->obj = obj; - if (d->componentComplete) - d->prop = QQmlProperty(d->obj, d->propName); - eval(); -} - -/*! - \qmlproperty string QtQml2::Binding::property - - The property to be updated. -*/ -QString QQmlBind::property() const -{ - Q_D(const QQmlBind); - return d->propName; -} - -void QQmlBind::setProperty(const QString &p) -{ - Q_D(QQmlBind); - if (!d->propName.isEmpty() && d->when.isValid() && d->when) { - /* if we switch the property name at runtime, we need to restore the - previous binding on the old object before continuing */ - d->when = false; - eval(); - d->when = true; - } - d->propName = p; - if (d->componentComplete) - d->prop = QQmlProperty(d->obj, d->propName); - eval(); -} - -/*! - \qmlproperty any QtQml2::Binding::value - - The value to be set on the target object and property. This can be a - constant (which isn't very useful), or a bound expression. -*/ -QVariant QQmlBind::value() const -{ - Q_D(const QQmlBind); - return d->value.value; -} - -void QQmlBind::setValue(const QVariant &v) -{ - Q_D(QQmlBind); - d->value = v; - eval(); -} - -void QQmlBind::setTarget(const QQmlProperty &p) -{ - Q_D(QQmlBind); - d->prop = p; -} - -void QQmlBind::classBegin() -{ - Q_D(QQmlBind); - d->componentComplete = false; -} - -void QQmlBind::componentComplete() -{ - Q_D(QQmlBind); - d->componentComplete = true; - if (!d->prop.isValid()) - d->prop = QQmlProperty(d->obj, d->propName); - eval(); -} - -void QQmlBind::eval() -{ - Q_D(QQmlBind); - if (!d->prop.isValid() || d->value.isNull || !d->componentComplete) - return; - - if (d->when.isValid()) { - if (!d->when) { - //restore any previous binding - if (d->prevBind) { - QQmlAbstractBinding *tmp = d->prevBind; - d->prevBind = 0; - tmp = QQmlPropertyPrivate::setBinding(d->prop, tmp); - if (tmp) //should this ever be true? - tmp->destroy(); - } - return; - } - - //save any set binding for restoration - QQmlAbstractBinding *tmp; - tmp = QQmlPropertyPrivate::setBinding(d->prop, 0); - if (tmp && d->prevBind) - tmp->destroy(); - else if (!d->prevBind) - d->prevBind = tmp; - } - - d->prop.write(d->value.value); -} - -QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlbind_p.h b/src/qml/qml/qqmlbind_p.h deleted file mode 100644 index 1e29c257f0..0000000000 --- a/src/qml/qml/qqmlbind_p.h +++ /dev/null @@ -1,92 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** 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, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQMLBIND_H -#define QQMLBIND_H - -#include - -#include - -QT_BEGIN_NAMESPACE - -class QQmlBindPrivate; -class Q_AUTOTEST_EXPORT QQmlBind : public QObject, public QQmlPropertyValueSource, public QQmlParserStatus -{ - Q_OBJECT - Q_DECLARE_PRIVATE(QQmlBind) - Q_INTERFACES(QQmlParserStatus) - Q_INTERFACES(QQmlPropertyValueSource) - Q_PROPERTY(QObject *target READ object WRITE setObject) - Q_PROPERTY(QString property READ property WRITE setProperty) - Q_PROPERTY(QVariant value READ value WRITE setValue) - Q_PROPERTY(bool when READ when WRITE setWhen) - -public: - QQmlBind(QObject *parent=0); - ~QQmlBind(); - - bool when() const; - void setWhen(bool); - - QObject *object(); - void setObject(QObject *); - - QString property() const; - void setProperty(const QString &); - - QVariant value() const; - void setValue(const QVariant &); - -protected: - virtual void setTarget(const QQmlProperty &); - virtual void classBegin(); - virtual void componentComplete(); - -private: - void eval(); -}; - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QQmlBind) - -#endif diff --git a/src/qml/qml/qqmlconnections.cpp b/src/qml/qml/qqmlconnections.cpp deleted file mode 100644 index 286933e557..0000000000 --- a/src/qml/qml/qqmlconnections.cpp +++ /dev/null @@ -1,321 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** 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, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qqmlconnections_p.h" - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -QT_BEGIN_NAMESPACE - -class QQmlConnectionsPrivate : public QObjectPrivate -{ -public: - QQmlConnectionsPrivate() : target(0), targetSet(false), ignoreUnknownSignals(false), componentcomplete(true) {} - - QList boundsignals; - QObject *target; - - bool targetSet; - bool ignoreUnknownSignals; - bool componentcomplete; - - QByteArray data; -}; - -/*! - \qmltype Connections - \instantiates QQmlConnections - \inqmlmodule QtQml 2 - \ingroup qtquick-interceptors - \brief Describes generalized connections to signals - - A Connections object creates a connection to a QML signal. - - When connecting to signals in QML, the usual way is to create an - "on" handler that reacts when a signal is received, like this: - - \qml - MouseArea { - onClicked: { foo(parameters) } - } - \endqml - - However, it is not possible to connect to a signal in this way in some - cases, such as when: - - \list - \li Multiple connections to the same signal are required - \li Creating connections outside the scope of the signal sender - \li Connecting to targets not defined in QML - \endlist - - When any of these are needed, the Connections type can be used instead. - - For example, the above code can be changed to use a Connections object, - like this: - - \qml - MouseArea { - Connections { - onClicked: foo(parameters) - } - } - \endqml - - More generally, the Connections object can be a child of some object other than - the sender of the signal: - - \qml - MouseArea { - id: area - } - // ... - \endqml - \qml - Connections { - target: area - onClicked: foo(parameters) - } - \endqml - - \sa QtQml -*/ -QQmlConnections::QQmlConnections(QObject *parent) : - QObject(*(new QQmlConnectionsPrivate), parent) -{ -} - -QQmlConnections::~QQmlConnections() -{ -} - -/*! - \qmlproperty Object QtQml2::Connections::target - This property holds the object that sends the signal. - - If this property is not set, the \c target defaults to the parent of the Connection. - - If set to null, no connection is made and any signal handlers are ignored - until the target is not null. -*/ -QObject *QQmlConnections::target() const -{ - Q_D(const QQmlConnections); - return d->targetSet ? d->target : parent(); -} - -class QQmlBoundSignalDeleter : public QObject -{ -public: - QQmlBoundSignalDeleter(QQmlBoundSignal *signal) : m_signal(signal) { m_signal->removeFromObject(); } - ~QQmlBoundSignalDeleter() { delete m_signal; } - -private: - QQmlBoundSignal *m_signal; -}; - -void QQmlConnections::setTarget(QObject *obj) -{ - Q_D(QQmlConnections); - d->targetSet = true; // even if setting to 0, it is *set* - if (d->target == obj) - return; - foreach (QQmlBoundSignal *s, d->boundsignals) { - // It is possible that target is being changed due to one of our signal - // handlers -> use deleteLater(). - if (s->isEvaluating()) - (new QQmlBoundSignalDeleter(s))->deleteLater(); - else - delete s; - } - d->boundsignals.clear(); - d->target = obj; - connectSignals(); - emit targetChanged(); -} - -/*! - \qmlproperty bool QtQml2::Connections::ignoreUnknownSignals - - Normally, a connection to a non-existent signal produces runtime errors. - - If this property is set to \c true, such errors are ignored. - This is useful if you intend to connect to different types of objects, handling - a different set of signals for each object. -*/ -bool QQmlConnections::ignoreUnknownSignals() const -{ - Q_D(const QQmlConnections); - return d->ignoreUnknownSignals; -} - -void QQmlConnections::setIgnoreUnknownSignals(bool ignore) -{ - Q_D(QQmlConnections); - d->ignoreUnknownSignals = ignore; -} - - - -QByteArray -QQmlConnectionsParser::compile(const QList &props) -{ - QByteArray rv; - QDataStream ds(&rv, QIODevice::WriteOnly); - - for(int ii = 0; ii < props.count(); ++ii) - { - QString propName = props.at(ii).name(); - int propLine = props.at(ii).location().line; - int propColumn = props.at(ii).location().column; - - if (!propName.startsWith(QLatin1String("on")) || !propName.at(2).isUpper()) { - error(props.at(ii), QQmlConnections::tr("Cannot assign to non-existent property \"%1\"").arg(propName)); - return QByteArray(); - } - - QList values = props.at(ii).assignedValues(); - - for (int i = 0; i < values.count(); ++i) { - const QVariant &value = values.at(i); - - if (value.userType() == qMetaTypeId()) { - error(props.at(ii), QQmlConnections::tr("Connections: nested objects not allowed")); - return QByteArray(); - } else if (value.userType() == qMetaTypeId()) { - error(props.at(ii), QQmlConnections::tr("Connections: syntax error")); - return QByteArray(); - } else { - QQmlScript::Variant v = qvariant_cast(value); - if (v.isScript()) { - ds << propName; - ds << rewriteSignalHandler(v, propName).toUtf8(); - ds << propLine; - ds << propColumn; - } else { - error(props.at(ii), QQmlConnections::tr("Connections: script expected")); - return QByteArray(); - } - } - } - } - - return rv; -} - -void QQmlConnectionsParser::setCustomData(QObject *object, - const QByteArray &data) -{ - QQmlConnectionsPrivate *p = - static_cast(QObjectPrivate::get(object)); - p->data = data; -} - - -void QQmlConnections::connectSignals() -{ - Q_D(QQmlConnections); - if (!d->componentcomplete || (d->targetSet && !target())) - return; - - QDataStream ds(d->data); - while (!ds.atEnd()) { - QString propName; - ds >> propName; - QByteArray script; - ds >> script; - int line; - ds >> line; - int column; - ds >> column; - - QQmlProperty prop(target(), propName); - if (prop.isValid() && (prop.type() & QQmlProperty::SignalProperty)) { - int signalIndex = QQmlPropertyPrivate::get(prop)->signalIndex(); - QQmlBoundSignal *signal = - new QQmlBoundSignal(target(), signalIndex, this, qmlEngine(this)); - - QString location; - QQmlContextData *ctxtdata = 0; - QQmlData *ddata = QQmlData::get(this); - if (ddata) { - ctxtdata = ddata->outerContext; - if (ctxtdata && !ctxtdata->url.isEmpty()) - location = ddata->outerContext->urlString; - } - - QQmlBoundSignalExpression *expression = ctxtdata ? - new QQmlBoundSignalExpression(target(), signalIndex, - ctxtdata, this, script, - true, location, line, column) : 0; - signal->takeExpression(expression); - d->boundsignals += signal; - } else { - if (!d->ignoreUnknownSignals) - qmlInfo(this) << tr("Cannot assign to non-existent property \"%1\"").arg(propName); - } - } -} - -void QQmlConnections::classBegin() -{ - Q_D(QQmlConnections); - d->componentcomplete=false; -} - -void QQmlConnections::componentComplete() -{ - Q_D(QQmlConnections); - d->componentcomplete=true; - connectSignals(); -} - -QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlconnections_p.h b/src/qml/qml/qqmlconnections_p.h deleted file mode 100644 index 9bc668e5f4..0000000000 --- a/src/qml/qml/qqmlconnections_p.h +++ /dev/null @@ -1,96 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** 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, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQMLCONNECTIONS_H -#define QQMLCONNECTIONS_H - -#include -#include - -#include -#include - -QT_BEGIN_NAMESPACE - -class QQmlBoundSignal; -class QQmlContext; -class QQmlConnectionsPrivate; -class Q_AUTOTEST_EXPORT QQmlConnections : public QObject, public QQmlParserStatus -{ - Q_OBJECT - Q_DECLARE_PRIVATE(QQmlConnections) - - Q_INTERFACES(QQmlParserStatus) - Q_PROPERTY(QObject *target READ target WRITE setTarget NOTIFY targetChanged) - Q_PROPERTY(bool ignoreUnknownSignals READ ignoreUnknownSignals WRITE setIgnoreUnknownSignals) - -public: - QQmlConnections(QObject *parent=0); - ~QQmlConnections(); - - QObject *target() const; - void setTarget(QObject *); - - bool ignoreUnknownSignals() const; - void setIgnoreUnknownSignals(bool ignore); - -Q_SIGNALS: - void targetChanged(); - -private: - void connectSignals(); - void classBegin(); - void componentComplete(); -}; - -class QQmlConnectionsParser : public QQmlCustomParser -{ -public: - virtual QByteArray compile(const QList &); - virtual void setCustomData(QObject *, const QByteArray &); -}; - - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QQmlConnections) - -#endif diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index 5bb9ac7df6..23d1cf785e 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -55,7 +55,6 @@ #include "qqmlxmlhttprequest_p.h" #include "qqmlscriptstring.h" #include "qqmlglobal_p.h" -#include "qquickworkerscript_p.h" #include "qqmlcomponent_p.h" #include "qqmlnetworkaccessmanagerfactory.h" #include "qqmldirparser_p.h" @@ -96,6 +95,7 @@ #include #include #include +#include #ifdef Q_OS_WIN // for %APPDATA% #include diff --git a/src/qml/qml/qqmllistmodel.cpp b/src/qml/qml/qqmllistmodel.cpp deleted file mode 100644 index 254eb58962..0000000000 --- a/src/qml/qml/qqmllistmodel.cpp +++ /dev/null @@ -1,2588 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** 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, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qqmllistmodel_p_p.h" -#include "qqmllistmodelworkeragent_p.h" -#include "qqmlopenmetaobject_p.h" -#include -#include - - -#include -#include -#include -#include -#include - -#include -#include -#include - -QT_BEGIN_NAMESPACE - -// Set to 1024 as a debugging aid - easier to distinguish uids from indices of elements/models. -enum { MIN_LISTMODEL_UID = 1024 }; - -static QAtomicInt uidCounter(MIN_LISTMODEL_UID); - -template -static bool isMemoryUsed(const char *mem) -{ - for (size_t i=0 ; i < sizeof(T) ; ++i) { - if (mem[i] != 0) - return true; - } - - return false; -} - -static QString roleTypeName(ListLayout::Role::DataType t) -{ - QString result; - const char *roleTypeNames[] = { "String", "Number", "Bool", "List", "QObject", "VariantMap", "DateTime" }; - - if (t > ListLayout::Role::Invalid && t < ListLayout::Role::MaxDataType) - result = QString::fromLatin1(roleTypeNames[t]); - - return result; -} - -const ListLayout::Role &ListLayout::getRoleOrCreate(const QString &key, Role::DataType type) -{ - QStringHash::Node *node = roleHash.findNode(key); - if (node) { - const Role &r = *node->value; - if (type != r.type) - qmlInfo(0) << QString::fromLatin1("Can't assign to existing role '%1' of different type [%2 -> %3]").arg(r.name).arg(roleTypeName(type)).arg(roleTypeName(r.type)); - return r; - } - - return createRole(key, type); -} - -const ListLayout::Role &ListLayout::getRoleOrCreate(v8::Handle key, Role::DataType type) -{ - QHashedV8String hashedKey(key); - QStringHash::Node *node = roleHash.findNode(hashedKey); - if (node) { - const Role &r = *node->value; - if (type != r.type) - qmlInfo(0) << QString::fromLatin1("Can't assign to existing role '%1' of different type [%2 -> %3]").arg(r.name).arg(roleTypeName(type)).arg(roleTypeName(r.type)); - return r; - } - - QString qkey; - qkey.resize(key->Length()); - key->Write(reinterpret_cast(qkey.data())); - - return createRole(qkey, type); -} - -const ListLayout::Role &ListLayout::createRole(const QString &key, ListLayout::Role::DataType type) -{ - const int dataSizes[] = { sizeof(QString), sizeof(double), sizeof(bool), sizeof(ListModel *), sizeof(QQmlGuard), sizeof(QVariantMap), sizeof(QDateTime) }; - const int dataAlignments[] = { sizeof(QString), sizeof(double), sizeof(bool), sizeof(ListModel *), sizeof(QObject *), sizeof(QVariantMap), sizeof(QDateTime) }; - - Role *r = new Role; - r->name = key; - r->type = type; - - if (type == Role::List) { - r->subLayout = new ListLayout; - } else { - r->subLayout = 0; - } - - int dataSize = dataSizes[type]; - int dataAlignment = dataAlignments[type]; - - int dataOffset = (currentBlockOffset + dataAlignment-1) & ~(dataAlignment-1); - if (dataOffset + dataSize > ListElement::BLOCK_SIZE) { - r->blockIndex = ++currentBlock; - r->blockOffset = 0; - currentBlockOffset = dataSize; - } else { - r->blockIndex = currentBlock; - r->blockOffset = dataOffset; - currentBlockOffset = dataOffset + dataSize; - } - - int roleIndex = roles.count(); - r->index = roleIndex; - - roles.append(r); - roleHash.insert(key, r); - - return *r; -} - -ListLayout::ListLayout(const ListLayout *other) : currentBlock(0), currentBlockOffset(0) -{ - for (int i=0 ; i < other->roles.count() ; ++i) { - Role *role = new Role(other->roles[i]); - roles.append(role); - roleHash.insert(role->name, role); - } - currentBlockOffset = other->currentBlockOffset; - currentBlock = other->currentBlock; -} - -ListLayout::~ListLayout() -{ - for (int i=0 ; i < roles.count() ; ++i) { - delete roles[i]; - } -} - -void ListLayout::sync(ListLayout *src, ListLayout *target) -{ - int roleOffset = target->roles.count(); - int newRoleCount = src->roles.count() - roleOffset; - - for (int i=0 ; i < newRoleCount ; ++i) { - Role *role = new Role(src->roles[roleOffset + i]); - target->roles.append(role); - target->roleHash.insert(role->name, role); - } - - target->currentBlockOffset = src->currentBlockOffset; - target->currentBlock = src->currentBlock; -} - -ListLayout::Role::Role(const Role *other) -{ - name = other->name; - type = other->type; - blockIndex = other->blockIndex; - blockOffset = other->blockOffset; - index = other->index; - if (other->subLayout) - subLayout = new ListLayout(other->subLayout); - else - subLayout = 0; -} - -ListLayout::Role::~Role() -{ - delete subLayout; -} - -const ListLayout::Role *ListLayout::getRoleOrCreate(const QString &key, const QVariant &data) -{ - Role::DataType type; - - switch (data.type()) { - case QVariant::Double: type = Role::Number; break; - case QVariant::Int: type = Role::Number; break; - case QVariant::UserType: type = Role::List; break; - case QVariant::Bool: type = Role::Bool; break; - case QVariant::String: type = Role::String; break; - case QVariant::Map: type = Role::VariantMap; break; - default: type = Role::Invalid; break; - } - - if (type == Role::Invalid) { - qmlInfo(0) << "Can't create role for unsupported data type"; - return 0; - } - - return &getRoleOrCreate(key, type); -} - -const ListLayout::Role *ListLayout::getExistingRole(const QString &key) -{ - Role *r = 0; - QStringHash::Node *node = roleHash.findNode(key); - if (node) - r = node->value; - return r; -} - -const ListLayout::Role *ListLayout::getExistingRole(v8::Handle key) -{ - Role *r = 0; - QHashedV8String hashedKey(key); - QStringHash::Node *node = roleHash.findNode(hashedKey); - if (node) - r = node->value; - return r; -} - -ModelObject *ListModel::getOrCreateModelObject(QQmlListModel *model, int elementIndex) -{ - ListElement *e = elements[elementIndex]; - if (e->m_objectCache == 0) { - e->m_objectCache = new ModelObject(model, elementIndex); - } - return e->m_objectCache; -} - -void ListModel::sync(ListModel *src, ListModel *target, QHash *targetModelHash) -{ - // Sanity check - target->m_uid = src->m_uid; - if (targetModelHash) - targetModelHash->insert(target->m_uid, target); - - // Build hash of elements <-> uid for each of the lists - QHash elementHash; - for (int i=0 ; i < target->elements.count() ; ++i) { - ListElement *e = target->elements.at(i); - int uid = e->getUid(); - ElementSync sync; - sync.target = e; - elementHash.insert(uid, sync); - } - for (int i=0 ; i < src->elements.count() ; ++i) { - ListElement *e = src->elements.at(i); - int uid = e->getUid(); - - QHash::iterator it = elementHash.find(uid); - if (it == elementHash.end()) { - ElementSync sync; - sync.src = e; - elementHash.insert(uid, sync); - } else { - ElementSync &sync = it.value(); - sync.src = e; - } - } - - // Get list of elements that are in the target but no longer in the source. These get deleted first. - QHash::iterator it = elementHash.begin(); - QHash::iterator end = elementHash.end(); - while (it != end) { - const ElementSync &s = it.value(); - if (s.src == 0) { - s.target->destroy(target->m_layout); - target->elements.removeOne(s.target); - delete s.target; - } - ++it; - } - - // Sync the layouts - ListLayout::sync(src->m_layout, target->m_layout); - - // Clear the target list, and append in correct order from the source - target->elements.clear(); - for (int i=0 ; i < src->elements.count() ; ++i) { - ListElement *srcElement = src->elements.at(i); - it = elementHash.find(srcElement->getUid()); - const ElementSync &s = it.value(); - ListElement *targetElement = s.target; - if (targetElement == 0) { - targetElement = new ListElement(srcElement->getUid()); - } - ListElement::sync(srcElement, src->m_layout, targetElement, target->m_layout, targetModelHash); - target->elements.append(targetElement); - } - - target->updateCacheIndices(); - - // Update values stored in target meta objects - for (int i=0 ; i < target->elements.count() ; ++i) { - ListElement *e = target->elements[i]; - if (e->m_objectCache) - e->m_objectCache->updateValues(); - } -} - -ListModel::ListModel(ListLayout *layout, QQmlListModel *modelCache, int uid) : m_layout(layout), m_modelCache(modelCache) -{ - if (uid == -1) - uid = uidCounter.fetchAndAddOrdered(1); - m_uid = uid; -} - -void ListModel::destroy() -{ - clear(); - m_uid = -1; - m_layout = 0; - if (m_modelCache && m_modelCache->m_primary == false) - delete m_modelCache; - m_modelCache = 0; -} - -int ListModel::appendElement() -{ - int elementIndex = elements.count(); - newElement(elementIndex); - return elementIndex; -} - -void ListModel::insertElement(int index) -{ - newElement(index); - updateCacheIndices(); -} - -void ListModel::move(int from, int to, int n) -{ - if (from > to) { - // Only move forwards - flip if backwards moving - int tfrom = from; - int tto = to; - from = tto; - to = tto+n; - n = tfrom-tto; - } - - QPODVector store; - for (int i=0 ; i < (to-from) ; ++i) - store.append(elements[from+n+i]); - for (int i=0 ; i < n ; ++i) - store.append(elements[from+i]); - for (int i=0 ; i < store.count() ; ++i) - elements[from+i] = store[i]; - - updateCacheIndices(); -} - -void ListModel::newElement(int index) -{ - ListElement *e = new ListElement; - elements.insert(index, e); -} - -void ListModel::updateCacheIndices() -{ - for (int i=0 ; i < elements.count() ; ++i) { - ListElement *e = elements.at(i); - if (e->m_objectCache) { - e->m_objectCache->m_elementIndex = i; - } - } -} - -QVariant ListModel::getProperty(int elementIndex, int roleIndex, const QQmlListModel *owner, QV8Engine *eng) -{ - ListElement *e = elements[elementIndex]; - const ListLayout::Role &r = m_layout->getExistingRole(roleIndex); - return e->getProperty(r, owner, eng); -} - -ListModel *ListModel::getListProperty(int elementIndex, const ListLayout::Role &role) -{ - ListElement *e = elements[elementIndex]; - return e->getListProperty(role); -} - -void ListModel::set(int elementIndex, v8::Handle object, QVector *roles, QV8Engine *eng) -{ - ListElement *e = elements[elementIndex]; - - v8::Local propertyNames = object->GetPropertyNames(); - int propertyCount = propertyNames->Length(); - - for (int i=0 ; i < propertyCount ; ++i) { - v8::Local propertyName = propertyNames->Get(i)->ToString(); - v8::Local propertyValue = object->Get(propertyName); - - // Check if this key exists yet - int roleIndex = -1; - - // Add the value now - if (propertyValue->IsString()) { - const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::String); - v8::Handle jsString = propertyValue->ToString(); - QString qstr; - qstr.resize(jsString->Length()); - jsString->Write(reinterpret_cast(qstr.data())); - roleIndex = e->setStringProperty(r, qstr); - } else if (propertyValue->IsNumber()) { - const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Number); - roleIndex = e->setDoubleProperty(r, propertyValue->NumberValue()); - } else if (propertyValue->IsArray()) { - const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::List); - ListModel *subModel = new ListModel(r.subLayout, 0, -1); - - v8::Handle subArray = v8::Handle::Cast(propertyValue); - int arrayLength = subArray->Length(); - for (int j=0 ; j < arrayLength ; ++j) { - v8::Handle subObject = subArray->Get(j)->ToObject(); - subModel->append(subObject, eng); - } - - roleIndex = e->setListProperty(r, subModel); - } else if (propertyValue->IsBoolean()) { - const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Bool); - roleIndex = e->setBoolProperty(r, propertyValue->BooleanValue()); - } else if (propertyValue->IsDate()) { - const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::DateTime); - QDateTime dt = QV8Engine::qtDateTimeFromJsDate(v8::Handle::Cast(propertyValue)->NumberValue()); - roleIndex = e->setDateTimeProperty(r, dt); - } else if (propertyValue->IsObject()) { - QV8ObjectResource *r = (QV8ObjectResource *) propertyValue->ToObject()->GetExternalResource(); - if (r && r->resourceType() == QV8ObjectResource::QObjectType) { - QObject *o = QV8QObjectWrapper::toQObject(r); - const ListLayout::Role &role = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::QObject); - if (role.type == ListLayout::Role::QObject) - roleIndex = e->setQObjectProperty(role, o); - } else { - const ListLayout::Role &role = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::VariantMap); - if (role.type == ListLayout::Role::VariantMap) - roleIndex = e->setVariantMapProperty(role, propertyValue->ToObject(), eng); - } - } else if (propertyValue.IsEmpty() || propertyValue->IsUndefined() || propertyValue->IsNull()) { - const ListLayout::Role *r = m_layout->getExistingRole(propertyName); - if (r) - e->clearProperty(*r); - } - - if (roleIndex != -1) - roles->append(roleIndex); - } - - if (e->m_objectCache) { - e->m_objectCache->updateValues(*roles); - } -} - -void ListModel::set(int elementIndex, v8::Handle object, QV8Engine *eng) -{ - ListElement *e = elements[elementIndex]; - - v8::Local propertyNames = object->GetPropertyNames(); - int propertyCount = propertyNames->Length(); - - for (int i=0 ; i < propertyCount ; ++i) { - v8::Local propertyName = propertyNames->Get(i)->ToString(); - v8::Local propertyValue = object->Get(propertyName); - - // Add the value now - if (propertyValue->IsString()) { - const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::String); - if (r.type == ListLayout::Role::String) { - v8::Handle jsString = propertyValue->ToString(); - QString qstr; - qstr.resize(jsString->Length()); - jsString->Write(reinterpret_cast(qstr.data())); - e->setStringPropertyFast(r, qstr); - } - } else if (propertyValue->IsNumber()) { - const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Number); - if (r.type == ListLayout::Role::Number) { - e->setDoublePropertyFast(r, propertyValue->NumberValue()); - } - } else if (propertyValue->IsArray()) { - const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::List); - if (r.type == ListLayout::Role::List) { - ListModel *subModel = new ListModel(r.subLayout, 0, -1); - - v8::Handle subArray = v8::Handle::Cast(propertyValue); - int arrayLength = subArray->Length(); - for (int j=0 ; j < arrayLength ; ++j) { - v8::Handle subObject = subArray->Get(j)->ToObject(); - subModel->append(subObject, eng); - } - - e->setListPropertyFast(r, subModel); - } - } else if (propertyValue->IsBoolean()) { - const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Bool); - if (r.type == ListLayout::Role::Bool) { - e->setBoolPropertyFast(r, propertyValue->BooleanValue()); - } - } else if (propertyValue->IsDate()) { - const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::DateTime); - if (r.type == ListLayout::Role::DateTime) { - QDateTime dt = QV8Engine::qtDateTimeFromJsDate(v8::Handle::Cast(propertyValue)->NumberValue()); - e->setDateTimePropertyFast(r, dt); - } - } else if (propertyValue->IsObject()) { - QV8ObjectResource *r = (QV8ObjectResource *) propertyValue->ToObject()->GetExternalResource(); - if (r && r->resourceType() == QV8ObjectResource::QObjectType) { - QObject *o = QV8QObjectWrapper::toQObject(r); - const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::QObject); - if (r.type == ListLayout::Role::QObject) - e->setQObjectPropertyFast(r, o); - } else { - const ListLayout::Role &role = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::VariantMap); - if (role.type == ListLayout::Role::VariantMap) - e->setVariantMapFast(role, propertyValue->ToObject(), eng); - } - } else if (propertyValue.IsEmpty() || propertyValue->IsUndefined() || propertyValue->IsNull()) { - const ListLayout::Role *r = m_layout->getExistingRole(propertyName); - if (r) - e->clearProperty(*r); - } - } -} - -void ListModel::clear() -{ - int elementCount = elements.count(); - for (int i=0 ; i < elementCount ; ++i) { - elements[i]->destroy(m_layout); - delete elements[i]; - } - elements.clear(); -} - -void ListModel::remove(int index, int count) -{ - for (int i=0 ; i < count ; ++i) { - elements[index+i]->destroy(m_layout); - delete elements[index+i]; - } - elements.remove(index, count); - updateCacheIndices(); -} - -void ListModel::insert(int elementIndex, v8::Handle object, QV8Engine *eng) -{ - insertElement(elementIndex); - set(elementIndex, object, eng); -} - -int ListModel::append(v8::Handle object, QV8Engine *eng) -{ - int elementIndex = appendElement(); - set(elementIndex, object, eng); - return elementIndex; -} - -int ListModel::setOrCreateProperty(int elementIndex, const QString &key, const QVariant &data) -{ - int roleIndex = -1; - - if (elementIndex >= 0 && elementIndex < elements.count()) { - ListElement *e = elements[elementIndex]; - - const ListLayout::Role *r = m_layout->getRoleOrCreate(key, data); - if (r) { - roleIndex = e->setVariantProperty(*r, data); - - if (roleIndex != -1 && e->m_objectCache) { - QVector roles; - roles << roleIndex; - e->m_objectCache->updateValues(roles); - } - } - } - - return roleIndex; -} - -int ListModel::setExistingProperty(int elementIndex, const QString &key, v8::Handle data, QV8Engine *eng) -{ - int roleIndex = -1; - - if (elementIndex >= 0 && elementIndex < elements.count()) { - ListElement *e = elements[elementIndex]; - const ListLayout::Role *r = m_layout->getExistingRole(key); - if (r) - roleIndex = e->setJsProperty(*r, data, eng); - } - - return roleIndex; -} - -inline char *ListElement::getPropertyMemory(const ListLayout::Role &role) -{ - ListElement *e = this; - int blockIndex = 0; - while (blockIndex < role.blockIndex) { - if (e->next == 0) { - e->next = new ListElement; - e->next->uid = uid; - } - e = e->next; - ++blockIndex; - } - - char *mem = &e->data[role.blockOffset]; - return mem; -} - -QString *ListElement::getStringProperty(const ListLayout::Role &role) -{ - char *mem = getPropertyMemory(role); - QString *s = reinterpret_cast(mem); - return s->data_ptr() ? s : 0; -} - -QObject *ListElement::getQObjectProperty(const ListLayout::Role &role) -{ - char *mem = getPropertyMemory(role); - QQmlGuard *o = reinterpret_cast *>(mem); - return o->data(); -} - -QVariantMap *ListElement::getVariantMapProperty(const ListLayout::Role &role) -{ - QVariantMap *map = 0; - - char *mem = getPropertyMemory(role); - if (isMemoryUsed(mem)) - map = reinterpret_cast(mem); - - return map; -} - -QDateTime *ListElement::getDateTimeProperty(const ListLayout::Role &role) -{ - QDateTime *dt = 0; - - char *mem = getPropertyMemory(role); - if (isMemoryUsed(mem)) - dt = reinterpret_cast(mem); - - return dt; -} - -QQmlGuard *ListElement::getGuardProperty(const ListLayout::Role &role) -{ - char *mem = getPropertyMemory(role); - - bool existingGuard = false; - for (size_t i=0 ; i < sizeof(QQmlGuard) ; ++i) { - if (mem[i] != 0) { - existingGuard = true; - break; - } - } - - QQmlGuard *o = 0; - - if (existingGuard) - o = reinterpret_cast *>(mem); - - return o; -} - -ListModel *ListElement::getListProperty(const ListLayout::Role &role) -{ - char *mem = getPropertyMemory(role); - ListModel **value = reinterpret_cast(mem); - return *value; -} - -QVariant ListElement::getProperty(const ListLayout::Role &role, const QQmlListModel *owner, QV8Engine *eng) -{ - char *mem = getPropertyMemory(role); - - QVariant data; - - switch (role.type) { - case ListLayout::Role::Number: - { - double *value = reinterpret_cast(mem); - data = *value; - } - break; - case ListLayout::Role::String: - { - QString *value = reinterpret_cast(mem); - if (value->data_ptr() != 0) - data = *value; - } - break; - case ListLayout::Role::Bool: - { - bool *value = reinterpret_cast(mem); - data = *value; - } - break; - case ListLayout::Role::List: - { - ListModel **value = reinterpret_cast(mem); - ListModel *model = *value; - - if (model) { - if (model->m_modelCache == 0) { - model->m_modelCache = new QQmlListModel(owner, model, eng); - QQmlEngine::setContextForObject(model->m_modelCache, QQmlEngine::contextForObject(owner)); - } - - QObject *object = model->m_modelCache; - data = QVariant::fromValue(object); - } - } - break; - case ListLayout::Role::QObject: - { - QQmlGuard *guard = reinterpret_cast *>(mem); - QObject *object = guard->data(); - if (object) - data = QVariant::fromValue(object); - } - break; - case ListLayout::Role::VariantMap: - { - if (isMemoryUsed(mem)) { - QVariantMap *map = reinterpret_cast(mem); - data = *map; - } - } - break; - case ListLayout::Role::DateTime: - { - if (isMemoryUsed(mem)) { - QDateTime *dt = reinterpret_cast(mem); - data = *dt; - } - } - break; - default: - break; - } - - return data; -} - -int ListElement::setStringProperty(const ListLayout::Role &role, const QString &s) -{ - int roleIndex = -1; - - if (role.type == ListLayout::Role::String) { - char *mem = getPropertyMemory(role); - QString *c = reinterpret_cast(mem); - bool changed; - if (c->data_ptr() == 0) { - new (mem) QString(s); - changed = true; - } else { - changed = c->compare(s) != 0; - *c = s; - } - if (changed) - roleIndex = role.index; - } - - return roleIndex; -} - -int ListElement::setDoubleProperty(const ListLayout::Role &role, double d) -{ - int roleIndex = -1; - - if (role.type == ListLayout::Role::Number) { - char *mem = getPropertyMemory(role); - double *value = new (mem) double; - bool changed = *value != d; - *value = d; - if (changed) - roleIndex = role.index; - } - - return roleIndex; -} - -int ListElement::setBoolProperty(const ListLayout::Role &role, bool b) -{ - int roleIndex = -1; - - if (role.type == ListLayout::Role::Bool) { - char *mem = getPropertyMemory(role); - bool *value = new (mem) bool; - bool changed = *value != b; - *value = b; - if (changed) - roleIndex = role.index; - } - - return roleIndex; -} - -int ListElement::setListProperty(const ListLayout::Role &role, ListModel *m) -{ - int roleIndex = -1; - - if (role.type == ListLayout::Role::List) { - char *mem = getPropertyMemory(role); - ListModel **value = new (mem) ListModel *; - if (*value) { - (*value)->destroy(); - delete *value; - } - *value = m; - roleIndex = role.index; - } - - return roleIndex; -} - -int ListElement::setQObjectProperty(const ListLayout::Role &role, QObject *o) -{ - int roleIndex = -1; - - if (role.type == ListLayout::Role::QObject) { - char *mem = getPropertyMemory(role); - QQmlGuard *g = reinterpret_cast *>(mem); - bool existingGuard = false; - for (size_t i=0 ; i < sizeof(QQmlGuard) ; ++i) { - if (mem[i] != 0) { - existingGuard = true; - break; - } - } - bool changed; - if (existingGuard) { - changed = g->data() != o; - g->~QQmlGuard(); - } else { - changed = true; - } - new (mem) QQmlGuard(o); - if (changed) - roleIndex = role.index; - } - - return roleIndex; -} - -int ListElement::setVariantMapProperty(const ListLayout::Role &role, v8::Handle o, QV8Engine *eng) -{ - int roleIndex = -1; - - if (role.type == ListLayout::Role::VariantMap) { - char *mem = getPropertyMemory(role); - if (isMemoryUsed(mem)) { - QVariantMap *map = reinterpret_cast(mem); - map->~QMap(); - } - new (mem) QVariantMap(eng->variantMapFromJS(o)); - roleIndex = role.index; - } - - return roleIndex; -} - -int ListElement::setVariantMapProperty(const ListLayout::Role &role, QVariantMap *m) -{ - int roleIndex = -1; - - if (role.type == ListLayout::Role::VariantMap) { - char *mem = getPropertyMemory(role); - if (isMemoryUsed(mem)) { - QVariantMap *map = reinterpret_cast(mem); - map->~QMap(); - } - if (m) - new (mem) QVariantMap(*m); - else - new (mem) QVariantMap; - roleIndex = role.index; - } - - return roleIndex; -} - -int ListElement::setDateTimeProperty(const ListLayout::Role &role, const QDateTime &dt) -{ - int roleIndex = -1; - - if (role.type == ListLayout::Role::DateTime) { - char *mem = getPropertyMemory(role); - if (isMemoryUsed(mem)) { - QDateTime *dt = reinterpret_cast(mem); - dt->~QDateTime(); - } - new (mem) QDateTime(dt); - roleIndex = role.index; - } - - return roleIndex; -} - -void ListElement::setStringPropertyFast(const ListLayout::Role &role, const QString &s) -{ - char *mem = getPropertyMemory(role); - new (mem) QString(s); -} - -void ListElement::setDoublePropertyFast(const ListLayout::Role &role, double d) -{ - char *mem = getPropertyMemory(role); - double *value = new (mem) double; - *value = d; -} - -void ListElement::setBoolPropertyFast(const ListLayout::Role &role, bool b) -{ - char *mem = getPropertyMemory(role); - bool *value = new (mem) bool; - *value = b; -} - -void ListElement::setQObjectPropertyFast(const ListLayout::Role &role, QObject *o) -{ - char *mem = getPropertyMemory(role); - new (mem) QQmlGuard(o); -} - -void ListElement::setListPropertyFast(const ListLayout::Role &role, ListModel *m) -{ - char *mem = getPropertyMemory(role); - ListModel **value = new (mem) ListModel *; - *value = m; -} - -void ListElement::setVariantMapFast(const ListLayout::Role &role, v8::Handle o, QV8Engine *eng) -{ - char *mem = getPropertyMemory(role); - QVariantMap *map = new (mem) QVariantMap; - *map = eng->variantMapFromJS(o); -} - -void ListElement::setDateTimePropertyFast(const ListLayout::Role &role, const QDateTime &dt) -{ - char *mem = getPropertyMemory(role); - new (mem) QDateTime(dt); -} - -void ListElement::clearProperty(const ListLayout::Role &role) -{ - switch (role.type) { - case ListLayout::Role::String: - setStringProperty(role, QString()); - break; - case ListLayout::Role::Number: - setDoubleProperty(role, 0.0); - break; - case ListLayout::Role::Bool: - setBoolProperty(role, false); - break; - case ListLayout::Role::List: - setListProperty(role, 0); - break; - case ListLayout::Role::QObject: - setQObjectProperty(role, 0); - break; - case ListLayout::Role::DateTime: - setDateTimeProperty(role, QDateTime()); - break; - case ListLayout::Role::VariantMap: - setVariantMapProperty(role, 0); - break; - default: - break; - } -} - -ListElement::ListElement() -{ - m_objectCache = 0; - uid = uidCounter.fetchAndAddOrdered(1); - next = 0; - memset(data, 0, sizeof(data)); -} - -ListElement::ListElement(int existingUid) -{ - m_objectCache = 0; - uid = existingUid; - next = 0; - memset(data, 0, sizeof(data)); -} - -ListElement::~ListElement() -{ - delete next; -} - -void ListElement::sync(ListElement *src, ListLayout *srcLayout, ListElement *target, ListLayout *targetLayout, QHash *targetModelHash) -{ - for (int i=0 ; i < srcLayout->roleCount() ; ++i) { - const ListLayout::Role &srcRole = srcLayout->getExistingRole(i); - const ListLayout::Role &targetRole = targetLayout->getExistingRole(i); - - switch (srcRole.type) { - case ListLayout::Role::List: - { - ListModel *srcSubModel = src->getListProperty(srcRole); - ListModel *targetSubModel = target->getListProperty(targetRole); - - if (srcSubModel) { - if (targetSubModel == 0) { - targetSubModel = new ListModel(targetRole.subLayout, 0, srcSubModel->getUid()); - target->setListPropertyFast(targetRole, targetSubModel); - } - ListModel::sync(srcSubModel, targetSubModel, targetModelHash); - } - } - break; - case ListLayout::Role::QObject: - { - QObject *object = src->getQObjectProperty(srcRole); - target->setQObjectProperty(targetRole, object); - } - break; - case ListLayout::Role::String: - case ListLayout::Role::Number: - case ListLayout::Role::Bool: - case ListLayout::Role::DateTime: - { - QVariant v = src->getProperty(srcRole, 0, 0); - target->setVariantProperty(targetRole, v); - } - case ListLayout::Role::VariantMap: - { - QVariantMap *map = src->getVariantMapProperty(srcRole); - target->setVariantMapProperty(targetRole, map); - } - break; - default: - break; - } - } - -} - -void ListElement::destroy(ListLayout *layout) -{ - if (layout) { - for (int i=0 ; i < layout->roleCount() ; ++i) { - const ListLayout::Role &r = layout->getExistingRole(i); - - switch (r.type) { - case ListLayout::Role::String: - { - QString *string = getStringProperty(r); - if (string) - string->~QString(); - } - break; - case ListLayout::Role::List: - { - ListModel *model = getListProperty(r); - if (model) { - model->destroy(); - delete model; - } - } - break; - case ListLayout::Role::QObject: - { - QQmlGuard *guard = getGuardProperty(r); - if (guard) - guard->~QQmlGuard(); - } - break; - case ListLayout::Role::VariantMap: - { - QVariantMap *map = getVariantMapProperty(r); - if (map) - map->~QMap(); - } - break; - case ListLayout::Role::DateTime: - { - QDateTime *dt = getDateTimeProperty(r); - if (dt) - dt->~QDateTime(); - } - break; - default: - // other types don't need explicit cleanup. - break; - } - } - - delete m_objectCache; - } - - if (next) - next->destroy(0); - uid = -1; -} - -int ListElement::setVariantProperty(const ListLayout::Role &role, const QVariant &d) -{ - int roleIndex = -1; - - switch (role.type) { - case ListLayout::Role::Number: - roleIndex = setDoubleProperty(role, d.toDouble()); - break; - case ListLayout::Role::String: - roleIndex = setStringProperty(role, d.toString()); - break; - case ListLayout::Role::Bool: - roleIndex = setBoolProperty(role, d.toBool()); - break; - case ListLayout::Role::List: - roleIndex = setListProperty(role, d.value()); - break; - case ListLayout::Role::VariantMap: { - QVariantMap map = d.toMap(); - roleIndex = setVariantMapProperty(role, &map); - } - break; - case ListLayout::Role::DateTime: - roleIndex = setDateTimeProperty(role, d.toDateTime()); - break; - default: - break; - } - - return roleIndex; -} - -int ListElement::setJsProperty(const ListLayout::Role &role, v8::Handle d, QV8Engine *eng) -{ - // Check if this key exists yet - int roleIndex = -1; - - // Add the value now - if (d->IsString()) { - v8::Handle jsString = d->ToString(); - QString qstr; - qstr.resize(jsString->Length()); - jsString->Write(reinterpret_cast(qstr.data())); - roleIndex = setStringProperty(role, qstr); - } else if (d->IsNumber()) { - roleIndex = setDoubleProperty(role, d->NumberValue()); - } else if (d->IsArray()) { - if (role.type == ListLayout::Role::List) { - ListModel *subModel = new ListModel(role.subLayout, 0, -1); - v8::Handle subArray = v8::Handle::Cast(d); - int arrayLength = subArray->Length(); - for (int j=0 ; j < arrayLength ; ++j) { - v8::Handle subObject = subArray->Get(j)->ToObject(); - subModel->append(subObject, eng); - } - roleIndex = setListProperty(role, subModel); - } else { - qmlInfo(0) << QString::fromLatin1("Can't assign to existing role '%1' of different type [%2 -> %3]").arg(role.name).arg(roleTypeName(role.type)).arg(roleTypeName(ListLayout::Role::List)); - } - } else if (d->IsBoolean()) { - roleIndex = setBoolProperty(role, d->BooleanValue()); - } else if (d->IsDate()) { - QDateTime dt = QV8Engine::qtDateTimeFromJsDate(v8::Handle::Cast(d)->NumberValue()); - roleIndex = setDateTimeProperty(role, dt); - } else if (d->IsObject()) { - QV8ObjectResource *r = (QV8ObjectResource *) d->ToObject()->GetExternalResource(); - if (role.type == ListLayout::Role::QObject && r && r->resourceType() == QV8ObjectResource::QObjectType) { - QObject *o = QV8QObjectWrapper::toQObject(r); - roleIndex = setQObjectProperty(role, o); - } else if (role.type == ListLayout::Role::VariantMap) { - roleIndex = setVariantMapProperty(role, d->ToObject(), eng); - } - } else if (d.IsEmpty() || d->IsUndefined() || d->IsNull()) { - clearProperty(role); - } - - return roleIndex; -} - -ModelObject::ModelObject(QQmlListModel *model, int elementIndex) -: m_model(model), m_elementIndex(elementIndex), m_meta(new ModelNodeMetaObject(this)) -{ - updateValues(); - setNodeUpdatesEnabled(true); -} - -void ModelObject::updateValues() -{ - int roleCount = m_model->m_listModel->roleCount(); - for (int i=0 ; i < roleCount ; ++i) { - const ListLayout::Role &role = m_model->m_listModel->getExistingRole(i); - QByteArray name = role.name.toUtf8(); - const QVariant &data = m_model->data(m_elementIndex, i); - setValue(name, data, role.type == ListLayout::Role::List); - } -} - -void ModelObject::updateValues(const QVector &roles) -{ - int roleCount = roles.count(); - for (int i=0 ; i < roleCount ; ++i) { - int roleIndex = roles.at(i); - const ListLayout::Role &role = m_model->m_listModel->getExistingRole(roleIndex); - QByteArray name = role.name.toUtf8(); - const QVariant &data = m_model->data(m_elementIndex, roleIndex); - setValue(name, data, role.type == ListLayout::Role::List); - } -} - -ModelNodeMetaObject::ModelNodeMetaObject(ModelObject *object) -: QQmlOpenMetaObject(object), m_enabled(false), m_obj(object) -{ -} - -ModelNodeMetaObject::~ModelNodeMetaObject() -{ -} - -void ModelNodeMetaObject::propertyWritten(int index) -{ - if (!m_enabled) - return; - - QV8Engine *eng = m_obj->m_model->engine(); - - QString propName = QString::fromUtf8(name(index)); - QVariant value = operator[](index); - - v8::HandleScope handle_scope; - v8::Context::Scope scope(eng->context()); - - v8::Handle v = eng->fromVariant(value); - - int roleIndex = m_obj->m_model->m_listModel->setExistingProperty(m_obj->m_elementIndex, propName, v, eng); - if (roleIndex != -1) { - QVector roles; - roles << roleIndex; - m_obj->m_model->emitItemsChanged(m_obj->m_elementIndex, 1, roles); - } -} - -DynamicRoleModelNode::DynamicRoleModelNode(QQmlListModel *owner, int uid) : m_owner(owner), m_uid(uid), m_meta(new DynamicRoleModelNodeMetaObject(this)) -{ - setNodeUpdatesEnabled(true); -} - -DynamicRoleModelNode *DynamicRoleModelNode::create(const QVariantMap &obj, QQmlListModel *owner) -{ - DynamicRoleModelNode *object = new DynamicRoleModelNode(owner, uidCounter.fetchAndAddOrdered(1)); - QVector roles; - object->updateValues(obj, roles); - return object; -} - -void DynamicRoleModelNode::sync(DynamicRoleModelNode *src, DynamicRoleModelNode *target, QHash *targetModelHash) -{ - for (int i=0 ; i < src->m_meta->count() ; ++i) { - const QByteArray &name = src->m_meta->name(i); - QVariant value = src->m_meta->value(i); - - QQmlListModel *srcModel = qobject_cast(value.value()); - QQmlListModel *targetModel = qobject_cast(target->m_meta->value(i).value()); - - if (srcModel) { - if (targetModel == 0) - targetModel = QQmlListModel::createWithOwner(target->m_owner); - - QQmlListModel::sync(srcModel, targetModel, targetModelHash); - - QObject *targetModelObject = targetModel; - value = QVariant::fromValue(targetModelObject); - } else if (targetModel) { - delete targetModel; - } - - target->setValue(name, value); - } -} - -void DynamicRoleModelNode::updateValues(const QVariantMap &object, QVector &roles) -{ - const QList &keys = object.keys(); - - QList::const_iterator it = keys.begin(); - QList::const_iterator end = keys.end(); - - while (it != end) { - const QString &key = *it; - - int roleIndex = m_owner->m_roles.indexOf(key); - if (roleIndex == -1) { - roleIndex = m_owner->m_roles.count(); - m_owner->m_roles.append(key); - } - - QVariant value = object[key]; - - if (value.type() == QVariant::List) { - QQmlListModel *subModel = QQmlListModel::createWithOwner(m_owner); - - QVariantList subArray = value.toList(); - QVariantList::const_iterator subIt = subArray.begin(); - QVariantList::const_iterator subEnd = subArray.end(); - while (subIt != subEnd) { - const QVariantMap &subObject = subIt->toMap(); - subModel->m_modelObjects.append(DynamicRoleModelNode::create(subObject, subModel)); - ++subIt; - } - - QObject *subModelObject = subModel; - value = QVariant::fromValue(subModelObject); - } - - const QByteArray &keyUtf8 = key.toUtf8(); - - QQmlListModel *existingModel = qobject_cast(m_meta->value(keyUtf8).value()); - if (existingModel) - delete existingModel; - - if (m_meta->setValue(keyUtf8, value)) - roles << roleIndex; - - ++it; - } -} - -DynamicRoleModelNodeMetaObject::DynamicRoleModelNodeMetaObject(DynamicRoleModelNode *object) - : QQmlOpenMetaObject(object), m_enabled(false), m_owner(object) -{ -} - -DynamicRoleModelNodeMetaObject::~DynamicRoleModelNodeMetaObject() -{ - for (int i=0 ; i < count() ; ++i) { - QQmlListModel *subModel = qobject_cast(value(i).value()); - if (subModel) - delete subModel; - } -} - -void DynamicRoleModelNodeMetaObject::propertyWrite(int index) -{ - if (!m_enabled) - return; - - QVariant v = value(index); - QQmlListModel *model = qobject_cast(v.value()); - if (model) - delete model; -} - -void DynamicRoleModelNodeMetaObject::propertyWritten(int index) -{ - if (!m_enabled) - return; - - QQmlListModel *parentModel = m_owner->m_owner; - - QVariant v = value(index); - if (v.type() == QVariant::List) { - QQmlListModel *subModel = QQmlListModel::createWithOwner(parentModel); - - QVariantList subArray = v.toList(); - QVariantList::const_iterator subIt = subArray.begin(); - QVariantList::const_iterator subEnd = subArray.end(); - while (subIt != subEnd) { - const QVariantMap &subObject = subIt->toMap(); - subModel->m_modelObjects.append(DynamicRoleModelNode::create(subObject, subModel)); - ++subIt; - } - - QObject *subModelObject = subModel; - v = QVariant::fromValue(subModelObject); - - setValue(index, v); - } - - int elementIndex = parentModel->m_modelObjects.indexOf(m_owner); - int roleIndex = parentModel->m_roles.indexOf(QString::fromLatin1(name(index).constData())); - - if (elementIndex != -1 && roleIndex != -1) { - - QVector roles; - roles << roleIndex; - - parentModel->emitItemsChanged(elementIndex, 1, roles); - } -} - -QQmlListModelParser::ListInstruction *QQmlListModelParser::ListModelData::instructions() const -{ - return (QQmlListModelParser::ListInstruction *)((char *)this + sizeof(ListModelData)); -} - -/*! - \qmltype ListModel - \instantiates QQmlListModel - \inqmlmodule QtQml.Models 2 - \brief Defines a free-form list data source - - The ListModel is a simple container of ListElement definitions, each containing data roles. - The contents can be defined dynamically, or explicitly in QML. - - This type is also available in the QtQuick 2 import. For full documentation, see \l QtQuick2::ListModel -*/ -/*! - \qmltype ListModel - \instantiates QQmlListModel - \inqmlmodule QtQuick 2 - \brief Defines a free-form list data source - \ingroup qtquick-models - - The ListModel is a simple container of ListElement definitions, each containing data roles. - The contents can be defined dynamically, or explicitly in QML. - - The number of elements in the model can be obtained from its \l count property. - A number of familiar methods are also provided to manipulate the contents of the - model, including append(), insert(), move(), remove() and set(). These methods - accept dictionaries as their arguments; these are translated to ListElement objects - by the model. - - Elements can be manipulated via the model using the setProperty() method, which - allows the roles of the specified element to be set and changed. - - \section1 Example Usage - - The following example shows a ListModel containing three elements, with the roles - "name" and "cost". - - \div {class="float-right"} - \inlineimage listmodel.png - \enddiv - - \snippet qml/listmodel/listmodel.qml 0 - - Roles (properties) in each element must begin with a lower-case letter and - should be common to all elements in a model. The ListElement documentation - provides more guidelines for how elements should be defined. - - Since the example model contains an \c id property, it can be referenced - by views, such as the ListView in this example: - - \snippet qml/listmodel/listmodel-simple.qml 0 - \dots 8 - \snippet qml/listmodel/listmodel-simple.qml 1 - - It is possible for roles to contain list data. In the following example we - create a list of fruit attributes: - - \snippet qml/listmodel/listmodel-nested.qml model - - The delegate displays all the fruit attributes: - - \div {class="float-right"} - \inlineimage listmodel-nested.png - \enddiv - - \snippet qml/listmodel/listmodel-nested.qml delegate - - \section1 Modifying List Models - - The content of a ListModel may be created and modified using the clear(), - append(), set(), insert() and setProperty() methods. For example: - - \snippet qml/listmodel/listmodel-modify.qml delegate - - Note that when creating content dynamically the set of available properties - cannot be changed once set. Whatever properties are first added to the model - are the only permitted properties in the model. - - \section1 Using Threaded List Models with WorkerScript - - ListModel can be used together with WorkerScript access a list model - from multiple threads. This is useful if list modifications are - synchronous and take some time: the list operations can be moved to a - different thread to avoid blocking of the main GUI thread. - - Here is an example that uses WorkerScript to periodically append the - current time to a list model: - - \snippet quick/threading/threadedlistmodel/timedisplay.qml 0 - - The included file, \tt dataloader.js, looks like this: - - \snippet quick/threading/threadedlistmodel/dataloader.js 0 - - The timer in the main example sends messages to the worker script by calling - \l WorkerScript::sendMessage(). When this message is received, - \l{WorkerScript::onMessage}{WorkerScript.onMessage()} is invoked in \c dataloader.js, - which appends the current time to the list model. - - Note the call to sync() from the \l{WorkerScript::onMessage}{WorkerScript.onMessage()} - handler. You must call sync() or else the changes made to the list from the external - thread will not be reflected in the list model in the main thread. - - \sa {qml-data-models}{Data Models}, {declarative/threading/threadedlistmodel}{Threaded ListModel example}, QtQml -*/ - -QQmlListModel::QQmlListModel(QObject *parent) -: QAbstractListModel(parent) -{ - m_mainThread = true; - m_primary = true; - m_agent = 0; - m_uid = uidCounter.fetchAndAddOrdered(1); - m_dynamicRoles = false; - - m_layout = new ListLayout; - m_listModel = new ListModel(m_layout, this, -1); - - m_engine = 0; -} - -QQmlListModel::QQmlListModel(const QQmlListModel *owner, ListModel *data, QV8Engine *eng, QObject *parent) -: QAbstractListModel(parent) -{ - m_mainThread = owner->m_mainThread; - m_primary = false; - m_agent = owner->m_agent; - - Q_ASSERT(owner->m_dynamicRoles == false); - m_dynamicRoles = false; - m_layout = 0; - m_listModel = data; - - m_engine = eng; -} - -QQmlListModel::QQmlListModel(QQmlListModel *orig, QQmlListModelWorkerAgent *agent) -: QAbstractListModel(agent) -{ - m_mainThread = false; - m_primary = true; - m_agent = agent; - m_dynamicRoles = orig->m_dynamicRoles; - - m_layout = new ListLayout(orig->m_layout); - m_listModel = new ListModel(m_layout, this, orig->m_listModel->getUid()); - - if (m_dynamicRoles) - sync(orig, this, 0); - else - ListModel::sync(orig->m_listModel, m_listModel, 0); - - m_engine = 0; -} - -QQmlListModel::~QQmlListModel() -{ - for (int i=0 ; i < m_modelObjects.count() ; ++i) - delete m_modelObjects[i]; - - if (m_primary) { - m_listModel->destroy(); - delete m_listModel; - - if (m_mainThread && m_agent) { - m_agent->modelDestroyed(); - m_agent->release(); - } - } - - m_listModel = 0; - - delete m_layout; - m_layout = 0; -} - -QQmlListModel *QQmlListModel::createWithOwner(QQmlListModel *newOwner) -{ - QQmlListModel *model = new QQmlListModel; - - model->m_mainThread = newOwner->m_mainThread; - model->m_engine = newOwner->m_engine; - model->m_agent = newOwner->m_agent; - model->m_dynamicRoles = newOwner->m_dynamicRoles; - - if (model->m_mainThread && model->m_agent) - model->m_agent->addref(); - - QQmlEngine::setContextForObject(model, QQmlEngine::contextForObject(newOwner)); - - return model; -} - -QV8Engine *QQmlListModel::engine() const -{ - if (m_engine == 0) { - m_engine = QQmlEnginePrivate::getV8Engine(qmlEngine(this)); - } - - return m_engine; -} - -void QQmlListModel::sync(QQmlListModel *src, QQmlListModel *target, QHash *targetModelHash) -{ - Q_ASSERT(src->m_dynamicRoles && target->m_dynamicRoles); - - target->m_uid = src->m_uid; - if (targetModelHash) - targetModelHash->insert(target->m_uid, target); - target->m_roles = src->m_roles; - - // Build hash of elements <-> uid for each of the lists - QHash elementHash; - for (int i=0 ; i < target->m_modelObjects.count() ; ++i) { - DynamicRoleModelNode *e = target->m_modelObjects.at(i); - int uid = e->getUid(); - ElementSync sync; - sync.target = e; - elementHash.insert(uid, sync); - } - for (int i=0 ; i < src->m_modelObjects.count() ; ++i) { - DynamicRoleModelNode *e = src->m_modelObjects.at(i); - int uid = e->getUid(); - - QHash::iterator it = elementHash.find(uid); - if (it == elementHash.end()) { - ElementSync sync; - sync.src = e; - elementHash.insert(uid, sync); - } else { - ElementSync &sync = it.value(); - sync.src = e; - } - } - - // Get list of elements that are in the target but no longer in the source. These get deleted first. - QHash::iterator it = elementHash.begin(); - QHash::iterator end = elementHash.end(); - while (it != end) { - const ElementSync &s = it.value(); - if (s.src == 0) { - int targetIndex = target->m_modelObjects.indexOf(s.target); - target->m_modelObjects.remove(targetIndex, 1); - delete s.target; - } - ++it; - } - - // Clear the target list, and append in correct order from the source - target->m_modelObjects.clear(); - for (int i=0 ; i < src->m_modelObjects.count() ; ++i) { - DynamicRoleModelNode *srcElement = src->m_modelObjects.at(i); - it = elementHash.find(srcElement->getUid()); - const ElementSync &s = it.value(); - DynamicRoleModelNode *targetElement = s.target; - if (targetElement == 0) { - targetElement = new DynamicRoleModelNode(target, srcElement->getUid()); - } - DynamicRoleModelNode::sync(srcElement, targetElement, targetModelHash); - target->m_modelObjects.append(targetElement); - } -} - -void QQmlListModel::emitItemsChanged(int index, int count, const QVector &roles) -{ - if (count <= 0) - return; - - if (m_mainThread) { - emit dataChanged(createIndex(index, 0), createIndex(index + count - 1, 0), roles);; - } else { - int uid = m_dynamicRoles ? getUid() : m_listModel->getUid(); - m_agent->data.changedChange(uid, index, count, roles); - } -} - -void QQmlListModel::emitItemsRemoved(int index, int count) -{ - if (count <= 0) - return; - - if (m_mainThread) { - beginRemoveRows(QModelIndex(), index, index + count - 1); - endRemoveRows(); - emit countChanged(); - } else { - int uid = m_dynamicRoles ? getUid() : m_listModel->getUid(); - if (index == 0 && count == this->count()) - m_agent->data.clearChange(uid); - m_agent->data.removeChange(uid, index, count); - } -} - -void QQmlListModel::emitItemsInserted(int index, int count) -{ - if (count <= 0) - return; - - if (m_mainThread) { - beginInsertRows(QModelIndex(), index, index + count - 1); - endInsertRows(); - emit countChanged(); - } else { - int uid = m_dynamicRoles ? getUid() : m_listModel->getUid(); - m_agent->data.insertChange(uid, index, count); - } -} - -void QQmlListModel::emitItemsMoved(int from, int to, int n) -{ - if (n <= 0) - return; - - if (m_mainThread) { - beginMoveRows(QModelIndex(), from, from + n - 1, QModelIndex(), to > from ? to + n : to); - endMoveRows(); - } else { - int uid = m_dynamicRoles ? getUid() : m_listModel->getUid(); - m_agent->data.moveChange(uid, from, n, to); - } -} - -QQmlListModelWorkerAgent *QQmlListModel::agent() -{ - if (m_agent) - return m_agent; - - m_agent = new QQmlListModelWorkerAgent(this); - return m_agent; -} - -QModelIndex QQmlListModel::index(int row, int column, const QModelIndex &parent) const -{ - return row >= 0 && row < count() && column == 0 && !parent.isValid() - ? createIndex(row, column) - : QModelIndex(); -} - -int QQmlListModel::rowCount(const QModelIndex &parent) const -{ - return !parent.isValid() ? count() : 0; -} - -QVariant QQmlListModel::data(const QModelIndex &index, int role) const -{ - return data(index.row(), role); -} - -QVariant QQmlListModel::data(int index, int role) const -{ - QVariant v; - - if (index >= count() || index < 0) - return v; - - if (m_dynamicRoles) - v = m_modelObjects[index]->getValue(m_roles[role]); - else - v = m_listModel->getProperty(index, role, this, engine()); - - return v; -} - -QHash QQmlListModel::roleNames() const -{ - QHash roleNames; - - if (m_dynamicRoles) { - for (int i = 0 ; i < m_roles.count() ; ++i) - roleNames.insert(i, m_roles.at(i).toUtf8()); - } else { - for (int i = 0 ; i < m_listModel->roleCount() ; ++i) { - const ListLayout::Role &r = m_listModel->getExistingRole(i); - roleNames.insert(i, r.name.toUtf8()); - } - } - - return roleNames; -} - -/*! - \qmlproperty bool QtQml2::ListModel::dynamicRoles - - By default, the type of a role is fixed the first time - the role is used. For example, if you create a role called - "data" and assign a number to it, you can no longer assign - a string to the "data" role. However, when the dynamicRoles - property is enabled, the type of a given role is not fixed - and can be different between elements. - - The dynamicRoles property must be set before any data is - added to the ListModel, and must be set from the main - thread. - - A ListModel that has data statically defined (via the - ListElement QML syntax) cannot have the dynamicRoles - property enabled. - - There is a significant performance cost to using a - ListModel with dynamic roles enabled. The cost varies - from platform to platform but is typically somewhere - between 4-6x slower than using static role types. - - Due to the performance cost of using dynamic roles, - they are disabled by default. -*/ -void QQmlListModel::setDynamicRoles(bool enableDynamicRoles) -{ - if (m_mainThread && m_agent == 0) { - if (enableDynamicRoles) { - if (m_layout->roleCount()) - qmlInfo(this) << tr("unable to enable dynamic roles as this model is not empty!"); - else - m_dynamicRoles = true; - } else { - if (m_roles.count()) { - qmlInfo(this) << tr("unable to enable static roles as this model is not empty!"); - } else { - m_dynamicRoles = false; - } - } - } else { - qmlInfo(this) << tr("dynamic role setting must be made from the main thread, before any worker scripts are created"); - } -} - -/*! - \qmlproperty int QtQml2::ListModel::count - The number of data entries in the model. -*/ -int QQmlListModel::count() const -{ - int count; - - if (m_dynamicRoles) - count = m_modelObjects.count(); - else { - count = m_listModel->elementCount(); - } - - return count; -} - -/*! - \qmlmethod QtQml2::ListModel::clear() - - Deletes all content from the model. - - \sa append(), remove() -*/ -void QQmlListModel::clear() -{ - int cleared = count(); - - if (m_dynamicRoles) { - for (int i=0 ; i < m_modelObjects.count() ; ++i) - delete m_modelObjects[i]; - m_modelObjects.clear(); - } else { - m_listModel->clear(); - } - - emitItemsRemoved(0, cleared); -} - -/*! - \qmlmethod QtQml2::ListModel::remove(int index, int count = 1) - - Deletes the content at \a index from the model. - - \sa clear() -*/ -void QQmlListModel::remove(QQmlV8Function *args) -{ - int argLength = args->Length(); - - if (argLength == 1 || argLength == 2) { - int index = (*args)[0]->Int32Value(); - int removeCount = (argLength == 2 ? ((*args)[1]->Int32Value()) : 1); - - if (index < 0 || index+removeCount > count() || removeCount <= 0) { - qmlInfo(this) << tr("remove: indices [%1 - %2] out of range [0 - %3]").arg(index).arg(index+removeCount).arg(count()); - return; - } - - if (m_dynamicRoles) { - for (int i=0 ; i < removeCount ; ++i) - delete m_modelObjects[index+i]; - m_modelObjects.remove(index, removeCount); - } else { - m_listModel->remove(index, removeCount); - } - - emitItemsRemoved(index, removeCount); - } else { - qmlInfo(this) << tr("remove: incorrect number of arguments"); - } -} - -/*! - \qmlmethod QtQml2::ListModel::insert(int index, jsobject dict) - - Adds a new item to the list model at position \a index, with the - values in \a dict. - - \code - fruitModel.insert(2, {"cost": 5.95, "name":"Pizza"}) - \endcode - - The \a index must be to an existing item in the list, or one past - the end of the list (equivalent to append). - - \sa set(), append() -*/ - -void QQmlListModel::insert(QQmlV8Function *args) -{ - if (args->Length() == 2) { - - v8::Handle arg0 = (*args)[0]; - int index = arg0->Int32Value(); - - if (index < 0 || index > count()) { - qmlInfo(this) << tr("insert: index %1 out of range").arg(index); - return; - } - - v8::Handle arg1 = (*args)[1]; - - if (arg1->IsArray()) { - v8::Handle objectArray = v8::Handle::Cast(arg1); - int objectArrayLength = objectArray->Length(); - for (int i=0 ; i < objectArrayLength ; ++i) { - v8::Handle argObject = objectArray->Get(i)->ToObject(); - - if (m_dynamicRoles) { - m_modelObjects.insert(index+i, DynamicRoleModelNode::create(args->engine()->variantMapFromJS(argObject), this)); - } else { - m_listModel->insert(index+i, argObject, args->engine()); - } - } - emitItemsInserted(index, objectArrayLength); - } else if (arg1->IsObject()) { - v8::Handle argObject = arg1->ToObject(); - - if (m_dynamicRoles) { - m_modelObjects.insert(index, DynamicRoleModelNode::create(args->engine()->variantMapFromJS(argObject), this)); - } else { - m_listModel->insert(index, argObject, args->engine()); - } - - emitItemsInserted(index, 1); - } else { - qmlInfo(this) << tr("insert: value is not an object"); - } - } else { - qmlInfo(this) << tr("insert: value is not an object"); - } -} - -/*! - \qmlmethod QtQml2::ListModel::move(int from, int to, int n) - - Moves \a n items \a from one position \a to another. - - The from and to ranges must exist; for example, to move the first 3 items - to the end of the list: - - \code - fruitModel.move(0, fruitModel.count - 3, 3) - \endcode - - \sa append() -*/ -void QQmlListModel::move(int from, int to, int n) -{ - if (n==0 || from==to) - return; - if (!canMove(from, to, n)) { - qmlInfo(this) << tr("move: out of range"); - return; - } - - if (m_dynamicRoles) { - - int realFrom = from; - int realTo = to; - int realN = n; - - if (from > to) { - // Only move forwards - flip if backwards moving - int tfrom = from; - int tto = to; - realFrom = tto; - realTo = tto+n; - realN = tfrom-tto; - } - - QPODVector store; - for (int i=0 ; i < (realTo-realFrom) ; ++i) - store.append(m_modelObjects[realFrom+realN+i]); - for (int i=0 ; i < realN ; ++i) - store.append(m_modelObjects[realFrom+i]); - for (int i=0 ; i < store.count() ; ++i) - m_modelObjects[realFrom+i] = store[i]; - - } else { - m_listModel->move(from, to, n); - } - - emitItemsMoved(from, to, n); -} - -/*! - \qmlmethod QtQml2::ListModel::append(jsobject dict) - - Adds a new item to the end of the list model, with the - values in \a dict. - - \code - fruitModel.append({"cost": 5.95, "name":"Pizza"}) - \endcode - - \sa set(), remove() -*/ -void QQmlListModel::append(QQmlV8Function *args) -{ - if (args->Length() == 1) { - v8::Handle arg = (*args)[0]; - - if (arg->IsArray()) { - v8::Handle objectArray = v8::Handle::Cast(arg); - int objectArrayLength = objectArray->Length(); - - int index = count(); - for (int i=0 ; i < objectArrayLength ; ++i) { - v8::Handle argObject = objectArray->Get(i)->ToObject(); - - if (m_dynamicRoles) { - m_modelObjects.append(DynamicRoleModelNode::create(args->engine()->variantMapFromJS(argObject), this)); - } else { - m_listModel->append(argObject, args->engine()); - } - } - - emitItemsInserted(index, objectArrayLength); - } else if (arg->IsObject()) { - v8::Handle argObject = arg->ToObject(); - - int index; - - if (m_dynamicRoles) { - index = m_modelObjects.count(); - m_modelObjects.append(DynamicRoleModelNode::create(args->engine()->variantMapFromJS(argObject), this)); - } else { - index = m_listModel->append(argObject, args->engine()); - } - - emitItemsInserted(index, 1); - } else { - qmlInfo(this) << tr("append: value is not an object"); - } - } else { - qmlInfo(this) << tr("append: value is not an object"); - } -} - -/*! - \qmlmethod object QtQml2::ListModel::get(int index) - - Returns the item at \a index in the list model. This allows the item - data to be accessed or modified from JavaScript: - - \code - Component.onCompleted: { - fruitModel.append({"cost": 5.95, "name":"Jackfruit"}); - console.log(fruitModel.get(0).cost); - fruitModel.get(0).cost = 10.95; - } - \endcode - - The \a index must be an element in the list. - - Note that properties of the returned object that are themselves objects - will also be models, and this get() method is used to access elements: - - \code - fruitModel.append(..., "attributes": - [{"name":"spikes","value":"7mm"}, - {"name":"color","value":"green"}]); - fruitModel.get(0).attributes.get(1).value; // == "green" - \endcode - - \warning The returned object is not guaranteed to remain valid. It - should not be used in \l{Property Binding}{property bindings}. - - \sa append() -*/ -QQmlV8Handle QQmlListModel::get(int index) const -{ - v8::Handle result = v8::Undefined(); - - if (index >= 0 && index < count()) { - QV8Engine *v8engine = engine(); - - if (m_dynamicRoles) { - DynamicRoleModelNode *object = m_modelObjects[index]; - result = v8engine->newQObject(object); - } else { - ModelObject *object = m_listModel->getOrCreateModelObject(const_cast(this), index); - result = v8engine->newQObject(object); - } - } - - return QQmlV8Handle::fromHandle(result); -} - -/*! - \qmlmethod QtQml2::ListModel::set(int index, jsobject dict) - - Changes the item at \a index in the list model with the - values in \a dict. Properties not appearing in \a dict - are left unchanged. - - \code - fruitModel.set(3, {"cost": 5.95, "name":"Pizza"}) - \endcode - - If \a index is equal to count() then a new item is appended to the - list. Otherwise, \a index must be an element in the list. - - \sa append() -*/ -void QQmlListModel::set(int index, const QQmlV8Handle &handle) -{ - v8::Handle valuemap = handle.toHandle(); - - if (!valuemap->IsObject() || valuemap->IsArray()) { - qmlInfo(this) << tr("set: value is not an object"); - return; - } - if (index > count() || index < 0) { - qmlInfo(this) << tr("set: index %1 out of range").arg(index); - return; - } - - v8::Handle object = valuemap->ToObject(); - - if (index == count()) { - - if (m_dynamicRoles) { - m_modelObjects.append(DynamicRoleModelNode::create(engine()->variantMapFromJS(object), this)); - } else { - m_listModel->insert(index, object, engine()); - } - - emitItemsInserted(index, 1); - } else { - - QVector roles; - - if (m_dynamicRoles) { - m_modelObjects[index]->updateValues(engine()->variantMapFromJS(object), roles); - } else { - m_listModel->set(index, object, &roles, engine()); - } - - if (roles.count()) - emitItemsChanged(index, 1, roles); - } -} - -/*! - \qmlmethod QtQml2::ListModel::setProperty(int index, string property, variant value) - - Changes the \a property of the item at \a index in the list model to \a value. - - \code - fruitModel.setProperty(3, "cost", 5.95) - \endcode - - The \a index must be an element in the list. - - \sa append() -*/ -void QQmlListModel::setProperty(int index, const QString& property, const QVariant& value) -{ - if (count() == 0 || index >= count() || index < 0) { - qmlInfo(this) << tr("set: index %1 out of range").arg(index); - return; - } - - if (m_dynamicRoles) { - int roleIndex = m_roles.indexOf(property); - if (roleIndex == -1) { - roleIndex = m_roles.count(); - m_roles.append(property); - } - if (m_modelObjects[index]->setValue(property.toUtf8(), value)) { - QVector roles; - roles << roleIndex; - emitItemsChanged(index, 1, roles); - } - } else { - int roleIndex = m_listModel->setOrCreateProperty(index, property, value); - if (roleIndex != -1) { - - QVector roles; - roles << roleIndex; - - emitItemsChanged(index, 1, roles); - } - } -} - -/*! - \qmlmethod QtQml2::ListModel::sync() - - Writes any unsaved changes to the list model after it has been modified - from a worker script. -*/ -void QQmlListModel::sync() -{ - // This is just a dummy method to make it look like sync() exists in - // ListModel (and not just QQmlListModelWorkerAgent) and to let - // us document sync(). - qmlInfo(this) << "List sync() can only be called from a WorkerScript"; -} - -bool QQmlListModelParser::compileProperty(const QQmlCustomParserProperty &prop, QList &instr, QByteArray &data) -{ - QList values = prop.assignedValues(); - for(int ii = 0; ii < values.count(); ++ii) { - const QVariant &value = values.at(ii); - - if(value.userType() == qMetaTypeId()) { - QQmlCustomParserNode node = - qvariant_cast(value); - - if (node.name() != listElementTypeName) { - const QMetaObject *mo = resolveType(node.name()); - if (mo != &QQmlListElement::staticMetaObject) { - error(node, QQmlListModel::tr("ListElement: cannot contain nested elements")); - return false; - } - listElementTypeName = node.name(); // cache right name for next time - } - - { - ListInstruction li; - li.type = ListInstruction::Push; - li.dataIdx = -1; - instr << li; - } - - QList props = node.properties(); - for(int jj = 0; jj < props.count(); ++jj) { - const QQmlCustomParserProperty &nodeProp = props.at(jj); - if (nodeProp.name().isEmpty()) { - error(nodeProp, QQmlListModel::tr("ListElement: cannot contain nested elements")); - return false; - } - if (nodeProp.name() == QStringLiteral("id")) { - error(nodeProp, QQmlListModel::tr("ListElement: cannot use reserved \"id\" property")); - return false; - } - - ListInstruction li; - int ref = data.count(); - data.append(nodeProp.name().toUtf8()); - data.append('\0'); - li.type = ListInstruction::Set; - li.dataIdx = ref; - instr << li; - - if(!compileProperty(nodeProp, instr, data)) - return false; - - li.type = ListInstruction::Pop; - li.dataIdx = -1; - instr << li; - } - - { - ListInstruction li; - li.type = ListInstruction::Pop; - li.dataIdx = -1; - instr << li; - } - - } else { - - QQmlScript::Variant variant = - qvariant_cast(value); - - int ref = data.count(); - - QByteArray d; - d += char(variant.type()); // type tag - if (variant.isString()) { - d += variant.asString().toUtf8(); - } else if (variant.isNumber()) { - d += QByteArray::number(variant.asNumber(),'g',20); - } else if (variant.isBoolean()) { - d += char(variant.asBoolean()); - } else if (variant.isScript()) { - if (definesEmptyList(variant.asScript())) { - d[0] = char(QQmlScript::Variant::Invalid); // marks empty list - } else { - QByteArray script = variant.asScript().toUtf8(); - bool ok; - int v = evaluateEnum(script, &ok); - if (!ok) { - using namespace QQmlJS; - AST::Node *node = variant.asAST(); - AST::StringLiteral *literal = 0; - if (AST::CallExpression *callExpr = AST::cast(node)) { - if (AST::IdentifierExpression *idExpr = AST::cast(callExpr->base)) { - if (idExpr->name == QLatin1String("QT_TR_NOOP") || idExpr->name == QLatin1String("QT_TRID_NOOP")) { - if (callExpr->arguments && !callExpr->arguments->next) - literal = AST::cast(callExpr->arguments->expression); - if (!literal) { - error(prop, QQmlListModel::tr("ListElement: improperly specified %1").arg(idExpr->name.toString())); - return false; - } - } else if (idExpr->name == QLatin1String("QT_TRANSLATE_NOOP")) { - if (callExpr->arguments && callExpr->arguments->next && !callExpr->arguments->next->next) - literal = AST::cast(callExpr->arguments->next->expression); - if (!literal) { - error(prop, QQmlListModel::tr("ListElement: improperly specified QT_TRANSLATE_NOOP")); - return false; - } - } - } - } - - if (literal) { - d[0] = char(QQmlScript::Variant::String); - d += literal->value.toUtf8(); - } else { - error(prop, QQmlListModel::tr("ListElement: cannot use script for property value")); - return false; - } - } else { - d[0] = char(QQmlScript::Variant::Number); - d += QByteArray::number(v); - } - } - } - d.append('\0'); - data.append(d); - - ListInstruction li; - li.type = ListInstruction::Value; - li.dataIdx = ref; - instr << li; - } - } - - return true; -} - -QByteArray QQmlListModelParser::compile(const QList &customProps) -{ - QList instr; - QByteArray data; - listElementTypeName = QString(); // unknown - - for(int ii = 0; ii < customProps.count(); ++ii) { - const QQmlCustomParserProperty &prop = customProps.at(ii); - if(!prop.name().isEmpty()) { // isn't default property - error(prop, QQmlListModel::tr("ListModel: undefined property '%1'").arg(prop.name())); - return QByteArray(); - } - - if(!compileProperty(prop, instr, data)) { - return QByteArray(); - } - } - - int size = sizeof(ListModelData) + - instr.count() * sizeof(ListInstruction) + - data.count(); - - QByteArray rv; - rv.resize(size); - - ListModelData *lmd = (ListModelData *)rv.data(); - lmd->dataOffset = sizeof(ListModelData) + - instr.count() * sizeof(ListInstruction); - lmd->instrCount = instr.count(); - for (int ii = 0; ii < instr.count(); ++ii) - lmd->instructions()[ii] = instr.at(ii); - ::memcpy(rv.data() + lmd->dataOffset, data.constData(), data.count()); - - return rv; -} - -void QQmlListModelParser::setCustomData(QObject *obj, const QByteArray &d) -{ - QQmlListModel *rv = static_cast(obj); - - QV8Engine *engine = QQmlEnginePrivate::getV8Engine(qmlEngine(rv)); - rv->m_engine = engine; - - const ListModelData *lmd = (const ListModelData *)d.constData(); - const char *data = ((const char *)lmd) + lmd->dataOffset; - - bool setRoles = false; - - QStack stack; - - for (int ii = 0; ii < lmd->instrCount; ++ii) { - const ListInstruction &instr = lmd->instructions()[ii]; - - switch(instr.type) { - case ListInstruction::Push: - { - Q_ASSERT(!rv->m_dynamicRoles); - - ListModel *subModel = 0; - - if (stack.count() == 0) { - subModel = rv->m_listModel; - } else { - const DataStackElement &e0 = stack.at(stack.size() - 1); - DataStackElement &e1 = stack[stack.size() - 2]; - - const ListLayout::Role &role = e1.model->getOrCreateListRole(e0.name); - if (role.type == ListLayout::Role::List) { - subModel = e1.model->getListProperty(e1.elementIndex, role); - - if (subModel == 0) { - subModel = new ListModel(role.subLayout, 0, -1); - QVariant vModel = QVariant::fromValue(subModel); - e1.model->setOrCreateProperty(e1.elementIndex, e0.name, vModel); - } - } - } - - DataStackElement e; - e.model = subModel; - e.elementIndex = subModel ? subModel->appendElement() : -1; - stack.push(e); - } - break; - - case ListInstruction::Pop: - stack.pop(); - break; - - case ListInstruction::Value: - { - const DataStackElement &e0 = stack.at(stack.size() - 1); - DataStackElement &e1 = stack[stack.size() - 2]; - - QString name = e0.name; - QVariant value; - - switch (QQmlScript::Variant::Type(data[instr.dataIdx])) { - case QQmlScript::Variant::Invalid: - { - const ListLayout::Role &role = e1.model->getOrCreateListRole(e0.name); - ListModel *emptyModel = new ListModel(role.subLayout, 0, -1); - value = QVariant::fromValue(emptyModel); - } - break; - case QQmlScript::Variant::Boolean: - value = bool(data[1 + instr.dataIdx]); - break; - case QQmlScript::Variant::Number: - value = QByteArray(data + 1 + instr.dataIdx).toDouble(); - break; - case QQmlScript::Variant::String: - value = QString::fromUtf8(data + 1 + instr.dataIdx); - break; - default: - Q_ASSERT("Format error in ListInstruction"); - } - - e1.model->setOrCreateProperty(e1.elementIndex, name, value); - setRoles = true; - } - break; - - case ListInstruction::Set: - { - DataStackElement e; - e.name = QString::fromUtf8(data + instr.dataIdx); - stack.push(e); - } - break; - } - } - - if (setRoles == false) - qmlInfo(obj) << "All ListElement declarations are empty, no roles can be created unless dynamicRoles is set."; -} - -bool QQmlListModelParser::definesEmptyList(const QString &s) -{ - if (s.startsWith(QLatin1Char('[')) && s.endsWith(QLatin1Char(']'))) { - for (int i=1; i -#include - -#include -#include -#include -#include -#include -#include - -#include -#include - -QT_BEGIN_NAMESPACE - - -class QQmlListModelWorkerAgent; -class ListModel; -class ListLayout; - -class Q_QML_PRIVATE_EXPORT QQmlListModel : public QAbstractListModel -{ - Q_OBJECT - Q_PROPERTY(int count READ count NOTIFY countChanged) - Q_PROPERTY(bool dynamicRoles READ dynamicRoles WRITE setDynamicRoles) - -public: - QQmlListModel(QObject *parent=0); - ~QQmlListModel(); - - QModelIndex index(int row, int column, const QModelIndex &parent) const; - int rowCount(const QModelIndex &parent) const; - QVariant data(const QModelIndex &index, int role) const; - QHash roleNames() const; - - QVariant data(int index, int role) const; - int count() const; - - Q_INVOKABLE void clear(); - Q_INVOKABLE void remove(QQmlV8Function *args); - Q_INVOKABLE void append(QQmlV8Function *args); - Q_INVOKABLE void insert(QQmlV8Function *args); - Q_INVOKABLE QQmlV8Handle get(int index) const; - Q_INVOKABLE void set(int index, const QQmlV8Handle &); - Q_INVOKABLE void setProperty(int index, const QString& property, const QVariant& value); - Q_INVOKABLE void move(int from, int to, int count); - Q_INVOKABLE void sync(); - - QQmlListModelWorkerAgent *agent(); - - bool dynamicRoles() const { return m_dynamicRoles; } - void setDynamicRoles(bool enableDynamicRoles); - -Q_SIGNALS: - void countChanged(); - -private: - friend class QQmlListModelParser; - friend class QQmlListModelWorkerAgent; - friend class ModelObject; - friend class ModelNodeMetaObject; - friend class ListModel; - friend class ListElement; - friend class DynamicRoleModelNode; - friend class DynamicRoleModelNodeMetaObject; - - // Constructs a flat list model for a worker agent - QQmlListModel(QQmlListModel *orig, QQmlListModelWorkerAgent *agent); - QQmlListModel(const QQmlListModel *owner, ListModel *data, QV8Engine *eng, QObject *parent=0); - - QV8Engine *engine() const; - - inline bool canMove(int from, int to, int n) const { return !(from+n > count() || to+n > count() || from < 0 || to < 0 || n < 0); } - - QQmlListModelWorkerAgent *m_agent; - mutable QV8Engine *m_engine; - bool m_mainThread; - bool m_primary; - - bool m_dynamicRoles; - - ListLayout *m_layout; - ListModel *m_listModel; - - QVector m_modelObjects; - QVector m_roles; - int m_uid; - - struct ElementSync - { - ElementSync() : src(0), target(0) {} - - DynamicRoleModelNode *src; - DynamicRoleModelNode *target; - }; - - int getUid() const { return m_uid; } - - static void sync(QQmlListModel *src, QQmlListModel *target, QHash *targetModelHash); - static QQmlListModel *createWithOwner(QQmlListModel *newOwner); - - void emitItemsChanged(int index, int count, const QVector &roles); - void emitItemsRemoved(int index, int count); - void emitItemsInserted(int index, int count); - void emitItemsMoved(int from, int to, int n); -}; - -// ### FIXME -class QQmlListElement : public QObject -{ -Q_OBJECT -}; - -class QQmlListModelParser : public QQmlCustomParser -{ -public: - QQmlListModelParser() : QQmlCustomParser(QQmlCustomParser::AcceptsSignalHandlers) {} - QByteArray compile(const QList &); - void setCustomData(QObject *, const QByteArray &); - -private: - struct ListInstruction - { - enum { Push, Pop, Value, Set } type; - int dataIdx; - }; - struct ListModelData - { - int dataOffset; - int instrCount; - ListInstruction *instructions() const; - }; - bool compileProperty(const QQmlCustomParserProperty &prop, QList &instr, QByteArray &data); - - bool definesEmptyList(const QString &); - - QString listElementTypeName; - - struct DataStackElement - { - DataStackElement() : model(0), elementIndex(0) {} - - QString name; - ListModel *model; - int elementIndex; - }; -}; - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QQmlListModel) -QML_DECLARE_TYPE(QQmlListElement) - -#endif // QQMLLISTMODEL_H diff --git a/src/qml/qml/qqmllistmodel_p_p.h b/src/qml/qml/qqmllistmodel_p_p.h deleted file mode 100644 index f2720c6c39..0000000000 --- a/src/qml/qml/qqmllistmodel_p_p.h +++ /dev/null @@ -1,378 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** 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, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQMLLISTMODEL_P_P_H -#define QQMLLISTMODEL_P_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "qqmllistmodel_p.h" -#include -#include "qqmlopenmetaobject_p.h" -#include - -QT_BEGIN_NAMESPACE - - -class DynamicRoleModelNode; - -class DynamicRoleModelNodeMetaObject : public QQmlOpenMetaObject -{ -public: - DynamicRoleModelNodeMetaObject(DynamicRoleModelNode *object); - ~DynamicRoleModelNodeMetaObject(); - - bool m_enabled; - -protected: - void propertyWrite(int index); - void propertyWritten(int index); - -private: - DynamicRoleModelNode *m_owner; -}; - -class DynamicRoleModelNode : public QObject -{ - Q_OBJECT -public: - DynamicRoleModelNode(QQmlListModel *owner, int uid); - - static DynamicRoleModelNode *create(const QVariantMap &obj, QQmlListModel *owner); - - void updateValues(const QVariantMap &object, QVector &roles); - - QVariant getValue(const QString &name) - { - return m_meta->value(name.toUtf8()); - } - - bool setValue(const QByteArray &name, const QVariant &val) - { - return m_meta->setValue(name, val); - } - - void setNodeUpdatesEnabled(bool enable) - { - m_meta->m_enabled = enable; - } - - int getUid() const - { - return m_uid; - } - - static void sync(DynamicRoleModelNode *src, DynamicRoleModelNode *target, QHash *targetModelHash); - -private: - QQmlListModel *m_owner; - int m_uid; - DynamicRoleModelNodeMetaObject *m_meta; - - friend class DynamicRoleModelNodeMetaObject; -}; - -class ModelObject; - -class ModelNodeMetaObject : public QQmlOpenMetaObject -{ -public: - ModelNodeMetaObject(ModelObject *object); - ~ModelNodeMetaObject(); - - bool m_enabled; - -protected: - void propertyWritten(int index); - -private: - - ModelObject *m_obj; -}; - -class ModelObject : public QObject -{ - Q_OBJECT -public: - ModelObject(QQmlListModel *model, int elementIndex); - - void setValue(const QByteArray &name, const QVariant &val, bool force) - { - if (force) { - QVariant existingValue = m_meta->value(name); - if (existingValue.isValid()) { - (*m_meta)[name] = QVariant(); - } - } - m_meta->setValue(name, val); - } - - void setNodeUpdatesEnabled(bool enable) - { - m_meta->m_enabled = enable; - } - - void updateValues(); - void updateValues(const QVector &roles); - - QQmlListModel *m_model; - int m_elementIndex; - -private: - ModelNodeMetaObject *m_meta; -}; - -class ListLayout -{ -public: - ListLayout() : currentBlock(0), currentBlockOffset(0) {} - ListLayout(const ListLayout *other); - ~ListLayout(); - - class Role - { - public: - - Role() : type(Invalid), blockIndex(-1), blockOffset(-1), index(-1), subLayout(0) {} - explicit Role(const Role *other); - ~Role(); - - // This enum must be kept in sync with the roleTypeNames variable in qdeclarativelistmodel.cpp - enum DataType - { - Invalid = -1, - - String, - Number, - Bool, - List, - QObject, - VariantMap, - DateTime, - - MaxDataType - }; - - QString name; - DataType type; - int blockIndex; - int blockOffset; - int index; - ListLayout *subLayout; - }; - - const Role *getRoleOrCreate(const QString &key, const QVariant &data); - const Role &getRoleOrCreate(v8::Handle key, Role::DataType type); - const Role &getRoleOrCreate(const QString &key, Role::DataType type); - - const Role &getExistingRole(int index) { return *roles.at(index); } - const Role *getExistingRole(const QString &key); - const Role *getExistingRole(v8::Handle key); - - int roleCount() const { return roles.count(); } - - static void sync(ListLayout *src, ListLayout *target); - -private: - const Role &createRole(const QString &key, Role::DataType type); - - int currentBlock; - int currentBlockOffset; - QVector roles; - QStringHash roleHash; -}; - -class ListElement -{ -public: - - ListElement(); - ListElement(int existingUid); - ~ListElement(); - - static void sync(ListElement *src, ListLayout *srcLayout, ListElement *target, ListLayout *targetLayout, QHash *targetModelHash); - - enum - { - BLOCK_SIZE = 64 - sizeof(int) - sizeof(ListElement *) - sizeof(ModelObject *) - }; - -private: - - void destroy(ListLayout *layout); - - int setVariantProperty(const ListLayout::Role &role, const QVariant &d); - - int setJsProperty(const ListLayout::Role &role, v8::Handle d, QV8Engine *eng); - - int setStringProperty(const ListLayout::Role &role, const QString &s); - int setDoubleProperty(const ListLayout::Role &role, double n); - int setBoolProperty(const ListLayout::Role &role, bool b); - int setListProperty(const ListLayout::Role &role, ListModel *m); - int setQObjectProperty(const ListLayout::Role &role, QObject *o); - int setVariantMapProperty(const ListLayout::Role &role, v8::Handle o, QV8Engine *eng); - int setVariantMapProperty(const ListLayout::Role &role, QVariantMap *m); - int setDateTimeProperty(const ListLayout::Role &role, const QDateTime &dt); - - void setStringPropertyFast(const ListLayout::Role &role, const QString &s); - void setDoublePropertyFast(const ListLayout::Role &role, double n); - void setBoolPropertyFast(const ListLayout::Role &role, bool b); - void setQObjectPropertyFast(const ListLayout::Role &role, QObject *o); - void setListPropertyFast(const ListLayout::Role &role, ListModel *m); - void setVariantMapFast(const ListLayout::Role &role, v8::Handle o, QV8Engine *eng); - void setDateTimePropertyFast(const ListLayout::Role &role, const QDateTime &dt); - - void clearProperty(const ListLayout::Role &role); - - QVariant getProperty(const ListLayout::Role &role, const QQmlListModel *owner, QV8Engine *eng); - ListModel *getListProperty(const ListLayout::Role &role); - QString *getStringProperty(const ListLayout::Role &role); - QObject *getQObjectProperty(const ListLayout::Role &role); - QQmlGuard *getGuardProperty(const ListLayout::Role &role); - QVariantMap *getVariantMapProperty(const ListLayout::Role &role); - QDateTime *getDateTimeProperty(const ListLayout::Role &role); - - inline char *getPropertyMemory(const ListLayout::Role &role); - - int getUid() const { return uid; } - - char data[BLOCK_SIZE]; - ListElement *next; - - int uid; - ModelObject *m_objectCache; - - friend class ListModel; -}; - -class ListModel -{ -public: - - ListModel(ListLayout *layout, QQmlListModel *modelCache, int uid); - ~ListModel() {} - - void destroy(); - - int setOrCreateProperty(int elementIndex, const QString &key, const QVariant &data); - int setExistingProperty(int uid, const QString &key, v8::Handle data, QV8Engine *eng); - - QVariant getProperty(int elementIndex, int roleIndex, const QQmlListModel *owner, QV8Engine *eng); - ListModel *getListProperty(int elementIndex, const ListLayout::Role &role); - - int roleCount() const - { - return m_layout->roleCount(); - } - - const ListLayout::Role &getExistingRole(int index) - { - return m_layout->getExistingRole(index); - } - - const ListLayout::Role &getOrCreateListRole(const QString &name) - { - return m_layout->getRoleOrCreate(name, ListLayout::Role::List); - } - - int elementCount() const - { - return elements.count(); - } - - void set(int elementIndex, v8::Handle object, QVector *roles, QV8Engine *eng); - void set(int elementIndex, v8::Handle object, QV8Engine *eng); - - int append(v8::Handle object, QV8Engine *eng); - void insert(int elementIndex, v8::Handle object, QV8Engine *eng); - - void clear(); - void remove(int index, int count); - - int appendElement(); - void insertElement(int index); - - void move(int from, int to, int n); - - int getUid() const { return m_uid; } - - static void sync(ListModel *src, ListModel *target, QHash *srcModelHash); - - ModelObject *getOrCreateModelObject(QQmlListModel *model, int elementIndex); - -private: - QPODVector elements; - ListLayout *m_layout; - int m_uid; - - QQmlListModel *m_modelCache; - - struct ElementSync - { - ElementSync() : src(0), target(0) {} - - ListElement *src; - ListElement *target; - }; - - void newElement(int index); - - void updateCacheIndices(); - - friend class ListElement; - friend class QQmlListModelWorkerAgent; -}; - -QT_END_NAMESPACE - -Q_DECLARE_METATYPE(ListModel *); - -#endif // QQUICKLISTMODEL_P_P_H - diff --git a/src/qml/qml/qqmllistmodelworkeragent.cpp b/src/qml/qml/qqmllistmodelworkeragent.cpp deleted file mode 100644 index 9554e6d1e5..0000000000 --- a/src/qml/qml/qqmllistmodelworkeragent.cpp +++ /dev/null @@ -1,259 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** 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, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qqmllistmodelworkeragent_p.h" -#include "qqmllistmodel_p_p.h" -#include -#include -#include - -#include -#include -#include - - -QT_BEGIN_NAMESPACE - - -void QQmlListModelWorkerAgent::Data::clearChange(int uid) -{ - for (int i=0 ; i < changes.count() ; ++i) { - if (changes[i].modelUid == uid) { - changes.removeAt(i); - --i; - } - } -} - -void QQmlListModelWorkerAgent::Data::insertChange(int uid, int index, int count) -{ - Change c = { uid, Change::Inserted, index, count, 0, QVector() }; - changes << c; -} - -void QQmlListModelWorkerAgent::Data::removeChange(int uid, int index, int count) -{ - Change c = { uid, Change::Removed, index, count, 0, QVector() }; - changes << c; -} - -void QQmlListModelWorkerAgent::Data::moveChange(int uid, int index, int count, int to) -{ - Change c = { uid, Change::Moved, index, count, to, QVector() }; - changes << c; -} - -void QQmlListModelWorkerAgent::Data::changedChange(int uid, int index, int count, const QVector &roles) -{ - Change c = { uid, Change::Changed, index, count, 0, roles }; - changes << c; -} - -QQmlListModelWorkerAgent::QQmlListModelWorkerAgent(QQmlListModel *model) -: m_ref(1), m_orig(model), m_copy(new QQmlListModel(model, this)) -{ -} - -QQmlListModelWorkerAgent::~QQmlListModelWorkerAgent() -{ - mutex.lock(); - syncDone.wakeAll(); - mutex.unlock(); -} - -void QQmlListModelWorkerAgent::setV8Engine(QV8Engine *eng) -{ - m_copy->m_engine = eng; -} - -void QQmlListModelWorkerAgent::addref() -{ - m_ref.ref(); -} - -void QQmlListModelWorkerAgent::release() -{ - bool del = !m_ref.deref(); - - if (del) - deleteLater(); -} - -void QQmlListModelWorkerAgent::modelDestroyed() -{ - m_orig = 0; -} - -int QQmlListModelWorkerAgent::count() const -{ - return m_copy->count(); -} - -void QQmlListModelWorkerAgent::clear() -{ - m_copy->clear(); -} - -void QQmlListModelWorkerAgent::remove(QQmlV8Function *args) -{ - m_copy->remove(args); -} - -void QQmlListModelWorkerAgent::append(QQmlV8Function *args) -{ - m_copy->append(args); -} - -void QQmlListModelWorkerAgent::insert(QQmlV8Function *args) -{ - m_copy->insert(args); -} - -QQmlV8Handle QQmlListModelWorkerAgent::get(int index) const -{ - return m_copy->get(index); -} - -void QQmlListModelWorkerAgent::set(int index, const QQmlV8Handle &value) -{ - m_copy->set(index, value); -} - -void QQmlListModelWorkerAgent::setProperty(int index, const QString& property, const QVariant& value) -{ - m_copy->setProperty(index, property, value); -} - -void QQmlListModelWorkerAgent::move(int from, int to, int count) -{ - m_copy->move(from, to, count); -} - -void QQmlListModelWorkerAgent::sync() -{ - Sync *s = new Sync; - s->data = data; - s->list = m_copy; - data.changes.clear(); - - mutex.lock(); - QCoreApplication::postEvent(this, s); - syncDone.wait(&mutex); - mutex.unlock(); -} - -bool QQmlListModelWorkerAgent::event(QEvent *e) -{ - if (e->type() == QEvent::User) { - bool cc = false; - QMutexLocker locker(&mutex); - if (m_orig) { - Sync *s = static_cast(e); - const QList &changes = s->data.changes; - - cc = m_orig->count() != s->list->count(); - - QHash targetModelDynamicHash; - QHash targetModelStaticHash; - - Q_ASSERT(m_orig->m_dynamicRoles == s->list->m_dynamicRoles); - if (m_orig->m_dynamicRoles) - QQmlListModel::sync(s->list, m_orig, &targetModelDynamicHash); - else - ListModel::sync(s->list->m_listModel, m_orig->m_listModel, &targetModelStaticHash); - - for (int ii = 0; ii < changes.count(); ++ii) { - const Change &change = changes.at(ii); - - QQmlListModel *model = 0; - if (m_orig->m_dynamicRoles) { - model = targetModelDynamicHash.value(change.modelUid); - } else { - ListModel *lm = targetModelStaticHash.value(change.modelUid); - if (lm) - model = lm->m_modelCache; - } - - if (model) { - switch (change.type) { - case Change::Inserted: - model->beginInsertRows( - QModelIndex(), change.index, change.index + change.count - 1); - model->endInsertRows(); - break; - case Change::Removed: - model->beginRemoveRows( - QModelIndex(), change.index, change.index + change.count - 1); - model->endRemoveRows(); - break; - case Change::Moved: - model->beginMoveRows( - QModelIndex(), - change.index, - change.index + change.count - 1, - QModelIndex(), - change.to > change.index ? change.to + change.count : change.to); - model->endMoveRows(); - break; - case Change::Changed: - emit model->dataChanged( - model->createIndex(change.index, 0), - model->createIndex(change.index + change.count - 1, 0), - change.roles); - break; - } - } - } - } - - syncDone.wakeAll(); - locker.unlock(); - - if (cc) - emit m_orig->countChanged(); - return true; - } - - return QObject::event(e); -} - -QT_END_NAMESPACE - diff --git a/src/qml/qml/qqmllistmodelworkeragent_p.h b/src/qml/qml/qqmllistmodelworkeragent_p.h deleted file mode 100644 index 614017069c..0000000000 --- a/src/qml/qml/qqmllistmodelworkeragent_p.h +++ /dev/null @@ -1,156 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** 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, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQUICKLISTMODELWORKERAGENT_P_H -#define QQUICKLISTMODELWORKERAGENT_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include - -#include -#include - -#include - -QT_BEGIN_NAMESPACE - - -class QQmlListModel; - -class QQmlListModelWorkerAgent : public QObject -{ - Q_OBJECT - Q_PROPERTY(int count READ count) - -public: - QQmlListModelWorkerAgent(QQmlListModel *); - ~QQmlListModelWorkerAgent(); - void setV8Engine(QV8Engine *eng); - - void addref(); - void release(); - - int count() const; - - Q_INVOKABLE void clear(); - Q_INVOKABLE void remove(QQmlV8Function *args); - Q_INVOKABLE void append(QQmlV8Function *args); - Q_INVOKABLE void insert(QQmlV8Function *args); - Q_INVOKABLE QQmlV8Handle get(int index) const; - Q_INVOKABLE void set(int index, const QQmlV8Handle &); - Q_INVOKABLE void setProperty(int index, const QString& property, const QVariant& value); - Q_INVOKABLE void move(int from, int to, int count); - Q_INVOKABLE void sync(); - - struct VariantRef - { - VariantRef() : a(0) {} - VariantRef(const VariantRef &r) : a(r.a) { if (a) a->addref(); } - VariantRef(QQmlListModelWorkerAgent *_a) : a(_a) { if (a) a->addref(); } - ~VariantRef() { if (a) a->release(); } - - VariantRef &operator=(const VariantRef &o) { - if (o.a) o.a->addref(); - if (a) a->release(); a = o.a; - return *this; - } - - QQmlListModelWorkerAgent *a; - }; - void modelDestroyed(); -protected: - virtual bool event(QEvent *); - -private: - friend class QQuickWorkerScriptEnginePrivate; - friend class QQmlListModel; - - struct Change - { - int modelUid; - enum { Inserted, Removed, Moved, Changed } type; - int index; // Inserted/Removed/Moved/Changed - int count; // Inserted/Removed/Moved/Changed - int to; // Moved - QVector roles; - }; - - struct Data - { - QList changes; - - void clearChange(int uid); - void insertChange(int uid, int index, int count); - void removeChange(int uid, int index, int count); - void moveChange(int uid, int index, int count, int to); - void changedChange(int uid, int index, int count, const QVector &roles); - }; - Data data; - - struct Sync : public QEvent { - Sync() : QEvent(QEvent::User) {} - Data data; - QQmlListModel *list; - }; - - QAtomicInt m_ref; - QQmlListModel *m_orig; - QQmlListModel *m_copy; - QMutex mutex; - QWaitCondition syncDone; -}; - -QT_END_NAMESPACE - -Q_DECLARE_METATYPE(QQmlListModelWorkerAgent::VariantRef) - -#endif // QQUICKLISTMODELWORKERAGENT_P_H - diff --git a/src/qml/qml/qqmltimer.cpp b/src/qml/qml/qqmltimer.cpp deleted file mode 100644 index a1cb8532f7..0000000000 --- a/src/qml/qml/qqmltimer.cpp +++ /dev/null @@ -1,328 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** 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, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qqmltimer_p.h" - -#include -#include "private/qpauseanimationjob_p.h" -#include - -#include - -QT_BEGIN_NAMESPACE - - - -class QQmlTimerPrivate : public QObjectPrivate, public QAnimationJobChangeListener -{ - Q_DECLARE_PUBLIC(QQmlTimer) -public: - QQmlTimerPrivate() - : interval(1000), running(false), repeating(false), triggeredOnStart(false) - , classBegun(false), componentComplete(false), firstTick(true) {} - - virtual void animationFinished(QAbstractAnimationJob *); - virtual void animationCurrentLoopChanged(QAbstractAnimationJob *) { Q_Q(QQmlTimer); q->ticked(); } - - int interval; - QPauseAnimationJob pause; - bool running : 1; - bool repeating : 1; - bool triggeredOnStart : 1; - bool classBegun : 1; - bool componentComplete : 1; - bool firstTick : 1; -}; - -/*! - \qmltype Timer - \instantiates QQmlTimer - \inqmlmodule QtQml 2 - \ingroup qtquick-interceptors - \brief Triggers a handler at a specified interval - - A Timer can be used to trigger an action either once, or repeatedly - at a given interval. - - Here is a Timer that shows the current date and time, and updates - the text every 500 milliseconds. It uses the JavaScript \c Date - object to access the current time. - - \qml - import QtQuick 2.0 - - Item { - Timer { - interval: 500; running: true; repeat: true - onTriggered: time.text = Date().toString() - } - - Text { id: time } - } - \endqml - - The Timer type is synchronized with the animation timer. Since the animation - timer is usually set to 60fps, the resolution of Timer will be - at best 16ms. - - If the Timer is running and one of its properties is changed, the - elapsed time will be reset. For example, if a Timer with interval of - 1000ms has its \e repeat property changed 500ms after starting, the - elapsed time will be reset to 0, and the Timer will be triggered - 1000ms later. - - \sa {declarative/toys/clocks}{Clocks example} -*/ - -QQmlTimer::QQmlTimer(QObject *parent) - : QObject(*(new QQmlTimerPrivate), parent) -{ - Q_D(QQmlTimer); - d->pause.addAnimationChangeListener(d, QAbstractAnimationJob::Completion | QAbstractAnimationJob::CurrentLoop); - d->pause.setLoopCount(1); - d->pause.setDuration(d->interval); -} - -/*! - \qmlproperty int QtQml2::Timer::interval - - Sets the \a interval between triggers, in milliseconds. - - The default interval is 1000 milliseconds. -*/ -void QQmlTimer::setInterval(int interval) -{ - Q_D(QQmlTimer); - if (interval != d->interval) { - d->interval = interval; - update(); - emit intervalChanged(); - } -} - -int QQmlTimer::interval() const -{ - Q_D(const QQmlTimer); - return d->interval; -} - -/*! - \qmlproperty bool QtQml2::Timer::running - - If set to true, starts the timer; otherwise stops the timer. - For a non-repeating timer, \a running is set to false after the - timer has been triggered. - - \a running defaults to false. - - \sa repeat -*/ -bool QQmlTimer::isRunning() const -{ - Q_D(const QQmlTimer); - return d->running; -} - -void QQmlTimer::setRunning(bool running) -{ - Q_D(QQmlTimer); - if (d->running != running) { - d->running = running; - d->firstTick = true; - emit runningChanged(); - update(); - } -} - -/*! - \qmlproperty bool QtQml2::Timer::repeat - - If \a repeat is true the timer is triggered repeatedly at the - specified interval; otherwise, the timer will trigger once at the - specified interval and then stop (i.e. running will be set to false). - - \a repeat defaults to false. - - \sa running -*/ -bool QQmlTimer::isRepeating() const -{ - Q_D(const QQmlTimer); - return d->repeating; -} - -void QQmlTimer::setRepeating(bool repeating) -{ - Q_D(QQmlTimer); - if (repeating != d->repeating) { - d->repeating = repeating; - update(); - emit repeatChanged(); - } -} - -/*! - \qmlproperty bool QtQml2::Timer::triggeredOnStart - - When a timer is started, the first trigger is usually after the specified - interval has elapsed. It is sometimes desirable to trigger immediately - when the timer is started; for example, to establish an initial - state. - - If \a triggeredOnStart is true, the timer is triggered immediately - when started, and subsequently at the specified interval. Note that if - \e repeat is set to false, the timer is triggered twice; once on start, - and again at the interval. - - \a triggeredOnStart defaults to false. - - \sa running -*/ -bool QQmlTimer::triggeredOnStart() const -{ - Q_D(const QQmlTimer); - return d->triggeredOnStart; -} - -void QQmlTimer::setTriggeredOnStart(bool triggeredOnStart) -{ - Q_D(QQmlTimer); - if (d->triggeredOnStart != triggeredOnStart) { - d->triggeredOnStart = triggeredOnStart; - update(); - emit triggeredOnStartChanged(); - } -} - -/*! - \qmlmethod QtQml2::Timer::start() - \brief Starts the timer - - If the timer is already running, calling this method has no effect. The - \c running property will be true following a call to \c start(). -*/ -void QQmlTimer::start() -{ - setRunning(true); -} - -/*! - \qmlmethod QtQml2::Timer::stop() - \brief Stops the timer - - If the timer is not running, calling this method has no effect. The - \c running property will be false following a call to \c stop(). -*/ -void QQmlTimer::stop() -{ - setRunning(false); -} - -/*! - \qmlmethod QtQml2::Timer::restart() - \brief Restarts the timer - - If the Timer is not running it will be started, otherwise it will be - stopped, reset to initial state and started. The \c running property - will be true following a call to \c restart(). -*/ -void QQmlTimer::restart() -{ - setRunning(false); - setRunning(true); -} - -void QQmlTimer::update() -{ - Q_D(QQmlTimer); - if (d->classBegun && !d->componentComplete) - return; - d->pause.stop(); - if (d->running) { - d->pause.setCurrentTime(0); - d->pause.setLoopCount(d->repeating ? -1 : 1); - d->pause.setDuration(d->interval); - d->pause.start(); - if (d->triggeredOnStart && d->firstTick) { - QCoreApplication::removePostedEvents(this, QEvent::MetaCall); - QMetaObject::invokeMethod(this, "ticked", Qt::QueuedConnection); - } - } -} - -void QQmlTimer::classBegin() -{ - Q_D(QQmlTimer); - d->classBegun = true; -} - -void QQmlTimer::componentComplete() -{ - Q_D(QQmlTimer); - d->componentComplete = true; - update(); -} - -/*! - \qmlsignal QtQml2::Timer::onTriggered() - - This handler is called when the Timer is triggered. -*/ -void QQmlTimer::ticked() -{ - Q_D(QQmlTimer); - if (d->running && (d->pause.currentTime() > 0 || (d->triggeredOnStart && d->firstTick))) - emit triggered(); - d->firstTick = false; -} - -void QQmlTimerPrivate::animationFinished(QAbstractAnimationJob *) -{ - Q_Q(QQmlTimer); - if (repeating || !running) - return; - running = false; - firstTick = false; - emit q->triggered(); - emit q->runningChanged(); -} - -QT_END_NAMESPACE diff --git a/src/qml/qml/qqmltimer_p.h b/src/qml/qml/qqmltimer_p.h deleted file mode 100644 index c625522851..0000000000 --- a/src/qml/qml/qqmltimer_p.h +++ /dev/null @@ -1,107 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** 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, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQMLTIMER_H -#define QQMLTIMER_H - -#include - -#include - -#include - -QT_BEGIN_NAMESPACE - -class QQmlTimerPrivate; -class Q_QML_PRIVATE_EXPORT QQmlTimer : public QObject, public QQmlParserStatus -{ - Q_OBJECT - Q_DECLARE_PRIVATE(QQmlTimer) - Q_INTERFACES(QQmlParserStatus) - Q_PROPERTY(int interval READ interval WRITE setInterval NOTIFY intervalChanged) - Q_PROPERTY(bool running READ isRunning WRITE setRunning NOTIFY runningChanged) - Q_PROPERTY(bool repeat READ isRepeating WRITE setRepeating NOTIFY repeatChanged) - Q_PROPERTY(bool triggeredOnStart READ triggeredOnStart WRITE setTriggeredOnStart NOTIFY triggeredOnStartChanged) - Q_PROPERTY(QObject *parent READ parent CONSTANT) - -public: - QQmlTimer(QObject *parent=0); - - void setInterval(int interval); - int interval() const; - - bool isRunning() const; - void setRunning(bool running); - - bool isRepeating() const; - void setRepeating(bool repeating); - - bool triggeredOnStart() const; - void setTriggeredOnStart(bool triggeredOnStart); - -protected: - void classBegin(); - void componentComplete(); - -public Q_SLOTS: - void start(); - void stop(); - void restart(); - -Q_SIGNALS: - void triggered(); - void runningChanged(); - void intervalChanged(); - void repeatChanged(); - void triggeredOnStartChanged(); - -private: - void update(); - -private Q_SLOTS: - void ticked(); -}; - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QQmlTimer) - -#endif diff --git a/src/qml/qml/qquickworkerscript.cpp b/src/qml/qml/qquickworkerscript.cpp deleted file mode 100644 index b9b027f6ad..0000000000 --- a/src/qml/qml/qquickworkerscript.cpp +++ /dev/null @@ -1,740 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** 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, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qquickworkerscript_p.h" -#include "qqmllistmodel_p.h" -#include "qqmllistmodelworkeragent_p.h" -#include "qqmlengine_p.h" -#include "qqmlexpression_p.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "qqmlnetworkaccessmanagerfactory.h" - -#include -#include - -QT_BEGIN_NAMESPACE - -class WorkerDataEvent : public QEvent -{ -public: - enum Type { WorkerData = QEvent::User }; - - WorkerDataEvent(int workerId, const QByteArray &data); - virtual ~WorkerDataEvent(); - - int workerId() const; - QByteArray data() const; - -private: - int m_id; - QByteArray m_data; -}; - -class WorkerLoadEvent : public QEvent -{ -public: - enum Type { WorkerLoad = WorkerDataEvent::WorkerData + 1 }; - - WorkerLoadEvent(int workerId, const QUrl &url); - - int workerId() const; - QUrl url() const; - -private: - int m_id; - QUrl m_url; -}; - -class WorkerRemoveEvent : public QEvent -{ -public: - enum Type { WorkerRemove = WorkerLoadEvent::WorkerLoad + 1 }; - - WorkerRemoveEvent(int workerId); - - int workerId() const; - -private: - int m_id; -}; - -class WorkerErrorEvent : public QEvent -{ -public: - enum Type { WorkerError = WorkerRemoveEvent::WorkerRemove + 1 }; - - WorkerErrorEvent(const QQmlError &error); - - QQmlError error() const; - -private: - QQmlError m_error; -}; - -class QQuickWorkerScriptEnginePrivate : public QObject -{ - Q_OBJECT -public: - enum WorkerEventTypes { - WorkerDestroyEvent = QEvent::User + 100 - }; - - QQuickWorkerScriptEnginePrivate(QQmlEngine *eng); - - class WorkerEngine : public QV8Engine - { - public: - WorkerEngine(QQuickWorkerScriptEnginePrivate *parent); - ~WorkerEngine(); - - void init(); - virtual QNetworkAccessManager *networkAccessManager(); - - QQuickWorkerScriptEnginePrivate *p; - - v8::Local sendFunction(int id); - void callOnMessage(v8::Handle object, v8::Handle arg); - private: - v8::Persistent onmessage; - v8::Persistent createsend; - QNetworkAccessManager *accessManager; - }; - - WorkerEngine *workerEngine; - static QQuickWorkerScriptEnginePrivate *get(QV8Engine *e) { - return static_cast(e)->p; - } - - QQmlEngine *qmlengine; - - QMutex m_lock; - QWaitCondition m_wait; - - struct WorkerScript { - WorkerScript(); - ~WorkerScript(); - - int id; - QUrl source; - bool initialized; - QQuickWorkerScript *owner; - v8::Persistent object; - }; - - QHash workers; - v8::Handle getWorker(WorkerScript *); - - int m_nextId; - - static v8::Handle sendMessage(const v8::Arguments &args); - -signals: - void stopThread(); - -protected: - virtual bool event(QEvent *); - -private: - void processMessage(int, const QByteArray &); - void processLoad(int, const QUrl &); - void reportScriptException(WorkerScript *, const QQmlError &error); -}; - -QQuickWorkerScriptEnginePrivate::WorkerEngine::WorkerEngine(QQuickWorkerScriptEnginePrivate *parent) -: QV8Engine(0), p(parent), accessManager(0) -{ -} - -QQuickWorkerScriptEnginePrivate::WorkerEngine::~WorkerEngine() -{ - qPersistentDispose(createsend); - qPersistentDispose(onmessage); - delete accessManager; -} - -void QQuickWorkerScriptEnginePrivate::WorkerEngine::init() -{ - initQmlGlobalObject(); -#define CALL_ONMESSAGE_SCRIPT \ - "(function(object, message) { "\ - "var isfunction = false; "\ - "try { "\ - "isfunction = object.WorkerScript.onMessage instanceof Function; "\ - "} catch (e) {}" \ - "if (isfunction) "\ - "object.WorkerScript.onMessage(message); "\ - "})" - -#define SEND_MESSAGE_CREATE_SCRIPT \ - "(function(method, engine) { "\ - "return (function(id) { "\ - "return (function(message) { "\ - "if (arguments.length) method(engine, id, message); "\ - "}); "\ - "}); "\ - "})" - - v8::HandleScope handle_scope; - v8::Context::Scope scope(context()); - - { - v8::Local onmessagescript = v8::Script::New(v8::String::New(CALL_ONMESSAGE_SCRIPT)); - onmessage = qPersistentNew(v8::Handle::Cast(onmessagescript->Run())); - } - { - v8::Local createsendscript = v8::Script::New(v8::String::New(SEND_MESSAGE_CREATE_SCRIPT)); - v8::Local createsendconstructor = v8::Local::Cast(createsendscript->Run()); - - v8::Handle args[] = { - V8FUNCTION(QQuickWorkerScriptEnginePrivate::sendMessage, this) - }; - v8::Local createsendvalue = createsendconstructor->Call(global(), 1, args); - - createsend = qPersistentNew(v8::Handle::Cast(createsendvalue)); - } -} - -// Requires handle and context scope -v8::Local QQuickWorkerScriptEnginePrivate::WorkerEngine::sendFunction(int id) -{ - v8::Handle args[] = { v8::Integer::New(id) }; - return v8::Local::Cast(createsend->Call(global(), 1, args)); -} - -// Requires handle and context scope -void QQuickWorkerScriptEnginePrivate::WorkerEngine::callOnMessage(v8::Handle object, - v8::Handle arg) -{ - v8::Handle args[] = { object, arg }; - onmessage->Call(global(), 2, args); -} - -QNetworkAccessManager *QQuickWorkerScriptEnginePrivate::WorkerEngine::networkAccessManager() -{ - if (!accessManager) { - if (p->qmlengine && p->qmlengine->networkAccessManagerFactory()) { - accessManager = p->qmlengine->networkAccessManagerFactory()->create(p); - } else { - accessManager = new QNetworkAccessManager(p); - } - } - return accessManager; -} - -QQuickWorkerScriptEnginePrivate::QQuickWorkerScriptEnginePrivate(QQmlEngine *engine) -: workerEngine(0), qmlengine(engine), m_nextId(0) -{ -} - -v8::Handle QQuickWorkerScriptEnginePrivate::sendMessage(const v8::Arguments &args) -{ - WorkerEngine *engine = (WorkerEngine*)V8ENGINE(); - - int id = args[1]->Int32Value(); - - QByteArray data = QV8Worker::serialize(args[2], engine); - - QMutexLocker locker(&engine->p->m_lock); - WorkerScript *script = engine->p->workers.value(id); - if (!script) - return v8::Undefined(); - - if (script->owner) - QCoreApplication::postEvent(script->owner, new WorkerDataEvent(0, data)); - - return v8::Undefined(); -} - -// Requires handle scope and context scope -v8::Handle QQuickWorkerScriptEnginePrivate::getWorker(WorkerScript *script) -{ - if (!script->initialized) { - script->initialized = true; - - script->object = qPersistentNew(workerEngine->contextWrapper()->urlScope(script->source)); - - workerEngine->contextWrapper()->setReadOnly(script->object, false); - - v8::Local api = v8::Object::New(); - api->Set(v8::String::New("sendMessage"), workerEngine->sendFunction(script->id)); - - script->object->Set(v8::String::New("WorkerScript"), api); - - workerEngine->contextWrapper()->setReadOnly(script->object, true); - } - - return script->object; -} - -bool QQuickWorkerScriptEnginePrivate::event(QEvent *event) -{ - if (event->type() == (QEvent::Type)WorkerDataEvent::WorkerData) { - WorkerDataEvent *workerEvent = static_cast(event); - processMessage(workerEvent->workerId(), workerEvent->data()); - return true; - } else if (event->type() == (QEvent::Type)WorkerLoadEvent::WorkerLoad) { - WorkerLoadEvent *workerEvent = static_cast(event); - processLoad(workerEvent->workerId(), workerEvent->url()); - return true; - } else if (event->type() == (QEvent::Type)WorkerDestroyEvent) { - emit stopThread(); - return true; - } else if (event->type() == (QEvent::Type)WorkerRemoveEvent::WorkerRemove) { - WorkerRemoveEvent *workerEvent = static_cast(event); - workers.remove(workerEvent->workerId()); - return true; - } else { - return QObject::event(event); - } -} - -void QQuickWorkerScriptEnginePrivate::processMessage(int id, const QByteArray &data) -{ - WorkerScript *script = workers.value(id); - if (!script) - return; - - v8::HandleScope handle_scope; - v8::Context::Scope scope(workerEngine->context()); - - v8::Handle value = QV8Worker::deserialize(data, workerEngine); - - v8::TryCatch tc; - workerEngine->callOnMessage(script->object, value); - - if (tc.HasCaught()) { - QQmlError error; - QQmlExpressionPrivate::exceptionToError(tc.Message(), error); - reportScriptException(script, error); - } -} - -void QQuickWorkerScriptEnginePrivate::processLoad(int id, const QUrl &url) -{ - if (url.isRelative()) - return; - - QString fileName = QQmlFile::urlToLocalFileOrQrc(url); - - QFile f(fileName); - if (f.open(QIODevice::ReadOnly)) { - QByteArray data = f.readAll(); - QString sourceCode = QString::fromUtf8(data); - QQmlScript::Parser::extractPragmas(sourceCode); - - v8::HandleScope handle_scope; - v8::Context::Scope scope(workerEngine->context()); - - WorkerScript *script = workers.value(id); - if (!script) - return; - script->source = url; - v8::Handle activation = getWorker(script); - if (activation.IsEmpty()) - return; - - // XXX ??? - // workerEngine->baseUrl = url; - - v8::TryCatch tc; - v8::Local program = workerEngine->qmlModeCompile(sourceCode, url.toString()); - - if (!tc.HasCaught()) - program->Run(activation); - - if (tc.HasCaught()) { - QQmlError error; - QQmlExpressionPrivate::exceptionToError(tc.Message(), error); - reportScriptException(script, error); - } - } else { - qWarning().nospace() << "WorkerScript: Cannot find source file " << url.toString(); - } -} - -void QQuickWorkerScriptEnginePrivate::reportScriptException(WorkerScript *script, - const QQmlError &error) -{ - QQuickWorkerScriptEnginePrivate *p = QQuickWorkerScriptEnginePrivate::get(workerEngine); - - QMutexLocker locker(&p->m_lock); - if (script->owner) - QCoreApplication::postEvent(script->owner, new WorkerErrorEvent(error)); -} - -WorkerDataEvent::WorkerDataEvent(int workerId, const QByteArray &data) -: QEvent((QEvent::Type)WorkerData), m_id(workerId), m_data(data) -{ -} - -WorkerDataEvent::~WorkerDataEvent() -{ -} - -int WorkerDataEvent::workerId() const -{ - return m_id; -} - -QByteArray WorkerDataEvent::data() const -{ - return m_data; -} - -WorkerLoadEvent::WorkerLoadEvent(int workerId, const QUrl &url) -: QEvent((QEvent::Type)WorkerLoad), m_id(workerId), m_url(url) -{ -} - -int WorkerLoadEvent::workerId() const -{ - return m_id; -} - -QUrl WorkerLoadEvent::url() const -{ - return m_url; -} - -WorkerRemoveEvent::WorkerRemoveEvent(int workerId) -: QEvent((QEvent::Type)WorkerRemove), m_id(workerId) -{ -} - -int WorkerRemoveEvent::workerId() const -{ - return m_id; -} - -WorkerErrorEvent::WorkerErrorEvent(const QQmlError &error) -: QEvent((QEvent::Type)WorkerError), m_error(error) -{ -} - -QQmlError WorkerErrorEvent::error() const -{ - return m_error; -} - -QQuickWorkerScriptEngine::QQuickWorkerScriptEngine(QQmlEngine *parent) -: QThread(parent), d(new QQuickWorkerScriptEnginePrivate(parent)) -{ - d->m_lock.lock(); - connect(d, SIGNAL(stopThread()), this, SLOT(quit()), Qt::DirectConnection); - start(QThread::LowestPriority); - d->m_wait.wait(&d->m_lock); - d->moveToThread(this); - d->m_lock.unlock(); -} - -QQuickWorkerScriptEngine::~QQuickWorkerScriptEngine() -{ - d->m_lock.lock(); - QCoreApplication::postEvent(d, new QEvent((QEvent::Type)QQuickWorkerScriptEnginePrivate::WorkerDestroyEvent)); - d->m_lock.unlock(); - - //We have to force to cleanup the main thread's event queue here - //to make sure the main GUI release all pending locks/wait conditions which - //some worker script/agent are waiting for (QQmlListModelWorkerAgent::sync() for example). - while (!isFinished()) { - // We can't simply wait here, because the worker thread will not terminate - // until the main thread processes the last data event it generates - QCoreApplication::processEvents(); - yieldCurrentThread(); - } - - d->deleteLater(); -} - -QQuickWorkerScriptEnginePrivate::WorkerScript::WorkerScript() -: id(-1), initialized(false), owner(0) -{ -} - -QQuickWorkerScriptEnginePrivate::WorkerScript::~WorkerScript() -{ - qPersistentDispose(object); -} - -int QQuickWorkerScriptEngine::registerWorkerScript(QQuickWorkerScript *owner) -{ - typedef QQuickWorkerScriptEnginePrivate::WorkerScript WorkerScript; - WorkerScript *script = new WorkerScript; - - script->id = d->m_nextId++; - script->owner = owner; - - d->m_lock.lock(); - d->workers.insert(script->id, script); - d->m_lock.unlock(); - - return script->id; -} - -void QQuickWorkerScriptEngine::removeWorkerScript(int id) -{ - QQuickWorkerScriptEnginePrivate::WorkerScript* script = d->workers.value(id); - if (script) { - script->owner = 0; - QCoreApplication::postEvent(d, new WorkerRemoveEvent(id)); - } -} - -void QQuickWorkerScriptEngine::executeUrl(int id, const QUrl &url) -{ - QCoreApplication::postEvent(d, new WorkerLoadEvent(id, url)); -} - -void QQuickWorkerScriptEngine::sendMessage(int id, const QByteArray &data) -{ - QCoreApplication::postEvent(d, new WorkerDataEvent(id, data)); -} - -void QQuickWorkerScriptEngine::run() -{ - d->m_lock.lock(); - - d->workerEngine = new QQuickWorkerScriptEnginePrivate::WorkerEngine(d); - d->workerEngine->init(); - - d->m_wait.wakeAll(); - - d->m_lock.unlock(); - - exec(); - - qDeleteAll(d->workers); - d->workers.clear(); - - delete d->workerEngine; d->workerEngine = 0; -} - - -/*! - \qmltype WorkerScript - \instantiates QQuickWorkerScript - \ingroup qtquick-threading - \inqmlmodule QtQuick 2 - \brief Enables the use of threads in a Qt Quick application - - Use WorkerScript to run operations in a new thread. - This is useful for running operations in the background so - that the main GUI thread is not blocked. - - Messages can be passed between the new thread and the parent thread - using \l sendMessage() and the \l {WorkerScript::onMessage}{onMessage()} handler. - - An example: - - \snippet qml/workerscript/workerscript.qml 0 - - The above worker script specifies a JavaScript file, "script.js", that handles - the operations to be performed in the new thread. Here is \c script.js: - - \quotefile qml/workerscript/script.js - - When the user clicks anywhere within the rectangle, \c sendMessage() is - called, triggering the \tt WorkerScript.onMessage() handler in - \tt script.js. This in turn sends a reply message that is then received - by the \tt onMessage() handler of \tt myWorker. - - - \section3 Restrictions - - Since the \c WorkerScript.onMessage() function is run in a separate thread, the - JavaScript file is evaluated in a context separate from the main QML engine. This means - that unlike an ordinary JavaScript file that is imported into QML, the \c script.js - in the above example cannot access the properties, methods or other attributes - of the QML item, nor can it access any context properties set on the QML object - through QQmlContext. - - Additionally, there are restrictions on the types of values that can be passed to and - from the worker script. See the sendMessage() documentation for details. - - Worker script can not use \l {qtqml-javascript-imports.html}{.import} syntax. - - \sa {declarative/threading/workerscript}{WorkerScript example}, - {declarative/threading/threadedlistmodel}{Threaded ListModel example} -*/ -QQuickWorkerScript::QQuickWorkerScript(QObject *parent) -: QObject(parent), m_engine(0), m_scriptId(-1), m_componentComplete(true) -{ -} - -QQuickWorkerScript::~QQuickWorkerScript() -{ - if (m_scriptId != -1) m_engine->removeWorkerScript(m_scriptId); -} - -/*! - \qmlproperty url WorkerScript::source - - This holds the url of the JavaScript file that implements the - \tt WorkerScript.onMessage() handler for threaded operations. -*/ -QUrl QQuickWorkerScript::source() const -{ - return m_source; -} - -void QQuickWorkerScript::setSource(const QUrl &source) -{ - if (m_source == source) - return; - - m_source = source; - - if (engine()) - m_engine->executeUrl(m_scriptId, m_source); - - emit sourceChanged(); -} - -/*! - \qmlmethod WorkerScript::sendMessage(jsobject message) - - Sends the given \a message to a worker script handler in another - thread. The other worker script handler can receive this message - through the onMessage() handler. - - The \c message object may only contain values of the following - types: - - \list - \li boolean, number, string - \li JavaScript objects and arrays - \li ListModel objects (any other type of QObject* is not allowed) - \endlist - - All objects and arrays are copied to the \c message. With the exception - of ListModel objects, any modifications by the other thread to an object - passed in \c message will not be reflected in the original object. -*/ -void QQuickWorkerScript::sendMessage(QQmlV8Function *args) -{ - if (!engine()) { - qWarning("QQuickWorkerScript: Attempt to send message before WorkerScript establishment"); - return; - } - - v8::Handle argument = v8::Undefined(); - if (args->Length() != 0) - argument = (*args)[0]; - - m_engine->sendMessage(m_scriptId, QV8Worker::serialize(argument, args->engine())); -} - -void QQuickWorkerScript::classBegin() -{ - m_componentComplete = false; -} - -QQuickWorkerScriptEngine *QQuickWorkerScript::engine() -{ - if (m_engine) return m_engine; - if (m_componentComplete) { - QQmlEngine *engine = qmlEngine(this); - if (!engine) { - qWarning("QQuickWorkerScript: engine() called without qmlEngine() set"); - return 0; - } - - m_engine = QQmlEnginePrivate::get(engine)->getWorkerScriptEngine(); - m_scriptId = m_engine->registerWorkerScript(this); - - if (m_source.isValid()) - m_engine->executeUrl(m_scriptId, m_source); - - return m_engine; - } - return 0; -} - -void QQuickWorkerScript::componentComplete() -{ - m_componentComplete = true; - engine(); // Get it started now. -} - -/*! - \qmlsignal WorkerScript::onMessage(jsobject msg) - - This handler is called when a message \a msg is received from a worker - script in another thread through a call to sendMessage(). -*/ - -bool QQuickWorkerScript::event(QEvent *event) -{ - if (event->type() == (QEvent::Type)WorkerDataEvent::WorkerData) { - QQmlEngine *engine = qmlEngine(this); - if (engine) { - WorkerDataEvent *workerEvent = static_cast(event); - QV8Engine *v8engine = QQmlEnginePrivate::get(engine)->v8engine(); - v8::HandleScope handle_scope; - v8::Context::Scope scope(v8engine->context()); - v8::Handle value = QV8Worker::deserialize(workerEvent->data(), v8engine); - emit message(QQmlV8Handle::fromHandle(value)); - } - return true; - } else if (event->type() == (QEvent::Type)WorkerErrorEvent::WorkerError) { - WorkerErrorEvent *workerEvent = static_cast(event); - QQmlEnginePrivate::warning(qmlEngine(this), workerEvent->error()); - return true; - } else { - return QObject::event(event); - } -} - -QT_END_NAMESPACE - -#include - diff --git a/src/qml/qml/qquickworkerscript_p.h b/src/qml/qml/qquickworkerscript_p.h deleted file mode 100644 index d568e58d0e..0000000000 --- a/src/qml/qml/qquickworkerscript_p.h +++ /dev/null @@ -1,126 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** 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, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQUICKWORKERSCRIPT_P_H -#define QQUICKWORKERSCRIPT_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "qqml.h" -#include "qqmlparserstatus.h" - -#include -#include -#include - -QT_BEGIN_NAMESPACE - - -class QQuickWorkerScript; -class QQuickWorkerScriptEnginePrivate; -class QQuickWorkerScriptEngine : public QThread -{ -Q_OBJECT -public: - QQuickWorkerScriptEngine(QQmlEngine *parent = 0); - virtual ~QQuickWorkerScriptEngine(); - - int registerWorkerScript(QQuickWorkerScript *); - void removeWorkerScript(int); - void executeUrl(int, const QUrl &); - void sendMessage(int, const QByteArray &); - -protected: - virtual void run(); - -private: - QQuickWorkerScriptEnginePrivate *d; -}; - -class QQmlV8Function; -class QQmlV8Handle; -class Q_AUTOTEST_EXPORT QQuickWorkerScript : public QObject, public QQmlParserStatus -{ - Q_OBJECT - Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged) - - Q_INTERFACES(QQmlParserStatus) -public: - QQuickWorkerScript(QObject *parent = 0); - virtual ~QQuickWorkerScript(); - - QUrl source() const; - void setSource(const QUrl &); - -public slots: - void sendMessage(QQmlV8Function*); - -signals: - void sourceChanged(); - void message(const QQmlV8Handle &messageObject); - -protected: - virtual void classBegin(); - virtual void componentComplete(); - virtual bool event(QEvent *); - -private: - QQuickWorkerScriptEngine *engine(); - QQuickWorkerScriptEngine *m_engine; - int m_scriptId; - QUrl m_source; - bool m_componentComplete; -}; - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QQuickWorkerScript) - -#endif // QQUICKWORKERSCRIPT_P_H diff --git a/src/qml/types/qqmlbind.cpp b/src/qml/types/qqmlbind.cpp new file mode 100644 index 0000000000..fcb3079891 --- /dev/null +++ b/src/qml/types/qqmlbind.cpp @@ -0,0 +1,310 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlbind_p.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +class QQmlBindPrivate : public QObjectPrivate +{ +public: + QQmlBindPrivate() : componentComplete(true), obj(0), prevBind(0) {} + ~QQmlBindPrivate() { if (prevBind) prevBind->destroy(); } + + QQmlNullableValue when; + bool componentComplete; + QQmlGuard obj; + QString propName; + QQmlNullableValue value; + QQmlProperty prop; + QQmlAbstractBinding *prevBind; +}; + + +/*! + \qmltype Binding + \instantiates QQmlBind + \inqmlmodule QtQml 2 + \ingroup qtquick-interceptors + \brief Enables the arbitrary creation of property bindings + + \section1 Binding to an inaccessible property + + Sometimes it is necessary to bind to a property of an object that wasn't + directly instantiated by QML - generally a property of a class exported + to QML by C++. In these cases, regular property binding doesn't work. Binding + allows you to bind any value to any property. + + For example, imagine a C++ application that maps an "app.enteredText" + property into QML. You could use Binding to update the enteredText property + like this. + \code + TextEdit { id: myTextField; text: "Please type here..." } + Binding { target: app; property: "enteredText"; value: myTextField.text } + \endcode + Whenever the text in the TextEdit is updated, the C++ property will be + updated also. + + \section1 "Single-branch" conditional binding + + In some circumstances you may want to control the value of a property + only when a certain condition is true (and relinquish control in all + other circumstances). This often isn't possible to accomplish with a direct + binding, as you need to supply values for all possible branches. + + \code + // produces warning: "Unable to assign [undefined] to double value" + value: if (mouse.pressed) mouse.mouseX + \endcode + + The above example will produce a warning whenever we release the mouse, as the value + of the binding is undefined when the mouse isn't pressed. We can use the Binding + type to rewrite the above code and avoid the warning. + + \qml + Binding on value { + when: mouse.pressed + value: mouse.mouseX + } + \endqml + + The Binding type will also restore any previously set direct bindings on + the property. In that sense, it functions much like a simplified State. + + \qml + // this is equivalent to the above Binding + State { + name: "pressed" + when: mouse.pressed + PropertyChanges { + target: obj + value: mouse.mouseX + } + } + \endqml + + If the binding target or binding property is changed, the bound value is + immediately pushed onto the new target. + + \sa QtQml +*/ +QQmlBind::QQmlBind(QObject *parent) + : QObject(*(new QQmlBindPrivate), parent) +{ +} + +QQmlBind::~QQmlBind() +{ +} + +/*! + \qmlproperty bool QtQml2::Binding::when + + This property holds when the binding is active. + This should be set to an expression that evaluates to true when you want the binding to be active. + + \code + Binding { + target: contactName; property: 'text' + value: name; when: list.ListView.isCurrentItem + } + \endcode + + When the binding becomes inactive again, any direct bindings that were previously + set on the property will be restored. +*/ +bool QQmlBind::when() const +{ + Q_D(const QQmlBind); + return d->when; +} + +void QQmlBind::setWhen(bool v) +{ + Q_D(QQmlBind); + if (!d->when.isNull && d->when == v) + return; + + d->when = v; + eval(); +} + +/*! + \qmlproperty Object QtQml2::Binding::target + + The object to be updated. +*/ +QObject *QQmlBind::object() +{ + Q_D(const QQmlBind); + return d->obj; +} + +void QQmlBind::setObject(QObject *obj) +{ + Q_D(QQmlBind); + if (d->obj && d->when.isValid() && d->when) { + /* if we switch the object at runtime, we need to restore the + previous binding on the old object before continuing */ + d->when = false; + eval(); + d->when = true; + } + d->obj = obj; + if (d->componentComplete) + d->prop = QQmlProperty(d->obj, d->propName); + eval(); +} + +/*! + \qmlproperty string QtQml2::Binding::property + + The property to be updated. +*/ +QString QQmlBind::property() const +{ + Q_D(const QQmlBind); + return d->propName; +} + +void QQmlBind::setProperty(const QString &p) +{ + Q_D(QQmlBind); + if (!d->propName.isEmpty() && d->when.isValid() && d->when) { + /* if we switch the property name at runtime, we need to restore the + previous binding on the old object before continuing */ + d->when = false; + eval(); + d->when = true; + } + d->propName = p; + if (d->componentComplete) + d->prop = QQmlProperty(d->obj, d->propName); + eval(); +} + +/*! + \qmlproperty any QtQml2::Binding::value + + The value to be set on the target object and property. This can be a + constant (which isn't very useful), or a bound expression. +*/ +QVariant QQmlBind::value() const +{ + Q_D(const QQmlBind); + return d->value.value; +} + +void QQmlBind::setValue(const QVariant &v) +{ + Q_D(QQmlBind); + d->value = v; + eval(); +} + +void QQmlBind::setTarget(const QQmlProperty &p) +{ + Q_D(QQmlBind); + d->prop = p; +} + +void QQmlBind::classBegin() +{ + Q_D(QQmlBind); + d->componentComplete = false; +} + +void QQmlBind::componentComplete() +{ + Q_D(QQmlBind); + d->componentComplete = true; + if (!d->prop.isValid()) + d->prop = QQmlProperty(d->obj, d->propName); + eval(); +} + +void QQmlBind::eval() +{ + Q_D(QQmlBind); + if (!d->prop.isValid() || d->value.isNull || !d->componentComplete) + return; + + if (d->when.isValid()) { + if (!d->when) { + //restore any previous binding + if (d->prevBind) { + QQmlAbstractBinding *tmp = d->prevBind; + d->prevBind = 0; + tmp = QQmlPropertyPrivate::setBinding(d->prop, tmp); + if (tmp) //should this ever be true? + tmp->destroy(); + } + return; + } + + //save any set binding for restoration + QQmlAbstractBinding *tmp; + tmp = QQmlPropertyPrivate::setBinding(d->prop, 0); + if (tmp && d->prevBind) + tmp->destroy(); + else if (!d->prevBind) + d->prevBind = tmp; + } + + d->prop.write(d->value.value); +} + +QT_END_NAMESPACE diff --git a/src/qml/types/qqmlbind_p.h b/src/qml/types/qqmlbind_p.h new file mode 100644 index 0000000000..1e29c257f0 --- /dev/null +++ b/src/qml/types/qqmlbind_p.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLBIND_H +#define QQMLBIND_H + +#include + +#include + +QT_BEGIN_NAMESPACE + +class QQmlBindPrivate; +class Q_AUTOTEST_EXPORT QQmlBind : public QObject, public QQmlPropertyValueSource, public QQmlParserStatus +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QQmlBind) + Q_INTERFACES(QQmlParserStatus) + Q_INTERFACES(QQmlPropertyValueSource) + Q_PROPERTY(QObject *target READ object WRITE setObject) + Q_PROPERTY(QString property READ property WRITE setProperty) + Q_PROPERTY(QVariant value READ value WRITE setValue) + Q_PROPERTY(bool when READ when WRITE setWhen) + +public: + QQmlBind(QObject *parent=0); + ~QQmlBind(); + + bool when() const; + void setWhen(bool); + + QObject *object(); + void setObject(QObject *); + + QString property() const; + void setProperty(const QString &); + + QVariant value() const; + void setValue(const QVariant &); + +protected: + virtual void setTarget(const QQmlProperty &); + virtual void classBegin(); + virtual void componentComplete(); + +private: + void eval(); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQmlBind) + +#endif diff --git a/src/qml/types/qqmlconnections.cpp b/src/qml/types/qqmlconnections.cpp new file mode 100644 index 0000000000..286933e557 --- /dev/null +++ b/src/qml/types/qqmlconnections.cpp @@ -0,0 +1,321 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlconnections_p.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +class QQmlConnectionsPrivate : public QObjectPrivate +{ +public: + QQmlConnectionsPrivate() : target(0), targetSet(false), ignoreUnknownSignals(false), componentcomplete(true) {} + + QList boundsignals; + QObject *target; + + bool targetSet; + bool ignoreUnknownSignals; + bool componentcomplete; + + QByteArray data; +}; + +/*! + \qmltype Connections + \instantiates QQmlConnections + \inqmlmodule QtQml 2 + \ingroup qtquick-interceptors + \brief Describes generalized connections to signals + + A Connections object creates a connection to a QML signal. + + When connecting to signals in QML, the usual way is to create an + "on" handler that reacts when a signal is received, like this: + + \qml + MouseArea { + onClicked: { foo(parameters) } + } + \endqml + + However, it is not possible to connect to a signal in this way in some + cases, such as when: + + \list + \li Multiple connections to the same signal are required + \li Creating connections outside the scope of the signal sender + \li Connecting to targets not defined in QML + \endlist + + When any of these are needed, the Connections type can be used instead. + + For example, the above code can be changed to use a Connections object, + like this: + + \qml + MouseArea { + Connections { + onClicked: foo(parameters) + } + } + \endqml + + More generally, the Connections object can be a child of some object other than + the sender of the signal: + + \qml + MouseArea { + id: area + } + // ... + \endqml + \qml + Connections { + target: area + onClicked: foo(parameters) + } + \endqml + + \sa QtQml +*/ +QQmlConnections::QQmlConnections(QObject *parent) : + QObject(*(new QQmlConnectionsPrivate), parent) +{ +} + +QQmlConnections::~QQmlConnections() +{ +} + +/*! + \qmlproperty Object QtQml2::Connections::target + This property holds the object that sends the signal. + + If this property is not set, the \c target defaults to the parent of the Connection. + + If set to null, no connection is made and any signal handlers are ignored + until the target is not null. +*/ +QObject *QQmlConnections::target() const +{ + Q_D(const QQmlConnections); + return d->targetSet ? d->target : parent(); +} + +class QQmlBoundSignalDeleter : public QObject +{ +public: + QQmlBoundSignalDeleter(QQmlBoundSignal *signal) : m_signal(signal) { m_signal->removeFromObject(); } + ~QQmlBoundSignalDeleter() { delete m_signal; } + +private: + QQmlBoundSignal *m_signal; +}; + +void QQmlConnections::setTarget(QObject *obj) +{ + Q_D(QQmlConnections); + d->targetSet = true; // even if setting to 0, it is *set* + if (d->target == obj) + return; + foreach (QQmlBoundSignal *s, d->boundsignals) { + // It is possible that target is being changed due to one of our signal + // handlers -> use deleteLater(). + if (s->isEvaluating()) + (new QQmlBoundSignalDeleter(s))->deleteLater(); + else + delete s; + } + d->boundsignals.clear(); + d->target = obj; + connectSignals(); + emit targetChanged(); +} + +/*! + \qmlproperty bool QtQml2::Connections::ignoreUnknownSignals + + Normally, a connection to a non-existent signal produces runtime errors. + + If this property is set to \c true, such errors are ignored. + This is useful if you intend to connect to different types of objects, handling + a different set of signals for each object. +*/ +bool QQmlConnections::ignoreUnknownSignals() const +{ + Q_D(const QQmlConnections); + return d->ignoreUnknownSignals; +} + +void QQmlConnections::setIgnoreUnknownSignals(bool ignore) +{ + Q_D(QQmlConnections); + d->ignoreUnknownSignals = ignore; +} + + + +QByteArray +QQmlConnectionsParser::compile(const QList &props) +{ + QByteArray rv; + QDataStream ds(&rv, QIODevice::WriteOnly); + + for(int ii = 0; ii < props.count(); ++ii) + { + QString propName = props.at(ii).name(); + int propLine = props.at(ii).location().line; + int propColumn = props.at(ii).location().column; + + if (!propName.startsWith(QLatin1String("on")) || !propName.at(2).isUpper()) { + error(props.at(ii), QQmlConnections::tr("Cannot assign to non-existent property \"%1\"").arg(propName)); + return QByteArray(); + } + + QList values = props.at(ii).assignedValues(); + + for (int i = 0; i < values.count(); ++i) { + const QVariant &value = values.at(i); + + if (value.userType() == qMetaTypeId()) { + error(props.at(ii), QQmlConnections::tr("Connections: nested objects not allowed")); + return QByteArray(); + } else if (value.userType() == qMetaTypeId()) { + error(props.at(ii), QQmlConnections::tr("Connections: syntax error")); + return QByteArray(); + } else { + QQmlScript::Variant v = qvariant_cast(value); + if (v.isScript()) { + ds << propName; + ds << rewriteSignalHandler(v, propName).toUtf8(); + ds << propLine; + ds << propColumn; + } else { + error(props.at(ii), QQmlConnections::tr("Connections: script expected")); + return QByteArray(); + } + } + } + } + + return rv; +} + +void QQmlConnectionsParser::setCustomData(QObject *object, + const QByteArray &data) +{ + QQmlConnectionsPrivate *p = + static_cast(QObjectPrivate::get(object)); + p->data = data; +} + + +void QQmlConnections::connectSignals() +{ + Q_D(QQmlConnections); + if (!d->componentcomplete || (d->targetSet && !target())) + return; + + QDataStream ds(d->data); + while (!ds.atEnd()) { + QString propName; + ds >> propName; + QByteArray script; + ds >> script; + int line; + ds >> line; + int column; + ds >> column; + + QQmlProperty prop(target(), propName); + if (prop.isValid() && (prop.type() & QQmlProperty::SignalProperty)) { + int signalIndex = QQmlPropertyPrivate::get(prop)->signalIndex(); + QQmlBoundSignal *signal = + new QQmlBoundSignal(target(), signalIndex, this, qmlEngine(this)); + + QString location; + QQmlContextData *ctxtdata = 0; + QQmlData *ddata = QQmlData::get(this); + if (ddata) { + ctxtdata = ddata->outerContext; + if (ctxtdata && !ctxtdata->url.isEmpty()) + location = ddata->outerContext->urlString; + } + + QQmlBoundSignalExpression *expression = ctxtdata ? + new QQmlBoundSignalExpression(target(), signalIndex, + ctxtdata, this, script, + true, location, line, column) : 0; + signal->takeExpression(expression); + d->boundsignals += signal; + } else { + if (!d->ignoreUnknownSignals) + qmlInfo(this) << tr("Cannot assign to non-existent property \"%1\"").arg(propName); + } + } +} + +void QQmlConnections::classBegin() +{ + Q_D(QQmlConnections); + d->componentcomplete=false; +} + +void QQmlConnections::componentComplete() +{ + Q_D(QQmlConnections); + d->componentcomplete=true; + connectSignals(); +} + +QT_END_NAMESPACE diff --git a/src/qml/types/qqmlconnections_p.h b/src/qml/types/qqmlconnections_p.h new file mode 100644 index 0000000000..9bc668e5f4 --- /dev/null +++ b/src/qml/types/qqmlconnections_p.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLCONNECTIONS_H +#define QQMLCONNECTIONS_H + +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +class QQmlBoundSignal; +class QQmlContext; +class QQmlConnectionsPrivate; +class Q_AUTOTEST_EXPORT QQmlConnections : public QObject, public QQmlParserStatus +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QQmlConnections) + + Q_INTERFACES(QQmlParserStatus) + Q_PROPERTY(QObject *target READ target WRITE setTarget NOTIFY targetChanged) + Q_PROPERTY(bool ignoreUnknownSignals READ ignoreUnknownSignals WRITE setIgnoreUnknownSignals) + +public: + QQmlConnections(QObject *parent=0); + ~QQmlConnections(); + + QObject *target() const; + void setTarget(QObject *); + + bool ignoreUnknownSignals() const; + void setIgnoreUnknownSignals(bool ignore); + +Q_SIGNALS: + void targetChanged(); + +private: + void connectSignals(); + void classBegin(); + void componentComplete(); +}; + +class QQmlConnectionsParser : public QQmlCustomParser +{ +public: + virtual QByteArray compile(const QList &); + virtual void setCustomData(QObject *, const QByteArray &); +}; + + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQmlConnections) + +#endif diff --git a/src/qml/types/qqmldelegatemodel.cpp b/src/qml/types/qqmldelegatemodel.cpp new file mode 100644 index 0000000000..efbd98bdbc --- /dev/null +++ b/src/qml/types/qqmldelegatemodel.cpp @@ -0,0 +1,3189 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmldelegatemodel_p_p.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QQmlDelegateModelEngineData : public QV8Engine::Deletable +{ +public: + enum + { + Model, + Groups, + IsUnresolved, + ItemsIndex, + PersistedItemsIndex, + InItems, + InPersistedItems, + StringCount + }; + + QQmlDelegateModelEngineData(QV8Engine *engine); + ~QQmlDelegateModelEngineData(); + + v8::Local array( + QV8Engine *engine, const QVector &changes); + v8::Local array( + QV8Engine *engine, const QVector &changes); + v8::Local array( + QV8Engine *engine, const QVector &changes); + + + inline v8::Local model() { return strings->Get(Model)->ToString(); } + inline v8::Local groups() { return strings->Get(Groups)->ToString(); } + inline v8::Local isUnresolved() { return strings->Get(IsUnresolved)->ToString(); } + inline v8::Local itemsIndex() { return strings->Get(ItemsIndex)->ToString(); } + inline v8::Local persistedItemsIndex() { return strings->Get(PersistedItemsIndex)->ToString(); } + inline v8::Local inItems() { return strings->Get(InItems)->ToString(); } + inline v8::Local inPersistedItems() { return strings->Get(InPersistedItems)->ToString(); } + + v8::Persistent strings; + v8::Persistent constructorChange; + v8::Persistent constructorChangeArray; +}; + +V8_DEFINE_EXTENSION(QQmlDelegateModelEngineData, engineData) + + +void QQmlDelegateModelPartsMetaObject::propertyCreated(int, QMetaPropertyBuilder &prop) +{ + prop.setWritable(false); +} + +QVariant QQmlDelegateModelPartsMetaObject::initialValue(int id) +{ + QQmlDelegateModelParts *parts = static_cast(object()); + QQmlPartsModel *m = new QQmlPartsModel( + parts->model, QString::fromUtf8(name(id)), parts); + parts->models.append(m); + return QVariant::fromValue(static_cast(m)); +} + +QQmlDelegateModelParts::QQmlDelegateModelParts(QQmlDelegateModel *parent) +: QObject(parent), model(parent) +{ + new QQmlDelegateModelPartsMetaObject(this); +} + +//--------------------------------------------------------------------------- + +/*! + \qmltype VisualDataModel + \instantiates QQmlDelegateModel + \inqmlmodule QtQuick 2 + \ingroup qtquick-models + \brief Encapsulates a model and delegate + + The VisualDataModel type encapsulates a model and the delegate that will + be instantiated for items in a model. + + This type is provided by QtQuick 2 for compatibility reasons. The same implementation + is now primarily available as DelegateModel in the QtQml.Models module. + + \sa {QtQml.Models2::DelegateModel} +*/ +/*! + \qmltype DelegateModel + \instantiates QQmlDelegateModel + \inqmlmodule QtQml.Models 2 + \brief Encapsulates a model and delegate + + The DelegateModel type encapsulates a model and the delegate that will + be instantiated for items in the model. + + This element is also available as DelegateModel in the QtQuick module. For full details, + see the \l DelegateModel documentation. + + The DelegateModel type encapsulates a model and the delegate that will + be instantiated for items in the model. + + It is usually not necessary to create a DelegateModel. + However, it can be useful for manipulating and accessing the \l modelIndex + when a QAbstractItemModel subclass is used as the + model. Also, DelegateModel is used together with \l Package to + provide delegates to multiple views, and with DelegateModelGroup to sort and filter + delegate items. + + The example below illustrates using a DelegateModel with a ListView. + + \snippet qml/visualdatamodel.qml 0 +*/ + +QQmlDelegateModelPrivate::QQmlDelegateModelPrivate(QQmlContext *ctxt) + : m_delegate(0) + , m_cacheMetaType(0) + , m_context(ctxt) + , m_parts(0) + , m_filterGroup(QStringLiteral("items")) + , m_count(0) + , m_groupCount(Compositor::MinimumGroupCount) + , m_compositorGroup(Compositor::Cache) + , m_complete(false) + , m_delegateValidated(false) + , m_reset(false) + , m_transaction(false) + , m_incubatorCleanupScheduled(false) + , m_cacheItems(0) + , m_items(0) + , m_persistedItems(0) +{ +} + +QQmlDelegateModelPrivate::~QQmlDelegateModelPrivate() +{ + qDeleteAll(m_finishedIncubating); + + if (m_cacheMetaType) + m_cacheMetaType->release(); +} + +void QQmlDelegateModelPrivate::init() +{ + Q_Q(QQmlDelegateModel); + m_compositor.setRemoveGroups(Compositor::GroupMask & ~Compositor::PersistedFlag); + + m_items = new QQmlDelegateModelGroup(QStringLiteral("items"), q, Compositor::Default, q); + m_items->setDefaultInclude(true); + m_persistedItems = new QQmlDelegateModelGroup(QStringLiteral("persistedItems"), q, Compositor::Persisted, q); + QQmlDelegateModelGroupPrivate::get(m_items)->emitters.insert(this); +} + +QQmlDelegateModel::QQmlDelegateModel() +: QQmlInstanceModel(*(new QQmlDelegateModelPrivate(0))) +{ + Q_D(QQmlDelegateModel); + d->init(); +} + +QQmlDelegateModel::QQmlDelegateModel(QQmlContext *ctxt, QObject *parent) +: QQmlInstanceModel(*(new QQmlDelegateModelPrivate(ctxt)), parent) +{ + Q_D(QQmlDelegateModel); + d->init(); +} + +QQmlDelegateModel::~QQmlDelegateModel() +{ + Q_D(QQmlDelegateModel); + + foreach (QQmlDelegateModelItem *cacheItem, d->m_cache) { + if (cacheItem->object) { + delete cacheItem->object; + + cacheItem->object = 0; + cacheItem->contextData->destroy(); + cacheItem->contextData = 0; + cacheItem->scriptRef -= 1; + } + cacheItem->groups &= ~Compositor::UnresolvedFlag; + cacheItem->objectRef = 0; + if (!cacheItem->isReferenced()) + delete cacheItem; + } +} + + +void QQmlDelegateModel::classBegin() +{ + Q_D(QQmlDelegateModel); + if (!d->m_context) + d->m_context = qmlContext(this); +} + +void QQmlDelegateModel::componentComplete() +{ + Q_D(QQmlDelegateModel); + d->m_complete = true; + + int defaultGroups = 0; + QStringList groupNames; + groupNames.append(QStringLiteral("items")); + groupNames.append(QStringLiteral("persistedItems")); + if (QQmlDelegateModelGroupPrivate::get(d->m_items)->defaultInclude) + defaultGroups |= Compositor::DefaultFlag; + if (QQmlDelegateModelGroupPrivate::get(d->m_persistedItems)->defaultInclude) + defaultGroups |= Compositor::PersistedFlag; + for (int i = Compositor::MinimumGroupCount; i < d->m_groupCount; ++i) { + QString name = d->m_groups[i]->name(); + if (name.isEmpty()) { + d->m_groups[i] = d->m_groups[d->m_groupCount - 1]; + --d->m_groupCount; + --i; + } else if (name.at(0).isUpper()) { + qmlInfo(d->m_groups[i]) << QQmlDelegateModelGroup::tr("Group names must start with a lower case letter"); + d->m_groups[i] = d->m_groups[d->m_groupCount - 1]; + --d->m_groupCount; + --i; + } else { + groupNames.append(name); + + QQmlDelegateModelGroupPrivate *group = QQmlDelegateModelGroupPrivate::get(d->m_groups[i]); + group->setModel(this, Compositor::Group(i)); + if (group->defaultInclude) + defaultGroups |= (1 << i); + } + } + + d->m_cacheMetaType = new QQmlDelegateModelItemMetaType( + QQmlEnginePrivate::getV8Engine(d->m_context->engine()), this, groupNames); + + d->m_compositor.setGroupCount(d->m_groupCount); + d->m_compositor.setDefaultGroups(defaultGroups); + d->updateFilterGroup(); + + while (!d->m_pendingParts.isEmpty()) + static_cast(d->m_pendingParts.first())->updateFilterGroup(); + + QVector inserts; + d->m_count = d->m_adaptorModel.count(); + d->m_compositor.append( + &d->m_adaptorModel, + 0, + d->m_count, + defaultGroups | Compositor::AppendFlag | Compositor::PrependFlag, + &inserts); + d->itemsInserted(inserts); + d->emitChanges(); + + if (d->m_adaptorModel.canFetchMore()) + QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest)); +} + +/*! + \qmlproperty model QtQml.Models2::DelegateModel::model + This property holds the model providing data for the DelegateModel. + + The model provides a set of data that is used to create the items + for a view. For large or dynamic datasets the model is usually + provided by a C++ model object. The C++ model object must be a \l + {QAbstractItemModel} subclass or a simple list. + + Models can also be created directly in QML, using a \l{ListModel} or + \l{XmlListModel}. + + \sa {qml-data-models}{Data Models} +*/ +QVariant QQmlDelegateModel::model() const +{ + Q_D(const QQmlDelegateModel); + return d->m_adaptorModel.model(); +} + +void QQmlDelegateModel::setModel(const QVariant &model) +{ + Q_D(QQmlDelegateModel); + + if (d->m_complete) + _q_itemsRemoved(0, d->m_count); + + d->m_adaptorModel.setModel(model, this, d->m_context->engine()); + d->m_adaptorModel.replaceWatchedRoles(QList(), d->m_watchedRoles); + for (int i = 0; d->m_parts && i < d->m_parts->models.count(); ++i) { + d->m_adaptorModel.replaceWatchedRoles( + QList(), d->m_parts->models.at(i)->watchedRoles()); + } + + if (d->m_complete) { + _q_itemsInserted(0, d->m_adaptorModel.count()); + if (d->m_adaptorModel.canFetchMore()) + QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest)); + } +} + +/*! + \qmlproperty Component QtQml.Models2::DelegateModel::delegate + + The delegate provides a template defining each item instantiated by a view. + The index is exposed as an accessible \c index property. Properties of the + model are also available depending upon the type of \l {qml-data-models}{Data Model}. +*/ +QQmlComponent *QQmlDelegateModel::delegate() const +{ + Q_D(const QQmlDelegateModel); + return d->m_delegate; +} + +void QQmlDelegateModel::setDelegate(QQmlComponent *delegate) +{ + Q_D(QQmlDelegateModel); + if (d->m_transaction) { + qmlInfo(this) << tr("The delegate of a DelegateModel cannot be changed within onUpdated."); + return; + } + bool wasValid = d->m_delegate != 0; + d->m_delegate = delegate; + d->m_delegateValidated = false; + if (wasValid && d->m_complete) { + for (int i = 1; i < d->m_groupCount; ++i) { + QQmlDelegateModelGroupPrivate::get(d->m_groups[i])->changeSet.remove( + 0, d->m_compositor.count(Compositor::Group(i))); + } + } + if (d->m_complete && d->m_delegate) { + for (int i = 1; i < d->m_groupCount; ++i) { + QQmlDelegateModelGroupPrivate::get(d->m_groups[i])->changeSet.insert( + 0, d->m_compositor.count(Compositor::Group(i))); + } + } + d->emitChanges(); +} + +/*! + \qmlproperty QModelIndex QtQml.Models2::DelegateModel::rootIndex + + QAbstractItemModel provides a hierarchical tree of data, whereas + QML only operates on list data. \c rootIndex allows the children of + any node in a QAbstractItemModel to be provided by this model. + + This property only affects models of type QAbstractItemModel that + are hierarchical (e.g, a tree model). + + For example, here is a simple interactive file system browser. + When a directory name is clicked, the view's \c rootIndex is set to the + QModelIndex node of the clicked directory, thus updating the view to show + the new directory's contents. + + \c main.cpp: + \snippet qml/visualdatamodel_rootindex/main.cpp 0 + + \c view.qml: + \snippet qml/visualdatamodel_rootindex/view.qml 0 + + If the \l model is a QAbstractItemModel subclass, the delegate can also + reference a \c hasModelChildren property (optionally qualified by a + \e model. prefix) that indicates whether the delegate's model item has + any child nodes. + + + \sa modelIndex(), parentModelIndex() +*/ +QVariant QQmlDelegateModel::rootIndex() const +{ + Q_D(const QQmlDelegateModel); + return QVariant::fromValue(QModelIndex(d->m_adaptorModel.rootIndex)); +} + +void QQmlDelegateModel::setRootIndex(const QVariant &root) +{ + Q_D(QQmlDelegateModel); + + QModelIndex modelIndex = qvariant_cast(root); + const bool changed = d->m_adaptorModel.rootIndex != modelIndex; + if (changed || !d->m_adaptorModel.isValid()) { + const int oldCount = d->m_count; + d->m_adaptorModel.rootIndex = modelIndex; + if (!d->m_adaptorModel.isValid() && d->m_adaptorModel.aim()) // The previous root index was invalidated, so we need to reconnect the model. + d->m_adaptorModel.setModel(d->m_adaptorModel.list.list(), this, d->m_context->engine()); + if (d->m_adaptorModel.canFetchMore()) + d->m_adaptorModel.fetchMore(); + if (d->m_complete) { + const int newCount = d->m_adaptorModel.count(); + if (oldCount) + _q_itemsRemoved(0, oldCount); + if (newCount) + _q_itemsInserted(0, newCount); + } + if (changed) + emit rootIndexChanged(); + } +} + +/*! + \qmlmethod QModelIndex QtQml.Models2::DelegateModel::modelIndex(int index) + + QAbstractItemModel provides a hierarchical tree of data, whereas + QML only operates on list data. This function assists in using + tree models in QML. + + Returns a QModelIndex for the specified index. + This value can be assigned to rootIndex. + + \sa rootIndex +*/ +QVariant QQmlDelegateModel::modelIndex(int idx) const +{ + Q_D(const QQmlDelegateModel); + return d->m_adaptorModel.modelIndex(idx); +} + +/*! + \qmlmethod QModelIndex QtQml.Models2::DelegateModel::parentModelIndex() + + QAbstractItemModel provides a hierarchical tree of data, whereas + QML only operates on list data. This function assists in using + tree models in QML. + + Returns a QModelIndex for the parent of the current rootIndex. + This value can be assigned to rootIndex. + + \sa rootIndex +*/ +QVariant QQmlDelegateModel::parentModelIndex() const +{ + Q_D(const QQmlDelegateModel); + return d->m_adaptorModel.parentModelIndex(); +} + +/*! + \qmlproperty int QtQml.Models2::DelegateModel::count +*/ + +int QQmlDelegateModel::count() const +{ + Q_D(const QQmlDelegateModel); + if (!d->m_delegate) + return 0; + return d->m_compositor.count(d->m_compositorGroup); +} + +QQmlDelegateModel::ReleaseFlags QQmlDelegateModelPrivate::release(QObject *object) +{ + QQmlDelegateModel::ReleaseFlags stat = 0; + if (!object) + return stat; + + if (QQmlDelegateModelItem *cacheItem = QQmlDelegateModelItem::dataForObject(object)) { + if (cacheItem->releaseObject()) { + cacheItem->destroyObject(); + emitDestroyingItem(object); + if (cacheItem->incubationTask) { + releaseIncubator(cacheItem->incubationTask); + cacheItem->incubationTask = 0; + } + cacheItem->Dispose(); + stat |= QQmlInstanceModel::Destroyed; + } else { + stat |= QQmlDelegateModel::Referenced; + } + } + return stat; +} + +/* + Returns ReleaseStatus flags. +*/ + +QQmlDelegateModel::ReleaseFlags QQmlDelegateModel::release(QObject *item) +{ + Q_D(QQmlDelegateModel); + QQmlInstanceModel::ReleaseFlags stat = d->release(item); + return stat; +} + +// Cancel a requested async item +void QQmlDelegateModel::cancel(int index) +{ + Q_D(QQmlDelegateModel); + if (!d->m_delegate || index < 0 || index >= d->m_compositor.count(d->m_compositorGroup)) { + qWarning() << "DelegateModel::cancel: index out range" << index << d->m_compositor.count(d->m_compositorGroup); + return; + } + + Compositor::iterator it = d->m_compositor.find(d->m_compositorGroup, index); + QQmlDelegateModelItem *cacheItem = it->inCache() ? d->m_cache.at(it.cacheIndex) : 0; + if (cacheItem) { + if (cacheItem->incubationTask && !cacheItem->isObjectReferenced()) { + d->releaseIncubator(cacheItem->incubationTask); + cacheItem->incubationTask = 0; + + if (cacheItem->object) { + QObject *object = cacheItem->object; + cacheItem->destroyObject(); + if (QQuickPackage *package = qmlobject_cast(object)) + d->emitDestroyingPackage(package); + else + d->emitDestroyingItem(object); + } + + cacheItem->scriptRef -= 1; + } + if (!cacheItem->isReferenced()) { + d->m_compositor.clearFlags(Compositor::Cache, it.cacheIndex, 1, Compositor::CacheFlag); + d->m_cache.removeAt(it.cacheIndex); + delete cacheItem; + Q_ASSERT(d->m_cache.count() == d->m_compositor.count(Compositor::Cache)); + } + } +} + +void QQmlDelegateModelPrivate::group_append( + QQmlListProperty *property, QQmlDelegateModelGroup *group) +{ + QQmlDelegateModelPrivate *d = static_cast(property->data); + if (d->m_complete) + return; + if (d->m_groupCount == Compositor::MaximumGroupCount) { + qmlInfo(d->q_func()) << QQmlDelegateModel::tr("The maximum number of supported DelegateModelGroups is 8"); + return; + } + d->m_groups[d->m_groupCount] = group; + d->m_groupCount += 1; +} + +int QQmlDelegateModelPrivate::group_count( + QQmlListProperty *property) +{ + QQmlDelegateModelPrivate *d = static_cast(property->data); + return d->m_groupCount - 1; +} + +QQmlDelegateModelGroup *QQmlDelegateModelPrivate::group_at( + QQmlListProperty *property, int index) +{ + QQmlDelegateModelPrivate *d = static_cast(property->data); + return index >= 0 && index < d->m_groupCount - 1 + ? d->m_groups[index + 1] + : 0; +} + +/*! + \qmlproperty list QtQml.Models2::DelegateModel::groups + + This property holds a visual data model's group definitions. + + Groups define a sub-set of the items in a visual data model and can be used to filter + a model. + + For every group defined in a DelegateModel two attached properties are added to each + delegate item. The first of the form DelegateModel.in\e{GroupName} holds whether the + item belongs to the group and the second DelegateModel.\e{groupName}Index holds the + index of the item in that group. + + The following example illustrates using groups to select items in a model. + + \snippet qml/visualdatagroup.qml 0 +*/ + +QQmlListProperty QQmlDelegateModel::groups() +{ + Q_D(QQmlDelegateModel); + return QQmlListProperty( + this, + d, + QQmlDelegateModelPrivate::group_append, + QQmlDelegateModelPrivate::group_count, + QQmlDelegateModelPrivate::group_at, + 0); +} + +/*! + \qmlproperty DelegateModelGroup QtQml.Models2::DelegateModel::items + + This property holds visual data model's default group to which all new items are added. +*/ + +QQmlDelegateModelGroup *QQmlDelegateModel::items() +{ + Q_D(QQmlDelegateModel); + return d->m_items; +} + +/*! + \qmlproperty DelegateModelGroup QtQml.Models2::DelegateModel::persistedItems + + This property holds visual data model's persisted items group. + + Items in this group are not destroyed when released by a view, instead they are persisted + until removed from the group. + + An item can be removed from the persistedItems group by setting the + DelegateModel.inPersistedItems property to false. If the item is not referenced by a view + at that time it will be destroyed. Adding an item to this group will not create a new + instance. + + Items returned by the \l QtQml.Models2::DelegateModelGroup::create() function are automatically added + to this group. +*/ + +QQmlDelegateModelGroup *QQmlDelegateModel::persistedItems() +{ + Q_D(QQmlDelegateModel); + return d->m_persistedItems; +} + +/*! + \qmlproperty string QtQml.Models2::DelegateModel::filterOnGroup + + This property holds the name of the group used to filter the visual data model. + + Only items which belong to this group are visible to a view. + + By default this is the \l items group. +*/ + +QString QQmlDelegateModel::filterGroup() const +{ + Q_D(const QQmlDelegateModel); + return d->m_filterGroup; +} + +void QQmlDelegateModel::setFilterGroup(const QString &group) +{ + Q_D(QQmlDelegateModel); + + if (d->m_transaction) { + qmlInfo(this) << tr("The group of a DelegateModel cannot be changed within onChanged"); + return; + } + + if (d->m_filterGroup != group) { + d->m_filterGroup = group; + d->updateFilterGroup(); + emit filterGroupChanged(); + } +} + +void QQmlDelegateModel::resetFilterGroup() +{ + setFilterGroup(QStringLiteral("items")); +} + +void QQmlDelegateModelPrivate::updateFilterGroup() +{ + Q_Q(QQmlDelegateModel); + if (!m_cacheMetaType) + return; + + QQmlListCompositor::Group previousGroup = m_compositorGroup; + m_compositorGroup = Compositor::Default; + for (int i = 1; i < m_groupCount; ++i) { + if (m_filterGroup == m_cacheMetaType->groupNames.at(i - 1)) { + m_compositorGroup = Compositor::Group(i); + break; + } + } + + QQmlDelegateModelGroupPrivate::get(m_groups[m_compositorGroup])->emitters.insert(this); + if (m_compositorGroup != previousGroup) { + QVector removes; + QVector inserts; + m_compositor.transition(previousGroup, m_compositorGroup, &removes, &inserts); + + QQmlChangeSet changeSet; + changeSet.move(removes, inserts); + emit q->modelUpdated(changeSet, false); + + if (changeSet.difference() != 0) + emit q->countChanged(); + + if (m_parts) { + foreach (QQmlPartsModel *model, m_parts->models) + model->updateFilterGroup(m_compositorGroup, changeSet); + } + } +} + +/*! + \qmlproperty object QtQml.Models2::DelegateModel::parts + + The \a parts property selects a DelegateModel which creates + delegates from the part named. This is used in conjunction with + the \l Package type. + + For example, the code below selects a model which creates + delegates named \e list from a \l Package: + + \code + DelegateModel { + id: visualModel + delegate: Package { + Item { Package.name: "list" } + } + model: myModel + } + + ListView { + width: 200; height:200 + model: visualModel.parts.list + } + \endcode + + \sa Package +*/ + +QObject *QQmlDelegateModel::parts() +{ + Q_D(QQmlDelegateModel); + if (!d->m_parts) + d->m_parts = new QQmlDelegateModelParts(this); + return d->m_parts; +} + +void QQmlDelegateModelPrivate::emitCreatedPackage(QQDMIncubationTask *incubationTask, QQuickPackage *package) +{ + for (int i = 1; i < m_groupCount; ++i) + QQmlDelegateModelGroupPrivate::get(m_groups[i])->createdPackage(incubationTask->index[i], package); +} + +void QQmlDelegateModelPrivate::emitInitPackage(QQDMIncubationTask *incubationTask, QQuickPackage *package) +{ + for (int i = 1; i < m_groupCount; ++i) + QQmlDelegateModelGroupPrivate::get(m_groups[i])->initPackage(incubationTask->index[i], package); +} + +void QQmlDelegateModelPrivate::emitDestroyingPackage(QQuickPackage *package) +{ + for (int i = 1; i < m_groupCount; ++i) + QQmlDelegateModelGroupPrivate::get(m_groups[i])->destroyingPackage(package); +} + +void QQDMIncubationTask::statusChanged(Status status) +{ + vdm->incubatorStatusChanged(this, status); +} + +void QQmlDelegateModelPrivate::releaseIncubator(QQDMIncubationTask *incubationTask) +{ + Q_Q(QQmlDelegateModel); + if (!incubationTask->isError()) + incubationTask->clear(); + m_finishedIncubating.append(incubationTask); + if (!m_incubatorCleanupScheduled) { + m_incubatorCleanupScheduled = true; + QCoreApplication::postEvent(q, new QEvent(QEvent::User)); + } +} + +void QQmlDelegateModelPrivate::removeCacheItem(QQmlDelegateModelItem *cacheItem) +{ + int cidx = m_cache.indexOf(cacheItem); + if (cidx >= 0) { + m_compositor.clearFlags(Compositor::Cache, cidx, 1, Compositor::CacheFlag); + m_cache.removeAt(cidx); + } + Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); +} + +void QQmlDelegateModelPrivate::incubatorStatusChanged(QQDMIncubationTask *incubationTask, QQmlIncubator::Status status) +{ + Q_Q(QQmlDelegateModel); + if (status != QQmlIncubator::Ready && status != QQmlIncubator::Error) + return; + + QQmlDelegateModelItem *cacheItem = incubationTask->incubating; + cacheItem->incubationTask = 0; + incubationTask->incubating = 0; + releaseIncubator(incubationTask); + + if (status == QQmlIncubator::Ready) { + if (QQuickPackage *package = qmlobject_cast(cacheItem->object)) + emitCreatedPackage(incubationTask, package); + else + emitCreatedItem(incubationTask, cacheItem->object); + } else if (status == QQmlIncubator::Error) { + qmlInfo(q, m_delegate->errors()) << "Error creating delegate"; + } + + if (!cacheItem->isObjectReferenced()) { + if (QQuickPackage *package = qmlobject_cast(cacheItem->object)) + emitDestroyingPackage(package); + else + emitDestroyingItem(cacheItem->object); + delete cacheItem->object; + cacheItem->object = 0; + cacheItem->scriptRef -= 1; + cacheItem->contextData->destroy(); + cacheItem->contextData = 0; + if (!cacheItem->isReferenced()) { + removeCacheItem(cacheItem); + delete cacheItem; + } + } +} + +void QQDMIncubationTask::setInitialState(QObject *o) +{ + vdm->setInitialState(this, o); +} + +void QQmlDelegateModelPrivate::setInitialState(QQDMIncubationTask *incubationTask, QObject *o) +{ + QQmlDelegateModelItem *cacheItem = incubationTask->incubating; + cacheItem->object = o; + + if (QQuickPackage *package = qmlobject_cast(cacheItem->object)) + emitInitPackage(incubationTask, package); + else + emitInitItem(incubationTask, cacheItem->object); +} + +QObject *QQmlDelegateModelPrivate::object(Compositor::Group group, int index, bool asynchronous) +{ + Q_Q(QQmlDelegateModel); + if (!m_delegate || index < 0 || index >= m_compositor.count(group)) { + qWarning() << "DelegateModel::item: index out range" << index << m_compositor.count(group); + return 0; + } else if (!m_context->isValid()) { + return 0; + } + + Compositor::iterator it = m_compositor.find(group, index); + + QQmlDelegateModelItem *cacheItem = it->inCache() ? m_cache.at(it.cacheIndex) : 0; + + if (!cacheItem) { + cacheItem = m_adaptorModel.createItem(m_cacheMetaType, m_context->engine(), it.modelIndex()); + if (!cacheItem) + return 0; + + cacheItem->groups = it->flags; + + m_cache.insert(it.cacheIndex, cacheItem); + m_compositor.setFlags(it, 1, Compositor::CacheFlag); + Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); + } + + // Bump the reference counts temporarily so neither the content data or the delegate object + // are deleted if incubatorStatusChanged() is called synchronously. + cacheItem->scriptRef += 1; + cacheItem->referenceObject(); + + if (cacheItem->incubationTask) { + if (!asynchronous && cacheItem->incubationTask->incubationMode() == QQmlIncubator::Asynchronous) { + // previously requested async - now needed immediately + cacheItem->incubationTask->forceCompletion(); + } + } else if (!cacheItem->object) { + QQmlContext *creationContext = m_delegate->creationContext(); + + cacheItem->scriptRef += 1; + + cacheItem->incubationTask = new QQDMIncubationTask(this, asynchronous ? QQmlIncubator::Asynchronous : QQmlIncubator::AsynchronousIfNested); + cacheItem->incubationTask->incubating = cacheItem; + cacheItem->incubationTask->clear(); + + for (int i = 1; i < m_groupCount; ++i) + cacheItem->incubationTask->index[i] = it.index[i]; + + QQmlContextData *ctxt = new QQmlContextData; + ctxt->setParent(QQmlContextData::get(creationContext ? creationContext : m_context)); + ctxt->contextObject = cacheItem; + cacheItem->contextData = ctxt; + + if (m_adaptorModel.hasProxyObject()) { + if (QQmlAdaptorModelProxyInterface *proxy + = qobject_cast(cacheItem)) { + ctxt = new QQmlContextData; + ctxt->setParent(cacheItem->contextData, true); + ctxt->contextObject = proxy->proxiedObject(); + } + } + + cacheItem->incubateObject( + m_delegate, + m_context->engine(), + ctxt, + QQmlContextData::get(m_context)); + } + + if (index == m_compositor.count(group) - 1 && m_adaptorModel.canFetchMore()) + QCoreApplication::postEvent(q, new QEvent(QEvent::UpdateRequest)); + + // Remove the temporary reference count. + cacheItem->scriptRef -= 1; + if (cacheItem->object) + return cacheItem->object; + + cacheItem->releaseObject(); + if (!cacheItem->isReferenced()) { + removeCacheItem(cacheItem); + delete cacheItem; + } + + return 0; +} + +/* + If asynchronous is true or the component is being loaded asynchronously due + to an ancestor being loaded asynchronously, item() may return 0. In this + case itemCreated() will be emitted when the item is available. The item + at this stage does not have any references, so item() must be called again + to ensure a reference is held. Any call to item() which returns a valid item + must be matched by a call to release() in order to destroy the item. +*/ +QObject *QQmlDelegateModel::object(int index, bool asynchronous) +{ + Q_D(QQmlDelegateModel); + if (!d->m_delegate || index < 0 || index >= d->m_compositor.count(d->m_compositorGroup)) { + qWarning() << "DelegateModel::item: index out range" << index << d->m_compositor.count(d->m_compositorGroup); + return 0; + } + + QObject *object = d->object(d->m_compositorGroup, index, asynchronous); + if (!object) + return 0; + + return object; +} + +QString QQmlDelegateModelPrivate::stringValue(Compositor::Group group, int index, const QString &name) +{ + Compositor::iterator it = m_compositor.find(group, index); + if (QQmlAdaptorModel *model = it.list()) { + QString role = name; + int dot = name.indexOf(QLatin1Char('.')); + if (dot > 0) + role = name.left(dot); + QVariant value = model->value(it.modelIndex(), role); + while (dot > 0) { + QObject *obj = qvariant_cast(value); + if (!obj) + return QString(); + int from = dot+1; + dot = name.indexOf(QLatin1Char('.'), from); + value = obj->property(name.mid(from, dot-from).toUtf8()); + } + return value.toString(); + } + return QString(); +} + +QString QQmlDelegateModel::stringValue(int index, const QString &name) +{ + Q_D(QQmlDelegateModel); + return d->stringValue(d->m_compositorGroup, index, name); +} + +int QQmlDelegateModel::indexOf(QObject *item, QObject *) const +{ + Q_D(const QQmlDelegateModel); + if (QQmlDelegateModelItem *cacheItem = QQmlDelegateModelItem::dataForObject(item)) + return cacheItem->groupIndex(d->m_compositorGroup); + return -1; +} + +void QQmlDelegateModel::setWatchedRoles(QList roles) +{ + Q_D(QQmlDelegateModel); + d->m_adaptorModel.replaceWatchedRoles(d->m_watchedRoles, roles); + d->m_watchedRoles = roles; +} + +void QQmlDelegateModelPrivate::addGroups( + Compositor::iterator from, int count, Compositor::Group group, int groupFlags) +{ + QVector inserts; + m_compositor.setFlags(from, count, group, groupFlags, &inserts); + itemsInserted(inserts); + emitChanges(); +} + +void QQmlDelegateModelPrivate::removeGroups( + Compositor::iterator from, int count, Compositor::Group group, int groupFlags) +{ + QVector removes; + m_compositor.clearFlags(from, count, group, groupFlags, &removes); + itemsRemoved(removes); + emitChanges(); +} + +void QQmlDelegateModelPrivate::setGroups( + Compositor::iterator from, int count, Compositor::Group group, int groupFlags) +{ + QVector removes; + QVector inserts; + + m_compositor.setFlags(from, count, group, groupFlags, &inserts); + itemsInserted(inserts); + const int removeFlags = ~groupFlags & Compositor::GroupMask; + + from = m_compositor.find(from.group, from.index[from.group]); + m_compositor.clearFlags(from, count, group, removeFlags, &removes); + itemsRemoved(removes); + emitChanges(); +} + +bool QQmlDelegateModel::event(QEvent *e) +{ + Q_D(QQmlDelegateModel); + if (e->type() == QEvent::UpdateRequest) { + d->m_adaptorModel.fetchMore(); + } else if (e->type() == QEvent::User) { + d->m_incubatorCleanupScheduled = false; + qDeleteAll(d->m_finishedIncubating); + d->m_finishedIncubating.clear(); + } + return QQmlInstanceModel::event(e); +} + +void QQmlDelegateModelPrivate::itemsChanged(const QVector &changes) +{ + if (!m_delegate) + return; + + QVarLengthArray, Compositor::MaximumGroupCount> translatedChanges(m_groupCount); + + foreach (const Compositor::Change &change, changes) { + for (int i = 1; i < m_groupCount; ++i) { + if (change.inGroup(i)) { + translatedChanges[i].append(QQmlChangeSet::Change(change.index[i], change.count)); + } + } + } + + for (int i = 1; i < m_groupCount; ++i) + QQmlDelegateModelGroupPrivate::get(m_groups[i])->changeSet.change(translatedChanges.at(i)); +} + +void QQmlDelegateModel::_q_itemsChanged(int index, int count, const QVector &roles) +{ + Q_D(QQmlDelegateModel); + if (count <= 0 || !d->m_complete) + return; + + if (d->m_adaptorModel.notify(d->m_cache, index, count, roles)) { + QVector changes; + d->m_compositor.listItemsChanged(&d->m_adaptorModel, index, count, &changes); + d->itemsChanged(changes); + d->emitChanges(); + } +} + +static void incrementIndexes(QQmlDelegateModelItem *cacheItem, int count, const int *deltas) +{ + if (QQDMIncubationTask *incubationTask = cacheItem->incubationTask) { + for (int i = 1; i < count; ++i) + incubationTask->index[i] += deltas[i]; + } + if (QQmlDelegateModelAttached *attached = cacheItem->attached) { + for (int i = 1; i < count; ++i) + attached->m_currentIndex[i] += deltas[i]; + } +} + +void QQmlDelegateModelPrivate::itemsInserted( + const QVector &inserts, + QVarLengthArray, Compositor::MaximumGroupCount> *translatedInserts, + QHash > *movedItems) +{ + int cacheIndex = 0; + + int inserted[Compositor::MaximumGroupCount]; + for (int i = 1; i < m_groupCount; ++i) + inserted[i] = 0; + + foreach (const Compositor::Insert &insert, inserts) { + for (; cacheIndex < insert.cacheIndex; ++cacheIndex) + incrementIndexes(m_cache.at(cacheIndex), m_groupCount, inserted); + + for (int i = 1; i < m_groupCount; ++i) { + if (insert.inGroup(i)) { + (*translatedInserts)[i].append( + QQmlChangeSet::Insert(insert.index[i], insert.count, insert.moveId)); + inserted[i] += insert.count; + } + } + + if (!insert.inCache()) + continue; + + if (movedItems && insert.isMove()) { + QList items = movedItems->take(insert.moveId); + Q_ASSERT(items.count() == insert.count); + m_cache = m_cache.mid(0, insert.cacheIndex) + items + m_cache.mid(insert.cacheIndex); + } + if (insert.inGroup()) { + for (int offset = 0; cacheIndex < insert.cacheIndex + insert.count; ++cacheIndex, ++offset) { + QQmlDelegateModelItem *cacheItem = m_cache.at(cacheIndex); + cacheItem->groups |= insert.flags & Compositor::GroupMask; + + if (QQDMIncubationTask *incubationTask = cacheItem->incubationTask) { + for (int i = 1; i < m_groupCount; ++i) + incubationTask->index[i] = cacheItem->groups & (1 << i) + ? insert.index[i] + offset + : insert.index[i]; + } + if (QQmlDelegateModelAttached *attached = cacheItem->attached) { + for (int i = 1; i < m_groupCount; ++i) + attached->m_currentIndex[i] = cacheItem->groups & (1 << i) + ? insert.index[i] + offset + : insert.index[i]; + } + } + } else { + cacheIndex = insert.cacheIndex + insert.count; + } + } + for (; cacheIndex < m_cache.count(); ++cacheIndex) + incrementIndexes(m_cache.at(cacheIndex), m_groupCount, inserted); +} + +void QQmlDelegateModelPrivate::itemsInserted(const QVector &inserts) +{ + QVarLengthArray, Compositor::MaximumGroupCount> translatedInserts(m_groupCount); + itemsInserted(inserts, &translatedInserts); + Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); + if (!m_delegate) + return; + + for (int i = 1; i < m_groupCount; ++i) + QQmlDelegateModelGroupPrivate::get(m_groups[i])->changeSet.insert(translatedInserts.at(i)); +} + +void QQmlDelegateModel::_q_itemsInserted(int index, int count) +{ + + Q_D(QQmlDelegateModel); + if (count <= 0 || !d->m_complete) + return; + + d->m_count += count; + + for (int i = 0, c = d->m_cache.count(); i < c; ++i) { + QQmlDelegateModelItem *item = d->m_cache.at(i); + if (item->modelIndex() >= index) + item->setModelIndex(item->modelIndex() + count); + } + + QVector inserts; + d->m_compositor.listItemsInserted(&d->m_adaptorModel, index, count, &inserts); + d->itemsInserted(inserts); + d->emitChanges(); +} + +void QQmlDelegateModelPrivate::itemsRemoved( + const QVector &removes, + QVarLengthArray, Compositor::MaximumGroupCount> *translatedRemoves, + QHash > *movedItems) +{ + int cacheIndex = 0; + int removedCache = 0; + + int removed[Compositor::MaximumGroupCount]; + for (int i = 1; i < m_groupCount; ++i) + removed[i] = 0; + + foreach (const Compositor::Remove &remove, removes) { + for (; cacheIndex < remove.cacheIndex; ++cacheIndex) + incrementIndexes(m_cache.at(cacheIndex), m_groupCount, removed); + + for (int i = 1; i < m_groupCount; ++i) { + if (remove.inGroup(i)) { + (*translatedRemoves)[i].append( + QQmlChangeSet::Remove(remove.index[i], remove.count, remove.moveId)); + removed[i] -= remove.count; + } + } + + if (!remove.inCache()) + continue; + + if (movedItems && remove.isMove()) { + movedItems->insert(remove.moveId, m_cache.mid(remove.cacheIndex, remove.count)); + QList::iterator begin = m_cache.begin() + remove.cacheIndex; + QList::iterator end = begin + remove.count; + m_cache.erase(begin, end); + } else { + for (; cacheIndex < remove.cacheIndex + remove.count - removedCache; ++cacheIndex) { + QQmlDelegateModelItem *cacheItem = m_cache.at(cacheIndex); + if (remove.inGroup(Compositor::Persisted) && cacheItem->objectRef == 0 && cacheItem->object) { + QObject *object = cacheItem->object; + cacheItem->destroyObject(); + if (QQuickPackage *package = qmlobject_cast(object)) + emitDestroyingPackage(package); + else + emitDestroyingItem(object); + cacheItem->scriptRef -= 1; + } + if (!cacheItem->isReferenced()) { + m_compositor.clearFlags(Compositor::Cache, cacheIndex, 1, Compositor::CacheFlag); + m_cache.removeAt(cacheIndex); + delete cacheItem; + --cacheIndex; + ++removedCache; + Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); + } else if (remove.groups() == cacheItem->groups) { + cacheItem->groups = 0; + if (QQDMIncubationTask *incubationTask = cacheItem->incubationTask) { + for (int i = 1; i < m_groupCount; ++i) + incubationTask->index[i] = -1; + } + if (QQmlDelegateModelAttached *attached = cacheItem->attached) { + for (int i = 1; i < m_groupCount; ++i) + attached->m_currentIndex[i] = -1; + } + } else { + if (QQDMIncubationTask *incubationTask = cacheItem->incubationTask) { + for (int i = 1; i < m_groupCount; ++i) { + if (remove.inGroup(i)) + incubationTask->index[i] = remove.index[i]; + } + } + if (QQmlDelegateModelAttached *attached = cacheItem->attached) { + for (int i = 1; i < m_groupCount; ++i) { + if (remove.inGroup(i)) + attached->m_currentIndex[i] = remove.index[i]; + } + } + cacheItem->groups &= ~remove.flags; + } + } + } + } + + for (; cacheIndex < m_cache.count(); ++cacheIndex) + incrementIndexes(m_cache.at(cacheIndex), m_groupCount, removed); +} + +void QQmlDelegateModelPrivate::itemsRemoved(const QVector &removes) +{ + QVarLengthArray, Compositor::MaximumGroupCount> translatedRemoves(m_groupCount); + itemsRemoved(removes, &translatedRemoves); + Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); + if (!m_delegate) + return; + + for (int i = 1; i < m_groupCount; ++i) + QQmlDelegateModelGroupPrivate::get(m_groups[i])->changeSet.remove(translatedRemoves.at(i)); +} + +void QQmlDelegateModel::_q_itemsRemoved(int index, int count) +{ + Q_D(QQmlDelegateModel); + if (count <= 0|| !d->m_complete) + return; + + d->m_count -= count; + + for (int i = 0, c = d->m_cache.count(); i < c; ++i) { + QQmlDelegateModelItem *item = d->m_cache.at(i); + if (item->modelIndex() >= index + count) + item->setModelIndex(item->modelIndex() - count); + else if (item->modelIndex() >= index) + item->setModelIndex(-1); + } + + QVector removes; + d->m_compositor.listItemsRemoved(&d->m_adaptorModel, index, count, &removes); + d->itemsRemoved(removes); + + d->emitChanges(); +} + +void QQmlDelegateModelPrivate::itemsMoved( + const QVector &removes, const QVector &inserts) +{ + QHash > movedItems; + + QVarLengthArray, Compositor::MaximumGroupCount> translatedRemoves(m_groupCount); + itemsRemoved(removes, &translatedRemoves, &movedItems); + + QVarLengthArray, Compositor::MaximumGroupCount> translatedInserts(m_groupCount); + itemsInserted(inserts, &translatedInserts, &movedItems); + Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); + Q_ASSERT(movedItems.isEmpty()); + if (!m_delegate) + return; + + for (int i = 1; i < m_groupCount; ++i) { + QQmlDelegateModelGroupPrivate::get(m_groups[i])->changeSet.move( + translatedRemoves.at(i), + translatedInserts.at(i)); + } +} + +void QQmlDelegateModel::_q_itemsMoved(int from, int to, int count) +{ + Q_D(QQmlDelegateModel); + if (count <= 0 || !d->m_complete) + return; + + const int minimum = qMin(from, to); + const int maximum = qMax(from, to) + count; + const int difference = from > to ? count : -count; + + for (int i = 0, c = d->m_cache.count(); i < c; ++i) { + QQmlDelegateModelItem *item = d->m_cache.at(i); + if (item->modelIndex() >= from && item->modelIndex() < from + count) + item->setModelIndex(item->modelIndex() - from + to); + else if (item->modelIndex() >= minimum && item->modelIndex() < maximum) + item->setModelIndex(item->modelIndex() + difference); + } + + QVector removes; + QVector inserts; + d->m_compositor.listItemsMoved(&d->m_adaptorModel, from, to, count, &removes, &inserts); + d->itemsMoved(removes, inserts); + d->emitChanges(); +} + +template v8::Local +QQmlDelegateModelPrivate::buildChangeList(const QVector &changes) +{ + v8::Local indexes = v8::Array::New(changes.count()); + v8::Local indexKey = v8::String::New("index"); + v8::Local countKey = v8::String::New("count"); + v8::Local moveIdKey = v8::String::New("moveId"); + + for (int i = 0; i < changes.count(); ++i) { + v8::Local object = v8::Object::New(); + object->Set(indexKey, v8::Integer::New(changes.at(i).index)); + object->Set(countKey, v8::Integer::New(changes.at(i).count)); + object->Set(moveIdKey, changes.at(i).moveId != -1 ? v8::Integer::New(changes.at(i).count) : v8::Undefined()); + indexes->Set(i, object); + } + return indexes; +} + +void QQmlDelegateModelPrivate::emitModelUpdated(const QQmlChangeSet &changeSet, bool reset) +{ + Q_Q(QQmlDelegateModel); + emit q->modelUpdated(changeSet, reset); + if (changeSet.difference() != 0) + emit q->countChanged(); +} + +void QQmlDelegateModelPrivate::emitChanges() +{ + if (m_transaction || !m_complete || !m_context->isValid()) + return; + + m_transaction = true; + QV8Engine *engine = QQmlEnginePrivate::getV8Engine(m_context->engine()); + for (int i = 1; i < m_groupCount; ++i) + QQmlDelegateModelGroupPrivate::get(m_groups[i])->emitChanges(engine); + m_transaction = false; + + const bool reset = m_reset; + m_reset = false; + for (int i = 1; i < m_groupCount; ++i) + QQmlDelegateModelGroupPrivate::get(m_groups[i])->emitModelUpdated(reset); + + foreach (QQmlDelegateModelItem *cacheItem, m_cache) { + if (cacheItem->attached) + cacheItem->attached->emitChanges(); + } +} + +void QQmlDelegateModel::_q_modelReset() +{ + Q_D(QQmlDelegateModel); + if (!d->m_delegate) + return; + + int oldCount = d->m_count; + d->m_adaptorModel.rootIndex = QModelIndex(); + + if (d->m_complete) { + d->m_count = d->m_adaptorModel.count(); + + for (int i = 0, c = d->m_cache.count(); i < c; ++i) { + QQmlDelegateModelItem *item = d->m_cache.at(i); + if (item->modelIndex() != -1) + item->setModelIndex(-1); + } + + QVector removes; + QVector inserts; + if (oldCount) + d->m_compositor.listItemsRemoved(&d->m_adaptorModel, 0, oldCount, &removes); + if (d->m_count) + d->m_compositor.listItemsInserted(&d->m_adaptorModel, 0, d->m_count, &inserts); + d->itemsMoved(removes, inserts); + d->m_reset = true; + + if (d->m_adaptorModel.canFetchMore()) + d->m_adaptorModel.fetchMore(); + + d->emitChanges(); + } + emit rootIndexChanged(); +} + +void QQmlDelegateModel::_q_rowsInserted(const QModelIndex &parent, int begin, int end) +{ + Q_D(QQmlDelegateModel); + if (parent == d->m_adaptorModel.rootIndex) + _q_itemsInserted(begin, end - begin + 1); +} + +void QQmlDelegateModel::_q_rowsAboutToBeRemoved(const QModelIndex &parent, int begin, int end) +{ + Q_D(QQmlDelegateModel); + if (!d->m_adaptorModel.rootIndex.isValid()) + return; + const QModelIndex index = d->m_adaptorModel.rootIndex; + if (index.parent() == parent && index.row() >= begin && index.row() <= end) { + const int oldCount = d->m_count; + d->m_count = 0; + d->m_adaptorModel.invalidateModel(this); + + if (d->m_complete && oldCount > 0) { + QVector removes; + d->m_compositor.listItemsRemoved(&d->m_adaptorModel, 0, oldCount, &removes); + d->itemsRemoved(removes); + d->emitChanges(); + } + } +} + +void QQmlDelegateModel::_q_rowsRemoved(const QModelIndex &parent, int begin, int end) +{ + Q_D(QQmlDelegateModel); + if (parent == d->m_adaptorModel.rootIndex) + _q_itemsRemoved(begin, end - begin + 1); +} + +void QQmlDelegateModel::_q_rowsMoved( + const QModelIndex &sourceParent, int sourceStart, int sourceEnd, + const QModelIndex &destinationParent, int destinationRow) +{ + Q_D(QQmlDelegateModel); + const int count = sourceEnd - sourceStart + 1; + if (destinationParent == d->m_adaptorModel.rootIndex && sourceParent == d->m_adaptorModel.rootIndex) { + _q_itemsMoved(sourceStart, sourceStart > destinationRow ? destinationRow : destinationRow - count, count); + } else if (sourceParent == d->m_adaptorModel.rootIndex) { + _q_itemsRemoved(sourceStart, count); + } else if (destinationParent == d->m_adaptorModel.rootIndex) { + _q_itemsInserted(destinationRow, count); + } +} + +void QQmlDelegateModel::_q_dataChanged(const QModelIndex &begin, const QModelIndex &end, const QVector &roles) +{ + Q_D(QQmlDelegateModel); + if (begin.parent() == d->m_adaptorModel.rootIndex) + _q_itemsChanged(begin.row(), end.row() - begin.row() + 1, roles); +} + +void QQmlDelegateModel::_q_layoutChanged() +{ + Q_D(QQmlDelegateModel); + _q_itemsChanged(0, d->m_count, QVector()); +} + +QQmlDelegateModelAttached *QQmlDelegateModel::qmlAttachedProperties(QObject *obj) +{ + if (QQmlDelegateModelItem *cacheItem = QQmlDelegateModelItem::dataForObject(obj)) { + if (cacheItem->object == obj) { // Don't create attached item for child objects. + cacheItem->attached = new QQmlDelegateModelAttached(cacheItem, obj); + return cacheItem->attached; + } + } + return new QQmlDelegateModelAttached(obj); +} + +bool QQmlDelegateModelPrivate::insert( + Compositor::insert_iterator &before, const v8::Local &object, int groups) +{ + if (!m_context->isValid()) + return false; + + QQmlDelegateModelItem *cacheItem = m_adaptorModel.createItem(m_cacheMetaType, m_context->engine(), -1); + if (!cacheItem) + return false; + + v8::Local propertyNames = object->GetPropertyNames(); + for (uint i = 0; i < propertyNames->Length(); ++i) { + v8::Local propertyName = propertyNames->Get(i)->ToString(); + cacheItem->setValue( + m_cacheMetaType->v8Engine->toString(propertyName), + m_cacheMetaType->v8Engine->toVariant(object->Get(propertyName), QVariant::Invalid)); + } + + cacheItem->groups = groups | Compositor::UnresolvedFlag | Compositor::CacheFlag; + + // Must be before the new object is inserted into the cache or its indexes will be adjusted too. + itemsInserted(QVector() << Compositor::Insert(before, 1, cacheItem->groups & ~Compositor::CacheFlag)); + + before = m_compositor.insert(before, 0, 0, 1, cacheItem->groups); + m_cache.insert(before.cacheIndex, cacheItem); + + return true; +} + +//============================================================================ + +QQmlDelegateModelItemMetaType::QQmlDelegateModelItemMetaType( + QV8Engine *engine, QQmlDelegateModel *model, const QStringList &groupNames) + : model(model) + , groupCount(groupNames.count() + 1) + , v8Engine(engine) + , metaObject(0) + , groupNames(groupNames) +{ +} + +QQmlDelegateModelItemMetaType::~QQmlDelegateModelItemMetaType() +{ + if (metaObject) + metaObject->release(); + qPersistentDispose(constructor); +} + +void QQmlDelegateModelItemMetaType::initializeMetaObject() +{ + QMetaObjectBuilder builder; + builder.setFlags(QMetaObjectBuilder::DynamicMetaObject); + builder.setClassName(QQmlDelegateModelAttached::staticMetaObject.className()); + builder.setSuperClass(&QQmlDelegateModelAttached::staticMetaObject); + + int notifierId = 0; + for (int i = 0; i < groupNames.count(); ++i, ++notifierId) { + QString propertyName = QStringLiteral("in") + groupNames.at(i); + propertyName.replace(2, 1, propertyName.at(2).toUpper()); + builder.addSignal("__" + propertyName.toUtf8() + "Changed()"); + QMetaPropertyBuilder propertyBuilder = builder.addProperty( + propertyName.toUtf8(), "bool", notifierId); + propertyBuilder.setWritable(true); + } + for (int i = 0; i < groupNames.count(); ++i, ++notifierId) { + const QString propertyName = groupNames.at(i) + QStringLiteral("Index"); + builder.addSignal("__" + propertyName.toUtf8() + "Changed()"); + QMetaPropertyBuilder propertyBuilder = builder.addProperty( + propertyName.toUtf8(), "int", notifierId); + propertyBuilder.setWritable(true); + } + + metaObject = new QQmlDelegateModelAttachedMetaObject(this, builder.toMetaObject()); +} + +void QQmlDelegateModelItemMetaType::initializeConstructor() +{ + v8::HandleScope handleScope; + v8::Context::Scope contextScope(v8Engine->context()); + + QQmlDelegateModelEngineData *data = engineData(v8Engine); + + constructor = qPersistentNew(v8::ObjectTemplate::New()); + + constructor->SetHasExternalResource(true); + constructor->SetAccessor(data->model(), get_model); + constructor->SetAccessor(data->groups(), get_groups, set_groups); + constructor->SetAccessor(data->isUnresolved(), get_member, 0, v8::Int32::New(30)); + constructor->SetAccessor(data->inItems(), get_member, set_member, v8::Int32::New(1)); + constructor->SetAccessor(data->inPersistedItems(), get_member, set_member, v8::Int32::New(2)); + constructor->SetAccessor(data->itemsIndex(), get_index, 0, v8::Int32::New(1)); + constructor->SetAccessor(data->persistedItemsIndex(), get_index, 0, v8::Int32::New(2)); + + for (int i = 2; i < groupNames.count(); ++i) { + QString propertyName = QStringLiteral("in") + groupNames.at(i); + propertyName.replace(2, 1, propertyName.at(2).toUpper()); + constructor->SetAccessor( + v8Engine->toString(propertyName), get_member, set_member, v8::Int32::New(i + 1)); + } + for (int i = 2; i < groupNames.count(); ++i) { + const QString propertyName = groupNames.at(i) + QStringLiteral("Index"); + constructor->SetAccessor( + v8Engine->toString(propertyName), get_index, 0, v8::Int32::New(i + 1)); + } +} + +int QQmlDelegateModelItemMetaType::parseGroups(const QStringList &groups) const +{ + int groupFlags = 0; + foreach (const QString &groupName, groups) { + int index = groupNames.indexOf(groupName); + if (index != -1) + groupFlags |= 2 << index; + } + return groupFlags; +} + +int QQmlDelegateModelItemMetaType::parseGroups(const v8::Local &groups) const +{ + int groupFlags = 0; + if (groups->IsString()) { + const QString groupName = v8Engine->toString(groups); + int index = groupNames.indexOf(groupName); + if (index != -1) + groupFlags |= 2 << index; + } else if (groups->IsArray()) { + v8::Local array = v8::Local::Cast(groups); + for (uint i = 0; i < array->Length(); ++i) { + const QString groupName = v8Engine->toString(array->Get(i)); + int index = groupNames.indexOf(groupName); + if (index != -1) + groupFlags |= 2 << index; + } + } + return groupFlags; +} + +v8::Handle QQmlDelegateModelItemMetaType::get_model( + v8::Local, const v8::AccessorInfo &info) +{ + QQmlDelegateModelItem *cacheItem = v8_resource_cast(info.This()); + V8ASSERT_TYPE(cacheItem, "Not a valid VisualData object"); + if (!cacheItem->metaType->model) + return v8::Undefined(); + + return cacheItem->get(); +} + +v8::Handle QQmlDelegateModelItemMetaType::get_groups( + v8::Local, const v8::AccessorInfo &info) +{ + QQmlDelegateModelItem *cacheItem = v8_resource_cast(info.This()); + V8ASSERT_TYPE(cacheItem, "Not a valid VisualData object"); + + QStringList groups; + for (int i = 1; i < cacheItem->metaType->groupCount; ++i) { + if (cacheItem->groups & (1 << i)) + groups.append(cacheItem->metaType->groupNames.at(i - 1)); + } + + return cacheItem->engine->fromVariant(groups); +} + +void QQmlDelegateModelItemMetaType::set_groups( + v8::Local, v8::Local value, const v8::AccessorInfo &info) +{ + QQmlDelegateModelItem *cacheItem = v8_resource_cast(info.This()); + V8ASSERT_TYPE_SETTER(cacheItem, "Not a valid VisualData object"); + + if (!cacheItem->metaType->model) + return; + QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(cacheItem->metaType->model); + + const int groupFlags = model->m_cacheMetaType->parseGroups(value); + const int cacheIndex = model->m_cache.indexOf(cacheItem); + Compositor::iterator it = model->m_compositor.find(Compositor::Cache, cacheIndex); + model->setGroups(it, 1, Compositor::Cache, groupFlags); +} + +v8::Handle QQmlDelegateModelItemMetaType::get_member( + v8::Local, const v8::AccessorInfo &info) +{ + QQmlDelegateModelItem *cacheItem = v8_resource_cast(info.This()); + V8ASSERT_TYPE(cacheItem, "Not a valid VisualData object"); + + return v8::Boolean::New(cacheItem->groups & (1 << info.Data()->Int32Value())); +} + +void QQmlDelegateModelItemMetaType::set_member( + v8::Local, v8::Local value, const v8::AccessorInfo &info) +{ + QQmlDelegateModelItem *cacheItem = v8_resource_cast(info.This()); + V8ASSERT_TYPE_SETTER(cacheItem, "Not a valid VisualData object"); + + if (!cacheItem->metaType->model) + return; + QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(cacheItem->metaType->model); + + Compositor::Group group = Compositor::Group(info.Data()->Int32Value()); + const bool member = value->BooleanValue(); + const int groupFlag = (1 << group); + if (member == ((cacheItem->groups & groupFlag) != 0)) + return; + + const int cacheIndex = model->m_cache.indexOf(cacheItem); + Compositor::iterator it = model->m_compositor.find(Compositor::Cache, cacheIndex); + if (member) + model->addGroups(it, 1, Compositor::Cache, groupFlag); + else + model->removeGroups(it, 1, Compositor::Cache, groupFlag); +} + +v8::Handle QQmlDelegateModelItemMetaType::get_index( + v8::Local, const v8::AccessorInfo &info) +{ + QQmlDelegateModelItem *cacheItem = v8_resource_cast(info.This()); + V8ASSERT_TYPE(cacheItem, "Not a valid VisualData object"); + + return v8::Integer::New(cacheItem->groupIndex(Compositor::Group(info.Data()->Int32Value()))); +} + + +//--------------------------------------------------------------------------- + +QQmlDelegateModelItem::QQmlDelegateModelItem( + QQmlDelegateModelItemMetaType *metaType, int modelIndex) + : QV8ObjectResource(metaType->v8Engine) + , metaType(metaType) + , contextData(0) + , object(0) + , attached(0) + , incubationTask(0) + , objectRef(0) + , scriptRef(0) + , groups(0) + , index(modelIndex) +{ + metaType->addref(); +} + +QQmlDelegateModelItem::~QQmlDelegateModelItem() +{ + Q_ASSERT(scriptRef == 0); + Q_ASSERT(objectRef == 0); + Q_ASSERT(!object); + + if (incubationTask && metaType->model) + QQmlDelegateModelPrivate::get(metaType->model)->releaseIncubator(incubationTask); + + metaType->release(); + +} + +void QQmlDelegateModelItem::Dispose() +{ + --scriptRef; + if (isReferenced()) + return; + + if (metaType->model) { + QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(metaType->model); + model->removeCacheItem(this); + } + delete this; +} + +/* + This is essentially a copy of QQmlComponent::create(); except it takes the QQmlContextData + arguments instead of QQmlContext which means we don't have to construct the rather weighty + wrapper class for every delegate item. +*/ +void QQmlDelegateModelItem::incubateObject( + QQmlComponent *component, + QQmlEngine *engine, + QQmlContextData *context, + QQmlContextData *forContext) +{ + QQmlIncubatorPrivate *incubatorPriv = QQmlIncubatorPrivate::get(incubationTask); + QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(engine); + QQmlComponentPrivate *componentPriv = QQmlComponentPrivate::get(component); + + incubatorPriv->compiledData = componentPriv->cc; + incubatorPriv->compiledData->addref(); + incubatorPriv->vme.init( + context, + componentPriv->cc, + componentPriv->start, + componentPriv->creationContext); + + enginePriv->incubate(*incubationTask, forContext); +} + +void QQmlDelegateModelItem::destroyObject() +{ + Q_ASSERT(object); + Q_ASSERT(contextData); + + QObjectPrivate *p = QObjectPrivate::get(object); + Q_ASSERT(p->declarativeData); + QQmlData *data = static_cast(p->declarativeData); + if (data->ownContext && data->context) + data->context->clearContext(); + object->deleteLater(); + + if (attached) { + attached->m_cacheItem = 0; + attached = 0; + } + + contextData->destroy(); + contextData = 0; + object = 0; +} + +QQmlDelegateModelItem *QQmlDelegateModelItem::dataForObject(QObject *object) +{ + QObjectPrivate *p = QObjectPrivate::get(object); + QQmlContextData *context = p->declarativeData + ? static_cast(p->declarativeData)->context + : 0; + for (context = context ? context->parent : 0; context; context = context->parent) { + if (QQmlDelegateModelItem *cacheItem = qobject_cast( + context->contextObject)) { + return cacheItem; + } + } + return 0; +} + +int QQmlDelegateModelItem::groupIndex(Compositor::Group group) +{ + if (QQmlDelegateModelPrivate * const model = metaType->model + ? QQmlDelegateModelPrivate::get(metaType->model) + : 0) { + return model->m_compositor.find(Compositor::Cache, model->m_cache.indexOf(this)).index[group]; + } + return -1; +} + +//--------------------------------------------------------------------------- + +QQmlDelegateModelAttachedMetaObject::QQmlDelegateModelAttachedMetaObject( + QQmlDelegateModelItemMetaType *metaType, QMetaObject *metaObject) + : metaType(metaType) + , metaObject(metaObject) + , memberPropertyOffset(QQmlDelegateModelAttached::staticMetaObject.propertyCount()) + , indexPropertyOffset(QQmlDelegateModelAttached::staticMetaObject.propertyCount() + metaType->groupNames.count()) +{ + // Don't reference count the meta-type here as that would create a circular reference. + // Instead we rely the fact that the meta-type's reference count can't reach 0 without first + // destroying all delegates with attached objects. + *static_cast(this) = *metaObject; +} + +QQmlDelegateModelAttachedMetaObject::~QQmlDelegateModelAttachedMetaObject() +{ + ::free(metaObject); +} + +void QQmlDelegateModelAttachedMetaObject::objectDestroyed(QObject *) +{ + release(); +} + +int QQmlDelegateModelAttachedMetaObject::metaCall(QObject *object, QMetaObject::Call call, int _id, void **arguments) +{ + QQmlDelegateModelAttached *attached = static_cast(object); + if (call == QMetaObject::ReadProperty) { + if (_id >= indexPropertyOffset) { + Compositor::Group group = Compositor::Group(_id - indexPropertyOffset + 1); + *static_cast(arguments[0]) = attached->m_currentIndex[group]; + return -1; + } else if (_id >= memberPropertyOffset) { + Compositor::Group group = Compositor::Group(_id - memberPropertyOffset + 1); + *static_cast(arguments[0]) = attached->m_cacheItem->groups & (1 << group); + return -1; + } + } else if (call == QMetaObject::WriteProperty) { + if (_id >= memberPropertyOffset) { + if (!metaType->model) + return -1; + QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(metaType->model); + Compositor::Group group = Compositor::Group(_id - memberPropertyOffset + 1); + const int groupFlag = 1 << group; + const bool member = attached->m_cacheItem->groups & groupFlag; + if (member && !*static_cast(arguments[0])) { + Compositor::iterator it = model->m_compositor.find( + group, attached->m_currentIndex[group]); + model->removeGroups(it, 1, group, groupFlag); + } else if (!member && *static_cast(arguments[0])) { + for (int i = 1; i < metaType->groupCount; ++i) { + if (attached->m_cacheItem->groups & (1 << i)) { + Compositor::iterator it = model->m_compositor.find( + Compositor::Group(i), attached->m_currentIndex[i]); + model->addGroups(it, 1, Compositor::Group(i), groupFlag); + break; + } + } + } + return -1; + } + } + return attached->qt_metacall(call, _id, arguments); +} + +QQmlDelegateModelAttached::QQmlDelegateModelAttached(QObject *parent) + : m_cacheItem(0) + , m_previousGroups(0) +{ + QQml_setParent_noEvent(this, parent); +} + +QQmlDelegateModelAttached::QQmlDelegateModelAttached( + QQmlDelegateModelItem *cacheItem, QObject *parent) + : m_cacheItem(cacheItem) + , m_previousGroups(cacheItem->groups) +{ + QQml_setParent_noEvent(this, parent); + if (QQDMIncubationTask *incubationTask = m_cacheItem->incubationTask) { + for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i) + m_currentIndex[i] = m_previousIndex[i] = incubationTask->index[i]; + } else { + QQmlDelegateModelPrivate * const model = QQmlDelegateModelPrivate::get(m_cacheItem->metaType->model); + Compositor::iterator it = model->m_compositor.find( + Compositor::Cache, model->m_cache.indexOf(m_cacheItem)); + for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i) + m_currentIndex[i] = m_previousIndex[i] = it.index[i]; + } + + if (!cacheItem->metaType->metaObject) + cacheItem->metaType->initializeMetaObject(); + + QObjectPrivate::get(this)->metaObject = cacheItem->metaType->metaObject; + cacheItem->metaType->metaObject->addref(); +} + +/*! + \qmlattachedproperty int QtQml.Models2::DelegateModel::model + + This attached property holds the visual data model this delegate instance belongs to. + + It is attached to each instance of the delegate. +*/ + +QQmlDelegateModel *QQmlDelegateModelAttached::model() const +{ + return m_cacheItem ? m_cacheItem->metaType->model : 0; +} + +/*! + \qmlattachedproperty stringlist QtQml.Models2::DelegateModel::groups + + This attached property holds the name of DelegateModelGroups the item belongs to. + + It is attached to each instance of the delegate. +*/ + +QStringList QQmlDelegateModelAttached::groups() const +{ + QStringList groups; + + if (!m_cacheItem) + return groups; + for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i) { + if (m_cacheItem->groups & (1 << i)) + groups.append(m_cacheItem->metaType->groupNames.at(i - 1)); + } + return groups; +} + +void QQmlDelegateModelAttached::setGroups(const QStringList &groups) +{ + if (!m_cacheItem) + return; + + QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_cacheItem->metaType->model); + + const int groupFlags = model->m_cacheMetaType->parseGroups(groups); + const int cacheIndex = model->m_cache.indexOf(m_cacheItem); + Compositor::iterator it = model->m_compositor.find(Compositor::Cache, cacheIndex); + model->setGroups(it, 1, Compositor::Cache, groupFlags); +} + +/*! + \qmlattachedproperty bool QtQml.Models2::DelegateModel::isUnresolved + + This attached property holds whether the visual item is bound to a data model index. + Returns true if the item is not bound to the model, and false if it is. + + An unresolved item can be bound to the data model using the DelegateModelGroup::resolve() + function. + + It is attached to each instance of the delegate. +*/ + +bool QQmlDelegateModelAttached::isUnresolved() const +{ + if (!m_cacheItem) + return false; + + return m_cacheItem->groups & Compositor::UnresolvedFlag; +} + +/*! + \qmlattachedproperty int QtQml.Models2::DelegateModel::inItems + + This attached property holds whether the item belongs to the default \l items DelegateModelGroup. + + Changing this property will add or remove the item from the items group. + + It is attached to each instance of the delegate. +*/ + +/*! + \qmlattachedproperty int QtQml.Models2::DelegateModel::itemsIndex + + This attached property holds the index of the item in the default \l items DelegateModelGroup. + + It is attached to each instance of the delegate. +*/ + +/*! + \qmlattachedproperty int QtQml.Models2::DelegateModel::inPersistedItems + + This attached property holds whether the item belongs to the \l persistedItems DelegateModelGroup. + + Changing this property will add or remove the item from the items group. Change with caution + as removing an item from the persistedItems group will destroy the current instance if it is + not referenced by a model. + + It is attached to each instance of the delegate. +*/ + +/*! + \qmlattachedproperty int QtQml.Models2::DelegateModel::persistedItemsIndex + + This attached property holds the index of the item in the \l persistedItems DelegateModelGroup. + + It is attached to each instance of the delegate. +*/ + +void QQmlDelegateModelAttached::emitChanges() +{ + const int groupChanges = m_previousGroups ^ m_cacheItem->groups; + m_previousGroups = m_cacheItem->groups; + + int indexChanges = 0; + for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i) { + if (m_previousIndex[i] != m_currentIndex[i]) { + m_previousIndex[i] = m_currentIndex[i]; + indexChanges |= (1 << i); + } + } + + int notifierId = 0; + const QMetaObject *meta = metaObject(); + for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i, ++notifierId) { + if (groupChanges & (1 << i)) + QMetaObject::activate(this, meta, notifierId, 0); + } + for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i, ++notifierId) { + if (indexChanges & (1 << i)) + QMetaObject::activate(this, meta, notifierId, 0); + } + + if (groupChanges) + emit groupsChanged(); +} + +//============================================================================ + +void QQmlDelegateModelGroupPrivate::setModel(QQmlDelegateModel *m, Compositor::Group g) +{ + Q_ASSERT(!model); + model = m; + group = g; +} + +bool QQmlDelegateModelGroupPrivate::isChangedConnected() +{ + Q_Q(QQmlDelegateModelGroup); + IS_SIGNAL_CONNECTED(q, QQmlDelegateModelGroup, changed, (const QQmlV8Handle &,const QQmlV8Handle &)); +} + +void QQmlDelegateModelGroupPrivate::emitChanges(QV8Engine *engine) +{ + Q_Q(QQmlDelegateModelGroup); + if (isChangedConnected() && !changeSet.isEmpty()) { + v8::HandleScope handleScope; + v8::Context::Scope contextScope(engine->context()); + v8::Local removed = engineData(engine)->array(engine, changeSet.removes()); + v8::Local inserted = engineData(engine)->array(engine, changeSet.inserts()); + emit q->changed(QQmlV8Handle::fromHandle(removed), QQmlV8Handle::fromHandle(inserted)); + } + if (changeSet.difference() != 0) + emit q->countChanged(); +} + +void QQmlDelegateModelGroupPrivate::emitModelUpdated(bool reset) +{ + for (QQmlDelegateModelGroupEmitterList::iterator it = emitters.begin(); it != emitters.end(); ++it) + it->emitModelUpdated(changeSet, reset); + changeSet.clear(); +} + +void QQmlDelegateModelGroupPrivate::createdPackage(int index, QQuickPackage *package) +{ + for (QQmlDelegateModelGroupEmitterList::iterator it = emitters.begin(); it != emitters.end(); ++it) + it->createdPackage(index, package); +} + +void QQmlDelegateModelGroupPrivate::initPackage(int index, QQuickPackage *package) +{ + for (QQmlDelegateModelGroupEmitterList::iterator it = emitters.begin(); it != emitters.end(); ++it) + it->initPackage(index, package); +} + +void QQmlDelegateModelGroupPrivate::destroyingPackage(QQuickPackage *package) +{ + for (QQmlDelegateModelGroupEmitterList::iterator it = emitters.begin(); it != emitters.end(); ++it) + it->destroyingPackage(package); +} + +/*! + \qmltype DelegateModelGroup + \instantiates QQmlDelegateModelGroup + \inqmlmodule QtQuick 2 + \ingroup qtquick-models + \brief Encapsulates a filtered set of visual data items + + The DelegateModelGroup type provides a means to address the model data of a DelegateModel's + delegate items, as well as sort and filter these delegate items. + + The initial set of instantiable delegate items in a DelegateModel is represented + by its \l {QtQml.Models2::DelegateModel::items}{items} group, which normally directly reflects + the contents of the model assigned to DelegateModel::model. This set can be changed to + the contents of any other member of DelegateModel::groups by assigning the \l name of that + DelegateModelGroup to the DelegateModel::filterOnGroup property. + + The data of an item in a DelegateModelGroup can be accessed using the get() function, which returns + information about group membership and indexes as well as model data. In combination + with the move() function this can be used to implement view sorting, with remove() to filter + items out of a view, or with setGroups() and \l Package delegates to categorize items into + different views. + + Data from models can be supplemented by inserting data directly into a DelegateModelGroup + with the insert() function. This can be used to introduce mock items into a view, or + placeholder items that are later \l {resolve()}{resolved} to real model data when it becomes + available. + + Delegate items can also be be instantiated directly from a DelegateModelGroup using the + create() function, making it possible to use DelegateModel without an accompanying view + type or to cherry-pick specific items that should be instantiated irregardless of whether + they're currently within a view's visible area. + + \sa {QML Dynamic View Ordering Tutorial} +*/ +/*! + \qmltype DelegateModelGroup + \instantiates QQmlDelegateModelGroup + \inqmlmodule QtQml.Models 2 + \brief Encapsulates a filtered set of visual data items + + The DelegateModelGroup type provides a means to address the model data of a DelegateModel's + delegate items, as well as sort and filter these delegate items. + + This element is also available as DelegateModelGroup in the QtQuick module. For full details, + see the \l DelegateModelGroup documentation. + + \sa {QtQuick::DelegateModelGroup} +*/ + + +QQmlDelegateModelGroup::QQmlDelegateModelGroup(QObject *parent) + : QObject(*new QQmlDelegateModelGroupPrivate, parent) +{ +} + +QQmlDelegateModelGroup::QQmlDelegateModelGroup( + const QString &name, QQmlDelegateModel *model, int index, QObject *parent) + : QObject(*new QQmlDelegateModelGroupPrivate, parent) +{ + Q_D(QQmlDelegateModelGroup); + d->name = name; + d->setModel(model, Compositor::Group(index)); +} + +QQmlDelegateModelGroup::~QQmlDelegateModelGroup() +{ +} + +/*! + \qmlproperty string QtQml.Models2::DelegateModelGroup::name + + This property holds the name of the group. + + Each group in a model must have a unique name starting with a lower case letter. +*/ + +QString QQmlDelegateModelGroup::name() const +{ + Q_D(const QQmlDelegateModelGroup); + return d->name; +} + +void QQmlDelegateModelGroup::setName(const QString &name) +{ + Q_D(QQmlDelegateModelGroup); + if (d->model) + return; + if (d->name != name) { + d->name = name; + emit nameChanged(); + } +} + +/*! + \qmlproperty int QtQml.Models2::DelegateModelGroup::count + + This property holds the number of items in the group. +*/ + +int QQmlDelegateModelGroup::count() const +{ + Q_D(const QQmlDelegateModelGroup); + if (!d->model) + return 0; + return QQmlDelegateModelPrivate::get(d->model)->m_compositor.count(d->group); +} + +/*! + \qmlproperty bool QtQml.Models2::DelegateModelGroup::includeByDefault + + This property holds whether new items are assigned to this group by default. +*/ + +bool QQmlDelegateModelGroup::defaultInclude() const +{ + Q_D(const QQmlDelegateModelGroup); + return d->defaultInclude; +} + +void QQmlDelegateModelGroup::setDefaultInclude(bool include) +{ + Q_D(QQmlDelegateModelGroup); + if (d->defaultInclude != include) { + d->defaultInclude = include; + + if (d->model) { + if (include) + QQmlDelegateModelPrivate::get(d->model)->m_compositor.setDefaultGroup(d->group); + else + QQmlDelegateModelPrivate::get(d->model)->m_compositor.clearDefaultGroup(d->group); + } + emit defaultIncludeChanged(); + } +} + +/*! + \qmlmethod object QtQml.Models2::DelegateModelGroup::get(int index) + + Returns a javascript object describing the item at \a index in the group. + + The returned object contains the same information that is available to a delegate from the + DelegateModel attached as well as the model for that item. It has the properties: + + \list + \li \b model The model data of the item. This is the same as the model context property in + a delegate + \li \b groups A list the of names of groups the item is a member of. This property can be + written to change the item's membership. + \li \b inItems Whether the item belongs to the \l {QtQml.Models2::DelegateModel::items}{items} group. + Writing to this property will add or remove the item from the group. + \li \b itemsIndex The index of the item within the \l {QtQml.Models2::DelegateModel::items}{items} group. + \li \b {in} Whether the item belongs to the dynamic group \e groupName. Writing to + this property will add or remove the item from the group. + \li \b {Index} The index of the item within the dynamic group \e groupName. + \li \b isUnresolved Whether the item is bound to an index in the model assigned to + DelegateModel::model. Returns true if the item is not bound to the model, and false if it is. + \endlist +*/ + +QQmlV8Handle QQmlDelegateModelGroup::get(int index) +{ + Q_D(QQmlDelegateModelGroup); + if (!d->model) + return QQmlV8Handle::fromHandle(v8::Undefined());; + + QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); + if (!model->m_context->isValid()) { + return QQmlV8Handle::fromHandle(v8::Undefined()); + } else if (index < 0 || index >= model->m_compositor.count(d->group)) { + qmlInfo(this) << tr("get: index out of range"); + return QQmlV8Handle::fromHandle(v8::Undefined()); + } + + Compositor::iterator it = model->m_compositor.find(d->group, index); + QQmlDelegateModelItem *cacheItem = it->inCache() + ? model->m_cache.at(it.cacheIndex) + : 0; + + if (!cacheItem) { + cacheItem = model->m_adaptorModel.createItem( + model->m_cacheMetaType, model->m_context->engine(), it.modelIndex()); + if (!cacheItem) + return QQmlV8Handle::fromHandle(v8::Undefined()); + cacheItem->groups = it->flags; + + model->m_cache.insert(it.cacheIndex, cacheItem); + model->m_compositor.setFlags(it, 1, Compositor::CacheFlag); + } + + if (model->m_cacheMetaType->constructor.IsEmpty()) + model->m_cacheMetaType->initializeConstructor(); + v8::Local handle = model->m_cacheMetaType->constructor->NewInstance(); + handle->SetExternalResource(cacheItem); + ++cacheItem->scriptRef; + + return QQmlV8Handle::fromHandle(handle); +} + +bool QQmlDelegateModelGroupPrivate::parseIndex( + const v8::Local &value, int *index, Compositor::Group *group) const +{ + if (value->IsInt32()) { + *index = value->Int32Value(); + return true; + } else if (value->IsObject()) { + v8::Local object = value->ToObject(); + QQmlDelegateModelItem * const cacheItem = v8_resource_cast(object); + if (QQmlDelegateModelPrivate *model = cacheItem && cacheItem->metaType->model + ? QQmlDelegateModelPrivate::get(cacheItem->metaType->model) + : 0) { + *index = model->m_cache.indexOf(cacheItem); + *group = Compositor::Cache; + return true; + } + } + return false; +} + +/*! + \qmlmethod QtQml.Models2::DelegateModelGroup::insert(int index, jsdict data, array groups = undefined) + \qmlmethod QtQml.Models2::DelegateModelGroup::insert(jsdict data, var groups = undefined) + + Creates a new entry at \a index in a DelegateModel with the values from \a data that + correspond to roles in the model assigned to DelegateModel::model. + + If no index is supplied the data is appended to the model. + + The optional \a groups parameter identifies the groups the new entry should belong to, + if unspecified this is equal to the group insert was called on. + + Data inserted into a DelegateModel can later be merged with an existing entry in + DelegateModel::model using the \l resolve() function. This can be used to create placeholder + items that are later replaced by actual data. +*/ + +void QQmlDelegateModelGroup::insert(QQmlV8Function *args) +{ + Q_D(QQmlDelegateModelGroup); + QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); + + int index = model->m_compositor.count(d->group); + Compositor::Group group = d->group; + + if (args->Length() == 0) + return; + + int i = 0; + v8::Local v = (*args)[i]; + if (d->parseIndex(v, &index, &group)) { + if (index < 0 || index > model->m_compositor.count(group)) { + qmlInfo(this) << tr("insert: index out of range"); + return; + } + if (++i == args->Length()) + return; + v = (*args)[i]; + } + + Compositor::insert_iterator before = index < model->m_compositor.count(group) + ? model->m_compositor.findInsertPosition(group, index) + : model->m_compositor.end(); + + int groups = 1 << d->group; + if (++i < args->Length()) + groups |= model->m_cacheMetaType->parseGroups((*args)[i]); + + if (v->IsArray()) { + return; + } else if (v->IsObject()) { + model->insert(before, v->ToObject(), groups); + model->emitChanges(); + } +} + +/*! + \qmlmethod QtQml.Models2::DelegateModelGroup::create(int index) + \qmlmethod QtQml.Models2::DelegateModelGroup::create(int index, jsdict data, array groups = undefined) + \qmlmethod QtQml.Models2::DelegateModelGroup::create(jsdict data, array groups = undefined) + + Returns a reference to the instantiated item at \a index in the group. + + If a \a data object is provided it will be \l {insert}{inserted} at \a index and an item + referencing this new entry will be returned. The optional \a groups parameter identifies + the groups the new entry should belong to, if unspecified this is equal to the group create() + was called on. + + All items returned by create are added to the + \l {QtQml.Models2::DelegateModel::persistedItems}{persistedItems} group. Items in this + group remain instantiated when not referenced by any view. +*/ + +void QQmlDelegateModelGroup::create(QQmlV8Function *args) +{ + Q_D(QQmlDelegateModelGroup); + if (!d->model) + return; + + if (args->Length() == 0) + return; + + QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); + + int index = model->m_compositor.count(d->group); + Compositor::Group group = d->group; + + int i = 0; + v8::Local v = (*args)[i]; + if (d->parseIndex(v, &index, &group)) + ++i; + + if (i < args->Length() && index >= 0 && index <= model->m_compositor.count(group)) { + v = (*args)[i]; + if (v->IsObject()) { + int groups = 1 << d->group; + if (++i < args->Length()) + groups |= model->m_cacheMetaType->parseGroups((*args)[i]); + + Compositor::insert_iterator before = index < model->m_compositor.count(group) + ? model->m_compositor.findInsertPosition(group, index) + : model->m_compositor.end(); + + index = before.index[d->group]; + group = d->group; + + if (!model->insert(before, v->ToObject(), groups)) { + return; + } + } + } + if (index < 0 || index >= model->m_compositor.count(group)) { + qmlInfo(this) << tr("create: index out of range"); + return; + } + + QObject *object = model->object(group, index, false); + if (object) { + QVector inserts; + Compositor::iterator it = model->m_compositor.find(group, index); + model->m_compositor.setFlags(it, 1, d->group, Compositor::PersistedFlag, &inserts); + model->itemsInserted(inserts); + model->m_cache.at(it.cacheIndex)->releaseObject(); + } + + args->returnValue(args->engine()->newQObject(object)); + model->emitChanges(); +} + +/*! + \qmlmethod QtQml.Models2::DelegateModelGroup::resolve(int from, int to) + + Binds an unresolved item at \a from to an item in DelegateModel::model at index \a to. + + Unresolved items are entries whose data has been \l {insert()}{inserted} into a DelegateModelGroup + instead of being derived from a DelegateModel::model index. Resolving an item will replace + the item at the target index with the unresolved item. A resolved an item will reflect the data + of the source model at its bound index and will move when that index moves like any other item. + + If a new item is replaced in the DelegateModelGroup onChanged() handler its insertion and + replacement will be communicated to views as an atomic operation, creating the appearance + that the model contents have not changed, or if the unresolved and model item are not adjacent + that the previously unresolved item has simply moved. + +*/ +void QQmlDelegateModelGroup::resolve(QQmlV8Function *args) +{ + Q_D(QQmlDelegateModelGroup); + if (!d->model) + return; + + QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); + + if (args->Length() < 2) + return; + + int from = -1; + int to = -1; + Compositor::Group fromGroup = d->group; + Compositor::Group toGroup = d->group; + + v8::Local v = (*args)[0]; + if (d->parseIndex(v, &from, &fromGroup)) { + if (from < 0 || from >= model->m_compositor.count(fromGroup)) { + qmlInfo(this) << tr("resolve: from index out of range"); + return; + } + } else { + qmlInfo(this) << tr("resolve: from index invalid"); + return; + } + + v = (*args)[1]; + if (d->parseIndex(v, &to, &toGroup)) { + if (to < 0 || to >= model->m_compositor.count(toGroup)) { + qmlInfo(this) << tr("resolve: to index out of range"); + return; + } + } else { + qmlInfo(this) << tr("resolve: to index invalid"); + return; + } + + Compositor::iterator fromIt = model->m_compositor.find(fromGroup, from); + Compositor::iterator toIt = model->m_compositor.find(toGroup, to); + + if (!fromIt->isUnresolved()) { + qmlInfo(this) << tr("resolve: from is not an unresolved item"); + return; + } + if (!toIt->list) { + qmlInfo(this) << tr("resolve: to is not a model item"); + return; + } + + const int unresolvedFlags = fromIt->flags; + const int resolvedFlags = toIt->flags; + const int resolvedIndex = toIt.modelIndex(); + void * const resolvedList = toIt->list; + + QQmlDelegateModelItem *cacheItem = model->m_cache.at(fromIt.cacheIndex); + cacheItem->groups &= ~Compositor::UnresolvedFlag; + + if (toIt.cacheIndex > fromIt.cacheIndex) + toIt.decrementIndexes(1, unresolvedFlags); + if (!toIt->inGroup(fromGroup) || toIt.index[fromGroup] > from) + from += 1; + + model->itemsMoved( + QVector() << Compositor::Remove(fromIt, 1, unresolvedFlags, 0), + QVector() << Compositor::Insert(toIt, 1, unresolvedFlags, 0)); + model->itemsInserted( + QVector() << Compositor::Insert(toIt, 1, (resolvedFlags & ~unresolvedFlags) | Compositor::CacheFlag)); + toIt.incrementIndexes(1, resolvedFlags | unresolvedFlags); + model->itemsRemoved(QVector() << Compositor::Remove(toIt, 1, resolvedFlags)); + + model->m_compositor.setFlags(toGroup, to, 1, unresolvedFlags & ~Compositor::UnresolvedFlag); + model->m_compositor.clearFlags(fromGroup, from, 1, unresolvedFlags); + + if (resolvedFlags & Compositor::CacheFlag) + model->m_compositor.insert(Compositor::Cache, toIt.cacheIndex, resolvedList, resolvedIndex, 1, Compositor::CacheFlag); + + Q_ASSERT(model->m_cache.count() == model->m_compositor.count(Compositor::Cache)); + + if (!cacheItem->isReferenced()) { + Q_ASSERT(toIt.cacheIndex == model->m_cache.indexOf(cacheItem)); + model->m_cache.removeAt(toIt.cacheIndex); + model->m_compositor.clearFlags(Compositor::Cache, toIt.cacheIndex, 1, Compositor::CacheFlag); + delete cacheItem; + Q_ASSERT(model->m_cache.count() == model->m_compositor.count(Compositor::Cache)); + } else { + cacheItem->resolveIndex(model->m_adaptorModel, resolvedIndex); + if (cacheItem->attached) + cacheItem->attached->emitUnresolvedChanged(); + } + + model->emitChanges(); +} + +/*! + \qmlmethod QtQml.Models2::DelegateModelGroup::remove(int index, int count) + + Removes \a count items starting at \a index from the group. +*/ + +void QQmlDelegateModelGroup::remove(QQmlV8Function *args) +{ + Q_D(QQmlDelegateModelGroup); + if (!d->model) + return; + Compositor::Group group = d->group; + int index = -1; + int count = 1; + + if (args->Length() == 0) + return; + + int i = 0; + v8::Local v = (*args)[i]; + if (!d->parseIndex(v, &index, &group)) { + qmlInfo(this) << tr("remove: invalid index"); + return; + } + + if (++i < args->Length()) { + v = (*args)[i]; + if (v->IsInt32()) + count = v->Int32Value(); + } + + QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); + if (index < 0 || index >= model->m_compositor.count(group)) { + qmlInfo(this) << tr("remove: index out of range"); + } else if (count != 0) { + Compositor::iterator it = model->m_compositor.find(group, index); + if (count < 0 || count > model->m_compositor.count(d->group) - it.index[d->group]) { + qmlInfo(this) << tr("remove: invalid count"); + } else { + model->removeGroups(it, count, d->group, 1 << d->group); + } + } +} + +bool QQmlDelegateModelGroupPrivate::parseGroupArgs( + QQmlV8Function *args, Compositor::Group *group, int *index, int *count, int *groups) const +{ + if (!model || !QQmlDelegateModelPrivate::get(model)->m_cacheMetaType) + return false; + + if (args->Length() < 2) + return false; + + int i = 0; + v8::Local v = (*args)[i]; + if (!parseIndex(v, index, group)) + return false; + + v = (*args)[++i]; + if (v->IsInt32()) { + *count = v->Int32Value(); + + if (++i == args->Length()) + return false; + v = (*args)[i]; + } + + *groups = QQmlDelegateModelPrivate::get(model)->m_cacheMetaType->parseGroups(v); + + return true; +} + +/*! + \qmlmethod QtQml.Models2::DelegateModelGroup::addGroups(int index, int count, stringlist groups) + + Adds \a count items starting at \a index to \a groups. +*/ + +void QQmlDelegateModelGroup::addGroups(QQmlV8Function *args) +{ + Q_D(QQmlDelegateModelGroup); + Compositor::Group group = d->group; + int index = -1; + int count = 1; + int groups = 0; + + if (!d->parseGroupArgs(args, &group, &index, &count, &groups)) + return; + + QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); + if (index < 0 || index >= model->m_compositor.count(group)) { + qmlInfo(this) << tr("addGroups: index out of range"); + } else if (count != 0) { + Compositor::iterator it = model->m_compositor.find(group, index); + if (count < 0 || count > model->m_compositor.count(d->group) - it.index[d->group]) { + qmlInfo(this) << tr("addGroups: invalid count"); + } else { + model->addGroups(it, count, d->group, groups); + } + } +} + +/*! + \qmlmethod QtQml.Models2::DelegateModelGroup::removeGroups(int index, int count, stringlist groups) + + Removes \a count items starting at \a index from \a groups. +*/ + +void QQmlDelegateModelGroup::removeGroups(QQmlV8Function *args) +{ + Q_D(QQmlDelegateModelGroup); + Compositor::Group group = d->group; + int index = -1; + int count = 1; + int groups = 0; + + if (!d->parseGroupArgs(args, &group, &index, &count, &groups)) + return; + + QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); + if (index < 0 || index >= model->m_compositor.count(group)) { + qmlInfo(this) << tr("removeGroups: index out of range"); + } else if (count != 0) { + Compositor::iterator it = model->m_compositor.find(group, index); + if (count < 0 || count > model->m_compositor.count(d->group) - it.index[d->group]) { + qmlInfo(this) << tr("removeGroups: invalid count"); + } else { + model->removeGroups(it, count, d->group, groups); + } + } +} + +/*! + \qmlmethod QtQml.Models2::DelegateModelGroup::setGroups(int index, int count, stringlist groups) + + Sets the \a groups \a count items starting at \a index belong to. +*/ + +void QQmlDelegateModelGroup::setGroups(QQmlV8Function *args) +{ + Q_D(QQmlDelegateModelGroup); + Compositor::Group group = d->group; + int index = -1; + int count = 1; + int groups = 0; + + if (!d->parseGroupArgs(args, &group, &index, &count, &groups)) + return; + + QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); + if (index < 0 || index >= model->m_compositor.count(group)) { + qmlInfo(this) << tr("setGroups: index out of range"); + } else if (count != 0) { + Compositor::iterator it = model->m_compositor.find(group, index); + if (count < 0 || count > model->m_compositor.count(d->group) - it.index[d->group]) { + qmlInfo(this) << tr("setGroups: invalid count"); + } else { + model->setGroups(it, count, d->group, groups); + } + } +} + +/*! + \qmlmethod QtQml.Models2::DelegateModelGroup::setGroups(int index, int count, stringlist groups) + + Sets the \a groups \a count items starting at \a index belong to. +*/ + +/*! + \qmlmethod QtQml.Models2::DelegateModelGroup::move(var from, var to, int count) + + Moves \a count at \a from in a group \a to a new position. +*/ + +void QQmlDelegateModelGroup::move(QQmlV8Function *args) +{ + Q_D(QQmlDelegateModelGroup); + + if (args->Length() < 2) + return; + + Compositor::Group fromGroup = d->group; + Compositor::Group toGroup = d->group; + int from = -1; + int to = -1; + int count = 1; + + if (!d->parseIndex((*args)[0], &from, &fromGroup)) { + qmlInfo(this) << tr("move: invalid from index"); + return; + } + + if (!d->parseIndex((*args)[1], &to, &toGroup)) { + qmlInfo(this) << tr("move: invalid to index"); + return; + } + + if (args->Length() > 2) { + v8::Local v = (*args)[2]; + if (v->IsInt32()) + count = v->Int32Value(); + } + + QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); + + if (count < 0) { + qmlInfo(this) << tr("move: invalid count"); + } else if (from < 0 || from + count > model->m_compositor.count(fromGroup)) { + qmlInfo(this) << tr("move: from index out of range"); + } else if (!model->m_compositor.verifyMoveTo(fromGroup, from, toGroup, to, count, d->group)) { + qmlInfo(this) << tr("move: to index out of range"); + } else if (count > 0) { + QVector removes; + QVector inserts; + + model->m_compositor.move(fromGroup, from, toGroup, to, count, d->group, &removes, &inserts); + model->itemsMoved(removes, inserts); + model->emitChanges(); + } + +} + +/*! + \qmlsignal QtQml.Models2::DelegateModelGroup::onChanged(array removed, array inserted) + + This handler is called when items have been removed from or inserted into the group. + + Each object in the \a removed and \a inserted arrays has two values; the \e index of the first + item inserted or removed and a \e count of the number of consecutive items inserted or removed. + + Each index is adjusted for previous changes with all removed items preceding any inserted + items. +*/ + +//============================================================================ + +QQmlPartsModel::QQmlPartsModel(QQmlDelegateModel *model, const QString &part, QObject *parent) + : QQmlInstanceModel(*new QObjectPrivate, parent) + , m_model(model) + , m_part(part) + , m_compositorGroup(Compositor::Cache) + , m_inheritGroup(true) +{ + QQmlDelegateModelPrivate *d = QQmlDelegateModelPrivate::get(m_model); + if (d->m_cacheMetaType) { + QQmlDelegateModelGroupPrivate::get(d->m_groups[1])->emitters.insert(this); + m_compositorGroup = Compositor::Default; + } else { + d->m_pendingParts.insert(this); + } +} + +QQmlPartsModel::~QQmlPartsModel() +{ +} + +QString QQmlPartsModel::filterGroup() const +{ + if (m_inheritGroup) + return m_model->filterGroup(); + return m_filterGroup; +} + +void QQmlPartsModel::setFilterGroup(const QString &group) +{ + if (QQmlDelegateModelPrivate::get(m_model)->m_transaction) { + qmlInfo(this) << tr("The group of a DelegateModel cannot be changed within onChanged"); + return; + } + + if (m_filterGroup != group || m_inheritGroup) { + m_filterGroup = group; + m_inheritGroup = false; + updateFilterGroup(); + + emit filterGroupChanged(); + } +} + +void QQmlPartsModel::resetFilterGroup() +{ + if (!m_inheritGroup) { + m_inheritGroup = true; + updateFilterGroup(); + emit filterGroupChanged(); + } +} + +void QQmlPartsModel::updateFilterGroup() +{ + QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_model); + if (!model->m_cacheMetaType) + return; + + if (m_inheritGroup) { + if (m_filterGroup == model->m_filterGroup) + return; + m_filterGroup = model->m_filterGroup; + } + + QQmlListCompositor::Group previousGroup = m_compositorGroup; + m_compositorGroup = Compositor::Default; + QQmlDelegateModelGroupPrivate::get(model->m_groups[Compositor::Default])->emitters.insert(this); + for (int i = 1; i < model->m_groupCount; ++i) { + if (m_filterGroup == model->m_cacheMetaType->groupNames.at(i - 1)) { + m_compositorGroup = Compositor::Group(i); + break; + } + } + + QQmlDelegateModelGroupPrivate::get(model->m_groups[m_compositorGroup])->emitters.insert(this); + if (m_compositorGroup != previousGroup) { + QVector removes; + QVector inserts; + model->m_compositor.transition(previousGroup, m_compositorGroup, &removes, &inserts); + + QQmlChangeSet changeSet; + changeSet.move(removes, inserts); + if (!changeSet.isEmpty()) + emit modelUpdated(changeSet, false); + + if (changeSet.difference() != 0) + emit countChanged(); + } +} + +void QQmlPartsModel::updateFilterGroup( + Compositor::Group group, const QQmlChangeSet &changeSet) +{ + if (!m_inheritGroup) + return; + + m_compositorGroup = group; + QQmlDelegateModelGroupPrivate::get(QQmlDelegateModelPrivate::get(m_model)->m_groups[m_compositorGroup])->emitters.insert(this); + + if (!changeSet.isEmpty()) + emit modelUpdated(changeSet, false); + + if (changeSet.difference() != 0) + emit countChanged(); + + emit filterGroupChanged(); +} + +int QQmlPartsModel::count() const +{ + QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_model); + return model->m_delegate + ? model->m_compositor.count(m_compositorGroup) + : 0; +} + +bool QQmlPartsModel::isValid() const +{ + return m_model->isValid(); +} + +QObject *QQmlPartsModel::object(int index, bool asynchronous) +{ + QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_model); + + if (!model->m_delegate || index < 0 || index >= model->m_compositor.count(m_compositorGroup)) { + qWarning() << "DelegateModel::item: index out range" << index << model->m_compositor.count(m_compositorGroup); + return 0; + } + + QObject *object = model->object(m_compositorGroup, index, asynchronous); + + if (QQuickPackage *package = qmlobject_cast(object)) { + QObject *part = package->part(m_part); + if (!part) + return 0; + m_packaged.insertMulti(part, package); + return part; + } + + model->release(object); + if (!model->m_delegateValidated) { + if (object) + qmlInfo(model->m_delegate) << tr("Delegate component must be Package type."); + model->m_delegateValidated = true; + } + + return 0; +} + +QQmlInstanceModel::ReleaseFlags QQmlPartsModel::release(QObject *item) +{ + QQmlInstanceModel::ReleaseFlags flags = 0; + + QHash::iterator it = m_packaged.find(item); + if (it != m_packaged.end()) { + QQuickPackage *package = *it; + QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_model); + flags = model->release(package); + m_packaged.erase(it); + if (!m_packaged.contains(item)) + flags &= ~Referenced; + if (flags & Destroyed) + QQmlDelegateModelPrivate::get(m_model)->emitDestroyingPackage(package); + } + return flags; +} + +QString QQmlPartsModel::stringValue(int index, const QString &role) +{ + return QQmlDelegateModelPrivate::get(m_model)->stringValue(m_compositorGroup, index, role); +} + +void QQmlPartsModel::setWatchedRoles(QList roles) +{ + QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_model); + model->m_adaptorModel.replaceWatchedRoles(m_watchedRoles, roles); + m_watchedRoles = roles; +} + +int QQmlPartsModel::indexOf(QObject *item, QObject *) const +{ + QHash::const_iterator it = m_packaged.find(item); + if (it != m_packaged.end()) { + if (QQmlDelegateModelItem *cacheItem = QQmlDelegateModelItem::dataForObject(*it)) + return cacheItem->groupIndex(m_compositorGroup); + } + return -1; +} + +void QQmlPartsModel::createdPackage(int index, QQuickPackage *package) +{ + emit createdItem(index, package->part(m_part)); +} + +void QQmlPartsModel::initPackage(int index, QQuickPackage *package) +{ + emit initItem(index, package->part(m_part)); +} + +void QQmlPartsModel::destroyingPackage(QQuickPackage *package) +{ + QObject *item = package->part(m_part); + Q_ASSERT(!m_packaged.contains(item)); + emit destroyingItem(item); +} + +void QQmlPartsModel::emitModelUpdated(const QQmlChangeSet &changeSet, bool reset) +{ + emit modelUpdated(changeSet, reset); + if (changeSet.difference() != 0) + emit countChanged(); +} + +//============================================================================ + +v8::Handle get_change_index(v8::Local, const v8::AccessorInfo &info) +{ + return info.This()->GetInternalField(0); +} + +v8::Handle get_change_count(v8::Local, const v8::AccessorInfo &info) +{ + return info.This()->GetInternalField(1); +} + +v8::Handle get_change_moveId(v8::Local, const v8::AccessorInfo &info) +{ + return info.This()->GetInternalField(2); +} + +class QQmlDelegateModelGroupChangeArray : public QV8ObjectResource +{ + V8_RESOURCE_TYPE(ChangeSetArrayType) +public: + QQmlDelegateModelGroupChangeArray(QV8Engine *engine) + : QV8ObjectResource(engine) + { + } + + virtual quint32 count() const = 0; + virtual const QQmlChangeSet::Change &at(int index) const = 0; + + static v8::Handle get_change(quint32 index, const v8::AccessorInfo &info) + { + QQmlDelegateModelGroupChangeArray *array = v8_resource_cast(info.This()); + V8ASSERT_TYPE(array, "Not a valid change array"); + + if (index >= array->count()) + return v8::Undefined(); + + const QQmlChangeSet::Change &change = array->at(index); + + v8::Local object = engineData(array->engine)->constructorChange->NewInstance(); + object->SetInternalField(0, v8::Int32::New(change.index)); + object->SetInternalField(1, v8::Int32::New(change.count)); + if (change.isMove()) + object->SetInternalField(2, v8::Int32::New(change.moveId)); + + return object; + } + + static v8::Handle get_length(v8::Local, const v8::AccessorInfo &info) + { + QQmlDelegateModelGroupChangeArray *array = v8_resource_cast(info.This()); + V8ASSERT_TYPE(array, "Not a valid change array"); + + return v8::Integer::New(array->count()); + } + + static v8::Local constructor() + { + v8::Local changeArray = v8::FunctionTemplate::New(); + changeArray->InstanceTemplate()->SetHasExternalResource(true); + changeArray->InstanceTemplate()->SetIndexedPropertyHandler(get_change); + changeArray->InstanceTemplate()->SetAccessor(v8::String::New("length"), get_length); + return changeArray->GetFunction(); + } +}; + +class QQmlDelegateModelGroupRemoveArray : public QQmlDelegateModelGroupChangeArray +{ +public: + QQmlDelegateModelGroupRemoveArray(QV8Engine *engine, const QVector &changes) + : QQmlDelegateModelGroupChangeArray(engine) + , changes(changes) + { + } + + quint32 count() const { return changes.count(); } + const QQmlChangeSet::Change &at(int index) const { return changes.at(index); } + +private: + QVector changes; +}; + +class QQmlDelegateModelGroupInsertArray : public QQmlDelegateModelGroupChangeArray +{ +public: + QQmlDelegateModelGroupInsertArray(QV8Engine *engine, const QVector &changes) + : QQmlDelegateModelGroupChangeArray(engine) + , changes(changes) + { + } + + quint32 count() const { return changes.count(); } + const QQmlChangeSet::Change &at(int index) const { return changes.at(index); } + +private: + QVector changes; +}; + +QQmlDelegateModelEngineData::QQmlDelegateModelEngineData(QV8Engine *) +{ + strings = qPersistentNew(v8::Array::New(StringCount)); + strings->Set(Model, v8::String::New("model")); + strings->Set(Groups, v8::String::New("groups")); + strings->Set(IsUnresolved, v8::String::New("isUnresolved")); + strings->Set(ItemsIndex, v8::String::New("itemsIndex")); + strings->Set(PersistedItemsIndex, v8::String::New("persistedItemsIndex")); + strings->Set(InItems, v8::String::New("inItems")); + strings->Set(InPersistedItems, v8::String::New("inPersistedItems")); + + v8::Local change = v8::FunctionTemplate::New(); + change->InstanceTemplate()->SetAccessor(v8::String::New("index"), get_change_index); + change->InstanceTemplate()->SetAccessor(v8::String::New("count"), get_change_count); + change->InstanceTemplate()->SetAccessor(v8::String::New("moveId"), get_change_moveId); + change->InstanceTemplate()->SetInternalFieldCount(3); + constructorChange = qPersistentNew(change->GetFunction()); + constructorChangeArray = qPersistentNew(QQmlDelegateModelGroupChangeArray::constructor()); +} + +QQmlDelegateModelEngineData::~QQmlDelegateModelEngineData() +{ + qPersistentDispose(strings); + qPersistentDispose(constructorChange); + qPersistentDispose(constructorChangeArray); +} + +v8::Local QQmlDelegateModelEngineData::array( + QV8Engine *engine, const QVector &changes) +{ + v8::Local array = constructorChangeArray->NewInstance(); + array->SetExternalResource(new QQmlDelegateModelGroupRemoveArray(engine, changes)); + return array; +} + +v8::Local QQmlDelegateModelEngineData::array( + QV8Engine *engine, const QVector &changes) +{ + v8::Local array = constructorChangeArray->NewInstance(); + array->SetExternalResource(new QQmlDelegateModelGroupInsertArray(engine, changes)); + return array; +} + +QT_END_NAMESPACE + diff --git a/src/qml/types/qqmldelegatemodel_p.h b/src/qml/types/qqmldelegatemodel_p.h new file mode 100644 index 0000000000..5702c59787 --- /dev/null +++ b/src/qml/types/qqmldelegatemodel_p.h @@ -0,0 +1,234 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLDATAMODEL_P_H +#define QQMLDATAMODEL_P_H + +#include +#include +#include + +#include +#include + +#include +#include + +Q_DECLARE_METATYPE(QModelIndex) + +QT_BEGIN_NAMESPACE + +class QQmlChangeSet; +class QQmlComponent; +class QQuickPackage; +class QQmlV8Function; +class QQmlDelegateModelGroup; +class QQmlDelegateModelAttached; +class QQmlDelegateModelPrivate; + + +class Q_QML_PRIVATE_EXPORT QQmlDelegateModel : public QQmlInstanceModel, public QQmlParserStatus +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QQmlDelegateModel) + + Q_PROPERTY(QVariant model READ model WRITE setModel) + Q_PROPERTY(QQmlComponent *delegate READ delegate WRITE setDelegate) + Q_PROPERTY(QString filterOnGroup READ filterGroup WRITE setFilterGroup NOTIFY filterGroupChanged RESET resetFilterGroup) + Q_PROPERTY(QQmlDelegateModelGroup *items READ items CONSTANT) //TODO : worth renaming? + Q_PROPERTY(QQmlDelegateModelGroup *persistedItems READ persistedItems CONSTANT) + Q_PROPERTY(QQmlListProperty groups READ groups CONSTANT) + Q_PROPERTY(QObject *parts READ parts CONSTANT) + Q_PROPERTY(QVariant rootIndex READ rootIndex WRITE setRootIndex NOTIFY rootIndexChanged) + Q_CLASSINFO("DefaultProperty", "delegate") + Q_INTERFACES(QQmlParserStatus) +public: + QQmlDelegateModel(); + QQmlDelegateModel(QQmlContext *, QObject *parent=0); + virtual ~QQmlDelegateModel(); + + void classBegin(); + void componentComplete(); + + QVariant model() const; + void setModel(const QVariant &); + + QQmlComponent *delegate() const; + void setDelegate(QQmlComponent *); + + QVariant rootIndex() const; + void setRootIndex(const QVariant &root); + + Q_INVOKABLE QVariant modelIndex(int idx) const; + Q_INVOKABLE QVariant parentModelIndex() const; + + int count() const; + bool isValid() const { return delegate() != 0; } + QObject *object(int index, bool asynchronous=false); + ReleaseFlags release(QObject *object); + void cancel(int index); + virtual QString stringValue(int index, const QString &role); + virtual void setWatchedRoles(QList roles); + + int indexOf(QObject *object, QObject *objectContext) const; + + QString filterGroup() const; + void setFilterGroup(const QString &group); + void resetFilterGroup(); + + QQmlDelegateModelGroup *items(); + QQmlDelegateModelGroup *persistedItems(); + QQmlListProperty groups(); + QObject *parts(); + + bool event(QEvent *); + + static QQmlDelegateModelAttached *qmlAttachedProperties(QObject *obj); + +Q_SIGNALS: + void filterGroupChanged(); + void defaultGroupsChanged(); + void rootIndexChanged(); + +private Q_SLOTS: + void _q_itemsChanged(int index, int count, const QVector &roles); + void _q_itemsInserted(int index, int count); + void _q_itemsRemoved(int index, int count); + void _q_itemsMoved(int from, int to, int count); + void _q_modelReset(); + void _q_rowsInserted(const QModelIndex &,int,int); + void _q_rowsAboutToBeRemoved(const QModelIndex &parent, int begin, int end); + void _q_rowsRemoved(const QModelIndex &,int,int); + void _q_rowsMoved(const QModelIndex &, int, int, const QModelIndex &, int); + void _q_dataChanged(const QModelIndex&,const QModelIndex&,const QVector &); + void _q_layoutChanged(); + +private: + Q_DISABLE_COPY(QQmlDelegateModel) +}; + +class QQmlDelegateModelGroupPrivate; +class Q_QML_PRIVATE_EXPORT QQmlDelegateModelGroup : public QObject +{ + Q_OBJECT + Q_PROPERTY(int count READ count NOTIFY countChanged) + Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) + Q_PROPERTY(bool includeByDefault READ defaultInclude WRITE setDefaultInclude NOTIFY defaultIncludeChanged) +public: + QQmlDelegateModelGroup(QObject *parent = 0); + QQmlDelegateModelGroup(const QString &name, QQmlDelegateModel *model, int compositorType, QObject *parent = 0); + ~QQmlDelegateModelGroup(); + + QString name() const; + void setName(const QString &name); + + int count() const; + + bool defaultInclude() const; + void setDefaultInclude(bool include); + + Q_INVOKABLE QQmlV8Handle get(int index); + +public Q_SLOTS: + void insert(QQmlV8Function *); + void create(QQmlV8Function *); + void resolve(QQmlV8Function *); + void remove(QQmlV8Function *); + void addGroups(QQmlV8Function *); + void removeGroups(QQmlV8Function *); + void setGroups(QQmlV8Function *); + void move(QQmlV8Function *); + +Q_SIGNALS: + void countChanged(); + void nameChanged(); + void defaultIncludeChanged(); + void changed(const QQmlV8Handle &removed, const QQmlV8Handle &inserted); +private: + Q_DECLARE_PRIVATE(QQmlDelegateModelGroup) +}; + +class QQmlDelegateModelItem; +class QQmlDelegateModelAttachedMetaObject; +class QQmlDelegateModelAttached : public QObject +{ + Q_OBJECT + Q_PROPERTY(QQmlDelegateModel *model READ model CONSTANT) + Q_PROPERTY(QStringList groups READ groups WRITE setGroups NOTIFY groupsChanged) + Q_PROPERTY(bool isUnresolved READ isUnresolved NOTIFY unresolvedChanged) +public: + QQmlDelegateModelAttached(QObject *parent); + QQmlDelegateModelAttached(QQmlDelegateModelItem *cacheItem, QObject *parent); + ~QQmlDelegateModelAttached() {} + + void setCacheItem(QQmlDelegateModelItem *item); + + QQmlDelegateModel *model() const; + + QStringList groups() const; + void setGroups(const QStringList &groups); + + bool isUnresolved() const; + + void emitChanges(); + + void emitUnresolvedChanged() { emit unresolvedChanged(); } + +Q_SIGNALS: + void groupsChanged(); + void unresolvedChanged(); + +public: + QQmlDelegateModelItem *m_cacheItem; + int m_previousGroups; + int m_currentIndex[QQmlListCompositor::MaximumGroupCount]; + int m_previousIndex[QQmlListCompositor::MaximumGroupCount]; + + friend class QQmlDelegateModelAttachedMetaObject; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQmlDelegateModel) +QML_DECLARE_TYPEINFO(QQmlDelegateModel, QML_HAS_ATTACHED_PROPERTIES) +QML_DECLARE_TYPE(QQmlDelegateModelGroup) + +#endif // QQMLDATAMODEL_P_H diff --git a/src/qml/types/qqmldelegatemodel_p_p.h b/src/qml/types/qqmldelegatemodel_p_p.h new file mode 100644 index 0000000000..68242f433d --- /dev/null +++ b/src/qml/types/qqmldelegatemodel_p_p.h @@ -0,0 +1,411 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLDATAMODEL_P_P_H +#define QQMLDATAMODEL_P_P_H + +#include "qqmldelegatemodel_p.h" + + +#include +#include + +#include +#include + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +QT_BEGIN_NAMESPACE + +typedef QQmlListCompositor Compositor; + +class QQmlDelegateModelAttachedMetaObject; + +class QQmlDelegateModelItemMetaType : public QQmlRefCount +{ +public: + QQmlDelegateModelItemMetaType(QV8Engine *engine, QQmlDelegateModel *model, const QStringList &groupNames); + ~QQmlDelegateModelItemMetaType(); + + void initializeMetaObject(); + void initializeConstructor(); + + int parseGroups(const QStringList &groupNames) const; + int parseGroups(const v8::Local &groupNames) const; + + static void release_index(v8::Persistent object, void *parameter); + static void release_model(v8::Persistent object, void *parameter); + + static v8::Handle get_model(v8::Local, const v8::AccessorInfo &info); + static v8::Handle get_groups(v8::Local, const v8::AccessorInfo &info); + static void set_groups( + v8::Local, v8::Local value, const v8::AccessorInfo &info); + static v8::Handle get_member(v8::Local, const v8::AccessorInfo &info); + static void set_member( + v8::Local, v8::Local value, const v8::AccessorInfo &info); + static v8::Handle get_index(v8::Local, const v8::AccessorInfo &info); + + QQmlGuard model; + const int groupCount; + QV8Engine * const v8Engine; + QQmlDelegateModelAttachedMetaObject *metaObject; + const QStringList groupNames; + v8::Persistent constructor; +}; + +class QQmlAdaptorModel; +class QQDMIncubationTask; + +class QQmlDelegateModelItem : public QObject, public QV8ObjectResource +{ + Q_OBJECT + Q_PROPERTY(int index READ modelIndex NOTIFY modelIndexChanged) + Q_PROPERTY(QObject *model READ modelObject CONSTANT) + V8_RESOURCE_TYPE(VisualDataItemType) +public: + QQmlDelegateModelItem(QQmlDelegateModelItemMetaType *metaType, int modelIndex); + ~QQmlDelegateModelItem(); + + void referenceObject() { ++objectRef; } + bool releaseObject() { return --objectRef == 0 && !(groups & Compositor::PersistedFlag); } + bool isObjectReferenced() const { return objectRef != 0 || (groups & Compositor::PersistedFlag); } + + bool isReferenced() const { + return scriptRef + || incubationTask + || ((groups & Compositor::UnresolvedFlag) && (groups & Compositor::GroupMask)); + } + + void Dispose(); + + QObject *modelObject() { return this; } + + void incubateObject( + QQmlComponent *component, + QQmlEngine *engine, + QQmlContextData *context, + QQmlContextData *forContext); + void destroyObject(); + + static QQmlDelegateModelItem *dataForObject(QObject *object); + + int groupIndex(Compositor::Group group); + + int modelIndex() const { return index; } + void setModelIndex(int idx) { index = idx; emit modelIndexChanged(); } + + virtual v8::Handle get() { return engine->newQObject(this); } + + virtual void setValue(const QString &role, const QVariant &value) { Q_UNUSED(role); Q_UNUSED(value); } + virtual bool resolveIndex(const QQmlAdaptorModel &, int) { return false; } + + QQmlDelegateModelItemMetaType * const metaType; + QQmlContextData *contextData; + QObject *object; + QQmlDelegateModelAttached *attached; + QQDMIncubationTask *incubationTask; + int objectRef; + int scriptRef; + int groups; + int index; + + +Q_SIGNALS: + void modelIndexChanged(); + +protected: + void objectDestroyed(QObject *); +}; + + +class QQmlDelegateModelPrivate; +class QQDMIncubationTask : public QQmlIncubator +{ +public: + QQDMIncubationTask(QQmlDelegateModelPrivate *l, IncubationMode mode) + : QQmlIncubator(mode) + , incubating(0) + , vdm(l) {} + + virtual void statusChanged(Status); + virtual void setInitialState(QObject *); + + QQmlDelegateModelItem *incubating; + QQmlDelegateModelPrivate *vdm; + int index[QQmlListCompositor::MaximumGroupCount]; +}; + + +class QQmlDelegateModelGroupEmitter +{ +public: + virtual ~QQmlDelegateModelGroupEmitter() {} + virtual void emitModelUpdated(const QQmlChangeSet &changeSet, bool reset) = 0; + virtual void createdPackage(int, QQuickPackage *) {} + virtual void initPackage(int, QQuickPackage *) {} + virtual void destroyingPackage(QQuickPackage *) {} + + QIntrusiveListNode emitterNode; +}; + +typedef QIntrusiveList QQmlDelegateModelGroupEmitterList; + +class QQmlDelegateModelGroupPrivate : public QObjectPrivate +{ +public: + Q_DECLARE_PUBLIC(QQmlDelegateModelGroup) + + QQmlDelegateModelGroupPrivate() : group(Compositor::Cache), defaultInclude(false) {} + + static QQmlDelegateModelGroupPrivate *get(QQmlDelegateModelGroup *group) { + return static_cast(QObjectPrivate::get(group)); } + + void setModel(QQmlDelegateModel *model, Compositor::Group group); + bool isChangedConnected(); + void emitChanges(QV8Engine *engine); + void emitModelUpdated(bool reset); + + void createdPackage(int index, QQuickPackage *package); + void initPackage(int index, QQuickPackage *package); + void destroyingPackage(QQuickPackage *package); + + bool parseIndex(const v8::Local &value, int *index, Compositor::Group *group) const; + bool parseGroupArgs( + QQmlV8Function *args, Compositor::Group *group, int *index, int *count, int *groups) const; + + Compositor::Group group; + QQmlGuard model; + QQmlDelegateModelGroupEmitterList emitters; + QQmlChangeSet changeSet; + QString name; + bool defaultInclude; +}; + +class QQmlDelegateModelParts; + +class QQmlDelegateModelPrivate : public QObjectPrivate, public QQmlDelegateModelGroupEmitter +{ + Q_DECLARE_PUBLIC(QQmlDelegateModel) +public: + QQmlDelegateModelPrivate(QQmlContext *); + ~QQmlDelegateModelPrivate(); + + static QQmlDelegateModelPrivate *get(QQmlDelegateModel *m) { + return static_cast(QObjectPrivate::get(m)); + } + + void init(); + void connectModel(QQmlAdaptorModel *model); + + QObject *object(Compositor::Group group, int index, bool asynchronous); + QQmlDelegateModel::ReleaseFlags release(QObject *object); + QString stringValue(Compositor::Group group, int index, const QString &name); + void emitCreatedPackage(QQDMIncubationTask *incubationTask, QQuickPackage *package); + void emitInitPackage(QQDMIncubationTask *incubationTask, QQuickPackage *package); + void emitCreatedItem(QQDMIncubationTask *incubationTask, QObject *item) { + emit q_func()->createdItem(incubationTask->index[m_compositorGroup], item); } + void emitInitItem(QQDMIncubationTask *incubationTask, QObject *item) { + emit q_func()->initItem(incubationTask->index[m_compositorGroup], item); } + void emitDestroyingPackage(QQuickPackage *package); + void emitDestroyingItem(QObject *item) { emit q_func()->destroyingItem(item); } + void removeCacheItem(QQmlDelegateModelItem *cacheItem); + + void updateFilterGroup(); + + void addGroups(Compositor::iterator from, int count, Compositor::Group group, int groupFlags); + void removeGroups(Compositor::iterator from, int count, Compositor::Group group, int groupFlags); + void setGroups(Compositor::iterator from, int count, Compositor::Group group, int groupFlags); + + void itemsInserted( + const QVector &inserts, + QVarLengthArray, Compositor::MaximumGroupCount> *translatedInserts, + QHash > *movedItems = 0); + void itemsInserted(const QVector &inserts); + void itemsRemoved( + const QVector &removes, + QVarLengthArray, Compositor::MaximumGroupCount> *translatedRemoves, + QHash > *movedItems = 0); + void itemsRemoved(const QVector &removes); + void itemsMoved( + const QVector &removes, const QVector &inserts); + void itemsChanged(const QVector &changes); + template static v8::Local buildChangeList(const QVector &changes); + void emitChanges(); + void emitModelUpdated(const QQmlChangeSet &changeSet, bool reset); + + bool insert(Compositor::insert_iterator &before, const v8::Local &object, int groups); + + static void group_append(QQmlListProperty *property, QQmlDelegateModelGroup *group); + static int group_count(QQmlListProperty *property); + static QQmlDelegateModelGroup *group_at(QQmlListProperty *property, int index); + + void releaseIncubator(QQDMIncubationTask *incubationTask); + void incubatorStatusChanged(QQDMIncubationTask *incubationTask, QQmlIncubator::Status status); + void setInitialState(QQDMIncubationTask *incubationTask, QObject *o); + + QQmlAdaptorModel m_adaptorModel; + QQmlListCompositor m_compositor; + QQmlComponent *m_delegate; + QQmlDelegateModelItemMetaType *m_cacheMetaType; + QQmlContext *m_context; + QQmlDelegateModelParts *m_parts; + QQmlDelegateModelGroupEmitterList m_pendingParts; + + QList m_cache; + QList m_finishedIncubating; + QList m_watchedRoles; + + QString m_filterGroup; + + int m_count; + int m_groupCount; + + QQmlListCompositor::Group m_compositorGroup; + bool m_complete : 1; + bool m_delegateValidated : 1; + bool m_reset : 1; + bool m_transaction : 1; + bool m_incubatorCleanupScheduled : 1; + + union { + struct { + QQmlDelegateModelGroup *m_cacheItems; + QQmlDelegateModelGroup *m_items; + QQmlDelegateModelGroup *m_persistedItems; + }; + QQmlDelegateModelGroup *m_groups[Compositor::MaximumGroupCount]; + }; +}; + +class QQmlPartsModel : public QQmlInstanceModel, public QQmlDelegateModelGroupEmitter +{ + Q_OBJECT + Q_PROPERTY(QString filterOnGroup READ filterGroup WRITE setFilterGroup NOTIFY filterGroupChanged RESET resetFilterGroup) +public: + QQmlPartsModel(QQmlDelegateModel *model, const QString &part, QObject *parent = 0); + ~QQmlPartsModel(); + + QString filterGroup() const; + void setFilterGroup(const QString &group); + void resetFilterGroup(); + void updateFilterGroup(); + void updateFilterGroup(Compositor::Group group, const QQmlChangeSet &changeSet); + + int count() const; + bool isValid() const; + QObject *object(int index, bool asynchronous=false); + ReleaseFlags release(QObject *item); + QString stringValue(int index, const QString &role); + QList watchedRoles() const { return m_watchedRoles; } + void setWatchedRoles(QList roles); + + int indexOf(QObject *item, QObject *objectContext) const; + + void emitModelUpdated(const QQmlChangeSet &changeSet, bool reset); + + void createdPackage(int index, QQuickPackage *package); + void initPackage(int index, QQuickPackage *package); + void destroyingPackage(QQuickPackage *package); + +Q_SIGNALS: + void filterGroupChanged(); + +private: + QQmlDelegateModel *m_model; + QHash m_packaged; + QString m_part; + QString m_filterGroup; + QList m_watchedRoles; + Compositor::Group m_compositorGroup; + bool m_inheritGroup; +}; + +class QMetaPropertyBuilder; + +class QQmlDelegateModelPartsMetaObject : public QQmlOpenMetaObject +{ +public: + QQmlDelegateModelPartsMetaObject(QObject *parent) + : QQmlOpenMetaObject(parent) {} + + virtual void propertyCreated(int, QMetaPropertyBuilder &); + virtual QVariant initialValue(int); +}; + +class QQmlDelegateModelParts : public QObject +{ +Q_OBJECT +public: + QQmlDelegateModelParts(QQmlDelegateModel *parent); + + QQmlDelegateModel *model; + QList models; +}; + +class QQmlDelegateModelAttachedMetaObject : public QAbstractDynamicMetaObject, public QQmlRefCount +{ +public: + QQmlDelegateModelAttachedMetaObject( + QQmlDelegateModelItemMetaType *metaType, QMetaObject *metaObject); + ~QQmlDelegateModelAttachedMetaObject(); + + void objectDestroyed(QObject *); + int metaCall(QObject *, QMetaObject::Call, int _id, void **); + +private: + QQmlDelegateModelItemMetaType * const metaType; + QMetaObject * const metaObject; + const int memberPropertyOffset; + const int indexPropertyOffset; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/types/qqmllistmodel.cpp b/src/qml/types/qqmllistmodel.cpp new file mode 100644 index 0000000000..9609e91b1c --- /dev/null +++ b/src/qml/types/qqmllistmodel.cpp @@ -0,0 +1,2588 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmllistmodel_p_p.h" +#include "qqmllistmodelworkeragent_p.h" +#include +#include +#include + + +#include +#include +#include +#include +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +// Set to 1024 as a debugging aid - easier to distinguish uids from indices of elements/models. +enum { MIN_LISTMODEL_UID = 1024 }; + +static QAtomicInt uidCounter(MIN_LISTMODEL_UID); + +template +static bool isMemoryUsed(const char *mem) +{ + for (size_t i=0 ; i < sizeof(T) ; ++i) { + if (mem[i] != 0) + return true; + } + + return false; +} + +static QString roleTypeName(ListLayout::Role::DataType t) +{ + QString result; + const char *roleTypeNames[] = { "String", "Number", "Bool", "List", "QObject", "VariantMap", "DateTime" }; + + if (t > ListLayout::Role::Invalid && t < ListLayout::Role::MaxDataType) + result = QString::fromLatin1(roleTypeNames[t]); + + return result; +} + +const ListLayout::Role &ListLayout::getRoleOrCreate(const QString &key, Role::DataType type) +{ + QStringHash::Node *node = roleHash.findNode(key); + if (node) { + const Role &r = *node->value; + if (type != r.type) + qmlInfo(0) << QString::fromLatin1("Can't assign to existing role '%1' of different type [%2 -> %3]").arg(r.name).arg(roleTypeName(type)).arg(roleTypeName(r.type)); + return r; + } + + return createRole(key, type); +} + +const ListLayout::Role &ListLayout::getRoleOrCreate(v8::Handle key, Role::DataType type) +{ + QHashedV8String hashedKey(key); + QStringHash::Node *node = roleHash.findNode(hashedKey); + if (node) { + const Role &r = *node->value; + if (type != r.type) + qmlInfo(0) << QString::fromLatin1("Can't assign to existing role '%1' of different type [%2 -> %3]").arg(r.name).arg(roleTypeName(type)).arg(roleTypeName(r.type)); + return r; + } + + QString qkey; + qkey.resize(key->Length()); + key->Write(reinterpret_cast(qkey.data())); + + return createRole(qkey, type); +} + +const ListLayout::Role &ListLayout::createRole(const QString &key, ListLayout::Role::DataType type) +{ + const int dataSizes[] = { sizeof(QString), sizeof(double), sizeof(bool), sizeof(ListModel *), sizeof(QQmlGuard), sizeof(QVariantMap), sizeof(QDateTime) }; + const int dataAlignments[] = { sizeof(QString), sizeof(double), sizeof(bool), sizeof(ListModel *), sizeof(QObject *), sizeof(QVariantMap), sizeof(QDateTime) }; + + Role *r = new Role; + r->name = key; + r->type = type; + + if (type == Role::List) { + r->subLayout = new ListLayout; + } else { + r->subLayout = 0; + } + + int dataSize = dataSizes[type]; + int dataAlignment = dataAlignments[type]; + + int dataOffset = (currentBlockOffset + dataAlignment-1) & ~(dataAlignment-1); + if (dataOffset + dataSize > ListElement::BLOCK_SIZE) { + r->blockIndex = ++currentBlock; + r->blockOffset = 0; + currentBlockOffset = dataSize; + } else { + r->blockIndex = currentBlock; + r->blockOffset = dataOffset; + currentBlockOffset = dataOffset + dataSize; + } + + int roleIndex = roles.count(); + r->index = roleIndex; + + roles.append(r); + roleHash.insert(key, r); + + return *r; +} + +ListLayout::ListLayout(const ListLayout *other) : currentBlock(0), currentBlockOffset(0) +{ + for (int i=0 ; i < other->roles.count() ; ++i) { + Role *role = new Role(other->roles[i]); + roles.append(role); + roleHash.insert(role->name, role); + } + currentBlockOffset = other->currentBlockOffset; + currentBlock = other->currentBlock; +} + +ListLayout::~ListLayout() +{ + for (int i=0 ; i < roles.count() ; ++i) { + delete roles[i]; + } +} + +void ListLayout::sync(ListLayout *src, ListLayout *target) +{ + int roleOffset = target->roles.count(); + int newRoleCount = src->roles.count() - roleOffset; + + for (int i=0 ; i < newRoleCount ; ++i) { + Role *role = new Role(src->roles[roleOffset + i]); + target->roles.append(role); + target->roleHash.insert(role->name, role); + } + + target->currentBlockOffset = src->currentBlockOffset; + target->currentBlock = src->currentBlock; +} + +ListLayout::Role::Role(const Role *other) +{ + name = other->name; + type = other->type; + blockIndex = other->blockIndex; + blockOffset = other->blockOffset; + index = other->index; + if (other->subLayout) + subLayout = new ListLayout(other->subLayout); + else + subLayout = 0; +} + +ListLayout::Role::~Role() +{ + delete subLayout; +} + +const ListLayout::Role *ListLayout::getRoleOrCreate(const QString &key, const QVariant &data) +{ + Role::DataType type; + + switch (data.type()) { + case QVariant::Double: type = Role::Number; break; + case QVariant::Int: type = Role::Number; break; + case QVariant::UserType: type = Role::List; break; + case QVariant::Bool: type = Role::Bool; break; + case QVariant::String: type = Role::String; break; + case QVariant::Map: type = Role::VariantMap; break; + default: type = Role::Invalid; break; + } + + if (type == Role::Invalid) { + qmlInfo(0) << "Can't create role for unsupported data type"; + return 0; + } + + return &getRoleOrCreate(key, type); +} + +const ListLayout::Role *ListLayout::getExistingRole(const QString &key) +{ + Role *r = 0; + QStringHash::Node *node = roleHash.findNode(key); + if (node) + r = node->value; + return r; +} + +const ListLayout::Role *ListLayout::getExistingRole(v8::Handle key) +{ + Role *r = 0; + QHashedV8String hashedKey(key); + QStringHash::Node *node = roleHash.findNode(hashedKey); + if (node) + r = node->value; + return r; +} + +ModelObject *ListModel::getOrCreateModelObject(QQmlListModel *model, int elementIndex) +{ + ListElement *e = elements[elementIndex]; + if (e->m_objectCache == 0) { + e->m_objectCache = new ModelObject(model, elementIndex); + } + return e->m_objectCache; +} + +void ListModel::sync(ListModel *src, ListModel *target, QHash *targetModelHash) +{ + // Sanity check + target->m_uid = src->m_uid; + if (targetModelHash) + targetModelHash->insert(target->m_uid, target); + + // Build hash of elements <-> uid for each of the lists + QHash elementHash; + for (int i=0 ; i < target->elements.count() ; ++i) { + ListElement *e = target->elements.at(i); + int uid = e->getUid(); + ElementSync sync; + sync.target = e; + elementHash.insert(uid, sync); + } + for (int i=0 ; i < src->elements.count() ; ++i) { + ListElement *e = src->elements.at(i); + int uid = e->getUid(); + + QHash::iterator it = elementHash.find(uid); + if (it == elementHash.end()) { + ElementSync sync; + sync.src = e; + elementHash.insert(uid, sync); + } else { + ElementSync &sync = it.value(); + sync.src = e; + } + } + + // Get list of elements that are in the target but no longer in the source. These get deleted first. + QHash::iterator it = elementHash.begin(); + QHash::iterator end = elementHash.end(); + while (it != end) { + const ElementSync &s = it.value(); + if (s.src == 0) { + s.target->destroy(target->m_layout); + target->elements.removeOne(s.target); + delete s.target; + } + ++it; + } + + // Sync the layouts + ListLayout::sync(src->m_layout, target->m_layout); + + // Clear the target list, and append in correct order from the source + target->elements.clear(); + for (int i=0 ; i < src->elements.count() ; ++i) { + ListElement *srcElement = src->elements.at(i); + it = elementHash.find(srcElement->getUid()); + const ElementSync &s = it.value(); + ListElement *targetElement = s.target; + if (targetElement == 0) { + targetElement = new ListElement(srcElement->getUid()); + } + ListElement::sync(srcElement, src->m_layout, targetElement, target->m_layout, targetModelHash); + target->elements.append(targetElement); + } + + target->updateCacheIndices(); + + // Update values stored in target meta objects + for (int i=0 ; i < target->elements.count() ; ++i) { + ListElement *e = target->elements[i]; + if (e->m_objectCache) + e->m_objectCache->updateValues(); + } +} + +ListModel::ListModel(ListLayout *layout, QQmlListModel *modelCache, int uid) : m_layout(layout), m_modelCache(modelCache) +{ + if (uid == -1) + uid = uidCounter.fetchAndAddOrdered(1); + m_uid = uid; +} + +void ListModel::destroy() +{ + clear(); + m_uid = -1; + m_layout = 0; + if (m_modelCache && m_modelCache->m_primary == false) + delete m_modelCache; + m_modelCache = 0; +} + +int ListModel::appendElement() +{ + int elementIndex = elements.count(); + newElement(elementIndex); + return elementIndex; +} + +void ListModel::insertElement(int index) +{ + newElement(index); + updateCacheIndices(); +} + +void ListModel::move(int from, int to, int n) +{ + if (from > to) { + // Only move forwards - flip if backwards moving + int tfrom = from; + int tto = to; + from = tto; + to = tto+n; + n = tfrom-tto; + } + + QPODVector store; + for (int i=0 ; i < (to-from) ; ++i) + store.append(elements[from+n+i]); + for (int i=0 ; i < n ; ++i) + store.append(elements[from+i]); + for (int i=0 ; i < store.count() ; ++i) + elements[from+i] = store[i]; + + updateCacheIndices(); +} + +void ListModel::newElement(int index) +{ + ListElement *e = new ListElement; + elements.insert(index, e); +} + +void ListModel::updateCacheIndices() +{ + for (int i=0 ; i < elements.count() ; ++i) { + ListElement *e = elements.at(i); + if (e->m_objectCache) { + e->m_objectCache->m_elementIndex = i; + } + } +} + +QVariant ListModel::getProperty(int elementIndex, int roleIndex, const QQmlListModel *owner, QV8Engine *eng) +{ + ListElement *e = elements[elementIndex]; + const ListLayout::Role &r = m_layout->getExistingRole(roleIndex); + return e->getProperty(r, owner, eng); +} + +ListModel *ListModel::getListProperty(int elementIndex, const ListLayout::Role &role) +{ + ListElement *e = elements[elementIndex]; + return e->getListProperty(role); +} + +void ListModel::set(int elementIndex, v8::Handle object, QVector *roles, QV8Engine *eng) +{ + ListElement *e = elements[elementIndex]; + + v8::Local propertyNames = object->GetPropertyNames(); + int propertyCount = propertyNames->Length(); + + for (int i=0 ; i < propertyCount ; ++i) { + v8::Local propertyName = propertyNames->Get(i)->ToString(); + v8::Local propertyValue = object->Get(propertyName); + + // Check if this key exists yet + int roleIndex = -1; + + // Add the value now + if (propertyValue->IsString()) { + const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::String); + v8::Handle jsString = propertyValue->ToString(); + QString qstr; + qstr.resize(jsString->Length()); + jsString->Write(reinterpret_cast(qstr.data())); + roleIndex = e->setStringProperty(r, qstr); + } else if (propertyValue->IsNumber()) { + const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Number); + roleIndex = e->setDoubleProperty(r, propertyValue->NumberValue()); + } else if (propertyValue->IsArray()) { + const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::List); + ListModel *subModel = new ListModel(r.subLayout, 0, -1); + + v8::Handle subArray = v8::Handle::Cast(propertyValue); + int arrayLength = subArray->Length(); + for (int j=0 ; j < arrayLength ; ++j) { + v8::Handle subObject = subArray->Get(j)->ToObject(); + subModel->append(subObject, eng); + } + + roleIndex = e->setListProperty(r, subModel); + } else if (propertyValue->IsBoolean()) { + const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Bool); + roleIndex = e->setBoolProperty(r, propertyValue->BooleanValue()); + } else if (propertyValue->IsDate()) { + const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::DateTime); + QDateTime dt = QV8Engine::qtDateTimeFromJsDate(v8::Handle::Cast(propertyValue)->NumberValue()); + roleIndex = e->setDateTimeProperty(r, dt); + } else if (propertyValue->IsObject()) { + QV8ObjectResource *r = (QV8ObjectResource *) propertyValue->ToObject()->GetExternalResource(); + if (r && r->resourceType() == QV8ObjectResource::QObjectType) { + QObject *o = QV8QObjectWrapper::toQObject(r); + const ListLayout::Role &role = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::QObject); + if (role.type == ListLayout::Role::QObject) + roleIndex = e->setQObjectProperty(role, o); + } else { + const ListLayout::Role &role = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::VariantMap); + if (role.type == ListLayout::Role::VariantMap) + roleIndex = e->setVariantMapProperty(role, propertyValue->ToObject(), eng); + } + } else if (propertyValue.IsEmpty() || propertyValue->IsUndefined() || propertyValue->IsNull()) { + const ListLayout::Role *r = m_layout->getExistingRole(propertyName); + if (r) + e->clearProperty(*r); + } + + if (roleIndex != -1) + roles->append(roleIndex); + } + + if (e->m_objectCache) { + e->m_objectCache->updateValues(*roles); + } +} + +void ListModel::set(int elementIndex, v8::Handle object, QV8Engine *eng) +{ + ListElement *e = elements[elementIndex]; + + v8::Local propertyNames = object->GetPropertyNames(); + int propertyCount = propertyNames->Length(); + + for (int i=0 ; i < propertyCount ; ++i) { + v8::Local propertyName = propertyNames->Get(i)->ToString(); + v8::Local propertyValue = object->Get(propertyName); + + // Add the value now + if (propertyValue->IsString()) { + const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::String); + if (r.type == ListLayout::Role::String) { + v8::Handle jsString = propertyValue->ToString(); + QString qstr; + qstr.resize(jsString->Length()); + jsString->Write(reinterpret_cast(qstr.data())); + e->setStringPropertyFast(r, qstr); + } + } else if (propertyValue->IsNumber()) { + const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Number); + if (r.type == ListLayout::Role::Number) { + e->setDoublePropertyFast(r, propertyValue->NumberValue()); + } + } else if (propertyValue->IsArray()) { + const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::List); + if (r.type == ListLayout::Role::List) { + ListModel *subModel = new ListModel(r.subLayout, 0, -1); + + v8::Handle subArray = v8::Handle::Cast(propertyValue); + int arrayLength = subArray->Length(); + for (int j=0 ; j < arrayLength ; ++j) { + v8::Handle subObject = subArray->Get(j)->ToObject(); + subModel->append(subObject, eng); + } + + e->setListPropertyFast(r, subModel); + } + } else if (propertyValue->IsBoolean()) { + const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Bool); + if (r.type == ListLayout::Role::Bool) { + e->setBoolPropertyFast(r, propertyValue->BooleanValue()); + } + } else if (propertyValue->IsDate()) { + const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::DateTime); + if (r.type == ListLayout::Role::DateTime) { + QDateTime dt = QV8Engine::qtDateTimeFromJsDate(v8::Handle::Cast(propertyValue)->NumberValue()); + e->setDateTimePropertyFast(r, dt); + } + } else if (propertyValue->IsObject()) { + QV8ObjectResource *r = (QV8ObjectResource *) propertyValue->ToObject()->GetExternalResource(); + if (r && r->resourceType() == QV8ObjectResource::QObjectType) { + QObject *o = QV8QObjectWrapper::toQObject(r); + const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::QObject); + if (r.type == ListLayout::Role::QObject) + e->setQObjectPropertyFast(r, o); + } else { + const ListLayout::Role &role = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::VariantMap); + if (role.type == ListLayout::Role::VariantMap) + e->setVariantMapFast(role, propertyValue->ToObject(), eng); + } + } else if (propertyValue.IsEmpty() || propertyValue->IsUndefined() || propertyValue->IsNull()) { + const ListLayout::Role *r = m_layout->getExistingRole(propertyName); + if (r) + e->clearProperty(*r); + } + } +} + +void ListModel::clear() +{ + int elementCount = elements.count(); + for (int i=0 ; i < elementCount ; ++i) { + elements[i]->destroy(m_layout); + delete elements[i]; + } + elements.clear(); +} + +void ListModel::remove(int index, int count) +{ + for (int i=0 ; i < count ; ++i) { + elements[index+i]->destroy(m_layout); + delete elements[index+i]; + } + elements.remove(index, count); + updateCacheIndices(); +} + +void ListModel::insert(int elementIndex, v8::Handle object, QV8Engine *eng) +{ + insertElement(elementIndex); + set(elementIndex, object, eng); +} + +int ListModel::append(v8::Handle object, QV8Engine *eng) +{ + int elementIndex = appendElement(); + set(elementIndex, object, eng); + return elementIndex; +} + +int ListModel::setOrCreateProperty(int elementIndex, const QString &key, const QVariant &data) +{ + int roleIndex = -1; + + if (elementIndex >= 0 && elementIndex < elements.count()) { + ListElement *e = elements[elementIndex]; + + const ListLayout::Role *r = m_layout->getRoleOrCreate(key, data); + if (r) { + roleIndex = e->setVariantProperty(*r, data); + + if (roleIndex != -1 && e->m_objectCache) { + QVector roles; + roles << roleIndex; + e->m_objectCache->updateValues(roles); + } + } + } + + return roleIndex; +} + +int ListModel::setExistingProperty(int elementIndex, const QString &key, v8::Handle data, QV8Engine *eng) +{ + int roleIndex = -1; + + if (elementIndex >= 0 && elementIndex < elements.count()) { + ListElement *e = elements[elementIndex]; + const ListLayout::Role *r = m_layout->getExistingRole(key); + if (r) + roleIndex = e->setJsProperty(*r, data, eng); + } + + return roleIndex; +} + +inline char *ListElement::getPropertyMemory(const ListLayout::Role &role) +{ + ListElement *e = this; + int blockIndex = 0; + while (blockIndex < role.blockIndex) { + if (e->next == 0) { + e->next = new ListElement; + e->next->uid = uid; + } + e = e->next; + ++blockIndex; + } + + char *mem = &e->data[role.blockOffset]; + return mem; +} + +QString *ListElement::getStringProperty(const ListLayout::Role &role) +{ + char *mem = getPropertyMemory(role); + QString *s = reinterpret_cast(mem); + return s->data_ptr() ? s : 0; +} + +QObject *ListElement::getQObjectProperty(const ListLayout::Role &role) +{ + char *mem = getPropertyMemory(role); + QQmlGuard *o = reinterpret_cast *>(mem); + return o->data(); +} + +QVariantMap *ListElement::getVariantMapProperty(const ListLayout::Role &role) +{ + QVariantMap *map = 0; + + char *mem = getPropertyMemory(role); + if (isMemoryUsed(mem)) + map = reinterpret_cast(mem); + + return map; +} + +QDateTime *ListElement::getDateTimeProperty(const ListLayout::Role &role) +{ + QDateTime *dt = 0; + + char *mem = getPropertyMemory(role); + if (isMemoryUsed(mem)) + dt = reinterpret_cast(mem); + + return dt; +} + +QQmlGuard *ListElement::getGuardProperty(const ListLayout::Role &role) +{ + char *mem = getPropertyMemory(role); + + bool existingGuard = false; + for (size_t i=0 ; i < sizeof(QQmlGuard) ; ++i) { + if (mem[i] != 0) { + existingGuard = true; + break; + } + } + + QQmlGuard *o = 0; + + if (existingGuard) + o = reinterpret_cast *>(mem); + + return o; +} + +ListModel *ListElement::getListProperty(const ListLayout::Role &role) +{ + char *mem = getPropertyMemory(role); + ListModel **value = reinterpret_cast(mem); + return *value; +} + +QVariant ListElement::getProperty(const ListLayout::Role &role, const QQmlListModel *owner, QV8Engine *eng) +{ + char *mem = getPropertyMemory(role); + + QVariant data; + + switch (role.type) { + case ListLayout::Role::Number: + { + double *value = reinterpret_cast(mem); + data = *value; + } + break; + case ListLayout::Role::String: + { + QString *value = reinterpret_cast(mem); + if (value->data_ptr() != 0) + data = *value; + } + break; + case ListLayout::Role::Bool: + { + bool *value = reinterpret_cast(mem); + data = *value; + } + break; + case ListLayout::Role::List: + { + ListModel **value = reinterpret_cast(mem); + ListModel *model = *value; + + if (model) { + if (model->m_modelCache == 0) { + model->m_modelCache = new QQmlListModel(owner, model, eng); + QQmlEngine::setContextForObject(model->m_modelCache, QQmlEngine::contextForObject(owner)); + } + + QObject *object = model->m_modelCache; + data = QVariant::fromValue(object); + } + } + break; + case ListLayout::Role::QObject: + { + QQmlGuard *guard = reinterpret_cast *>(mem); + QObject *object = guard->data(); + if (object) + data = QVariant::fromValue(object); + } + break; + case ListLayout::Role::VariantMap: + { + if (isMemoryUsed(mem)) { + QVariantMap *map = reinterpret_cast(mem); + data = *map; + } + } + break; + case ListLayout::Role::DateTime: + { + if (isMemoryUsed(mem)) { + QDateTime *dt = reinterpret_cast(mem); + data = *dt; + } + } + break; + default: + break; + } + + return data; +} + +int ListElement::setStringProperty(const ListLayout::Role &role, const QString &s) +{ + int roleIndex = -1; + + if (role.type == ListLayout::Role::String) { + char *mem = getPropertyMemory(role); + QString *c = reinterpret_cast(mem); + bool changed; + if (c->data_ptr() == 0) { + new (mem) QString(s); + changed = true; + } else { + changed = c->compare(s) != 0; + *c = s; + } + if (changed) + roleIndex = role.index; + } + + return roleIndex; +} + +int ListElement::setDoubleProperty(const ListLayout::Role &role, double d) +{ + int roleIndex = -1; + + if (role.type == ListLayout::Role::Number) { + char *mem = getPropertyMemory(role); + double *value = new (mem) double; + bool changed = *value != d; + *value = d; + if (changed) + roleIndex = role.index; + } + + return roleIndex; +} + +int ListElement::setBoolProperty(const ListLayout::Role &role, bool b) +{ + int roleIndex = -1; + + if (role.type == ListLayout::Role::Bool) { + char *mem = getPropertyMemory(role); + bool *value = new (mem) bool; + bool changed = *value != b; + *value = b; + if (changed) + roleIndex = role.index; + } + + return roleIndex; +} + +int ListElement::setListProperty(const ListLayout::Role &role, ListModel *m) +{ + int roleIndex = -1; + + if (role.type == ListLayout::Role::List) { + char *mem = getPropertyMemory(role); + ListModel **value = new (mem) ListModel *; + if (*value) { + (*value)->destroy(); + delete *value; + } + *value = m; + roleIndex = role.index; + } + + return roleIndex; +} + +int ListElement::setQObjectProperty(const ListLayout::Role &role, QObject *o) +{ + int roleIndex = -1; + + if (role.type == ListLayout::Role::QObject) { + char *mem = getPropertyMemory(role); + QQmlGuard *g = reinterpret_cast *>(mem); + bool existingGuard = false; + for (size_t i=0 ; i < sizeof(QQmlGuard) ; ++i) { + if (mem[i] != 0) { + existingGuard = true; + break; + } + } + bool changed; + if (existingGuard) { + changed = g->data() != o; + g->~QQmlGuard(); + } else { + changed = true; + } + new (mem) QQmlGuard(o); + if (changed) + roleIndex = role.index; + } + + return roleIndex; +} + +int ListElement::setVariantMapProperty(const ListLayout::Role &role, v8::Handle o, QV8Engine *eng) +{ + int roleIndex = -1; + + if (role.type == ListLayout::Role::VariantMap) { + char *mem = getPropertyMemory(role); + if (isMemoryUsed(mem)) { + QVariantMap *map = reinterpret_cast(mem); + map->~QMap(); + } + new (mem) QVariantMap(eng->variantMapFromJS(o)); + roleIndex = role.index; + } + + return roleIndex; +} + +int ListElement::setVariantMapProperty(const ListLayout::Role &role, QVariantMap *m) +{ + int roleIndex = -1; + + if (role.type == ListLayout::Role::VariantMap) { + char *mem = getPropertyMemory(role); + if (isMemoryUsed(mem)) { + QVariantMap *map = reinterpret_cast(mem); + map->~QMap(); + } + if (m) + new (mem) QVariantMap(*m); + else + new (mem) QVariantMap; + roleIndex = role.index; + } + + return roleIndex; +} + +int ListElement::setDateTimeProperty(const ListLayout::Role &role, const QDateTime &dt) +{ + int roleIndex = -1; + + if (role.type == ListLayout::Role::DateTime) { + char *mem = getPropertyMemory(role); + if (isMemoryUsed(mem)) { + QDateTime *dt = reinterpret_cast(mem); + dt->~QDateTime(); + } + new (mem) QDateTime(dt); + roleIndex = role.index; + } + + return roleIndex; +} + +void ListElement::setStringPropertyFast(const ListLayout::Role &role, const QString &s) +{ + char *mem = getPropertyMemory(role); + new (mem) QString(s); +} + +void ListElement::setDoublePropertyFast(const ListLayout::Role &role, double d) +{ + char *mem = getPropertyMemory(role); + double *value = new (mem) double; + *value = d; +} + +void ListElement::setBoolPropertyFast(const ListLayout::Role &role, bool b) +{ + char *mem = getPropertyMemory(role); + bool *value = new (mem) bool; + *value = b; +} + +void ListElement::setQObjectPropertyFast(const ListLayout::Role &role, QObject *o) +{ + char *mem = getPropertyMemory(role); + new (mem) QQmlGuard(o); +} + +void ListElement::setListPropertyFast(const ListLayout::Role &role, ListModel *m) +{ + char *mem = getPropertyMemory(role); + ListModel **value = new (mem) ListModel *; + *value = m; +} + +void ListElement::setVariantMapFast(const ListLayout::Role &role, v8::Handle o, QV8Engine *eng) +{ + char *mem = getPropertyMemory(role); + QVariantMap *map = new (mem) QVariantMap; + *map = eng->variantMapFromJS(o); +} + +void ListElement::setDateTimePropertyFast(const ListLayout::Role &role, const QDateTime &dt) +{ + char *mem = getPropertyMemory(role); + new (mem) QDateTime(dt); +} + +void ListElement::clearProperty(const ListLayout::Role &role) +{ + switch (role.type) { + case ListLayout::Role::String: + setStringProperty(role, QString()); + break; + case ListLayout::Role::Number: + setDoubleProperty(role, 0.0); + break; + case ListLayout::Role::Bool: + setBoolProperty(role, false); + break; + case ListLayout::Role::List: + setListProperty(role, 0); + break; + case ListLayout::Role::QObject: + setQObjectProperty(role, 0); + break; + case ListLayout::Role::DateTime: + setDateTimeProperty(role, QDateTime()); + break; + case ListLayout::Role::VariantMap: + setVariantMapProperty(role, 0); + break; + default: + break; + } +} + +ListElement::ListElement() +{ + m_objectCache = 0; + uid = uidCounter.fetchAndAddOrdered(1); + next = 0; + memset(data, 0, sizeof(data)); +} + +ListElement::ListElement(int existingUid) +{ + m_objectCache = 0; + uid = existingUid; + next = 0; + memset(data, 0, sizeof(data)); +} + +ListElement::~ListElement() +{ + delete next; +} + +void ListElement::sync(ListElement *src, ListLayout *srcLayout, ListElement *target, ListLayout *targetLayout, QHash *targetModelHash) +{ + for (int i=0 ; i < srcLayout->roleCount() ; ++i) { + const ListLayout::Role &srcRole = srcLayout->getExistingRole(i); + const ListLayout::Role &targetRole = targetLayout->getExistingRole(i); + + switch (srcRole.type) { + case ListLayout::Role::List: + { + ListModel *srcSubModel = src->getListProperty(srcRole); + ListModel *targetSubModel = target->getListProperty(targetRole); + + if (srcSubModel) { + if (targetSubModel == 0) { + targetSubModel = new ListModel(targetRole.subLayout, 0, srcSubModel->getUid()); + target->setListPropertyFast(targetRole, targetSubModel); + } + ListModel::sync(srcSubModel, targetSubModel, targetModelHash); + } + } + break; + case ListLayout::Role::QObject: + { + QObject *object = src->getQObjectProperty(srcRole); + target->setQObjectProperty(targetRole, object); + } + break; + case ListLayout::Role::String: + case ListLayout::Role::Number: + case ListLayout::Role::Bool: + case ListLayout::Role::DateTime: + { + QVariant v = src->getProperty(srcRole, 0, 0); + target->setVariantProperty(targetRole, v); + } + case ListLayout::Role::VariantMap: + { + QVariantMap *map = src->getVariantMapProperty(srcRole); + target->setVariantMapProperty(targetRole, map); + } + break; + default: + break; + } + } + +} + +void ListElement::destroy(ListLayout *layout) +{ + if (layout) { + for (int i=0 ; i < layout->roleCount() ; ++i) { + const ListLayout::Role &r = layout->getExistingRole(i); + + switch (r.type) { + case ListLayout::Role::String: + { + QString *string = getStringProperty(r); + if (string) + string->~QString(); + } + break; + case ListLayout::Role::List: + { + ListModel *model = getListProperty(r); + if (model) { + model->destroy(); + delete model; + } + } + break; + case ListLayout::Role::QObject: + { + QQmlGuard *guard = getGuardProperty(r); + if (guard) + guard->~QQmlGuard(); + } + break; + case ListLayout::Role::VariantMap: + { + QVariantMap *map = getVariantMapProperty(r); + if (map) + map->~QMap(); + } + break; + case ListLayout::Role::DateTime: + { + QDateTime *dt = getDateTimeProperty(r); + if (dt) + dt->~QDateTime(); + } + break; + default: + // other types don't need explicit cleanup. + break; + } + } + + delete m_objectCache; + } + + if (next) + next->destroy(0); + uid = -1; +} + +int ListElement::setVariantProperty(const ListLayout::Role &role, const QVariant &d) +{ + int roleIndex = -1; + + switch (role.type) { + case ListLayout::Role::Number: + roleIndex = setDoubleProperty(role, d.toDouble()); + break; + case ListLayout::Role::String: + roleIndex = setStringProperty(role, d.toString()); + break; + case ListLayout::Role::Bool: + roleIndex = setBoolProperty(role, d.toBool()); + break; + case ListLayout::Role::List: + roleIndex = setListProperty(role, d.value()); + break; + case ListLayout::Role::VariantMap: { + QVariantMap map = d.toMap(); + roleIndex = setVariantMapProperty(role, &map); + } + break; + case ListLayout::Role::DateTime: + roleIndex = setDateTimeProperty(role, d.toDateTime()); + break; + default: + break; + } + + return roleIndex; +} + +int ListElement::setJsProperty(const ListLayout::Role &role, v8::Handle d, QV8Engine *eng) +{ + // Check if this key exists yet + int roleIndex = -1; + + // Add the value now + if (d->IsString()) { + v8::Handle jsString = d->ToString(); + QString qstr; + qstr.resize(jsString->Length()); + jsString->Write(reinterpret_cast(qstr.data())); + roleIndex = setStringProperty(role, qstr); + } else if (d->IsNumber()) { + roleIndex = setDoubleProperty(role, d->NumberValue()); + } else if (d->IsArray()) { + if (role.type == ListLayout::Role::List) { + ListModel *subModel = new ListModel(role.subLayout, 0, -1); + v8::Handle subArray = v8::Handle::Cast(d); + int arrayLength = subArray->Length(); + for (int j=0 ; j < arrayLength ; ++j) { + v8::Handle subObject = subArray->Get(j)->ToObject(); + subModel->append(subObject, eng); + } + roleIndex = setListProperty(role, subModel); + } else { + qmlInfo(0) << QString::fromLatin1("Can't assign to existing role '%1' of different type [%2 -> %3]").arg(role.name).arg(roleTypeName(role.type)).arg(roleTypeName(ListLayout::Role::List)); + } + } else if (d->IsBoolean()) { + roleIndex = setBoolProperty(role, d->BooleanValue()); + } else if (d->IsDate()) { + QDateTime dt = QV8Engine::qtDateTimeFromJsDate(v8::Handle::Cast(d)->NumberValue()); + roleIndex = setDateTimeProperty(role, dt); + } else if (d->IsObject()) { + QV8ObjectResource *r = (QV8ObjectResource *) d->ToObject()->GetExternalResource(); + if (role.type == ListLayout::Role::QObject && r && r->resourceType() == QV8ObjectResource::QObjectType) { + QObject *o = QV8QObjectWrapper::toQObject(r); + roleIndex = setQObjectProperty(role, o); + } else if (role.type == ListLayout::Role::VariantMap) { + roleIndex = setVariantMapProperty(role, d->ToObject(), eng); + } + } else if (d.IsEmpty() || d->IsUndefined() || d->IsNull()) { + clearProperty(role); + } + + return roleIndex; +} + +ModelObject::ModelObject(QQmlListModel *model, int elementIndex) +: m_model(model), m_elementIndex(elementIndex), m_meta(new ModelNodeMetaObject(this)) +{ + updateValues(); + setNodeUpdatesEnabled(true); +} + +void ModelObject::updateValues() +{ + int roleCount = m_model->m_listModel->roleCount(); + for (int i=0 ; i < roleCount ; ++i) { + const ListLayout::Role &role = m_model->m_listModel->getExistingRole(i); + QByteArray name = role.name.toUtf8(); + const QVariant &data = m_model->data(m_elementIndex, i); + setValue(name, data, role.type == ListLayout::Role::List); + } +} + +void ModelObject::updateValues(const QVector &roles) +{ + int roleCount = roles.count(); + for (int i=0 ; i < roleCount ; ++i) { + int roleIndex = roles.at(i); + const ListLayout::Role &role = m_model->m_listModel->getExistingRole(roleIndex); + QByteArray name = role.name.toUtf8(); + const QVariant &data = m_model->data(m_elementIndex, roleIndex); + setValue(name, data, role.type == ListLayout::Role::List); + } +} + +ModelNodeMetaObject::ModelNodeMetaObject(ModelObject *object) +: QQmlOpenMetaObject(object), m_enabled(false), m_obj(object) +{ +} + +ModelNodeMetaObject::~ModelNodeMetaObject() +{ +} + +void ModelNodeMetaObject::propertyWritten(int index) +{ + if (!m_enabled) + return; + + QV8Engine *eng = m_obj->m_model->engine(); + + QString propName = QString::fromUtf8(name(index)); + QVariant value = operator[](index); + + v8::HandleScope handle_scope; + v8::Context::Scope scope(eng->context()); + + v8::Handle v = eng->fromVariant(value); + + int roleIndex = m_obj->m_model->m_listModel->setExistingProperty(m_obj->m_elementIndex, propName, v, eng); + if (roleIndex != -1) { + QVector roles; + roles << roleIndex; + m_obj->m_model->emitItemsChanged(m_obj->m_elementIndex, 1, roles); + } +} + +DynamicRoleModelNode::DynamicRoleModelNode(QQmlListModel *owner, int uid) : m_owner(owner), m_uid(uid), m_meta(new DynamicRoleModelNodeMetaObject(this)) +{ + setNodeUpdatesEnabled(true); +} + +DynamicRoleModelNode *DynamicRoleModelNode::create(const QVariantMap &obj, QQmlListModel *owner) +{ + DynamicRoleModelNode *object = new DynamicRoleModelNode(owner, uidCounter.fetchAndAddOrdered(1)); + QVector roles; + object->updateValues(obj, roles); + return object; +} + +void DynamicRoleModelNode::sync(DynamicRoleModelNode *src, DynamicRoleModelNode *target, QHash *targetModelHash) +{ + for (int i=0 ; i < src->m_meta->count() ; ++i) { + const QByteArray &name = src->m_meta->name(i); + QVariant value = src->m_meta->value(i); + + QQmlListModel *srcModel = qobject_cast(value.value()); + QQmlListModel *targetModel = qobject_cast(target->m_meta->value(i).value()); + + if (srcModel) { + if (targetModel == 0) + targetModel = QQmlListModel::createWithOwner(target->m_owner); + + QQmlListModel::sync(srcModel, targetModel, targetModelHash); + + QObject *targetModelObject = targetModel; + value = QVariant::fromValue(targetModelObject); + } else if (targetModel) { + delete targetModel; + } + + target->setValue(name, value); + } +} + +void DynamicRoleModelNode::updateValues(const QVariantMap &object, QVector &roles) +{ + const QList &keys = object.keys(); + + QList::const_iterator it = keys.begin(); + QList::const_iterator end = keys.end(); + + while (it != end) { + const QString &key = *it; + + int roleIndex = m_owner->m_roles.indexOf(key); + if (roleIndex == -1) { + roleIndex = m_owner->m_roles.count(); + m_owner->m_roles.append(key); + } + + QVariant value = object[key]; + + if (value.type() == QVariant::List) { + QQmlListModel *subModel = QQmlListModel::createWithOwner(m_owner); + + QVariantList subArray = value.toList(); + QVariantList::const_iterator subIt = subArray.begin(); + QVariantList::const_iterator subEnd = subArray.end(); + while (subIt != subEnd) { + const QVariantMap &subObject = subIt->toMap(); + subModel->m_modelObjects.append(DynamicRoleModelNode::create(subObject, subModel)); + ++subIt; + } + + QObject *subModelObject = subModel; + value = QVariant::fromValue(subModelObject); + } + + const QByteArray &keyUtf8 = key.toUtf8(); + + QQmlListModel *existingModel = qobject_cast(m_meta->value(keyUtf8).value()); + if (existingModel) + delete existingModel; + + if (m_meta->setValue(keyUtf8, value)) + roles << roleIndex; + + ++it; + } +} + +DynamicRoleModelNodeMetaObject::DynamicRoleModelNodeMetaObject(DynamicRoleModelNode *object) + : QQmlOpenMetaObject(object), m_enabled(false), m_owner(object) +{ +} + +DynamicRoleModelNodeMetaObject::~DynamicRoleModelNodeMetaObject() +{ + for (int i=0 ; i < count() ; ++i) { + QQmlListModel *subModel = qobject_cast(value(i).value()); + if (subModel) + delete subModel; + } +} + +void DynamicRoleModelNodeMetaObject::propertyWrite(int index) +{ + if (!m_enabled) + return; + + QVariant v = value(index); + QQmlListModel *model = qobject_cast(v.value()); + if (model) + delete model; +} + +void DynamicRoleModelNodeMetaObject::propertyWritten(int index) +{ + if (!m_enabled) + return; + + QQmlListModel *parentModel = m_owner->m_owner; + + QVariant v = value(index); + if (v.type() == QVariant::List) { + QQmlListModel *subModel = QQmlListModel::createWithOwner(parentModel); + + QVariantList subArray = v.toList(); + QVariantList::const_iterator subIt = subArray.begin(); + QVariantList::const_iterator subEnd = subArray.end(); + while (subIt != subEnd) { + const QVariantMap &subObject = subIt->toMap(); + subModel->m_modelObjects.append(DynamicRoleModelNode::create(subObject, subModel)); + ++subIt; + } + + QObject *subModelObject = subModel; + v = QVariant::fromValue(subModelObject); + + setValue(index, v); + } + + int elementIndex = parentModel->m_modelObjects.indexOf(m_owner); + int roleIndex = parentModel->m_roles.indexOf(QString::fromLatin1(name(index).constData())); + + if (elementIndex != -1 && roleIndex != -1) { + + QVector roles; + roles << roleIndex; + + parentModel->emitItemsChanged(elementIndex, 1, roles); + } +} + +QQmlListModelParser::ListInstruction *QQmlListModelParser::ListModelData::instructions() const +{ + return (QQmlListModelParser::ListInstruction *)((char *)this + sizeof(ListModelData)); +} + +/*! + \qmltype ListModel + \instantiates QQmlListModel + \inqmlmodule QtQml.Models 2 + \brief Defines a free-form list data source + + The ListModel is a simple container of ListElement definitions, each containing data roles. + The contents can be defined dynamically, or explicitly in QML. + + This type is also available in the QtQuick 2 import. For full documentation, see \l QtQuick2::ListModel +*/ +/*! + \qmltype ListModel + \instantiates QQmlListModel + \inqmlmodule QtQuick 2 + \brief Defines a free-form list data source + \ingroup qtquick-models + + The ListModel is a simple container of ListElement definitions, each containing data roles. + The contents can be defined dynamically, or explicitly in QML. + + The number of elements in the model can be obtained from its \l count property. + A number of familiar methods are also provided to manipulate the contents of the + model, including append(), insert(), move(), remove() and set(). These methods + accept dictionaries as their arguments; these are translated to ListElement objects + by the model. + + Elements can be manipulated via the model using the setProperty() method, which + allows the roles of the specified element to be set and changed. + + \section1 Example Usage + + The following example shows a ListModel containing three elements, with the roles + "name" and "cost". + + \div {class="float-right"} + \inlineimage listmodel.png + \enddiv + + \snippet qml/listmodel/listmodel.qml 0 + + Roles (properties) in each element must begin with a lower-case letter and + should be common to all elements in a model. The ListElement documentation + provides more guidelines for how elements should be defined. + + Since the example model contains an \c id property, it can be referenced + by views, such as the ListView in this example: + + \snippet qml/listmodel/listmodel-simple.qml 0 + \dots 8 + \snippet qml/listmodel/listmodel-simple.qml 1 + + It is possible for roles to contain list data. In the following example we + create a list of fruit attributes: + + \snippet qml/listmodel/listmodel-nested.qml model + + The delegate displays all the fruit attributes: + + \div {class="float-right"} + \inlineimage listmodel-nested.png + \enddiv + + \snippet qml/listmodel/listmodel-nested.qml delegate + + \section1 Modifying List Models + + The content of a ListModel may be created and modified using the clear(), + append(), set(), insert() and setProperty() methods. For example: + + \snippet qml/listmodel/listmodel-modify.qml delegate + + Note that when creating content dynamically the set of available properties + cannot be changed once set. Whatever properties are first added to the model + are the only permitted properties in the model. + + \section1 Using Threaded List Models with WorkerScript + + ListModel can be used together with WorkerScript access a list model + from multiple threads. This is useful if list modifications are + synchronous and take some time: the list operations can be moved to a + different thread to avoid blocking of the main GUI thread. + + Here is an example that uses WorkerScript to periodically append the + current time to a list model: + + \snippet quick/threading/threadedlistmodel/timedisplay.qml 0 + + The included file, \tt dataloader.js, looks like this: + + \snippet quick/threading/threadedlistmodel/dataloader.js 0 + + The timer in the main example sends messages to the worker script by calling + \l WorkerScript::sendMessage(). When this message is received, + \l{WorkerScript::onMessage}{WorkerScript.onMessage()} is invoked in \c dataloader.js, + which appends the current time to the list model. + + Note the call to sync() from the \l{WorkerScript::onMessage}{WorkerScript.onMessage()} + handler. You must call sync() or else the changes made to the list from the external + thread will not be reflected in the list model in the main thread. + + \sa {qml-data-models}{Data Models}, {declarative/threading/threadedlistmodel}{Threaded ListModel example}, QtQml +*/ + +QQmlListModel::QQmlListModel(QObject *parent) +: QAbstractListModel(parent) +{ + m_mainThread = true; + m_primary = true; + m_agent = 0; + m_uid = uidCounter.fetchAndAddOrdered(1); + m_dynamicRoles = false; + + m_layout = new ListLayout; + m_listModel = new ListModel(m_layout, this, -1); + + m_engine = 0; +} + +QQmlListModel::QQmlListModel(const QQmlListModel *owner, ListModel *data, QV8Engine *eng, QObject *parent) +: QAbstractListModel(parent) +{ + m_mainThread = owner->m_mainThread; + m_primary = false; + m_agent = owner->m_agent; + + Q_ASSERT(owner->m_dynamicRoles == false); + m_dynamicRoles = false; + m_layout = 0; + m_listModel = data; + + m_engine = eng; +} + +QQmlListModel::QQmlListModel(QQmlListModel *orig, QQmlListModelWorkerAgent *agent) +: QAbstractListModel(agent) +{ + m_mainThread = false; + m_primary = true; + m_agent = agent; + m_dynamicRoles = orig->m_dynamicRoles; + + m_layout = new ListLayout(orig->m_layout); + m_listModel = new ListModel(m_layout, this, orig->m_listModel->getUid()); + + if (m_dynamicRoles) + sync(orig, this, 0); + else + ListModel::sync(orig->m_listModel, m_listModel, 0); + + m_engine = 0; +} + +QQmlListModel::~QQmlListModel() +{ + for (int i=0 ; i < m_modelObjects.count() ; ++i) + delete m_modelObjects[i]; + + if (m_primary) { + m_listModel->destroy(); + delete m_listModel; + + if (m_mainThread && m_agent) { + m_agent->modelDestroyed(); + m_agent->release(); + } + } + + m_listModel = 0; + + delete m_layout; + m_layout = 0; +} + +QQmlListModel *QQmlListModel::createWithOwner(QQmlListModel *newOwner) +{ + QQmlListModel *model = new QQmlListModel; + + model->m_mainThread = newOwner->m_mainThread; + model->m_engine = newOwner->m_engine; + model->m_agent = newOwner->m_agent; + model->m_dynamicRoles = newOwner->m_dynamicRoles; + + if (model->m_mainThread && model->m_agent) + model->m_agent->addref(); + + QQmlEngine::setContextForObject(model, QQmlEngine::contextForObject(newOwner)); + + return model; +} + +QV8Engine *QQmlListModel::engine() const +{ + if (m_engine == 0) { + m_engine = QQmlEnginePrivate::getV8Engine(qmlEngine(this)); + } + + return m_engine; +} + +void QQmlListModel::sync(QQmlListModel *src, QQmlListModel *target, QHash *targetModelHash) +{ + Q_ASSERT(src->m_dynamicRoles && target->m_dynamicRoles); + + target->m_uid = src->m_uid; + if (targetModelHash) + targetModelHash->insert(target->m_uid, target); + target->m_roles = src->m_roles; + + // Build hash of elements <-> uid for each of the lists + QHash elementHash; + for (int i=0 ; i < target->m_modelObjects.count() ; ++i) { + DynamicRoleModelNode *e = target->m_modelObjects.at(i); + int uid = e->getUid(); + ElementSync sync; + sync.target = e; + elementHash.insert(uid, sync); + } + for (int i=0 ; i < src->m_modelObjects.count() ; ++i) { + DynamicRoleModelNode *e = src->m_modelObjects.at(i); + int uid = e->getUid(); + + QHash::iterator it = elementHash.find(uid); + if (it == elementHash.end()) { + ElementSync sync; + sync.src = e; + elementHash.insert(uid, sync); + } else { + ElementSync &sync = it.value(); + sync.src = e; + } + } + + // Get list of elements that are in the target but no longer in the source. These get deleted first. + QHash::iterator it = elementHash.begin(); + QHash::iterator end = elementHash.end(); + while (it != end) { + const ElementSync &s = it.value(); + if (s.src == 0) { + int targetIndex = target->m_modelObjects.indexOf(s.target); + target->m_modelObjects.remove(targetIndex, 1); + delete s.target; + } + ++it; + } + + // Clear the target list, and append in correct order from the source + target->m_modelObjects.clear(); + for (int i=0 ; i < src->m_modelObjects.count() ; ++i) { + DynamicRoleModelNode *srcElement = src->m_modelObjects.at(i); + it = elementHash.find(srcElement->getUid()); + const ElementSync &s = it.value(); + DynamicRoleModelNode *targetElement = s.target; + if (targetElement == 0) { + targetElement = new DynamicRoleModelNode(target, srcElement->getUid()); + } + DynamicRoleModelNode::sync(srcElement, targetElement, targetModelHash); + target->m_modelObjects.append(targetElement); + } +} + +void QQmlListModel::emitItemsChanged(int index, int count, const QVector &roles) +{ + if (count <= 0) + return; + + if (m_mainThread) { + emit dataChanged(createIndex(index, 0), createIndex(index + count - 1, 0), roles);; + } else { + int uid = m_dynamicRoles ? getUid() : m_listModel->getUid(); + m_agent->data.changedChange(uid, index, count, roles); + } +} + +void QQmlListModel::emitItemsRemoved(int index, int count) +{ + if (count <= 0) + return; + + if (m_mainThread) { + beginRemoveRows(QModelIndex(), index, index + count - 1); + endRemoveRows(); + emit countChanged(); + } else { + int uid = m_dynamicRoles ? getUid() : m_listModel->getUid(); + if (index == 0 && count == this->count()) + m_agent->data.clearChange(uid); + m_agent->data.removeChange(uid, index, count); + } +} + +void QQmlListModel::emitItemsInserted(int index, int count) +{ + if (count <= 0) + return; + + if (m_mainThread) { + beginInsertRows(QModelIndex(), index, index + count - 1); + endInsertRows(); + emit countChanged(); + } else { + int uid = m_dynamicRoles ? getUid() : m_listModel->getUid(); + m_agent->data.insertChange(uid, index, count); + } +} + +void QQmlListModel::emitItemsMoved(int from, int to, int n) +{ + if (n <= 0) + return; + + if (m_mainThread) { + beginMoveRows(QModelIndex(), from, from + n - 1, QModelIndex(), to > from ? to + n : to); + endMoveRows(); + } else { + int uid = m_dynamicRoles ? getUid() : m_listModel->getUid(); + m_agent->data.moveChange(uid, from, n, to); + } +} + +QQmlListModelWorkerAgent *QQmlListModel::agent() +{ + if (m_agent) + return m_agent; + + m_agent = new QQmlListModelWorkerAgent(this); + return m_agent; +} + +QModelIndex QQmlListModel::index(int row, int column, const QModelIndex &parent) const +{ + return row >= 0 && row < count() && column == 0 && !parent.isValid() + ? createIndex(row, column) + : QModelIndex(); +} + +int QQmlListModel::rowCount(const QModelIndex &parent) const +{ + return !parent.isValid() ? count() : 0; +} + +QVariant QQmlListModel::data(const QModelIndex &index, int role) const +{ + return data(index.row(), role); +} + +QVariant QQmlListModel::data(int index, int role) const +{ + QVariant v; + + if (index >= count() || index < 0) + return v; + + if (m_dynamicRoles) + v = m_modelObjects[index]->getValue(m_roles[role]); + else + v = m_listModel->getProperty(index, role, this, engine()); + + return v; +} + +QHash QQmlListModel::roleNames() const +{ + QHash roleNames; + + if (m_dynamicRoles) { + for (int i = 0 ; i < m_roles.count() ; ++i) + roleNames.insert(i, m_roles.at(i).toUtf8()); + } else { + for (int i = 0 ; i < m_listModel->roleCount() ; ++i) { + const ListLayout::Role &r = m_listModel->getExistingRole(i); + roleNames.insert(i, r.name.toUtf8()); + } + } + + return roleNames; +} + +/*! + \qmlproperty bool QtQml2::ListModel::dynamicRoles + + By default, the type of a role is fixed the first time + the role is used. For example, if you create a role called + "data" and assign a number to it, you can no longer assign + a string to the "data" role. However, when the dynamicRoles + property is enabled, the type of a given role is not fixed + and can be different between elements. + + The dynamicRoles property must be set before any data is + added to the ListModel, and must be set from the main + thread. + + A ListModel that has data statically defined (via the + ListElement QML syntax) cannot have the dynamicRoles + property enabled. + + There is a significant performance cost to using a + ListModel with dynamic roles enabled. The cost varies + from platform to platform but is typically somewhere + between 4-6x slower than using static role types. + + Due to the performance cost of using dynamic roles, + they are disabled by default. +*/ +void QQmlListModel::setDynamicRoles(bool enableDynamicRoles) +{ + if (m_mainThread && m_agent == 0) { + if (enableDynamicRoles) { + if (m_layout->roleCount()) + qmlInfo(this) << tr("unable to enable dynamic roles as this model is not empty!"); + else + m_dynamicRoles = true; + } else { + if (m_roles.count()) { + qmlInfo(this) << tr("unable to enable static roles as this model is not empty!"); + } else { + m_dynamicRoles = false; + } + } + } else { + qmlInfo(this) << tr("dynamic role setting must be made from the main thread, before any worker scripts are created"); + } +} + +/*! + \qmlproperty int QtQml2::ListModel::count + The number of data entries in the model. +*/ +int QQmlListModel::count() const +{ + int count; + + if (m_dynamicRoles) + count = m_modelObjects.count(); + else { + count = m_listModel->elementCount(); + } + + return count; +} + +/*! + \qmlmethod QtQml2::ListModel::clear() + + Deletes all content from the model. + + \sa append(), remove() +*/ +void QQmlListModel::clear() +{ + int cleared = count(); + + if (m_dynamicRoles) { + for (int i=0 ; i < m_modelObjects.count() ; ++i) + delete m_modelObjects[i]; + m_modelObjects.clear(); + } else { + m_listModel->clear(); + } + + emitItemsRemoved(0, cleared); +} + +/*! + \qmlmethod QtQml2::ListModel::remove(int index, int count = 1) + + Deletes the content at \a index from the model. + + \sa clear() +*/ +void QQmlListModel::remove(QQmlV8Function *args) +{ + int argLength = args->Length(); + + if (argLength == 1 || argLength == 2) { + int index = (*args)[0]->Int32Value(); + int removeCount = (argLength == 2 ? ((*args)[1]->Int32Value()) : 1); + + if (index < 0 || index+removeCount > count() || removeCount <= 0) { + qmlInfo(this) << tr("remove: indices [%1 - %2] out of range [0 - %3]").arg(index).arg(index+removeCount).arg(count()); + return; + } + + if (m_dynamicRoles) { + for (int i=0 ; i < removeCount ; ++i) + delete m_modelObjects[index+i]; + m_modelObjects.remove(index, removeCount); + } else { + m_listModel->remove(index, removeCount); + } + + emitItemsRemoved(index, removeCount); + } else { + qmlInfo(this) << tr("remove: incorrect number of arguments"); + } +} + +/*! + \qmlmethod QtQml2::ListModel::insert(int index, jsobject dict) + + Adds a new item to the list model at position \a index, with the + values in \a dict. + + \code + fruitModel.insert(2, {"cost": 5.95, "name":"Pizza"}) + \endcode + + The \a index must be to an existing item in the list, or one past + the end of the list (equivalent to append). + + \sa set(), append() +*/ + +void QQmlListModel::insert(QQmlV8Function *args) +{ + if (args->Length() == 2) { + + v8::Handle arg0 = (*args)[0]; + int index = arg0->Int32Value(); + + if (index < 0 || index > count()) { + qmlInfo(this) << tr("insert: index %1 out of range").arg(index); + return; + } + + v8::Handle arg1 = (*args)[1]; + + if (arg1->IsArray()) { + v8::Handle objectArray = v8::Handle::Cast(arg1); + int objectArrayLength = objectArray->Length(); + for (int i=0 ; i < objectArrayLength ; ++i) { + v8::Handle argObject = objectArray->Get(i)->ToObject(); + + if (m_dynamicRoles) { + m_modelObjects.insert(index+i, DynamicRoleModelNode::create(args->engine()->variantMapFromJS(argObject), this)); + } else { + m_listModel->insert(index+i, argObject, args->engine()); + } + } + emitItemsInserted(index, objectArrayLength); + } else if (arg1->IsObject()) { + v8::Handle argObject = arg1->ToObject(); + + if (m_dynamicRoles) { + m_modelObjects.insert(index, DynamicRoleModelNode::create(args->engine()->variantMapFromJS(argObject), this)); + } else { + m_listModel->insert(index, argObject, args->engine()); + } + + emitItemsInserted(index, 1); + } else { + qmlInfo(this) << tr("insert: value is not an object"); + } + } else { + qmlInfo(this) << tr("insert: value is not an object"); + } +} + +/*! + \qmlmethod QtQml2::ListModel::move(int from, int to, int n) + + Moves \a n items \a from one position \a to another. + + The from and to ranges must exist; for example, to move the first 3 items + to the end of the list: + + \code + fruitModel.move(0, fruitModel.count - 3, 3) + \endcode + + \sa append() +*/ +void QQmlListModel::move(int from, int to, int n) +{ + if (n==0 || from==to) + return; + if (!canMove(from, to, n)) { + qmlInfo(this) << tr("move: out of range"); + return; + } + + if (m_dynamicRoles) { + + int realFrom = from; + int realTo = to; + int realN = n; + + if (from > to) { + // Only move forwards - flip if backwards moving + int tfrom = from; + int tto = to; + realFrom = tto; + realTo = tto+n; + realN = tfrom-tto; + } + + QPODVector store; + for (int i=0 ; i < (realTo-realFrom) ; ++i) + store.append(m_modelObjects[realFrom+realN+i]); + for (int i=0 ; i < realN ; ++i) + store.append(m_modelObjects[realFrom+i]); + for (int i=0 ; i < store.count() ; ++i) + m_modelObjects[realFrom+i] = store[i]; + + } else { + m_listModel->move(from, to, n); + } + + emitItemsMoved(from, to, n); +} + +/*! + \qmlmethod QtQml2::ListModel::append(jsobject dict) + + Adds a new item to the end of the list model, with the + values in \a dict. + + \code + fruitModel.append({"cost": 5.95, "name":"Pizza"}) + \endcode + + \sa set(), remove() +*/ +void QQmlListModel::append(QQmlV8Function *args) +{ + if (args->Length() == 1) { + v8::Handle arg = (*args)[0]; + + if (arg->IsArray()) { + v8::Handle objectArray = v8::Handle::Cast(arg); + int objectArrayLength = objectArray->Length(); + + int index = count(); + for (int i=0 ; i < objectArrayLength ; ++i) { + v8::Handle argObject = objectArray->Get(i)->ToObject(); + + if (m_dynamicRoles) { + m_modelObjects.append(DynamicRoleModelNode::create(args->engine()->variantMapFromJS(argObject), this)); + } else { + m_listModel->append(argObject, args->engine()); + } + } + + emitItemsInserted(index, objectArrayLength); + } else if (arg->IsObject()) { + v8::Handle argObject = arg->ToObject(); + + int index; + + if (m_dynamicRoles) { + index = m_modelObjects.count(); + m_modelObjects.append(DynamicRoleModelNode::create(args->engine()->variantMapFromJS(argObject), this)); + } else { + index = m_listModel->append(argObject, args->engine()); + } + + emitItemsInserted(index, 1); + } else { + qmlInfo(this) << tr("append: value is not an object"); + } + } else { + qmlInfo(this) << tr("append: value is not an object"); + } +} + +/*! + \qmlmethod object QtQml2::ListModel::get(int index) + + Returns the item at \a index in the list model. This allows the item + data to be accessed or modified from JavaScript: + + \code + Component.onCompleted: { + fruitModel.append({"cost": 5.95, "name":"Jackfruit"}); + console.log(fruitModel.get(0).cost); + fruitModel.get(0).cost = 10.95; + } + \endcode + + The \a index must be an element in the list. + + Note that properties of the returned object that are themselves objects + will also be models, and this get() method is used to access elements: + + \code + fruitModel.append(..., "attributes": + [{"name":"spikes","value":"7mm"}, + {"name":"color","value":"green"}]); + fruitModel.get(0).attributes.get(1).value; // == "green" + \endcode + + \warning The returned object is not guaranteed to remain valid. It + should not be used in \l{Property Binding}{property bindings}. + + \sa append() +*/ +QQmlV8Handle QQmlListModel::get(int index) const +{ + v8::Handle result = v8::Undefined(); + + if (index >= 0 && index < count()) { + QV8Engine *v8engine = engine(); + + if (m_dynamicRoles) { + DynamicRoleModelNode *object = m_modelObjects[index]; + result = v8engine->newQObject(object); + } else { + ModelObject *object = m_listModel->getOrCreateModelObject(const_cast(this), index); + result = v8engine->newQObject(object); + } + } + + return QQmlV8Handle::fromHandle(result); +} + +/*! + \qmlmethod QtQml2::ListModel::set(int index, jsobject dict) + + Changes the item at \a index in the list model with the + values in \a dict. Properties not appearing in \a dict + are left unchanged. + + \code + fruitModel.set(3, {"cost": 5.95, "name":"Pizza"}) + \endcode + + If \a index is equal to count() then a new item is appended to the + list. Otherwise, \a index must be an element in the list. + + \sa append() +*/ +void QQmlListModel::set(int index, const QQmlV8Handle &handle) +{ + v8::Handle valuemap = handle.toHandle(); + + if (!valuemap->IsObject() || valuemap->IsArray()) { + qmlInfo(this) << tr("set: value is not an object"); + return; + } + if (index > count() || index < 0) { + qmlInfo(this) << tr("set: index %1 out of range").arg(index); + return; + } + + v8::Handle object = valuemap->ToObject(); + + if (index == count()) { + + if (m_dynamicRoles) { + m_modelObjects.append(DynamicRoleModelNode::create(engine()->variantMapFromJS(object), this)); + } else { + m_listModel->insert(index, object, engine()); + } + + emitItemsInserted(index, 1); + } else { + + QVector roles; + + if (m_dynamicRoles) { + m_modelObjects[index]->updateValues(engine()->variantMapFromJS(object), roles); + } else { + m_listModel->set(index, object, &roles, engine()); + } + + if (roles.count()) + emitItemsChanged(index, 1, roles); + } +} + +/*! + \qmlmethod QtQml2::ListModel::setProperty(int index, string property, variant value) + + Changes the \a property of the item at \a index in the list model to \a value. + + \code + fruitModel.setProperty(3, "cost", 5.95) + \endcode + + The \a index must be an element in the list. + + \sa append() +*/ +void QQmlListModel::setProperty(int index, const QString& property, const QVariant& value) +{ + if (count() == 0 || index >= count() || index < 0) { + qmlInfo(this) << tr("set: index %1 out of range").arg(index); + return; + } + + if (m_dynamicRoles) { + int roleIndex = m_roles.indexOf(property); + if (roleIndex == -1) { + roleIndex = m_roles.count(); + m_roles.append(property); + } + if (m_modelObjects[index]->setValue(property.toUtf8(), value)) { + QVector roles; + roles << roleIndex; + emitItemsChanged(index, 1, roles); + } + } else { + int roleIndex = m_listModel->setOrCreateProperty(index, property, value); + if (roleIndex != -1) { + + QVector roles; + roles << roleIndex; + + emitItemsChanged(index, 1, roles); + } + } +} + +/*! + \qmlmethod QtQml2::ListModel::sync() + + Writes any unsaved changes to the list model after it has been modified + from a worker script. +*/ +void QQmlListModel::sync() +{ + // This is just a dummy method to make it look like sync() exists in + // ListModel (and not just QQmlListModelWorkerAgent) and to let + // us document sync(). + qmlInfo(this) << "List sync() can only be called from a WorkerScript"; +} + +bool QQmlListModelParser::compileProperty(const QQmlCustomParserProperty &prop, QList &instr, QByteArray &data) +{ + QList values = prop.assignedValues(); + for(int ii = 0; ii < values.count(); ++ii) { + const QVariant &value = values.at(ii); + + if(value.userType() == qMetaTypeId()) { + QQmlCustomParserNode node = + qvariant_cast(value); + + if (node.name() != listElementTypeName) { + const QMetaObject *mo = resolveType(node.name()); + if (mo != &QQmlListElement::staticMetaObject) { + error(node, QQmlListModel::tr("ListElement: cannot contain nested elements")); + return false; + } + listElementTypeName = node.name(); // cache right name for next time + } + + { + ListInstruction li; + li.type = ListInstruction::Push; + li.dataIdx = -1; + instr << li; + } + + QList props = node.properties(); + for(int jj = 0; jj < props.count(); ++jj) { + const QQmlCustomParserProperty &nodeProp = props.at(jj); + if (nodeProp.name().isEmpty()) { + error(nodeProp, QQmlListModel::tr("ListElement: cannot contain nested elements")); + return false; + } + if (nodeProp.name() == QStringLiteral("id")) { + error(nodeProp, QQmlListModel::tr("ListElement: cannot use reserved \"id\" property")); + return false; + } + + ListInstruction li; + int ref = data.count(); + data.append(nodeProp.name().toUtf8()); + data.append('\0'); + li.type = ListInstruction::Set; + li.dataIdx = ref; + instr << li; + + if(!compileProperty(nodeProp, instr, data)) + return false; + + li.type = ListInstruction::Pop; + li.dataIdx = -1; + instr << li; + } + + { + ListInstruction li; + li.type = ListInstruction::Pop; + li.dataIdx = -1; + instr << li; + } + + } else { + + QQmlScript::Variant variant = + qvariant_cast(value); + + int ref = data.count(); + + QByteArray d; + d += char(variant.type()); // type tag + if (variant.isString()) { + d += variant.asString().toUtf8(); + } else if (variant.isNumber()) { + d += QByteArray::number(variant.asNumber(),'g',20); + } else if (variant.isBoolean()) { + d += char(variant.asBoolean()); + } else if (variant.isScript()) { + if (definesEmptyList(variant.asScript())) { + d[0] = char(QQmlScript::Variant::Invalid); // marks empty list + } else { + QByteArray script = variant.asScript().toUtf8(); + bool ok; + int v = evaluateEnum(script, &ok); + if (!ok) { + using namespace QQmlJS; + AST::Node *node = variant.asAST(); + AST::StringLiteral *literal = 0; + if (AST::CallExpression *callExpr = AST::cast(node)) { + if (AST::IdentifierExpression *idExpr = AST::cast(callExpr->base)) { + if (idExpr->name == QLatin1String("QT_TR_NOOP") || idExpr->name == QLatin1String("QT_TRID_NOOP")) { + if (callExpr->arguments && !callExpr->arguments->next) + literal = AST::cast(callExpr->arguments->expression); + if (!literal) { + error(prop, QQmlListModel::tr("ListElement: improperly specified %1").arg(idExpr->name.toString())); + return false; + } + } else if (idExpr->name == QLatin1String("QT_TRANSLATE_NOOP")) { + if (callExpr->arguments && callExpr->arguments->next && !callExpr->arguments->next->next) + literal = AST::cast(callExpr->arguments->next->expression); + if (!literal) { + error(prop, QQmlListModel::tr("ListElement: improperly specified QT_TRANSLATE_NOOP")); + return false; + } + } + } + } + + if (literal) { + d[0] = char(QQmlScript::Variant::String); + d += literal->value.toUtf8(); + } else { + error(prop, QQmlListModel::tr("ListElement: cannot use script for property value")); + return false; + } + } else { + d[0] = char(QQmlScript::Variant::Number); + d += QByteArray::number(v); + } + } + } + d.append('\0'); + data.append(d); + + ListInstruction li; + li.type = ListInstruction::Value; + li.dataIdx = ref; + instr << li; + } + } + + return true; +} + +QByteArray QQmlListModelParser::compile(const QList &customProps) +{ + QList instr; + QByteArray data; + listElementTypeName = QString(); // unknown + + for(int ii = 0; ii < customProps.count(); ++ii) { + const QQmlCustomParserProperty &prop = customProps.at(ii); + if(!prop.name().isEmpty()) { // isn't default property + error(prop, QQmlListModel::tr("ListModel: undefined property '%1'").arg(prop.name())); + return QByteArray(); + } + + if(!compileProperty(prop, instr, data)) { + return QByteArray(); + } + } + + int size = sizeof(ListModelData) + + instr.count() * sizeof(ListInstruction) + + data.count(); + + QByteArray rv; + rv.resize(size); + + ListModelData *lmd = (ListModelData *)rv.data(); + lmd->dataOffset = sizeof(ListModelData) + + instr.count() * sizeof(ListInstruction); + lmd->instrCount = instr.count(); + for (int ii = 0; ii < instr.count(); ++ii) + lmd->instructions()[ii] = instr.at(ii); + ::memcpy(rv.data() + lmd->dataOffset, data.constData(), data.count()); + + return rv; +} + +void QQmlListModelParser::setCustomData(QObject *obj, const QByteArray &d) +{ + QQmlListModel *rv = static_cast(obj); + + QV8Engine *engine = QQmlEnginePrivate::getV8Engine(qmlEngine(rv)); + rv->m_engine = engine; + + const ListModelData *lmd = (const ListModelData *)d.constData(); + const char *data = ((const char *)lmd) + lmd->dataOffset; + + bool setRoles = false; + + QStack stack; + + for (int ii = 0; ii < lmd->instrCount; ++ii) { + const ListInstruction &instr = lmd->instructions()[ii]; + + switch(instr.type) { + case ListInstruction::Push: + { + Q_ASSERT(!rv->m_dynamicRoles); + + ListModel *subModel = 0; + + if (stack.count() == 0) { + subModel = rv->m_listModel; + } else { + const DataStackElement &e0 = stack.at(stack.size() - 1); + DataStackElement &e1 = stack[stack.size() - 2]; + + const ListLayout::Role &role = e1.model->getOrCreateListRole(e0.name); + if (role.type == ListLayout::Role::List) { + subModel = e1.model->getListProperty(e1.elementIndex, role); + + if (subModel == 0) { + subModel = new ListModel(role.subLayout, 0, -1); + QVariant vModel = QVariant::fromValue(subModel); + e1.model->setOrCreateProperty(e1.elementIndex, e0.name, vModel); + } + } + } + + DataStackElement e; + e.model = subModel; + e.elementIndex = subModel ? subModel->appendElement() : -1; + stack.push(e); + } + break; + + case ListInstruction::Pop: + stack.pop(); + break; + + case ListInstruction::Value: + { + const DataStackElement &e0 = stack.at(stack.size() - 1); + DataStackElement &e1 = stack[stack.size() - 2]; + + QString name = e0.name; + QVariant value; + + switch (QQmlScript::Variant::Type(data[instr.dataIdx])) { + case QQmlScript::Variant::Invalid: + { + const ListLayout::Role &role = e1.model->getOrCreateListRole(e0.name); + ListModel *emptyModel = new ListModel(role.subLayout, 0, -1); + value = QVariant::fromValue(emptyModel); + } + break; + case QQmlScript::Variant::Boolean: + value = bool(data[1 + instr.dataIdx]); + break; + case QQmlScript::Variant::Number: + value = QByteArray(data + 1 + instr.dataIdx).toDouble(); + break; + case QQmlScript::Variant::String: + value = QString::fromUtf8(data + 1 + instr.dataIdx); + break; + default: + Q_ASSERT("Format error in ListInstruction"); + } + + e1.model->setOrCreateProperty(e1.elementIndex, name, value); + setRoles = true; + } + break; + + case ListInstruction::Set: + { + DataStackElement e; + e.name = QString::fromUtf8(data + instr.dataIdx); + stack.push(e); + } + break; + } + } + + if (setRoles == false) + qmlInfo(obj) << "All ListElement declarations are empty, no roles can be created unless dynamicRoles is set."; +} + +bool QQmlListModelParser::definesEmptyList(const QString &s) +{ + if (s.startsWith(QLatin1Char('[')) && s.endsWith(QLatin1Char(']'))) { + for (int i=1; i +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + + +class QQmlListModelWorkerAgent; +class ListModel; +class ListLayout; + +class Q_QML_PRIVATE_EXPORT QQmlListModel : public QAbstractListModel +{ + Q_OBJECT + Q_PROPERTY(int count READ count NOTIFY countChanged) + Q_PROPERTY(bool dynamicRoles READ dynamicRoles WRITE setDynamicRoles) + +public: + QQmlListModel(QObject *parent=0); + ~QQmlListModel(); + + QModelIndex index(int row, int column, const QModelIndex &parent) const; + int rowCount(const QModelIndex &parent) const; + QVariant data(const QModelIndex &index, int role) const; + QHash roleNames() const; + + QVariant data(int index, int role) const; + int count() const; + + Q_INVOKABLE void clear(); + Q_INVOKABLE void remove(QQmlV8Function *args); + Q_INVOKABLE void append(QQmlV8Function *args); + Q_INVOKABLE void insert(QQmlV8Function *args); + Q_INVOKABLE QQmlV8Handle get(int index) const; + Q_INVOKABLE void set(int index, const QQmlV8Handle &); + Q_INVOKABLE void setProperty(int index, const QString& property, const QVariant& value); + Q_INVOKABLE void move(int from, int to, int count); + Q_INVOKABLE void sync(); + + QQmlListModelWorkerAgent *agent(); + + bool dynamicRoles() const { return m_dynamicRoles; } + void setDynamicRoles(bool enableDynamicRoles); + +Q_SIGNALS: + void countChanged(); + +private: + friend class QQmlListModelParser; + friend class QQmlListModelWorkerAgent; + friend class ModelObject; + friend class ModelNodeMetaObject; + friend class ListModel; + friend class ListElement; + friend class DynamicRoleModelNode; + friend class DynamicRoleModelNodeMetaObject; + + // Constructs a flat list model for a worker agent + QQmlListModel(QQmlListModel *orig, QQmlListModelWorkerAgent *agent); + QQmlListModel(const QQmlListModel *owner, ListModel *data, QV8Engine *eng, QObject *parent=0); + + QV8Engine *engine() const; + + inline bool canMove(int from, int to, int n) const { return !(from+n > count() || to+n > count() || from < 0 || to < 0 || n < 0); } + + QQmlListModelWorkerAgent *m_agent; + mutable QV8Engine *m_engine; + bool m_mainThread; + bool m_primary; + + bool m_dynamicRoles; + + ListLayout *m_layout; + ListModel *m_listModel; + + QVector m_modelObjects; + QVector m_roles; + int m_uid; + + struct ElementSync + { + ElementSync() : src(0), target(0) {} + + DynamicRoleModelNode *src; + DynamicRoleModelNode *target; + }; + + int getUid() const { return m_uid; } + + static void sync(QQmlListModel *src, QQmlListModel *target, QHash *targetModelHash); + static QQmlListModel *createWithOwner(QQmlListModel *newOwner); + + void emitItemsChanged(int index, int count, const QVector &roles); + void emitItemsRemoved(int index, int count); + void emitItemsInserted(int index, int count); + void emitItemsMoved(int from, int to, int n); +}; + +// ### FIXME +class QQmlListElement : public QObject +{ +Q_OBJECT +}; + +class QQmlListModelParser : public QQmlCustomParser +{ +public: + QQmlListModelParser() : QQmlCustomParser(QQmlCustomParser::AcceptsSignalHandlers) {} + QByteArray compile(const QList &); + void setCustomData(QObject *, const QByteArray &); + +private: + struct ListInstruction + { + enum { Push, Pop, Value, Set } type; + int dataIdx; + }; + struct ListModelData + { + int dataOffset; + int instrCount; + ListInstruction *instructions() const; + }; + bool compileProperty(const QQmlCustomParserProperty &prop, QList &instr, QByteArray &data); + + bool definesEmptyList(const QString &); + + QString listElementTypeName; + + struct DataStackElement + { + DataStackElement() : model(0), elementIndex(0) {} + + QString name; + ListModel *model; + int elementIndex; + }; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQmlListModel) +QML_DECLARE_TYPE(QQmlListElement) + +#endif // QQMLLISTMODEL_H diff --git a/src/qml/types/qqmllistmodel_p_p.h b/src/qml/types/qqmllistmodel_p_p.h new file mode 100644 index 0000000000..0190081320 --- /dev/null +++ b/src/qml/types/qqmllistmodel_p_p.h @@ -0,0 +1,378 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLLISTMODEL_P_P_H +#define QQMLLISTMODEL_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qqmllistmodel_p.h" +#include +#include +#include + +QT_BEGIN_NAMESPACE + + +class DynamicRoleModelNode; + +class DynamicRoleModelNodeMetaObject : public QQmlOpenMetaObject +{ +public: + DynamicRoleModelNodeMetaObject(DynamicRoleModelNode *object); + ~DynamicRoleModelNodeMetaObject(); + + bool m_enabled; + +protected: + void propertyWrite(int index); + void propertyWritten(int index); + +private: + DynamicRoleModelNode *m_owner; +}; + +class DynamicRoleModelNode : public QObject +{ + Q_OBJECT +public: + DynamicRoleModelNode(QQmlListModel *owner, int uid); + + static DynamicRoleModelNode *create(const QVariantMap &obj, QQmlListModel *owner); + + void updateValues(const QVariantMap &object, QVector &roles); + + QVariant getValue(const QString &name) + { + return m_meta->value(name.toUtf8()); + } + + bool setValue(const QByteArray &name, const QVariant &val) + { + return m_meta->setValue(name, val); + } + + void setNodeUpdatesEnabled(bool enable) + { + m_meta->m_enabled = enable; + } + + int getUid() const + { + return m_uid; + } + + static void sync(DynamicRoleModelNode *src, DynamicRoleModelNode *target, QHash *targetModelHash); + +private: + QQmlListModel *m_owner; + int m_uid; + DynamicRoleModelNodeMetaObject *m_meta; + + friend class DynamicRoleModelNodeMetaObject; +}; + +class ModelObject; + +class ModelNodeMetaObject : public QQmlOpenMetaObject +{ +public: + ModelNodeMetaObject(ModelObject *object); + ~ModelNodeMetaObject(); + + bool m_enabled; + +protected: + void propertyWritten(int index); + +private: + + ModelObject *m_obj; +}; + +class ModelObject : public QObject +{ + Q_OBJECT +public: + ModelObject(QQmlListModel *model, int elementIndex); + + void setValue(const QByteArray &name, const QVariant &val, bool force) + { + if (force) { + QVariant existingValue = m_meta->value(name); + if (existingValue.isValid()) { + (*m_meta)[name] = QVariant(); + } + } + m_meta->setValue(name, val); + } + + void setNodeUpdatesEnabled(bool enable) + { + m_meta->m_enabled = enable; + } + + void updateValues(); + void updateValues(const QVector &roles); + + QQmlListModel *m_model; + int m_elementIndex; + +private: + ModelNodeMetaObject *m_meta; +}; + +class ListLayout +{ +public: + ListLayout() : currentBlock(0), currentBlockOffset(0) {} + ListLayout(const ListLayout *other); + ~ListLayout(); + + class Role + { + public: + + Role() : type(Invalid), blockIndex(-1), blockOffset(-1), index(-1), subLayout(0) {} + explicit Role(const Role *other); + ~Role(); + + // This enum must be kept in sync with the roleTypeNames variable in qdeclarativelistmodel.cpp + enum DataType + { + Invalid = -1, + + String, + Number, + Bool, + List, + QObject, + VariantMap, + DateTime, + + MaxDataType + }; + + QString name; + DataType type; + int blockIndex; + int blockOffset; + int index; + ListLayout *subLayout; + }; + + const Role *getRoleOrCreate(const QString &key, const QVariant &data); + const Role &getRoleOrCreate(v8::Handle key, Role::DataType type); + const Role &getRoleOrCreate(const QString &key, Role::DataType type); + + const Role &getExistingRole(int index) { return *roles.at(index); } + const Role *getExistingRole(const QString &key); + const Role *getExistingRole(v8::Handle key); + + int roleCount() const { return roles.count(); } + + static void sync(ListLayout *src, ListLayout *target); + +private: + const Role &createRole(const QString &key, Role::DataType type); + + int currentBlock; + int currentBlockOffset; + QVector roles; + QStringHash roleHash; +}; + +class ListElement +{ +public: + + ListElement(); + ListElement(int existingUid); + ~ListElement(); + + static void sync(ListElement *src, ListLayout *srcLayout, ListElement *target, ListLayout *targetLayout, QHash *targetModelHash); + + enum + { + BLOCK_SIZE = 64 - sizeof(int) - sizeof(ListElement *) - sizeof(ModelObject *) + }; + +private: + + void destroy(ListLayout *layout); + + int setVariantProperty(const ListLayout::Role &role, const QVariant &d); + + int setJsProperty(const ListLayout::Role &role, v8::Handle d, QV8Engine *eng); + + int setStringProperty(const ListLayout::Role &role, const QString &s); + int setDoubleProperty(const ListLayout::Role &role, double n); + int setBoolProperty(const ListLayout::Role &role, bool b); + int setListProperty(const ListLayout::Role &role, ListModel *m); + int setQObjectProperty(const ListLayout::Role &role, QObject *o); + int setVariantMapProperty(const ListLayout::Role &role, v8::Handle o, QV8Engine *eng); + int setVariantMapProperty(const ListLayout::Role &role, QVariantMap *m); + int setDateTimeProperty(const ListLayout::Role &role, const QDateTime &dt); + + void setStringPropertyFast(const ListLayout::Role &role, const QString &s); + void setDoublePropertyFast(const ListLayout::Role &role, double n); + void setBoolPropertyFast(const ListLayout::Role &role, bool b); + void setQObjectPropertyFast(const ListLayout::Role &role, QObject *o); + void setListPropertyFast(const ListLayout::Role &role, ListModel *m); + void setVariantMapFast(const ListLayout::Role &role, v8::Handle o, QV8Engine *eng); + void setDateTimePropertyFast(const ListLayout::Role &role, const QDateTime &dt); + + void clearProperty(const ListLayout::Role &role); + + QVariant getProperty(const ListLayout::Role &role, const QQmlListModel *owner, QV8Engine *eng); + ListModel *getListProperty(const ListLayout::Role &role); + QString *getStringProperty(const ListLayout::Role &role); + QObject *getQObjectProperty(const ListLayout::Role &role); + QQmlGuard *getGuardProperty(const ListLayout::Role &role); + QVariantMap *getVariantMapProperty(const ListLayout::Role &role); + QDateTime *getDateTimeProperty(const ListLayout::Role &role); + + inline char *getPropertyMemory(const ListLayout::Role &role); + + int getUid() const { return uid; } + + char data[BLOCK_SIZE]; + ListElement *next; + + int uid; + ModelObject *m_objectCache; + + friend class ListModel; +}; + +class ListModel +{ +public: + + ListModel(ListLayout *layout, QQmlListModel *modelCache, int uid); + ~ListModel() {} + + void destroy(); + + int setOrCreateProperty(int elementIndex, const QString &key, const QVariant &data); + int setExistingProperty(int uid, const QString &key, v8::Handle data, QV8Engine *eng); + + QVariant getProperty(int elementIndex, int roleIndex, const QQmlListModel *owner, QV8Engine *eng); + ListModel *getListProperty(int elementIndex, const ListLayout::Role &role); + + int roleCount() const + { + return m_layout->roleCount(); + } + + const ListLayout::Role &getExistingRole(int index) + { + return m_layout->getExistingRole(index); + } + + const ListLayout::Role &getOrCreateListRole(const QString &name) + { + return m_layout->getRoleOrCreate(name, ListLayout::Role::List); + } + + int elementCount() const + { + return elements.count(); + } + + void set(int elementIndex, v8::Handle object, QVector *roles, QV8Engine *eng); + void set(int elementIndex, v8::Handle object, QV8Engine *eng); + + int append(v8::Handle object, QV8Engine *eng); + void insert(int elementIndex, v8::Handle object, QV8Engine *eng); + + void clear(); + void remove(int index, int count); + + int appendElement(); + void insertElement(int index); + + void move(int from, int to, int n); + + int getUid() const { return m_uid; } + + static void sync(ListModel *src, ListModel *target, QHash *srcModelHash); + + ModelObject *getOrCreateModelObject(QQmlListModel *model, int elementIndex); + +private: + QPODVector elements; + ListLayout *m_layout; + int m_uid; + + QQmlListModel *m_modelCache; + + struct ElementSync + { + ElementSync() : src(0), target(0) {} + + ListElement *src; + ListElement *target; + }; + + void newElement(int index); + + void updateCacheIndices(); + + friend class ListElement; + friend class QQmlListModelWorkerAgent; +}; + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(ListModel *); + +#endif // QQUICKLISTMODEL_P_P_H + diff --git a/src/qml/types/qqmllistmodelworkeragent.cpp b/src/qml/types/qqmllistmodelworkeragent.cpp new file mode 100644 index 0000000000..9554e6d1e5 --- /dev/null +++ b/src/qml/types/qqmllistmodelworkeragent.cpp @@ -0,0 +1,259 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmllistmodelworkeragent_p.h" +#include "qqmllistmodel_p_p.h" +#include +#include +#include + +#include +#include +#include + + +QT_BEGIN_NAMESPACE + + +void QQmlListModelWorkerAgent::Data::clearChange(int uid) +{ + for (int i=0 ; i < changes.count() ; ++i) { + if (changes[i].modelUid == uid) { + changes.removeAt(i); + --i; + } + } +} + +void QQmlListModelWorkerAgent::Data::insertChange(int uid, int index, int count) +{ + Change c = { uid, Change::Inserted, index, count, 0, QVector() }; + changes << c; +} + +void QQmlListModelWorkerAgent::Data::removeChange(int uid, int index, int count) +{ + Change c = { uid, Change::Removed, index, count, 0, QVector() }; + changes << c; +} + +void QQmlListModelWorkerAgent::Data::moveChange(int uid, int index, int count, int to) +{ + Change c = { uid, Change::Moved, index, count, to, QVector() }; + changes << c; +} + +void QQmlListModelWorkerAgent::Data::changedChange(int uid, int index, int count, const QVector &roles) +{ + Change c = { uid, Change::Changed, index, count, 0, roles }; + changes << c; +} + +QQmlListModelWorkerAgent::QQmlListModelWorkerAgent(QQmlListModel *model) +: m_ref(1), m_orig(model), m_copy(new QQmlListModel(model, this)) +{ +} + +QQmlListModelWorkerAgent::~QQmlListModelWorkerAgent() +{ + mutex.lock(); + syncDone.wakeAll(); + mutex.unlock(); +} + +void QQmlListModelWorkerAgent::setV8Engine(QV8Engine *eng) +{ + m_copy->m_engine = eng; +} + +void QQmlListModelWorkerAgent::addref() +{ + m_ref.ref(); +} + +void QQmlListModelWorkerAgent::release() +{ + bool del = !m_ref.deref(); + + if (del) + deleteLater(); +} + +void QQmlListModelWorkerAgent::modelDestroyed() +{ + m_orig = 0; +} + +int QQmlListModelWorkerAgent::count() const +{ + return m_copy->count(); +} + +void QQmlListModelWorkerAgent::clear() +{ + m_copy->clear(); +} + +void QQmlListModelWorkerAgent::remove(QQmlV8Function *args) +{ + m_copy->remove(args); +} + +void QQmlListModelWorkerAgent::append(QQmlV8Function *args) +{ + m_copy->append(args); +} + +void QQmlListModelWorkerAgent::insert(QQmlV8Function *args) +{ + m_copy->insert(args); +} + +QQmlV8Handle QQmlListModelWorkerAgent::get(int index) const +{ + return m_copy->get(index); +} + +void QQmlListModelWorkerAgent::set(int index, const QQmlV8Handle &value) +{ + m_copy->set(index, value); +} + +void QQmlListModelWorkerAgent::setProperty(int index, const QString& property, const QVariant& value) +{ + m_copy->setProperty(index, property, value); +} + +void QQmlListModelWorkerAgent::move(int from, int to, int count) +{ + m_copy->move(from, to, count); +} + +void QQmlListModelWorkerAgent::sync() +{ + Sync *s = new Sync; + s->data = data; + s->list = m_copy; + data.changes.clear(); + + mutex.lock(); + QCoreApplication::postEvent(this, s); + syncDone.wait(&mutex); + mutex.unlock(); +} + +bool QQmlListModelWorkerAgent::event(QEvent *e) +{ + if (e->type() == QEvent::User) { + bool cc = false; + QMutexLocker locker(&mutex); + if (m_orig) { + Sync *s = static_cast(e); + const QList &changes = s->data.changes; + + cc = m_orig->count() != s->list->count(); + + QHash targetModelDynamicHash; + QHash targetModelStaticHash; + + Q_ASSERT(m_orig->m_dynamicRoles == s->list->m_dynamicRoles); + if (m_orig->m_dynamicRoles) + QQmlListModel::sync(s->list, m_orig, &targetModelDynamicHash); + else + ListModel::sync(s->list->m_listModel, m_orig->m_listModel, &targetModelStaticHash); + + for (int ii = 0; ii < changes.count(); ++ii) { + const Change &change = changes.at(ii); + + QQmlListModel *model = 0; + if (m_orig->m_dynamicRoles) { + model = targetModelDynamicHash.value(change.modelUid); + } else { + ListModel *lm = targetModelStaticHash.value(change.modelUid); + if (lm) + model = lm->m_modelCache; + } + + if (model) { + switch (change.type) { + case Change::Inserted: + model->beginInsertRows( + QModelIndex(), change.index, change.index + change.count - 1); + model->endInsertRows(); + break; + case Change::Removed: + model->beginRemoveRows( + QModelIndex(), change.index, change.index + change.count - 1); + model->endRemoveRows(); + break; + case Change::Moved: + model->beginMoveRows( + QModelIndex(), + change.index, + change.index + change.count - 1, + QModelIndex(), + change.to > change.index ? change.to + change.count : change.to); + model->endMoveRows(); + break; + case Change::Changed: + emit model->dataChanged( + model->createIndex(change.index, 0), + model->createIndex(change.index + change.count - 1, 0), + change.roles); + break; + } + } + } + } + + syncDone.wakeAll(); + locker.unlock(); + + if (cc) + emit m_orig->countChanged(); + return true; + } + + return QObject::event(e); +} + +QT_END_NAMESPACE + diff --git a/src/qml/types/qqmllistmodelworkeragent_p.h b/src/qml/types/qqmllistmodelworkeragent_p.h new file mode 100644 index 0000000000..614017069c --- /dev/null +++ b/src/qml/types/qqmllistmodelworkeragent_p.h @@ -0,0 +1,156 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKLISTMODELWORKERAGENT_P_H +#define QQUICKLISTMODELWORKERAGENT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include + +#include +#include + +#include + +QT_BEGIN_NAMESPACE + + +class QQmlListModel; + +class QQmlListModelWorkerAgent : public QObject +{ + Q_OBJECT + Q_PROPERTY(int count READ count) + +public: + QQmlListModelWorkerAgent(QQmlListModel *); + ~QQmlListModelWorkerAgent(); + void setV8Engine(QV8Engine *eng); + + void addref(); + void release(); + + int count() const; + + Q_INVOKABLE void clear(); + Q_INVOKABLE void remove(QQmlV8Function *args); + Q_INVOKABLE void append(QQmlV8Function *args); + Q_INVOKABLE void insert(QQmlV8Function *args); + Q_INVOKABLE QQmlV8Handle get(int index) const; + Q_INVOKABLE void set(int index, const QQmlV8Handle &); + Q_INVOKABLE void setProperty(int index, const QString& property, const QVariant& value); + Q_INVOKABLE void move(int from, int to, int count); + Q_INVOKABLE void sync(); + + struct VariantRef + { + VariantRef() : a(0) {} + VariantRef(const VariantRef &r) : a(r.a) { if (a) a->addref(); } + VariantRef(QQmlListModelWorkerAgent *_a) : a(_a) { if (a) a->addref(); } + ~VariantRef() { if (a) a->release(); } + + VariantRef &operator=(const VariantRef &o) { + if (o.a) o.a->addref(); + if (a) a->release(); a = o.a; + return *this; + } + + QQmlListModelWorkerAgent *a; + }; + void modelDestroyed(); +protected: + virtual bool event(QEvent *); + +private: + friend class QQuickWorkerScriptEnginePrivate; + friend class QQmlListModel; + + struct Change + { + int modelUid; + enum { Inserted, Removed, Moved, Changed } type; + int index; // Inserted/Removed/Moved/Changed + int count; // Inserted/Removed/Moved/Changed + int to; // Moved + QVector roles; + }; + + struct Data + { + QList changes; + + void clearChange(int uid); + void insertChange(int uid, int index, int count); + void removeChange(int uid, int index, int count); + void moveChange(int uid, int index, int count, int to); + void changedChange(int uid, int index, int count, const QVector &roles); + }; + Data data; + + struct Sync : public QEvent { + Sync() : QEvent(QEvent::User) {} + Data data; + QQmlListModel *list; + }; + + QAtomicInt m_ref; + QQmlListModel *m_orig; + QQmlListModel *m_copy; + QMutex mutex; + QWaitCondition syncDone; +}; + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QQmlListModelWorkerAgent::VariantRef) + +#endif // QQUICKLISTMODELWORKERAGENT_P_H + diff --git a/src/qml/types/qqmlmodelsmodule.cpp b/src/qml/types/qqmlmodelsmodule.cpp new file mode 100644 index 0000000000..4f6b0a5580 --- /dev/null +++ b/src/qml/types/qqmlmodelsmodule.cpp @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Research In Motion. +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlmodelsmodule_p.h" +#include +#include +#include + +QT_BEGIN_NAMESPACE + +void QQmlModelsModule::defineModule() +{ + const char uri[] = "QtQml.Models"; + + qmlRegisterType(uri, 2, 1, "ListElement"); + qmlRegisterCustomType(uri, 2, 1, "ListModel", new QQmlListModelParser); + qmlRegisterType(uri, 2, 1, "DelegateModel"); + qmlRegisterType(uri, 2, 1, "DelegateModelGroup"); + qmlRegisterType(uri, 2, 1, "ObjectModel"); +} + +QT_END_NAMESPACE diff --git a/src/qml/types/qqmlmodelsmodule_p.h b/src/qml/types/qqmlmodelsmodule_p.h new file mode 100644 index 0000000000..6e72dadf8b --- /dev/null +++ b/src/qml/types/qqmlmodelsmodule_p.h @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Research In Motion. +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLMODELSMODULE_H +#define QQMLMODELSMODULE_H + +#include + +QT_BEGIN_NAMESPACE + +class Q_QML_PRIVATE_EXPORT QQmlModelsModule +{ +public: + static void defineModule(); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/types/qqmlobjectmodel.cpp b/src/qml/types/qqmlobjectmodel.cpp new file mode 100644 index 0000000000..7f7bf92fa3 --- /dev/null +++ b/src/qml/types/qqmlobjectmodel.cpp @@ -0,0 +1,269 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlobjectmodel_p.h" + +#include +#include +#include + +#include +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +QHash QQmlObjectModelAttached::attachedProperties; + + +class QQmlObjectModelPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QQmlObjectModel) +public: + class Item { + public: + Item(QObject *i) : item(i), ref(0) {} + + void addRef() { ++ref; } + bool deref() { return --ref == 0; } + + QObject *item; + int ref; + }; + + QQmlObjectModelPrivate() : QObjectPrivate() {} + + static void children_append(QQmlListProperty *prop, QObject *item) { + static_cast(prop->data)->children.append(Item(item)); + static_cast(prop->data)->itemAppended(); + static_cast(prop->data)->emitChildrenChanged(); + } + + static int children_count(QQmlListProperty *prop) { + return static_cast(prop->data)->children.count(); + } + + static QObject *children_at(QQmlListProperty *prop, int index) { + return static_cast(prop->data)->children.at(index).item; + } + + static void children_clear(QQmlListProperty *prop) { + static_cast(prop->data)->itemCleared(static_cast(prop->data)->children); + static_cast(prop->data)->children.clear(); + static_cast(prop->data)->emitChildrenChanged(); + } + + void itemAppended() { + Q_Q(QQmlObjectModel); + QQmlObjectModelAttached *attached = QQmlObjectModelAttached::properties(children.last().item); + attached->setIndex(children.count()-1); + QQmlChangeSet changeSet; + changeSet.insert(children.count() - 1, 1); + emit q->modelUpdated(changeSet, false); + emit q->countChanged(); + } + + void itemCleared(const QList &children) { + Q_Q(QQmlObjectModel); + foreach (const Item &child, children) + emit q->destroyingItem(child.item); + emit q->countChanged(); + } + + void emitChildrenChanged() { + Q_Q(QQmlObjectModel); + emit q->childrenChanged(); + } + + int indexOf(QObject *item) const { + for (int i = 0; i < children.count(); ++i) + if (children.at(i).item == item) + return i; + return -1; + } + + + QList children; +}; + + +/*! + \qmltype ObjectModel + \instantiates QQmlObjectModel + \inqmlmodule QtQml.Models 2 + \ingroup qtquick-models + \brief Defines a set of items to be used as a model + + A ObjectModel contains the visual items to be used in a view. + When a ObjectModel is used in a view, the view does not require + a delegate since the ObjectModel already contains the visual + delegate (items). + + An item can determine its index within the + model via the \l{ObjectModel::index}{index} attached property. + + The example below places three colored rectangles in a ListView. + \code + import QtQuick 2.0 + + Rectangle { + ObjectModel { + id: itemModel + Rectangle { height: 30; width: 80; color: "red" } + Rectangle { height: 30; width: 80; color: "green" } + Rectangle { height: 30; width: 80; color: "blue" } + } + + ListView { + anchors.fill: parent + model: itemModel + } + } + \endcode + + \image visualitemmodel.png + + \sa {quick/views/objectmodel}{ObjectModel example} +*/ +/*! + \qmltype VisualItemModel + \instantiates QQmlObjectModel + \inqmlmodule QtQuick 2 + \brief Defines a set of objects to be used as a model + + The VisualItemModel type encapsulates contains the objects to be used + as a model. + + This element is now primarily available as ObjectModel in the QtQml.Models module. + VisualItemModel continues to be provided, with the same implementation, in QtQuick for + compatibility reasons. + + For full details about the type, see the \l ObjectModel documentation. + + \sa {QtQml.Models2::ObjectModel} +*/ + +QQmlObjectModel::QQmlObjectModel(QObject *parent) + : QQmlInstanceModel(*(new QQmlObjectModelPrivate), parent) +{ +} + +/*! + \qmlattachedproperty int QtQml.Models2::ObjectModel::index + This attached property holds the index of this delegate's item within the model. + + It is attached to each instance of the delegate. +*/ + +QQmlListProperty QQmlObjectModel::children() +{ + Q_D(QQmlObjectModel); + return QQmlListProperty(this, + d, + d->children_append, + d->children_count, + d->children_at, + d->children_clear); +} + +/*! + \qmlproperty int QtQml.Models2::ObjectModel::count + + The number of items in the model. This property is readonly. +*/ +int QQmlObjectModel::count() const +{ + Q_D(const QQmlObjectModel); + return d->children.count(); +} + +bool QQmlObjectModel::isValid() const +{ + return true; +} + +QObject *QQmlObjectModel::object(int index, bool) +{ + Q_D(QQmlObjectModel); + QQmlObjectModelPrivate::Item &item = d->children[index]; + item.addRef(); + if (item.ref == 1) { + emit initItem(index, item.item); + emit createdItem(index, item.item); + } + return item.item; +} + +QQmlInstanceModel::ReleaseFlags QQmlObjectModel::release(QObject *item) +{ + Q_D(QQmlObjectModel); + int idx = d->indexOf(item); + if (idx >= 0) { + if (!d->children[idx].deref()) + return QQmlInstanceModel::Referenced; + } + return 0; +} + +QString QQmlObjectModel::stringValue(int index, const QString &name) +{ + Q_D(QQmlObjectModel); + if (index < 0 || index >= d->children.count()) + return QString(); + return QQmlEngine::contextForObject(d->children.at(index).item)->contextProperty(name).toString(); +} + +int QQmlObjectModel::indexOf(QObject *item, QObject *) const +{ + Q_D(const QQmlObjectModel); + return d->indexOf(item); +} + +QQmlObjectModelAttached *QQmlObjectModel::qmlAttachedProperties(QObject *obj) +{ + return QQmlObjectModelAttached::properties(obj); +} + +QT_END_NAMESPACE + diff --git a/src/qml/types/qqmlobjectmodel_p.h b/src/qml/types/qqmlobjectmodel_p.h new file mode 100644 index 0000000000..59a4a551a7 --- /dev/null +++ b/src/qml/types/qqmlobjectmodel_p.h @@ -0,0 +1,170 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLINSTANCEMODEL_P_H +#define QQMLINSTANCEMODEL_P_H + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QObject; +class QQmlChangeSet; + +class Q_QML_PRIVATE_EXPORT QQmlInstanceModel : public QObject +{ + Q_OBJECT + + Q_PROPERTY(int count READ count NOTIFY countChanged) + +public: + virtual ~QQmlInstanceModel() {} + + enum ReleaseFlag { Referenced = 0x01, Destroyed = 0x02 }; + Q_DECLARE_FLAGS(ReleaseFlags, ReleaseFlag) + + virtual int count() const = 0; + virtual bool isValid() const = 0; + virtual QObject *object(int index, bool asynchronous=false) = 0; + virtual ReleaseFlags release(QObject *object) = 0; + virtual void cancel(int) {} + virtual QString stringValue(int, const QString &) = 0; + virtual void setWatchedRoles(QList roles) = 0; + + virtual int indexOf(QObject *object, QObject *objectContext) const = 0; + +Q_SIGNALS: + void countChanged(); + void modelUpdated(const QQmlChangeSet &changeSet, bool reset); + void createdItem(int index, QObject *object); + void initItem(int index, QObject *object); + void destroyingItem(QObject *object); + +protected: + QQmlInstanceModel(QObjectPrivate &dd, QObject *parent = 0) + : QObject(dd, parent) {} + +private: + Q_DISABLE_COPY(QQmlInstanceModel) +}; + +class QQmlObjectModelAttached; +class QQmlObjectModelPrivate; +class Q_QML_PRIVATE_EXPORT QQmlObjectModel : public QQmlInstanceModel +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QQmlObjectModel) + + Q_PROPERTY(QQmlListProperty children READ children NOTIFY childrenChanged DESIGNABLE false) + Q_CLASSINFO("DefaultProperty", "children") + +public: + QQmlObjectModel(QObject *parent=0); + virtual ~QQmlObjectModel() {} + + virtual int count() const; + virtual bool isValid() const; + virtual QObject *object(int index, bool asynchronous=false); + virtual ReleaseFlags release(QObject *object); + virtual QString stringValue(int index, const QString &role); + virtual void setWatchedRoles(QList) {} + + virtual int indexOf(QObject *object, QObject *objectContext) const; + + QQmlListProperty children(); + + static QQmlObjectModelAttached *qmlAttachedProperties(QObject *obj); + +Q_SIGNALS: + void childrenChanged(); + +private: + Q_DISABLE_COPY(QQmlObjectModel) +}; + +class QQmlObjectModelAttached : public QObject +{ + Q_OBJECT + +public: + QQmlObjectModelAttached(QObject *parent) + : QObject(parent), m_index(0) {} + ~QQmlObjectModelAttached() { + attachedProperties.remove(parent()); + } + + Q_PROPERTY(int index READ index NOTIFY indexChanged) + int index() const { return m_index; } + void setIndex(int idx) { + if (m_index != idx) { + m_index = idx; + emit indexChanged(); + } + } + + static QQmlObjectModelAttached *properties(QObject *obj) { + QQmlObjectModelAttached *rv = attachedProperties.value(obj); + if (!rv) { + rv = new QQmlObjectModelAttached(obj); + attachedProperties.insert(obj, rv); + } + return rv; + } + +Q_SIGNALS: + void indexChanged(); + +public: + int m_index; + + static QHash attachedProperties; +}; + + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQmlInstanceModel) +QML_DECLARE_TYPE(QQmlObjectModel) +QML_DECLARE_TYPEINFO(QQmlObjectModel, QML_HAS_ATTACHED_PROPERTIES) + +#endif // QQMLINSTANCEMODEL_P_H diff --git a/src/qml/types/qqmltimer.cpp b/src/qml/types/qqmltimer.cpp new file mode 100644 index 0000000000..a1cb8532f7 --- /dev/null +++ b/src/qml/types/qqmltimer.cpp @@ -0,0 +1,328 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmltimer_p.h" + +#include +#include "private/qpauseanimationjob_p.h" +#include + +#include + +QT_BEGIN_NAMESPACE + + + +class QQmlTimerPrivate : public QObjectPrivate, public QAnimationJobChangeListener +{ + Q_DECLARE_PUBLIC(QQmlTimer) +public: + QQmlTimerPrivate() + : interval(1000), running(false), repeating(false), triggeredOnStart(false) + , classBegun(false), componentComplete(false), firstTick(true) {} + + virtual void animationFinished(QAbstractAnimationJob *); + virtual void animationCurrentLoopChanged(QAbstractAnimationJob *) { Q_Q(QQmlTimer); q->ticked(); } + + int interval; + QPauseAnimationJob pause; + bool running : 1; + bool repeating : 1; + bool triggeredOnStart : 1; + bool classBegun : 1; + bool componentComplete : 1; + bool firstTick : 1; +}; + +/*! + \qmltype Timer + \instantiates QQmlTimer + \inqmlmodule QtQml 2 + \ingroup qtquick-interceptors + \brief Triggers a handler at a specified interval + + A Timer can be used to trigger an action either once, or repeatedly + at a given interval. + + Here is a Timer that shows the current date and time, and updates + the text every 500 milliseconds. It uses the JavaScript \c Date + object to access the current time. + + \qml + import QtQuick 2.0 + + Item { + Timer { + interval: 500; running: true; repeat: true + onTriggered: time.text = Date().toString() + } + + Text { id: time } + } + \endqml + + The Timer type is synchronized with the animation timer. Since the animation + timer is usually set to 60fps, the resolution of Timer will be + at best 16ms. + + If the Timer is running and one of its properties is changed, the + elapsed time will be reset. For example, if a Timer with interval of + 1000ms has its \e repeat property changed 500ms after starting, the + elapsed time will be reset to 0, and the Timer will be triggered + 1000ms later. + + \sa {declarative/toys/clocks}{Clocks example} +*/ + +QQmlTimer::QQmlTimer(QObject *parent) + : QObject(*(new QQmlTimerPrivate), parent) +{ + Q_D(QQmlTimer); + d->pause.addAnimationChangeListener(d, QAbstractAnimationJob::Completion | QAbstractAnimationJob::CurrentLoop); + d->pause.setLoopCount(1); + d->pause.setDuration(d->interval); +} + +/*! + \qmlproperty int QtQml2::Timer::interval + + Sets the \a interval between triggers, in milliseconds. + + The default interval is 1000 milliseconds. +*/ +void QQmlTimer::setInterval(int interval) +{ + Q_D(QQmlTimer); + if (interval != d->interval) { + d->interval = interval; + update(); + emit intervalChanged(); + } +} + +int QQmlTimer::interval() const +{ + Q_D(const QQmlTimer); + return d->interval; +} + +/*! + \qmlproperty bool QtQml2::Timer::running + + If set to true, starts the timer; otherwise stops the timer. + For a non-repeating timer, \a running is set to false after the + timer has been triggered. + + \a running defaults to false. + + \sa repeat +*/ +bool QQmlTimer::isRunning() const +{ + Q_D(const QQmlTimer); + return d->running; +} + +void QQmlTimer::setRunning(bool running) +{ + Q_D(QQmlTimer); + if (d->running != running) { + d->running = running; + d->firstTick = true; + emit runningChanged(); + update(); + } +} + +/*! + \qmlproperty bool QtQml2::Timer::repeat + + If \a repeat is true the timer is triggered repeatedly at the + specified interval; otherwise, the timer will trigger once at the + specified interval and then stop (i.e. running will be set to false). + + \a repeat defaults to false. + + \sa running +*/ +bool QQmlTimer::isRepeating() const +{ + Q_D(const QQmlTimer); + return d->repeating; +} + +void QQmlTimer::setRepeating(bool repeating) +{ + Q_D(QQmlTimer); + if (repeating != d->repeating) { + d->repeating = repeating; + update(); + emit repeatChanged(); + } +} + +/*! + \qmlproperty bool QtQml2::Timer::triggeredOnStart + + When a timer is started, the first trigger is usually after the specified + interval has elapsed. It is sometimes desirable to trigger immediately + when the timer is started; for example, to establish an initial + state. + + If \a triggeredOnStart is true, the timer is triggered immediately + when started, and subsequently at the specified interval. Note that if + \e repeat is set to false, the timer is triggered twice; once on start, + and again at the interval. + + \a triggeredOnStart defaults to false. + + \sa running +*/ +bool QQmlTimer::triggeredOnStart() const +{ + Q_D(const QQmlTimer); + return d->triggeredOnStart; +} + +void QQmlTimer::setTriggeredOnStart(bool triggeredOnStart) +{ + Q_D(QQmlTimer); + if (d->triggeredOnStart != triggeredOnStart) { + d->triggeredOnStart = triggeredOnStart; + update(); + emit triggeredOnStartChanged(); + } +} + +/*! + \qmlmethod QtQml2::Timer::start() + \brief Starts the timer + + If the timer is already running, calling this method has no effect. The + \c running property will be true following a call to \c start(). +*/ +void QQmlTimer::start() +{ + setRunning(true); +} + +/*! + \qmlmethod QtQml2::Timer::stop() + \brief Stops the timer + + If the timer is not running, calling this method has no effect. The + \c running property will be false following a call to \c stop(). +*/ +void QQmlTimer::stop() +{ + setRunning(false); +} + +/*! + \qmlmethod QtQml2::Timer::restart() + \brief Restarts the timer + + If the Timer is not running it will be started, otherwise it will be + stopped, reset to initial state and started. The \c running property + will be true following a call to \c restart(). +*/ +void QQmlTimer::restart() +{ + setRunning(false); + setRunning(true); +} + +void QQmlTimer::update() +{ + Q_D(QQmlTimer); + if (d->classBegun && !d->componentComplete) + return; + d->pause.stop(); + if (d->running) { + d->pause.setCurrentTime(0); + d->pause.setLoopCount(d->repeating ? -1 : 1); + d->pause.setDuration(d->interval); + d->pause.start(); + if (d->triggeredOnStart && d->firstTick) { + QCoreApplication::removePostedEvents(this, QEvent::MetaCall); + QMetaObject::invokeMethod(this, "ticked", Qt::QueuedConnection); + } + } +} + +void QQmlTimer::classBegin() +{ + Q_D(QQmlTimer); + d->classBegun = true; +} + +void QQmlTimer::componentComplete() +{ + Q_D(QQmlTimer); + d->componentComplete = true; + update(); +} + +/*! + \qmlsignal QtQml2::Timer::onTriggered() + + This handler is called when the Timer is triggered. +*/ +void QQmlTimer::ticked() +{ + Q_D(QQmlTimer); + if (d->running && (d->pause.currentTime() > 0 || (d->triggeredOnStart && d->firstTick))) + emit triggered(); + d->firstTick = false; +} + +void QQmlTimerPrivate::animationFinished(QAbstractAnimationJob *) +{ + Q_Q(QQmlTimer); + if (repeating || !running) + return; + running = false; + firstTick = false; + emit q->triggered(); + emit q->runningChanged(); +} + +QT_END_NAMESPACE diff --git a/src/qml/types/qqmltimer_p.h b/src/qml/types/qqmltimer_p.h new file mode 100644 index 0000000000..c625522851 --- /dev/null +++ b/src/qml/types/qqmltimer_p.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLTIMER_H +#define QQMLTIMER_H + +#include + +#include + +#include + +QT_BEGIN_NAMESPACE + +class QQmlTimerPrivate; +class Q_QML_PRIVATE_EXPORT QQmlTimer : public QObject, public QQmlParserStatus +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QQmlTimer) + Q_INTERFACES(QQmlParserStatus) + Q_PROPERTY(int interval READ interval WRITE setInterval NOTIFY intervalChanged) + Q_PROPERTY(bool running READ isRunning WRITE setRunning NOTIFY runningChanged) + Q_PROPERTY(bool repeat READ isRepeating WRITE setRepeating NOTIFY repeatChanged) + Q_PROPERTY(bool triggeredOnStart READ triggeredOnStart WRITE setTriggeredOnStart NOTIFY triggeredOnStartChanged) + Q_PROPERTY(QObject *parent READ parent CONSTANT) + +public: + QQmlTimer(QObject *parent=0); + + void setInterval(int interval); + int interval() const; + + bool isRunning() const; + void setRunning(bool running); + + bool isRepeating() const; + void setRepeating(bool repeating); + + bool triggeredOnStart() const; + void setTriggeredOnStart(bool triggeredOnStart); + +protected: + void classBegin(); + void componentComplete(); + +public Q_SLOTS: + void start(); + void stop(); + void restart(); + +Q_SIGNALS: + void triggered(); + void runningChanged(); + void intervalChanged(); + void repeatChanged(); + void triggeredOnStartChanged(); + +private: + void update(); + +private Q_SLOTS: + void ticked(); +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQmlTimer) + +#endif diff --git a/src/qml/types/qquickpackage.cpp b/src/qml/types/qquickpackage.cpp new file mode 100644 index 0000000000..e885524b27 --- /dev/null +++ b/src/qml/types/qquickpackage.cpp @@ -0,0 +1,198 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickpackage_p.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +/*! + \qmltype Package + \instantiates QQuickPackage + \inqmlmodule QtQuick 2 + \ingroup qtquick-views + \brief Specifies a collection of named items + + The Package class is used in conjunction with + VisualDataModel to enable delegates with a shared context + to be provided to multiple views. + + Any item within a Package may be assigned a name via the + \l{Package::name}{Package.name} attached property. + + The example below creates a Package containing two named items; + \e list and \e grid. The third item in the package (the \l Rectangle) is parented to whichever + delegate it should appear in. This allows an item to move + between views. + + \snippet quick/views/package/Delegate.qml 0 + + These named items are used as the delegates by the two views who + reference the special \l{VisualDataModel::parts} property to select + a model which provides the chosen delegate. + + \snippet quick/views/package/view.qml 0 + + \sa {quick/views/package}{Package example}, {quick/demos/photoviewer}{Photo Viewer example}, QtQml +*/ + +/*! + \qmlattachedproperty string QtQuick2::Package::name + This attached property holds the name of an item within a Package. +*/ + + +class QQuickPackagePrivate : public QObjectPrivate +{ +public: + QQuickPackagePrivate() {} + + struct DataGuard : public QQmlGuard + { + DataGuard(QObject *obj, QList *l) : list(l) { (QQmlGuard&)*this = obj; } + QList *list; + void objectDestroyed(QObject *) { + // we assume priv will always be destroyed after objectDestroyed calls + list->removeOne(*this); + } + }; + + QList dataList; + static void data_append(QQmlListProperty *prop, QObject *o) { + QList *list = static_cast *>(prop->data); + list->append(DataGuard(o, list)); + } + static void data_clear(QQmlListProperty *prop) { + QList *list = static_cast *>(prop->data); + list->clear(); + } + static QObject *data_at(QQmlListProperty *prop, int index) { + QList *list = static_cast *>(prop->data); + return list->at(index); + } + static int data_count(QQmlListProperty *prop) { + QList *list = static_cast *>(prop->data); + return list->count(); + } +}; + +QHash QQuickPackageAttached::attached; + +QQuickPackageAttached::QQuickPackageAttached(QObject *parent) +: QObject(parent) +{ + attached.insert(parent, this); +} + +QQuickPackageAttached::~QQuickPackageAttached() +{ + attached.remove(parent()); +} + +QString QQuickPackageAttached::name() const +{ + return _name; +} + +void QQuickPackageAttached::setName(const QString &n) +{ + _name = n; +} + +QQuickPackage::QQuickPackage(QObject *parent) + : QObject(*(new QQuickPackagePrivate), parent) +{ +} + +QQuickPackage::~QQuickPackage() +{ +} + +QQmlListProperty QQuickPackage::data() +{ + Q_D(QQuickPackage); + return QQmlListProperty(this, &d->dataList, QQuickPackagePrivate::data_append, + QQuickPackagePrivate::data_count, + QQuickPackagePrivate::data_at, + QQuickPackagePrivate::data_clear); +} + +bool QQuickPackage::hasPart(const QString &name) +{ + Q_D(QQuickPackage); + for (int ii = 0; ii < d->dataList.count(); ++ii) { + QObject *obj = d->dataList.at(ii); + QQuickPackageAttached *a = QQuickPackageAttached::attached.value(obj); + if (a && a->name() == name) + return true; + } + return false; +} + +QObject *QQuickPackage::part(const QString &name) +{ + Q_D(QQuickPackage); + if (name.isEmpty() && !d->dataList.isEmpty()) + return d->dataList.at(0); + + for (int ii = 0; ii < d->dataList.count(); ++ii) { + QObject *obj = d->dataList.at(ii); + QQuickPackageAttached *a = QQuickPackageAttached::attached.value(obj); + if (a && a->name() == name) + return obj; + } + + if (name == QLatin1String("default") && !d->dataList.isEmpty()) + return d->dataList.at(0); + + return 0; +} + +QQuickPackageAttached *QQuickPackage::qmlAttachedProperties(QObject *o) +{ + return new QQuickPackageAttached(o); +} + + + +QT_END_NAMESPACE diff --git a/src/qml/types/qquickpackage_p.h b/src/qml/types/qquickpackage_p.h new file mode 100644 index 0000000000..9427c886a8 --- /dev/null +++ b/src/qml/types/qquickpackage_p.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKPACKAGE_H +#define QQUICKPACKAGE_H + +#include + +QT_BEGIN_NAMESPACE + +class QQuickPackagePrivate; +class QQuickPackageAttached; +class Q_AUTOTEST_EXPORT QQuickPackage : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QQuickPackage) + + Q_CLASSINFO("DefaultProperty", "data") + Q_PROPERTY(QQmlListProperty data READ data) + +public: + QQuickPackage(QObject *parent=0); + virtual ~QQuickPackage(); + + QQmlListProperty data(); + + QObject *part(const QString & = QString()); + bool hasPart(const QString &); + + static QQuickPackageAttached *qmlAttachedProperties(QObject *); +}; + +class QQuickPackageAttached : public QObject +{ +Q_OBJECT +Q_PROPERTY(QString name READ name WRITE setName) +public: + QQuickPackageAttached(QObject *parent); + virtual ~QQuickPackageAttached(); + + QString name() const; + void setName(const QString &n); + + static QHash attached; +private: + QString _name; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickPackage) +QML_DECLARE_TYPEINFO(QQuickPackage, QML_HAS_ATTACHED_PROPERTIES) + +#endif // QQUICKPACKAGE_H diff --git a/src/qml/types/qquickworkerscript.cpp b/src/qml/types/qquickworkerscript.cpp new file mode 100644 index 0000000000..ad09e1ec0c --- /dev/null +++ b/src/qml/types/qquickworkerscript.cpp @@ -0,0 +1,740 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickworkerscript_p.h" +#include "qqmllistmodel_p.h" +#include "qqmllistmodelworkeragent_p.h" +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "qqmlnetworkaccessmanagerfactory.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +class WorkerDataEvent : public QEvent +{ +public: + enum Type { WorkerData = QEvent::User }; + + WorkerDataEvent(int workerId, const QByteArray &data); + virtual ~WorkerDataEvent(); + + int workerId() const; + QByteArray data() const; + +private: + int m_id; + QByteArray m_data; +}; + +class WorkerLoadEvent : public QEvent +{ +public: + enum Type { WorkerLoad = WorkerDataEvent::WorkerData + 1 }; + + WorkerLoadEvent(int workerId, const QUrl &url); + + int workerId() const; + QUrl url() const; + +private: + int m_id; + QUrl m_url; +}; + +class WorkerRemoveEvent : public QEvent +{ +public: + enum Type { WorkerRemove = WorkerLoadEvent::WorkerLoad + 1 }; + + WorkerRemoveEvent(int workerId); + + int workerId() const; + +private: + int m_id; +}; + +class WorkerErrorEvent : public QEvent +{ +public: + enum Type { WorkerError = WorkerRemoveEvent::WorkerRemove + 1 }; + + WorkerErrorEvent(const QQmlError &error); + + QQmlError error() const; + +private: + QQmlError m_error; +}; + +class QQuickWorkerScriptEnginePrivate : public QObject +{ + Q_OBJECT +public: + enum WorkerEventTypes { + WorkerDestroyEvent = QEvent::User + 100 + }; + + QQuickWorkerScriptEnginePrivate(QQmlEngine *eng); + + class WorkerEngine : public QV8Engine + { + public: + WorkerEngine(QQuickWorkerScriptEnginePrivate *parent); + ~WorkerEngine(); + + void init(); + virtual QNetworkAccessManager *networkAccessManager(); + + QQuickWorkerScriptEnginePrivate *p; + + v8::Local sendFunction(int id); + void callOnMessage(v8::Handle object, v8::Handle arg); + private: + v8::Persistent onmessage; + v8::Persistent createsend; + QNetworkAccessManager *accessManager; + }; + + WorkerEngine *workerEngine; + static QQuickWorkerScriptEnginePrivate *get(QV8Engine *e) { + return static_cast(e)->p; + } + + QQmlEngine *qmlengine; + + QMutex m_lock; + QWaitCondition m_wait; + + struct WorkerScript { + WorkerScript(); + ~WorkerScript(); + + int id; + QUrl source; + bool initialized; + QQuickWorkerScript *owner; + v8::Persistent object; + }; + + QHash workers; + v8::Handle getWorker(WorkerScript *); + + int m_nextId; + + static v8::Handle sendMessage(const v8::Arguments &args); + +signals: + void stopThread(); + +protected: + virtual bool event(QEvent *); + +private: + void processMessage(int, const QByteArray &); + void processLoad(int, const QUrl &); + void reportScriptException(WorkerScript *, const QQmlError &error); +}; + +QQuickWorkerScriptEnginePrivate::WorkerEngine::WorkerEngine(QQuickWorkerScriptEnginePrivate *parent) +: QV8Engine(0), p(parent), accessManager(0) +{ +} + +QQuickWorkerScriptEnginePrivate::WorkerEngine::~WorkerEngine() +{ + qPersistentDispose(createsend); + qPersistentDispose(onmessage); + delete accessManager; +} + +void QQuickWorkerScriptEnginePrivate::WorkerEngine::init() +{ + initQmlGlobalObject(); +#define CALL_ONMESSAGE_SCRIPT \ + "(function(object, message) { "\ + "var isfunction = false; "\ + "try { "\ + "isfunction = object.WorkerScript.onMessage instanceof Function; "\ + "} catch (e) {}" \ + "if (isfunction) "\ + "object.WorkerScript.onMessage(message); "\ + "})" + +#define SEND_MESSAGE_CREATE_SCRIPT \ + "(function(method, engine) { "\ + "return (function(id) { "\ + "return (function(message) { "\ + "if (arguments.length) method(engine, id, message); "\ + "}); "\ + "}); "\ + "})" + + v8::HandleScope handle_scope; + v8::Context::Scope scope(context()); + + { + v8::Local onmessagescript = v8::Script::New(v8::String::New(CALL_ONMESSAGE_SCRIPT)); + onmessage = qPersistentNew(v8::Handle::Cast(onmessagescript->Run())); + } + { + v8::Local createsendscript = v8::Script::New(v8::String::New(SEND_MESSAGE_CREATE_SCRIPT)); + v8::Local createsendconstructor = v8::Local::Cast(createsendscript->Run()); + + v8::Handle args[] = { + V8FUNCTION(QQuickWorkerScriptEnginePrivate::sendMessage, this) + }; + v8::Local createsendvalue = createsendconstructor->Call(global(), 1, args); + + createsend = qPersistentNew(v8::Handle::Cast(createsendvalue)); + } +} + +// Requires handle and context scope +v8::Local QQuickWorkerScriptEnginePrivate::WorkerEngine::sendFunction(int id) +{ + v8::Handle args[] = { v8::Integer::New(id) }; + return v8::Local::Cast(createsend->Call(global(), 1, args)); +} + +// Requires handle and context scope +void QQuickWorkerScriptEnginePrivate::WorkerEngine::callOnMessage(v8::Handle object, + v8::Handle arg) +{ + v8::Handle args[] = { object, arg }; + onmessage->Call(global(), 2, args); +} + +QNetworkAccessManager *QQuickWorkerScriptEnginePrivate::WorkerEngine::networkAccessManager() +{ + if (!accessManager) { + if (p->qmlengine && p->qmlengine->networkAccessManagerFactory()) { + accessManager = p->qmlengine->networkAccessManagerFactory()->create(p); + } else { + accessManager = new QNetworkAccessManager(p); + } + } + return accessManager; +} + +QQuickWorkerScriptEnginePrivate::QQuickWorkerScriptEnginePrivate(QQmlEngine *engine) +: workerEngine(0), qmlengine(engine), m_nextId(0) +{ +} + +v8::Handle QQuickWorkerScriptEnginePrivate::sendMessage(const v8::Arguments &args) +{ + WorkerEngine *engine = (WorkerEngine*)V8ENGINE(); + + int id = args[1]->Int32Value(); + + QByteArray data = QV8Worker::serialize(args[2], engine); + + QMutexLocker locker(&engine->p->m_lock); + WorkerScript *script = engine->p->workers.value(id); + if (!script) + return v8::Undefined(); + + if (script->owner) + QCoreApplication::postEvent(script->owner, new WorkerDataEvent(0, data)); + + return v8::Undefined(); +} + +// Requires handle scope and context scope +v8::Handle QQuickWorkerScriptEnginePrivate::getWorker(WorkerScript *script) +{ + if (!script->initialized) { + script->initialized = true; + + script->object = qPersistentNew(workerEngine->contextWrapper()->urlScope(script->source)); + + workerEngine->contextWrapper()->setReadOnly(script->object, false); + + v8::Local api = v8::Object::New(); + api->Set(v8::String::New("sendMessage"), workerEngine->sendFunction(script->id)); + + script->object->Set(v8::String::New("WorkerScript"), api); + + workerEngine->contextWrapper()->setReadOnly(script->object, true); + } + + return script->object; +} + +bool QQuickWorkerScriptEnginePrivate::event(QEvent *event) +{ + if (event->type() == (QEvent::Type)WorkerDataEvent::WorkerData) { + WorkerDataEvent *workerEvent = static_cast(event); + processMessage(workerEvent->workerId(), workerEvent->data()); + return true; + } else if (event->type() == (QEvent::Type)WorkerLoadEvent::WorkerLoad) { + WorkerLoadEvent *workerEvent = static_cast(event); + processLoad(workerEvent->workerId(), workerEvent->url()); + return true; + } else if (event->type() == (QEvent::Type)WorkerDestroyEvent) { + emit stopThread(); + return true; + } else if (event->type() == (QEvent::Type)WorkerRemoveEvent::WorkerRemove) { + WorkerRemoveEvent *workerEvent = static_cast(event); + workers.remove(workerEvent->workerId()); + return true; + } else { + return QObject::event(event); + } +} + +void QQuickWorkerScriptEnginePrivate::processMessage(int id, const QByteArray &data) +{ + WorkerScript *script = workers.value(id); + if (!script) + return; + + v8::HandleScope handle_scope; + v8::Context::Scope scope(workerEngine->context()); + + v8::Handle value = QV8Worker::deserialize(data, workerEngine); + + v8::TryCatch tc; + workerEngine->callOnMessage(script->object, value); + + if (tc.HasCaught()) { + QQmlError error; + QQmlExpressionPrivate::exceptionToError(tc.Message(), error); + reportScriptException(script, error); + } +} + +void QQuickWorkerScriptEnginePrivate::processLoad(int id, const QUrl &url) +{ + if (url.isRelative()) + return; + + QString fileName = QQmlFile::urlToLocalFileOrQrc(url); + + QFile f(fileName); + if (f.open(QIODevice::ReadOnly)) { + QByteArray data = f.readAll(); + QString sourceCode = QString::fromUtf8(data); + QQmlScript::Parser::extractPragmas(sourceCode); + + v8::HandleScope handle_scope; + v8::Context::Scope scope(workerEngine->context()); + + WorkerScript *script = workers.value(id); + if (!script) + return; + script->source = url; + v8::Handle activation = getWorker(script); + if (activation.IsEmpty()) + return; + + // XXX ??? + // workerEngine->baseUrl = url; + + v8::TryCatch tc; + v8::Local program = workerEngine->qmlModeCompile(sourceCode, url.toString()); + + if (!tc.HasCaught()) + program->Run(activation); + + if (tc.HasCaught()) { + QQmlError error; + QQmlExpressionPrivate::exceptionToError(tc.Message(), error); + reportScriptException(script, error); + } + } else { + qWarning().nospace() << "WorkerScript: Cannot find source file " << url.toString(); + } +} + +void QQuickWorkerScriptEnginePrivate::reportScriptException(WorkerScript *script, + const QQmlError &error) +{ + QQuickWorkerScriptEnginePrivate *p = QQuickWorkerScriptEnginePrivate::get(workerEngine); + + QMutexLocker locker(&p->m_lock); + if (script->owner) + QCoreApplication::postEvent(script->owner, new WorkerErrorEvent(error)); +} + +WorkerDataEvent::WorkerDataEvent(int workerId, const QByteArray &data) +: QEvent((QEvent::Type)WorkerData), m_id(workerId), m_data(data) +{ +} + +WorkerDataEvent::~WorkerDataEvent() +{ +} + +int WorkerDataEvent::workerId() const +{ + return m_id; +} + +QByteArray WorkerDataEvent::data() const +{ + return m_data; +} + +WorkerLoadEvent::WorkerLoadEvent(int workerId, const QUrl &url) +: QEvent((QEvent::Type)WorkerLoad), m_id(workerId), m_url(url) +{ +} + +int WorkerLoadEvent::workerId() const +{ + return m_id; +} + +QUrl WorkerLoadEvent::url() const +{ + return m_url; +} + +WorkerRemoveEvent::WorkerRemoveEvent(int workerId) +: QEvent((QEvent::Type)WorkerRemove), m_id(workerId) +{ +} + +int WorkerRemoveEvent::workerId() const +{ + return m_id; +} + +WorkerErrorEvent::WorkerErrorEvent(const QQmlError &error) +: QEvent((QEvent::Type)WorkerError), m_error(error) +{ +} + +QQmlError WorkerErrorEvent::error() const +{ + return m_error; +} + +QQuickWorkerScriptEngine::QQuickWorkerScriptEngine(QQmlEngine *parent) +: QThread(parent), d(new QQuickWorkerScriptEnginePrivate(parent)) +{ + d->m_lock.lock(); + connect(d, SIGNAL(stopThread()), this, SLOT(quit()), Qt::DirectConnection); + start(QThread::LowestPriority); + d->m_wait.wait(&d->m_lock); + d->moveToThread(this); + d->m_lock.unlock(); +} + +QQuickWorkerScriptEngine::~QQuickWorkerScriptEngine() +{ + d->m_lock.lock(); + QCoreApplication::postEvent(d, new QEvent((QEvent::Type)QQuickWorkerScriptEnginePrivate::WorkerDestroyEvent)); + d->m_lock.unlock(); + + //We have to force to cleanup the main thread's event queue here + //to make sure the main GUI release all pending locks/wait conditions which + //some worker script/agent are waiting for (QQmlListModelWorkerAgent::sync() for example). + while (!isFinished()) { + // We can't simply wait here, because the worker thread will not terminate + // until the main thread processes the last data event it generates + QCoreApplication::processEvents(); + yieldCurrentThread(); + } + + d->deleteLater(); +} + +QQuickWorkerScriptEnginePrivate::WorkerScript::WorkerScript() +: id(-1), initialized(false), owner(0) +{ +} + +QQuickWorkerScriptEnginePrivate::WorkerScript::~WorkerScript() +{ + qPersistentDispose(object); +} + +int QQuickWorkerScriptEngine::registerWorkerScript(QQuickWorkerScript *owner) +{ + typedef QQuickWorkerScriptEnginePrivate::WorkerScript WorkerScript; + WorkerScript *script = new WorkerScript; + + script->id = d->m_nextId++; + script->owner = owner; + + d->m_lock.lock(); + d->workers.insert(script->id, script); + d->m_lock.unlock(); + + return script->id; +} + +void QQuickWorkerScriptEngine::removeWorkerScript(int id) +{ + QQuickWorkerScriptEnginePrivate::WorkerScript* script = d->workers.value(id); + if (script) { + script->owner = 0; + QCoreApplication::postEvent(d, new WorkerRemoveEvent(id)); + } +} + +void QQuickWorkerScriptEngine::executeUrl(int id, const QUrl &url) +{ + QCoreApplication::postEvent(d, new WorkerLoadEvent(id, url)); +} + +void QQuickWorkerScriptEngine::sendMessage(int id, const QByteArray &data) +{ + QCoreApplication::postEvent(d, new WorkerDataEvent(id, data)); +} + +void QQuickWorkerScriptEngine::run() +{ + d->m_lock.lock(); + + d->workerEngine = new QQuickWorkerScriptEnginePrivate::WorkerEngine(d); + d->workerEngine->init(); + + d->m_wait.wakeAll(); + + d->m_lock.unlock(); + + exec(); + + qDeleteAll(d->workers); + d->workers.clear(); + + delete d->workerEngine; d->workerEngine = 0; +} + + +/*! + \qmltype WorkerScript + \instantiates QQuickWorkerScript + \ingroup qtquick-threading + \inqmlmodule QtQuick 2 + \brief Enables the use of threads in a Qt Quick application + + Use WorkerScript to run operations in a new thread. + This is useful for running operations in the background so + that the main GUI thread is not blocked. + + Messages can be passed between the new thread and the parent thread + using \l sendMessage() and the \l {WorkerScript::onMessage}{onMessage()} handler. + + An example: + + \snippet qml/workerscript/workerscript.qml 0 + + The above worker script specifies a JavaScript file, "script.js", that handles + the operations to be performed in the new thread. Here is \c script.js: + + \quotefile qml/workerscript/script.js + + When the user clicks anywhere within the rectangle, \c sendMessage() is + called, triggering the \tt WorkerScript.onMessage() handler in + \tt script.js. This in turn sends a reply message that is then received + by the \tt onMessage() handler of \tt myWorker. + + + \section3 Restrictions + + Since the \c WorkerScript.onMessage() function is run in a separate thread, the + JavaScript file is evaluated in a context separate from the main QML engine. This means + that unlike an ordinary JavaScript file that is imported into QML, the \c script.js + in the above example cannot access the properties, methods or other attributes + of the QML item, nor can it access any context properties set on the QML object + through QQmlContext. + + Additionally, there are restrictions on the types of values that can be passed to and + from the worker script. See the sendMessage() documentation for details. + + Worker script can not use \l {qtqml-javascript-imports.html}{.import} syntax. + + \sa {declarative/threading/workerscript}{WorkerScript example}, + {declarative/threading/threadedlistmodel}{Threaded ListModel example} +*/ +QQuickWorkerScript::QQuickWorkerScript(QObject *parent) +: QObject(parent), m_engine(0), m_scriptId(-1), m_componentComplete(true) +{ +} + +QQuickWorkerScript::~QQuickWorkerScript() +{ + if (m_scriptId != -1) m_engine->removeWorkerScript(m_scriptId); +} + +/*! + \qmlproperty url WorkerScript::source + + This holds the url of the JavaScript file that implements the + \tt WorkerScript.onMessage() handler for threaded operations. +*/ +QUrl QQuickWorkerScript::source() const +{ + return m_source; +} + +void QQuickWorkerScript::setSource(const QUrl &source) +{ + if (m_source == source) + return; + + m_source = source; + + if (engine()) + m_engine->executeUrl(m_scriptId, m_source); + + emit sourceChanged(); +} + +/*! + \qmlmethod WorkerScript::sendMessage(jsobject message) + + Sends the given \a message to a worker script handler in another + thread. The other worker script handler can receive this message + through the onMessage() handler. + + The \c message object may only contain values of the following + types: + + \list + \li boolean, number, string + \li JavaScript objects and arrays + \li ListModel objects (any other type of QObject* is not allowed) + \endlist + + All objects and arrays are copied to the \c message. With the exception + of ListModel objects, any modifications by the other thread to an object + passed in \c message will not be reflected in the original object. +*/ +void QQuickWorkerScript::sendMessage(QQmlV8Function *args) +{ + if (!engine()) { + qWarning("QQuickWorkerScript: Attempt to send message before WorkerScript establishment"); + return; + } + + v8::Handle argument = v8::Undefined(); + if (args->Length() != 0) + argument = (*args)[0]; + + m_engine->sendMessage(m_scriptId, QV8Worker::serialize(argument, args->engine())); +} + +void QQuickWorkerScript::classBegin() +{ + m_componentComplete = false; +} + +QQuickWorkerScriptEngine *QQuickWorkerScript::engine() +{ + if (m_engine) return m_engine; + if (m_componentComplete) { + QQmlEngine *engine = qmlEngine(this); + if (!engine) { + qWarning("QQuickWorkerScript: engine() called without qmlEngine() set"); + return 0; + } + + m_engine = QQmlEnginePrivate::get(engine)->getWorkerScriptEngine(); + m_scriptId = m_engine->registerWorkerScript(this); + + if (m_source.isValid()) + m_engine->executeUrl(m_scriptId, m_source); + + return m_engine; + } + return 0; +} + +void QQuickWorkerScript::componentComplete() +{ + m_componentComplete = true; + engine(); // Get it started now. +} + +/*! + \qmlsignal WorkerScript::onMessage(jsobject msg) + + This handler is called when a message \a msg is received from a worker + script in another thread through a call to sendMessage(). +*/ + +bool QQuickWorkerScript::event(QEvent *event) +{ + if (event->type() == (QEvent::Type)WorkerDataEvent::WorkerData) { + QQmlEngine *engine = qmlEngine(this); + if (engine) { + WorkerDataEvent *workerEvent = static_cast(event); + QV8Engine *v8engine = QQmlEnginePrivate::get(engine)->v8engine(); + v8::HandleScope handle_scope; + v8::Context::Scope scope(v8engine->context()); + v8::Handle value = QV8Worker::deserialize(workerEvent->data(), v8engine); + emit message(QQmlV8Handle::fromHandle(value)); + } + return true; + } else if (event->type() == (QEvent::Type)WorkerErrorEvent::WorkerError) { + WorkerErrorEvent *workerEvent = static_cast(event); + QQmlEnginePrivate::warning(qmlEngine(this), workerEvent->error()); + return true; + } else { + return QObject::event(event); + } +} + +QT_END_NAMESPACE + +#include + diff --git a/src/qml/types/qquickworkerscript_p.h b/src/qml/types/qquickworkerscript_p.h new file mode 100644 index 0000000000..1ab5208e45 --- /dev/null +++ b/src/qml/types/qquickworkerscript_p.h @@ -0,0 +1,126 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKWORKERSCRIPT_P_H +#define QQUICKWORKERSCRIPT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qqml.h" + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + + +class QQuickWorkerScript; +class QQuickWorkerScriptEnginePrivate; +class QQuickWorkerScriptEngine : public QThread +{ +Q_OBJECT +public: + QQuickWorkerScriptEngine(QQmlEngine *parent = 0); + virtual ~QQuickWorkerScriptEngine(); + + int registerWorkerScript(QQuickWorkerScript *); + void removeWorkerScript(int); + void executeUrl(int, const QUrl &); + void sendMessage(int, const QByteArray &); + +protected: + virtual void run(); + +private: + QQuickWorkerScriptEnginePrivate *d; +}; + +class QQmlV8Function; +class QQmlV8Handle; +class Q_AUTOTEST_EXPORT QQuickWorkerScript : public QObject, public QQmlParserStatus +{ + Q_OBJECT + Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged) + + Q_INTERFACES(QQmlParserStatus) +public: + QQuickWorkerScript(QObject *parent = 0); + virtual ~QQuickWorkerScript(); + + QUrl source() const; + void setSource(const QUrl &); + +public slots: + void sendMessage(QQmlV8Function*); + +signals: + void sourceChanged(); + void message(const QQmlV8Handle &messageObject); + +protected: + virtual void classBegin(); + virtual void componentComplete(); + virtual bool event(QEvent *); + +private: + QQuickWorkerScriptEngine *engine(); + QQuickWorkerScriptEngine *m_engine; + int m_scriptId; + QUrl m_source; + bool m_componentComplete; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickWorkerScript) + +#endif // QQUICKWORKERSCRIPT_P_H diff --git a/src/qml/types/types.pri b/src/qml/types/types.pri new file mode 100644 index 0000000000..22e62ea8de --- /dev/null +++ b/src/qml/types/types.pri @@ -0,0 +1,26 @@ +SOURCES += \ + $$PWD/qqmlbind.cpp \ + $$PWD/qqmlconnections.cpp \ + $$PWD/qqmldelegatemodel.cpp \ + $$PWD/qqmllistmodel.cpp \ + $$PWD/qqmllistmodelworkeragent.cpp \ + $$PWD/qqmlmodelsmodule.cpp \ + $$PWD/qqmlobjectmodel.cpp \ + $$PWD/qqmltimer.cpp \ + $$PWD/qquickpackage.cpp \ + $$PWD/qquickworkerscript.cpp + +HEADERS += \ + $$PWD/qqmlbind_p.h \ + $$PWD/qqmlconnections_p.h \ + $$PWD/qqmldelegatemodel_p.h \ + $$PWD/qqmldelegatemodel_p_p.h \ + $$PWD/qqmllistmodel_p.h \ + $$PWD/qqmllistmodel_p_p.h \ + $$PWD/qqmllistmodelworkeragent_p.h \ + $$PWD/qqmlmodelsmodule_p.h \ + $$PWD/qqmlobjectmodel_p.h \ + $$PWD/qqmltimer_p.h \ + $$PWD/qquickpackage_p.h \ + $$PWD/qquickworkerscript_p.h + -- cgit v1.2.3 From 9de735316042cdf28e7013727b3198b10f15235b Mon Sep 17 00:00:00 2001 From: Debao Zhang Date: Thu, 7 Mar 2013 17:23:30 +0800 Subject: QQuickView's equivalent in QtQuick1 is QDeclarativeView Change-Id: I12104e8bd58a406a0ff03c110e2df54bbc1c816c Reviewed-by: Friedemann Kleint --- src/quick/items/qquickview.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/quick/items/qquickview.cpp b/src/quick/items/qquickview.cpp index 29991357c9..65343cdc52 100644 --- a/src/quick/items/qquickview.cpp +++ b/src/quick/items/qquickview.cpp @@ -494,7 +494,7 @@ void QQuickViewPrivate::setRootObject(QObject *obj) << "loaded has 'import QtQuick 1.0' or 'import Qt 4.7', this error will occur." << endl << endl << "To load files with 'import QtQuick 1.0' or 'import Qt 4.7', use the" << endl - << "QQuickView class in the qtquick1 module." << endl; + << "QDeclarativeView class in the qtquick1 module." << endl; delete obj; root = 0; } -- cgit v1.2.3 From 1366ad57bca2e45adfd624106602af6bf1f12044 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Wed, 27 Feb 2013 13:54:26 +0100 Subject: QQuickWindow: expose activeFocusItem as a property Change-Id: Ifd393399f93b04313cd66d7873fb99b505640c8a Reviewed-by: Alan Alpert (Personal) <416365416c@gmail.com> --- src/quick/items/qquickwindow.cpp | 10 ++++++++++ src/quick/items/qquickwindow.h | 2 ++ 2 files changed, 12 insertions(+) diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 48e2aceebe..594897eae3 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -403,6 +403,8 @@ void QQuickWindowPrivate::init(QQuickWindow *c) QObject::connect(context, SIGNAL(initialized()), q, SIGNAL(sceneGraphInitialized()), Qt::DirectConnection); QObject::connect(context, SIGNAL(invalidated()), q, SIGNAL(sceneGraphInvalidated()), Qt::DirectConnection); QObject::connect(context, SIGNAL(invalidated()), q, SLOT(cleanupSceneGraph()), Qt::DirectConnection); + + QObject::connect(q, SIGNAL(focusObjectChanged(QObject*)), q, SIGNAL(activeFocusItemChanged())); } /*! @@ -2985,6 +2987,14 @@ QColor QQuickWindow::color() const \since Qt 5.1 */ +/*! + \qmlproperty Item QtQuick.Window2::Window::activeFocusItem + \since Qt 5.1 + + The item which currently has active focus or \c null if there is + no item with active focus. + */ + #include "moc_qquickwindow.cpp" QT_END_NAMESPACE diff --git a/src/quick/items/qquickwindow.h b/src/quick/items/qquickwindow.h index 959c52b37c..a2ba9e9fe0 100644 --- a/src/quick/items/qquickwindow.h +++ b/src/quick/items/qquickwindow.h @@ -64,6 +64,7 @@ class Q_QUICK_EXPORT QQuickWindow : public QWindow Q_PRIVATE_PROPERTY(QQuickWindow::d_func(), QQmlListProperty data READ data DESIGNABLE false) Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) Q_PROPERTY(QQuickItem* contentItem READ contentItem CONSTANT FINAL) + Q_PROPERTY(QQuickItem* activeFocusItem READ activeFocusItem NOTIFY activeFocusItemChanged REVISION 1) Q_CLASSINFO("DefaultProperty", "data") Q_DECLARE_PRIVATE(QQuickWindow) public: @@ -129,6 +130,7 @@ Q_SIGNALS: void beforeRendering(); void afterRendering(); void colorChanged(const QColor &); + Q_REVISION(1) void activeFocusItemChanged(); public Q_SLOTS: void update(); -- cgit v1.2.3 From 70ce4ec6ae9321a601a5af37d11bd284b2203bfc Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Fri, 1 Mar 2013 15:26:54 +0100 Subject: Add TextEdit::selectByKeyboard The main use case is for enabling text selection by keyboard for read-only editors. Change-Id: Ieaa9af366fd0eaf863a104a2fdf33c9ddad38b10 Reviewed-by: Alan Alpert (Personal) <416365416c@gmail.com> Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickitemsmodule.cpp | 1 + src/quick/items/qquicktextedit.cpp | 46 +++++++- src/quick/items/qquicktextedit_p.h | 5 + src/quick/items/qquicktextedit_p_p.h | 4 +- .../quick/qquicktextedit/tst_qquicktextedit.cpp | 124 +++++++++++++++++++++ tests/testapplications/text/textedit.qml | 7 +- 6 files changed, 184 insertions(+), 3 deletions(-) diff --git a/src/quick/items/qquickitemsmodule.cpp b/src/quick/items/qquickitemsmodule.cpp index 20c35f42f2..cd49377822 100644 --- a/src/quick/items/qquickitemsmodule.cpp +++ b/src/quick/items/qquickitemsmodule.cpp @@ -158,6 +158,7 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor) qmlRegisterType(uri,major,minor,"Scale"); qmlRegisterType(uri,major,minor,"Text"); qmlRegisterType(uri,major,minor,"TextEdit"); + qmlRegisterType(uri,2,1,"TextEdit"); qmlRegisterType(uri,major,minor,"TextInput"); qmlRegisterType(uri,major,minor,"ViewSection"); diff --git a/src/quick/items/qquicktextedit.cpp b/src/quick/items/qquicktextedit.cpp index dabbc96614..e30b9cb3fd 100644 --- a/src/quick/items/qquicktextedit.cpp +++ b/src/quick/items/qquicktextedit.cpp @@ -1238,6 +1238,44 @@ void QQuickTextEdit::componentComplete() if (d->cursorComponent && isCursorVisible()) QQuickTextUtil::createCursor(d); } + +/*! + \qmlproperty bool QtQuick2::TextEdit::selectByKeyboard + \since QtQuick 2.1 + + Defaults to true when the editor is editable, and false + when read-only. + + If true, the user can use the keyboard to select text + even if the editor is read-only. If false, the user + cannot use the keyboard to select text even if the + editor is editable. + + \sa readOnly +*/ +bool QQuickTextEdit::selectByKeyboard() const +{ + Q_D(const QQuickTextEdit); + if (d->selectByKeyboardSet) + return d->selectByKeyboard; + return !isReadOnly(); +} + +void QQuickTextEdit::setSelectByKeyboard(bool on) +{ + Q_D(QQuickTextEdit); + bool was = selectByKeyboard(); + d->selectByKeyboardSet = true; + if (was != on) { + d->selectByKeyboard = on; + if (on) + d->control->setTextInteractionFlags(d->control->textInteractionFlags() | Qt::TextSelectableByKeyboard); + else + d->control->setTextInteractionFlags(d->control->textInteractionFlags() & ~Qt::TextSelectableByKeyboard); + emit selectByKeyboardChanged(on); + } +} + /*! \qmlproperty bool QtQuick2::TextEdit::selectByMouse @@ -1316,8 +1354,12 @@ void QQuickTextEdit::setReadOnly(bool r) Qt::TextInteractionFlags flags = Qt::LinksAccessibleByMouse; if (d->selectByMouse) flags = flags | Qt::TextSelectableByMouse; + if (d->selectByKeyboardSet && d->selectByKeyboard) + flags = flags | Qt::TextSelectableByKeyboard; + else if (!d->selectByKeyboardSet && !r) + flags = flags | Qt::TextSelectableByKeyboard; if (!r) - flags = flags | Qt::TextSelectableByKeyboard | Qt::TextEditable; + flags = flags | Qt::TextEditable; d->control->setTextInteractionFlags(flags); if (!r) d->control->moveCursor(QTextCursor::End); @@ -1327,6 +1369,8 @@ void QQuickTextEdit::setReadOnly(bool r) #endif q_canPasteChanged(); emit readOnlyChanged(r); + if (!d->selectByKeyboardSet) + emit selectByKeyboardChanged(!r); } bool QQuickTextEdit::isReadOnly() const diff --git a/src/quick/items/qquicktextedit_p.h b/src/quick/items/qquicktextedit_p.h index 255c8ac670..8a2d9b1e92 100644 --- a/src/quick/items/qquicktextedit_p.h +++ b/src/quick/items/qquicktextedit_p.h @@ -90,6 +90,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickTextEdit : public QQuickImplicitSizeItem #ifndef QT_NO_IM Q_PROPERTY(Qt::InputMethodHints inputMethodHints READ inputMethodHints WRITE setInputMethodHints NOTIFY inputMethodHintsChanged) #endif + Q_PROPERTY(bool selectByKeyboard READ selectByKeyboard WRITE setSelectByKeyboard NOTIFY selectByKeyboardChanged REVISION 1) Q_PROPERTY(bool selectByMouse READ selectByMouse WRITE setSelectByMouse NOTIFY selectByMouseChanged) Q_PROPERTY(SelectionMode mouseSelectionMode READ mouseSelectionMode WRITE setMouseSelectionMode NOTIFY mouseSelectionModeChanged) Q_PROPERTY(bool canPaste READ canPaste NOTIFY canPasteChanged) @@ -201,6 +202,9 @@ public: void setInputMethodHints(Qt::InputMethodHints hints); #endif + bool selectByKeyboard() const; + void setSelectByKeyboard(bool); + bool selectByMouse() const; void setSelectByMouse(bool); @@ -274,6 +278,7 @@ Q_SIGNALS: void activeFocusOnPressChanged(bool activeFocusOnPressed); void persistentSelectionChanged(bool isPersistentSelection); void textMarginChanged(qreal textMargin); + Q_REVISION(1) void selectByKeyboardChanged(bool selectByKeyboard); void selectByMouseChanged(bool selectByMouse); void mouseSelectionModeChanged(SelectionMode mode); void linkActivated(const QString &link); diff --git a/src/quick/items/qquicktextedit_p_p.h b/src/quick/items/qquicktextedit_p_p.h index f65af3d2d3..dd0f76f8d9 100644 --- a/src/quick/items/qquicktextedit_p_p.h +++ b/src/quick/items/qquicktextedit_p_p.h @@ -87,7 +87,7 @@ public: , documentDirty(true), dirty(false), richText(false), cursorVisible(false), cursorPending(false) , focusOnPress(true), persistentSelection(false), requireImplicitWidth(false) , selectByMouse(false), canPaste(false), canPasteValid(false), hAlignImplicit(true) - , textCached(true), inLayout(false) + , textCached(true), inLayout(false), selectByKeyboard(false), selectByKeyboardSet(false) { } @@ -168,6 +168,8 @@ public: bool hAlignImplicit:1; bool textCached:1; bool inLayout:1; + bool selectByKeyboard:1; + bool selectByKeyboardSet:1; }; QT_END_NAMESPACE diff --git a/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp b/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp index 4c4a04b293..b9041fb719 100644 --- a/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp +++ b/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp @@ -134,6 +134,9 @@ private slots: void dragMouseSelection(); void mouseSelectionMode_accessors(); void selectByMouse(); + void selectByKeyboard(); + void keyboardSelection_data(); + void keyboardSelection(); void renderType(); void inputMethodHints(); @@ -2063,6 +2066,127 @@ void tst_qquicktextedit::selectByMouse() QCOMPARE(spy.at(1).at(0).toBool(), false); } +void tst_qquicktextedit::selectByKeyboard() +{ + QQmlComponent oldComponent(&engine); + oldComponent.setData("import QtQuick 2.0\n TextEdit { selectByKeyboard: true }", QUrl()); + QVERIFY(!oldComponent.create()); + + QQmlComponent component(&engine); + component.setData("import QtQuick 2.1\n TextEdit { }", QUrl()); + QScopedPointer object(component.create()); + QQuickTextEdit *edit = qobject_cast(object.data()); + QVERIFY(edit); + + QSignalSpy spy(edit, SIGNAL(selectByKeyboardChanged(bool))); + + QCOMPARE(edit->isReadOnly(), false); + QCOMPARE(edit->selectByKeyboard(), true); + + edit->setReadOnly(true); + QCOMPARE(edit->selectByKeyboard(), false); + QCOMPARE(spy.count(), 1); + QCOMPARE(spy.at(0).at(0).toBool(), false); + + edit->setSelectByKeyboard(true); + QCOMPARE(edit->selectByKeyboard(), true); + QCOMPARE(spy.count(), 2); + QCOMPARE(spy.at(1).at(0).toBool(), true); + + edit->setReadOnly(false); + QCOMPARE(edit->selectByKeyboard(), true); + QCOMPARE(spy.count(), 2); + + edit->setSelectByKeyboard(false); + QCOMPARE(edit->selectByKeyboard(), false); + QCOMPARE(spy.count(), 3); + QCOMPARE(spy.at(2).at(0).toBool(), false); +} + +Q_DECLARE_METATYPE(QKeySequence::StandardKey) + +void tst_qquicktextedit::keyboardSelection_data() +{ + QTest::addColumn("text"); + QTest::addColumn("readOnly"); + QTest::addColumn("selectByKeyboard"); + QTest::addColumn("cursorPosition"); + QTest::addColumn("standardKey"); + QTest::addColumn("selectedText"); + + QTest::newRow("editable - select first char") + << QStringLiteral("editable - select first char") << false << true << 0 << QKeySequence::SelectNextChar << QStringLiteral("e"); + QTest::newRow("editable - select first word") + << QStringLiteral("editable - select first char") << false << true << 0 << QKeySequence::SelectNextWord << QStringLiteral("editable "); + + QTest::newRow("editable - cannot select first char") + << QStringLiteral("editable - cannot select first char") << false << false << 0 << QKeySequence::SelectNextChar << QStringLiteral(""); + QTest::newRow("editable - cannot select first word") + << QStringLiteral("editable - cannot select first word") << false << false << 0 << QKeySequence::SelectNextWord << QStringLiteral(""); + + QTest::newRow("editable - select last char") + << QStringLiteral("editable - select last char") << false << true << 27 << QKeySequence::SelectPreviousChar << QStringLiteral("r"); + QTest::newRow("editable - select last word") + << QStringLiteral("editable - select last word") << false << true << 27 << QKeySequence::SelectPreviousWord << QStringLiteral("word"); + + QTest::newRow("editable - cannot select last char") + << QStringLiteral("editable - cannot select last char") << false << false << 35 << QKeySequence::SelectPreviousChar << QStringLiteral(""); + QTest::newRow("editable - cannot select last word") + << QStringLiteral("editable - cannot select last word") << false << false << 35 << QKeySequence::SelectPreviousWord << QStringLiteral(""); + + QTest::newRow("read-only - cannot select first char") + << QStringLiteral("read-only - cannot select first char") << true << false << 0 << QKeySequence::SelectNextChar << QStringLiteral(""); + QTest::newRow("read-only - cannot select first word") + << QStringLiteral("read-only - cannot select first word") << true << false << 0 << QKeySequence::SelectNextWord << QStringLiteral(""); + + QTest::newRow("read-only - cannot select last char") + << QStringLiteral("read-only - cannot select last char") << true << false << 35 << QKeySequence::SelectPreviousChar << QStringLiteral(""); + QTest::newRow("read-only - cannot select last word") + << QStringLiteral("read-only - cannot select last word") << true << false << 35 << QKeySequence::SelectPreviousWord << QStringLiteral(""); + + QTest::newRow("read-only - select first char") + << QStringLiteral("read-only - select first char") << true << true << 0 << QKeySequence::SelectNextChar << QStringLiteral("r"); + QTest::newRow("read-only - select first word") + << QStringLiteral("read-only - select first word") << true << true << 0 << QKeySequence::SelectNextWord << QStringLiteral("read"); + + QTest::newRow("read-only - select last char") + << QStringLiteral("read-only - select last char") << true << true << 28 << QKeySequence::SelectPreviousChar << QStringLiteral("r"); + QTest::newRow("read-only - select last word") + << QStringLiteral("read-only - select last word") << true << true << 28 << QKeySequence::SelectPreviousWord << QStringLiteral("word"); +} + +void tst_qquicktextedit::keyboardSelection() +{ + QFETCH(QString, text); + QFETCH(bool, readOnly); + QFETCH(bool, selectByKeyboard); + QFETCH(int, cursorPosition); + QFETCH(QKeySequence::StandardKey, standardKey); + QFETCH(QString, selectedText); + + QQmlComponent component(&engine); + component.setData("import QtQuick 2.1\n TextEdit { focus: true }", QUrl()); + QScopedPointer object(component.create()); + QQuickTextEdit *edit = qobject_cast(object.data()); + QVERIFY(edit); + + edit->setText(text); + edit->setReadOnly(readOnly); + edit->setSelectByKeyboard(selectByKeyboard); + edit->setCursorPosition(cursorPosition); + + QQuickWindow window; + edit->setParentItem(window.contentItem()); + window.show(); + window.requestActivate(); + QTest::qWaitForWindowActive(&window); + QVERIFY(edit->hasActiveFocus()); + + simulateKeys(&window, standardKey); + + QCOMPARE(edit->selectedText(), selectedText); +} + void tst_qquicktextedit::renderType() { QQmlComponent component(&engine); diff --git a/tests/testapplications/text/textedit.qml b/tests/testapplications/text/textedit.qml index 789a52894e..e0d7dbdde4 100644 --- a/tests/testapplications/text/textedit.qml +++ b/tests/testapplications/text/textedit.qml @@ -39,7 +39,7 @@ ** ****************************************************************************/ -import QtQuick 2.0 +import QtQuick 2.1 Rectangle { height: 360; width: 640 @@ -75,6 +75,7 @@ Rectangle { wrapMode: { wrapvalue.model.get(wrapvalue.currentIndex).value } smooth: { smoothvalue.model.get(smoothvalue.currentIndex).value } selectByMouse: { mousevalue.model.get(mousevalue.currentIndex).value } + selectByKeyboard: { keyboardvalue.model.get(keyboardvalue.currentIndex).value } onLinkActivated: { bordercolor.border.color = "red" } Rectangle { id: bordercolor; color: "transparent"; border.color: "green"; anchors.fill: parent } } @@ -227,6 +228,10 @@ Rectangle { id: mousevalue controlname: "Mouse" model: ListModel { ListElement { name: "Off"; value: false } ListElement { name: "On"; value: true } } } + ControlView { + id: keyboardvalue + controlname: "Keyboard" + model: ListModel { ListElement { name: "Off"; value: false } ListElement { name: "On"; value: true } } } ControlView { id: halignvalue controlname: "HAlign" -- cgit v1.2.3 From 789e6eb98376c6a235c1a32d009a9b13dd97d93c Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Thu, 14 Mar 2013 21:06:51 +0100 Subject: photosurface example: simplify PinchArea usage; fileUrls; license FileDialog.filePaths was changed to fileUrls. No need to do so much scripting because the PinchArea.target takes care of the math already, and with better rotational symmetry too. Example QML should have a BSD license. Change-Id: I892292574c81c032d948c1c2d4924fc7127dd008 Reviewed-by: Paul Olav Tvete --- examples/quick/demos/photosurface/photosurface.qml | 74 ++++++++++------------ 1 file changed, 33 insertions(+), 41 deletions(-) diff --git a/examples/quick/demos/photosurface/photosurface.qml b/examples/quick/demos/photosurface/photosurface.qml index 80957180d0..53e0edffbe 100644 --- a/examples/quick/demos/photosurface/photosurface.qml +++ b/examples/quick/demos/photosurface/photosurface.qml @@ -1,39 +1,38 @@ /**************************************************************************** ** -** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** -** This file is part of the QtQml module of the Qt Toolkit. +** This file is part of the examples of the Qt Toolkit. ** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: ** -** 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. +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. ** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. ** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** @@ -54,7 +53,7 @@ Rectangle { id: fileDialog title: "Choose a folder with some images" selectFolder: true - onAccepted: { folderModel.folder = filePaths[0] + "/" } + onAccepted: folderModel.folder = fileUrl + "/" } Repeater { @@ -85,18 +84,11 @@ Rectangle { } PinchArea { anchors.fill: parent - property real initialScale - property real initialRotation - onPinchStarted: { - initialScale = image.scale - initialRotation = parent.rotation - } - onPinchUpdated: { - image.scale = initialScale * pinch.scale; - parent.rotation = initialRotation + pinch.rotation - if (Math.abs(photoFrame.rotation) < 4) - parent.rotation = 0; - } + pinch.target: photoFrame + pinch.minimumRotation: -360 + pinch.maximumRotation: 360 + pinch.minimumScale: 0.1 + pinch.maximumScale: 10 onPinchFinished: photoFrame.border.color = "black"; MouseArea { id: dragArea -- cgit v1.2.3 From 9146d36552c2eb99166b773bb027d34c671d66e5 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Thu, 28 Feb 2013 17:57:38 +0100 Subject: Create common base class for declarative dialogs QQuickAbstractFileDialog inherits QQuickAbstractDialog, and so can the future platform dialog types. Also some header comment corrections. Change-Id: I86bc6d975223979c19d94a3fd70e4b5130b73f47 Reviewed-by: Liang Qi --- src/imports/dialogs/DefaultFileDialog.qml | 57 +++--- src/imports/dialogs/WidgetFileDialog.qml | 65 +++--- src/imports/dialogs/dialogs.pro | 8 +- src/imports/dialogs/plugin.cpp | 97 ++++----- src/imports/dialogs/qml/Button.qml | 57 +++--- src/imports/dialogs/qml/TextField.qml | 57 +++--- src/imports/dialogs/qquickabstractdialog.cpp | 248 +++++++++++++++++++++++ src/imports/dialogs/qquickabstractdialog_p.h | 133 ++++++++++++ src/imports/dialogs/qquickabstractfiledialog.cpp | 58 +----- src/imports/dialogs/qquickabstractfiledialog_p.h | 38 +--- src/imports/dialogs/qquickfiledialog.cpp | 67 +----- src/imports/dialogs/qquickfiledialog_p.h | 10 +- src/imports/dialogs/qquickplatformfiledialog.cpp | 6 +- src/imports/dialogs/qquickplatformfiledialog_p.h | 2 +- src/imports/widgets/widgets.pro | 8 +- 15 files changed, 581 insertions(+), 330 deletions(-) create mode 100644 src/imports/dialogs/qquickabstractdialog.cpp create mode 100644 src/imports/dialogs/qquickabstractdialog_p.h diff --git a/src/imports/dialogs/DefaultFileDialog.qml b/src/imports/dialogs/DefaultFileDialog.qml index 80bf6b5cb1..9e05ace7b5 100644 --- a/src/imports/dialogs/DefaultFileDialog.qml +++ b/src/imports/dialogs/DefaultFileDialog.qml @@ -1,43 +1,42 @@ -/**************************************************************************** +/***************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** -** This file is part of the QtDeclarative module of the Qt Toolkit. +** This file is part of the QtQuick.Dialogs module of the Qt Toolkit. ** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: ** -** 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. +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. ** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. ** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** -****************************************************************************/ +*****************************************************************************/ import QtQuick 2.1 import QtQuick.Dialogs 1.0 diff --git a/src/imports/dialogs/WidgetFileDialog.qml b/src/imports/dialogs/WidgetFileDialog.qml index 837c7f8a57..c8f59d20a7 100644 --- a/src/imports/dialogs/WidgetFileDialog.qml +++ b/src/imports/dialogs/WidgetFileDialog.qml @@ -1,43 +1,42 @@ -/**************************************************************************** +/***************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** 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, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** +** This file is part of the QtQuick.Dialogs module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** -****************************************************************************/ +*****************************************************************************/ import QtQuick 2.1 import QtQuick.PrivateWidgets 1.0 diff --git a/src/imports/dialogs/dialogs.pro b/src/imports/dialogs/dialogs.pro index 1a8ec05b85..784517f73e 100644 --- a/src/imports/dialogs/dialogs.pro +++ b/src/imports/dialogs/dialogs.pro @@ -4,15 +4,17 @@ TARGETPATH = QtQuick/Dialogs IMPORT_VERSION = 1.0 SOURCES += \ - plugin.cpp \ qquickabstractfiledialog.cpp \ qquickplatformfiledialog.cpp \ - qquickfiledialog.cpp + qquickfiledialog.cpp \ + qquickabstractdialog.cpp \ + plugin.cpp HEADERS += \ qquickabstractfiledialog_p.h \ qquickplatformfiledialog_p.h \ - qquickfiledialog_p.h + qquickfiledialog_p.h \ + qquickabstractdialog_p.h QML_FILES += \ DefaultFileDialog.qml \ diff --git a/src/imports/dialogs/plugin.cpp b/src/imports/dialogs/plugin.cpp index 46bd0dcb9d..f7d72bf496 100644 --- a/src/imports/dialogs/plugin.cpp +++ b/src/imports/dialogs/plugin.cpp @@ -3,7 +3,7 @@ ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** -** This file is part of the plugins of the Qt Toolkit. +** This file is part of the QtQuick.Dialogs module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage @@ -48,6 +48,8 @@ //#define PURE_QML_ONLY +#define DIALOGS_MAJOR_MINOR 1, 0 + QT_BEGIN_NAMESPACE /*! @@ -73,66 +75,53 @@ class QtQuick2DialogsPlugin : public QQmlExtensionPlugin public: QtQuick2DialogsPlugin() : QQmlExtensionPlugin() { } - virtual void initializeEngine(QQmlEngine *, const char *uri) { - bool needQml = false; - QDir qmlDir(baseUrl().toLocalFile()); - // If there is no support for native dialogs on the platform, we need to - // either re-use QFileDialog, or register a QML implementation instead. -#ifdef PURE_QML_ONLY - needQml = true; -#else - if (!QGuiApplicationPrivate::platformTheme()->usePlatformNativeDialog(QPlatformTheme::FileDialog)) { - needQml = true; - // If there is not a QApplication, there's no point in trying to load - // widget-based dialogs, because a runtime error will occur. - if (QCoreApplication::instance()->metaObject()->className() == QLatin1String("QApplication")) { - // Test whether PrivateWidgets can load. It's not currently possible - // to use the given engine for that, so we need to create a temporary one. - // That doesn't work in registerTypes either, which is why it's done here. - QString dialogQmlPath(qmlDir.filePath("WidgetFileDialog.qml")); - QQmlEngine tempEngine; - QQmlComponent widgetDialogComponent(&tempEngine); - QFile widgetDialogQmlFile(dialogQmlPath); - widgetDialogQmlFile.open(QIODevice::ReadOnly); - widgetDialogComponent.setData(widgetDialogQmlFile.readAll(), QUrl()); - - switch (widgetDialogComponent.status()) { - case QQmlComponent::Ready: - needQml = (qmlRegisterType(QUrl::fromLocalFile(dialogQmlPath), uri, 1, 0, "FileDialog") < 0); - // returns -1 unless we omit the module from qmldir, because otherwise - // QtQuick.Dialogs is already a protected namespace - // after the qmldir having been parsed. (QQmlImportDatabase::importPlugin) - // But omitting the module from qmldir results in this warning: - // "Module 'QtQuick.Dialogs' does not contain a module identifier directive - - // it cannot be protected from external registrations." - // TODO register all types in registerTypes, to avoid the warning - // But, in that case we cannot check for existence by trying to instantiate the component. - // So it will have to just look for a file (qmldir?) and assume - // that whatever modules are installed are also in working order. - break; - default: - break; - } - } - } -#endif - if (needQml) { - QString dialogQmlPath = qmlDir.filePath("DefaultFileDialog.qml"); - qmlRegisterType(uri, 1, 0, "AbstractFileDialog"); // implementation wrapper - // qDebug() << "registering FileDialog as " << dialogQmlPath << "success?" << - qmlRegisterType(QUrl::fromLocalFile(dialogQmlPath), uri, 1, 0, "FileDialog"); - } - } - virtual void registerTypes(const char *uri) { Q_ASSERT(QLatin1String(uri) == QLatin1String("QtQuick.Dialogs")); + QDir qmlDir(baseUrl().toLocalFile()); + QDir widgetsDir(baseUrl().toLocalFile()); + // TODO: find the directory by searching rather than assuming a relative path + widgetsDir.cd("../PrivateWidgets"); + // Prefer the QPA dialog helpers if the platform supports them. + // Else if there is a QWidget-based implementation, check whether it's + // possible to instantiate it from Qt Quick. + // Otherwise fall back to a pure-QML implementation. + + // FileDialog #ifndef PURE_QML_ONLY - // Prefer the QPA file dialog helper if the platform supports it if (QGuiApplicationPrivate::platformTheme()->usePlatformNativeDialog(QPlatformTheme::FileDialog)) - qmlRegisterType(uri, 1, 0, "FileDialog"); + qmlRegisterType(uri, DIALOGS_MAJOR_MINOR, "FileDialog"); + else #endif + registerWidgetOrQmlImplementation(widgetsDir, "WidgetFileDialog.qml", + qmlDir, "DefaultFileDialog.qml", "FileDialog", uri); } + +protected: + template + void registerWidgetOrQmlImplementation(QDir widgetsDir, QString widgetQmlFile, + QDir qmlDir, QString qmlFile, const char *qmlName, const char *uri) { + bool needQml = true; +#ifndef PURE_QML_ONLY + // If there is a qmldir and we have a QApplication instance (as opposed to a + // widget-free QGuiApplication), assume that the widget-based dialog will work. + if (widgetsDir.exists("qmldir") && !widgetQmlFile.isEmpty() && + !qstrcmp(QCoreApplication::instance()->metaObject()->className(), "QApplication")) { + QString dialogQmlPath = qmlDir.filePath(widgetQmlFile); + if (qmlRegisterType(QUrl::fromLocalFile(dialogQmlPath), uri, DIALOGS_MAJOR_MINOR, qmlName) >= 0) + needQml = false; + // qDebug() << "registering" << qmlName << " as " << dialogQmlPath << "success?" << needQml; + } +#endif + if (needQml) { + QString dialogQmlPath = qmlDir.filePath(qmlFile); + QByteArray abstractTypeName = QByteArray("Abstract") + qmlName; + qmlRegisterType(uri, DIALOGS_MAJOR_MINOR, abstractTypeName); // implementation wrapper + // qDebug() << "registering" << qmlName << " as " << dialogQmlPath << "success?" << + qmlRegisterType(QUrl::fromLocalFile(dialogQmlPath), uri, DIALOGS_MAJOR_MINOR, qmlName); + } + } + }; QT_END_NAMESPACE diff --git a/src/imports/dialogs/qml/Button.qml b/src/imports/dialogs/qml/Button.qml index 42ee9f79e8..936c72c876 100644 --- a/src/imports/dialogs/qml/Button.qml +++ b/src/imports/dialogs/qml/Button.qml @@ -1,43 +1,42 @@ -/**************************************************************************** +/***************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** -** This file is part of the QtQml module of the Qt Toolkit. +** This file is part of the QtQuick.Dialogs module of the Qt Toolkit. ** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: ** -** 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. +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. ** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. ** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** -****************************************************************************/ +*****************************************************************************/ import QtQuick 2.1 diff --git a/src/imports/dialogs/qml/TextField.qml b/src/imports/dialogs/qml/TextField.qml index 204eef71b9..da93239fd5 100644 --- a/src/imports/dialogs/qml/TextField.qml +++ b/src/imports/dialogs/qml/TextField.qml @@ -1,43 +1,42 @@ -/**************************************************************************** +/***************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** -** This file is part of the QtQml module of the Qt Toolkit. +** This file is part of the QtQuick.Dialogs module of the Qt Toolkit. ** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: ** -** 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. +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. ** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. ** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** -****************************************************************************/ +*****************************************************************************/ import QtQuick 2.1 diff --git a/src/imports/dialogs/qquickabstractdialog.cpp b/src/imports/dialogs/qquickabstractdialog.cpp new file mode 100644 index 0000000000..abdc9e067c --- /dev/null +++ b/src/imports/dialogs/qquickabstractdialog.cpp @@ -0,0 +1,248 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQuick.Dialogs module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickabstractdialog_p.h" +#include "qquickitem.h" + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +QQuickAbstractDialog::QQuickAbstractDialog(QObject *parent) + : QObject(parent) + , m_parentWindow(0) + , m_visible(false) + , m_modality(Qt::WindowModal) + , m_qmlImplementation(0) + , m_dialogWindow(0) +{ +} + +QQuickAbstractDialog::~QQuickAbstractDialog() +{ +} + +void QQuickAbstractDialog::setVisible(bool v) +{ + if (m_visible == v) return; + m_visible = v; + if (helper()) { + if (v) { + Qt::WindowFlags flags = Qt::Dialog; + if (!title().isEmpty()) + flags |= Qt::WindowTitleHint; + m_visible = helper()->show(flags, m_modality, parentWindow()); + } else { + helper()->hide(); + } + } else { + // For a pure QML implementation, there is no helper. + // But m_implementation is probably either an Item or a Window at this point. + if (!m_dialogWindow) { + m_dialogWindow = qobject_cast(m_qmlImplementation); + if (!m_dialogWindow) { + QQuickItem *dlgItem = qobject_cast(m_qmlImplementation); + if (dlgItem) { + m_dialogWindow = dlgItem->window(); + // An Item-based dialog implementation doesn't come with a window, so + // we have to instantiate one iff the platform allows it. + if (!m_dialogWindow && QGuiApplicationPrivate::platformIntegration()-> + hasCapability(QPlatformIntegration::MultipleWindows)) { + QQuickWindow *win = new QQuickWindow; + ((QObject *)win)->setParent(this); // memory management only + m_dialogWindow = win; + dlgItem->setParentItem(win->contentItem()); + m_dialogWindow->setMinimumSize(QSize(dlgItem->width(), dlgItem->height())); + connect(win, SIGNAL(widthChanged(int)), this, SLOT(windowGeometryChanged())); + connect(win, SIGNAL(heightChanged(int)), this, SLOT(windowGeometryChanged())); + } + + QQuickItem *parentItem = qobject_cast(parent()); + // qDebug() << "item implementation" << dlgItem << "has window" << m_dialogWindow << "and parent" << parentItem; + + // If the platform does not support multiple windows, but the dialog is + // implemented as an Item, then just reparent it and make it visible. + // TODO QTBUG-29818: put it into a fake Item-based window, when we have a reusable self-decorated one. + if (parentItem && !m_dialogWindow) + dlgItem->setParentItem(parentItem); + } + } + if (m_dialogWindow) + connect(m_dialogWindow, SIGNAL(visibleChanged(bool)), this, SLOT(visibleChanged(bool))); + } + if (m_dialogWindow) { + if (v) { + m_dialogWindow->setTransientParent(parentWindow()); + m_dialogWindow->setTitle(title()); + m_dialogWindow->setModality(m_modality); + } + m_dialogWindow->setVisible(v); + } + } + + emit visibilityChanged(); +} + +void QQuickAbstractDialog::setModality(Qt::WindowModality m) +{ + if (m_modality == m) return; + m_modality = m; + emit modalityChanged(); +} + +void QQuickAbstractDialog::accept() +{ + setVisible(false); + emit accepted(); +} + +void QQuickAbstractDialog::reject() +{ + setVisible(false); + emit rejected(); +} + +void QQuickAbstractDialog::visibleChanged(bool v) +{ + m_visible = v; + emit visibilityChanged(); +} + +void QQuickAbstractDialog::windowGeometryChanged() +{ + QQuickItem *content = qobject_cast(m_qmlImplementation); +qDebug() << Q_FUNC_INFO << m_dialogWindow << content; + if (m_dialogWindow && content) { + content->setWidth(m_dialogWindow->width()); + content->setHeight(m_dialogWindow->height()); + } +} + +QQuickWindow *QQuickAbstractDialog::parentWindow() +{ + QQuickItem *parentItem = qobject_cast(parent()); + if (parentItem) + m_parentWindow = parentItem->window(); + return m_parentWindow; +} + +void QQuickAbstractDialog::setQmlImplementation(QObject *obj) +{ + m_qmlImplementation = obj; + if (m_dialogWindow) { + disconnect(this, SLOT(visibleChanged(bool))); + // Can't necessarily delete because m_dialogWindow might have been provided by the QML. + m_dialogWindow = 0; + } +} + +int QQuickAbstractDialog::x() const +{ + if (m_dialogWindow) + return m_dialogWindow->x(); + return -1; +} + +int QQuickAbstractDialog::y() const +{ + if (m_dialogWindow) + return m_dialogWindow->y(); + return -1; +} + +int QQuickAbstractDialog::width() const +{ + if (m_dialogWindow) + return m_dialogWindow->width(); + return -1; +} + +int QQuickAbstractDialog::height() const +{ + if (m_dialogWindow) + return m_dialogWindow->height(); + return -1; +} + +void QQuickAbstractDialog::setX(int arg) +{ + if (helper()) { + // TODO + } else if (m_dialogWindow) { + m_dialogWindow->setX(arg); + } + emit geometryChanged(); +} + +void QQuickAbstractDialog::setY(int arg) +{ + if (helper()) { + // TODO + } else if (m_dialogWindow) { + m_dialogWindow->setY(arg); + } + emit geometryChanged(); +} + +void QQuickAbstractDialog::setWidth(int arg) +{ + if (helper()) { + // TODO + } else if (m_dialogWindow) { + m_dialogWindow->setWidth(arg); + } + emit geometryChanged(); +} + +void QQuickAbstractDialog::setHeight(int arg) +{ + if (helper()) { + // TODO + } else if (m_dialogWindow) { + m_dialogWindow->setHeight(arg); + } + emit geometryChanged(); +} + +QT_END_NAMESPACE diff --git a/src/imports/dialogs/qquickabstractdialog_p.h b/src/imports/dialogs/qquickabstractdialog_p.h new file mode 100644 index 0000000000..2ad24f98e0 --- /dev/null +++ b/src/imports/dialogs/qquickabstractdialog_p.h @@ -0,0 +1,133 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQuick.Dialogs module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKABSTRACTDIALOG_P_H +#define QQUICKABSTRACTDIALOG_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QQuickAbstractDialog : public QObject +{ + Q_OBJECT + Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibilityChanged) + Q_PROPERTY(Qt::WindowModality modality READ modality WRITE setModality NOTIFY modalityChanged) + Q_PROPERTY(QString title READ title WRITE setTitle NOTIFY titleChanged) + Q_PROPERTY(int x READ x WRITE setX NOTIFY geometryChanged) + Q_PROPERTY(int y READ y WRITE setY NOTIFY geometryChanged) + Q_PROPERTY(int width READ width WRITE setWidth NOTIFY geometryChanged) + Q_PROPERTY(int height READ height WRITE setHeight NOTIFY geometryChanged) + +public: + QQuickAbstractDialog(QObject *parent = 0); + virtual ~QQuickAbstractDialog(); + + bool isVisible() const { return m_visible; } + Qt::WindowModality modality() const { return m_modality; } + virtual QString title() const = 0; + QObject* qmlImplementation() { return m_qmlImplementation; } + + int x() const; + int y() const; + int width() const; + int height() const; + + virtual void setVisible(bool v); + virtual void setModality(Qt::WindowModality m); + virtual void setTitle(QString t) = 0; + void setQmlImplementation(QObject* obj); + void setX(int arg); + void setY(int arg); + void setWidth(int arg); + void setHeight(int arg); + +public Q_SLOTS: + void open() { setVisible(true); } + void close() { setVisible(false); } + +Q_SIGNALS: + void visibilityChanged(); + void geometryChanged(); + void modalityChanged(); + void titleChanged(); + void accepted(); + void rejected(); + +protected Q_SLOTS: + void accept(); + void reject(); + void visibleChanged(bool v); + void windowGeometryChanged(); + +protected: + virtual QPlatformDialogHelper *helper() = 0; + QQuickWindow *parentWindow(); + +protected: + QQuickWindow *m_parentWindow; + bool m_visible; + Qt::WindowModality m_modality; + +protected: // variables for pure-QML implementations only + QObject *m_qmlImplementation; + QWindow *m_dialogWindow; + + Q_DISABLE_COPY(QQuickAbstractDialog) +}; + +QT_END_NAMESPACE + +#endif // QQUICKABSTRACTDIALOG_P_H diff --git a/src/imports/dialogs/qquickabstractfiledialog.cpp b/src/imports/dialogs/qquickabstractfiledialog.cpp index 905cb5d030..32442de41f 100644 --- a/src/imports/dialogs/qquickabstractfiledialog.cpp +++ b/src/imports/dialogs/qquickabstractfiledialog.cpp @@ -3,7 +3,7 @@ ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** -** This file is part of the QtQml module of the Qt Toolkit. +** This file is part of the QtQuick.Dialogs module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage @@ -49,16 +49,14 @@ QT_BEGIN_NAMESPACE QQuickAbstractFileDialog::QQuickAbstractFileDialog(QObject *parent) - : QObject(parent) + : QQuickAbstractDialog(parent) , m_dlgHelper(0) - , m_parentWindow(0) , m_options(QSharedPointer(new QFileDialogOptions())) - , m_visible(false) - , m_modality(Qt::WindowModal) , m_selectExisting(true) , m_selectMultiple(false) , m_selectFolder(false) { + connect(this, SIGNAL(accepted()), this, SIGNAL(selectionAccepted())); } QQuickAbstractFileDialog::~QQuickAbstractFileDialog() @@ -67,27 +65,17 @@ QQuickAbstractFileDialog::~QQuickAbstractFileDialog() void QQuickAbstractFileDialog::setVisible(bool v) { - if (m_visible == v) return; - m_visible = v; - if (helper()) { - if (v) { - helper()->setOptions(m_options); - helper()->setFilter(); - m_visible = helper()->show(Qt::Dialog, m_modality, parentWindow()); - emit filterSelected(); - } else { - helper()->hide(); - } + if (helper() && v) { + m_dlgHelper->setOptions(m_options); + m_dlgHelper->setFilter(); + emit filterSelected(); } - - emit visibilityChanged(); + QQuickAbstractDialog::setVisible(v); } -void QQuickAbstractFileDialog::setModality(Qt::WindowModality m) +QString QQuickAbstractFileDialog::title() const { - if (m_modality == m) return; - m_modality = m; - emit modalityChanged(); + return m_options->windowTitle(); } void QQuickAbstractFileDialog::setTitle(QString t) @@ -177,24 +165,6 @@ QList QQuickAbstractFileDialog::fileUrls() return ret; } -void QQuickAbstractFileDialog::accept() -{ - setVisible(false); - emit accepted(); -} - -void QQuickAbstractFileDialog::reject() -{ - setVisible(false); - emit rejected(); -} - -void QQuickAbstractFileDialog::visibleChanged(bool v) -{ - m_visible = v; - emit visibilityChanged(); -} - void QQuickAbstractFileDialog::updateModes() { // The 4 possible modes are AnyFile, ExistingFile, Directory, ExistingFiles @@ -222,12 +192,4 @@ void QQuickAbstractFileDialog::updateModes() emit fileModeChanged(); } -QQuickWindow *QQuickAbstractFileDialog::parentWindow() -{ - QQuickItem *parentItem = qobject_cast(parent()); - if (parentItem) - m_parentWindow = parentItem->window(); - return m_parentWindow; -} - QT_END_NAMESPACE diff --git a/src/imports/dialogs/qquickabstractfiledialog_p.h b/src/imports/dialogs/qquickabstractfiledialog_p.h index e9565108d6..965f1a7029 100644 --- a/src/imports/dialogs/qquickabstractfiledialog_p.h +++ b/src/imports/dialogs/qquickabstractfiledialog_p.h @@ -3,7 +3,7 @@ ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** -** This file is part of the QtQml module of the Qt Toolkit. +** This file is part of the QtQuick.Dialogs module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage @@ -57,32 +57,27 @@ #include #include #include +#include "qquickabstractdialog_p.h" QT_BEGIN_NAMESPACE -class QQuickAbstractFileDialog : public QObject +class QQuickAbstractFileDialog : public QQuickAbstractDialog { Q_OBJECT - Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibilityChanged) - Q_PROPERTY(Qt::WindowModality modality READ modality WRITE setModality NOTIFY modalityChanged) - Q_PROPERTY(QString title READ title WRITE setTitle NOTIFY titleChanged) Q_PROPERTY(bool selectExisting READ selectExisting WRITE setSelectExisting NOTIFY fileModeChanged) Q_PROPERTY(bool selectMultiple READ selectMultiple WRITE setSelectMultiple NOTIFY fileModeChanged) Q_PROPERTY(bool selectFolder READ selectFolder WRITE setSelectFolder NOTIFY fileModeChanged) Q_PROPERTY(QString folder READ folder WRITE setFolder NOTIFY folderChanged) Q_PROPERTY(QStringList nameFilters READ nameFilters WRITE setNameFilters NOTIFY nameFiltersChanged) Q_PROPERTY(QString selectedNameFilter READ selectedNameFilter WRITE selectNameFilter NOTIFY filterSelected) - Q_PROPERTY(QUrl fileUrl READ fileUrl NOTIFY accepted) - Q_PROPERTY(QList fileUrls READ fileUrls NOTIFY accepted) - // TODO: QTBUG-29817: provide x y width and height (after QPlatformDialogHelper provides it) + Q_PROPERTY(QUrl fileUrl READ fileUrl NOTIFY selectionAccepted) + Q_PROPERTY(QList fileUrls READ fileUrls NOTIFY selectionAccepted) public: QQuickAbstractFileDialog(QObject *parent = 0); virtual ~QQuickAbstractFileDialog(); - bool isVisible() const { return m_visible; } - Qt::WindowModality modality() const { return m_modality; } - QString title() const { return m_options->windowTitle(); } + virtual QString title() const; bool selectExisting() const { return m_selectExisting; } bool selectMultiple() const { return m_selectMultiple; } bool selectFolder() const { return m_selectFolder; } @@ -93,10 +88,7 @@ public: virtual QList fileUrls(); public Q_SLOTS: - void open() { setVisible(true); } - void close() { setVisible(false); } - virtual void setVisible(bool v); - void setModality(Qt::WindowModality m); + void setVisible(bool v); void setTitle(QString t); void setSelectExisting(bool s); void setSelectMultiple(bool s); @@ -106,32 +98,18 @@ public Q_SLOTS: void selectNameFilter(QString f); Q_SIGNALS: - void visibilityChanged(); - void modalityChanged(); - void titleChanged(); void folderChanged(); void nameFiltersChanged(); void filterSelected(); void fileModeChanged(); - void accepted(); - void rejected(); - -protected Q_SLOTS: - void accept(); - void reject(); - void visibleChanged(bool v); + void selectionAccepted(); protected: - virtual QPlatformFileDialogHelper *helper() = 0; void updateModes(); - QQuickWindow *parentWindow(); protected: QPlatformFileDialogHelper *m_dlgHelper; - QQuickWindow *m_parentWindow; QSharedPointer m_options; - bool m_visible; - Qt::WindowModality m_modality; bool m_selectExisting; bool m_selectMultiple; bool m_selectFolder; diff --git a/src/imports/dialogs/qquickfiledialog.cpp b/src/imports/dialogs/qquickfiledialog.cpp index 89b8b4cc5b..f78e8a6f8d 100644 --- a/src/imports/dialogs/qquickfiledialog.cpp +++ b/src/imports/dialogs/qquickfiledialog.cpp @@ -3,7 +3,7 @@ ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** -** This file is part of the QtQml module of the Qt Toolkit. +** This file is part of the QtQuick.Dialogs module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage @@ -42,7 +42,6 @@ #include "qquickfiledialog_p.h" #include #include -#include QT_BEGIN_NAMESPACE @@ -56,8 +55,10 @@ QT_BEGIN_NAMESPACE \internal AbstractFileDialog provides only the API for implementing a file dialog. - The implementation (e.g. a Window or Item) can be provided as \l implementation, - which is the default property (the only allowed child element). + The implementation (e.g. a Window or preferably an Item, in case it is + shown on a device that doesn't support multiple windows) can be provided as + \l implementation, which is the default property (the only allowed child + element). */ /*! @@ -77,8 +78,8 @@ QT_BEGIN_NAMESPACE \inmodule QtQuick.Dialogs \internal - The QQuickFileDialog class is a concrete subclass of \l - QQuickAbstractFileDialog, but it is abstract from the QML perspective + The QQuickFileDialog class is a concrete subclass of + \l QQuickAbstractFileDialog, but it is abstract from the QML perspective because it needs to enclose a graphical implementation. It exists in order to provide accessors and helper functions which the QML implementation will need. @@ -91,8 +92,6 @@ QT_BEGIN_NAMESPACE */ QQuickFileDialog::QQuickFileDialog(QObject *parent) : QQuickAbstractFileDialog(parent) - , m_implementation(0) - , m_dialogWindow(0) { } @@ -117,51 +116,6 @@ QList QQuickFileDialog::fileUrls() This property holds whether the dialog is visible. By default this is false. */ -void QQuickFileDialog::setVisible(bool v) -{ - if (m_visible == v) return; - m_visible = v; - // For a pure QML implementation, there is no helper. - // But m_implementation is probably either an Item or a Window at this point. - if (!m_dialogWindow) { - m_dialogWindow = qobject_cast(m_implementation); - if (!m_dialogWindow) { - QQuickItem *dlgItem = qobject_cast(m_implementation); - if (dlgItem) { - m_dialogWindow = dlgItem->window(); - // An Item-based dialog implementation doesn't come with a window, so - // we have to instantiate one iff the platform allows it. - if (!m_dialogWindow && QGuiApplicationPrivate::platformIntegration()-> - hasCapability(QPlatformIntegration::MultipleWindows)) { - QQuickWindow *win = new QQuickWindow; - m_dialogWindow = win; - dlgItem->setParentItem(win->contentItem()); - m_dialogWindow->setMinimumSize(QSize(dlgItem->width(), dlgItem->height())); - } - - QQuickItem *parentItem = qobject_cast(parent()); - // qDebug() << "item implementation" << dlgItem << "has window" << m_dialogWindow << "and parent" << parentItem; - - // If the platform does not support multiple windows, but the dialog is - // implemented as an Item, then just reparent it and make it visible. - // TODO QTBUG-29818: put it into a fake Item-based window, when we have a reusable self-decorated one. - if (parentItem && !m_dialogWindow) - dlgItem->setParentItem(parentItem); - } - } - if (m_dialogWindow) - connect(m_dialogWindow, SIGNAL(visibleChanged(bool)), this, SLOT(visibleChanged(bool))); - } - if (m_dialogWindow) { - if (v) { - m_dialogWindow->setTransientParent(parentWindow()); - m_dialogWindow->setTitle(title()); - m_dialogWindow->setModality(m_modality); - } - m_dialogWindow->setVisible(v); - } - emit visibilityChanged(); -} /*! \qmlproperty bool AbstractFileDialog::filePaths @@ -219,12 +173,5 @@ QUrl QQuickFileDialog::pathFolder(const QString &path) The QML object which implements the actual file dialog. Should be either a \l Window or an \l Item. */ -void QQuickFileDialog::setImplementation(QObject *obj) -{ - m_implementation = obj; - if (m_dialogWindow) - disconnect(this, SLOT(visibleChanged(bool))); - m_dialogWindow = 0; -} QT_END_NAMESPACE diff --git a/src/imports/dialogs/qquickfiledialog_p.h b/src/imports/dialogs/qquickfiledialog_p.h index 0a660b316b..93e11f9e3e 100644 --- a/src/imports/dialogs/qquickfiledialog_p.h +++ b/src/imports/dialogs/qquickfiledialog_p.h @@ -3,7 +3,7 @@ ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** -** This file is part of the QtQml module of the Qt Toolkit. +** This file is part of the QtQuick.Dialogs module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage @@ -60,21 +60,17 @@ QT_BEGIN_NAMESPACE class QQuickFileDialog : public QQuickAbstractFileDialog { Q_OBJECT - Q_PROPERTY(QObject* implementation READ implementation WRITE setImplementation DESIGNABLE false) + Q_PROPERTY(QObject* implementation READ qmlImplementation WRITE setQmlImplementation DESIGNABLE false) Q_CLASSINFO("DefaultProperty", "implementation") // AbstractFileDialog in QML can have only one child public: explicit QQuickFileDialog(QObject *parent = 0); ~QQuickFileDialog(); - QObject* implementation() { return m_implementation; } virtual QList fileUrls(); signals: public Q_SLOTS: - void setImplementation(QObject* obj); - virtual void setVisible(bool v); - void clearSelection(); bool addSelection(QString path); @@ -85,8 +81,6 @@ protected: Q_INVOKABLE QUrl pathFolder(const QString &path); private: - QObject *m_implementation; - QWindow *m_dialogWindow; QStringList m_selections; Q_DISABLE_COPY(QQuickFileDialog) diff --git a/src/imports/dialogs/qquickplatformfiledialog.cpp b/src/imports/dialogs/qquickplatformfiledialog.cpp index fbc385f61d..d767f65499 100644 --- a/src/imports/dialogs/qquickplatformfiledialog.cpp +++ b/src/imports/dialogs/qquickplatformfiledialog.cpp @@ -3,7 +3,7 @@ ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** -** This file is part of the QtQml module of the Qt Toolkit. +** This file is part of the QtQuick.Dialogs module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage @@ -55,7 +55,7 @@ QT_BEGIN_NAMESPACE \inqmlmodule QtQuick.Dialogs 1 \ingroup qtquick-visual \brief Dialog component for choosing files from a local filesystem. - \since 5.1 + \since Qt 5.1 FileDialog provides a basic file chooser: it allows the user to select existing files and/or directories, or create new filenames. The dialog is @@ -66,7 +66,7 @@ QT_BEGIN_NAMESPACE chooses a file: \qml - import QtQuick 2.0 + import QtQuick 2.1 import QtQuick.Dialogs 1.0 FileDialog { diff --git a/src/imports/dialogs/qquickplatformfiledialog_p.h b/src/imports/dialogs/qquickplatformfiledialog_p.h index 3559543319..5431836271 100644 --- a/src/imports/dialogs/qquickplatformfiledialog_p.h +++ b/src/imports/dialogs/qquickplatformfiledialog_p.h @@ -3,7 +3,7 @@ ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** -** This file is part of the QtQml module of the Qt Toolkit. +** This file is part of the QtQuick.Dialogs module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage diff --git a/src/imports/widgets/widgets.pro b/src/imports/widgets/widgets.pro index 57281178a0..7de5aa6c5a 100644 --- a/src/imports/widgets/widgets.pro +++ b/src/imports/widgets/widgets.pro @@ -4,13 +4,15 @@ TARGETPATH = QtQuick/PrivateWidgets IMPORT_VERSION = 1.0 SOURCES += \ - widgetsplugin.cpp \ qquickqfiledialog.cpp \ - ../dialogs/qquickabstractfiledialog.cpp + ../dialogs/qquickabstractfiledialog.cpp \ + ../dialogs/qquickabstractdialog.cpp \ + widgetsplugin.cpp HEADERS += \ qquickqfiledialog_p.h \ - ../dialogs/qquickabstractfiledialog_p.h + ../dialogs/qquickabstractfiledialog_p.h \ + ../dialogs/qquickabstractdialog_p.h QT += quick-private gui-private core-private qml-private v8-private widgets -- cgit v1.2.3 From 10c9f850c1654c893e4784c4faaefb100830d7bd Mon Sep 17 00:00:00 2001 From: Oswald Buddenhagen Date: Thu, 7 Mar 2013 16:31:03 +0100 Subject: improve dependencies of qmlparser test first, this doesn't need gui. second, use qml instead of qmldevtools - there is no need to use the static library here, and after bootstrapping it would pose a real problem (testlib is not bootstrapped). on the downside, this makes the test rely on private_tests. Change-Id: Ic550fa05dcd0f3ba333850640a5b7e3fdd47d905 Reviewed-by: Oswald Buddenhagen --- tests/auto/qml/qml.pro | 2 +- tests/auto/qml/qqmlparser/qqmlparser.pro | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/auto/qml/qml.pro b/tests/auto/qml/qml.pro index e278d29162..ede7689a0c 100644 --- a/tests/auto/qml/qml.pro +++ b/tests/auto/qml/qml.pro @@ -23,7 +23,6 @@ PUBLICTESTS += \ qqmlqt \ qqmltranslation \ qqmlxmlhttprequest \ - qqmlparser \ qtqmlmodules \ qquickfolderlistmodel @@ -48,6 +47,7 @@ PRIVATETESTS += \ qqmllistcompositor \ qqmllistmodel \ qqmllistmodelworkerscript \ + qqmlparser \ qquickworkerscript \ qqmlbundle \ qrcqml \ diff --git a/tests/auto/qml/qqmlparser/qqmlparser.pro b/tests/auto/qml/qqmlparser/qqmlparser.pro index f181a876e8..c666b71d6c 100644 --- a/tests/auto/qml/qqmlparser/qqmlparser.pro +++ b/tests/auto/qml/qqmlparser/qqmlparser.pro @@ -1,6 +1,6 @@ CONFIG += testcase TARGET = tst_qqmlparser -QT += qmldevtools-private testlib gui-private +QT += qml-private testlib macx:CONFIG -= app_bundle SOURCES += tst_qqmlparser.cpp -- cgit v1.2.3 From d0af156930f80c7133b0190765d21b12a3d4a76a Mon Sep 17 00:00:00 2001 From: Oswald Buddenhagen Date: Thu, 1 Nov 2012 20:05:49 +0100 Subject: optionally bootstrap QmlDevTools and qmlmin QmlDevTools is really meant for host tools (the two only ones using this being qmlmin in this repo and lupdate in qttools). qmake magic will take care of bootstrapping them while x-building. Change-Id: I29d921af483659d5455be0ad080dc1a88540c036 Reviewed-by: Joerg Bornemann --- src/qmldevtools/qmldevtools.pro | 9 ++++++++- tests/auto/headersclean/headersclean.pro | 2 +- tests/auto/qmldevtools/compile/compile.pro | 9 ++++++--- tests/auto/qmldevtools/compile/tst_compile.cpp | 15 +++------------ tools/qmlmin/qmlmin.pro | 1 + 5 files changed, 19 insertions(+), 17 deletions(-) diff --git a/src/qmldevtools/qmldevtools.pro b/src/qmldevtools/qmldevtools.pro index 3da35264f9..a65ef94da9 100644 --- a/src/qmldevtools/qmldevtools.pro +++ b/src/qmldevtools/qmldevtools.pro @@ -1,6 +1,13 @@ +option(host_build) TARGET = QtQmlDevTools QT = core -CONFIG += static internal_module +CONFIG += static no_module_headers internal_module + +MODULE_PRIVATE_INCLUDES = \ + \$\$QT_MODULE_INCLUDE_BASE \ + \$\$QT_MODULE_INCLUDE_BASE/QtQml \ + \$\$QT_MODULE_INCLUDE_BASE/QtQml/$$QT.qml.VERSION \ + \$\$QT_MODULE_INCLUDE_BASE/QtQml/$$QT.qml.VERSION/QtQml load(qt_module) diff --git a/tests/auto/headersclean/headersclean.pro b/tests/auto/headersclean/headersclean.pro index 4457fe1c38..2698d67124 100644 --- a/tests/auto/headersclean/headersclean.pro +++ b/tests/auto/headersclean/headersclean.pro @@ -1,2 +1,2 @@ -QT = qml quick qmltest qmldevtools +QT = qml quick qmltest load(qt_headersclean) diff --git a/tests/auto/qmldevtools/compile/compile.pro b/tests/auto/qmldevtools/compile/compile.pro index ffe0778763..1c65daf909 100644 --- a/tests/auto/qmldevtools/compile/compile.pro +++ b/tests/auto/qmldevtools/compile/compile.pro @@ -1,9 +1,12 @@ -CONFIG += testcase +option(host_build) TARGET = tst_compile -QT = core qmldevtools-private testlib +force_bootstrap: \ + QT = bootstrap-private +else: \ + QT = core +QT += qmldevtools-private macx:CONFIG -= app_bundle SOURCES += tst_compile.cpp -CONFIG += parallel_test DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 diff --git a/tests/auto/qmldevtools/compile/tst_compile.cpp b/tests/auto/qmldevtools/compile/tst_compile.cpp index ad63922763..6b13d1b4bb 100644 --- a/tests/auto/qmldevtools/compile/tst_compile.cpp +++ b/tests/auto/qmldevtools/compile/tst_compile.cpp @@ -38,7 +38,6 @@ ** $QT_END_LICENSE$ ** ****************************************************************************/ -#include #include #include @@ -46,17 +45,9 @@ #include #include -class tst_compile : public QObject +int main() { - Q_OBJECT -public: - tst_compile() { } - -private slots: // Nothing - this test just makes sure that the QmlDevTools headers // are present, and that we can link against the library. -}; - -QTEST_MAIN(tst_compile) - -#include "tst_compile.moc" + return 0; +} diff --git a/tools/qmlmin/qmlmin.pro b/tools/qmlmin/qmlmin.pro index c7da0ebe4e..2cbf196863 100644 --- a/tools/qmlmin/qmlmin.pro +++ b/tools/qmlmin/qmlmin.pro @@ -1,3 +1,4 @@ +option(host_build) QT = core qmldevtools-private SOURCES += main.cpp -- cgit v1.2.3 From b7032bed6799c72ef8d89e5763586349b6991b93 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Wed, 13 Mar 2013 18:31:57 +0100 Subject: QQuickLoader: fix the recursion guard for size updates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task-number: QTBUG-30183 Change-Id: Ic8720e1e35bf2f349d74d2021dd202849da67852 Reviewed-by: Jan Arve Sæther Reviewed-by: Caroline Chao Reviewed-by: Jens Bache-Wiig --- src/quick/items/qquickloader.cpp | 9 ++++++--- tests/auto/quick/qquickloader/data/QTBUG_30183.qml | 12 ++++++++++++ tests/auto/quick/qquickloader/tst_qquickloader.cpp | 17 +++++++++++++++++ 3 files changed, 35 insertions(+), 3 deletions(-) create mode 100644 tests/auto/quick/qquickloader/data/QTBUG_30183.qml diff --git a/src/quick/items/qquickloader.cpp b/src/quick/items/qquickloader.cpp index 0434c2af41..0d14f3e266 100644 --- a/src/quick/items/qquickloader.cpp +++ b/src/quick/items/qquickloader.cpp @@ -887,16 +887,19 @@ void QQuickLoader::setAsynchronous(bool a) void QQuickLoaderPrivate::_q_updateSize(bool loaderGeometryChanged) { Q_Q(QQuickLoader); - if (!item || updatingSize) + if (!item) return; - updatingSize = true; - if (loaderGeometryChanged && q->widthValid()) item->setWidth(q->width()); if (loaderGeometryChanged && q->heightValid()) item->setHeight(q->height()); + if (updatingSize) + return; + + updatingSize = true; + q->setImplicitSize(getImplicitWidth(), getImplicitHeight()); updatingSize = false; diff --git a/tests/auto/quick/qquickloader/data/QTBUG_30183.qml b/tests/auto/quick/qquickloader/data/QTBUG_30183.qml new file mode 100644 index 0000000000..1f626d969f --- /dev/null +++ b/tests/auto/quick/qquickloader/data/QTBUG_30183.qml @@ -0,0 +1,12 @@ +import QtQuick 2.0 + +Loader { + width: implicitWidth + height: implicitHeight + + sourceComponent: Rectangle { + color: "green" + implicitWidth: 240 + implicitHeight: 120 + } +} diff --git a/tests/auto/quick/qquickloader/tst_qquickloader.cpp b/tests/auto/quick/qquickloader/tst_qquickloader.cpp index d01e8aae52..50ded4d95a 100644 --- a/tests/auto/quick/qquickloader/tst_qquickloader.cpp +++ b/tests/auto/quick/qquickloader/tst_qquickloader.cpp @@ -126,6 +126,7 @@ private slots: void parented(); void sizeBound(); + void QTBUG_30183(); private: QQmlEngine engine; @@ -1126,6 +1127,22 @@ void tst_QQuickLoader::sizeBound() delete root; } +void tst_QQuickLoader::QTBUG_30183() +{ + QQmlComponent component(&engine, testFileUrl("/QTBUG_30183.qml")); + QQuickLoader *loader = qobject_cast(component.create()); + QVERIFY(loader != 0); + QCOMPARE(loader->width(), 240.0); + QCOMPARE(loader->height(), 120.0); + + // the loaded item must follow the size + QQuickItem *rect = qobject_cast(loader->item()); + QVERIFY(rect); + QCOMPARE(rect->width(), 240.0); + QCOMPARE(rect->height(), 120.0); + + delete loader; +} QTEST_MAIN(tst_QQuickLoader) -- cgit v1.2.3 From bb803baa360f24f9a01e1cb1652962476724dd2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20R=C3=B8dal?= Date: Mon, 18 Mar 2013 09:12:43 +0100 Subject: Fixed invalid GLSL in openglunderqml example. First argument to pow is not allowed to be negative. Task-number: QTBUG-30191 Change-Id: Iac6c25d84e26777564d6741690aab552a0bb513e Reviewed-by: Gunnar Sletta --- examples/quick/scenegraph/openglunderqml/squircle.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/quick/scenegraph/openglunderqml/squircle.cpp b/examples/quick/scenegraph/openglunderqml/squircle.cpp index 8e34144af0..84509fb615 100644 --- a/examples/quick/scenegraph/openglunderqml/squircle.cpp +++ b/examples/quick/scenegraph/openglunderqml/squircle.cpp @@ -108,7 +108,7 @@ void Squircle::paint() "uniform lowp float t;" "varying highp vec2 coords;" "void main() {" - " lowp float i = 1. - (pow(coords.x, 4.) + pow(coords.y, 4.));" + " lowp float i = 1. - (pow(abs(coords.x), 4.) + pow(abs(coords.y), 4.));" " i = smoothstep(t - 0.8, t + 0.8, i);" " i = floor(i * 20.) / 20.;" " gl_FragColor = vec4(coords * .5 + .5, i, i);" -- cgit v1.2.3 From 7fded5040f56b8def7a93fcce03f93f06bcc6bc4 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Mon, 11 Mar 2013 11:42:00 +0100 Subject: Cosmetic changes in DefaultFileDialog and dialog shared controls Implementation of the button for QML-based dialogs matches the one for QML examples. The button and the text field use system palette colors, as does the file dialog. Change-Id: Ibf45d57bdab8799ae6aa69ba543c0e05c55b01d3 Reviewed-by: Jens Bache-Wiig --- src/imports/dialogs/DefaultFileDialog.qml | 19 ++++----- src/imports/dialogs/dialogs.pro | 2 - src/imports/dialogs/images/titlebar.png | Bin 1436 -> 0 bytes src/imports/dialogs/images/titlebar.sci | 5 --- src/imports/dialogs/qml/Button.qml | 64 +++++++++++++++--------------- src/imports/dialogs/qml/TextField.qml | 11 +---- 6 files changed, 40 insertions(+), 61 deletions(-) delete mode 100644 src/imports/dialogs/images/titlebar.png delete mode 100644 src/imports/dialogs/images/titlebar.sci diff --git a/src/imports/dialogs/DefaultFileDialog.qml b/src/imports/dialogs/DefaultFileDialog.qml index 9e05ace7b5..5542e59cb5 100644 --- a/src/imports/dialogs/DefaultFileDialog.qml +++ b/src/imports/dialogs/DefaultFileDialog.qml @@ -213,7 +213,7 @@ AbstractFileDialog { delegate: folderDelegate highlight: Rectangle { color: "transparent" - border.color: palette.midlight + border.color: Qt.darker(palette.window, 1.3) } highlightMoveDuration: 0 highlightMoveVelocity: -1 @@ -257,11 +257,10 @@ AbstractFileDialog { id: titleBar width: parent.width height: currentPathField.height * 1.5 - BorderImage { - source: "images/titlebar.sci" + Rectangle { anchors.fill: parent - anchors.topMargin: -7 - anchors.bottomMargin: -7 + color: Qt.darker(palette.window, 1.1) + border.color: Qt.darker(palette.window, 1.3) } Rectangle { id: upButton @@ -291,7 +290,7 @@ AbstractFileDialog { anchors.left: parent.left; anchors.right: parent.right; anchors.verticalCenter: parent.verticalCenter anchors.leftMargin: textX; anchors.rightMargin: 4 text: root.urlToPath(view.model.folder) - color: "white" + color: palette.text elide: Text.ElideLeft; horizontalAlignment: Text.AlignRight; verticalAlignment: Text.AlignVCenter MouseArea { anchors.fill: parent @@ -320,12 +319,8 @@ AbstractFileDialog { width: parent.width height: buttonRow.height + buttonRow.spacing * 2 anchors.bottom: parent.bottom - gradient: Gradient { - GradientStop { position: 0.0; color: palette.dark } - GradientStop { position: 0.3; color: palette.mid } - GradientStop { position: 0.85; color: palette.mid } - GradientStop { position: 1.0; color: palette.light } - } + color: Qt.darker(palette.window, 1.1) + border.color: Qt.darker(palette.window, 1.3) Row { id: buttonRow diff --git a/src/imports/dialogs/dialogs.pro b/src/imports/dialogs/dialogs.pro index 784517f73e..ec425c1b4f 100644 --- a/src/imports/dialogs/dialogs.pro +++ b/src/imports/dialogs/dialogs.pro @@ -23,8 +23,6 @@ QML_FILES += \ qml/TextField.qml \ qml/qmldir \ images/folder.png \ - images/titlebar.png \ - images/titlebar.sci \ images/up.png QT += quick-private gui-private core-private diff --git a/src/imports/dialogs/images/titlebar.png b/src/imports/dialogs/images/titlebar.png deleted file mode 100644 index 51c90082d0..0000000000 Binary files a/src/imports/dialogs/images/titlebar.png and /dev/null differ diff --git a/src/imports/dialogs/images/titlebar.sci b/src/imports/dialogs/images/titlebar.sci deleted file mode 100644 index 0418d94cd6..0000000000 --- a/src/imports/dialogs/images/titlebar.sci +++ /dev/null @@ -1,5 +0,0 @@ -border.left: 10 -border.top: 12 -border.bottom: 12 -border.right: 10 -source: titlebar.png diff --git a/src/imports/dialogs/qml/Button.qml b/src/imports/dialogs/qml/Button.qml index 936c72c876..491dc2e251 100644 --- a/src/imports/dialogs/qml/Button.qml +++ b/src/imports/dialogs/qml/Button.qml @@ -41,49 +41,47 @@ import QtQuick 2.1 Item { - height: label.implicitHeight * 2 - width: Math.max(label.implicitWidth * 1.2, height * 2.5); - anchors.verticalCenter: parent.verticalCenter - property alias text: label.text - property string tooltip + id: container + + property alias text: buttonLabel.text + property alias label: buttonLabel signal clicked + property alias containsMouse: mouseArea.containsMouse + property alias pressed: mouseArea.pressed + implicitHeight: buttonLabel.implicitHeight + implicitWidth: buttonLabel.implicitWidth + height: buttonLabel.implicitHeight + 12 + width: Math.max(80, implicitWidth + 8) + SystemPalette { id: palette } + Rectangle { - antialiasing: true - border.color: mouseArea.pressed ? palette.highlight : palette.light - color: "transparent" - anchors.fill: parent - anchors.rightMargin: 1 - anchors.bottomMargin: 1 - radius: 3 - } - Rectangle { - border.color: palette.dark + id: frame anchors.fill: parent - anchors.leftMargin: 1 - anchors.topMargin: 1 - radius: 3 - } - Rectangle { + color: palette.button gradient: Gradient { - GradientStop { position: 0.0; color: mouseArea.pressed ? palette.dark : palette.light } - GradientStop { position: 0.2; color: palette.button } - GradientStop { position: 0.8; color: palette.button } - GradientStop { position: 1.0; color: mouseArea.pressed ? palette.light : palette.dark } + GradientStop { position: 0.0; color: mouseArea.pressed ? Qt.darker(palette.button, 1.3) : palette.button } + GradientStop { position: 1.0; color: Qt.darker(palette.button, 1.3) } } - anchors.fill: parent - anchors.margins: 1 - radius: 3 - } - Text { - id: label - anchors.centerIn: parent - color: palette.buttonText + antialiasing: true + radius: 5 + border.color: Qt.darker(palette.button, 1.5) + border.width: 1 } MouseArea { id: mouseArea anchors.fill: parent - onClicked: parent.clicked() + onClicked: container.clicked() + hoverEnabled: true + } + + Text { + id: buttonLabel + width: parent.width + horizontalAlignment: Text.Center + text: container.text + color: palette.buttonText + anchors.verticalCenter: parent.verticalCenter } } diff --git a/src/imports/dialogs/qml/TextField.qml b/src/imports/dialogs/qml/TextField.qml index da93239fd5..89487e82ca 100644 --- a/src/imports/dialogs/qml/TextField.qml +++ b/src/imports/dialogs/qml/TextField.qml @@ -53,16 +53,9 @@ Item { Rectangle { id: rect anchors.fill: parent - anchors.leftMargin: -radius - border.color: palette.light radius: height / 4 - antialiasing: true - gradient: Gradient { - GradientStop { position: 0.0; color: palette.dark } - GradientStop { position: 0.2; color: palette.button } - GradientStop { position: 0.8; color: palette.button } - GradientStop { position: 1.0; color: palette.light } - } + color: palette.button + border.color: Qt.darker(palette.button, 1.5) } TextInput { -- cgit v1.2.3 From 69186a08270a9c585b19a5140a5fcf52e2bf5bc3 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Mon, 11 Mar 2013 11:05:30 +0100 Subject: Add ColorDialog to QtQuick.Dialogs As with FileDialog, it tries QPA, then QColorDialog, and falls back to a QML implementation (which is also provided here) if neither type of native dialog is available. Change-Id: I384928e1f7322bb6b867d4618d07c88c70e3cbfe Reviewed-by: Jens Bache-Wiig --- examples/quick/dialogs/ColorDialogs.qml | 109 ++++++++ src/imports/dialogs/DefaultColorDialog.qml | 316 ++++++++++++++++++++++ src/imports/dialogs/WidgetColorDialog.qml | 44 +++ src/imports/dialogs/dialogs.pro | 14 + src/imports/dialogs/images/checkers.png | Bin 0 -> 149 bytes src/imports/dialogs/images/copy.png | Bin 0 -> 1338 bytes src/imports/dialogs/images/crosshairs.png | Bin 0 -> 876 bytes src/imports/dialogs/images/slider_handle.png | Bin 0 -> 1551 bytes src/imports/dialogs/images/sunken_frame.png | Bin 0 -> 623 bytes src/imports/dialogs/plugin.cpp | 12 + src/imports/dialogs/qml/ColorSlider.qml | 138 ++++++++++ src/imports/dialogs/qml/TextField.qml | 13 +- src/imports/dialogs/qml/qmldir | 1 + src/imports/dialogs/qquickabstractcolordialog.cpp | 115 ++++++++ src/imports/dialogs/qquickabstractcolordialog_p.h | 100 +++++++ src/imports/dialogs/qquickcolordialog.cpp | 125 +++++++++ src/imports/dialogs/qquickcolordialog_p.h | 80 ++++++ src/imports/dialogs/qquickplatformcolordialog.cpp | 237 ++++++++++++++++ src/imports/dialogs/qquickplatformcolordialog_p.h | 78 ++++++ src/imports/widgets/qquickqcolordialog.cpp | 172 ++++++++++++ src/imports/widgets/qquickqcolordialog_p.h | 78 ++++++ src/imports/widgets/widgets.pro | 4 + src/imports/widgets/widgetsplugin.cpp | 2 + 23 files changed, 1637 insertions(+), 1 deletion(-) create mode 100644 examples/quick/dialogs/ColorDialogs.qml create mode 100644 src/imports/dialogs/DefaultColorDialog.qml create mode 100644 src/imports/dialogs/WidgetColorDialog.qml create mode 100644 src/imports/dialogs/images/checkers.png create mode 100644 src/imports/dialogs/images/copy.png create mode 100644 src/imports/dialogs/images/crosshairs.png create mode 100644 src/imports/dialogs/images/slider_handle.png create mode 100644 src/imports/dialogs/images/sunken_frame.png create mode 100755 src/imports/dialogs/qml/ColorSlider.qml create mode 100644 src/imports/dialogs/qquickabstractcolordialog.cpp create mode 100644 src/imports/dialogs/qquickabstractcolordialog_p.h create mode 100644 src/imports/dialogs/qquickcolordialog.cpp create mode 100644 src/imports/dialogs/qquickcolordialog_p.h create mode 100644 src/imports/dialogs/qquickplatformcolordialog.cpp create mode 100644 src/imports/dialogs/qquickplatformcolordialog_p.h create mode 100644 src/imports/widgets/qquickqcolordialog.cpp create mode 100644 src/imports/widgets/qquickqcolordialog_p.h diff --git a/examples/quick/dialogs/ColorDialogs.qml b/examples/quick/dialogs/ColorDialogs.qml new file mode 100644 index 0000000000..7817c8edf8 --- /dev/null +++ b/examples/quick/dialogs/ColorDialogs.qml @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtQuick.Dialogs 1.0 +import "../shared" + +Rectangle { + width: 320 + height: 200 + color: palette.window + + SystemPalette { id: palette } + + ColorDialog { + id: colorDialog + visible: colorDialogVisible.checked + modality: colorDialogModal.checked ? Qt.WindowModal : Qt.NonModal + title: "Choose a color" + color: "green" + showAlphaChannel: colorDialogAlpha.checked + onAccepted: { console.log("Accepted: " + color) } + onRejected: { console.log("Rejected") } + } + + Column { + anchors.fill: parent + anchors.margins: 8 + spacing: 8 + Text { + font.bold: true + text: "Color dialog properties:" + } + CheckBox { + id: colorDialogModal + text: "Modal" + checked: true + Binding on checked { value: colorDialog.modality != Qt.NonModal } + } + CheckBox { + id: colorDialogAlpha + text: "Show alpha channel" + Binding on checked { value: colorDialog.showAlphaChannel } + } + CheckBox { + id: colorDialogVisible + text: "Visible" + Binding on checked { value: colorDialog.visible } + } + Row { + id: colorRow + spacing: parent.spacing + height: colorLabel.implicitHeight * 2.0 + Rectangle { + color: colorDialog.color + height: parent.height + width: height * 2 + border.color: "black" + MouseArea { + anchors.fill: parent + onClicked: colorDialog.open() + } + } + Text { + id: colorLabel + color: palette.windowText + text: "current color: " + colorDialog.color + anchors.verticalCenter: parent.verticalCenter + } + } + } +} diff --git a/src/imports/dialogs/DefaultColorDialog.qml b/src/imports/dialogs/DefaultColorDialog.qml new file mode 100644 index 0000000000..7322aa18e7 --- /dev/null +++ b/src/imports/dialogs/DefaultColorDialog.qml @@ -0,0 +1,316 @@ +/***************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQuick.Dialogs module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +*****************************************************************************/ + +import QtQuick 2.1 +import QtQuick.Dialogs 1.0 +import "qml" + +AbstractColorDialog { + id: root + + Rectangle { + id: content + width: 320 + height: (usePaletteMap ? width : 0) + bottomMinHeight + color: palette.window + property real bottomMinHeight: sliders.height + buttonRow.height + outerSpacing * 3 + property real spacing: 8 + property real outerSpacing: 12 + property bool usePaletteMap: true + property bool inited: false + + SystemPalette { id: palette } + + Binding { + target: root + property: "color" + value: Qt.hsla(hueSlider.value, saturationSlider.value, lightnessSlider.value, alphaSlider.value) + } + + Item { + id: paletteFrame + visible: content.usePaletteMap + anchors { + top: parent.top + left: parent.left + right: parent.right + margins: content.outerSpacing + } + height: Math.min(content.height - content.bottomMinHeight, content.width - content.outerSpacing * 2) + + Image { + id: paletteMap + x: (parent.width - width) / 2 + width: height + height: parent.height + source: "images/checkers.png" + fillMode: Image.Tile + + // note we smoothscale the shader from a smaller version to improve performance + ShaderEffect { + id: map + width: 64 + height: 64 + opacity: alphaSlider.value + scale: paletteMap.width / width; + layer.enabled: true + layer.smooth: true + anchors.centerIn: parent + property real hue: hueSlider.value + + fragmentShader: " + varying mediump vec2 qt_TexCoord0; + uniform highp float qt_Opacity; + uniform highp float hue; + + highp float hueToIntensity(highp float v1, highp float v2, highp float h) { + h = fract(h); + if (h < 1.0 / 6.0) + return v1 + (v2 - v1) * 6.0 * h; + else if (h < 1.0 / 2.0) + return v2; + else if (h < 2.0 / 3.0) + return v1 + (v2 - v1) * 6.0 * (2.0 / 3.0 - h); + + return v1; + } + + highp vec3 HSLtoRGB(highp vec3 color) { + highp float h = color.x; + highp float l = color.z; + highp float s = color.y; + + if (s < 1.0 / 256.0) + return vec3(l, l, l); + + highp float v1; + highp float v2; + if (l < 0.5) + v2 = l * (1.0 + s); + else + v2 = (l + s) - (s * l); + + v1 = 2.0 * l - v2; + + highp float d = 1.0 / 3.0; + highp float r = hueToIntensity(v1, v2, h + d); + highp float g = hueToIntensity(v1, v2, h); + highp float b = hueToIntensity(v1, v2, h - d); + return vec3(r, g, b); + } + + void main() { + lowp vec4 c = vec4(1.0); + c.rgb = HSLtoRGB(vec3(hue, 1.0 - qt_TexCoord0.t, qt_TexCoord0.s)); + gl_FragColor = c * qt_Opacity; + } + " + } + + MouseArea { + id: mapMouseArea + anchors.fill: parent + onPositionChanged: { + if (pressed && containsMouse) { + var xx = Math.max(0, Math.min(mouse.x, parent.width)) + var yy = Math.max(0, Math.min(mouse.y, parent.height)) + saturationSlider.value = 1.0 - yy / parent.height + lightnessSlider.value = xx / parent.width + // TODO if we constrain the movement here, can avoid the containsMouse test + crosshairs.x = mouse.x - crosshairs.radius + crosshairs.y = mouse.y - crosshairs.radius + } + } + onPressed: positionChanged(mouse) + } + + Image { + id: crosshairs + property int radius: width / 2 // truncated to int + source: "images/crosshairs.png" + } + + BorderImage { + anchors.fill: parent + anchors.margins: -1 + anchors.leftMargin: -2 + source: "images/sunken_frame.png" + border.left: 8 + border.right: 8 + border.top: 8 + border.bottom: 8 + } + } + } + + Column { + id: sliders + anchors { + top: paletteFrame.bottom + left: parent.left + right: parent.right + margins: content.outerSpacing + } + spacing: content.spacing + + ColorSlider { + id: hueSlider + value: 0.5 + text: qsTr("Hue") + trackDelegate: Rectangle { + rotation: -90 + transformOrigin: Item.TopLeft + gradient: Gradient { + GradientStop {position: 0.000; color: Qt.rgba(1, 0, 0, 1)} + GradientStop {position: 0.167; color: Qt.rgba(1, 1, 0, 1)} + GradientStop {position: 0.333; color: Qt.rgba(0, 1, 0, 1)} + GradientStop {position: 0.500; color: Qt.rgba(0, 1, 1, 1)} + GradientStop {position: 0.667; color: Qt.rgba(0, 0, 1, 1)} + GradientStop {position: 0.833; color: Qt.rgba(1, 0, 1, 1)} + GradientStop {position: 1.000; color: Qt.rgba(1, 0, 0, 1)} + } + } + } + + ColorSlider { + id: saturationSlider + visible: !content.usePaletteMap + value: 0.5 + text: qsTr("Saturation") + trackDelegate: Rectangle { + rotation: -90 + transformOrigin: Item.TopLeft + gradient: Gradient { + GradientStop { position: 0; color: Qt.hsla(hueSlider.value, 0.0, lightnessSlider.value, 1.0) } + GradientStop { position: 1; color: Qt.hsla(hueSlider.value, 1.0, lightnessSlider.value, 1.0) } + } + } + } + + ColorSlider { + id: lightnessSlider + visible: !content.usePaletteMap + value: 0.5 + text: qsTr("Luminosity") + trackDelegate: Rectangle { + rotation: -90 + transformOrigin: Item.TopLeft + gradient: Gradient { + GradientStop { position: 0; color: "black" } + GradientStop { position: 0.5; color: Qt.hsla(hueSlider.value, saturationSlider.value, 0.5, 1.0) } + GradientStop { position: 1; color: "white" } + } + } + } + + ColorSlider { + id: alphaSlider + minimum: 0.0 + maximum: 1.0 + value: 1.0 + text: qsTr("Alpha") + visible: root.showAlphaChannel + trackDelegate: Item { + rotation: -90 + transformOrigin: Item.TopLeft + Image { + anchors {fill: parent} + source: "images/checkers.png" + fillMode: Image.TileVertically + } + Rectangle { + anchors.fill: parent + gradient: Gradient { + GradientStop { position: 0; color: "transparent" } + GradientStop { position: 1; color: Qt.hsla(hueSlider.value, + saturationSlider.value, + lightnessSlider.value, 1.0) } + } } + } + } + } + + Item { + id: buttonRow + height: buttonsOnly.height + width: parent.width + anchors { + left: parent.left + right: parent.right + bottom: content.bottom + margins: content.outerSpacing + } + Row { + spacing: content.spacing + TextField { + id: colorField + text: root.color + anchors.verticalCenter: parent.verticalCenter + onAccepted: root.color = text + Component.onCompleted: width = implicitWidth + 10 + } + Image { + id: copyIcon + anchors.verticalCenter: parent.verticalCenter + source: "images/copy.png" + MouseArea { + anchors.fill: parent + onClicked: colorField.copyAll() + } + } + } + Row { + id: buttonsOnly + spacing: content.spacing + anchors.right: parent.right + Button { + id: cancelButton + text: "Cancel" + onClicked: root.reject() + } + Button { + id: okButton + text: "OK" + onClicked: root.accept() + } + } + } + } +} diff --git a/src/imports/dialogs/WidgetColorDialog.qml b/src/imports/dialogs/WidgetColorDialog.qml new file mode 100644 index 0000000000..ed7c7ab77a --- /dev/null +++ b/src/imports/dialogs/WidgetColorDialog.qml @@ -0,0 +1,44 @@ +/***************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQuick.Dialogs module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +*****************************************************************************/ + +import QtQuick 2.1 +import QtQuick.PrivateWidgets 1.0 + +QtColorDialog { } diff --git a/src/imports/dialogs/dialogs.pro b/src/imports/dialogs/dialogs.pro index ec425c1b4f..b7704dadda 100644 --- a/src/imports/dialogs/dialogs.pro +++ b/src/imports/dialogs/dialogs.pro @@ -7,6 +7,9 @@ SOURCES += \ qquickabstractfiledialog.cpp \ qquickplatformfiledialog.cpp \ qquickfiledialog.cpp \ + qquickabstractcolordialog.cpp \ + qquickplatformcolordialog.cpp \ + qquickcolordialog.cpp \ qquickabstractdialog.cpp \ plugin.cpp @@ -14,14 +17,25 @@ HEADERS += \ qquickabstractfiledialog_p.h \ qquickplatformfiledialog_p.h \ qquickfiledialog_p.h \ + qquickabstractcolordialog_p.h \ + qquickplatformcolordialog_p.h \ + qquickcolordialog_p.h \ qquickabstractdialog_p.h QML_FILES += \ DefaultFileDialog.qml \ WidgetFileDialog.qml \ + DefaultColorDialog.qml \ + WidgetColorDialog.qml \ qml/Button.qml \ + qml/ColorSlider.qml \ qml/TextField.qml \ qml/qmldir \ + images/checkers.png \ + images/copy.png \ + images/crosshairs.png \ + images/slider_handle.png \ + images/sunken_frame.png \ images/folder.png \ images/up.png diff --git a/src/imports/dialogs/images/checkers.png b/src/imports/dialogs/images/checkers.png new file mode 100644 index 0000000000..458d33de9d Binary files /dev/null and b/src/imports/dialogs/images/checkers.png differ diff --git a/src/imports/dialogs/images/copy.png b/src/imports/dialogs/images/copy.png new file mode 100644 index 0000000000..2aeb28288f Binary files /dev/null and b/src/imports/dialogs/images/copy.png differ diff --git a/src/imports/dialogs/images/crosshairs.png b/src/imports/dialogs/images/crosshairs.png new file mode 100644 index 0000000000..9a61946eca Binary files /dev/null and b/src/imports/dialogs/images/crosshairs.png differ diff --git a/src/imports/dialogs/images/slider_handle.png b/src/imports/dialogs/images/slider_handle.png new file mode 100644 index 0000000000..e3b9654392 Binary files /dev/null and b/src/imports/dialogs/images/slider_handle.png differ diff --git a/src/imports/dialogs/images/sunken_frame.png b/src/imports/dialogs/images/sunken_frame.png new file mode 100644 index 0000000000..178c3092d2 Binary files /dev/null and b/src/imports/dialogs/images/sunken_frame.png differ diff --git a/src/imports/dialogs/plugin.cpp b/src/imports/dialogs/plugin.cpp index f7d72bf496..67653e5965 100644 --- a/src/imports/dialogs/plugin.cpp +++ b/src/imports/dialogs/plugin.cpp @@ -44,6 +44,9 @@ #include "qquickfiledialog_p.h" #include "qquickabstractfiledialog_p.h" #include "qquickplatformfiledialog_p.h" +#include "qquickcolordialog_p.h" +#include "qquickabstractcolordialog_p.h" +#include "qquickplatformcolordialog_p.h" #include //#define PURE_QML_ONLY @@ -95,6 +98,15 @@ public: #endif registerWidgetOrQmlImplementation(widgetsDir, "WidgetFileDialog.qml", qmlDir, "DefaultFileDialog.qml", "FileDialog", uri); + + // ColorDialog +#ifndef PURE_QML_ONLY + if (QGuiApplicationPrivate::platformTheme()->usePlatformNativeDialog(QPlatformTheme::ColorDialog)) + qmlRegisterType(uri, DIALOGS_MAJOR_MINOR, "ColorDialog"); + else +#endif + registerWidgetOrQmlImplementation(widgetsDir, "WidgetColorDialog.qml", + qmlDir, "DefaultColorDialog.qml", "ColorDialog", uri); } protected: diff --git a/src/imports/dialogs/qml/ColorSlider.qml b/src/imports/dialogs/qml/ColorSlider.qml new file mode 100755 index 0000000000..8fc9717380 --- /dev/null +++ b/src/imports/dialogs/qml/ColorSlider.qml @@ -0,0 +1,138 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Graphical Effects module. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.1 + +Item { + id: colorSlider + + property real value: 1 + property real maximum: 1 + property real minimum: 0 + property string text: "" + property bool pressed: mouseArea.pressed + property bool integer: false + property Component trackDelegate + property string handleSource: "../images/slider_handle.png" + + width: parent.width + height: handle.height + textText.implicitHeight + + function updatePos() { + if (maximum > minimum) { + var pos = (track.width - 10) * (value - minimum) / (maximum - minimum) + 5; + return Math.min(Math.max(pos, 5), track.width - 5) - 10; + } else { + return 5; + } + } + + SystemPalette { id: palette } + + Column { + id: column + width: parent.width + spacing: 12 + Text { + id: textText + anchors.horizontalCenter: parent.horizontalCenter + text: colorSlider.text + anchors.left: parent.left + color: palette.windowText + } + + Item { + id: track + height: 8 + anchors.left: parent.left + anchors.right: parent.right + + Loader { + sourceComponent: trackDelegate + width: parent.height + height: parent.width + y: width + } + + BorderImage { + source: "../images/sunken_frame.png" + border.left: 8 + border.right: 8 + border.top:8 + border.bottom: 8 + anchors.fill: track + anchors.margins: -1 + anchors.topMargin: -2 + anchors.leftMargin: -2 + } + + Image { + id: handle + anchors.verticalCenter: parent.verticalCenter + smooth: true + source: "../images/slider_handle.png" + x: updatePos() - 8 + z: 1 + } + + MouseArea { + id: mouseArea + anchors {left: parent.left; right: parent.right; verticalCenter: parent.verticalCenter} + height: handle.height + width: handle.width + preventStealing: true + + onPressed: { + var handleX = Math.max(0, Math.min(mouseX, mouseArea.width)) + var realValue = (maximum - minimum) * handleX / mouseArea.width + minimum; + value = colorSlider.integer ? Math.round(realValue) : realValue; + } + + onPositionChanged: { + if (pressed) { + var handleX = Math.max(0, Math.min(mouseX, mouseArea.width)) + var realValue = (maximum - minimum) * handleX / mouseArea.width + minimum; + value = colorSlider.integer ? Math.round(realValue) : realValue; + } + } + } + } + } +} diff --git a/src/imports/dialogs/qml/TextField.qml b/src/imports/dialogs/qml/TextField.qml index 89487e82ca..baa469caa9 100644 --- a/src/imports/dialogs/qml/TextField.qml +++ b/src/imports/dialogs/qml/TextField.qml @@ -45,11 +45,20 @@ Item { property alias textInput: textInput property alias text: textInput.text + property real implicitWidth: textInput.implicitWidth + rect.radius * 2 + property real implicitHeight: textInput.implicitHeight + rect.radius * 2 signal accepted signal downPressed + function copyAll() { + textInput.selectAll() + textInput.copy() + } + SystemPalette { id: palette } - height: textInput.implicitHeight + 4 + height: textInput.implicitHeight + 8 + clip: true + Rectangle { id: rect anchors.fill: parent @@ -62,6 +71,8 @@ Item { id: textInput color: palette.text anchors.fill: parent + anchors.leftMargin: rect.radius + anchors.rightMargin: rect.radius verticalAlignment: Text.AlignVCenter onAccepted: root.accepted() Keys.onDownPressed: root.downPressed() diff --git a/src/imports/dialogs/qml/qmldir b/src/imports/dialogs/qml/qmldir index 85575c738a..c475502cf1 100644 --- a/src/imports/dialogs/qml/qmldir +++ b/src/imports/dialogs/qml/qmldir @@ -1,2 +1,3 @@ Button 1.0 Button.qml +ColorSlider 1.0 ColorSlider.qml TextField 1.0 TextField.qml diff --git a/src/imports/dialogs/qquickabstractcolordialog.cpp b/src/imports/dialogs/qquickabstractcolordialog.cpp new file mode 100644 index 0000000000..9a9f3bc11b --- /dev/null +++ b/src/imports/dialogs/qquickabstractcolordialog.cpp @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQuick.Dialogs module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickabstractcolordialog_p.h" +#include "qquickitem.h" + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +QQuickAbstractColorDialog::QQuickAbstractColorDialog(QObject *parent) + : QQuickAbstractDialog(parent) + , m_dlgHelper(0) + , m_options(QSharedPointer(new QColorDialogOptions())) +{ + // On the Mac, modality doesn't work unless you call exec(). But this is a reasonable default anyway. + m_modality = Qt::NonModal; + connect(this, SIGNAL(accepted()), this, SIGNAL(selectionAccepted())); +} + +QQuickAbstractColorDialog::~QQuickAbstractColorDialog() +{ +} + +void QQuickAbstractColorDialog::setVisible(bool v) +{ + if (helper() && v) { + m_dlgHelper->setOptions(m_options); + } + QQuickAbstractDialog::setVisible(v); +} + +void QQuickAbstractColorDialog::setModality(Qt::WindowModality m) +{ +#ifdef Q_OS_MAC + // On the Mac, modality doesn't work unless you call exec() + m_modality = Qt::NonModal; + emit modalityChanged(); + return; +#endif + QQuickAbstractDialog::setModality(m); +} + +QString QQuickAbstractColorDialog::title() const +{ + return m_options->windowTitle(); +} + +bool QQuickAbstractColorDialog::showAlphaChannel() const +{ + return m_options->testOption(QColorDialogOptions::ShowAlphaChannel); +} + +void QQuickAbstractColorDialog::setTitle(QString t) +{ + if (m_options->windowTitle() == t) return; + m_options->setWindowTitle(t); + emit titleChanged(); +} + +void QQuickAbstractColorDialog::setColor(QColor arg) +{ + if (m_color != arg) { + m_color = arg; + emit colorChanged(); + } +} + +void QQuickAbstractColorDialog::setShowAlphaChannel(bool arg) +{ + m_options->setOption(QColorDialogOptions::ShowAlphaChannel, arg); + emit showAlphaChannelChanged(); +} + +QT_END_NAMESPACE diff --git a/src/imports/dialogs/qquickabstractcolordialog_p.h b/src/imports/dialogs/qquickabstractcolordialog_p.h new file mode 100644 index 0000000000..3301605c86 --- /dev/null +++ b/src/imports/dialogs/qquickabstractcolordialog_p.h @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQuick.Dialogs module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKABSTRACTCOLORDIALOG_P_H +#define QQUICKABSTRACTCOLORDIALOG_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include +#include "qquickabstractdialog_p.h" + +QT_BEGIN_NAMESPACE + +class QQuickAbstractColorDialog : public QQuickAbstractDialog +{ + Q_OBJECT + Q_PROPERTY(bool showAlphaChannel READ showAlphaChannel WRITE setShowAlphaChannel NOTIFY showAlphaChannelChanged) + Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) + +public: + QQuickAbstractColorDialog(QObject *parent = 0); + virtual ~QQuickAbstractColorDialog(); + + virtual QString title() const; + bool showAlphaChannel() const; + QColor color() const { return m_color; } + +public Q_SLOTS: + void setVisible(bool v); + void setModality(Qt::WindowModality m); + void setTitle(QString t); + void setColor(QColor arg); + void setShowAlphaChannel(bool arg); + +Q_SIGNALS: + void showAlphaChannelChanged(); + void colorChanged(); + void selectionAccepted(); + +protected: + QPlatformColorDialogHelper *m_dlgHelper; + QSharedPointer m_options; + QColor m_color; + + Q_DISABLE_COPY(QQuickAbstractColorDialog) +}; + +QT_END_NAMESPACE + +#endif // QQUICKABSTRACTCOLORDIALOG_P_H diff --git a/src/imports/dialogs/qquickcolordialog.cpp b/src/imports/dialogs/qquickcolordialog.cpp new file mode 100644 index 0000000000..39af99770e --- /dev/null +++ b/src/imports/dialogs/qquickcolordialog.cpp @@ -0,0 +1,125 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQuick.Dialogs module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickcolordialog_p.h" +#include +#include + +QT_BEGIN_NAMESPACE + +/*! + \qmltype AbstractColorDialog + \instantiates QQuickColorDialog + \inqmlmodule QtQuick.Dialogs 1 + \ingroup qtquick-visual + \brief API wrapper for QML file dialog implementations + \since 5.1 + \internal + + AbstractColorDialog provides only the API for implementing a color dialog. + The implementation (e.g. a Window or preferably an Item, in case it is + shown on a device that doesn't support multiple windows) can be provided as + \l implementation, which is the default property (the only allowed child + element). +*/ + +/*! + \qmlsignal QtQuick::Dialogs::AbstractColorDialog::accepted + + The \a accepted signal is emitted by \l accept(). +*/ + +/*! + \qmlsignal QtQuick::Dialogs::AbstractColorDialog::rejected + + The \a accepted signal is emitted by \l reject(). +*/ + +/*! + \class QQuickColorDialog + \inmodule QtQuick.Dialogs + \internal + + The QQuickColorDialog class is a concrete subclass of + \l QQuickAbstractColorDialog, but it is abstract from the QML perspective + because it needs to enclose a graphical implementation. It exists in order + to provide accessors and helper functions which the QML implementation will + need. + + \since 5.1 +*/ + +/*! + Constructs a file dialog wrapper with parent window \a parent. +*/ +QQuickColorDialog::QQuickColorDialog(QObject *parent) + : QQuickAbstractColorDialog(parent) +{ +} + + +/*! + Destroys the file dialog wrapper. +*/ +QQuickColorDialog::~QQuickColorDialog() +{ +} + +/*! + \qmlproperty bool AbstractColorDialog::visible + + This property holds whether the dialog is visible. By default this is false. +*/ + +/*! + \qmlproperty bool AbstractColorDialog::filePaths + + A list of files to be populated as the user chooses. +*/ + +/*! + \qmlproperty QObject AbstractColorDialog::implementation + + The QML object which implements the actual file dialog. Should be either a + \l Window or an \l Item. +*/ + +QT_END_NAMESPACE diff --git a/src/imports/dialogs/qquickcolordialog_p.h b/src/imports/dialogs/qquickcolordialog_p.h new file mode 100644 index 0000000000..ff6953fc0f --- /dev/null +++ b/src/imports/dialogs/qquickcolordialog_p.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQuick.Dialogs module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKCOLORDIALOG_P_H +#define QQUICKCOLORDIALOG_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qquickabstractcolordialog_p.h" + +QT_BEGIN_NAMESPACE + +class QQuickColorDialog : public QQuickAbstractColorDialog +{ + Q_OBJECT + Q_PROPERTY(QObject* implementation READ qmlImplementation WRITE setQmlImplementation DESIGNABLE false) + Q_CLASSINFO("DefaultProperty", "implementation") // AbstractColorDialog in QML can have only one child + +public: + explicit QQuickColorDialog(QObject *parent = 0); + ~QQuickColorDialog(); + +protected: + virtual QPlatformColorDialogHelper *helper() { return 0; } + + Q_DISABLE_COPY(QQuickColorDialog) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickColorDialog *) + +#endif // QQUICKCOLORDIALOG_P_H diff --git a/src/imports/dialogs/qquickplatformcolordialog.cpp b/src/imports/dialogs/qquickplatformcolordialog.cpp new file mode 100644 index 0000000000..491a2e687c --- /dev/null +++ b/src/imports/dialogs/qquickplatformcolordialog.cpp @@ -0,0 +1,237 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQuick.Dialogs module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickplatformcolordialog_p.h" +#include "qquickitem.h" + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +/*! + \qmltype ColorDialog + \instantiates QQuickPlatformColorDialog + \inqmlmodule QtQuick.Dialogs 1 + \ingroup qtquick-visual + \brief Dialog component for choosing a color. + \since Qt 5.1 + + ColorDialog allows the user to select a color. The dialog is initially + invisible. You need to set the properties as desired first, then set + \l visible to true or call \l open(). + + Here is a minimal example to open a color dialog and exit after the user + chooses a color: + + \qml + import QtQuick 2.1 + import QtQuick.Dialogs 1.0 + + ColorDialog { + id: colorDialog + title: "Please choose a color" + onAccepted: { + console.log("You chose: " + colorDialog.color) + Qt.quit() + } + onRejected: { + console.log("Canceled") + Qt.quit() + } + Component.onCompleted: visible = true + } + \endqml + + A ColorDialog window is automatically transient for its parent window. So + whether you declare the dialog inside an \l Item or inside a \l Window, the + dialog will appear centered over the window containing the item, or over + the Window that you declared. + + The implementation of ColorDialog will be a platform color dialog if + possible. If that isn't possible, then it will try to instantiate a + \l QColorDialog. If that also isn't possible, then it will fall back to a QML + implementation, DefaultColorDialog.qml. In that case you can customize the + appearance by editing this file. DefaultColorDialog.qml contains a Rectangle + to hold the dialog's contents, because certain embedded systems do not + support multiple top-level windows. When the dialog becomes visible, it + will automatically be wrapped in a Window if possible, or simply reparented + on top of the main window if there can only be one window. +*/ + +/*! + \qmlsignal QtQuick::Dialogs::ColorDialog::accepted + + The \a accepted signal is emitted when the user has finished using the + dialog. You can then inspect the \a color property to get the selection. + + Example: + + \qml + ColorDialog { + onAccepted: { console.log("Selected color: " + color) } + } + \endqml +*/ + +/*! + \qmlsignal QtQuick::Dialogs::ColorDialog::rejected + + The \a rejected signal is emitted when the user has dismissed the dialog, + either by closing the dialog window or by pressing the Cancel button. +*/ + +/*! + \class QQuickPlatformColorDialog + \inmodule QtQuick.Dialogs + \internal + + \brief The QQuickPlatformColorDialog class provides a color dialog + + The dialog is implemented via the QPlatformColorDialogHelper when possible; + otherwise it falls back to a QColorDialog or a QML implementation. + + \since 5.1 +*/ + +/*! + Constructs a color dialog with parent window \a parent. +*/ +QQuickPlatformColorDialog::QQuickPlatformColorDialog(QObject *parent) : + QQuickAbstractColorDialog(parent) +{ +} + +/*! + Destroys the color dialog. +*/ +QQuickPlatformColorDialog::~QQuickPlatformColorDialog() +{ + if (m_dlgHelper) + m_dlgHelper->hide(); + delete m_dlgHelper; +} + +QPlatformColorDialogHelper *QQuickPlatformColorDialog::helper() +{ + QQuickItem *parentItem = qobject_cast(parent()); + if (parentItem) + m_parentWindow = parentItem->window(); + + if ( !m_dlgHelper && QGuiApplicationPrivate::platformTheme()-> + usePlatformNativeDialog(QPlatformTheme::ColorDialog) ) { + m_dlgHelper = static_cast(QGuiApplicationPrivate::platformTheme() + ->createPlatformDialogHelper(QPlatformTheme::ColorDialog)); + if (!m_dlgHelper) + return m_dlgHelper; + connect(m_dlgHelper, SIGNAL(accept()), this, SLOT(accept())); + connect(m_dlgHelper, SIGNAL(reject()), this, SLOT(reject())); + connect(m_dlgHelper, SIGNAL(currentColorChanged(QColor)), this, SLOT(setColor(QColor))); + connect(m_dlgHelper, SIGNAL(colorSelected(QColor)), this, SLOT(setColor(QColor))); + } + + return m_dlgHelper; +} + +/*! + \qmlproperty bool ColorDialog::visible + + This property holds whether the dialog is visible. By default this is + false. + + \sa modality +*/ + +/*! + \qmlproperty Qt::WindowModality ColorDialog::modality + + Whether the dialog should be shown modal with respect to the window + containing the dialog's parent Item, modal with respect to the whole + application, or non-modal. + + By default it is \l NonModal. + + Modality does not mean that there are any blocking calls to wait for the + dialog to be accepted or rejected; it's only that the user will be + prevented from interacting with the parent window and/or the application + windows at the same time. + + On MacOS the color dialog is only allowed to be non-modal. +*/ + +/*! + \qmlmethod void ColorDialog::open() + + Shows the dialog to the user. It is equivalent to setting \l visible to + true. +*/ + +/*! + \qmlmethod void ColorDialog::close() + + Closes the dialog. +*/ + +/*! + \qmlproperty string ColorDialog::title + + The title of the dialog window. +*/ + +/*! + \qmlproperty bool ColorDialog::showAlphaChannel + + Whether the dialog will provide a means of changing the opacity. + + By default, this property is true. This property must be set to the desired + value before opening the dialog. Usually the alpha channel is represented + by an additional slider control. +*/ + +/*! + \qmlproperty color ColorDialog::color + + The color which the user selected. +*/ + +QT_END_NAMESPACE diff --git a/src/imports/dialogs/qquickplatformcolordialog_p.h b/src/imports/dialogs/qquickplatformcolordialog_p.h new file mode 100644 index 0000000000..55d301da8b --- /dev/null +++ b/src/imports/dialogs/qquickplatformcolordialog_p.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQuick.Dialogs module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKPLATFORMCOLORDIALOG_P_H +#define QQUICKPLATFORMCOLORDIALOG_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qquickabstractcolordialog_p.h" + +QT_BEGIN_NAMESPACE + +class QQuickPlatformColorDialog : public QQuickAbstractColorDialog +{ + Q_OBJECT + +public: + QQuickPlatformColorDialog(QObject *parent = 0); + virtual ~QQuickPlatformColorDialog(); + +protected: + QPlatformColorDialogHelper *helper(); + + Q_DISABLE_COPY(QQuickPlatformColorDialog) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickPlatformColorDialog *) + +#endif // QQUICKPLATFORMCOLORDIALOG_P_H diff --git a/src/imports/widgets/qquickqcolordialog.cpp b/src/imports/widgets/qquickqcolordialog.cpp new file mode 100644 index 0000000000..abe6ffd004 --- /dev/null +++ b/src/imports/widgets/qquickqcolordialog.cpp @@ -0,0 +1,172 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickqcolordialog_p.h" +#include "qquickitem.h" + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QColorDialogHelper : public QPlatformColorDialogHelper +{ +public: + QColorDialogHelper() : + QPlatformColorDialogHelper() + { + connect(&m_dialog, SIGNAL(currentColorChanged(const QColor&)), this, SIGNAL(currentColorChanged(const QColor&))); + connect(&m_dialog, SIGNAL(colorSelected(const QColor&)), this, SIGNAL(colorSelected(const QColor&))); + connect(&m_dialog, SIGNAL(accepted()), this, SIGNAL(accept())); + connect(&m_dialog, SIGNAL(rejected()), this, SIGNAL(reject())); + } + + virtual void setCurrentColor(const QColor &c) { m_dialog.setCurrentColor(c); } + virtual QColor currentColor() const { return m_dialog.currentColor(); } + + virtual void exec() { m_dialog.exec(); } + + virtual bool show(Qt::WindowFlags f, Qt::WindowModality m, QWindow *parent) { + m_dialog.windowHandle()->setTransientParent(parent); + m_dialog.windowHandle()->setFlags(f); + m_dialog.setWindowModality(m); + m_dialog.setWindowTitle(QPlatformColorDialogHelper::options()->windowTitle()); + m_dialog.setOptions((QColorDialog::ColorDialogOptions)((int)(QPlatformColorDialogHelper::options()->options()))); + m_dialog.show(); + return m_dialog.isVisible(); + } + + virtual void hide() { m_dialog.hide(); } + +private: + QColorDialog m_dialog; +}; + +/*! + \qmltype QtColorDialog + \instantiates QQuickQColorDialog + \inqmlmodule QtQuick.PrivateWidgets 1 + \ingroup qtquick-visual + \brief Dialog component for choosing a color. + \since 5.1 + \internal + + QtColorDialog provides a means to instantiate and manage a QColorDialog. + It is not recommended to be used directly; it is an implementation + detail of \l ColorDialog in the \l QtQuick.Dialogs module. + + To use this type, you will need to import the module with the following line: + \code + import QtQuick.PrivateWidgets 1.0 + \endcode +*/ + +/*! + \qmlsignal QtQuick::Dialogs::ColorDialog::accepted + + The \a accepted signal is emitted when the user has finished using the + dialog. You can then inspect the \a color property to get the selection. + + Example: + + \qml + ColorDialog { + onAccepted: { console.log("Selected color: " + color) } + } + \endqml +*/ + +/*! + \qmlsignal QtQuick::Dialogs::ColorDialog::rejected + + The \a rejected signal is emitted when the user has dismissed the dialog, + either by closing the dialog window or by pressing the Cancel button. +*/ + +/*! + \class QQuickQColorDialog + \inmodule QtQuick.PrivateWidgets + \internal + + \brief The QQuickQColorDialog class is a wrapper for a QColorDialog. + + \since 5.1 +*/ + +/*! + Constructs a file dialog with parent window \a parent. +*/ +QQuickQColorDialog::QQuickQColorDialog(QObject *parent) + : QQuickAbstractColorDialog(parent) +{ +} + +/*! + Destroys the file dialog. +*/ +QQuickQColorDialog::~QQuickQColorDialog() +{ + if (m_dlgHelper) + m_dlgHelper->hide(); + delete m_dlgHelper; +} + +QPlatformColorDialogHelper *QQuickQColorDialog::helper() +{ + QQuickItem *parentItem = qobject_cast(parent()); + if (parentItem) + m_parentWindow = parentItem->window(); + + if (!m_dlgHelper) { + m_dlgHelper = new QColorDialogHelper(); + connect(m_dlgHelper, SIGNAL(currentColorChanged(const QColor&)), this, SLOT(setColor(QColor))); + connect(m_dlgHelper, SIGNAL(colorSelected(const QColor&)), this, SLOT(setColor(QColor))); + connect(m_dlgHelper, SIGNAL(accept()), this, SLOT(accept())); + connect(m_dlgHelper, SIGNAL(reject()), this, SLOT(reject())); + } + + return m_dlgHelper; +} + +QT_END_NAMESPACE diff --git a/src/imports/widgets/qquickqcolordialog_p.h b/src/imports/widgets/qquickqcolordialog_p.h new file mode 100644 index 0000000000..3fb0476299 --- /dev/null +++ b/src/imports/widgets/qquickqcolordialog_p.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKQCOLORDIALOG_P_H +#define QQUICKQCOLORDIALOG_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "../dialogs/qquickabstractcolordialog_p.h" + +QT_BEGIN_NAMESPACE + +class QQuickQColorDialog : public QQuickAbstractColorDialog +{ + Q_OBJECT + +public: + QQuickQColorDialog(QObject *parent = 0); + virtual ~QQuickQColorDialog(); + +protected: + QPlatformColorDialogHelper *helper(); + + Q_DISABLE_COPY(QQuickQColorDialog) +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickQColorDialog *) + +#endif // QQUICKQCOLORDIALOG_P_H diff --git a/src/imports/widgets/widgets.pro b/src/imports/widgets/widgets.pro index 7de5aa6c5a..c444674a75 100644 --- a/src/imports/widgets/widgets.pro +++ b/src/imports/widgets/widgets.pro @@ -6,12 +6,16 @@ IMPORT_VERSION = 1.0 SOURCES += \ qquickqfiledialog.cpp \ ../dialogs/qquickabstractfiledialog.cpp \ + qquickqcolordialog.cpp \ + ../dialogs/qquickabstractcolordialog.cpp \ ../dialogs/qquickabstractdialog.cpp \ widgetsplugin.cpp HEADERS += \ qquickqfiledialog_p.h \ ../dialogs/qquickabstractfiledialog_p.h \ + qquickqcolordialog_p.h \ + ../dialogs/qquickabstractcolordialog_p.h \ ../dialogs/qquickabstractdialog_p.h QT += quick-private gui-private core-private qml-private v8-private widgets diff --git a/src/imports/widgets/widgetsplugin.cpp b/src/imports/widgets/widgetsplugin.cpp index 6d3a56ca27..a29c9b3609 100644 --- a/src/imports/widgets/widgetsplugin.cpp +++ b/src/imports/widgets/widgetsplugin.cpp @@ -42,6 +42,7 @@ #include #include #include "qquickqfiledialog_p.h" +#include "qquickqcolordialog_p.h" QT_BEGIN_NAMESPACE @@ -75,6 +76,7 @@ public: Q_ASSERT(QLatin1String(uri) == QLatin1String("QtQuick.PrivateWidgets")); qmlRegisterType(uri, 1, 0, "QtFileDialog"); + qmlRegisterType(uri, 1, 0, "QtColorDialog"); } }; -- cgit v1.2.3 From 464626ffa25797b98d349e7a7fda1ed220d7d173 Mon Sep 17 00:00:00 2001 From: Liang Qi Date: Mon, 18 Mar 2013 09:46:09 +0100 Subject: QQuickItem: Support FocusReason in focusNextPrev Change-Id: Ib1b69607e1b4b52bad6e382d8102a42553f13432 Reviewed-by: Frederik Gladhorn --- src/quick/items/qquickitem.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index 0b3d91ffe5..d110a01290 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -2114,7 +2114,7 @@ bool QQuickItemPrivate::focusNextPrev(QQuickItem *item, bool forward) if (current == item) return false; - current->forceActiveFocus(); + current->forceActiveFocus(forward ? Qt::TabFocusReason : Qt::BacktabFocusReason); return true; } -- cgit v1.2.3 From 9237c2ecb5c6fc8820c28a636d1112990c950bbe Mon Sep 17 00:00:00 2001 From: Alan Alpert Date: Mon, 4 Mar 2013 14:03:56 -0800 Subject: Remove duplicated docs In the move of docs to a separate file, this copy of docs seemed to be resurrected by mistake. Removing it and properly formatting the code. Change-Id: Id7257c232880a8829e8864af12c5b7b5c484a398 Reviewed-by: Jerome Pasion Reviewed-by: Christopher Adams --- examples/quick/views/views.qml | 90 ++++++++---------------------------------- 1 file changed, 17 insertions(+), 73 deletions(-) diff --git a/examples/quick/views/views.qml b/examples/quick/views/views.qml index 0724d11456..1bc6f6a3d8 100644 --- a/examples/quick/views/views.qml +++ b/examples/quick/views/views.qml @@ -42,78 +42,22 @@ import QtQuick 2.0 import QtQml.Models 2.1 import "../shared" as Examples -/*! - \title QtQuick Examples - Views - \example quick/views - \brief This is a collection of QML model and view examples - \image qml-modelviews-example.png - - This is a collection of small QML examples relating to model and view functionality. They - are focused on the views side, which is the visual representation of the data in the models. - - \section2 GridView and PathView demonstrate usage of these elements to display views. - \snippet examples/quick/views/gridview/gridview-example.qml 0 - - \section2 Dynamic List demonstrates animation of runtime additions and removals to a ListView. - - The ListView.onAdd signal handler runs an animation when new items are added to the - view, and the ListView.onRemove another when they are removed. - \snippet examples/quick/views/listview/dynamiclist.qml 0 - \snippet examples/quick/views/listview/dynamiclist.qml 1 - - \section2 Expanding Delegates demonstrates delegates that expand when activated. - - It has a complex delegate the size and appearance of which can change, displacing - other items in the view. - \snippet examples/quick/views/listview/expandingdelegates.qml 0 - \snippet examples/quick/views/listview/expandingdelegates.qml 1 - \snippet examples/quick/views/listview/expandingdelegates.qml 2 - \snippet examples/quick/views/listview/expandingdelegates.qml 3 - - \section2 Highlight demonstrates adding a custom highlight to a ListView. - \snippet examples/quick/views/listview/highlight.qml 0 - - \section2 Highlight Ranges shows the three different highlight range modes of ListView. - \snippet examples/quick/views/listview/highlightranges.qml 0 - \snippet examples/quick/views/listview/highlightranges.qml 1 - \snippet examples/quick/views/listview/highlightranges.qml 2 - - \section2 Sections demonstrates the various section headers and footers available to ListView. - \snippet examples/quick/views/listview/sections.qml 0 - - \section2 Packages demonstrates using Packages to transition delegates between two views. - - It has a Package which defines delegate items for each view and an item that can - be transferred between delegates. - - \snippet examples/quick/views/package/Delegate.qml 0 - - A DelegateModel allows the individual views to access their specific items from - the shared package delegate. - - \snippet examples/quick/views/package/view.qml 0 - - \section2 ObjectModel uses a ObjectModel for the model instead of a ListModel. - - \snippet examples/quick/views/objectmodel/objectmodel.qml 0 - */ - - Item { - height: 480 - width: 320 - Examples.LauncherList { - id: ll - anchors.fill: parent - Component.onCompleted: { - addExample("GridView", "A simple GridView", Qt.resolvedUrl("gridview/gridview-example.qml")) - addExample("Dynamic List", "A dynamically alterable list", Qt.resolvedUrl("listview/dynamiclist.qml")) - addExample("Expanding Delegates", "A ListView with delegates that expand", Qt.resolvedUrl("listview/expandingdelegates.qml")) - addExample("Highlight", "A ListView with a custom highlight", Qt.resolvedUrl("listview/highlight.qml")) - addExample("Highlight Ranges", "The three highlight ranges of ListView", Qt.resolvedUrl("listview/highlightranges.qml")) - addExample("Sections", "ListView section headers and footers", Qt.resolvedUrl("listview/sections.qml")) - addExample("Packages", "Transitions between a ListView and GridView", Qt.resolvedUrl("package/view.qml")) - addExample("PathView", "A simple PathView", Qt.resolvedUrl("pathview/pathview-example.qml")) - addExample("ObjectModel", "Using a ObjectModel", Qt.resolvedUrl("objectmodel/objectmodel.qml")) - } +Item { + height: 480 + width: 320 + Examples.LauncherList { + id: ll + anchors.fill: parent + Component.onCompleted: { + addExample("GridView", "A simple GridView", Qt.resolvedUrl("gridview/gridview-example.qml")) + addExample("Dynamic List", "A dynamically alterable list", Qt.resolvedUrl("listview/dynamiclist.qml")) + addExample("Expanding Delegates", "A ListView with delegates that expand", Qt.resolvedUrl("listview/expandingdelegates.qml")) + addExample("Highlight", "A ListView with a custom highlight", Qt.resolvedUrl("listview/highlight.qml")) + addExample("Highlight Ranges", "The three highlight ranges of ListView", Qt.resolvedUrl("listview/highlightranges.qml")) + addExample("Sections", "ListView section headers and footers", Qt.resolvedUrl("listview/sections.qml")) + addExample("Packages", "Transitions between a ListView and GridView", Qt.resolvedUrl("package/view.qml")) + addExample("PathView", "A simple PathView", Qt.resolvedUrl("pathview/pathview-example.qml")) + addExample("ObjectModel", "Using a ObjectModel", Qt.resolvedUrl("objectmodel/objectmodel.qml")) } + } } -- cgit v1.2.3 From a89a652bf5a9cb01ad2babc4c1374ba0b4d07edd Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Mon, 18 Mar 2013 15:30:55 +0100 Subject: Fix hexadecimal escape sequence validation in strings. Give an error message when the sequence does not conform to the grammar. Although the specification does not explicitly state that this is an error, this is the behaviour of both JSC and V8. Change-Id: I34d189f07628bc6cc40b13bfbb8d09bee7810ced Reviewed-by: Simon Hausmann --- src/qml/qml/parser/qqmljslexer.cpp | 57 ++++++++++++++-------- src/qml/qml/parser/qqmljslexer_p.h | 4 +- .../tests/ecma/LexicalConventions/7.7.4.js | 3 +- .../parserstress/tests/ecma_2/RegExp/hex-001.js | 3 ++ tests/auto/qml/qmlmin/tst_qmlmin.cpp | 1 + .../qqmlecmascript/data/stringParsing_error.6.qml | 9 ++++ .../auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp | 2 +- 7 files changed, 55 insertions(+), 24 deletions(-) create mode 100644 tests/auto/qml/qqmlecmascript/data/stringParsing_error.6.qml diff --git a/src/qml/qml/parser/qqmljslexer.cpp b/src/qml/qml/parser/qqmljslexer.cpp index edd85ec878..0df49279e2 100644 --- a/src/qml/qml/parser/qqmljslexer.cpp +++ b/src/qml/qml/parser/qqmljslexer.cpp @@ -54,7 +54,7 @@ QT_END_NAMESPACE using namespace QQmlJS; -static int regExpFlagFromChar(const QChar &ch) +static inline int regExpFlagFromChar(const QChar &ch) { switch (ch.unicode()) { case 'g': return Lexer::RegExp_Global; @@ -64,7 +64,7 @@ static int regExpFlagFromChar(const QChar &ch) return 0; } -static unsigned char convertHex(ushort c) +static inline unsigned char convertHex(ushort c) { if (c >= '0' && c <= '9') return (c - '0'); @@ -74,12 +74,12 @@ static unsigned char convertHex(ushort c) return (c - 'A' + 10); } -static QChar convertHex(QChar c1, QChar c2) +static inline QChar convertHex(QChar c1, QChar c2) { return QChar((convertHex(c1.unicode()) << 4) + convertHex(c2.unicode())); } -static QChar convertUnicode(QChar c1, QChar c2, QChar c3, QChar c4) +static inline QChar convertUnicode(QChar c1, QChar c2, QChar c3, QChar c4) { return QChar((convertHex(c3.unicode()) << 4) + convertHex(c4.unicode()), (convertHex(c1.unicode()) << 4) + convertHex(c2.unicode())); @@ -329,6 +329,27 @@ QChar Lexer::decodeUnicodeEscapeCharacter(bool *ok) return QChar(); } +QChar Lexer::decodeHexEscapeCharacter(bool *ok) +{ + if (isHexDigit(_codePtr[0]) && isHexDigit(_codePtr[1])) { + scanChar(); + + const QChar c1 = _char; + scanChar(); + + const QChar c2 = _char; + scanChar(); + + if (ok) + *ok = true; + + return convertHex(c1, c2); + } + + *ok = false; + return QChar(); +} + static inline bool isIdentifierStart(QChar ch) { // fast path for ascii @@ -705,35 +726,29 @@ again: scanChar(); QChar u; - bool ok = false; switch (_char.unicode()) { // unicode escape sequence - case 'u': + case 'u': { + bool ok = false; u = decodeUnicodeEscapeCharacter(&ok); if (! ok) { _errorCode = IllegalUnicodeEscapeSequence; _errorMessage = QCoreApplication::translate("QQmlParser", "Illegal unicode escape sequence"); return T_ERROR; } - break; + } break; // hex escape sequence - case 'x': - if (isHexDigit(_codePtr[0]) && isHexDigit(_codePtr[1])) { - scanChar(); - - const QChar c1 = _char; - scanChar(); - - const QChar c2 = _char; - scanChar(); - - u = convertHex(c1, c2); - } else { - u = _char; + case 'x': { + bool ok = false; + u = decodeHexEscapeCharacter(&ok); + if (!ok) { + _errorCode = IllegalHexadecimalEscapeSequence; + _errorMessage = QCoreApplication::translate("QQmlParser", "Illegal hexadecimal escape sequence"); + return T_ERROR; } - break; + } break; // single character escape sequence case '\\': u = QLatin1Char('\\'); scanChar(); break; diff --git a/src/qml/qml/parser/qqmljslexer_p.h b/src/qml/qml/parser/qqmljslexer_p.h index e1b51da92b..23af61d650 100644 --- a/src/qml/qml/parser/qqmljslexer_p.h +++ b/src/qml/qml/parser/qqmljslexer_p.h @@ -128,7 +128,8 @@ public: IllegalUnicodeEscapeSequence, UnclosedComment, IllegalExponentIndicator, - IllegalIdentifier + IllegalIdentifier, + IllegalHexadecimalEscapeSequence }; enum RegExpBodyPrefix { @@ -203,6 +204,7 @@ private: void syncProhibitAutomaticSemicolon(); QChar decodeUnicodeEscapeCharacter(bool *ok); + QChar decodeHexEscapeCharacter(bool *ok); private: Engine *_engine; diff --git a/tests/auto/qml/parserstress/tests/ecma/LexicalConventions/7.7.4.js b/tests/auto/qml/parserstress/tests/ecma/LexicalConventions/7.7.4.js index 4a3173db6c..4b799f8df1 100644 --- a/tests/auto/qml/parserstress/tests/ecma/LexicalConventions/7.7.4.js +++ b/tests/auto/qml/parserstress/tests/ecma/LexicalConventions/7.7.4.js @@ -135,9 +135,10 @@ new TestCase( SECTION, "\\x1E1", String.fromCharCode(30)+"1", "\x1E new TestCase( SECTION, "\\x0F0", String.fromCharCode(15)+"0", "\x0F0" ); // G is out of hex range - +/* Invalid testcase: we no longer silently ignore invalid hexadecimal escape sequences. new TestCase( SECTION, "\\xG", "xG", "\xG" ); new TestCase( SECTION, "\\xCG", "xCG", "\xCG" ); +*/ // DoubleStringCharacter::EscapeSequence::CharacterEscapeSequence::\ NonEscapeCharacter new TestCase( SECTION, "\\a", "a", "\a" ); diff --git a/tests/auto/qml/parserstress/tests/ecma_2/RegExp/hex-001.js b/tests/auto/qml/parserstress/tests/ecma_2/RegExp/hex-001.js index 3e85ac7abf..f2dccd9267 100644 --- a/tests/auto/qml/parserstress/tests/ecma_2/RegExp/hex-001.js +++ b/tests/auto/qml/parserstress/tests/ecma_2/RegExp/hex-001.js @@ -55,7 +55,10 @@ startTest(); AddRegExpCases( new RegExp("\x41"), "new RegExp('\\x41')", "A", "A", 1, 0, ["A"] ); AddRegExpCases( new RegExp("\x412"),"new RegExp('\\x412')", "A2", "A2", 1, 0, ["A2"] ); + +/* Invalid testcase: we no longer silently ignore invalid hexadecimal escape sequences. AddRegExpCases( new RegExp("\x1g"), "new RegExp('\\x1g')", "x1g","x1g", 1, 0, ["x1g"] ); +*/ AddRegExpCases( new RegExp("A"), "new RegExp('A')", "\x41", "\\x41", 1, 0, ["A"] ); AddRegExpCases( new RegExp("A"), "new RegExp('A')", "\x412", "\\x412", 1, 0, ["A"] ); diff --git a/tests/auto/qml/qmlmin/tst_qmlmin.cpp b/tests/auto/qml/qmlmin/tst_qmlmin.cpp index 2957a70d04..65549efddc 100644 --- a/tests/auto/qml/qmlmin/tst_qmlmin.cpp +++ b/tests/auto/qml/qmlmin/tst_qmlmin.cpp @@ -126,6 +126,7 @@ void tst_qmlmin::initTestCase() invalidFiles << "tests/auto/qml/qqmlecmascript/data/stringParsing_error.3.qml"; invalidFiles << "tests/auto/qml/qqmlecmascript/data/stringParsing_error.4.qml"; invalidFiles << "tests/auto/qml/qqmlecmascript/data/stringParsing_error.5.qml"; + invalidFiles << "tests/auto/qml/qqmlecmascript/data/stringParsing_error.6.qml"; invalidFiles << "tests/auto/qml/qqmlecmascript/data/numberParsing_error.1.qml"; invalidFiles << "tests/auto/qml/qqmlecmascript/data/numberParsing_error.2.qml"; } diff --git a/tests/auto/qml/qqmlecmascript/data/stringParsing_error.6.qml b/tests/auto/qml/qqmlecmascript/data/stringParsing_error.6.qml new file mode 100644 index 0000000000..8ee5b59d9e --- /dev/null +++ b/tests/auto/qml/qqmlecmascript/data/stringParsing_error.6.qml @@ -0,0 +1,9 @@ + +import QtQuick 2.0 + +QtObject { + function code() { + "\x0G" + } +} + diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp index d022e9dedc..06590f0ad6 100644 --- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp +++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp @@ -7383,7 +7383,7 @@ void tst_qqmlecmascript::numberParsing() void tst_qqmlecmascript::stringParsing() { - for (int i = 1; i < 6; ++i) { + for (int i = 1; i < 7; ++i) { QString file("stringParsing_error.%1.qml"); file = file.arg(i); QQmlComponent component(&engine, testFileUrl(file)); -- cgit v1.2.3 From 0f4617f9fbcf2cc9d3199efbf7faca68dac20dac Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Mon, 18 Mar 2013 15:31:22 +0100 Subject: Remove unused field in test class. Change-Id: Id9bfbd1c84acb59a669908e933ed1f63a4987526 Reviewed-by: Simon Hausmann --- tests/auto/qml/qqmlparser/tst_qqmlparser.cpp | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/tests/auto/qml/qqmlparser/tst_qqmlparser.cpp b/tests/auto/qml/qqmlparser/tst_qqmlparser.cpp index ec55709d35..3d08ec3e16 100644 --- a/tests/auto/qml/qqmlparser/tst_qqmlparser.cpp +++ b/tests/auto/qml/qqmlparser/tst_qqmlparser.cpp @@ -75,15 +75,9 @@ using namespace QQmlJS; class Check: public AST::Visitor { - Engine *engine; QList nodeStack; public: - Check(Engine *engine) - : engine(engine) - { - } - void operator()(AST::Node *node) { AST::Node::accept(node, this); @@ -206,7 +200,7 @@ void tst_qqmlparser::qmlParser() else parser.parseProgram(); - check::Check chk(&engine); + check::Check chk; chk(parser.rootNode()); } #endif -- cgit v1.2.3 From db8fe19e901704c6e2bfdb7febf121dd7321268d Mon Sep 17 00:00:00 2001 From: Gunnar Sletta Date: Tue, 19 Mar 2013 12:11:53 +0100 Subject: Mark renderNode test as insignificant, as CI doesn't manage to run it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I43c2306f8b1c095140c9712821ddcc2dcea1b95d Reviewed-by: Samuel Rødal --- tests/auto/quick/rendernode/rendernode.pro | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/auto/quick/rendernode/rendernode.pro b/tests/auto/quick/rendernode/rendernode.pro index 425faef0a6..1e2c50a6d1 100644 --- a/tests/auto/quick/rendernode/rendernode.pro +++ b/tests/auto/quick/rendernode/rendernode.pro @@ -15,3 +15,4 @@ OTHER_FILES += \ data/RenderOrder.qml \ data/MessUpState.qml \ DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 +CONFIG+=insignificant_test -- cgit v1.2.3 From d6625ac911a9aaca544246884aaecc448e7550a0 Mon Sep 17 00:00:00 2001 From: Shawn Rutledge Date: Mon, 18 Mar 2013 15:53:52 +0100 Subject: Get rid of warning Module 'QtQuick.Dialogs'...cannot be protected... After I86bc6d975223979c19d94a3fd70e4b5130b73f47 it's possible for qmldir to have the module declaration again. The reason it was absent was that src/imports/dialogs/plugin.cpp was registering new types in initializeEngine. Change-Id: I6b28a3b97df7f7817cfdc1e745c4ce6f048302f1 Reviewed-by: Liang Qi Reviewed-by: Jens Bache-Wiig --- src/imports/dialogs/qmldir | 1 + 1 file changed, 1 insertion(+) diff --git a/src/imports/dialogs/qmldir b/src/imports/dialogs/qmldir index 50867d7361..df9a035ade 100644 --- a/src/imports/dialogs/qmldir +++ b/src/imports/dialogs/qmldir @@ -1,2 +1,3 @@ +module QtQuick.Dialogs plugin dialogplugin typeinfo plugins.qmltypes -- cgit v1.2.3 From 7148a79775e31eb5e3053f82f4ee5a9ba702ddae Mon Sep 17 00:00:00 2001 From: Frederik Gladhorn Date: Tue, 19 Mar 2013 15:04:44 +0100 Subject: Make sure that TextInteractionFlags get set. The first time the function is called, it could fail to set the flags. Calling setSelectByKeyboard(true) would not actually set the text interaction flags (was == on). The test didn't detect it because it called setReadOnly before setSelectByKeyboard. Change-Id: Ia54cc782b6ad5a74f1d7029c92fa230116d034b0 Reviewed-by: J-P Nurmi --- src/quick/items/qquicktextedit.cpp | 4 ++-- tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/quick/items/qquicktextedit.cpp b/src/quick/items/qquicktextedit.cpp index e30b9cb3fd..feabbba864 100644 --- a/src/quick/items/qquicktextedit.cpp +++ b/src/quick/items/qquicktextedit.cpp @@ -1265,8 +1265,8 @@ void QQuickTextEdit::setSelectByKeyboard(bool on) { Q_D(QQuickTextEdit); bool was = selectByKeyboard(); - d->selectByKeyboardSet = true; - if (was != on) { + if (!d->selectByKeyboardSet || was != on) { + d->selectByKeyboardSet = true; d->selectByKeyboard = on; if (on) d->control->setTextInteractionFlags(d->control->textInteractionFlags() | Qt::TextSelectableByKeyboard); diff --git a/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp b/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp index b9041fb719..bce1f9e4a2 100644 --- a/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp +++ b/tests/auto/quick/qquicktextedit/tst_qquicktextedit.cpp @@ -2171,8 +2171,8 @@ void tst_qquicktextedit::keyboardSelection() QVERIFY(edit); edit->setText(text); - edit->setReadOnly(readOnly); edit->setSelectByKeyboard(selectByKeyboard); + edit->setReadOnly(readOnly); edit->setCursorPosition(cursorPosition); QQuickWindow window; -- cgit v1.2.3 From 9582ca0ce1758572d1ad59548fe221ca2e51598b Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Tue, 19 Mar 2013 13:04:54 +0100 Subject: Fix multi-line string content. ECMA5.1, paragraph 7.8.4, item 9 under semantics: The SV of LineContinuation :: \ LineTerminatorSequence is the empty character sequence. So, do not add any line-terminator inside a multi-line string. Escaped characters like \r and \n are added of course. Change-Id: I8c58b7971b1d1bc90adc795ea278541758246e01 Reviewed-by: Lars Knoll --- src/qml/qml/parser/qqmljslexer.cpp | 13 +------------ tests/auto/qml/v4/data/equals.qml | 3 +++ 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/src/qml/qml/parser/qqmljslexer.cpp b/src/qml/qml/parser/qqmljslexer.cpp index 0df49279e2..cb78238f99 100644 --- a/src/qml/qml/parser/qqmljslexer.cpp +++ b/src/qml/qml/parser/qqmljslexer.cpp @@ -782,22 +782,11 @@ again: return T_ERROR; case '\r': - if (isLineTerminatorSequence() == 2) { - _tokenText += QLatin1Char('\r'); - u = QLatin1Char('\n'); - } else { - u = QLatin1Char('\r'); - } - scanChar(); - break; - case '\n': case 0x2028u: case 0x2029u: - u = _char; scanChar(); - break; - + continue; default: // non escape character diff --git a/tests/auto/qml/v4/data/equals.qml b/tests/auto/qml/v4/data/equals.qml index c32603cc7e..2862bb7ac9 100644 --- a/tests/auto/qml/v4/data/equals.qml +++ b/tests/auto/qml/v4/data/equals.qml @@ -44,5 +44,8 @@ QtObject { property bool test32: true != zero property bool test33: true == 1 property bool test34: true != 1 + + property bool test35: "a\ +b" === "ab" } -- cgit v1.2.3 From 471645f6dba6a21b85cb6788f0a0a07b6d5a804a Mon Sep 17 00:00:00 2001 From: Alan Alpert Date: Fri, 21 Dec 2012 13:37:30 -0800 Subject: Add QQmlApplicationEngine This helper class exposes QML application functionality that QML-only applications want to have, but QML-using applications may not. Change-Id: If91c3f55ffa2a4aecdd9d6cc62f6ad09fd35b0dd Reviewed-by: Alan Alpert (Personal) <416365416c@gmail.com> --- src/qml/qml/qml.pri | 7 +- src/qml/qml/qqmlapplicationengine.cpp | 285 +++++++++++++++++++++ src/qml/qml/qqmlapplicationengine.h | 80 ++++++ src/qml/qml/qqmlapplicationengine_p.h | 88 +++++++ src/qml/qml/qqmlengine.cpp | 10 + src/qml/qml/qqmlengine.h | 1 + tests/auto/qml/qml.pro | 3 +- .../qml/qqmlapplicationengine/data/TestItem.qml | 4 + .../qqmlapplicationengine/data/applicationTest.qml | 14 + .../qml/qqmlapplicationengine/data/basicTest.qml | 5 + .../qqmlapplicationengine.pro | 3 + .../qml/qqmlapplicationengine/testapp/main.cpp | 50 ++++ .../qml/qqmlapplicationengine/testapp/main.qml | 11 + .../qml/qqmlapplicationengine/testapp/main.qrc | 5 + .../qml/qqmlapplicationengine/testapp/testapp.pro | 11 + .../tst_qqmlapplicationengine.cpp | 162 ++++++++++++ .../tst_qqmlapplicationengine.pro | 10 + 17 files changed, 746 insertions(+), 3 deletions(-) create mode 100644 src/qml/qml/qqmlapplicationengine.cpp create mode 100644 src/qml/qml/qqmlapplicationengine.h create mode 100644 src/qml/qml/qqmlapplicationengine_p.h create mode 100644 tests/auto/qml/qqmlapplicationengine/data/TestItem.qml create mode 100644 tests/auto/qml/qqmlapplicationengine/data/applicationTest.qml create mode 100644 tests/auto/qml/qqmlapplicationengine/data/basicTest.qml create mode 100644 tests/auto/qml/qqmlapplicationengine/qqmlapplicationengine.pro create mode 100644 tests/auto/qml/qqmlapplicationengine/testapp/main.cpp create mode 100644 tests/auto/qml/qqmlapplicationengine/testapp/main.qml create mode 100644 tests/auto/qml/qqmlapplicationengine/testapp/main.qrc create mode 100644 tests/auto/qml/qqmlapplicationengine/testapp/testapp.pro create mode 100644 tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp create mode 100644 tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.pro diff --git a/src/qml/qml/qml.pri b/src/qml/qml/qml.pri index a660abe7d7..aafc50db9b 100644 --- a/src/qml/qml/qml.pri +++ b/src/qml/qml/qml.pri @@ -50,7 +50,8 @@ SOURCES += \ $$PWD/qqmlbundle.cpp \ $$PWD/qqmlmemoryprofiler.cpp \ $$PWD/qqmlplatform.cpp \ - $$PWD/qqmlbinding.cpp + $$PWD/qqmlbinding.cpp \ + $$PWD/qqmlapplicationengine.cpp HEADERS += \ $$PWD/qqmlglobal_p.h \ @@ -121,7 +122,9 @@ HEADERS += \ $$PWD/qqmlmemoryprofiler_p.h \ $$PWD/qqmlplatform_p.h \ $$PWD/qqmlbinding_p.h \ - $$PWD/qqmlextensionplugin_p.h + $$PWD/qqmlextensionplugin_p.h \ + $$PWD/qqmlapplicationengine_p.h \ + $$PWD/qqmlapplicationengine.h include(parser/parser.pri) diff --git a/src/qml/qml/qqmlapplicationengine.cpp b/src/qml/qml/qqmlapplicationengine.cpp new file mode 100644 index 0000000000..7dc2c77922 --- /dev/null +++ b/src/qml/qml/qqmlapplicationengine.cpp @@ -0,0 +1,285 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Research In Motion. +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include "qqmlapplicationengine.h" +#include "qqmlapplicationengine_p.h" + +QT_BEGIN_NAMESPACE + +QQmlApplicationEnginePrivate::QQmlApplicationEnginePrivate(QQmlEngine *e) + : QQmlEnginePrivate(e) +{ +} + +QQmlApplicationEnginePrivate::~QQmlApplicationEnginePrivate() +{ + qDeleteAll(objects); +#ifndef QT_NO_TRANSLATIONS + qDeleteAll(translators); +#endif +} + +void QQmlApplicationEnginePrivate::init() +{ + Q_Q(QQmlApplicationEngine); + q->connect(&statusMapper, SIGNAL(mapped(QObject*)), + q, SLOT(_q_finishLoad(QObject*))); + q->connect(q, SIGNAL(quit()), QCoreApplication::instance(), SLOT(quit())); +#ifndef QT_NO_TRANSLATIONS + QTranslator* qtTranslator = new QTranslator; + if (qtTranslator->load(QLatin1String("qt_") + QLocale::system().name(), QLibraryInfo::location(QLibraryInfo::TranslationsPath))) + QCoreApplication::installTranslator(qtTranslator); + translators << qtTranslator; +#endif +} + +void QQmlApplicationEnginePrivate::loadTranslations(const QUrl &rootFile) +{ +#ifndef QT_NO_TRANSLATIONS + if (rootFile.scheme() != QLatin1String("file") && rootFile.scheme() != QLatin1String("qrc")) + return; + + QFileInfo fi(rootFile.toLocalFile()); + + QTranslator *translator = new QTranslator; + if (translator->load(QLatin1String("qml_") + QLocale::system().name(), fi.path() + QLatin1String("/i18n"))) { + QCoreApplication::installTranslator(translator); + translators << translator; + } else { + delete translator; + } +#endif +} + +void QQmlApplicationEnginePrivate::startLoad(const QUrl &url, const QByteArray &data, bool dataFlag) +{ + Q_Q(QQmlApplicationEngine); + + loadTranslations(url); //Translations must be loaded before the QML file is + QQmlComponent *c = new QQmlComponent(q, q); + + if (dataFlag) + c->setData(data,url); + else + c->loadUrl(url); + + if (!c->isLoading()) { + _q_finishLoad(c); + return; + } + statusMapper.setMapping(c, c); + q->connect(c, SIGNAL(statusChanged(QQmlComponent::Status)), + &statusMapper, SLOT(map())); +} + +void QQmlApplicationEnginePrivate::_q_finishLoad(QObject *o) +{ + Q_Q(QQmlApplicationEngine); + QQmlComponent *c = qobject_cast(o); + if (!c) + return; + switch (c->status()) { + case QQmlComponent::Error: + qWarning() << "QQmlApplicationEngine failed to load component"; + qWarning() << qPrintable(c->errorString()); + q->objectCreated(0, c->url()); + break; + case QQmlComponent::Ready: + objects << c->create(); + q->objectCreated(objects.last(), c->url()); + break; + case QQmlComponent::Loading: + case QQmlComponent::Null: + return; //These cases just wait for the next status update + } + delete c; +} + +/*! + \class QQmlApplicationEngine + \since 5.1 + \inmodule QtQml + \brief QQmlApplicationEngine provides a convenient way to load an application from a single QML file. + + This class combines a QQmlEngine and QQmlComponent to provide a convenient way to load a single QML file. It also exposes some central application functionality to QML, which a C++/QML hybrid application would normally control from C++. + + It can be used like so: + + \code + #include + #include + + int main(int argc, char *argv[]) + { + QGuiApplication app(argc, argv); + QQmlApplicationEngine engine("main.qml"); + return app.exec(); + } + \endcode + + You can also use QCoreApplication with QQmlApplicationEngine, if you are not using any QML modules which require a QGuiApplication (such as QtQuick). + + List of configuration changes from a default QQmlEngine: + + \list + \li Connecting Qt.quit() to QCoreApplication::quit() + \li Automatically loads translation files from an i18n directory adjacent to the main QML file. + \endlist + + The engine behavior can be further tweaked by using the inherited methods from QQmlEngine. +*/ + +/*! + \fn QQmlApplicationEngine::objectCreated(QObject *object, const QUrl &url) + + This signal is emitted when an object finishes loading. If loading was successful, \a object contains a pointer to the loaded object. + Otherwise the pointer is NULL. The \a url loaded is also provided, note that if a QString file path was initially passed to the + QQmlApplicationEngine, this url will be the equivalent of QUrl::fromLocalFile(filePath). +*/ + +/*! + Create a new QQmlApplicationEngine with the given \a parent. You will have to call load() later in + order to load a QML file. +*/ +QQmlApplicationEngine::QQmlApplicationEngine(QObject *parent) +: QQmlEngine(*(new QQmlApplicationEnginePrivate(this)), parent) +{ + Q_D(QQmlApplicationEngine); + d->init(); +} + +/*! + Create a new QQmlApplicationEngine and loads the QML file at the given \a url. + This is provided as a convenience, and is the same as using the empty constructor and calling load afterwards. +*/ +QQmlApplicationEngine::QQmlApplicationEngine(const QUrl &url, QObject *parent) + : QQmlEngine(*(new QQmlApplicationEnginePrivate(this)), parent) +{ + Q_D(QQmlApplicationEngine); + d->init(); + load(url); +} + +/*! + Create a new QQmlApplicationEngine and loads the QML file at the given + \a filePath, which must be a local file path. If a relative path is + given then it will be interpreted as relative to the working directory of the + application. + + This is provided as a convenience, and is the same as using the empty constructor and calling load afterwards. +*/ +QQmlApplicationEngine::QQmlApplicationEngine(const QString &filePath, QObject *parent) + : QQmlEngine(*(new QQmlApplicationEnginePrivate(this)), parent) +{ + Q_D(QQmlApplicationEngine); + d->init(); + load(QUrl::fromLocalFile(filePath)); +} + +/*! + Destroys the QQmlApplicationEngine and all QML objects it loaded. +*/ +QQmlApplicationEngine::~QQmlApplicationEngine() +{ + //Instantiated root objects cleaned up in private class +} + +/*! + Loads the root QML file located at \a url. The object tree defined by the file + is created immediately for local file urls. Remote urls are loaded asynchronously, + listen to the objectCreated signal to determine when the object + tree is ready. + + If an error occurs, error messages are printed with qWarning. +*/ +void QQmlApplicationEngine::load(const QUrl &url) +{ + Q_D(QQmlApplicationEngine); + d->startLoad(url); +} + +/*! + Loads the root QML file located at \a filePath. \a filePath must be a path to + a local file. If \a filePath is a relative path, it is taken as relative to + the application's working directory. The object tree defined by the file is + instantiated immediately. + + If an error occurs, error messages are printed with qWarning. +*/ +void QQmlApplicationEngine::load(const QString &filePath) +{ + Q_D(QQmlApplicationEngine); + d->startLoad(QUrl::fromLocalFile(filePath)); +} + +/*! + Loads the QML given in \a data. The object tree defined by \a data is + instantiated immediately. + + If a \a url is specified it is used as the base url of the component. This affects + relative paths within the data and error messages. + + If an error occurs, error messages are printed with qWarning. +*/ +void QQmlApplicationEngine::loadData(const QByteArray &data, const QUrl &url) +{ + Q_D(QQmlApplicationEngine); + d->startLoad(url, data, true); +} + +/*! + Returns a list of all the root objects instantiated by the + QQmlApplicationEngine. This will only contain objects loaded via load() or a + convenience constructor. +*/ + +QList QQmlApplicationEngine::rootObjects() +{ + Q_D(QQmlApplicationEngine); + return d->objects; +} + +QT_END_NAMESPACE + +#include "moc_qqmlapplicationengine.cpp" diff --git a/src/qml/qml/qqmlapplicationengine.h b/src/qml/qml/qqmlapplicationengine.h new file mode 100644 index 0000000000..b5de998100 --- /dev/null +++ b/src/qml/qml/qqmlapplicationengine.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Research In Motion. +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLAPPLICATIONENGINE_H +#define QQMLAPPLICATIONENGINE_H + +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QQmlApplicationEnginePrivate; +class Q_QML_EXPORT QQmlApplicationEngine : public QQmlEngine +{ + Q_OBJECT +public: + QQmlApplicationEngine(QObject *parent=0); + QQmlApplicationEngine(const QUrl &url, QObject *parent=0); + QQmlApplicationEngine(const QString &filePath, QObject *parent=0); + ~QQmlApplicationEngine(); + + QList rootObjects(); +public Q_SLOTS: + void load(const QUrl &url); + void load(const QString &filePath); + void loadData(const QByteArray &data, const QUrl &url = QUrl()); + +Q_SIGNALS: + void objectCreated(QObject *object, const QUrl &url); + +private: + Q_DISABLE_COPY(QQmlApplicationEngine) + Q_DECLARE_PRIVATE(QQmlApplicationEngine) + Q_PRIVATE_SLOT(d_func(), void _q_finishLoad(QObject*)) +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/qml/qqmlapplicationengine_p.h b/src/qml/qml/qqmlapplicationengine_p.h new file mode 100644 index 0000000000..db144af504 --- /dev/null +++ b/src/qml/qml/qqmlapplicationengine_p.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Research In Motion. +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLAPPLICATIONENGINE_P_H +#define QQMLAPPLICATIONENGINE_P_H + +#include "qqmlapplicationengine.h" +#include "qqmlengine_p.h" +#include +#include +#include +#include + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +QT_BEGIN_NAMESPACE + +class QTranslator; +class Q_QML_PRIVATE_EXPORT QQmlApplicationEnginePrivate : public QQmlEnginePrivate +{ + Q_DECLARE_PUBLIC(QQmlApplicationEngine) +public: + QQmlApplicationEnginePrivate(QQmlEngine *e); + ~QQmlApplicationEnginePrivate(); + void init(); + + void startLoad(const QUrl &url, const QByteArray &data = QByteArray(), bool dataFlag = false); + void loadTranslations(const QUrl &rootFile); + void _q_finishLoad(QObject *component); + QList objects; + QSignalMapper statusMapper; + QObject *appObj; + +#ifndef QT_NO_TRANSLATIONS + QList translators; +#endif +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index 23d1cf785e..96d01920d9 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -813,6 +813,16 @@ QQmlEngine::QQmlEngine(QObject *parent) d->init(); } +/*! +* \internal +*/ +QQmlEngine::QQmlEngine(QQmlEnginePrivate &dd, QObject *parent) +: QJSEngine(dd, parent) +{ + Q_D(QQmlEngine); + d->init(); +} + /*! Destroys the QQmlEngine. diff --git a/src/qml/qml/qqmlengine.h b/src/qml/qml/qqmlengine.h index 9433fcbcb3..45826a4a67 100644 --- a/src/qml/qml/qqmlengine.h +++ b/src/qml/qml/qqmlengine.h @@ -142,6 +142,7 @@ public: static void setObjectOwnership(QObject *, ObjectOwnership); static ObjectOwnership objectOwnership(QObject *); protected: + QQmlEngine(QQmlEnginePrivate &dd, QObject *p); virtual bool event(QEvent *); Q_SIGNALS: diff --git a/tests/auto/qml/qml.pro b/tests/auto/qml/qml.pro index ede7689a0c..584533c65b 100644 --- a/tests/auto/qml/qml.pro +++ b/tests/auto/qml/qml.pro @@ -24,7 +24,8 @@ PUBLICTESTS += \ qqmltranslation \ qqmlxmlhttprequest \ qtqmlmodules \ - qquickfolderlistmodel + qquickfolderlistmodel \ + qqmlapplicationengine PRIVATETESTS += \ animation \ diff --git a/tests/auto/qml/qqmlapplicationengine/data/TestItem.qml b/tests/auto/qml/qqmlapplicationengine/data/TestItem.qml new file mode 100644 index 0000000000..e879577e10 --- /dev/null +++ b/tests/auto/qml/qqmlapplicationengine/data/TestItem.qml @@ -0,0 +1,4 @@ +import QtQml 2.0 + +QtObject { +} diff --git a/tests/auto/qml/qqmlapplicationengine/data/applicationTest.qml b/tests/auto/qml/qqmlapplicationengine/data/applicationTest.qml new file mode 100644 index 0000000000..2a1b4fbf57 --- /dev/null +++ b/tests/auto/qml/qqmlapplicationengine/data/applicationTest.qml @@ -0,0 +1,14 @@ +import QtQml 2.0 + +QtObject { + property string originalName + property string originalVersion + property string currentName: Qt.application.name + property string currentVersion: Qt.application.version + Component.onCompleted: { + originalName = Qt.application.name + originalVersion = Qt.application.version + Qt.application.name = "Test B" + Qt.application.version = "0.0B" + } +} diff --git a/tests/auto/qml/qqmlapplicationengine/data/basicTest.qml b/tests/auto/qml/qqmlapplicationengine/data/basicTest.qml new file mode 100644 index 0000000000..837835f6df --- /dev/null +++ b/tests/auto/qml/qqmlapplicationengine/data/basicTest.qml @@ -0,0 +1,5 @@ +import QtQml 2.0 + +QtObject { + property bool success: true +} diff --git a/tests/auto/qml/qqmlapplicationengine/qqmlapplicationengine.pro b/tests/auto/qml/qqmlapplicationengine/qqmlapplicationengine.pro new file mode 100644 index 0000000000..4a2dde7c47 --- /dev/null +++ b/tests/auto/qml/qqmlapplicationengine/qqmlapplicationengine.pro @@ -0,0 +1,3 @@ +TEMPLATE = subdirs +SUBDIRS = tst_qqmlapplicationengine.pro \ + testapp diff --git a/tests/auto/qml/qqmlapplicationengine/testapp/main.cpp b/tests/auto/qml/qqmlapplicationengine/testapp/main.cpp new file mode 100644 index 0000000000..fe64bb35ad --- /dev/null +++ b/tests/auto/qml/qqmlapplicationengine/testapp/main.cpp @@ -0,0 +1,50 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Research In Motion. +** Contact: http://www.qt-project.org/legal +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +int main (int argc, char *argv[]) +{ + QCoreApplication app(argc, argv); + QQmlApplicationEngine e(QUrl("qrc:///main.qml")); + return app.exec(); +} diff --git a/tests/auto/qml/qqmlapplicationengine/testapp/main.qml b/tests/auto/qml/qqmlapplicationengine/testapp/main.qml new file mode 100644 index 0000000000..c75485a7f7 --- /dev/null +++ b/tests/auto/qml/qqmlapplicationengine/testapp/main.qml @@ -0,0 +1,11 @@ +import QtQml 2.0 + +QtObject { + id: root + property Timer t: Timer { interval: 1; running: true; onTriggered: Qt.quit(); } + property Connections c: Connections { + target: Qt.application + onAboutToQuit: console.log("End"); + } + Component.onCompleted: console.log("Start: " + Qt.application.arguments[1]); +} diff --git a/tests/auto/qml/qqmlapplicationengine/testapp/main.qrc b/tests/auto/qml/qqmlapplicationengine/testapp/main.qrc new file mode 100644 index 0000000000..5f6483ac33 --- /dev/null +++ b/tests/auto/qml/qqmlapplicationengine/testapp/main.qrc @@ -0,0 +1,5 @@ + + + main.qml + + diff --git a/tests/auto/qml/qqmlapplicationengine/testapp/testapp.pro b/tests/auto/qml/qqmlapplicationengine/testapp/testapp.pro new file mode 100644 index 0000000000..34d2718178 --- /dev/null +++ b/tests/auto/qml/qqmlapplicationengine/testapp/testapp.pro @@ -0,0 +1,11 @@ +TEMPLATE = app +TARGET = testapp +DESTDIR = ./ +CONFIG -= app_bundle +CONFIG += console + +QT += qml + +# Input +SOURCES += main.cpp +RESOURCES += main.qrc diff --git a/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp b/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp new file mode 100644 index 0000000000..1c11fcbc73 --- /dev/null +++ b/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp @@ -0,0 +1,162 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Research In Motion. +** Contact: http://www.qt-project.org/legal +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** 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, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "../../shared/util.h" +#include +#include +#include +#include + +class tst_qqmlapplicationengine : public QQmlDataTest +{ + Q_OBJECT +public: + tst_qqmlapplicationengine() {} + + +private slots: + void initTestCase(); + void basicLoading(); + void application(); + void applicationProperties(); +private: + QString buildDir; + QString srcDir; +}; + +void tst_qqmlapplicationengine::initTestCase() +{ + buildDir = QDir::currentPath(); + QQmlDataTest::initTestCase(); //Changes current path to src dir + srcDir = QDir::currentPath(); +} + +void tst_qqmlapplicationengine::basicLoading() +{ + int size = 0; + + QQmlApplicationEngine *test = new QQmlApplicationEngine(testFileUrl("basicTest.qml")); + QCOMPARE(test->rootObjects().size(), ++size); + QVERIFY(test->rootObjects()[size -1]); + QVERIFY(test->rootObjects()[size -1]->property("success").toBool()); + + QSignalSpy objectCreated(test, SIGNAL(objectCreated(QObject*,const QUrl&))); + test->load(testFileUrl("basicTest.qml")); + QCOMPARE(objectCreated.count(), size);//one less than rootObjects().size() because we missed the first one + QCOMPARE(test->rootObjects().size(), ++size); + QVERIFY(test->rootObjects()[size -1]); + QVERIFY(test->rootObjects()[size -1]->property("success").toBool()); + + QByteArray testQml("import QtQml 2.0; QtObject{property bool success: true; property TestItem t: TestItem{}}"); + test->loadData(testQml, testFileUrl("dynamicTest.qml")); + QCOMPARE(objectCreated.count(), size); + QCOMPARE(test->rootObjects().size(), ++size); + QVERIFY(test->rootObjects()[size -1]); + QVERIFY(test->rootObjects()[size -1]->property("success").toBool()); + + delete test; +} + +void tst_qqmlapplicationengine::application() +{ + /* This test batches together some tests about running an external application + written with QQmlApplicationEngine. The application tests the following functionality + which is easier to do by watching a separate process: + -Loads relative paths from the working directory + -quits when quit is called + -emits aboutToQuit after quit is called + -has access to application command line arguments + + Note that checking the output means that on builds with extra debugging, this might fail with a false positive. + Also the testapp is automatically built and installed in shadow builds, so it does NOT use testData + */ + QDir::setCurrent(buildDir); + QProcess *testProcess = new QProcess(this); + QTest::ignoreMessage(QtWarningMsg, "Don't know how to handle 'QProcess::ExitStatus', use qRegisterMetaType to register it."); + QSignalSpy processFinished(testProcess, SIGNAL(finished(int,QProcess::ExitStatus))); + QStringList args; + args << QLatin1String("testData"); + testProcess->start(QLatin1String("testapp/testapp"), args); + QTRY_VERIFY(processFinished.count());//Application should immediately exit + QCOMPARE(processFinished[0][0].toInt(), 0); + QByteArray test_stdout = testProcess->readAllStandardOutput(); + QByteArray test_stderr = testProcess->readAllStandardError(); + QByteArray test_stderr_target("Start: testData\nEnd\n"); +#ifdef Q_OS_WIN + test_stderr_target.replace('\n', QByteArray("\r\n")); +#endif + QCOMPARE(test_stdout, QByteArray("")); + QCOMPARE(test_stderr, test_stderr_target); + delete testProcess; + QDir::setCurrent(srcDir); +} + +void tst_qqmlapplicationengine::applicationProperties() +{ + QCoreApplication* coreApp = QCoreApplication::instance(); + QString originalName = coreApp->applicationName(); + QString originalVersion = coreApp->applicationVersion(); + QString firstName = QLatin1String("Test A"); + QString firstVersion = QLatin1String("0.0A"); + QString secondName = QLatin1String("Test B"); + QString secondVersion = QLatin1String("0.0B"); + + coreApp->setApplicationName(firstName); + coreApp->setApplicationVersion(firstVersion); + + QQmlApplicationEngine *test = new QQmlApplicationEngine(testFileUrl("applicationTest.qml")); + QObject* root = test->rootObjects().at(0); + QVERIFY(root); + QCOMPARE(root->property("originalName").toString(), firstName); + QCOMPARE(root->property("originalVersion").toString(), firstVersion); + QCOMPARE(root->property("currentName").toString(), secondName); + QCOMPARE(root->property("currentVersion").toString(), secondVersion); + QCOMPARE(coreApp->applicationName(), secondName); + QCOMPARE(coreApp->applicationVersion(), secondVersion); + + coreApp->setApplicationName(originalName); + coreApp->setApplicationVersion(originalVersion); + delete test; +} + +QTEST_MAIN(tst_qqmlapplicationengine) + +#include "tst_qqmlapplicationengine.moc" diff --git a/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.pro b/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.pro new file mode 100644 index 0000000000..18c38a80b6 --- /dev/null +++ b/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.pro @@ -0,0 +1,10 @@ +CONFIG += testcase +TARGET = tst_qqmlapplicationengine +macx:CONFIG -= app_bundle + + +SOURCES += tst_qqmlapplicationengine.cpp +TESTDATA += data/* + +include (../../shared/util.pri) +QT += core-private gui-private qml-private network testlib -- cgit v1.2.3