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