Changeset 438 for trunk/src


Ignore:
Timestamp:
Dec 24, 2009, 3:29:44 AM (16 years ago)
Author:
Dmitry A. Kuminov
Message:

gui: First DnD bits.

Location:
trunk/src/gui/kernel
Files:
4 edited

Legend:

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

    r437 r438  
    701701#define SV_WORKAREA_XRIGHT  53
    702702#define SV_WORKAREA_XLEFT   54
     703
     704#ifndef QT_NO_DRAGANDDROP
     705extern MRESULT qt_dispatchDragAndDrop(QWidget *, const QMSG &); // qdnd_pm.cpp
     706#endif
    703707
    704708/*!
  • trunk/src/gui/kernel/qdnd.cpp

    r2 r438  
    100100"........aa."};
    101101
    102 #ifdef Q_WS_WIN
     102#if defined(Q_WS_WIN) || defined(Q_WS_PM)
    103103/* XPM */
    104104static const char * const ignore_xpm[] = {
    105105"24 30 3 1",
    106106".        c None",
     107#if defined(Q_WS_WIN)
    107108"a        c #000000",
    108 "X        c #FFFFFF",
     109"X        c #FFFFFF", // Windows cursor is traditionally white
     110#else
     111"a        c #FFFFFF",
     112"X        c #000000", // OS/2 cursor is traditionally black
     113#endif
    109114"aa......................",
    110115"aXa.....................",
     
    327332    Q_ASSERT(!instance);
    328333
    329 #ifdef Q_WS_WIN
     334#if defined(Q_WS_WIN) || defined(Q_WS_PM)
    330335    n_cursor = 4;
    331336#else
     
    340345    pm_cursor[1] = QPixmap((const char **)copy_xpm);
    341346    pm_cursor[2] = QPixmap((const char **)link_xpm);
    342 #ifdef Q_WS_WIN
     347#if defined(Q_WS_WIN) || defined(Q_WS_PM)
    343348    pm_cursor[3] = QPixmap((const char **)ignore_xpm);
    344349#endif
     
    487492    : QInternalMimeData()
    488493{
     494#ifdef Q_WS_PM
     495    d = 0;
     496#endif
    489497}
    490498
  • trunk/src/gui/kernel/qdnd_p.h

    r2 r438  
    185185};
    186186
     187#if defined(Q_WS_PM)
     188class QPMDragData;
     189#endif
     190
    187191class QDropData : public QInternalMimeData
    188192{
     
    200204public:
    201205    LPDATAOBJECT currentDataObject;
     206#elif defined(Q_WS_PM)
     207    friend class QPMDragData;
     208    QPMDragData *d;
    202209#endif
    203210};
  • trunk/src/gui/kernel/qdnd_pm.cpp

    r107 r438  
    5151#include "qdatastream.h"
    5252#include "qcursor.h"
    53 #include "qt_os2.h"
     53#include "qdesktopwidget.h"
    5454#include "qdnd_p.h"
    5555#include "qdebug.h"
    5656
     57#include "qt_os2.h"
     58
    5759QT_BEGIN_NAMESPACE
    5860
    5961#if !defined(QT_NO_DRAGANDDROP) && !defined(QT_NO_CLIPBOARD)
     62
     63/** \internal
     64 *  Data for QDragEnterEvent/QDragMoveEvent/QPMDropEvent.
     65 */
     66class QPMDragData
     67{
     68public:
     69    QPMDragData();
     70    ~QPMDragData();
     71
     72    void initialize( DRAGINFO *di );
     73    void reset( bool isAccepted, bool isActAccepted );
     74    void reset() { reset( FALSE, FALSE ); }
     75
     76    void setDropped( bool d ) { dropped = d; }
     77    bool isDropped() const { return dropped; }
     78
     79    DRAGINFO *info() const { return di; }
     80
     81    bool provides( const char *format );
     82    const char *format( int fn );
     83    QByteArray encodedData( const char *format );
     84
     85private:
     86
     87    void initWorkers();
     88
     89    bool initialized : 1;
     90    bool dropped : 1;
     91    bool gotWorkers : 1;
     92
     93    DRAGINFO *di;
     94    // @todo later
     95//  QPtrList<QPMMime::DropWorker> workers;
     96};
     97
     98QPMDragData::QPMDragData()
     99    : initialized(false), dropped(false)
     100    , gotWorkers(false), di(NULL)
     101{
     102    QDragManager *manager = QDragManager::self();
     103    Q_ASSERT(!manager->dropData->d);
     104    manager->dropData->d = this;
     105}
     106
     107QPMDragData::~QPMDragData()
     108{
     109    reset();
     110
     111    QDragManager *manager = QDragManager::self();
     112    Q_ASSERT(manager->dropData->d == this);
     113    manager->dropData->d = 0;
     114}
     115
     116void QPMDragData::initialize(DRAGINFO *info)
     117{
     118    Q_ASSERT(info);
     119    if (initialized || !info)
     120        return;
     121
     122    initialized = true;
     123    di = info;
     124}
     125
     126void QPMDragData::initWorkers()
     127{
     128    Q_ASSERT(initialized);
     129    if (!initialized || gotWorkers)
     130        return;
     131
     132    gotWorkers = true;
     133
     134    // @todo later
     135#if 0
     136    // go through all convertors and collect DropWorkers to use
     137    QPtrList<QPMMime> mimes = QPMMime::all();
     138    for ( QPMMime *mime = mimes.first(); mime; mime = mimes.next() ) {
     139        QPMMime::DropWorker *wrk = mime->dropWorkerFor( di );
     140        if ( wrk ) {
     141            if ( wrk->isExclusive() ) {
     142                // ignore all other workers if some of them identified itself
     143                // as exclusive
     144                workers.clear();
     145                workers.append( wrk );
     146                break;
     147            }
     148            // ensure there are no duplicates
     149            if ( !workers.containsRef( wrk ) )
     150                workers.append( wrk );
     151        }
     152    }
     153
     154#if defined(QT_DEBUG_DND)
     155    qDebug( "QPMDragData: %d drop workers for DRAGINFO %p",
     156            workers.count(), di );
     157#endif
     158
     159    // init all workers
     160    for ( QPMMime::DropWorker *w = workers.first(); w; w = workers.next() ) {
     161        w->nfo = di;
     162        w->init();
     163    }
     164#endif
     165}
     166
     167void QPMDragData::reset(bool isAccepted, bool isActAccepted)
     168{
     169    if (!initialized)
     170        return;
     171
     172    // @todo later
     173#if 0
     174    // cleanup all workers
     175    for ( QPMMime::DropWorker *w = workers.first(); w; w = workers.next() ) {
     176        w->cleanup( isAccepted, isActAccepted );
     177        w->nfo = NULL;
     178    }
     179
     180    workers.clear();
     181#endif
     182    di = NULL;
     183    initialized = dropped = gotWorkers = FALSE;
     184}
     185
     186// @todo later
     187#if 0
     188class QPMDropEvent : public QDropEvent
     189{
     190public:
     191    QPMDropEvent( const QPoint &pos, QPMDragData *data )
     192        : QDropEvent( pos ) { d = data; }
     193
     194    inline bool isTrulyAccepted() const { return accpt; }
     195    inline bool isActionTrulyAccepted() const { return accptact; }
     196};
     197
     198class QPMDragEnterEvent : public QDragEnterEvent
     199{
     200public:
     201    QPMDragEnterEvent( const QPoint &pos, QPMDragData *data )
     202        : QDragEnterEvent( pos ) { d = data; }
     203
     204    inline bool isTrulyAccepted() const { return accpt; }
     205    inline bool isActionTrulyAccepted() const { return accptact; }
     206};
     207
     208class QPMDragMoveEvent : public QDragMoveEvent
     209{
     210public:
     211    QPMDragMoveEvent( const QPoint &pos, QPMDragData *data )
     212        : QDragMoveEvent( pos ) { d = data; }
     213
     214    inline bool isTrulyAccepted() const { return accpt; }
     215    inline bool isActionTrulyAccepted() const { return accptact; }
     216};
     217
     218bool QDropEvent::provides( const char* mimeType ) const
     219{
     220    QPMDragData *data = static_cast<QPMDragData *> (d);
     221    Q_ASSERT( data );
     222    if ( !data )
     223        return FALSE;
     224
     225    return data->provides( mimeType );
     226}
     227
     228const char *QDropEvent::format( int fn ) const
     229{
     230    QPMDragData *data = static_cast<QPMDragData *> (d);
     231    Q_ASSERT( data );
     232    if ( !data )
     233        return NULL;
     234
     235    return data->format( fn );
     236}
     237
     238QByteArray QDropEvent::encodedData( const char *format ) const
     239{
     240    QByteArray array;
     241
     242    QPMDragData *data = static_cast<QPMDragData *> (d);
     243    Q_ASSERT( data );
     244
     245    if ( !data || !data->isDropped() ) {
     246        // This is a QDragEnterEvent/QDragMoveEvent subclass; we cannot provide
     247        // any MIME contents yet because it's impossible to do data transfers
     248        // before DM_DROP is sent. Return shortly.
     249        return array;
     250    }
     251
     252    array = data->encodedData( format );
     253    return array;
     254}
     255#endif
     256
     257/*!
     258 *  \internal
     259 *  Direct manipulation (Drag & Drop) event handler
     260 */
     261MRESULT qt_dispatchDragAndDrop(QWidget *widget, const QMSG &qmsg)
     262{
     263    static HWND lastDragOverHwnd = 0; // last target window
     264    static USHORT lastDragOverOp = 0; // last DM_DRAGOVER operation
     265    static USHORT lastRealOp = 0; // last real op (never DO_DEFAULT or DO_UNKNOWN)
     266    static USHORT defaultOp = 0; // default op for DO_DEFAULT or DO_UNKNOWN
     267
     268    static USHORT supportedOps = 0; // operations supported by all items
     269    static bool sourceAllowsOp = FALSE; // does source allow requested operation
     270
     271    static bool lastAccept = FALSE; // last reply from the target
     272    static bool lastAcceptAction = FALSE; // last action reply from the target
     273
     274    static QPMDragData dragData;
     275
     276    Q_ASSERT(widget);
     277
     278    BOOL ok = FALSE;
     279
     280    switch( qmsg.msg ) {
     281        case DM_DRAGOVER: {
     282            bool first = lastDragOverHwnd != qmsg.hwnd;
     283            if (first) {
     284                // the first DM_DRAGOVER message
     285                lastDragOverHwnd = qmsg.hwnd;
     286                lastAccept = lastAcceptAction = FALSE;
     287                supportedOps = DO_COPYABLE | DO_MOVEABLE | DO_LINKABLE;
     288                // ensure drag data is reset (just in case of a wrong msg flow...)
     289                dragData.reset();
     290            }
     291
     292            Q_ASSERT(first || widget->acceptDrops());
     293            if (!widget->acceptDrops()) {
     294                if (!first) {
     295                    // Odin32 apps are dramatically bogus, they continue to send
     296                    // DM_DRAGOVER even if we reply DOR_NEVERDROP. Simulate
     297                    // DM_DRAGLEAVE
     298                    lastDragOverHwnd = 0;
     299                    dragData.reset();
     300                }
     301                return MRFROM2SHORT(DOR_NEVERDROP, 0);
     302            }
     303
     304            DRAGINFO *info = (DRAGINFO *)qmsg.mp1;
     305            ok = DrgAccessDraginfo(info);
     306            Q_ASSERT(ok && info);
     307            if (!ok || !info)
     308                return MRFROM2SHORT(DOR_NEVERDROP, 0);
     309
     310            USHORT dropReply = DOR_DROP;
     311
     312            if (first) {
     313                // determine the set of operations supported by *all* items
     314                // (this implies that DRAGITEM::fsSupportedOps is a bit field)
     315                ULONG itemCount = DrgQueryDragitemCount(info);
     316                for (ULONG i = 0; i < itemCount; ++ i) {
     317                    PDRAGITEM item = DrgQueryDragitemPtr(info, i);
     318                    Q_ASSERT(item);
     319                    if (!item) {
     320                        dropReply = DOR_NEVERDROP;
     321                        break;
     322                    }
     323                    supportedOps &= item->fsSupportedOps;
     324                }
     325                if (dropReply != DOR_NEVERDROP) {
     326                    Q_ASSERT(itemCount);
     327                    if (!itemCount || !supportedOps) {
     328                        // items don't have even a single common operation...
     329                        dropReply = DOR_NEVERDROP;
     330                    } else {
     331                        // determine the default operation
     332                        // (in order MOVE, COPY, LINK, for compatibility with Win32)
     333                        if      (supportedOps & DO_MOVEABLE) defaultOp = DO_MOVE;
     334                        else if (supportedOps & DO_COPYABLE) defaultOp = DO_COPY;
     335                        else if (supportedOps & DO_LINKABLE) defaultOp = DO_LINK;
     336                        else Q_ASSERT(false); // should never happen
     337                    }
     338                }
     339            }
     340
     341            if (dropReply != DOR_NEVERDROP) {
     342
     343                if (first || lastDragOverOp != info->usOperation) {
     344                    // the current drop operation was changed by a modifier key
     345                    lastDragOverOp = info->usOperation;
     346                    lastRealOp = info->usOperation;
     347                    if (lastRealOp == DO_DEFAULT || lastRealOp == DO_UNKNOWN) {
     348                        // the default operation is requested
     349                        lastRealOp = defaultOp;
     350                    }
     351                    sourceAllowsOp =
     352                        ((supportedOps & DO_MOVEABLE) && lastRealOp == DO_MOVE) ||
     353                        ((supportedOps & DO_COPYABLE) && lastRealOp == DO_COPY) ||
     354                        ((supportedOps & DO_LINKABLE) && lastRealOp == DO_LINK);
     355                }
     356
     357                // Note that if sourceAllowsOp = false here, we have to deliver
     358                // events anyway (stealing them from Qt would be confusing), but
     359                // we will silently ignore any accept commands and always reject
     360                // the drop. Other platforms seem to do the same.
     361
     362                // convert the operation to an Action
     363                Qt::DropAction action = Qt::CopyAction;
     364                if      (lastRealOp == DO_COPY) action = Qt::CopyAction;
     365                else if (lastRealOp == DO_MOVE) action = Qt::MoveAction;
     366                else if (lastRealOp == DO_LINK) action = Qt::LinkAction;
     367                else Q_ASSERT(false); // should never happen
     368
     369                // flip y coordinate
     370                QPoint pnt(info->xDrop, info->yDrop);
     371                pnt.setY(QApplication::desktop()->height() - (pnt.y() + 1));
     372                pnt = widget->mapFromGlobal(pnt);
     373
     374                // initialize drag data used in QPMDrag.../QPMDrop... events
     375                if (first)
     376                    dragData.initialize(info);
     377
     378                // @todo later
     379#if 0
     380                QDropEvent *de = NULL;
     381                QPMDragEnterEvent *dee = NULL;
     382                QPMDragMoveEvent *dme = NULL;
     383
     384                if (first)
     385                    de = dee = new QPMDragEnterEvent(pnt, &dragData);
     386                else
     387                    de = dme = new QPMDragMoveEvent(pnt, &dragData);
     388
     389                de->setAction(action);
     390                de->accept(lastAccept);
     391                de->acceptAction(lastAcceptAction);
     392
     393                QApplication::sendEvent(widget, de);
     394
     395                // if not allowed or not accepted, always reply DOR_NODROP
     396                // to have DM_DRAGOVER delivered to us again in any case
     397
     398                dropReply = sourceAllowsOp && de->isAccepted() ?
     399                            DOR_DROP : DOR_NODROP;
     400
     401                lastAccept = dee ? dee->isTrulyAccepted() :
     402                                   dme->isTrulyAccepted();
     403                lastAcceptAction = dee ? dee->isActionTrulyAccepted() :
     404                                         dme->isActionTrulyAccepted();
     405                delete de;
     406#endif
     407            }
     408
     409            DrgFreeDraginfo(info);
     410
     411            return MRFROM2SHORT(dropReply, lastRealOp);
     412        }
     413        case DM_DRAGLEAVE: {
     414            // Odin32 apps produce incorrect message flow, ignore
     415            Q_ASSERT(lastDragOverHwnd != 0);
     416            if (lastDragOverHwnd == 0)
     417                return 0;
     418
     419            lastDragOverHwnd = 0;
     420            dragData.reset();
     421
     422            if (!widget->acceptDrops())
     423                return 0;
     424
     425            QDragLeaveEvent de;
     426            QApplication::sendEvent(widget, &de);
     427            return 0;
     428        }
     429        case DM_DROP: {
     430            // Odin32 apps produce incorrect message flow, ignore
     431            Q_ASSERT(lastDragOverHwnd != 0);
     432            if (lastDragOverHwnd == 0)
     433                return 0;
     434
     435            // Odin32 apps send DM_DROP even if we replied DOR_NEVERDROP or
     436            // DOR_NODROP, simulate DM_DRAGLEAVE
     437            Q_ASSERT(lastAccept || lastAcceptAction);
     438            if (!lastAccept && !lastAcceptAction) {
     439                WinSendMsg(qmsg.hwnd, DM_DRAGLEAVE, 0, 0);
     440                return 0;
     441            }
     442
     443            lastDragOverHwnd = 0;
     444
     445            Q_ASSERT(widget->acceptDrops());
     446            if (!widget->acceptDrops())
     447                return 0;
     448
     449            DRAGINFO *info = (DRAGINFO *)qmsg.mp1;
     450            ok = DrgAccessDraginfo(info);
     451            Q_ASSERT(ok && info);
     452            if (!ok || !info)
     453                return MRFROM2SHORT(DOR_NEVERDROP, 0);
     454
     455            Q_ASSERT(lastRealOp == info->usOperation);
     456
     457            // convert the operation to an Action
     458            Qt::DropAction action = Qt::CopyAction;
     459            if      (lastRealOp == DO_COPY ) action = Qt::CopyAction;
     460            else if (lastRealOp == DO_MOVE ) action = Qt::MoveAction;
     461            else if (lastRealOp == DO_LINK ) action = Qt::LinkAction;
     462            else Q_ASSERT(false); // should never happen
     463
     464            // flip y coordinate
     465            QPoint pnt(info->xDrop, info->yDrop);
     466            pnt.setY(QApplication::desktop()->height() - (pnt.y() + 1));
     467
     468            dragData.setDropped(true);
     469
     470            // @todo later
     471#if 0
     472            QPMDropEvent de(widget->mapFromGlobal(pnt), &dragData);
     473            de.setAction(action);
     474            de.accept(lastAccept);
     475            de.acceptAction(lastAcceptAction);
     476
     477            QApplication::sendEvent(widget, &de);
     478
     479            dragData.reset(de.isTrulyAccepted(), de.isActionTrulyAccepted());
     480
     481            // If the target has accepted the particular Drop action (using
     482            // acceptProposedAction() rather than just accept()), it means that
     483            // it will perform the necessary operation on its own (for example,
     484            // will delete the source if the accepted action is Move). In this
     485            // case, we always send DMFL_TARGETFAIL to the source to prevent it
     486            // from doing the same on its side.
     487
     488            ULONG targetReply =
     489                de.isTrulyAccepted() && !de.isActionTrulyAccepted() ?
     490                DMFL_TARGETSUCCESSFUL : DMFL_TARGETFAIL;
     491#else
     492            ULONG targetReply = DMFL_TARGETFAIL;
     493#endif
     494
     495            // send DM_ENDCONVERSATION for every item
     496            ULONG itemCount = DrgQueryDragitemCount(info);
     497            for (ULONG i = 0; i < itemCount; ++ i) {
     498                PDRAGITEM item = DrgQueryDragitemPtr(info, i);
     499                Q_ASSERT(item);
     500                if (!item)
     501                    continue;
     502                // it is possible that this item required DM_RENDERPREPARE but
     503                // returned FALSE in reply to it (so hwndItem may be NULL)
     504                if (!item->hwndItem)
     505                    continue;
     506                DrgSendTransferMsg(item->hwndItem, DM_ENDCONVERSATION,
     507                                   MPFROMLONG(item->ulItemID),
     508                                   MPFROMLONG(targetReply));
     509            }
     510
     511            DrgDeleteDraginfoStrHandles(info);
     512            DrgFreeDraginfo(info);
     513
     514            return 0;
     515        }
     516        default:
     517            break;
     518    }
     519
     520    return WinDefWindowProc(qmsg.hwnd, qmsg.msg, qmsg.mp1, qmsg.mp2);
     521}
    60522
    61523//---------------------------------------------------------------------
Note: See TracChangeset for help on using the changeset viewer.