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

Last change on this file was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

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