summaryrefslogtreecommitdiff
path: root/src/location/maps/qgeomapscene.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/location/maps/qgeomapscene.cpp')
-rw-r--r--src/location/maps/qgeomapscene.cpp438
1 files changed, 195 insertions, 243 deletions
diff --git a/src/location/maps/qgeomapscene.cpp b/src/location/maps/qgeomapscene.cpp
index cb24861f..3d256734 100644
--- a/src/location/maps/qgeomapscene.cpp
+++ b/src/location/maps/qgeomapscene.cpp
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2014 Jolla Ltd, author: <gunnar.sletta@jollamobile.com>
** Contact: http://www.qt-project.org/legal
**
** This file is part of the QtLocation module of the Qt Toolkit.
@@ -49,15 +50,8 @@
#include <QtPositioning/private/qdoublevector2d_p.h>
#include <QtPositioning/private/qdoublevector3d_p.h>
-#include <Qt3D/qglscenenode.h>
-#include <Qt3D/qglbuilder.h>
-#include <Qt3D/qglmaterial.h>
-#include <Qt3D/qgltexture2d.h>
-#include <Qt3D/qgeometrydata.h>
-#include <Qt3D/qglcamera.h>
-#include <Qt3D/qglpainter.h>
-#include <Qt3D/QGLLightParameters>
-
+#include <QtQuick/QSGSimpleTextureNode>
+#include <QtQuick/QQuickWindow>
#include <QHash>
@@ -78,9 +72,10 @@ public:
QGeoCameraData cameraData_;
QSet<QGeoTileSpec> visibleTiles_;
- QGLCamera *camera_;
- QGLSceneNode *sceneNode_;
- QGLLightParameters* light_;
+ QDoubleVector3D cameraUp_;
+ QDoubleVector3D cameraEye_;
+ QDoubleVector3D cameraCenter_;
+ QMatrix4x4 projectionMatrix_;
// scales up the tile geometry and the camera altitude, resulting in no visible effect
// other than to control the accuracy of the render by keeping the values in a sensible range
@@ -94,9 +89,7 @@ public:
// it is 1<<zoomLevel
int sideLength_;
- QHash<QGeoTileSpec, QGLSceneNode *> nodes_;
QHash<QGeoTileSpec, QSharedPointer<QGeoTileTexture> > textures_;
- QList<QSharedPointer<QGeoTileTexture> > newUploads_;
// tilesToGrid transform
int minTileX_; // the minimum tile index, i.e. 0 to sideLength which is 1<< zoomLevel
@@ -130,14 +123,9 @@ public:
void setVisibleTiles(const QSet<QGeoTileSpec> &tiles);
void removeTiles(const QSet<QGeoTileSpec> &oldTiles);
- void updateTiles(const QSet<QGeoTileSpec> &tiles);
- QGeometryData buildGeometry(const QGeoTileSpec &spec);
- QGLSceneNode *buildSceneNodeFromGeometry(const QGeometryData &geom);
+ bool buildGeometry(const QGeoTileSpec &spec, QSGGeometry::TexturedPoint2D *vertices);
void setTileBounds(const QSet<QGeoTileSpec> &tiles);
void setupCamera();
- void setScalingOnTextures();
-
- void paintGL(QGLPainter *painter);
private:
QGeoMapScene *q_ptr;
@@ -177,13 +165,7 @@ void QGeoMapScene::setCameraData(const QGeoCameraData &cameraData)
d->cameraData_ = cameraData;
d->intZoomLevel_ = static_cast<int>(std::floor(d->cameraData_.zoomLevel()));
float delta = cameraData.zoomLevel() - d->intZoomLevel_;
- if (qAbs(delta) < 0.05) {
- d->linearScaling_ = false;
- d->setScalingOnTextures();
- } else {
- d->linearScaling_ = true;
- d->setScalingOnTextures();
- }
+ d->linearScaling_ = qAbs(delta) > 0.05;
d->sideLength_ = 1 << d->intZoomLevel_;
}
@@ -211,30 +193,12 @@ QDoubleVector2D QGeoMapScene::mercatorToScreenPosition(const QDoubleVector2D &me
return d->mercatorToScreenPosition(mercator);
}
-QGLCamera *QGeoMapScene::camera() const
-{
- Q_D(const QGeoMapScene);
- return d->camera_;
-}
-
-QGLSceneNode *QGeoMapScene::sceneNode() const
-{
- Q_D(const QGeoMapScene);
- return d->sceneNode_;
-}
-
bool QGeoMapScene::verticalLock() const
{
Q_D(const QGeoMapScene);
return d->verticalLock_;
}
-void QGeoMapScene::paintGL(QGLPainter *painter)
-{
- Q_D(QGeoMapScene);
- d->paintGL(painter);
-}
-
QSet<QGeoTileSpec> QGeoMapScene::texturedTiles()
{
Q_D(QGeoMapScene);
@@ -247,9 +211,6 @@ QSet<QGeoTileSpec> QGeoMapScene::texturedTiles()
QGeoMapScenePrivate::QGeoMapScenePrivate(QGeoMapScene *scene)
: tileSize_(0),
- camera_(new QGLCamera()),
- sceneNode_(new QGLSceneNode()),
- light_(new QGLLightParameters()),
scaleFactor_(10.0),
intZoomLevel_(0),
sideLength_(0),
@@ -269,14 +230,11 @@ QGeoMapScenePrivate::QGeoMapScenePrivate(QGeoMapScene *scene)
screenHeight_(0.0),
useVerticalLock_(false),
verticalLock_(false),
- linearScaling_(true),
+ linearScaling_(false),
q_ptr(scene) {}
QGeoMapScenePrivate::~QGeoMapScenePrivate()
{
- delete sceneNode_;
- delete camera_;
- delete light_;
}
QDoubleVector2D QGeoMapScenePrivate::screenPositionToMercator(const QDoubleVector2D &pos) const
@@ -340,7 +298,7 @@ QDoubleVector2D QGeoMapScenePrivate::mercatorToScreenPosition(const QDoubleVecto
return QDoubleVector2D(x + screenOffsetX_, y + screenOffsetY_);
}
-QGeometryData QGeoMapScenePrivate::buildGeometry(const QGeoTileSpec &spec)
+bool QGeoMapScenePrivate::buildGeometry(const QGeoTileSpec &spec, QSGGeometry::TexturedPoint2D *vertices)
{
int x = spec.x();
@@ -352,7 +310,7 @@ QGeometryData QGeoMapScenePrivate::buildGeometry(const QGeoTileSpec &spec)
|| (spec.y() < minTileY_)
|| (maxTileY_ < spec.y())
|| (spec.zoom() != tileZ_)) {
- return 0;
+ return false;
}
double edge = scaleFactor_ * tileSize_;
@@ -368,102 +326,21 @@ QGeometryData QGeoMapScenePrivate::buildGeometry(const QGeoTileSpec &spec)
y1 *= edge;
y2 *= edge;
- QGeometryData g;
-
- QDoubleVector3D n = QDoubleVector3D(0, 0, 1);
-
//Texture coordinate order for veritcal flip of texture
- g.appendVertex(QVector3D(x1, y1, 0.0));
- g.appendNormal(n);
- g.appendTexCoord(QVector2D(0.0, 0.0));
-
- g.appendVertex(QVector3D(x1, y2, 0.0));
- g.appendNormal(n);
- g.appendTexCoord(QVector2D(0.0, 1.0));
-
- g.appendVertex(QVector3D(x2, y2, 0.0));
- g.appendNormal(n);
- g.appendTexCoord(QVector2D(1.0, 1.0));
+ vertices[0].set(x1, y1, 0, 0);
+ vertices[1].set(x1, y2, 0, 1);
+ vertices[2].set(x2, y1, 1, 0);
+ vertices[3].set(x2, y2, 1, 1);
- g.appendVertex(QVector3D(x2, y1, 0.0));
- g.appendNormal(n);
- g.appendTexCoord(QVector2D(1.0, 0.0));
-
- return g;
-}
-
-QGLSceneNode *QGeoMapScenePrivate::buildSceneNodeFromGeometry(const QGeometryData &geom)
-{
- QGLBuilder builder;
- builder.addQuads(geom);
- return builder.finalizedSceneNode();
-}
-
-
-void QGeoMapScenePrivate::setScalingOnTextures()
-{
- if (!linearScaling_) {
- foreach (const QSharedPointer<QGeoTileTexture> &tex, textures_.values()) {
- tex->texture->setBindOptions(tex->texture->bindOptions() &
- (~QGLTexture2D::LinearFilteringBindOption));
- }
- } else {
- foreach (const QSharedPointer<QGeoTileTexture> &tex, textures_.values()) {
- tex->texture->setBindOptions(tex->texture->bindOptions() |
- (QGLTexture2D::LinearFilteringBindOption));
- }
- }
+ return true;
}
void QGeoMapScenePrivate::addTile(const QGeoTileSpec &spec, QSharedPointer<QGeoTileTexture> texture)
{
if (!visibleTiles_.contains(spec)) // Don't add the geometry if it isn't visible
return;
- if (linearScaling_) {
- texture->texture->setBindOptions(texture->texture->bindOptions() |
- (QGLTexture2D::LinearFilteringBindOption));
- } else {
- texture->texture->setBindOptions(texture->texture->bindOptions() &
- (~QGLTexture2D::LinearFilteringBindOption));
- }
- //Avoid expensive conversion of ARGB32_Premultiplied to ARGB32
- if (texture->texture->image().format() == QImage::Format_ARGB32_Premultiplied) {
- texture->texture->setBindOptions(texture->texture->bindOptions() |
- (QGLTexture2D::PremultipliedAlphaBindOption));
- }
-
- //There are tiles for different zoom levels, no need for mipmaps
- texture->texture->setBindOptions(texture->texture->bindOptions() & (~QGLTexture2D::MipmapBindOption));
-
- //We flip the texture coordinates instead of the texture
- texture->texture->setBindOptions(texture->texture->bindOptions() & (~QGLTexture2D::InvertedYBindOption));
-
- QGLSceneNode *node = nodes_.value(spec, 0);
- if (!node) {
- QGeometryData geom = buildGeometry(spec);
- node = buildSceneNodeFromGeometry(geom);
- if (!node)
- return;
-
- QGLMaterial *mat = new QGLMaterial(node);
- mat->setTexture(texture->texture);
- node->setEffect(QGL::LitDecalTexture2D);
- node->setMaterial(mat);
-
- sceneNode_->addNode(node);
- nodes_.insert(spec, node);
- textures_.insert(spec, texture);
- newUploads_ << texture;
-
- } else {
- // TODO handle texture updates when we make node removal more efficient
- if (textures_[spec].data() != texture.data()) {
- textures_.insert(spec, texture);
- node->material()->setTexture(texture->texture);
- newUploads_ << texture;
- }
- }
+ textures_.insert(spec, texture);
}
// return true if new tiles introduced in [tiles]
@@ -485,51 +362,11 @@ void QGeoMapScenePrivate::setVisibleTiles(const QSet<QGeoTileSpec> &tiles)
if (!toRemove.isEmpty())
removeTiles(toRemove);
- // only need to update tiles when the bounds have changed,
- if (visibleTiles_ != tiles && !toUpdate.isEmpty())
- updateTiles(toUpdate);
-
visibleTiles_ = tiles;
if (newTilesIntroduced)
emit q->newTilesVisible(visibleTiles_);
}
-void QGeoMapScenePrivate::updateTiles(const QSet<QGeoTileSpec> &tiles)
-{
- typedef QSet<QGeoTileSpec>::const_iterator iter;
- iter i = tiles.constBegin();
- iter end = tiles.constEnd();
- for (; i != end; ++i) {
- QGeoTileSpec tile = *i;
- QGLSceneNode *node = nodes_.value(tile, 0);
-
- if (node) {
- QGeometryData geom = buildGeometry(tile);
- // if the new geometry (after wrapping) is the same as the old one,
- // it can be reused
- if ( node->children().size() > 0) {
- if (node->children().front()->geometry() == geom)
- continue;
- }
-
- sceneNode_->removeNode(node);
- QGLSceneNode *newNode = buildSceneNodeFromGeometry(geom);
- if (newNode) {
- QGLMaterial *mat = new QGLMaterial(newNode);
- mat->setTexture(textures_[tile]->texture);
- newNode->setEffect(QGL::LitDecalTexture2D);
- newNode->setMaterial(mat);
- sceneNode_->addNode(newNode);
- nodes_.insert(tile, newNode);
- } else {
- nodes_.remove(tile);
- textures_.remove(tile);
- }
- delete node;
- }
- }
-}
-
void QGeoMapScenePrivate::removeTiles(const QSet<QGeoTileSpec> &oldTiles)
{
typedef QSet<QGeoTileSpec>::const_iterator iter;
@@ -538,14 +375,7 @@ void QGeoMapScenePrivate::removeTiles(const QSet<QGeoTileSpec> &oldTiles)
for (; i != end; ++i) {
QGeoTileSpec tile = *i;
- QGLSceneNode *node = nodes_.value(tile, 0);
- if (node) {
- // TODO protect with mutex?
- sceneNode_->removeNode(node);
- nodes_.remove(tile);
- textures_.remove(tile);
- delete node;
- }
+ textures_.remove(tile);
}
}
@@ -629,7 +459,6 @@ void QGeoMapScenePrivate::setTileBounds(const QSet<QGeoTileSpec> &tiles)
void QGeoMapScenePrivate::setupCamera()
{
-
double f = 1.0 * qMin(screenSize_.width(), screenSize_.height());
// fraction of zoom level
@@ -734,81 +563,204 @@ void QGeoMapScenePrivate::setupCamera()
double nearPlane = 1.0;
double farPlane = 4.0 * edge;
- // TODO protect with mutex?
- // set glcamera parameters
- camera_->setCenter(center);
- camera_->setEye(eye);
- camera_->setUpVector(up);
- camera_->setNearPlane(nearPlane);
- camera_->setFarPlane(farPlane);
+ cameraUp_ = up;
+ cameraCenter_ = center;
+ cameraEye_ = eye;
+
+ float halfWidth = 1;
+ float halfHeight = 1;
+ if (aspectRatio > 1.0) {
+ halfWidth *= aspectRatio;
+ } else if (aspectRatio > 0.0f && aspectRatio < 1.0f) {
+ halfHeight /= aspectRatio;
+ }
+ projectionMatrix_.setToIdentity();
+ projectionMatrix_.frustum(-halfWidth, halfWidth, -halfHeight, halfHeight, nearPlane, farPlane);
}
-void QGeoMapScenePrivate::paintGL(QGLPainter *painter)
+class QGeoMapTileContainerNode : public QSGTransformNode
{
- // TODO protect with mutex?
-
- // TODO add a shortcut here for when we don't need to repeat and clip the map
- // NOTE- this is important as the repeat code below removes a lot of accuracy
- // by converting to float and adding/removing large numbers when at high zoom
-
- // do any pending upload/releases
- while (!newUploads_.isEmpty()) {
- if (!newUploads_.front()->textureBound) {
- newUploads_.front()->texture->bind();
- newUploads_.front()->texture->clearImage();
- newUploads_.front()->textureBound = true;
- }
- newUploads_.pop_front();
+public:
+ void addChild(const QGeoTileSpec &spec, QSGSimpleTextureNode *node)
+ {
+ tiles.insert(spec, node);
+ appendChildNode(node);
}
+ QHash<QGeoTileSpec, QSGSimpleTextureNode *> tiles;
+};
- glEnable(GL_SCISSOR_TEST);
+class QGeoMapRootNode : public QSGClipNode
+{
+public:
+ QGeoMapRootNode()
+ : isTextureLinear(false)
+ , geometry(QSGGeometry::defaultAttributes_Point2D(), 4)
+ , root(new QSGTransformNode())
+ , tiles(new QGeoMapTileContainerNode())
+ , wrapLeft(new QGeoMapTileContainerNode())
+ , wrapRight(new QGeoMapTileContainerNode())
+ {
+ setIsRectangular(true);
+ setGeometry(&geometry);
+ root->appendChildNode(tiles);
+ root->appendChildNode(wrapLeft);
+ root->appendChildNode(wrapRight);
+ appendChildNode(root);
+ }
- painter->setScissor(QRect(screenOffsetX_, screenOffsetY_, screenWidth_, screenHeight_));
+ ~QGeoMapRootNode()
+ {
+ qDeleteAll(textures.values());
+ }
- painter->setCamera(camera_);
+ void setClipRect(const QRect &rect)
+ {
+ if (rect != clipRect) {
+ QSGGeometry::updateRectGeometry(&geometry, rect);
+ QSGClipNode::setClipRect(rect);
+ clipRect = rect;
+ markDirty(DirtyGeometry);
+ }
+ }
- painter->setMainLight(light_);
+ void updateTiles(QGeoMapTileContainerNode *root, QGeoMapScenePrivate *d, double camAdjust);
- sceneNode_->draw(painter);
+ bool isTextureLinear;
- QGLCamera *camera = camera_;
+ QSGGeometry geometry;
+ QRect clipRect;
- bool old = camera->blockSignals(true);
+ QSGTransformNode *root;
- glDisable(GL_DEPTH_TEST);
+ QGeoMapTileContainerNode *tiles; // The majority of the tiles
+ QGeoMapTileContainerNode *wrapLeft; // When zoomed out, the tiles that wrap around on the left.
+ QGeoMapTileContainerNode *wrapRight; // When zoomed out, the tiles that wrap around on the right
- double sideLength = scaleFactor_ * tileSize_ * sideLength_;
+ QHash<QGeoTileSpec, QSGTexture *> textures;
+};
- QDoubleVector3D c = QDoubleVector3D(camera->center());
- c.setX(c.x() + sideLength);
- camera->setCenter(c);
+static bool qgeomapscene_isTileInViewport(const QSGGeometry::TexturedPoint2D *tp, const QMatrix4x4 &matrix) {
+ QPolygonF polygon; polygon.reserve(4);
+ for (int i=0; i<4; ++i)
+ polygon << matrix * QPointF(tp[i].x, tp[i].y);
+ return QRectF(-1, -1, 2, 2).intersects(polygon.boundingRect());
+}
- QDoubleVector3D e = QDoubleVector3D(camera->eye());
- e.setX(e.x() + sideLength);
- camera->setEye(e);
+void QGeoMapRootNode::updateTiles(QGeoMapTileContainerNode *root,
+ QGeoMapScenePrivate *d,
+ double camAdjust)
+{
+ // Set up the matrix...
+ QDoubleVector3D eye = d->cameraEye_;
+ eye.setX(eye.x() + camAdjust);
+ QDoubleVector3D center = d->cameraCenter_;
+ center.setX(center.x() + camAdjust);
+ QMatrix4x4 cameraMatrix;
+ cameraMatrix.lookAt(eye, center, d->cameraUp_);
+ root->setMatrix(d->projectionMatrix_ * cameraMatrix);
+
+ QSet<QGeoTileSpec> tilesInSG = QSet<QGeoTileSpec>::fromList(root->tiles.keys());
+ QSet<QGeoTileSpec> toRemove = tilesInSG - d->visibleTiles_;
+ QSet<QGeoTileSpec> toAdd = d->visibleTiles_ - tilesInSG;
+
+ foreach (const QGeoTileSpec &s, toRemove)
+ delete root->tiles.take(s);
+
+ for (QHash<QGeoTileSpec, QSGSimpleTextureNode *>::iterator it = root->tiles.begin();
+ it != root->tiles.end(); ) {
+ QSGGeometry visualGeometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4);
+ QSGGeometry::TexturedPoint2D *v = visualGeometry.vertexDataAsTexturedPoint2D();
+ bool ok = d->buildGeometry(it.key(), v) && qgeomapscene_isTileInViewport(v, root->matrix());
+ QSGSimpleTextureNode *node = it.value();
+ QSGNode::DirtyState dirtyBits = 0;
+
+ // Check and handle changes to vertex data.
+ if (ok && memcmp(node->geometry()->vertexData(), v, 4 * sizeof(QSGGeometry::TexturedPoint2D)) != 0) {
+ if (v[0].x == v[3].x || v[0].y == v[3].y) { // top-left == bottom-right => invalid => remove
+ ok = false;
+ } else {
+ memcpy(node->geometry()->vertexData(), v, 4 * sizeof(QSGGeometry::TexturedPoint2D));
+ dirtyBits |= QSGNode::DirtyGeometry;
+ }
+ }
- painter->setCamera(camera);
- sceneNode_->draw(painter);
+ if (!ok) {
+ it = root->tiles.erase(it);
+ delete node;
+ } else {
+ if (isTextureLinear != d->linearScaling_) {
+ node->setFiltering(d->linearScaling_ ? QSGTexture::Linear : QSGTexture::Nearest);
+ dirtyBits |= QSGNode::DirtyMaterial;
+ }
+ if (dirtyBits != 0)
+ node->markDirty(dirtyBits);
+ it++;
+ }
+ }
- c.setX(c.x() - 2 * sideLength);
- camera->setCenter(c);
- e.setX(e.x() - 2 * sideLength);
- camera->setEye(e);
+ foreach (const QGeoTileSpec &s, toAdd) {
+ QGeoTileTexture *tileTexture = d->textures_.value(s).data();
+ if (!tileTexture || tileTexture->image.isNull())
+ continue;
+ QSGSimpleTextureNode *tileNode = new QSGSimpleTextureNode();
+ // note: setTexture will update coordinates so do it here, before we buildGeometry
+ tileNode->setTexture(textures.value(s));
+ Q_ASSERT(tileNode->geometry());
+ Q_ASSERT(tileNode->geometry()->attributes() == QSGGeometry::defaultAttributes_TexturedPoint2D().attributes);
+ Q_ASSERT(tileNode->geometry()->vertexCount() == 4);
+ if (d->buildGeometry(s, tileNode->geometry()->vertexDataAsTexturedPoint2D())
+ && qgeomapscene_isTileInViewport(tileNode->geometry()->vertexDataAsTexturedPoint2D(), root->matrix())) {
+ tileNode->setFiltering(d->linearScaling_ ? QSGTexture::Linear : QSGTexture::Nearest);
+ root->addChild(s, tileNode);
+ } else {
+ delete tileNode;
+ }
+ }
+}
- painter->setCamera(camera);
- sceneNode_->draw(painter);
+QSGNode *QGeoMapScene::updateSceneGraph(QSGNode *oldNode, QQuickWindow *window)
+{
+ Q_D(QGeoMapScene);
+ float w = d->screenSize_.width();
+ float h = d->screenSize_.height();
+ if (w <= 0 || h <= 0) {
+ delete oldNode;
+ return 0;
+ }
- c.setX(c.x() + sideLength);
- camera->setCenter(c);
- e.setX(e.x() + sideLength);
- camera->setEye(e);
+ QGeoMapRootNode *mapRoot = static_cast<QGeoMapRootNode *>(oldNode);
+ if (!mapRoot)
+ mapRoot = new QGeoMapRootNode();
+
+ mapRoot->setClipRect(QRect(d->screenOffsetX_, d->screenOffsetY_, d->screenWidth_, d->screenHeight_));
+
+ QMatrix4x4 itemSpaceMatrix;
+ itemSpaceMatrix.scale(w / 2, h / 2);
+ itemSpaceMatrix.translate(1, 1);
+ itemSpaceMatrix.scale(1, -1);
+ mapRoot->root->setMatrix(itemSpaceMatrix);
+
+ QSet<QGeoTileSpec> textures = QSet<QGeoTileSpec>::fromList(mapRoot->textures.keys());
+ QSet<QGeoTileSpec> toRemove = textures - d->visibleTiles_;
+ QSet<QGeoTileSpec> toAdd = d->visibleTiles_ - textures;
+
+ foreach (const QGeoTileSpec &spec, toRemove)
+ mapRoot->textures.take(spec)->deleteLater();
+ foreach (const QGeoTileSpec &spec, toAdd) {
+ QGeoTileTexture *tileTexture = d->textures_.value(spec).data();
+ if (!tileTexture || tileTexture->image.isNull())
+ continue;
+ mapRoot->textures.insert(spec, window->createTextureFromImage(tileTexture->image));
+ }
- painter->setCamera(camera);
- sceneNode_->draw(painter);
+ double sideLength = d->scaleFactor_ * d->tileSize_ * d->sideLength_;
+ mapRoot->updateTiles(mapRoot->tiles, d, 0);
+ mapRoot->updateTiles(mapRoot->wrapLeft, d, +sideLength);
+ mapRoot->updateTiles(mapRoot->wrapRight, d, -sideLength);
- glEnable(GL_DEPTH_TEST);
+ mapRoot->isTextureLinear = d->linearScaling_;
- camera->blockSignals(old);
+ return mapRoot;
}
QT_END_NAMESPACE