1 | /****************************************************************************
|
---|
2 | ** $Id$
|
---|
3 | **
|
---|
4 | ** Implementation of the PM direct manipulation protocol (drag and drop) for Qt.
|
---|
5 | **
|
---|
6 | ** Copyright (C) 1992-2002 Trolltech AS. All rights reserved.
|
---|
7 | ** Copyright (C) 2004 Norman ASA. Initial OS/2 Port.
|
---|
8 | ** Copyright (C) 2005 netlabs.org. Further OS/2 Development.
|
---|
9 | **
|
---|
10 | ** This file is part of the kernel module of the Qt GUI Toolkit.
|
---|
11 | **
|
---|
12 | ** This file may be distributed under the terms of the Q Public License
|
---|
13 | ** as defined by Trolltech AS of Norway and appearing in the file
|
---|
14 | ** LICENSE.QPL included in the packaging of this file.
|
---|
15 | **
|
---|
16 | ** This file may be distributed and/or modified under the terms of the
|
---|
17 | ** GNU General Public License version 2 as published by the Free Software
|
---|
18 | ** Foundation and appearing in the file LICENSE.GPL included in the
|
---|
19 | ** packaging of this file.
|
---|
20 | **
|
---|
21 | ** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
|
---|
22 | ** licenses may use this file in accordance with the Qt Commercial License
|
---|
23 | ** Agreement provided with the Software.
|
---|
24 | **
|
---|
25 | ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
---|
26 | ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
---|
27 | **
|
---|
28 | ** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
|
---|
29 | ** information about Qt Commercial License Agreements.
|
---|
30 | ** See http://www.trolltech.com/qpl/ for QPL licensing information.
|
---|
31 | ** See http://www.trolltech.com/gpl/ for GPL licensing information.
|
---|
32 | **
|
---|
33 | ** Contact info@trolltech.com if any conditions of this licensing are
|
---|
34 | ** not clear to you.
|
---|
35 | **
|
---|
36 | **********************************************************************/
|
---|
37 |
|
---|
38 | #include "qapplication.h"
|
---|
39 |
|
---|
40 | #ifndef QT_NO_DRAGANDDROP
|
---|
41 |
|
---|
42 | #include "qapplication_p.h"
|
---|
43 | #include "qeventloop.h"
|
---|
44 | #include "qpainter.h"
|
---|
45 | #include "qwidget.h"
|
---|
46 | #include "qdragobject.h"
|
---|
47 | #include "qimage.h"
|
---|
48 | #include "qbuffer.h"
|
---|
49 | #include "qdatastream.h"
|
---|
50 | #include "qbitmap.h"
|
---|
51 | #include "qptrlist.h"
|
---|
52 | #include "qt_os2.h"
|
---|
53 |
|
---|
54 | #if defined(QT_ACCESSIBILITY_SUPPORT)
|
---|
55 | #include "qaccessible.h"
|
---|
56 | #endif
|
---|
57 |
|
---|
58 | // if you enable this, it makes sense to enable it in qmime_pm.cpp as well
|
---|
59 | //#define QT_DEBUG_DND
|
---|
60 |
|
---|
61 | extern void qt_pmMouseButtonUp(); // defined in qapplication_pm.cpp
|
---|
62 |
|
---|
63 | bool QDragManager::eventFilter( QObject *, QEvent *)
|
---|
64 | {
|
---|
65 | // not used in the PM implementation
|
---|
66 | return FALSE;
|
---|
67 | }
|
---|
68 |
|
---|
69 | void QDragManager::timerEvent( QTimerEvent* )
|
---|
70 | {
|
---|
71 | // not used in the PM implementation
|
---|
72 | }
|
---|
73 |
|
---|
74 | void QDragManager::cancel( bool deleteSource /* = TRUE */ )
|
---|
75 | {
|
---|
76 | // Note: currently, this method is called only by
|
---|
77 | // QDragObject::~QDragObject() with deleteSource = FALSE
|
---|
78 |
|
---|
79 | Q_ASSERT( object && !beingCancelled );
|
---|
80 | if ( !object || beingCancelled )
|
---|
81 | return;
|
---|
82 |
|
---|
83 | beingCancelled = TRUE;
|
---|
84 |
|
---|
85 | dragSource = 0;
|
---|
86 |
|
---|
87 | if ( !deleteSource ) {
|
---|
88 | object = 0;
|
---|
89 | } else {
|
---|
90 | QDragObject *obj = object;
|
---|
91 | object = 0;
|
---|
92 | delete obj;
|
---|
93 | }
|
---|
94 |
|
---|
95 | #ifndef QT_NO_CURSOR
|
---|
96 | if ( restoreCursor ) {
|
---|
97 | QApplication::restoreOverrideCursor();
|
---|
98 | restoreCursor = FALSE;
|
---|
99 | }
|
---|
100 | #endif
|
---|
101 | #if defined(QT_ACCESSIBILITY_SUPPORT)
|
---|
102 | QAccessible::updateAccessibility( this, 0, QAccessible::DragDropEnd );
|
---|
103 | #endif
|
---|
104 | }
|
---|
105 |
|
---|
106 | void QDragManager::move( const QPoint & )
|
---|
107 | {
|
---|
108 | // not used in the PM implementation
|
---|
109 | }
|
---|
110 |
|
---|
111 |
|
---|
112 | void QDragManager::drop()
|
---|
113 | {
|
---|
114 | // not used in the PM implementation
|
---|
115 | }
|
---|
116 |
|
---|
117 | /** \internal
|
---|
118 | * Data for QDragEnterEvent/QDragMoveEvent/QPMDropEvent.
|
---|
119 | */
|
---|
120 | class QPMDragData
|
---|
121 | {
|
---|
122 | public:
|
---|
123 | QPMDragData()
|
---|
124 | : initialized( FALSE ), dropped( FALSE )
|
---|
125 | , gotWorkers( FALSE ), di( NULL )
|
---|
126 | {}
|
---|
127 |
|
---|
128 | ~QPMDragData() { reset(); }
|
---|
129 |
|
---|
130 | void initialize( DRAGINFO *di );
|
---|
131 | void reset( bool isAccepted, bool isActAccepted );
|
---|
132 | void reset() { reset( FALSE, FALSE ); }
|
---|
133 |
|
---|
134 | void setDropped( bool d ) { dropped = d; }
|
---|
135 | bool isDropped() const { return dropped; }
|
---|
136 |
|
---|
137 | DRAGINFO *info() const { return di; }
|
---|
138 |
|
---|
139 | bool provides( const char *format );
|
---|
140 | const char *format( int fn );
|
---|
141 | QByteArray encodedData( const char *format );
|
---|
142 |
|
---|
143 | private:
|
---|
144 |
|
---|
145 | void initWorkers();
|
---|
146 |
|
---|
147 | bool initialized : 1;
|
---|
148 | bool dropped : 1;
|
---|
149 | bool gotWorkers : 1;
|
---|
150 |
|
---|
151 | DRAGINFO *di;
|
---|
152 | QPtrList<QPMMime::DropWorker> workers;
|
---|
153 | };
|
---|
154 |
|
---|
155 | void QPMDragData::initialize( DRAGINFO *info )
|
---|
156 | {
|
---|
157 | Q_ASSERT( info );
|
---|
158 | if ( initialized || !info )
|
---|
159 | return;
|
---|
160 |
|
---|
161 | initialized = TRUE;
|
---|
162 | di = info;
|
---|
163 | }
|
---|
164 |
|
---|
165 | void QPMDragData::initWorkers()
|
---|
166 | {
|
---|
167 | Q_ASSERT( initialized );
|
---|
168 | if ( !initialized || gotWorkers )
|
---|
169 | return;
|
---|
170 |
|
---|
171 | gotWorkers = TRUE;
|
---|
172 |
|
---|
173 | // go through all convertors and collect DropWorkers to use
|
---|
174 | QPtrList<QPMMime> mimes = QPMMime::all();
|
---|
175 | for ( QPMMime *mime = mimes.first(); mime; mime = mimes.next() ) {
|
---|
176 | QPMMime::DropWorker *wrk = mime->dropWorkerFor( di );
|
---|
177 | if ( wrk ) {
|
---|
178 | if ( wrk->isExclusive() ) {
|
---|
179 | // ignore all other workers if some of them identified itself
|
---|
180 | // as exclusive
|
---|
181 | workers.clear();
|
---|
182 | workers.append( wrk );
|
---|
183 | break;
|
---|
184 | }
|
---|
185 | // ensure there are no duplicates
|
---|
186 | if ( !workers.containsRef( wrk ) )
|
---|
187 | workers.append( wrk );
|
---|
188 | }
|
---|
189 | }
|
---|
190 |
|
---|
191 | #if defined(QT_DEBUG_DND)
|
---|
192 | qDebug( "QPMDragData: %d drop workers for DRAGINFO %p",
|
---|
193 | workers.count(), di );
|
---|
194 | #endif
|
---|
195 |
|
---|
196 | // init all workers
|
---|
197 | for ( QPMMime::DropWorker *w = workers.first(); w; w = workers.next() ) {
|
---|
198 | w->nfo = di;
|
---|
199 | w->init();
|
---|
200 | }
|
---|
201 | }
|
---|
202 |
|
---|
203 | void QPMDragData::reset( bool isAccepted, bool isActAccepted )
|
---|
204 | {
|
---|
205 | if ( !initialized )
|
---|
206 | return;
|
---|
207 |
|
---|
208 | // cleanup all workers
|
---|
209 | for ( QPMMime::DropWorker *w = workers.first(); w; w = workers.next() ) {
|
---|
210 | w->cleanup( isAccepted, isActAccepted );
|
---|
211 | w->nfo = NULL;
|
---|
212 | }
|
---|
213 |
|
---|
214 | workers.clear();
|
---|
215 | di = NULL;
|
---|
216 | initialized = dropped = gotWorkers = FALSE;
|
---|
217 | }
|
---|
218 |
|
---|
219 | bool QPMDragData::provides( const char *format )
|
---|
220 | {
|
---|
221 | if ( !gotWorkers )
|
---|
222 | initWorkers();
|
---|
223 |
|
---|
224 | bool result = FALSE;
|
---|
225 |
|
---|
226 | for ( QPMMime::DropWorker *w = workers.first();
|
---|
227 | w && !result; w = workers.next() )
|
---|
228 | result = w->provides( format );
|
---|
229 |
|
---|
230 | return result;
|
---|
231 | }
|
---|
232 |
|
---|
233 | const char *QPMDragData::format( int fn )
|
---|
234 | {
|
---|
235 | if ( !gotWorkers )
|
---|
236 | initWorkers();
|
---|
237 |
|
---|
238 | for ( QPMMime::DropWorker *w = workers.first();
|
---|
239 | w; w = workers.next() ) {
|
---|
240 | int count = w->formatCount();
|
---|
241 | if ( fn < count )
|
---|
242 | return w->format( fn );
|
---|
243 | fn = fn - count;
|
---|
244 | }
|
---|
245 |
|
---|
246 | return NULL;
|
---|
247 | }
|
---|
248 |
|
---|
249 | QByteArray QPMDragData::encodedData( const char *format )
|
---|
250 | {
|
---|
251 | if ( !gotWorkers )
|
---|
252 | initWorkers();
|
---|
253 |
|
---|
254 | QByteArray result;
|
---|
255 |
|
---|
256 | for ( QPMMime::DropWorker *w = workers.first();
|
---|
257 | w && result.isNull(); w = workers.next() )
|
---|
258 | result = w->encodedData( format );
|
---|
259 |
|
---|
260 | return result;
|
---|
261 | }
|
---|
262 |
|
---|
263 | class QPMDropEvent : public QDropEvent
|
---|
264 | {
|
---|
265 | public:
|
---|
266 | QPMDropEvent( const QPoint &pos, QPMDragData *data )
|
---|
267 | : QDropEvent( pos ) { d = data; }
|
---|
268 |
|
---|
269 | inline bool isTrulyAccepted() const { return accpt; }
|
---|
270 | inline bool isActionTrulyAccepted() const { return accptact; }
|
---|
271 | };
|
---|
272 |
|
---|
273 | class QPMDragEnterEvent : public QDragEnterEvent
|
---|
274 | {
|
---|
275 | public:
|
---|
276 | QPMDragEnterEvent( const QPoint &pos, QPMDragData *data )
|
---|
277 | : QDragEnterEvent( pos ) { d = data; }
|
---|
278 |
|
---|
279 | inline bool isTrulyAccepted() const { return accpt; }
|
---|
280 | inline bool isActionTrulyAccepted() const { return accptact; }
|
---|
281 | };
|
---|
282 |
|
---|
283 | class QPMDragMoveEvent : public QDragMoveEvent
|
---|
284 | {
|
---|
285 | public:
|
---|
286 | QPMDragMoveEvent( const QPoint &pos, QPMDragData *data )
|
---|
287 | : QDragMoveEvent( pos ) { d = data; }
|
---|
288 |
|
---|
289 | inline bool isTrulyAccepted() const { return accpt; }
|
---|
290 | inline bool isActionTrulyAccepted() const { return accptact; }
|
---|
291 | };
|
---|
292 |
|
---|
293 | bool QDropEvent::provides( const char* mimeType ) const
|
---|
294 | {
|
---|
295 | QPMDragData *data = static_cast<QPMDragData *> (d);
|
---|
296 | Q_ASSERT( data );
|
---|
297 | if ( !data )
|
---|
298 | return FALSE;
|
---|
299 |
|
---|
300 | return data->provides( mimeType );
|
---|
301 | }
|
---|
302 |
|
---|
303 | const char *QDropEvent::format( int fn ) const
|
---|
304 | {
|
---|
305 | QPMDragData *data = static_cast<QPMDragData *> (d);
|
---|
306 | Q_ASSERT( data );
|
---|
307 | if ( !data )
|
---|
308 | return NULL;
|
---|
309 |
|
---|
310 | return data->format( fn );
|
---|
311 | }
|
---|
312 |
|
---|
313 | QByteArray QDropEvent::encodedData( const char *format ) const
|
---|
314 | {
|
---|
315 | QByteArray array;
|
---|
316 |
|
---|
317 | QPMDragData *data = static_cast<QPMDragData *> (d);
|
---|
318 | Q_ASSERT( data );
|
---|
319 |
|
---|
320 | if ( !data || !data->isDropped() ) {
|
---|
321 | // This is a QDragEnterEvent/QDragMoveEvent subclass; we cannot provide
|
---|
322 | // any MIME contents yet because it's impossible to do data transfers
|
---|
323 | // before DM_DROP is sent. Return shortly.
|
---|
324 | return array;
|
---|
325 | }
|
---|
326 |
|
---|
327 | array = data->encodedData( format );
|
---|
328 | return array;
|
---|
329 | }
|
---|
330 |
|
---|
331 | /*!
|
---|
332 | * \internal
|
---|
333 | * Direct manipulation (Drag & Drop) event handler
|
---|
334 | */
|
---|
335 | MRESULT qt_dispatchDragAndDrop( QWidget *widget, const QMSG &qmsg )
|
---|
336 | {
|
---|
337 | static HWND lastDragOverHwnd = 0; // last target window
|
---|
338 | static USHORT lastDragOverOp = 0; // last DM_DRAGOVER operation
|
---|
339 | static USHORT lastRealOp = 0; // last real op (never DO_DEFAULT or DO_UNKNOWN)
|
---|
340 | static USHORT defaultOp = 0; // default op for DO_DEFAULT or DO_UNKNOWN
|
---|
341 |
|
---|
342 | static USHORT supportedOps = 0; // operations supported by all items
|
---|
343 | static bool sourceAllowsOp = FALSE; // does source allow requested operation
|
---|
344 |
|
---|
345 | static bool lastAccept = FALSE; // last reply from the target
|
---|
346 | static bool lastAcceptAction = FALSE; // last action reply from the target
|
---|
347 |
|
---|
348 | static QPMDragData dragData;
|
---|
349 |
|
---|
350 | Q_ASSERT( widget );
|
---|
351 |
|
---|
352 | BOOL ok = FALSE;
|
---|
353 |
|
---|
354 | switch( qmsg.msg ) {
|
---|
355 | case DM_DRAGOVER: {
|
---|
356 | bool first = lastDragOverHwnd != qmsg.hwnd;
|
---|
357 | if ( first ) {
|
---|
358 | // the first DM_DRAGOVER message
|
---|
359 | lastDragOverHwnd = qmsg.hwnd;
|
---|
360 | lastAccept = lastAcceptAction = FALSE;
|
---|
361 | supportedOps = DO_COPYABLE | DO_MOVEABLE | DO_LINKABLE;
|
---|
362 | // ensure drag data is reset (just in case of a wrong msg flow...)
|
---|
363 | dragData.reset();
|
---|
364 | }
|
---|
365 |
|
---|
366 | Q_ASSERT( first || widget->acceptDrops() );
|
---|
367 | if ( !widget->acceptDrops() ) {
|
---|
368 | if ( !first ) {
|
---|
369 | // Odin32 apps are dramatically bogus, they continue to send
|
---|
370 | // DM_DRAGOVER even if we reply DOR_NEVERDROP. Simulate
|
---|
371 | // DM_DRAGLEAVE
|
---|
372 | lastDragOverHwnd = 0;
|
---|
373 | dragData.reset();
|
---|
374 | }
|
---|
375 | return MRFROM2SHORT( DOR_NEVERDROP, 0 );
|
---|
376 | }
|
---|
377 |
|
---|
378 | DRAGINFO *info = (DRAGINFO *) qmsg.mp1;
|
---|
379 | ok = DrgAccessDraginfo( info );
|
---|
380 | Q_ASSERT( ok && info );
|
---|
381 | if ( !ok || !info )
|
---|
382 | return MRFROM2SHORT( DOR_NEVERDROP, 0 );
|
---|
383 |
|
---|
384 | USHORT dropReply = DOR_DROP;
|
---|
385 |
|
---|
386 | if ( first ) {
|
---|
387 | // determine the set of operations supported by *all* items
|
---|
388 | // (this implies that DRAGITEM::fsSupportedOps is a bit field)
|
---|
389 | ULONG itemCount = DrgQueryDragitemCount( info );
|
---|
390 | for ( ULONG i = 0; i < itemCount; ++ i ) {
|
---|
391 | PDRAGITEM item = DrgQueryDragitemPtr( info, i );
|
---|
392 | Q_ASSERT( item );
|
---|
393 | if ( !item ) {
|
---|
394 | dropReply = DOR_NEVERDROP;
|
---|
395 | break;
|
---|
396 | }
|
---|
397 | supportedOps &= item->fsSupportedOps;
|
---|
398 | }
|
---|
399 | if ( dropReply != DOR_NEVERDROP ) {
|
---|
400 | Q_ASSERT( itemCount );
|
---|
401 | if ( !itemCount || !supportedOps ) {
|
---|
402 | // items don't have even a single common operation...
|
---|
403 | dropReply = DOR_NEVERDROP;
|
---|
404 | } else {
|
---|
405 | // determine the default operation
|
---|
406 | // (in order MOVE, COPY, LINK, for compatibility with Win32)
|
---|
407 | if ( supportedOps & DO_MOVEABLE ) defaultOp = DO_MOVE;
|
---|
408 | else if ( supportedOps & DO_COPYABLE ) defaultOp = DO_COPY;
|
---|
409 | else if ( supportedOps & DO_LINKABLE ) defaultOp = DO_LINK;
|
---|
410 | else Q_ASSERT( FALSE ); // should never happen
|
---|
411 | }
|
---|
412 | }
|
---|
413 | }
|
---|
414 |
|
---|
415 | if ( dropReply != DOR_NEVERDROP ) {
|
---|
416 |
|
---|
417 | if ( first || lastDragOverOp != info->usOperation ) {
|
---|
418 | // the current drop operation was changed by a modifier key
|
---|
419 | lastDragOverOp = info->usOperation;
|
---|
420 | lastRealOp = info->usOperation;
|
---|
421 | if ( lastRealOp == DO_DEFAULT || lastRealOp == DO_UNKNOWN ) {
|
---|
422 | // the default operation is requested
|
---|
423 | lastRealOp = defaultOp;
|
---|
424 | }
|
---|
425 | sourceAllowsOp =
|
---|
426 | ((supportedOps & DO_MOVEABLE) && lastRealOp == DO_MOVE) ||
|
---|
427 | ((supportedOps & DO_COPYABLE) && lastRealOp == DO_COPY) ||
|
---|
428 | ((supportedOps & DO_LINKABLE) && lastRealOp == DO_LINK);
|
---|
429 | }
|
---|
430 |
|
---|
431 | // Note that if sourceAllowsOp = false here, we have to deliver
|
---|
432 | // events anyway (stealing them from Qt would be confusing), but
|
---|
433 | // we will silently ignore any accept commands and always reject
|
---|
434 | // the drop. Other platforms seem to do the same.
|
---|
435 |
|
---|
436 | // convert the operation to an Action
|
---|
437 | QDropEvent::Action action = QDropEvent::Copy;
|
---|
438 | if ( lastRealOp == DO_COPY ) action = QDropEvent::Copy;
|
---|
439 | else if ( lastRealOp == DO_MOVE ) action = QDropEvent::Move;
|
---|
440 | else if ( lastRealOp == DO_LINK ) action = QDropEvent::Link;
|
---|
441 | else Q_ASSERT( FALSE ); // should never happen
|
---|
442 |
|
---|
443 | // flip y coordinate
|
---|
444 | QPoint pnt( info->xDrop, info->yDrop );
|
---|
445 | pnt.setY( QApplication::desktop()->height() - (pnt.y() + 1) );
|
---|
446 | pnt = widget->mapFromGlobal( pnt );
|
---|
447 |
|
---|
448 | // initialize drag data used in QPMDrag.../QPMDrop... events
|
---|
449 | if ( first )
|
---|
450 | dragData.initialize( info );
|
---|
451 |
|
---|
452 | QDropEvent *de = NULL;
|
---|
453 | QPMDragEnterEvent *dee = NULL;
|
---|
454 | QPMDragMoveEvent *dme = NULL;
|
---|
455 |
|
---|
456 | if ( first )
|
---|
457 | de = dee = new QPMDragEnterEvent( pnt, &dragData );
|
---|
458 | else
|
---|
459 | de = dme = new QPMDragMoveEvent( pnt, &dragData );
|
---|
460 |
|
---|
461 | de->setAction( action );
|
---|
462 | de->accept( lastAccept );
|
---|
463 | de->acceptAction( lastAcceptAction );
|
---|
464 |
|
---|
465 | QApplication::sendEvent( widget, de );
|
---|
466 |
|
---|
467 | // if not allowed or not accepted, always reply DOR_NODROP
|
---|
468 | // to have DM_DRAGOVER delivered to us again in any case
|
---|
469 |
|
---|
470 | dropReply = sourceAllowsOp && de->isAccepted() ?
|
---|
471 | DOR_DROP : DOR_NODROP;
|
---|
472 |
|
---|
473 | lastAccept = dee ? dee->isTrulyAccepted() :
|
---|
474 | dme->isTrulyAccepted();
|
---|
475 | lastAcceptAction = dee ? dee->isActionTrulyAccepted() :
|
---|
476 | dme->isActionTrulyAccepted();
|
---|
477 |
|
---|
478 | delete de;
|
---|
479 | }
|
---|
480 |
|
---|
481 | DrgFreeDraginfo( info );
|
---|
482 |
|
---|
483 | return MRFROM2SHORT( dropReply, lastRealOp );
|
---|
484 | }
|
---|
485 | case DM_DRAGLEAVE: {
|
---|
486 | // Odin32 apps produce incorrect message flow, ignore
|
---|
487 | Q_ASSERT( lastDragOverHwnd != 0 );
|
---|
488 | if ( lastDragOverHwnd == 0 )
|
---|
489 | return 0;
|
---|
490 |
|
---|
491 | lastDragOverHwnd = 0;
|
---|
492 | dragData.reset();
|
---|
493 |
|
---|
494 | if ( !widget->acceptDrops() )
|
---|
495 | return 0;
|
---|
496 |
|
---|
497 | QDragLeaveEvent de;
|
---|
498 | QApplication::sendEvent( widget, &de );
|
---|
499 | return 0;
|
---|
500 | }
|
---|
501 | case DM_DROP: {
|
---|
502 | // Odin32 apps produce incorrect message flow, ignore
|
---|
503 | Q_ASSERT( lastDragOverHwnd != 0 );
|
---|
504 | if ( lastDragOverHwnd == 0 )
|
---|
505 | return 0;
|
---|
506 |
|
---|
507 | // Odin32 apps send DM_DROP even if we replied DOR_NEVERDROP or
|
---|
508 | // DOR_NODROP, simulate DM_DRAGLEAVE
|
---|
509 | Q_ASSERT( lastAccept || lastAcceptAction );
|
---|
510 | if ( !lastAccept && !lastAcceptAction ) {
|
---|
511 | WinSendMsg( qmsg.hwnd, DM_DRAGLEAVE, 0, 0 );
|
---|
512 | return 0;
|
---|
513 | }
|
---|
514 |
|
---|
515 | lastDragOverHwnd = 0;
|
---|
516 |
|
---|
517 | Q_ASSERT( widget->acceptDrops() );
|
---|
518 | if ( !widget->acceptDrops() )
|
---|
519 | return 0;
|
---|
520 |
|
---|
521 | DRAGINFO *info = (DRAGINFO *) qmsg.mp1;
|
---|
522 | ok = DrgAccessDraginfo( info );
|
---|
523 | Q_ASSERT( ok && info );
|
---|
524 | if ( !ok || !info )
|
---|
525 | return MRFROM2SHORT( DOR_NEVERDROP, 0 );
|
---|
526 |
|
---|
527 | Q_ASSERT( lastRealOp == info->usOperation );
|
---|
528 |
|
---|
529 | // convert the operation to an Action
|
---|
530 | QDropEvent::Action action = QDropEvent::Copy;
|
---|
531 | if ( lastRealOp == DO_COPY ) action = QDropEvent::Copy;
|
---|
532 | else if ( lastRealOp == DO_MOVE ) action = QDropEvent::Move;
|
---|
533 | else if ( lastRealOp == DO_LINK ) action = QDropEvent::Link;
|
---|
534 | else Q_ASSERT( FALSE ); // should never happen
|
---|
535 |
|
---|
536 | // flip y coordinate
|
---|
537 | QPoint pnt( info->xDrop, info->yDrop );
|
---|
538 | pnt.setY( QApplication::desktop()->height() - (pnt.y() + 1) );
|
---|
539 |
|
---|
540 | dragData.setDropped( TRUE );
|
---|
541 |
|
---|
542 | QPMDropEvent de( widget->mapFromGlobal( pnt ), &dragData );
|
---|
543 | de.setAction( action );
|
---|
544 | de.accept( lastAccept );
|
---|
545 | de.acceptAction( lastAcceptAction );
|
---|
546 |
|
---|
547 | QApplication::sendEvent( widget, &de );
|
---|
548 |
|
---|
549 | dragData.reset( de.isTrulyAccepted(), de.isActionTrulyAccepted() );
|
---|
550 |
|
---|
551 | // If the target has accepted the particular Drop action (using
|
---|
552 | // acceptAction() rather than just accept()), it means that it will
|
---|
553 | // perform the necessary operation on its own (for example, will
|
---|
554 | // delete the source if the accepted action is Move). In this case,
|
---|
555 | // we always send DMFL_TARGETFAIL to the source to prevent it from
|
---|
556 | // doing the same on its side.
|
---|
557 |
|
---|
558 | ULONG targetReply =
|
---|
559 | de.isTrulyAccepted() && !de.isActionTrulyAccepted() ?
|
---|
560 | DMFL_TARGETSUCCESSFUL : DMFL_TARGETFAIL;
|
---|
561 |
|
---|
562 | // send DM_ENDCONVERSATION for every item
|
---|
563 | ULONG itemCount = DrgQueryDragitemCount( info );
|
---|
564 | for ( ULONG i = 0; i < itemCount; ++ i ) {
|
---|
565 | PDRAGITEM item = DrgQueryDragitemPtr( info, i );
|
---|
566 | Q_ASSERT( item );
|
---|
567 | if ( !item )
|
---|
568 | continue;
|
---|
569 | // it is possible that this item required DM_RENDERPREPARE but
|
---|
570 | // returned FALSE in reply to it (so hwndItem may be NULL)
|
---|
571 | if ( !item->hwndItem )
|
---|
572 | continue;
|
---|
573 | DrgSendTransferMsg( item->hwndItem, DM_ENDCONVERSATION,
|
---|
574 | MPFROMLONG( item->ulItemID ),
|
---|
575 | MPFROMLONG( targetReply ) );
|
---|
576 | }
|
---|
577 |
|
---|
578 | DrgDeleteDraginfoStrHandles( info );
|
---|
579 | DrgFreeDraginfo( info );
|
---|
580 |
|
---|
581 | return 0;
|
---|
582 | }
|
---|
583 | default:
|
---|
584 | break;
|
---|
585 | }
|
---|
586 |
|
---|
587 | return WinDefWindowProc( qmsg.hwnd, qmsg.msg, qmsg.mp1, qmsg.mp2 );
|
---|
588 | }
|
---|
589 |
|
---|
590 | class QPMCoopDragWorker : public QPMMime::DragWorker, public QPMObjectWindow
|
---|
591 | {
|
---|
592 | public:
|
---|
593 | QPMCoopDragWorker() : info( NULL ) {}
|
---|
594 | bool collectWorkers( QDragObject *o );
|
---|
595 |
|
---|
596 | // DragpWorker interface
|
---|
597 | void init();
|
---|
598 | bool cleanup( bool isCancelled );
|
---|
599 | bool isExclusive() const { return TRUE; }
|
---|
600 | ULONG itemCount() const { return 0; }
|
---|
601 | HWND hwnd() const;
|
---|
602 | DRAGINFO *createDragInfo( const char *name, USHORT supportedOps );
|
---|
603 |
|
---|
604 | // QPMObjectWindow interface
|
---|
605 | MRESULT message( ULONG msg, MPARAM mp1, MPARAM mp2 );
|
---|
606 |
|
---|
607 | private:
|
---|
608 | QPtrList<DragWorker> workers;
|
---|
609 | DRAGINFO *info;
|
---|
610 | };
|
---|
611 |
|
---|
612 | bool QPMCoopDragWorker::collectWorkers( QDragObject *o )
|
---|
613 | {
|
---|
614 | Q_ASSERT( o );
|
---|
615 | if ( !o )
|
---|
616 | return FALSE;
|
---|
617 |
|
---|
618 | bool gotExcl = FALSE; // got isExclusive() worker?
|
---|
619 | bool skipExcl = FALSE; // skip isExclusive() or itemCount() > 1 workers?
|
---|
620 | ULONG coopLevel = 0; // itemCount() level for !isExclusive() workers
|
---|
621 |
|
---|
622 | bool gotExclForMime = FALSE;
|
---|
623 |
|
---|
624 | // go through all formats and all convertors to collect DragWorkers
|
---|
625 | const char *fmt = NULL;
|
---|
626 | for ( int i = 0; (fmt = o->format( i )); ++i ) {
|
---|
627 | #if defined(QT_DEBUG_DND)
|
---|
628 | qDebug( "QPMCoopDragWorker: Searching for worker for mime [%s]...", fmt );
|
---|
629 | #endif
|
---|
630 | QPtrList<QPMMime> mimes = QPMMime::all();
|
---|
631 | for ( QPMMime *mime = mimes.first(); mime; mime = mimes.next() ) {
|
---|
632 | QPMMime::DragWorker *wrk = mime->dragWorkerFor( fmt, o );
|
---|
633 | if ( !wrk )
|
---|
634 | continue;
|
---|
635 | #if defined(QT_DEBUG_DND)
|
---|
636 | qDebug( "QPMCoopDragWorker: Got worker %p (isExclusive()=%d, "
|
---|
637 | "itemCount()=%ld) from convertor '%s' (gotExclForMime=%d, "
|
---|
638 | "gotExcl=%d, skipExcl=%d, coopLevel=%ld)",
|
---|
639 | wrk, wrk->isExclusive(), wrk->itemCount(),
|
---|
640 | mime->convertorName(), gotExclForMime, gotExcl, skipExcl,
|
---|
641 | coopLevel );
|
---|
642 | #endif
|
---|
643 | if ( wrk->isExclusive() ) {
|
---|
644 | if ( !skipExcl && !gotExclForMime ) {
|
---|
645 | gotExclForMime = TRUE;
|
---|
646 | if ( !gotExcl ) {
|
---|
647 | gotExcl = TRUE;
|
---|
648 | workers.append( wrk );
|
---|
649 | } else {
|
---|
650 | // skip everything exclusive unless it's exactly the
|
---|
651 | // same worker
|
---|
652 | skipExcl = !workers.containsRef( wrk );
|
---|
653 | }
|
---|
654 | }
|
---|
655 | // continue to search for a fall-back cooperative 1-item worker
|
---|
656 | // (like QPMMimeAnyMime) for the case if this worker quits
|
---|
657 | // the game
|
---|
658 | continue;
|
---|
659 | }
|
---|
660 | ULONG itemCnt = wrk->itemCount();
|
---|
661 | if ( itemCnt == 0 ) {
|
---|
662 | #if defined(QT_CHECK_RANGE)
|
---|
663 | qWarning( "Cooperative DragWorker %p for mime [%s] has "
|
---|
664 | "itemCount() = 0!", wrk, fmt );
|
---|
665 | #endif
|
---|
666 | continue;
|
---|
667 | }
|
---|
668 | if ( itemCnt > 1 ) {
|
---|
669 | // coop workers with item count > 1 are also considered exclusive
|
---|
670 | // here, because may not co-exist with 1-item workers that should
|
---|
671 | // always be able to contribute
|
---|
672 | if ( !gotExcl && !skipExcl && !gotExclForMime ) {
|
---|
673 | gotExclForMime = TRUE;
|
---|
674 | workers.append( wrk );
|
---|
675 | if ( !coopLevel )
|
---|
676 | coopLevel = itemCnt;
|
---|
677 | // only those for the same number of items can proceed
|
---|
678 | if ( itemCnt != coopLevel )
|
---|
679 | skipExcl = TRUE;
|
---|
680 | }
|
---|
681 | // continue to search for a fall-back cooperative 1-item worker
|
---|
682 | // (like QPMMimeAnyMime) for the case if this worker quits
|
---|
683 | // the game
|
---|
684 | continue;
|
---|
685 | }
|
---|
686 | workers.append( wrk );
|
---|
687 | // Don't search for other workrers for the same mime type --
|
---|
688 | // we've already got a drag worker for it and adding another
|
---|
689 | // one would just introduce mime type duplicates on the drop
|
---|
690 | // target's side (where the first encountered drop worker
|
---|
691 | // for the given RMF would be used for actual data transfer
|
---|
692 | // anyway). See also QClipboard::setData().
|
---|
693 | break;
|
---|
694 | }
|
---|
695 | if ( gotExclForMime ) {
|
---|
696 | // ensure we have a fall-back coop (should be the last added item)
|
---|
697 | DragWorker *w = workers.getLast();
|
---|
698 | if ( w->isExclusive() || w->itemCount() > 1 ) {
|
---|
699 | #if defined(QT_CHECK_STATE)
|
---|
700 | qWarning( "DragWorker %p for [%s] (isExclusive()=%d, itemCount()"
|
---|
701 | "=%ld) has no fall-back cooperative 1-item worker!",
|
---|
702 | w, fmt, w->isExclusive(), w->itemCount() );
|
---|
703 | #endif
|
---|
704 | workers.removeLast();
|
---|
705 | }
|
---|
706 | gotExclForMime = FALSE;
|
---|
707 | } else {
|
---|
708 | // got a regular (non-fall-back) 1-item coop, skip evreything else
|
---|
709 | skipExcl = TRUE;
|
---|
710 | }
|
---|
711 | }
|
---|
712 |
|
---|
713 | // remove either all exclusive workers or all their fall-back workers
|
---|
714 | // (depending on skipExcl) and remove duplicates
|
---|
715 | for ( DragWorker *wrk = workers.first(); wrk; ) {
|
---|
716 | bool excl = wrk->isExclusive() || wrk->itemCount() > 1;
|
---|
717 | if ( skipExcl == excl || workers.containsRef( wrk ) > 1 ) {
|
---|
718 | workers.remove();
|
---|
719 | wrk = workers.current();
|
---|
720 | } else {
|
---|
721 | wrk = workers.next();
|
---|
722 | }
|
---|
723 | }
|
---|
724 |
|
---|
725 | #if defined(QT_DEBUG_DND)
|
---|
726 | for ( DragWorker *wrk = workers.first(); wrk; wrk = workers.next() ) {
|
---|
727 | qDebug( "QPMCoopDragWorker: Will use worker %p (isExclusive()=%d, "
|
---|
728 | "itemCount()=%ld)", wrk, wrk->isExclusive(), wrk->itemCount() );
|
---|
729 | }
|
---|
730 | #endif
|
---|
731 |
|
---|
732 | Q_ASSERT( workers.count() > 0 );
|
---|
733 | return workers.count() > 0;
|
---|
734 | }
|
---|
735 |
|
---|
736 | HWND QPMCoopDragWorker::hwnd() const
|
---|
737 | {
|
---|
738 | DragWorker *firstWorker = workers.getFirst();
|
---|
739 | Q_ASSERT( firstWorker );
|
---|
740 | if ( !firstWorker )
|
---|
741 | return 0;
|
---|
742 |
|
---|
743 | if ( firstWorker->isExclusive() && firstWorker->itemCount() == 0 ) {
|
---|
744 | // this is a super exclusive worker that will do everything on its own
|
---|
745 | return firstWorker->hwnd();
|
---|
746 | }
|
---|
747 |
|
---|
748 | return QPMObjectWindow::hwnd();
|
---|
749 | }
|
---|
750 |
|
---|
751 | void QPMCoopDragWorker::init()
|
---|
752 | {
|
---|
753 | Q_ASSERT( source() );
|
---|
754 | for ( DragWorker *wrk = workers.first(); wrk; wrk = workers.next() ) {
|
---|
755 | wrk->src = source();
|
---|
756 | wrk->init();
|
---|
757 | }
|
---|
758 | }
|
---|
759 |
|
---|
760 | bool QPMCoopDragWorker::cleanup( bool isCancelled )
|
---|
761 | {
|
---|
762 | bool moveDisallowed = FALSE;
|
---|
763 |
|
---|
764 | for ( DragWorker *wrk = workers.first(); wrk; wrk = workers.next() ) {
|
---|
765 | // disallow the Move operation if at least one worker asked so
|
---|
766 | moveDisallowed |= wrk->cleanup( isCancelled );
|
---|
767 | wrk->src = NULL;
|
---|
768 | }
|
---|
769 | workers.clear();
|
---|
770 | info = NULL;
|
---|
771 | return moveDisallowed;
|
---|
772 | }
|
---|
773 |
|
---|
774 | DRAGINFO *QPMCoopDragWorker::createDragInfo( const char *name, USHORT supportedOps )
|
---|
775 | {
|
---|
776 | Q_ASSERT( !info );
|
---|
777 | if ( info )
|
---|
778 | return NULL;
|
---|
779 |
|
---|
780 | DragWorker *firstWorker = workers.getFirst();
|
---|
781 | Q_ASSERT( firstWorker );
|
---|
782 | if ( !firstWorker )
|
---|
783 | return NULL;
|
---|
784 |
|
---|
785 | ULONG itemCnt = firstWorker->itemCount();
|
---|
786 |
|
---|
787 | if ( firstWorker->isExclusive() && itemCnt == 0 ) {
|
---|
788 | // this is a super exclusive worker that will do everything on its own
|
---|
789 | #if defined(QT_DEBUG_DND)
|
---|
790 | qDebug( "QPMCoopDragWorker: Will redirect to super worker %p",
|
---|
791 | firstWorker );
|
---|
792 | #endif
|
---|
793 | return firstWorker->createDragInfo( name, supportedOps );
|
---|
794 | }
|
---|
795 |
|
---|
796 | // note that all workers at this place require the same amount of items
|
---|
797 | // (guaranteed by collectWorkers())
|
---|
798 |
|
---|
799 | #if defined(QT_DEBUG_DND)
|
---|
800 | qDebug( "QPMCoopDragWorker: itemCnt=%ld", itemCnt );
|
---|
801 | #endif
|
---|
802 |
|
---|
803 | info = DrgAllocDraginfo( itemCnt );
|
---|
804 | Q_ASSERT( info );
|
---|
805 | if ( !info )
|
---|
806 | return NULL;
|
---|
807 |
|
---|
808 | // collect all mechanism/format pairs
|
---|
809 | QCString allFormats;
|
---|
810 | for ( DragWorker *wrk = workers.first(); wrk; wrk = workers.next() ) {
|
---|
811 | QCString formats = wrk->composeFormatString();
|
---|
812 | Q_ASSERT( !formats.isNull() );
|
---|
813 | if ( !formats.isNull() ) {
|
---|
814 | if ( allFormats.isNull() )
|
---|
815 | allFormats = formats;
|
---|
816 | else {
|
---|
817 | allFormats += ",";
|
---|
818 | allFormats += formats;
|
---|
819 | }
|
---|
820 | }
|
---|
821 | }
|
---|
822 |
|
---|
823 | #if defined(QT_DEBUG_DND)
|
---|
824 | qDebug( "QPMCoopDragWorker: allFormats=%s", allFormats.data() );
|
---|
825 | #endif
|
---|
826 |
|
---|
827 | static ULONG itemID = 0;
|
---|
828 |
|
---|
829 | const char *type = NULL;
|
---|
830 | const char *ext = NULL;
|
---|
831 | firstWorker->defaultFileType( type, ext );
|
---|
832 |
|
---|
833 | bool ok = TRUE;
|
---|
834 | for ( ULONG i = 0; i < itemCnt; ++i ) {
|
---|
835 | DRAGITEM *item = DrgQueryDragitemPtr( info, i );
|
---|
836 | Q_ASSERT( item );
|
---|
837 | if ( !item ) {
|
---|
838 | ok = FALSE;
|
---|
839 | break;
|
---|
840 | }
|
---|
841 |
|
---|
842 | QCString targetName;
|
---|
843 | if ( itemCnt == 1 ) targetName = name;
|
---|
844 | else targetName.sprintf( "%s %ld", name, i + 1 );
|
---|
845 |
|
---|
846 | if ( ext ) {
|
---|
847 | targetName += ".";
|
---|
848 | targetName += ext;
|
---|
849 | }
|
---|
850 |
|
---|
851 | #if defined(QT_DEBUG_DND)
|
---|
852 | qDebug( "QPMCoopDragWorker: item %ld: type=[%s], name=\"%s\"",
|
---|
853 | i, type, targetName.data() );
|
---|
854 | #endif
|
---|
855 |
|
---|
856 | // Note 1: DRAGITEM::hstrType is actually ignored by WPS,
|
---|
857 | // only the target extension matters.
|
---|
858 |
|
---|
859 | // Note 2: We're not required to fill in the hwndItem field because we
|
---|
860 | // use the DC_PREPARE flag (to indicate it will be filled later, after
|
---|
861 | // DM_RENDERPREPARE); however, Mozilla refuses to render if hwndItem
|
---|
862 | // is initially 0. Set it to our HWND instead (we'll issue a warning if
|
---|
863 | // DM_RENDER or DM_ENDCONVERSATION is erroneously sent to us)
|
---|
864 |
|
---|
865 | item->hwndItem = hwnd();
|
---|
866 | item->ulItemID = itemID ++;
|
---|
867 | item->hstrType = DrgAddStrHandle( type ? type : DRT_UNKNOWN );
|
---|
868 | item->hstrRMF = DrgAddStrHandle( allFormats );
|
---|
869 | item->hstrContainerName = 0;
|
---|
870 | item->hstrSourceName = 0;
|
---|
871 | item->hstrTargetName = DrgAddStrHandle( targetName );
|
---|
872 | item->cxOffset = 0;
|
---|
873 | item->cyOffset = 0;
|
---|
874 | item->fsControl = DC_PREPARE; // require DM_RENDERPREPARE from target
|
---|
875 | item->fsSupportedOps = supportedOps;
|
---|
876 | }
|
---|
877 |
|
---|
878 | if ( !ok ) {
|
---|
879 | DrgFreeDraginfo( info );
|
---|
880 | info = NULL;
|
---|
881 | }
|
---|
882 |
|
---|
883 | return info;
|
---|
884 | }
|
---|
885 |
|
---|
886 | extern void qt_DrgFreeDragtransfer( DRAGTRANSFER *xfer ); // defined in qmime_pm.cpp
|
---|
887 |
|
---|
888 | MRESULT QPMCoopDragWorker::message( ULONG msg, MPARAM mp1, MPARAM mp2 )
|
---|
889 | {
|
---|
890 | if ( msg == DM_RENDERPREPARE ) {
|
---|
891 | if ( !info ) {
|
---|
892 | #if defined(QT_CHECK_STATE)
|
---|
893 | qWarning( "Drop target sent DM_RENDERPREPARE after the DnD session "
|
---|
894 | "is over!" );
|
---|
895 | #endif
|
---|
896 | // free the given DRAGTRANSFER structure to avoud memory leak
|
---|
897 | DRAGTRANSFER *xfer = (DRAGTRANSFER *) mp1;
|
---|
898 | if ( xfer )
|
---|
899 | qt_DrgFreeDragtransfer( xfer );
|
---|
900 | return (MRESULT) FALSE;
|
---|
901 | }
|
---|
902 |
|
---|
903 | DRAGTRANSFER *xfer = (DRAGTRANSFER *) mp1;
|
---|
904 | Q_ASSERT( xfer && xfer->pditem );
|
---|
905 | if ( !xfer || !xfer->pditem )
|
---|
906 | return (MRESULT) FALSE;
|
---|
907 |
|
---|
908 | // find the item's index (ordinal number)
|
---|
909 | ULONG itemCnt = DrgQueryDragitemCount( info );
|
---|
910 | ULONG index = 0;
|
---|
911 | for ( ; index < itemCnt; ++index )
|
---|
912 | if ( DrgQueryDragitemPtr( info, index ) == xfer->pditem )
|
---|
913 | break;
|
---|
914 |
|
---|
915 | Q_ASSERT( index < itemCnt );
|
---|
916 | if ( index >= itemCnt )
|
---|
917 | return (MRESULT) FALSE;
|
---|
918 |
|
---|
919 | #if defined(QT_DEBUG_DND)
|
---|
920 | qDebug( "QPMCoopDragWorker: Got DM_RENDERPREPARE to %s for item %ld "
|
---|
921 | "(id=%ld)",
|
---|
922 | QPMMime::queryHSTR( xfer->hstrSelectedRMF ).data(),
|
---|
923 | index, xfer->pditem->ulItemID );
|
---|
924 | #endif
|
---|
925 |
|
---|
926 | QCString drm, drf;
|
---|
927 | if ( !QPMMime::parseRMF( xfer->hstrSelectedRMF, drm, drf ) ) {
|
---|
928 | Q_ASSERT( FALSE );
|
---|
929 | return (MRESULT) FALSE;
|
---|
930 | }
|
---|
931 |
|
---|
932 | DragWorker *wrk = NULL;
|
---|
933 | for ( wrk = workers.first(); wrk; wrk = workers.next() )
|
---|
934 | if ( wrk->prepare( drm, drf, xfer->pditem, index ) )
|
---|
935 | break;
|
---|
936 | if ( !wrk ) {
|
---|
937 | #if defined(QT_DEBUG_DND)
|
---|
938 | qDebug( "QPMCoopDragWorker: No suitable worker found" );
|
---|
939 | #endif
|
---|
940 | return (MRESULT) FALSE;
|
---|
941 | }
|
---|
942 |
|
---|
943 | xfer->pditem->hwndItem = wrk->hwnd();
|
---|
944 | Q_ASSERT( xfer->pditem->hwndItem );
|
---|
945 | return (MRESULT) ( xfer->pditem->hwndItem ? TRUE : FALSE );
|
---|
946 | }
|
---|
947 |
|
---|
948 | if ( msg == DM_RENDER || msg == DM_ENDCONVERSATION ) {
|
---|
949 | #if defined(QT_CHECK_STATE)
|
---|
950 | qWarning( "Drop target sent DM_RENDER or DM_ENDCONVERSATION to the "
|
---|
951 | "drag source window instead of the drag item window!" );
|
---|
952 | #endif
|
---|
953 | if ( msg == DM_RENDER ) {
|
---|
954 | // free the given DRAGTRANSFER structure to avoud memory leak
|
---|
955 | DRAGTRANSFER *xfer = (DRAGTRANSFER *) mp1;
|
---|
956 | if ( xfer )
|
---|
957 | qt_DrgFreeDragtransfer( xfer );
|
---|
958 | }
|
---|
959 | }
|
---|
960 |
|
---|
961 | return (MRESULT) FALSE;
|
---|
962 | }
|
---|
963 |
|
---|
964 | bool QDragManager::drag( QDragObject *o, QDragObject::DragMode mode )
|
---|
965 | {
|
---|
966 | // return shortly if drag is already in progress or being cancelled
|
---|
967 | if ( object || beingCancelled )
|
---|
968 | return FALSE;
|
---|
969 |
|
---|
970 | QDragObject::setTarget( 0 );
|
---|
971 |
|
---|
972 | // detect a mouse button to end dragging
|
---|
973 | LONG vkTerminate = 0;
|
---|
974 | {
|
---|
975 | ULONG msg = WinQuerySysValue( HWND_DESKTOP, SV_BEGINDRAG ) & 0xFFFF;
|
---|
976 | switch( msg ) {
|
---|
977 | case WM_BUTTON1MOTIONSTART: vkTerminate = VK_BUTTON1; break;
|
---|
978 | case WM_BUTTON2MOTIONSTART: vkTerminate = VK_BUTTON2; break;
|
---|
979 | case WM_BUTTON3MOTIONSTART: vkTerminate = VK_BUTTON3; break;
|
---|
980 | }
|
---|
981 |
|
---|
982 | if ( WinGetKeyState( HWND_DESKTOP, vkTerminate ) & 0x8000 ) {
|
---|
983 | // prefer the default button if it is pressed
|
---|
984 | } else if ( WinGetKeyState( HWND_DESKTOP, VK_BUTTON2 ) & 0x8000 ) {
|
---|
985 | vkTerminate = VK_BUTTON2;
|
---|
986 | } else if ( WinGetKeyState( HWND_DESKTOP, VK_BUTTON1 ) & 0x8000 ) {
|
---|
987 | vkTerminate = VK_BUTTON1;
|
---|
988 | } else if ( WinGetKeyState( HWND_DESKTOP, VK_BUTTON3 ) & 0x8000 ) {
|
---|
989 | vkTerminate = VK_BUTTON3;
|
---|
990 | } else {
|
---|
991 | vkTerminate = 0;
|
---|
992 | }
|
---|
993 | }
|
---|
994 |
|
---|
995 | if ( !vkTerminate ) {
|
---|
996 | #if defined(QT_CHECK_STATE)
|
---|
997 | qWarning( "QDragManager: No valid mouse button pressed, "
|
---|
998 | "dragging cancelled!" );
|
---|
999 | #endif
|
---|
1000 | delete o;
|
---|
1001 | return FALSE;
|
---|
1002 | }
|
---|
1003 |
|
---|
1004 | USHORT supportedOps = 0;
|
---|
1005 |
|
---|
1006 | switch ( mode ) {
|
---|
1007 | case QDragObject::DragDefault:
|
---|
1008 | supportedOps = DO_MOVEABLE | DO_COPYABLE | DO_LINKABLE;
|
---|
1009 | break;
|
---|
1010 | case QDragObject::DragMove:
|
---|
1011 | supportedOps = DO_MOVEABLE;
|
---|
1012 | break;
|
---|
1013 | case QDragObject::DragCopy:
|
---|
1014 | supportedOps = DO_COPYABLE;
|
---|
1015 | break;
|
---|
1016 | case QDragObject::DragCopyOrMove:
|
---|
1017 | supportedOps = DO_MOVEABLE | DO_COPYABLE;
|
---|
1018 | break;
|
---|
1019 | case QDragObject::DragLink:
|
---|
1020 | supportedOps = DO_LINKABLE;
|
---|
1021 | break;
|
---|
1022 | }
|
---|
1023 |
|
---|
1024 | static QPMCoopDragWorker dragWorker;
|
---|
1025 |
|
---|
1026 | bool ok = dragWorker.collectWorkers( o );
|
---|
1027 | Q_ASSERT( ok );
|
---|
1028 | Q_ASSERT( dragWorker.hwnd() );
|
---|
1029 | if ( !ok || ! dragWorker.hwnd() ) {
|
---|
1030 | delete o;
|
---|
1031 | return FALSE;
|
---|
1032 | }
|
---|
1033 |
|
---|
1034 | object = o;
|
---|
1035 | dragSource = (QWidget *) (object->parent());
|
---|
1036 |
|
---|
1037 | dragWorker.src = o;
|
---|
1038 | dragWorker.init();
|
---|
1039 | DRAGINFO *info = dragWorker.createDragInfo( o->name(), supportedOps );
|
---|
1040 |
|
---|
1041 | Q_ASSERT( info );
|
---|
1042 | if ( !info ) {
|
---|
1043 | dragWorker.cleanup( TRUE /* isCancelled */);
|
---|
1044 | dragWorker.src = NULL;
|
---|
1045 | dragSource = 0;
|
---|
1046 | object = 0;
|
---|
1047 | delete o;
|
---|
1048 | return FALSE;
|
---|
1049 | }
|
---|
1050 |
|
---|
1051 | #if defined(QT_ACCESSIBILITY_SUPPORT)
|
---|
1052 | QAccessible::updateAccessibility( this, 0, QAccessible::DragDropStart );
|
---|
1053 | #endif
|
---|
1054 |
|
---|
1055 | /// @todo (dmik)
|
---|
1056 | // updatePixmap();
|
---|
1057 |
|
---|
1058 | DRAGIMAGE img;
|
---|
1059 | img.cb = sizeof (DRAGIMAGE);
|
---|
1060 | img.hImage = WinQuerySysPointer( HWND_DESKTOP, SPTR_FILE, FALSE );
|
---|
1061 | img.fl = DRG_ICON;
|
---|
1062 | img.cxOffset = 0;
|
---|
1063 | img.cyOffset = 0;
|
---|
1064 |
|
---|
1065 | // the mouse is most likely captured by Qt at this point, uncapture it
|
---|
1066 | // or DrgDrag() will definitely fail
|
---|
1067 | WinSetCapture( HWND_DESKTOP, 0 );
|
---|
1068 |
|
---|
1069 | HWND target = DrgDrag( dragWorker.hwnd(), info, &img, 1, vkTerminate,
|
---|
1070 | (PVOID) 0x80000000L ); // don't lock the desktop PS
|
---|
1071 |
|
---|
1072 | #if defined(QT_DEBUG_DND)
|
---|
1073 | qDebug( "QDragManager: DrgDrag() returned %08lX (err=0x%08lX)",
|
---|
1074 | target, WinGetLastError( 0 ) );
|
---|
1075 | #endif
|
---|
1076 |
|
---|
1077 | // we won't get any mouse release event, so manually adjust qApp state
|
---|
1078 | qt_pmMouseButtonUp();
|
---|
1079 |
|
---|
1080 | bool moveDisallowed = dragWorker.cleanup( beingCancelled || target == 0 );
|
---|
1081 | dragWorker.src = NULL;
|
---|
1082 |
|
---|
1083 | moveDisallowed |= beingCancelled || target == 0 ||
|
---|
1084 | info->usOperation != DO_MOVE;
|
---|
1085 |
|
---|
1086 | #if defined(QT_DEBUG_DND)
|
---|
1087 | qDebug( "QDragManager: moveDisallowed=%d", moveDisallowed );
|
---|
1088 | #endif
|
---|
1089 |
|
---|
1090 | if ( target == 0 )
|
---|
1091 | DrgDeleteDraginfoStrHandles( info );
|
---|
1092 | DrgFreeDraginfo( info );
|
---|
1093 |
|
---|
1094 | if ( !beingCancelled ) {
|
---|
1095 | QDragObject::setTarget( QWidget::find( target ) );
|
---|
1096 | cancel(); // this will delete o (object)
|
---|
1097 | }
|
---|
1098 |
|
---|
1099 | beingCancelled = FALSE;
|
---|
1100 |
|
---|
1101 | /// @todo (dmik)
|
---|
1102 | // updatePixmap();
|
---|
1103 |
|
---|
1104 | return !moveDisallowed;
|
---|
1105 | }
|
---|
1106 |
|
---|
1107 | void QDragManager::updatePixmap()
|
---|
1108 | {
|
---|
1109 | /// @todo (dmik) later
|
---|
1110 |
|
---|
1111 | /// @todo (dmik) remove
|
---|
1112 | // if ( object ) {
|
---|
1113 | // if ( cursor ) {
|
---|
1114 | //#ifndef Q_OS_TEMP
|
---|
1115 | // for ( int i=0; i<n_cursor; i++ ) {
|
---|
1116 | // DestroyCursor(cursor[i]);
|
---|
1117 | // }
|
---|
1118 | //#endif
|
---|
1119 | // delete [] cursor;
|
---|
1120 | // cursor = 0;
|
---|
1121 | // }
|
---|
1122 | //
|
---|
1123 | // QPixmap pm = object->pixmap();
|
---|
1124 | // if ( pm.isNull() ) {
|
---|
1125 | // // None.
|
---|
1126 | // } else {
|
---|
1127 | // cursor = new HCURSOR[n_cursor];
|
---|
1128 | // QPoint pm_hot = object->pixmapHotSpot();
|
---|
1129 | // for (int cnum=0; cnum<n_cursor; cnum++) {
|
---|
1130 | // QPixmap cpm = pm_cursor[cnum];
|
---|
1131 | //
|
---|
1132 | // int x1 = QMIN(-pm_hot.x(),0);
|
---|
1133 | // int x2 = QMAX(pm.width()-pm_hot.x(),cpm.width());
|
---|
1134 | // int y1 = QMIN(-pm_hot.y(),0);
|
---|
1135 | // int y2 = QMAX(pm.height()-pm_hot.y(),cpm.height());
|
---|
1136 | //
|
---|
1137 | // int w = x2-x1+1;
|
---|
1138 | // int h = y2-y1+1;
|
---|
1139 | //
|
---|
1140 | //#ifndef Q_OS_TEMP
|
---|
1141 | // if ( qWinVersion() & WV_DOS_based ) {
|
---|
1142 | // // Limited cursor size
|
---|
1143 | // int reqw = GetSystemMetrics(SM_CXCURSOR);
|
---|
1144 | // int reqh = GetSystemMetrics(SM_CYCURSOR);
|
---|
1145 | // if ( reqw < w ) {
|
---|
1146 | // // Not wide enough - move objectpm right
|
---|
1147 | // pm_hot.setX(pm_hot.x()-w+reqw);
|
---|
1148 | // }
|
---|
1149 | // if ( reqh < h ) {
|
---|
1150 | // // Not tall enough - move objectpm down
|
---|
1151 | // pm_hot.setY(pm_hot.y()-h+reqh);
|
---|
1152 | // }
|
---|
1153 | // // Always use system cursor size
|
---|
1154 | // w = reqw;
|
---|
1155 | // h = reqh;
|
---|
1156 | // }
|
---|
1157 | //#endif
|
---|
1158 | //
|
---|
1159 | // QPixmap colorbits(w,h,-1,QPixmap::NormalOptim);
|
---|
1160 | // {
|
---|
1161 | // QPainter p(&colorbits);
|
---|
1162 | // p.fillRect(0,0,w,h,color1);
|
---|
1163 | // p.drawPixmap(QMAX(0,-pm_hot.x()),QMAX(0,-pm_hot.y()),pm);
|
---|
1164 | // p.drawPixmap(QMAX(0,pm_hot.x()),QMAX(0,pm_hot.y()),cpm);
|
---|
1165 | // }
|
---|
1166 | //
|
---|
1167 | // QBitmap maskbits(w,h,TRUE,QPixmap::NormalOptim);
|
---|
1168 | // {
|
---|
1169 | // QPainter p(&maskbits);
|
---|
1170 | // if ( pm.mask() ) {
|
---|
1171 | // QBitmap m(*pm.mask());
|
---|
1172 | // m.setMask(m);
|
---|
1173 | // p.drawPixmap(QMAX(0,-pm_hot.x()),QMAX(0,-pm_hot.y()),m);
|
---|
1174 | // } else {
|
---|
1175 | // p.fillRect(QMAX(0,-pm_hot.x()),QMAX(0,-pm_hot.y()),
|
---|
1176 | // pm.width(),pm.height(),color1);
|
---|
1177 | // }
|
---|
1178 | // if ( cpm.mask() ) {
|
---|
1179 | // QBitmap m(*cpm.mask());
|
---|
1180 | // m.setMask(m);
|
---|
1181 | // p.drawPixmap(QMAX(0,pm_hot.x()),QMAX(0,pm_hot.y()),m);
|
---|
1182 | // } else {
|
---|
1183 | // p.fillRect(QMAX(0,pm_hot.x()),QMAX(0,pm_hot.y()),
|
---|
1184 | // cpm.width(),cpm.height(),
|
---|
1185 | // color1);
|
---|
1186 | // }
|
---|
1187 | // }
|
---|
1188 | //
|
---|
1189 | // HBITMAP im = qt_createIconMask(maskbits);
|
---|
1190 | // ICONINFO ii;
|
---|
1191 | // ii.fIcon = FALSE;
|
---|
1192 | // ii.xHotspot = QMAX(0,pm_hot.x());
|
---|
1193 | // ii.yHotspot = QMAX(0,pm_hot.y());
|
---|
1194 | // ii.hbmMask = im;
|
---|
1195 | // ii.hbmColor = colorbits.hbm();
|
---|
1196 | // cursor[cnum] = CreateIconIndirect(&ii);
|
---|
1197 | // DeleteObject( im );
|
---|
1198 | // }
|
---|
1199 | // }
|
---|
1200 | // }
|
---|
1201 | }
|
---|
1202 |
|
---|
1203 | #endif // QT_NO_DRAGANDDROP
|
---|