qt-bugs@ issue : 166518 Trolltech task ID : 167509 bugs.kde.org number : applied: no author: Matthias Kretz os: unix Consider: QWidget toplevel; QWidget dropWidget(&toplevel); dropWidget.setAcceptDrops(true); QWidget visibleFooWidget(&toplevel); find_child in qdnd_x11.cpp will find visibleFooWidget because it's the last entry in toplevel.children() and the QPoint of the drop is contained in it. But since it does not acceptDrops() handle_xdnd_position will now look at the parent widget which is toplevel. That one isWindow() so the iteration stops and no QDragEnterEvent will be sent. But it never even looked at dropWidget even though it also contains the QPoint of the drop! This patch makes the algorithm look at _all_ widgets that contain the QPoint. This bug effectively breaks all drops on QAbstractScrollArea subclasses with the KDE4 Oxygen widget style. Index: src/gui/kernel/qdnd_x11.cpp =================================================================== --- src/gui/kernel/qdnd_x11.cpp (revision 671753) +++ src/gui/kernel/qdnd_x11.cpp (working copy) @@ -58,6 +58,7 @@ #include "qimagewriter.h" #include "qbuffer.h" #include "qtextcodec.h" +#include "qstack.h" #include "qdnd_p.h" #include "qt_x11_p.h" @@ -715,7 +716,44 @@ void qt_xdnd_cleanup() } -static QWidget *find_child(QWidget *tlw, QPoint & p) +static QWidget *find_next_child(QWidget *child, QPoint &p, QStack &chosenLine) +{ + p = child->mapToParent(p); + QWidget *widget = child->parentWidget(); + if (!widget) { + return widget; + } + + bool done = false; + bool backwards = true; + while (!done) { + done = true; + if (((QExtraWidget*)widget)->extraData() && + ((QExtraWidget*)widget)->extraData()->xDndProxy != 0) + break; // stop searching for widgets under the mouse cursor if found widget is a proxy. + QObjectList children = widget->children(); + if (!chosenLine.isEmpty() && !children.isEmpty()) { + for(int i = backwards ? chosenLine.pop() : children.size(); i > 0;) { + --i; + QWidget *w = qobject_cast(children.at(i)); + if (!w) + continue; + if (w->isVisible() && + w->geometry().contains(p) && + !w->isWindow()) { + widget = w; + done = false; + backwards = false; + p = widget->mapFromParent(p); + chosenLine << i; + break; + } + } + } + } + return widget; +} +static QWidget *find_child(QWidget *tlw, QPoint & p, QStack &chosenLine) { QWidget *widget = tlw; @@ -739,6 +777,7 @@ static QWidget *find_child(QWidget *tlw, widget = w; done = false; p = widget->mapFromParent(p); + chosenLine.push(i); break; } } @@ -827,7 +866,8 @@ static void handle_xdnd_position(QWidget const unsigned long *l = (const unsigned long *)xe->xclient.data.l; QPoint p((l[2] & 0xffff0000) >> 16, l[2] & 0x0000ffff); - QWidget * c = find_child(w, p); // changes p to to c-local coordinates + QStack chosenLine; + QWidget * c = find_child(w, p, chosenLine); // changes p to to c-local coordinates if (!passive && checkEmbedded(c, xe)) return; @@ -861,8 +901,7 @@ static void handle_xdnd_position(QWidget if (!passive) { // otherwise just reject while (c && !c->acceptDrops() && !c->isWindow()) { - p = c->mapToParent(p); - c = c->parentWidget(); + c = find_next_child(c, p, chosenLine); } QWidget *target_widget = c && c->acceptDrops() ? c : 0;