update.
[chise/xemacs-chise.git-] / src / offix.c
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
359 unsigned int
360 DndDragButtons(XEvent *event)
361 {
362   if(!DndIsDropMessage(event)) return 0;
363   return (unsigned int)(event->xclient.data.l[1]);
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 }