Revision control
Copy as Markdown
// Copyright (C) 2018 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
// Qt-Security score:significant
#include "qqmlpreviewposition.h"
#include <QtCore/qiodevice.h>
#include <QtGui/qwindow.h>
#include <QtGui/qscreen.h>
#include <QtGui/qguiapplication.h>
#include <QtCore/QIODevice>
#include <private/qhighdpiscaling_p.h>
QT_BEGIN_NAMESPACE
static QList<QQmlPreviewPosition::ScreenData> initScreensData()
{
QList<QQmlPreviewPosition::ScreenData> screensData;
for (QScreen *screen : QGuiApplication::screens()) {
QQmlPreviewPosition::ScreenData sd{screen->name(), screen->geometry()};
screensData.append(sd);
}
return screensData;
}
static QScreen *findScreen(const QString &nameOfScreen)
{
for (QScreen *screen : QGuiApplication::screens()) {
if (screen->name() == nameOfScreen)
return screen;
}
return nullptr;
}
static QDataStream &operator<<(QDataStream &out, const QQmlPreviewPosition::ScreenData &screenData)
{
out << screenData.name;
out << screenData.rect;
return out;
}
static QDataStream &operator>>(QDataStream &in, QQmlPreviewPosition::ScreenData &screenData)
{
in >> screenData.name;
in >> screenData.rect;
return in;
}
bool QQmlPreviewPosition::ScreenData::operator==(const QQmlPreviewPosition::ScreenData &other) const
{
return other.rect == rect && other.name == name;
}
QQmlPreviewPosition::QQmlPreviewPosition()
: m_settings("QtProject", "QtQmlPreview")
{
m_savePositionTimer.setSingleShot(true);
m_savePositionTimer.setInterval(500);
QObject::connect(&m_savePositionTimer, &QTimer::timeout, &m_savePositionTimer, [this]() {
saveWindowPosition();
});
}
QQmlPreviewPosition::~QQmlPreviewPosition()
{
}
void QQmlPreviewPosition::takePosition(QWindow *window, InitializeState state)
{
Q_ASSERT(window);
// only save the position if we already tried to get the last saved position
if (m_initializeState == PositionInitialized) {
m_hasPosition = true;
auto screen = window->screen();
auto nativePosition = QHighDpiScaling::mapPositionToNative(window->framePosition(),
screen->handle());
m_lastWindowPosition = { screen->name(), nativePosition, window->size() };
m_savePositionTimer.start();
}
if (state == InitializePosition)
m_initializeState = InitializePosition;
}
void QQmlPreviewPosition::saveWindowPosition()
{
if (m_hasPosition) {
const QByteArray positionAsByteArray = fromPositionToByteArray(m_lastWindowPosition);
if (!m_settingsKey.isNull())
m_settings.setValue(m_settingsKey, positionAsByteArray);
m_settings.setValue(QLatin1String("global_lastpostion"), positionAsByteArray);
}
}
void QQmlPreviewPosition::loadWindowPositionSettings(const QUrl &url)
{
m_settingsKey = url.toString(QUrl::PreferLocalFile) + QLatin1String("_lastpostion");
if (m_settings.contains(m_settingsKey)) {
m_hasPosition = true;
readLastPositionFromByteArray(m_settings.value(m_settingsKey).toByteArray());
}
}
void QQmlPreviewPosition::initLastSavedWindowPosition(QWindow *window)
{
Q_ASSERT(window);
m_initializeState = PositionInitialized;
if (m_currentInitScreensData.isEmpty())
m_currentInitScreensData = initScreensData();
// if it is the first time we just use the fall back from a last shown qml file
if (!m_hasPosition) {
if (!m_settings.contains(QLatin1String("global_lastpostion")))
return;
readLastPositionFromByteArray(m_settings.value(QLatin1String("global_lastpostion"))
.toByteArray());
}
setPosition(m_lastWindowPosition, window);
}
QByteArray QQmlPreviewPosition::fromPositionToByteArray(
const QQmlPreviewPosition::Position &position)
{
QByteArray array;
QDataStream stream(&array, QIODevice::WriteOnly);
stream.setVersion(QDataStream::Qt_5_12);
const quint16 majorVersion = 1;
const quint16 minorVersion = 0;
stream << majorVersion << minorVersion << m_currentInitScreensData << position.screenName
<< position.nativePosition << position.size;
return array;
}
void QQmlPreviewPosition::readLastPositionFromByteArray(const QByteArray &array)
{
QDataStream stream(array);
stream.setVersion(QDataStream::Qt_5_12);
// no version check for 1.0
//const quint16 currentMajorVersion = 1;
quint16 majorVersion = 0;
quint16 minorVersion = 0;
stream >> majorVersion >> minorVersion;
QList<ScreenData> initScreensData;
stream >> initScreensData;
if (m_currentInitScreensData != initScreensData)
return;
QString nameOfScreen;
stream >> nameOfScreen;
QScreen *screen = findScreen(nameOfScreen);
if (!screen)
return;
QPoint nativePosition;
stream >> nativePosition;
QSize size;
stream >> size;
m_lastWindowPosition = { nameOfScreen, nativePosition, size };
}
void QQmlPreviewPosition::setPosition(const QQmlPreviewPosition::Position &position,
QWindow *window)
{
if (QScreen *screen = findScreen(position.screenName)) {
window->setScreen(screen);
const auto point = QHighDpiScaling::mapPositionFromNative(position.nativePosition,
screen->handle());
const QRect geometry(point, position.size);
if (screen->virtualGeometry().contains(geometry))
window->setFramePosition(point);
else
qWarning("preview position is out of screen");
}
}
QT_END_NAMESPACE