author | Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com> |
Fri, 14 May 2010 16:40:13 +0300 | |
changeset 22 | 79de32ba3296 |
parent 18 | 2f34d5167611 |
permissions | -rw-r--r-- |
0 | 1 |
/**************************************************************************** |
2 |
** |
|
18
2f34d5167611
Revision: 201011
Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
parents:
0
diff
changeset
|
3 |
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). |
0 | 4 |
** All rights reserved. |
5 |
** Contact: Nokia Corporation (qt-info@nokia.com) |
|
6 |
** |
|
7 |
** This file is part of the QtGui module of the Qt Toolkit. |
|
8 |
** |
|
9 |
** $QT_BEGIN_LICENSE:LGPL$ |
|
10 |
** No Commercial Usage |
|
11 |
** This file contains pre-release code and may not be distributed. |
|
12 |
** You may use this file in accordance with the terms and conditions |
|
13 |
** contained in the Technology Preview License Agreement accompanying |
|
14 |
** this package. |
|
15 |
** |
|
16 |
** GNU Lesser General Public License Usage |
|
17 |
** Alternatively, this file may be used under the terms of the GNU Lesser |
|
18 |
** General Public License version 2.1 as published by the Free Software |
|
19 |
** Foundation and appearing in the file LICENSE.LGPL included in the |
|
20 |
** packaging of this file. Please review the following information to |
|
21 |
** ensure the GNU Lesser General Public License version 2.1 requirements |
|
22 |
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
|
23 |
** |
|
24 |
** In addition, as a special exception, Nokia gives you certain additional |
|
25 |
** rights. These rights are described in the Nokia Qt LGPL Exception |
|
26 |
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
|
27 |
** |
|
28 |
** If you have questions regarding the use of this file, please contact |
|
29 |
** Nokia at qt-info@nokia.com. |
|
30 |
** |
|
31 |
** |
|
32 |
** |
|
33 |
** |
|
34 |
** |
|
35 |
** |
|
36 |
** |
|
37 |
** |
|
38 |
** $QT_END_LICENSE$ |
|
39 |
** |
|
40 |
****************************************************************************/ |
|
41 |
||
42 |
/* The following copyright notice pertains to the code as contributed |
|
43 |
to Qt, not to Nokia's modifications. It is replicated |
|
44 |
in doc/dnd.doc, where the documentation system can see it. */ |
|
45 |
||
46 |
/* Copyright 1996 Daniel Dardailler. |
|
47 |
||
48 |
Permission to use, copy, modify, distribute, and sell this software |
|
49 |
for any purpose is hereby granted without fee, provided that the above |
|
50 |
copyright notice appear in all copies and that both that copyright |
|
51 |
notice and this permission notice appear in supporting documentation, |
|
52 |
and that the name of Daniel Dardailler not be used in advertising or |
|
53 |
publicity pertaining to distribution of the software without specific, |
|
54 |
written prior permission. Daniel Dardailler makes no representations |
|
55 |
about the suitability of this software for any purpose. It is |
|
56 |
provided "as is" without express or implied warranty. |
|
57 |
||
58 |
Modifications Copyright 1999 Matt Koss, under the same license as |
|
59 |
above. |
|
60 |
************************************************************/ |
|
61 |
||
62 |
/***********************************************************/ |
|
63 |
/* Motif Drag&Drop Dynamic Protocol messaging API code */ |
|
64 |
/* Only requires Xlib layer - not MT safe */ |
|
65 |
/* Author: Daniel Dardailler, daniel@x.org */ |
|
66 |
/* Adapted by: Matt Koss, koss@napri.sk */ |
|
67 |
/* Further adaptions by: Nokia Corporation and/or its subsidiary(-ies) */ |
|
68 |
/***********************************************************/ |
|
69 |
||
70 |
#include "qplatformdefs.h" |
|
71 |
||
72 |
#include "qapplication.h" |
|
73 |
||
74 |
#ifndef QT_NO_DRAGANDDROP |
|
75 |
||
76 |
#include "qdebug.h" |
|
77 |
#include "qtextcodec.h" |
|
78 |
#include "qwidget.h" |
|
79 |
#include "qevent.h" |
|
80 |
#include "qt_x11_p.h" |
|
81 |
#include "qx11info_x11.h" |
|
82 |
#include "qiodevice.h" |
|
83 |
#include "qdnd_p.h" |
|
84 |
||
85 |
#include <stdlib.h> |
|
86 |
||
87 |
QT_BEGIN_NAMESPACE |
|
88 |
||
89 |
static Window sourceWindow = XNone; |
|
90 |
static QWidget *dropWidget = 0; |
|
91 |
static Qt::DropAction lastAcceptedAction = Qt::IgnoreAction; |
|
92 |
||
93 |
static Atom Dnd_selection = 0; |
|
94 |
static Time Dnd_selection_time; |
|
95 |
||
96 |
static Atom * src_targets ; |
|
97 |
static ushort num_src_targets ; |
|
98 |
||
99 |
// Motif definitions |
|
100 |
#define DndVersion 1 |
|
101 |
#define DndRevision 0 |
|
102 |
#define DndIncludeVersion (DndVersion * 10 + DndRevision) |
|
103 |
||
104 |
/* The following values are used in the DndData structure */ |
|
105 |
||
106 |
/* protocol style */ |
|
107 |
#define DND_DRAG_NONE 0 |
|
108 |
#define DND_DRAG_DROP_ONLY 1 |
|
109 |
#define DND_DRAG_DYNAMIC 5 |
|
110 |
||
111 |
/* message type */ |
|
112 |
#define DND_TOP_LEVEL_ENTER 0 |
|
113 |
#define DND_TOP_LEVEL_LEAVE 1 |
|
114 |
#define DND_DRAG_MOTION 2 |
|
115 |
#define DND_DROP_SITE_ENTER 3 |
|
116 |
#define DND_DROP_SITE_LEAVE 4 |
|
117 |
#define DND_DROP_START 5 |
|
118 |
#define DND_OPERATION_CHANGED 8 |
|
119 |
||
120 |
/* operation(s) */ |
|
121 |
#define DND_NOOP 0L |
|
122 |
#define DND_MOVE (1L << 0) |
|
123 |
#define DND_COPY (1L << 1) |
|
124 |
#define DND_LINK (1L << 2) |
|
125 |
||
126 |
static Qt::DropActions DndOperationsToQtDropActions(uchar op) |
|
127 |
{ |
|
128 |
Qt::DropActions actions = Qt::IgnoreAction; |
|
129 |
if (op | DND_MOVE) |
|
130 |
actions |= Qt::MoveAction; |
|
131 |
if (op | DND_COPY) |
|
132 |
actions |= Qt::CopyAction; |
|
133 |
if (op | DND_LINK) |
|
134 |
actions |= Qt::LinkAction; |
|
135 |
return actions; |
|
136 |
} |
|
137 |
||
138 |
static uchar QtDropActionToDndOperation(Qt::DropAction action) |
|
139 |
{ |
|
140 |
switch (action & Qt::ActionMask) { |
|
141 |
case Qt::CopyAction: |
|
142 |
default: |
|
143 |
return DND_COPY; |
|
144 |
case Qt::MoveAction: |
|
145 |
return DND_MOVE; |
|
146 |
case Qt::LinkAction: |
|
147 |
return DND_LINK; |
|
148 |
} |
|
149 |
} |
|
150 |
||
151 |
||
152 |
/* status */ |
|
153 |
#define DND_NO_DROP_SITE 1 |
|
154 |
#define DND_INVALID_DROP_SITE 2 |
|
155 |
#define DND_VALID_DROP_SITE 3 |
|
156 |
||
157 |
/* completion */ |
|
158 |
#define DND_DROP 0 |
|
159 |
#define DND_DROP_HELP 1 |
|
160 |
#define DND_DROP_CANCEL 2 |
|
161 |
||
162 |
#define BYTE unsigned char |
|
163 |
#define CARD32 unsigned int |
|
164 |
#define CARD16 unsigned short |
|
165 |
#define INT16 signed short |
|
166 |
||
167 |
/* Client side structure used in the API */ |
|
168 |
typedef struct { |
|
169 |
unsigned char reason; /* message type: DND_TOP_LEVEL_ENTER, etc */ |
|
170 |
Time time ; |
|
171 |
unsigned char operation; |
|
172 |
unsigned char operations; |
|
173 |
unsigned char status; |
|
174 |
unsigned char completion; |
|
175 |
short x ; |
|
176 |
short y ; |
|
177 |
Window src_window ; |
|
178 |
Atom property ; |
|
179 |
} DndData ; |
|
180 |
||
181 |
||
182 |
typedef struct _DndSrcProp { |
|
183 |
BYTE byte_order ; |
|
184 |
BYTE protocol_version ; |
|
185 |
CARD16 target_index ; |
|
186 |
CARD32 selection ; |
|
187 |
} DndSrcProp ; |
|
188 |
||
189 |
typedef struct _DndReceiverProp { |
|
190 |
BYTE byte_order ; |
|
191 |
BYTE protocol_version ; |
|
192 |
BYTE protocol_style ; |
|
193 |
BYTE pad1; |
|
194 |
CARD32 proxy_window; |
|
195 |
CARD16 num_drop_sites ; |
|
196 |
CARD16 pad2; |
|
197 |
CARD32 total_size; |
|
198 |
} DndReceiverProp ; |
|
199 |
||
200 |
/* need to use some union hack since window and property are in |
|
201 |
different order depending on the message ... */ |
|
202 |
typedef struct _DndTop { |
|
203 |
CARD32 src_window; |
|
204 |
CARD32 property; |
|
205 |
} DndTop ; |
|
206 |
||
207 |
typedef struct _DndPot { |
|
208 |
INT16 x; |
|
209 |
INT16 y; |
|
210 |
CARD32 property; |
|
211 |
CARD32 src_window; |
|
212 |
} DndPot ; |
|
213 |
||
214 |
typedef struct _DndMessage { |
|
215 |
BYTE reason; |
|
216 |
BYTE byte_order; |
|
217 |
CARD16 flags; |
|
218 |
CARD32 time; |
|
219 |
union { |
|
220 |
DndTop top ; |
|
221 |
DndPot pot ; |
|
222 |
} data ; |
|
223 |
} DndMessage ; |
|
224 |
||
225 |
typedef struct { |
|
226 |
BYTE byte_order; |
|
227 |
BYTE protocol_version; |
|
228 |
CARD16 num_target_lists; |
|
229 |
CARD32 data_size; |
|
230 |
/* then come series of CARD16,CARD32,CARD32,CARD32... */ |
|
231 |
} DndTargets; |
|
232 |
||
233 |
||
234 |
/* protocol version */ |
|
235 |
#define DND_PROTOCOL_VERSION 0 |
|
236 |
||
237 |
||
238 |
#define DND_EVENT_TYPE_MASK ((BYTE)0x80) |
|
239 |
#define DND_EVENT_TYPE_SHIFT 7 |
|
240 |
#define DND_CLEAR_EVENT_TYPE ((BYTE)0x7F) |
|
241 |
||
242 |
/* message_type is data[0] of the client_message |
|
243 |
this return 1 (receiver bit up) or 0 (initiator) */ |
|
244 |
#define DND_GET_EVENT_TYPE(message_type) \ |
|
245 |
((char) (((message_type) & DND_EVENT_TYPE_MASK) >> DND_EVENT_TYPE_SHIFT)) |
|
246 |
||
247 |
/* event_type can be 0 (initiator) or 1 (receiver) */ |
|
248 |
#define DND_SET_EVENT_TYPE(event_type) \ |
|
249 |
(((BYTE)(event_type) << DND_EVENT_TYPE_SHIFT) & DND_EVENT_TYPE_MASK) |
|
250 |
||
251 |
||
252 |
#define DND_OPERATION_MASK ((CARD16) 0x000F) |
|
253 |
#define DND_OPERATION_SHIFT 0 |
|
254 |
#define DND_STATUS_MASK ((CARD16) 0x00F0) |
|
255 |
#define DND_STATUS_SHIFT 4 |
|
256 |
#define DND_OPERATIONS_MASK ((CARD16) 0x0F00) |
|
257 |
#define DND_OPERATIONS_SHIFT 8 |
|
258 |
#define DND_COMPLETION_MASK ((CARD16) 0xF000) |
|
259 |
#define DND_COMPLETION_SHIFT 12 |
|
260 |
||
261 |
#define DND_GET_OPERATION(flags) \ |
|
262 |
((unsigned char) \ |
|
263 |
(((flags) & DND_OPERATION_MASK) >> DND_OPERATION_SHIFT)) |
|
264 |
||
265 |
#define DND_SET_OPERATION(operation) \ |
|
266 |
(((CARD16)(operation) << DND_OPERATION_SHIFT)\ |
|
267 |
& DND_OPERATION_MASK) |
|
268 |
||
269 |
#define DND_GET_STATUS(flags) \ |
|
270 |
((unsigned char) \ |
|
271 |
(((flags) & DND_STATUS_MASK) >> DND_STATUS_SHIFT)) |
|
272 |
||
273 |
#define DND_SET_STATUS(status) \ |
|
274 |
(((CARD16)(status) << DND_STATUS_SHIFT)\ |
|
275 |
& DND_STATUS_MASK) |
|
276 |
||
277 |
#define DND_GET_OPERATIONS(flags) \ |
|
278 |
((unsigned char) \ |
|
279 |
(((flags) & DND_OPERATIONS_MASK) >> DND_OPERATIONS_SHIFT)) |
|
280 |
||
281 |
#define DND_SET_OPERATIONS(operation) \ |
|
282 |
(((CARD16)(operation) << DND_OPERATIONS_SHIFT)\ |
|
283 |
& DND_OPERATIONS_MASK) |
|
284 |
||
285 |
#define DND_GET_COMPLETION(flags) \ |
|
286 |
((unsigned char) \ |
|
287 |
(((flags) & DND_COMPLETION_MASK) >> DND_COMPLETION_SHIFT)) |
|
288 |
||
289 |
#define DND_SET_COMPLETION(completion) \ |
|
290 |
(((CARD16)(completion) << DND_COMPLETION_SHIFT)\ |
|
291 |
& DND_COMPLETION_MASK) |
|
292 |
||
293 |
||
294 |
#define SWAP4BYTES(l) {\ |
|
295 |
struct { unsigned t :32;} bit32;\ |
|
296 |
char n, *tp = (char *) &bit32;\ |
|
297 |
bit32.t = l;\ |
|
298 |
n = tp[0]; tp[0] = tp[3]; tp[3] = n;\ |
|
299 |
n = tp[1]; tp[1] = tp[2]; tp[2] = n;\ |
|
300 |
l = bit32.t;\ |
|
301 |
} |
|
302 |
||
303 |
#define SWAP2BYTES(s) {\ |
|
304 |
struct { unsigned t :16; } bit16;\ |
|
305 |
char n, *tp = (char *) &bit16;\ |
|
306 |
bit16.t = s;\ |
|
307 |
n = tp[0]; tp[0] = tp[1]; tp[1] = n;\ |
|
308 |
s = bit16.t;\ |
|
309 |
} |
|
310 |
||
311 |
||
312 |
/** Private extern functions */ |
|
313 |
||
314 |
static unsigned char DndByteOrder (); |
|
315 |
||
316 |
||
317 |
/***** Targets/Index stuff */ |
|
318 |
||
319 |
typedef struct { |
|
320 |
int num_targets; |
|
321 |
Atom *targets; |
|
322 |
} DndTargetsTableEntryRec, * DndTargetsTableEntry; |
|
323 |
||
324 |
typedef struct { |
|
325 |
int num_entries; |
|
326 |
DndTargetsTableEntry entries; |
|
327 |
} DndTargetsTableRec, * DndTargetsTable; |
|
328 |
||
329 |
||
330 |
static ushort _DndIndexToTargets(Display * display, |
|
331 |
int index, |
|
332 |
Atom ** targets); |
|
333 |
||
334 |
extern void qt_x11_intern_atom(const char *, Atom *); |
|
335 |
||
336 |
///////////////////////////////////////////////////////////////// |
|
337 |
||
338 |
static unsigned char DndByteOrder () |
|
339 |
{ |
|
340 |
static unsigned char byte_order = 0; |
|
341 |
||
342 |
if (!byte_order) { |
|
343 |
unsigned int endian = 1; |
|
344 |
byte_order = (*((char *)&endian))?'l':'B'; |
|
345 |
} |
|
346 |
return byte_order ; |
|
347 |
} |
|
348 |
||
349 |
||
350 |
||
351 |
static void DndReadSourceProperty(Display * dpy, |
|
352 |
Window window, Atom dnd_selection, |
|
353 |
Atom ** targets, unsigned short * num_targets) |
|
354 |
{ |
|
355 |
unsigned char *retval = 0; |
|
356 |
Atom type ; |
|
357 |
int format ; |
|
358 |
unsigned long bytesafter, lengthRtn; |
|
359 |
||
360 |
if ((XGetWindowProperty (dpy, window, dnd_selection, 0L, 100000L, |
|
361 |
False, ATOM(_MOTIF_DRAG_INITIATOR_INFO), &type, |
|
362 |
&format, &lengthRtn, &bytesafter, |
|
363 |
&retval) != Success) |
|
364 |
|| (type == XNone)) { |
|
365 |
*num_targets = 0; |
|
366 |
return ; |
|
367 |
} |
|
368 |
||
369 |
DndSrcProp * src_prop = (DndSrcProp *)retval; |
|
370 |
||
371 |
if (src_prop->byte_order != DndByteOrder()) { |
|
372 |
SWAP2BYTES(src_prop->target_index); |
|
373 |
SWAP4BYTES(src_prop->selection); |
|
374 |
} |
|
375 |
||
376 |
*num_targets = _DndIndexToTargets(dpy, src_prop->target_index, targets); |
|
377 |
||
378 |
XFree((char*)src_prop); |
|
379 |
} |
|
380 |
||
381 |
||
382 |
/* Position the _MOTIF_DRAG_RECEIVER_INFO property on the dropsite window. |
|
383 |
Called by the receiver of the drop to indicate the |
|
384 |
supported protocol style : dynamic, drop_only or none */ |
|
385 |
static void DndWriteReceiverProperty(Display * dpy, Window window, |
|
386 |
unsigned char protocol_style) |
|
387 |
{ |
|
388 |
DndReceiverProp receiver_prop ; |
|
389 |
||
390 |
receiver_prop.byte_order = DndByteOrder() ; |
|
391 |
receiver_prop.protocol_version = DND_PROTOCOL_VERSION; |
|
392 |
receiver_prop.protocol_style = protocol_style ; |
|
393 |
receiver_prop.proxy_window = XNone ; |
|
394 |
receiver_prop.num_drop_sites = 0 ; |
|
395 |
receiver_prop.total_size = sizeof(DndReceiverProp); |
|
396 |
||
397 |
/* write the buffer to the property */ |
|
398 |
XChangeProperty (dpy, window, ATOM(_MOTIF_DRAG_RECEIVER_INFO), ATOM(_MOTIF_DRAG_RECEIVER_INFO), |
|
399 |
8, PropModeReplace, |
|
400 |
(unsigned char *)&receiver_prop, |
|
401 |
sizeof(DndReceiverProp)); |
|
402 |
} |
|
403 |
||
404 |
||
405 |
/* protocol style equiv (preregister stuff really) */ |
|
406 |
#define DND_DRAG_DROP_ONLY_EQUIV 3 |
|
407 |
#define DND_DRAG_DYNAMIC_EQUIV1 2 |
|
408 |
#define DND_DRAG_DYNAMIC_EQUIV2 4 |
|
409 |
||
410 |
||
411 |
/* Produce a client message to be sent by the caller */ |
|
412 |
static void DndFillClientMessage(Display * dpy, Window window, |
|
413 |
XClientMessageEvent *cm, |
|
414 |
DndData * dnd_data, |
|
415 |
char receiver) |
|
416 |
{ |
|
417 |
DndMessage * dnd_message = (DndMessage*)&cm->data.b[0] ; |
|
418 |
||
419 |
cm->display = dpy; |
|
420 |
cm->type = ClientMessage; |
|
421 |
cm->serial = LastKnownRequestProcessed(dpy); |
|
422 |
cm->send_event = True; |
|
423 |
cm->window = window; |
|
424 |
cm->format = 8; |
|
425 |
cm->message_type = ATOM(_MOTIF_DRAG_AND_DROP_MESSAGE); |
|
426 |
||
427 |
dnd_message->reason = dnd_data->reason | DND_SET_EVENT_TYPE(receiver); |
|
428 |
||
429 |
dnd_message->byte_order = DndByteOrder(); |
|
430 |
||
431 |
/* we're filling in flags with more stuff that necessary, |
|
432 |
depending on the reason, but it doesn't matter */ |
|
433 |
dnd_message->flags = 0 ; |
|
434 |
dnd_message->flags |= DND_SET_STATUS(dnd_data->status) ; |
|
435 |
dnd_message->flags |= DND_SET_OPERATION(dnd_data->operation) ; |
|
436 |
dnd_message->flags |= DND_SET_OPERATIONS(dnd_data->operations) ; |
|
437 |
dnd_message->flags |= DND_SET_COMPLETION(dnd_data->completion) ; |
|
438 |
||
439 |
dnd_message->time = dnd_data->time ; |
|
440 |
||
441 |
switch(dnd_data->reason) { |
|
442 |
case DND_DROP_SITE_LEAVE: break ; |
|
443 |
case DND_TOP_LEVEL_ENTER: |
|
444 |
case DND_TOP_LEVEL_LEAVE: |
|
445 |
dnd_message->data.top.src_window = dnd_data->src_window ; |
|
446 |
dnd_message->data.top.property = dnd_data->property ; |
|
447 |
break ; /* cannot fall through since the byte layout is different in |
|
448 |
both set of messages, see top and pot union stuff */ |
|
449 |
||
450 |
case DND_DRAG_MOTION: |
|
451 |
case DND_OPERATION_CHANGED: |
|
452 |
case DND_DROP_SITE_ENTER: |
|
453 |
case DND_DROP_START: |
|
454 |
dnd_message->data.pot.x = dnd_data->x ; /* mouse position */ |
|
455 |
dnd_message->data.pot.y = dnd_data->y ; |
|
456 |
dnd_message->data.pot.src_window = dnd_data->src_window ; |
|
457 |
dnd_message->data.pot.property = dnd_data->property ; |
|
458 |
break ; |
|
459 |
default: |
|
460 |
break ; |
|
461 |
} |
|
462 |
||
463 |
} |
|
464 |
||
465 |
static Bool DndParseClientMessage(XClientMessageEvent *cm, DndData * dnd_data, |
|
466 |
char * receiver) |
|
467 |
{ |
|
468 |
DndMessage * dnd_message = (DndMessage*)&cm->data.b[0] ; |
|
469 |
||
470 |
if (cm->message_type != ATOM(_MOTIF_DRAG_AND_DROP_MESSAGE)) { |
|
471 |
return False ; |
|
472 |
} |
|
473 |
||
474 |
if (dnd_message->byte_order != DndByteOrder()) { |
|
475 |
SWAP2BYTES(dnd_message->flags); |
|
476 |
SWAP4BYTES(dnd_message->time); |
|
477 |
} /* do the rest in the switch */ |
|
478 |
||
479 |
dnd_data->reason = dnd_message->reason ; |
|
480 |
if (DND_GET_EVENT_TYPE(dnd_data->reason)) |
|
481 |
*receiver = 1 ; |
|
482 |
else |
|
483 |
*receiver = 0 ; |
|
484 |
dnd_data->reason &= DND_CLEAR_EVENT_TYPE ; |
|
485 |
||
486 |
dnd_data->time = dnd_message->time ; |
|
487 |
||
488 |
/* we're reading in more stuff that necessary. but who cares */ |
|
489 |
dnd_data->status = DND_GET_STATUS(dnd_message->flags) ; |
|
490 |
dnd_data->operation = DND_GET_OPERATION(dnd_message->flags) ; |
|
491 |
dnd_data->operations = DND_GET_OPERATIONS(dnd_message->flags) ; |
|
492 |
dnd_data->completion = DND_GET_COMPLETION(dnd_message->flags) ; |
|
493 |
||
494 |
switch(dnd_data->reason) { |
|
495 |
case DND_TOP_LEVEL_ENTER: |
|
496 |
case DND_TOP_LEVEL_LEAVE: |
|
497 |
if (dnd_message->byte_order != DndByteOrder()) { |
|
498 |
SWAP4BYTES(dnd_message->data.top.src_window); |
|
499 |
SWAP4BYTES(dnd_message->data.top.property); |
|
500 |
} |
|
501 |
dnd_data->src_window = dnd_message->data.top.src_window ; |
|
502 |
dnd_data->property = dnd_message->data.top.property ; |
|
503 |
break ; /* cannot fall through, see above comment in write msg */ |
|
504 |
||
505 |
case DND_DRAG_MOTION: |
|
506 |
case DND_OPERATION_CHANGED: |
|
507 |
case DND_DROP_SITE_ENTER: |
|
508 |
case DND_DROP_START: |
|
509 |
if (dnd_message->byte_order != DndByteOrder()) { |
|
510 |
SWAP2BYTES(dnd_message->data.pot.x); |
|
511 |
SWAP2BYTES(dnd_message->data.pot.y); |
|
512 |
SWAP4BYTES(dnd_message->data.pot.property); |
|
513 |
SWAP4BYTES(dnd_message->data.pot.src_window); |
|
514 |
} |
|
515 |
dnd_data->x = dnd_message->data.pot.x ; |
|
516 |
dnd_data->y = dnd_message->data.pot.y ; |
|
517 |
dnd_data->property = dnd_message->data.pot.property ; |
|
518 |
dnd_data->src_window = dnd_message->data.pot.src_window ; |
|
519 |
break ; |
|
520 |
||
521 |
case DND_DROP_SITE_LEAVE: |
|
522 |
break; |
|
523 |
default: |
|
524 |
break ; |
|
525 |
} |
|
526 |
||
527 |
return True ; |
|
528 |
} |
|
529 |
||
530 |
||
531 |
static Window MotifWindow(Display *display) |
|
532 |
{ |
|
533 |
Atom type; |
|
534 |
int format; |
|
535 |
unsigned long size; |
|
536 |
unsigned long bytes_after; |
|
537 |
unsigned char *property = 0; |
|
538 |
Window motif_window ; |
|
539 |
||
540 |
/* this version does no caching, so it's slow: round trip each time */ |
|
541 |
||
542 |
if ((XGetWindowProperty (display, RootWindow(display, 0), |
|
543 |
ATOM(_MOTIF_DRAG_WINDOW), |
|
544 |
0L, 100000L, False, AnyPropertyType, |
|
545 |
&type, &format, &size, &bytes_after, |
|
546 |
&property) == Success) && |
|
547 |
(type != XNone)) { |
|
548 |
motif_window = *(Window *)property; |
|
549 |
} else { |
|
550 |
XSetWindowAttributes sAttributes; |
|
551 |
||
552 |
/* really, this should be done on a separate connection, |
|
553 |
with XSetCloseDownMode (RetainPermanent), so that |
|
554 |
others don't have to recreate it; hopefully, some real |
|
555 |
Motif application will be around to do it */ |
|
556 |
||
557 |
sAttributes.override_redirect = True; |
|
558 |
sAttributes.event_mask = PropertyChangeMask; |
|
559 |
motif_window = XCreateWindow (display, |
|
560 |
RootWindow (display, 0), |
|
561 |
-170, -560, 1, 1, 0, 0, |
|
562 |
InputOnly, CopyFromParent, |
|
563 |
(CWOverrideRedirect |CWEventMask), |
|
564 |
&sAttributes); |
|
565 |
XMapWindow (display, motif_window); |
|
566 |
} |
|
567 |
||
568 |
if (property) { |
|
569 |
XFree ((char *)property); |
|
570 |
} |
|
571 |
||
572 |
return (motif_window); |
|
573 |
} |
|
574 |
||
575 |
||
576 |
static DndTargetsTable TargetsTable(Display *display) |
|
577 |
{ |
|
578 |
Atom type; |
|
579 |
int format; |
|
580 |
unsigned long size; |
|
581 |
unsigned long bytes_after; |
|
582 |
Window motif_window = MotifWindow(display) ; |
|
583 |
unsigned char *retval; |
|
584 |
DndTargetsTable targets_table ; |
|
585 |
int i,j ; |
|
586 |
char * target_data ; |
|
587 |
||
588 |
/* this version does no caching, so it's slow: round trip each time */ |
|
589 |
/* ideally, register for property notify on this target_list |
|
590 |
atom and update when necessary only */ |
|
591 |
||
592 |
if ((XGetWindowProperty (display, motif_window, |
|
593 |
ATOM(_MOTIF_DRAG_TARGETS), 0L, 100000L, |
|
594 |
False, ATOM(_MOTIF_DRAG_TARGETS), |
|
595 |
&type, &format, &size, &bytes_after, |
|
596 |
&retval) != Success) || |
|
597 |
type == XNone) { |
|
598 |
qWarning("QMotifDND: Cannot get property on Motif window"); |
|
599 |
return 0; |
|
600 |
} |
|
601 |
||
602 |
DndTargets * target_prop = (DndTargets *)retval; |
|
603 |
||
604 |
if (target_prop->protocol_version != DND_PROTOCOL_VERSION) { |
|
605 |
qWarning("QMotifDND: Protocol mismatch"); |
|
606 |
} |
|
607 |
||
608 |
if (target_prop->byte_order != DndByteOrder()) { |
|
609 |
/* need to swap num_target_lists and size */ |
|
610 |
SWAP2BYTES(target_prop->num_target_lists); |
|
611 |
SWAP4BYTES(target_prop->data_size); |
|
612 |
} |
|
613 |
||
614 |
/* now parse DndTarget prop data in a TargetsTable */ |
|
615 |
||
616 |
targets_table = (DndTargetsTable)malloc(sizeof(DndTargetsTableRec)); |
|
617 |
targets_table->num_entries = target_prop->num_target_lists ; |
|
618 |
targets_table->entries = (DndTargetsTableEntry) |
|
619 |
malloc(sizeof(DndTargetsTableEntryRec) * target_prop->num_target_lists); |
|
620 |
||
621 |
target_data = (char*)target_prop + sizeof(*target_prop) ; |
|
622 |
||
623 |
for (i = 0 ; i < targets_table->num_entries; i++) { |
|
624 |
CARD16 num_targets ; |
|
625 |
CARD32 atom ; |
|
626 |
||
627 |
memcpy(&num_targets, target_data, 2); |
|
628 |
target_data += 2; |
|
629 |
||
630 |
/* potential swap needed here */ |
|
631 |
if (target_prop->byte_order != DndByteOrder()) |
|
632 |
SWAP2BYTES(num_targets); |
|
633 |
||
634 |
targets_table->entries[i].num_targets = num_targets ; |
|
635 |
targets_table->entries[i].targets = (Atom *) |
|
636 |
malloc(sizeof(Atom) * targets_table->entries[i].num_targets); |
|
637 |
||
638 |
||
639 |
for (j = 0; j < num_targets; j++) { |
|
640 |
memcpy(&atom, target_data, 4); |
|
641 |
target_data += 4; |
|
642 |
||
643 |
/* another potential swap needed here */ |
|
644 |
if (target_prop->byte_order != DndByteOrder()) |
|
645 |
SWAP4BYTES(atom); |
|
646 |
||
647 |
targets_table->entries[i].targets[j] = (Atom) atom ; |
|
648 |
} |
|
649 |
} |
|
650 |
||
651 |
if (target_prop) { |
|
652 |
XFree((char *)target_prop); |
|
653 |
} |
|
654 |
||
655 |
return targets_table ; |
|
656 |
} |
|
657 |
||
658 |
||
659 |
static ushort _DndIndexToTargets(Display * display, |
|
660 |
int index, |
|
661 |
Atom ** targets) |
|
662 |
{ |
|
663 |
DndTargetsTable targets_table; |
|
664 |
int i ; |
|
665 |
||
666 |
/* again, slow: no caching here, alloc/free each time */ |
|
667 |
||
668 |
if (!(targets_table = TargetsTable (display)) || |
|
669 |
(index >= targets_table->num_entries)) { |
|
670 |
if (targets_table) |
|
671 |
XFree((char*)targets_table); |
|
672 |
return 0; |
|
673 |
} |
|
674 |
||
675 |
/* transfer the correct target list index */ |
|
676 |
*targets = (Atom*)malloc(sizeof(Atom)*targets_table-> |
|
677 |
entries[index].num_targets); |
|
678 |
memcpy((char*)*targets, |
|
679 |
(char*)targets_table->entries[index].targets, |
|
680 |
sizeof(Atom)*targets_table->entries[index].num_targets); |
|
681 |
||
682 |
/* free the target table and its guts */ |
|
683 |
for (i=0 ; i < targets_table->num_entries; i++) |
|
684 |
XFree((char*)targets_table->entries[i].targets); |
|
685 |
||
686 |
int tmp = targets_table->entries[index].num_targets; |
|
687 |
XFree((char*)targets_table); |
|
688 |
||
689 |
return tmp; // targets_table->entries[index].num_targets; |
|
690 |
} |
|
691 |
||
692 |
||
693 |
QByteArray QX11Data::motifdndFormat(int n) |
|
694 |
{ |
|
695 |
if (!motifdnd_active) |
|
696 |
return 0; // should not happen |
|
697 |
||
698 |
if (n >= num_src_targets) |
|
699 |
return 0; |
|
700 |
||
701 |
Atom target = src_targets[n]; |
|
702 |
||
703 |
if (target == XA_STRING) |
|
704 |
return "text/plain;charset=ISO-8859-1"; |
|
705 |
if (target == ATOM(UTF8_STRING)) |
|
706 |
return "text/plain;charset=UTF-8"; |
|
707 |
if (target == ATOM(COMPOUND_TEXT)) |
|
708 |
return QByteArray("text/plain;charset=") + QTextCodec::codecForLocale()->name(); |
|
709 |
if (target == ATOM(TEXT)) |
|
710 |
return "text/plain"; |
|
711 |
||
712 |
return ("x-motif-dnd/" + X11->xdndAtomToString(target)); |
|
713 |
} |
|
714 |
||
715 |
||
716 |
QVariant QX11Data::motifdndObtainData(const char *mimeType) |
|
717 |
{ |
|
718 |
QByteArray result; |
|
719 |
||
720 |
if (Dnd_selection == 0 || !dropWidget) |
|
721 |
return result; |
|
722 |
||
723 |
// try to convert the selection to the requested property |
|
724 |
// qDebug("trying to convert to '%s'", mimeType); |
|
725 |
||
726 |
int n=0; |
|
727 |
QByteArray f; |
|
728 |
do { |
|
729 |
f = motifdndFormat(n); |
|
730 |
if (f.isEmpty()) |
|
731 |
return result; |
|
732 |
n++; |
|
733 |
} while(qstricmp(mimeType, f.data())); |
|
734 |
||
735 |
Atom conversion_type = XNone; |
|
736 |
if (f == "text/plain;charset=ISO-8859-1") { |
|
737 |
conversion_type = XA_STRING; |
|
738 |
} else if (f == "text/plain;charset=UTF-8") { |
|
739 |
conversion_type = ATOM(UTF8_STRING); |
|
740 |
} else if (f == (QByteArray("text/plain;charset=") + QTextCodec::codecForLocale()->name())) { |
|
741 |
conversion_type = ATOM(COMPOUND_TEXT); |
|
742 |
} else if (f == "text/plain") { |
|
743 |
conversion_type = ATOM(TEXT); |
|
744 |
} else if (f.startsWith("x-motif-dnd/")) { |
|
745 |
// strip off the "x-motif-dnd/" prefix |
|
746 |
conversion_type = X11->xdndStringToAtom(f.remove(0, 12)); |
|
747 |
} |
|
748 |
||
749 |
if (XGetSelectionOwner(X11->display, Dnd_selection) == XNone) { |
|
750 |
return result; // should never happen? |
|
751 |
} |
|
752 |
||
753 |
QWidget* tw = dropWidget; |
|
754 |
if ((dropWidget->windowType() == Qt::Desktop)) { |
|
755 |
tw = new QWidget; |
|
756 |
} |
|
757 |
||
758 |
// convert selection to the appropriate type |
|
759 |
XConvertSelection (X11->display, Dnd_selection, conversion_type, |
|
760 |
Dnd_selection, tw->internalWinId(), Dnd_selection_time); |
|
761 |
||
762 |
XFlush(X11->display); |
|
763 |
||
764 |
XEvent xevent; |
|
765 |
bool got=X11->clipboardWaitForEvent(tw->internalWinId(), SelectionNotify, &xevent, 5000); |
|
766 |
if (got) { |
|
767 |
Atom type; |
|
768 |
||
769 |
if (X11->clipboardReadProperty(tw->internalWinId(), Dnd_selection, true, &result, 0, &type, 0, false)) { |
|
770 |
} |
|
771 |
} |
|
772 |
||
773 |
// we have to convert selection in order to indicate success to the initiator |
|
774 |
XConvertSelection (X11->display, Dnd_selection, ATOM(XmTRANSFER_SUCCESS), |
|
775 |
Dnd_selection, tw->internalWinId(), Dnd_selection_time); |
|
776 |
||
777 |
// wait again for SelectionNotify event |
|
778 |
X11->clipboardWaitForEvent(tw->internalWinId(), SelectionNotify, &xevent, 5000); |
|
779 |
||
780 |
if ((dropWidget->windowType() == Qt::Desktop)) { |
|
781 |
delete tw; |
|
782 |
} |
|
783 |
||
784 |
return result; |
|
785 |
} |
|
786 |
||
787 |
||
788 |
void QX11Data::motifdndEnable(QWidget *widget, bool) |
|
789 |
{ |
|
790 |
DndWriteReceiverProperty(display, widget->internalWinId(), DND_DRAG_DYNAMIC); |
|
791 |
} |
|
792 |
||
793 |
||
794 |
void QX11Data::motifdndHandle(QWidget *widget, const XEvent * xe, bool /* passive */) |
|
795 |
{ |
|
796 |
XEvent event = *xe; |
|
797 |
XClientMessageEvent cm ; |
|
798 |
DndData dnd_data ; |
|
799 |
char receiver ; |
|
800 |
||
801 |
if (!(DndParseClientMessage ((XClientMessageEvent*)&event, |
|
802 |
&dnd_data, &receiver))) { |
|
803 |
return; |
|
804 |
} |
|
805 |
||
806 |
switch (dnd_data.reason) { |
|
807 |
||
808 |
case DND_DRAG_MOTION: |
|
809 |
{ |
|
810 |
QPoint p = widget->mapFromGlobal(QPoint(dnd_data.x, dnd_data.y)); |
|
811 |
QWidget *c = widget->childAt(p); |
|
812 |
||
813 |
if (!c || !c->acceptDrops()) { |
|
814 |
// not over a drop site |
|
815 |
if (dropWidget) { |
|
816 |
QDragLeaveEvent dragLeaveEvent; |
|
817 |
QApplication::sendEvent(dropWidget, &dragLeaveEvent); |
|
818 |
||
819 |
dropWidget = 0; |
|
820 |
lastAcceptedAction = Qt::IgnoreAction; |
|
821 |
||
822 |
dnd_data.reason = DND_DROP_SITE_LEAVE; |
|
823 |
dnd_data.time = X11->time; |
|
824 |
DndFillClientMessage (event.xclient.display, sourceWindow, &cm, &dnd_data, receiver); |
|
825 |
XSendEvent(event.xbutton.display, sourceWindow, False, 0, (XEvent *)&cm) ; |
|
826 |
} else { |
|
827 |
dnd_data.reason = DND_DRAG_MOTION; |
|
828 |
dnd_data.status = DND_NO_DROP_SITE; |
|
829 |
dnd_data.time = X11->time; |
|
830 |
dnd_data.operation = DND_NOOP; |
|
831 |
dnd_data.operations = DND_NOOP; |
|
832 |
DndFillClientMessage (event.xclient.display, sourceWindow, &cm, &dnd_data, receiver); |
|
833 |
XSendEvent(event.xbutton.display, sourceWindow, False, 0, (XEvent *)&cm) ; |
|
834 |
} |
|
835 |
} else { |
|
836 |
Q_ASSERT(c != 0); |
|
837 |
p = c->mapFrom(widget, p); |
|
838 |
||
839 |
if (dropWidget != c) { |
|
840 |
if (dropWidget) { |
|
841 |
QDragLeaveEvent le; |
|
842 |
QApplication::sendEvent(dropWidget, &le); |
|
843 |
} |
|
844 |
||
845 |
dropWidget = c; |
|
846 |
lastAcceptedAction = Qt::IgnoreAction; |
|
847 |
||
848 |
const Qt::DropActions possibleActions = |
|
849 |
DndOperationsToQtDropActions(dnd_data.operations); |
|
850 |
QDragEnterEvent de(p, possibleActions, QDragManager::self()->dropData, |
|
851 |
QApplication::mouseButtons(), QApplication::keyboardModifiers()); |
|
852 |
QApplication::sendEvent(dropWidget, &de); |
|
853 |
||
854 |
dnd_data.reason = DND_DROP_SITE_ENTER; |
|
855 |
dnd_data.time = X11->time; |
|
856 |
if (de.isAccepted()) { |
|
857 |
lastAcceptedAction = de.dropAction(); |
|
858 |
||
859 |
dnd_data.status = DND_VALID_DROP_SITE; |
|
860 |
dnd_data.operation = QtDropActionToDndOperation(lastAcceptedAction); |
|
861 |
} else { |
|
862 |
dnd_data.status = DND_INVALID_DROP_SITE; |
|
863 |
dnd_data.operation = DND_NOOP; |
|
864 |
dnd_data.operations = DND_NOOP; |
|
865 |
} |
|
866 |
DndFillClientMessage (event.xclient.display, sourceWindow, &cm, &dnd_data, receiver); |
|
867 |
XSendEvent(event.xbutton.display, sourceWindow, False, 0, (XEvent *)&cm); |
|
868 |
} else { |
|
869 |
const Qt::DropActions possibleActions = |
|
870 |
DndOperationsToQtDropActions(dnd_data.operations); |
|
871 |
QDragMoveEvent me(p, possibleActions, QDragManager::self()->dropData, |
|
872 |
QApplication::mouseButtons(), QApplication::keyboardModifiers()); |
|
873 |
if (lastAcceptedAction != Qt::IgnoreAction) { |
|
874 |
me.setDropAction(lastAcceptedAction); |
|
875 |
me.accept(); |
|
876 |
} |
|
877 |
QApplication::sendEvent(dropWidget, &me); |
|
878 |
||
879 |
dnd_data.reason = DND_DRAG_MOTION; |
|
880 |
dnd_data.time = X11->time; |
|
881 |
||
882 |
if (me.isAccepted()) { |
|
883 |
lastAcceptedAction = me.dropAction(); |
|
884 |
||
885 |
dnd_data.status = DND_VALID_DROP_SITE; |
|
886 |
dnd_data.operation = QtDropActionToDndOperation(lastAcceptedAction); |
|
887 |
} else { |
|
888 |
dnd_data.status = DND_INVALID_DROP_SITE; |
|
889 |
dnd_data.operation = DND_NOOP; |
|
890 |
dnd_data.operations = DND_NOOP; |
|
891 |
} |
|
892 |
||
893 |
DndFillClientMessage (event.xclient.display, sourceWindow, &cm, &dnd_data, receiver); |
|
894 |
XSendEvent(event.xbutton.display, sourceWindow, False, 0, (XEvent *)&cm); |
|
895 |
} |
|
896 |
} |
|
897 |
||
898 |
break; |
|
899 |
} |
|
900 |
||
901 |
case DND_TOP_LEVEL_ENTER: |
|
902 |
{ |
|
903 |
/* get the size of our drop site for later use */ |
|
904 |
||
905 |
motifdnd_active = true; |
|
906 |
sourceWindow = dnd_data.src_window; |
|
907 |
||
908 |
/* no answer needed, just read source property */ |
|
909 |
DndReadSourceProperty (event.xclient.display, |
|
910 |
sourceWindow, |
|
911 |
dnd_data.property, |
|
912 |
&src_targets, &num_src_targets); |
|
913 |
||
914 |
break; |
|
915 |
} |
|
916 |
||
917 |
case DND_TOP_LEVEL_LEAVE: |
|
918 |
{ |
|
919 |
XEvent nextEvent; |
|
920 |
if (XCheckTypedWindowEvent(X11->display, widget->winId(), ClientMessage, &nextEvent)) { |
|
921 |
// we just want to check, not eat (should use XPeekIfEvent) |
|
922 |
XPutBackEvent(X11->display, &nextEvent); |
|
923 |
||
924 |
if (DndParseClientMessage (&nextEvent.xclient, &dnd_data, &receiver) |
|
925 |
&& dnd_data.reason == DND_DROP_START) { |
|
926 |
// expecting drop next, keeping DnD alive |
|
927 |
break; |
|
928 |
} |
|
929 |
} |
|
930 |
||
931 |
// not expecting drop, need to send drag leave events and such here |
|
932 |
if (dropWidget) { |
|
933 |
QDragLeaveEvent le; |
|
934 |
QApplication::sendEvent(dropWidget, &le); |
|
935 |
} |
|
936 |
||
937 |
sourceWindow = XNone; |
|
938 |
dropWidget = 0; |
|
939 |
lastAcceptedAction = Qt::IgnoreAction; |
|
940 |
||
941 |
motifdnd_active = false; |
|
942 |
||
943 |
break; |
|
944 |
} |
|
945 |
||
946 |
case DND_OPERATION_CHANGED: |
|
947 |
// ### need to echo |
|
948 |
break; |
|
949 |
||
950 |
case DND_DROP_START: |
|
951 |
{ |
|
952 |
Q_ASSERT(motifdnd_active); |
|
953 |
Q_ASSERT(sourceWindow == dnd_data.src_window); |
|
954 |
||
955 |
if (!dropWidget || lastAcceptedAction == Qt::IgnoreAction) { |
|
956 |
// echo DROP_START |
|
957 |
dnd_data.reason = DND_DROP_START; |
|
958 |
dnd_data.status = DND_NO_DROP_SITE; |
|
959 |
dnd_data.operation = DND_NOOP; |
|
960 |
dnd_data.operations = DND_NOOP; |
|
961 |
DndFillClientMessage (event.xclient.display, sourceWindow, &cm, &dnd_data, 0); |
|
962 |
XSendEvent(event.xbutton.display, sourceWindow, False, 0, (XEvent *)&cm); |
|
963 |
||
964 |
// we have to convert selection in order to indicate failure to the initiator |
|
965 |
XConvertSelection (X11->display, dnd_data.property, ATOM(XmTRANSFER_FAILURE), |
|
966 |
dnd_data.property, dnd_data.src_window, dnd_data.time); |
|
967 |
||
968 |
if (dropWidget) { |
|
969 |
QDragLeaveEvent e; |
|
970 |
QApplication::sendEvent(dropWidget, &e); |
|
971 |
} |
|
972 |
||
973 |
motifdnd_active = false; |
|
974 |
sourceWindow = XNone; |
|
975 |
dropWidget = 0; |
|
976 |
lastAcceptedAction = Qt::IgnoreAction; |
|
977 |
||
978 |
return; |
|
979 |
} |
|
980 |
||
981 |
// store selection and its time |
|
982 |
Dnd_selection = dnd_data.property; |
|
983 |
Dnd_selection_time = dnd_data.time; |
|
984 |
||
985 |
QPoint p(dnd_data.x, dnd_data.y); |
|
986 |
QDropEvent de(dropWidget->mapFromGlobal(p), Qt::CopyAction, QDragManager::self()->dropData, |
|
987 |
QApplication::mouseButtons(), QApplication::keyboardModifiers()); |
|
988 |
if (lastAcceptedAction != Qt::IgnoreAction) { |
|
989 |
de.setDropAction(lastAcceptedAction); |
|
990 |
de.accept(); |
|
991 |
} |
|
992 |
QApplication::sendEvent(dropWidget, &de); |
|
993 |
||
994 |
// reset |
|
995 |
Dnd_selection = XNone; |
|
996 |
Dnd_selection_time = 0; |
|
997 |
||
998 |
// echo DROP_START depending on the result of the dropEvent |
|
999 |
if (de.isAccepted()) { |
|
1000 |
dnd_data.reason = DND_DROP_START; |
|
1001 |
dnd_data.status = DND_VALID_DROP_SITE; |
|
1002 |
dnd_data.operation = QtDropActionToDndOperation(de.dropAction()); |
|
1003 |
} else { |
|
1004 |
dnd_data.reason = DND_DROP_START; |
|
1005 |
dnd_data.status = DND_NO_DROP_SITE; |
|
1006 |
dnd_data.operation = DND_NOOP; |
|
1007 |
dnd_data.operations = DND_NOOP; |
|
1008 |
} |
|
1009 |
DndFillClientMessage (event.xclient.display, sourceWindow, &cm, &dnd_data, 0); |
|
1010 |
XSendEvent(event.xbutton.display, sourceWindow, False, 0, (XEvent *)&cm); |
|
1011 |
||
1012 |
sourceWindow = XNone; |
|
1013 |
dropWidget = 0; |
|
1014 |
lastAcceptedAction = Qt::IgnoreAction; |
|
1015 |
||
1016 |
motifdnd_active = false; |
|
1017 |
||
1018 |
break; |
|
1019 |
} |
|
1020 |
||
1021 |
default: |
|
1022 |
break; |
|
1023 |
} // end of switch (dnd_data.reason) |
|
1024 |
} |
|
1025 |
||
1026 |
QT_END_NAMESPACE |
|
1027 |
||
1028 |
#endif // QT_NO_DRAGANDDROP |