Changeset 458 for trunk/src/gui/kernel


Ignore:
Timestamp:
Jan 21, 2010, 12:46:24 PM (16 years ago)
Author:
Dmitry A. Kuminov
Message:

gui: DnD: Added support for drag sources that don't support synchronous data rendering (such as EPM). At present, these applications must be hard coded in qdnd_pm.cpp using the EXE name and the window class name.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/gui/kernel/qdnd_pm.cpp

    r454 r458  
    5353#include "qdesktopwidget.h"
    5454#include "qfile.h"
     55#include "qfileinfo.h"
    5556#include "qdnd_p.h"
    5657#include "qdebug.h"
     
    9091    void reset() { reset(false); }
    9192
    92     void setDropped(bool d) { dropped = d; }
    93     bool isDropped() const { return dropped; }
     93    void setInFakeDrop(bool d) { fakeDrop = d; }
     94    bool inFakeDrop() const { return fakeDrop; }
    9495
    9596    DRAGINFO *info() const { return di; }
     97    bool canSyncRender() const { return canSyncRndr; }
    9698
    9799    bool hasFormat_sys(const QString &mimeType);
     
    106108
    107109    bool initialized : 1;
    108     bool dropped : 1;
     110    bool fakeDrop : 1;
    109111    bool gotWorkers : 1;
     112    bool canSyncRndr : 1;
    110113
    111114    DRAGINFO *di;
     
    125128
    126129QPMDragData::QPMDragData()
    127     : initialized(false), dropped(false)
    128     , gotWorkers(false), di(NULL)
     130    : initialized(false), fakeDrop(false)
     131    , gotWorkers(false), canSyncRndr(false), di(NULL)
    129132    , lastDragOverWidget(0), lastDragOverOp(0)
    130133    , supportedOps(0), sourceAllowsOp(false)
     
    147150    initialized = true;
    148151    di = info;
     152
     153    // Detect if the source supports synchronous rendering which means it can
     154    // process DM_RENDER and reply with DM_RENDERCOMPLETE from within both
     155    // DM_DRAGOVER and DM_DROP (i.e. while it is running the DnD session with
     156    // DrgDrag()). Mozilla apps are known to only support synchronous rendering
     157    // (an attempt to send DM_RENDER to them after DM_DROP will crash them).
     158    // By the contrast, EPM only supports asyncronous rendering (it will hang
     159    // if we send DM_RENDER and/or wait for DM_RENDERCOMPLETE before returning
     160    // from DM_DRAGOVER/DM_DROP). WPS (e.g. desktop folders) seem to support
     161    // both (we prefer the syncrhonous rendering in such cases since it lets
     162    // Qt apps access dragged data right from QDragEnterEvent/QDragLeaveEvent
     163    // and QDropEvent message handlers which is used by many of them for better
     164    // visualization.
     165
     166    // get class name
     167    QByteArray className(256, '\0');
     168    LONG classNameLen = WinQueryClassName(info->hwndSource,
     169                                          className.size(), className.data());
     170    className.truncate(classNameLen);
     171
     172    DEBUG(("QPMDragData::initialize: info->hwndSource %08lX className \"%s\"",
     173           info->hwndSource, QString::fromLocal8Bit(className).toUtf8().data()));
     174
     175    // get EXE name
     176    PID pid = 0;
     177    TID tid = 0;
     178    QByteArray exePath(CCHMAXPATH, '\0');
     179    BOOL ok = WinQueryWindowProcess(info->hwndSource, &pid, &tid);
     180    if (ok) {
     181        QByteArray buf(4192, '\0');
     182        APIRET arc = DosQuerySysState(QS_PROCESS, 0, pid, tid,
     183                                      buf.data(), buf.size());
     184        if (arc == NO_ERROR) {
     185            qsGrec_t *grec = *(qsGrec_t **)buf.data();
     186            qsPrec_t *prec = (qsPrec_t *)((ULONG)grec + sizeof(qsGrec_t));
     187            arc = DosQueryModuleName(prec->hMte, exePath.size(), exePath.data());
     188            if (arc == NO_ERROR)
     189                exePath.truncate(qstrlen(exePath));
     190        }
     191    }
     192
     193    DEBUG(("QPMDragData::initialize: pid %lu exePath \"%s\"",
     194           pid, QString::fromLocal8Bit(exePath).toUtf8().data()));
     195
     196    QString exeName = QFileInfo(QFile::decodeName(exePath)).fileName();
     197
     198    // start with a positive
     199    canSyncRndr = true;
     200
     201    if (exeName.toUpper() == QLatin1String("EPM.EXE") &&
     202        qstrcmp(className, "FileWnd") == 0) {
     203        // EPM only supports async rendering
     204        canSyncRndr = false;
     205    }
     206
     207    DEBUG(("QPMDragData::initialize: canSyncRender %d", canSyncRndr));
    149208}
    150209
     
    196255    workers.clear();
    197256    di = 0;
    198     initialized = dropped = gotWorkers = false;
     257    initialized = fakeDrop = gotWorkers = canSyncRndr = false;
    199258}
    200259
     
    229288    QVariant result;
    230289
    231     // we may only do data transfer after DM_DROP is sent. Return shortly.
    232     if (!isDropped())
     290    // If the source doesn't support synchronous rendering, we may only do data
     291    // transfer after DM_DROP is processed by us. Return shortly.
     292    if (!canSyncRender() && !inFakeDrop())
    233293        return result;
    234294
     
    360420void QDragManager::sendDropEvent(QWidget *receiver, QEvent *event)
    361421{
     422    // Note: inDrag = true causes QWidget::getPS() to return a special HPS for
     423    // drawing during DnD. However, if we send the message from a fake DM_DROP
     424    // that we post to ourselves to render data after the DnD session ends
     425    // (inFakeDrop = true), a normal HPS has to be used.
     426
     427    bool inFakeDrop = dropData->d->inFakeDrop();
     428
    362429    Q_ASSERT(!inDrag);
    363     inDrag = true;
     430    if (!inFakeDrop)
     431        inDrag = true;
     432
    364433    QApplication::sendEvent(receiver, event);
    365     // make sure that all issued update requests are handled before we
    366     // return from the DM_DRAGOVER/DM_DRAGLEAVE/DM_DROP message; otherwise
    367     // we'll get screen corruption due to unexpected screen updates while
    368     // dragging
    369     QApplication::sendPostedEvents(0, QEvent::UpdateRequest);
    370     inDrag = false;
     434
     435    if (!inFakeDrop) {
     436        // make sure that all issued update requests are handled before we
     437        // return from the DM_DRAGOVER/DM_DRAGLEAVE/DM_DROP message; otherwise
     438        // we'll get screen corruption due to unexpected screen updates while
     439        // dragging
     440        QApplication::sendPostedEvents(0, QEvent::UpdateRequest);
     441        inDrag = false;
     442    }
    371443}
    372444
     
    561633            DEBUG(("DM_DROP: hwnd %lX di %p", qmsg.hwnd, qmsg.mp1));
    562634
    563             // Odin32 apps produce incorrect message flow, ignore
    564             Q_ASSERT(dragData->lastDragOverWidget != 0);
    565             if (dragData->lastDragOverWidget == 0)
    566                 return 0;
    567 
    568             // Odin32 apps send DM_DROP even if we replied DOR_NEVERDROP or
    569             // DOR_NODROP, simulate DM_DRAGLEAVE
    570             Q_ASSERT(dragData->lastDropReply == DOR_DROP);
    571             if (dragData->lastDropReply != DOR_DROP) {
    572                 QMSG qmsg2 = qmsg;
    573                 qmsg2.msg = DM_DRAGLEAVE;
    574                 dispatchDragAndDrop(widget, qmsg2);
    575                 return 0;
     635            // sanity
     636            Q_ASSERT(!dragData->canSyncRender() || qmsg.mp1);
     637
     638            DRAGINFO *info = 0;
     639
     640            if (dragData->canSyncRender() || qmsg.mp1) {
     641                // Odin32 apps produce incorrect message flow, ignore
     642                Q_ASSERT(dragData->lastDragOverWidget != 0);
     643                if (dragData->lastDragOverWidget == 0)
     644                    return 0;
     645
     646                // Odin32 apps send DM_DROP even if we replied DOR_NEVERDROP or
     647                // DOR_NODROP, simulate DM_DRAGLEAVE
     648                Q_ASSERT(dragData->lastDropReply == DOR_DROP);
     649                if (dragData->lastDropReply != DOR_DROP) {
     650                    QMSG qmsg2 = qmsg;
     651                    qmsg2.msg = DM_DRAGLEAVE;
     652                    dispatchDragAndDrop(widget, qmsg2);
     653                    return 0;
     654                }
     655
     656                // sanity
     657                Q_ASSERT((DRAGINFO *)qmsg.mp1 == dragData->info());
     658
     659                info = (DRAGINFO *)qmsg.mp1;
     660                ok = DrgAccessDraginfo(info);
     661                Q_ASSERT(ok && info);
     662                if (!ok || !info)
     663                    return 0;
     664
     665                if (!dragData->canSyncRender()) {
     666                    // The source doesn't support syncrhonous rendering (i.e.
     667                    // it won't issue DM_RENDERCOMPLETE until we return from
     668                    // DM_DROP) so we have to post a message to ourselves to do
     669                    // it asynchronously. For simplicity, we reuse DM_DRAG with
     670                    // mp1 = 0 for this purpose.
     671                    WinPostMsg(qmsg.hwnd, DM_DROP, 0, 0);
     672                    return 0;
     673                }
     674            } else {
     675                // we're in a fake DM_DROP we posted above
     676                dragData->setInFakeDrop(true);
     677                info = dragData->info();
     678                Q_ASSERT(info);
    576679            }
    577 
    578             DRAGINFO *info = (DRAGINFO *)qmsg.mp1;
    579             ok = DrgAccessDraginfo(info);
    580             Q_ASSERT(ok && info);
    581             if (!ok || !info)
    582                 return 0;
    583680
    584681            // flip y coordinate
     
    597694
    598695            QMimeData *data = manager->source() ? manager->dragPrivate()->data : manager->dropData;
    599 
    600             dragData->setDropped(true);
    601696
    602697            QDropEvent de(pnt, dragData->lastAction, data, mouseButtons(),
Note: See TracChangeset for help on using the changeset viewer.