1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the QtGui module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file.  Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 #include "qplatformdefs.h"
43 
44 #include "qapplication.h"
45 #include "qabstracteventdispatcher.h"
46 
47 #ifndef QT_NO_DRAGANDDROP
48 
49 #include "qwidget.h"
50 #include "qpainter.h"
51 #include "qpixmap.h"
52 #include "qbitmap.h"
53 #include "qdesktopwidget.h"
54 #include "qevent.h"
55 #include "qiodevice.h"
56 #include "qpointer.h"
57 #include "qcursor.h"
58 #include "qelapsedtimer.h"
59 #include "qvariant.h"
60 #include "qvector.h"
61 #include "qurl.h"
62 #include "qdebug.h"
63 #include "qimagewriter.h"
64 #include "qbuffer.h"
65 #include "qtextcodec.h"
66 
67 #include "qdnd_p.h"
68 #include "qapplication_p.h"
69 #include "qt_x11_p.h"
70 #include "qx11info_x11.h"
71 
72 #include "qwidget_p.h"
73 #include "qcursor_p.h"
74 
75 #ifndef QT_NO_XFIXES
76 #include <X11/extensions/Xfixes.h>
77 #endif
78 
79 QT_BEGIN_NAMESPACE
80 
81 // #define DND_DEBUG
82 #ifdef DND_DEBUG
83 #define DEBUG qDebug
84 #else
85 #define DEBUG if(0) qDebug
86 #endif
87 
88 #ifdef DND_DEBUG
89 #define DNDDEBUG qDebug()
90 #else
91 #define DNDDEBUG if(0) qDebug()
92 #endif
93 
findXdndDropTransactionByWindow(Window window)94 static int findXdndDropTransactionByWindow(Window window)
95 {
96     int at = -1;
97     for (int i = 0; i < X11->dndDropTransactions.count(); ++i) {
98         const QXdndDropTransaction &t = X11->dndDropTransactions.at(i);
99         if (t.target == window || t.proxy_target == window) {
100             at = i;
101             break;
102         }
103     }
104     return at;
105 }
106 
findXdndDropTransactionByTime(Time timestamp)107 static int findXdndDropTransactionByTime(Time timestamp)
108 {
109     int at = -1;
110     for (int i = 0; i < X11->dndDropTransactions.count(); ++i) {
111         const QXdndDropTransaction &t = X11->dndDropTransactions.at(i);
112         if (t.timestamp == timestamp) {
113             at = i;
114             break;
115         }
116     }
117     return at;
118 }
119 
120 // timer used to discard old XdndDrop transactions
121 static int transaction_expiry_timer = -1;
122 enum { XdndDropTransactionTimeout = 5000 }; // 5 seconds
123 
restartXdndDropExpiryTimer()124 static void restartXdndDropExpiryTimer()
125 {
126     if (transaction_expiry_timer != -1)
127         QDragManager::self()->killTimer(transaction_expiry_timer);
128     transaction_expiry_timer = QDragManager::self()->startTimer(XdndDropTransactionTimeout);
129 }
130 
131 
132 // find an ancestor with XdndAware on it
findXdndAwareParent(Window window)133 static Window findXdndAwareParent(Window window)
134 {
135     Window target = 0;
136     forever {
137         // check if window has XdndAware
138         Atom type = 0;
139         int f;
140         unsigned long n, a;
141         unsigned char *data = 0;
142         if (XGetWindowProperty(X11->display, window, ATOM(XdndAware), 0, 0, False,
143                                AnyPropertyType, &type, &f,&n,&a,&data) == Success) {
144 	    if (data)
145                 XFree(data);
146 	    if (type) {
147                 target = window;
148                 break;
149             }
150         }
151 
152         // try window's parent
153         Window root;
154         Window parent;
155         Window *children;
156         uint unused;
157         if (!XQueryTree(X11->display, window, &root, &parent, &children, &unused))
158             break;
159         if (children)
160             XFree(children);
161         if (window == root)
162             break;
163         window = parent;
164     }
165     return target;
166 }
167 
168 
169 
170 
171 // and all this stuff is copied -into- qapp_x11.cpp
172 
173 static void handle_xdnd_position(QWidget *, const XEvent *, bool);
174 static void handle_xdnd_status(QWidget * w, const XEvent * xe, bool /*passive*/);
175 
176 const int xdnd_version = 5;
177 
xdndaction_to_qtaction(Atom atom)178 static Qt::DropAction xdndaction_to_qtaction(Atom atom)
179 {
180     if (atom == ATOM(XdndActionCopy) || atom == 0)
181         return Qt::CopyAction;
182     if (atom == ATOM(XdndActionLink))
183         return Qt::LinkAction;
184     if (atom == ATOM(XdndActionMove))
185         return Qt::MoveAction;
186     return Qt::CopyAction;
187 }
188 
qtaction_to_xdndaction(Qt::DropAction a)189 static int qtaction_to_xdndaction(Qt::DropAction a)
190 {
191     switch (a) {
192     case Qt::CopyAction:
193         return ATOM(XdndActionCopy);
194     case Qt::LinkAction:
195         return ATOM(XdndActionLink);
196     case Qt::MoveAction:
197     case Qt::TargetMoveAction:
198         return ATOM(XdndActionMove);
199     case Qt::IgnoreAction:
200         return XNone;
201     default:
202         return ATOM(XdndActionCopy);
203     }
204 }
205 
206 // clean up the stuff used.
207 static void qt_xdnd_cleanup();
208 
209 static void qt_xdnd_send_leave();
210 
211 // real variables:
212 // xid of current drag source
213 static Atom qt_xdnd_dragsource_xid = 0;
214 
215 // the types in this drop. 100 is no good, but at least it's big.
216 const int qt_xdnd_max_type = 100;
217 static Atom qt_xdnd_types[qt_xdnd_max_type + 1];
218 
219 // timer used when target wants "continuous" move messages (eg. scroll)
220 static int heartbeat = -1;
221 // rectangle in which the answer will be the same
222 static QRect qt_xdnd_source_sameanswer;
223 // top-level window we sent position to last.
224 static Window qt_xdnd_current_target;
225 // window to send events to (always valid if qt_xdnd_current_target)
226 static Window qt_xdnd_current_proxy_target;
227 static Time qt_xdnd_source_current_time;
228 
229 // widget we forwarded position to last, and local position
230 static QPointer<QWidget> qt_xdnd_current_widget;
231 static QPoint qt_xdnd_current_position;
232 // timestamp from the XdndPosition and XdndDrop
233 static Time qt_xdnd_target_current_time;
234 // screen number containing the pointer... -1 means default
235 static int qt_xdnd_current_screen = -1;
236 // state of dragging... true if dragging, false if not
237 bool qt_xdnd_dragging = false;
238 
239 static bool waiting_for_status = false;
240 
241 // used to preset each new QDragMoveEvent
242 static Qt::DropAction last_target_accepted_action = Qt::IgnoreAction;
243 
244 // Shift/Ctrl handling, and final drop status
245 static Qt::DropAction global_accepted_action = Qt::CopyAction;
246 static Qt::DropActions possible_actions = Qt::IgnoreAction;
247 
248 // for embedding only
249 static QWidget* current_embedding_widget  = 0;
250 static XEvent last_enter_event;
251 
252 // cursors
253 static QCursor *noDropCursor = 0;
254 static QCursor *moveCursor = 0;
255 static QCursor *copyCursor = 0;
256 static QCursor *linkCursor = 0;
257 
258 static QPixmap *defaultPm = 0;
259 
260 static const int default_pm_hotx = -2;
261 static const int default_pm_hoty = -16;
262 static const char* const default_pm[] = {
263 "13 9 3 1",
264 ".      c None",
265 "       c #000000",
266 "X      c #FFFFFF",
267 "X X X X X X X",
268 " X X X X X X ",
269 "X ......... X",
270 " X.........X ",
271 "X ......... X",
272 " X.........X ",
273 "X ......... X",
274 " X X X X X X ",
275 "X X X X X X X"
276 };
277 
278 class QShapedPixmapWidget : public QWidget
279 {
280     Q_OBJECT
281 public:
QShapedPixmapWidget(QWidget * w)282     QShapedPixmapWidget(QWidget* w) :
283         QWidget(w,
284                 Qt::Tool | Qt::FramelessWindowHint
285                 | Qt::X11BypassWindowManagerHint
286                 | Qt::BypassGraphicsProxyWidget)
287     {
288         setAttribute(Qt::WA_X11NetWmWindowTypeDND);
289     }
290 
setPixmap(const QPixmap & pm)291     void setPixmap(const QPixmap &pm)
292     {
293         QBitmap mask = pm.mask();
294         if (!mask.isNull()) {
295             setMask(mask);
296         } else {
297             clearMask();
298         }
299         resize(pm.width(),pm.height());
300         pixmap = pm;
301         update();
302     }
303     QPoint pm_hot;
304 
305 protected:
306     QPixmap pixmap;
paintEvent(QPaintEvent *)307     void paintEvent(QPaintEvent*)
308     {
309         QPainter p(this);
310         p.drawPixmap(0, 0, pixmap);
311     }
312 };
313 
314 #include "qdnd_x11.moc"
315 
316 struct XdndData {
317     QShapedPixmapWidget *deco;
318     QWidget* desktop_proxy;
319 };
320 
321 static XdndData xdnd_data = { 0, 0 };
322 
323 class QExtraWidget : public QWidget
324 {
325     Q_DECLARE_PRIVATE(QWidget)
326 public:
327     inline QWExtra* extraData();
328     inline QTLWExtra* topData();
329 };
330 
extraData()331 inline QWExtra* QExtraWidget::extraData() { return d_func()->extraData(); }
topData()332 inline QTLWExtra* QExtraWidget::topData() { return d_func()->topData(); }
333 
334 
xdndProxy(WId w)335 static WId xdndProxy(WId w)
336 {
337     Atom type = XNone;
338     int f;
339     unsigned long n, a;
340     unsigned char *retval = 0;
341     XGetWindowProperty(X11->display, w, ATOM(XdndProxy), 0, 1, False,
342                        XA_WINDOW, &type, &f,&n,&a,&retval);
343     WId *proxy_id_ptr = (WId *)retval;
344     WId proxy_id = 0;
345     if (type == XA_WINDOW && proxy_id_ptr) {
346         proxy_id = *proxy_id_ptr;
347         XFree(proxy_id_ptr);
348         proxy_id_ptr = 0;
349         // Already exists. Real?
350         X11->ignoreBadwindow();
351         XGetWindowProperty(X11->display, proxy_id, ATOM(XdndProxy), 0, 1, False,
352                            XA_WINDOW, &type, &f,&n,&a,&retval);
353         proxy_id_ptr = (WId *)retval;
354         if (X11->badwindow() || type != XA_WINDOW || !proxy_id_ptr || *proxy_id_ptr != proxy_id)
355             // Bogus - we will overwrite.
356             proxy_id = 0;
357     }
358     if (proxy_id_ptr)
359         XFree(proxy_id_ptr);
360     return proxy_id;
361 }
362 
xdndEnable(QWidget * w,bool on)363 static bool xdndEnable(QWidget* w, bool on)
364 {
365     DNDDEBUG << "xdndEnable" << w << on;
366     if (on) {
367         QWidget * xdnd_widget = 0;
368         if ((w->windowType() == Qt::Desktop)) {
369             if (xdnd_data.desktop_proxy) // *WE* already have one.
370                 return false;
371 
372             // As per Xdnd4, use XdndProxy
373             XGrabServer(X11->display);
374             Q_ASSERT(w->testAttribute(Qt::WA_WState_Created));
375             WId proxy_id = xdndProxy(w->effectiveWinId());
376 
377             if (!proxy_id) {
378                 xdnd_widget = xdnd_data.desktop_proxy = new QWidget;
379                 proxy_id = xdnd_data.desktop_proxy->effectiveWinId();
380                 XChangeProperty (X11->display, w->effectiveWinId(), ATOM(XdndProxy),
381                                  XA_WINDOW, 32, PropModeReplace, (unsigned char *)&proxy_id, 1);
382                 XChangeProperty (X11->display, proxy_id, ATOM(XdndProxy),
383                                  XA_WINDOW, 32, PropModeReplace, (unsigned char *)&proxy_id, 1);
384             }
385 
386             XUngrabServer(X11->display);
387         } else {
388             xdnd_widget = w->window();
389         }
390         if (xdnd_widget) {
391             DNDDEBUG << "setting XdndAware for" << xdnd_widget << xdnd_widget->effectiveWinId();
392             Atom atm = (Atom)xdnd_version;
393             Q_ASSERT(xdnd_widget->testAttribute(Qt::WA_WState_Created));
394             XChangeProperty(X11->display, xdnd_widget->effectiveWinId(), ATOM(XdndAware),
395                              XA_ATOM, 32, PropModeReplace, (unsigned char *)&atm, 1);
396             return true;
397         } else {
398             return false;
399         }
400     } else {
401         if ((w->windowType() == Qt::Desktop)) {
402             XDeleteProperty(X11->display, w->internalWinId(), ATOM(XdndProxy));
403             delete xdnd_data.desktop_proxy;
404             xdnd_data.desktop_proxy = 0;
405         } else {
406             DNDDEBUG << "not deleting XDndAware";
407         }
408         return true;
409     }
410 }
411 
xdndAtomToString(Atom a)412 QByteArray QX11Data::xdndAtomToString(Atom a)
413 {
414     if (!a) return 0;
415 
416     if (a == XA_STRING || a == ATOM(UTF8_STRING)) {
417         return "text/plain"; // some Xdnd clients are dumb
418     }
419     char *atom = XGetAtomName(display, a);
420     QByteArray result = atom;
421     XFree(atom);
422     return result;
423 }
424 
xdndStringToAtom(const char * mimeType)425 Atom QX11Data::xdndStringToAtom(const char *mimeType)
426 {
427     if (!mimeType || !*mimeType)
428         return 0;
429     return XInternAtom(display, mimeType, False);
430 }
431 
432 //$$$
xdndMimeAtomToString(Atom a)433 QString QX11Data::xdndMimeAtomToString(Atom a)
434 {
435     QString atomName;
436     if (a) {
437         char *atom = XGetAtomName(display, a);
438         atomName = QString::fromLatin1(atom);
439         XFree(atom);
440     }
441     return atomName;
442 }
443 
444 //$$$
xdndMimeStringToAtom(const QString & mimeType)445 Atom QX11Data::xdndMimeStringToAtom(const QString &mimeType)
446 {
447     if (mimeType.isEmpty())
448         return 0;
449     return XInternAtom(display, mimeType.toLatin1().constData(), False);
450 }
451 
452 //$$$ replace ccxdndAtomToString()
xdndMimeFormatsForAtom(Atom a)453 QStringList QX11Data::xdndMimeFormatsForAtom(Atom a)
454 {
455     QStringList formats;
456     if (a) {
457         QString atomName = xdndMimeAtomToString(a);
458         formats.append(atomName);
459 
460         // special cases for string type
461         if (a == ATOM(UTF8_STRING) || a == XA_STRING
462             || a == ATOM(TEXT) || a == ATOM(COMPOUND_TEXT))
463             formats.append(QLatin1String("text/plain"));
464 
465         // special cases for uris
466         if (atomName == QLatin1String("text/x-moz-url"))
467             formats.append(QLatin1String("text/uri-list"));
468 
469         // special case for images
470         if (a == XA_PIXMAP)
471             formats.append(QLatin1String("image/ppm"));
472     }
473     return formats;
474 }
475 
476 //$$$
xdndMimeDataForAtom(Atom a,QMimeData * mimeData,QByteArray * data,Atom * atomFormat,int * dataFormat)477 bool QX11Data::xdndMimeDataForAtom(Atom a, QMimeData *mimeData, QByteArray *data, Atom *atomFormat, int *dataFormat)
478 {
479     bool ret = false;
480     *atomFormat = a;
481     *dataFormat = 8;
482     QString atomName = xdndMimeAtomToString(a);
483     if (QInternalMimeData::hasFormatHelper(atomName, mimeData)) {
484         *data = QInternalMimeData::renderDataHelper(atomName, mimeData);
485         if (atomName == QLatin1String("application/x-color"))
486             *dataFormat = 16;
487         ret = true;
488     } else {
489         if ((a == ATOM(UTF8_STRING) || a == XA_STRING
490              || a == ATOM(TEXT) || a == ATOM(COMPOUND_TEXT))
491             && QInternalMimeData::hasFormatHelper(QLatin1String("text/plain"), mimeData)) {
492             if (a == ATOM(UTF8_STRING)){
493                 *data = QInternalMimeData::renderDataHelper(QLatin1String("text/plain"), mimeData);
494                 ret = true;
495             } else if (a == XA_STRING) {
496                 *data = QString::fromUtf8(QInternalMimeData::renderDataHelper(
497                         QLatin1String("text/plain"), mimeData)).toLocal8Bit();
498                 ret = true;
499             } else if (a == ATOM(TEXT) || a == ATOM(COMPOUND_TEXT)) {
500                 // the ICCCM states that TEXT and COMPOUND_TEXT are in the
501                 // encoding of choice, so we choose the encoding of the locale
502                 QByteArray strData = QString::fromUtf8(QInternalMimeData::renderDataHelper(
503                                      QLatin1String("text/plain"), mimeData)).toLocal8Bit();
504                 char *list[] = { strData.data(), NULL };
505 
506                 XICCEncodingStyle style = (a == ATOM(COMPOUND_TEXT))
507                                         ? XCompoundTextStyle : XStdICCTextStyle;
508                 XTextProperty textprop;
509                 if (list[0] != NULL
510                     && XmbTextListToTextProperty(X11->display, list, 1, style,
511                                                  &textprop) == Success) {
512                     *atomFormat = textprop.encoding;
513                     *dataFormat = textprop.format;
514                     *data = QByteArray((const char *) textprop.value, textprop.nitems * textprop.format / 8);
515                     ret = true;
516 
517                     DEBUG("    textprop type %lx\n"
518                     "    textprop name '%s'\n"
519                     "    format %d\n"
520                     "    %ld items\n"
521                     "    %d bytes\n",
522                     textprop.encoding,
523                     X11->xdndMimeAtomToString(textprop.encoding).toLatin1().data(),
524                     textprop.format, textprop.nitems, data->size());
525 
526                     XFree(textprop.value);
527                 }
528             }
529         } else if (atomName == QLatin1String("text/x-moz-url") &&
530                    QInternalMimeData::hasFormatHelper(QLatin1String("text/uri-list"), mimeData)) {
531             QByteArray uri = QInternalMimeData::renderDataHelper(
532                              QLatin1String("text/uri-list"), mimeData).split('\n').first();
533             QString mozUri = QString::fromLatin1(uri, uri.size());
534             mozUri += QLatin1Char('\n');
535             *data = QByteArray(reinterpret_cast<const char *>(mozUri.utf16()), mozUri.length() * 2);
536             ret = true;
537         } else if ((a == XA_PIXMAP || a == XA_BITMAP) && mimeData->hasImage()) {
538             QPixmap pm = qvariant_cast<QPixmap>(mimeData->imageData());
539             if (a == XA_BITMAP && pm.depth() != 1) {
540                 QImage img = pm.toImage();
541                 img = img.convertToFormat(QImage::Format_MonoLSB);
542                 pm = QPixmap::fromImage(img);
543             }
544             QDragManager *dm = QDragManager::self();
545             if (dm) {
546                 Pixmap handle = pm.handle();
547                 *data = QByteArray((const char *) &handle, sizeof(Pixmap));
548                 dm->xdndMimeTransferedPixmap[dm->xdndMimeTransferedPixmapIndex] = pm;
549                 dm->xdndMimeTransferedPixmapIndex =
550                             (dm->xdndMimeTransferedPixmapIndex + 1) % 2;
551                 ret = true;
552             }
553         } else {
554             DEBUG("QClipboard: xdndMimeDataForAtom(): converting to type '%s' is not supported", qPrintable(atomName));
555         }
556     }
557     return ret && data != 0;
558 }
559 
560 //$$$
xdndMimeAtomsForFormat(const QString & format)561 QList<Atom> QX11Data::xdndMimeAtomsForFormat(const QString &format)
562 {
563     QList<Atom> atoms;
564     atoms.append(xdndMimeStringToAtom(format));
565 
566     // special cases for strings
567     if (format == QLatin1String("text/plain")) {
568         atoms.append(ATOM(UTF8_STRING));
569         atoms.append(XA_STRING);
570         atoms.append(ATOM(TEXT));
571         atoms.append(ATOM(COMPOUND_TEXT));
572     }
573 
574     // special cases for uris
575     if (format == QLatin1String("text/uri-list")) {
576         atoms.append(xdndMimeStringToAtom(QLatin1String("text/x-moz-url")));
577     }
578 
579     //special cases for images
580     if (format == QLatin1String("image/ppm"))
581         atoms.append(XA_PIXMAP);
582     if (format == QLatin1String("image/pbm"))
583         atoms.append(XA_BITMAP);
584 
585     return atoms;
586 }
587 
588 //$$$
xdndMimeConvertToFormat(Atom a,const QByteArray & data,const QString & format,QVariant::Type requestedType,const QByteArray & encoding)589 QVariant QX11Data::xdndMimeConvertToFormat(Atom a, const QByteArray &data, const QString &format, QVariant::Type requestedType, const QByteArray &encoding)
590 {
591     QString atomName = xdndMimeAtomToString(a);
592     if (atomName == format)
593         return data;
594 
595     if (!encoding.isEmpty()
596         && atomName == format + QLatin1String(";charset=") + QString::fromLatin1(encoding)) {
597 
598         if (requestedType == QVariant::String) {
599             QTextCodec *codec = QTextCodec::codecForName(encoding);
600             if (codec)
601                 return codec->toUnicode(data);
602         }
603 
604         return data;
605     }
606 
607     // special cases for string types
608     if (format == QLatin1String("text/plain")) {
609         if (a == ATOM(UTF8_STRING))
610             return QString::fromUtf8(data);
611         if (a == XA_STRING)
612             return QString::fromLatin1(data);
613         if (a == ATOM(TEXT) || a == ATOM(COMPOUND_TEXT))
614             // #### might be wrong for COMPUND_TEXT
615             return QString::fromLocal8Bit(data, data.size());
616     }
617 
618     // special case for uri types
619     if (format == QLatin1String("text/uri-list")) {
620         if (atomName == QLatin1String("text/x-moz-url")) {
621             // we expect this as utf16 <url><space><title>
622             // the first part is a url that should only contain ascci char
623             // so it should be safe to check that the second char is 0
624             // to verify that it is utf16
625             if (data.size() > 1 && data.at(1) == 0)
626                 return QString::fromRawData((const QChar *)data.constData(),
627                                 data.size() / 2).split(QLatin1Char('\n')).first().toLatin1();
628         }
629     }
630 
631     // special cas for images
632     if (format == QLatin1String("image/ppm")) {
633         if (a == XA_PIXMAP && data.size() == sizeof(Pixmap)) {
634             Pixmap xpm = *((Pixmap*)data.data());
635             if (!xpm)
636                 return QByteArray();
637             QPixmap qpm = QPixmap::fromX11Pixmap(xpm);
638             QImageWriter imageWriter;
639             imageWriter.setFormat("PPMRAW");
640             QImage imageToWrite = qpm.toImage();
641             QBuffer buf;
642             buf.open(QIODevice::WriteOnly);
643             imageWriter.setDevice(&buf);
644             imageWriter.write(imageToWrite);
645             return buf.buffer();
646         }
647     }
648     return QVariant();
649 }
650 
651 //$$$ middle of xdndObtainData
xdndMimeAtomForFormat(const QString & format,QVariant::Type requestedType,const QList<Atom> & atoms,QByteArray * encoding)652 Atom QX11Data::xdndMimeAtomForFormat(const QString &format, QVariant::Type requestedType, const QList<Atom> &atoms, QByteArray *encoding)
653 {
654     encoding->clear();
655 
656     // find matches for string types
657     if (format == QLatin1String("text/plain")) {
658         if (atoms.contains(ATOM(UTF8_STRING)))
659             return ATOM(UTF8_STRING);
660         if (atoms.contains(ATOM(COMPOUND_TEXT)))
661             return ATOM(COMPOUND_TEXT);
662         if (atoms.contains(ATOM(TEXT)))
663             return ATOM(TEXT);
664         if (atoms.contains(XA_STRING))
665             return XA_STRING;
666     }
667 
668     // find matches for uri types
669     if (format == QLatin1String("text/uri-list")) {
670         Atom a = xdndMimeStringToAtom(format);
671         if (a && atoms.contains(a))
672             return a;
673         a = xdndMimeStringToAtom(QLatin1String("text/x-moz-url"));
674         if (a && atoms.contains(a))
675             return a;
676     }
677 
678     // find match for image
679     if (format == QLatin1String("image/ppm")) {
680         if (atoms.contains(XA_PIXMAP))
681             return XA_PIXMAP;
682     }
683 
684     // for string/text requests try to use a format with a well-defined charset
685     // first to avoid encoding problems
686     if (requestedType == QVariant::String
687         && format.startsWith(QLatin1String("text/"))
688         && !format.contains(QLatin1String("charset="))) {
689 
690         QString formatWithCharset = format;
691         formatWithCharset.append(QLatin1String(";charset=utf-8"));
692 
693         Atom a = xdndMimeStringToAtom(formatWithCharset);
694         if (a && atoms.contains(a)) {
695             *encoding = "utf-8";
696             return a;
697         }
698     }
699 
700     Atom a = xdndMimeStringToAtom(format);
701     if (a && atoms.contains(a))
702         return a;
703 
704     return 0;
705 }
706 
xdndSetup()707 void QX11Data::xdndSetup() {
708     QCursorData::initialize();
709     qAddPostRoutine(qt_xdnd_cleanup);
710 }
711 
712 
qt_xdnd_cleanup()713 void qt_xdnd_cleanup()
714 {
715     delete noDropCursor;
716     noDropCursor = 0;
717     delete copyCursor;
718     copyCursor = 0;
719     delete moveCursor;
720     moveCursor = 0;
721     delete linkCursor;
722     linkCursor = 0;
723     delete defaultPm;
724     defaultPm = 0;
725     delete xdnd_data.desktop_proxy;
726     xdnd_data.desktop_proxy = 0;
727     delete xdnd_data.deco;
728     xdnd_data.deco = 0;
729 }
730 
731 
find_child(QWidget * tlw,QPoint & p)732 static QWidget *find_child(QWidget *tlw, QPoint & p)
733 {
734     QWidget *widget = tlw;
735 
736     p = widget->mapFromGlobal(p);
737     bool done = false;
738     while (!done) {
739         done = true;
740         if (((QExtraWidget*)widget)->extraData() &&
741              ((QExtraWidget*)widget)->extraData()->xDndProxy != 0)
742             break; // stop searching for widgets under the mouse cursor if found widget is a proxy.
743         QObjectList children = widget->children();
744         if (!children.isEmpty()) {
745             for(int i = children.size(); i > 0;) {
746                 --i;
747                 QWidget *w = qobject_cast<QWidget *>(children.at(i));
748                 if (!w)
749                     continue;
750                 if (w->testAttribute(Qt::WA_TransparentForMouseEvents))
751                     continue;
752                 if (w->isVisible() &&
753                      w->geometry().contains(p) &&
754                      !w->isWindow()) {
755                     widget = w;
756                     done = false;
757                     p = widget->mapFromParent(p);
758                     break;
759                 }
760             }
761         }
762     }
763     return widget;
764 }
765 
766 
checkEmbedded(QWidget * w,const XEvent * xe)767 static bool checkEmbedded(QWidget* w, const XEvent* xe)
768 {
769     if (!w)
770         return false;
771 
772     if (current_embedding_widget != 0 && current_embedding_widget != w) {
773         qt_xdnd_current_target = ((QExtraWidget*)current_embedding_widget)->extraData()->xDndProxy;
774         qt_xdnd_current_proxy_target = qt_xdnd_current_target;
775         qt_xdnd_send_leave();
776         qt_xdnd_current_target = 0;
777         qt_xdnd_current_proxy_target = 0;
778         current_embedding_widget = 0;
779     }
780 
781     QWExtra* extra = ((QExtraWidget*)w)->extraData();
782     if (extra && extra->xDndProxy != 0) {
783 
784         if (current_embedding_widget != w) {
785 
786             last_enter_event.xany.window = extra->xDndProxy;
787             XSendEvent(X11->display, extra->xDndProxy, False, NoEventMask, &last_enter_event);
788             current_embedding_widget = w;
789         }
790 
791         ((XEvent*)xe)->xany.window = extra->xDndProxy;
792         XSendEvent(X11->display, extra->xDndProxy, False, NoEventMask, (XEvent*)xe);
793         if (qt_xdnd_current_widget != w) {
794             qt_xdnd_current_widget = w;
795         }
796         return true;
797     }
798     current_embedding_widget = 0;
799     return false;
800 }
801 
xdndHandleEnter(QWidget *,const XEvent * xe,bool)802 void QX11Data::xdndHandleEnter(QWidget *, const XEvent * xe, bool /*passive*/)
803 {
804     motifdnd_active = false;
805 
806     last_enter_event.xclient = xe->xclient;
807 
808     const long *l = xe->xclient.data.l;
809     int version = (int)(((unsigned long)(l[1])) >> 24);
810 
811     if (version > xdnd_version)
812         return;
813 
814     qt_xdnd_dragsource_xid = l[0];
815 
816     int j = 0;
817     if (l[1] & 1) {
818         // get the types from XdndTypeList
819         Atom   type = XNone;
820         int f;
821         unsigned long n, a;
822         unsigned char *retval = 0;
823         XGetWindowProperty(X11->display, qt_xdnd_dragsource_xid, ATOM(XdndTypelist), 0,
824                            qt_xdnd_max_type, False, XA_ATOM, &type, &f,&n,&a,&retval);
825         if (retval) {
826             Atom *data = (Atom *)retval;
827             for (; j<qt_xdnd_max_type && j < (int)n; j++) {
828                 qt_xdnd_types[j] = data[j];
829             }
830             XFree((uchar*)data);
831         }
832     } else {
833         // get the types from the message
834         int i;
835         for(i=2; i < 5; i++) {
836             qt_xdnd_types[j++] = l[i];
837         }
838     }
839     qt_xdnd_types[j] = 0;
840 }
841 
handle_xdnd_position(QWidget * w,const XEvent * xe,bool passive)842 static void handle_xdnd_position(QWidget *w, const XEvent * xe, bool passive)
843 {
844     const unsigned long *l = (const unsigned long *)xe->xclient.data.l;
845 
846     QPoint p((l[2] & 0xffff0000) >> 16, l[2] & 0x0000ffff);
847     QWidget * c = find_child(w, p); // changes p to to c-local coordinates
848 
849     if (!passive && checkEmbedded(c, xe))
850         return;
851 
852     if (!c || (!c->acceptDrops() && (c->windowType() == Qt::Desktop)))
853         return;
854 
855     if (l[0] != qt_xdnd_dragsource_xid) {
856         DEBUG("xdnd drag position from unexpected source (%08lx not %08lx)", l[0], qt_xdnd_dragsource_xid);
857         return;
858     }
859 
860     // timestamp from the source
861     if (l[3] != 0) {
862         // Some X server/client combination swallow the first 32 bit and
863         // interpret a set bit 31 as negative sign.
864         qt_xdnd_target_current_time = X11->userTime =
865             ((sizeof(Time) == 8 && xe->xclient.data.l[3] < 0)
866              ? uint(l[3])
867              : l[3]);
868     }
869 
870     QDragManager *manager = QDragManager::self();
871     QMimeData *dropData = manager->object ? manager->dragPrivate()->data : manager->dropData;
872 
873     XClientMessageEvent response;
874     response.type = ClientMessage;
875     response.window = qt_xdnd_dragsource_xid;
876     response.format = 32;
877     response.message_type = ATOM(XdndStatus);
878     response.data.l[0] = w->effectiveWinId();
879     response.data.l[1] = 0; // flags
880     response.data.l[2] = 0; // x, y
881     response.data.l[3] = 0; // w, h
882     response.data.l[4] = 0; // action
883 
884     if (!passive) { // otherwise just reject
885         while (c && !c->acceptDrops() && !c->isWindow()) {
886             p = c->mapToParent(p);
887             c = c->parentWidget();
888         }
889         QWidget *target_widget = c && c->acceptDrops() ? c : 0;
890 
891         QRect answerRect(c->mapToGlobal(p), QSize(1,1));
892 
893         if (manager->object) {
894             possible_actions = manager->dragPrivate()->possible_actions;
895         } else {
896             possible_actions = Qt::DropActions(xdndaction_to_qtaction(l[4]));
897 //             possible_actions |= Qt::CopyAction;
898         }
899         QDragMoveEvent me(p, possible_actions, dropData, QApplication::mouseButtons(), QApplication::keyboardModifiers());
900 
901         Qt::DropAction accepted_action = Qt::IgnoreAction;
902 
903 
904         if (target_widget != qt_xdnd_current_widget) {
905             if (qt_xdnd_current_widget) {
906                 QDragLeaveEvent e;
907                 QApplication::sendEvent(qt_xdnd_current_widget, &e);
908             }
909             if (qt_xdnd_current_widget != target_widget) {
910                 qt_xdnd_current_widget = target_widget;
911             }
912             if (target_widget) {
913                 qt_xdnd_current_position = p;
914 
915                 last_target_accepted_action = Qt::IgnoreAction;
916                 QDragEnterEvent de(p, possible_actions, dropData, QApplication::mouseButtons(), QApplication::keyboardModifiers());
917                 QApplication::sendEvent(target_widget, &de);
918                 if (de.isAccepted() && de.dropAction() != Qt::IgnoreAction)
919                     last_target_accepted_action = de.dropAction();
920             }
921         }
922 
923         DEBUG() << "qt_handle_xdnd_position action=" << X11->xdndAtomToString(l[4]);
924         if (!target_widget) {
925             answerRect = QRect(p, QSize(1, 1));
926         } else {
927             qt_xdnd_current_widget = c;
928             qt_xdnd_current_position = p;
929 
930             if (last_target_accepted_action != Qt::IgnoreAction) {
931                 me.setDropAction(last_target_accepted_action);
932                 me.accept();
933             }
934             QApplication::sendEvent(c, &me);
935             if (me.isAccepted()) {
936                 response.data.l[1] = 1; // yes
937                 accepted_action = me.dropAction();
938                 last_target_accepted_action = accepted_action;
939             } else {
940                 response.data.l[0] = 0;
941                 last_target_accepted_action = Qt::IgnoreAction;
942             }
943             answerRect = me.answerRect().intersected(c->rect());
944         }
945         answerRect = QRect(c->mapToGlobal(answerRect.topLeft()), answerRect.size());
946 
947         if (answerRect.left() < 0)
948             answerRect.setLeft(0);
949         if (answerRect.right() > 4096)
950             answerRect.setRight(4096);
951         if (answerRect.top() < 0)
952             answerRect.setTop(0);
953         if (answerRect.bottom() > 4096)
954             answerRect.setBottom(4096);
955         if (answerRect.width() < 0)
956             answerRect.setWidth(0);
957         if (answerRect.height() < 0)
958             answerRect.setHeight(0);
959 
960         response.data.l[2] = (answerRect.x() << 16) + answerRect.y();
961         response.data.l[3] = (answerRect.width() << 16) + answerRect.height();
962         response.data.l[4] = qtaction_to_xdndaction(accepted_action);
963     }
964 
965     // reset
966     qt_xdnd_target_current_time = CurrentTime;
967 
968     QWidget * source = QWidget::find(qt_xdnd_dragsource_xid);
969     if (source && (source->windowType() == Qt::Desktop) && !source->acceptDrops())
970         source = 0;
971 
972     DEBUG() << "sending XdndStatus";
973     if (source)
974         handle_xdnd_status(source, (const XEvent *)&response, passive);
975     else
976         XSendEvent(X11->display, qt_xdnd_dragsource_xid, False, NoEventMask, (XEvent*)&response);
977 }
978 
xdnd_position_scanner(Display *,XEvent * event,XPointer)979 static Bool xdnd_position_scanner(Display *, XEvent *event, XPointer)
980 {
981     if (event->type != ClientMessage)
982         return false;
983     XClientMessageEvent *ev = &event->xclient;
984 
985     if (ev->message_type == ATOM(XdndPosition))
986         return true;
987 
988     return false;
989 }
990 
xdndHandlePosition(QWidget * w,const XEvent * xe,bool passive)991 void QX11Data::xdndHandlePosition(QWidget * w, const XEvent * xe, bool passive)
992 {
993     DEBUG("xdndHandlePosition");
994     while (XCheckIfEvent(X11->display, (XEvent *)xe, xdnd_position_scanner, 0))
995         ;
996 
997     handle_xdnd_position(w, xe, passive);
998 }
999 
1000 
handle_xdnd_status(QWidget *,const XEvent * xe,bool)1001 static void handle_xdnd_status(QWidget *, const XEvent * xe, bool)
1002 {
1003     const unsigned long *l = (const unsigned long *)xe->xclient.data.l;
1004     // ignore late status messages
1005     if (l[0] && l[0] != qt_xdnd_current_proxy_target)
1006         return;
1007     Qt::DropAction newAction = (l[1] & 0x1) ? xdndaction_to_qtaction(l[4]) : Qt::IgnoreAction;
1008 
1009     if ((int)(l[1] & 2) == 0) {
1010         QPoint p((l[2] & 0xffff0000) >> 16, l[2] & 0x0000ffff);
1011         QSize s((l[3] & 0xffff0000) >> 16, l[3] & 0x0000ffff);
1012         qt_xdnd_source_sameanswer = QRect(p, s);
1013     } else {
1014         qt_xdnd_source_sameanswer = QRect();
1015     }
1016     QDragManager *manager = QDragManager::self();
1017     manager->willDrop = (l[1] & 0x1);
1018     if (global_accepted_action != newAction)
1019         manager->emitActionChanged(newAction);
1020     global_accepted_action = newAction;
1021     manager->updateCursor();
1022     waiting_for_status = false;
1023 }
1024 
xdnd_status_scanner(Display *,XEvent * event,XPointer)1025 static Bool xdnd_status_scanner(Display *, XEvent *event, XPointer)
1026 {
1027     if (event->type != ClientMessage)
1028         return false;
1029     XClientMessageEvent *ev = &event->xclient;
1030 
1031     if (ev->message_type == ATOM(XdndStatus))
1032         return true;
1033 
1034     return false;
1035 }
1036 
xdndHandleStatus(QWidget * w,const XEvent * xe,bool passive)1037 void QX11Data::xdndHandleStatus(QWidget * w, const XEvent * xe, bool passive)
1038 {
1039     DEBUG("xdndHandleStatus");
1040     while (XCheckIfEvent(X11->display, (XEvent *)xe, xdnd_status_scanner, 0))
1041         ;
1042 
1043     handle_xdnd_status(w, xe, passive);
1044     DEBUG("xdndHandleStatus end");
1045 }
1046 
xdndHandleLeave(QWidget * w,const XEvent * xe,bool)1047 void QX11Data::xdndHandleLeave(QWidget *w, const XEvent * xe, bool /*passive*/)
1048 {
1049     DEBUG("xdnd leave");
1050     if (!qt_xdnd_current_widget ||
1051          w->window() != qt_xdnd_current_widget->window()) {
1052         return; // sanity
1053     }
1054 
1055     if (checkEmbedded(current_embedding_widget, xe)) {
1056         current_embedding_widget = 0;
1057         qt_xdnd_current_widget = 0;
1058         return;
1059     }
1060 
1061     const unsigned long *l = (const unsigned long *)xe->xclient.data.l;
1062 
1063     QDragLeaveEvent e;
1064     QApplication::sendEvent(qt_xdnd_current_widget, &e);
1065 
1066     if (l[0] != qt_xdnd_dragsource_xid) {
1067         // This often happens - leave other-process window quickly
1068         DEBUG("xdnd drag leave from unexpected source (%08lx not %08lx", l[0], qt_xdnd_dragsource_xid);
1069         qt_xdnd_current_widget = 0;
1070         return;
1071     }
1072 
1073     qt_xdnd_dragsource_xid = 0;
1074     qt_xdnd_types[0] = 0;
1075     qt_xdnd_current_widget = 0;
1076 }
1077 
1078 
qt_xdnd_send_leave()1079 void qt_xdnd_send_leave()
1080 {
1081     if (!qt_xdnd_current_target)
1082         return;
1083 
1084     QDragManager *manager = QDragManager::self();
1085 
1086     XClientMessageEvent leave;
1087     leave.type = ClientMessage;
1088     leave.window = qt_xdnd_current_target;
1089     leave.format = 32;
1090     leave.message_type = ATOM(XdndLeave);
1091     leave.data.l[0] = manager->dragPrivate()->source->effectiveWinId();
1092     leave.data.l[1] = 0; // flags
1093     leave.data.l[2] = 0; // x, y
1094     leave.data.l[3] = 0; // w, h
1095     leave.data.l[4] = 0; // just null
1096 
1097     QWidget * w = QWidget::find(qt_xdnd_current_proxy_target);
1098 
1099     if (w && (w->windowType() == Qt::Desktop) && !w->acceptDrops())
1100         w = 0;
1101 
1102     if (w)
1103         X11->xdndHandleLeave(w, (const XEvent *)&leave, false);
1104     else
1105         XSendEvent(X11->display, qt_xdnd_current_proxy_target, False,
1106                     NoEventMask, (XEvent*)&leave);
1107 
1108     // reset the drag manager state
1109     manager->willDrop = false;
1110     if (global_accepted_action != Qt::IgnoreAction)
1111         manager->emitActionChanged(Qt::IgnoreAction);
1112     global_accepted_action = Qt::IgnoreAction;
1113     manager->updateCursor();
1114     qt_xdnd_current_target = 0;
1115     qt_xdnd_current_proxy_target = 0;
1116     qt_xdnd_source_current_time = 0;
1117     waiting_for_status = false;
1118 }
1119 
xdndHandleDrop(QWidget *,const XEvent * xe,bool passive)1120 void QX11Data::xdndHandleDrop(QWidget *, const XEvent * xe, bool passive)
1121 {
1122     DEBUG("xdndHandleDrop");
1123     if (!qt_xdnd_current_widget) {
1124         qt_xdnd_dragsource_xid = 0;
1125         return; // sanity
1126     }
1127 
1128     if (!passive && checkEmbedded(qt_xdnd_current_widget, xe)){
1129         current_embedding_widget = 0;
1130         qt_xdnd_dragsource_xid = 0;
1131         qt_xdnd_current_widget = 0;
1132         return;
1133     }
1134     const unsigned long *l = (const unsigned long *)xe->xclient.data.l;
1135 
1136     QDragManager *manager = QDragManager::self();
1137     DEBUG("xdnd drop");
1138 
1139     if (l[0] != qt_xdnd_dragsource_xid) {
1140         DEBUG("xdnd drop from unexpected source (%08lx not %08lx", l[0], qt_xdnd_dragsource_xid);
1141         return;
1142     }
1143 
1144     // update the "user time" from the timestamp in the event.
1145     if (l[2] != 0) {
1146         // Some X server/client combination swallow the first 32 bit and
1147         // interpret a set bit 31 as negative sign.
1148         qt_xdnd_target_current_time = X11->userTime =
1149             ((sizeof(Time) == 8 && xe->xclient.data.l[2] < 0)
1150              ? uint(l[2])
1151              :  l[2]);
1152     }
1153 
1154     if (!passive) {
1155         // this could be a same-application drop, just proxied due to
1156         // some XEMBEDding, so try to find the real QMimeData used
1157         // based on the timestamp for this drop.
1158         QMimeData *dropData = 0;
1159         const int at = findXdndDropTransactionByTime(qt_xdnd_target_current_time);
1160         if (at != -1) {
1161             dropData = QDragManager::dragPrivate(X11->dndDropTransactions.at(at).object)->data;
1162             // Can't use the source QMimeData if we need the image conversion code from xdndObtainData
1163             if (dropData && dropData->hasImage())
1164                 dropData = 0;
1165         }
1166         // if we can't find it, then use the data in the drag manager
1167         if (!dropData) {
1168             if (manager->object && !manager->dragPrivate()->data->hasImage())
1169                 dropData = manager->dragPrivate()->data;
1170             else
1171                 dropData = manager->dropData;
1172         }
1173 
1174         // Drop coming from another app? Update keyboard modifiers.
1175         if (!qt_xdnd_dragging) {
1176             QApplicationPrivate::modifier_buttons = QApplication::queryKeyboardModifiers();
1177         }
1178 
1179         QDropEvent de(qt_xdnd_current_position, possible_actions, dropData,
1180                       QApplication::mouseButtons(), QApplication::keyboardModifiers());
1181         QApplication::sendEvent(qt_xdnd_current_widget, &de);
1182         if (!de.isAccepted()) {
1183             // Ignore a failed drag
1184             global_accepted_action = Qt::IgnoreAction;
1185         } else {
1186             global_accepted_action = de.dropAction();
1187         }
1188         XClientMessageEvent finished;
1189         finished.type = ClientMessage;
1190         finished.window = qt_xdnd_dragsource_xid;
1191         finished.format = 32;
1192         finished.message_type = ATOM(XdndFinished);
1193         DNDDEBUG << "xdndHandleDrop"
1194              << "qt_xdnd_current_widget" << qt_xdnd_current_widget
1195              << (qt_xdnd_current_widget ? qt_xdnd_current_widget->effectiveWinId() : 0)
1196              << "t_xdnd_current_widget->window()"
1197              << (qt_xdnd_current_widget ? qt_xdnd_current_widget->window() : 0)
1198              << (qt_xdnd_current_widget ? qt_xdnd_current_widget->window()->internalWinId() : 0);
1199         finished.data.l[0] = qt_xdnd_current_widget?qt_xdnd_current_widget->window()->internalWinId():0;
1200         finished.data.l[1] = de.isAccepted() ? 1 : 0; // flags
1201         finished.data.l[2] = qtaction_to_xdndaction(global_accepted_action);
1202         XSendEvent(X11->display, qt_xdnd_dragsource_xid, False,
1203                     NoEventMask, (XEvent*)&finished);
1204     } else {
1205         QDragLeaveEvent e;
1206         QApplication::sendEvent(qt_xdnd_current_widget, &e);
1207     }
1208     qt_xdnd_dragsource_xid = 0;
1209     qt_xdnd_current_widget = 0;
1210     waiting_for_status = false;
1211 
1212     // reset
1213     qt_xdnd_target_current_time = CurrentTime;
1214 }
1215 
1216 
xdndHandleFinished(QWidget *,const XEvent * xe,bool passive)1217 void QX11Data::xdndHandleFinished(QWidget *, const XEvent * xe, bool passive)
1218 {
1219     DEBUG("xdndHandleFinished");
1220     const unsigned long *l = (const unsigned long *)xe->xclient.data.l;
1221 
1222     DNDDEBUG << "xdndHandleFinished, l[0]" << l[0]
1223              << "qt_xdnd_current_target" << qt_xdnd_current_target
1224              << "qt_xdnd_current_proxy_targe" << qt_xdnd_current_proxy_target;
1225 
1226     if (l[0]) {
1227         int at = findXdndDropTransactionByWindow(l[0]);
1228         if (at != -1) {
1229             restartXdndDropExpiryTimer();
1230 
1231             QXdndDropTransaction t = X11->dndDropTransactions.takeAt(at);
1232             QDragManager *manager = QDragManager::self();
1233 
1234             Window target = qt_xdnd_current_target;
1235             Window proxy_target = qt_xdnd_current_proxy_target;
1236             QWidget *embedding_widget = current_embedding_widget;
1237             QDrag *currentObject = manager->object;
1238 
1239             qt_xdnd_current_target = t.target;
1240             qt_xdnd_current_proxy_target = t.proxy_target;
1241             current_embedding_widget = t.embedding_widget;
1242             manager->object = t.object;
1243 
1244             if (!passive)
1245                 (void) checkEmbedded(qt_xdnd_current_widget, xe);
1246 
1247             current_embedding_widget = 0;
1248             qt_xdnd_current_target = 0;
1249             qt_xdnd_current_proxy_target = 0;
1250 
1251             if (t.object)
1252                 t.object->deleteLater();
1253 
1254             qt_xdnd_current_target = target;
1255             qt_xdnd_current_proxy_target = proxy_target;
1256             current_embedding_widget = embedding_widget;
1257             manager->object = currentObject;
1258         }
1259     }
1260     waiting_for_status = false;
1261 }
1262 
1263 
timerEvent(QTimerEvent * e)1264 void QDragManager::timerEvent(QTimerEvent* e)
1265 {
1266     if (e->timerId() == heartbeat && qt_xdnd_source_sameanswer.isNull()) {
1267         move(QCursor::pos());
1268     } else if (e->timerId() == transaction_expiry_timer) {
1269         for (int i = 0; i < X11->dndDropTransactions.count(); ++i) {
1270             const QXdndDropTransaction &t = X11->dndDropTransactions.at(i);
1271             if (t.targetWidget) {
1272                 // dnd within the same process, don't delete these
1273                 continue;
1274             }
1275             t.object->deleteLater();
1276             X11->dndDropTransactions.removeAt(i--);
1277         }
1278 
1279         killTimer(transaction_expiry_timer);
1280         transaction_expiry_timer = -1;
1281     }
1282 }
1283 
eventFilter(QObject * o,QEvent * e)1284 bool QDragManager::eventFilter(QObject * o, QEvent * e)
1285 {
1286     if (beingCancelled) {
1287         if (e->type() == QEvent::KeyRelease && ((QKeyEvent*)e)->key() == Qt::Key_Escape) {
1288             qApp->removeEventFilter(this);
1289             Q_ASSERT(object == 0);
1290             beingCancelled = false;
1291             eventLoop->exit();
1292             return true; // block the key release
1293         }
1294         return false;
1295     }
1296 
1297     Q_ASSERT(object != 0);
1298 
1299     if (!o->isWidgetType())
1300         return false;
1301 
1302     if (e->type() == QEvent::MouseMove) {
1303         QMouseEvent* me = (QMouseEvent *)e;
1304         move(me->globalPos());
1305         return true;
1306     } else if (e->type() == QEvent::MouseButtonRelease) {
1307         DEBUG("pre drop");
1308         qApp->removeEventFilter(this);
1309         if (willDrop)
1310             drop();
1311         else
1312             cancel();
1313         DEBUG("drop, resetting object");
1314         beingCancelled = false;
1315         eventLoop->exit();
1316         return true;
1317     }
1318 
1319     if (e->type() == QEvent::ShortcutOverride) {
1320         // prevent accelerators from firing while dragging
1321         e->accept();
1322         return true;
1323     }
1324 
1325     if (e->type() == QEvent::KeyPress || e->type() == QEvent::KeyRelease) {
1326         QKeyEvent *ke = ((QKeyEvent*)e);
1327         if (ke->key() == Qt::Key_Escape && e->type() == QEvent::KeyPress) {
1328             cancel();
1329             qApp->removeEventFilter(this);
1330             beingCancelled = false;
1331             eventLoop->exit();
1332         } else {
1333             qt_xdnd_source_sameanswer = QRect(); // force move
1334             move(QCursor::pos());
1335         }
1336         return true; // Eat all key events
1337     }
1338 
1339     // ### We bind modality to widgets, so we have to do this
1340     // ###  "manually".
1341     // DnD is modal - eat all other interactive events
1342     switch (e->type()) {
1343       case QEvent::MouseButtonPress:
1344       case QEvent::MouseButtonRelease:
1345       case QEvent::MouseButtonDblClick:
1346       case QEvent::MouseMove:
1347       case QEvent::KeyPress:
1348       case QEvent::KeyRelease:
1349       case QEvent::Wheel:
1350       case QEvent::ShortcutOverride:
1351 #ifdef QT3_SUPPORT
1352       case QEvent::Accel:
1353       case QEvent::AccelAvailable:
1354 #endif
1355         return true;
1356       default:
1357         return false;
1358     }
1359 }
1360 
updateCursor()1361 void QDragManager::updateCursor()
1362 {
1363     if (!noDropCursor) {
1364 #ifndef QT_NO_CURSOR
1365         noDropCursor = new QCursor(Qt::ForbiddenCursor);
1366         moveCursor = new QCursor(Qt::DragMoveCursor);
1367         copyCursor = new QCursor(Qt::DragCopyCursor);
1368         linkCursor = new QCursor(Qt::DragLinkCursor);
1369 #endif
1370     }
1371 
1372     QCursor *c;
1373     if (willDrop) {
1374         if (global_accepted_action == Qt::CopyAction) {
1375             c = copyCursor;
1376         } else if (global_accepted_action == Qt::LinkAction) {
1377             c = linkCursor;
1378         } else {
1379             c = moveCursor;
1380         }
1381         if (xdnd_data.deco) {
1382             xdnd_data.deco->show();
1383             xdnd_data.deco->raise();
1384         }
1385     } else {
1386         c = noDropCursor;
1387         //if (qt_xdnd_deco)
1388         //    qt_xdnd_deco->hide();
1389     }
1390 #ifndef QT_NO_CURSOR
1391     if (c)
1392         qApp->changeOverrideCursor(*c);
1393 #endif
1394 }
1395 
1396 
cancel(bool deleteSource)1397 void QDragManager::cancel(bool deleteSource)
1398 {
1399     DEBUG("QDragManager::cancel");
1400     Q_ASSERT(heartbeat != -1);
1401     killTimer(heartbeat);
1402     heartbeat = -1;
1403     beingCancelled = true;
1404     qt_xdnd_dragging = false;
1405 
1406     if (qt_xdnd_current_target)
1407         qt_xdnd_send_leave();
1408 
1409 #ifndef QT_NO_CURSOR
1410     if (restoreCursor) {
1411         QApplication::restoreOverrideCursor();
1412         restoreCursor = false;
1413     }
1414 #endif
1415 
1416     if (deleteSource && object)
1417         object->deleteLater();
1418     object = 0;
1419     qDeleteInEventHandler(xdnd_data.deco);
1420     xdnd_data.deco = 0;
1421 
1422     global_accepted_action = Qt::IgnoreAction;
1423 }
1424 
1425 #ifndef QT_NO_SHAPE
1426 static
windowInteractsWithPosition(const QPoint & pos,Window w,int shapeType)1427 bool windowInteractsWithPosition(const QPoint & pos, Window w, int shapeType)
1428 {
1429     int nrectanglesRet, dummyOrdering;
1430     XRectangle *rectangles = XShapeGetRectangles(QX11Info::display(), w, shapeType, &nrectanglesRet, &dummyOrdering);
1431     bool interacts = false;
1432     if (rectangles) {
1433         for (int i = 0; !interacts && i < nrectanglesRet; ++i)
1434             interacts = QRect(rectangles[i].x, rectangles[i].y, rectangles[i].width, rectangles[i].height).contains(pos);
1435         XFree(rectangles);
1436     }
1437     return interacts;
1438 }
1439 #endif
1440 
1441 static
findRealWindow(const QPoint & pos,Window w,int md,bool ignoreNonXdndAwareWindows)1442 Window findRealWindow(const QPoint & pos, Window w, int md, bool ignoreNonXdndAwareWindows)
1443 {
1444     if (xdnd_data.deco && w == xdnd_data.deco->effectiveWinId())
1445         return 0;
1446 
1447     if (md) {
1448         X11->ignoreBadwindow();
1449         XWindowAttributes attr;
1450         XGetWindowAttributes(X11->display, w, &attr);
1451         if (X11->badwindow())
1452             return 0;
1453 
1454         if (attr.map_state == IsViewable
1455             && QRect(attr.x,attr.y,attr.width,attr.height).contains(pos)) {
1456             bool windowContainsMouse = !ignoreNonXdndAwareWindows;
1457             {
1458                 Atom   type = XNone;
1459                 int f;
1460                 unsigned long n, a;
1461                 unsigned char *data;
1462 
1463                 XGetWindowProperty(X11->display, w, ATOM(XdndAware), 0, 0, False,
1464                                    AnyPropertyType, &type, &f,&n,&a,&data);
1465                 if (data) XFree(data);
1466                 if (type) {
1467 #ifdef QT_NO_SHAPE
1468                     return w;
1469 #else // !QT_NO_SHAPE
1470                     const QPoint relPos = pos - QPoint(attr.x,attr.y);
1471                     // When ShapeInput and ShapeBounding are not set they return a single rectangle with the geometry of the window, this is why we
1472                     // need an && here so that in the case one is set and the other is not we still get the correct result.
1473 #if defined(ShapeInput) && defined(ShapeBounding)
1474                     windowContainsMouse = windowInteractsWithPosition(relPos, w, ShapeInput) && windowInteractsWithPosition(relPos, w, ShapeBounding);
1475 #elif defined(ShapeBounding)
1476                     windowContainsMouse = windowInteractsWithPosition(relPos, w, ShapeBounding);
1477 #else
1478                     windowContainsMouse = true;
1479 #endif
1480                     if (windowContainsMouse)
1481                         return w;
1482 #endif // QT_NO_SHAPE
1483                 }
1484             }
1485 
1486             Window r, p;
1487             Window* c;
1488             uint nc;
1489             if (XQueryTree(X11->display, w, &r, &p, &c, &nc)) {
1490                 r=0;
1491                 for (uint i=nc; !r && i--;) {
1492                     r = findRealWindow(pos-QPoint(attr.x,attr.y),
1493                                         c[i], md-1, ignoreNonXdndAwareWindows);
1494                 }
1495                 XFree(c);
1496                 if (r)
1497                     return r;
1498 
1499                 // We didn't find a client window!  Just use the
1500                 // innermost window.
1501             }
1502 
1503             // No children!
1504             if (!windowContainsMouse)
1505                 return 0;
1506             else
1507                 return w;
1508         }
1509     }
1510     return 0;
1511 }
1512 
move(const QPoint & globalPos)1513 void QDragManager::move(const QPoint & globalPos)
1514 {
1515 #ifdef QT_NO_CURSOR
1516     Q_UNUSED(globalPos);
1517     return;
1518 #else
1519     DEBUG() << "QDragManager::move enter";
1520     if (!object) {
1521         // perhaps the target crashed?
1522         return;
1523     }
1524 
1525     int screen = QCursor::x11Screen();
1526     if ((qt_xdnd_current_screen == -1 && screen != X11->defaultScreen) || (screen != qt_xdnd_current_screen)) {
1527         // recreate the pixmap on the new screen...
1528         delete xdnd_data.deco;
1529         QWidget* parent = object->source()->window()->x11Info().screen() == screen
1530             ? object->source()->window() : QApplication::desktop()->screen(screen);
1531         xdnd_data.deco = new QShapedPixmapWidget(parent);
1532         if (!QWidget::mouseGrabber()) {
1533             updatePixmap();
1534             xdnd_data.deco->grabMouse();
1535         }
1536     }
1537     xdnd_data.deco->move(QCursor::pos() - xdnd_data.deco->pm_hot);
1538 
1539     if (qt_xdnd_source_sameanswer.contains(globalPos) && qt_xdnd_source_sameanswer.isValid())
1540         return;
1541 
1542     qt_xdnd_current_screen = screen;
1543     Window rootwin = QX11Info::appRootWindow(qt_xdnd_current_screen);
1544     Window target = 0;
1545     int lx = 0, ly = 0;
1546     if (!XTranslateCoordinates(X11->display, rootwin, rootwin, globalPos.x(), globalPos.y(), &lx, &ly, &target))
1547         // some weird error...
1548         return;
1549 
1550     if (target == rootwin) {
1551         // Ok.
1552     } else if (target) {
1553         //me
1554         Window src = rootwin;
1555         while (target != 0) {
1556             DNDDEBUG << "checking target for XdndAware" << QWidget::find(target) << target;
1557             int lx2, ly2;
1558             Window t;
1559             // translate coordinates
1560             if (!XTranslateCoordinates(X11->display, src, target, lx, ly, &lx2, &ly2, &t)) {
1561                 target = 0;
1562                 break;
1563             }
1564             lx = lx2;
1565             ly = ly2;
1566             src = target;
1567 
1568 	    // check if it has XdndAware
1569 	    Atom type = 0;
1570 	    int f;
1571 	    unsigned long n, a;
1572 	    unsigned char *data = 0;
1573 	    XGetWindowProperty(X11->display, target, ATOM(XdndAware), 0, 0, False,
1574                                AnyPropertyType, &type, &f,&n,&a,&data);
1575 	    if (data)
1576                 XFree(data);
1577 	    if (type) {
1578                 DNDDEBUG << "Found XdndAware on " << QWidget::find(target) << target;
1579                 break;
1580             }
1581 
1582             // find child at the coordinates
1583             if (!XTranslateCoordinates(X11->display, src, src, lx, ly, &lx2, &ly2, &target)) {
1584                 target = 0;
1585                 break;
1586             }
1587         }
1588         if (xdnd_data.deco && (!target || target == xdnd_data.deco->effectiveWinId())) {
1589             DNDDEBUG << "need to find real window";
1590             target = findRealWindow(globalPos, rootwin, 6, true);
1591             if (target == 0)
1592                 target = findRealWindow(globalPos, rootwin, 6, false);
1593             DNDDEBUG << "real window found" << QWidget::find(target) << target;
1594         }
1595     }
1596 
1597     QWidget* w;
1598     if (target) {
1599         w = QWidget::find((WId)target);
1600         if (w && (w->windowType() == Qt::Desktop) && !w->acceptDrops())
1601             w = 0;
1602     } else {
1603         w = 0;
1604         target = rootwin;
1605     }
1606 
1607     DNDDEBUG << "and the final target is " << QWidget::find(target) << target;
1608     DNDDEBUG << "the widget w is" << w;
1609 
1610     WId proxy_target = xdndProxy(target);
1611     if (!proxy_target)
1612         proxy_target = target;
1613     int target_version = 1;
1614 
1615     if (proxy_target) {
1616         Atom   type = XNone;
1617         int r, f;
1618         unsigned long n, a;
1619         unsigned char *retval;
1620         X11->ignoreBadwindow();
1621         r = XGetWindowProperty(X11->display, proxy_target, ATOM(XdndAware), 0,
1622                                1, False, AnyPropertyType, &type, &f,&n,&a,&retval);
1623         int *tv = (int *)retval;
1624         if (r != Success || X11->badwindow()) {
1625             target = 0;
1626         } else {
1627             target_version = qMin(xdnd_version,tv ? *tv : 1);
1628             if (tv)
1629                 XFree(tv);
1630 //             if (!(!X11->badwindow() && type))
1631 //                 target = 0;
1632         }
1633     }
1634 
1635     if (target != qt_xdnd_current_target) {
1636         if (qt_xdnd_current_target)
1637             qt_xdnd_send_leave();
1638 
1639         qt_xdnd_current_target = target;
1640         qt_xdnd_current_proxy_target = proxy_target;
1641         if (target) {
1642             QVector<Atom> types;
1643             int flags = target_version << 24;
1644             QStringList fmts = QInternalMimeData::formatsHelper(dragPrivate()->data);
1645             for (int i = 0; i < fmts.size(); ++i) {
1646                 QList<Atom> atoms = X11->xdndMimeAtomsForFormat(fmts.at(i));
1647                 for (int j = 0; j < atoms.size(); ++j) {
1648                     if (!types.contains(atoms.at(j)))
1649                         types.append(atoms.at(j));
1650                 }
1651             }
1652             if (types.size() > 3) {
1653                 XChangeProperty(X11->display,
1654                                 dragPrivate()->source->effectiveWinId(), ATOM(XdndTypelist),
1655                                 XA_ATOM, 32, PropModeReplace,
1656                                 (unsigned char *)types.data(),
1657                                 types.size());
1658                 flags |= 0x0001;
1659             }
1660             XClientMessageEvent enter;
1661             enter.type = ClientMessage;
1662             enter.window = target;
1663             enter.format = 32;
1664             enter.message_type = ATOM(XdndEnter);
1665             enter.data.l[0] = dragPrivate()->source->effectiveWinId();
1666             enter.data.l[1] = flags;
1667             enter.data.l[2] = types.size()>0 ? types.at(0) : 0;
1668             enter.data.l[3] = types.size()>1 ? types.at(1) : 0;
1669             enter.data.l[4] = types.size()>2 ? types.at(2) : 0;
1670             // provisionally set the rectangle to 5x5 pixels...
1671             qt_xdnd_source_sameanswer = QRect(globalPos.x() - 2,
1672                                               globalPos.y() -2 , 5, 5);
1673 
1674             DEBUG("sending Xdnd enter");
1675             if (w)
1676                 X11->xdndHandleEnter(w, (const XEvent *)&enter, false);
1677             else if (target)
1678                 XSendEvent(X11->display, proxy_target, False, NoEventMask, (XEvent*)&enter);
1679             waiting_for_status = false;
1680         }
1681     }
1682     if (waiting_for_status)
1683         return;
1684 
1685     if (target) {
1686         waiting_for_status = true;
1687 
1688         XClientMessageEvent move;
1689         move.type = ClientMessage;
1690         move.window = target;
1691         move.format = 32;
1692         move.message_type = ATOM(XdndPosition);
1693         move.window = target;
1694         move.data.l[0] = dragPrivate()->source->effectiveWinId();
1695         move.data.l[1] = 0; // flags
1696         move.data.l[2] = (globalPos.x() << 16) + globalPos.y();
1697         move.data.l[3] = X11->time;
1698         move.data.l[4] = qtaction_to_xdndaction(defaultAction(dragPrivate()->possible_actions, QApplication::keyboardModifiers()));
1699         DEBUG("sending Xdnd position");
1700 
1701         qt_xdnd_source_current_time = X11->time;
1702 
1703         if (w)
1704             handle_xdnd_position(w, (const XEvent *)&move, false);
1705         else
1706             XSendEvent(X11->display, proxy_target, False, NoEventMask,
1707                        (XEvent*)&move);
1708     } else {
1709         if (willDrop) {
1710             willDrop = false;
1711             updateCursor();
1712         }
1713     }
1714     DEBUG() << "QDragManager::move leave";
1715 #endif
1716 }
1717 
1718 
drop()1719 void QDragManager::drop()
1720 {
1721     Q_ASSERT(heartbeat != -1);
1722     killTimer(heartbeat);
1723     heartbeat = -1;
1724     qt_xdnd_dragging = false;
1725 
1726     if (!qt_xdnd_current_target)
1727         return;
1728 
1729     qDeleteInEventHandler(xdnd_data.deco);
1730     xdnd_data.deco = 0;
1731 
1732     XClientMessageEvent drop;
1733     drop.type = ClientMessage;
1734     drop.window = qt_xdnd_current_target;
1735     drop.format = 32;
1736     drop.message_type = ATOM(XdndDrop);
1737     drop.data.l[0] = dragPrivate()->source->effectiveWinId();
1738     drop.data.l[1] = 0; // flags
1739     drop.data.l[2] = X11->time;
1740 
1741     drop.data.l[3] = 0;
1742     drop.data.l[4] = 0;
1743 
1744     QWidget * w = QWidget::find(qt_xdnd_current_proxy_target);
1745 
1746     if (w && (w->windowType() == Qt::Desktop) && !w->acceptDrops())
1747         w = 0;
1748 
1749     QXdndDropTransaction t = {
1750         X11->time,
1751         qt_xdnd_current_target,
1752         qt_xdnd_current_proxy_target,
1753         w,
1754         current_embedding_widget,
1755         object
1756     };
1757     X11->dndDropTransactions.append(t);
1758     restartXdndDropExpiryTimer();
1759 
1760     if (w)
1761         X11->xdndHandleDrop(w, (const XEvent *)&drop, false);
1762     else
1763         XSendEvent(X11->display, qt_xdnd_current_proxy_target, False,
1764                    NoEventMask, (XEvent*)&drop);
1765 
1766     qt_xdnd_current_target = 0;
1767     qt_xdnd_current_proxy_target = 0;
1768     qt_xdnd_source_current_time = 0;
1769     current_embedding_widget = 0;
1770     object = 0;
1771 
1772 #ifndef QT_NO_CURSOR
1773     if (restoreCursor) {
1774         QApplication::restoreOverrideCursor();
1775         restoreCursor = false;
1776     }
1777 #endif
1778 }
1779 
1780 
1781 
xdndHandleBadwindow()1782 bool QX11Data::xdndHandleBadwindow()
1783 {
1784     if (qt_xdnd_current_target) {
1785         QDragManager *manager = QDragManager::self();
1786         if (manager->object) {
1787             qt_xdnd_current_target = 0;
1788             qt_xdnd_current_proxy_target = 0;
1789             manager->object->deleteLater();
1790             manager->object = 0;
1791             xdnd_data.deco->deleteLater(); //delay freeing to avoid crash QTBUG-19363
1792             xdnd_data.deco = 0;
1793             return true;
1794         }
1795     }
1796     if (qt_xdnd_dragsource_xid) {
1797         qt_xdnd_dragsource_xid = 0;
1798         if (qt_xdnd_current_widget) {
1799             QApplication::postEvent(qt_xdnd_current_widget, new QDragLeaveEvent);
1800             qt_xdnd_current_widget = 0;
1801         }
1802         return true;
1803     }
1804     return false;
1805 }
1806 
xdndHandleSelectionRequest(const XSelectionRequestEvent * req)1807 void QX11Data::xdndHandleSelectionRequest(const XSelectionRequestEvent * req)
1808 {
1809     if (!req)
1810         return;
1811     XEvent evt;
1812     evt.xselection.type = SelectionNotify;
1813     evt.xselection.display = req->display;
1814     evt.xselection.requestor = req->requestor;
1815     evt.xselection.selection = req->selection;
1816     evt.xselection.target = XNone;
1817     evt.xselection.property = XNone;
1818     evt.xselection.time = req->time;
1819 
1820     QDragManager *manager = QDragManager::self();
1821     QDrag *currentObject = manager->object;
1822 
1823     // which transaction do we use? (note: -2 means use current manager->object)
1824     int at = -1;
1825 
1826     // figure out which data the requestor is really interested in
1827     if (manager->object && req->time == qt_xdnd_source_current_time) {
1828         // requestor wants the current drag data
1829         at = -2;
1830     } else {
1831         // if someone has requested data in response to XdndDrop, find the corresponding transaction. the
1832         // spec says to call XConvertSelection() using the timestamp from the XdndDrop
1833         at = findXdndDropTransactionByTime(req->time);
1834         if (at == -1) {
1835             // no dice, perhaps the client was nice enough to use the same window id in XConvertSelection()
1836             // that we sent the XdndDrop event to.
1837             at = findXdndDropTransactionByWindow(req->requestor);
1838         }
1839         if (at == -1 && req->time == CurrentTime) {
1840             // previous Qt versions always requested the data on a child of the target window
1841             // using CurrentTime... but it could be asking for either drop data or the current drag's data
1842             Window target = findXdndAwareParent(req->requestor);
1843             if (target) {
1844                 if (qt_xdnd_current_target && qt_xdnd_current_target == target)
1845                     at = -2;
1846                 else
1847                     at = findXdndDropTransactionByWindow(target);
1848             }
1849         }
1850     }
1851     if (at >= 0) {
1852         restartXdndDropExpiryTimer();
1853 
1854         // use the drag object from an XdndDrop tansaction
1855         manager->object = X11->dndDropTransactions.at(at).object;
1856     } else if (at != -2) {
1857         // no transaction found, we'll have to reject the request
1858         manager->object = 0;
1859     }
1860     if (manager->object) {
1861         Atom atomFormat = req->target;
1862         int dataFormat = 0;
1863         QByteArray data;
1864         if (X11->xdndMimeDataForAtom(req->target, manager->dragPrivate()->data,
1865                                      &data, &atomFormat, &dataFormat)) {
1866             int dataSize = data.size() / (dataFormat / 8);
1867             XChangeProperty (X11->display, req->requestor, req->property,
1868                              atomFormat, dataFormat, PropModeReplace,
1869                              (unsigned char *)data.data(), dataSize);
1870             evt.xselection.property = req->property;
1871             evt.xselection.target = atomFormat;
1872         }
1873     }
1874 
1875     // reset manager->object in case we modified it above
1876     manager->object = currentObject;
1877 
1878     // ### this can die if req->requestor crashes at the wrong
1879     // ### moment
1880     XSendEvent(X11->display, req->requestor, False, 0, &evt);
1881 }
1882 
xdndObtainData(const char * format,QVariant::Type requestedType)1883 static QVariant xdndObtainData(const char *format, QVariant::Type requestedType)
1884 {
1885     QByteArray result;
1886 
1887     QWidget* w;
1888     QDragManager *manager = QDragManager::self();
1889     if (qt_xdnd_dragsource_xid && manager->object &&
1890          (w=QWidget::find(qt_xdnd_dragsource_xid))
1891          && (!(w->windowType() == Qt::Desktop) || w->acceptDrops()))
1892     {
1893         QDragPrivate * o = QDragManager::self()->dragPrivate();
1894         const QString mimeType = QString::fromLatin1(format);
1895         if (o->data->hasFormat(mimeType)) {
1896             result = o->data->data(mimeType);
1897         } else if (mimeType.startsWith(QLatin1String("image/")) && o->data->hasImage()) {
1898             // ### duplicated from QInternalMimeData::renderDataHelper
1899             QImage image = qvariant_cast<QImage>(o->data->imageData());
1900             QBuffer buf(&result);
1901             buf.open(QBuffer::WriteOnly);
1902             image.save(&buf, mimeType.mid(mimeType.indexOf(QLatin1Char('/')) + 1).toLatin1().toUpper());
1903         }
1904         return result;
1905     }
1906 
1907     QList<Atom> atoms;
1908     int i = 0;
1909     while ((qt_xdnd_types[i])) {
1910         atoms.append(qt_xdnd_types[i]);
1911         ++i;
1912     }
1913     QByteArray encoding;
1914     Atom a = X11->xdndMimeAtomForFormat(QLatin1String(format), requestedType, atoms, &encoding);
1915     if (!a)
1916         return result;
1917 
1918     if (XGetSelectionOwner(X11->display, ATOM(XdndSelection)) == XNone)
1919         return result; // should never happen?
1920 
1921     QWidget* tw = qt_xdnd_current_widget;
1922     if (!qt_xdnd_current_widget || (qt_xdnd_current_widget->windowType() == Qt::Desktop))
1923         tw = new QWidget;
1924 
1925     XConvertSelection(X11->display, ATOM(XdndSelection), a, ATOM(XdndSelection), tw->effectiveWinId(),
1926                       qt_xdnd_target_current_time);
1927     XFlush(X11->display);
1928 
1929     XEvent xevent;
1930     bool got=X11->clipboardWaitForEvent(tw->effectiveWinId(), SelectionNotify, &xevent, 5000);
1931     if (got) {
1932         Atom type;
1933 
1934         if (X11->clipboardReadProperty(tw->effectiveWinId(), ATOM(XdndSelection), true, &result, 0, &type, 0)) {
1935             if (type == ATOM(INCR)) {
1936                 int nbytes = result.size() >= 4 ? *((int*)result.data()) : 0;
1937                 result = X11->clipboardReadIncrementalProperty(tw->effectiveWinId(), ATOM(XdndSelection), nbytes, false);
1938             } else if (type != a && type != XNone) {
1939                 DEBUG("Qt clipboard: unknown atom %ld", type);
1940             }
1941         }
1942     }
1943     if (!qt_xdnd_current_widget || (qt_xdnd_current_widget->windowType() == Qt::Desktop))
1944         delete tw;
1945 
1946     return X11->xdndMimeConvertToFormat(a, result, QLatin1String(format), requestedType, encoding);
1947 }
1948 
1949 
1950 /*
1951   Enable drag and drop for widget w by installing the proper
1952   properties on w's toplevel widget.
1953 */
dndEnable(QWidget * w,bool on)1954 bool QX11Data::dndEnable(QWidget* w, bool on)
1955 {
1956     w = w->window();
1957 
1958     if (bool(((QExtraWidget*)w)->topData()->dnd) == on)
1959         return true; // been there, done that
1960     ((QExtraWidget*)w)->topData()->dnd = on ? 1 : 0;
1961 
1962     motifdndEnable(w, on);
1963     return xdndEnable(w, on);
1964 }
1965 
drag(QDrag * o)1966 Qt::DropAction QDragManager::drag(QDrag * o)
1967 {
1968     if (object == o || !o || !o->d_func()->source)
1969         return Qt::IgnoreAction;
1970 
1971     if (object) {
1972         cancel();
1973         qApp->removeEventFilter(this);
1974         beingCancelled = false;
1975     }
1976 
1977     if (object) {
1978         // the last drag and drop operation hasn't finished, so we are going to wait
1979         // for one second to see if it does... if the finish message comes after this,
1980         // then we could still have problems, but this is highly unlikely
1981         QApplication::flush();
1982 
1983         QElapsedTimer timer;
1984         timer.start();
1985         do {
1986             XEvent event;
1987             // Pass the event through the event dispatcher filter so that applications
1988             // which install an event filter on the dispatcher get to handle it first.
1989             if (XCheckTypedEvent(X11->display, ClientMessage, &event) &&
1990                 !QAbstractEventDispatcher::instance()->filterEvent(&event))
1991                 qApp->x11ProcessEvent(&event);
1992 
1993             // sleep 50 ms, so we don't use up CPU cycles all the time.
1994             struct timeval usleep_tv;
1995             usleep_tv.tv_sec = 0;
1996             usleep_tv.tv_usec = 50000;
1997             select(0, 0, 0, 0, &usleep_tv);
1998         } while (object && timer.hasExpired(1000));
1999     }
2000 
2001     object = o;
2002     object->d_func()->target = 0;
2003     xdnd_data.deco = new QShapedPixmapWidget(object->source()->window());
2004 
2005     willDrop = false;
2006 
2007     updatePixmap();
2008 
2009     qApp->installEventFilter(this);
2010     XSetSelectionOwner(X11->display, ATOM(XdndSelection), dragPrivate()->source->window()->internalWinId(), X11->time);
2011     global_accepted_action = Qt::CopyAction;
2012     qt_xdnd_source_sameanswer = QRect();
2013 #ifndef QT_NO_CURSOR
2014     // set the override cursor (must be done here, since it is updated
2015     // in the call to move() below)
2016     qApp->setOverrideCursor(Qt::ArrowCursor);
2017     restoreCursor = true;
2018 #endif
2019     move(QCursor::pos());
2020     heartbeat = startTimer(200);
2021 
2022     qt_xdnd_dragging = true;
2023 
2024     if (!QWidget::mouseGrabber())
2025         xdnd_data.deco->grabMouse();
2026 
2027     eventLoop = new QEventLoop;
2028     (void) eventLoop->exec();
2029     delete eventLoop;
2030     eventLoop = 0;
2031 
2032 #ifndef QT_NO_CURSOR
2033     if (restoreCursor) {
2034         qApp->restoreOverrideCursor();
2035         restoreCursor = false;
2036     }
2037 #endif
2038 
2039     // delete cursors as they may be different next drag.
2040     delete noDropCursor;
2041     noDropCursor = 0;
2042     delete copyCursor;
2043     copyCursor = 0;
2044     delete moveCursor;
2045     moveCursor = 0;
2046     delete linkCursor;
2047     linkCursor = 0;
2048 
2049     delete xdnd_data.deco;
2050     xdnd_data.deco = 0;
2051     if (heartbeat != -1)
2052         killTimer(heartbeat);
2053     heartbeat = -1;
2054     qt_xdnd_current_screen = -1;
2055     qt_xdnd_dragging = false;
2056 
2057     return global_accepted_action;
2058     // object persists until we get an xdnd_finish message
2059 }
2060 
updatePixmap()2061 void QDragManager::updatePixmap()
2062 {
2063     if (xdnd_data.deco) {
2064         QPixmap pm;
2065         QPoint pm_hot(default_pm_hotx,default_pm_hoty);
2066         if (object) {
2067             pm = dragPrivate()->pixmap;
2068             if (!pm.isNull())
2069                 pm_hot = dragPrivate()->hotspot;
2070         }
2071         if (pm.isNull()) {
2072             if (!defaultPm)
2073                 defaultPm = new QPixmap(default_pm);
2074             pm = *defaultPm;
2075         }
2076         xdnd_data.deco->pm_hot = pm_hot;
2077         xdnd_data.deco->setPixmap(pm);
2078         xdnd_data.deco->move(QCursor::pos()-pm_hot);
2079         xdnd_data.deco->show();
2080     }
2081 }
2082 
retrieveData_sys(const QString & mimetype,QVariant::Type requestedType) const2083 QVariant QDropData::retrieveData_sys(const QString &mimetype, QVariant::Type requestedType) const
2084 {
2085     QByteArray mime = mimetype.toLatin1();
2086     QVariant data = X11->motifdnd_active
2087                       ? X11->motifdndObtainData(mime)
2088                       : xdndObtainData(mime, requestedType);
2089     return data;
2090 }
2091 
hasFormat_sys(const QString & format) const2092 bool QDropData::hasFormat_sys(const QString &format) const
2093 {
2094     return formats().contains(format);
2095 }
2096 
formats_sys() const2097 QStringList QDropData::formats_sys() const
2098 {
2099     QStringList formats;
2100     if (X11->motifdnd_active) {
2101         int i = 0;
2102         QByteArray fmt;
2103         while (!(fmt = X11->motifdndFormat(i)).isEmpty()) {
2104             formats.append(QLatin1String(fmt));
2105             ++i;
2106         }
2107     } else {
2108         int i = 0;
2109         while ((qt_xdnd_types[i])) {
2110             QStringList formatsForAtom = X11->xdndMimeFormatsForAtom(qt_xdnd_types[i]);
2111             for (int j = 0; j < formatsForAtom.size(); ++j) {
2112                 if (!formats.contains(formatsForAtom.at(j)))
2113                     formats.append(formatsForAtom.at(j));
2114             }
2115             ++i;
2116         }
2117     }
2118     return formats;
2119 }
2120 
2121 QT_END_NAMESPACE
2122 
2123 #endif // QT_NO_DRAGANDDROP
2124