1 /* $Header: /cvsroot/lesstif/lesstif/test/extra/daniel/test1.c,v 1.2 2002/05/15 10:55:06 amai Exp $ */
2 /***********************************************************/
3 /* Copyright 1996 Daniel Dardailler.
4 Permission to use, copy, modify, distribute, and sell this software
5 for any purpose is hereby granted without fee, provided that the above
6 copyright notice appear in all copies and that both that copyright
7 notice and this permission notice appear in supporting documentation,
8 and that the name of Daniel Dardailler not be used in advertising or
9 publicity pertaining to distribution of the software without specific,
10 written prior permission. Daniel Dardailler makes no representations
11 about the suitability of this software for any purpose. It is
12 provided "as is" without express or implied warranty.
13 ************************************************************/
14
15 #include <stdlib.h>
16 #include <stdio.h>
17
18 #include <X11/Xlib.h>
19 #include <X11/Intrinsic.h>
20 #include <X11/StringDefs.h>
21 #include <X11/Xos.h>
22 #include <X11/Xatom.h>
23 #include <X11/Shell.h>
24 #include <X11/Xaw/Label.h>
25 #include <X11/Xaw/Box.h>
26 #include <X11/cursorfont.h>
27 #include "Dnd.h"
28
29 /************************************************************
30 This demo program creates two Aw labels, one is draggrable,
31 the other is a drop site (change the label when you drop)
32 It does a bunch of tracing.
33 ************************************************************/
34
35
36 static void StartDrag(Widget w, XEvent *event,
37 String *params, Cardinal *num_params);
38 static Atom Dnd_wm_state, Dnd_selection, Dnd_transfer_success ;
39 static Cursor valid_cursor, invalid_cursor;
40 static String drag_trans = "<Btn2Down>: StartDrag()" ;
41 static Atom my_targets[] = { XA_STRING } ;
42 static Atom cv_targets[3] ;
43 static XtPointer closures[3] ;
44 static Boolean in_drag = False ;
45 static Widget drag_label, drop_label, top_level, box ;
46 static XtActionsRec drag_actions [] = {{"StartDrag", StartDrag}} ;
47
48
49 static void
SelectTopLevels(Display * dpy,Window win)50 SelectTopLevels(
51 Display *dpy,
52 Window win)
53 {
54 Window root, parent;
55 Window *children;
56 unsigned int nchildren;
57 int i;
58 Atom type = None;
59 int format;
60 unsigned long nitems, after;
61 unsigned char *data;
62
63 if (win != DefaultRootWindow(dpy)) {
64 /* only ask property kid of the root */
65 XGetWindowProperty(dpy, win, Dnd_wm_state , 0, 0, False,
66 AnyPropertyType,
67 &type, &format, &nitems, &after, &data);
68 XFree(data);
69 } else {
70 printf ("WM_STATE on: ");
71 }
72
73 /* if the window has the WM_STATE property, select D&D events for it */
74 if (type) {
75 XSelectInput(dpy, win, EnterWindowMask | LeaveWindowMask
76 | Button2MotionMask);
77 printf("%d ",win);
78 } else {
79 if (!XQueryTree(dpy, win, &root, &parent, &children, &nchildren) ||
80 (nchildren == 0))
81 return ;
82 for (i = nchildren - 1; i >= 0; i--) {
83 SelectTopLevels(dpy, children[i]) ;
84 }
85 XFree(children);
86 }
87
88 if (win == DefaultRootWindow(dpy)) printf("\n----------\n");
89 }
90
91 static void
ChangeCursorInvalid(Display * dpy,Time time)92 ChangeCursorInvalid(Display * dpy, Time time)
93 {
94 printf("ChangeCursorInvalid\n");
95 XChangeActivePointerGrab(dpy,
96 ButtonMotionMask|ButtonReleaseMask|
97 EnterWindowMask|LeaveWindowMask,
98 invalid_cursor, time);
99 }
100
101 static void
ChangeCursorValid(Display * dpy,Time time)102 ChangeCursorValid(Display * dpy, Time time)
103 {
104 printf("ChangeCursorValid\n");
105 XChangeActivePointerGrab(dpy,
106 ButtonMotionMask|ButtonReleaseMask|
107 EnterWindowMask|LeaveWindowMask,
108 valid_cursor, time);
109 }
110
111
112 static void
StartDrag(Widget w,XEvent * event,String * params,Cardinal * num_params)113 StartDrag(
114 Widget w,
115 XEvent *event,
116 String *params, Cardinal *num_params)
117 {
118 printf("StartDrag\n");
119
120 /* grab the pointer so that we get all crossing events */
121 XGrabPointer(XtDisplay(w), DefaultRootWindow(XtDisplay(w)), True,
122 ButtonMotionMask|ButtonReleaseMask|
123 EnterWindowMask|LeaveWindowMask,
124 GrabModeSync, GrabModeAsync,
125 None, invalid_cursor,
126 event->xbutton.time);
127
128 in_drag = True ;
129
130 XAllowEvents(XtDisplay(w), SyncPointer, event->xbutton.time);
131 }
132
133 static Boolean
SourceConvert(Widget w,Atom * selection,Atom * target,Atom * type,XtPointer * value,unsigned long * length,int * format)134 SourceConvert(
135 Widget w,
136 Atom *selection,
137 Atom *target,
138 Atom *type,
139 XtPointer *value,
140 unsigned long *length,
141 int *format )
142 {
143 printf("SourceConvert %d %s\n", *target,
144 XGetAtomName(XtDisplay(w),*target));
145
146 if (*target == XA_STRING) {
147 String label ;
148 *type = XA_STRING;
149 XtVaGetValues(drag_label, XtNlabel, &label, NULL);
150 *value = XtNewString(label);
151 *length = strlen(label);
152 printf("value %s length %d\n", *value, *length);
153 *format = 8;
154 return True;
155 } else
156 if (*target == Dnd_transfer_success) {
157 printf("transfer success\n");
158 *type = Dnd_transfer_success;
159 *length = 0 ;
160 *value = NULL ;
161 *format = 32;
162 return True;
163 } else
164 if (*target == XInternAtom(XtDisplay(w), "DELETE", False)) {
165 *type = XInternAtom(XtDisplay(w), "NULL", False);
166 *value = NULL;
167 *length = 0;
168 *format = 8;
169 return True;
170 }
171
172 return False;
173 }
174
175 static void
ReceiverConvert(Widget w,XtPointer client_data,Atom * selection,Atom * type,XtPointer val,unsigned long * length,int * format)176 ReceiverConvert(
177 Widget w,
178 XtPointer client_data,
179 Atom *selection,
180 Atom *type,
181 XtPointer val,
182 unsigned long *length,
183 int *format )
184 {
185 printf("ReceiverConvert selection %d %s\n",
186 *selection, XGetAtomName(XtDisplay(w), *selection));
187 printf("type %d %s\n",*type,
188 (*type)?XGetAtomName(XtDisplay(w), *type):"00");
189 printf("format %d\n",*format);
190 printf("length %d\n",*length);
191
192 if (*type != 0 && *type != Dnd_transfer_success) {
193 XtVaSetValues(drop_label, XtNlabel, val, NULL);
194
195 /* copy data received in our drop site */
196 /* indicate success by asking the source to convert
197 XmTRANSFER_SUCCESS */
198 XtGetSelectionValue(w, *selection,
199 Dnd_transfer_success, ReceiverConvert, NULL,
200 CurrentTime);
201 /* we should get called again with this target */
202 }
203 /* else, got transfer success back, free whatever we want */
204
205 /* Note: I get some weird
206 Warning:
207 Name: dd
208 Class: ApplicationShell
209 We lost the drop selection
210 on a (real) Motif initiator side when I get to this point...
211 No time to investigate right now */
212 }
213
214 int
main(argc,argv)215 main(argc, argv)
216 int argc;
217 char **argv;
218 {
219 XtAppContext app_context;
220 unsigned char src_protocol_style = DND_DRAG_DYNAMIC ;
221 char my_dnd_selection_name[30] ;
222 Boolean do_messaging = False, do_drop = False, in_drop_site = False ;
223 Window cur_window = 0 ;
224 Atom * src_targets ;
225 unsigned short num_src_targets ;
226 Position dropx, dropy ;
227 Dimension dropw, droph ;
228
229 top_level = XtVaAppInitialize(&app_context, "DndTest",
230 NULL, 0, &argc, argv, NULL,
231 XtNallowShellResize, True, NULL);
232 XtAppAddActions(app_context, (XtActionList)drag_actions,
233 XtNumber(drag_actions));
234
235 /* Init atoms */
236 Dnd_wm_state = XInternAtom(XtDisplay(top_level), "WM_STATE", False);
237 Dnd_transfer_success = XInternAtom(XtDisplay(top_level),
238 "XmTRANSFER_SUCCESS", False);
239
240 /* This one needs to be unique for each drag, not just for each client,
241 as there can be race condition with drag transfer not finish while
242 another drag is started */
243 sprintf(my_dnd_selection_name, "_MY_DND_SELECTION_%d", getpid());
244 Dnd_selection = XInternAtom(XtDisplay(top_level),
245 my_dnd_selection_name, False);
246
247
248 invalid_cursor = XCreateFontCursor(XtDisplay(top_level),XC_pirate);
249 valid_cursor = XCreateFontCursor(XtDisplay(top_level), XC_target);
250
251 box = XtCreateManagedWidget("box",
252 boxWidgetClass, top_level, NULL, 0);
253
254 drag_label = XtVaCreateManagedWidget("drag",
255 labelWidgetClass, box, NULL);
256 XtOverrideTranslations(drag_label, XtParseTranslationTable(drag_trans));
257
258 drop_label = XtVaCreateManagedWidget("drop",
259 labelWidgetClass, box, NULL);
260
261 XtRealizeWidget(top_level); /* need the windows right below */
262
263
264 /* register as a drag source. In real life, one would have to
265 update the property per drag source, for real target indication */
266 DndWriteSourceProperty(XtDisplay(top_level), XtWindow(top_level),
267 Dnd_selection, my_targets, 1);
268
269 /* gotta be ready to convert our selection - same thing */
270 XtOwnSelection(top_level, Dnd_selection, CurrentTime,
271 SourceConvert, NULL, NULL);
272
273 if (argc > 1) {
274 if (strcmp(argv[1], "none") == 0)
275 src_protocol_style = DND_DRAG_NONE ;
276 else
277 if (strcmp(argv[1], "drop_only") == 0)
278 src_protocol_style = DND_DRAG_DROP_ONLY;
279 else
280 if (strcmp(argv[1], "dynamic") == 0)
281 src_protocol_style = DND_DRAG_DYNAMIC ;
282 }
283
284 /* register the top_level as a drop site too */
285 DndWriteReceiverProperty(XtDisplay(top_level), XtWindow(top_level),
286 src_protocol_style);
287
288 /* really just need to be done on start drag (and tracked too as window
289 are added, destroyed. Motif uses private properties to find these */
290 SelectTopLevels(XtDisplay(top_level),
291 DefaultRootWindow(XtDisplay(top_level)));
292
293 /* for now use my own loop for tracking events directly,
294 rather messy... */
295 while(1) {
296 XEvent event ;
297 unsigned char receiv_protocol_style;
298 XClientMessageEvent cm ;
299 DndData dnd_data ;
300 char receiver ;
301
302 XtAppNextEvent(app_context, &event);
303
304 switch(event.type) {
305
306 case EnterNotify:
307 if (!in_drag) break ;
308
309 printf("enter win %d sub %d\n", event.xcrossing.window,
310 event.xcrossing.subwindow);
311
312 cur_window = event.xcrossing.window ;
313
314 /* just enter a new top_level, check its dnd data */
315 DndReadReceiverProperty(event.xcrossing.display,
316 event.xcrossing.window,
317 &receiv_protocol_style);
318
319 printf("receiv_protocol_style %d\n",receiv_protocol_style);
320
321 if (receiv_protocol_style == DND_DRAG_NONE) {
322 ChangeCursorInvalid(event.xcrossing.display,
323 event.xcrossing.time) ;
324 do_messaging = False ;
325 do_drop = False ;
326 } else
327 if (receiv_protocol_style == DND_DRAG_DROP_ONLY) {
328 ChangeCursorValid(event.xcrossing.display,
329 event.xcrossing.time) ;
330 do_messaging = False ;
331 do_drop = True ;
332 } else
333 if (receiv_protocol_style == DND_DRAG_DYNAMIC) {
334
335 /* we'll get valid visual on drop_site_entered */
336 ChangeCursorInvalid(event.xcrossing.display,
337 event.xcrossing.time) ;
338
339 dnd_data.reason = DND_TOP_LEVEL_ENTER ;
340 dnd_data.time = event.xcrossing.time ;
341 dnd_data.src_window = XtWindow(top_level);
342 dnd_data.property = Dnd_selection ;
343
344 DndFillClientMessage (event.xcrossing.display,
345 cur_window,
346 &cm, &dnd_data, 0);
347
348 XSendEvent(event.xcrossing.display,
349 cur_window, False, 0, (XEvent *)&cm) ;
350
351 printf("XSendEvent DND_TOP_LEVEL_ENTER %d\n", cur_window);
352
353 do_messaging = True ;
354 do_drop = False ;
355 }
356 break ;
357 case LeaveNotify:
358 if (!in_drag || !do_messaging) break ;
359
360 printf("leave win %d sub %d\n", event.xcrossing.window,
361 event.xcrossing.subwindow);
362
363 ChangeCursorInvalid(event.xcrossing.display,
364 event.xcrossing.time) ;
365
366 dnd_data.reason = DND_TOP_LEVEL_LEAVE ;
367 dnd_data.time = event.xcrossing.time ;
368 dnd_data.src_window = XtWindow(top_level);
369
370 DndFillClientMessage (event.xcrossing.display,
371 cur_window,
372 &cm, &dnd_data, 0);
373
374 XSendEvent(event.xcrossing.display,
375 cur_window, False, 0, (XEvent *)&cm) ;
376
377 printf("XSendEvent DND_TOP_LEVEL_LEAVE %d\n",
378 cur_window);
379
380 do_messaging = False ;
381 cur_window = 0 ;
382
383 break ;
384 case MotionNotify:
385 if (!in_drag || !do_messaging) break ;
386
387 printf("motion win %d sub %d\n", event.xmotion.window,
388 event.xmotion.subwindow);
389
390 dnd_data.reason = DND_DRAG_MOTION ;
391 dnd_data.time = event.xmotion.time ;
392 dnd_data.operation = DND_MOVE|DND_COPY;
393 dnd_data.operations = DND_MOVE|DND_COPY;
394 dnd_data.x = event.xmotion.x_root ;
395 dnd_data.y = event.xmotion.y_root ;
396
397 DndFillClientMessage (event.xmotion.display,
398 cur_window,
399 &cm, &dnd_data, 0);
400
401 XSendEvent(event.xmotion.display,
402 cur_window, False, 0, (XEvent *)&cm) ;
403
404 printf("XSendEvent DND_DRAG_MOTION %d\n", cur_window);
405
406 break ;
407 case ButtonRelease:
408 if (!in_drag) break ;
409
410 printf("release in %d\n",cur_window);
411 XUngrabPointer(XtDisplay(top_level), event.xbutton.time);
412 in_drag = False ;
413
414 if (do_drop) {
415
416 dnd_data.reason = DND_DROP_START ;
417 dnd_data.time = event.xbutton.time ;
418 dnd_data.operation = DND_MOVE|DND_COPY;
419 dnd_data.operations = DND_MOVE|DND_COPY;
420 dnd_data.src_window = XtWindow(top_level);
421 dnd_data.property = Dnd_selection ;
422
423 DndFillClientMessage (event.xbutton.display,
424 cur_window,
425 &cm, &dnd_data, 0);
426
427 XSendEvent(event.xbutton.display,
428 cur_window, False, 0, (XEvent *)&cm) ;
429
430 printf("XSendEvent DND_DROP_START %d\n", cur_window);
431
432 do_messaging = False ;
433 do_drop = False ;
434 }
435 break ;
436 case ClientMessage:
437
438 if (!(DndParseClientMessage ((XClientMessageEvent*)&event,
439 &dnd_data, &receiver))) {
440 printf("not a valid Dnd client message\n");
441 break ;
442 }
443
444 if (dnd_data.reason == DND_DRAG_MOTION) {
445 if (receiver) {
446 printf("receiver echoing drag motion\n");
447 /* don't need to do anything, really, since
448 we're not supporting better_x,y position */
449 } else {
450 printf("source sending a drag motion\n");
451 /* check if in drop site, and depending on the state,
452 send a drop site enter or drop site leave or echo */
453 printf("x %d y %d\n",dnd_data.x, dnd_data.y);
454 if (dnd_data.x > dropx && dnd_data.y > dropy &&
455 dnd_data.x < dropx + (Position)dropw &&
456 dnd_data.y < dropy + (Position)droph) {
457 if (!in_drop_site) {
458 in_drop_site = True ;
459
460 dnd_data.reason = DND_DROP_SITE_ENTER ;
461 dnd_data.time = CurrentTime ;
462 dnd_data.operation = DND_MOVE|DND_COPY;
463 dnd_data.operations = DND_MOVE|DND_COPY;
464
465 DndFillClientMessage (event.xclient.display,
466 cur_window,
467 &cm, &dnd_data, 0);
468
469 XSendEvent(event.xbutton.display,
470 cur_window, False, 0,
471 (XEvent *)&cm) ;
472
473
474 printf("XSendEvent DND_DROP_SITE_ENTER %d\n",
475 cur_window);
476
477 } else {
478 dnd_data.reason = DND_DRAG_MOTION ;
479 dnd_data.time = CurrentTime ;
480 dnd_data.operation = DND_MOVE|DND_COPY;
481 dnd_data.operations = DND_MOVE|DND_COPY;
482
483 DndFillClientMessage (event.xclient.display,
484 cur_window,
485 &cm, &dnd_data, 0);
486
487 XSendEvent(event.xbutton.display,
488 cur_window, False, 0,
489 (XEvent *)&cm) ;
490
491
492 printf("XSendEvent DND_DRAG_MOTION %d\n",
493 cur_window);
494
495 }
496 } else {
497 if (in_drop_site) {
498 in_drop_site = False ;
499
500 dnd_data.reason = DND_DROP_SITE_LEAVE ;
501 dnd_data.time = CurrentTime ;
502
503 DndFillClientMessage (event.xclient.display,
504 cur_window,
505 &cm, &dnd_data, 0);
506
507 XSendEvent(event.xbutton.display,
508 cur_window, False, 0,
509 (XEvent *)&cm) ;
510
511
512 printf("XSendEvent DND_DROP_SITE_LEAVE %d\n",
513 cur_window);
514 }
515 }
516 }
517 } else
518 if (dnd_data.reason == DND_TOP_LEVEL_ENTER) {
519 /* get the size of our drop site for later use */
520 XtVaGetValues(drop_label,
521 XtNwidth, &dropw, XtNheight, &droph, NULL);
522 XtTranslateCoords(drop_label, 0, 0, &dropx, &dropy);
523 printf("x %d y %d w %d h %d\n", dropx, dropy, dropw, droph);
524
525 printf("source sending a top level enter %d\n",
526 dnd_data.src_window);
527
528 cur_window = dnd_data.src_window ;
529
530 /* no answer needed, just read source property */
531 DndReadSourceProperty (event.xclient.display,
532 cur_window,
533 dnd_data.property,
534 &src_targets, &num_src_targets);
535 printf("src_targets %d num_src_targets %d\n",
536 src_targets[0], num_src_targets);
537 /* we only support string for now */
538 if (num_src_targets && src_targets[0] == XA_STRING)
539 printf("src target ok\n");
540 } else
541 if (dnd_data.reason == DND_TOP_LEVEL_LEAVE) {
542 printf("source sending a top level leave\n");
543 cur_window = 0 ;
544 /* no need to do anything */
545 } else
546 if (dnd_data.reason == DND_DROP_SITE_ENTER) {
547 printf("receiver sending drop site enter\n");
548 ChangeCursorValid(event.xclient.display, CurrentTime);
549 do_drop = True ;
550 } else
551 if (dnd_data.reason == DND_DROP_SITE_LEAVE) {
552 printf("receiver sending drop site leave\n");
553 ChangeCursorInvalid(event.xclient.display, CurrentTime);
554 do_drop = False ;
555 } else
556 if (dnd_data.reason == DND_OPERATION_CHANGED) {
557 if (receiver) {
558 printf("receiver echoing operation changed\n");
559 } else {
560 printf("source sending an operation changed\n");
561 /* need to echo */
562 }
563 } else
564 if (dnd_data.reason == DND_DROP_START) {
565 if (receiver) {
566 printf("receiver echoing drop start\n");
567 } else {
568 printf("source sending a drop start\n");
569
570 if (!in_drop_site) break ;
571
572 /* need to echo and then request a convert */
573 dnd_data.reason = DND_DROP_START ;
574 dnd_data.time = CurrentTime ;
575
576 DndFillClientMessage (event.xclient.display,
577 cur_window,
578 &cm, &dnd_data, 0);
579
580 XSendEvent(event.xbutton.display,
581 cur_window, False, 0,
582 (XEvent *)&cm) ;
583
584
585 printf("XSendEvent DND_DROP_START %d\n",
586 cur_window);
587
588 /* ask for a convertion - the selection name is
589 the same as the property on which the source
590 stored its target, weird, but that's the way it
591 was done in Motif... */
592 cv_targets[0] = XA_STRING ;
593 cv_targets[1] = XInternAtom(XtDisplay(top_level),
594 "TEXT", False);
595 cv_targets[2] = XInternAtom(XtDisplay(top_level),
596 "COMPOUND_TEXT", False);
597 XtGetSelectionValues(top_level, dnd_data.property,
598 cv_targets, XtNumber(cv_targets),
599 ReceiverConvert, closures,
600 CurrentTime);
601
602 }
603 printf("drop action %d\n", dnd_data.completion);
604 }
605 break ;
606 default:
607 break ;
608 }
609
610 XtDispatchEvent(&event);
611 }
612
613 exit(0);
614 }
615