Merge "Merge remote-tracking branch 'origin/5.14' into 5.15"
commit
08fff0cb55
|
|
@ -272,7 +272,7 @@ QDateTime &QFileInfoPrivate::getFileTime(QAbstractFileEngine::FileTime request)
|
|||
info objects, just append one to the file name given to the constructors
|
||||
or setFile().
|
||||
|
||||
The file's dates are returned by created(), lastModified(), lastRead() and
|
||||
The file's dates are returned by birthTime(), lastModified(), lastRead() and
|
||||
fileTime(). Information about the file's access permissions is
|
||||
obtained with isReadable(), isWritable() and isExecutable(). The
|
||||
file's ownership is available from owner(), ownerId(), group() and
|
||||
|
|
|
|||
|
|
@ -290,6 +290,16 @@ Q_DECL_CONSTEXPR inline bool operator==(const QTzTransitionRule &lhs, const QTzT
|
|||
Q_DECL_CONSTEXPR inline bool operator!=(const QTzTransitionRule &lhs, const QTzTransitionRule &rhs) noexcept
|
||||
{ return !operator==(lhs, rhs); }
|
||||
|
||||
// These are stored separately from QTzTimeZonePrivate so that they can be
|
||||
// cached, avoiding the need to re-parse them from disk constantly.
|
||||
struct QTzTimeZoneCacheEntry
|
||||
{
|
||||
QVector<QTzTransitionTime> m_tranTimes;
|
||||
QVector<QTzTransitionRule> m_tranRules;
|
||||
QList<QByteArray> m_abbreviations;
|
||||
QByteArray m_posixRule;
|
||||
};
|
||||
|
||||
class Q_AUTOTEST_EXPORT QTzTimeZonePrivate final : public QTimeZonePrivate
|
||||
{
|
||||
QTzTimeZonePrivate(const QTzTimeZonePrivate &) = default;
|
||||
|
|
@ -337,13 +347,11 @@ private:
|
|||
QVector<QTimeZonePrivate::Data> getPosixTransitions(qint64 msNear) const;
|
||||
|
||||
Data dataForTzTransition(QTzTransitionTime tran) const;
|
||||
QVector<QTzTransitionTime> m_tranTimes;
|
||||
QVector<QTzTransitionRule> m_tranRules;
|
||||
QList<QByteArray> m_abbreviations;
|
||||
#if QT_CONFIG(icu)
|
||||
mutable QSharedDataPointer<QTimeZonePrivate> m_icu;
|
||||
#endif
|
||||
QByteArray m_posixRule;
|
||||
QTzTimeZoneCacheEntry cached_data;
|
||||
QVector<QTzTransitionTime> tranCache() const { return cached_data.m_tranTimes; }
|
||||
};
|
||||
#endif // Q_OS_UNIX
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 Crimson AS <info@crimson.no>
|
||||
** Copyright (C) 2013 John Layt <jlayt@kde.org>
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
|
|
@ -42,6 +43,7 @@
|
|||
#include "private/qlocale_tools_p.h"
|
||||
|
||||
#include <QtCore/QFile>
|
||||
#include <QtCore/QMutex>
|
||||
#include <QtCore/QHash>
|
||||
#include <QtCore/QDataStream>
|
||||
#include <QtCore/QDateTime>
|
||||
|
|
@ -649,14 +651,26 @@ QTzTimeZonePrivate *QTzTimeZonePrivate::clone() const
|
|||
return new QTzTimeZonePrivate(*this);
|
||||
}
|
||||
|
||||
void QTzTimeZonePrivate::init(const QByteArray &ianaId)
|
||||
class QTzTimeZoneCache
|
||||
{
|
||||
public:
|
||||
QTzTimeZoneCacheEntry fetchEntry(const QByteArray &ianaId);
|
||||
|
||||
private:
|
||||
QTzTimeZoneCacheEntry findEntry(const QByteArray &ianaId);
|
||||
QHash<QByteArray, QTzTimeZoneCacheEntry> m_cache;
|
||||
QMutex m_mutex;
|
||||
};
|
||||
|
||||
QTzTimeZoneCacheEntry QTzTimeZoneCache::findEntry(const QByteArray &ianaId)
|
||||
{
|
||||
QTzTimeZoneCacheEntry ret;
|
||||
QFile tzif;
|
||||
if (ianaId.isEmpty()) {
|
||||
// Open system tz
|
||||
tzif.setFileName(QStringLiteral("/etc/localtime"));
|
||||
if (!tzif.open(QIODevice::ReadOnly))
|
||||
return;
|
||||
return ret;
|
||||
} else {
|
||||
// Open named tz, try modern path first, if fails try legacy path
|
||||
tzif.setFileName(QLatin1String("/usr/share/zoneinfo/") + QString::fromLocal8Bit(ianaId));
|
||||
|
|
@ -669,9 +683,9 @@ void QTzTimeZonePrivate::init(const QByteArray &ianaId)
|
|||
if (PosixZone::parse(begin, zoneInfo.constEnd()).hasValidOffset()
|
||||
&& (begin == zoneInfo.constEnd()
|
||||
|| PosixZone::parse(begin, zoneInfo.constEnd()).hasValidOffset())) {
|
||||
m_id = m_posixRule = ianaId;
|
||||
ret.m_posixRule = ianaId;
|
||||
}
|
||||
return;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -682,59 +696,59 @@ void QTzTimeZonePrivate::init(const QByteArray &ianaId)
|
|||
bool ok = false;
|
||||
QTzHeader hdr = parseTzHeader(ds, &ok);
|
||||
if (!ok || ds.status() != QDataStream::Ok)
|
||||
return;
|
||||
return ret;
|
||||
QVector<QTzTransition> tranList = parseTzTransitions(ds, hdr.tzh_timecnt, false);
|
||||
if (ds.status() != QDataStream::Ok)
|
||||
return;
|
||||
return ret;
|
||||
QVector<QTzType> typeList = parseTzTypes(ds, hdr.tzh_typecnt);
|
||||
if (ds.status() != QDataStream::Ok)
|
||||
return;
|
||||
return ret;
|
||||
QMap<int, QByteArray> abbrevMap = parseTzAbbreviations(ds, hdr.tzh_charcnt, typeList);
|
||||
if (ds.status() != QDataStream::Ok)
|
||||
return;
|
||||
return ret;
|
||||
parseTzLeapSeconds(ds, hdr.tzh_leapcnt, false);
|
||||
if (ds.status() != QDataStream::Ok)
|
||||
return;
|
||||
return ret;
|
||||
typeList = parseTzIndicators(ds, typeList, hdr.tzh_ttisstdcnt, hdr.tzh_ttisgmtcnt);
|
||||
if (ds.status() != QDataStream::Ok)
|
||||
return;
|
||||
return ret;
|
||||
|
||||
// If version 2 then parse the second block of data
|
||||
if (hdr.tzh_version == '2' || hdr.tzh_version == '3') {
|
||||
ok = false;
|
||||
QTzHeader hdr2 = parseTzHeader(ds, &ok);
|
||||
if (!ok || ds.status() != QDataStream::Ok)
|
||||
return;
|
||||
return ret;
|
||||
tranList = parseTzTransitions(ds, hdr2.tzh_timecnt, true);
|
||||
if (ds.status() != QDataStream::Ok)
|
||||
return;
|
||||
return ret;
|
||||
typeList = parseTzTypes(ds, hdr2.tzh_typecnt);
|
||||
if (ds.status() != QDataStream::Ok)
|
||||
return;
|
||||
return ret;
|
||||
abbrevMap = parseTzAbbreviations(ds, hdr2.tzh_charcnt, typeList);
|
||||
if (ds.status() != QDataStream::Ok)
|
||||
return;
|
||||
return ret;
|
||||
parseTzLeapSeconds(ds, hdr2.tzh_leapcnt, true);
|
||||
if (ds.status() != QDataStream::Ok)
|
||||
return;
|
||||
return ret;
|
||||
typeList = parseTzIndicators(ds, typeList, hdr2.tzh_ttisstdcnt, hdr2.tzh_ttisgmtcnt);
|
||||
if (ds.status() != QDataStream::Ok)
|
||||
return;
|
||||
m_posixRule = parseTzPosixRule(ds);
|
||||
return ret;
|
||||
ret.m_posixRule = parseTzPosixRule(ds);
|
||||
if (ds.status() != QDataStream::Ok)
|
||||
return;
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Translate the TZ file into internal format
|
||||
|
||||
// Translate the array index based tz_abbrind into list index
|
||||
const int size = abbrevMap.size();
|
||||
m_abbreviations.clear();
|
||||
m_abbreviations.reserve(size);
|
||||
ret.m_abbreviations.clear();
|
||||
ret.m_abbreviations.reserve(size);
|
||||
QVector<int> abbrindList;
|
||||
abbrindList.reserve(size);
|
||||
for (auto it = abbrevMap.cbegin(), end = abbrevMap.cend(); it != end; ++it) {
|
||||
m_abbreviations.append(it.value());
|
||||
ret.m_abbreviations.append(it.value());
|
||||
abbrindList.append(it.key());
|
||||
}
|
||||
for (int i = 0; i < typeList.size(); ++i)
|
||||
|
|
@ -752,7 +766,7 @@ void QTzTimeZonePrivate::init(const QByteArray &ianaId)
|
|||
|
||||
// Now for each transition time calculate and store our rule:
|
||||
const int tranCount = tranList.count();;
|
||||
m_tranTimes.reserve(tranCount);
|
||||
ret.m_tranTimes.reserve(tranCount);
|
||||
// The DST offset when in effect: usually stable, usually an hour:
|
||||
int lastDstOff = 3600;
|
||||
for (int i = 0; i < tranCount; i++) {
|
||||
|
|
@ -806,24 +820,45 @@ void QTzTimeZonePrivate::init(const QByteArray &ianaId)
|
|||
rule.abbreviationIndex = tz_type.tz_abbrind;
|
||||
|
||||
// If the rule already exist then use that, otherwise add it
|
||||
int ruleIndex = m_tranRules.indexOf(rule);
|
||||
int ruleIndex = ret.m_tranRules.indexOf(rule);
|
||||
if (ruleIndex == -1) {
|
||||
m_tranRules.append(rule);
|
||||
tran.ruleIndex = m_tranRules.size() - 1;
|
||||
ret.m_tranRules.append(rule);
|
||||
tran.ruleIndex = ret.m_tranRules.size() - 1;
|
||||
} else {
|
||||
tran.ruleIndex = ruleIndex;
|
||||
}
|
||||
|
||||
tran.atMSecsSinceEpoch = tz_tran.tz_time * 1000;
|
||||
m_tranTimes.append(tran);
|
||||
ret.m_tranTimes.append(tran);
|
||||
}
|
||||
if (m_tranTimes.isEmpty() && m_posixRule.isEmpty())
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
QTzTimeZoneCacheEntry QTzTimeZoneCache::fetchEntry(const QByteArray &ianaId)
|
||||
{
|
||||
QMutexLocker locker(&m_mutex);
|
||||
|
||||
// search the cache...
|
||||
const auto& it = m_cache.find(ianaId);
|
||||
if (it != m_cache.constEnd())
|
||||
return *it;
|
||||
|
||||
// ... or build a new entry from scratch
|
||||
QTzTimeZoneCacheEntry ret = findEntry(ianaId);
|
||||
m_cache[ianaId] = ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void QTzTimeZonePrivate::init(const QByteArray &ianaId)
|
||||
{
|
||||
static QTzTimeZoneCache tzCache;
|
||||
const auto &entry = tzCache.fetchEntry(ianaId);
|
||||
if (entry.m_tranTimes.isEmpty() && entry.m_posixRule.isEmpty())
|
||||
return; // Invalid after all !
|
||||
|
||||
if (ianaId.isEmpty())
|
||||
m_id = systemTimeZoneId();
|
||||
else
|
||||
m_id = ianaId;
|
||||
cached_data = std::move(entry);
|
||||
m_id = ianaId.isEmpty() ? systemTimeZoneId() : ianaId;
|
||||
}
|
||||
|
||||
QLocale::Country QTzTimeZonePrivate::country() const
|
||||
|
|
@ -903,12 +938,12 @@ QString QTzTimeZonePrivate::displayName(QTimeZone::TimeType timeType,
|
|||
}
|
||||
|
||||
// Otherwise is strange sequence, so work backwards through trans looking for first match, if any
|
||||
auto it = std::partition_point(m_tranTimes.cbegin(), m_tranTimes.cend(),
|
||||
auto it = std::partition_point(tranCache().cbegin(), tranCache().cend(),
|
||||
[currentMSecs](const QTzTransitionTime &at) {
|
||||
return at.atMSecsSinceEpoch <= currentMSecs;
|
||||
});
|
||||
|
||||
while (it != m_tranTimes.cbegin()) {
|
||||
while (it != tranCache().cbegin()) {
|
||||
--it;
|
||||
tran = dataForTzTransition(*it);
|
||||
int offset = tran.daylightTimeOffset;
|
||||
|
|
@ -944,7 +979,7 @@ int QTzTimeZonePrivate::daylightTimeOffset(qint64 atMSecsSinceEpoch) const
|
|||
bool QTzTimeZonePrivate::hasDaylightTime() const
|
||||
{
|
||||
// TODO Perhaps cache as frequently accessed?
|
||||
for (const QTzTransitionRule &rule : m_tranRules) {
|
||||
for (const QTzTransitionRule &rule : cached_data.m_tranRules) {
|
||||
if (rule.dstOffset != 0)
|
||||
return true;
|
||||
}
|
||||
|
|
@ -960,11 +995,11 @@ QTimeZonePrivate::Data QTzTimeZonePrivate::dataForTzTransition(QTzTransitionTime
|
|||
{
|
||||
QTimeZonePrivate::Data data;
|
||||
data.atMSecsSinceEpoch = tran.atMSecsSinceEpoch;
|
||||
QTzTransitionRule rule = m_tranRules.at(tran.ruleIndex);
|
||||
QTzTransitionRule rule = cached_data.m_tranRules.at(tran.ruleIndex);
|
||||
data.standardTimeOffset = rule.stdOffset;
|
||||
data.daylightTimeOffset = rule.dstOffset;
|
||||
data.offsetFromUtc = rule.stdOffset + rule.dstOffset;
|
||||
data.abbreviation = QString::fromUtf8(m_abbreviations.at(rule.abbreviationIndex));
|
||||
data.abbreviation = QString::fromUtf8(cached_data.m_abbreviations.at(rule.abbreviationIndex));
|
||||
return data;
|
||||
}
|
||||
|
||||
|
|
@ -972,37 +1007,37 @@ QVector<QTimeZonePrivate::Data> QTzTimeZonePrivate::getPosixTransitions(qint64 m
|
|||
{
|
||||
const int year = QDateTime::fromMSecsSinceEpoch(msNear, Qt::UTC).date().year();
|
||||
// The Data::atMSecsSinceEpoch of the single entry if zone is constant:
|
||||
qint64 atTime = m_tranTimes.isEmpty() ? msNear : m_tranTimes.last().atMSecsSinceEpoch;
|
||||
return calculatePosixTransitions(m_posixRule, year - 1, year + 1, atTime);
|
||||
qint64 atTime = tranCache().isEmpty() ? msNear : tranCache().last().atMSecsSinceEpoch;
|
||||
return calculatePosixTransitions(cached_data.m_posixRule, year - 1, year + 1, atTime);
|
||||
}
|
||||
|
||||
QTimeZonePrivate::Data QTzTimeZonePrivate::data(qint64 forMSecsSinceEpoch) const
|
||||
{
|
||||
// If the required time is after the last transition (or there were none)
|
||||
// and we have a POSIX rule, then use it:
|
||||
if (!m_posixRule.isEmpty()
|
||||
&& (m_tranTimes.isEmpty() || m_tranTimes.last().atMSecsSinceEpoch < forMSecsSinceEpoch)) {
|
||||
if (!cached_data.m_posixRule.isEmpty()
|
||||
&& (tranCache().isEmpty() || tranCache().last().atMSecsSinceEpoch < forMSecsSinceEpoch)) {
|
||||
QVector<QTimeZonePrivate::Data> posixTrans = getPosixTransitions(forMSecsSinceEpoch);
|
||||
auto it = std::partition_point(posixTrans.cbegin(), posixTrans.cend(),
|
||||
[forMSecsSinceEpoch] (const QTimeZonePrivate::Data &at) {
|
||||
return at.atMSecsSinceEpoch <= forMSecsSinceEpoch;
|
||||
});
|
||||
// Use most recent, if any in the past; or the first if we have no other rules:
|
||||
if (it > posixTrans.cbegin() || (m_tranTimes.isEmpty() && it < posixTrans.cend())) {
|
||||
if (it > posixTrans.cbegin() || (tranCache().isEmpty() && it < posixTrans.cend())) {
|
||||
QTimeZonePrivate::Data data = *(it > posixTrans.cbegin() ? it - 1 : it);
|
||||
data.atMSecsSinceEpoch = forMSecsSinceEpoch;
|
||||
return data;
|
||||
}
|
||||
}
|
||||
if (m_tranTimes.isEmpty()) // Only possible if !isValid()
|
||||
if (tranCache().isEmpty()) // Only possible if !isValid()
|
||||
return invalidData();
|
||||
|
||||
// Otherwise, use the rule for the most recent or first transition:
|
||||
auto last = std::partition_point(m_tranTimes.cbegin(), m_tranTimes.cend(),
|
||||
auto last = std::partition_point(tranCache().cbegin(), tranCache().cend(),
|
||||
[forMSecsSinceEpoch] (const QTzTransitionTime &at) {
|
||||
return at.atMSecsSinceEpoch <= forMSecsSinceEpoch;
|
||||
});
|
||||
if (last > m_tranTimes.cbegin())
|
||||
if (last > tranCache().cbegin())
|
||||
--last;
|
||||
Data data = dataForTzTransition(*last);
|
||||
data.atMSecsSinceEpoch = forMSecsSinceEpoch;
|
||||
|
|
@ -1018,8 +1053,8 @@ QTimeZonePrivate::Data QTzTimeZonePrivate::nextTransition(qint64 afterMSecsSince
|
|||
{
|
||||
// If the required time is after the last transition (or there were none)
|
||||
// and we have a POSIX rule, then use it:
|
||||
if (!m_posixRule.isEmpty()
|
||||
&& (m_tranTimes.isEmpty() || m_tranTimes.last().atMSecsSinceEpoch < afterMSecsSinceEpoch)) {
|
||||
if (!cached_data.m_posixRule.isEmpty()
|
||||
&& (tranCache().isEmpty() || tranCache().last().atMSecsSinceEpoch < afterMSecsSinceEpoch)) {
|
||||
QVector<QTimeZonePrivate::Data> posixTrans = getPosixTransitions(afterMSecsSinceEpoch);
|
||||
auto it = std::partition_point(posixTrans.cbegin(), posixTrans.cend(),
|
||||
[afterMSecsSinceEpoch] (const QTimeZonePrivate::Data &at) {
|
||||
|
|
@ -1030,19 +1065,19 @@ QTimeZonePrivate::Data QTzTimeZonePrivate::nextTransition(qint64 afterMSecsSince
|
|||
}
|
||||
|
||||
// Otherwise, if we can find a valid tran, use its rule:
|
||||
auto last = std::partition_point(m_tranTimes.cbegin(), m_tranTimes.cend(),
|
||||
auto last = std::partition_point(tranCache().cbegin(), tranCache().cend(),
|
||||
[afterMSecsSinceEpoch] (const QTzTransitionTime &at) {
|
||||
return at.atMSecsSinceEpoch <= afterMSecsSinceEpoch;
|
||||
});
|
||||
return last != m_tranTimes.cend() ? dataForTzTransition(*last) : invalidData();
|
||||
return last != tranCache().cend() ? dataForTzTransition(*last) : invalidData();
|
||||
}
|
||||
|
||||
QTimeZonePrivate::Data QTzTimeZonePrivate::previousTransition(qint64 beforeMSecsSinceEpoch) const
|
||||
{
|
||||
// If the required time is after the last transition (or there were none)
|
||||
// and we have a POSIX rule, then use it:
|
||||
if (!m_posixRule.isEmpty()
|
||||
&& (m_tranTimes.isEmpty() || m_tranTimes.last().atMSecsSinceEpoch < beforeMSecsSinceEpoch)) {
|
||||
if (!cached_data.m_posixRule.isEmpty()
|
||||
&& (tranCache().isEmpty() || tranCache().last().atMSecsSinceEpoch < beforeMSecsSinceEpoch)) {
|
||||
QVector<QTimeZonePrivate::Data> posixTrans = getPosixTransitions(beforeMSecsSinceEpoch);
|
||||
auto it = std::partition_point(posixTrans.cbegin(), posixTrans.cend(),
|
||||
[beforeMSecsSinceEpoch] (const QTimeZonePrivate::Data &at) {
|
||||
|
|
@ -1051,15 +1086,15 @@ QTimeZonePrivate::Data QTzTimeZonePrivate::previousTransition(qint64 beforeMSecs
|
|||
if (it > posixTrans.cbegin())
|
||||
return *--it;
|
||||
// It fell between the last transition (if any) and the first of the POSIX rule:
|
||||
return m_tranTimes.isEmpty() ? invalidData() : dataForTzTransition(m_tranTimes.last());
|
||||
return tranCache().isEmpty() ? invalidData() : dataForTzTransition(tranCache().last());
|
||||
}
|
||||
|
||||
// Otherwise if we can find a valid tran then use its rule
|
||||
auto last = std::partition_point(m_tranTimes.cbegin(), m_tranTimes.cend(),
|
||||
auto last = std::partition_point(tranCache().cbegin(), tranCache().cend(),
|
||||
[beforeMSecsSinceEpoch] (const QTzTransitionTime &at) {
|
||||
return at.atMSecsSinceEpoch < beforeMSecsSinceEpoch;
|
||||
});
|
||||
return last > m_tranTimes.cbegin() ? dataForTzTransition(*--last) : invalidData();
|
||||
return last > tranCache().cbegin() ? dataForTzTransition(*--last) : invalidData();
|
||||
}
|
||||
|
||||
static long getSymloopMax()
|
||||
|
|
|
|||
|
|
@ -1191,16 +1191,16 @@
|
|||
\li none | dotted | dashed | dot-dash | dot-dot-dash | solid | double | groove | ridge | inset | outset
|
||||
\li Border style for text tables and table cells.
|
||||
\row \li \c border-top-style
|
||||
\li <color>
|
||||
\li <border-style>
|
||||
\li Top border style for table cells.
|
||||
\row \li \c border-bottom-style
|
||||
\li <color>
|
||||
\li <border-style>
|
||||
\li Bottom border style for table cells.
|
||||
\row \li \c border-left-style
|
||||
\li <color>
|
||||
\li <border-style>
|
||||
\li Left border style for table cells.
|
||||
\row \li \c border-right-style
|
||||
\li <color>
|
||||
\li <border-style>
|
||||
\li Right border style for table cells.
|
||||
\row \li \c border-width
|
||||
\li <width>px
|
||||
|
|
|
|||
|
|
@ -182,6 +182,7 @@ static const char *xcb_atomnames = {
|
|||
"XdndActionCopy\0"
|
||||
"XdndActionLink\0"
|
||||
"XdndActionMove\0"
|
||||
"XdndActionAsk\0"
|
||||
"XdndActionPrivate\0"
|
||||
|
||||
// Xkb
|
||||
|
|
|
|||
|
|
@ -183,6 +183,7 @@ public:
|
|||
XdndActionCopy,
|
||||
XdndActionLink,
|
||||
XdndActionMove,
|
||||
XdndActionAsk,
|
||||
XdndActionPrivate,
|
||||
|
||||
// Xkb
|
||||
|
|
|
|||
|
|
@ -216,6 +216,22 @@ void QXcbDrag::endDrag()
|
|||
initiatorWindow.clear();
|
||||
}
|
||||
|
||||
Qt::DropAction QXcbDrag::defaultAction(Qt::DropActions possibleActions, Qt::KeyboardModifiers modifiers) const
|
||||
{
|
||||
if (currentDrag() || drop_actions.isEmpty())
|
||||
return QBasicDrag::defaultAction(possibleActions, modifiers);
|
||||
|
||||
return toDropAction(drop_actions.first());
|
||||
}
|
||||
|
||||
void QXcbDrag::handlePropertyNotifyEvent(const xcb_property_notify_event_t *event)
|
||||
{
|
||||
if (event->window != xdnd_dragsource || event->atom != atom(QXcbAtom::XdndActionList))
|
||||
return;
|
||||
|
||||
readActionList();
|
||||
}
|
||||
|
||||
static
|
||||
bool windowInteractsWithPosition(xcb_connection_t *connection, const QPoint & pos, xcb_window_t w, xcb_shape_sk_t shapeType)
|
||||
{
|
||||
|
|
@ -470,16 +486,20 @@ void QXcbDrag::move(const QPoint &globalPos, Qt::MouseButtons b, Qt::KeyboardMod
|
|||
move.data.data32[1] = 0; // flags
|
||||
move.data.data32[2] = (globalPos.x() << 16) + globalPos.y();
|
||||
move.data.data32[3] = connection()->time();
|
||||
move.data.data32[4] = toXdndAction(defaultAction(currentDrag()->supportedActions(), mods));
|
||||
const auto supportedActions = currentDrag()->supportedActions();
|
||||
const auto requestedAction = defaultAction(supportedActions, mods);
|
||||
move.data.data32[4] = toXdndAction(requestedAction);
|
||||
|
||||
qCDebug(lcQpaXDnd) << "sending XdndPosition to target:" << target;
|
||||
|
||||
source_time = connection()->time();
|
||||
|
||||
if (w)
|
||||
if (w) {
|
||||
handle_xdnd_position(w, &move, b, mods);
|
||||
else
|
||||
} else {
|
||||
setActionList(requestedAction, supportedActions);
|
||||
xcb_send_event(xcb_connection(), false, proxy_target, XCB_EVENT_MASK_NO_EVENT, (const char *)&move);
|
||||
}
|
||||
}
|
||||
|
||||
static const bool isUnity = qgetenv("XDG_CURRENT_DESKTOP").toLower() == "unity";
|
||||
|
|
@ -560,6 +580,16 @@ Qt::DropAction QXcbDrag::toDropAction(xcb_atom_t a) const
|
|||
return Qt::CopyAction;
|
||||
}
|
||||
|
||||
Qt::DropActions QXcbDrag::toDropActions(const QVector<xcb_atom_t> &atoms) const
|
||||
{
|
||||
Qt::DropActions actions;
|
||||
for (const auto actionAtom : atoms) {
|
||||
if (actionAtom != atom(QXcbAtom::XdndActionAsk))
|
||||
actions |= toDropAction(actionAtom);
|
||||
}
|
||||
return actions;
|
||||
}
|
||||
|
||||
xcb_atom_t QXcbDrag::toXdndAction(Qt::DropAction a) const
|
||||
{
|
||||
switch (a) {
|
||||
|
|
@ -577,6 +607,60 @@ xcb_atom_t QXcbDrag::toXdndAction(Qt::DropAction a) const
|
|||
}
|
||||
}
|
||||
|
||||
void QXcbDrag::readActionList()
|
||||
{
|
||||
drop_actions.clear();
|
||||
auto reply = Q_XCB_REPLY(xcb_get_property, xcb_connection(), false, xdnd_dragsource,
|
||||
atom(QXcbAtom::XdndActionList), XCB_ATOM_ATOM,
|
||||
0, 1024);
|
||||
if (reply && reply->type != XCB_NONE && reply->format == 32) {
|
||||
int length = xcb_get_property_value_length(reply.get()) / 4;
|
||||
|
||||
xcb_atom_t *atoms = (xcb_atom_t *)xcb_get_property_value(reply.get());
|
||||
for (int i = 0; i < length; ++i)
|
||||
drop_actions.append(atoms[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void QXcbDrag::setActionList(Qt::DropAction requestedAction, Qt::DropActions supportedActions)
|
||||
{
|
||||
#ifndef QT_NO_CLIPBOARD
|
||||
QVector<xcb_atom_t> actions;
|
||||
if (requestedAction != Qt::IgnoreAction)
|
||||
actions.append(toXdndAction(requestedAction));
|
||||
|
||||
auto checkAppend = [this, requestedAction, supportedActions, &actions](Qt::DropAction action) {
|
||||
if (requestedAction != action && supportedActions & action)
|
||||
actions.append(toXdndAction(action));
|
||||
};
|
||||
|
||||
checkAppend(Qt::CopyAction);
|
||||
checkAppend(Qt::MoveAction);
|
||||
checkAppend(Qt::LinkAction);
|
||||
|
||||
if (current_actions != actions) {
|
||||
xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, connection()->clipboard()->owner(),
|
||||
atom(QXcbAtom::XdndActionList),
|
||||
XCB_ATOM_ATOM, 32, actions.size(), actions.constData());
|
||||
current_actions = actions;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void QXcbDrag::startListeningForActionListChanges()
|
||||
{
|
||||
connection()->addWindowEventListener(xdnd_dragsource, this);
|
||||
const uint32_t event_mask[] = { XCB_EVENT_MASK_PROPERTY_CHANGE };
|
||||
xcb_change_window_attributes(xcb_connection(), xdnd_dragsource, XCB_CW_EVENT_MASK, event_mask);
|
||||
}
|
||||
|
||||
void QXcbDrag::stopListeningForActionListChanges()
|
||||
{
|
||||
const uint32_t event_mask[] = { XCB_EVENT_MASK_NO_EVENT };
|
||||
xcb_change_window_attributes(xcb_connection(), xdnd_dragsource, XCB_CW_EVENT_MASK, event_mask);
|
||||
connection()->removeWindowEventListener(xdnd_dragsource);
|
||||
}
|
||||
|
||||
int QXcbDrag::findTransactionByWindow(xcb_window_t window)
|
||||
{
|
||||
int at = -1;
|
||||
|
|
@ -657,6 +741,9 @@ void QXcbDrag::handleEnter(QPlatformWindow *, const xcb_client_message_event_t *
|
|||
return;
|
||||
|
||||
xdnd_dragsource = event->data.data32[0];
|
||||
startListeningForActionListChanges();
|
||||
readActionList();
|
||||
|
||||
if (!proxy)
|
||||
proxy = xdndProxy(connection(), xdnd_dragsource);
|
||||
current_proxy_target = proxy ? proxy : xdnd_dragsource;
|
||||
|
|
@ -723,7 +810,9 @@ void QXcbDrag::handle_xdnd_position(QPlatformWindow *w, const xcb_client_message
|
|||
supported_actions = currentDrag()->supportedActions();
|
||||
} else {
|
||||
dropData = m_dropData;
|
||||
supported_actions = Qt::DropActions(toDropAction(e->data.data32[4]));
|
||||
supported_actions = toDropActions(drop_actions);
|
||||
if (e->data.data32[4] != atom(QXcbAtom::XdndActionAsk))
|
||||
supported_actions |= Qt::DropActions(toDropAction(e->data.data32[4]));
|
||||
}
|
||||
|
||||
auto buttons = currentDrag() ? b : connection()->queryMouseButtons();
|
||||
|
|
@ -867,8 +956,10 @@ void QXcbDrag::handleLeave(QPlatformWindow *w, const xcb_client_message_event_t
|
|||
// If the target receives XdndLeave, it frees any cached data and forgets the whole incident.
|
||||
qCDebug(lcQpaXDnd) << "target:" << event->window << "received XdndLeave";
|
||||
|
||||
if (!currentWindow || w != currentWindow.data()->handle())
|
||||
if (!currentWindow || w != currentWindow.data()->handle()) {
|
||||
stopListeningForActionListChanges();
|
||||
return; // sanity
|
||||
}
|
||||
|
||||
// ###
|
||||
// if (checkEmbedded(current_embedding_widget, event)) {
|
||||
|
|
@ -883,6 +974,8 @@ void QXcbDrag::handleLeave(QPlatformWindow *w, const xcb_client_message_event_t
|
|||
event->data.data32[0], xdnd_dragsource);
|
||||
}
|
||||
|
||||
stopListeningForActionListChanges();
|
||||
|
||||
QWindowSystemInterface::handleDrag(w->window(), nullptr, QPoint(), Qt::IgnoreAction, { }, { });
|
||||
}
|
||||
|
||||
|
|
@ -929,6 +1022,7 @@ void QXcbDrag::handleDrop(QPlatformWindow *, const xcb_client_message_event_t *e
|
|||
qCDebug(lcQpaXDnd) << "target:" << event->window << "received XdndDrop";
|
||||
|
||||
if (!currentWindow) {
|
||||
stopListeningForActionListChanges();
|
||||
xdnd_dragsource = 0;
|
||||
return; // sanity
|
||||
}
|
||||
|
|
@ -951,7 +1045,7 @@ void QXcbDrag::handleDrop(QPlatformWindow *, const xcb_client_message_event_t *e
|
|||
supported_drop_actions = Qt::DropActions(l[4]);
|
||||
} else {
|
||||
dropData = m_dropData;
|
||||
supported_drop_actions = accepted_drop_action;
|
||||
supported_drop_actions = accepted_drop_action | toDropActions(drop_actions);
|
||||
}
|
||||
|
||||
if (!dropData)
|
||||
|
|
@ -986,6 +1080,8 @@ void QXcbDrag::handleDrop(QPlatformWindow *, const xcb_client_message_event_t *e
|
|||
xcb_send_event(xcb_connection(), false, current_proxy_target,
|
||||
XCB_EVENT_MASK_NO_EVENT, (char *)&finished);
|
||||
|
||||
stopListeningForActionListChanges();
|
||||
|
||||
dropped = true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ class QXcbScreen;
|
|||
class QDrag;
|
||||
class QShapedPixmapWindow;
|
||||
|
||||
class QXcbDrag : public QXcbObject, public QBasicDrag
|
||||
class QXcbDrag : public QXcbObject, public QBasicDrag, public QXcbWindowEventListener
|
||||
{
|
||||
public:
|
||||
QXcbDrag(QXcbConnection *c);
|
||||
|
|
@ -82,6 +82,10 @@ public:
|
|||
void drop(const QPoint &globalPos, Qt::MouseButtons b, Qt::KeyboardModifiers mods) override;
|
||||
void endDrag() override;
|
||||
|
||||
Qt::DropAction defaultAction(Qt::DropActions possibleActions, Qt::KeyboardModifiers modifiers) const override;
|
||||
|
||||
void handlePropertyNotifyEvent(const xcb_property_notify_event_t *event) override;
|
||||
|
||||
void handleEnter(QPlatformWindow *window, const xcb_client_message_event_t *event, xcb_window_t proxy = 0);
|
||||
void handlePosition(QPlatformWindow *w, const xcb_client_message_event_t *event);
|
||||
void handleLeave(QPlatformWindow *w, const xcb_client_message_event_t *event);
|
||||
|
|
@ -114,8 +118,14 @@ private:
|
|||
void send_leave();
|
||||
|
||||
Qt::DropAction toDropAction(xcb_atom_t atom) const;
|
||||
Qt::DropActions toDropActions(const QVector<xcb_atom_t> &atoms) const;
|
||||
xcb_atom_t toXdndAction(Qt::DropAction a) const;
|
||||
|
||||
void readActionList();
|
||||
void setActionList(Qt::DropAction requestedAction, Qt::DropActions supportedActions);
|
||||
void startListeningForActionListChanges();
|
||||
void stopListeningForActionListChanges();
|
||||
|
||||
QPointer<QWindow> initiatorWindow;
|
||||
QPointer<QWindow> currentWindow;
|
||||
QPoint currentPosition;
|
||||
|
|
@ -159,6 +169,9 @@ private:
|
|||
|
||||
QVector<xcb_atom_t> drag_types;
|
||||
|
||||
QVector<xcb_atom_t> current_actions;
|
||||
QVector<xcb_atom_t> drop_actions;
|
||||
|
||||
struct Transaction
|
||||
{
|
||||
xcb_timestamp_t timestamp;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2019 Crimson AS <info@crimson.no>
|
||||
** Copyright (C) 2018 Klaralvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author David Faure <david.faure@kdab.com>
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
|
|
@ -30,14 +31,57 @@
|
|||
#include <QTest>
|
||||
#include <qdebug.h>
|
||||
|
||||
// Enable to test *every* zone, rather than a hand-picked few, in some _data() sets:
|
||||
// #define EXHAUSTIVE
|
||||
|
||||
class tst_QTimeZone : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void isTimeZoneIdAvailable();
|
||||
void systemTimeZone();
|
||||
void zoneByName_data();
|
||||
void zoneByName();
|
||||
void transitionList_data();
|
||||
void transitionList();
|
||||
void transitionsForward_data() { transitionList_data(); }
|
||||
void transitionsForward();
|
||||
void transitionsReverse_data() { transitionList_data(); }
|
||||
void transitionsReverse();
|
||||
};
|
||||
|
||||
static QVector<QByteArray> enoughZones()
|
||||
{
|
||||
#ifdef EXHAUSTIVE
|
||||
auto available = QTimeZone::availableTimeZoneIds();
|
||||
QVector<QByteArray> result;
|
||||
result.reserve(available.size() + 1);
|
||||
for (conat auto &name : available)
|
||||
result << name;
|
||||
#else
|
||||
QVector<QByteArray> result{
|
||||
QByteArray("UTC"),
|
||||
// Those named overtly in tst_QDateTime:
|
||||
QByteArray("Europe/Oslo"),
|
||||
QByteArray("America/Vancouver"),
|
||||
QByteArray("Europe/Berlin"),
|
||||
QByteArray("America/Sao_Paulo"),
|
||||
QByteArray("Pacific/Auckland"),
|
||||
QByteArray("Australia/Eucla"),
|
||||
QByteArray("Asia/Kathmandu"),
|
||||
QByteArray("Pacific/Kiritimati"),
|
||||
QByteArray("Pacific/Apia"),
|
||||
QByteArray("UTC+12:00"),
|
||||
QByteArray("Australia/Sydney"),
|
||||
QByteArray("Asia/Singapore"),
|
||||
QByteArray("Australia/Brisbane")
|
||||
};
|
||||
#endif
|
||||
result << QByteArray("Vulcan/ShiKahr"); // invalid: also worth testing
|
||||
return result;
|
||||
}
|
||||
|
||||
void tst_QTimeZone::isTimeZoneIdAvailable()
|
||||
{
|
||||
const QList<QByteArray> available = QTimeZone::availableTimeZoneIds();
|
||||
|
|
@ -47,6 +91,84 @@ void tst_QTimeZone::isTimeZoneIdAvailable()
|
|||
}
|
||||
}
|
||||
|
||||
void tst_QTimeZone::systemTimeZone()
|
||||
{
|
||||
QBENCHMARK {
|
||||
QTimeZone::systemTimeZone();
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QTimeZone::zoneByName_data()
|
||||
{
|
||||
QTest::addColumn<QByteArray>("name");
|
||||
|
||||
const auto names = enoughZones();
|
||||
for (const auto &name : names)
|
||||
QTest::newRow(name.constData()) << name;
|
||||
}
|
||||
|
||||
void tst_QTimeZone::zoneByName()
|
||||
{
|
||||
QFETCH(QByteArray, name);
|
||||
QTimeZone zone;
|
||||
QBENCHMARK {
|
||||
zone = QTimeZone(name);
|
||||
}
|
||||
Q_UNUSED(zone);
|
||||
}
|
||||
|
||||
void tst_QTimeZone::transitionList_data()
|
||||
{
|
||||
QTest::addColumn<QByteArray>("name");
|
||||
QTest::newRow("system") << QByteArray(); // Handled specially in the test.
|
||||
|
||||
const auto names = enoughZones();
|
||||
for (const auto &name : names) {
|
||||
QTimeZone zone(name);
|
||||
if (zone.isValid() && zone.hasTransitions())
|
||||
QTest::newRow(name.constData()) << name;
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QTimeZone::transitionList()
|
||||
{
|
||||
QFETCH(QByteArray, name);
|
||||
const QTimeZone zone = name.isEmpty() ? QTimeZone::systemTimeZone() : QTimeZone(name);
|
||||
const QDateTime early = QDate(1625, 6, 8).startOfDay(Qt::UTC); // Cassini's birth date
|
||||
const QDateTime late // End of 32-bit signed time_t
|
||||
= QDateTime::fromSecsSinceEpoch(std::numeric_limits<qint32>::max(), Qt::UTC);
|
||||
QTimeZone::OffsetDataList seq;
|
||||
QBENCHMARK {
|
||||
seq = zone.transitions(early, late);
|
||||
}
|
||||
Q_UNUSED(seq);
|
||||
}
|
||||
|
||||
void tst_QTimeZone::transitionsForward()
|
||||
{
|
||||
QFETCH(QByteArray, name);
|
||||
const QTimeZone zone = name.isEmpty() ? QTimeZone::systemTimeZone() : QTimeZone(name);
|
||||
const QDateTime early = QDate(1625, 6, 8).startOfDay(Qt::UTC); // Cassini's birth date
|
||||
QBENCHMARK {
|
||||
QTimeZone::OffsetData tran = zone.nextTransition(early);
|
||||
while (tran.atUtc.isValid())
|
||||
tran = zone.nextTransition(tran.atUtc);
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QTimeZone::transitionsReverse()
|
||||
{
|
||||
QFETCH(QByteArray, name);
|
||||
const QTimeZone zone = name.isEmpty() ? QTimeZone::systemTimeZone() : QTimeZone(name);
|
||||
const QDateTime late // End of 32-bit signed time_t
|
||||
= QDateTime::fromSecsSinceEpoch(std::numeric_limits<qint32>::max(), Qt::UTC);
|
||||
QBENCHMARK {
|
||||
QTimeZone::OffsetData tran = zone.previousTransition(late);
|
||||
while (tran.atUtc.isValid())
|
||||
tran = zone.previousTransition(tran.atUtc);
|
||||
}
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_QTimeZone)
|
||||
|
||||
#include "main.moc"
|
||||
|
|
|
|||
Loading…
Reference in New Issue