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