source: trunk/src/gui/kernel/qmotifdnd_x11.cpp@ 336

Last change on this file since 336 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 33.4 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information ([email protected])
5**
6** This file is part of the QtGui module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you are unsure which license is appropriate for your use, please
37** contact the sales department at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42/* The following copyright notice pertains to the code as contributed
43to Trolltech, not to Trolltech's modifications. It is replicated
44in 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, [email protected] */
66/* Adapted by: Matt Koss, [email protected] */
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
87QT_BEGIN_NAMESPACE
88
89static Window sourceWindow = XNone;
90static QWidget *dropWidget = 0;
91static Qt::DropAction lastAcceptedAction = Qt::IgnoreAction;
92
93static Atom Dnd_selection = 0;
94static Time Dnd_selection_time;
95
96static Atom * src_targets ;
97static 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
126static 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
138static 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 */
168typedef 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
182typedef struct _DndSrcProp {
183 BYTE byte_order ;
184 BYTE protocol_version ;
185 CARD16 target_index ;
186 CARD32 selection ;
187} DndSrcProp ;
188
189typedef 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 ... */
202typedef struct _DndTop {
203 CARD32 src_window;
204 CARD32 property;
205} DndTop ;
206
207typedef struct _DndPot {
208 INT16 x;
209 INT16 y;
210 CARD32 property;
211 CARD32 src_window;
212} DndPot ;
213
214typedef 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
225typedef 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) {\
295struct { unsigned t :32;} bit32;\
296char n, *tp = (char *) &bit32;\
297bit32.t = l;\
298n = tp[0]; tp[0] = tp[3]; tp[3] = n;\
299n = tp[1]; tp[1] = tp[2]; tp[2] = n;\
300l = bit32.t;\
301}
302
303#define SWAP2BYTES(s) {\
304struct { unsigned t :16; } bit16;\
305char n, *tp = (char *) &bit16;\
306bit16.t = s;\
307n = tp[0]; tp[0] = tp[1]; tp[1] = n;\
308s = bit16.t;\
309}
310
311
312/** Private extern functions */
313
314static unsigned char DndByteOrder ();
315
316
317/***** Targets/Index stuff */
318
319typedef struct {
320 int num_targets;
321 Atom *targets;
322} DndTargetsTableEntryRec, * DndTargetsTableEntry;
323
324typedef struct {
325 int num_entries;
326 DndTargetsTableEntry entries;
327} DndTargetsTableRec, * DndTargetsTable;
328
329
330static ushort _DndIndexToTargets(Display * display,
331 int index,
332 Atom ** targets);
333
334extern void qt_x11_intern_atom(const char *, Atom *);
335
336/////////////////////////////////////////////////////////////////
337
338static 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
351static 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 */
385static 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 */
412static 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
465static 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
531static 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
576static 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
659static 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
693QByteArray 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
716QVariant 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
788void QX11Data::motifdndEnable(QWidget *widget, bool)
789{
790 DndWriteReceiverProperty(display, widget->internalWinId(), DND_DRAG_DYNAMIC);
791}
792
793
794void 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
1026QT_END_NAMESPACE
1027
1028#endif // QT_NO_DRAGANDDROP
Note: See TracBrowser for help on using the repository browser.