428
+ − 1 /*
+ − 2 This is a modified DND 1.0 library which does not depend on Xt
+ − 3 event handling.
+ − 4 Modifications Copyright (c) 1997 Oliver Graf <ograf@fga.de>
+ − 5
+ − 6 Original DND lib
+ − 7 Copyright (C) 1996 C�sar Crusius
+ − 8
+ − 9 This file is part of the DND Library. This library is free
+ − 10 software; you can redistribute it and/or modify it under the terms of
+ − 11 the GNU Library General Public License as published by the Free
+ − 12 Software Foundation; either version 2 of the License, or (at your
+ − 13 option) any later version. This library is distributed in the hope
+ − 14 that it will be useful, but WITHOUT ANY WARRANTY; without even the
+ − 15 implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ − 16 PURPOSE. See the GNU Library General Public License for more details.
+ − 17 You should have received a copy of the GNU Library General Public
+ − 18 License along with this library; if not, write to the Free Software
+ − 19 Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ − 20 */
+ − 21
+ − 22 /* #define DEBUG */
+ − 23
+ − 24 #include "offix.h"
+ − 25 #include <X11/cursorfont.h>
+ − 26 #include <X11/Xatom.h>
+ − 27 #include <X11/Xmu/WinUtil.h>
+ − 28 #include <stdio.h>
+ − 29 #include <stdlib.h>
+ − 30 #include <limits.h>
+ − 31
+ − 32 /* Local variables */
+ − 33 static Display *dpy; /* current display */
+ − 34 static int DragPrecision; /* minimum dx,dy to start drag */
+ − 35 static int Dragging; /* Drag state flag */
+ − 36 static int DataOK; /* Non-zero if data registered */
+ − 37 static Atom DndProtocol; /* ClientMessage identifier */
+ − 38 static Atom DndSelection; /* For the data transfers */
+ − 39 static Atom OldDndProtocol; /* Version 0 atom */
+ − 40 static Atom OldDndSelection;/* Version 0 atom */
+ − 41 static Atom WM_STATE; /* Needed for icon stuff */
+ − 42 static Window Target; /* Drop window */
+ − 43 static Widget MainWidget; /* Main widget of application */
+ − 44 static int DataType; /* Current drag data type */
+ − 45 static int RootFlag; /* Non-zero if dropped on root */
+ − 46 static XColor Black,White; /* For the cursors */
+ − 47
+ − 48 /*=========================================================================
+ − 49 * Data for the standard Dnd cursors
+ − 50 *=========================================================================*/
+ − 51 #include "offix-cursors.h"
+ − 52
+ − 53 /*=============================================================== CursorData
+ − 54 * CursorData contains all the data for the cursors bitmaps
+ − 55 *==========================================================================*/
+ − 56 typedef struct
+ − 57 {
+ − 58 int Width,Height;
+ − 59 unsigned char *ImageData,*MaskData;
+ − 60 int HotSpotX,HotSpotY;
+ − 61 Pixmap ImagePixmap,MaskPixmap;
+ − 62 Cursor CursorID;
+ − 63 } CursorData;
+ − 64
+ − 65 static CursorData DndCursor[DndEND]={
+ − 66 { 0,0,NULL,NULL,0,0,0 },
+ − 67 { grey_width, grey_height,grey_bits,grey_mask_bits,
+ − 68 grey_x_hot,grey_y_hot},
+ − 69 { file_width,file_height,file_bits,file_mask_bits,
+ − 70 file_x_hot,file_y_hot},
+ − 71 { files_width,files_height,files_bits,files_mask_bits,
+ − 72 files_x_hot,files_y_hot},
+ − 73 { text_width,text_height,text_bits,text_mask_bits,
+ − 74 text_x_hot,text_y_hot },
+ − 75 { dir_width,dir_height,dir_bits,dir_mask_bits,
+ − 76 dir_x_hot,dir_y_hot },
+ − 77 { link_width,link_height,link_bits,link_mask_bits,
+ − 78 link_x_hot,link_y_hot},
+ − 79 { app_width,app_height,app_bits,app_mask_bits,
+ − 80 app_x_hot,app_y_hot },
+ − 81 { url_width,url_height,url_bits,url_mask_bits,
+ − 82 url_x_hot,url_y_hot },
+ − 83 { mime_width,mime_height,mime_bits,mime_mask_bits,
+ − 84 mime_x_hot,mime_y_hot }
+ − 85 };
+ − 86
+ − 87 /* Local prototypes */
+ − 88 int DndIsDragging(void);
+ − 89 void DndStartAction(Widget widget,
+ − 90 XtPointer data,
+ − 91 XEvent *event,
+ − 92 Boolean *p);
+ − 93 void DndPropertyHandler(Widget widget,
+ − 94 XtPointer data,
+ − 95 XEvent *event,
+ − 96 Boolean *p);
+ − 97
+ − 98 /*======================================================== DndHandleDragging
+ − 99 * Takes care of the drag and drop process. Wait until the pointer had moved
+ − 100 * a little. Then takes control over the pointer until the buttons are
+ − 101 * released. After that send a Drag And Drop ClientMessage event. Returns
+ − 102 * non-zero if a drop did take place.
+ − 103 *===========================================================================*/
+ − 104 int
+ − 105 DndHandleDragging(Widget widget,XEvent *event)
+ − 106 {
+ − 107 XEvent Event;
+ − 108 Window root = RootWindowOfScreen(XtScreenOfObject(widget));
+ − 109 XtAppContext app= XtWidgetToApplicationContext(widget);
+ − 110 Window DispatchWindow;
+ − 111 int DropX,DropY;
+ − 112
+ − 113 if(Dragging) return 0;
+ − 114
+ − 115 XUngrabPointer(dpy,CurrentTime);
+ − 116 /* Take control over the pointer */
+ − 117 XGrabPointer(dpy,root,False,
+ − 118 ButtonMotionMask|ButtonPressMask|ButtonReleaseMask,
+ − 119 GrabModeSync,GrabModeAsync,root,
+ − 120 DndCursor[DataType].CursorID,
+ − 121 CurrentTime);
+ − 122
+ − 123 /* Wait for button release */
+ − 124 Dragging=1; RootFlag=0;
+ − 125 while(Dragging)
+ − 126 {
+ − 127 XAllowEvents(dpy,SyncPointer,CurrentTime);
+ − 128 XtAppNextEvent(app,&Event);
+ − 129 switch(Event.type)
+ − 130 {
+ − 131 case ButtonRelease:
+ − 132 if(Event.xbutton.subwindow)
+ − 133 RootFlag=0;
+ − 134 else
+ − 135 RootFlag=1;
+ − 136 Dragging=0;
+ − 137 break;
+ − 138 default:
+ − 139 XtDispatchEvent(&Event);
+ − 140 break;
+ − 141 }
+ − 142 }
+ − 143 DataOK=0;
+ − 144 /* Now release the pointer */
+ − 145 XUngrabPointer(dpy,CurrentTime);
+ − 146 /* Try to guess if the drop occurred in the root window */
+ − 147 if(!RootFlag)
+ − 148 {
+ − 149 Target=XmuClientWindow(dpy,Event.xbutton.subwindow);
+ − 150 if (Target==Event.xbutton.subwindow)
+ − 151 DispatchWindow=Target;
+ − 152 else
+ − 153 DispatchWindow=PointerWindow;
+ − 154 }
+ − 155 else
+ − 156 Target=DispatchWindow=XtWindow(MainWidget);
+ − 157
+ − 158 /* Now build the event structure */
+ − 159 DropX=Event.xbutton.x_root;
+ − 160 DropY=Event.xbutton.y_root;
+ − 161 Event.xclient.type = ClientMessage;
+ − 162 Event.xclient.display = dpy;
+ − 163 Event.xclient.message_type = DndProtocol;
+ − 164 Event.xclient.format = 32;
+ − 165 Event.xclient.window = Target;
+ − 166 Event.xclient.data.l[0] = DataType;
+ − 167 Event.xclient.data.l[1] = (long)event->xbutton.state;
+ − 168 Event.xclient.data.l[2] = (long)XtWindow(widget);
+ − 169 Event.xclient.data.l[3] = DropX + 65536L*(long)DropY;
+ − 170 Event.xclient.data.l[4] = 1;
+ − 171
+ − 172 /* Send the drop message */
+ − 173 XSendEvent(dpy,DispatchWindow,True,NoEventMask,&Event);
+ − 174 /* Send an old style version of the message just in case */
+ − 175 Event.xclient.message_type = OldDndProtocol;
+ − 176 XSendEvent(dpy,DispatchWindow,True,NoEventMask,&Event);
+ − 177
+ − 178 #ifdef DEBUG
+ − 179 fprintf(stderr,"ClientMessage sent to 0x%x(0x%x).\n",
+ − 180 DispatchWindow,Target);
+ − 181 fprintf(stderr,"The drop coordinates are (%d,%d).\n",DropX,DropY);
+ − 182 #endif
+ − 183
+ − 184 return 1;
+ − 185 }
+ − 186
+ − 187 /*=============================================================== DndIsIcon
+ − 188 * Return non-zero if the application is iconic (widget=toplevel)
+ − 189 *========================================================================*/
+ − 190 int
+ − 191 DndIsIcon(Widget widget)
+ − 192 {
+ − 193 Atom JunkAtom;
+ − 194 int JunkInt;
+ − 195 unsigned long WinState,JunkLong;
+ − 196 unsigned char *Property;
+ − 197
+ − 198 XGetWindowProperty(dpy,XtWindow(widget),WM_STATE,
+ − 199 0L,2L,False,AnyPropertyType,
+ − 200 &JunkAtom,&JunkInt,&WinState,&JunkLong,
+ − 201 &Property);
+ − 202 WinState=(unsigned long)(*((long*)Property));
+ − 203 return (WinState==3);
+ − 204 }
+ − 205
+ − 206 /*============================================================ DndInitialize
+ − 207 * Must be called anywhere before the top level widget creation and the
+ − 208 * main loop. Initialize global variables and bind the DndDispatch function
+ − 209 * to the top level widget. Creates the cursors to be used in drag actions.
+ − 210 *=========================================================================*/
+ − 211 void
+ − 212 DndInitialize(Widget shell)
+ − 213 {
+ − 214 int screen,i;
+ − 215 Colormap colormap;
+ − 216 Window root;
+ − 217
+ − 218 dpy = XtDisplayOfObject(shell);
+ − 219 screen = DefaultScreen(dpy);
+ − 220 colormap= DefaultColormap(dpy,screen);
+ − 221 root = DefaultRootWindow(dpy);
+ − 222
+ − 223
+ − 224 Black.pixel=BlackPixel(dpy,screen);
+ − 225 White.pixel=WhitePixel(dpy,screen);
+ − 226 XQueryColor(dpy,colormap,&Black);
+ − 227 XQueryColor(dpy,colormap,&White);
+ − 228
+ − 229 for(i=1;i!=DndEND;i++)
+ − 230 {
+ − 231 DndCursor[i].ImagePixmap=
+ − 232 XCreateBitmapFromData(dpy,root,
+ − 233 (char *) DndCursor[i].ImageData,
+ − 234 DndCursor[i].Width,
+ − 235 DndCursor[i].Height);
+ − 236 DndCursor[i].MaskPixmap=
+ − 237 XCreateBitmapFromData(dpy,root,
+ − 238 (char *) DndCursor[i].MaskData,
+ − 239 DndCursor[i].Width,
+ − 240 DndCursor[i].Height);
+ − 241 DndCursor[i].CursorID=
+ − 242 XCreatePixmapCursor(dpy,DndCursor[i].ImagePixmap,
+ − 243 DndCursor[i].MaskPixmap,
+ − 244 &Black,&White,
+ − 245 DndCursor[i].HotSpotX,
+ − 246 DndCursor[i].HotSpotY);
+ − 247 }
+ − 248
+ − 249 DndCursor[0].CursorID=XCreateFontCursor(dpy,XC_question_arrow);
+ − 250
+ − 251 /* These two are for older versions */
+ − 252 OldDndProtocol=XInternAtom(dpy,"DndProtocol",FALSE);
+ − 253 OldDndSelection=XInternAtom(dpy,"DndSelection",FALSE);
+ − 254 /* Now the correct stuff */
+ − 255 DndProtocol=XInternAtom(dpy,"_DND_PROTOCOL",FALSE);
+ − 256 DndSelection=XInternAtom(dpy,"_DND_SELECTION",FALSE);
+ − 257
+ − 258 WM_STATE=XInternAtom(dpy,"WM_STATE",True);
+ − 259 Dragging=0;
+ − 260 DragPrecision=10;
+ − 261 RootFlag=0;
+ − 262 MainWidget=shell;
+ − 263 }
+ − 264
+ − 265 int
+ − 266 DndIsDragging(void)
+ − 267 {
+ − 268 return Dragging;
+ − 269 }
+ − 270
+ − 271 /*================================================================= DndSetData
+ − 272 * Updates the selection data.
+ − 273 *===========================================================================*/
+ − 274 void
+ − 275 DndSetData(int Type,unsigned char *Data,unsigned long Size)
+ − 276 {
+ − 277 Window root = DefaultRootWindow(dpy);
+ − 278 int AuxSize;
+ − 279 unsigned char *AuxData;
+ − 280 unsigned long BackSize=Size;
+ − 281
+ − 282 if (DataOK) return;
+ − 283
+ − 284 /* Set the data type -- allow any type */
+ − 285 DataType = Type;
+ − 286
+ − 287 /* Set the data */
+ − 288 AuxData = Data;
+ − 289 AuxSize = ( Size <= INT_MAX ? (int)Size : INT_MAX );
+ − 290 XChangeProperty(dpy,root,DndSelection,XA_STRING,8,
+ − 291 PropModeReplace,Data,AuxSize);
+ − 292 for(Size-=(unsigned long)AuxSize;Size;Size-=(unsigned long)AuxSize)
+ − 293 {
+ − 294 Data+=AuxSize;
+ − 295 AuxSize = ( (Size<=(INT_MAX)) ? (int)Size : (INT_MAX) );
+ − 296 XChangeProperty(dpy,root,DndSelection,XA_STRING,8,
+ − 297 PropModeAppend,Data,AuxSize);
+ − 298 }
+ − 299
+ − 300 /* Set the data for old DND version */
+ − 301 Size = BackSize;
+ − 302 AuxData = Data;
+ − 303 AuxSize = ( Size <= INT_MAX ? (int)Size : INT_MAX );
+ − 304 XChangeProperty(dpy,root,OldDndSelection,XA_STRING,8,
+ − 305 PropModeReplace,Data,AuxSize);
+ − 306 for(Size-=(unsigned long)AuxSize;Size;Size-=(unsigned long)AuxSize)
+ − 307 {
+ − 308 Data+=AuxSize;
+ − 309 AuxSize = ( (Size<=(INT_MAX)) ? (int)Size : (INT_MAX) );
+ − 310 XChangeProperty(dpy,root,OldDndSelection,XA_STRING,8,
+ − 311 PropModeAppend,Data,AuxSize);
+ − 312 }
+ − 313
+ − 314 /* Everything is now ok */
+ − 315 DataOK=1;
+ − 316 }
+ − 317
+ − 318 /*================================================================== DndGetData
+ − 319 * Return a pointer to the current data. See HOWTO for more details.
+ − 320 *===========================================================================*/
+ − 321 void
+ − 322 DndGetData(XEvent *event, unsigned char **Data,unsigned long *Size)
+ − 323 {
+ − 324 Window root = DefaultRootWindow(dpy);
+ − 325
+ − 326 Atom ActualType,ActualDndSelection;
+ − 327 int ActualFormat;
+ − 328 unsigned long RemainingBytes;
+ − 329
+ − 330 ActualDndSelection=(DndProtocolVersion(event) == 0L ?
+ − 331 OldDndSelection :
+ − 332 DndSelection );
+ − 333
+ − 334 XGetWindowProperty(dpy,root,ActualDndSelection,
+ − 335 0L,1000000L,
+ − 336 FALSE,AnyPropertyType,
+ − 337 &ActualType,&ActualFormat,
+ − 338 Size,&RemainingBytes,
+ − 339 Data);
+ − 340 }
+ − 341
+ − 342 /*================================== DndDataType DndDragButtons DndSourceWidget
+ − 343 *
+ − 344 * Return information about the Dnd event received. If a non-dnd event is
+ − 345 * passed, the function DndDataType returns DndNotDnd, and the others
+ − 346 * return zero.
+ − 347 *===========================================================================*/
+ − 348 int
+ − 349 DndDataType(XEvent *event)
+ − 350 {
+ − 351 int Type;
+ − 352
+ − 353 if(!DndIsDropMessage(event)) return DndNotDnd;
+ − 354 Type=(int)(event->xclient.data.l[0]);
+ − 355 if(Type>=DndEND) Type=DndUnknown;
+ − 356 return Type;
+ − 357 }
+ − 358
647
+ − 359 int
428
+ − 360 DndDragButtons(XEvent *event)
+ − 361 {
+ − 362 if(!DndIsDropMessage(event)) return 0;
647
+ − 363 return (int)(event->xclient.data.l[1]);
428
+ − 364 }
+ − 365
+ − 366 Window
+ − 367 DndSourceWindow(XEvent *event)
+ − 368 {
+ − 369 if(!DndIsDropMessage(event)) return 0;
+ − 370 if(DndProtocolVersion(event)<__DragAndDropH__)
+ − 371 /* We will try to do something about it, but nothing is certain */
+ − 372 return XtWindow((Widget)(event->xclient.data.l[2]));
+ − 373 return (Window)(event->xclient.data.l[2]);
+ − 374 }
+ − 375
+ − 376 void
+ − 377 DndDropRootCoordinates(XEvent *event,int *x,int *y)
+ − 378 {
+ − 379 if(!DndIsDropMessage(event))
+ − 380 {
+ − 381 *x=0; *y=0;
+ − 382 return;
+ − 383 }
+ − 384
+ − 385 /* If it is an old protocol version we try to get the coordinates
+ − 386 using the current pointer position. Of course, the pointer may have
+ − 387 moved since the drop, but there's nothing we can do about it.
+ − 388 */
+ − 389 if(DndProtocolVersion(event)<1L)
+ − 390 {
+ − 391 Window root_return,child_return;
+ − 392 int win_x_return,win_y_return;
+ − 393 unsigned int mask_return;
+ − 394
+ − 395 XQueryPointer(dpy,DefaultRootWindow(dpy),
+ − 396 &root_return,&child_return,x,y,
+ − 397 &win_x_return,&win_y_return,&mask_return);
+ − 398 return;
+ − 399 }
+ − 400 /* Thanks god you are using a decent protocol version */
+ − 401 *x=(int)((long)(event->xclient.data.l[3]) & 0xffff);
+ − 402 *y=(int)((long)(event->xclient.data.l[3])/65536);
+ − 403 }
+ − 404
+ − 405 void
+ − 406 DndDropCoordinates(Widget widget,XEvent *event,int *x,int *y)
+ − 407 {
+ − 408 int root_x,root_y;
+ − 409 Window child_return;
+ − 410
+ − 411 DndDropRootCoordinates(event,&root_x,&root_y);
+ − 412 XTranslateCoordinates(dpy,DefaultRootWindow(dpy),
+ − 413 XtWindow(widget),
+ − 414 root_x,root_y,
+ − 415 x,y,
+ − 416 &child_return);
+ − 417 }
+ − 418
+ − 419 long
+ − 420 DndProtocolVersion(XEvent *event)
+ − 421 {
+ − 422 if(!DndIsDropMessage(event)) return -1L;
+ − 423 return event->xclient.data.l[4];
+ − 424 }
+ − 425
+ − 426 int
+ − 427 DndIsDropMessage(XEvent *event)
+ − 428 {
+ − 429 if(event->xclient.type != ClientMessage) return 0;
+ − 430 if(event->xclient.message_type == OldDndProtocol &&
+ − 431 event->xclient.data.l[4]==0) return 1;
+ − 432 if(event->xclient.message_type == DndProtocol) return 1;
+ − 433 return 0;
+ − 434 }
+ − 435
+ − 436 void
+ − 437 DndChangeCursor(int Type,int width,int height,char *image,char *mask,
+ − 438 int hot_x,int hot_y)
+ − 439 {
+ − 440 DndCursor[Type].ImagePixmap=
+ − 441 XCreateBitmapFromData(dpy,DefaultRootWindow(dpy),
+ − 442 image,width,height);
+ − 443 DndCursor[Type].MaskPixmap=
+ − 444 XCreateBitmapFromData(dpy,DefaultRootWindow(dpy),
+ − 445 mask,width,height);
+ − 446 DndCursor[Type].CursorID=
+ − 447 XCreatePixmapCursor(dpy,DndCursor[Type].ImagePixmap,
+ − 448 DndCursor[Type].MaskPixmap,
+ − 449 &Black,&White,
+ − 450 hot_x,hot_y);
+ − 451 }