summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLiang Qi <liang.qi@qt.io>2020-03-24 14:36:02 +0100
committerLiang Qi <liang.qi@qt.io>2020-03-24 14:36:02 +0100
commit576dc2c7010904b2ace4cdd7284599d696832186 (patch)
tree839f30eff1d150f3e0228fe1f4145986cf2eebe0 /src
parent6a972f44f3f88d6179e8e4eef54396b5808b3f4d (diff)
parentcc1d891b8ed1c4f23183e4b06f46e5840a993e35 (diff)
Merge remote-tracking branch 'origin/5.14' into 5.15
Conflicts: src/plugins/platforms/xcb/qxcbdrag.cpp Change-Id: I0b47324b70b0b4894e54b21aa3e7a5041f9bd5e3
Diffstat (limited to 'src')
-rw-r--r--src/corelib/io/qfileinfo.cpp2
-rw-r--r--src/corelib/time/qtimezoneprivate_p.h16
-rw-r--r--src/corelib/time/qtimezoneprivate_tz.cpp141
-rw-r--r--src/gui/doc/src/richtext.qdoc8
-rw-r--r--src/plugins/platforms/xcb/qxcbatom.cpp1
-rw-r--r--src/plugins/platforms/xcb/qxcbatom.h1
-rw-r--r--src/plugins/platforms/xcb/qxcbdrag.cpp108
-rw-r--r--src/plugins/platforms/xcb/qxcbdrag.h15
8 files changed, 223 insertions, 69 deletions
diff --git a/src/corelib/io/qfileinfo.cpp b/src/corelib/io/qfileinfo.cpp
index 64b1557231..84bca6e993 100644
--- a/src/corelib/io/qfileinfo.cpp
+++ b/src/corelib/io/qfileinfo.cpp
@@ -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
diff --git a/src/corelib/time/qtimezoneprivate_p.h b/src/corelib/time/qtimezoneprivate_p.h
index a57f61f381..f1b7fea1f9 100644
--- a/src/corelib/time/qtimezoneprivate_p.h
+++ b/src/corelib/time/qtimezoneprivate_p.h
@@ -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
diff --git a/src/corelib/time/qtimezoneprivate_tz.cpp b/src/corelib/time/qtimezoneprivate_tz.cpp
index 5e55c6897d..c451f95a0e 100644
--- a/src/corelib/time/qtimezoneprivate_tz.cpp
+++ b/src/corelib/time/qtimezoneprivate_tz.cpp
@@ -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()
diff --git a/src/gui/doc/src/richtext.qdoc b/src/gui/doc/src/richtext.qdoc
index b7c731cf43..a963124849 100644
--- a/src/gui/doc/src/richtext.qdoc
+++ b/src/gui/doc/src/richtext.qdoc
@@ -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
diff --git a/src/plugins/platforms/xcb/qxcbatom.cpp b/src/plugins/platforms/xcb/qxcbatom.cpp
index ff5c50b702..780816605a 100644
--- a/src/plugins/platforms/xcb/qxcbatom.cpp
+++ b/src/plugins/platforms/xcb/qxcbatom.cpp
@@ -182,6 +182,7 @@ static const char *xcb_atomnames = {
"XdndActionCopy\0"
"XdndActionLink\0"
"XdndActionMove\0"
+ "XdndActionAsk\0"
"XdndActionPrivate\0"
// Xkb
diff --git a/src/plugins/platforms/xcb/qxcbatom.h b/src/plugins/platforms/xcb/qxcbatom.h
index 80b5887395..9cf93ec314 100644
--- a/src/plugins/platforms/xcb/qxcbatom.h
+++ b/src/plugins/platforms/xcb/qxcbatom.h
@@ -183,6 +183,7 @@ public:
XdndActionCopy,
XdndActionLink,
XdndActionMove,
+ XdndActionAsk,
XdndActionPrivate,
// Xkb
diff --git a/src/plugins/platforms/xcb/qxcbdrag.cpp b/src/plugins/platforms/xcb/qxcbdrag.cpp
index e76fc8bd40..cc735f40a8 100644
--- a/src/plugins/platforms/xcb/qxcbdrag.cpp
+++ b/src/plugins/platforms/xcb/qxcbdrag.cpp
@@ -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;
}
diff --git a/src/plugins/platforms/xcb/qxcbdrag.h b/src/plugins/platforms/xcb/qxcbdrag.h
index 7bef7a818a..0d16afc47e 100644
--- a/src/plugins/platforms/xcb/qxcbdrag.h
+++ b/src/plugins/platforms/xcb/qxcbdrag.h
@@ -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;