00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include "qplatformdefs.h"
00025
00026 #include "qapplication.h"
00027
00028 #ifndef QT_NO_DRAGANDDROP
00029
00030 #include "qwidget.h"
00031 #include "qpixmap.h"
00032 #include "qbitmap.h"
00033 #include "qdesktopwidget.h"
00034 #include "qevent.h"
00035 #include "qdatetime.h"
00036 #include "qiodevice.h"
00037 #include "qpointer.h"
00038 #include "qcursor.h"
00039 #include "qvariant.h"
00040 #include "qvector.h"
00041 #include "qurl.h"
00042 #include "qdebug.h"
00043 #include "qimagewriter.h"
00044 #include "qbuffer.h"
00045
00046 #include "qdnd_p.h"
00047 #include "qt_x11_p.h"
00048 #include "qx11info_x11.h"
00049
00050 #include "qwidget_p.h"
00051 #include "qcursor_p.h"
00052
00053
00054 #ifdef DND_DEBUG
00055 #define DEBUG qDebug
00056 #else
00057 #define DEBUG if(0) qDebug
00058 #endif
00059
00060 #ifdef DND_DEBUG
00061 #define DNDDEBUG qDebug()
00062 #else
00063 #define DNDDEBUG if(0) qDebug()
00064 #endif
00065
00066 static int findXdndDropTransactionByWindow(Window window)
00067 {
00068 int at = -1;
00069 for (int i = 0; i < X11->dndDropTransactions.count(); ++i) {
00070 const QXdndDropTransaction &t = X11->dndDropTransactions.at(i);
00071 if (t.target == window || t.proxy_target == window) {
00072 at = i;
00073 break;
00074 }
00075 }
00076 return at;
00077 }
00078
00079 static int findXdndDropTransactionByTime(Time timestamp)
00080 {
00081 int at = -1;
00082 for (int i = 0; i < X11->dndDropTransactions.count(); ++i) {
00083 const QXdndDropTransaction &t = X11->dndDropTransactions.at(i);
00084 if (t.timestamp == timestamp) {
00085 at = i;
00086 break;
00087 }
00088 }
00089 return at;
00090 }
00091
00092
00093 static int transaction_expiry_timer = -1;
00094 enum { XdndDropTransactionTimeout = 5000 };
00095
00096 static void restartXdndDropExpiryTimer()
00097 {
00098 if (transaction_expiry_timer != -1)
00099 QDragManager::self()->killTimer(transaction_expiry_timer);
00100 transaction_expiry_timer = QDragManager::self()->startTimer(XdndDropTransactionTimeout);
00101 }
00102
00103
00104
00105 static Window findXdndAwareParent(Window window)
00106 {
00107 Window target = 0;
00108 forever {
00109
00110 Atom type = 0;
00111 int f;
00112 unsigned long n, a;
00113 unsigned char *data = 0;
00114 if (XGetWindowProperty(X11->display, window, ATOM(XdndAware), 0, 0, False,
00115 AnyPropertyType, &type, &f,&n,&a,&data) == Success) {
00116 if (data)
00117 XFree(data);
00118 if (type) {
00119 target = window;
00120 break;
00121 }
00122 }
00123
00124
00125 Window root;
00126 Window parent;
00127 Window *children;
00128 uint unused;
00129 if (!XQueryTree(X11->display, window, &root, &parent, &children, &unused))
00130 break;
00131 if (children)
00132 XFree(children);
00133 if (window == root)
00134 break;
00135 window = parent;
00136 }
00137 return target;
00138 }
00139
00140
00141
00142
00143
00144
00145 static void handle_xdnd_position(QWidget *, const XEvent *, bool);
00146 static void handle_xdnd_status(QWidget * w, const XEvent * xe, bool );
00147
00148 const int xdnd_version = 5;
00149
00150 static Qt::DropAction xdndaction_to_qtaction(Atom atom)
00151 {
00152 if (atom == ATOM(XdndActionCopy) || atom == 0)
00153 return Qt::CopyAction;
00154 if (atom == ATOM(XdndActionLink))
00155 return Qt::LinkAction;
00156 if (atom == ATOM(XdndActionMove))
00157 return Qt::MoveAction;
00158 return Qt::CopyAction;
00159 }
00160
00161 static int qtaction_to_xdndaction(Qt::DropAction a)
00162 {
00163 switch (a) {
00164 case Qt::CopyAction:
00165 return ATOM(XdndActionCopy);
00166 case Qt::LinkAction:
00167 return ATOM(XdndActionLink);
00168 case Qt::MoveAction:
00169 case Qt::TargetMoveAction:
00170 return ATOM(XdndActionMove);
00171 case Qt::IgnoreAction:
00172 return XNone;
00173 default:
00174 return ATOM(XdndActionCopy);
00175 }
00176 }
00177
00178
00179 static void qt_xdnd_cleanup();
00180
00181 static void qt_xdnd_send_leave();
00182
00183
00184
00185 static Atom qt_xdnd_dragsource_xid = 0;
00186
00187
00188 const int qt_xdnd_max_type = 100;
00189 static Atom qt_xdnd_types[qt_xdnd_max_type + 1];
00190
00191
00192 static int heartbeat = -1;
00193
00194 static QRect qt_xdnd_source_sameanswer;
00195
00196 static Window qt_xdnd_current_target;
00197
00198 static Window qt_xdnd_current_proxy_target;
00199 static Time qt_xdnd_source_current_time;
00200
00201
00202 static QPointer<QWidget> qt_xdnd_current_widget;
00203 static QPoint qt_xdnd_current_position;
00204
00205 static Time qt_xdnd_target_current_time;
00206
00207 static int qt_xdnd_current_screen = -1;
00208
00209 bool qt_xdnd_dragging = false;
00210
00211 static bool waiting_for_status = false;
00212
00213
00214 static Qt::DropAction last_target_accepted_action = Qt::IgnoreAction;
00215
00216
00217 static Qt::DropAction global_accepted_action = Qt::CopyAction;
00218 static Qt::DropActions possible_actions = Qt::IgnoreAction;
00219
00220
00221 static QWidget* current_embedding_widget = 0;
00222 static XEvent last_enter_event;
00223
00224
00225 static QCursor *noDropCursor = 0;
00226 static QCursor *moveCursor = 0;
00227 static QCursor *copyCursor = 0;
00228 static QCursor *linkCursor = 0;
00229
00230 static QPixmap *defaultPm = 0;
00231
00232 static const int default_pm_hotx = -2;
00233 static const int default_pm_hoty = -16;
00234 static const char* const default_pm[] = {
00235 "13 9 3 1",
00236 ". c None",
00237 " c #000000",
00238 "X c #FFFFFF",
00239 "X X X X X X X",
00240 " X X X X X X ",
00241 "X ......... X",
00242 " X.........X ",
00243 "X ......... X",
00244 " X.........X ",
00245 "X ......... X",
00246 " X X X X X X ",
00247 "X X X X X X X"
00248 };
00249
00250 class QShapedPixmapWidget : public QWidget {
00251
00252 public:
00253 QShapedPixmapWidget(int screen = -1) :
00254 QWidget(QApplication::desktop()->screen(screen),
00255 Qt::Tool | Qt::FramelessWindowHint | Qt::X11BypassWindowManagerHint)
00256 {
00257 }
00258
00259 void setPixmap(const QPixmap &pm)
00260 {
00261 QBitmap mask = pm.mask();
00262 if (!mask.isNull()) {
00263 setMask(mask);
00264 } else {
00265 clearMask();
00266 }
00267 resize(pm.width(),pm.height());
00268 QPalette p = palette();
00269 p.setBrush(backgroundRole(), QBrush(pm));
00270 setPalette(p);
00271 update();
00272 }
00273 QPoint pm_hot;
00274 };
00275
00276 struct XdndData {
00277 QShapedPixmapWidget *deco;
00278 QWidget* desktop_proxy;
00279 };
00280
00281 static XdndData xdnd_data = { 0, 0 };
00282
00283 class QExtraWidget : public QWidget
00284 {
00285 Q_DECLARE_PRIVATE(QWidget)
00286 public:
00287 inline QWExtra* extraData();
00288 inline QTLWExtra* topData();
00289 };
00290
00291 inline QWExtra* QExtraWidget::extraData() { return d_func()->extraData(); }
00292 inline QTLWExtra* QExtraWidget::topData() { return d_func()->topData(); }
00293
00294
00295 static WId xdndProxy(WId w)
00296 {
00297 Atom type = XNone;
00298 int f;
00299 unsigned long n, a;
00300 WId *proxy_id_ptr;
00301 XGetWindowProperty(X11->display, w, ATOM(XdndProxy), 0, 1, False,
00302 XA_WINDOW, &type, &f,&n,&a,(uchar**)&proxy_id_ptr);
00303 WId proxy_id = 0;
00304 if (type == XA_WINDOW && proxy_id_ptr) {
00305 proxy_id = *proxy_id_ptr;
00306 XFree(proxy_id_ptr);
00307 proxy_id_ptr = 0;
00308
00309 X11->ignoreBadwindow();
00310 XGetWindowProperty(X11->display, proxy_id, ATOM(XdndProxy), 0, 1, False,
00311 XA_WINDOW, &type, &f,&n,&a,(uchar**)&proxy_id_ptr);
00312 if (X11->badwindow() || type != XA_WINDOW || !proxy_id_ptr || *proxy_id_ptr != proxy_id)
00313
00314 proxy_id = 0;
00315 }
00316 if (proxy_id_ptr)
00317 XFree(proxy_id_ptr);
00318 return proxy_id;
00319 }
00320
00321 static bool xdndEnable(QWidget* w, bool on)
00322 {
00323 DNDDEBUG << "xdndEnable" << w << on;
00324 if (on) {
00325 QWidget * xdnd_widget = 0;
00326 if ((w->windowType() == Qt::Desktop)) {
00327 if (xdnd_data.desktop_proxy)
00328 return false;
00329
00330
00331 XGrabServer(X11->display);
00332 Q_ASSERT(w->testAttribute(Qt::WA_WState_Created));
00333 WId proxy_id = xdndProxy(w->internalWinId());
00334
00335 if (!proxy_id) {
00336 xdnd_widget = xdnd_data.desktop_proxy = new QWidget;
00337 proxy_id = xdnd_data.desktop_proxy->internalWinId();
00338 XChangeProperty (X11->display, w->internalWinId(), ATOM(XdndProxy),
00339 XA_WINDOW, 32, PropModeReplace, (unsigned char *)&proxy_id, 1);
00340 XChangeProperty (X11->display, proxy_id, ATOM(XdndProxy),
00341 XA_WINDOW, 32, PropModeReplace, (unsigned char *)&proxy_id, 1);
00342 }
00343
00344 XUngrabServer(X11->display);
00345 } else {
00346 xdnd_widget = w->window();
00347 }
00348 if (xdnd_widget) {
00349 DNDDEBUG << "setting XdndAware for" << xdnd_widget << xdnd_widget->internalWinId();
00350 Atom atm = (Atom)xdnd_version;
00351 Q_ASSERT(xdnd_widget->testAttribute(Qt::WA_WState_Created));
00352 XChangeProperty(X11->display, xdnd_widget->internalWinId(), ATOM(XdndAware),
00353 XA_ATOM, 32, PropModeReplace, (unsigned char *)&atm, 1);
00354 return true;
00355 } else {
00356 return false;
00357 }
00358 } else {
00359 if ((w->windowType() == Qt::Desktop)) {
00360 XDeleteProperty(X11->display, w->internalWinId(), ATOM(XdndProxy));
00361 delete xdnd_data.desktop_proxy;
00362 xdnd_data.desktop_proxy = 0;
00363 } else {
00364 DNDDEBUG << "not deleting XDndAware";
00365 }
00366 return true;
00367 }
00368 }
00369
00370 QByteArray QX11Data::xdndAtomToString(Atom a)
00371 {
00372 if (!a) return 0;
00373
00374 if (a == XA_STRING || a == ATOM(UTF8_STRING)) {
00375 return "text/plain";
00376 }
00377 char *atom = XGetAtomName(display, a);
00378 QByteArray result = atom;
00379 XFree(atom);
00380 return result;
00381 }
00382
00383 Atom QX11Data::xdndStringToAtom(const char *mimeType)
00384 {
00385 if (!mimeType || !*mimeType)
00386 return 0;
00387 return XInternAtom(display, mimeType, False);
00388 }
00389
00390
00391 QString QX11Data::xdndMimeAtomToString(Atom a)
00392 {
00393 QString atomName;
00394 if (a) {
00395 char *atom = XGetAtomName(display, a);
00396 atomName = QString::fromLatin1(atom);
00397 XFree(atom);
00398 }
00399 return atomName;
00400 }
00401
00402
00403 Atom QX11Data::xdndMimeStringToAtom(const QString &mimeType)
00404 {
00405 if (mimeType.isEmpty())
00406 return 0;
00407 return XInternAtom(display, mimeType.toLatin1().constData(), False);
00408 }
00409
00410
00411 QStringList QX11Data::xdndMimeFormatsForAtom(Atom a)
00412 {
00413 QStringList formats;
00414 if (a) {
00415 QString atomName = xdndMimeAtomToString(a);
00416 formats.append(atomName);
00417
00418
00419 if (a == ATOM(UTF8_STRING) || a == XA_STRING
00420 || a == ATOM(TEXT) || a == ATOM(COMPOUND_TEXT))
00421 formats.append(QLatin1String("text/plain"));
00422
00423
00424 if (atomName == QLatin1String("text/x-moz-url"))
00425 formats.append(QLatin1String("text/uri-list"));
00426
00427
00428 if (a == XA_PIXMAP)
00429 formats.append(QLatin1String("image/ppm"));
00430 }
00431 return formats;
00432 }
00433
00434
00435 bool QX11Data::xdndMimeDataForAtom(Atom a, QMimeData *mimeData, QByteArray *data, Atom *atomFormat, int *dataFormat)
00436 {
00437 bool ret = false;
00438 *atomFormat = a;
00439 *dataFormat = 8;
00440 QString atomName = xdndMimeAtomToString(a);
00441 if (QInternalMimeData::hasFormatHelper(atomName, mimeData)) {
00442 *data = QInternalMimeData::renderDataHelper(atomName, mimeData);
00443 if (atomName == QLatin1String("application/x-color"))
00444 *dataFormat = 16;
00445 ret = true;
00446 } else {
00447 if ((a == ATOM(UTF8_STRING) || a == XA_STRING
00448 || a == ATOM(TEXT) || a == ATOM(COMPOUND_TEXT))
00449 && QInternalMimeData::hasFormatHelper(QLatin1String("text/plain"), mimeData)) {
00450 if (a == ATOM(UTF8_STRING)){
00451 *data = QInternalMimeData::renderDataHelper(QLatin1String("text/plain"), mimeData);
00452 ret = true;
00453 } else if (a == XA_STRING) {
00454 *data = QString::fromUtf8(QInternalMimeData::renderDataHelper(
00455 QLatin1String("text/plain"), mimeData)).toLocal8Bit();
00456 ret = true;
00457 } else if (a == ATOM(TEXT) || a == ATOM(COMPOUND_TEXT)) {
00458
00459
00460 QByteArray strData = QString::fromUtf8(QInternalMimeData::renderDataHelper(
00461 QLatin1String("text/plain"), mimeData)).toLocal8Bit();
00462 char *list[] = { strData.data(), NULL };
00463
00464 XICCEncodingStyle style = (a == ATOM(COMPOUND_TEXT))
00465 ? XCompoundTextStyle : XStdICCTextStyle;
00466 XTextProperty textprop;
00467 if (list[0] != NULL
00468 && XmbTextListToTextProperty(X11->display, list, 1, style,
00469 &textprop) == Success) {
00470 *atomFormat = textprop.encoding;
00471 *dataFormat = textprop.format;
00472 *data = QByteArray((const char *) textprop.value, textprop.nitems * textprop.format / 8);
00473
00474 DEBUG(" textprop type %lx\n"
00475 " textprop name '%s'\n"
00476 " format %d\n"
00477 " %ld items\n"
00478 " %d bytes\n",
00479 textprop.encoding,
00480 X11->xdndMimeAtomToString(textprop.encoding).toLatin1().data(),
00481 textprop.format, textprop.nitems, data->size());
00482
00483 XFree(textprop.value);
00484 }
00485 }
00486 } else if (atomName == QLatin1String("text/x-moz-url") &&
00487 QInternalMimeData::hasFormatHelper(QLatin1String("text/uri-list"), mimeData)) {
00488 QByteArray uri = QInternalMimeData::renderDataHelper(
00489 QLatin1String("text/uri-list"), mimeData).split('\n').first();
00490 QString mozUri = QString::fromLatin1(uri, uri.size());
00491 mozUri += QLatin1Char('\n');
00492 *data = QByteArray(reinterpret_cast<const char *>(mozUri.utf16()), mozUri.length() * 2);
00493 ret = true;
00494 } else if ((a == XA_PIXMAP || a == XA_BITMAP) && mimeData->hasImage()) {
00495 QPixmap pm = qvariant_cast<QPixmap>(mimeData->imageData());
00496 if (a == XA_BITMAP && pm.depth() != 1) {
00497 QImage img = pm.toImage();
00498 img = img.convertToFormat(QImage::Format_MonoLSB);
00499 pm = QPixmap::fromImage(img);
00500 }
00501 QDragManager *dm = QDragManager::self();
00502 if (dm) {
00503 Pixmap handle = pm.handle();
00504 *data = QByteArray((const char *) &handle, sizeof(Pixmap));
00505 dm->xdndMimeTransferedPixmap[dm->xdndMimeTransferedPixmapIndex] = pm;
00506 dm->xdndMimeTransferedPixmapIndex =
00507 (dm->xdndMimeTransferedPixmapIndex + 1) % 2;
00508 }
00509 }
00510 }
00511 return data;
00512 }
00513
00514
00515 QList<Atom> QX11Data::xdndMimeAtomsForFormat(const QString &format)
00516 {
00517 QList<Atom> atoms;
00518 atoms.append(xdndMimeStringToAtom(format));
00519
00520
00521 if (format == QLatin1String("text/plain")) {
00522 atoms.append(ATOM(UTF8_STRING));
00523 atoms.append(XA_STRING);
00524 atoms.append(ATOM(TEXT));
00525 atoms.append(ATOM(COMPOUND_TEXT));
00526 }
00527
00528
00529 if (format == QLatin1String("text/uri-list")) {
00530 atoms.append(xdndMimeStringToAtom(QLatin1String("text/x-moz-url")));
00531 }
00532
00533
00534 if (format == QLatin1String("image/ppm"))
00535 atoms.append(XA_PIXMAP);
00536 if (format == QLatin1String("image/pbm"))
00537 atoms.append(XA_BITMAP);
00538
00539 return atoms;
00540 }
00541
00542
00543 QByteArray QX11Data::xdndMimeConvertToFormat(Atom a, const QByteArray &data, const QString &format)
00544 {
00545 QString atomName = xdndMimeAtomToString(a);
00546 if (atomName == format)
00547 return data;
00548
00549
00550 if (format == QLatin1String("text/plain")) {
00551 if (a == ATOM(UTF8_STRING))
00552 return data;
00553 if (a == XA_STRING)
00554 return QString::fromLatin1(data).toUtf8();
00555 if (a == ATOM(TEXT) || a == ATOM(COMPOUND_TEXT))
00556
00557 return QString::fromLocal8Bit(data, data.size()).toUtf8();
00558 }
00559
00560
00561 if (format == QLatin1String("text/uri-list")) {
00562 if (atomName == QLatin1String("text/x-moz-url")) {
00563
00564
00565
00566
00567 if (data.size() > 1 && data.at(1) == 0)
00568 return QString::fromUtf16(reinterpret_cast<const ushort *>(data.constData()),
00569 data.size() / 2).split(QLatin1Char('\n')).first().toLatin1();
00570 }
00571 }
00572
00573
00574 if (format == QLatin1String("image/ppm")) {
00575 if (a == XA_PIXMAP && data.size() == sizeof(Pixmap)) {
00576 Pixmap xpm = *((Pixmap*)data.data());
00577 Display *dpy = display;
00578 Window r;
00579 int x,y;
00580 uint w,h,bw,d;
00581 if (!xpm)
00582 return QByteArray();
00583 XGetGeometry(dpy,xpm, &r,&x,&y,&w,&h,&bw,&d);
00584 QImageWriter imageWriter;
00585 GC gc = XCreateGC(dpy, xpm, 0, 0);
00586 QImage imageToWrite;
00587 if (d == 1) {
00588 QBitmap qbm(w,h);
00589 XCopyArea(dpy,xpm,qbm.handle(),gc,0,0,w,h,0,0);
00590 imageWriter.setFormat("PBMRAW");
00591 imageToWrite = qbm.toImage();
00592 } else {
00593 QPixmap qpm(w,h);
00594 XCopyArea(dpy,xpm,qpm.handle(),gc,0,0,w,h,0,0);
00595 imageWriter.setFormat("PPMRAW");
00596 imageToWrite = qpm.toImage();
00597 }
00598 XFreeGC(dpy,gc);
00599 QBuffer buf;
00600 buf.open(QIODevice::WriteOnly);
00601 imageWriter.setDevice(&buf);
00602 imageWriter.write(imageToWrite);
00603 return buf.buffer();
00604 }
00605 }
00606 return QByteArray();
00607 }
00608
00609
00610 Atom QX11Data::xdndMimeAtomForFormat(const QString &format, const QList<Atom> &atoms)
00611 {
00612 Atom a = xdndMimeStringToAtom(format);
00613 if (a && atoms.contains(a))
00614 return a;
00615
00616
00617 if (format == QLatin1String("text/plain")) {
00618 if (atoms.contains(ATOM(UTF8_STRING)))
00619 return ATOM(UTF8_STRING);
00620 if (atoms.contains(ATOM(COMPOUND_TEXT)))
00621 return XA_STRING;
00622 if (atoms.contains(ATOM(TEXT)))
00623 return XA_STRING;
00624 if (atoms.contains(XA_STRING))
00625 return XA_STRING;
00626 }
00627
00628
00629 if (format == QLatin1String("text/uri-list")) {
00630 Atom a = xdndMimeStringToAtom(QLatin1String("text/x-moz-url"));
00631 if (a && atoms.contains(a))
00632 return a;
00633 }
00634
00635
00636 if (format == QLatin1String("image/ppm")) {
00637 if (atoms.contains(XA_PIXMAP))
00638 return XA_PIXMAP;
00639 }
00640
00641 return 0;
00642 }
00643
00644 void QX11Data::xdndSetup() {
00645 QCursorData::initialize();
00646 qAddPostRoutine(qt_xdnd_cleanup);
00647 }
00648
00649
00650 void qt_xdnd_cleanup()
00651 {
00652 delete noDropCursor;
00653 noDropCursor = 0;
00654 delete copyCursor;
00655 copyCursor = 0;
00656 delete moveCursor;
00657 moveCursor = 0;
00658 delete linkCursor;
00659 linkCursor = 0;
00660 delete defaultPm;
00661 defaultPm = 0;
00662 delete xdnd_data.desktop_proxy;
00663 xdnd_data.desktop_proxy = 0;
00664 delete xdnd_data.deco;
00665 xdnd_data.deco = 0;
00666 }
00667
00668
00669 static QWidget *find_child(QWidget *tlw, QPoint & p)
00670 {
00671 QWidget *widget = tlw;
00672
00673 p = widget->mapFromGlobal(p);
00674 bool done = false;
00675 while (!done) {
00676 done = true;
00677 if (((QExtraWidget*)widget)->extraData() &&
00678 ((QExtraWidget*)widget)->extraData()->xDndProxy != 0)
00679 break;
00680 QObjectList children = widget->children();
00681 if (!children.isEmpty()) {
00682 for(int i = children.size(); i > 0;) {
00683 --i;
00684 QWidget *w = qobject_cast<QWidget *>(children.at(i));
00685 if (!w)
00686 continue;
00687 if (w->isVisible() &&
00688 w->geometry().contains(p) &&
00689 !w->isWindow()) {
00690 widget = w;
00691 done = false;
00692 p = widget->mapFromParent(p);
00693 break;
00694 }
00695 }
00696 }
00697 }
00698 return widget;
00699 }
00700
00701
00702 static bool checkEmbedded(QWidget* w, const XEvent* xe)
00703 {
00704 if (!w)
00705 return false;
00706
00707 if (current_embedding_widget != 0 && current_embedding_widget != w) {
00708 qt_xdnd_current_target = ((QExtraWidget*)current_embedding_widget)->extraData()->xDndProxy;
00709 qt_xdnd_current_proxy_target = qt_xdnd_current_target;
00710 qt_xdnd_send_leave();
00711 qt_xdnd_current_target = 0;
00712 qt_xdnd_current_proxy_target = 0;
00713 current_embedding_widget = 0;
00714 }
00715
00716 QWExtra* extra = ((QExtraWidget*)w)->extraData();
00717 if (extra && extra->xDndProxy != 0) {
00718
00719 if (current_embedding_widget != w) {
00720
00721 last_enter_event.xany.window = extra->xDndProxy;
00722 XSendEvent(X11->display, extra->xDndProxy, False, NoEventMask, &last_enter_event);
00723 current_embedding_widget = w;
00724 }
00725
00726 ((XEvent*)xe)->xany.window = extra->xDndProxy;
00727 XSendEvent(X11->display, extra->xDndProxy, False, NoEventMask, (XEvent*)xe);
00728 if (qt_xdnd_current_widget != w) {
00729 qt_xdnd_current_widget = w;
00730 }
00731 return true;
00732 }
00733 current_embedding_widget = 0;
00734 return false;
00735 }
00736
00737 void QX11Data::xdndHandleEnter(QWidget *, const XEvent * xe, bool )
00738 {
00739 motifdnd_active = false;
00740
00741 last_enter_event.xclient = xe->xclient;
00742
00743 const long *l = xe->xclient.data.l;
00744 int version = (int)(((unsigned long)(l[1])) >> 24);
00745
00746 if (version > xdnd_version)
00747 return;
00748
00749 qt_xdnd_dragsource_xid = l[0];
00750
00751 int j = 0;
00752 if (l[1] & 1) {
00753
00754 Atom type = XNone;
00755 int f;
00756 unsigned long n, a;
00757 Atom *data;
00758 XGetWindowProperty(X11->display, qt_xdnd_dragsource_xid, ATOM(XdndTypelist), 0,
00759 qt_xdnd_max_type, False, XA_ATOM, &type, &f,&n,&a,(uchar**)&data);
00760 for (; j<qt_xdnd_max_type && j < (int)n; j++) {
00761 qt_xdnd_types[j] = data[j];
00762 }
00763 if (data)
00764 XFree((uchar*)data);
00765 } else {
00766
00767 int i;
00768 for(i=2; i < 5; i++) {
00769 qt_xdnd_types[j++] = l[i];
00770 }
00771 }
00772 qt_xdnd_types[j] = 0;
00773 }
00774
00775 static void handle_xdnd_position(QWidget *w, const XEvent * xe, bool passive)
00776 {
00777 const unsigned long *l = (const unsigned long *)xe->xclient.data.l;
00778
00779 QPoint p((l[2] & 0xffff0000) >> 16, l[2] & 0x0000ffff);
00780 QWidget * c = find_child(w, p);
00781
00782 if (!passive && checkEmbedded(c, xe))
00783 return;
00784
00785 if (!c || !c->acceptDrops() && (c->windowType() == Qt::Desktop))
00786 return;
00787
00788 if (l[0] != qt_xdnd_dragsource_xid) {
00789 DEBUG("xdnd drag position from unexpected source (%08lx not %08lx)", l[0], qt_xdnd_dragsource_xid);
00790 return;
00791 }
00792
00793 if (l[3] != 0) {
00794
00795 qt_xdnd_target_current_time = X11->userTime = l[3];
00796 }
00797
00798 QDragManager *manager = QDragManager::self();
00799 QMimeData *dropData = manager->object ? manager->dragPrivate()->data : manager->dropData;
00800
00801 XClientMessageEvent response;
00802 response.type = ClientMessage;
00803 response.window = qt_xdnd_dragsource_xid;
00804 response.format = 32;
00805 response.message_type = ATOM(XdndStatus);
00806 response.data.l[0] = w->internalWinId();
00807 response.data.l[1] = 0;
00808 response.data.l[2] = 0;
00809 response.data.l[3] = 0;
00810 response.data.l[4] = 0;
00811
00812 if (!passive) {
00813 while (c && !c->acceptDrops() && !c->isWindow()) {
00814 p = c->mapToParent(p);
00815 c = c->parentWidget();
00816 }
00817 QWidget *target_widget = c && c->acceptDrops() ? c : 0;
00818
00819 QRect answerRect(c->mapToGlobal(p), QSize(1,1));
00820
00821 if (manager->object) {
00822 possible_actions = manager->dragPrivate()->possible_actions;
00823 } else {
00824 possible_actions = Qt::DropActions(xdndaction_to_qtaction(l[4]));
00825
00826 }
00827 QDragMoveEvent me(p, possible_actions, dropData, QApplication::mouseButtons(), QApplication::keyboardModifiers());
00828
00829 Qt::DropAction accepted_action = Qt::IgnoreAction;
00830
00831
00832 if (target_widget != qt_xdnd_current_widget) {
00833 if (qt_xdnd_current_widget) {
00834 QDragLeaveEvent e;
00835 QApplication::sendEvent(qt_xdnd_current_widget, &e);
00836 }
00837 if (qt_xdnd_current_widget != target_widget) {
00838 qt_xdnd_current_widget = target_widget;
00839 }
00840 if (target_widget) {
00841 qt_xdnd_current_position = p;
00842
00843 last_target_accepted_action = Qt::IgnoreAction;
00844 QDragEnterEvent de(p, possible_actions, dropData, QApplication::mouseButtons(), QApplication::keyboardModifiers());
00845 QApplication::sendEvent(target_widget, &de);
00846 if (de.isAccepted() && de.dropAction() != Qt::IgnoreAction)
00847 last_target_accepted_action = de.dropAction();
00848 }
00849 }
00850
00851 DEBUG() << "qt_handle_xdnd_position action=" << X11->xdndAtomToString(l[4]);
00852 if (!target_widget) {
00853 answerRect = QRect(p, QSize(1, 1));
00854 } else {
00855 qt_xdnd_current_widget = c;
00856 qt_xdnd_current_position = p;
00857
00858 if (last_target_accepted_action != Qt::IgnoreAction) {
00859 me.setDropAction(last_target_accepted_action);
00860 me.accept();
00861 }
00862 QApplication::sendEvent(c, &me);
00863 if (me.isAccepted()) {
00864 response.data.l[1] = 1;
00865 accepted_action = me.dropAction();
00866 last_target_accepted_action = accepted_action;
00867 } else {
00868 response.data.l[0] = 0;
00869 last_target_accepted_action = Qt::IgnoreAction;
00870 }
00871 answerRect = me.answerRect().intersected(c->rect());
00872 }
00873 answerRect = QRect(c->mapToGlobal(answerRect.topLeft()), answerRect.size());
00874
00875 if (answerRect.left() < 0)
00876 answerRect.setLeft(0);
00877 if (answerRect.right() > 4096)
00878 answerRect.setRight(4096);
00879 if (answerRect.top() < 0)
00880 answerRect.setTop(0);
00881 if (answerRect.bottom() > 4096)
00882 answerRect.setBottom(4096);
00883 if (answerRect.width() < 0)
00884 answerRect.setWidth(0);
00885 if (answerRect.height() < 0)
00886 answerRect.setHeight(0);
00887
00888 response.data.l[2] = (answerRect.x() << 16) + answerRect.y();
00889 response.data.l[3] = (answerRect.width() << 16) + answerRect.height();
00890 response.data.l[4] = qtaction_to_xdndaction(accepted_action);
00891 }
00892
00893
00894 qt_xdnd_target_current_time = CurrentTime;
00895
00896 QWidget * source = QWidget::find(qt_xdnd_dragsource_xid);
00897 if (source && (source->windowType() == Qt::Desktop) && !source->acceptDrops())
00898 source = 0;
00899
00900 DEBUG() << "sending XdndStatus";
00901 if (source)
00902 handle_xdnd_status(source, (const XEvent *)&response, passive);
00903 else
00904 XSendEvent(X11->display, qt_xdnd_dragsource_xid, False, NoEventMask, (XEvent*)&response);
00905 }
00906
00907 static Bool xdnd_position_scanner(Display *, XEvent *event, XPointer)
00908 {
00909 if (event->type != ClientMessage)
00910 return false;
00911 XClientMessageEvent *ev = &event->xclient;
00912
00913 if (ev->message_type == ATOM(XdndPosition))
00914 return true;
00915
00916 return false;
00917 }
00918
00919 void QX11Data::xdndHandlePosition(QWidget * w, const XEvent * xe, bool passive)
00920 {
00921 DEBUG("xdndHandlePosition");
00922 while (XCheckIfEvent(X11->display, (XEvent *)xe, xdnd_position_scanner, 0))
00923 ;
00924
00925 handle_xdnd_position(w, xe, passive);
00926 }
00927
00928
00929 static void handle_xdnd_status(QWidget *, const XEvent * xe, bool)
00930 {
00931 const unsigned long *l = (const unsigned long *)xe->xclient.data.l;
00932
00933 if (l[0] && l[0] != qt_xdnd_current_proxy_target)
00934 return;
00935 Qt::DropAction newAction = (l[1] & 0x1) ? xdndaction_to_qtaction(l[4]) : Qt::IgnoreAction;
00936
00937 if ((int)(l[1] & 2) == 0) {
00938 QPoint p((l[2] & 0xffff0000) >> 16, l[2] & 0x0000ffff);
00939 QSize s((l[3] & 0xffff0000) >> 16, l[3] & 0x0000ffff);
00940 qt_xdnd_source_sameanswer = QRect(p, s);
00941 } else {
00942 qt_xdnd_source_sameanswer = QRect();
00943 }
00944 QDragManager *manager = QDragManager::self();
00945 manager->willDrop = (l[1] & 0x1);
00946 if (global_accepted_action != newAction)
00947 manager->emitActionChanged(newAction);
00948 global_accepted_action = newAction;
00949 manager->updateCursor();
00950 waiting_for_status = false;
00951 }
00952
00953 static Bool xdnd_status_scanner(Display *, XEvent *event, XPointer)
00954 {
00955 if (event->type != ClientMessage)
00956 return false;
00957 XClientMessageEvent *ev = &event->xclient;
00958
00959 if (ev->message_type == ATOM(XdndStatus))
00960 return true;
00961
00962 return false;
00963 }
00964
00965 void QX11Data::xdndHandleStatus(QWidget * w, const XEvent * xe, bool passive)
00966 {
00967 DEBUG("xdndHandleStatus");
00968 while (XCheckIfEvent(X11->display, (XEvent *)xe, xdnd_status_scanner, 0))
00969 ;
00970
00971 handle_xdnd_status(w, xe, passive);
00972 DEBUG("xdndHandleStatus end");
00973 }
00974
00975 void QX11Data::xdndHandleLeave(QWidget *w, const XEvent * xe, bool )
00976 {
00977 DEBUG("xdnd leave");
00978 if (!qt_xdnd_current_widget ||
00979 w->window() != qt_xdnd_current_widget->window()) {
00980 return;
00981 }
00982
00983 if (checkEmbedded(current_embedding_widget, xe)) {
00984 current_embedding_widget = 0;
00985 qt_xdnd_current_widget = 0;
00986 return;
00987 }
00988
00989 const unsigned long *l = (const unsigned long *)xe->xclient.data.l;
00990
00991 QDragLeaveEvent e;
00992 QApplication::sendEvent(qt_xdnd_current_widget, &e);
00993
00994 if (l[0] != qt_xdnd_dragsource_xid) {
00995
00996 DEBUG("xdnd drag leave from unexpected source (%08lx not %08lx", l[0], qt_xdnd_dragsource_xid);
00997 qt_xdnd_current_widget = 0;
00998 return;
00999 }
01000
01001 qt_xdnd_dragsource_xid = 0;
01002 qt_xdnd_types[0] = 0;
01003 qt_xdnd_current_widget = 0;
01004 }
01005
01006
01007 void qt_xdnd_send_leave()
01008 {
01009 if (!qt_xdnd_current_target)
01010 return;
01011
01012 XClientMessageEvent leave;
01013 leave.type = ClientMessage;
01014 leave.window = qt_xdnd_current_target;
01015 leave.format = 32;
01016 leave.message_type = ATOM(XdndLeave);
01017 leave.data.l[0] = qt_xdnd_dragsource_xid;
01018 leave.data.l[1] = 0;
01019 leave.data.l[2] = 0;
01020 leave.data.l[3] = 0;
01021 leave.data.l[4] = 0;
01022
01023 QWidget * w = QWidget::find(qt_xdnd_current_proxy_target);
01024
01025 if (w && (w->windowType() == Qt::Desktop) && !w->acceptDrops())
01026 w = 0;
01027
01028 if (w)
01029 X11->xdndHandleLeave(w, (const XEvent *)&leave, false);
01030 else
01031 XSendEvent(X11->display, qt_xdnd_current_proxy_target, False,
01032 NoEventMask, (XEvent*)&leave);
01033
01034 QDragManager *manager = QDragManager::self();
01035 manager->willDrop = false;
01036 if (global_accepted_action != Qt::IgnoreAction)
01037 manager->emitActionChanged(Qt::IgnoreAction);
01038 global_accepted_action = Qt::IgnoreAction;
01039 manager->updateCursor();
01040 qt_xdnd_current_target = 0;
01041 qt_xdnd_current_proxy_target = 0;
01042 qt_xdnd_source_current_time = 0;
01043 waiting_for_status = false;
01044 }
01045
01046
01047
01048 void QX11Data::xdndHandleDrop(QWidget *, const XEvent * xe, bool passive)
01049 {
01050 DEBUG("xdndHandleDrop");
01051 if (!qt_xdnd_current_widget) {
01052 qt_xdnd_dragsource_xid = 0;
01053 return;
01054 }
01055
01056 if (!passive && checkEmbedded(qt_xdnd_current_widget, xe)){
01057 current_embedding_widget = 0;
01058 qt_xdnd_dragsource_xid = 0;
01059 qt_xdnd_current_widget = 0;
01060 return;
01061 }
01062 const unsigned long *l = (const unsigned long *)xe->xclient.data.l;
01063
01064 QDragManager *manager = QDragManager::self();
01065 DEBUG("xdnd drop");
01066
01067 if (l[0] != qt_xdnd_dragsource_xid) {
01068 DEBUG("xdnd drop from unexpected source (%08lx not %08lx", l[0], qt_xdnd_dragsource_xid);
01069 return;
01070 }
01071
01072 if (l[2] != 0) {
01073
01074 qt_xdnd_target_current_time = X11->userTime = l[2];
01075 }
01076
01077 if (!passive) {
01078 QMimeData *dropData = (manager->object) ? manager->dragPrivate()->data : manager->dropData;
01079 QDropEvent de(qt_xdnd_current_position, possible_actions, dropData,
01080 QApplication::mouseButtons(), QApplication::keyboardModifiers());
01081 QApplication::sendEvent(qt_xdnd_current_widget, &de);
01082 if (!de.isAccepted()) {
01083
01084 global_accepted_action = Qt::IgnoreAction;
01085 } else {
01086 global_accepted_action = de.dropAction();
01087 }
01088 XClientMessageEvent finished;
01089 finished.type = ClientMessage;
01090 finished.window = qt_xdnd_dragsource_xid;
01091 finished.format = 32;
01092 finished.message_type = ATOM(XdndFinished);
01093 DNDDEBUG << "xdndHandleDrop"
01094 << "qt_xdnd_current_widget" << qt_xdnd_current_widget
01095 << (qt_xdnd_current_widget ? qt_xdnd_current_widget->internalWinId() : 0)
01096 << "t_xdnd_current_widget->window()"
01097 << (qt_xdnd_current_widget ? qt_xdnd_current_widget->window() : 0)
01098 << (qt_xdnd_current_widget ? qt_xdnd_current_widget->window()->internalWinId() : 0);
01099 finished.data.l[0] = qt_xdnd_current_widget?qt_xdnd_current_widget->window()->internalWinId():0;
01100 finished.data.l[1] = de.isAccepted() ? 1 : 0;
01101 finished.data.l[2] = qtaction_to_xdndaction(global_accepted_action);
01102 XSendEvent(X11->display, qt_xdnd_dragsource_xid, False,
01103 NoEventMask, (XEvent*)&finished);
01104 } else {
01105 QDragLeaveEvent e;
01106 QApplication::sendEvent(qt_xdnd_current_widget, &e);
01107 }
01108 qt_xdnd_dragsource_xid = 0;
01109 qt_xdnd_current_widget = 0;
01110 waiting_for_status = false;
01111
01112
01113 qt_xdnd_target_current_time = CurrentTime;
01114 }
01115
01116
01117 void QX11Data::xdndHandleFinished(QWidget *, const XEvent * xe, bool passive)
01118 {
01119 DEBUG("xdndHandleFinished");
01120 const unsigned long *l = (const unsigned long *)xe->xclient.data.l;
01121
01122 DNDDEBUG << "xdndHandleFinished, l[0]" << l[0]
01123 << "qt_xdnd_current_target" << qt_xdnd_current_target
01124 << "qt_xdnd_current_proxy_targe" << qt_xdnd_current_proxy_target;
01125
01126 if (l[0]) {
01127 int at = findXdndDropTransactionByWindow(l[0]);
01128 if (at != -1) {
01129 restartXdndDropExpiryTimer();
01130
01131 QXdndDropTransaction t = X11->dndDropTransactions.takeAt(at);
01132 QDragManager *manager = QDragManager::self();
01133
01134 Window target = qt_xdnd_current_target;
01135 Window proxy_target = qt_xdnd_current_proxy_target;
01136 QWidget *embedding_widget = current_embedding_widget;
01137 QDrag *currentObject = manager->object;
01138
01139 qt_xdnd_current_target = t.target;
01140 qt_xdnd_current_proxy_target = t.proxy_target;
01141 current_embedding_widget = t.embedding_widget;
01142 manager->object = t.object;
01143
01144 if (!passive)
01145 (void) checkEmbedded(qt_xdnd_current_widget, xe);
01146
01147 current_embedding_widget = 0;
01148 qt_xdnd_current_target = 0;
01149 qt_xdnd_current_proxy_target = 0;
01150
01151 if (t.object)
01152 t.object->deleteLater();
01153
01154 qt_xdnd_current_target = target;
01155 qt_xdnd_current_proxy_target = proxy_target;
01156 current_embedding_widget = embedding_widget;
01157 manager->object = currentObject;
01158 }
01159 }
01160 waiting_for_status = false;
01161 }
01162
01163
01164 void QDragManager::timerEvent(QTimerEvent* e)
01165 {
01166 if (e->timerId() == heartbeat && qt_xdnd_source_sameanswer.isNull()) {
01167 move(QCursor::pos());
01168 } else if (e->timerId() == transaction_expiry_timer) {
01169 for (int i = 0; i < X11->dndDropTransactions.count(); ++i) {
01170 const QXdndDropTransaction &t = X11->dndDropTransactions.at(i);
01171 if (t.targetWidget) {
01172
01173 continue;
01174 }
01175 t.object->deleteLater();
01176 X11->dndDropTransactions.removeAt(i--);
01177 }
01178
01179 killTimer(transaction_expiry_timer);
01180 transaction_expiry_timer = -1;
01181 }
01182 }
01183
01184 bool QDragManager::eventFilter(QObject * o, QEvent * e)
01185 {
01186 if (beingCancelled) {
01187 if (e->type() == QEvent::KeyRelease && ((QKeyEvent*)e)->key() == Qt::Key_Escape) {
01188 qApp->removeEventFilter(this);
01189 Q_ASSERT(object == 0);
01190 beingCancelled = false;
01191 eventLoop->exit();
01192 return true;
01193 }
01194 return false;
01195 }
01196
01197 Q_ASSERT(object != 0);
01198
01199 if (!o->isWidgetType())
01200 return false;
01201
01202 if (e->type() == QEvent::MouseMove) {
01203 QMouseEvent* me = (QMouseEvent *)e;
01204 move(me->globalPos());
01205 return true;
01206 } else if (e->type() == QEvent::MouseButtonRelease) {
01207 DEBUG("pre drop");
01208 qApp->removeEventFilter(this);
01209 if (willDrop)
01210 drop();
01211 else
01212 cancel();
01213 DEBUG("drop, resetting object");
01214 beingCancelled = false;
01215 eventLoop->exit();
01216 return true;
01217 }
01218
01219 if (e->type() == QEvent::KeyPress || e->type() == QEvent::KeyRelease) {
01220 QKeyEvent *ke = ((QKeyEvent*)e);
01221 if (ke->key() == Qt::Key_Escape && e->type() == QEvent::KeyPress) {
01222 cancel();
01223 qApp->removeEventFilter(this);
01224 beingCancelled = false;
01225 eventLoop->exit();
01226 } else {
01227 qt_xdnd_source_sameanswer = QRect();
01228 move(QCursor::pos());
01229 }
01230 return true;
01231 }
01232
01233
01234
01235
01236 switch (e->type()) {
01237 case QEvent::MouseButtonPress:
01238 case QEvent::MouseButtonRelease:
01239 case QEvent::MouseButtonDblClick:
01240 case QEvent::MouseMove:
01241 case QEvent::KeyPress:
01242 case QEvent::KeyRelease:
01243 case QEvent::Wheel:
01244 case QEvent::ShortcutOverride:
01245 #ifdef QT3_SUPPORT
01246 case QEvent::Accel:
01247 case QEvent::AccelAvailable:
01248 #endif
01249 return true;
01250 default:
01251 return false;
01252 }
01253 }
01254
01255 void QDragManager::updateCursor()
01256 {
01257 if (!noDropCursor) {
01258 noDropCursor = new QCursor(Qt::ForbiddenCursor);
01259 moveCursor = new QCursor(dragCursor(Qt::MoveAction), 0,0);
01260 copyCursor = new QCursor(dragCursor(Qt::CopyAction), 0,0);
01261 linkCursor = new QCursor(dragCursor(Qt::LinkAction), 0,0);
01262 }
01263
01264 QCursor *c;
01265 if (willDrop) {
01266 if (global_accepted_action == Qt::CopyAction) {
01267 c = copyCursor;
01268 } else if (global_accepted_action == Qt::LinkAction) {
01269 c = linkCursor;
01270 } else {
01271 c = moveCursor;
01272 }
01273 if (xdnd_data.deco) {
01274 xdnd_data.deco->show();
01275 xdnd_data.deco->raise();
01276 }
01277 } else {
01278 c = noDropCursor;
01279
01280
01281 }
01282 #ifndef QT_NO_CURSOR
01283 if (c)
01284 qApp->changeOverrideCursor(*c);
01285 #endif
01286 }
01287
01288
01289 void QDragManager::cancel(bool deleteSource)
01290 {
01291 DEBUG("QDragManager::cancel");
01292 Q_ASSERT(heartbeat != -1);
01293 killTimer(heartbeat);
01294 heartbeat = -1;
01295 beingCancelled = true;
01296 qt_xdnd_dragging = false;
01297
01298 if (qt_xdnd_current_target)
01299 qt_xdnd_send_leave();
01300
01301 #ifndef QT_NO_CURSOR
01302 if (restoreCursor) {
01303 QApplication::restoreOverrideCursor();
01304 restoreCursor = false;
01305 }
01306 #endif
01307
01308 if (deleteSource && object)
01309 object->deleteLater();
01310 object = 0;
01311 delete xdnd_data.deco;
01312 xdnd_data.deco = 0;
01313
01314 global_accepted_action = Qt::IgnoreAction;
01315 }
01316
01317 static
01318 Window findRealWindow(const QPoint & pos, Window w, int md)
01319 {
01320 if (xdnd_data.deco && w == xdnd_data.deco->internalWinId())
01321 return 0;
01322
01323 if (md) {
01324 X11->ignoreBadwindow();
01325 XWindowAttributes attr;
01326 XGetWindowAttributes(X11->display, w, &attr);
01327 if (X11->badwindow())
01328 return 0;
01329
01330 if (attr.map_state == IsViewable
01331 && QRect(attr.x,attr.y,attr.width,attr.height).contains(pos)) {
01332 {
01333 Atom type = XNone;
01334 int f;
01335 unsigned long n, a;
01336 unsigned char *data;
01337
01338 XGetWindowProperty(X11->display, w, ATOM(XdndAware), 0, 0, False,
01339 AnyPropertyType, &type, &f,&n,&a,&data);
01340 if (data) XFree(data);
01341 if (type)
01342 return w;
01343 }
01344
01345 Window r, p;
01346 Window* c;
01347 uint nc;
01348 if (XQueryTree(X11->display, w, &r, &p, &c, &nc)) {
01349 r=0;
01350 for (uint i=nc; !r && i--;) {
01351 r = findRealWindow(pos-QPoint(attr.x,attr.y),
01352 c[i], md-1);
01353 }
01354 XFree(c);
01355 if (r)
01356 return r;
01357
01358
01359
01360 }
01361
01362
01363 return w;
01364 }
01365 }
01366 return 0;
01367 }
01368
01369 void QDragManager::move(const QPoint & globalPos)
01370 {
01371 DEBUG() << "QDragManager::move enter";
01372 if (!object) {
01373
01374 return;
01375 }
01376
01377 int screen = QCursor::x11Screen();
01378 if ((qt_xdnd_current_screen == -1 && screen != X11->defaultScreen) || (screen != qt_xdnd_current_screen)) {
01379
01380 delete xdnd_data.deco;
01381 xdnd_data.deco = new QShapedPixmapWidget(screen);
01382 if (!QWidget::mouseGrabber()) {
01383 updatePixmap();
01384 xdnd_data.deco->grabMouse();
01385 }
01386 }
01387 xdnd_data.deco->move(QCursor::pos() - xdnd_data.deco->pm_hot);
01388
01389 if (qt_xdnd_source_sameanswer.contains(globalPos) && qt_xdnd_source_sameanswer.isValid())
01390 return;
01391
01392 qt_xdnd_current_screen = screen;
01393 Window rootwin = QX11Info::appRootWindow(qt_xdnd_current_screen);
01394 Window target = 0;
01395 int lx = 0, ly = 0;
01396 if (!XTranslateCoordinates(X11->display, rootwin, rootwin, globalPos.x(), globalPos.y(), &lx, &ly, &target))
01397
01398 return;
01399
01400 if (target == rootwin) {
01401
01402 } else if (target) {
01403
01404 Window src = rootwin;
01405 while (target != 0) {
01406 DNDDEBUG << "checking target for XdndAware" << QWidget::find(target) << target;
01407 int lx2, ly2;
01408 Window t;
01409
01410 if (!XTranslateCoordinates(X11->display, src, target, lx, ly, &lx2, &ly2, &t)) {
01411 target = 0;
01412 break;
01413 }
01414 lx = lx2;
01415 ly = ly2;
01416 src = target;
01417
01418
01419 Atom type = 0;
01420 int f;
01421 unsigned long n, a;
01422 unsigned char *data = 0;
01423 XGetWindowProperty(X11->display, target, ATOM(XdndAware), 0, 0, False,
01424 AnyPropertyType, &type, &f,&n,&a,&data);
01425 if (data)
01426 XFree(data);
01427 if (type) {
01428 DNDDEBUG << "Found XdndAware on " << QWidget::find(target) << target;
01429 break;
01430 }
01431
01432
01433 if (!XTranslateCoordinates(X11->display, src, src, lx, ly, &lx2, &ly2, &target)) {
01434 target = 0;
01435 break;
01436 }
01437 }
01438 if (xdnd_data.deco && (!target || target == xdnd_data.deco->internalWinId())) {
01439 DNDDEBUG << "need to find real window";
01440 target = findRealWindow(globalPos, rootwin, 6);
01441 DNDDEBUG << "real window found" << QWidget::find(target) << target;
01442 }
01443 }
01444
01445 QWidget* w;
01446 if (target) {
01447 w = QWidget::find((WId)target);
01448 if (w && (w->windowType() == Qt::Desktop) && !w->acceptDrops())
01449 w = 0;
01450 } else {
01451 w = 0;
01452 target = rootwin;
01453 }
01454
01455 DNDDEBUG << "and the final target is " << QWidget::find(target) << target;
01456 DNDDEBUG << "the widget w is" << w;
01457
01458 WId proxy_target = xdndProxy(target);
01459 if (!proxy_target)
01460 proxy_target = target;
01461 int target_version = 1;
01462
01463 if (proxy_target) {
01464 Atom type = XNone;
01465 int r, f;
01466 unsigned long n, a;
01467 int *tv;
01468 X11->ignoreBadwindow();
01469 r = XGetWindowProperty(X11->display, proxy_target, ATOM(XdndAware), 0,
01470 1, False, AnyPropertyType, &type, &f,&n,&a,(uchar**)&tv);
01471 if (r != Success || X11->badwindow()) {
01472 target = 0;
01473 } else {
01474 target_version = qMin(xdnd_version,tv ? *tv : 1);
01475 if (tv)
01476 XFree(tv);
01477
01478
01479 }
01480 }
01481
01482 if (target != qt_xdnd_current_target) {
01483 if (qt_xdnd_current_target)
01484 qt_xdnd_send_leave();
01485
01486 qt_xdnd_current_target = target;
01487 qt_xdnd_current_proxy_target = proxy_target;
01488 if (target) {
01489 QVector<Atom> types;
01490 int flags = target_version << 24;
01491 QStringList fmts = QInternalMimeData::formatsHelper(dragPrivate()->data);
01492 for (int i = 0; i < fmts.size(); ++i) {
01493 QList<Atom> atoms = X11->xdndMimeAtomsForFormat(fmts.at(i));
01494 for (int j = 0; j < atoms.size(); ++j) {
01495 if (!types.contains(atoms.at(j)))
01496 types.append(atoms.at(j));
01497 }
01498 }
01499 if (types.size() > 3) {
01500 XChangeProperty(X11->display,
01501 dragPrivate()->source->internalWinId(), ATOM(XdndTypelist),
01502 XA_ATOM, 32, PropModeReplace,
01503 (unsigned char *)types.data(),
01504 types.size());
01505 flags |= 0x0001;
01506 }
01507 XClientMessageEvent enter;
01508 enter.type = ClientMessage;
01509 enter.window = target;
01510 enter.format = 32;
01511 enter.message_type = ATOM(XdndEnter);
01512 enter.data.l[0] = dragPrivate()->source->internalWinId();
01513 enter.data.l[1] = flags;
01514 enter.data.l[2] = types.size()>0 ? types.at(0) : 0;
01515 enter.data.l[3] = types.size()>1 ? types.at(1) : 0;
01516 enter.data.l[4] = types.size()>2 ? types.at(2) : 0;
01517
01518 qt_xdnd_source_sameanswer = QRect(globalPos.x() - 2,
01519 globalPos.y() -2 , 5, 5);
01520
01521 DEBUG("sending Xdnd enter");
01522 if (w)
01523 X11->xdndHandleEnter(w, (const XEvent *)&enter, false);
01524 else if (target)
01525 XSendEvent(X11->display, proxy_target, False, NoEventMask, (XEvent*)&enter);
01526 waiting_for_status = false;
01527 }
01528 }
01529 if (waiting_for_status)
01530 return;
01531
01532 if (target) {
01533 waiting_for_status = true;
01534
01535 XClientMessageEvent move;
01536 move.type = ClientMessage;
01537 move.window = target;
01538 move.format = 32;
01539 move.message_type = ATOM(XdndPosition);
01540 move.window = target;
01541 move.data.l[0] = dragPrivate()->source->internalWinId();
01542 move.data.l[1] = 0;
01543 move.data.l[2] = (globalPos.x() << 16) + globalPos.y();
01544 move.data.l[3] = X11->time;
01545 move.data.l[4] = qtaction_to_xdndaction(defaultAction(dragPrivate()->possible_actions, QApplication::keyboardModifiers()));
01546 DEBUG("sending Xdnd position");
01547
01548 qt_xdnd_source_current_time = X11->time;
01549
01550 if (w)
01551 handle_xdnd_position(w, (const XEvent *)&move, false);
01552 else
01553 XSendEvent(X11->display, proxy_target, False, NoEventMask,
01554 (XEvent*)&move);
01555 } else {
01556 if (willDrop) {
01557 willDrop = false;
01558 updateCursor();
01559 }
01560 }
01561 DEBUG() << "QDragManager::move leave";
01562 }
01563
01564
01565 void QDragManager::drop()
01566 {
01567 Q_ASSERT(heartbeat != -1);
01568 killTimer(heartbeat);
01569 heartbeat = -1;
01570 qt_xdnd_dragging = false;
01571
01572 if (!qt_xdnd_current_target)
01573 return;
01574
01575 delete xdnd_data.deco;
01576 xdnd_data.deco = 0;
01577
01578 XClientMessageEvent drop;
01579 drop.type = ClientMessage;
01580 drop.window = qt_xdnd_current_target;
01581 drop.format = 32;
01582 drop.message_type = ATOM(XdndDrop);
01583 drop.data.l[0] = dragPrivate()->source->internalWinId();
01584 drop.data.l[1] = 0;
01585 drop.data.l[2] = X11->time;
01586
01587 drop.data.l[3] = 0;
01588 drop.data.l[4] = 0;
01589
01590 QWidget * w = QWidget::find(qt_xdnd_current_proxy_target);
01591
01592 if (w && (w->windowType() == Qt::Desktop) && !w->acceptDrops())
01593 w = 0;
01594
01595 QXdndDropTransaction t = {
01596 X11->time,
01597 qt_xdnd_current_target,
01598 qt_xdnd_current_proxy_target,
01599 w,
01600 current_embedding_widget,
01601 object
01602 };
01603 X11->dndDropTransactions.append(t);
01604 restartXdndDropExpiryTimer();
01605
01606 if (w)
01607 X11->xdndHandleDrop(w, (const XEvent *)&drop, false);
01608 else
01609 XSendEvent(X11->display, qt_xdnd_current_proxy_target, False,
01610 NoEventMask, (XEvent*)&drop);
01611
01612 qt_xdnd_current_target = 0;
01613 qt_xdnd_current_proxy_target = 0;
01614 qt_xdnd_source_current_time = 0;
01615 current_embedding_widget = 0;
01616 object = 0;
01617
01618 #ifndef QT_NO_CURSOR
01619 if (restoreCursor) {
01620 QApplication::restoreOverrideCursor();
01621 restoreCursor = false;
01622 }
01623 #endif
01624 }
01625
01626
01627
01628 bool QX11Data::xdndHandleBadwindow()
01629 {
01630 QDragManager *manager = QDragManager::self();
01631 if (manager->object && qt_xdnd_current_target) {
01632 qt_xdnd_current_target = 0;
01633 qt_xdnd_current_proxy_target = 0;
01634 manager->object->deleteLater();
01635 manager->object = 0;
01636 delete xdnd_data.deco;
01637 xdnd_data.deco = 0;
01638 return true;
01639 }
01640 if (qt_xdnd_dragsource_xid) {
01641 qt_xdnd_dragsource_xid = 0;
01642 if (qt_xdnd_current_widget) {
01643 QDragLeaveEvent e;
01644 QApplication::sendEvent(qt_xdnd_current_widget, &e);
01645 qt_xdnd_current_widget = 0;
01646 }
01647 return true;
01648 }
01649 return false;
01650 }
01651
01652 void QX11Data::xdndHandleSelectionRequest(const XSelectionRequestEvent * req)
01653 {
01654 if (!req)
01655 return;
01656 XEvent evt;
01657 evt.xselection.type = SelectionNotify;
01658 evt.xselection.display = req->display;
01659 evt.xselection.requestor = req->requestor;
01660 evt.xselection.selection = req->selection;
01661 evt.xselection.target = XNone;
01662 evt.xselection.property = XNone;
01663 evt.xselection.time = req->time;
01664
01665 QDragManager *manager = QDragManager::self();
01666 QDrag *currentObject = manager->object;
01667
01668
01669 int at = -1;
01670
01671
01672 if (manager->object && req->time == qt_xdnd_source_current_time) {
01673
01674 at = -2;
01675 } else {
01676
01677
01678 at = findXdndDropTransactionByTime(req->time);
01679 if (at == -1) {
01680
01681
01682 at = findXdndDropTransactionByWindow(req->requestor);
01683 }
01684 if (at == -1 && req->time == CurrentTime) {
01685
01686
01687 Window target = findXdndAwareParent(req->requestor);
01688 if (target) {
01689 if (qt_xdnd_current_target && qt_xdnd_current_target == target)
01690 at = -2;
01691 else
01692 at = findXdndDropTransactionByWindow(target);
01693 }
01694 }
01695 }
01696 if (at >= 0) {
01697 restartXdndDropExpiryTimer();
01698
01699
01700 manager->object = X11->dndDropTransactions.at(at).object;
01701 } else if (at != -2) {
01702
01703 manager->object = 0;
01704 }
01705 if (manager->object) {
01706 Atom atomFormat = req->target;
01707 int dataFormat = 0;
01708 QByteArray data;
01709 if (X11->xdndMimeDataForAtom(req->target, manager->dragPrivate()->data,
01710 &data, &atomFormat, &dataFormat)) {
01711 int dataSize = data.size() / (dataFormat / 8);
01712 XChangeProperty (X11->display, req->requestor, req->property,
01713 atomFormat, dataFormat, PropModeReplace,
01714 (unsigned char *)data.data(), dataSize);
01715 evt.xselection.property = req->property;
01716 evt.xselection.target = atomFormat;
01717 }
01718 }
01719
01720
01721 manager->object = currentObject;
01722
01723
01724
01725 XSendEvent(X11->display, req->requestor, False, 0, &evt);
01726 }
01727
01728 static QByteArray xdndObtainData(const char *format)
01729 {
01730 QByteArray result;
01731
01732 QWidget* w;
01733 QDragManager *manager = QDragManager::self();
01734 if (qt_xdnd_dragsource_xid && manager->object &&
01735 (w=QWidget::find(qt_xdnd_dragsource_xid))
01736 && (!(w->windowType() == Qt::Desktop) || w->acceptDrops()))
01737 {
01738 QDragPrivate * o = QDragManager::self()->dragPrivate();
01739 if (o->data->hasFormat(QLatin1String(format)))
01740 result = o->data->data(QLatin1String(format));
01741 return result;
01742 }
01743
01744 QList<Atom> atoms;
01745 int i = 0;
01746 while ((qt_xdnd_types[i])) {
01747 atoms.append(qt_xdnd_types[i]);
01748 ++i;
01749 }
01750 Atom a = X11->xdndMimeAtomForFormat(QLatin1String(format), atoms);
01751 if (!a)
01752 return result;
01753
01754 if (XGetSelectionOwner(X11->display, ATOM(XdndSelection)) == XNone)
01755 return result;
01756
01757 QWidget* tw = qt_xdnd_current_widget;
01758 if (!qt_xdnd_current_widget || (qt_xdnd_current_widget->windowType() == Qt::Desktop))
01759 tw = new QWidget;
01760
01761 XConvertSelection(X11->display, ATOM(XdndSelection), a, ATOM(XdndSelection), tw->internalWinId(),
01762 qt_xdnd_target_current_time);
01763 XFlush(X11->display);
01764
01765 XEvent xevent;
01766 bool got=X11->clipboardWaitForEvent(tw->internalWinId(), SelectionNotify, &xevent, 5000);
01767 if (got) {
01768 Atom type;
01769
01770 if (X11->clipboardReadProperty(tw->internalWinId(), ATOM(XdndSelection), true, &result, 0, &type, 0, false)) {
01771 if (type == ATOM(INCR)) {
01772 int nbytes = result.size() >= 4 ? *((int*)result.data()) : 0;
01773 result = X11->clipboardReadIncrementalProperty(tw->internalWinId(), ATOM(XdndSelection), nbytes, false);
01774 } else if (type != a && type != XNone) {
01775 DEBUG("Qt clipboard: unknown atom %ld", type);
01776 }
01777 }
01778 }
01779 if (!qt_xdnd_current_widget || (qt_xdnd_current_widget->windowType() == Qt::Desktop))
01780 delete tw;
01781
01782 return X11->xdndMimeConvertToFormat(a, result, QLatin1String(format));
01783 }
01784
01785
01786
01787
01788
01789
01790 bool QX11Data::dndEnable(QWidget* w, bool on)
01791 {
01792 w = w->window();
01793
01794 if (on) {
01795 if (((QExtraWidget*)w)->topData()->dnd)
01796 return true;
01797 ((QExtraWidget*)w)->topData()->dnd = 1;
01798 }
01799
01800 motifdndEnable(w, on);
01801 return xdndEnable(w, on);
01802 }
01803
01804 Qt::DropAction QDragManager::drag(QDrag * o)
01805 {
01806 if (object == o || !o || !o->d_func()->source)
01807 return Qt::IgnoreAction;
01808
01809 if (object) {
01810 cancel();
01811 qApp->removeEventFilter(this);
01812 beingCancelled = false;
01813 }
01814
01815 if (object) {
01816
01817
01818
01819 QApplication::flush();
01820
01821 QTime started = QTime::currentTime();
01822 QTime now = started;
01823 do {
01824 XEvent event;
01825 if (XCheckTypedEvent(X11->display, ClientMessage, &event))
01826 qApp->x11ProcessEvent(&event);
01827
01828 now = QTime::currentTime();
01829 if (started > now)
01830 started = now;
01831
01832
01833 struct timeval usleep_tv;
01834 usleep_tv.tv_sec = 0;
01835 usleep_tv.tv_usec = 50000;
01836 select(0, 0, 0, 0, &usleep_tv);
01837 } while (object && started.msecsTo(now) < 1000);
01838 }
01839
01840 object = o;
01841 object->d_func()->target = 0;
01842 xdnd_data.deco = new QShapedPixmapWidget();
01843
01844 willDrop = false;
01845
01846 updatePixmap();
01847
01848 qApp->installEventFilter(this);
01849 XSetSelectionOwner(X11->display, ATOM(XdndSelection), dragPrivate()->source->window()->internalWinId(), X11->time);
01850 global_accepted_action = Qt::CopyAction;
01851 qt_xdnd_source_sameanswer = QRect();
01852 move(QCursor::pos());
01853 heartbeat = startTimer(200);
01854
01855 #ifndef QT_NO_CURSOR
01856 qApp->setOverrideCursor(Qt::ArrowCursor);
01857 restoreCursor = true;
01858 updateCursor();
01859 #endif
01860
01861 qt_xdnd_dragging = true;
01862
01863 if (!QWidget::mouseGrabber())
01864 xdnd_data.deco->grabMouse();
01865
01866 eventLoop = new QEventLoop;
01867 (void) eventLoop->exec();
01868 delete eventLoop;
01869 eventLoop = 0;
01870
01871 #ifndef QT_NO_CURSOR
01872 if (restoreCursor) {
01873 qApp->restoreOverrideCursor();
01874 restoreCursor = false;
01875 }
01876 #endif
01877
01878
01879 delete noDropCursor;
01880 noDropCursor = 0;
01881 delete copyCursor;
01882 copyCursor = 0;
01883 delete moveCursor;
01884 moveCursor = 0;
01885 delete linkCursor;
01886 linkCursor = 0;
01887
01888 delete xdnd_data.deco;
01889 xdnd_data.deco = 0;
01890 if (heartbeat != -1)
01891 killTimer(heartbeat);
01892 heartbeat = -1;
01893 qt_xdnd_current_screen = -1;
01894 qt_xdnd_dragging = false;
01895
01896 return global_accepted_action;
01897
01898 }
01899
01900 void QDragManager::updatePixmap()
01901 {
01902 if (xdnd_data.deco) {
01903 QPixmap pm;
01904 QPoint pm_hot(default_pm_hotx,default_pm_hoty);
01905 if (object) {
01906 pm = dragPrivate()->pixmap;
01907 if (!pm.isNull())
01908 pm_hot = dragPrivate()->hotspot;
01909 }
01910 if (pm.isNull()) {
01911 if (!defaultPm)
01912 defaultPm = new QPixmap(default_pm);
01913 pm = *defaultPm;
01914 }
01915 xdnd_data.deco->pm_hot = pm_hot;
01916 xdnd_data.deco->setPixmap(pm);
01917 xdnd_data.deco->move(QCursor::pos()-pm_hot);
01918 xdnd_data.deco->show();
01919 }
01920 }
01921
01922 QVariant QDropData::retrieveData_sys(const QString &mimetype, QVariant::Type) const
01923 {
01924 QByteArray mime = mimetype.toLatin1();
01925 QByteArray data = X11->motifdnd_active
01926 ? X11->motifdndObtainData(mime)
01927 : xdndObtainData(mime);
01928 return data;
01929 }
01930
01931 bool QDropData::hasFormat_sys(const QString &format) const
01932 {
01933 return formats().contains(format);
01934 }
01935
01936 QStringList QDropData::formats_sys() const
01937 {
01938 QStringList formats;
01939 if (X11->motifdnd_active) {
01940 int i = 0;
01941 QByteArray fmt;
01942 while (!(fmt = X11->motifdndFormat(i)).isEmpty()) {
01943 formats.append(QLatin1String(fmt));
01944 ++i;
01945 }
01946 } else {
01947 int i = 0;
01948 while ((qt_xdnd_types[i])) {
01949 QStringList formatsForAtom = X11->xdndMimeFormatsForAtom(qt_xdnd_types[i]);
01950 for (int j = 0; j < formatsForAtom.size(); ++j) {
01951 if (!formats.contains(formatsForAtom.at(j)))
01952 formats.append(formatsForAtom.at(j));
01953 }
01954 ++i;
01955 }
01956 }
01957 return formats;
01958 }
01959
01960 #endif // QT_NO_DRAGANDDROP