Changeset 97


Ignore:
Timestamp:
Jul 6, 2006, 11:25:56 PM (19 years ago)
Author:
dmik
Message:

kernel: Created the initial implementation of the Drag'n'Drop support for OS/2 (not fully functiona yet: updating widget contents under the drag pointer will cause screen corruption, setting the drag object's pixmap is not possible). See ticket:22 for more info.

Location:
trunk
Files:
1 added
9 edited

Legend:

Unmodified
Added
Removed
  • trunk/configure.cmd

    r95 r97  
    6969 *  yet on OS/2. NOTE: don't touch this variable!
    7070 */
    71 G.!QTMandatoryDefines  = 'QT_NO_DRAGANDDROP QT_NO_PRINTER QT_NO_IPV6'
     71G.!QTMandatoryDefines  = 'QT_NO_PRINTER QT_NO_IPV6'
    7272
    7373/* Configuration cache file */
  • trunk/include/qdragobject.h

    r8 r97  
    219219
    220220#ifndef QT_NO_DRAGANDDROP
    221 //@@TODO (dmik): no QDragManager yet (MOC ignores preprocessor macros)
     221
     222// QDragManager is not part of the public API.  It is defined in a
     223// header file simply so different .cpp files can implement different
     224// member functions.
    222225//
    223 //// QDragManager is not part of the public API.  It is defined in a
    224 //// header file simply so different .cpp files can implement different
    225 //// member functions.
    226 ////
    227 //
    228 //class Q_EXPORT QDragManager: public QObject {
    229 //    Q_OBJECT
    230 //
    231 //private:
    232 //    QDragManager();
    233 //    ~QDragManager();
    234 //    // only friend classes can use QDragManager.
    235 //    friend class QDragObject;
    236 //    friend class QDragMoveEvent;
    237 //    friend class QDropEvent;
    238 //    friend class QApplication;
    239 //
    240 //    bool eventFilter( QObject *, QEvent * );
    241 //    void timerEvent( QTimerEvent* );
    242 //
    243 //    bool drag( QDragObject *, QDragObject::DragMode );
    244 //
    245 //    void cancel( bool deleteSource = TRUE );
    246 //    void move( const QPoint & );
    247 //    void drop();
    248 //    void updatePixmap();
    249 //
    250 //private:
    251 //    QDragObject * object;
    252 //    void updateMode( ButtonState newstate );
    253 //    void updateCursor();
    254 //
    255 //    QWidget * dragSource;
    256 //    QWidget * dropWidget;
    257 //    bool beingCancelled;
    258 //    bool restoreCursor;
    259 //    bool willDrop;
    260 //
    261 //    QPixmap *pm_cursor;
    262 //    int n_cursor;
    263 //#if defined(Q_DISABLE_COPY) // Disabled copy constructor and operator=
    264 //    QDragManager( const QDragManager & );
    265 //    QDragManager &operator=( const QDragManager & );
    266 //#endif
    267 //};
    268 //
     226
     227class Q_EXPORT QDragManager: public QObject {
     228    Q_OBJECT
     229
     230private:
     231    QDragManager();
     232    ~QDragManager();
     233    // only friend classes can use QDragManager.
     234    friend class QDragObject;
     235    friend class QDragMoveEvent;
     236    friend class QDropEvent;
     237    friend class QApplication;
     238
     239    bool eventFilter( QObject *, QEvent * );
     240    void timerEvent( QTimerEvent* );
     241
     242    bool drag( QDragObject *, QDragObject::DragMode );
     243
     244    void cancel( bool deleteSource = TRUE );
     245    void move( const QPoint & );
     246    void drop();
     247    void updatePixmap();
     248
     249private:
     250    QDragObject * object;
     251    void updateMode( ButtonState newstate );
     252    void updateCursor();
     253
     254    QWidget * dragSource;
     255    QWidget * dropWidget;
     256    bool beingCancelled;
     257    bool restoreCursor;
     258    bool willDrop;
     259
     260    QPixmap *pm_cursor;
     261    int n_cursor;
     262#if defined(Q_DISABLE_COPY) // Disabled copy constructor and operator=
     263    QDragManager( const QDragManager & );
     264    QDragManager &operator=( const QDragManager & );
     265#endif
     266};
     267
    269268#endif
    270269
  • trunk/include/qmime.h

    r8 r97  
    160160
    161161#ifndef QT_H
    162 #include "qptrlist.h" // down here for GCC 2.7.* compatibility
     162#include "qptrlist.h"
     163#include "qstrlist.h"
    163164#endif // QT_H
    164165
    165166/*
    166   Encapsulation of conversion between MIME and OS/2 PM clipboard formats.
    167   Not need on X11, as the underlying protocol uses the MIME standard
    168   directly.
     167  Encapsulation of conversion between MIME and OS/2 PM clipboard formats
     168  and between MIME and Direct Manipulation (DND) mechanisms and formats.
    169169*/
    170170
    171 class Q_EXPORT QPMMime {
     171class Q_EXPORT QPMMime
     172{
    172173public:
     174
     175#if !defined(QT_NO_DRAGANDDROP)
     176
     177    class Messenger
     178    {
     179    public:
     180        Messenger();
     181        virtual ~Messenger();
     182        HWND hwnd() const { return w; }
     183        virtual MRESULT message( ULONG msg, MPARAM mp1, MPARAM mp2 ) = 0;
     184    private:
     185        HWND w;
     186        static MRESULT EXPENTRY windowProc( HWND, ULONG, MPARAM, MPARAM );
     187    };
     188
     189    class DragWorker
     190    {
     191    public:
     192        DragWorker() : src( NULL ) {}
     193        virtual ~DragWorker() {}
     194   
     195        const QMimeSource *source() const { return src; }
     196       
     197        virtual void init() {}
     198        // methods always implemented
     199        virtual bool cleanup( bool isCancelled ) = 0;
     200        virtual bool isExclusive() const = 0;
     201        virtual ULONG itemCount() const = 0;
     202        virtual HWND hwnd() const = 0;
     203        // methods implemented if isExclusive() == TRUE and itemCount() == 0
     204        virtual DRAGINFO *createDragInfo( const char *name, USHORT supportedOps )
     205                                        { return NULL; }
     206        // methods implemented if itemCount() >= 0
     207        virtual QCString composeFormatString() { return QCString(); }
     208        virtual bool prepare( const char *drm, const char *drf, DRAGITEM *item,
     209                              ULONG itemIndex ) { return FALSE; }
     210        virtual void defaultFileType( const char *&type, const char *&ext ) {};
     211       
     212    private:
     213        const QMimeSource *src;
     214        friend class QPMCoopDragWorker;
     215        friend class QDragManager;
     216    };
     217   
     218    class DefaultDragWorker : public DragWorker, public Messenger
     219    {
     220    private:
     221        DefaultDragWorker( bool exclusive );
     222    public:
     223        virtual ~DefaultDragWorker();
     224   
     225        // DragpWorker interface
     226        bool cleanup( bool isCancelled );
     227        bool isExclusive() const;
     228        ULONG itemCount() const;
     229        HWND hwnd() const { return Messenger::hwnd(); }
     230        QCString composeFormatString();
     231        bool prepare( const char *drm, const char *drf, DRAGITEM *item,
     232                      ULONG itemIndex );
     233        void defaultFileType( const char *&type, const char *&ext );
     234       
     235        // Messenger interface
     236        MRESULT message( ULONG msg, MPARAM mp1, MPARAM mp2 );
     237
     238        class Provider
     239        {
     240        public:
     241            virtual const char *format( const char *drf ) const = 0;
     242            virtual bool provide( const char *drf, const QByteArray &allData,
     243                                  ULONG itemIndex, QByteArray &itemData ) = 0;
     244            virtual void fileType( const char *drf, const char *&type,
     245                                   const char *&ext ) {};
     246        };
     247       
     248        bool addProvider( const char *drf, Provider *provider,
     249                          ULONG itemCount = 1 );
     250       
     251        static bool canRender( const char *drm );
     252       
     253    private:
     254        struct Data;
     255        Data *d;
     256        friend class QPMMime;
     257    };
     258   
     259    class DropWorker
     260    {
     261    public:
     262        DropWorker() : nfo( NULL ) {}
     263        virtual ~DropWorker() {}
     264       
     265        DRAGINFO *info() const { return nfo; }
     266
     267        virtual void init() {}
     268        virtual void cleanup( bool isAccepted, bool isActAccepted ) {}
     269        virtual bool isExclusive() const = 0;
     270        virtual bool provides( const char *format ) const = 0;
     271        virtual int formatCount() const = 0;
     272        virtual const char *format( int fn ) const = 0;
     273        virtual QByteArray encodedData( const char *format ) const = 0;
     274       
     275    private:
     276        DRAGINFO *nfo;
     277        friend class QPMDragData;
     278    };
     279
     280    class DefaultDropWorker : public DropWorker, public Messenger
     281    {
     282    private:
     283        DefaultDropWorker();
     284    public:
     285        virtual ~DefaultDropWorker();
     286   
     287        // DropWorker interface
     288        void cleanup( bool isAccepted, bool isActAccepted );
     289        bool isExclusive() const;
     290        bool provides( const char *format ) const;
     291        int formatCount() const;
     292        const char *format( int fn ) const;
     293        QByteArray encodedData( const char *format ) const;
     294       
     295        // Messenger interface
     296        MRESULT message( ULONG msg, MPARAM mp1, MPARAM mp2 );
     297   
     298        class Provider
     299        {
     300        public:
     301            virtual const char *drf( const char *format ) const = 0;
     302            virtual bool provide( const char *format, ULONG itemIndex,
     303                                  const QByteArray &itemData,
     304                                  QByteArray &allData ) = 0;
     305        };
     306       
     307        bool addProvider( const char *format, Provider *provider );
     308        bool addExclusiveProvider( const char *format, Provider *provider );
     309       
     310        static bool canRender( DRAGITEM *item, const char *drf );
     311        static bool getSupportedRMFs( DRAGITEM *item, QPtrList<QStrList> &list );
     312       
     313    private:
     314        struct Data;
     315        Data *d;
     316        friend class QPMMime;
     317    };
     318
     319#endif // !QT_NO_DRAGANDDROP
     320
    173321    enum { CFI_Storage = CFI_POINTER & CFI_HANDLE };
    174322   
     
    182330    static const char* cfToMime( ulong cf );
    183331
    184     static ulong registerMimeType( const char *mime );
    185 
     332#if !defined(QT_NO_DRAGANDDROP)
     333   
     334    static QCString queryHSTR( HSTR hstr );
     335    static QCString querySourceNameFull( DRAGITEM *item );
     336    static bool canTargetRenderAsOS2File( DRAGITEM *item, QCString *fullName = NULL );
     337    static bool parseRMFs( HSTR rmfs, QPtrList<QStrList> &list );
     338    static bool parseRMF( HSTR rmf, QCString &mechanism, QCString &format );
     339   
     340    static DefaultDragWorker *defaultCoopDragWorker();
     341    static DefaultDragWorker *defaultExclDragWorker();
     342    static DefaultDropWorker *defaultDropWorker();
     343
     344#endif // !QT_NO_DRAGANDDROP
     345
     346    // Clipboard format convertor interface
     347   
    186348    virtual const char* convertorName() = 0;
    187349    virtual int countCf() = 0;
     
    193355    virtual QByteArray convertToMime( QByteArray data, const char *mime, ulong cf ) = 0;
    194356    virtual QByteArray convertFromMime( QByteArray data, const char *mime, ulong cf ) = 0;
     357   
     358#if !defined(QT_NO_DRAGANDDROP)
     359
     360    // Direct Manipulation (DND) convertor interface
     361
     362    virtual DragWorker *dragWorkerFor( const char *mime, QMimeSource *src ) { return 0; }
     363    virtual DropWorker *dropWorkerFor( DRAGINFO *info ) { return 0; }
     364   
     365#endif // !QT_NO_DRAGANDDROP
    195366};
    196367
  • trunk/include/qwindowdefs_pm.h

    r61 r97  
    7878#endif
    7979
     80/*
     81 *  Undoc'd DC_PREPAREITEM, see
     82 *  http://lxr.mozilla.org/seamonkey/source/widget/src/os2/nsDragService.cpp
     83 */
     84#if !defined (DC_PREPAREITEM)
     85#define DC_PREPAREITEM 0x40
     86#endif
     87
    8088typedef HWND WId;
    8189
  • trunk/src/kernel/qapplication_pm.cpp

    r78 r97  
    657657    // there is no need to unregister private window classes -- it is done
    658658    // automatically upon process termination.
    659 //    QAsciiDictIterator<int> it(*winclassNames);
    660 //    const char *k;
    661 //    while ( (k = it.currentKey()) ) {
    662 //    }
    663659    delete winclassNames;
    664660    winclassNames = 0;
     
    913909extern uint qGlobalPostedEventsCount();
    914910
     911#ifndef QT_NO_DRAGANDDROP
     912extern MRESULT qt_dispatchDragAndDrop( QWidget *, const QMSG & ); // qdnd_pm.cpp
     913#endif
     914
    915915/*!
    916916    The message procedure calls this function for every message
     
    945945    }
    946946}
    947 
    948947
    949948//
     
    11691168        result = widget->translateWheelEvent( qmsg );
    11701169        rc = (MRESULT) result;
     1170#endif
     1171#ifndef QT_NO_DRAGANDDROP
     1172    } else if ( msg >= WM_DRAGFIRST && msg <= WM_DRAGLAST ) {
     1173        RETURN( qt_dispatchDragAndDrop( widget, qmsg ) );
    11711174#endif
    11721175    } else {
     
    15891592            break;
    15901593
    1591 /// @todo (dmik) later
     1594/// @todo (dmik) remove?
    15921595//      case WM_IME_STARTCOMPOSITION:
    15931596//          result = QInputContext::startComposition();
     
    21822185static int qt_extraKeyState = 0;
    21832186
    2184 // State holder for mouse buttons
    2185 static int qt_buttonState = 0;
     2187static int mouseButtonState()
     2188{
     2189    int state = 0;
     2190
     2191    if ( WinGetKeyState( HWND_DESKTOP, VK_BUTTON1 ) & 0x8000 )
     2192        state |= Qt::LeftButton;
     2193    if ( WinGetKeyState( HWND_DESKTOP, VK_BUTTON2 ) & 0x8000 )
     2194        state |= Qt::RightButton;
     2195    if ( WinGetKeyState( HWND_DESKTOP, VK_BUTTON3 ) & 0x8000 )
     2196        state |= Qt::MidButton;
     2197
     2198    return state;
     2199}
    21862200
    21872201//
     
    21952209    autoCaptureWnd = h;
    21962210
    2197     if ( !qt_buttonState ) {
     2211    if ( !mouseButtonState() ) {
    21982212        // all buttons released, we don't actually capture the mouse
    21992213        // (see QWidget::translateMouseEvent())
     
    22422256static int translateButtonState( USHORT s, int type, int button )
    22432257{
    2244     if ( type == QEvent::MouseButtonPress )
    2245         qt_buttonState |= ( button & Qt::MouseButtonMask );
    2246     if ( type == QEvent::MouseButtonRelease )
    2247         qt_buttonState &= ~( button & Qt::MouseButtonMask );
    2248 
    2249     int bst = qt_buttonState;
     2258    int bst = mouseButtonState();
     2259
    22502260    if ( type == QEvent::ContextMenu ) {
    22512261        if ( WinGetKeyState( HWND_DESKTOP, VK_SHIFT ) & 0x8000 )
     
    22792289}
    22802290
    2281 //@@TODO (dmik): later
    2282 //// In DnD, the mouse release event never appears, so the
    2283 //// mouse button state machine must be manually reset
    2284 ///*! \internal */
    2285 //void QApplication::winMouseButtonUp()
    2286 //{
    2287 //    qt_button_down = 0;
    2288 //    releaseAutoCapture();
    2289 //}
     2291/*! \internal
     2292  In DnD, the mouse release event never appears, so the
     2293  mouse button state machine must be manually reset
     2294*/
     2295void qt_pmMouseButtonUp()
     2296{
     2297    // release any stored mouse capture
     2298    qt_button_down = 0;
     2299    autoCaptureReleased = TRUE;
     2300    releaseAutoCapture();
     2301}
    22902302
    22912303bool QETWidget::translateMouseEvent( const QMSG &qmsg )
     
    23582370    type   = (QEvent::Type)mouseTbl[++i];       // event type
    23592371    button = mouseTbl[++i];                     // which button
    2360 //@@TODO (dmik): later (extra buttons)
     2372/// @todo (dmik) later (extra buttons)
    23612373//    if ( button > Qt::MidButton ) {
    23622374//      switch( GET_XBUTTON_WPARAM( msg.wParam ) ) {
     
    24242436#endif
    24252437        if ( curWin != winId() ) {              // new current window
    2426 //@@TODO (dmik): add CS_HITTEST to our window classes and handle WM_HITTEST,
     2438/// @todo (dmik)           
     2439//  add CS_HITTEST to our window classes and handle WM_HITTEST,
    24272440//  otherwise disabled windows will not get mouse events?
    24282441            qt_dispatchEnterLeave( this, QWidget::find(curWin) );
  • trunk/src/kernel/qmime_pm.cpp

    r20 r97  
    4040#ifndef QT_NO_MIME
    4141
     42#include <stdlib.h>
     43#include <time.h>
     44
    4245#include "qstrlist.h"
    4346#include "qimage.h"
     
    4548#include "qdragobject.h"
    4649#include "qbuffer.h"
     50#include "qapplication.h"
    4751#include "qapplication_p.h"
     52#include "qeventloop.h"
    4853#include "qtextcodec.h"
    49 #include "qregexp.h"
     54#include "qdir.h"
     55#include "qfile.h"
     56#include "qurl.h"
     57#include "qvaluelist.h"
     58#include "qintdict.h"
     59#include "qptrlist.h"
    5060#include "qt_os2.h"
    5161
     62// if you enable this, it makes sense to enable it in qdnd_pm.cpp as well
     63//#define QT_DEBUG_DND
     64
     65#if !defined(QT_NO_DRAGANDDROP)
     66
     67#define DRT_URL "UniformResourceLocator"
     68
     69#define DRM_OS2FILE "DRM_OS2FILE"
     70#define DRM_SHAREDMEM "DRM_SHAREDMEM"
     71
     72#define DRF_UNKNOWN "DRF_UNKNOWN"
     73#define DRF_TEXT "DRF_TEXT"
     74#define DRF_POINTERDATA "DRF_POINTERDATA"
     75
     76#define MIME_TEXT_PLAIN "text/plain"
     77#define MIME_TEXT_PLAIN_CHARSET_SYSTEM "text/plain;charset=system"
     78#define MIME_TEXT_URI_LIST "text/uri-list"
     79
     80/*! \internal
     81  According to my tests, DrgFreeDragtransfer() appears to be bogus: when the
     82  drag source attempts to free the DRAGTRANSFER structure passed to it in
     83  DM_RENDERPREPARE/DM_RENDER by another process, the shared memory object is not
     84  actually released until DrgFreeDragtransfer() is called for the second time.
     85  This method tries to fix this problem.
     86 
     87  \note The problem (and the solution) was not tested on platforms other than
     88  eCS 1.2.1 GA!
     89*/
     90void qt_DrgFreeDragtransfer( DRAGTRANSFER *xfer )
     91{
     92    Q_ASSERT( xfer );
     93    if ( xfer ) {
     94        BOOL ok = DrgFreeDragtransfer( xfer );
     95        Q_ASSERT( ok );
     96        if ( ok ) {
     97            ULONG size = ~0, flags = 0;
     98            APIRET rc = DosQueryMem( xfer, &size, &flags );
     99            Q_ASSERT( rc == 0 );
     100            if ( rc == 0 && !(flags & PAG_FREE) ) {
     101                PID pid;
     102                TID tid;
     103                ok = WinQueryWindowProcess( xfer->hwndClient, &pid, &tid );
     104                Q_ASSERT( ok );
     105                if ( ok ) {
     106                    PPIB ppib = NULL;
     107                    DosGetInfoBlocks( NULL, &ppib );
     108                    if ( ppib->pib_ulpid != pid ) {
     109#if defined(QT_DEBUG_DND)                       
     110                        qDebug( "qt_DrgFreeDragtransfer: Will free xfer %p "
     111                                "TWICE (other process)!", xfer );
     112#endif                       
     113                        DrgFreeDragtransfer( xfer );
     114                    }
     115                }
     116            }
     117        }
     118    }
     119}
     120
     121#define SEA_TYPE ".TYPE"
     122
     123/*! \internal
     124  Sets a single .TYPE EA vaule on a given fle.
     125*/
     126static void qt_SetFileTypeEA( const char *name, const char *type)
     127{
     128    #pragma pack(1)
     129   
     130    struct MY_FEA2 {
     131        ULONG   oNextEntryOffset;  /*  Offset to next entry. */
     132        BYTE    fEA;               /*  Extended attributes flag. */
     133        BYTE    cbName;            /*  Length of szName, not including NULL. */
     134        USHORT  cbValue;           /*  Value length. */
     135        CHAR    szName[0];         /*  Extended attribute name. */
     136        /* EA value follows here */
     137    };
     138   
     139    struct MY_FEA2LIST {
     140        ULONG   cbList;            /*  Total bytes of structure including full list. */
     141        MY_FEA2 list[0];           /*  Variable-length FEA2 structures. */
     142    };
     143   
     144    struct MY_FEA2_VAL {
     145        USHORT  usEAType;          /* EA value type (one of EAT_* constants) */
     146        USHORT  usValueLen;        /* Length of the value data following */
     147        CHAR    aValueData[0];     /* value data */
     148    };
     149   
     150    struct MY_FEA2_MVMT {
     151        USHORT      usEAType;      /* Always EAT_MVMT */
     152        USHORT      usCodePage;    /* 0 - default */
     153        USHORT      cbNumEntries;  /* Number of MYFEA2_VAL structs following */
     154        MY_FEA2_VAL aValues[0];    /* MYFEA2_VAL value structures */
     155    };
     156   
     157    #pragma pack()
     158   
     159    uint typeLen = qstrlen( type );
     160    uint valLen = sizeof( MY_FEA2_VAL ) + typeLen;
     161    uint mvmtLen = sizeof( MY_FEA2_MVMT ) + valLen;
     162    uint fea2Len = sizeof( MY_FEA2 ) + sizeof( SEA_TYPE );
     163    uint fullLen = sizeof( MY_FEA2LIST ) + fea2Len + mvmtLen;
     164   
     165    uchar *eaData = new uchar[ fullLen ];
     166
     167    MY_FEA2LIST *fea2List = (MY_FEA2LIST *) eaData;
     168    fea2List->cbList = fullLen;
     169   
     170    MY_FEA2 *fea2 = fea2List->list;
     171    fea2->oNextEntryOffset = 0;
     172    fea2->fEA = 0;
     173    fea2->cbName = sizeof( SEA_TYPE ) - 1;
     174    fea2->cbValue = mvmtLen;
     175    strcpy( fea2->szName, SEA_TYPE );
     176   
     177    MY_FEA2_MVMT *mvmt = (MY_FEA2_MVMT *) (fea2->szName + sizeof( SEA_TYPE ));
     178    mvmt->usEAType = EAT_MVMT;
     179    mvmt->usCodePage = 0;
     180    mvmt->cbNumEntries = 1;
     181   
     182    MY_FEA2_VAL *val = mvmt->aValues;
     183    val->usEAType = EAT_ASCII;
     184    val->usValueLen = typeLen;
     185    memcpy( val->aValueData, type, typeLen );
     186
     187    EAOP2 eaop2;
     188    eaop2.fpGEA2List = 0;
     189    eaop2.fpFEA2List = (FEA2LIST *) fea2List;
     190    eaop2.oError = 0;
     191
     192    APIRET rc = DosSetPathInfo( name, FIL_QUERYEASIZE,
     193                                &eaop2, sizeof( eaop2 ), 0 );
     194#ifndef QT_NO_DEBUG
     195    if ( rc )
     196        qSystemWarning( "Could not set file EAs", rc );
     197#endif   
     198
     199    delete[] eaData;   
     200}
     201
     202static int hex_to_int( uchar c )
     203{
     204    if ( c >= 'A' && c <= 'F' ) return c - 'A' + 10;
     205    if ( c >= 'a' && c <= 'f' ) return c - 'a' + 10;
     206    if ( c >= '0' && c <= '9' ) return c - '0';
     207    return -1;
     208}
     209
     210static inline int hex_to_int( char c )
     211{
     212    return hex_to_int( (uchar) c );
     213}
     214
     215// -----------------------------------------------------------------------------
     216
     217static QPtrList<HWND> msgWindows;
     218
     219static void destroyAllMessengerWindows()
     220{
     221    for ( HWND *w = msgWindows.first(); w; w = msgWindows.next() ) {
     222        Q_ASSERT( *w );
     223        WinDestroyWindow( *w );
     224        // tell Messenger the window has been destroyed
     225        *w = 0;
     226    }
     227}
     228
     229QPMMime::Messenger::Messenger() : w( 0 )
     230{
     231    static const char *ClassName = "QPMMime::Messenger";
     232    static bool classRegistered = FALSE;
     233    if ( !classRegistered ) {
     234        WinRegisterClass( 0, ClassName, windowProc, 0, QWL_USER + sizeof(PVOID) );
     235        classRegistered = TRUE;
     236        // make sure windows are destroyed before the message queue
     237        qAddPostRoutine ( destroyAllMessengerWindows );
     238    }
     239
     240    w = WinCreateWindow( HWND_OBJECT, ClassName,
     241                         NULL, 0, 0, 0, 0, 0, NULL,
     242                         HWND_BOTTOM, 0, this, NULL );
     243    Q_ASSERT( w );
     244    if ( w )
     245        msgWindows.append( &w );
     246}
     247
     248QPMMime::Messenger::~Messenger()
     249{
     250    if ( w ) {
     251        msgWindows.removeRef( &w );
     252        HWND h = w;
     253        w = 0; // tell windowProc() we're unsafe
     254        WinDestroyWindow( h );
     255    }
     256}
     257
     258//static
     259MRESULT EXPENTRY QPMMime::Messenger::windowProc( HWND hwnd, ULONG msg,
     260                                                 MPARAM mp1, MPARAM mp2 )
     261{
     262    if ( msg == WM_CREATE ) {
     263        Messenger *that = (Messenger *) mp1;
     264        if ( !that )
     265            return (MRESULT) TRUE;
     266        WinSetWindowPtr( hwnd, QWL_USER, that );
     267        return (MRESULT) FALSE;
     268    }
     269
     270    Messenger *that = (Messenger *) WinQueryWindowPtr( hwnd, QWL_USER );
     271    Q_ASSERT( that );
     272   
     273    // Note: before WinCreateWindow() returns to the constructor or after the
     274    // destructor has been called, w is 0. We use this to determine that the
     275    // object is in the unsafe state (VTBL is not yet initialized or has been
     276    // already uninitialized), so message() points to never-never land.
     277    if ( !that || !that->w )
     278        return (MRESULT) FALSE;
     279   
     280    return that->message( msg, mp1, mp2 );
     281}
     282
     283// -----------------------------------------------------------------------------
     284
     285struct QPMMime::DefaultDragWorker::Data
     286{
     287    Data( bool excl ) : exclusive( excl ) {}
     288   
     289    struct Request
     290    {
     291        Request( ULONG i, Provider *p, const char *m, const char *f )
     292            : index( i ), provider( p ), drm( m ), drf( f )
     293            , xfer( NULL ), rendered( FALSE ), sharedMem( NULL ) {}
     294
     295        ~Request()
     296        {
     297            // free memory allocated for the target that requested DRM_SHAREDMEM
     298            if ( sharedMem )
     299                DosFreeMem( sharedMem );
     300            Q_ASSERT( !xfer );
     301        }
     302       
     303        ULONG index;
     304        Provider *provider;
     305        QCString drm;
     306        QCString drf;
     307        DRAGTRANSFER *xfer;
     308        bool rendered;
     309        PVOID sharedMem;
     310    };
     311   
     312    inline bool isInitialized() { return providers.count() != 0; }
     313    void cleanupRequests();
     314   
     315    struct DrfProvider
     316    {
     317        DrfProvider() : prov( NULL ) {}
     318        DrfProvider( const char *d, Provider *p ) : drf( d ), prov( p ) {}
     319        const QCString drf;
     320        Provider * const prov;
     321    };
     322
     323    typedef QValueList<DrfProvider> DrfProviderList;
     324   
     325    Provider *providerFor( const char *drf )
     326    {
     327        for ( DrfProviderList::const_iterator it = providers.begin();
     328              it != providers.end(); ++it )
     329            if ( qstrcmp( (*it).drf, drf ) == 0 )
     330                return (*it).prov;
     331        return NULL;
     332    }
     333   
     334    const bool exclusive : 1;
     335    DrfProviderList providers;
     336   
     337    ULONG itemCnt;
     338    QIntDict<Request> requests;
     339    bool renderOk : 1;
     340};
     341
     342void QPMMime::DefaultDragWorker::Data::cleanupRequests()
     343{
     344    if ( requests.count() ) {
     345#if defined(QT_CHECK_STATE)
     346        qWarning( "In the previous DnD session, the drop target sent "
     347                  "DM_RENDERPREPARE/DM_RENDER\n"
     348                  "for some drag item but didn't send DM_ENDCONVERSATION!" );
     349#endif       
     350        requests.clear();
     351    }
     352}
     353
     354QPMMime::DefaultDragWorker::DefaultDragWorker( bool exclusive )
     355    : d( new Data( exclusive ) )
     356{
     357    d->itemCnt = 0;
     358    d->requests.setAutoDelete( TRUE );
     359    d->renderOk = TRUE;
     360}
     361
     362QPMMime::DefaultDragWorker::~DefaultDragWorker()
     363{
     364    d->cleanupRequests();
     365    delete d;
     366}
     367
     368bool QPMMime::DefaultDragWorker::cleanup( bool isCancelled )
     369{
     370    // the return value is: TRUE if the source-side Move for the given
     371    // drag object should be *dis*allowed, FALSE otherwise (including cases
     372    // when this DragWorker didn't participate to the conversation at all)
     373   
     374    // sanity check
     375    Q_ASSERT( d->isInitialized() );
     376    if ( !d->isInitialized() )
     377        return TRUE;
     378   
     379    bool moveDisallowed = FALSE;
     380
     381#if defined(QT_DEBUG_DND)                       
     382    qDebug( "DefaultDragWorker: Session ended (cancelled=%d, requests.left=%d)",
     383            isCancelled, d->requests.count() );
     384#endif
     385
     386    if ( d->requests.count() ) {
     387        // always disallow Move if not all requests got DM_ENDCONVERSATION
     388        moveDisallowed = TRUE;
     389    } else {
     390        // disallow Move if rendering of some item failed
     391        moveDisallowed = !d->renderOk;
     392    }
     393
     394#if defined(QT_DEBUG_DND)                       
     395    qDebug( "DefaultDragWorker: moveDisallowed=%d", moveDisallowed );
     396#endif
     397   
     398    // Note: remaining requests will be lazily deleted by cleanupRequests()
     399    // when a new DND session is started
     400   
     401    d->renderOk = TRUE;
     402    d->itemCnt = 0;
     403       
     404    // Indicate we're cleaned up (i.e. the DND session is finished)
     405    d->providers.clear();
     406
     407    return moveDisallowed;
     408}
     409
     410bool QPMMime::DefaultDragWorker::isExclusive() const
     411{
     412    return d->exclusive;
     413}       
     414
     415ULONG QPMMime::DefaultDragWorker::itemCount() const
     416{
     417    return d->itemCnt;
     418}
     419
     420QCString QPMMime::DefaultDragWorker::composeFormatString()
     421{
     422    QCString formats;
     423
     424    // sanity checks
     425    Q_ASSERT( d->isInitialized() );
     426    if ( !d->isInitialized() )
     427        return formats;
     428
     429    bool first = TRUE;
     430    for ( Data::DrfProviderList::const_iterator it = d->providers.begin();
     431          it != d->providers.end(); ++it ) {
     432        if ( first )
     433            first = FALSE;
     434        else
     435            formats += ",";
     436        formats += (*it).drf;
     437    }
     438
     439    Q_ASSERT( !formats.isNull() );
     440    if ( formats.isNull() )
     441        return formats;
     442   
     443    // DRM_SHAREDMEM comes first to prevent native DRM_OS2FILE
     444    // rendering on the target side w/o involving the source.
     445    // Also, we add <DRM_SHAREDMEM,DRF_POINTERDATA> just like WPS does it
     446    // (however, it doesn't help when dropping objects to it -- WPS still
     447    // chooses DRM_OS2FILE).
     448    formats = "("DRM_SHAREDMEM","DRM_OS2FILE")x(" + formats + "),"
     449              "<"DRM_SHAREDMEM","DRF_POINTERDATA">";
     450
     451#if defined(QT_DEBUG_DND)                       
     452    qDebug( "DefaultDragWorker: formats=%s, itemCnt=%ld",
     453            formats.data(), d->itemCnt );
     454#endif
     455
     456    return formats;
     457}
     458
     459bool QPMMime::DefaultDragWorker::prepare( const char *drm, const char *drf,
     460                                          DRAGITEM *item, ULONG itemIndex )
     461{
     462    // sanity checks
     463    Q_ASSERT( d->isInitialized() );
     464    if ( !d->isInitialized() )
     465        return FALSE;
     466
     467    Q_ASSERT( item && itemIndex < d->itemCnt );
     468    if ( !item || itemIndex >= d->itemCnt )
     469        return FALSE;
     470   
     471#if defined(QT_DEBUG_DND)                       
     472    qDebug( "DefaultDragWorker: Preparing item %ld (id=%ld) for <%s,%s>",
     473            itemIndex, item->ulItemID, drm, drf );
     474#endif
     475
     476    Provider *p = d->providerFor( drf );
     477   
     478    if ( !canRender( drm ) || p == NULL ) {
     479#if defined(QT_DEBUG_DND)                       
     480        qDebug( "DefaultDragWorker: Cannot render the given RMF" );
     481#endif
     482        return FALSE;
     483    }
     484
     485    Data::Request *req = d->requests[ item->ulItemID ];
     486
     487    if ( req ) {
     488        // this item has been already prepared, ensure it has also been
     489        // rendered already
     490        Q_ASSERT( req->index == itemIndex );
     491        Q_ASSERT( req->rendered );
     492        if ( req->index != itemIndex || !req->rendered )
     493            return FALSE;
     494        // remove the old request to free resources
     495        d->requests.remove( item->ulItemID );
     496    }
     497
     498    // store the request
     499    req = new Data::Request( itemIndex, p, drm, drf );
     500    d->requests.insert( item->ulItemID, req );
     501   
     502    return TRUE;
     503}
     504
     505void QPMMime::DefaultDragWorker::defaultFileType( const char *&type,
     506                                                  const char *&ext )
     507{
     508    Q_ASSERT( d->providers.count() );
     509    if ( d->providers.count() ) {
     510        Provider *p = d->providers.front().prov;
     511        Q_ASSERT( p );
     512        if ( p )
     513            p->fileType( d->providers.front().drf, type, ext );
     514    }
     515}
     516
     517MRESULT QPMMime::DefaultDragWorker::message( ULONG msg, MPARAM mp1, MPARAM mp2 )
     518{
     519    if ( msg == DM_RENDER ) {
     520        DRAGTRANSFER *xfer = (DRAGTRANSFER *) mp1;
     521
     522        // sanity checks
     523        Q_ASSERT( d->isInitialized() );
     524        Q_ASSERT( xfer );
     525        if ( !d->isInitialized() || !xfer )
     526            return (MRESULT) FALSE;
     527
     528        Q_ASSERT( xfer->hwndClient && xfer->pditem );
     529        if ( !xfer->hwndClient || !xfer->pditem )
     530            return (MRESULT) FALSE;
     531
     532        Data::Request *req = d->requests[ xfer->pditem->ulItemID ];
     533
     534        // check that this item has been prepared (should always be the case
     535        // because the target would never know our hwnd() otherwise)
     536        Q_ASSERT( req ); // prepared
     537        Q_ASSERT( !req->xfer ); // no DM_RENDER requested
     538        if ( !req || req->xfer )
     539            return (MRESULT) FALSE;
     540
     541#if defined(QT_DEBUG_DND)                       
     542        qDebug( "DefaultDragWorker: Got DM_RENDER to %s for item %ld (id=%ld)",
     543                queryHSTR( xfer->hstrSelectedRMF ).data(),
     544                req->index, xfer->pditem->ulItemID );
     545#endif
     546
     547        QCString drm, drf;
     548        if ( !parseRMF( xfer->hstrSelectedRMF, drm, drf ) )
     549            Q_ASSERT( /* parseRMF() = */ FALSE );
     550       
     551        if ( req->drm != drm || req->drf != drf ) {
     552            xfer->fsReply = DMFL_RENDERRETRY;
     553            return (MRESULT) FALSE;
     554        }
     555       
     556        // indicate that DM_RENDER was requested
     557        req->xfer = xfer;
     558       
     559#if defined(QT_DEBUG_DND)                       
     560        qDebug( "DefaultDragWorker: Will render from [%s] using provider %p",
     561                req->provider->format( drf ), req->provider );
     562#endif
     563
     564        // We would lile to post WM_USER to ourselves to do actual rendering
     565        // after we return from DM_RENDER. But we are inside DrgDrag() at this
     566        // point (our DND implementation is fully synchronous by design), so
     567        // PM will not  deliver this message to us until we return from
     568        // DrgDrag(). Thus, we have to send it.
     569       
     570        WinSendMsg( hwnd(), WM_USER, MPFROMLONG( xfer->pditem->ulItemID ),
     571                                     MPFROMP( req ) );
     572       
     573        return (MRESULT) TRUE;
     574    }
     575
     576    if ( msg == WM_USER ) {
     577        // sanity checks
     578        Q_ASSERT( d->isInitialized() );
     579        if ( !d->isInitialized() )
     580            return (MRESULT) FALSE;
     581
     582        ULONG itemId = LONGFROMMP( mp1 );
     583       
     584        // sanity checks
     585        Data::Request *req = d->requests[ itemId ];
     586        Q_ASSERT( req ); // prepared
     587        Q_ASSERT( req->xfer != NULL ); // DM_RENDER requested
     588        Q_ASSERT( !req->rendered ); // not yet rendered
     589        Q_ASSERT( (Data::Request *) PVOIDFROMMP( mp2 ) == req );
     590        if ( !req || req->xfer == NULL || req->rendered ||
     591             (Data::Request *) PVOIDFROMMP( mp2 ) != req )
     592            return (MRESULT) FALSE;
     593
     594        Q_ASSERT( source() && req->provider && req->index < d->itemCnt );
     595        if ( !source() || !req->provider || req->index >= d->itemCnt )
     596            return (MRESULT) FALSE;
     597           
     598#if defined(QT_DEBUG_DND)                       
     599        qDebug( "DefaultDragWorker: Got DO_RENDER for item %ld (id=%ld), "
     600                "provider=%p, drm=%s, drf=%s",
     601                req->index, req->xfer->pditem->ulItemID,
     602                req->provider, req->drm.data(), req->drf.data() );
     603#endif
     604
     605        bool renderOk = FALSE;
     606        QByteArray allData =
     607            source()->encodedData( req->provider->format( req->drf ) );
     608        QByteArray itemData;
     609
     610        renderOk = req->provider->provide( req->drf, allData,
     611                                           req->index, itemData );
     612
     613        if ( renderOk ) {
     614            enum DRM { OS2File, SharedMem } drmType;
     615            if ( qstrcmp( req->drm, DRM_SHAREDMEM ) == 0 ) drmType = SharedMem;
     616            else drmType = OS2File;
     617           
     618            if ( drmType == OS2File ) {
     619                QCString renderToName = queryHSTR( req->xfer->hstrRenderToName );
     620                Q_ASSERT( !renderToName.isEmpty() );
     621                renderOk = !renderToName.isEmpty();
     622                if ( renderOk ) {
     623#if defined(QT_DEBUG_DND)                       
     624                    qDebug( "DefaultDragWorker: Will write to \"%s\"",
     625                            renderToName.data() );
     626#endif
     627                    QFile file( QFile::decodeName( renderToName ) );
     628                    renderOk = file.open( IO_WriteOnly );
     629                    if ( renderOk ) {
     630                        Q_LONG written =
     631                            file.writeBlock( itemData.data(), itemData.size() );
     632                        renderOk = written == ( Q_LONG ) itemData.size() &&
     633                                   file.status() == IO_Ok;
     634                        file.close();
     635                        if ( renderOk && req->xfer->pditem->hstrType ) {
     636                            // since WPS ignores hstrType, write it manually
     637                            // to the .TYPE EA of the created file
     638                            qt_SetFileTypeEA( renderToName,
     639                                queryHSTR( req->xfer->pditem->hstrType ) );
     640                        }
     641                    }
     642                }
     643            } else {
     644                PID pid;
     645                TID tid;
     646                bool isSameProcess = FALSE;
     647                renderOk = WinQueryWindowProcess( req->xfer->hwndClient,
     648                                                  &pid, &tid );
     649                if ( renderOk ) {
     650                    PPIB ppib = NULL;
     651                    DosGetInfoBlocks( NULL, &ppib );
     652                    isSameProcess = ppib->pib_ulpid == pid;
     653                   
     654                    ULONG sz = itemData.size() + sizeof (ULONG);
     655                    char *ptr = NULL;
     656                    APIRET rc = isSameProcess ?
     657                                DosAllocMem( (PPVOID) &ptr, sz,
     658                                              PAG_COMMIT | PAG_READ | PAG_WRITE ) :               
     659                                DosAllocSharedMem( (PPVOID) &ptr, NULL, sz,
     660                                                    OBJ_GIVEABLE | PAG_COMMIT |
     661                                                    PAG_READ | PAG_WRITE );               
     662                    renderOk = rc == 0;
     663                    if ( renderOk && !isSameProcess ) {
     664                        rc = DosGiveSharedMem( ptr, pid, PAG_READ );
     665                        renderOk = rc == 0;
     666                    }
     667                    if ( renderOk ) {
     668                        *(ULONG *) ptr = itemData.size();
     669                        memcpy( ptr + sizeof (ULONG), itemData.data(),
     670                                itemData.size() );
     671                        req->xfer->hstrRenderToName = (HSTR) ptr;
     672                        req->sharedMem = ptr;
     673#if defined(QT_DEBUG_DND)                       
     674                        qDebug( "DefaultDragWorker: Created shared memory object %p",
     675                                ptr );
     676#endif
     677#ifndef QT_NO_DEBUG
     678                    } else {
     679                        qSystemWarning( "DosAllocSharedMem/DosGiveSharedMem "
     680                                        "failed", rc );
     681#endif                       
     682                    }
     683#ifndef QT_NO_DEBUG
     684                } else {
     685                    qSystemWarning( "WinQueryWindowProcess failed" );
     686#endif                   
     687                }
     688            }
     689        }
     690
     691        req->rendered = TRUE;
     692        // cumulative render result
     693        d->renderOk &= renderOk;
     694
     695#if defined(QT_DEBUG_DND)                       
     696        qDebug( "DefaultDragWorker: renderOk=%d, overall.renderOk=%d",
     697                renderOk, d->renderOk );
     698#endif
     699
     700        // note that we don't allow the target to retry
     701        USHORT reply = renderOk ? DMFL_RENDEROK : DMFL_RENDERFAIL;
     702        DrgPostTransferMsg( req->xfer->hwndClient, DM_RENDERCOMPLETE,
     703                            req->xfer, reply, 0, FALSE );
     704
     705        // DRAGTRANSFER is no more necessary, free it early
     706        qt_DrgFreeDragtransfer( req->xfer );
     707#if defined(QT_DEBUG_DND)                       
     708        {
     709            ULONG size = ~0, flags = 0;
     710            DosQueryMem( req->xfer, &size, &flags );
     711            qDebug( "DefaultDragWorker: Freed DRAGTRANSFER: "
     712                    "req->xfer=%p, size=%lu(0x%08lX), flags=0x%08lX",
     713                    req->xfer, size, size, flags );
     714        }
     715#endif       
     716        req->xfer = NULL;
     717       
     718        return (MRESULT) FALSE;
     719    }
     720   
     721    if ( msg == DM_ENDCONVERSATION ) {
     722        // we don't check that isInitialized() is TRUE here, because WPS
     723        // (and probably some other apps) may send this message after
     724        // cleanup() is called up on return from DrgDrag
     725       
     726        ULONG itemId = LONGFROMMP( mp1 );
     727        ULONG flags = LONGFROMMP( mp2 );
     728       
     729        // sanity check (don't assert, see above)
     730        Data::Request *req = d->requests[ itemId ];
     731        Q_ASSERT( req );
     732        if ( !req )
     733            return (MRESULT) FALSE;
     734
     735#if defined(QT_DEBUG_DND)                       
     736        qDebug( "DefaultDragWorker: Got DM_ENDCONVERSATION for item %ld (id=%ld), "
     737                "provider=%p, drm=%s, drf=%s, rendered=%d, outdated=%d",
     738                req->index, itemId, req->provider,
     739                req->drm.data(), req->drf.data(), req->rendered,
     740                !d->isInitialized() );
     741#endif
     742
     743        // proceed further only if it's not an outdated request
     744        // from the previous DND session
     745        if ( d->isInitialized() ) {
     746            if ( !req->rendered ) {
     747                // we treat cancelling the render request (for any reason)
     748                // as a failure
     749                d->renderOk = FALSE;
     750            } else {
     751                // the overall success is TRUE only if target says Okay
     752                d->renderOk &= flags == DMFL_TARGETSUCCESSFUL;
     753            }
     754        }
     755       
     756        // delete the request
     757        d->requests.remove( itemId );
     758       
     759        return (MRESULT) FALSE;
     760    }
     761   
     762    return (MRESULT) FALSE;
     763}
     764
     765bool QPMMime::DefaultDragWorker::addProvider( const char *drf, Provider *provider,
     766                                              ULONG itemCnt /* = 1 */ )
     767{
     768    // make sure remaining requests from the previous DND session are deleted
     769    d->cleanupRequests();
     770
     771    Q_ASSERT( drf && provider && itemCnt >= 1 );
     772    if ( drf && provider && itemCnt >= 1 ) {
     773        if ( d->providers.count() == 0 ) {
     774            // first provider
     775            d->itemCnt = itemCnt;
     776            d->providers.push_back( Data::DrfProvider( drf, provider ) );
     777            return TRUE;
     778        }
     779        // next provider, must not be exclusive and itemCnt must match
     780        if ( !d->exclusive && d->itemCnt == itemCnt ) {
     781            // ensure there are no dups (several providers for the same drf)
     782            if ( !d->providerFor( drf ) )
     783                d->providers.push_back( Data::DrfProvider( drf, provider ) );
     784            return TRUE;
     785        }
     786    }
     787    return FALSE;
     788}
     789
     790// static
     791bool QPMMime::DefaultDragWorker::canRender( const char *drm )
     792{
     793    return qstrcmp( drm, DRM_SHAREDMEM ) == 0 ||
     794           qstrcmp( drm, DRM_OS2FILE ) == 0;
     795}
     796
     797//------------------------------------------------------------------------------
     798
     799struct QPMMime::DefaultDropWorker::Data
     800{
     801    struct MimeProvider
     802    {
     803        MimeProvider() : prov( NULL ) {}
     804        MimeProvider( const char *m, Provider *p ) : mime( m ), prov( p ) {}
     805        const QCString mime;
     806        Provider * const prov;
     807    };
     808
     809    typedef QValueList<MimeProvider> MimeProviderList;
     810   
     811    Provider *providerFor( const char *mime )
     812    {
     813        for ( MimeProviderList::const_iterator it = providers.begin();
     814              it != providers.end(); ++it )
     815            if ( qstrcmp( (*it).mime, mime ) == 0 )
     816                return (*it).prov;
     817        return NULL;
     818    }
     819   
     820    bool exclusive : 1;
     821    MimeProviderList providers;
     822
     823    bool sending_DM_RENDER : 1;
     824    bool got_DM_RENDERCOMPLETE : 1;
     825    USHORT flags_DM_RENDERCOMPLETE;
     826    int waiting_DM_RENDERCOMPLETE;
     827};
     828
     829QPMMime::DefaultDropWorker::DefaultDropWorker() : d( new Data() )
     830{
     831    d->exclusive = FALSE;
     832    d->sending_DM_RENDER = d->got_DM_RENDERCOMPLETE = FALSE;
     833    d->flags_DM_RENDERCOMPLETE = 0;
     834    d->waiting_DM_RENDERCOMPLETE = 0;
     835}
     836
     837QPMMime::DefaultDropWorker::~DefaultDropWorker()
     838{
     839    delete d;
     840}
     841
     842void QPMMime::DefaultDropWorker::cleanup( bool isAccepted, bool isActAccepted )
     843{
     844    if ( d->waiting_DM_RENDERCOMPLETE != 0 ) {
     845#if defined(QT_CHECK_STATE)
     846        qWarning( "The previous drag source didn't post DM_RENDERCOMPLETE!\n"
     847                  "Contact the drag source developer." );
     848#endif       
     849        qApp->eventLoop()->exitLoop();
     850    }
     851
     852    d->providers.clear();
     853    d->exclusive = FALSE;
     854    d->sending_DM_RENDER = d->got_DM_RENDERCOMPLETE = FALSE;
     855    d->flags_DM_RENDERCOMPLETE = 0;
     856    d->waiting_DM_RENDERCOMPLETE = 0;
     857}
     858
     859bool QPMMime::DefaultDropWorker::isExclusive() const
     860{
     861    return d->exclusive;
     862}
     863
     864bool QPMMime::DefaultDropWorker::provides( const char *format ) const
     865{
     866    return d->providerFor( format ) != NULL;
     867}
     868
     869int QPMMime::DefaultDropWorker::formatCount() const
     870{
     871    return d->providers.count();
     872}
     873
     874const char *QPMMime::DefaultDropWorker::format( int fn ) const
     875{
     876    if ( fn >= 0 && (uint) fn < d->providers.count() )
     877        return d->providers[ fn ].mime;
     878    return NULL;   
     879}
     880
     881static QCString composeTempFileName()
     882{
     883    static char defTmpDir[ 3 ] = { 0 };
     884    const char *tmpDir = getenv( "TEMP" );
     885    if ( !tmpDir ) tmpDir = getenv( "TMP" );
     886    if ( !tmpDir || !QFile::exists( QFile::decodeName( tmpDir ) ) ) {
     887        if ( !defTmpDir[ 0 ] ) {
     888            ULONG bootDrive = 0;
     889            DosQuerySysInfo( QSV_BOOT_DRIVE, QSV_BOOT_DRIVE,
     890                             &bootDrive, sizeof (bootDrive) );
     891            defTmpDir[ 0 ] = bootDrive;
     892            defTmpDir[ 1 ] = ':';
     893        }
     894        tmpDir = defTmpDir;
     895    }
     896
     897    static bool srandDone = FALSE;
     898    if ( !srandDone ) {
     899        srand( time( NULL ) );
     900        srandDone = TRUE;
     901    }
     902   
     903    ULONG num = rand();
     904    enum { Attempts = 100 };
     905    int attempts = Attempts;
     906   
     907    QCString tmpName;
     908    do {
     909        tmpName.sprintf( "%s\\%08lX.tmp", tmpDir, num );
     910        if ( !QFile::exists( QFile::decodeName( tmpName ) ) )
     911            break;
     912        num = rand();
     913    } while ( --attempts > 0 );
     914   
     915    Q_ASSERT( attempts > 0 );
     916    if ( attempts <= 0 )
     917        tmpName.resize( 0 );
     918   
     919    return tmpName;
     920}
     921
     922QByteArray QPMMime::DefaultDropWorker::encodedData( const char *format ) const
     923{
     924#if defined(QT_DEBUG_DND)                       
     925    qDebug( "DefaultDropWorker::encodedData(%s)", format );
     926#endif
     927
     928    QByteArray data;
     929
     930    Q_ASSERT( info() );
     931    if ( !info() )
     932        return data;
     933   
     934    ULONG itemCount = DrgQueryDragitemCount( info() );
     935    Q_ASSERT( itemCount );
     936    if ( !itemCount )
     937        return data;
     938   
     939    Provider *provider = d->providerFor( format );
     940    if ( !provider )
     941        return data;
     942
     943    const char *drf = provider->drf( format );
     944    Q_ASSERT( drf );
     945    if ( !drf )
     946        return data;
     947
     948    // Note: Allocating and freeing DRAGTRANSFER structures is a real mess. It's
     949    // absolutely unclear how they can be reused for multiple items and/or render
     950    // requests. My practice shows, that they cannot be reused at all, especially
     951    // when the source and the target are the same process: if we have multiple
     952    // items and use the same DRAGTRANSFER for all of them, the source will call
     953    // DrgFreeDragtransfer() every time that will eventually destroy the memory
     954    // object before the target finishes to work with it, so that the next
     955    // DrgFreeDragtransfer() will generate a segfault in PMCTLS. Also note that
     956    // using a number > 1 as an argument to DrgAllocDragtransfer() won't help
     957    // because that will still allocate a single memory object. Thus, we will
     958    // always allocate a new struct per every item. It seems to work.
     959
     960    QCString renderToName = composeTempFileName();
     961    HSTR hstrRenderToName = DrgAddStrHandle( renderToName );
     962   
     963    HSTR rmfOS2File =
     964        DrgAddStrHandle( QCString().sprintf( "<"DRM_OS2FILE",%s>", drf ) );
     965    HSTR rmfSharedMem =
     966        DrgAddStrHandle( QCString().sprintf( "<"DRM_SHAREDMEM",%s>", drf ) );
     967   
     968    MRESULT mrc;
     969    bool renderOk = FALSE;
     970   
     971    DRAGTRANSFER *xfer = NULL;
     972    QCString srcFileName;
     973
     974    QByteArray allData, itemData;
     975   
     976    for ( ULONG i = 0; i < itemCount; ++i ) {
     977        DRAGITEM *item = DrgQueryDragitemPtr( info(), i );
     978        Q_ASSERT( item );
     979        if ( !item ) {
     980            renderOk = FALSE;
     981            break;
     982        }
     983
     984        enum { None, OS2File, SharedMem } drm = None;
     985        bool needToTalk = TRUE;
     986       
     987        // determine the mechanism to use (prefer DRM_SHAREDMEM)
     988       
     989        if ( DrgVerifyRMF( item, DRM_SHAREDMEM, drf ) &&
     990             DrgVerifyRMF( item, DRM_SHAREDMEM, DRF_POINTERDATA ) )
     991            drm = SharedMem;
     992        if ( DrgVerifyRMF( item, DRM_OS2FILE, drf ) ) {
     993            srcFileName = querySourceNameFull( item );
     994            // If the source provides the full file name, we prefer DRM_OS2FILE
     995            // even if there is also DRM_SHAREDMEM available because we don't
     996            // need to do any communication in this case at all. This will help
     997            // with some native drag sources (such as DragText) that cannot send
     998            // DM_RENDERCOMPLETE synchronously (before we return from DM_DROP)
     999            // and would hang otherwise.
     1000            if ( !srcFileName.isEmpty() ) {
     1001                needToTalk = FALSE;
     1002                drm = OS2File;
     1003            } else if ( drm == None ) {
     1004                srcFileName = renderToName;
     1005                drm = OS2File;
     1006            }
     1007        }
     1008        Q_ASSERT( drm != None );
     1009        if ( drm == None ) {
     1010            renderOk = FALSE;
     1011            break;
     1012        }
     1013       
     1014        if ( needToTalk ) {
     1015            // need to perform a conversation with the source,
     1016            // allocate a new DRAGTRANSFER structure for each item
     1017            xfer = DrgAllocDragtransfer( 1 );
     1018            Q_ASSERT( xfer );
     1019            if ( !xfer ) {
     1020                renderOk = FALSE;
     1021                break;
     1022            }
     1023   
     1024            xfer->cb = sizeof( DRAGTRANSFER );
     1025            xfer->hwndClient = hwnd();
     1026            xfer->ulTargetInfo = (ULONG) info();
     1027            xfer->usOperation = info()->usOperation;
     1028           
     1029            xfer->pditem = item;
     1030            if ( drm == OS2File ) {
     1031                xfer->hstrSelectedRMF = rmfOS2File;
     1032                xfer->hstrRenderToName = hstrRenderToName;
     1033            } else {
     1034                xfer->hstrSelectedRMF = rmfSharedMem;
     1035                xfer->hstrRenderToName = 0;
     1036            }
     1037   
     1038#if defined(QT_DEBUG_DND)                       
     1039            qDebug( "DefaultDropWorker: Will use %s to render item %p",
     1040                    queryHSTR( xfer->hstrSelectedRMF ).data(), item );
     1041#endif
     1042
     1043            mrc = (MRESULT) TRUE;
     1044            if ( (item->fsControl & DC_PREPARE) |
     1045                 (item->fsControl & DC_PREPAREITEM) ) {
     1046#if defined(QT_DEBUG_DND)                       
     1047                qDebug( "DefaultDropWorker: Sending DM_RENDERPREPARE to 0x%08lX...",
     1048                        info()->hwndSource );
     1049#endif               
     1050                mrc = DrgSendTransferMsg( info()->hwndSource, DM_RENDERPREPARE,
     1051                                          MPFROMP (xfer), 0 );
     1052#if defined(QT_DEBUG_DND)                       
     1053                qDebug( "DefaultDropWorker: Finisned sending DM_RENDERPREPARE\n"
     1054                        " mrc=%p, xfer->fsReply=0x%08hX", mrc, xfer->fsReply );
     1055#endif               
     1056                renderOk = (BOOL) mrc;
     1057            }
     1058   
     1059            if ( (BOOL) mrc ) {
     1060#if defined(QT_DEBUG_DND)                       
     1061                qDebug( "DefaultDropWorker: Sending DM_RENDER to 0x%08lX...",
     1062                        item->hwndItem );
     1063#endif
     1064                d->sending_DM_RENDER = TRUE;
     1065                mrc = DrgSendTransferMsg( item->hwndItem, DM_RENDER,
     1066                                          MPFROMP (xfer), 0 );
     1067                d->sending_DM_RENDER = FALSE;
     1068#if defined(QT_DEBUG_DND)                       
     1069                qDebug( "DefaultDropWorker: Finisned Sending DM_RENDER\n"
     1070                        " mrc=%p, xfer->fsReply=0x%hX, got_DM_RENDERCOMPLETE=%d",
     1071                        mrc, xfer->fsReply, d->got_DM_RENDERCOMPLETE );
     1072#endif
     1073
     1074                if ( !(BOOL) mrc || d->got_DM_RENDERCOMPLETE ) {
     1075                    if ( d->got_DM_RENDERCOMPLETE )
     1076                        renderOk = (d->flags_DM_RENDERCOMPLETE & DMFL_RENDEROK);
     1077                    else
     1078                        renderOk = FALSE;
     1079                } else {
     1080                    // synchronously wait for DM_RENDERCOMPLETE
     1081                    d->waiting_DM_RENDERCOMPLETE = qApp->loopLevel() + 1;
     1082#if defined(QT_DEBUG_DND)                       
     1083                    qDebug( "DefaultDropWorker: Waiting for DM_RENDERCOMPLETE..." );
     1084#endif
     1085                    int level = qApp->eventLoop()->enterLoop();
     1086#if defined(QT_DEBUG_DND)                       
     1087                    qDebug( "DefaultDropWorker: Finished waiting for "
     1088                            "DM_RENDERCOMPLETE (%d)\n"
     1089                            " got_DM_RENDERCOMPLETE=%d, usFS=0x%hX",
     1090                            level, d->got_DM_RENDERCOMPLETE, d->flags_DM_RENDERCOMPLETE );
     1091#endif
     1092                    // JFTR: at this point, cleanup() might have been called,
     1093                    // as a result of either program exit or getting another       
     1094                    // DM_DRAGOVER (if the source app has crashed) before getting
     1095                    // DM_RENDERCOMPLETE from the source. Use data members with
     1096                    // care!
     1097                    d->waiting_DM_RENDERCOMPLETE = 0;
     1098                    renderOk = d->got_DM_RENDERCOMPLETE &&
     1099                               (d->flags_DM_RENDERCOMPLETE & DMFL_RENDEROK);
     1100                }
     1101   
     1102                d->got_DM_RENDERCOMPLETE = FALSE;
     1103            }
     1104        } else {
     1105#if defined(QT_DEBUG_DND)                       
     1106            qDebug( "DefaultDropWorker: Source supports <"DRM_OS2FILE",%s> and "
     1107                    "provides a file \"%s\" for item %p (no need to render)",
     1108                    drf, srcFileName.data(), item );
     1109#endif
     1110            renderOk = TRUE;
     1111        }
     1112
     1113        if ( renderOk ) {
     1114            if ( drm == OS2File ) {
     1115#if defined(QT_DEBUG_DND)                       
     1116                qDebug( "DefaultDragWorker: Will read from \"%s\"",
     1117                        srcFileName.data() );
     1118#endif
     1119                QFile file( QFile::decodeName( srcFileName ) );
     1120                renderOk = file.open( IO_ReadOnly );
     1121                if ( renderOk ) {
     1122                    itemData = file.readAll();
     1123                    renderOk = file.status() == IO_Ok;
     1124                    file.close();
     1125                }
     1126                bool ok = file.remove();
     1127                Q_ASSERT( (ok = ok) );
     1128            } else {
     1129                Q_ASSERT( xfer->hstrRenderToName );
     1130                renderOk = xfer->hstrRenderToName != 0;
     1131                if ( renderOk ) {
     1132                    const char *ptr = (const char *) xfer->hstrRenderToName;
     1133                    ULONG size = ~0;
     1134                    ULONG flags = 0;
     1135                    APIRET rc = DosQueryMem( (PVOID) ptr, &size, &flags );
     1136                    renderOk = rc == 0;
     1137                    if ( renderOk ) {
     1138#if defined(QT_DEBUG_DND)                       
     1139                        qDebug( "DefaultDropWorker: Got shared data=%p, size=%lu "
     1140                                "(0x%08lX), flags=0x%08lX", ptr, size, size, flags );
     1141#endif
     1142                        Q_ASSERT( flags & (PAG_COMMIT | PAG_READ | PAG_BASE) ==
     1143                                  (PAG_COMMIT | PAG_READ | PAG_BASE) );
     1144                        renderOk = flags & (PAG_COMMIT | PAG_READ | PAG_BASE) ==
     1145                                   (PAG_COMMIT | PAG_READ | PAG_BASE);
     1146#ifndef QT_NO_DEBUG
     1147                    } else {
     1148                        qSystemWarning( "DosQueryMem failed", rc );
     1149#endif                       
     1150                    }
     1151                    if ( renderOk ) {
     1152                        ULONG realSize = *(ULONG *) ptr;
     1153#if defined(QT_DEBUG_DND)                       
     1154                        qDebug( "DefaultDropWorker: realSize=%lu", realSize );
     1155#endif
     1156                        Q_ASSERT( realSize <= size );
     1157                        renderOk = realSize <= size;
     1158                        if ( renderOk ) {
     1159                            itemData.resize( realSize );
     1160                            memcpy( itemData.data(), ptr + sizeof (ULONG), realSize );
     1161                        }
     1162                    }
     1163                    // free memory only if it is given by another process,
     1164                    // otherwise DefaultDragWorker will free it
     1165                    if ( flags & PAG_SHARED )
     1166                        DosFreeMem( (PVOID) xfer->hstrRenderToName );
     1167                }
     1168            }
     1169        }
     1170
     1171        if ( renderOk )
     1172            renderOk = provider->provide( format, i, itemData, allData );
     1173
     1174        if ( needToTalk ) {
     1175            // free the DRAGTRANSFER structure
     1176            DrgFreeDragtransfer( xfer );
     1177#if defined(QT_DEBUG_DND)                       
     1178            {
     1179                ULONG size = ~0, flags = 0;
     1180                DosQueryMem( xfer, &size, &flags );
     1181                qDebug( "DefaultDropWorker: Freed DRAGTRANSFER: "
     1182                        "xfer=%p, size=%lu(0x%08lX), flags=0x%08lX",
     1183                        xfer, size, size, flags );
     1184            }
     1185#endif           
     1186            xfer = NULL;
     1187        }
     1188       
     1189        if ( !renderOk )
     1190            break;
     1191    }
     1192
     1193#if defined(QT_DEBUG_DND)                       
     1194    qDebug( "DefaultDropWorker: renderOk=%d", renderOk );
     1195#endif
     1196
     1197    DrgDeleteStrHandle( rmfSharedMem );
     1198    DrgDeleteStrHandle( rmfOS2File );
     1199    DrgDeleteStrHandle( hstrRenderToName );
     1200
     1201    if ( renderOk )
     1202        data = allData;
     1203
     1204    return data;
     1205}
     1206
     1207MRESULT QPMMime::DefaultDropWorker::message( ULONG msg, MPARAM mp1, MPARAM mp2 )
     1208{
     1209    switch ( msg ) {
     1210        case DM_RENDERCOMPLETE: {
     1211            // sanity check
     1212            Q_ASSERT( info() );
     1213            if ( !info() )
     1214                return (MRESULT) FALSE;
     1215
     1216#if defined(QT_DEBUG_DND)                       
     1217            qDebug( "DefaultDropWorker: Got DM_RENDERCOMPLETE" );
     1218#endif           
     1219            d->got_DM_RENDERCOMPLETE = TRUE;
     1220            d->flags_DM_RENDERCOMPLETE = SHORT1FROMMP( mp2 );
     1221
     1222            if (d->sending_DM_RENDER)
     1223            {
     1224                DRAGTRANSFER *xfer = (DRAGTRANSFER *) mp1;
     1225#if defined(QT_CHECK_STATE)
     1226                qWarning( "Drag item 0x%08lX sent DM_RENDERCOMPLETE w/o first "
     1227                          "replying to DM_RENDER!\n"
     1228                          "Contact the drag source developer.",
     1229                          xfer->pditem->hwndItem );
     1230#endif               
     1231                return (MRESULT) FALSE;
     1232            }
     1233           
     1234            // stop synchronous waiting for DM_RENDERCOMPLETE
     1235            if ( d->waiting_DM_RENDERCOMPLETE != 0 )
     1236                qApp->eventLoop()->exitLoop();
     1237            return (MRESULT) FALSE;
     1238        }
     1239        default:
     1240            break;
     1241    }
     1242   
     1243    return (MRESULT) FALSE;
     1244}
     1245
     1246bool QPMMime::DefaultDropWorker::addProvider( const char *format,
     1247                                              Provider *provider )
     1248{
     1249    Q_ASSERT( format && provider );
     1250    if ( format && provider && !d->exclusive ) {
     1251        // ensure there are no dups (several providers for the same mime)
     1252        if ( !d->providerFor( format ) )
     1253            d->providers.push_back( Data::MimeProvider( format, provider ) );
     1254        return TRUE;
     1255    }
     1256    return FALSE;
     1257}
     1258
     1259bool QPMMime::DefaultDropWorker::addExclusiveProvider( const char *format,
     1260                                                       Provider *provider )
     1261{
     1262    Q_ASSERT( format && provider );
     1263    if ( format && provider && !d->exclusive ) {
     1264        d->exclusive = TRUE;
     1265        d->providers.clear();
     1266        d->providers.push_back( Data::MimeProvider( format, provider ) );
     1267        return TRUE;
     1268    }
     1269    return FALSE;
     1270}
     1271
     1272// static
     1273bool QPMMime::DefaultDropWorker::canRender( DRAGITEM *item, const char *drf )
     1274{
     1275    return DrgVerifyRMF( item, DRM_OS2FILE, drf ) ||
     1276           (DrgVerifyRMF( item, DRM_SHAREDMEM, drf ) &&
     1277            DrgVerifyRMF( item, DRM_SHAREDMEM, DRF_POINTERDATA ));
     1278}
     1279
     1280// static
     1281/*! \internal
     1282  Parses the rendering mechanism/format specification of the given \a item
     1283  and stores only those mechanism branches in the given \a list that represent
     1284  mechanisms supported by this worker. Returns FALSE if fails to parse the
     1285  RMF specification. Note that if no supported mechanisms are found, TRUE is
     1286  returned but the \a list will simply contain zero items. 
     1287  \note The method clears the given \a list variable before proceeding and sets
     1288  auto-deletion to TRUE.
     1289  \sa canRender(), PMMime::parseRMFs()
     1290*/
     1291bool QPMMime::DefaultDropWorker::getSupportedRMFs( DRAGITEM *item,
     1292                                                   QPtrList<QStrList> &list )
     1293{
     1294    if ( !parseRMFs( item->hstrRMF, list ) )
     1295        return FALSE;
     1296   
     1297    for ( QStrList *mech = list.first(); mech; ) {
     1298        const char *drm = mech->first();
     1299        if ( qstrcmp( drm, DRM_OS2FILE ) == 0 ) {
     1300            mech = list.next();
     1301            continue;
     1302        }
     1303        if ( qstrcmp( drm, DRM_SHAREDMEM ) == 0 ) {
     1304            const char *drf = mech->next();
     1305            // accept DRM_SHAREDMEM only if there is DRF_POINTERDATA
     1306            for( ; drf; drf = mech->next() ) {
     1307                if ( qstrcmp( drf, DRF_POINTERDATA ) == 0 )
     1308                    break;
     1309            }
     1310            if ( drf ) {
     1311                mech = list.next();
     1312                continue;
     1313            }
     1314        }
     1315        // remove the unsupported mechanism branch from the list
     1316        bool wasLast = list.getLast() == mech;
     1317        list.removeRef( mech );
     1318        // after deleting the last item, the current one will be set to the new
     1319        // last item which was already analyzed earlier, so set to 0 to stop
     1320        mech = wasLast ? 0 : list.current();
     1321    }
     1322   
     1323    return TRUE;
     1324}
     1325
     1326#endif // !QT_NO_DRAGANDDROP
     1327
     1328//------------------------------------------------------------------------------
    521329
    531330static QPtrList<QPMMime> mimes;
     
    961373QPMMime::QPMMime()
    971374{
    98     mimes.append( this );
     1375    // we prepend the convertor to let it override other convertors
     1376    mimes.prepend( this );
    991377}
    1001378
     
    1151393    ulong cf;
    1161394    QCString mime;
     1395#if !defined(QT_NO_DRAGANDDROP)
     1396    QCString drf;
     1397#endif // !QT_NO_DRAGANDDROP
    1171398};
    118 
    119 static QPtrList<QPMRegisteredMimeType> mimetypes;
    1201399
    1211400
    1221401class QPMMimeAnyMime : public QPMMime {
    1231402public:
    124     const char* convertorName();
     1403    QPMMimeAnyMime();
     1404    ~QPMMimeAnyMime();
     1405   
     1406    const char *convertorName();
    1251407    int countCf();
    1261408    ulong cf( int index );
    1271409    ulong flFor( ulong cf );
    1281410    ulong cfFor( const char* mime );
    129     const char* mimeFor( ulong cf );
     1411    const char *mimeFor( ulong cf );
    1301412    bool canConvert( const char* mime, ulong cf );
    1311413    QByteArray convertToMime( QByteArray data, const char *, ulong );
    1321414    QByteArray convertFromMime( QByteArray data, const char *, ulong );
     1415
     1416#if !defined(QT_NO_DRAGANDDROP)
     1417   
     1418    DragWorker *dragWorkerFor( const char *mime, QMimeSource *src );
     1419    DropWorker *dropWorkerFor( DRAGINFO *info );
     1420
     1421    class AnyDragProvider : public DefaultDragWorker::Provider
     1422    {
     1423    public:
     1424        AnyDragProvider( QPMMimeAnyMime *am ) : anyMime( am ) {}
     1425        // Provider interface
     1426        const char *format( const char *drf ) const;
     1427        bool provide( const char *drf, const QByteArray &allData,
     1428                      ULONG itemIndex, QByteArray &itemData );
     1429        void fileType( const char *drf, const char *&type, const char *&ext );
     1430    private:
     1431        QPMMimeAnyMime *anyMime;
     1432    };
     1433
     1434    class AnyDropProvider : public DefaultDropWorker::Provider
     1435    {
     1436    public:
     1437        AnyDropProvider( QPMMimeAnyMime *am ) : anyMime( am ) {}
     1438        // Provider interface
     1439        const char *drf( const char *format ) const;
     1440        bool provide( const char *format, ULONG itemIndex,
     1441                      const QByteArray &itemData, QByteArray &allData );
     1442    private:
     1443        QPMMimeAnyMime *anyMime;
     1444    };
     1445
     1446#endif // !QT_NO_DRAGANDDROP
     1447   
     1448private:
     1449    ulong registerMimeType( const char *mime );
     1450    const char *lookupMimeType( ulong cf );
     1451
     1452    QPtrList<QPMRegisteredMimeType> mimetypes;
     1453   
     1454#if !defined(QT_NO_DRAGANDDROP)
     1455
     1456    AnyDragProvider anyDragProvider;
     1457    AnyDropProvider anyDropProvider;
     1458
     1459    friend class AnyDragProvider;
     1460    friend class AnyDropProvider;
     1461   
     1462#endif // !QT_NO_DRAGANDDROP
    1331463};
     1464
     1465QPMMimeAnyMime::QPMMimeAnyMime()
     1466#if !defined(QT_NO_DRAGANDDROP)
     1467    : anyDragProvider( AnyDragProvider( this ) )
     1468    , anyDropProvider( AnyDropProvider( this ) )
     1469#endif // !QT_NO_DRAGANDDROP
     1470{
     1471    mimetypes.setAutoDelete( TRUE );
     1472}
     1473
     1474QPMMimeAnyMime::~QPMMimeAnyMime()
     1475{
     1476    // dereference all atoms we've referenced
     1477    HATOMTBL tbl = WinQuerySystemAtomTable();
     1478    for ( QPMRegisteredMimeType *mt = mimetypes.first();
     1479          mt; mt = mimetypes.next() ) {
     1480        WinDeleteAtom( tbl, 0xFFFF0000 | mt->cf );
     1481    }
     1482}
    1341483
    1351484const char* QPMMimeAnyMime::convertorName()
     
    1761525        if ( mt->cf == cf )
    1771526            return mt->mime;
    178     return 0;
     1527    // try to determine the mime type from the clipboard format ID
     1528    return lookupMimeType( cf );
    1791529}
    1801530
    1811531bool QPMMimeAnyMime::canConvert( const char* mime, ulong cf )
    1821532{
    183     QPMRegisteredMimeType *mt = mimetypes.current();
    184     do {
    185         if ( mt ) // quick check with most-recent
    186             if ( mt->cf == cf )
    187                 break;
    188         for ( mt = mimetypes.first(); mt; mt = mimetypes.next() )
    189             if ( mt->cf == cf )
    190                 break;
    191         if ( !mt ) {
    192             ulong f = registerMimeType( mime );
    193             return f && f == cf;
    194         }
    195     } while ( 0 );
    196 
    197     return qstricmp( mt->mime, mime ) == 0;
     1533    return mime && cf && qstricmp( mimeFor( cf ), mime ) == 0;
    1981534}
    1991535
     
    2081544}
    2091545
    210 
     1546#if !defined(QT_NO_DRAGANDDROP)
     1547
     1548const char *QPMMimeAnyMime::AnyDragProvider::format( const char *drf ) const
     1549{
     1550    QPMRegisteredMimeType *mt = anyMime->mimetypes.current();
     1551    if ( mt ) // quick check with most-recent
     1552        if ( qstricmp( mt->drf, drf ) == 0 )
     1553            return mt->mime;
     1554    for ( mt = anyMime->mimetypes.first(); mt; mt = anyMime->mimetypes.next() )
     1555        if ( qstricmp( mt->drf, drf ) == 0 )
     1556            return mt->mime;
     1557       
     1558    Q_ASSERT( /* mt->mime != */ NULL );
     1559    return NULL;
     1560}
     1561
     1562bool QPMMimeAnyMime::AnyDragProvider::provide( const char *drf,
     1563                                               const QByteArray &allData,
     1564                                               ULONG itemIndex,
     1565                                               QByteArray &itemData )
     1566{
     1567    // always straight through coversion
     1568    itemData = allData;
     1569    return TRUE;
     1570}
     1571
     1572void QPMMimeAnyMime::AnyDragProvider::fileType( const char *drf, const char *&type,
     1573                                                const char *&ext )
     1574{
     1575    // file type = mime
     1576    type = format( drf );
     1577    Q_ASSERT( type );
     1578   
     1579    // no way to determine the extension
     1580    ext = NULL;
     1581
     1582    /// @todo (dmik) later, we can add a hack method to qmime.cpp that returns
     1583    //  the extension->mime map of the current QMimeSourceFactory and use it
     1584    //  to determine the extension.
     1585};
     1586
     1587const char *QPMMimeAnyMime::AnyDropProvider::drf( const char *format ) const
     1588{
     1589    ulong cf = anyMime->cfFor( format );
     1590    if ( cf ) {
     1591        // current is what was found by cfFor()
     1592        Q_ASSERT( anyMime->mimetypes.current()->drf );
     1593        return anyMime->mimetypes.current()->drf;
     1594    }
     1595       
     1596    Q_ASSERT( FALSE );
     1597    return NULL;
     1598}
     1599
     1600bool QPMMimeAnyMime::AnyDropProvider::provide( const char *format,
     1601                                             ULONG /* itemIndex */,
     1602                                             const QByteArray &itemData,
     1603                                             QByteArray &allData )
     1604{
     1605    // always straight through coversion
     1606    allData = itemData;
     1607    return TRUE;
     1608}
     1609
     1610QPMMime::DragWorker *QPMMimeAnyMime::dragWorkerFor( const char *mime, QMimeSource *src )
     1611{
     1612    ulong cf = cfFor( mime );
     1613    if ( cf ) {
     1614        DefaultDragWorker *defWorker = defaultCoopDragWorker();
     1615        QCString drf;
     1616        drf.sprintf( "CF_%04lX", cf );
     1617        // current is what was found by cfFor()
     1618        mimetypes.current()->drf = drf;
     1619        // add a cooperative provider
     1620        defWorker->addProvider( drf, &anyDragProvider );
     1621        return defWorker;
     1622    }
     1623   
     1624    return NULL;
     1625}
     1626
     1627QPMMime::DropWorker *QPMMimeAnyMime::dropWorkerFor( DRAGINFO *info )
     1628{
     1629    ULONG itemCount = DrgQueryDragitemCount( info );
     1630    Q_ASSERT( itemCount );
     1631    if ( !itemCount )
     1632        return NULL;
     1633
     1634    if ( itemCount == 1 ) {
     1635        DRAGITEM *item = DrgQueryDragitemPtr( info, 0 );
     1636        Q_ASSERT( item );
     1637        if ( !item )
     1638            return NULL;
     1639
     1640        DefaultDropWorker *defWorker = defaultDropWorker();
     1641        bool atLeastOneSupported = FALSE;
     1642       
     1643        // check that we support one of DRMs and the format is MIME_hhhh
     1644        QPtrList<QStrList> list;
     1645        defWorker->getSupportedRMFs( item, list );
     1646        for( QStrList *mech = list.first(); mech; mech = list.next() ) {
     1647#if defined(QT_DEBUG_DND)                       
     1648            qDebug( "QPMMimeAnyMime: Supported drm: %s", mech->getFirst() );           
     1649#endif
     1650            for( const char *drf = mech->at( 1 ); drf; drf = mech->next() ) {
     1651                if ( qstrncmp( drf, "CF_", 3 ) == 0 ) {
     1652                    // get the atom ID
     1653                    ulong cf = 0;
     1654                    uint len = qstrlen ( drf );
     1655                    for ( uint i = 3; i < len; ++i ) {
     1656                        int hex = hex_to_int( drf[ i ] );
     1657                        if ( hex < 0 ) { cf = 0; break; }
     1658                        cf = (cf << 4) | (uint) hex;
     1659                    }
     1660                    Q_ASSERT( cf );
     1661                    if ( !cf )
     1662                        continue;
     1663#if defined(QT_DEBUG_DND)                       
     1664                    qDebug( "QPMMimeAnyMime: drf %s is atom 0x%08lX", drf, cf );
     1665#endif                   
     1666                    const char *mime = mimeFor( cf );
     1667                    if ( mime ) {
     1668#if defined(QT_DEBUG_DND)                       
     1669                        qDebug( "QPMMimeAnyMime: Will provide [%s] for drf %s",
     1670                                mime, drf );
     1671#endif                       
     1672                        // current is what was found by mimeFor()
     1673                        mimetypes.current()->drf = drf;
     1674                        // add a cooperative provider (can coexist with others)
     1675                        defWorker->addProvider( mime, &anyDropProvider );
     1676                        atLeastOneSupported = TRUE;
     1677                    }
     1678                }
     1679            }
     1680        }
     1681       
     1682        if ( atLeastOneSupported )
     1683            return defWorker;
     1684    }
     1685   
     1686    return NULL;
     1687}
     1688
     1689#endif // !QT_NO_DRAGANDDROP
     1690
     1691ulong QPMMimeAnyMime::registerMimeType( const char *mime )
     1692{
     1693    QCString atom;
     1694    atom.sprintf( "mime:%s", mime );
     1695   
     1696    ulong f = WinAddAtom( WinQuerySystemAtomTable(), atom );
     1697    if ( !f ) {
     1698#ifndef QT_NO_DEBUG
     1699        qSystemWarning( "QPMMime: Failed to register the clipboard format" );
     1700#endif
     1701        return 0;
     1702    }
     1703
     1704    mimetypes.append( new QPMRegisteredMimeType ( f, mime ) );
     1705    Q_ASSERT( mimetypes.current()->cf == f );
     1706    return mimetypes.current()->cf == f ? f : 0;
     1707}
     1708
     1709const char *QPMMimeAnyMime::lookupMimeType( ulong cf )
     1710{
     1711    HATOMTBL tbl = WinQuerySystemAtomTable();
     1712    ULONG len = WinQueryAtomLength( tbl, cf );
     1713    QCString atom( len + 1 );
     1714    WinQueryAtomName( tbl, cf, atom.data(), atom.size() );
     1715
     1716    // see if this atom represents a mime type   
     1717    if ( qstrncmp( atom, "mime:", 5 ) == 0 ) {
     1718        atom = atom.right( atom.length() - 5 );
     1719        if ( !atom.isEmpty() ) {
     1720            ulong f = WinAddAtom( tbl, (PSZ) (0xFFFF0000 | cf) );
     1721            if ( !f ) {
     1722#ifndef QT_NO_DEBUG
     1723                qSystemWarning( "QPMMime: Failed to reference the clipboard format" );
     1724#endif
     1725                return 0;
     1726            }
     1727            mimetypes.append( new QPMRegisteredMimeType ( cf, atom ) );
     1728            Q_ASSERT( mimetypes.current()->cf == cf );
     1729            return mimetypes.current()->cf == cf ? mimetypes.current()->mime : NULL;
     1730        }
     1731    }
     1732   
     1733    return NULL;
     1734}
    2111735
    2121736class QPMMimeText : public QPMMime {
    2131737public:
    2141738    QPMMimeText();
     1739    ~QPMMimeText();
    2151740    const char* convertorName();
    2161741    int countCf();
     
    2221747    QByteArray convertToMime( QByteArray data, const char *, ulong cf );
    2231748    QByteArray convertFromMime( QByteArray data, const char *, ulong cf );
     1749
     1750#if !defined(QT_NO_DRAGANDDROP)
     1751   
     1752    DragWorker *dragWorkerFor( const char *mime, QMimeSource *src );
     1753    DropWorker *dropWorkerFor( DRAGINFO *info );
     1754
     1755    class NativeFileDrag : public DragWorker, public Messenger
     1756    {
     1757    public:
     1758        // DragWorker interface
     1759        bool cleanup( bool isCancelled ) { return TRUE; } // always disallow Move
     1760        bool isExclusive() const { return TRUE; }
     1761        ULONG itemCount() const { return 0; } // super exclusive
     1762        HWND hwnd() const { return Messenger::hwnd(); }
     1763        DRAGINFO *createDragInfo( const char *name, USHORT supportedOps );
     1764        // Messenger interface (dummy implementation, we don't need to interact)
     1765        MRESULT message( ULONG msg, MPARAM mp1, MPARAM mp2 ) { return 0; }
     1766    };
     1767   
     1768    class NativeFileDrop : public DropWorker
     1769    {
     1770    public:
     1771        // DropWorker interface
     1772        bool isExclusive() const { return TRUE; }
     1773        bool provides( const char *format ) const;
     1774        int formatCount() const;
     1775        const char *format( int fn ) const;
     1776        QByteArray encodedData( const char *format ) const;
     1777    };
     1778   
     1779    class TextDragProvider : public DefaultDragWorker::Provider
     1780    {
     1781    public:
     1782        TextDragProvider() : exclusive( FALSE ) {}
     1783        bool exclusive;
     1784        // Provider interface
     1785        const char *format( const char *drf ) const;
     1786        bool provide( const char *drf, const QByteArray &allData,
     1787                      ULONG itemIndex, QByteArray &itemData );
     1788        void fileType( const char *drf, const char *&type, const char *&ext );
     1789    };
     1790
     1791    class TextDropProvider : public DefaultDropWorker::Provider
     1792    {
     1793    public:
     1794        // Provider interface
     1795        const char *drf( const char *format ) const;
     1796        bool provide( const char *format, ULONG itemIndex,
     1797                      const QByteArray &itemData, QByteArray &allData );
     1798    };
     1799   
     1800#endif // !QT_NO_DRAGANDDROP
     1801   
    2241802private:
    2251803    const ulong CF_TextUnicode;
     1804#if !defined(QT_NO_DRAGANDDROP)
     1805    NativeFileDrag nativeFileDrag;
     1806    NativeFileDrop nativeFileDrop;
     1807    TextDragProvider textDragProvider;
     1808    TextDropProvider textDropProvider;
     1809#endif // !QT_NO_DRAGANDDROP
    2261810};
    2271811
     
    2341818}
    2351819
     1820QPMMimeText::~QPMMimeText()
     1821{
     1822    WinDeleteAtom( WinQuerySystemAtomTable(), 0xFFFF0000 | CF_TextUnicode );
     1823}
     1824
    2361825const char* QPMMimeText::convertorName()
    2371826{
     
    2611850ulong QPMMimeText::cfFor( const char* mime )
    2621851{
    263 /// @todo (dmik) do we want to accept "text/plain" w/o "charset="?
    264 //    if ( qstricmp( mime, "text/plain" ) == 0 )
    265 //      return CF_TEXT;
     1852    if ( qstrnicmp( mime, MIME_TEXT_PLAIN, qstrlen( MIME_TEXT_PLAIN ) ) != 0 ||
     1853         (mime[10] != 0 && mime[10] != ' ' && mime[10] != ';') )
     1854        return 0;
    2661855
    2671856    QCString m( mime );
     
    2761865        if ( cs == "ISO-10646-UCS-2" )
    2771866            return CF_TextUnicode;
    278     }
    279     return 0;
     1867        // any other 'charset' spec
     1868        return 0;
     1869    }
     1870   
     1871    // no 'charset' spec
     1872    return CF_TEXT;
    2801873}
    2811874
     
    2831876{
    2841877    if ( cf == CF_TEXT )
    285         return "text/plain;charset=system";
     1878        return MIME_TEXT_PLAIN_CHARSET_SYSTEM;
    2861879    else if ( cf == CF_TextUnicode )
    2871880        return "text/plain;charset=ISO-10646-UCS-2";
     
    4072000}
    4082001
     2002#if !defined(QT_NO_DRAGANDDROP)
     2003
     2004DRAGINFO *QPMMimeText::NativeFileDrag::createDragInfo( const char *name,
     2005                                                       USHORT supportedOps )
     2006{
     2007    Q_ASSERT( source() );
     2008    if ( !source() )
     2009        return NULL;
     2010   
     2011    // obtain the list of files
     2012    QStringList list;
     2013    QUriDrag::decodeLocalFiles( source(), list );
     2014    ULONG itemCnt = list.count();
     2015    Q_ASSERT( itemCnt );
     2016    if ( !itemCnt )
     2017        return NULL;
     2018   
     2019#if defined(QT_DEBUG_DND)                       
     2020    qDebug( "QPMMimeText::NativeFileDrag: itemCnt=%ld", itemCnt );
     2021#endif
     2022   
     2023    DRAGINFO *info = DrgAllocDraginfo( itemCnt );
     2024    Q_ASSERT( info );
     2025    if ( !info )
     2026        return NULL;
     2027   
     2028    bool ok = TRUE;
     2029    QStringList::Iterator it = list.begin();
     2030    for ( ULONG i = 0; i < itemCnt; ++i, ++it ) {
     2031        DRAGITEM *item = DrgQueryDragitemPtr( info, i );
     2032        Q_ASSERT( item );
     2033        if ( !item ) {
     2034            ok = FALSE;
     2035            break;
     2036        }
     2037
     2038        QCString fileName = QDir::convertSeparators( *it ).local8Bit();
     2039
     2040        int sep = fileName.findRev( '\\' );
     2041        Q_ASSERT( sep > 0 && sep < int( fileName.length() ) - 1 );
     2042        if ( sep <= 0 || sep >= int( fileName.length() ) - 1 ) {
     2043            ok = FALSE;
     2044            break;
     2045        }
     2046
     2047        item->hstrSourceName = DrgAddStrHandle( fileName.data() + sep + 1 );
     2048        fileName.truncate( sep + 1 );
     2049        item->hstrContainerName = DrgAddStrHandle( fileName );
     2050
     2051#if defined(QT_DEBUG_DND)                       
     2052        qDebug( "QPMMimeText::NativeFileDrag: item %ld: dir=\"%s\", name=\"%s\"",
     2053                i, queryHSTR( item->hstrContainerName ).data(),
     2054                   queryHSTR( item->hstrSourceName ).data() );
     2055#endif
     2056       
     2057        item->hwndItem = hwnd();
     2058        item->ulItemID = 0;
     2059        item->hstrType = DrgAddStrHandle( DRT_UNKNOWN );
     2060        item->hstrRMF = DrgAddStrHandle( "<"DRM_OS2FILE","DRF_UNKNOWN">" );
     2061        item->hstrTargetName = 0;
     2062        item->cxOffset = 0;
     2063        item->cyOffset = 0;
     2064        item->fsControl = 0;
     2065        item->fsSupportedOps = supportedOps;
     2066    }   
     2067
     2068    if ( !ok ) {
     2069        DrgFreeDraginfo( info );
     2070        info = NULL;
     2071    }
     2072   
     2073    return info;
     2074}
     2075
     2076bool QPMMimeText::NativeFileDrop::provides( const char *format ) const
     2077{
     2078    return qstricmp( format, MIME_TEXT_URI_LIST ) == 0;
     2079}
     2080
     2081int QPMMimeText::NativeFileDrop::formatCount() const
     2082{
     2083    return 1;
     2084}
     2085
     2086const char *QPMMimeText::NativeFileDrop::format( int fn ) const
     2087{
     2088    return fn == 0 ? MIME_TEXT_URI_LIST : NULL;
     2089}
     2090
     2091QByteArray QPMMimeText::NativeFileDrop::encodedData( const char *format ) const
     2092{
     2093    QByteArray data;
     2094
     2095    Q_ASSERT( info() );
     2096    if ( !info() )
     2097        return data;
     2098   
     2099    ULONG itemCount = DrgQueryDragitemCount( info() );
     2100    Q_ASSERT( itemCount );
     2101    if ( !itemCount )
     2102        return data;
     2103   
     2104    // sanity check
     2105    if ( qstricmp( format, MIME_TEXT_URI_LIST ) != 0 )
     2106        return data;
     2107   
     2108    QCString texturi;
     2109
     2110    for ( ULONG i = 0; i < itemCount; ++i ) {
     2111        DRAGITEM *item = DrgQueryDragitemPtr( info(), i );
     2112        Q_ASSERT( item );
     2113        QCString fullName;
     2114        if ( !item || !canTargetRenderAsOS2File( item, &fullName ) )
     2115            return data;
     2116        QString fn = QFile::decodeName( fullName );
     2117        texturi += QUriDrag::localFileToUri( fn );
     2118        texturi += "\r\n";
     2119    }
     2120   
     2121    data = texturi;
     2122    return data;
     2123}
     2124
     2125const char *QPMMimeText::TextDragProvider::format( const char *drf ) const
     2126{
     2127    if ( qstrcmp( drf, DRF_TEXT ) == 0 ) {
     2128        if ( exclusive )
     2129            return MIME_TEXT_URI_LIST;
     2130        else
     2131            return MIME_TEXT_PLAIN_CHARSET_SYSTEM;
     2132    }
     2133    return NULL;
     2134}
     2135
     2136bool QPMMimeText::TextDragProvider::provide( const char *drf,
     2137                                             const QByteArray &allData,
     2138                                             ULONG itemIndex,
     2139                                             QByteArray &itemData )
     2140{
     2141    if ( qstrcmp( drf, DRF_TEXT ) == 0 ) {
     2142        if ( exclusive ) {
     2143            // locate the required item
     2144            int dataSize = allData.size();
     2145            if ( !dataSize )
     2146                return FALSE;
     2147            int begin = 0, end = 0, next = 0;
     2148            do {
     2149                begin = next;
     2150                end = allData.find( '\r', begin );
     2151                if ( end >= 0 ) {
     2152                    next = end + 1;
     2153                    if ( next < dataSize && allData[ next ] == '\n' )
     2154                        ++ next;
     2155                } else {
     2156                    end = allData.find( '\n', begin );
     2157                    if ( end >= 0 )
     2158                        next = end + 1;
     2159                }
     2160            } while ( itemIndex-- && end >= 0 && next < dataSize );
     2161            int urlLen = end - begin;
     2162            if ( urlLen <= 0 )
     2163                return FALSE;
     2164            QCString urlStr( urlLen + 1 );
     2165            memcpy( urlStr.data(), allData.data() + begin, urlLen );
     2166            // decode from UTF-8
     2167            urlStr = QString::fromUtf8( urlStr ).local8Bit();
     2168            // encode special/national chars to escaped %XX within urlStr
     2169            // (see QUrl::encode(), but special chars a bit differ here: for
     2170            // example, Mozilla doesn't like when ':' is encoded after 'http').
     2171            const QCString special( "<>#@\"&%$,;?={}|^~[]\'`\\ \n\t\r" );
     2172            QCString itemStr( urlLen * 3 + 1 );
     2173            int itemLen = 0;
     2174            for ( int i = 0; i < urlLen; ++i ) {
     2175                uchar ch = urlStr[ i ];
     2176                if ( ch >= 128 || special.contains( ch ) ) {
     2177                    itemStr[ itemLen++ ] = '%';
     2178                    uchar c = ch / 16;
     2179                    c += c > 9 ? 'A' - 10 : '0';
     2180                    itemStr[ itemLen++ ] = c;
     2181                    c = ch % 16;
     2182                    c += c > 9 ? 'A' - 10 : '0';
     2183                    itemStr[ itemLen++ ] = c;
     2184                } else {
     2185                    itemStr[ itemLen++ ] = ch;
     2186                }
     2187            }
     2188            itemData = itemStr;
     2189            // resize on QByteArray to ensure the trailing \0 is stripped
     2190            itemData.resize( itemLen );
     2191        } else {
     2192            itemData = allData;
     2193        }
     2194        return TRUE;
     2195    }
     2196    return FALSE;
     2197}
     2198
     2199void QPMMimeText::TextDragProvider::fileType( const char *drf, const char *&type,
     2200                                              const char *&ext )
     2201{
     2202    if ( qstrcmp( drf, DRF_TEXT ) == 0 ) {
     2203        if ( exclusive ) {
     2204            type = DRT_URL;
     2205            // no extension for URLs
     2206        } else {
     2207            type = DRT_TEXT;
     2208            ext = "txt";
     2209        }
     2210    }
     2211};
     2212
     2213const char *QPMMimeText::TextDropProvider::drf( const char *format ) const
     2214{
     2215    // sanity check
     2216    if ( qstricmp( format, MIME_TEXT_PLAIN_CHARSET_SYSTEM ) == 0 ||
     2217         qstricmp( format, MIME_TEXT_URI_LIST ) == 0 )
     2218        return DRF_TEXT;
     2219    return NULL;
     2220}
     2221
     2222bool QPMMimeText::TextDropProvider::provide( const char *format,
     2223                                             ULONG /* itemIndex */,
     2224                                             const QByteArray &itemData,
     2225                                             QByteArray &allData )
     2226{
     2227    if ( qstricmp( format, MIME_TEXT_PLAIN_CHARSET_SYSTEM ) == 0 ) {
     2228        allData = itemData;
     2229        return TRUE;
     2230    }
     2231
     2232    if ( qstricmp( format, MIME_TEXT_URI_LIST ) == 0 ) {
     2233        // decode escaped %XX within itemData (see QUrl::decode())
     2234        size_t dataSize = itemData.size();
     2235        size_t strLen = 0;
     2236        QCString itemStr( dataSize + 1 );
     2237        for ( size_t i = 0; i < dataSize; ) {
     2238            uchar ch = itemData[ i++ ];
     2239            if ( ch == '%' && (i + 2 <= dataSize) ) {
     2240                int hi = hex_to_int( itemData[ i ] );
     2241                if ( hi >= 0 ) {
     2242                    int lo = hex_to_int( itemData[ i + 1 ] );
     2243                    if ( lo >= 0 ) {
     2244                        ch = (hi << 4) + lo;
     2245                        i += 2;
     2246                    }
     2247                }
     2248            }
     2249            itemStr[ strLen++ ] = ch;
     2250        }
     2251        itemStr.truncate( strLen );
     2252        // check that itemData is a valid URL
     2253        QUrl url( QString::fromLocal8Bit( itemStr ) );
     2254        if ( !url.isValid() )
     2255            return FALSE;
     2256        // oops, QUrl incorrectly handles the 'file:' protocol, do it for it
     2257        QString urlStr;
     2258        if ( url.isLocalFile() )
     2259            urlStr = url.protocol() + ":///" + url.path();
     2260        else
     2261            urlStr = url.toString();
     2262        // append the URL to the list
     2263        QCString str ( allData );
     2264        str += QUriDrag::unicodeUriToUri( urlStr );
     2265        str += "\r\n";
     2266        allData = str;
     2267        return TRUE;
     2268    }
     2269   
     2270    return FALSE;
     2271}
     2272
     2273QPMMime::DragWorker *QPMMimeText::dragWorkerFor( const char *mime, QMimeSource *src )
     2274{
     2275    if ( cfFor( mime ) == CF_TEXT ) {
     2276        // add a cooperative provider
     2277        textDragProvider.exclusive = FALSE;
     2278        DefaultDragWorker *defWorker = defaultCoopDragWorker();
     2279        defWorker->addProvider( DRF_TEXT, &textDragProvider );
     2280        return defWorker;
     2281    }
     2282   
     2283    if ( qstricmp( mime, MIME_TEXT_URI_LIST ) == 0 ) {
     2284        // see what kind of items text/uri-list represents
     2285        QStrList uriList;
     2286        QStringList fileList;
     2287        QUriDrag::decode( src, uriList );
     2288        QUriDrag::decodeLocalFiles( src, fileList );
     2289        if ( fileList.count() && fileList.count() == uriList.count() ) {
     2290            // all items are local files, return an exclusive file drag worker
     2291            return &nativeFileDrag;
     2292        }
     2293        if ( uriList.count() && !fileList.count() ) {
     2294            // all items are non-files, add an exclusive provider for the
     2295            // specified item count
     2296            textDragProvider.exclusive = TRUE;
     2297            DefaultDragWorker *defWorker = defaultExclDragWorker();
     2298            bool ok = defWorker->addProvider( DRF_TEXT, &textDragProvider,
     2299                                              uriList.count() );
     2300            return ok ? defWorker : NULL;
     2301        }
     2302        // if items are mixed, we return NULL to fallback to QPMMimeAnyMime
     2303    }
     2304   
     2305    return NULL;
     2306}
     2307
     2308QPMMime::DropWorker *QPMMimeText::dropWorkerFor( DRAGINFO *info )
     2309{
     2310    ULONG itemCount = DrgQueryDragitemCount( info );
     2311    Q_ASSERT( itemCount );
     2312    if ( !itemCount )
     2313        return NULL;
     2314   
     2315    if ( itemCount == 1 ) {
     2316        DRAGITEM *item = DrgQueryDragitemPtr( info, 0 );
     2317        Q_ASSERT( item );
     2318        if ( !item )
     2319            return NULL;
     2320        // proceed only if the target cannot render DRM_OS2FILE on its own
     2321        // and if the item type is not "UniformResourceLocator" (which will be
     2322        // processed below)
     2323        if ( !canTargetRenderAsOS2File( item ) &&
     2324             !DrgVerifyType( item, DRT_URL ) ) {
     2325            DefaultDropWorker *defWorker = defaultDropWorker();
     2326            // check that we support one of DRMs and the format is DRF_TEXT
     2327            if ( defWorker->canRender( item, DRF_TEXT ) ) {
     2328                // add a cooperative provider (can coexist with others)
     2329                defWorker->addProvider( MIME_TEXT_PLAIN_CHARSET_SYSTEM,
     2330                                        &textDropProvider );
     2331                return defWorker;
     2332            }
     2333            return NULL;
     2334        }
     2335    }
     2336
     2337    // Either the target can render DRM_OS2FILE on its own (so it's a valid
     2338    // file/directory name), or it's an "UniformResourceLocator", or there is
     2339    // more than one drag item. Check that all items are of either one type
     2340    // or another. If so, we can represent them as 'text/uri-list'.
     2341    bool allAreFiles = TRUE;
     2342    bool allAreURLs = TRUE;
     2343    DefaultDropWorker *defWorker = defaultDropWorker();
     2344    for ( ULONG i = 0; i < itemCount; ++i ) {
     2345        DRAGITEM *item = DrgQueryDragitemPtr( info, i );
     2346        Q_ASSERT( item );
     2347        if ( !item )
     2348            return NULL;
     2349        if (allAreFiles)
     2350            allAreFiles &= canTargetRenderAsOS2File( item );
     2351        if (allAreURLs)
     2352            allAreURLs &= DrgVerifyType( item, DRT_URL ) &&
     2353                          defWorker->canRender( item, DRF_TEXT );
     2354        if (!allAreFiles && !allAreURLs)
     2355            return NULL;
     2356    }
     2357
     2358    if (allAreFiles) {
     2359        // return an exclusive drop worker
     2360        return &nativeFileDrop;
     2361    }
     2362   
     2363    // add an exclusive provider (can neither coexist with other workers
     2364    // or providers)
     2365    bool ok = defWorker->addExclusiveProvider( MIME_TEXT_URI_LIST,
     2366                                               &textDropProvider );
     2367    return ok ? defWorker : NULL;
     2368}
     2369
     2370#endif // !QT_NO_DRAGANDDROP
     2371
    4092372
    4102373class QPMMimeImage : public QPMMime {
     
    5192482
    5202483
    521 static QPMMimeAnyMime *anymime = 0;
    522 
    5232484static
    5242485void cleanup_mimes()
     
    5282489        delete wm;
    5292490    }
    530     mimetypes.setAutoDelete( TRUE );
    531     mimetypes.clear();
    532     anymime = 0;
    5332491}
    5342492
     
    5392497{
    5402498    if ( mimes.isEmpty() ) {
     2499        // add standard convertors so that QPMMimeAnyMime is the last in the list
     2500        new QPMMimeAnyMime;
    5412501        new QPMMimeImage;
    5422502        new QPMMimeText;
    543         anymime = new QPMMimeAnyMime;
    544 
    5452503        qAddPostRoutine( cleanup_mimes );
    5462504    }
    547 }
    548 
    549 /*!
    550     \internal
    551   This is an internal function.
    552 */
    553 ulong QPMMime::registerMimeType( const char *mime )
    554 {
    555     // first, look if other non-QPMMimeAnyMime convertors support this mime,
    556     // to avoid double handling of this mime type by another (probably,
    557     // system-integrated and most likely more specialized) convertor and the
    558     // dummy "pass-through" QPMMimeAnyMime convertor (which will handle the
    559     // given mime type after once we call mimetyes.append (...)).
    560     QPtrListIterator<QPMMime> it( mimes );
    561     for ( QPMMime* c; (c = *it); ++ it )
    562         if ( c != anymime && c->cfFor( mime ) )
    563             return 0;
    564 
    565     ulong f = WinAddAtom( WinQuerySystemAtomTable(), mime );
    566     if ( !f ) {
    567 #ifndef QT_NO_DEBUG
    568         qSystemWarning( "QPMMime: Failed to register clipboard format" );
    569 #endif
    570         return 0;
    571     }
    572     QPMRegisteredMimeType *mt = mimetypes.current();
    573     if ( !mt || mt->cf != f ) {
    574         for ( mt = mimetypes.first(); mt && mt->cf != f; mt = mimetypes.next() )
    575             ;
    576         if ( !mt ) {
    577             mimetypes.append( new QPMRegisteredMimeType ( f, mime ) );
    578             // successful memory allocation check
    579             if ( !(mt = mimetypes.current()) || mt->cf != f )
    580                 return 0;
    581         }
    582     }
    583     return f;
    5842505}
    5852506
     
    6272548}
    6282549
     2550#if !defined(QT_NO_DRAGANDDROP)
     2551
     2552/*!
     2553  Returns a string represented by \a hstr.
     2554*/
     2555QCString QPMMime::queryHSTR( HSTR hstr )
     2556{
     2557    QCString str;
     2558    ULONG len = DrgQueryStrNameLen( hstr );
     2559    if ( len ) {
     2560        str.resize( len + 1 );
     2561        DrgQueryStrName( hstr, str.size(), str.data() );
     2562    }
     2563    return str;
     2564}
     2565
     2566/*!
     2567  Returns a string that is a concatenation of \c hstrContainerName and
     2568  \c hstrSourceName fileds of the given \a item structure.
     2569*/
     2570QCString QPMMime::querySourceNameFull( DRAGITEM *item )
     2571{
     2572    QCString fullName;
     2573    if ( !item )
     2574        return fullName;
     2575
     2576    ULONG pathLen = DrgQueryStrNameLen( item->hstrContainerName );
     2577    ULONG nameLen = DrgQueryStrNameLen( item->hstrSourceName );
     2578    if ( !pathLen || !nameLen )
     2579        return fullName;
     2580   
     2581    fullName.resize( pathLen + nameLen + 1 );
     2582    DrgQueryStrName( item->hstrContainerName, pathLen + 1, fullName.data() );
     2583    DrgQueryStrName( item->hstrSourceName, nameLen + 1, fullName.data() + pathLen );
     2584   
     2585    return fullName;
     2586}
     2587
     2588/*! \internal
     2589  Checks that the given drag \a item supports the DRM_OS2FILE rendering
     2590  mechanism and can be rendered by a target w/o involving the source (i.e.,
     2591  DRM_OS2FILE is the first supported format and a valid file name with full
     2592  path is provided). If the function returns TRUE, \a fullName (if not NULL)
     2593  will be assigned the item's full source file name (composed from
     2594  \c hstrContainerName and \c hstrSourceName fields).
     2595 */
     2596bool QPMMime::canTargetRenderAsOS2File( DRAGITEM *item, QCString *fullName /* = NULL */ )
     2597{
     2598    if ( !item )
     2599        return FALSE;
     2600   
     2601    if ( item->fsControl & (DC_PREPARE | DC_PREPAREITEM) )
     2602        return FALSE;
     2603
     2604    {
     2605        // DrgVerifyNativeRMF doesn't work on my system (ECS 1.2.1 GA):
     2606        // it always returns FALSE regardless of arguments. Use simplified
     2607        // hstrRMF parsing to determine whether DRM_OS2FILE is the native
     2608        // mechanism or not (i.e. "^\s*[\(<]\s*DRM_OS2FILE\s*,.*").
     2609           
     2610        QCString rmf = queryHSTR( item->hstrRMF );
     2611        bool ok = FALSE;
     2612        int i = rmf.find( DRM_OS2FILE );
     2613        if ( i >= 1 ) {
     2614            for( int j = i - 1; j >= 0; --j ) {
     2615                char ch = rmf.data()[j];
     2616                if ( ch == ' ' ) continue;
     2617                if ( ch == '<' || ch == '(' ) {
     2618                    if ( ok ) return FALSE;
     2619                    ok = TRUE;
     2620                } else {
     2621                    return FALSE;
     2622                }
     2623            }
     2624        }
     2625        if ( ok ) {
     2626            ok = FALSE;
     2627            int drmLen = strlen( DRM_OS2FILE );
     2628            for( int j = i + drmLen; j < (int) rmf.length(); ++j ) {
     2629                char ch = rmf.data()[j];
     2630                if ( ch == ' ' ) continue;
     2631                if ( ch == ',' ) {
     2632                    ok = TRUE;
     2633                    break;
     2634                }
     2635                return FALSE;
     2636            }
     2637        }
     2638        if ( !ok )
     2639            return FALSE;
     2640    }
     2641
     2642    QCString srcFullName = querySourceNameFull( item );
     2643    if ( srcFullName.isEmpty() )
     2644        return FALSE;
     2645   
     2646    QCString srcFullName2( srcFullName.length() + 1 );
     2647    APIRET rc = DosQueryPathInfo( srcFullName, FIL_QUERYFULLNAME,
     2648                                  srcFullName2.data(), srcFullName2.size() );
     2649    if ( rc != 0 )
     2650        return FALSE;
     2651   
     2652    QString s1 = QFile::decodeName( srcFullName.data() );
     2653    QString s2 = QFile::decodeName( srcFullName2.data() );
     2654   
     2655    if ( s1.lower() != s2.lower() )
     2656        return FALSE;
     2657
     2658    if ( fullName )
     2659        *fullName = srcFullName;
     2660    return TRUE;
     2661}
     2662
     2663// static
     2664/*! \internal
     2665  Parses the given \a rmfs list (full rendering mechanism/format specification)
     2666  and builds a \a list of mechanism branches. Each mechanism branch is also a
     2667  list, where the first item is the mechahism name and all subsequent items are
     2668  formats supported by this mechanism. Returns FALSE if fails to parse \a rmf.
     2669  \note The method clears the given \a list variable before proceeding and sets
     2670  auto-deletion to TRUE.
     2671*/
     2672bool QPMMime::parseRMFs( HSTR rmfs, QPtrList<QStrList> &list )
     2673{
     2674    // The format of the RMF list is "elem {,elem,elem...}"
     2675    // where elem is "(mechanism{,mechanism...}) x (format{,format...})"
     2676    // or "<mechanism,format>".
     2677    // We use a simple FSM to parse it. In terms of FSM, the format is:
     2678    //
     2679    // STRT ( BCM m CMCH echanism CMCH , NCM m CMCH echanism CMCH ) ECM x
     2680    //     SCMF ( BCF f CFCH ormat CFCH , NCF f CFCH ormat CFCH ) ECF , STRT
     2681    // STRT < BP m PMCH echanism PMCH , SPMF f PFCH ormat PFCH > EP , STRT
     2682
     2683    QCString str = queryHSTR( rmfs );
     2684    uint len = str.length();
     2685
     2686    enum {
     2687        // states
     2688        STRT = 0, BCM, CMCH, NCM, ECM, SCMF, BCF, CFCH, NCF, ECF,
     2689        BP, PMCH, SPMF, PFCH, EP,
     2690        STATES_COUNT,
     2691        // pseudo states
     2692        Err, Skip,
     2693        // inputs
     2694        obr = 0, cbr, xx, lt, gt, cm, any, ws,
     2695        INPUTS_COUNT,
     2696    };
     2697
     2698    static const char Chars[] =  { '(', ')', 'x', 'X', '<', '>', ',', ' ', 0 };
     2699    static const char Inputs[] = { obr, cbr, xx,  xx,  lt,  gt,  cm,  ws };
     2700    static const uchar Fsm [STATES_COUNT] [INPUTS_COUNT] = {
     2701             /* 0 obr  1 cbr  2 xx   3 lt   4 gt   5 cm   6 any  7 ws */
     2702/* STRT 0  */ { BCM,   Err,   Err,   BP,    Err,   Err,   Err,   Skip },
     2703/* BCM  1  */ { Err,   Err,   Err,   Err,   Err,   Err,   CMCH,  Skip },
     2704/* CMCH 2  */ { Err,   ECM,   CMCH,  Err,   Err,   NCM,   CMCH,  CMCH },
     2705/* NCM  3  */ { Err,   Err,   Err,   Err,   Err,   Err,   CMCH,  Skip },
     2706/* ECM  4  */ { Err,   Err,   SCMF,  Err,   Err,   Err,   Err,   Skip },
     2707/* SCMF 5  */ { BCF,   Err,   Err,   Err,   Err,   Err,   Err,   Skip }, 
     2708/* BCF  6  */ { Err,   Err,   Err,   Err,   Err,   Err,   CFCH,  Skip },
     2709/* CFCH 7  */ { Err,   ECF,   CFCH,  Err,   Err,   NCF,   CFCH,  CFCH },
     2710/* NCF  8  */ { Err,   Err,   Err,   Err,   Err,   Err,   CFCH,  Skip },
     2711/* ECF  9  */ { Err,   Err,   Err,   Err,   Err,   STRT,  Err,   Skip },
     2712/* BP   10 */ { Err,   Err,   Err,   Err,   Err,   Err,   PMCH,  Skip },
     2713/* PMCH 11 */ { Err,   Err,   PMCH,  Err,   Err,   SPMF,  PMCH,  PMCH  },
     2714/* SPMF 12 */ { Err,   Err,   Err,   Err,   Err,   Err,   PFCH,  Skip },
     2715/* PFCH 13 */ { Err,   Err,   PFCH,  Err,   EP,    Err,   PFCH,  PFCH },
     2716/* EP   14 */ { Err,   Err,   Err,   Err,   Err,   STRT,  Err,   Skip }
     2717    };
     2718   
     2719    list.clear();
     2720    list.setAutoDelete( TRUE );
     2721   
     2722    QPtrList<QStrList> mech;
     2723    QCString buf;
     2724    QStrList *m = NULL;
     2725   
     2726    uint state = STRT;
     2727    uint start = 0, end = 0, space = 0;
     2728   
     2729    for ( uint i = 0; i < len && state != Err ; ++i ) {
     2730        char ch = str[i];
     2731        char *p = strchr( Chars, ch );
     2732        uint input = p ? Inputs[ p - Chars ] : any;
     2733        uint new_state = Fsm[ state ][ input ];
     2734        switch ( new_state ) {
     2735            case Skip:
     2736                continue;
     2737            case CMCH:
     2738            case CFCH:
     2739            case PMCH:
     2740            case PFCH:
     2741                if ( state != new_state )
     2742                    start = end = i;
     2743                ++end;
     2744                // accumulate trailing space for truncation
     2745                if ( input == ws ) ++space;
     2746                else space = 0;
     2747                break;
     2748            case NCM:
     2749            case ECM:
     2750            case SPMF:
     2751                buf = QCString( str.data() + start, end - start - space + 1 );
     2752                // find the mechanism branch in the output list
     2753                for ( m = list.first(); m; m = list.next() ) {
     2754                    if ( qstrcmp( m->getFirst(), buf ) == 0 )
     2755                        break;
     2756                }
     2757                if ( !m ) {
     2758                    // append to the output list if not found
     2759                    m = new QStrList();
     2760                    m->append( buf );
     2761                    list.append( m );
     2762                }
     2763                // store in the current list for making a cross product
     2764                mech.append( m );
     2765                start = end = 0;
     2766                break;
     2767            case NCF:
     2768            case ECF:
     2769            case EP:
     2770                buf = QCString( str.data() + start, end - start - space + 1 );
     2771                // make a cross product with all current mechanisms
     2772                for ( m = mech.first(); m; m = mech.next() )
     2773                    m->append( buf );
     2774                if ( new_state != NCF )
     2775                    mech.clear();
     2776                start = end = 0;
     2777                break;
     2778            default:
     2779                break;
     2780        }
     2781        state = new_state;
     2782    }
     2783   
     2784    return state == ECF || state == EP;
     2785}
     2786
     2787// static
     2788/*! \internal
     2789  Splits the given \a rmf (rendering mechanism/format pair) to a \a mechanism
     2790  and a \a format string. Returns FALSE if fails to parse \a rmf. 
     2791 */
     2792bool QPMMime::parseRMF( HSTR rmf, QCString &mechanism, QCString &format )
     2793{
     2794    QPtrList<QStrList> list;
     2795    if ( !parseRMFs( rmf, list ) )
     2796        return FALSE;
     2797   
     2798    if ( list.count() != 1 || list.getFirst()->count() != 2 )
     2799        return FALSE;
     2800   
     2801    QStrList *m = list.getFirst();
     2802    mechanism = m->first();
     2803    format = m->next();
     2804   
     2805    return TRUE;
     2806}
     2807
     2808// static
     2809/*! \internal */
     2810QPMMime::DefaultDragWorker *QPMMime::defaultCoopDragWorker()
     2811{
     2812    static DefaultDragWorker defCoopDragWorker( FALSE /* exclusive */ );
     2813    return &defCoopDragWorker;
     2814}
     2815
     2816// static
     2817/*! \internal */
     2818QPMMime::DefaultDragWorker *QPMMime::defaultExclDragWorker()
     2819{
     2820    static DefaultDragWorker defExclDragWorker( TRUE /* exclusive */ );
     2821    return &defExclDragWorker;
     2822}
     2823
     2824// static
     2825/*! \internal */
     2826QPMMime::DefaultDropWorker *QPMMime::defaultDropWorker()
     2827{
     2828    static DefaultDropWorker defaultDropWorker;
     2829    return &defaultDropWorker;
     2830}
     2831
     2832#endif // !QT_NO_DRAGANDDROP
     2833
    6292834/*!
    6302835  \fn const char* QPMMime::convertorName()
     
    7232928*/
    7242929
     2930/*!
     2931  \class QPMMime::DragWorker
     2932 
     2933  \internal
     2934  This class is responsible for the sources's part of the Direct
     2935  Manipulation conversation after the drop event occurs on a target.
     2936 
     2937  Drag workers can be super exclusive (solely responsible for converting the
     2938  given mime type to a set of DRAGITEM structures), exclusive (cannot coexist
     2939  with other workers but don't manage the DRAGINFO/DRAGITEM creation), or
     2940  or cooperative (can coexist with other drag and share the same set of DRAGITEM
     2941  structures in order to represent different mime data types). As opposed to
     2942  super exclusive workers (identified by isExclusive() returning TRUE and by
     2943  itemCount() returning zero), exclusive and cooperative workers do not
     2944  create DRAGINFO/DRAGITEM structures on their own, they implement a subset
     2945  of methods that is used by the drag manager to fill drag structures it creates.
     2946
     2947  If a super exlusive or an exclusive worker is encoundered when starting the
     2948  object drag, it will be used only if there are no any other workers found for
     2949  \b other mime types of the object being dragged. If a cooperative worker
     2950  with item count greater than one is encountered, it will be used only if all
     2951  other found workers are also cooperative and require the same number of items.
     2952  In both cases, if the above conditions are broken, the respective workers
     2953  are discarded (ignored). Instead, a special fall-back cooperative worker
     2954  (that requires a single DRAGITEM, supports any mime type and can coexist with
     2955  other one-item cooperative workers) will be used for the given mime type.
     2956
     2957  \note Subclasses must NOT free the DRAGINFO structure they allocated and
     2958  returned by createDragInfo().
     2959  \note Every exclusive drag worker must implement createDragInfo() and must not
     2960  implement composeFormatSting()/canRender()/prepare()/defaultFileType(). And
     2961  vice versa, every cooperative drag worker must implement the latter three
     2962  functions but not the former two.
     2963  \note The return value of cleanup() is whether the Move operation is
     2964  disallowed by this worker or not (if the worker doesn't participate in the
     2965  DND session, it should return FALSE, to let other workers allow Move).
     2966  \note If you need more details, please contact the developers of Qt for OS/2.
     2967*/
     2968
     2969/*!
     2970  \class QPMMime::DefaultDragWorker
     2971 
     2972  \internal
     2973  This class is a Drag Worker that supports standard DRM_SHAREDMEM and
     2974  DRM_OS2FILE and rendering mechanisms. It uses
     2975  QPMMime::DefaultDragWorker::Provider subclasses to map mime types of the
     2976  object being dragged to rendering formats and apply preprocessing of data
     2977  before rendering.
     2978 
     2979  \note If you need more details, please contact the developers of Qt for OS/2.
     2980*/
     2981
     2982/*!
     2983  \class QPMMime::DropWorker
     2984 
     2985  \internal
     2986  This class is responsible for the target's part of the Direct
     2987  Manipulation conversation after the drop event occurs on a target.
     2988 
     2989  Drop workers can be exclusive (solely responsible for converting the given
     2990  set of DRAGITEM structures) or cooperative (can coexist with other drop
     2991  workers in order to produce different mime data types from the same set of
     2992  DRAGITEM structures). If an exclusive drop worker is encountered when
     2993  processing the drop event, all other workers are silently ignored.
     2994 
     2995  \note Subclasses must NOT free the DRAGINFO structure pointed to by info().
     2996  \note Subclasses must NOT send DM_ENDCONVERSATION to the source.
     2997  \note If you need more details, please contact the developers of Qt for OS/2.
     2998*/
     2999
     3000/*!
     3001  \class QPMMime::DefaultDropWorker
     3002 
     3003  \internal
     3004  This class is a Drop Worker that supports standard DRM_SHAREDMEM and
     3005  DRM_OS2FILE and rendering mechanisms. It uses
     3006  QPMMime::DefaultDropWorker::Provider subclasses to map various rendering
     3007  formats to particular mime types and apply postprocessing of data after
     3008  rendering.
     3009 
     3010  \note If you need more details, please contact the developers of Qt for OS/2.
     3011*/
     3012
    7253013#endif // QT_NO_MIME
  • trunk/src/kernel/qt_kernel.pri

    r77 r97  
    128128                  $$KERNEL_CPP/qcursor_pm.cpp \
    129129                  $$KERNEL_CPP/qdesktopwidget_pm.cpp \
     130                  $$KERNEL_CPP/qdnd_pm.cpp \
    130131                  $$KERNEL_CPP/qeventloop_pm.cpp \
    131132                  $$KERNEL_CPP/qfont_pm.cpp \
  • trunk/src/kernel/qwidget_pm.cpp

    r77 r97  
    10511051    }
    10521052
    1053     bool accept_drops = acceptDrops();
    1054     if ( accept_drops )
    1055         setAcceptDrops( FALSE ); // ole dnd unregister (we will register again below)
    10561053    if ( testWFlags(WType_Desktop) )
    10571054        old_winfid = 0;
     
    11441141
    11451142    reparentFocusWidgets( oldtlw );             // fix focus chains
    1146 
    1147     if ( accept_drops )
    1148         setAcceptDrops( TRUE );
    1149 
    1150 //@@TODO (dmik): remove?
    1151 //#ifdef Q_OS_TEMP
    1152 //    // Show borderless toplevel windows in tasklist & NavBar
    1153 //    if ( !parent ) {
    1154 //      QString txt = caption().isEmpty()?qAppName():caption();
    1155 //      SetWindowText( winId(), (TCHAR*)txt.ucs2() );
    1156 //    }
    1157 //#endif
    11581143}
    11591144
     
    25042489void QWidget::createSysExtra()
    25052490{
    2506 //@@TODO (dmik): later
    2507 //    extra->dropTarget = 0;
    25082491}
    25092492
    25102493void QWidget::deleteSysExtra()
    25112494{
    2512     setAcceptDrops( FALSE );
    25132495}
    25142496
     
    25322514bool QWidget::acceptDrops() const
    25332515{
    2534     return 0;
    2535 /// @todo (dmik) later
    2536 //    return ( extra && extra->dropTarget );
     2516    return testWState( WState_DND );   
    25372517}
    25382518
    25392519void QWidget::setAcceptDrops( bool on )
    25402520{
    2541 /// @todo (dmik) later
    2542 //    // Enablement is defined by extra->dropTarget != 0.
    2543 //
    2544 //    if ( on ) {
    2545 //      // Turn on.
    2546 //      createExtra();
    2547 //      QWExtra *extra = extraData();
    2548 //      if ( !extra->dropTarget )
    2549 //          extra->dropTarget = qt_olednd_register( this );
    2550 //    } else {
    2551 //      // Turn off.
    2552 //      QWExtra *extra = extraData();
    2553 //      if ( extra && extra->dropTarget ) {
    2554 //          qt_olednd_unregister(this, extra->dropTarget);
    2555 //          extra->dropTarget = 0;
    2556 //      }
    2557 //    }
     2521    if ( testWState(WState_DND) != on ) {
     2522        if ( on )
     2523            setWState( WState_DND );
     2524        else
     2525            clearWState( WState_DND );
     2526    }
    25582527}
    25592528
  • trunk/src/qt.pro

    r93 r97  
    203203os2 {
    204204        # this section should be removed one day
    205     # when an OS/2 version of every module is ready
     205        # when an OS/2 version of every module is ready
    206206        include($$KERNEL_CPP/qt_kernel.pri)
    207207        include($$WIDGETS_CPP/qt_widgets.pri)
     
    240240
    241241#install directives
    242 #@@todo(dmik): need this on OS/2?
    243242!os2:include(qt_install.pri)
    244243!staticlib:PRL_EXPORT_DEFINES += QT_SHARED
Note: See TracChangeset for help on using the changeset viewer.