1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the QtGui module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file. Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
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
DndOperationsToQtDropActions(uchar op)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
QtDropActionToDndOperation(Qt::DropAction action)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
DndByteOrder()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
DndReadSourceProperty(Display * dpy,Window window,Atom dnd_selection,Atom ** targets,unsigned short * num_targets)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 */
DndWriteReceiverProperty(Display * dpy,Window window,unsigned char protocol_style)385 static void DndWriteReceiverProperty(Display * dpy, Window window,
386 unsigned char protocol_style)
387 {
388 DndReceiverProp receiver_prop;
389
390 // squelch potential valgrind errors about uninitialized reads
391 memset(&receiver_prop, 0, sizeof(receiver_prop));
392
393 receiver_prop.byte_order = DndByteOrder() ;
394 receiver_prop.protocol_version = DND_PROTOCOL_VERSION;
395 receiver_prop.protocol_style = protocol_style ;
396 receiver_prop.proxy_window = XNone ;
397 receiver_prop.num_drop_sites = 0 ;
398 receiver_prop.total_size = sizeof(DndReceiverProp);
399
400 /* write the buffer to the property */
401 XChangeProperty (dpy, window, ATOM(_MOTIF_DRAG_RECEIVER_INFO), ATOM(_MOTIF_DRAG_RECEIVER_INFO),
402 8, PropModeReplace,
403 (unsigned char *)&receiver_prop,
404 sizeof(DndReceiverProp));
405 }
406
407
408 /* protocol style equiv (preregister stuff really) */
409 #define DND_DRAG_DROP_ONLY_EQUIV 3
410 #define DND_DRAG_DYNAMIC_EQUIV1 2
411 #define DND_DRAG_DYNAMIC_EQUIV2 4
412
413
414 /* Produce a client message to be sent by the caller */
DndFillClientMessage(Display * dpy,Window window,XClientMessageEvent * cm,DndData * dnd_data,char receiver)415 static void DndFillClientMessage(Display * dpy, Window window,
416 XClientMessageEvent *cm,
417 DndData * dnd_data,
418 char receiver)
419 {
420 DndMessage * dnd_message = (DndMessage*)&cm->data.b[0] ;
421
422 cm->display = dpy;
423 cm->type = ClientMessage;
424 cm->serial = LastKnownRequestProcessed(dpy);
425 cm->send_event = True;
426 cm->window = window;
427 cm->format = 8;
428 cm->message_type = ATOM(_MOTIF_DRAG_AND_DROP_MESSAGE);
429
430 dnd_message->reason = dnd_data->reason | DND_SET_EVENT_TYPE(receiver);
431
432 dnd_message->byte_order = DndByteOrder();
433
434 /* we're filling in flags with more stuff that necessary,
435 depending on the reason, but it doesn't matter */
436 dnd_message->flags = 0 ;
437 dnd_message->flags |= DND_SET_STATUS(dnd_data->status) ;
438 dnd_message->flags |= DND_SET_OPERATION(dnd_data->operation) ;
439 dnd_message->flags |= DND_SET_OPERATIONS(dnd_data->operations) ;
440 dnd_message->flags |= DND_SET_COMPLETION(dnd_data->completion) ;
441
442 dnd_message->time = dnd_data->time ;
443
444 switch(dnd_data->reason) {
445 case DND_DROP_SITE_LEAVE: break ;
446 case DND_TOP_LEVEL_ENTER:
447 case DND_TOP_LEVEL_LEAVE:
448 dnd_message->data.top.src_window = dnd_data->src_window ;
449 dnd_message->data.top.property = dnd_data->property ;
450 break ; /* cannot fall through since the byte layout is different in
451 both set of messages, see top and pot union stuff */
452
453 case DND_DRAG_MOTION:
454 case DND_OPERATION_CHANGED:
455 case DND_DROP_SITE_ENTER:
456 case DND_DROP_START:
457 dnd_message->data.pot.x = dnd_data->x ; /* mouse position */
458 dnd_message->data.pot.y = dnd_data->y ;
459 dnd_message->data.pot.src_window = dnd_data->src_window ;
460 dnd_message->data.pot.property = dnd_data->property ;
461 break ;
462 default:
463 break ;
464 }
465
466 }
467
DndParseClientMessage(XClientMessageEvent * cm,DndData * dnd_data,char * receiver)468 static Bool DndParseClientMessage(XClientMessageEvent *cm, DndData * dnd_data,
469 char * receiver)
470 {
471 DndMessage * dnd_message = (DndMessage*)&cm->data.b[0] ;
472
473 if (cm->message_type != ATOM(_MOTIF_DRAG_AND_DROP_MESSAGE)) {
474 return False ;
475 }
476
477 if (dnd_message->byte_order != DndByteOrder()) {
478 SWAP2BYTES(dnd_message->flags);
479 SWAP4BYTES(dnd_message->time);
480 } /* do the rest in the switch */
481
482 dnd_data->reason = dnd_message->reason ;
483 if (DND_GET_EVENT_TYPE(dnd_data->reason))
484 *receiver = 1 ;
485 else
486 *receiver = 0 ;
487 dnd_data->reason &= DND_CLEAR_EVENT_TYPE ;
488
489 dnd_data->time = dnd_message->time ;
490
491 /* we're reading in more stuff that necessary. but who cares */
492 dnd_data->status = DND_GET_STATUS(dnd_message->flags) ;
493 dnd_data->operation = DND_GET_OPERATION(dnd_message->flags) ;
494 dnd_data->operations = DND_GET_OPERATIONS(dnd_message->flags) ;
495 dnd_data->completion = DND_GET_COMPLETION(dnd_message->flags) ;
496
497 switch(dnd_data->reason) {
498 case DND_TOP_LEVEL_ENTER:
499 case DND_TOP_LEVEL_LEAVE:
500 if (dnd_message->byte_order != DndByteOrder()) {
501 SWAP4BYTES(dnd_message->data.top.src_window);
502 SWAP4BYTES(dnd_message->data.top.property);
503 }
504 dnd_data->src_window = dnd_message->data.top.src_window ;
505 dnd_data->property = dnd_message->data.top.property ;
506 break ; /* cannot fall through, see above comment in write msg */
507
508 case DND_DRAG_MOTION:
509 case DND_OPERATION_CHANGED:
510 case DND_DROP_SITE_ENTER:
511 case DND_DROP_START:
512 if (dnd_message->byte_order != DndByteOrder()) {
513 SWAP2BYTES(dnd_message->data.pot.x);
514 SWAP2BYTES(dnd_message->data.pot.y);
515 SWAP4BYTES(dnd_message->data.pot.property);
516 SWAP4BYTES(dnd_message->data.pot.src_window);
517 }
518 dnd_data->x = dnd_message->data.pot.x ;
519 dnd_data->y = dnd_message->data.pot.y ;
520 dnd_data->property = dnd_message->data.pot.property ;
521 dnd_data->src_window = dnd_message->data.pot.src_window ;
522 break ;
523
524 case DND_DROP_SITE_LEAVE:
525 break;
526 default:
527 break ;
528 }
529
530 return True ;
531 }
532
533
MotifWindow(Display * display)534 static Window MotifWindow(Display *display)
535 {
536 Atom type;
537 int format;
538 unsigned long size;
539 unsigned long bytes_after;
540 unsigned char *property = 0;
541 Window motif_window ;
542
543 /* this version does no caching, so it's slow: round trip each time */
544
545 if ((XGetWindowProperty (display, RootWindow(display, 0),
546 ATOM(_MOTIF_DRAG_WINDOW),
547 0L, 100000L, False, AnyPropertyType,
548 &type, &format, &size, &bytes_after,
549 &property) == Success) &&
550 (type != XNone)) {
551 motif_window = *(Window *)property;
552 } else {
553 XSetWindowAttributes sAttributes;
554
555 /* really, this should be done on a separate connection,
556 with XSetCloseDownMode (RetainPermanent), so that
557 others don't have to recreate it; hopefully, some real
558 Motif application will be around to do it */
559
560 sAttributes.override_redirect = True;
561 sAttributes.event_mask = PropertyChangeMask;
562 motif_window = XCreateWindow (display,
563 RootWindow (display, 0),
564 -170, -560, 1, 1, 0, 0,
565 InputOnly, CopyFromParent,
566 (CWOverrideRedirect |CWEventMask),
567 &sAttributes);
568 XMapWindow (display, motif_window);
569 }
570
571 if (property) {
572 XFree ((char *)property);
573 }
574
575 return (motif_window);
576 }
577
578
TargetsTable(Display * display)579 static DndTargetsTable TargetsTable(Display *display)
580 {
581 Atom type;
582 int format;
583 unsigned long size;
584 unsigned long bytes_after;
585 Window motif_window = MotifWindow(display) ;
586 unsigned char *retval;
587 DndTargetsTable targets_table ;
588 int i,j ;
589 char * target_data ;
590
591 /* this version does no caching, so it's slow: round trip each time */
592 /* ideally, register for property notify on this target_list
593 atom and update when necessary only */
594
595 if ((XGetWindowProperty (display, motif_window,
596 ATOM(_MOTIF_DRAG_TARGETS), 0L, 100000L,
597 False, ATOM(_MOTIF_DRAG_TARGETS),
598 &type, &format, &size, &bytes_after,
599 &retval) != Success) ||
600 type == XNone) {
601 qWarning("QMotifDND: Cannot get property on Motif window");
602 return 0;
603 }
604
605 DndTargets * target_prop = (DndTargets *)retval;
606
607 if (target_prop->protocol_version != DND_PROTOCOL_VERSION) {
608 qWarning("QMotifDND: Protocol mismatch");
609 }
610
611 if (target_prop->byte_order != DndByteOrder()) {
612 /* need to swap num_target_lists and size */
613 SWAP2BYTES(target_prop->num_target_lists);
614 SWAP4BYTES(target_prop->data_size);
615 }
616
617 /* now parse DndTarget prop data in a TargetsTable */
618
619 targets_table = (DndTargetsTable)malloc(sizeof(DndTargetsTableRec));
620 targets_table->num_entries = target_prop->num_target_lists ;
621 targets_table->entries = (DndTargetsTableEntry)
622 malloc(sizeof(DndTargetsTableEntryRec) * target_prop->num_target_lists);
623
624 target_data = (char*)target_prop + sizeof(*target_prop) ;
625
626 for (i = 0 ; i < targets_table->num_entries; i++) {
627 CARD16 num_targets ;
628 CARD32 atom ;
629
630 memcpy(&num_targets, target_data, 2);
631 target_data += 2;
632
633 /* potential swap needed here */
634 if (target_prop->byte_order != DndByteOrder())
635 SWAP2BYTES(num_targets);
636
637 targets_table->entries[i].num_targets = num_targets ;
638 targets_table->entries[i].targets = (Atom *)
639 malloc(sizeof(Atom) * targets_table->entries[i].num_targets);
640
641
642 for (j = 0; j < num_targets; j++) {
643 memcpy(&atom, target_data, 4);
644 target_data += 4;
645
646 /* another potential swap needed here */
647 if (target_prop->byte_order != DndByteOrder())
648 SWAP4BYTES(atom);
649
650 targets_table->entries[i].targets[j] = (Atom) atom ;
651 }
652 }
653
654 if (target_prop) {
655 XFree((char *)target_prop);
656 }
657
658 return targets_table ;
659 }
660
661
_DndIndexToTargets(Display * display,int index,Atom ** targets)662 static ushort _DndIndexToTargets(Display * display,
663 int index,
664 Atom ** targets)
665 {
666 DndTargetsTable targets_table;
667 int i ;
668
669 /* again, slow: no caching here, alloc/free each time */
670
671 if (!(targets_table = TargetsTable (display)) ||
672 (index >= targets_table->num_entries)) {
673 if (targets_table)
674 XFree((char*)targets_table);
675 return 0;
676 }
677
678 /* transfer the correct target list index */
679 *targets = (Atom*)malloc(sizeof(Atom)*targets_table->
680 entries[index].num_targets);
681 memcpy((char*)*targets,
682 (char*)targets_table->entries[index].targets,
683 sizeof(Atom)*targets_table->entries[index].num_targets);
684
685 /* free the target table and its guts */
686 for (i=0 ; i < targets_table->num_entries; i++)
687 XFree((char*)targets_table->entries[i].targets);
688
689 int tmp = targets_table->entries[index].num_targets;
690 XFree((char*)targets_table);
691
692 return tmp; // targets_table->entries[index].num_targets;
693 }
694
695
motifdndFormat(int n)696 QByteArray QX11Data::motifdndFormat(int n)
697 {
698 if (!motifdnd_active)
699 return 0; // should not happen
700
701 if (n >= num_src_targets)
702 return 0;
703
704 Atom target = src_targets[n];
705
706 if (target == XA_STRING)
707 return "text/plain;charset=ISO-8859-1";
708 if (target == ATOM(UTF8_STRING))
709 return "text/plain;charset=UTF-8";
710 if (target == ATOM(COMPOUND_TEXT))
711 return QByteArray("text/plain;charset=") + QTextCodec::codecForLocale()->name();
712 if (target == ATOM(TEXT))
713 return "text/plain";
714
715 return ("x-motif-dnd/" + X11->xdndAtomToString(target));
716 }
717
718
motifdndObtainData(const char * mimeType)719 QVariant QX11Data::motifdndObtainData(const char *mimeType)
720 {
721 QByteArray result;
722
723 if (Dnd_selection == 0 || !dropWidget)
724 return result;
725
726 // try to convert the selection to the requested property
727 // qDebug("trying to convert to '%s'", mimeType);
728
729 int n=0;
730 QByteArray f;
731 do {
732 f = motifdndFormat(n);
733 if (f.isEmpty())
734 return result;
735 n++;
736 } while(qstricmp(mimeType, f.data()));
737
738 Atom conversion_type = XNone;
739 if (f == "text/plain;charset=ISO-8859-1") {
740 conversion_type = XA_STRING;
741 } else if (f == "text/plain;charset=UTF-8") {
742 conversion_type = ATOM(UTF8_STRING);
743 } else if (f == (QByteArray("text/plain;charset=") + QTextCodec::codecForLocale()->name())) {
744 conversion_type = ATOM(COMPOUND_TEXT);
745 } else if (f == "text/plain") {
746 conversion_type = ATOM(TEXT);
747 } else if (f.startsWith("x-motif-dnd/")) {
748 // strip off the "x-motif-dnd/" prefix
749 conversion_type = X11->xdndStringToAtom(f.remove(0, 12));
750 }
751
752 if (XGetSelectionOwner(X11->display, Dnd_selection) == XNone) {
753 return result; // should never happen?
754 }
755
756 QWidget* tw = dropWidget;
757 if ((dropWidget->windowType() == Qt::Desktop)) {
758 tw = new QWidget;
759 }
760
761 // convert selection to the appropriate type
762 XConvertSelection (X11->display, Dnd_selection, conversion_type,
763 Dnd_selection, tw->internalWinId(), Dnd_selection_time);
764
765 XFlush(X11->display);
766
767 XEvent xevent;
768 bool got=X11->clipboardWaitForEvent(tw->internalWinId(), SelectionNotify, &xevent, 5000);
769 if (got) {
770 Atom type;
771
772 if (X11->clipboardReadProperty(tw->internalWinId(), Dnd_selection, true, &result, 0, &type, 0)) {
773 }
774 }
775
776 // we have to convert selection in order to indicate success to the initiator
777 XConvertSelection (X11->display, Dnd_selection, ATOM(XmTRANSFER_SUCCESS),
778 Dnd_selection, tw->internalWinId(), Dnd_selection_time);
779
780 // wait again for SelectionNotify event
781 X11->clipboardWaitForEvent(tw->internalWinId(), SelectionNotify, &xevent, 5000);
782
783 if ((dropWidget->windowType() == Qt::Desktop)) {
784 delete tw;
785 }
786
787 return result;
788 }
789
790
motifdndEnable(QWidget * widget,bool)791 void QX11Data::motifdndEnable(QWidget *widget, bool)
792 {
793 DndWriteReceiverProperty(display, widget->internalWinId(), DND_DRAG_DYNAMIC);
794 }
795
796
motifdndHandle(QWidget * widget,const XEvent * xe,bool)797 void QX11Data::motifdndHandle(QWidget *widget, const XEvent * xe, bool /* passive */)
798 {
799 XEvent event = *xe;
800 XClientMessageEvent cm ;
801 DndData dnd_data ;
802 char receiver ;
803
804 if (!(DndParseClientMessage ((XClientMessageEvent*)&event,
805 &dnd_data, &receiver))) {
806 return;
807 }
808
809 switch (dnd_data.reason) {
810
811 case DND_DRAG_MOTION:
812 {
813 QPoint p = widget->mapFromGlobal(QPoint(dnd_data.x, dnd_data.y));
814 QWidget *c = widget->childAt(p);
815
816 if (!c || !c->acceptDrops()) {
817 // not over a drop site
818 if (dropWidget) {
819 QDragLeaveEvent dragLeaveEvent;
820 QApplication::sendEvent(dropWidget, &dragLeaveEvent);
821
822 dropWidget = 0;
823 lastAcceptedAction = Qt::IgnoreAction;
824
825 dnd_data.reason = DND_DROP_SITE_LEAVE;
826 dnd_data.time = X11->time;
827 DndFillClientMessage (event.xclient.display, sourceWindow, &cm, &dnd_data, receiver);
828 XSendEvent(event.xbutton.display, sourceWindow, False, 0, (XEvent *)&cm) ;
829 } else {
830 dnd_data.reason = DND_DRAG_MOTION;
831 dnd_data.status = DND_NO_DROP_SITE;
832 dnd_data.time = X11->time;
833 dnd_data.operation = DND_NOOP;
834 dnd_data.operations = DND_NOOP;
835 DndFillClientMessage (event.xclient.display, sourceWindow, &cm, &dnd_data, receiver);
836 XSendEvent(event.xbutton.display, sourceWindow, False, 0, (XEvent *)&cm) ;
837 }
838 } else {
839 Q_ASSERT(c != 0);
840 p = c->mapFrom(widget, p);
841
842 if (dropWidget != c) {
843 if (dropWidget) {
844 QDragLeaveEvent le;
845 QApplication::sendEvent(dropWidget, &le);
846 }
847
848 dropWidget = c;
849 lastAcceptedAction = Qt::IgnoreAction;
850
851 const Qt::DropActions possibleActions =
852 DndOperationsToQtDropActions(dnd_data.operations);
853 QDragEnterEvent de(p, possibleActions, QDragManager::self()->dropData,
854 QApplication::mouseButtons(), QApplication::keyboardModifiers());
855 QApplication::sendEvent(dropWidget, &de);
856
857 dnd_data.reason = DND_DROP_SITE_ENTER;
858 dnd_data.time = X11->time;
859 if (de.isAccepted()) {
860 lastAcceptedAction = de.dropAction();
861
862 dnd_data.status = DND_VALID_DROP_SITE;
863 dnd_data.operation = QtDropActionToDndOperation(lastAcceptedAction);
864 } else {
865 dnd_data.status = DND_INVALID_DROP_SITE;
866 dnd_data.operation = DND_NOOP;
867 dnd_data.operations = DND_NOOP;
868 }
869 DndFillClientMessage (event.xclient.display, sourceWindow, &cm, &dnd_data, receiver);
870 XSendEvent(event.xbutton.display, sourceWindow, False, 0, (XEvent *)&cm);
871 } else {
872 const Qt::DropActions possibleActions =
873 DndOperationsToQtDropActions(dnd_data.operations);
874 QDragMoveEvent me(p, possibleActions, QDragManager::self()->dropData,
875 QApplication::mouseButtons(), QApplication::keyboardModifiers());
876 if (lastAcceptedAction != Qt::IgnoreAction) {
877 me.setDropAction(lastAcceptedAction);
878 me.accept();
879 }
880 QApplication::sendEvent(dropWidget, &me);
881
882 dnd_data.reason = DND_DRAG_MOTION;
883 dnd_data.time = X11->time;
884
885 if (me.isAccepted()) {
886 lastAcceptedAction = me.dropAction();
887
888 dnd_data.status = DND_VALID_DROP_SITE;
889 dnd_data.operation = QtDropActionToDndOperation(lastAcceptedAction);
890 } else {
891 dnd_data.status = DND_INVALID_DROP_SITE;
892 dnd_data.operation = DND_NOOP;
893 dnd_data.operations = DND_NOOP;
894 }
895
896 DndFillClientMessage (event.xclient.display, sourceWindow, &cm, &dnd_data, receiver);
897 XSendEvent(event.xbutton.display, sourceWindow, False, 0, (XEvent *)&cm);
898 }
899 }
900
901 break;
902 }
903
904 case DND_TOP_LEVEL_ENTER:
905 {
906 /* get the size of our drop site for later use */
907
908 motifdnd_active = true;
909 sourceWindow = dnd_data.src_window;
910
911 /* no answer needed, just read source property */
912 DndReadSourceProperty (event.xclient.display,
913 sourceWindow,
914 dnd_data.property,
915 &src_targets, &num_src_targets);
916
917 break;
918 }
919
920 case DND_TOP_LEVEL_LEAVE:
921 {
922 XEvent nextEvent;
923 if (XCheckTypedWindowEvent(X11->display, widget->winId(), ClientMessage, &nextEvent)) {
924 // we just want to check, not eat (should use XPeekIfEvent)
925 XPutBackEvent(X11->display, &nextEvent);
926
927 if (DndParseClientMessage (&nextEvent.xclient, &dnd_data, &receiver)
928 && dnd_data.reason == DND_DROP_START) {
929 // expecting drop next, keeping DnD alive
930 break;
931 }
932 }
933
934 // not expecting drop, need to send drag leave events and such here
935 if (dropWidget) {
936 QDragLeaveEvent le;
937 QApplication::sendEvent(dropWidget, &le);
938 }
939
940 sourceWindow = XNone;
941 dropWidget = 0;
942 lastAcceptedAction = Qt::IgnoreAction;
943
944 motifdnd_active = false;
945
946 break;
947 }
948
949 case DND_OPERATION_CHANGED:
950 // ### need to echo
951 break;
952
953 case DND_DROP_START:
954 {
955 Q_ASSERT(motifdnd_active);
956 Q_ASSERT(sourceWindow == dnd_data.src_window);
957
958 if (!dropWidget || lastAcceptedAction == Qt::IgnoreAction) {
959 // echo DROP_START
960 dnd_data.reason = DND_DROP_START;
961 dnd_data.status = DND_NO_DROP_SITE;
962 dnd_data.operation = DND_NOOP;
963 dnd_data.operations = DND_NOOP;
964 DndFillClientMessage (event.xclient.display, sourceWindow, &cm, &dnd_data, 0);
965 XSendEvent(event.xbutton.display, sourceWindow, False, 0, (XEvent *)&cm);
966
967 // we have to convert selection in order to indicate failure to the initiator
968 XConvertSelection (X11->display, dnd_data.property, ATOM(XmTRANSFER_FAILURE),
969 dnd_data.property, dnd_data.src_window, dnd_data.time);
970
971 if (dropWidget) {
972 QDragLeaveEvent e;
973 QApplication::sendEvent(dropWidget, &e);
974 }
975
976 motifdnd_active = false;
977 sourceWindow = XNone;
978 dropWidget = 0;
979 lastAcceptedAction = Qt::IgnoreAction;
980
981 return;
982 }
983
984 // store selection and its time
985 Dnd_selection = dnd_data.property;
986 Dnd_selection_time = dnd_data.time;
987
988 QPoint p(dnd_data.x, dnd_data.y);
989 QDropEvent de(dropWidget->mapFromGlobal(p), Qt::CopyAction, QDragManager::self()->dropData,
990 QApplication::mouseButtons(), QApplication::keyboardModifiers());
991 if (lastAcceptedAction != Qt::IgnoreAction) {
992 de.setDropAction(lastAcceptedAction);
993 de.accept();
994 }
995 QApplication::sendEvent(dropWidget, &de);
996
997 // reset
998 Dnd_selection = XNone;
999 Dnd_selection_time = 0;
1000
1001 // echo DROP_START depending on the result of the dropEvent
1002 if (de.isAccepted()) {
1003 dnd_data.reason = DND_DROP_START;
1004 dnd_data.status = DND_VALID_DROP_SITE;
1005 dnd_data.operation = QtDropActionToDndOperation(de.dropAction());
1006 } else {
1007 dnd_data.reason = DND_DROP_START;
1008 dnd_data.status = DND_NO_DROP_SITE;
1009 dnd_data.operation = DND_NOOP;
1010 dnd_data.operations = DND_NOOP;
1011 }
1012 DndFillClientMessage (event.xclient.display, sourceWindow, &cm, &dnd_data, 0);
1013 XSendEvent(event.xbutton.display, sourceWindow, False, 0, (XEvent *)&cm);
1014
1015 sourceWindow = XNone;
1016 dropWidget = 0;
1017 lastAcceptedAction = Qt::IgnoreAction;
1018
1019 motifdnd_active = false;
1020
1021 break;
1022 }
1023
1024 default:
1025 break;
1026 } // end of switch (dnd_data.reason)
1027 }
1028
1029 QT_END_NAMESPACE
1030
1031 #endif // QT_NO_DRAGANDDROP
1032