197
|
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 <values.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 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 DndHandleDragging(Widget widget,XEvent *event)
|
|
105 {
|
|
106 XEvent Event;
|
|
107 Window root = RootWindowOfScreen(XtScreenOfObject(widget));
|
|
108 XtAppContext app= XtWidgetToApplicationContext(widget);
|
|
109 Window DispatchWindow;
|
|
110 int DropX,DropY;
|
|
111
|
|
112 if(Dragging) return 0;
|
|
113
|
|
114 XUngrabPointer(dpy,CurrentTime);
|
|
115 /* Take control over the pointer */
|
|
116 XGrabPointer(dpy,root,False,
|
|
117 ButtonMotionMask|ButtonPressMask|ButtonReleaseMask,
|
|
118 GrabModeSync,GrabModeAsync,root,
|
|
119 DndCursor[DataType].CursorID,
|
|
120 CurrentTime);
|
|
121
|
|
122 /* Wait for button release */
|
|
123 Dragging=1; RootFlag=0;
|
|
124 while(Dragging)
|
|
125 {
|
|
126 XAllowEvents(dpy,SyncPointer,CurrentTime);
|
|
127 XtAppNextEvent(app,&Event);
|
|
128 switch(Event.type)
|
|
129 {
|
|
130 case ButtonRelease:
|
|
131 if(Event.xbutton.subwindow)
|
|
132 RootFlag=0;
|
|
133 else
|
|
134 RootFlag=1;
|
|
135 Dragging=0;
|
|
136 break;
|
|
137 default:
|
|
138 XtDispatchEvent(&Event);
|
|
139 break;
|
|
140 }
|
|
141 }
|
|
142 DataOK=0;
|
|
143 /* Now release the pointer */
|
|
144 XUngrabPointer(dpy,CurrentTime);
|
|
145 /* Try to guess if the drop occurred in the root window */
|
|
146 if(!RootFlag)
|
|
147 {
|
|
148 Target=XmuClientWindow(dpy,Event.xbutton.subwindow);
|
|
149 if (Target==Event.xbutton.subwindow)
|
|
150 DispatchWindow=Target;
|
|
151 else
|
|
152 DispatchWindow=PointerWindow;
|
|
153 }
|
|
154 else
|
|
155 Target=DispatchWindow=XtWindow(MainWidget);
|
|
156
|
|
157 /* Now build the event structure */
|
|
158 DropX=Event.xbutton.x_root;
|
|
159 DropY=Event.xbutton.y_root;
|
|
160 Event.xclient.type = ClientMessage;
|
|
161 Event.xclient.display = dpy;
|
|
162 Event.xclient.message_type = DndProtocol;
|
|
163 Event.xclient.format = 32;
|
|
164 Event.xclient.window = Target;
|
|
165 Event.xclient.data.l[0] = DataType;
|
|
166 Event.xclient.data.l[1] = (long)event->xbutton.state;
|
|
167 Event.xclient.data.l[2] = (long)XtWindow(widget);
|
|
168 Event.xclient.data.l[3] = DropX + 65536L*(long)DropY;
|
|
169 Event.xclient.data.l[4] = 1;
|
|
170
|
|
171 /* Send the drop message */
|
|
172 XSendEvent(dpy,DispatchWindow,True,NoEventMask,&Event);
|
|
173 /* Send an old style version of the message just in case */
|
|
174 Event.xclient.message_type = OldDndProtocol;
|
|
175 XSendEvent(dpy,DispatchWindow,True,NoEventMask,&Event);
|
|
176
|
|
177 #ifdef DEBUG
|
|
178 fprintf(stderr,"ClientMessage sent to 0x%x(0x%x).\n",
|
|
179 DispatchWindow,Target);
|
|
180 fprintf(stderr,"The drop coordinates are (%d,%d).\n",DropX,DropY);
|
|
181 #endif
|
|
182
|
|
183 return 1;
|
|
184 }
|
|
185
|
|
186 /*=============================================================== DndIsIcon
|
|
187 * Return non-zero if the application is iconic (widget=toplevel)
|
|
188 *========================================================================*/
|
|
189 int
|
|
190 DndIsIcon(Widget widget)
|
|
191 {
|
|
192 Atom JunkAtom;
|
|
193 int JunkInt;
|
|
194 unsigned long WinState,JunkLong;
|
|
195 unsigned char *Property;
|
|
196
|
|
197 XGetWindowProperty(dpy,XtWindow(widget),WM_STATE,
|
|
198 0L,2L,False,AnyPropertyType,
|
|
199 &JunkAtom,&JunkInt,&WinState,&JunkLong,
|
|
200 &Property);
|
|
201 WinState=(unsigned long)(*((long*)Property));
|
|
202 return (WinState==3);
|
|
203 }
|
|
204
|
|
205 /*============================================================ DndInitialize
|
|
206 * Must be called anywhere before the top level widget creation and the
|
|
207 * main loop. Initialize global variables and bind the DndDispatch function
|
|
208 * to the top level widget. Creates the cursors to be used in drag actions.
|
|
209 *=========================================================================*/
|
|
210 void
|
|
211 DndInitialize(Widget shell)
|
|
212 {
|
|
213 int screen,i;
|
|
214 Colormap colormap;
|
|
215 Window root;
|
|
216
|
|
217 dpy = XtDisplayOfObject(shell);
|
|
218 screen = DefaultScreen(dpy);
|
|
219 colormap= DefaultColormap(dpy,screen);
|
|
220 root = DefaultRootWindow(dpy);
|
|
221
|
|
222
|
|
223 Black.pixel=BlackPixel(dpy,screen);
|
|
224 White.pixel=WhitePixel(dpy,screen);
|
|
225 XQueryColor(dpy,colormap,&Black);
|
|
226 XQueryColor(dpy,colormap,&White);
|
|
227
|
|
228 for(i=1;i!=DndEND;i++)
|
|
229 {
|
|
230 DndCursor[i].ImagePixmap=
|
|
231 XCreateBitmapFromData(dpy,root,
|
|
232 DndCursor[i].ImageData,
|
|
233 DndCursor[i].Width,
|
|
234 DndCursor[i].Height);
|
|
235 DndCursor[i].MaskPixmap=
|
|
236 XCreateBitmapFromData(dpy,root,
|
|
237 DndCursor[i].MaskData,
|
|
238 DndCursor[i].Width,
|
|
239 DndCursor[i].Height);
|
|
240 DndCursor[i].CursorID=
|
|
241 XCreatePixmapCursor(dpy,DndCursor[i].ImagePixmap,
|
|
242 DndCursor[i].MaskPixmap,
|
|
243 &Black,&White,
|
|
244 DndCursor[i].HotSpotX,
|
|
245 DndCursor[i].HotSpotY);
|
|
246 }
|
|
247
|
|
248 DndCursor[0].CursorID=XCreateFontCursor(dpy,XC_question_arrow);
|
|
249
|
|
250 /* These two are for older versions */
|
|
251 OldDndProtocol=XInternAtom(dpy,"DndProtocol",FALSE);
|
|
252 OldDndSelection=XInternAtom(dpy,"DndSelection",FALSE);
|
|
253 /* Now the correct stuff */
|
|
254 DndProtocol=XInternAtom(dpy,"_DND_PROTOCOL",FALSE);
|
|
255 DndSelection=XInternAtom(dpy,"_DND_SELECTION",FALSE);
|
|
256
|
|
257 WM_STATE=XInternAtom(dpy,"WM_STATE",True);
|
|
258 Dragging=0;
|
|
259 DragPrecision=10;
|
|
260 RootFlag=0;
|
|
261 MainWidget=shell;
|
|
262 }
|
|
263
|
|
264 int DndIsDragging(void) { return Dragging; }
|
|
265
|
|
266 /*================================================================= DndSetData
|
|
267 * Updates the selection data.
|
|
268 *===========================================================================*/
|
|
269 void DndSetData(int Type,unsigned char *Data,unsigned long Size)
|
|
270 {
|
|
271 Window root = DefaultRootWindow(dpy);
|
|
272 int AuxSize;
|
|
273 unsigned char *AuxData;
|
|
274 unsigned long BackSize=Size;
|
|
275
|
|
276 if (DataOK) return;
|
|
277
|
|
278 /* Set the data type -- allow any type */
|
|
279 DataType = Type;
|
|
280
|
|
281 /* Set the data */
|
|
282 AuxData = Data;
|
|
283 AuxSize = ( Size <= MAXINT ? (int)Size : MAXINT );
|
|
284 XChangeProperty(dpy,root,DndSelection,XA_STRING,8,
|
|
285 PropModeReplace,Data,AuxSize);
|
|
286 for(Size-=(unsigned long)AuxSize;Size;Size-=(unsigned long)AuxSize)
|
|
287 {
|
|
288 Data+=AuxSize;
|
|
289 AuxSize = ( (Size<=(MAXINT)) ? (int)Size : (MAXINT) );
|
|
290 XChangeProperty(dpy,root,DndSelection,XA_STRING,8,
|
|
291 PropModeAppend,Data,AuxSize);
|
|
292 }
|
|
293
|
|
294 /* Set the data for old DND version */
|
|
295 Size = BackSize;
|
|
296 AuxData = Data;
|
|
297 AuxSize = ( Size <= MAXINT ? (int)Size : MAXINT );
|
|
298 XChangeProperty(dpy,root,OldDndSelection,XA_STRING,8,
|
|
299 PropModeReplace,Data,AuxSize);
|
|
300 for(Size-=(unsigned long)AuxSize;Size;Size-=(unsigned long)AuxSize)
|
|
301 {
|
|
302 Data+=AuxSize;
|
|
303 AuxSize = ( (Size<=(MAXINT)) ? (int)Size : (MAXINT) );
|
|
304 XChangeProperty(dpy,root,OldDndSelection,XA_STRING,8,
|
|
305 PropModeAppend,Data,AuxSize);
|
|
306 }
|
|
307
|
|
308 /* Everything is now ok */
|
|
309 DataOK=1;
|
|
310 }
|
|
311
|
|
312 /*================================================================== DndGetData
|
|
313 * Return a pointer to the current data. Se HOWTO for more details.
|
|
314 *===========================================================================*/
|
|
315 void DndGetData(XEvent *event, unsigned char **Data,unsigned long *Size)
|
|
316 {
|
|
317 Window root = DefaultRootWindow(dpy);
|
|
318
|
|
319 Atom ActualType,ActualDndSelection;
|
|
320 int ActualFormat;
|
|
321 unsigned long RemainingBytes;
|
|
322
|
|
323 ActualDndSelection=(DndProtocolVersion(event) == 0L ?
|
|
324 OldDndSelection :
|
|
325 DndSelection );
|
|
326
|
|
327 XGetWindowProperty(dpy,root,ActualDndSelection,
|
|
328 0L,1000000L,
|
|
329 FALSE,AnyPropertyType,
|
|
330 &ActualType,&ActualFormat,
|
|
331 Size,&RemainingBytes,
|
|
332 Data);
|
|
333 }
|
|
334
|
|
335 /*================================== DndDataType DndDragButtons DndSourceWidget
|
|
336 *
|
|
337 * Return information about the Dnd event received. If a non-dnd event is
|
|
338 * passed, the function DndDataType returns DndNotDnd, and the others
|
|
339 * return zero.
|
|
340 *===========================================================================*/
|
|
341 int DndDataType(XEvent *event)
|
|
342 {
|
|
343 int Type;
|
|
344
|
|
345 if(!DndIsDropMessage(event)) return DndNotDnd;
|
|
346 Type=(int)(event->xclient.data.l[0]);
|
|
347 if(Type>=DndEND) Type=DndUnknown;
|
|
348 return Type;
|
|
349 }
|
|
350
|
|
351 unsigned int DndDragButtons(XEvent *event)
|
|
352 {
|
|
353 if(!DndIsDropMessage(event)) return 0;
|
|
354 return (unsigned int)(event->xclient.data.l[1]);
|
|
355 }
|
|
356
|
|
357 Window DndSourceWindow(XEvent *event)
|
|
358 {
|
|
359 if(!DndIsDropMessage(event)) return 0;
|
|
360 if(DndProtocolVersion(event)<__DragAndDropH__)
|
|
361 /* We will try to do something about it, but nothing is certain */
|
|
362 return XtWindow((Widget)(event->xclient.data.l[2]));
|
|
363 return (Window)(event->xclient.data.l[2]);
|
|
364 }
|
|
365
|
|
366 void DndDropRootCoordinates(XEvent *event,int *x,int *y)
|
|
367 {
|
|
368 if(!DndIsDropMessage(event))
|
|
369 {
|
|
370 *x=0; *y=0;
|
|
371 return;
|
|
372 }
|
|
373
|
|
374 /* If it is an old protocol version we try to get the coordinates
|
|
375 using the current pointer position. Of course, the pointer may have
|
|
376 moved since the drop, but there's nothing we can do about it.
|
|
377 */
|
|
378 if(DndProtocolVersion(event)<1L)
|
|
379 {
|
|
380 Window root_return,child_return;
|
|
381 int win_x_return,win_y_return;
|
|
382 unsigned int mask_return;
|
|
383
|
|
384 XQueryPointer(dpy,DefaultRootWindow(dpy),
|
|
385 &root_return,&child_return,x,y,
|
|
386 &win_x_return,&win_y_return,&mask_return);
|
|
387 return;
|
|
388 }
|
|
389 /* Thanks god you are using a decent protocol version */
|
|
390 *x=(int)((long)(event->xclient.data.l[3]) & 0xffff);
|
|
391 *y=(int)((long)(event->xclient.data.l[3])/65536);
|
|
392 }
|
|
393
|
|
394 void DndDropCoordinates(Widget widget,XEvent *event,int *x,int *y)
|
|
395 {
|
|
396 int root_x,root_y;
|
|
397 Window child_return;
|
|
398
|
|
399 DndDropRootCoordinates(event,&root_x,&root_y);
|
|
400 XTranslateCoordinates(dpy,DefaultRootWindow(dpy),
|
|
401 XtWindow(widget),
|
|
402 root_x,root_y,
|
|
403 x,y,
|
|
404 &child_return);
|
|
405 }
|
|
406
|
|
407 long DndProtocolVersion(XEvent *event)
|
|
408 {
|
|
409 if(!DndIsDropMessage(event)) return -1L;
|
|
410 return event->xclient.data.l[4];
|
|
411 }
|
|
412
|
|
413 int DndIsDropMessage(XEvent *event)
|
|
414 {
|
|
415 if(event->xclient.type != ClientMessage) return 0;
|
|
416 if(event->xclient.message_type == OldDndProtocol &&
|
|
417 event->xclient.data.l[4]==0) return 1;
|
|
418 if(event->xclient.message_type == DndProtocol) return 1;
|
|
419 return 0;
|
|
420 }
|
|
421
|
|
422 void DndChangeCursor(int Type,int width,int height,char *image,char *mask,
|
|
423 int hot_x,int hot_y)
|
|
424 {
|
|
425 DndCursor[Type].ImagePixmap=
|
|
426 XCreateBitmapFromData(dpy,DefaultRootWindow(dpy),
|
|
427 image,width,height);
|
|
428 DndCursor[Type].MaskPixmap=
|
|
429 XCreateBitmapFromData(dpy,DefaultRootWindow(dpy),
|
|
430 mask,width,height);
|
|
431 DndCursor[Type].CursorID=
|
|
432 XCreatePixmapCursor(dpy,DndCursor[Type].ImagePixmap,
|
|
433 DndCursor[Type].MaskPixmap,
|
|
434 &Black,&White,
|
|
435 hot_x,hot_y);
|
|
436 }
|