source: trunk/src/gui/kernel/qdnd_x11.cpp@ 603

Last change on this file since 603 was 561, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.1 sources.

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