diff options
author | Aaron Kennedy <aaron.kennedy@nokia.com> | 2009-11-18 03:25:37 (GMT) |
---|---|---|
committer | Aaron Kennedy <aaron.kennedy@nokia.com> | 2009-11-18 03:25:37 (GMT) |
commit | e2dbda508f2a65e1ca8c31df6339b1bbec14e933 (patch) | |
tree | f40af8a17bc07005af529abe82c46e4e953585cc | |
parent | 4ba2513ca6fa42c37c45a573b4643b5e1d97db3b (diff) | |
parent | 9614a2775df27602b1d1bb0946feb4bb593cbf39 (diff) | |
download | Qt-e2dbda508f2a65e1ca8c31df6339b1bbec14e933.zip Qt-e2dbda508f2a65e1ca8c31df6339b1bbec14e933.tar.gz Qt-e2dbda508f2a65e1ca8c31df6339b1bbec14e933.tar.bz2 |
Merge branch 'kinetic-declarativeui' of scm.dev.nokia.troll.no:qt/kinetic into kinetic-declarativeui
18 files changed, 439 insertions, 335 deletions
diff --git a/demos/declarative/samegame/content/samegame.js b/demos/declarative/samegame/content/samegame.js index 4d5a6be..3598c26 100755 --- a/demos/declarative/samegame/content/samegame.js +++ b/demos/declarative/samegame/content/samegame.js @@ -210,34 +210,22 @@ function saveHighScore(name) { if(scoresURL!="") sendHighScore(name); //OfflineStorage - var db = openDatabase("SameGameScores", "1.0", "Local SameGame High Scores",100); + var db = openDatabaseSync("SameGameScores", "1.0", "Local SameGame High Scores",100); var dataStr = "INSERT INTO Scores VALUES(?, ?, ?, ?)"; var data = [name, gameCanvas.score, maxX+"x"+maxY ,Math.floor(timer/1000)]; db.transaction( function(tx) { - tx.executeSql('CREATE TABLE IF NOT EXISTS Scores(name TEXT, score NUMBER, gridSize TEXT, time NUMBER)',[]); + tx.executeSql('CREATE TABLE IF NOT EXISTS Scores(name TEXT, score NUMBER, gridSize TEXT, time NUMBER)'); tx.executeSql(dataStr, data); - tx.executeSql('SELECT * FROM Scores WHERE gridSize = "12x17" ORDER BY score desc LIMIT 10',[], - function(tx, rs) { - var r = "\nHIGH SCORES for a standard sized grid\n\n" - for(var i = 0; i < rs.rows.length; i++){ - r += (i+1)+". " + rs.rows.item(i).name +' got ' - + rs.rows.item(i).score + ' points in ' - + rs.rows.item(i).time + ' seconds.\n'; - } - dialog.show(r); - }, - function(tx, error) { - print("ERROR:", error.message); - } - ); - }, - function() { - print("ERROR in transaction"); - }, - function() { - //print("Transaction successful"); + var rs = tx.executeSql('SELECT * FROM Scores WHERE gridSize = "12x17" ORDER BY score desc LIMIT 10'); + var r = "\nHIGH SCORES for a standard sized grid\n\n" + for(var i = 0; i < rs.rows.length; i++){ + r += (i+1)+". " + rs.rows.item(i).name +' got ' + + rs.rows.item(i).score + ' points in ' + + rs.rows.item(i).time + ' seconds.\n'; + } + dialog.show(r); } ); } diff --git a/demos/declarative/webbrowser/webbrowser.qml b/demos/declarative/webbrowser/webbrowser.qml index 8a01af5..23e0a20 100644 --- a/demos/declarative/webbrowser/webbrowser.qml +++ b/demos/declarative/webbrowser/webbrowser.qml @@ -202,11 +202,11 @@ Item { var sc = zoom/zoomFactor; scaleAnim.to = sc; flickVX.from = flickable.viewportX - flickVX.to = Math.min(Math.max(0,centerX-flickable.width/2),webView.width*sc-flickable.width) - finalX.value = Math.min(Math.max(0,centerX-flickable.width/2),webView.width*sc-flickable.width) + flickVX.to = Math.max(0,Math.min(centerX-flickable.width/2,webView.width*sc-flickable.width)) + finalX.value = flickVX.to flickVY.from = flickable.viewportY - flickVY.to = Math.min(Math.max(0,centerY-flickable.height/2),webView.height*sc-flickable.height) - finalY.value = Math.min(Math.max(0,centerY-flickable.height/2),webView.height*sc-flickable.height) + flickVY.to = Math.max(0,Math.min(centerY-flickable.height/2,webView.height*sc-flickable.height)) + finalY.value = flickVY.to finalZoom.value = zoom quickZoom.start() } @@ -214,11 +214,12 @@ Item { preferredWidth: flickable.width preferredHeight: flickable.height - zoomFactor: flickable.width > 980 ? flickable.width : flickable.width/980 + zoomFactor: flickable.width > 980 ? 1 : flickable.width/980 onUrlChanged: { if (url != null) { webBrowser.urlString = url.toString(); } } - onDoubleClick: { if (!heuristicZoom(clickX,clickY,2.5)) { - var zf = flickable.width > 980 ? flickable.width : flickable.width/980; + onDoubleClick: { + if (!heuristicZoom(clickX,clickY,2.5)) { + var zf = flickable.width > 980 ? 1 : flickable.width/980; doZoom(zf,clickX/zoomFactor*zf,clickY/zoomFactor*zf) } } diff --git a/doc/src/declarative/globalobject.qdoc b/doc/src/declarative/globalobject.qdoc index dc08c28..6d9d1f2 100644 --- a/doc/src/declarative/globalobject.qdoc +++ b/doc/src/declarative/globalobject.qdoc @@ -220,25 +220,13 @@ provide the ability to access local offline storage in an SQL database. These databases are user-specific and QML-specific. They are stored in the \c Databases subdirectory of QmlEngine::offlineStoragePath(), currently as SQLite databases. -The API is based on the HTML5 offline storage SQL draft API. The main difference is that this QML -API is currently synchronous. You should avoid relying on synchronicity to make your scripts more -portable, both to/from HTML5 and to future QML versions. +The API conforms to the Synchronous API of the HTML5 Web Database API, +\link http://www.w3.org/TR/2009/WD-webdatabase-20091029/ W3C Working Draft 29 October 2009\endlink. The API can be used from JavaScript functions in your QML: \quotefile declarative/sql/hello.qml -\section2 DbObject openDatabase(string name, string version, string description, int estimatedsize) - -Opens a database identified by the given \c name and \c version. -If a database with this name and version does not exist, it is created. The \c version should -always be "1.0" until schema upgrade semantics are defined. - -The \c description and \c estimatedsize are provided to allow application tools to give the user -control over the databases created, but are otherwise not used by QML. - -The returned DbObject has a \c transaction() method by which SQL transactions can be done. - When a database is first created, an INI file is also created specifying its characteristics: \table @@ -247,21 +235,9 @@ When a database is first created, an INI file is also created specifying its cha \row \o Version \o The version of the database passed to \c openDatabase() \row \o Description \o The description of the database passed to \c openDatabase() \row \o EstimatedSize \o The estimated size of the database passed to \c openDatabase() -\row \o DbType \o Currently "QSQLITE" +\row \o Driver \o Currently "QSQLITE" \endtable This data can be used by application tools. - -\section2 void DbObject::transaction(function usesql(DbTxObject), function errorcb=0, function successcb=0) - -Executes \c usesql() in a database transaction. The \c DbTxObject object has a \c executeSql() method by which the -code of \c usesql can execute SQL. The optional second and third arguments are an error callback and success -callback respectively (note that the order of these is the opposite to DbTxObject::executeSql()). - -\section2 void DbTxObject::executeSql(string sql, function successcb=0, function errorcb=0) - -Executes \c sql as an SQL block. The optional second and third arguments are a success callback and error -callback respectively (note that the order of these is the opposite to DbObject::transaction()). - */ diff --git a/examples/declarative/sql/hello.qml b/examples/declarative/sql/hello.qml index dc3ca35..96e0675 100644 --- a/examples/declarative/sql/hello.qml +++ b/examples/declarative/sql/hello.qml @@ -3,35 +3,24 @@ import Qt 4.6 Text { Script { function findGreetings() { - var db = openDatabase("QmlExampleDB", "1.0", "The Example QML SQL!", 1000000); + var db = openDatabaseSync("QmlExampleDB", "1.0", "The Example QML SQL!", 1000000); db.transaction( function(tx) { // Create the database if it doesn't already exist - tx.executeSql('CREATE TABLE IF NOT EXISTS Greeting(salutation TEXT, salutee TEXT)', []); + tx.executeSql('CREATE TABLE IF NOT EXISTS Greeting(salutation TEXT, salutee TEXT)'); // Add (another) greeting row tx.executeSql('INSERT INTO Greeting VALUES(?, ?)', [ 'hello', 'world' ]); // Show all added greetings - tx.executeSql('SELECT * FROM Greeting', [], - function(tx, rs) { - var r = "" - for(var i = 0; i < rs.rows.length; i++) { - r += rs.rows.item(i).salutation + ", " + rs.rows.item(i).salutee + "\n" - } - text = r - }, - function(tx, error) { - print("ERROR:", error.message) - } - ); - }, - function() { - print("ERROR in transaction"); - }, - function() { - print("Transaction successful"); + var rs = tx.executeSql('SELECT * FROM Greeting'); + + var r = "" + for(var i = 0; i < rs.rows.length; i++) { + r += rs.rows.item(i).salutation + ", " + rs.rows.item(i).salutee + "\n" + } + text = r } ) } diff --git a/examples/declarative/tutorials/samegame/samegame4/content/samegame.js b/examples/declarative/tutorials/samegame/samegame4/content/samegame.js index d5778fe..b833385 100755 --- a/examples/declarative/tutorials/samegame/samegame4/content/samegame.js +++ b/examples/declarative/tutorials/samegame/samegame4/content/samegame.js @@ -211,34 +211,22 @@ function saveHighScore(name) { if(scoresURL!="") sendHighScore(name); //OfflineStorage - var db = openDatabase("SameGameScores", "1.0", "Local SameGame High Scores",100); + var db = openDatabaseSync("SameGameScores", "1.0", "Local SameGame High Scores",100); var dataStr = "INSERT INTO Scores VALUES(?, ?, ?, ?)"; var data = [name, gameCanvas.score, maxX+"x"+maxY ,Math.floor(timer/1000)]; db.transaction( function(tx) { - tx.executeSql('CREATE TABLE IF NOT EXISTS Scores(name TEXT, score NUMBER, gridSize TEXT, time NUMBER)',[]); + tx.executeSql('CREATE TABLE IF NOT EXISTS Scores(name TEXT, score NUMBER, gridSize TEXT, time NUMBER)'); tx.executeSql(dataStr, data); - tx.executeSql('SELECT * FROM Scores WHERE gridSize = "12x17" ORDER BY score desc LIMIT 10',[], - function(tx, rs) { - var r = "\nHIGH SCORES for a standard sized grid\n\n" - for(var i = 0; i < rs.rows.length; i++){ - r += (i+1)+". " + rs.rows.item(i).name +' got ' - + rs.rows.item(i).score + ' points in ' - + rs.rows.item(i).time + ' seconds.\n'; - } - dialog.show(r); - }, - function(tx, error) { - print("ERROR:", error.message); - } - ); - }, - function() { - print("ERROR in transaction"); - }, - function() { - //print("Transaction successful"); + var rs = tx.executeSql('SELECT * FROM Scores WHERE gridSize = "12x17" ORDER BY score desc LIMIT 10'); + var r = "\nHIGH SCORES for a standard sized grid\n\n" + for(var i = 0; i < rs.rows.length; i++){ + r += (i+1)+". " + rs.rows.item(i).name +' got ' + + rs.rows.item(i).score + ' points in ' + + rs.rows.item(i).time + ' seconds.\n'; + } + dialog.show(r); } ); } diff --git a/src/declarative/graphicsitems/qmlgraphicswebview.cpp b/src/declarative/graphicsitems/qmlgraphicswebview.cpp index c8bb504..da6c00d 100644 --- a/src/declarative/graphicsitems/qmlgraphicswebview.cpp +++ b/src/declarative/graphicsitems/qmlgraphicswebview.cpp @@ -252,11 +252,9 @@ void QmlGraphicsWebView::pageUrlChanged() { Q_D(QmlGraphicsWebView); - if (d->preferredwidth) { - page()->setViewportSize(QSize(d->preferredwidth,-1)); - } else { - page()->setViewportSize(QSize(-1,-1)); - } + page()->setViewportSize(QSize( + d->preferredwidth>0 ? d->preferredwidth : width(), + d->preferredheight>0 ? d->preferredheight : height())); expandToWebPage(); if ((d->url.isEmpty() && page()->mainFrame()->url() != QUrl(QLatin1String("about:blank"))) @@ -310,11 +308,11 @@ void QmlGraphicsWebView::setUrl(const QUrl &url) if (isComponentComplete()) { d->url = url; - if (d->preferredwidth) { - page()->setViewportSize(QSize(d->preferredwidth,-1)); - } else { - page()->setViewportSize(QSize(-1,-1)); - } + page()->setViewportSize(QSize( + d->preferredwidth>0 ? d->preferredwidth : width(), + d->preferredheight>0 ? d->preferredheight : height())); + if (d->preferredwidth > 0 && d->preferredheight > 0) + page()->setPreferredContentsSize(QSize(d->preferredwidth,d->preferredheight)); QUrl seturl = url; if (seturl.isEmpty()) seturl = QUrl(QLatin1String("about:blank")); @@ -388,6 +386,11 @@ void QmlGraphicsWebView::focusChanged(bool hasFocus) QmlGraphicsItem::focusChanged(hasFocus); } +void QmlGraphicsWebView::initialLayout() +{ + // nothing useful to do at this point +} + void QmlGraphicsWebView::contentsSizeChanged(const QSize&) { expandToWebPage(); @@ -397,9 +400,10 @@ void QmlGraphicsWebView::expandToWebPage() { Q_D(QmlGraphicsWebView); QSize cs = page()->mainFrame()->contentsSize(); - qreal zoom = zoomFactor(); - if (cs.width() < d->preferredwidth*zoom) - cs.setWidth(d->preferredwidth*zoom); + if (cs.width() < d->preferredwidth) + cs.setWidth(d->preferredwidth); + if (cs.height() < d->preferredheight) + cs.setHeight(d->preferredheight); if (widthValid()) cs.setWidth(width()); if (heightValid()) @@ -828,7 +832,9 @@ void QmlGraphicsWebView::setZoomFactor(qreal factor) return; page()->mainFrame()->setZoomFactor(factor); - page()->setViewportSize(QSize(d->preferredwidth ? d->preferredwidth : -1,-1)); + page()->setViewportSize(QSize( + d->preferredwidth>0 ? d->preferredwidth*factor : width()*factor, + d->preferredheight>0 ? d->preferredheight*factor : height()*factor)); expandToWebPage(); emit zoomFactorChanged(); @@ -952,7 +958,8 @@ void QmlGraphicsWebView::setPage(QWebPage *page) } d->page = page; d->page->setViewportSize(QSize( - d->preferredwidth>0 ? d->preferredwidth : -1, -1)); + d->preferredwidth>0 ? d->preferredwidth : width(), + d->preferredheight>0 ? d->preferredheight : height())); d->page->mainFrame()->setScrollBarPolicy(Qt::Horizontal,Qt::ScrollBarAlwaysOff); d->page->mainFrame()->setScrollBarPolicy(Qt::Vertical,Qt::ScrollBarAlwaysOff); connect(d->page,SIGNAL(repaintRequested(QRect)),this,SLOT(paintPage(QRect))); @@ -960,6 +967,7 @@ void QmlGraphicsWebView::setPage(QWebPage *page) connect(d->page->mainFrame(), SIGNAL(titleChanged(QString)), this, SIGNAL(titleChanged(QString))); connect(d->page->mainFrame(), SIGNAL(iconChanged()), this, SIGNAL(iconChanged())); connect(d->page->mainFrame(), SIGNAL(contentsSizeChanged(QSize)), this, SLOT(contentsSizeChanged(QSize))); + connect(d->page->mainFrame(), SIGNAL(initialLayoutCompleted()), this, SLOT(initialLayout())); connect(d->page,SIGNAL(loadStarted()),this,SLOT(doLoadStarted())); connect(d->page,SIGNAL(loadProgress(int)),this,SLOT(doLoadProgress(int))); @@ -1023,7 +1031,8 @@ void QmlGraphicsWebView::setHtml(const QString &html, const QUrl &baseUrl) { Q_D(QmlGraphicsWebView); page()->setViewportSize(QSize( - d->preferredwidth>0 ? d->preferredwidth : width(), height())); + d->preferredwidth>0 ? d->preferredwidth : width(), + d->preferredheight>0 ? d->preferredheight : height())); if (isComponentComplete()) page()->mainFrame()->setHtml(html, baseUrl); else { @@ -1037,7 +1046,8 @@ void QmlGraphicsWebView::setContent(const QByteArray &data, const QString &mimeT { Q_D(QmlGraphicsWebView); page()->setViewportSize(QSize( - d->preferredwidth>0 ? d->preferredwidth : width(), height())); + d->preferredwidth>0 ? d->preferredwidth : width(), + d->preferredheight>0 ? d->preferredheight : height())); if (isComponentComplete()) page()->mainFrame()->setContent(data,mimeType,qmlContext(this)->resolvedUrl(baseUrl)); diff --git a/src/declarative/graphicsitems/qmlgraphicswebview_p.h b/src/declarative/graphicsitems/qmlgraphicswebview_p.h index 1d55830..e2e4888 100644 --- a/src/declarative/graphicsitems/qmlgraphicswebview_p.h +++ b/src/declarative/graphicsitems/qmlgraphicswebview_p.h @@ -208,6 +208,7 @@ private Q_SLOTS: void windowObjectCleared(); void pageUrlChanged(); void contentsSizeChanged(const QSize&); + void initialLayout(); protected: void drawContents(QPainter *, const QRect &); diff --git a/src/declarative/qml/qmlsqldatabase.cpp b/src/declarative/qml/qmlsqldatabase.cpp index 6e32fb7..9e4c198 100644 --- a/src/declarative/qml/qmlsqldatabase.cpp +++ b/src/declarative/qml/qmlsqldatabase.cpp @@ -119,7 +119,7 @@ public: QSqlRecord r = query.record(); QScriptValue row = engine()->newObject(); for (int j=0; j<r.count(); ++j) { - row.setProperty(r.fieldName(j), QScriptValue(engine(),r.value(j).toString())); // XXX only strings + row.setProperty(r.fieldName(j), QScriptValue(engine(),r.value(j).toString())); } return row; } @@ -221,6 +221,45 @@ QScriptClassPropertyIterator *QmlSqlQueryScriptClass::newIterator(const QScriptV return new QmlSqlQueryScriptClassPropertyIterator(object); } +enum SqlException { + UNKNOWN_ERR, + DATABASE_ERR, + VERSION_ERR, + TOO_LARGE_ERR, + QUOTA_ERR, + SYNTAX_ERR, + CONSTRAINT_ERR, + TIMEOUT_ERR +}; + +static const char* sqlerror[] = { + "UNKNOWN_ERR", + "DATABASE_ERR", + "VERSION_ERR", + "TOO_LARGE_ERR", + "QUOTA_ERR", + "SYNTAX_ERR", + "CONSTRAINT_ERR", + "TIMEOUT_ERR", + 0 +}; + +#define THROW_SQL(error, desc) \ +{ \ + QScriptValue errorValue = context->throwError(desc); \ + errorValue.setProperty(QLatin1String("code"), error); \ + return errorValue; \ +} + + +static QString databaseFile(const QString& connectionName, QScriptEngine *engine) +{ + QString basename = QmlEnginePrivate::get(engine)->offlineStoragePath + + QDir::separator() + QLatin1String("Databases") + QDir::separator(); + basename += connectionName; + return basename; +} + static QScriptValue qmlsqldatabase_item(QScriptContext *context, QScriptEngine *engine) @@ -242,80 +281,142 @@ static QScriptValue qmlsqldatabase_executeSql(QScriptContext *context, QScriptEn { QSqlDatabase db = qscriptvalue_cast<QSqlDatabase>(context->thisObject()); QString sql = context->argument(0).toString(); - QScriptValue values = context->argument(1); - QScriptValue cb = context->argument(2); - QScriptValue cberr = context->argument(3); QSqlQuery query(db); bool err = false; + + QScriptValue result; + if (query.prepare(sql)) { - if (values.isObject()) { - for (QScriptValueIterator it(values); it.hasNext();) { - it.next(); - query.bindValue(it.name(),it.value().toVariant()); + if (context->argumentCount() > 1) { + QScriptValue values = context->argument(1); + if (values.isObject()) { + for (QScriptValueIterator it(values); it.hasNext();) { + it.next(); + query.bindValue(it.name(),it.value().toVariant()); + } + } else { + query.bindValue(0,values.toVariant()); } - } else { - query.bindValue(0,values.toVariant()); } if (query.exec()) { - QScriptValue rs = engine->newObject(); + result = engine->newObject(); if (!QmlEnginePrivate::get(engine)->sqlQueryClass) QmlEnginePrivate::get(engine)->sqlQueryClass= new QmlSqlQueryScriptClass(engine); QScriptValue rows = engine->newObject(QmlEnginePrivate::get(engine)->sqlQueryClass); rows.setData(engine->newVariant(qVariantFromValue(query))); rows.setProperty(QLatin1String("item"), engine->newFunction(qmlsqldatabase_item,1), QScriptValue::SkipInEnumeration); - rs.setProperty(QLatin1String("rows"),rows); - rs.setProperty(QLatin1String("rowsAffected"),query.numRowsAffected()); - rs.setProperty(QLatin1String("insertId"),query.lastInsertId().toString()); // XXX only string - cb.call(QScriptValue(), QScriptValueList() << context->thisObject() << rs); + result.setProperty(QLatin1String("rows"),rows); + result.setProperty(QLatin1String("rowsAffected"),query.numRowsAffected()); + result.setProperty(QLatin1String("insertId"),query.lastInsertId().toString()); } else { err = true; } } else { err = true; } - if (err) { - QScriptValue error = engine->newObject(); - error.setProperty(QLatin1String("message"), query.lastError().text()); - cberr.call(QScriptValue(), QScriptValueList() << context->thisObject() << error); + if (err) + THROW_SQL(DATABASE_ERR,query.lastError().text()); + return result; +} + +static QScriptValue qmlsqldatabase_executeSql_readonly(QScriptContext *context, QScriptEngine *engine) +{ + QString sql = context->argument(0).toString(); + if (sql.startsWith(QLatin1String("SELECT"),Qt::CaseInsensitive)) { + qmlsqldatabase_executeSql(context,engine); + } else { + THROW_SQL(SYNTAX_ERR,QmlEngine::tr("Read-only Transaction")) } return engine->undefinedValue(); } -static QScriptValue qmlsqldatabase_transaction(QScriptContext *context, QScriptEngine *engine) +static QScriptValue qmlsqldatabase_change_version(QScriptContext *context, QScriptEngine *engine) { - QSqlDatabase db = qscriptvalue_cast<QSqlDatabase>(context->thisObject()); - if (context->argumentCount() < 1) + if (context->argumentCount() < 2) return engine->undefinedValue(); - QScriptValue cb = context->argument(0); - if (!cb.isFunction()) + + QSqlDatabase db = qscriptvalue_cast<QSqlDatabase>(context->thisObject()); + QString from_version = context->argument(0).toString(); + QString to_version = context->argument(1).toString(); + QScriptValue callback = context->argument(2); + + QScriptValue instance = engine->newObject(); + instance.setProperty(QLatin1String("executeSql"), engine->newFunction(qmlsqldatabase_executeSql,1)); + QScriptValue tx = engine->newVariant(instance,qVariantFromValue(db)); + + if (from_version!=context->thisObject().property(QLatin1String("version")).toString()) { + THROW_SQL(2,QmlEngine::tr("Version mismatch")); return engine->undefinedValue(); + } + + if (callback.isFunction()) { + db.transaction(); + callback.call(QScriptValue(), QScriptValueList() << tx); + if (engine->hasUncaughtException()) { + db.rollback(); + } else { + if (!db.commit()) { + db.rollback(); + THROW_SQL(0,QmlEngine::tr("SQL transaction failed")); + } else { + context->thisObject().setProperty(QLatin1String("version"), to_version, QScriptValue::ReadOnly); + QSettings ini(databaseFile(db.connectionName(),engine)+QLatin1String(".ini"),QSettings::IniFormat); + ini.setValue(QLatin1String("Version"), to_version); + } + } + } + + return engine->undefinedValue(); +} + +static QScriptValue qmlsqldatabase_transaction(QScriptContext *context, QScriptEngine *engine) +{ + QSqlDatabase db = qscriptvalue_cast<QSqlDatabase>(context->thisObject()); + QScriptValue callback = context->argument(0); + if (!callback.isFunction()) + THROW_SQL(UNKNOWN_ERR,QmlEngine::tr("transaction: missing callback")); - // Call synchronously... - XXX could do asynch with threads QScriptValue instance = engine->newObject(); - instance.setProperty(QLatin1String("executeSql"), engine->newFunction(qmlsqldatabase_executeSql,4)); + instance.setProperty(QLatin1String("executeSql"), engine->newFunction(qmlsqldatabase_executeSql,1)); QScriptValue tx = engine->newVariant(instance,qVariantFromValue(db)); db.transaction(); - cb.call(QScriptValue(), QScriptValueList() << tx); + callback.call(QScriptValue(), QScriptValueList() << tx); if (engine->hasUncaughtException()) { db.rollback(); - QScriptValue cb = context->argument(1); - if (cb.isFunction()) - cb.call(); } else { - db.commit(); - QScriptValue cb = context->argument(2); - if (cb.isFunction()) - cb.call(); + if (!db.commit()) + db.rollback(); } return engine->undefinedValue(); } +static QScriptValue qmlsqldatabase_read_transaction(QScriptContext *context, QScriptEngine *engine) +{ + QSqlDatabase db = qscriptvalue_cast<QSqlDatabase>(context->thisObject()); + QScriptValue callback = context->argument(0); + if (!callback.isFunction()) + return engine->undefinedValue(); + + QScriptValue instance = engine->newObject(); + instance.setProperty(QLatin1String("executeSql"), engine->newFunction(qmlsqldatabase_executeSql_readonly,1)); + QScriptValue tx = engine->newVariant(instance,qVariantFromValue(db)); + + db.transaction(); + callback.call(QScriptValue(), QScriptValueList() << tx); + if (engine->hasUncaughtException()) { + db.rollback(); + } else { + if (!db.commit()) + db.rollback(); + } + return engine->undefinedValue(); +} /* Currently documented in doc/src/declarastive/globalobject.qdoc */ -static QScriptValue qmlsqldatabase_open(QScriptContext *context, QScriptEngine *engine) +static QScriptValue qmlsqldatabase_open_sync(QScriptContext *context, QScriptEngine *engine) { QSqlDatabase database; @@ -323,43 +424,71 @@ static QScriptValue qmlsqldatabase_open(QScriptContext *context, QScriptEngine * QString dbversion = context->argument(1).toString(); QString dbdescription = context->argument(2).toString(); int dbestimatedsize = context->argument(3).toNumber(); + QScriptValue dbcreationCallback = context->argument(4); QCryptographicHash md5(QCryptographicHash::Md5); md5.addData(dbname.toUtf8()); - md5.addData(dbversion.toUtf8()); QString dbid(QLatin1String(md5.result().toHex())); - // Uses SQLLITE (like HTML5), but any could be used. + QString basename = databaseFile(dbid,engine); + bool created = false; + QString version = dbversion; - if (QSqlDatabase::connectionNames().contains(dbid)) { - database = QSqlDatabase::database(dbid); - } else { - database = QSqlDatabase::addDatabase(QLatin1String("QSQLITE"), dbid); - } - if (!database.isOpen()) { - QString basename = QmlEnginePrivate::get(engine)->offlineStoragePath - + QDir::separator() + QLatin1String("Databases") + QDir::separator(); - QDir().mkpath(basename); - basename += dbid; - database.setDatabaseName(basename+QLatin1String(".sqlite")); + { QSettings ini(basename+QLatin1String(".ini"),QSettings::IniFormat); - ini.setValue(QLatin1String("Name"), dbname); - ini.setValue(QLatin1String("Version"), dbversion); - ini.setValue(QLatin1String("Description"), dbdescription); - ini.setValue(QLatin1String("EstimatedSize"), dbestimatedsize); - ini.setValue(QLatin1String("DbType"), QLatin1String("QSQLITE")); - database.open(); + + if (QSqlDatabase::connectionNames().contains(dbid)) { + database = QSqlDatabase::database(dbid); + } else { + created = !QFile::exists(basename+QLatin1String(".sqlite")); + database = QSqlDatabase::addDatabase(QLatin1String("QSQLITE"), dbid); + QDir().mkpath(basename); + if (created) { + ini.setValue(QLatin1String("Name"), dbname); + if (dbcreationCallback.isFunction()) + version = QString(); + ini.setValue(QLatin1String("Version"), version); + ini.setValue(QLatin1String("Description"), dbdescription); + ini.setValue(QLatin1String("EstimatedSize"), dbestimatedsize); + ini.setValue(QLatin1String("Driver"), QLatin1String("QSQLITE")); + } else { + if (!dbversion.isEmpty() && ini.value(QLatin1String("Version")) != dbversion) { + // Incompatible + THROW_SQL(VERSION_ERR,QmlEngine::tr("SQL: database version mismatch")); + } + } + database.setDatabaseName(basename+QLatin1String(".sqlite")); + } + if (!database.isOpen()) + database.open(); } QScriptValue instance = engine->newObject(); - instance.setProperty(QLatin1String("transaction"), engine->newFunction(qmlsqldatabase_transaction,3)); - return engine->newVariant(instance,qVariantFromValue(database)); + instance.setProperty(QLatin1String("transaction"), engine->newFunction(qmlsqldatabase_transaction,1)); + instance.setProperty(QLatin1String("readTransaction"), engine->newFunction(qmlsqldatabase_read_transaction,1)); + instance.setProperty(QLatin1String("version"), version, QScriptValue::ReadOnly); + instance.setProperty(QLatin1String("changeVersion"), engine->newFunction(qmlsqldatabase_change_version,3)); + + QScriptValue result = engine->newVariant(instance,qVariantFromValue(database)); + + if (created && dbcreationCallback.isFunction()) { + dbcreationCallback.call(QScriptValue(), QScriptValueList() << result); + } + + return result; } void qt_add_qmlsqldatabase(QScriptEngine *engine) { - QScriptValue openDatabase = engine->newFunction(qmlsqldatabase_open, 4); - engine->globalObject().setProperty(QLatin1String("openDatabase"), openDatabase); + QScriptValue openDatabase = engine->newFunction(qmlsqldatabase_open_sync, 4); + engine->globalObject().setProperty(QLatin1String("openDatabaseSync"), openDatabase); + + QScriptValue sqlExceptionPrototype = engine->newObject(); + for (int i=0; sqlerror[i]; ++i) + sqlExceptionPrototype.setProperty(QLatin1String(sqlerror[i]), + i,QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); + + engine->globalObject().setProperty(QLatin1String("SQLException"), sqlExceptionPrototype); } /* diff --git a/tests/auto/declarative/repeater/data/itemlist.qml b/tests/auto/declarative/repeater/data/itemlist.qml new file mode 100644 index 0000000..8d28bf8 --- /dev/null +++ b/tests/auto/declarative/repeater/data/itemlist.qml @@ -0,0 +1,49 @@ +// This example demonstrates placing items in a view using +// a VisualItemModel + +import Qt 4.6 + +Rectangle { + color: "lightgray" + width: 240 + height: 320 + + function checkProperties() { + testObject.error = false; + if (testObject.useModel && view.model != itemModel) { + print("model property incorrect"); + testObject.error = true; + } + } + + VisualItemModel { + id: itemModel + objectName: "itemModel" + Rectangle { + objectName: "item1" + height: view.height; width: view.width; color: "#FFFEF0" + Text { objectName: "text1"; text: "index: " + parent.VisualItemModel.index; font.bold: true; anchors.centerIn: parent } + } + Rectangle { + objectName: "item2" + height: view.height; width: view.width; color: "#F0FFF7" + Text { objectName: "text2"; text: "index: " + parent.VisualItemModel.index; font.bold: true; anchors.centerIn: parent } + } + Rectangle { + objectName: "item3" + height: view.height; width: view.width; color: "#F4F0FF" + Text { objectName: "text3"; text: "index: " + parent.VisualItemModel.index; font.bold: true; anchors.centerIn: parent } + } + } + + Column { + objectName: "container" + Repeater { + id: view + objectName: "repeater" + anchors.fill: parent + anchors.bottomMargin: 30 + model: testObject.useModel ? itemModel : 0 + } + } +} diff --git a/tests/auto/declarative/sql/data/1-creation.js b/tests/auto/declarative/sql/data/1-creation.js index 95fa99e..aab9b5d 100644 --- a/tests/auto/declarative/sql/data/1-creation.js +++ b/tests/auto/declarative/sql/data/1-creation.js @@ -1,20 +1,15 @@ -var db = openDatabase("QmlTestDB", "", "Test database from Qt autotests", 1000000); -var r="transaction_not_finished"; +function test() { + var db = openDatabaseSync("QmlTestDB", "", "Test database from Qt autotests", 1000000); + var r="transaction_not_finished"; -// Asynchronous in WebKit, so must wait before calling test() -db.transaction( - function(tx) { - tx.executeSql('CREATE TABLE IF NOT EXISTS Greeting(salutation TEXT, salutee TEXT)', [], - function(tx, rs) { }, function(tx, error) { r="CREATE FAILED: "+error.message }); - tx.executeSql('INSERT INTO Greeting VALUES(?, ?)', [ 'hello', 'world' ], - function(tx, rs) { }, function(tx, error) { r="INSERT FAILED: "+error.message }); - }, - function(tx, error) { r="TRANSACTION FAILED: "+error.message }, - function(tx, result) { if (r=="transaction_not_finished") r="passed" } -); + // Asynchronous in WebKit, so must wait before calling test() + db.transaction( + function(tx) { + tx.executeSql('CREATE TABLE IF NOT EXISTS Greeting(salutation TEXT, salutee TEXT)'); + tx.executeSql('INSERT INTO Greeting VALUES(?, ?)', [ 'hello', 'world' ]); + r = "passed"; + } + ); - -function test() -{ return r; } diff --git a/tests/auto/declarative/sql/data/2-selection-bindnames.js b/tests/auto/declarative/sql/data/2-selection-bindnames.js index c00acc14..21f34db 100644 --- a/tests/auto/declarative/sql/data/2-selection-bindnames.js +++ b/tests/auto/declarative/sql/data/2-selection-bindnames.js @@ -1,24 +1,16 @@ -var db = openDatabase("QmlTestDB", "", "Test database from Qt autotests", 1000000); -var r=0; +function test() { + var db = openDatabaseSync("QmlTestDB", "", "Test database from Qt autotests", 1000000); + var r="transaction_not_finished"; -db.transaction( - function(tx) { - tx.executeSql('SELECT * FROM Greeting WHERE salutation=:p2 AND salutee=:p1', {':p1':'world', ':p2':'hello'}, - function(tx, rs) { - if ( rs.rows.length != 4 ) { - if (r==0) r = "SELECT RETURNED WRONG VALUE "+rs.rows.length+rs.rows.item(0)+rs.rows.item(1) - } - }, - function(tx, error) { if (r==0) r="SELECT FAILED: "+error.message } - ); - }, - function(tx, error) { if (r==0) r="TRANSACTION FAILED: "+error.message }, - function(tx, result) { if (r==0) r="passed" } -); + db.transaction( + function(tx) { + var rs = tx.executeSql('SELECT * FROM Greeting WHERE salutation=:p2 AND salutee=:p1', {':p1':'world', ':p2':'hello'}); + if ( rs.rows.length != 4 ) + r = "SELECT RETURNED WRONG VALUE "+rs.rows.length+rs.rows.item(0)+rs.rows.item(1) + else + r = "passed"; + } + ); - -function test() -{ - if (r == 0) r = "transaction_not_finished"; return r; } diff --git a/tests/auto/declarative/sql/data/2-selection.js b/tests/auto/declarative/sql/data/2-selection.js index 3acf686..f141d2c 100644 --- a/tests/auto/declarative/sql/data/2-selection.js +++ b/tests/auto/declarative/sql/data/2-selection.js @@ -1,30 +1,19 @@ -var db = openDatabase("QmlTestDB", "", "Test database from Qt autotests", 1000000); -var r=0; +function test() { + var db = openDatabaseSync("QmlTestDB", "", "Test database from Qt autotests", 1000000); + var r="transaction_not_finished"; -db.transaction( - function(tx) { - tx.executeSql('INSERT INTO Greeting VALUES(?, ?)', [ 'hello', 'world' ], - function(tx, rs) { }, function(tx, error) { if (r==0) r="INSERT 1 FAILED: "+error.message }); - tx.executeSql('INSERT INTO Greeting VALUES(?, ?)', [ 'hello', 'world' ], - function(tx, rs) { }, function(tx, error) { if (r==0) r="INSERT 2 FAILED: "+error.message }); - tx.executeSql('INSERT INTO Greeting VALUES(?, ?)', [ 'hello', 'world' ], - function(tx, rs) { }, function(tx, error) { if (r==0) r="INSERT 3 FAILED: "+error.message }); - tx.executeSql('SELECT * FROM Greeting', [], - function(tx, rs) { - if ( rs.rows.length != 4 ) { // 1 from test1, 3 from this test. - if (r==0) r = "SELECT RETURNED WRONG VALUE "+rs.rows.length+rs.rows[0]+rs.rows[1] - } - }, - function(tx, error) { if (r==0) r="SELECT FAILED: "+error.message } - ); - }, - function(tx, error) { if (r==0) r="TRANSACTION FAILED: "+error.message }, - function(tx, result) { if (r==0) r="passed" } -); + db.transaction( + function(tx) { + tx.executeSql('INSERT INTO Greeting VALUES(?, ?)', [ 'hello', 'world' ]); + tx.executeSql('INSERT INTO Greeting VALUES(?, ?)', [ 'hello', 'world' ]); + tx.executeSql('INSERT INTO Greeting VALUES(?, ?)', [ 'hello', 'world' ]); + var rs = tx.executeSql('SELECT * FROM Greeting'); + if ( rs.rows.length != 4 ) // 1 from test1, 3 from this test. + r = "SELECT RETURNED WRONG VALUE "+rs.rows.length+rs.rows[0]+rs.rows[1] + else + r = "passed"; + } + ); - -function test() -{ - if (r == 0) r = "transaction_not_finished"; return r; } diff --git a/tests/auto/declarative/sql/data/3-iteration-item-function.js b/tests/auto/declarative/sql/data/3-iteration-item-function.js index bad9b82..57c8a17 100644 --- a/tests/auto/declarative/sql/data/3-iteration-item-function.js +++ b/tests/auto/declarative/sql/data/3-iteration-item-function.js @@ -1,27 +1,19 @@ -var db = openDatabase("QmlTestDB", "", "Test database from Qt autotests", 1000000); -var r=0; +function test() { + var db = openDatabaseSync("QmlTestDB", "", "Test database from Qt autotests", 1000000); + var r="transaction_not_finished"; -db.transaction( - function(tx) { - tx.executeSql('SELECT * FROM Greeting', [], - function(tx, rs) { - var r1="" - for(var i = 0; i < rs.rows.length; i++) { - r1 += rs.rows.item(i).salutation + ", " + rs.rows.item(i).salutee + ";" - } - if (r1 != "hello, world;hello, world;hello, world;hello, world;") - r = "SELECTED DATA WRONG: "+r1; - }, - function(tx, error) { if (r==0) r="SELECT FAILED: "+error.message } - ); - }, - function(tx, error) { if (r==0) r="TRANSACTION FAILED: "+error.message }, - function(tx, result) { if (r==0) r="passed" } -); + db.transaction( + function(tx) { + var rs = tx.executeSql('SELECT * FROM Greeting'); + var r1="" + for(var i = 0; i < rs.rows.length; i++) + r1 += rs.rows.item(i).salutation + ", " + rs.rows.item(i).salutee + ";" + if (r1 != "hello, world;hello, world;hello, world;hello, world;") + r = "SELECTED DATA WRONG: "+r1; + else + r = "passed"; + } + ); - -function test() -{ - if (r == 0) r = "transaction_not_finished"; return r; } diff --git a/tests/auto/declarative/sql/data/4-iteration-index.js b/tests/auto/declarative/sql/data/4-iteration-index.js index 298737d..512cf8d 100644 --- a/tests/auto/declarative/sql/data/4-iteration-index.js +++ b/tests/auto/declarative/sql/data/4-iteration-index.js @@ -1,27 +1,19 @@ -var db = openDatabase("QmlTestDB", "", "Test database from Qt autotests", 1000000); -var r=0; +function test() { + var db = openDatabaseSync("QmlTestDB", "", "Test database from Qt autotests", 1000000); + var r="transaction_not_finished"; -db.transaction( - function(tx) { - tx.executeSql('SELECT * FROM Greeting', [], - function(tx, rs) { - var r1="" - for(var i = 0; i < rs.rows.length; i++) { - r1 += rs.rows[i].salutation + ", " + rs.rows[i].salutee + ";" - } - if (r1 != "hello, world;hello, world;hello, world;hello, world;") - r = "SELECTED DATA WRONG: "+r1; - }, - function(tx, error) { if (r==0) r="SELECT FAILED: "+error.message } - ); - }, - function(tx, error) { if (r==0) r="TRANSACTION FAILED: "+error.message }, - function(tx, result) { if (r==0) r="passed" } -); + db.transaction( + function(tx) { + var rs = tx.executeSql('SELECT * FROM Greeting'); + var r1="" + for(var i = 0; i < rs.rows.length; i++) + r1 += rs.rows[i].salutation + ", " + rs.rows[i].salutee + ";" + if (r1 != "hello, world;hello, world;hello, world;hello, world;") + r = "SELECTED DATA WRONG: "+r1; + else + r = "passed"; + } + ); - -function test() -{ - if (r == 0) r = "transaction_not_finished"; return r; } diff --git a/tests/auto/declarative/sql/data/5-iteration-iterator.js b/tests/auto/declarative/sql/data/5-iteration-iterator.js index 51f0504..ae4fd34 100644 --- a/tests/auto/declarative/sql/data/5-iteration-iterator.js +++ b/tests/auto/declarative/sql/data/5-iteration-iterator.js @@ -1,27 +1,19 @@ -var db = openDatabase("QmlTestDB", "", "Test database from Qt autotests", 1000000); -var r=0; +function test() { + var db = openDatabaseSync("QmlTestDB", "", "Test database from Qt autotests", 1000000); + var r="transaction_not_finished"; -db.transaction( - function(tx) { - tx.executeSql('SELECT * FROM Greeting', [], - function(tx, rs) { - var r1="" - for(var i in rs.rows) { - r1 += rs.rows[i].salutation + ", " + rs.rows[i].salutee + ";" - } - if (r1 != "hello, world;hello, world;hello, world;hello, world;") - r = "SELECTED DATA WRONG: "+r1; - }, - function(tx, error) { if (r==0) r="SELECT FAILED: "+error.message } - ); - }, - function(tx, error) { if (r==0) r="TRANSACTION FAILED: "+error.message }, - function(tx, result) { if (r==0) r="passed" } -); + db.transaction( + function(tx) { + var rs = tx.executeSql('SELECT * FROM Greeting') + var r1="" + for(var i in rs.rows) + r1 += rs.rows[i].salutation + ", " + rs.rows[i].salutee + ";" + if (r1 != "hello, world;hello, world;hello, world;hello, world;") + r = "SELECTED DATA WRONG: "+r1; + else + r = "passed"; + } + ); - -function test() -{ - if (r == 0) r = "transaction_not_finished"; return r; } diff --git a/tests/auto/declarative/sql/data/6-iteration-efficient.js b/tests/auto/declarative/sql/data/6-iteration-efficient.js index 6711fb0..fe0acfc 100644 --- a/tests/auto/declarative/sql/data/6-iteration-efficient.js +++ b/tests/auto/declarative/sql/data/6-iteration-efficient.js @@ -1,32 +1,29 @@ -var db = openDatabase("QmlTestDB", "", "Test database from Qt autotests", 1000000); -var r=0; -var fbefore="FORWARD WRONG" -var fafter="FORWARD WRONG" +function test() { + var db = openDatabaseSync("QmlTestDB", "", "Test database from Qt autotests", 1000000); + var r="transaction_not_finished"; -db.transaction( - function(tx) { - tx.executeSql('SELECT * FROM Greeting', [], - function(tx, rs) { - var r1="" - if (!rs.rows.forwardOnly) fbefore="" - rs.rows.forwardOnly = true; - if (rs.rows.forwardOnly) fafter=""; + db.transaction( + function(tx) { + var rs = tx.executeSql('SELECT * FROM Greeting'); + var r1="" + var fbefiore = rs.rows.forwardOnly; + rs.rows.forwardOnly = true; + var fafter = rs.rows.forwardOnly; + if (fbefore) + r = "forward wrong before"; + else if (!fafter) + r = "forward wrong after"; + else { for(var i=0; rs.rows[i]; ++i) { r1 += rs.rows[i].salutation + ", " + rs.rows[i].salutee + ";" } if (r1 != "hello, world;hello, world;hello, world;hello, world;") r = "SELECTED DATA WRONG: "+r1; - }, - function(tx, error) { if (r==0) r="SELECT FAILED: "+error.message } - ); - }, - function(tx, error) { if (r==0) r="TRANSACTION FAILED: "+error.message }, - function(tx, result) { if (r==0) r=fbefore+"passed"+fafter } -); + else + r = "passed"; + } + } + ); - -function test() -{ - if (r == 0) r = "transaction_not_finished"; return r; } diff --git a/tests/auto/declarative/sql/data/7a-error.js b/tests/auto/declarative/sql/data/7a-error.js new file mode 100644 index 0000000..65d0c03 --- /dev/null +++ b/tests/auto/declarative/sql/data/7a-error.js @@ -0,0 +1,20 @@ +function test() { + var db = openDatabaseSync("QmlTestDB", "", "Test database from Qt autotests", 1000000); + var r="transaction_not_finished"; + + try { + db.transaction( + function(tx) { + var rs = tx.executeSql('SELECT * FROM NotExists'); + r = "SHOULD NOT SUCCEED"; + } + ); + } catch (err) { + if (err.message == "no such table: NotExists Unable to execute statement") + r = "passed"; + else + r = "WRONG ERROR="+err.message; + } + + return r; +} diff --git a/tests/auto/declarative/sql/tst_sql.cpp b/tests/auto/declarative/sql/tst_sql.cpp index 973d7b1..cbd14ab 100644 --- a/tests/auto/declarative/sql/tst_sql.cpp +++ b/tests/auto/declarative/sql/tst_sql.cpp @@ -144,20 +144,23 @@ void tst_sql::testQml_data() QTest::newRow("iteration-index") << "data/4-iteration-index.js" << "passed" << 1 << true; // Some HTML5 documents say to use rows by index, others by item() function QTest::newRow("iteration-iterator") << "data/5-iteration-iterator.js" << "passed" << 1 << true; // As with previous, WebKit doesn't give an array QTest::newRow("iteration-efficient") << "data/6-iteration-efficient.js" << "passed" << 1 << true; // It's very inefficient to find the total number of results, here is a solution + QTest::newRow("error-a") << "data/7a-error.js" << "passed" << 1 << false; } void tst_sql::validateAgainstWebkit_data() { - testQml_data(); + QTest::addColumn<QString>("jsfile"); // The input file + QTest::addColumn<QString>("result"); // The required output from the js test() function + QTest::addColumn<int>("databases"); // The number of databases that should have been created + QTest::addColumn<bool>("qmlextension"); // Things WebKit can't do + QTest::newRow("creation") << "data/1-creation.js" << "passed" << 1 << false; } void tst_sql::validateAgainstWebkit() { // Validates tests against WebKit (HTML5) support. // - // WebKit SQL is asynchronous, so tests are divided into code plus a test() - // function which is executed "later" (via QTRY_). - // + QFETCH(QString, jsfile); QFETCH(QString, result); QFETCH(int, databases); @@ -174,14 +177,15 @@ void tst_sql::validateAgainstWebkit() webpage.settings()->setOfflineStoragePath(dbDir()); webpage.settings()->setAttribute(QWebSettings::OfflineStorageDatabaseEnabled, true); - webpage.mainFrame()->evaluateJavaScript(js); - QTest::qWait(200); // WebKit db access is asynchronous - QTRY_COMPARE(webpage.mainFrame()->evaluateJavaScript("test()").toString(),result); - QTest::qWait(200); // WebKit crashes if you quit it too fast + QEXPECT_FAIL("","WebKit doesn't support openDatabaseSync yet", Continue); + QCOMPARE(webpage.mainFrame()->evaluateJavaScript(js).toString(),result); + /* + QTest::qWait(100); // WebKit crashes if you quit it too fast QWebSecurityOrigin origin = webpage.mainFrame()->securityOrigin(); QList<QWebDatabase> dbs = origin.databases(); QCOMPARE(dbs.count(), databases); + */ } void tst_sql::testQml() |