2 This is a modified DND 1.0 library which does not depend on Xt
4 Modifications Copyright (c) 1997 Oliver Graf <ograf@fga.de>
7 Copyright (C) 1996 César Crusius
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.
25 #include <X11/cursorfont.h>
26 #include <X11/Xatom.h>
27 #include <X11/Xmu/WinUtil.h>
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 */
48 /*=========================================================================
49 * Data for the standard Dnd cursors
50 *=========================================================================*/
51 #include "offix-cursors.h"
53 /*=============================================================== CursorData
54 * CursorData contains all the data for the cursors bitmaps
55 *==========================================================================*/
59 unsigned char *ImageData,*MaskData;
60 int HotSpotX,HotSpotY;
61 Pixmap ImagePixmap,MaskPixmap;
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 }
87 /* Local prototypes */
88 int DndIsDragging(void);
89 void DndStartAction(Widget widget,
93 void DndPropertyHandler(Widget widget,
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 *===========================================================================*/
105 DndHandleDragging(Widget widget,XEvent *event)
108 Window root = RootWindowOfScreen(XtScreenOfObject(widget));
109 XtAppContext app= XtWidgetToApplicationContext(widget);
110 Window DispatchWindow;
113 if(Dragging) return 0;
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,
123 /* Wait for button release */
124 Dragging=1; RootFlag=0;
127 XAllowEvents(dpy,SyncPointer,CurrentTime);
128 XtAppNextEvent(app,&Event);
132 if(Event.xbutton.subwindow)
139 XtDispatchEvent(&Event);
144 /* Now release the pointer */
145 XUngrabPointer(dpy,CurrentTime);
146 /* Try to guess if the drop occurred in the root window */
149 Target=XmuClientWindow(dpy,Event.xbutton.subwindow);
150 if (Target==Event.xbutton.subwindow)
151 DispatchWindow=Target;
153 DispatchWindow=PointerWindow;
156 Target=DispatchWindow=XtWindow(MainWidget);
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;
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);
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);
187 /*=============================================================== DndIsIcon
188 * Return non-zero if the application is iconic (widget=toplevel)
189 *========================================================================*/
191 DndIsIcon(Widget widget)
195 unsigned long WinState,JunkLong;
196 unsigned char *Property;
198 XGetWindowProperty(dpy,XtWindow(widget),WM_STATE,
199 0L,2L,False,AnyPropertyType,
200 &JunkAtom,&JunkInt,&WinState,&JunkLong,
202 WinState=(unsigned long)(*((long*)Property));
203 return (WinState==3);
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 *=========================================================================*/
212 DndInitialize(Widget shell)
218 dpy = XtDisplayOfObject(shell);
219 screen = DefaultScreen(dpy);
220 colormap= DefaultColormap(dpy,screen);
221 root = DefaultRootWindow(dpy);
224 Black.pixel=BlackPixel(dpy,screen);
225 White.pixel=WhitePixel(dpy,screen);
226 XQueryColor(dpy,colormap,&Black);
227 XQueryColor(dpy,colormap,&White);
229 for(i=1;i!=DndEND;i++)
231 DndCursor[i].ImagePixmap=
232 XCreateBitmapFromData(dpy,root,
233 (char *) DndCursor[i].ImageData,
235 DndCursor[i].Height);
236 DndCursor[i].MaskPixmap=
237 XCreateBitmapFromData(dpy,root,
238 (char *) DndCursor[i].MaskData,
240 DndCursor[i].Height);
241 DndCursor[i].CursorID=
242 XCreatePixmapCursor(dpy,DndCursor[i].ImagePixmap,
243 DndCursor[i].MaskPixmap,
245 DndCursor[i].HotSpotX,
246 DndCursor[i].HotSpotY);
249 DndCursor[0].CursorID=XCreateFontCursor(dpy,XC_question_arrow);
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);
258 WM_STATE=XInternAtom(dpy,"WM_STATE",True);
271 /*================================================================= DndSetData
272 * Updates the selection data.
273 *===========================================================================*/
275 DndSetData(int Type,unsigned char *Data,unsigned long Size)
277 Window root = DefaultRootWindow(dpy);
279 unsigned char *AuxData;
280 unsigned long BackSize=Size;
284 /* Set the data type -- allow any type */
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)
295 AuxSize = ( (Size<=(INT_MAX)) ? (int)Size : (INT_MAX) );
296 XChangeProperty(dpy,root,DndSelection,XA_STRING,8,
297 PropModeAppend,Data,AuxSize);
300 /* Set the data for old DND version */
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)
309 AuxSize = ( (Size<=(INT_MAX)) ? (int)Size : (INT_MAX) );
310 XChangeProperty(dpy,root,OldDndSelection,XA_STRING,8,
311 PropModeAppend,Data,AuxSize);
314 /* Everything is now ok */
318 /*================================================================== DndGetData
319 * Return a pointer to the current data. See HOWTO for more details.
320 *===========================================================================*/
322 DndGetData(XEvent *event, unsigned char **Data,unsigned long *Size)
324 Window root = DefaultRootWindow(dpy);
326 Atom ActualType,ActualDndSelection;
328 unsigned long RemainingBytes;
330 ActualDndSelection=(DndProtocolVersion(event) == 0L ?
334 XGetWindowProperty(dpy,root,ActualDndSelection,
336 FALSE,AnyPropertyType,
337 &ActualType,&ActualFormat,
338 Size,&RemainingBytes,
342 /*================================== DndDataType DndDragButtons DndSourceWidget
344 * Return information about the Dnd event received. If a non-dnd event is
345 * passed, the function DndDataType returns DndNotDnd, and the others
347 *===========================================================================*/
349 DndDataType(XEvent *event)
353 if(!DndIsDropMessage(event)) return DndNotDnd;
354 Type=(int)(event->xclient.data.l[0]);
355 if(Type>=DndEND) Type=DndUnknown;
360 DndDragButtons(XEvent *event)
362 if(!DndIsDropMessage(event)) return 0;
363 return (unsigned int)(event->xclient.data.l[1]);
367 DndSourceWindow(XEvent *event)
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]);
377 DndDropRootCoordinates(XEvent *event,int *x,int *y)
379 if(!DndIsDropMessage(event))
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.
389 if(DndProtocolVersion(event)<1L)
391 Window root_return,child_return;
392 int win_x_return,win_y_return;
393 unsigned int mask_return;
395 XQueryPointer(dpy,DefaultRootWindow(dpy),
396 &root_return,&child_return,x,y,
397 &win_x_return,&win_y_return,&mask_return);
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);
406 DndDropCoordinates(Widget widget,XEvent *event,int *x,int *y)
411 DndDropRootCoordinates(event,&root_x,&root_y);
412 XTranslateCoordinates(dpy,DefaultRootWindow(dpy),
420 DndProtocolVersion(XEvent *event)
422 if(!DndIsDropMessage(event)) return -1L;
423 return event->xclient.data.l[4];
427 DndIsDropMessage(XEvent *event)
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;
437 DndChangeCursor(int Type,int width,int height,char *image,char *mask,
440 DndCursor[Type].ImagePixmap=
441 XCreateBitmapFromData(dpy,DefaultRootWindow(dpy),
443 DndCursor[Type].MaskPixmap=
444 XCreateBitmapFromData(dpy,DefaultRootWindow(dpy),
446 DndCursor[Type].CursorID=
447 XCreatePixmapCursor(dpy,DndCursor[Type].ImagePixmap,
448 DndCursor[Type].MaskPixmap,