0
|
1 |
/****************************************************************************
|
|
2 |
**
|
|
3 |
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
|
|
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
|