src/gui/kernel/qmotifdnd_x11.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the QtGui module of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 /* The following copyright notice pertains to the code as contributed
       
    43 to Qt, not to Nokia's modifications. It is replicated
       
    44 in doc/dnd.doc, where the documentation system can see it. */
       
    45 
       
    46 /* Copyright 1996 Daniel Dardailler.
       
    47 
       
    48    Permission to use, copy, modify, distribute, and sell this software
       
    49    for any purpose is hereby granted without fee, provided that the above
       
    50    copyright notice appear in all copies and that both that copyright
       
    51    notice and this permission notice appear in supporting documentation,
       
    52    and that the name of Daniel Dardailler not be used in advertising or
       
    53    publicity pertaining to distribution of the software without specific,
       
    54    written prior permission.  Daniel Dardailler makes no representations
       
    55    about the suitability of this software for any purpose.  It is
       
    56    provided "as is" without express or implied warranty.
       
    57 
       
    58    Modifications Copyright 1999 Matt Koss, under the same license as
       
    59    above.
       
    60 ************************************************************/
       
    61 
       
    62 /***********************************************************/
       
    63 /* Motif Drag&Drop Dynamic Protocol messaging API code */
       
    64 /* Only requires Xlib layer - not MT safe */
       
    65 /* Author: Daniel Dardailler, daniel@x.org */
       
    66 /* Adapted by: Matt Koss, koss@napri.sk */
       
    67 /* Further adaptions by: Nokia Corporation and/or its subsidiary(-ies) */
       
    68 /***********************************************************/
       
    69 
       
    70 #include "qplatformdefs.h"
       
    71 
       
    72 #include "qapplication.h"
       
    73 
       
    74 #ifndef QT_NO_DRAGANDDROP
       
    75 
       
    76 #include "qdebug.h"
       
    77 #include "qtextcodec.h"
       
    78 #include "qwidget.h"
       
    79 #include "qevent.h"
       
    80 #include "qt_x11_p.h"
       
    81 #include "qx11info_x11.h"
       
    82 #include "qiodevice.h"
       
    83 #include "qdnd_p.h"
       
    84 
       
    85 #include <stdlib.h>
       
    86 
       
    87 QT_BEGIN_NAMESPACE
       
    88 
       
    89 static Window sourceWindow = XNone;
       
    90 static QWidget *dropWidget = 0;
       
    91 static Qt::DropAction lastAcceptedAction = Qt::IgnoreAction;
       
    92 
       
    93 static Atom Dnd_selection = 0;
       
    94 static Time Dnd_selection_time;
       
    95 
       
    96 static Atom * src_targets ;
       
    97 static ushort num_src_targets ;
       
    98 
       
    99 // Motif definitions
       
   100 #define DndVersion 1
       
   101 #define DndRevision 0
       
   102 #define DndIncludeVersion (DndVersion * 10 + DndRevision)
       
   103 
       
   104 /* The following values are used in the DndData structure */
       
   105 
       
   106 /* protocol style */
       
   107 #define DND_DRAG_NONE            0
       
   108 #define DND_DRAG_DROP_ONLY       1
       
   109 #define DND_DRAG_DYNAMIC         5
       
   110 
       
   111 /* message type */
       
   112 #define DND_TOP_LEVEL_ENTER   0
       
   113 #define DND_TOP_LEVEL_LEAVE   1
       
   114 #define DND_DRAG_MOTION       2
       
   115 #define DND_DROP_SITE_ENTER   3
       
   116 #define DND_DROP_SITE_LEAVE   4
       
   117 #define DND_DROP_START        5
       
   118 #define DND_OPERATION_CHANGED 8
       
   119 
       
   120 /* operation(s) */
       
   121 #define DND_NOOP        0L
       
   122 #define DND_MOVE         (1L << 0)
       
   123 #define DND_COPY        (1L << 1)
       
   124 #define DND_LINK        (1L << 2)
       
   125 
       
   126 static Qt::DropActions DndOperationsToQtDropActions(uchar op)
       
   127 {
       
   128     Qt::DropActions actions = Qt::IgnoreAction;
       
   129     if (op | DND_MOVE)
       
   130         actions |= Qt::MoveAction;
       
   131     if (op | DND_COPY)
       
   132         actions |= Qt::CopyAction;
       
   133     if (op | DND_LINK)
       
   134         actions |= Qt::LinkAction;
       
   135     return actions;
       
   136 }
       
   137 
       
   138 static uchar QtDropActionToDndOperation(Qt::DropAction action)
       
   139 {
       
   140     switch (action & Qt::ActionMask) {
       
   141     case Qt::CopyAction:
       
   142     default:
       
   143         return DND_COPY;
       
   144     case Qt::MoveAction:
       
   145         return DND_MOVE;
       
   146     case Qt::LinkAction:
       
   147         return DND_LINK;
       
   148     }
       
   149 }
       
   150 
       
   151 
       
   152 /* status */
       
   153 #define DND_NO_DROP_SITE        1
       
   154 #define DND_INVALID_DROP_SITE   2
       
   155 #define DND_VALID_DROP_SITE        3
       
   156 
       
   157 /* completion */
       
   158 #define DND_DROP        0
       
   159 #define DND_DROP_HELP   1
       
   160 #define DND_DROP_CANCEL 2
       
   161 
       
   162 #define BYTE unsigned char
       
   163 #define CARD32 unsigned int
       
   164 #define CARD16 unsigned short
       
   165 #define INT16  signed short
       
   166 
       
   167 /* Client side structure used in the API */
       
   168 typedef struct {
       
   169     unsigned char       reason;  /* message type: DND_TOP_LEVEL_ENTER, etc */
       
   170     Time                time ;
       
   171     unsigned char       operation;
       
   172     unsigned char       operations;
       
   173     unsigned char       status;
       
   174     unsigned char       completion;
       
   175     short               x ;
       
   176     short               y ;
       
   177     Window              src_window ;
       
   178     Atom                property ;
       
   179 } DndData ;
       
   180 
       
   181 
       
   182 typedef struct _DndSrcProp {
       
   183     BYTE                byte_order ;
       
   184     BYTE                protocol_version ;
       
   185     CARD16              target_index ;
       
   186     CARD32              selection ;
       
   187 } DndSrcProp ;
       
   188 
       
   189 typedef struct _DndReceiverProp {
       
   190     BYTE                byte_order ;
       
   191     BYTE                protocol_version ;
       
   192     BYTE                protocol_style ;
       
   193     BYTE                pad1;
       
   194     CARD32              proxy_window;
       
   195     CARD16              num_drop_sites ;
       
   196     CARD16              pad2;
       
   197     CARD32              total_size;
       
   198 } DndReceiverProp ;
       
   199 
       
   200 /* need to use some union hack since window and property are in
       
   201    different order depending on the message ... */
       
   202 typedef struct _DndTop {
       
   203     CARD32                src_window;
       
   204     CARD32                property;
       
   205 } DndTop ;
       
   206 
       
   207 typedef struct _DndPot {
       
   208     INT16                x;
       
   209     INT16                y;
       
   210     CARD32                property;
       
   211     CARD32                src_window;
       
   212 } DndPot ;
       
   213 
       
   214 typedef struct _DndMessage {
       
   215     BYTE                reason;
       
   216     BYTE                byte_order;
       
   217     CARD16                flags;
       
   218     CARD32                time;
       
   219     union {
       
   220         DndTop top ;
       
   221         DndPot pot ;
       
   222     } data ;
       
   223 } DndMessage ;
       
   224 
       
   225 typedef struct {
       
   226     BYTE        byte_order;
       
   227     BYTE        protocol_version;
       
   228     CARD16        num_target_lists;
       
   229     CARD32        data_size;
       
   230     /* then come series of CARD16,CARD32,CARD32,CARD32... */
       
   231 } DndTargets;
       
   232 
       
   233 
       
   234 /* protocol version */
       
   235 #define DND_PROTOCOL_VERSION 0
       
   236 
       
   237 
       
   238 #define DND_EVENT_TYPE_MASK  ((BYTE)0x80)
       
   239 #define DND_EVENT_TYPE_SHIFT 7
       
   240 #define DND_CLEAR_EVENT_TYPE  ((BYTE)0x7F)
       
   241 
       
   242 /* message_type is data[0] of the client_message
       
   243    this return 1 (receiver bit up) or 0 (initiator) */
       
   244 #define DND_GET_EVENT_TYPE(message_type) \
       
   245 ((char) (((message_type) & DND_EVENT_TYPE_MASK) >> DND_EVENT_TYPE_SHIFT))
       
   246 
       
   247 /* event_type can be 0 (initiator) or 1 (receiver) */
       
   248 #define DND_SET_EVENT_TYPE(event_type) \
       
   249 (((BYTE)(event_type) << DND_EVENT_TYPE_SHIFT) & DND_EVENT_TYPE_MASK)
       
   250 
       
   251 
       
   252 #define DND_OPERATION_MASK ((CARD16) 0x000F)
       
   253 #define DND_OPERATION_SHIFT 0
       
   254 #define DND_STATUS_MASK ((CARD16) 0x00F0)
       
   255 #define DND_STATUS_SHIFT 4
       
   256 #define DND_OPERATIONS_MASK ((CARD16) 0x0F00)
       
   257 #define DND_OPERATIONS_SHIFT 8
       
   258 #define DND_COMPLETION_MASK ((CARD16) 0xF000)
       
   259 #define DND_COMPLETION_SHIFT 12
       
   260 
       
   261 #define DND_GET_OPERATION(flags) \
       
   262 ((unsigned char) \
       
   263 (((flags) & DND_OPERATION_MASK) >> DND_OPERATION_SHIFT))
       
   264 
       
   265 #define DND_SET_OPERATION(operation) \
       
   266 (((CARD16)(operation) << DND_OPERATION_SHIFT)\
       
   267 & DND_OPERATION_MASK)
       
   268 
       
   269 #define DND_GET_STATUS(flags) \
       
   270 ((unsigned char) \
       
   271 (((flags) & DND_STATUS_MASK) >> DND_STATUS_SHIFT))
       
   272 
       
   273 #define DND_SET_STATUS(status) \
       
   274 (((CARD16)(status) << DND_STATUS_SHIFT)\
       
   275 & DND_STATUS_MASK)
       
   276 
       
   277 #define DND_GET_OPERATIONS(flags) \
       
   278 ((unsigned char) \
       
   279 (((flags) & DND_OPERATIONS_MASK) >> DND_OPERATIONS_SHIFT))
       
   280 
       
   281 #define DND_SET_OPERATIONS(operation) \
       
   282 (((CARD16)(operation) << DND_OPERATIONS_SHIFT)\
       
   283 & DND_OPERATIONS_MASK)
       
   284 
       
   285 #define DND_GET_COMPLETION(flags) \
       
   286 ((unsigned char) \
       
   287 (((flags) & DND_COMPLETION_MASK) >> DND_COMPLETION_SHIFT))
       
   288 
       
   289 #define DND_SET_COMPLETION(completion) \
       
   290 (((CARD16)(completion) << DND_COMPLETION_SHIFT)\
       
   291 & DND_COMPLETION_MASK)
       
   292 
       
   293 
       
   294 #define SWAP4BYTES(l) {\
       
   295 struct { unsigned t :32;} bit32;\
       
   296 char n, *tp = (char *) &bit32;\
       
   297 bit32.t = l;\
       
   298 n = tp[0]; tp[0] = tp[3]; tp[3] = n;\
       
   299 n = tp[1]; tp[1] = tp[2]; tp[2] = n;\
       
   300 l = bit32.t;\
       
   301 }
       
   302 
       
   303 #define SWAP2BYTES(s) {\
       
   304 struct { unsigned t :16; } bit16;\
       
   305 char n, *tp = (char *) &bit16;\
       
   306 bit16.t = s;\
       
   307 n = tp[0]; tp[0] = tp[1]; tp[1] = n;\
       
   308 s = bit16.t;\
       
   309 }
       
   310 
       
   311 
       
   312 /** Private extern functions */
       
   313 
       
   314 static unsigned char DndByteOrder ();
       
   315 
       
   316 
       
   317 /***** Targets/Index stuff */
       
   318 
       
   319 typedef struct {
       
   320     int            num_targets;
       
   321     Atom    *targets;
       
   322 } DndTargetsTableEntryRec, * DndTargetsTableEntry;
       
   323 
       
   324 typedef struct {
       
   325     int        num_entries;
       
   326     DndTargetsTableEntry entries;
       
   327 } DndTargetsTableRec, * DndTargetsTable;
       
   328 
       
   329 
       
   330 static ushort _DndIndexToTargets(Display * display,
       
   331                                  int index,
       
   332                                  Atom ** targets);
       
   333 
       
   334 extern void qt_x11_intern_atom(const char *, Atom *);
       
   335 
       
   336 /////////////////////////////////////////////////////////////////
       
   337 
       
   338 static unsigned char DndByteOrder ()
       
   339 {
       
   340     static unsigned char byte_order = 0;
       
   341 
       
   342     if (!byte_order) {
       
   343         unsigned int endian = 1;
       
   344         byte_order = (*((char *)&endian))?'l':'B';
       
   345     }
       
   346     return byte_order ;
       
   347 }
       
   348 
       
   349 
       
   350 
       
   351 static void DndReadSourceProperty(Display * dpy,
       
   352                                   Window window, Atom dnd_selection,
       
   353                                   Atom ** targets, unsigned short * num_targets)
       
   354 {
       
   355     unsigned char *retval = 0;
       
   356     Atom type ;
       
   357     int format ;
       
   358     unsigned long bytesafter, lengthRtn;
       
   359 
       
   360     if ((XGetWindowProperty (dpy, window, dnd_selection, 0L, 100000L,
       
   361                              False, ATOM(_MOTIF_DRAG_INITIATOR_INFO), &type,
       
   362                              &format, &lengthRtn, &bytesafter,
       
   363                              &retval) != Success)
       
   364         || (type == XNone)) {
       
   365         *num_targets = 0;
       
   366         return ;
       
   367     }
       
   368 
       
   369     DndSrcProp * src_prop = (DndSrcProp *)retval;
       
   370 
       
   371     if (src_prop->byte_order != DndByteOrder()) {
       
   372         SWAP2BYTES(src_prop->target_index);
       
   373         SWAP4BYTES(src_prop->selection);
       
   374     }
       
   375 
       
   376     *num_targets = _DndIndexToTargets(dpy, src_prop->target_index, targets);
       
   377 
       
   378     XFree((char*)src_prop);
       
   379 }
       
   380 
       
   381 
       
   382 /* Position the _MOTIF_DRAG_RECEIVER_INFO property on the dropsite window.
       
   383    Called by the receiver of the drop to indicate the
       
   384    supported protocol style : dynamic, drop_only or none */
       
   385 static void DndWriteReceiverProperty(Display * dpy, Window window,
       
   386                                      unsigned char protocol_style)
       
   387 {
       
   388     DndReceiverProp receiver_prop ;
       
   389 
       
   390     receiver_prop.byte_order = DndByteOrder() ;
       
   391     receiver_prop.protocol_version = DND_PROTOCOL_VERSION;
       
   392     receiver_prop.protocol_style = protocol_style ;
       
   393     receiver_prop.proxy_window =  XNone ;
       
   394     receiver_prop.num_drop_sites = 0 ;
       
   395     receiver_prop.total_size = sizeof(DndReceiverProp);
       
   396 
       
   397     /* write the buffer to the property */
       
   398     XChangeProperty (dpy, window, ATOM(_MOTIF_DRAG_RECEIVER_INFO), ATOM(_MOTIF_DRAG_RECEIVER_INFO),
       
   399                      8, PropModeReplace,
       
   400                      (unsigned char *)&receiver_prop,
       
   401                      sizeof(DndReceiverProp));
       
   402 }
       
   403 
       
   404 
       
   405 /* protocol style equiv (preregister stuff really) */
       
   406 #define DND_DRAG_DROP_ONLY_EQUIV 3
       
   407 #define DND_DRAG_DYNAMIC_EQUIV1  2
       
   408 #define DND_DRAG_DYNAMIC_EQUIV2  4
       
   409 
       
   410 
       
   411 /* Produce a client message to be sent by the caller */
       
   412 static void DndFillClientMessage(Display * dpy, Window window,
       
   413                                  XClientMessageEvent *cm,
       
   414                                  DndData * dnd_data,
       
   415                                  char receiver)
       
   416 {
       
   417     DndMessage * dnd_message = (DndMessage*)&cm->data.b[0] ;
       
   418 
       
   419     cm->display = dpy;
       
   420     cm->type = ClientMessage;
       
   421     cm->serial = LastKnownRequestProcessed(dpy);
       
   422     cm->send_event = True;
       
   423     cm->window = window;
       
   424     cm->format = 8;
       
   425     cm->message_type = ATOM(_MOTIF_DRAG_AND_DROP_MESSAGE);
       
   426 
       
   427     dnd_message->reason = dnd_data->reason | DND_SET_EVENT_TYPE(receiver);
       
   428 
       
   429     dnd_message->byte_order = DndByteOrder();
       
   430 
       
   431     /* we're filling in flags with more stuff that necessary,
       
   432        depending on the reason, but it doesn't matter */
       
   433     dnd_message->flags = 0 ;
       
   434     dnd_message->flags |= DND_SET_STATUS(dnd_data->status) ;
       
   435     dnd_message->flags |= DND_SET_OPERATION(dnd_data->operation) ;
       
   436     dnd_message->flags |= DND_SET_OPERATIONS(dnd_data->operations) ;
       
   437     dnd_message->flags |= DND_SET_COMPLETION(dnd_data->completion) ;
       
   438 
       
   439     dnd_message->time = dnd_data->time ;
       
   440 
       
   441     switch(dnd_data->reason) {
       
   442     case DND_DROP_SITE_LEAVE: break ;
       
   443     case DND_TOP_LEVEL_ENTER:
       
   444     case DND_TOP_LEVEL_LEAVE:
       
   445         dnd_message->data.top.src_window = dnd_data->src_window ;
       
   446         dnd_message->data.top.property = dnd_data->property ;
       
   447         break ; /* cannot fall through since the byte layout is different in
       
   448                    both set of messages, see top and pot union stuff */
       
   449 
       
   450     case DND_DRAG_MOTION:
       
   451     case DND_OPERATION_CHANGED:
       
   452     case DND_DROP_SITE_ENTER:
       
   453     case DND_DROP_START:
       
   454         dnd_message->data.pot.x = dnd_data->x ; /* mouse position */
       
   455         dnd_message->data.pot.y = dnd_data->y ;
       
   456         dnd_message->data.pot.src_window = dnd_data->src_window ;
       
   457         dnd_message->data.pot.property = dnd_data->property ;
       
   458         break ;
       
   459     default:
       
   460         break ;
       
   461     }
       
   462 
       
   463 }
       
   464 
       
   465 static Bool DndParseClientMessage(XClientMessageEvent *cm, DndData * dnd_data,
       
   466                                   char * receiver)
       
   467 {
       
   468     DndMessage * dnd_message = (DndMessage*)&cm->data.b[0] ;
       
   469 
       
   470     if (cm->message_type != ATOM(_MOTIF_DRAG_AND_DROP_MESSAGE)) {
       
   471         return False ;
       
   472     }
       
   473 
       
   474     if (dnd_message->byte_order != DndByteOrder()) {
       
   475         SWAP2BYTES(dnd_message->flags);
       
   476         SWAP4BYTES(dnd_message->time);
       
   477     } /* do the rest in the switch */
       
   478 
       
   479     dnd_data->reason = dnd_message->reason  ;
       
   480     if (DND_GET_EVENT_TYPE(dnd_data->reason))
       
   481         *receiver = 1 ;
       
   482     else
       
   483         *receiver = 0 ;
       
   484     dnd_data->reason &= DND_CLEAR_EVENT_TYPE ;
       
   485 
       
   486     dnd_data->time = dnd_message->time ;
       
   487 
       
   488     /* we're reading in more stuff that necessary. but who cares */
       
   489     dnd_data->status = DND_GET_STATUS(dnd_message->flags) ;
       
   490     dnd_data->operation = DND_GET_OPERATION(dnd_message->flags) ;
       
   491     dnd_data->operations = DND_GET_OPERATIONS(dnd_message->flags) ;
       
   492     dnd_data->completion = DND_GET_COMPLETION(dnd_message->flags) ;
       
   493 
       
   494     switch(dnd_data->reason) {
       
   495     case DND_TOP_LEVEL_ENTER:
       
   496     case DND_TOP_LEVEL_LEAVE:
       
   497         if (dnd_message->byte_order != DndByteOrder()) {
       
   498             SWAP4BYTES(dnd_message->data.top.src_window);
       
   499             SWAP4BYTES(dnd_message->data.top.property);
       
   500         }
       
   501         dnd_data->src_window = dnd_message->data.top.src_window ;
       
   502         dnd_data->property = dnd_message->data.top.property ;
       
   503         break ; /* cannot fall through, see above comment in write msg */
       
   504 
       
   505     case DND_DRAG_MOTION:
       
   506     case DND_OPERATION_CHANGED:
       
   507     case DND_DROP_SITE_ENTER:
       
   508     case DND_DROP_START:
       
   509         if (dnd_message->byte_order != DndByteOrder()) {
       
   510             SWAP2BYTES(dnd_message->data.pot.x);
       
   511             SWAP2BYTES(dnd_message->data.pot.y);
       
   512             SWAP4BYTES(dnd_message->data.pot.property);
       
   513             SWAP4BYTES(dnd_message->data.pot.src_window);
       
   514         }
       
   515         dnd_data->x = dnd_message->data.pot.x ;
       
   516         dnd_data->y = dnd_message->data.pot.y ;
       
   517         dnd_data->property = dnd_message->data.pot.property ;
       
   518         dnd_data->src_window = dnd_message->data.pot.src_window ;
       
   519         break ;
       
   520 
       
   521     case DND_DROP_SITE_LEAVE:
       
   522         break;
       
   523     default:
       
   524         break ;
       
   525     }
       
   526 
       
   527     return True ;
       
   528 }
       
   529 
       
   530 
       
   531 static Window MotifWindow(Display *display)
       
   532 {
       
   533     Atom            type;
       
   534     int             format;
       
   535     unsigned long   size;
       
   536     unsigned long   bytes_after;
       
   537     unsigned char  *property = 0;
       
   538     Window            motif_window ;
       
   539 
       
   540     /* this version does no caching, so it's slow: round trip each time */
       
   541 
       
   542     if ((XGetWindowProperty (display, RootWindow(display, 0),
       
   543                              ATOM(_MOTIF_DRAG_WINDOW),
       
   544                              0L, 100000L, False, AnyPropertyType,
       
   545                              &type, &format, &size, &bytes_after,
       
   546                              &property) == Success) &&
       
   547         (type != XNone)) {
       
   548         motif_window = *(Window *)property;
       
   549     } else {
       
   550         XSetWindowAttributes sAttributes;
       
   551 
       
   552         /* really, this should be done on a separate connection,
       
   553            with XSetCloseDownMode (RetainPermanent), so that
       
   554            others don't have to recreate it; hopefully, some real
       
   555            Motif application will be around to do it */
       
   556 
       
   557         sAttributes.override_redirect = True;
       
   558         sAttributes.event_mask = PropertyChangeMask;
       
   559         motif_window = XCreateWindow (display,
       
   560                                       RootWindow (display, 0),
       
   561                                       -170, -560, 1, 1, 0, 0,
       
   562                                       InputOnly, CopyFromParent,
       
   563                                       (CWOverrideRedirect |CWEventMask),
       
   564                                       &sAttributes);
       
   565         XMapWindow (display, motif_window);
       
   566     }
       
   567 
       
   568     if (property) {
       
   569         XFree ((char *)property);
       
   570     }
       
   571 
       
   572     return (motif_window);
       
   573 }
       
   574 
       
   575 
       
   576 static DndTargetsTable TargetsTable(Display *display)
       
   577 {
       
   578     Atom            type;
       
   579     int             format;
       
   580     unsigned long   size;
       
   581     unsigned long   bytes_after;
       
   582     Window motif_window = MotifWindow(display) ;
       
   583     unsigned char  *retval;
       
   584     DndTargetsTable targets_table ;
       
   585     int i,j ;
       
   586     char * target_data ;
       
   587 
       
   588     /* this version does no caching, so it's slow: round trip each time */
       
   589     /* ideally, register for property notify on this target_list
       
   590        atom and update when necessary only */
       
   591 
       
   592     if ((XGetWindowProperty (display, motif_window,
       
   593                              ATOM(_MOTIF_DRAG_TARGETS), 0L, 100000L,
       
   594                              False, ATOM(_MOTIF_DRAG_TARGETS),
       
   595                              &type, &format, &size, &bytes_after,
       
   596                              &retval) != Success) ||
       
   597         type == XNone) {
       
   598         qWarning("QMotifDND: Cannot get property on Motif window");
       
   599         return 0;
       
   600     }
       
   601 
       
   602     DndTargets * target_prop = (DndTargets *)retval;
       
   603 
       
   604     if (target_prop->protocol_version != DND_PROTOCOL_VERSION) {
       
   605         qWarning("QMotifDND: Protocol mismatch");
       
   606     }
       
   607 
       
   608     if (target_prop->byte_order != DndByteOrder()) {
       
   609         /* need to swap num_target_lists and size */
       
   610         SWAP2BYTES(target_prop->num_target_lists);
       
   611         SWAP4BYTES(target_prop->data_size);
       
   612     }
       
   613 
       
   614     /* now parse DndTarget prop data in a TargetsTable */
       
   615 
       
   616     targets_table = (DndTargetsTable)malloc(sizeof(DndTargetsTableRec));
       
   617     targets_table->num_entries = target_prop->num_target_lists ;
       
   618     targets_table->entries = (DndTargetsTableEntry)
       
   619                              malloc(sizeof(DndTargetsTableEntryRec) * target_prop->num_target_lists);
       
   620 
       
   621     target_data = (char*)target_prop + sizeof(*target_prop) ;
       
   622 
       
   623     for (i = 0 ; i < targets_table->num_entries; i++) {
       
   624         CARD16 num_targets ;
       
   625         CARD32 atom ;
       
   626 
       
   627         memcpy(&num_targets, target_data, 2);
       
   628         target_data += 2;
       
   629 
       
   630         /* potential swap needed here */
       
   631         if (target_prop->byte_order != DndByteOrder())
       
   632             SWAP2BYTES(num_targets);
       
   633 
       
   634         targets_table->entries[i].num_targets = num_targets ;
       
   635         targets_table->entries[i].targets = (Atom *)
       
   636                                             malloc(sizeof(Atom) * targets_table->entries[i].num_targets);
       
   637 
       
   638 
       
   639         for (j = 0; j < num_targets; j++) {
       
   640             memcpy(&atom, target_data, 4);
       
   641             target_data += 4;
       
   642 
       
   643             /* another potential swap needed here */
       
   644             if (target_prop->byte_order != DndByteOrder())
       
   645                 SWAP4BYTES(atom);
       
   646 
       
   647             targets_table->entries[i].targets[j] = (Atom) atom ;
       
   648         }
       
   649     }
       
   650 
       
   651     if (target_prop) {
       
   652         XFree((char *)target_prop);
       
   653     }
       
   654 
       
   655     return targets_table ;
       
   656 }
       
   657 
       
   658 
       
   659 static ushort _DndIndexToTargets(Display * display,
       
   660                               int index,
       
   661                               Atom ** targets)
       
   662 {
       
   663     DndTargetsTable        targets_table;
       
   664     int i ;
       
   665 
       
   666     /* again, slow: no caching here, alloc/free each time */
       
   667 
       
   668     if (!(targets_table = TargetsTable (display)) ||
       
   669         (index >= targets_table->num_entries)) {
       
   670         if (targets_table)
       
   671             XFree((char*)targets_table);
       
   672         return 0;
       
   673     }
       
   674 
       
   675     /* transfer the correct target list index */
       
   676     *targets = (Atom*)malloc(sizeof(Atom)*targets_table->
       
   677                              entries[index].num_targets);
       
   678     memcpy((char*)*targets,
       
   679            (char*)targets_table->entries[index].targets,
       
   680            sizeof(Atom)*targets_table->entries[index].num_targets);
       
   681 
       
   682     /* free the target table and its guts */
       
   683     for (i=0 ; i < targets_table->num_entries; i++)
       
   684         XFree((char*)targets_table->entries[i].targets);
       
   685 
       
   686     int tmp = targets_table->entries[index].num_targets;
       
   687     XFree((char*)targets_table);
       
   688 
       
   689     return tmp; // targets_table->entries[index].num_targets;
       
   690 }
       
   691 
       
   692 
       
   693 QByteArray QX11Data::motifdndFormat(int n)
       
   694 {
       
   695     if (!motifdnd_active)
       
   696         return 0; // should not happen
       
   697 
       
   698     if (n >= num_src_targets)
       
   699         return 0;
       
   700 
       
   701     Atom target = src_targets[n];
       
   702 
       
   703     if (target == XA_STRING)
       
   704         return "text/plain;charset=ISO-8859-1";
       
   705     if (target == ATOM(UTF8_STRING))
       
   706         return "text/plain;charset=UTF-8";
       
   707     if (target == ATOM(COMPOUND_TEXT))
       
   708         return QByteArray("text/plain;charset=") + QTextCodec::codecForLocale()->name();
       
   709     if (target == ATOM(TEXT))
       
   710         return "text/plain";
       
   711 
       
   712     return ("x-motif-dnd/" + X11->xdndAtomToString(target));
       
   713 }
       
   714 
       
   715 
       
   716 QVariant QX11Data::motifdndObtainData(const char *mimeType)
       
   717 {
       
   718     QByteArray result;
       
   719 
       
   720     if (Dnd_selection == 0 || !dropWidget)
       
   721         return result;
       
   722 
       
   723     // try to convert the selection to the requested property
       
   724     // qDebug("trying to convert to '%s'", mimeType);
       
   725 
       
   726     int n=0;
       
   727     QByteArray f;
       
   728     do {
       
   729         f = motifdndFormat(n);
       
   730         if (f.isEmpty())
       
   731             return result;
       
   732         n++;
       
   733     } while(qstricmp(mimeType, f.data()));
       
   734 
       
   735     Atom conversion_type = XNone;
       
   736     if (f == "text/plain;charset=ISO-8859-1") {
       
   737         conversion_type = XA_STRING;
       
   738     } else if (f == "text/plain;charset=UTF-8") {
       
   739         conversion_type = ATOM(UTF8_STRING);
       
   740     } else if (f == (QByteArray("text/plain;charset=") + QTextCodec::codecForLocale()->name())) {
       
   741         conversion_type = ATOM(COMPOUND_TEXT);
       
   742     } else if (f == "text/plain") {
       
   743         conversion_type = ATOM(TEXT);
       
   744     } else if (f.startsWith("x-motif-dnd/")) {
       
   745         // strip off the "x-motif-dnd/" prefix
       
   746         conversion_type = X11->xdndStringToAtom(f.remove(0, 12));
       
   747     }
       
   748 
       
   749     if (XGetSelectionOwner(X11->display, Dnd_selection) == XNone) {
       
   750         return result; // should never happen?
       
   751     }
       
   752 
       
   753     QWidget* tw = dropWidget;
       
   754     if ((dropWidget->windowType() == Qt::Desktop)) {
       
   755         tw = new QWidget;
       
   756     }
       
   757 
       
   758     // convert selection to the appropriate type
       
   759     XConvertSelection (X11->display, Dnd_selection, conversion_type,
       
   760                        Dnd_selection, tw->internalWinId(), Dnd_selection_time);
       
   761 
       
   762     XFlush(X11->display);
       
   763 
       
   764     XEvent xevent;
       
   765     bool got=X11->clipboardWaitForEvent(tw->internalWinId(), SelectionNotify, &xevent, 5000);
       
   766     if (got) {
       
   767         Atom type;
       
   768 
       
   769         if (X11->clipboardReadProperty(tw->internalWinId(), Dnd_selection, true, &result, 0, &type, 0, false)) {
       
   770         }
       
   771     }
       
   772 
       
   773     //   we have to convert selection in order to indicate success to the initiator
       
   774     XConvertSelection (X11->display, Dnd_selection, ATOM(XmTRANSFER_SUCCESS),
       
   775                        Dnd_selection, tw->internalWinId(), Dnd_selection_time);
       
   776 
       
   777     // wait again for SelectionNotify event
       
   778     X11->clipboardWaitForEvent(tw->internalWinId(), SelectionNotify, &xevent, 5000);
       
   779 
       
   780     if ((dropWidget->windowType() == Qt::Desktop)) {
       
   781         delete tw;
       
   782     }
       
   783 
       
   784     return result;
       
   785 }
       
   786 
       
   787 
       
   788 void QX11Data::motifdndEnable(QWidget *widget, bool)
       
   789 {
       
   790     DndWriteReceiverProperty(display, widget->internalWinId(), DND_DRAG_DYNAMIC);
       
   791 }
       
   792 
       
   793 
       
   794 void QX11Data::motifdndHandle(QWidget *widget, const XEvent * xe, bool /* passive */)
       
   795 {
       
   796     XEvent event = *xe;
       
   797     XClientMessageEvent cm ;
       
   798     DndData dnd_data ;
       
   799     char receiver ;
       
   800 
       
   801     if (!(DndParseClientMessage ((XClientMessageEvent*)&event,
       
   802                                  &dnd_data, &receiver))) {
       
   803         return;
       
   804     }
       
   805 
       
   806     switch (dnd_data.reason) {
       
   807 
       
   808     case DND_DRAG_MOTION:
       
   809         {
       
   810             QPoint p = widget->mapFromGlobal(QPoint(dnd_data.x, dnd_data.y));
       
   811             QWidget *c = widget->childAt(p);
       
   812 
       
   813             if (!c || !c->acceptDrops()) {
       
   814                 // not over a drop site
       
   815                 if (dropWidget) {
       
   816                     QDragLeaveEvent dragLeaveEvent;
       
   817                     QApplication::sendEvent(dropWidget, &dragLeaveEvent);
       
   818 
       
   819                     dropWidget = 0;
       
   820                     lastAcceptedAction = Qt::IgnoreAction;
       
   821 
       
   822                     dnd_data.reason = DND_DROP_SITE_LEAVE;
       
   823                     dnd_data.time = X11->time;
       
   824                     DndFillClientMessage (event.xclient.display, sourceWindow, &cm, &dnd_data, receiver);
       
   825                     XSendEvent(event.xbutton.display, sourceWindow, False, 0, (XEvent *)&cm) ;
       
   826                 } else {
       
   827                     dnd_data.reason = DND_DRAG_MOTION;
       
   828                     dnd_data.status = DND_NO_DROP_SITE;
       
   829                     dnd_data.time = X11->time;
       
   830                     dnd_data.operation = DND_NOOP;
       
   831                     dnd_data.operations = DND_NOOP;
       
   832                     DndFillClientMessage (event.xclient.display, sourceWindow, &cm, &dnd_data, receiver);
       
   833                     XSendEvent(event.xbutton.display, sourceWindow, False, 0, (XEvent *)&cm) ;
       
   834                 }
       
   835             } else {
       
   836                 Q_ASSERT(c != 0);
       
   837                 p = c->mapFrom(widget, p);
       
   838 
       
   839                 if (dropWidget != c) {
       
   840                     if (dropWidget) {
       
   841                         QDragLeaveEvent le;
       
   842                         QApplication::sendEvent(dropWidget, &le);
       
   843                     }
       
   844 
       
   845                     dropWidget = c;
       
   846                     lastAcceptedAction = Qt::IgnoreAction;
       
   847 
       
   848                     const Qt::DropActions possibleActions =
       
   849                         DndOperationsToQtDropActions(dnd_data.operations);
       
   850                     QDragEnterEvent de(p, possibleActions, QDragManager::self()->dropData,
       
   851                                        QApplication::mouseButtons(), QApplication::keyboardModifiers());
       
   852                     QApplication::sendEvent(dropWidget, &de);
       
   853 
       
   854                     dnd_data.reason = DND_DROP_SITE_ENTER;
       
   855                     dnd_data.time = X11->time;
       
   856                     if (de.isAccepted()) {
       
   857                         lastAcceptedAction = de.dropAction();
       
   858 
       
   859                         dnd_data.status = DND_VALID_DROP_SITE;
       
   860                         dnd_data.operation = QtDropActionToDndOperation(lastAcceptedAction);
       
   861                     } else {
       
   862                         dnd_data.status = DND_INVALID_DROP_SITE;
       
   863                         dnd_data.operation = DND_NOOP;
       
   864                         dnd_data.operations = DND_NOOP;
       
   865                     }
       
   866                     DndFillClientMessage (event.xclient.display, sourceWindow, &cm, &dnd_data, receiver);
       
   867                     XSendEvent(event.xbutton.display, sourceWindow, False, 0, (XEvent *)&cm);
       
   868                 } else {
       
   869                     const Qt::DropActions possibleActions =
       
   870                         DndOperationsToQtDropActions(dnd_data.operations);
       
   871                     QDragMoveEvent me(p, possibleActions, QDragManager::self()->dropData,
       
   872                                       QApplication::mouseButtons(), QApplication::keyboardModifiers());
       
   873                     if (lastAcceptedAction != Qt::IgnoreAction) {
       
   874                         me.setDropAction(lastAcceptedAction);
       
   875                         me.accept();
       
   876                     }
       
   877                     QApplication::sendEvent(dropWidget, &me);
       
   878 
       
   879                     dnd_data.reason = DND_DRAG_MOTION;
       
   880                     dnd_data.time = X11->time;
       
   881 
       
   882                     if (me.isAccepted()) {
       
   883                         lastAcceptedAction = me.dropAction();
       
   884 
       
   885                         dnd_data.status = DND_VALID_DROP_SITE;
       
   886                         dnd_data.operation = QtDropActionToDndOperation(lastAcceptedAction);
       
   887                     } else {
       
   888                         dnd_data.status = DND_INVALID_DROP_SITE;
       
   889                         dnd_data.operation = DND_NOOP;
       
   890                         dnd_data.operations = DND_NOOP;
       
   891                     }
       
   892 
       
   893                     DndFillClientMessage (event.xclient.display, sourceWindow, &cm, &dnd_data, receiver);
       
   894                     XSendEvent(event.xbutton.display, sourceWindow, False, 0, (XEvent *)&cm);
       
   895                 }
       
   896             }
       
   897 
       
   898             break;
       
   899         }
       
   900 
       
   901     case DND_TOP_LEVEL_ENTER:
       
   902         {
       
   903             /* get the size of our drop site for later use */
       
   904 
       
   905             motifdnd_active = true;
       
   906             sourceWindow = dnd_data.src_window;
       
   907 
       
   908             /* no answer needed, just read source property */
       
   909             DndReadSourceProperty (event.xclient.display,
       
   910                                    sourceWindow,
       
   911                                    dnd_data.property,
       
   912                                    &src_targets, &num_src_targets);
       
   913 
       
   914             break;
       
   915         }
       
   916 
       
   917     case DND_TOP_LEVEL_LEAVE:
       
   918         {
       
   919             XEvent nextEvent;
       
   920             if (XCheckTypedWindowEvent(X11->display, widget->winId(), ClientMessage, &nextEvent)) {
       
   921                 // we just want to check, not eat (should use XPeekIfEvent)
       
   922                 XPutBackEvent(X11->display, &nextEvent);
       
   923 
       
   924                 if (DndParseClientMessage (&nextEvent.xclient, &dnd_data, &receiver)
       
   925                     && dnd_data.reason == DND_DROP_START) {
       
   926                     // expecting drop next, keeping DnD alive
       
   927                     break;
       
   928                 }
       
   929             }
       
   930 
       
   931             // not expecting drop, need to send drag leave events and such here
       
   932             if (dropWidget) {
       
   933                 QDragLeaveEvent le;
       
   934                 QApplication::sendEvent(dropWidget, &le);
       
   935             }
       
   936 
       
   937             sourceWindow = XNone;
       
   938             dropWidget = 0;
       
   939             lastAcceptedAction = Qt::IgnoreAction;
       
   940 
       
   941             motifdnd_active = false;
       
   942 
       
   943             break;
       
   944         }
       
   945 
       
   946     case DND_OPERATION_CHANGED:
       
   947         // ### need to echo
       
   948         break;
       
   949 
       
   950     case DND_DROP_START:
       
   951         {
       
   952             Q_ASSERT(motifdnd_active);
       
   953             Q_ASSERT(sourceWindow == dnd_data.src_window);
       
   954 
       
   955             if (!dropWidget || lastAcceptedAction == Qt::IgnoreAction) {
       
   956                 // echo DROP_START
       
   957                 dnd_data.reason = DND_DROP_START;
       
   958                 dnd_data.status = DND_NO_DROP_SITE;
       
   959                 dnd_data.operation = DND_NOOP;
       
   960                 dnd_data.operations = DND_NOOP;
       
   961                 DndFillClientMessage (event.xclient.display, sourceWindow, &cm, &dnd_data, 0);
       
   962                 XSendEvent(event.xbutton.display, sourceWindow, False, 0, (XEvent *)&cm);
       
   963 
       
   964                 // we have to convert selection in order to indicate failure to the initiator
       
   965                 XConvertSelection (X11->display, dnd_data.property, ATOM(XmTRANSFER_FAILURE),
       
   966                                    dnd_data.property, dnd_data.src_window, dnd_data.time);
       
   967 
       
   968                 if (dropWidget) {
       
   969                     QDragLeaveEvent e;
       
   970                     QApplication::sendEvent(dropWidget, &e);
       
   971                 }
       
   972 
       
   973                 motifdnd_active = false;
       
   974                 sourceWindow = XNone;
       
   975                 dropWidget = 0;
       
   976                 lastAcceptedAction = Qt::IgnoreAction;
       
   977 
       
   978                 return;
       
   979             }
       
   980 
       
   981             // store selection and its time
       
   982             Dnd_selection = dnd_data.property;
       
   983             Dnd_selection_time = dnd_data.time;
       
   984 
       
   985             QPoint p(dnd_data.x, dnd_data.y);
       
   986             QDropEvent de(dropWidget->mapFromGlobal(p), Qt::CopyAction, QDragManager::self()->dropData,
       
   987                           QApplication::mouseButtons(), QApplication::keyboardModifiers());
       
   988             if (lastAcceptedAction != Qt::IgnoreAction) {
       
   989                 de.setDropAction(lastAcceptedAction);
       
   990                 de.accept();
       
   991             }
       
   992             QApplication::sendEvent(dropWidget, &de);
       
   993 
       
   994             // reset
       
   995             Dnd_selection = XNone;
       
   996             Dnd_selection_time = 0;
       
   997 
       
   998             // echo DROP_START depending on the result of the dropEvent
       
   999             if (de.isAccepted()) {
       
  1000                 dnd_data.reason = DND_DROP_START;
       
  1001                 dnd_data.status = DND_VALID_DROP_SITE;
       
  1002                 dnd_data.operation = QtDropActionToDndOperation(de.dropAction());
       
  1003             } else {
       
  1004                 dnd_data.reason = DND_DROP_START;
       
  1005                 dnd_data.status = DND_NO_DROP_SITE;
       
  1006                 dnd_data.operation = DND_NOOP;
       
  1007                 dnd_data.operations = DND_NOOP;
       
  1008             }
       
  1009             DndFillClientMessage (event.xclient.display, sourceWindow, &cm, &dnd_data, 0);
       
  1010             XSendEvent(event.xbutton.display, sourceWindow, False, 0, (XEvent *)&cm);
       
  1011 
       
  1012             sourceWindow = XNone;
       
  1013             dropWidget = 0;
       
  1014             lastAcceptedAction = Qt::IgnoreAction;
       
  1015 
       
  1016             motifdnd_active = false;
       
  1017 
       
  1018             break;
       
  1019         }
       
  1020 
       
  1021     default:
       
  1022         break;
       
  1023     }   //  end of switch (dnd_data.reason)
       
  1024 }
       
  1025 
       
  1026 QT_END_NAMESPACE
       
  1027 
       
  1028 #endif // QT_NO_DRAGANDDROP