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

Last change on this file since 324 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 65.0 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information ([email protected])
5**
6** This file is part of the QtGui module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** 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 are unsure which license is appropriate for your use, please
37** contact the sales department 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
510 DEBUG(" textprop type %lx\n"
511 " textprop name '%s'\n"
512 " format %d\n"
513 " %ld items\n"
514 " %d bytes\n",
515 textprop.encoding,
516 X11->xdndMimeAtomToString(textprop.encoding).toLatin1().data(),
517 textprop.format, textprop.nitems, data->size());
518
519 XFree(textprop.value);
520 }
521 }
522 } else if (atomName == QLatin1String("text/x-moz-url") &&
523 QInternalMimeData::hasFormatHelper(QLatin1String("text/uri-list"), mimeData)) {
524 QByteArray uri = QInternalMimeData::renderDataHelper(
525 QLatin1String("text/uri-list"), mimeData).split('\n').first();
526 QString mozUri = QString::fromLatin1(uri, uri.size());
527 mozUri += QLatin1Char('\n');
528 *data = QByteArray(reinterpret_cast<const char *>(mozUri.utf16()), mozUri.length() * 2);
529 ret = true;
530 } else if ((a == XA_PIXMAP || a == XA_BITMAP) && mimeData->hasImage()) {
531 QPixmap pm = qvariant_cast<QPixmap>(mimeData->imageData());
532 if (a == XA_BITMAP && pm.depth() != 1) {
533 QImage img = pm.toImage();
534 img = img.convertToFormat(QImage::Format_MonoLSB);
535 pm = QPixmap::fromImage(img);
536 }
537 QDragManager *dm = QDragManager::self();
538 if (dm) {
539 Pixmap handle = pm.handle();
540 *data = QByteArray((const char *) &handle, sizeof(Pixmap));
541 dm->xdndMimeTransferedPixmap[dm->xdndMimeTransferedPixmapIndex] = pm;
542 dm->xdndMimeTransferedPixmapIndex =
543 (dm->xdndMimeTransferedPixmapIndex + 1) % 2;
544 }
545 }
546 }
547 return data;
548}
549
550//$$$
551QList<Atom> QX11Data::xdndMimeAtomsForFormat(const QString &format)
552{
553 QList<Atom> atoms;
554 atoms.append(xdndMimeStringToAtom(format));
555
556 // special cases for strings
557 if (format == QLatin1String("text/plain")) {
558 atoms.append(ATOM(UTF8_STRING));
559 atoms.append(XA_STRING);
560 atoms.append(ATOM(TEXT));
561 atoms.append(ATOM(COMPOUND_TEXT));
562 }
563
564 // special cases for uris
565 if (format == QLatin1String("text/uri-list")) {
566 atoms.append(xdndMimeStringToAtom(QLatin1String("text/x-moz-url")));
567 }
568
569 //special cases for images
570 if (format == QLatin1String("image/ppm"))
571 atoms.append(XA_PIXMAP);
572 if (format == QLatin1String("image/pbm"))
573 atoms.append(XA_BITMAP);
574
575 return atoms;
576}
577
578//$$$
579QVariant QX11Data::xdndMimeConvertToFormat(Atom a, const QByteArray &data, const QString &format, QVariant::Type requestedType, const QByteArray &encoding)
580{
581 QString atomName = xdndMimeAtomToString(a);
582 if (atomName == format)
583 return data;
584
585 if (!encoding.isEmpty()
586 && atomName == format + QLatin1String(";charset=") + QString::fromLatin1(encoding)) {
587
588 if (requestedType == QVariant::String) {
589 QTextCodec *codec = QTextCodec::codecForName(encoding);
590 if (codec)
591 return codec->toUnicode(data);
592 }
593
594 return data;
595 }
596
597 // special cases for string types
598 if (format == QLatin1String("text/plain")) {
599 if (a == ATOM(UTF8_STRING))
600 return QString::fromUtf8(data);
601 if (a == XA_STRING)
602 return QString::fromLatin1(data);
603 if (a == ATOM(TEXT) || a == ATOM(COMPOUND_TEXT))
604 // #### might be wrong for COMPUND_TEXT
605 return QString::fromLocal8Bit(data, data.size());
606 }
607
608 // special case for uri types
609 if (format == QLatin1String("text/uri-list")) {
610 if (atomName == QLatin1String("text/x-moz-url")) {
611 // we expect this as utf16 <url><space><title>
612 // the first part is a url that should only contain ascci char
613 // so it should be safe to check that the second char is 0
614 // to verify that it is utf16
615 if (data.size() > 1 && data.at(1) == 0)
616 return QString::fromUtf16(reinterpret_cast<const ushort *>(data.constData()),
617 data.size() / 2).split(QLatin1Char('\n')).first().toLatin1();
618 }
619 }
620
621 // special cas for images
622 if (format == QLatin1String("image/ppm")) {
623 if (a == XA_PIXMAP && data.size() == sizeof(Pixmap)) {
624 Pixmap xpm = *((Pixmap*)data.data());
625 Display *dpy = display;
626 Window r;
627 int x,y;
628 uint w,h,bw,d;
629 if (!xpm)
630 return QByteArray();
631 XGetGeometry(dpy,xpm, &r,&x,&y,&w,&h,&bw,&d);
632 QImageWriter imageWriter;
633 GC gc = XCreateGC(dpy, xpm, 0, 0);
634 QImage imageToWrite;
635 if (d == 1) {
636 QBitmap qbm(w,h);
637 XCopyArea(dpy,xpm,qbm.handle(),gc,0,0,w,h,0,0);
638 imageWriter.setFormat("PBMRAW");
639 imageToWrite = qbm.toImage();
640 } else {
641 QPixmap qpm(w,h);
642 XCopyArea(dpy,xpm,qpm.handle(),gc,0,0,w,h,0,0);
643 imageWriter.setFormat("PPMRAW");
644 imageToWrite = qpm.toImage();
645 }
646 XFreeGC(dpy,gc);
647 QBuffer buf;
648 buf.open(QIODevice::WriteOnly);
649 imageWriter.setDevice(&buf);
650 imageWriter.write(imageToWrite);
651 return buf.buffer();
652 }
653 }
654 return QVariant();
655}
656
657//$$$ middle of xdndObtainData
658Atom QX11Data::xdndMimeAtomForFormat(const QString &format, QVariant::Type requestedType, const QList<Atom> &atoms, QByteArray *encoding)
659{
660 encoding->clear();
661
662 // find matches for string types
663 if (format == QLatin1String("text/plain")) {
664 if (atoms.contains(ATOM(UTF8_STRING)))
665 return ATOM(UTF8_STRING);
666 if (atoms.contains(ATOM(COMPOUND_TEXT)))
667 return ATOM(COMPOUND_TEXT);
668 if (atoms.contains(ATOM(TEXT)))
669 return ATOM(TEXT);
670 if (atoms.contains(XA_STRING))
671 return XA_STRING;
672 }
673
674 // find matches for uri types
675 if (format == QLatin1String("text/uri-list")) {
676 Atom a = xdndMimeStringToAtom(format);
677 if (a && atoms.contains(a))
678 return a;
679 a = xdndMimeStringToAtom(QLatin1String("text/x-moz-url"));
680 if (a && atoms.contains(a))
681 return a;
682 }
683
684 // find match for image
685 if (format == QLatin1String("image/ppm")) {
686 if (atoms.contains(XA_PIXMAP))
687 return XA_PIXMAP;
688 }
689
690 // for string/text requests try to use a format with a well-defined charset
691 // first to avoid encoding problems
692 if (requestedType == QVariant::String
693 && format.startsWith(QLatin1String("text/"))
694 && !format.contains(QLatin1String("charset="))) {
695
696 QString formatWithCharset = format;
697 formatWithCharset.append(QLatin1String(";charset=utf-8"));
698
699 Atom a = xdndMimeStringToAtom(formatWithCharset);
700 if (a && atoms.contains(a)) {
701 *encoding = "utf-8";
702 return a;
703 }
704 }
705
706 Atom a = xdndMimeStringToAtom(format);
707 if (a && atoms.contains(a))
708 return a;
709
710 return 0;
711}
712
713void QX11Data::xdndSetup() {
714 QCursorData::initialize();
715 qAddPostRoutine(qt_xdnd_cleanup);
716}
717
718
719void qt_xdnd_cleanup()
720{
721 delete noDropCursor;
722 noDropCursor = 0;
723 delete copyCursor;
724 copyCursor = 0;
725 delete moveCursor;
726 moveCursor = 0;
727 delete linkCursor;
728 linkCursor = 0;
729 delete defaultPm;
730 defaultPm = 0;
731 delete xdnd_data.desktop_proxy;
732 xdnd_data.desktop_proxy = 0;
733 delete xdnd_data.deco;
734 xdnd_data.deco = 0;
735}
736
737
738static QWidget *find_child(QWidget *tlw, QPoint & p)
739{
740 QWidget *widget = tlw;
741
742 p = widget->mapFromGlobal(p);
743 bool done = false;
744 while (!done) {
745 done = true;
746 if (((QExtraWidget*)widget)->extraData() &&
747 ((QExtraWidget*)widget)->extraData()->xDndProxy != 0)
748 break; // stop searching for widgets under the mouse cursor if found widget is a proxy.
749 QObjectList children = widget->children();
750 if (!children.isEmpty()) {
751 for(int i = children.size(); i > 0;) {
752 --i;
753 QWidget *w = qobject_cast<QWidget *>(children.at(i));
754 if (!w)
755 continue;
756 if (w->testAttribute(Qt::WA_TransparentForMouseEvents))
757 continue;
758 if (w->isVisible() &&
759 w->geometry().contains(p) &&
760 !w->isWindow()) {
761 widget = w;
762 done = false;
763 p = widget->mapFromParent(p);
764 break;
765 }
766 }
767 }
768 }
769 return widget;
770}
771
772
773static bool checkEmbedded(QWidget* w, const XEvent* xe)
774{
775 if (!w)
776 return false;
777
778 if (current_embedding_widget != 0 && current_embedding_widget != w) {
779 qt_xdnd_current_target = ((QExtraWidget*)current_embedding_widget)->extraData()->xDndProxy;
780 qt_xdnd_current_proxy_target = qt_xdnd_current_target;
781 qt_xdnd_send_leave();
782 qt_xdnd_current_target = 0;
783 qt_xdnd_current_proxy_target = 0;
784 current_embedding_widget = 0;
785 }
786
787 QWExtra* extra = ((QExtraWidget*)w)->extraData();
788 if (extra && extra->xDndProxy != 0) {
789
790 if (current_embedding_widget != w) {
791
792 last_enter_event.xany.window = extra->xDndProxy;
793 XSendEvent(X11->display, extra->xDndProxy, False, NoEventMask, &last_enter_event);
794 current_embedding_widget = w;
795 }
796
797 ((XEvent*)xe)->xany.window = extra->xDndProxy;
798 XSendEvent(X11->display, extra->xDndProxy, False, NoEventMask, (XEvent*)xe);
799 if (qt_xdnd_current_widget != w) {
800 qt_xdnd_current_widget = w;
801 }
802 return true;
803 }
804 current_embedding_widget = 0;
805 return false;
806}
807
808void QX11Data::xdndHandleEnter(QWidget *, const XEvent * xe, bool /*passive*/)
809{
810 motifdnd_active = false;
811
812 last_enter_event.xclient = xe->xclient;
813
814 const long *l = xe->xclient.data.l;
815 int version = (int)(((unsigned long)(l[1])) >> 24);
816
817 if (version > xdnd_version)
818 return;
819
820 qt_xdnd_dragsource_xid = l[0];
821
822 int j = 0;
823 if (l[1] & 1) {
824 // get the types from XdndTypeList
825 Atom type = XNone;
826 int f;
827 unsigned long n, a;
828 unsigned char *retval;
829 XGetWindowProperty(X11->display, qt_xdnd_dragsource_xid, ATOM(XdndTypelist), 0,
830 qt_xdnd_max_type, False, XA_ATOM, &type, &f,&n,&a,&retval);
831 Atom *data = (Atom *)retval;
832 for (; j<qt_xdnd_max_type && j < (int)n; j++) {
833 qt_xdnd_types[j] = data[j];
834 }
835 if (data)
836 XFree((uchar*)data);
837 } else {
838 // get the types from the message
839 int i;
840 for(i=2; i < 5; i++) {
841 qt_xdnd_types[j++] = l[i];
842 }
843 }
844 qt_xdnd_types[j] = 0;
845}
846
847static void handle_xdnd_position(QWidget *w, const XEvent * xe, bool passive)
848{
849 const unsigned long *l = (const unsigned long *)xe->xclient.data.l;
850
851 QPoint p((l[2] & 0xffff0000) >> 16, l[2] & 0x0000ffff);
852 QWidget * c = find_child(w, p); // changes p to to c-local coordinates
853
854 if (!passive && checkEmbedded(c, xe))
855 return;
856
857 if (!c || (!c->acceptDrops() && (c->windowType() == Qt::Desktop)))
858 return;
859
860 if (l[0] != qt_xdnd_dragsource_xid) {
861 DEBUG("xdnd drag position from unexpected source (%08lx not %08lx)", l[0], qt_xdnd_dragsource_xid);
862 return;
863 }
864
865 // timestamp from the source
866 if (l[3] != 0) {
867 // Some X server/client combination swallow the first 32 bit and
868 // interpret a set bit 31 as negative sign.
869 qt_xdnd_target_current_time = X11->userTime =
870 ((sizeof(Time) == 8 && xe->xclient.data.l[3] < 0)
871 ? uint(l[3])
872 : l[3]);
873 }
874
875 QDragManager *manager = QDragManager::self();
876 QMimeData *dropData = manager->object ? manager->dragPrivate()->data : manager->dropData;
877
878 XClientMessageEvent response;
879 response.type = ClientMessage;
880 response.window = qt_xdnd_dragsource_xid;
881 response.format = 32;
882 response.message_type = ATOM(XdndStatus);
883 response.data.l[0] = w->effectiveWinId();
884 response.data.l[1] = 0; // flags
885 response.data.l[2] = 0; // x, y
886 response.data.l[3] = 0; // w, h
887 response.data.l[4] = 0; // action
888
889 if (!passive) { // otherwise just reject
890 while (c && !c->acceptDrops() && !c->isWindow()) {
891 p = c->mapToParent(p);
892 c = c->parentWidget();
893 }
894 QWidget *target_widget = c && c->acceptDrops() ? c : 0;
895
896 QRect answerRect(c->mapToGlobal(p), QSize(1,1));
897
898 if (manager->object) {
899 possible_actions = manager->dragPrivate()->possible_actions;
900 } else {
901 possible_actions = Qt::DropActions(xdndaction_to_qtaction(l[4]));
902// possible_actions |= Qt::CopyAction;
903 }
904 QDragMoveEvent me(p, possible_actions, dropData, QApplication::mouseButtons(), QApplication::keyboardModifiers());
905
906 Qt::DropAction accepted_action = Qt::IgnoreAction;
907
908
909 if (target_widget != qt_xdnd_current_widget) {
910 if (qt_xdnd_current_widget) {
911 QDragLeaveEvent e;
912 QApplication::sendEvent(qt_xdnd_current_widget, &e);
913 }
914 if (qt_xdnd_current_widget != target_widget) {
915 qt_xdnd_current_widget = target_widget;
916 }
917 if (target_widget) {
918 qt_xdnd_current_position = p;
919
920 last_target_accepted_action = Qt::IgnoreAction;
921 QDragEnterEvent de(p, possible_actions, dropData, QApplication::mouseButtons(), QApplication::keyboardModifiers());
922 QApplication::sendEvent(target_widget, &de);
923 if (de.isAccepted() && de.dropAction() != Qt::IgnoreAction)
924 last_target_accepted_action = de.dropAction();
925 }
926 }
927
928 DEBUG() << "qt_handle_xdnd_position action=" << X11->xdndAtomToString(l[4]);
929 if (!target_widget) {
930 answerRect = QRect(p, QSize(1, 1));
931 } else {
932 qt_xdnd_current_widget = c;
933 qt_xdnd_current_position = p;
934
935 if (last_target_accepted_action != Qt::IgnoreAction) {
936 me.setDropAction(last_target_accepted_action);
937 me.accept();
938 }
939 QApplication::sendEvent(c, &me);
940 if (me.isAccepted()) {
941 response.data.l[1] = 1; // yes
942 accepted_action = me.dropAction();
943 last_target_accepted_action = accepted_action;
944 } else {
945 response.data.l[0] = 0;
946 last_target_accepted_action = Qt::IgnoreAction;
947 }
948 answerRect = me.answerRect().intersected(c->rect());
949 }
950 answerRect = QRect(c->mapToGlobal(answerRect.topLeft()), answerRect.size());
951
952 if (answerRect.left() < 0)
953 answerRect.setLeft(0);
954 if (answerRect.right() > 4096)
955 answerRect.setRight(4096);
956 if (answerRect.top() < 0)
957 answerRect.setTop(0);
958 if (answerRect.bottom() > 4096)
959 answerRect.setBottom(4096);
960 if (answerRect.width() < 0)
961 answerRect.setWidth(0);
962 if (answerRect.height() < 0)
963 answerRect.setHeight(0);
964
965 response.data.l[2] = (answerRect.x() << 16) + answerRect.y();
966 response.data.l[3] = (answerRect.width() << 16) + answerRect.height();
967 response.data.l[4] = qtaction_to_xdndaction(accepted_action);
968 }
969
970 // reset
971 qt_xdnd_target_current_time = CurrentTime;
972
973 QWidget * source = QWidget::find(qt_xdnd_dragsource_xid);
974 if (source && (source->windowType() == Qt::Desktop) && !source->acceptDrops())
975 source = 0;
976
977 DEBUG() << "sending XdndStatus";
978 if (source)
979 handle_xdnd_status(source, (const XEvent *)&response, passive);
980 else
981 XSendEvent(X11->display, qt_xdnd_dragsource_xid, False, NoEventMask, (XEvent*)&response);
982}
983
984static Bool xdnd_position_scanner(Display *, XEvent *event, XPointer)
985{
986 if (event->type != ClientMessage)
987 return false;
988 XClientMessageEvent *ev = &event->xclient;
989
990 if (ev->message_type == ATOM(XdndPosition))
991 return true;
992
993 return false;
994}
995
996void QX11Data::xdndHandlePosition(QWidget * w, const XEvent * xe, bool passive)
997{
998 DEBUG("xdndHandlePosition");
999 while (XCheckIfEvent(X11->display, (XEvent *)xe, xdnd_position_scanner, 0))
1000 ;
1001
1002 handle_xdnd_position(w, xe, passive);
1003}
1004
1005
1006static void handle_xdnd_status(QWidget *, const XEvent * xe, bool)
1007{
1008 const unsigned long *l = (const unsigned long *)xe->xclient.data.l;
1009 // ignore late status messages
1010 if (l[0] && l[0] != qt_xdnd_current_proxy_target)
1011 return;
1012 Qt::DropAction newAction = (l[1] & 0x1) ? xdndaction_to_qtaction(l[4]) : Qt::IgnoreAction;
1013
1014 if ((int)(l[1] & 2) == 0) {
1015 QPoint p((l[2] & 0xffff0000) >> 16, l[2] & 0x0000ffff);
1016 QSize s((l[3] & 0xffff0000) >> 16, l[3] & 0x0000ffff);
1017 qt_xdnd_source_sameanswer = QRect(p, s);
1018 } else {
1019 qt_xdnd_source_sameanswer = QRect();
1020 }
1021 QDragManager *manager = QDragManager::self();
1022 manager->willDrop = (l[1] & 0x1);
1023 if (global_accepted_action != newAction)
1024 manager->emitActionChanged(newAction);
1025 global_accepted_action = newAction;
1026 manager->updateCursor();
1027 waiting_for_status = false;
1028}
1029
1030static Bool xdnd_status_scanner(Display *, XEvent *event, XPointer)
1031{
1032 if (event->type != ClientMessage)
1033 return false;
1034 XClientMessageEvent *ev = &event->xclient;
1035
1036 if (ev->message_type == ATOM(XdndStatus))
1037 return true;
1038
1039 return false;
1040}
1041
1042void QX11Data::xdndHandleStatus(QWidget * w, const XEvent * xe, bool passive)
1043{
1044 DEBUG("xdndHandleStatus");
1045 while (XCheckIfEvent(X11->display, (XEvent *)xe, xdnd_status_scanner, 0))
1046 ;
1047
1048 handle_xdnd_status(w, xe, passive);
1049 DEBUG("xdndHandleStatus end");
1050}
1051
1052void QX11Data::xdndHandleLeave(QWidget *w, const XEvent * xe, bool /*passive*/)
1053{
1054 DEBUG("xdnd leave");
1055 if (!qt_xdnd_current_widget ||
1056 w->window() != qt_xdnd_current_widget->window()) {
1057 return; // sanity
1058 }
1059
1060 if (checkEmbedded(current_embedding_widget, xe)) {
1061 current_embedding_widget = 0;
1062 qt_xdnd_current_widget = 0;
1063 return;
1064 }
1065
1066 const unsigned long *l = (const unsigned long *)xe->xclient.data.l;
1067
1068 QDragLeaveEvent e;
1069 QApplication::sendEvent(qt_xdnd_current_widget, &e);
1070
1071 if (l[0] != qt_xdnd_dragsource_xid) {
1072 // This often happens - leave other-process window quickly
1073 DEBUG("xdnd drag leave from unexpected source (%08lx not %08lx", l[0], qt_xdnd_dragsource_xid);
1074 qt_xdnd_current_widget = 0;
1075 return;
1076 }
1077
1078 qt_xdnd_dragsource_xid = 0;
1079 qt_xdnd_types[0] = 0;
1080 qt_xdnd_current_widget = 0;
1081}
1082
1083
1084void qt_xdnd_send_leave()
1085{
1086 if (!qt_xdnd_current_target)
1087 return;
1088
1089 XClientMessageEvent leave;
1090 leave.type = ClientMessage;
1091 leave.window = qt_xdnd_current_target;
1092 leave.format = 32;
1093 leave.message_type = ATOM(XdndLeave);
1094 leave.data.l[0] = qt_xdnd_dragsource_xid;
1095 leave.data.l[1] = 0; // flags
1096 leave.data.l[2] = 0; // x, y
1097 leave.data.l[3] = 0; // w, h
1098 leave.data.l[4] = 0; // just null
1099
1100 QWidget * w = QWidget::find(qt_xdnd_current_proxy_target);
1101
1102 if (w && (w->windowType() == Qt::Desktop) && !w->acceptDrops())
1103 w = 0;
1104
1105 if (w)
1106 X11->xdndHandleLeave(w, (const XEvent *)&leave, false);
1107 else
1108 XSendEvent(X11->display, qt_xdnd_current_proxy_target, False,
1109 NoEventMask, (XEvent*)&leave);
1110 // reset the drag manager state
1111 QDragManager *manager = QDragManager::self();
1112 manager->willDrop = false;
1113 if (global_accepted_action != Qt::IgnoreAction)
1114 manager->emitActionChanged(Qt::IgnoreAction);
1115 global_accepted_action = Qt::IgnoreAction;
1116 manager->updateCursor();
1117 qt_xdnd_current_target = 0;
1118 qt_xdnd_current_proxy_target = 0;
1119 qt_xdnd_source_current_time = 0;
1120 waiting_for_status = false;
1121}
1122
1123
1124
1125void QX11Data::xdndHandleDrop(QWidget *, const XEvent * xe, bool passive)
1126{
1127 DEBUG("xdndHandleDrop");
1128 if (!qt_xdnd_current_widget) {
1129 qt_xdnd_dragsource_xid = 0;
1130 return; // sanity
1131 }
1132
1133 if (!passive && checkEmbedded(qt_xdnd_current_widget, xe)){
1134 current_embedding_widget = 0;
1135 qt_xdnd_dragsource_xid = 0;
1136 qt_xdnd_current_widget = 0;
1137 return;
1138 }
1139 const unsigned long *l = (const unsigned long *)xe->xclient.data.l;
1140
1141 QDragManager *manager = QDragManager::self();
1142 DEBUG("xdnd drop");
1143
1144 if (l[0] != qt_xdnd_dragsource_xid) {
1145 DEBUG("xdnd drop from unexpected source (%08lx not %08lx", l[0], qt_xdnd_dragsource_xid);
1146 return;
1147 }
1148
1149 // update the "user time" from the timestamp in the event.
1150 if (l[2] != 0) {
1151 // Some X server/client combination swallow the first 32 bit and
1152 // interpret a set bit 31 as negative sign.
1153 qt_xdnd_target_current_time = X11->userTime =
1154 ((sizeof(Time) == 8 && xe->xclient.data.l[2] < 0)
1155 ? uint(l[2])
1156 : l[2]);
1157 }
1158
1159 if (!passive) {
1160 // this could be a same-application drop, just proxied due to
1161 // some XEMBEDding, so try to find the real QMimeData used
1162 // based on the timestamp for this drop.
1163 QMimeData *dropData = 0;
1164 int at = findXdndDropTransactionByTime(qt_xdnd_target_current_time);
1165 if (at != -1)
1166 dropData = QDragManager::dragPrivate(X11->dndDropTransactions.at(at).object)->data;
1167 // if we can't find it, then use the data in the drag manager
1168 if (!dropData)
1169 dropData = (manager->object) ? manager->dragPrivate()->data : manager->dropData;
1170
1171 QDropEvent de(qt_xdnd_current_position, possible_actions, dropData,
1172 QApplication::mouseButtons(), QApplication::keyboardModifiers());
1173 QApplication::sendEvent(qt_xdnd_current_widget, &de);
1174 if (!de.isAccepted()) {
1175 // Ignore a failed drag
1176 global_accepted_action = Qt::IgnoreAction;
1177 } else {
1178 global_accepted_action = de.dropAction();
1179 }
1180 XClientMessageEvent finished;
1181 finished.type = ClientMessage;
1182 finished.window = qt_xdnd_dragsource_xid;
1183 finished.format = 32;
1184 finished.message_type = ATOM(XdndFinished);
1185 DNDDEBUG << "xdndHandleDrop"
1186 << "qt_xdnd_current_widget" << qt_xdnd_current_widget
1187 << (qt_xdnd_current_widget ? qt_xdnd_current_widget->effectiveWinId() : 0)
1188 << "t_xdnd_current_widget->window()"
1189 << (qt_xdnd_current_widget ? qt_xdnd_current_widget->window() : 0)
1190 << (qt_xdnd_current_widget ? qt_xdnd_current_widget->window()->internalWinId() : 0);
1191 finished.data.l[0] = qt_xdnd_current_widget?qt_xdnd_current_widget->window()->internalWinId():0;
1192 finished.data.l[1] = de.isAccepted() ? 1 : 0; // flags
1193 finished.data.l[2] = qtaction_to_xdndaction(global_accepted_action);
1194 XSendEvent(X11->display, qt_xdnd_dragsource_xid, False,
1195 NoEventMask, (XEvent*)&finished);
1196 } else {
1197 QDragLeaveEvent e;
1198 QApplication::sendEvent(qt_xdnd_current_widget, &e);
1199 }
1200 qt_xdnd_dragsource_xid = 0;
1201 qt_xdnd_current_widget = 0;
1202 waiting_for_status = false;
1203
1204 // reset
1205 qt_xdnd_target_current_time = CurrentTime;
1206}
1207
1208
1209void QX11Data::xdndHandleFinished(QWidget *, const XEvent * xe, bool passive)
1210{
1211 DEBUG("xdndHandleFinished");
1212 const unsigned long *l = (const unsigned long *)xe->xclient.data.l;
1213
1214 DNDDEBUG << "xdndHandleFinished, l[0]" << l[0]
1215 << "qt_xdnd_current_target" << qt_xdnd_current_target
1216 << "qt_xdnd_current_proxy_targe" << qt_xdnd_current_proxy_target;
1217
1218 if (l[0]) {
1219 int at = findXdndDropTransactionByWindow(l[0]);
1220 if (at != -1) {
1221 restartXdndDropExpiryTimer();
1222
1223 QXdndDropTransaction t = X11->dndDropTransactions.takeAt(at);
1224 QDragManager *manager = QDragManager::self();
1225
1226 Window target = qt_xdnd_current_target;
1227 Window proxy_target = qt_xdnd_current_proxy_target;
1228 QWidget *embedding_widget = current_embedding_widget;
1229 QDrag *currentObject = manager->object;
1230
1231 qt_xdnd_current_target = t.target;
1232 qt_xdnd_current_proxy_target = t.proxy_target;
1233 current_embedding_widget = t.embedding_widget;
1234 manager->object = t.object;
1235
1236 if (!passive)
1237 (void) checkEmbedded(qt_xdnd_current_widget, xe);
1238
1239 current_embedding_widget = 0;
1240 qt_xdnd_current_target = 0;
1241 qt_xdnd_current_proxy_target = 0;
1242
1243 if (t.object)
1244 t.object->deleteLater();
1245
1246 qt_xdnd_current_target = target;
1247 qt_xdnd_current_proxy_target = proxy_target;
1248 current_embedding_widget = embedding_widget;
1249 manager->object = currentObject;
1250 }
1251 }
1252 waiting_for_status = false;
1253}
1254
1255
1256void QDragManager::timerEvent(QTimerEvent* e)
1257{
1258 if (e->timerId() == heartbeat && qt_xdnd_source_sameanswer.isNull()) {
1259 move(QCursor::pos());
1260 } else if (e->timerId() == transaction_expiry_timer) {
1261 for (int i = 0; i < X11->dndDropTransactions.count(); ++i) {
1262 const QXdndDropTransaction &t = X11->dndDropTransactions.at(i);
1263 if (t.targetWidget) {
1264 // dnd within the same process, don't delete these
1265 continue;
1266 }
1267 t.object->deleteLater();
1268 X11->dndDropTransactions.removeAt(i--);
1269 }
1270
1271 killTimer(transaction_expiry_timer);
1272 transaction_expiry_timer = -1;
1273 }
1274}
1275
1276bool QDragManager::eventFilter(QObject * o, QEvent * e)
1277{
1278 if (beingCancelled) {
1279 if (e->type() == QEvent::KeyRelease && ((QKeyEvent*)e)->key() == Qt::Key_Escape) {
1280 qApp->removeEventFilter(this);
1281 Q_ASSERT(object == 0);
1282 beingCancelled = false;
1283 eventLoop->exit();
1284 return true; // block the key release
1285 }
1286 return false;
1287 }
1288
1289 Q_ASSERT(object != 0);
1290
1291 if (!o->isWidgetType())
1292 return false;
1293
1294 if (e->type() == QEvent::MouseMove) {
1295 QMouseEvent* me = (QMouseEvent *)e;
1296 move(me->globalPos());
1297 return true;
1298 } else if (e->type() == QEvent::MouseButtonRelease) {
1299 DEBUG("pre drop");
1300 qApp->removeEventFilter(this);
1301 if (willDrop)
1302 drop();
1303 else
1304 cancel();
1305 DEBUG("drop, resetting object");
1306 beingCancelled = false;
1307 eventLoop->exit();
1308 return true;
1309 }
1310
1311 if (e->type() == QEvent::KeyPress || e->type() == QEvent::KeyRelease) {
1312 QKeyEvent *ke = ((QKeyEvent*)e);
1313 if (ke->key() == Qt::Key_Escape && e->type() == QEvent::KeyPress) {
1314 cancel();
1315 qApp->removeEventFilter(this);
1316 beingCancelled = false;
1317 eventLoop->exit();
1318 } else {
1319 qt_xdnd_source_sameanswer = QRect(); // force move
1320 move(QCursor::pos());
1321 }
1322 return true; // Eat all key events
1323 }
1324
1325 // ### We bind modality to widgets, so we have to do this
1326 // ### "manually".
1327 // DnD is modal - eat all other interactive events
1328 switch (e->type()) {
1329 case QEvent::MouseButtonPress:
1330 case QEvent::MouseButtonRelease:
1331 case QEvent::MouseButtonDblClick:
1332 case QEvent::MouseMove:
1333 case QEvent::KeyPress:
1334 case QEvent::KeyRelease:
1335 case QEvent::Wheel:
1336 case QEvent::ShortcutOverride:
1337#ifdef QT3_SUPPORT
1338 case QEvent::Accel:
1339 case QEvent::AccelAvailable:
1340#endif
1341 return true;
1342 default:
1343 return false;
1344 }
1345}
1346
1347void QDragManager::updateCursor()
1348{
1349 if (!noDropCursor) {
1350#ifndef QT_NO_CURSOR
1351 noDropCursor = new QCursor(Qt::ForbiddenCursor);
1352 moveCursor = new QCursor(dragCursor(Qt::MoveAction), 0,0);
1353 copyCursor = new QCursor(dragCursor(Qt::CopyAction), 0,0);
1354 linkCursor = new QCursor(dragCursor(Qt::LinkAction), 0,0);
1355#endif
1356 }
1357
1358 QCursor *c;
1359 if (willDrop) {
1360 if (global_accepted_action == Qt::CopyAction) {
1361 c = copyCursor;
1362 } else if (global_accepted_action == Qt::LinkAction) {
1363 c = linkCursor;
1364 } else {
1365 c = moveCursor;
1366 }
1367 if (xdnd_data.deco) {
1368 xdnd_data.deco->show();
1369 xdnd_data.deco->raise();
1370 }
1371 } else {
1372 c = noDropCursor;
1373 //if (qt_xdnd_deco)
1374 // qt_xdnd_deco->hide();
1375 }
1376#ifndef QT_NO_CURSOR
1377 if (c)
1378 qApp->changeOverrideCursor(*c);
1379#endif
1380}
1381
1382
1383void QDragManager::cancel(bool deleteSource)
1384{
1385 DEBUG("QDragManager::cancel");
1386 Q_ASSERT(heartbeat != -1);
1387 killTimer(heartbeat);
1388 heartbeat = -1;
1389 beingCancelled = true;
1390 qt_xdnd_dragging = false;
1391
1392 if (qt_xdnd_current_target)
1393 qt_xdnd_send_leave();
1394
1395#ifndef QT_NO_CURSOR
1396 if (restoreCursor) {
1397 QApplication::restoreOverrideCursor();
1398 restoreCursor = false;
1399 }
1400#endif
1401
1402 if (deleteSource && object)
1403 object->deleteLater();
1404 object = 0;
1405 qDeleteInEventHandler(xdnd_data.deco);
1406 xdnd_data.deco = 0;
1407
1408 global_accepted_action = Qt::IgnoreAction;
1409}
1410
1411static
1412Window findRealWindow(const QPoint & pos, Window w, int md)
1413{
1414 if (xdnd_data.deco && w == xdnd_data.deco->effectiveWinId())
1415 return 0;
1416
1417 if (md) {
1418 X11->ignoreBadwindow();
1419 XWindowAttributes attr;
1420 XGetWindowAttributes(X11->display, w, &attr);
1421 if (X11->badwindow())
1422 return 0;
1423
1424 if (attr.map_state == IsViewable
1425 && QRect(attr.x,attr.y,attr.width,attr.height).contains(pos)) {
1426 {
1427 Atom type = XNone;
1428 int f;
1429 unsigned long n, a;
1430 unsigned char *data;
1431
1432 XGetWindowProperty(X11->display, w, ATOM(XdndAware), 0, 0, False,
1433 AnyPropertyType, &type, &f,&n,&a,&data);
1434 if (data) XFree(data);
1435 if (type)
1436 return w;
1437 }
1438
1439 Window r, p;
1440 Window* c;
1441 uint nc;
1442 if (XQueryTree(X11->display, w, &r, &p, &c, &nc)) {
1443 r=0;
1444 for (uint i=nc; !r && i--;) {
1445 r = findRealWindow(pos-QPoint(attr.x,attr.y),
1446 c[i], md-1);
1447 }
1448 XFree(c);
1449 if (r)
1450 return r;
1451
1452 // We didn't find a client window! Just use the
1453 // innermost window.
1454 }
1455
1456 // No children!
1457 return w;
1458 }
1459 }
1460 return 0;
1461}
1462
1463void QDragManager::move(const QPoint & globalPos)
1464{
1465#ifdef QT_NO_CURSOR
1466 Q_UNUSED(globalPos);
1467 return;
1468#else
1469 DEBUG() << "QDragManager::move enter";
1470 if (!object) {
1471 // perhaps the target crashed?
1472 return;
1473 }
1474
1475 int screen = QCursor::x11Screen();
1476 if ((qt_xdnd_current_screen == -1 && screen != X11->defaultScreen) || (screen != qt_xdnd_current_screen)) {
1477 // recreate the pixmap on the new screen...
1478 delete xdnd_data.deco;
1479 QWidget* parent = object->source()->window()->x11Info().screen() == screen
1480 ? object->source()->window() : QApplication::desktop()->screen(screen);
1481 xdnd_data.deco = new QShapedPixmapWidget(parent);
1482 if (!QWidget::mouseGrabber()) {
1483 updatePixmap();
1484 xdnd_data.deco->grabMouse();
1485 }
1486 }
1487 xdnd_data.deco->move(QCursor::pos() - xdnd_data.deco->pm_hot);
1488
1489 if (qt_xdnd_source_sameanswer.contains(globalPos) && qt_xdnd_source_sameanswer.isValid())
1490 return;
1491
1492 qt_xdnd_current_screen = screen;
1493 Window rootwin = QX11Info::appRootWindow(qt_xdnd_current_screen);
1494 Window target = 0;
1495 int lx = 0, ly = 0;
1496 if (!XTranslateCoordinates(X11->display, rootwin, rootwin, globalPos.x(), globalPos.y(), &lx, &ly, &target))
1497 // some weird error...
1498 return;
1499
1500 if (target == rootwin) {
1501 // Ok.
1502 } else if (target) {
1503 //me
1504 Window src = rootwin;
1505 while (target != 0) {
1506 DNDDEBUG << "checking target for XdndAware" << QWidget::find(target) << target;
1507 int lx2, ly2;
1508 Window t;
1509 // translate coordinates
1510 if (!XTranslateCoordinates(X11->display, src, target, lx, ly, &lx2, &ly2, &t)) {
1511 target = 0;
1512 break;
1513 }
1514 lx = lx2;
1515 ly = ly2;
1516 src = target;
1517
1518 // check if it has XdndAware
1519 Atom type = 0;
1520 int f;
1521 unsigned long n, a;
1522 unsigned char *data = 0;
1523 XGetWindowProperty(X11->display, target, ATOM(XdndAware), 0, 0, False,
1524 AnyPropertyType, &type, &f,&n,&a,&data);
1525 if (data)
1526 XFree(data);
1527 if (type) {
1528 DNDDEBUG << "Found XdndAware on " << QWidget::find(target) << target;
1529 break;
1530 }
1531
1532 // find child at the coordinates
1533 if (!XTranslateCoordinates(X11->display, src, src, lx, ly, &lx2, &ly2, &target)) {
1534 target = 0;
1535 break;
1536 }
1537 }
1538 if (xdnd_data.deco && (!target || target == xdnd_data.deco->effectiveWinId())) {
1539 DNDDEBUG << "need to find real window";
1540 target = findRealWindow(globalPos, rootwin, 6);
1541 DNDDEBUG << "real window found" << QWidget::find(target) << target;
1542 }
1543 }
1544
1545 QWidget* w;
1546 if (target) {
1547 w = QWidget::find((WId)target);
1548 if (w && (w->windowType() == Qt::Desktop) && !w->acceptDrops())
1549 w = 0;
1550 } else {
1551 w = 0;
1552 target = rootwin;
1553 }
1554
1555 DNDDEBUG << "and the final target is " << QWidget::find(target) << target;
1556 DNDDEBUG << "the widget w is" << w;
1557
1558 WId proxy_target = xdndProxy(target);
1559 if (!proxy_target)
1560 proxy_target = target;
1561 int target_version = 1;
1562
1563 if (proxy_target) {
1564 Atom type = XNone;
1565 int r, f;
1566 unsigned long n, a;
1567 unsigned char *retval;
1568 X11->ignoreBadwindow();
1569 r = XGetWindowProperty(X11->display, proxy_target, ATOM(XdndAware), 0,
1570 1, False, AnyPropertyType, &type, &f,&n,&a,&retval);
1571 int *tv = (int *)retval;
1572 if (r != Success || X11->badwindow()) {
1573 target = 0;
1574 } else {
1575 target_version = qMin(xdnd_version,tv ? *tv : 1);
1576 if (tv)
1577 XFree(tv);
1578// if (!(!X11->badwindow() && type))
1579// target = 0;
1580 }
1581 }
1582
1583 if (target != qt_xdnd_current_target) {
1584 if (qt_xdnd_current_target)
1585 qt_xdnd_send_leave();
1586
1587 qt_xdnd_current_target = target;
1588 qt_xdnd_current_proxy_target = proxy_target;
1589 if (target) {
1590 QVector<Atom> types;
1591 int flags = target_version << 24;
1592 QStringList fmts = QInternalMimeData::formatsHelper(dragPrivate()->data);
1593 for (int i = 0; i < fmts.size(); ++i) {
1594 QList<Atom> atoms = X11->xdndMimeAtomsForFormat(fmts.at(i));
1595 for (int j = 0; j < atoms.size(); ++j) {
1596 if (!types.contains(atoms.at(j)))
1597 types.append(atoms.at(j));
1598 }
1599 }
1600 if (types.size() > 3) {
1601 XChangeProperty(X11->display,
1602 dragPrivate()->source->effectiveWinId(), ATOM(XdndTypelist),
1603 XA_ATOM, 32, PropModeReplace,
1604 (unsigned char *)types.data(),
1605 types.size());
1606 flags |= 0x0001;
1607 }
1608 XClientMessageEvent enter;
1609 enter.type = ClientMessage;
1610 enter.window = target;
1611 enter.format = 32;
1612 enter.message_type = ATOM(XdndEnter);
1613 enter.data.l[0] = dragPrivate()->source->effectiveWinId();
1614 enter.data.l[1] = flags;
1615 enter.data.l[2] = types.size()>0 ? types.at(0) : 0;
1616 enter.data.l[3] = types.size()>1 ? types.at(1) : 0;
1617 enter.data.l[4] = types.size()>2 ? types.at(2) : 0;
1618 // provisionally set the rectangle to 5x5 pixels...
1619 qt_xdnd_source_sameanswer = QRect(globalPos.x() - 2,
1620 globalPos.y() -2 , 5, 5);
1621
1622 DEBUG("sending Xdnd enter");
1623 if (w)
1624 X11->xdndHandleEnter(w, (const XEvent *)&enter, false);
1625 else if (target)
1626 XSendEvent(X11->display, proxy_target, False, NoEventMask, (XEvent*)&enter);
1627 waiting_for_status = false;
1628 }
1629 }
1630 if (waiting_for_status)
1631 return;
1632
1633 if (target) {
1634 waiting_for_status = true;
1635
1636 XClientMessageEvent move;
1637 move.type = ClientMessage;
1638 move.window = target;
1639 move.format = 32;
1640 move.message_type = ATOM(XdndPosition);
1641 move.window = target;
1642 move.data.l[0] = dragPrivate()->source->effectiveWinId();
1643 move.data.l[1] = 0; // flags
1644 move.data.l[2] = (globalPos.x() << 16) + globalPos.y();
1645 move.data.l[3] = X11->time;
1646 move.data.l[4] = qtaction_to_xdndaction(defaultAction(dragPrivate()->possible_actions, QApplication::keyboardModifiers()));
1647 DEBUG("sending Xdnd position");
1648
1649 qt_xdnd_source_current_time = X11->time;
1650
1651 if (w)
1652 handle_xdnd_position(w, (const XEvent *)&move, false);
1653 else
1654 XSendEvent(X11->display, proxy_target, False, NoEventMask,
1655 (XEvent*)&move);
1656 } else {
1657 if (willDrop) {
1658 willDrop = false;
1659 updateCursor();
1660 }
1661 }
1662 DEBUG() << "QDragManager::move leave";
1663#endif
1664}
1665
1666
1667void QDragManager::drop()
1668{
1669 Q_ASSERT(heartbeat != -1);
1670 killTimer(heartbeat);
1671 heartbeat = -1;
1672 qt_xdnd_dragging = false;
1673
1674 if (!qt_xdnd_current_target)
1675 return;
1676
1677 qDeleteInEventHandler(xdnd_data.deco);
1678 xdnd_data.deco = 0;
1679
1680 XClientMessageEvent drop;
1681 drop.type = ClientMessage;
1682 drop.window = qt_xdnd_current_target;
1683 drop.format = 32;
1684 drop.message_type = ATOM(XdndDrop);
1685 drop.data.l[0] = dragPrivate()->source->effectiveWinId();
1686 drop.data.l[1] = 0; // flags
1687 drop.data.l[2] = X11->time;
1688
1689 drop.data.l[3] = 0;
1690 drop.data.l[4] = 0;
1691
1692 QWidget * w = QWidget::find(qt_xdnd_current_proxy_target);
1693
1694 if (w && (w->windowType() == Qt::Desktop) && !w->acceptDrops())
1695 w = 0;
1696
1697 QXdndDropTransaction t = {
1698 X11->time,
1699 qt_xdnd_current_target,
1700 qt_xdnd_current_proxy_target,
1701 w,
1702 current_embedding_widget,
1703 object
1704 };
1705 X11->dndDropTransactions.append(t);
1706 restartXdndDropExpiryTimer();
1707
1708 if (w)
1709 X11->xdndHandleDrop(w, (const XEvent *)&drop, false);
1710 else
1711 XSendEvent(X11->display, qt_xdnd_current_proxy_target, False,
1712 NoEventMask, (XEvent*)&drop);
1713
1714 qt_xdnd_current_target = 0;
1715 qt_xdnd_current_proxy_target = 0;
1716 qt_xdnd_source_current_time = 0;
1717 current_embedding_widget = 0;
1718 object = 0;
1719
1720#ifndef QT_NO_CURSOR
1721 if (restoreCursor) {
1722 QApplication::restoreOverrideCursor();
1723 restoreCursor = false;
1724 }
1725#endif
1726}
1727
1728
1729
1730bool QX11Data::xdndHandleBadwindow()
1731{
1732 if (qt_xdnd_current_target) {
1733 QDragManager *manager = QDragManager::self();
1734 if (manager->object) {
1735 qt_xdnd_current_target = 0;
1736 qt_xdnd_current_proxy_target = 0;
1737 manager->object->deleteLater();
1738 manager->object = 0;
1739 delete xdnd_data.deco;
1740 xdnd_data.deco = 0;
1741 return true;
1742 }
1743 }
1744 if (qt_xdnd_dragsource_xid) {
1745 qt_xdnd_dragsource_xid = 0;
1746 if (qt_xdnd_current_widget) {
1747 QApplication::postEvent(qt_xdnd_current_widget, new QDragLeaveEvent);
1748 qt_xdnd_current_widget = 0;
1749 }
1750 return true;
1751 }
1752 return false;
1753}
1754
1755void QX11Data::xdndHandleSelectionRequest(const XSelectionRequestEvent * req)
1756{
1757 if (!req)
1758 return;
1759 XEvent evt;
1760 evt.xselection.type = SelectionNotify;
1761 evt.xselection.display = req->display;
1762 evt.xselection.requestor = req->requestor;
1763 evt.xselection.selection = req->selection;
1764 evt.xselection.target = XNone;
1765 evt.xselection.property = XNone;
1766 evt.xselection.time = req->time;
1767
1768 QDragManager *manager = QDragManager::self();
1769 QDrag *currentObject = manager->object;
1770
1771 // which transaction do we use? (note: -2 means use current manager->object)
1772 int at = -1;
1773
1774 // figure out which data the requestor is really interested in
1775 if (manager->object && req->time == qt_xdnd_source_current_time) {
1776 // requestor wants the current drag data
1777 at = -2;
1778 } else {
1779 // if someone has requested data in response to XdndDrop, find the corresponding transaction. the
1780 // spec says to call XConvertSelection() using the timestamp from the XdndDrop
1781 at = findXdndDropTransactionByTime(req->time);
1782 if (at == -1) {
1783 // no dice, perhaps the client was nice enough to use the same window id in XConvertSelection()
1784 // that we sent the XdndDrop event to.
1785 at = findXdndDropTransactionByWindow(req->requestor);
1786 }
1787 if (at == -1 && req->time == CurrentTime) {
1788 // previous Qt versions always requested the data on a child of the target window
1789 // using CurrentTime... but it could be asking for either drop data or the current drag's data
1790 Window target = findXdndAwareParent(req->requestor);
1791 if (target) {
1792 if (qt_xdnd_current_target && qt_xdnd_current_target == target)
1793 at = -2;
1794 else
1795 at = findXdndDropTransactionByWindow(target);
1796 }
1797 }
1798 }
1799 if (at >= 0) {
1800 restartXdndDropExpiryTimer();
1801
1802 // use the drag object from an XdndDrop tansaction
1803 manager->object = X11->dndDropTransactions.at(at).object;
1804 } else if (at != -2) {
1805 // no transaction found, we'll have to reject the request
1806 manager->object = 0;
1807 }
1808 if (manager->object) {
1809 Atom atomFormat = req->target;
1810 int dataFormat = 0;
1811 QByteArray data;
1812 if (X11->xdndMimeDataForAtom(req->target, manager->dragPrivate()->data,
1813 &data, &atomFormat, &dataFormat)) {
1814 int dataSize = data.size() / (dataFormat / 8);
1815 XChangeProperty (X11->display, req->requestor, req->property,
1816 atomFormat, dataFormat, PropModeReplace,
1817 (unsigned char *)data.data(), dataSize);
1818 evt.xselection.property = req->property;
1819 evt.xselection.target = atomFormat;
1820 }
1821 }
1822
1823 // reset manager->object in case we modified it above
1824 manager->object = currentObject;
1825
1826 // ### this can die if req->requestor crashes at the wrong
1827 // ### moment
1828 XSendEvent(X11->display, req->requestor, False, 0, &evt);
1829}
1830
1831static QVariant xdndObtainData(const char *format, QVariant::Type requestedType)
1832{
1833 QByteArray result;
1834
1835 QWidget* w;
1836 QDragManager *manager = QDragManager::self();
1837 if (qt_xdnd_dragsource_xid && manager->object &&
1838 (w=QWidget::find(qt_xdnd_dragsource_xid))
1839 && (!(w->windowType() == Qt::Desktop) || w->acceptDrops()))
1840 {
1841 QDragPrivate * o = QDragManager::self()->dragPrivate();
1842 if (o->data->hasFormat(QLatin1String(format)))
1843 result = o->data->data(QLatin1String(format));
1844 return result;
1845 }
1846
1847 QList<Atom> atoms;
1848 int i = 0;
1849 while ((qt_xdnd_types[i])) {
1850 atoms.append(qt_xdnd_types[i]);
1851 ++i;
1852 }
1853 QByteArray encoding;
1854 Atom a = X11->xdndMimeAtomForFormat(QLatin1String(format), requestedType, atoms, &encoding);
1855 if (!a)
1856 return result;
1857
1858 if (XGetSelectionOwner(X11->display, ATOM(XdndSelection)) == XNone)
1859 return result; // should never happen?
1860
1861 QWidget* tw = qt_xdnd_current_widget;
1862 if (!qt_xdnd_current_widget || (qt_xdnd_current_widget->windowType() == Qt::Desktop))
1863 tw = new QWidget;
1864
1865 XConvertSelection(X11->display, ATOM(XdndSelection), a, ATOM(XdndSelection), tw->effectiveWinId(),
1866 qt_xdnd_target_current_time);
1867 XFlush(X11->display);
1868
1869 XEvent xevent;
1870 bool got=X11->clipboardWaitForEvent(tw->effectiveWinId(), SelectionNotify, &xevent, 5000);
1871 if (got) {
1872 Atom type;
1873
1874 if (X11->clipboardReadProperty(tw->effectiveWinId(), ATOM(XdndSelection), true, &result, 0, &type, 0, false)) {
1875 if (type == ATOM(INCR)) {
1876 int nbytes = result.size() >= 4 ? *((int*)result.data()) : 0;
1877 result = X11->clipboardReadIncrementalProperty(tw->effectiveWinId(), ATOM(XdndSelection), nbytes, false);
1878 } else if (type != a && type != XNone) {
1879 DEBUG("Qt clipboard: unknown atom %ld", type);
1880 }
1881 }
1882 }
1883 if (!qt_xdnd_current_widget || (qt_xdnd_current_widget->windowType() == Qt::Desktop))
1884 delete tw;
1885
1886 return X11->xdndMimeConvertToFormat(a, result, QLatin1String(format), requestedType, encoding);
1887}
1888
1889
1890/*
1891 Enable drag and drop for widget w by installing the proper
1892 properties on w's toplevel widget.
1893*/
1894bool QX11Data::dndEnable(QWidget* w, bool on)
1895{
1896 w = w->window();
1897
1898 if (bool(((QExtraWidget*)w)->topData()->dnd) == on)
1899 return true; // been there, done that
1900 ((QExtraWidget*)w)->topData()->dnd = on ? 1 : 0;
1901
1902 motifdndEnable(w, on);
1903 return xdndEnable(w, on);
1904}
1905
1906Qt::DropAction QDragManager::drag(QDrag * o)
1907{
1908 if (object == o || !o || !o->d_func()->source)
1909 return Qt::IgnoreAction;
1910
1911 if (object) {
1912 cancel();
1913 qApp->removeEventFilter(this);
1914 beingCancelled = false;
1915 }
1916
1917 if (object) {
1918 // the last drag and drop operation hasn't finished, so we are going to wait
1919 // for one second to see if it does... if the finish message comes after this,
1920 // then we could still have problems, but this is highly unlikely
1921 QApplication::flush();
1922
1923 QTime started = QTime::currentTime();
1924 QTime now = started;
1925 do {
1926 XEvent event;
1927 if (XCheckTypedEvent(X11->display, ClientMessage, &event))
1928 qApp->x11ProcessEvent(&event);
1929
1930 now = QTime::currentTime();
1931 if (started > now) // crossed midnight
1932 started = now;
1933
1934 // sleep 50 ms, so we don't use up CPU cycles all the time.
1935 struct timeval usleep_tv;
1936 usleep_tv.tv_sec = 0;
1937 usleep_tv.tv_usec = 50000;
1938 select(0, 0, 0, 0, &usleep_tv);
1939 } while (object && started.msecsTo(now) < 1000);
1940 }
1941
1942 object = o;
1943 object->d_func()->target = 0;
1944 xdnd_data.deco = new QShapedPixmapWidget(object->source()->window());
1945
1946 willDrop = false;
1947
1948 updatePixmap();
1949
1950 qApp->installEventFilter(this);
1951 XSetSelectionOwner(X11->display, ATOM(XdndSelection), dragPrivate()->source->window()->internalWinId(), X11->time);
1952 global_accepted_action = Qt::CopyAction;
1953 qt_xdnd_source_sameanswer = QRect();
1954#ifndef QT_NO_CURSOR
1955 // set the override cursor (must be done here, since it is updated
1956 // in the call to move() below)
1957 qApp->setOverrideCursor(Qt::ArrowCursor);
1958 restoreCursor = true;
1959#endif
1960 move(QCursor::pos());
1961 heartbeat = startTimer(200);
1962
1963 qt_xdnd_dragging = true;
1964
1965 if (!QWidget::mouseGrabber())
1966 xdnd_data.deco->grabMouse();
1967
1968 eventLoop = new QEventLoop;
1969 (void) eventLoop->exec();
1970 delete eventLoop;
1971 eventLoop = 0;
1972
1973#ifndef QT_NO_CURSOR
1974 if (restoreCursor) {
1975 qApp->restoreOverrideCursor();
1976 restoreCursor = false;
1977 }
1978#endif
1979
1980 // delete cursors as they may be different next drag.
1981 delete noDropCursor;
1982 noDropCursor = 0;
1983 delete copyCursor;
1984 copyCursor = 0;
1985 delete moveCursor;
1986 moveCursor = 0;
1987 delete linkCursor;
1988 linkCursor = 0;
1989
1990 delete xdnd_data.deco;
1991 xdnd_data.deco = 0;
1992 if (heartbeat != -1)
1993 killTimer(heartbeat);
1994 heartbeat = -1;
1995 qt_xdnd_current_screen = -1;
1996 qt_xdnd_dragging = false;
1997
1998 return global_accepted_action;
1999 // object persists until we get an xdnd_finish message
2000}
2001
2002void QDragManager::updatePixmap()
2003{
2004 if (xdnd_data.deco) {
2005 QPixmap pm;
2006 QPoint pm_hot(default_pm_hotx,default_pm_hoty);
2007 if (object) {
2008 pm = dragPrivate()->pixmap;
2009 if (!pm.isNull())
2010 pm_hot = dragPrivate()->hotspot;
2011 }
2012 if (pm.isNull()) {
2013 if (!defaultPm)
2014 defaultPm = new QPixmap(default_pm);
2015 pm = *defaultPm;
2016 }
2017 xdnd_data.deco->pm_hot = pm_hot;
2018 xdnd_data.deco->setPixmap(pm);
2019 xdnd_data.deco->move(QCursor::pos()-pm_hot);
2020 xdnd_data.deco->show();
2021 }
2022}
2023
2024QVariant QDropData::retrieveData_sys(const QString &mimetype, QVariant::Type requestedType) const
2025{
2026 QByteArray mime = mimetype.toLatin1();
2027 QVariant data = X11->motifdnd_active
2028 ? X11->motifdndObtainData(mime)
2029 : xdndObtainData(mime, requestedType);
2030 return data;
2031}
2032
2033bool QDropData::hasFormat_sys(const QString &format) const
2034{
2035 return formats().contains(format);
2036}
2037
2038QStringList QDropData::formats_sys() const
2039{
2040 QStringList formats;
2041 if (X11->motifdnd_active) {
2042 int i = 0;
2043 QByteArray fmt;
2044 while (!(fmt = X11->motifdndFormat(i)).isEmpty()) {
2045 formats.append(QLatin1String(fmt));
2046 ++i;
2047 }
2048 } else {
2049 int i = 0;
2050 while ((qt_xdnd_types[i])) {
2051 QStringList formatsForAtom = X11->xdndMimeFormatsForAtom(qt_xdnd_types[i]);
2052 for (int j = 0; j < formatsForAtom.size(); ++j) {
2053 if (!formats.contains(formatsForAtom.at(j)))
2054 formats.append(formatsForAtom.at(j));
2055 }
2056 ++i;
2057 }
2058 }
2059 return formats;
2060}
2061
2062QT_END_NAMESPACE
2063
2064#endif // QT_NO_DRAGANDDROP
Note: See TracBrowser for help on using the repository browser.