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 }
|