|
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 |