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