Changeset 97
- Timestamp:
- Jul 6, 2006, 11:25:56 PM (19 years ago)
- Location:
- trunk
- Files:
-
- 1 added
- 9 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/configure.cmd
r95 r97 69 69 * yet on OS/2. NOTE: don't touch this variable! 70 70 */ 71 G.!QTMandatoryDefines = 'QT_NO_ DRAGANDDROP QT_NO_PRINTER QT_NO_IPV6'71 G.!QTMandatoryDefines = 'QT_NO_PRINTER QT_NO_IPV6' 72 72 73 73 /* Configuration cache file */ -
trunk/include/qdragobject.h
r8 r97 219 219 220 220 #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. 222 225 // 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 227 class Q_EXPORT QDragManager: public QObject { 228 Q_OBJECT 229 230 private: 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 249 private: 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 269 268 #endif 270 269 -
trunk/include/qmime.h
r8 r97 160 160 161 161 #ifndef QT_H 162 #include "qptrlist.h" // down here for GCC 2.7.* compatibility 162 #include "qptrlist.h" 163 #include "qstrlist.h" 163 164 #endif // QT_H 164 165 165 166 /* 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. 169 169 */ 170 170 171 class Q_EXPORT QPMMime { 171 class Q_EXPORT QPMMime 172 { 172 173 public: 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 173 321 enum { CFI_Storage = CFI_POINTER & CFI_HANDLE }; 174 322 … … 182 330 static const char* cfToMime( ulong cf ); 183 331 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 186 348 virtual const char* convertorName() = 0; 187 349 virtual int countCf() = 0; … … 193 355 virtual QByteArray convertToMime( QByteArray data, const char *mime, ulong cf ) = 0; 194 356 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 195 366 }; 196 367 -
trunk/include/qwindowdefs_pm.h
r61 r97 78 78 #endif 79 79 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 80 88 typedef HWND WId; 81 89 -
trunk/src/kernel/qapplication_pm.cpp
r78 r97 657 657 // there is no need to unregister private window classes -- it is done 658 658 // automatically upon process termination. 659 // QAsciiDictIterator<int> it(*winclassNames);660 // const char *k;661 // while ( (k = it.currentKey()) ) {662 // }663 659 delete winclassNames; 664 660 winclassNames = 0; … … 913 909 extern uint qGlobalPostedEventsCount(); 914 910 911 #ifndef QT_NO_DRAGANDDROP 912 extern MRESULT qt_dispatchDragAndDrop( QWidget *, const QMSG & ); // qdnd_pm.cpp 913 #endif 914 915 915 /*! 916 916 The message procedure calls this function for every message … … 945 945 } 946 946 } 947 948 947 949 948 // … … 1169 1168 result = widget->translateWheelEvent( qmsg ); 1170 1169 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 ) ); 1171 1174 #endif 1172 1175 } else { … … 1589 1592 break; 1590 1593 1591 /// @todo (dmik) later1594 /// @todo (dmik) remove? 1592 1595 // case WM_IME_STARTCOMPOSITION: 1593 1596 // result = QInputContext::startComposition(); … … 2182 2185 static int qt_extraKeyState = 0; 2183 2186 2184 // State holder for mouse buttons 2185 static int qt_buttonState = 0; 2187 static 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 } 2186 2200 2187 2201 // … … 2195 2209 autoCaptureWnd = h; 2196 2210 2197 if ( ! qt_buttonState) {2211 if ( !mouseButtonState() ) { 2198 2212 // all buttons released, we don't actually capture the mouse 2199 2213 // (see QWidget::translateMouseEvent()) … … 2242 2256 static int translateButtonState( USHORT s, int type, int button ) 2243 2257 { 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 2250 2260 if ( type == QEvent::ContextMenu ) { 2251 2261 if ( WinGetKeyState( HWND_DESKTOP, VK_SHIFT ) & 0x8000 ) … … 2279 2289 } 2280 2290 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 */ 2295 void qt_pmMouseButtonUp() 2296 { 2297 // release any stored mouse capture 2298 qt_button_down = 0; 2299 autoCaptureReleased = TRUE; 2300 releaseAutoCapture(); 2301 } 2290 2302 2291 2303 bool QETWidget::translateMouseEvent( const QMSG &qmsg ) … … 2358 2370 type = (QEvent::Type)mouseTbl[++i]; // event type 2359 2371 button = mouseTbl[++i]; // which button 2360 // @@TODO (dmik):later (extra buttons)2372 /// @todo (dmik) later (extra buttons) 2361 2373 // if ( button > Qt::MidButton ) { 2362 2374 // switch( GET_XBUTTON_WPARAM( msg.wParam ) ) { … … 2424 2436 #endif 2425 2437 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, 2427 2440 // otherwise disabled windows will not get mouse events? 2428 2441 qt_dispatchEnterLeave( this, QWidget::find(curWin) ); -
trunk/src/kernel/qmime_pm.cpp
r20 r97 40 40 #ifndef QT_NO_MIME 41 41 42 #include <stdlib.h> 43 #include <time.h> 44 42 45 #include "qstrlist.h" 43 46 #include "qimage.h" … … 45 48 #include "qdragobject.h" 46 49 #include "qbuffer.h" 50 #include "qapplication.h" 47 51 #include "qapplication_p.h" 52 #include "qeventloop.h" 48 53 #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" 50 60 #include "qt_os2.h" 51 61 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 */ 90 void 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 */ 126 static 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 202 static 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 210 static inline int hex_to_int( char c ) 211 { 212 return hex_to_int( (uchar) c ); 213 } 214 215 // ----------------------------------------------------------------------------- 216 217 static QPtrList<HWND> msgWindows; 218 219 static 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 229 QPMMime::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 248 QPMMime::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 259 MRESULT 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 285 struct 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 342 void 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 354 QPMMime::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 362 QPMMime::DefaultDragWorker::~DefaultDragWorker() 363 { 364 d->cleanupRequests(); 365 delete d; 366 } 367 368 bool 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 410 bool QPMMime::DefaultDragWorker::isExclusive() const 411 { 412 return d->exclusive; 413 } 414 415 ULONG QPMMime::DefaultDragWorker::itemCount() const 416 { 417 return d->itemCnt; 418 } 419 420 QCString 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 459 bool 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 505 void 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 517 MRESULT 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 765 bool 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 791 bool QPMMime::DefaultDragWorker::canRender( const char *drm ) 792 { 793 return qstrcmp( drm, DRM_SHAREDMEM ) == 0 || 794 qstrcmp( drm, DRM_OS2FILE ) == 0; 795 } 796 797 //------------------------------------------------------------------------------ 798 799 struct 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 829 QPMMime::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 837 QPMMime::DefaultDropWorker::~DefaultDropWorker() 838 { 839 delete d; 840 } 841 842 void 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 859 bool QPMMime::DefaultDropWorker::isExclusive() const 860 { 861 return d->exclusive; 862 } 863 864 bool QPMMime::DefaultDropWorker::provides( const char *format ) const 865 { 866 return d->providerFor( format ) != NULL; 867 } 868 869 int QPMMime::DefaultDropWorker::formatCount() const 870 { 871 return d->providers.count(); 872 } 873 874 const 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 881 static 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 922 QByteArray 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 1207 MRESULT 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 1246 bool 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 1259 bool 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 1273 bool 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 */ 1291 bool 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 //------------------------------------------------------------------------------ 52 1329 53 1330 static QPtrList<QPMMime> mimes; … … 96 1373 QPMMime::QPMMime() 97 1374 { 98 mimes.append( this ); 1375 // we prepend the convertor to let it override other convertors 1376 mimes.prepend( this ); 99 1377 } 100 1378 … … 115 1393 ulong cf; 116 1394 QCString mime; 1395 #if !defined(QT_NO_DRAGANDDROP) 1396 QCString drf; 1397 #endif // !QT_NO_DRAGANDDROP 117 1398 }; 118 119 static QPtrList<QPMRegisteredMimeType> mimetypes;120 1399 121 1400 122 1401 class QPMMimeAnyMime : public QPMMime { 123 1402 public: 124 const char* convertorName(); 1403 QPMMimeAnyMime(); 1404 ~QPMMimeAnyMime(); 1405 1406 const char *convertorName(); 125 1407 int countCf(); 126 1408 ulong cf( int index ); 127 1409 ulong flFor( ulong cf ); 128 1410 ulong cfFor( const char* mime ); 129 const char *mimeFor( ulong cf );1411 const char *mimeFor( ulong cf ); 130 1412 bool canConvert( const char* mime, ulong cf ); 131 1413 QByteArray convertToMime( QByteArray data, const char *, ulong ); 132 1414 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 1448 private: 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 133 1463 }; 1464 1465 QPMMimeAnyMime::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 1474 QPMMimeAnyMime::~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 } 134 1483 135 1484 const char* QPMMimeAnyMime::convertorName() … … 176 1525 if ( mt->cf == cf ) 177 1526 return mt->mime; 178 return 0; 1527 // try to determine the mime type from the clipboard format ID 1528 return lookupMimeType( cf ); 179 1529 } 180 1530 181 1531 bool QPMMimeAnyMime::canConvert( const char* mime, ulong cf ) 182 1532 { 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; 198 1534 } 199 1535 … … 208 1544 } 209 1545 210 1546 #if !defined(QT_NO_DRAGANDDROP) 1547 1548 const 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 1562 bool 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 1572 void 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 1587 const 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 1600 bool 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 1610 QPMMime::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 1627 QPMMime::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 1691 ulong 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 1709 const 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 } 211 1735 212 1736 class QPMMimeText : public QPMMime { 213 1737 public: 214 1738 QPMMimeText(); 1739 ~QPMMimeText(); 215 1740 const char* convertorName(); 216 1741 int countCf(); … … 222 1747 QByteArray convertToMime( QByteArray data, const char *, ulong cf ); 223 1748 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 224 1802 private: 225 1803 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 226 1810 }; 227 1811 … … 234 1818 } 235 1819 1820 QPMMimeText::~QPMMimeText() 1821 { 1822 WinDeleteAtom( WinQuerySystemAtomTable(), 0xFFFF0000 | CF_TextUnicode ); 1823 } 1824 236 1825 const char* QPMMimeText::convertorName() 237 1826 { … … 261 1850 ulong QPMMimeText::cfFor( const char* mime ) 262 1851 { 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; 266 1855 267 1856 QCString m( mime ); … … 276 1865 if ( cs == "ISO-10646-UCS-2" ) 277 1866 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; 280 1873 } 281 1874 … … 283 1876 { 284 1877 if ( cf == CF_TEXT ) 285 return "text/plain;charset=system";1878 return MIME_TEXT_PLAIN_CHARSET_SYSTEM; 286 1879 else if ( cf == CF_TextUnicode ) 287 1880 return "text/plain;charset=ISO-10646-UCS-2"; … … 407 2000 } 408 2001 2002 #if !defined(QT_NO_DRAGANDDROP) 2003 2004 DRAGINFO *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 2076 bool QPMMimeText::NativeFileDrop::provides( const char *format ) const 2077 { 2078 return qstricmp( format, MIME_TEXT_URI_LIST ) == 0; 2079 } 2080 2081 int QPMMimeText::NativeFileDrop::formatCount() const 2082 { 2083 return 1; 2084 } 2085 2086 const char *QPMMimeText::NativeFileDrop::format( int fn ) const 2087 { 2088 return fn == 0 ? MIME_TEXT_URI_LIST : NULL; 2089 } 2090 2091 QByteArray 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 2125 const 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 2136 bool 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 2199 void 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 2213 const 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 2222 bool 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 2273 QPMMime::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 2308 QPMMime::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 409 2372 410 2373 class QPMMimeImage : public QPMMime { … … 519 2482 520 2483 521 static QPMMimeAnyMime *anymime = 0;522 523 2484 static 524 2485 void cleanup_mimes() … … 528 2489 delete wm; 529 2490 } 530 mimetypes.setAutoDelete( TRUE );531 mimetypes.clear();532 anymime = 0;533 2491 } 534 2492 … … 539 2497 { 540 2498 if ( mimes.isEmpty() ) { 2499 // add standard convertors so that QPMMimeAnyMime is the last in the list 2500 new QPMMimeAnyMime; 541 2501 new QPMMimeImage; 542 2502 new QPMMimeText; 543 anymime = new QPMMimeAnyMime;544 545 2503 qAddPostRoutine( cleanup_mimes ); 546 2504 } 547 }548 549 /*!550 \internal551 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 the558 // dummy "pass-through" QPMMimeAnyMime convertor (which will handle the559 // 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_DEBUG568 qSystemWarning( "QPMMime: Failed to register clipboard format" );569 #endif570 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 check579 if ( !(mt = mimetypes.current()) || mt->cf != f )580 return 0;581 }582 }583 return f;584 2505 } 585 2506 … … 627 2548 } 628 2549 2550 #if !defined(QT_NO_DRAGANDDROP) 2551 2552 /*! 2553 Returns a string represented by \a hstr. 2554 */ 2555 QCString 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 */ 2570 QCString 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 */ 2596 bool 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 */ 2672 bool 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 */ 2792 bool 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 */ 2810 QPMMime::DefaultDragWorker *QPMMime::defaultCoopDragWorker() 2811 { 2812 static DefaultDragWorker defCoopDragWorker( FALSE /* exclusive */ ); 2813 return &defCoopDragWorker; 2814 } 2815 2816 // static 2817 /*! \internal */ 2818 QPMMime::DefaultDragWorker *QPMMime::defaultExclDragWorker() 2819 { 2820 static DefaultDragWorker defExclDragWorker( TRUE /* exclusive */ ); 2821 return &defExclDragWorker; 2822 } 2823 2824 // static 2825 /*! \internal */ 2826 QPMMime::DefaultDropWorker *QPMMime::defaultDropWorker() 2827 { 2828 static DefaultDropWorker defaultDropWorker; 2829 return &defaultDropWorker; 2830 } 2831 2832 #endif // !QT_NO_DRAGANDDROP 2833 629 2834 /*! 630 2835 \fn const char* QPMMime::convertorName() … … 723 2928 */ 724 2929 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 725 3013 #endif // QT_NO_MIME -
trunk/src/kernel/qt_kernel.pri
r77 r97 128 128 $$KERNEL_CPP/qcursor_pm.cpp \ 129 129 $$KERNEL_CPP/qdesktopwidget_pm.cpp \ 130 $$KERNEL_CPP/qdnd_pm.cpp \ 130 131 $$KERNEL_CPP/qeventloop_pm.cpp \ 131 132 $$KERNEL_CPP/qfont_pm.cpp \ -
trunk/src/kernel/qwidget_pm.cpp
r77 r97 1051 1051 } 1052 1052 1053 bool accept_drops = acceptDrops();1054 if ( accept_drops )1055 setAcceptDrops( FALSE ); // ole dnd unregister (we will register again below)1056 1053 if ( testWFlags(WType_Desktop) ) 1057 1054 old_winfid = 0; … … 1144 1141 1145 1142 reparentFocusWidgets( oldtlw ); // fix focus chains 1146 1147 if ( accept_drops )1148 setAcceptDrops( TRUE );1149 1150 //@@TODO (dmik): remove?1151 //#ifdef Q_OS_TEMP1152 // // Show borderless toplevel windows in tasklist & NavBar1153 // if ( !parent ) {1154 // QString txt = caption().isEmpty()?qAppName():caption();1155 // SetWindowText( winId(), (TCHAR*)txt.ucs2() );1156 // }1157 //#endif1158 1143 } 1159 1144 … … 2504 2489 void QWidget::createSysExtra() 2505 2490 { 2506 //@@TODO (dmik): later2507 // extra->dropTarget = 0;2508 2491 } 2509 2492 2510 2493 void QWidget::deleteSysExtra() 2511 2494 { 2512 setAcceptDrops( FALSE );2513 2495 } 2514 2496 … … 2532 2514 bool QWidget::acceptDrops() const 2533 2515 { 2534 return 0; 2535 /// @todo (dmik) later 2536 // return ( extra && extra->dropTarget ); 2516 return testWState( WState_DND ); 2537 2517 } 2538 2518 2539 2519 void QWidget::setAcceptDrops( bool on ) 2540 2520 { 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 } 2558 2527 } 2559 2528 -
trunk/src/qt.pro
r93 r97 203 203 os2 { 204 204 # this section should be removed one day 205 205 # when an OS/2 version of every module is ready 206 206 include($$KERNEL_CPP/qt_kernel.pri) 207 207 include($$WIDGETS_CPP/qt_widgets.pri) … … 240 240 241 241 #install directives 242 #@@todo(dmik): need this on OS/2?243 242 !os2:include(qt_install.pri) 244 243 !staticlib:PRL_EXPORT_DEFINES += QT_SHARED
Note:
See TracChangeset
for help on using the changeset viewer.